ユーザ用ツール

ハイハイスクールアドベンチャー技術資料

概要

この文書は、ハイハイスクールアドベンチャーの技術的な資料である。

データファイル構成

プログラム本体以外には以下のファイルを使用している。

  • シナリオデータファイル
  • 画面データファイル
    • 部屋データ
    • アイテムデータ
  • 語句辞書ファイル
  • 初期データ
  • 音データファイル
  • メッセージファイル1)
  • セーブデータファイル2)

ゲームエンジン仕様

ゲームエンジンは、汎用のアドベンチャーゲームエンジンを目指してデザインしたものの、入出力と強く結びついた動作をきれいに落とし込み切れていないので事実上のハイハイスクールアドベンチャー専用エンジンとなっている。

エンジンはフロントエンドからコマンド(動詞+目的語)を受け取るとシナリオデータの集合を走査し、条件を満たしているものを実行する。 エンジン単体でできることは、システム内のフラグや状態変数を書き換えることだけで、入出力を伴うものはターゲット環境に合わせて実装された入出力部が処理する。

シナリオデータは、32bitの条件ブロックに続いて、32bitを1単位とし、複数の語からなる動作節がある。 最大で256組の動作を定義でき、一つの動作は96byte (4byteの条件ブロック + 23シナリオ語)からなる。 条件ブロックは、場所、動詞、目的語の3byteをキーとし、1byteは未使用である。

場所、動詞、目的語にはワイルドカードがあり、例えば、どの部屋であっても、北へ移動しろというコマンドが来たら、アクションを起こすというような場合には、場所と目的語にはワイルドカードを渡しておく。

エンジンは条件ブロックを評価し、条件を満たしたら、動作節を実行する。 複数の条件ブロックを満たす場合には先にある条件ブロックが優先的にマッチし、以下のものは対象外となる

動作節は、複数の条件語からなる条件節に続いて、アクション節が続く構成となっている。 条件ブロックに続いて条件節が来るのは奇異に映るかもしれないが、最初の条件ブロックは、場所、動詞、目的語だけをキーとしたものであり、続く条件節は、さらに詳細な条件の確認のためのものである。

たとえば、任意の場所で、「拾う」「ネクタイ」というコマンドが来たとして、その場所に「ネクタイ」がある場合とない場合とで動作は変わってくる。 この「ネクタイ」の有無をチェックするのが条件節の働きになる。 。

フォーマット

76543210765432107654321076543210
actoppadtypidoffsetvalue
typid

actビットで、条件語(act=0)かアクション語(act=1)かを判別している。 opは条件やアクションを具体的に示す。 3bit 8種類を持つことができる。 padはパディングエリアで、この4bitは使用されていない。 typとidはペアで、typで示される種類のデータのid番目にアクセスする。 offsetは8ビットのデータとしても使われるし、typ(3)+id(5)としても使われる。 typ(id)同士を比較したり内容をコピーしたりするときには offsetをこのように分割する。 valueは8ビットのデータである。

条件語

条件語はactビットが0のものを指す。 条件語は以下のようなフォーマットを持つ。

76543210765432107654321076543210
0cond typ1id1typ2id2value

cond

condは以下の8種類がある。

意味
0NOP(常に成功)
1EQ
2NE
3GT
4GE
5LT
6LE
7未定義

オペランド

第一オペランドは typ1 + id1 であらわされ、第二オペランドは typ2 + id2または valueが使われる。 第二オペランドが typ2 + id2なのか valueなのかは、typ2によって決まる。

typは以下のものがある。

意味
0None (typ2ならvalueを使う)
1Fact
2Place
3System
4Vector

Factおよび Placeはフラグである。 ハイハイスクールアドベンチャーにおいては、Factは懐中電灯が使用中かとか、先生が出現しているかとかそいうい情報を、Placeはアイテムの場所を管理している。idは5ビットあるので32種類ずつ持つことができるが、そんなには使っていない。

Systemは文字通りシステム変数で、Vectorは部屋と部屋のリンク情報を管理している。

例えば、

76543210765432107654321076543210
0001----00100001000-----00001010

このビット列0x1021000A3)は、Fact(1) == 10 を意味している。

アクション語

アクション語は act == 1のものを指し、以下のようなフォーマットを持つ。

76543210765432107654321076543210
1action typ1id1typ2id2value

アクション

アクションは以下の8種類である。

意味
0Nop
1Move(部屋の移動)
2Asgn(値の代入)
3Mesg(メッセージの出力)
4Dlog(選択ダイアログの表示)
5Look(Look動作)
6Snd(音を鳴らす)
7Over(ゲームオーバー)
Move

指定された方向への移動を試みる。 方向は value で与えられる、0~7が有効で、以下のような意味を持っている。

01234567
西

現在いる場所から与えられた方向への移動を試みるが、移動先が0になっている場合はその方向へは移動できない。 それ以外の場合には与えられた数字で示される場所へ移動する。

Asgn

オペランド1 (typ1 + id1)で示されるフラグに、与えられた値をセットする。 値はオペランド2 (typ2 + id2)で与えられる。

typ2 == 0 なら value(即値)が値として採用される。 typは条件語で用いているものと同じである。

Mesg

valueで与えられた番号の文字列を出力する。

Dlog

選択ダイアログを表示する。 ダイアログ番号は value で与えられる。 つまり256種類持たせられるが、もちろんそんなに使ってはいない。 ダイアログの実装、および得られた値の処理は実装依存になっている。

Look

「見る」コマンドに特化した動作である。 特定の目的語を与えられた「見る」コマンドは一種の移動として処理されるが、移動ベクタには登録されていない。 valueで与えられた「部屋」へ移動したかのように処理される。 value==0が与えられると元の部屋に戻る。

これら特殊な「部屋」は、どの部屋ともつながっていない。 もしつながりのある部屋を与えれば、そこから移動することができるだろうが、想定外の動作である。

Snd

valueで与えられた番号の音を鳴らす。 0はハイハイスクールの校歌である。 全ての音が存在しているかどうかは保証されない。 M5版では音を鳴らす機能は実装されていない。

Over

valueで表されるゲームオーバーを迎える。

画面データ仕様

部屋データ

部屋データは、一部屋 0xa00バイト(2.5KB)からなり、各部屋は1KBの画像描画データおよび、0x500バイトの部屋固有のメッセージデータおよび、0x100バイトの特定のコマンドとメッセージとのリンク情報からなる。

部屋にしても、ものにしても、FM-7上で、BASICを使って描画することを前提につくられている。 線を引き、色を塗るという動作を座標と色データの形で格納している。

線の描画は繊細で、BASICで使っているであろうロジックに近しいもので描画しないと、色を塗るときの座標が想定している境界線とマッチしなくなる可能性がある。4)

このため、移植する際には、まずグラフィックスの描画ルーチンを実装し、データを実際に描かせてみてから先へ進めることにしている。 とはいえ、昨今は256×152のビットマップバッファを作って、それをターゲットの環境にレンダリングさせているだけなので、ほぼほぼ、問題なく動いている。

描画データ

先頭には、トーンパターンが何色分定義されているのかが1byteで表されている。 トーンパターンは、1-6の色を指定されたパターンで置き換えるためのもので、カラーパレット機能のようなものである。

その後ろに3バイト(BRG)のトーンデータが指定された数だけ続き、その後ろがようやく最初の描画データになる。

描画を始める前に、描画域全体を青(色コード1)で塗りつぶす。

(x,y)のポイントデータの羅列で、最初のペアから次のペアへの線を白(色コード7)で描いていく。 読みだした(x,y)がともに0xffなら線描パートは終わりである。 yだけが0xffの場合はそのペアを捨てて、次のペアを新しい始点として線を描いていく。

続いて、(x,y,c)のペアで色塗りデータが続く。(x,y)ともに0xffなら色塗りは終了である。

画像によっては、この後ろに、白線での輪郭描画、黒線での輪郭描画のデータが続き、最後にトーン処理を行う。

トーンデータは最初に書いたように、パターン数+パターン数*3バイトのパターンデータとして登録されている。 最大で6 (カラーコード1-6に対応)のパターンがあり、対応するカラーコードを置き換えていく。

PC-9801では、パターンデータのままに、縞模様を使った中間色画像として描画されたが、PalmOS以降ではそれらしい色に置き換えるように変更してある。

PalmOSとAndroidには縞模様の中間色画像を表示する機能も付けてあるが、あえて表示する価値はないと思われる。

トーンデータ 線画データ
nt1(B)t1(R)t1(G)x0y0x1y1xn0xffx0y0x1y10xff0xff
ペイントデータ 白線データ
xycxyc0xff0xffx0y0xn0xffx0y0x1y10xff0xff
黒線データ
x0y0x1y1xn0xffx0y0x1y10xff0xff
トーンデータ

トーンデータは、3バイトを一組として、BRGの横8dot分のトーンパターンを定義している。 カラーコード1-6で描かれている部分をこのトーンパターンで置き換え、中間色表現による色表現を行う。

要は、当該カラーコードのある点に対して、

((b >> (7 - (x % 8)) & 1) | (((r >> (7 - (x % 8)) & 1) << 1) | (((g >> (7 - (x % 8)) & 1) << 2)

の色を置いていく(8色モードの場合)ことになる。

なお、今どきの端末の多くはRGB565であったり ARGB8888であったりと、より多色を使った表示がかのうなので、あえて中間色を使う必要性は高くない。

中間色は、要は、交互に色を並べることで、その中間の色に見せる(白+赤ならピンクに見えるように)というものなので、パターンのうち1が立っているビットの密度を数えて、それをB/R/Gそれぞれの色の輝度(0~7)と見なし、疑似的にRGB333の色コードとして変換、これをさらにRGB565やARGB8888に変換して表示している。

メッセージデータ

メッセージデータは、部屋データのオフセット 0x500から0x500バイトの領域に配置される。 先頭からメッセージ番号0,1,2… と続く。

メッセージは最初の2バイトがメッセージ長で、その後ろに長さ分のメッセージデータが続き、その後ろに次のメッセージが配置される。 メッセージはUTF-8でエンコーディングされている。 オリジナルとなったPC-9801版はShift_JISだったが、時代にそぐわないので、PalmOS版を作るときにすべてUTF-8にした。 メッセージ長はbig endianであるので、処理には注意。 メッセージ長0の文字列が現れたらメッセージは終わりである。

メッセージ番号0のメッセージは、そのマップが表示されたときに表示されるメッセージで、それ以外のメッセージは、その場所で特定の動作をしたときに表示されるメッセージである。 その特定の動作は、コマンド・メッセージリンクで定義されている。

メッセージ長メッセージメッセージ長メッセージ 終端マーク
上位下位文字列0(UTF-8)…上位下位文字列1(UTF-8)…00

コマンド・メッセージリンク

描画データのあと、オフセット0x400から0x100バイトのデータが、コマンド・メッセージリンクになる。 3バイト一組のデータで、動詞+目的語+メッセージIDである。 後続のメッセージ部のメッセージ数分だけのエントリーが存在している。

その場所で、動詞+目的語の動作をしたときに、ID番目のメッセージを表示する。

ただ表示するだけなので、ゲーム中で何ら作用を持たないものであることは明らかであるが、 こういう仕掛けを一所懸命しこんだであろう原作者たちの思いを表すものである。

リンクデータ0 終端マーク
動詞目的語メッセージ番号0

ものデータ

ものデータは、落ちているものと○○先生の描画データである。 それぞれの描画データは0.5KBである。

ものデータは、そのものがある部屋に来たときに、部屋の画像の上に表示される。 このことにより、任意の部屋にものを置けば、そのものが即座に表示されるし、拾えば即座に消える。

なお、通常のアイテムの描画データの扱いは一意だが、先生だけは異なった処理を行う。

語句辞書仕様

辞書ファイルは、0x200バイトの動詞領域および0x200バイトの目的語領域からなる。 語句は逆HAL符号5)で符号化され、5bytesを一組としている。

01234
符号化語意味

符号化語部分は最大4byteだが 0が現れたらその後ろは無視される。 先頭が0か0x200バイトを超えたらそれ以上はワードは存在しない。

符号化されたワードをデコードすると英単語が得られる。 単語の意味するところは意味バイトが示している。 つまり、例えば look と see とに同じ意味バイトを割り当てることで、どちらの動詞が与えられても「見る」という意味として解釈できる仕組みになっている。

目的語に関しても同様の構造になっている。

初期データ

初期データは、以下の3つのパートからなる。

  • マップ間のリンク情報(8bytes x 87)
  • アイテムの所在情報(12bytes)
  • フラグ(15bytes)

スタート時に初期状態が読み込まれ、セーブ/ロード時にはこれらの情報が保存され読み込まれる。 つまり、ゲームのステータスのすべてはこの情報だけで完結している。

音データ

メッセージファイル

セーブデータファイル

ゲームデータがセーブされるとセーブデータファイルが作成される。 セーブデータファイルはシステム間で互換性が保たれている。 ファイル形式で保存され、ファイルシステム経由でアクセスできる、M5版、Raspberry Pi Pico版、Windows版、Qt版、SDL2版、AvaloniaUI版では相互にセーブデータをコピーすれば他のプラットフォームでもそのままロードしてプレイすることができる。

Web版は、ブラウザが管理するローカルDBに、Androidもシステムの提供するDB内に保存される関係で、他のプラットフォームとセーブデータをやり取りすることはできない。

セーブデータは、コアシステムのワークとユーザデータ6)からなり、バイトデータの集合となっている。 データがすべてバイト単位であるため、エンディアンに関わらず、データに互換性がある。

コアデータ

コアデータはシステムの状態を保持している8bytesのデータ列である。

オフセット内容
+0現在のマップID
+1ビューマップID
+2直近の動詞
+3直近の目的語
+4選択された選択肢
+5乱数値
+6選択状態
+7選択肢のメッセージID

多分、現在のマップIDとビューマップID以外は実は保存する意味はない。

ユーザデータ

ユーザデータは、マップ間のリンク、フラグ情報、アイテム情報の三つからなっている。

マップリンク

マップ間のリンクは、マップIDごとに、北、南、西、東、上、下、入、出の8bytesからなり、87部屋分のデータ配列となっている。 それぞれ、その部屋の各方向にはどの部屋がつながっているのかを部屋IDで表している。

リンクはユーザの行動によって変化するので、断面データを見ても、ゲーム全体の正解ルートはわからないし、この先にどう変化するのかもわからないので、ゲーム攻略には多分使えない。

フラグ

フラグは、特定の動作が行われたかどうかや、消耗するアイテムの場合消耗度合いなどが記録されたデータで15bytesのデータである。

アイテム

アイテムは12種類存在している。 それぞれのアイテムがどこにあるのかの所在が記されている。 消費されて消えてなくなったのかどうかもここに記されている。

データを改ざんして、すべてのアイテムを最初から所有している状態から始めることもできるだろうが、だとしても、それだけで攻略できるほどこのゲームはぬるくはない。

仮にすべてのアイテムを保有した状態から開始したとしても、そもそも学校に入れないし、コアデータを改ざんして、学校内のどこかの部屋にいることにしたとして、それでもほとんどの行動はできない。

アイテムを集めなくていいので行かなければならない場所は減るが、それでも必要なフラグを立てるための行動が求められるので、ほとんど難易度は下がらないだろう。

1)
QT版のみ
2)
保存された場合
3)
do not care '-' を '0'にした場合の値
4)
実際PalmOSで実装したときに、内蔵の線描をつかったらpaintしたときに想定外のところが塗られてしまった。
5)
命名はわたし。
6)
マップ間のリンクデータおよびアイテム情報、フラグ情報

This website uses cookies. By using the website, you agree with storing cookies on your computer. Also, you acknowledge that you have read and understand our Privacy Policy. If you do not agree, please leave the website.

More information