newlabの使い道を頑張って考えてみる

こんにちは。
[去年](https://qiita.com/mjhd-devlion/items/375ea9894294919380fd)に引き続きHSP Advent Calendarに投稿してみますが、去年も申し上げました通り私は猫がこの上なく好きでして、wait命令を忘れてプログラムの動作が停止した時やHSPスクリプトエディタが落ちた時など、大学に住み着いている野良猫にご飯をあげることで心の平静を保っていたのですが(猫駆動開発といいます)、この野良猫たちがその後思いの外増えすぎまして、このままでいいのかと心配が募り心から猫を愛でることのできない日々を送っているmjhdです。
猫について書きたいことは山ほどあるのですがあまり技術的内容から離れすぎるとQiitaの運営に記事を削除されてしまうらしいのでここら辺で我慢します。

今日はHSPの知られざる機能**newlab**についてお話ししたいと思います。

# 謎の命令newlab
HSPにはひっそりとnewlabという命令が存在します。
この命令は、ラベル型変数を初期化し、パラメータに応じて**ラベルのない場所でもgoto, gosubで飛べるようにする**という命令です。
OHDLには以下のような説明がありますが、実際の使い方がピンとこない方もいらっしゃるのではないでしょうか？

```
ラベル型変数を初期化

newlab p1,p2

p1=変数    : 初期化する変数名
p2=参照元  : 参照されるラベルまたはオプション
解説

指定されたラベルが代入されたラベル型の変数を初期化します。
p1で、初期化される変数名を指定します。
p2で、変数に保存されるラベルの参照元を指定します。
p2にラベルを指定した場合は、ラベルが示す場所を参照元とします。
この場合は、「変数=*ラベル名」と記述した場合と変わらない動作となります。
p2に数値を指定した場合は、以下の動作を行ないます。

     値  : 参照されるラベル
 --------------------------------------------------
      0    次に実行されるプログラム位置を参照する
      1    次の1ステートをスキップした後に実行されるプログラム位置を参照する

(http://ohdl.hsproom.me/?q=newlab)
```

特に面白いのが、第二引数です。
p2に0を指定した場合、newlab命令の**次の位置**にラベルを設置し、p1に代入します。
p2に1を指定した場合、newlab命令の**次の次の位置**にラベルを設定し、p1に代入します。
面白いですね。

# なんのための命令なの？
特に第二引数に1を指定した場合は、いわゆる**[コルーチン](https://ja.wikipedia.org/wiki/%E3%82%B3%E3%83%AB%E3%83%BC%E3%83%81%E3%83%B3)**というものらしいです。

Python、Ruby、C#などの言語には**yield**という形で実装があったりして、イテレータを生成するために使用したりします。
これらの言語の場合、yieldを呼んだ時点でそのブロックにおける変数のスコープを抜け、次呼び出された時に変数の状態を保ったまま処理を続けることができるため、**一時的に処理を中断し、呼び出し元に戻る**ために使われています。

HSPは他の言語と比較して、変数にスコープがなかったり、yieldのように状態を保持する機能がなかったりなどの違いがあるため、今回はHSP向けのコルーチンの使い方をいくつか紹介したいと思います。

# 第二引数が0の場合
newlab命令の直後から処理を再開するパターンです。
ごめんなさい、実用的な例が思い浮かびませんでした。

# 第二引数が1の場合
newlab命令の次の次から処理を再開するパターンです。
newlab命令の直後にはreturn命令がきます。

## テキスト送り
以下の例は、村人のセリフを送る例です。コルーチンは、RPGなどのシナリオ処理に向いてるでしょう。

```c

message = "" // メッセージ

// レベル1のステージ読み込み処理を呼び出す
gosub *start

goto *main

*start
	message = "こんにちは。▼"
	
	newlab nextmessage, 1: return
	
	message = "こんにちは。\nここはハジメノ村さ。▼"
	
	newlab nextmessage, 1: return
	
	message = "僕は何の変哲もない村人。▼"
	
	newlab nextmessage, 1: return
	
	message = "君とは仲良くなれそうだ、うちに寄って行かない？▼"
	
	newlab nextmessage, 1: return
	
	message = "みたいな感じです。▼"
	
	newlab nextmessage, 1: return
stop

*main
	
	redraw 0
	
	// 背景塗りつぶし
	hsvcolor 100, 200, 50
	boxf
	
	// 文字表示
	hsvcolor 100, 200, 230
	pos 100, 100
	mes "村人"
	mes message
	
	// クリックされたら次のメッセージを読み込む
	stick key
	if (key & 256) {
		gosub nextmessage
	}
	
	redraw 1
	
	await 33
	
goto *main

```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/43622/d899a3d3-6e50-9c74-c36b-ec8482681bc1.png)

## 順番に状態を変化させる
以下の例は、ゲームにおいて現在ステージなどの、順番に推移する状態を変化されるためにコルーチンを用いたものです。
左クリックをすると、レベルが上がっていくのが分かると思います。

```c

caption = "" // ステージ名
bgcolor = 0  // 背景色

// レベル1のステージ読み込み処理を呼び出す
gosub *level1

goto *main

*level1	
	caption = "LEVEL1"
	bgcolor = 40
	// レベル1のステージ読み込み処理
	newlab nextlevel, 1: return
	
	caption = "LEVEL2"
	bgcolor = 80
	// レベル2のステージ読み込み処理
	newlab nextlevel, 1: return
	
	caption = "LEVEL3"
	bgcolor = 120
	// レベル3のステージ読み込み処理
	newlab nextlevel, 1: return
	
	caption = "LEVEL4"
	bgcolor = 160
	// レベル4のステージ読み込み処理
	newlab nextlevel, 1: return
	
	caption = "CLEAR"
	bgcolor = 191
stop

*main
	
	redraw 0
	
	// 背景塗りつぶし
	hsvcolor bgcolor, 200, 50
	boxf
	
	// 文字表示
	hsvcolor bgcolor, 200, 230
	pos 100, 100
	mes "現在のレベル:"+caption
	
	// クリックされたら次のステージへ
	stick key
	if (key & 256) {
		gosub nextlevel
	}
	
	redraw 1
	
	await 33
	
goto *main

```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/43622/e65aaddd-efc0-0e44-0133-daaa8457fa09.png)

この他にも、ゲームのフレームによって場面を切り替える際や、ノベルゲーム、RPGなどのラベルが増えやすい処理に使えると思います。
使いこなすには多少頭を使いますが、すっきりかけると気持ちいいです。
基本的には、何かの合図に合わせて順番通りに処理をする場合に使いやすいようです。

是非皆さんも、newlabを使った記述方法を色々と考えてみてください。