C#で伺か(SSP)にSSTP通信してエミリをつついてシルフィアをなでる

前回[C#で伺か(SSP)にSSTP通信する - Qiita](https://qiita.com/khsk/items/177741a6c573790a9379)でゴーストに好きな発話をさせることに成功しましたが、
私はスクリプターではなくユーザーなので操作するのはゴーストではなくユーザー操作側でありたいです。

ここでは
[イベント - 里々Wiki](http://soliton.sub.jp/satori/index.php?%A5%A4%A5%D9%A5%F3%A5%C8)
や
[UKADOC Project SHIORI Eventリスト](http://ssp.shillest.net/ukadoc/manual/list_shiori_event.html)
だけではなく、その転送？先のもっと直接的なイベントを呼び出します。
だって`OnMouseMove`イベントだけで「こする」「なでる」動作を呼びだしずらくないですか？


# エミリちゃんを紐解く

まずは一番簡単そうな`OnMouseDoubleClick`でエミリの口をつついてみようと思います。

[OnMouseDoubleClickのReferenceは規定さてれいますが](http://ssp.shillest.net/ukadoc/manual/list_shiori_event.html#OnMouseDoubleClick)
それでも実装はゴースト側しだいなので確認しておきましょう。
ゴーストのフォルダからそれらしいフォルダを探して…

`ssp\ghost\emily4\ghost\master\aya_mouse_core.dic`
を覗くと

```
//---- OnMouseDoubleClickイベント -------------------------------------------------------
OnMouseDoubleClick
{
	_prefix = EMGetPrefix
	_name = "%(_prefix)_DoubleClick%(reference[3])%(reference[4])"
	if mouse_double_click_name != _name {
		mouse_double_click_name = _name
		mouse_double_click_counter = 0
	}
	else {
		mouse_double_click_counter += 1
	}
	
	_script = EMMouseHitExec(_name)
	if STRLEN(_script) != 0 {
		_script
		return
	}
	
	EVAL("%(_prefix)_DoubleClick%(reference[3])")
}
```

と記述されていました。

EMGETPrefixも一応乗せておくと、

```
EMGetPrefix
{
	if sender == 'The Hand' {
		'MouseTH'
	}
	else {
		'Mouse'
	}
}
```
なので、`Mouse`と呼び変えてＯＫです。

このイベントの組み方では、referenceの3と4を指定することで特定の部位をダブルクリックしたものとみなせそうですね。

じゃあ実際にどんな反応を返してほしいかをさぐるために、呼び出されるイベントを調べて面白い反応を探して見ます。

次はコアではなく実際のトークが書かれていそうな
`ssp\ghost\emily4\ghost\master\aya_mouse.dic`
を見ます。

```
Mouse_DoubleClick0Mouth : nonoverlap
{
	"\u\s[10]\h\s[9]もがもがもがー！\w9\w9\uこの口はお芋さん専用です、と申しております。\w9\h\s[7]\n\n勝手に訳すなーっ！\e"

	"\t\1\s[10]\0\s[6]（ぱく）\w9\w9\1…\w5…\w5食べ物じゃないからね。\e"

	"\t\1\s[10]\0\s[1]…\w5…\w5え、\w5えっと…\w5…\w5\w9\w9\1キスされた訳じゃないんだから。\e"
}
```

ありました。
0は仕様からさくら側、というか本体側のことなので、このイベントはエミリの口をダブルクリックしたときのイベントです。
`OnMouseDoubleClick`とあわせて、

```
Reference3:0
Reference4:Mouth
```
とすればよさそうですね。

では前回書いたコードの`message`変数を書き換えて実行してみます

```cs
message = @"NOTIFY SSTP/1.5
Sender: リサ
Event: OnMouseDoubleClick
Reference3:0
Reference4:Mouth
Option: nodescript, notranslate
Charset: UTF-8

";
```

![emi.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62354/45d79538-f8d9-ca19-3a29-8b29b6f32a10.gif)

ランダムなトーク内容までは指定できませんが、口のダブルクリックをコマンド上で実現できました。

# なでる

なでる、というイベントは、マウスでおこなう場合は一定時間継続して同じ部位の上でマウスを動かすことで実現されていると思います。
エミリの場合、頭をなでるトーク名はこう


```
Mouse_Nade0Head : nonoverlap
{
	"\u\s[10]\h\s[4]むむぅ\w5…\w5…\w9\n\s[7]ボク、\w5もう子供じゃないんだからっ！\w9\w9\u\s[11]…\w5…\w5どこが？\w9\w9\h\s[4]\n\n%(charname(1))までいじめる\w5…\w5…\e"
	
	"\1\s[10]ヅラがずれるよ？\w9\nあんまりなでると。\w9\h\s[7]ヅラじゃない！\w9\w9\u\n\nじゃ、\w4付け耳？\w9\h\n\n付け耳でもない！\e"
	
	"\0\s[21]…\w4…\w4…\w4…\w4…\w9\w9\1\s[11]わざとらしく照れてもねぇ。\w9\w9\0\s[4]\n\nテディが冷たい。\e"
	
	"\u\s[10]\h\s[21]…\w5…\w5ごろごろ。\w9\w9\u\s[10]帰れ。\w9\w9\h\s[4]\n\nな、\w5なんでー。\e"
	
	"\u\s[10]\h\s[21]え、\w5えっ…\w5と\w5…\w5…。\w9\w9\nこういうのあんまり慣れてないから、\w5どう反応すればいいのか\w5…\w5…。\w9\w9\u\s[11]…\w5…。\e"
	
	"\u\s[10]\h\s[5]にゃ～。\w9\w9\u…\w5…\w5よかったね。\e"
	
	"\u\s[10]\h\s[6]…\w5…\w5にゃ。\w9\w9\uまぁ、\w5なでられるのはきもちいいよね。\w9\w9\h\s[5]\n\nにゃはは。"
}
```

では、`aya_mouse_core.dic`のほうで`Nade`に転送している部分はというと

```
//---- OnMouseMoveイベント --------------------------------------------------------------
OnMouseMove
{
	OnMouseMoveWheel(0,64)
}

OnMouseWheel
{
	OnMouseMoveWheel(1,4)
}

OnMouseMoveWheel
{
	_side = TOINT(reference[3])
	if _argv[0] {
		_side += 10
	}
	
	_specialsuffix = GetSpecialSuffix
	
	if STRLEN(reference[4]) != 0
	{
		if (GETTICKCOUNT - TOINT(stroketimer[_side])) > 1500 {
			stroke[_side] = 0
		}
		stroketimer[_side] = GETTICKCOUNT
		
		//----どこかが撫でられている
		_stroke = TOINT(stroke[_side])
		_stroke += 1
		stroke[_side] = _stroke
		
		//---- 撫でられた量が一定量に達したら「なでられている」と判断
		if _stroke >= _argv[1] {
			//talking判定は不評なので解除
			//if 'talking' !_in_ status {
			//---- 撫でられた。撫でられた部位を見てトークする
			_name = ""
			_prefix = EMGetPrefix
			if _argv[0] {
				_name = "%(_prefix)_Wheel%(reference[3])%(reference[4])"
			}
			else {
				_name = "%(_prefix)_Nade%(reference[3])%(reference[4])"
			}
			EMMouseHitExec(_name)
			stroke[_side] = 0
			//}
		}
	}
	else
	{
		// 定義された部位はどこも撫でられていない
		stroke[_side] = 0
	}
}
```


なんとなーく読んでなんとなーく面倒だという気がしますね？
内部で保持する「撫でられ量」を一定以上にする必要がありそうです。


こういった場合はもう面倒なので、ゴースト独自のイベントを直接呼んでしまうことにします。

すなわち、`Mouse_Nade0Head`を直接callするのです。
SHIORI Eventリストからは外れるので汎用性はなくなりますが、
組み方しだいでは好感度や前提条件などをもろもろ飛ばして実行できるやりかたですね。

```cs
message = @"NOTIFY SSTP/1.5
Sender: リサ
Event: Mouse_Nade0Head
Option: nodescript, notranslate
Charset: UTF-8

";
```

これで動く…はずなのですが、

問題発生。
**まったく動かない**
実はエミリで書くのは記事用で、実際にイベントを発行して遊んでいたゴーストは別の子だったんですよね。
その子は里々で書かれており、エミリは文(YAYA？)で書かれているのでイベントの受け取り方が違うのかしらん？

ということで、急遽里々製の別のゴーストを受信してみました。

[おすすめゴーストのトップにいた](https://twitter.com/i/moments/959669848270807041)

シルフィア氏です。

里々は非常に読みやすくて助かりますが、一応コードも見ておきますか


```dic_responce.txt
＊OnMouseMove
＞なでられ加算	（count、（Status）、talking）＞０｜｜（なでられ回数）＜（なでられ反応回数）
＄なでられ回数	
＞（R３）（R４）なでられ

＊なでられ加算
＄なでられ回数＝（なでられ回数）＋（whenlist,（R4）＝＝,Lip,４,Face,４,Shoulder,４,xxx,４,BSHair,３,SHair,３,Hand,３,Foot,３,Shoes,３,２）
＄なでられ初期化タイマ＝２

＊なでられ初期化
（set,なでられ回数,）
```

このようになでる処理は複雑なので、直接

```
＊０頭撫で
＞０頭撫で好感度中	（好感度）＞＝２００
：bebbmtbkobhrわわ。bebbmtbksbht……わたしって、そんなに幼く見えるでしょうか？
```

こちらを送信してみます

```cs
message = @"NOTIFY SSTP/1.5
Sender: リサ
Option: nodescript, notranslate
Charset: UTF-8
Event: ０頭撫で


";
```

![syl.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62354/32c46d94-6b51-a10d-c5a5-117cf4192b62.gif)


やはり里々では成功しました。
私は伺か全般に加え栞、SAORIにまったく詳しくないのでエミリでなぜ失敗しているのか教えてもらえると助かるのですが…。

里々に関してはコミュニケーション含め実行できることが確認できているので、一応全ゴースト対応というわけではないですが、CLIでゴーストの標準の反応を引き出す手法として勘弁してください。


# 余談

当初は汎用のイベント送信プログラムを組もうと思っていたのですが、ゴーストごとのイベントの記述に特徴がありそうで`OnMouseDoubleClick`など簡単なものを除けば辞書を見ながらゴーストごとに最適なイベント名とリファレンスを設定しないといけないのかもしれないなあと思いました。

私はあまり複数ゴーストを使わないので特化でいいのですが汎用的な操作コマンドが作られるといいですね(他人事

***

余談の余談ですが任意たんの辞書ってどうやって読むんでしょうかね…？
そもそもSSPで動くのかしら