サイログ。

~雑多な記事置き場~

Ruby+YAMLでゲームブック作成スクリプト(ソース付き)

ゲームブックを作ろうと考えたときに問題になるのが、パラグラフの管理(ランダムに置き換えたりとか)というわけで、
昔、パラグラフ管理ソフトを作ったのですが、最近またゲームブック作りたい熱が出てきているので、
そのソフトのプログラムを見てみると・・・

うーん、やっぱりややこしいなぁ。

というわけで、ゲームブックのソースをYAMLで書いて、Rubyで処理するようにしてみたら・・・。


うわっ、思ったより簡単やった・・・。


これを使うと、ゲームブックの元が以下のようなYAMLファイルで出来てしまいます。

head:
  ending: エピローグ

title: なんやわけわからんけど、ゲームブックです

preface: |
    まえがき

paragraph:
  - label: pg01
    number: first
    text: |
      右へは#{label[:pg02]}へ、左へ行は#{label[:pg03]}へ、
      真ん中へは#{label[:pg05]}へ。
  - label: pg02
    text: 何もなかった。#{label[:pg01]}へ。
  - label: pg03
    text: あれ? なんか「14」とか書いてるよ・・・#{label[:pg04]}へ。
  - label: pg04
    number: 4    
    text: ごめんなさい! ゲームオーバー!
  - label: pg05
    number: last
    text: おめっとさん

ending: エンディング

postscript: あとがき

コレを変換すると、こうなります。

[アドベンチャーゲームブック]

なんやわけわからんけど、ゲームブックです
[まえがき]

まえがき

- 1 -

右へは3へ、左へ行は2へ、
真ん中へは5へ。

- 2 -

あれ? なんか「14」とか書いてるよ・・・4へ。

- 3 -

何もなかった。1へ。

- 4 -

ごめんなさい! ゲームオーバー!

- 5 -

おめっとさん

[エピローグ]

エンディング
[あとがき]

あとがき

このように変換できます。
簡単なものなので、ランダムかどうかわかりにくいけど、ちゃんとパラグラフが入れ替わっています。
また、パラグラフ番号の固定とかもちゃんとできるようになっています。

スクリプトを↓に載せておきます。
引数は、元のYAMLファイル名だけです。
気軽にバンバン使っちゃってください。
(勿論、無保証で、トラブルとかの責任は負わないので、そこんとこヨロシク!)

#YAML+Rubyでゲームブック作成
#2007.5.20 Cyross Makoto

$KCODE = "UTF8"

require 'yaml'
require 'kconv'

$VNUM = "2.00"
$VDATE = "2007.5.20"

class MakeParagraphManager
  @@heads = {"title" => "アドベンチャーゲームブック",
             "preface" => "まえがき",
             "opening" => "オープニング",
             "ruling" => "ルール解説",
             "backstory" => "背景",
             "ending" => "エンディング",
             "postscript" => "あとがき"}

  def initialize(args)
    raise Exception.new("No args. you should set gamebook script filename!") if args.length == 0
    @filename = args.shift
    @gb_data = YAML.load_file(@filename)
    @paragraph_size = @gb_data["paragraph"].length
    @paragraph_hash = Hash.new
  end
  
  def exec
    process_const if @gb_data["const"]
    randomize_paragraph
    output
  end
  
  def process_const
    chash = @gb_data["const"]
    ks = chash.keys
    ks.each{|k|
      chash[k.intern] = chash[k]
    }
  end
  
  def randomize_paragraph
    parray = Array.new(@paragraph_size)
    @paragraph_size.times{|n| parray[n] = n + 1}
    pdata = @gb_data["paragraph"]
    pdata.each_with_index{|p, i|
      if p["number"]
        pn = p["number"]
        pn, p["number"] = 1, 1 if pn == "first"
        pn, p["number"] = @paragraph_size, @paragraph_size if pn == "last"
        raise Exception.new("No args. you should set gamebook script filename!") if (pn < 1 || pn > @paragraph_size)
        parray[i] = nil
      end
    }
    parray.compact!
    pdata.each{|p|
      next if p["number"]
      v = rand(parray.size)
      p["number"] = parray[v]
      parray.delete_at(v)
    }
    pdata.each{|p|
      @paragraph_hash[p["label"].intern] = p["number"]
    }
    unless parray.empty?
      @paragraph_size.times{|n|
        pn = n + 1
        ar = pdata.find_all{|p| p["number"] == pn }
        next unless ar
        raise Exception.new("Paragraph number is overlaped! : #{pn} ") if (ar.size > 2)
      }
    end
    @gb_data["paragraph"].sort!{|a, b| a["number"] <=> b["number"] }
  end
  
  def output
    label = @paragraph_hash
    const = @gb_data["const"]
    head = @gb_data["head"]
    File.open(@filename+"gb.txt", "w"){|f|
      f.print format("title")+"\n" if @gb_data["title"]
      f.print format("preface")+"\n" if @gb_data["preface"]
      f.print format("opening")+"\n" if @gb_data["opening"]
      f.print format("ruling")+"\n" if @gb_data["ruling"]
      f.print format("backstory")+"\n" if @gb_data["backstory"]
      @gb_data["paragraph"].each{|p|
        f.print "- "+p["number"].to_s+" -\n\n"
        txt = p["text"].to_s.toutf8
        f.print eval("\""+txt+"\"")+"\n"
        f.print "\n" unless txt =~ /\n$/
      }
      f.print format("ending")+"\n" if @gb_data["ending"]
      f.print format("postscript")+"\n" if @gb_data["postscript"]
    }
  end
  
  def format(name)
    str = nil
    str = @gb_data["head"][name] if @gb_data["head"]
    str ||= @@heads[name]
    return ("["+ str + "]" + "\n\n" + @gb_data[name]).toutf8
  end

  private :randomize_paragraph, :output, :format
end

m = MakeParagraphManager.new(ARGV)
m.exec

解説とかは、別の機会に。