rogue: メッセージを3行表示

rogue のメッセージは単行で表示される。メッセージは何か行動を起こす度に出力されるのでスペースキーを押して、次のメッセージへと切り替えていく。このスペースキーでの切り替えは、ゆっくり読める所は良いのだけど、実際は頻繁に押さなくてはならなくてすごく煩わしい。

では、なぜ、頻繁に押さなければならないかというと、画面が1行と狭くてメッセージが収まりきらないからなので、広くすれば解決できると思うので、やってみる。

画面の構成と文字の出力

rogueは画面をcursesで描画していて、1つの全画面(window)にステータス、マップ、メッセージを描いている。対して、メッセージの改良は、複数行で表示したいし、いずれ過去ログを見れるようにもしたい。となると、独立したwindowにした方が制御しやすそうなのと、更に好みのバッファサイズにするとなると、curses の pad がぴったりくるので、メッセージをpadで描画するようにする。

  msg_win = newpad(MSG_PAD_NLINES, MSG_PAD_COLS);
  wmove(msg_win, MSG_PAD_BOT, 0);
  scrollok(msg_win, TRUE);
  idlok(msg_win, TRUE);

ちょこちょこ書いたけど、必須なのは newpad() のみ。

次に、padでのメッセージの出力。これは、vw_printw()など window* を指定できる関数で行い、画面に反映するために prefresh() を呼べば出力できる。

  vw_printw(msg_win, fmt, args);
  prefresh(msg_win, MSG_PAD_VIEW_TOP,0,VIEW_MSG_TOP,VIEW_MSG_LEFT,VIEW_MSG_BOT,VIEW_MSG_RIGHT);

このコードに切り替える事で、3行表示できるようになって改行毎に自動スクロールされるようになった。

画面更新されない

しかし、i (inventory)とか o (option) キーで別画面を表示した後、ゲーム画面に戻すとメッセージが消えてしまうという事が起こった。

色々いじってみた結果、windowであれば refresh() 時に再描画されるけど、padは再描画されない事で消えてしまっていたので、refresh() 後に prefresh() を追加することで表示できるようになった。

command.c:command(void)

    if (!((running || count) && jump))
+ {
      refresh(); /* Draw screen */
+     prefresh(msg_win, MSG_PAD_VIEW_TOP, 0, VIEW_MSG_TOP, VIEW_MSG_LEFT, VIEW_MSG_BOT, VIEW_MSG_RIGHT);
+ }

refresh -> prefresh の順でないと表示されなくて、これに気付くのに時間がかかった。

最後、スクショ。

Level: 1  Gold: 53     Hp: 10(12)  Str: 16(16)  Arm: 4















            ------
            |....+######
            |....|     #    ---+--------------------
            |....|     #    |............@.........|
            |....|     #####+.............B........|
            ------          |......................|
                            ------------------------
you found 53 gold pieces
You barely miss the bat
The bat has injured you