にしへ

衝突判定(Sprite.check)入れて、デバッグやイベントコールバック関連を組み込み始めたり。
明日はドット素材の補給のターン。互いの勢いを阻害せずに上手いこと回して行かねば。

require 'dxruby'

# @todo
# - 描画関連はその内分離(予定)
# - 向き・歩行アニメ、武器を振る、ジャンプ
# - 敵・戦闘 

class TaskForce
  def initialize
    @tasks = []
  end
  def add(task) ; @tasks << task ; end
  def delete(task) ; @tasks.delete(task) ; end
  def delete_if(&block) ; @tasks.delete_if(&block) ; end  # { |t| !t.update }
  def sort ; ; end
  def run(system)
    Sprite.update(@tasks)
    task_size = @tasks.size - 1
    col_flag = false
    0.upto(task_size) { |i|
      (i+1).upto(task_size) { |j|
        col_flag = true if Sprite.check(@tasks[i], @tasks[j])
      }
    }
    Sprite.draw(@tasks)
    system[:debug_text]["Shoooooooot!!!!!"] if col_flag
    # system[:debug_map_no].call
  end
  def clean_sprite ; Sprite.clean ; end
end

module Task
  def task_init( id: nil, priority: 0, visible: true, **opt)
    @task_id, @priority = id, priority
    @active, @visible = true, visible
  end
end

module Animation
  def animation_init(image = nil)
    return unless image
    @image_tiles = image
    self.image = @image_tiles[@anime_index = 0]
  end
  def anime_size
    @image_tiles.size
  end
  def anime_frame(index)
    # return if index < 0 || index >= anime_size 
    @anime_index = index
    self.image = @image_tiles[@anime_index]
  end
end

class Actor < Sprite
  include Task
  def initialize(res, **opt)
    task_init(**opt)
    image = res[:image][opt[:image]][@task_id]
    if image
      if image.is_a?(Array)
        extend Animation
        image = animation_init(image)
     end
    else
      # 空もしくは空画像を生成予定
      self.visible = false
    end
    super(0, 0, image)
  end
  def update ; @active ; end
  
  def move(x, y)
    self.x, self.y = x, y  
    self
  end
end

class Player < Actor
  def initialize(res)
    @task_id = :kukumaru
    super(res, id: :kukumaru, image: :chara)
    self.scale_x = self.scale_y = 3
    self.x = Window.width / 2 - self.image.width / 2
    self.y = Window.height / 1.5 - self.image.height / 2
    anime_frame(2)
  end
  def update
    unless @routine
      @routine = Fiber.new {
        loop {
          self.x += Input.x * 2
          self.y += Input.y * 2
          
          Fiber.yield
        }
      }
    end
    @routine.resume
  end
  def hit(target)
    # p "hit to #{target}"
  end
  def shot(target)
    # p "shot to #{target}"
  end

end

class Enemy < Actor
  def initialize(res)
    super(res, id: :enemy, image: :enemy)
    self.scale_x = self.scale_y = 3
    self.x = Window.width / 2 - self.image.width / 2
    self.y = Window.height / 1.5 - self.image.height / 2
    anime_frame(rand(@image_tiles.size))
  end
end

class Artifact < Actor
end

class Map
  include Task
  WIDTH, HEIGHT = 20, 15
  SIZE = 32 # 16*2
  def initialize(res)
    @image_array = res[:image][:map][:mapchip]
    @map_data = [
      [5,5,5,5,5,5,0,3,3,3,3,3,3,0,5,5,5,5,5,5],
      [5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5],
      [5,0,0,0,4,0,0,0,0,0,0,0,4,0,0,0,0,0,0,5],
      [5,0,0,0,0,0,0,0,0,2,2,0,0,0,0,0,0,0,0,5],
      [5,0,0,0,0,0,0,0,2,1,1,2,0,0,0,0,0,0,0,5],
      [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0],
      [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
      [0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
      [0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0],
      [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
      [5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5],
      [5,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,5],
      [5,0,4,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,5],
      [5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,5],
      [5,5,5,5,5,5,0,0,0,0,0,0,0,0,5,5,5,5,5,5]
    ]
    @base_x = @base_y = @start_x = @start_y = 0
    @size_x, @size_y = 20, 15
    @z = 0
  end
  def draw
    Window.draw_tile(@base_x, @base_y, @map_data, @image_array, 
     @start_x, @start_y, @size_x, @size_y, @z)
  end  
end

class Event
  def initialize
    @events = {}
  end
  def add(name, listener)
    @events[name] = [] unless @events[name]
    @events[name] << listener
  end
  def delete(name, listener = nil)
    if @events[name]
      unless listener
        @events[name].clear
      else
        @events[name].delete(listener)
      end
    end
  end
  def clear ; @events.clear ; end
  def trigger(name)
    @events[name] or @events[name].each(&:call)
  end
end

module Game
  def self.start
    Game::Base.new.play
  end
  class Base
    def initialize(*opt)
      Window.caption = "九々。"
      Window.mag_filter = TEXF_POINT

      @task_force = TaskForce.new
      @res = {image: {}, data: {}}
      @debug_font_max = Font.new(40, "", weight: true, italic: true)
      @debug_font_min = Font.new(12, "")
      @system = { 
        event: Event.new,
        debug_text: proc {|text, x = 5, y = 5| Window.draw_font(x, y, text, @debug_font_max) },
        debug_map_no: proc {
            Map::WIDTH.times { |x| 
              Map::HEIGHT.times { |y|
                Window.draw_font(x*Map::SIZE, y*Map::SIZE, "#{y*Map::WIDTH+x}", @debug_font_min) 
              }
            }
          }
      }
      load_resource
    end
    RESOURCE_DATA = {
      chara: {x: 4, y: 1},
      map: {x: 3, y: 2},
      enemy: {x: 7, y: 1}
    } 
    def load_resource
      RESOURCE_DATA.each { |type, v|
        buff = @res[:image][type] = {}
        Dir.glob("img/#{type}/*.png") { |f|
          buff[File.basename(f, ".*").to_sym] = Image.load_tiles(f, v[:x], v[:y]).freeze
          buff.freeze
        }
      }
    end

    def ready
      Fiber.new {
        @task_force.add(Map.new(@res))
        @task_force.add(Enemy.new(@res).move(320-64, 224))
        @task_force.add(Enemy.new(@res).move(320+48, 224) )
        @task_force.add(Player.new(@res))

        loop {
          screen_shot          
          @task_force.run(@system)
          Fiber.yield
        }
      }
    end
    def screen_shot
      Window.get_screen_shot("screen_shot#{Time.now.to_i}.png", FORMAT_PNG) if Input.key_push?(K_S)
    end
    def play
      @routine = ready
      Window.loop { @routine.resume }
    end
  end

end