見出し画像

ゲームメイン(3)

シークレットドアの処理

再描画すべき座標にシークレットドアがある場合は、ドアを隠すための壁を描画するように処理します。

次のソースコードはシークレットドアがあるかどうかを判定している部分です。

関数showにより描画すべき文字を取得し、それがシークレットドアだった場合に関数secretdoorを呼び出しています。

--- misc.c ---
89: /*
90:  * Secret doors show as walls
91:  */
92: if ((ch = show(y, x)) == SECRETDOOR)
93: ch = secretdoor(y, x);

関数secretdoorでは、すべての部屋を走査し、描画すべき壁の文字を取得しています。部屋の上下には文字'-'を、部屋の左右には文字'|'を設定します。
処理すべき部屋がない場合は文字'p'を設定しています。

次のソースコードは関数secretdoorの実装部分です。

--- misc.c ---
165: /*
166:  * secret_door:
167:  *	Figure out what a secret door looks like.
168:  */
169: 
170: secretdoor(y, x)
171: register int y, x;
172: {
173:     register int i;
174:     register struct room *rp;
175:     register coord *cpp;
176:     static coord cp;
177: 
178:     cp.y = y;
179:     cp.x = x;
180:     cpp = &cp;
181:     for (rp = rooms, i = 0; i < MAXROOMS; rp++, i++)
182:         if (inroom(rp, cpp))
183:             if (y == rp->r_pos.y || y == rp->r_pos.y + rp->r_max.y - 1)
184:                 return('-');
185:             else
186:                 return('|');
187: 
188:     return('p');
189: }

未踏の部屋にある外壁を描かない処理

主人公が通路を歩いている際、まだ主人公が足を踏み入れたことがない部屋の外壁を描かないという処理をしています。

次の図は実際のゲーム画面のものです。ドア(文字'+')は描かれていますが、隣接する外壁(このケースでは文字'|')が描かれていないことがわかります。

通路にいるときは部屋の外壁は描かれない

この処理は主人公が盲目ではない場合に実行されます。主人公が盲目の場合、主人公の周辺を描く処理を一切実行しません。

次のソースコードは、主人公の周囲にある壁を描画しないようにするための実装部分です。

--- misc.c ---
94: /*
95:  * Don't show room walls if he is in a passage
96:  */
97: if (off(player, ISBLIND))
98: {
99: if (y == hero.y && x == hero.x
100:     || (inpass && (ch == '-' || ch == '|')))
101:     continue;
102: }
103: else if (y != hero.y || x != hero.x)
104:     continue;
105: wmove(cw, y, x);
106: waddch(cw, ch);

変数inpassは主人公が通路にいるかどうかを保持するためのフラグです。

--- misc.c ---
64: inpass = ((rp = roomin(&hero)) == NULL);

連続移動する場合の処理

通常、主人公が移動するのは1キャラクタ分ずつなのですが、連続的に移動させることができます。その際の処理をこのタイミングで実行しています。

連続移動する方法は3種類あります。

(1) 移動コマンドを英大文字で入力
(2) 数値+移動コマンド
(3) fコマンド+移動コマンド

1番目の方法は移動コマンドを英大文字で入力する方法です。移動した先にアイテムがあればアイテムを取ったところで止まります。ドアがあればドアの上で止まります。

2番目の方法は指定回数だけ連続移動する方法です。たとえばキーボードを1、0、lと入力すれば右方向へ10回移動します。これはリピート機能を使った連続移動なので、移動している際に、たとえばゴールドがあっても主人公の動きは止まりません。

3番目の方法は主人公の周辺に何かが見つかるまで移動する方法です。移動中にゴールドなどのアイテムがあれば主人公が移動を停止させます。

次の図は3種類の動きの違いを簡単にまとめたものです。

連続移動の動きの違い

そのうち、3番目の方法の場合に次の処理を実行させています。次のソースコードはfコマンドによる連続移動を判定させている部分です。

--- misc.c ---
107: if (door_stop && !firstmove && running)

変数door_stopはfコマンドを使って連続移動した場合にTRUEに設定されます。連続移動方法の判定はここで行っています。

変数firstmoveもfコマンドを使って連続移動した場合にTRUEに設定されますが、2歩目からFALSEに設定されるので、ここでの処理は2歩目から行われるということです。

変数runningは連続移動時に立つフラグです。

次のソースコードはfコマンドの処理を実装している部分です。

--- command.c ---
96: switch (ch)
97: {
98: when 'f':
99:     if (!on(player, ISBLIND))
100:     {
101:         door_stop = TRUE;
102:         firstmove = TRUE;
103:     }
104:     if (count && !newcount)
105:         ch = direction;
106:     else
107:         ch = readchar();
108:     switch (ch)
109:     {
110:         case 'h': case 'j': case 'k': case 'l':
111:         case 'y': case 'u': case 'b': case 'n':
112:             ch = toupper(ch);
113:     }
114:     direction = ch;
115: }

112行目で関数toupperを使っていますが、これは1番目の連続移動方法と動きを統合するためのトリックです。

次のソースコードは主人公の移動を処理している部分です。通常の移動では関数do_move、連続移動の場合は関数do_runを呼び出すようになっています。

ここに処理を統合させるために先のトリックを使っています。

--- command.c ---
125: when 'h' : do_move(0, -1);
126: when 'j' : do_move(1, 0);
127: when 'k' : do_move(-1, 0);
128: when 'l' : do_move(0, 1);
129: when 'y' : do_move(-1, -1);
130: when 'u' : do_move(-1, 1);
131: when 'b' : do_move(1, -1);
132: when 'n' : do_move(1, 1);
133: when 'H' : do_run('h');
134: when 'J' : do_run('j');
135: when 'K' : do_run('k');
136: when 'L' : do_run('l');
137: when 'Y' : do_run('y');
138: when 'U' : do_run('u');
139: when 'B' : do_run('b');
140: when 'N' : do_run('n');

次のソースコードは関数do_runの実装部分です。変数runningがTRUEに設定されていることがわかります。

--- move.c ---
30: /*
31:  * do_run:
32:  *    Start the hero running
33:  */
34: 
35: do_run(ch)
36: char ch;
37: {
38:     running = TRUE;
39:     after = FALSE;
40:     runch = ch;
41: }

変数runchは連続移動させるために必要な情報となる移動コマンド文字です。プログラム側で移動コマンドを叩き続けているということですね。

変数afterは後処理で呼び出すべきデーモン・フューズなどの処理を省略するためのフラグです。値がFALSEになっている場合、後処理でデーモン・フューズなどが実行されなくなります。

次のソースコードは後処理の部分です。再掲となります。

--- command.c ---
286: /*
287:  * Kick off the rest if the daemons and fuses
288:  */
289: if (after)
290: {
291:     look(FALSE);
292:     do_daemons(AFTER);
293:     do_fuses(AFTER);
294:     if (ISRING(LEFT, R_SEARCH))
295:         search();
296:     else if (ISRING(LEFT, R_TELEPORT) && rnd(100) < 2)
297:         teleport();
298:     if (ISRING(RIGHT, R_SEARCH))
299:         search();
300:     else if (ISRING(RIGHT, R_TELEPORT) && rnd(100) < 2)
301:         teleport();
302: }

後処理のではデーモン・フューズの処理については説明しましたが、リングの処理については説明していませんでした。次回はリングの処理についての説明をします。

この記事が気に入ったら、サポートをしてみませんか?気軽にクリエイターを支援できます。

記事を気に入っていただけて良かったです!
2
Cプログラミングの話題を中心としたマガジンを作成しています。完成したマガジンは電子書籍にまとめなおしてAmazonで販売しています。 Amazon著者ページ https://amzn.to/2IIHyv0

この記事が入っているマガジン

コードリーディング - rogue -

  • 39本
コードリーディング - rogue -
コメントを投稿するには、 ログイン または 会員登録 をする必要があります。