川のぬし釣り3

 ~MOKUZI~

 BGMの不具合を修正
 BGMの不具合を修正2
 音のID一覧
 文字コードの一覧
 水上で立ったまま釣りを始める不具合を修正
 敵(動物)のID一覧
 魚のID一覧
 釣りメモのRAM配置
 びくのRAM配置
 動物とのエンカウント
 与ダメージ計算式
 被ダメージ計算式
 体力と経験値の最大値
 与ダメージがオーバーフローする不具合を修正
 BGMの不具合を修正3
 魚の体長算出ルーチン
 大物が釣れる確率
 現在フィールドに存在する魚データの初期化ルーチン
 画面範囲内に存在する魚配列の初期化ルーチン
 魚のアタリ判定ルーチン
 ファイト中の魚の挙動について
 フィールドにおける魚の生成時の座標範囲マップ



BGMの不具合を修正
釣り中はBGMが川のせせらぎに変わりますが、釣りをしているときに釣りメモを開いてから閉じると、フィールドのBGMに戻ります。
フィールドに戻ったときにも釣りは継続中ですから、本来はせせらぎ音が流れるハズでは・・・?
大した問題ではありませんが、なんとなくこれを修正してみました♪(*^_^*)


釣りメモを閉じたときに、釣り中であってもなくてもフィールドのBGMを鳴らすようになっているのが原因です。
したがって、釣りメモを閉じたあとで、釣り中かどうかの判定を行い、釣り中ならば川のせせらぎ音を鳴らすようにすればいいです。
バンク0の後半が空きエリアーとなっているようですので、ルーチンを0:3000H(バンク0Hのアドレス3000Hという意味。以下同様。)~に追加することにします。
そして、0:0FD5Hの「LD (0DD9EH),A」を「JP 3000H」に置き換えます。

3000H~には以下のルーチンを書き込みます。


   ; 追加ルーチン 0:3000H~
   LD (0DD9EH),A ; 元々の0:0FD5Hで行われていた処理
   LD A,(0FF90H) ; RAM領域FF90Hの値の...
   BIT 2,A       ; ビット2が1なら釣り中
   LD A,(0DD9EH) ; 元のルーチンへ戻る前にAレジスタの値をセット
   JP Z,0FD8H    ; 釣り中ではないときはここで元のルーチンへ戻る
   LD A,0FH      ; 釣り中ならばここへ来る
   LD (0DD9EH),A ; (DD9EH)へ音のID?(0FH=せせらぎ音)を格納
   JP 0FE8H      ; 元のルーチンへ戻る



これで、釣りメモを開いてからフィールドに戻ってきたときでも、釣り中ならちゃんとせせらぎ音が流れるようになります。
なお、釣り中かどうかの判定にFF90Hの値を用いましたが、(FF90H)のビット2は釣り糸を垂らすと1となり、いざ魚とのファイトが始まると0になります。

ただし、上記のルーチンを追加することによって、別の問題が発生します!

タライに乗って渓流から上流湖へ初めて移動した際に、タライが壊れるイベントが発生しますが、釣り糸を垂らした状態で上流湖へマップ遷移すると、タライが壊れたあとも川のせせらぎが流れたままとなってしまいます。
釣りメモを閉じたときとマップが切り替わったときとで同じルーチンを通っていることが原因です。
修正前は、マップ遷移時には釣りをしている状態でもマップに応じたBGMが流れるようになっていたため、上流湖へ移動した時点で、釣り糸を垂らしているかどうかに関わらず上流湖のBGMが流れるようになっていました。
ところが、修正後のルーチンではマップ遷移後に釣り糸を垂らしている状態だと川のせせらぎが流れるようになっています。
そして、タライが壊れるイベントの発生時に川のせせらぎ音が流れている状態は想定されていないため、タライが壊れて歩行状態となった(=(FF90H)のビット2がリセットされた)あとでも新たに上流湖のBGMが流れることはありません。

この問題を解決する方針としては、
 1.タライが壊れた時点で(釣り中フラグが解除されたタイミングで)上流湖のBGMを鳴らすようにする。
 2.マップ遷移時には釣り状態を解除する。
 3.上記の修正したルーチンを元に戻す。
などが考えられますが、3番目の方法だと、釣り状態のままマップ遷移をした際に、釣り状態であるにも関わらず川のせせらぎ流れないという、上記の釣りメモと同様の問題が発生してしまいます。
2番目の方法でもいいのですが、さすがにそこまで仕様を変えてしまうのはどうかという気がします。
1番目の方法だと「釣り中は川のせせらぎを鳴らす」かつ「釣り中ではないときはマップのBGMを鳴らす」という両条件をともに満たしていて、例外を設ける必要がないので今回は1番目の方法で修正することにしました。

タライが初めに岩にぶつかったときに釣り中フラグとタライフラグが解除される((FF90H)=0Hとなる)ため、BGM切り替えのタイミングとしては、タライが初めに岩にぶつかったときが条件面的にも演出面的にも好都合です。

7:6975H~の3バイトを「JP 3040H」に置き換えます。
そして、3040H~に以下のルーチンを書き込みます。(空き番地ならどこでも可。)


   ; 追加ルーチン 3040H~
   LD HL,0C9B6H      ; 元々の7:6975Hで行われていた処理
   INC (HL)          ; 7:6978Hで行われていた処理をここで実行
   LD A,(0FF00H+90H) ; 釣り中フラグを取得
   BIT 2,A           ; 釣り中かどうか?
   JP Z,6979H        ; 釣り中ではないならばBGMの変更は不要
   PUSH BC           ; サブルーチン呼び出しに備えてBCレジスタを退避
   PUSH DE           ; サブルーチン呼び出しに備えてDEレジスタを退避
   PUSH HL           ; サブルーチン呼び出しに備えてHLレジスタを退避
   RES 2,A           ; BGM再生ルーチンを呼び出す前に...
   LD (0FF00H+90H),A ; 釣り中フラグを解除しておく
   LD A,3FH          ; 上流湖のBGMのID=3FH
   CALL 0FC7H        ; BGM再生ルーチンをコール
   POP HL            ; HLレジスタを復帰
   POP DE            ; DEレジスタを復帰
   POP BC            ; BCレジスタを復帰
   JP 6979H          ; 元のルーチンへ戻る



これで、釣りをしているときにタライが岩にぶつかったタイミングで上流湖のBGMが流れるようになります。
釣り中フラグはこのルーチンのあとですぐに解除されるので((FF90H)=0Hとなる)、ルーチン内でフラグを解除したまま元には戻していません。
また、AFレジスタに関してはこのルーチンの後続処理にてどっちみち破壊されるため、退避は不要です。



BGMの不具合を修正2
セレクトボタンを押すとポーズをかけることができますが、ポーズ中にBGMの一部の音が鳴ったままになることがあります。
これも大した問題ではありませんが、せっかくなので修正してみました!(*^▽^*)☆


ポーズはフィールドの移動中や魚とのファイト中だけでなく、会話中やマップ切り替えの途中でもかけることができます。
ポーズ中かどうか?の判定にはC984Hの値を使用しています。
(C984H)が非ゼロのときはポーズ中で、ゼロならばポーズ中ではありません。
上の修正(釣りメモの件)と同様に、バンク0の後半の空き領域にプログラムを追加することにします。今回は3020Hに追加しました。
12DFH~の3バイトを「JP 3020H」に置き換えます。

3020H~には以下のルーチンを書き込みます。


   ; 追加ルーチン 3020H~
   OR A              ; (C984H)=0か?
   JP Z,12E2H        ; ポーズ中ではないので元のルーチンへ戻る
   LD A,80H          ; ポーズ中ならばここへ来る
   LD (0FF00H+26H),A ; 音を消すための手続き
   XOR A             ; 音を消すための手続き
   LD (0FF00H+25H),A ; 音を消すための手続き
   JP 12E6H          ; 元のルーチンへ戻る



これで、ポーズをかけると鳴ったままの音が消えるようになります。
音を消すためのルーチンは、釣りメモを開いたときに音を消すルーチンを参考にしました。
なお、ポーズ切り替えのタイミングで音を消すだけだとうまくいきません。
マップ切り替えのタイミングでは音のフェードアウト処理がポーズ切り替え処理の後で行われるため、音に関するメモリーが上書きされてしまうからです。
12DFHは定期的に通るので、やや強引な気もしますがここに挿入すると確実に音を消すことができます。



音のID一覧
音のIDをまとめてみました。
マップ切り替えなどのタイミングで(DD9EH)へ書き込むと好きな音を鳴らすことができます。
IDは16進数で表記しています。

音のID一覧



文字コードの一覧
文字コードをまとめてみました。
この文字の符号は何かな?と気になったときに参照してください。

文字コードの一覧



水上で立ったまま釣りを始める不具合を修正

タライを所持している状態で、Bボタンを押しながら水面に向かって十字キーを押すと、移動先の水面の上で釣りを始めるという現象を発見しました。
本来ならば水上ではタライが水に流されてゆきますが、この方法を利用すれば流されずに釣りをすることができます。
ゲームの進行には影響を与えませんが、いわゆる「バグ技」として悪用されうる不具合ですので修正してみました!(*^^*)☆


本来ならば移動中にマップチップの地形判定が行われ、進行先のマップチップの地形情報は(FFAFH)に格納されます。
移動先が水面のとき、(FFAFH)=0となり、タライを所持していればタライに乗ります。
タライに乗っているときは(FF90H)のビット3が1となり、乗っていないときは0となります。
なお、移動中には(FF90H)のビット0が1となり、移動中ではないときは0となります。
十字キーとBボタンが同時に押されると、釣りが始まるので(FF90H)のビット3が1となりますが、このとき、移動中であるにも関わらず進行先の地形判定が行われません。
釣りをやめて釣り中フラグが解除されると、やっと形判判定が行われて、それから(FFAFH)=0となり、タライに乗り始めます。
したがって、この不具合を解消するためには、「釣り中は地形判定を行わない」のではなく、「『釣り中』かつ『移動中ではない』ならば地形判定を行わない」ようにすれば十分です。
このあたりのルーチンはバンク1の中で行われていますが、1:7000H~が空き番地となっていますので、ここにプログラムを追加することにします。
そして、1:4AB3H~の3バイトを「JP NZ,7000H」に置き換えます。

1:7000H~には以下のルーチンを書き込みます。


   ; 追加ルーチン 1:7000H~
   BIT 0,A    ; 釣り中かつ移動中かどうか?
   JP Z,4CE6H ; 釣り中だが移動中ではないときは地形判定を行わない
   JP 4AB6H   ; 釣り中かつ移動中のとき、地形判定を行う



これで十字キーとBボタンを同時に押したときでも、進行先が水上ならばちゃんとタライに乗って釣りをするようになります。
元々、釣り中フラグと移動中フラグが同時に立つことはないという想定だったのでしょう。(;^^)



敵(動物)のID一覧
動物のIDとHP、倒したときにもらえる経験値をまとめてみました。
IDは16進数で表記しています。HPと経験値は10進数で表記しています。

動物のID一覧



魚のID一覧
魚のIDと体長(最小値、基準値、最大値、幻湖最大値)、安静/抵抗時間の最短秒数および最長秒数、釣れるエサ/ルアー/フライをまとめてみました。
IDは16進数で表記しています。

魚のID一覧



釣りメモのRAM配置
釣りメモには釣り上げた魚の情報が記録されています。
これは(CAA0H)~(CB3FH)に格納されています。
1種類の魚につき4バイトの情報で構成されていて、魚の種類は全40種類ですから、4バイト×40=160バイトの領域となっています。

以下の表のアドレスは、CAA0H+魚のID×4を基準とした相対アドレスです。

アドレス 内容
+0H フラグ情報
 FFHのとき:未釣り上げ
 非FFHのとき:[0]大物フラグ/[1]育成フラグ
+1H 体長の最高値
+2H 体長の平均値
+3H 釣り上げた総数




びくのRAM配置
びくの情報は(CA33H)~(CA52H)に格納されています。
1種類の魚につき2バイトの情報(順に、魚のIDと体長)で構成されていて、びくの最大容量は16ですから、2バイト×16=32バイトの領域となっています。
(CA32H)には、びくの最大容量値が格納されています。



動物とのエンカウント
エンカウント判定は、一歩進むごとに1:5F31H~のサブルーチンの中で行われています。
動物の出現する場所としない場所とがあり、出現する場所では毎歩1/64の確率でエンカウントします。



与ダメージ計算式
プレイヤーが攻撃した際の敵に与えるダメージ量の計算は、6:61A9H~のサブルーチンの中で行われています。
与ダメージ量は次の計算式によって求められます。

与ダメージ量=(((プレイヤーのHP/2+{0~3の乱数値})を256で割った余り)×{2:弱点に当たったとき/1:弱点以外に当たったとき})を256で割った余り

このように書くとわかりにくいのですが、簡単にいうと、与ダメージ量は大体プレイヤーのHPの半分くらい、ということです。
しかし、この計算値はプレイヤーのHP((C9CFH)と(C9D0H)にリトルエンディアンで格納されている)が249以下のときは整合性が取れているのですが、250以上になるとおかしなことになる場合があります。
たとえば、HPが250のときの与ダメージ量は通常125~128の範囲ですが、弱点に当たったときはこれが2倍となるので、250~256となります。
ところが256だとオーバーフローするため、与ダメージ量が0となってしまいます。



被ダメージ計算式
プレイヤーが攻撃を受けた際に敵から受けるダメージ量の計算は、6:6331H~のサブルーチンの中で行われています。
被ダメージ量は次の計算式によって求められます。

被ダメージ量=敵のHP/2+{0~3の乱数値}

敵のHPは1バイト値であるため、計算式もシンプルになっています。
与ダメージと違って、オーバーフローしておかしな値になったりすることもありません。



体力と経験値の最大値
たいりょくの最大値は経験値によって決まり、具体的には次の計算式(1D2DH~のサブルーチン)によって求められます。

たいりょくの最大値=(経験値/10)×3+30

ただし、この計算によって求めた値が999より大きいときは、上限値として999が設定されます。
よって、経験値が3230以上のとき、たいりょくの最大値は999となります。
経験値の加算処理は1D5DH~のサブルーチンで行われていて、上限値は9999となっています。
倒した動物の経験値をテーブルから取得し、その値を現在の経験値に累積してゆきます。
上限値(9999)のチェック以外に経験値の上昇を制限する処理はありませんので、根性があれば経験値を9999まで上げることは可能です。
したがって、たいりょくは250以上となり得るため、上記の与ダメージのオーバーフローの問題は実際に発生します。



与ダメージがオーバーフローする不具合を修正
この作品には攻撃力という独立したパラメーターはなく、体力が与ダメージを支配しています。
体力が増えるに連れて与ダメージは増えるのですが、体力の値によっては計算式中でオーバーフローが発生し、与ダメージが暴落する場合があります。
(詳しくは上記の「与ダメージ計算式」を参照してください。)
仮に、そういう仕様だといわれれば、そうなのかなあ・・・?という感じもしますが、「0ポイントのダメージをあたえた!!」と表示されるのは違和感があります。
しかも攻撃が弱点に当たったにも関わらず、かえって値がオーバーフローして与ダメージが暴落することもあり、これは不具合といわざるを得ないでしょう。

修正するにあたっては、「理論上255を上回る与ダメージ量となったときは、上限値として、与ダメージ量を255とする」という方針で対応することにしました。
たとえば、たいりょくが255で乱数値が1のときに攻撃が弱点に当たると、理論上の与ダメージ量は256となりますが、実際にはこれを256で割った余りの「0」が与ダメージとなります。
この場合には「255」が与ダメージとなるように修正します。
動物との戦闘などのルーチンはバンク6の中で行われており、6:7000H~が空き番地となっていますので、ここにルーチンを追加することにします。
そして、6:61B4H~の3バイトを「JP 7000H」に置き換えます。

6:7000H~には以下のルーチンを書き込みます。


   ; 追加ルーチン 6:7000H~
   CP 2H         ; 体力<512かどうか?
   JR C,7009H    ; 体力<512のとき、HLを半分にする処理へ
   LD HL,0FFH    ; 体力≧512のとき、割り算を行う代わりに...
   JR 700DH      ; HLを上限値の255に設定する
   SRL H         ; @7009H 体力<512ならば...
   RR L          ; HLを半分にする
   LD A,(0C96DH) ; @700DH 乱数値を取得して...
   AND 3H        ; 0~3の範囲に変換し...
   ADD A,L       ; HLに加算する
   LD L,A        ; (HLへの加算処理)
   LD A,0H       ; (HLへの加算処理)
   ADC A,H       ; (HLへの加算処理)
   LD H,A        ; (HLへの加算処理)
   OR A          ; HL<256かどうか?
   JR Z,701EH    ; HL<256のとき、値の加工は行わない
   LD HL,0FFH    ; HL≧256のとき、HLを上限値の255に設定する
   PUSH HL       ; @701EH ここで、HL<256となっている
   CALL 5FF8H    ; 弱点判定を行う
   CP 2H         ; 攻撃が弱点に当たったかどうか?
   POP HL        ; Lレジスタに与ダメージ量が格納されている
   LD A,L        ; A=弱点ではないときの与ダメージ量
   JP NZ,61CEH   ; 弱点に当たっていないとき、元のルーチンへ戻る
   ADD A,A       ; 弱点に当たったとき、与ダメージ量を2倍にする
   JP NC,61CEH   ; 2倍してもオーバーフローしなかったので、元のルーチンへ戻る
   LD A,0FFH     ; 2倍するとオーバーフローしたので、与ダメージ量を上限値の255に設定して...
   JP 61CEH      ; 元のルーチンへ戻る



これで与ダメージがオーバーフローして暴落することはなくなります!(*^^*)☆



BGMの不具合を修正3
通常、宿屋に泊まるとBGMが消えて、キー入力をするとプレイヤーが起床するとともに宿屋のBGMが流れ始めます。
一方で、動物に襲われて倒れたとき(たいりょくが0になったとき)は宿屋からのスタートとなりますが、このとき元から宿屋のBGMが流れていて、キー入力をするとプレイヤーが起床するとともに宿屋のBGMが再度初めから流れ始めます。
これは不具合か仕様か微妙なところですが、宿屋のBGMが止まって再度初めから同じBGMが流れるというのはおかしな感じがするため、意図した演出ではなく不具合であるという判断をしました。

修正の方法は色々思いつきますが、今回は簡単に「たいりょくが0の状態で宿屋へ入ったときはBGMを流さない」ように対応しました。
フィールド上でたいりょくが0となると自動的に宿屋へ送られるため、「たいりょくが0の状態で宿屋へ入る」という状況は、戦闘などで倒れた場合にしか起こり得ません。

宿屋へ入ったときの処理はバンク1の中で行われているので、1:7010H~にルーチンを追加することにします。
そして、1:4662H~の3バイトを「JP 7010H」に置き換えます。


   ; 追加ルーチン 1:7010H~
   PUSH HL       ; 体力=0かどうか?
   LD A,(0C9CFH) ; (体力のゼロ判定処理)
   LD L,A        ; (体力のゼロ判定処理)
   LD A,(0C9D0H) ; (体力のゼロ判定処理)
   OR L          ; (体力のゼロ判定処理)
   POP HL        ; (体力のゼロ判定処理)
   LD A,18H      ; 宿屋のBGMのID=18H
   CALL NZ,0FC7H ; 体力>0の場合に限り、宿屋のBGMを流す
   JP 4667H      ; 元のルーチンへ戻る



これで、たいりょくが0となって宿屋からの再スタートとなった場合はキー入力するまでは無音状態となります。



魚の体長算出ルーチン
魚の体長は、釣り上げたあとに6:42C7H~のルーチンの中で算出されます。
まず、2160H~のテーブルから魚の種類ごとに決まっている基準体長を取得します。
そしてこの基準体長に対してばらつきを加減して体長が決定されます。

ばらつき量は基準体長によって異なり、5段階にわかれています。
ばらつき量(の絶対値)の最大値を定義するテーブル(ここでは仮に「ばらつきテーブル」と呼びます)には次のように5バイトが並んでいます。
 DB 02H,04H,07H,0CH,12H
このテーブルにアクセスするためのインデックスは、次のように基準体長によって決まります。
(条件式が真のとき、インデックスは右の値となる。)

条件式 IDX
  0cm<基準体長<11cm?
 11cm≦基準体長<21cm?
 21cm≦基準体長<41cm?
 41cm≦基準体長<61cm?
 61cm≦基準体長?

たとえば、イトモロコちゃん(ID=16H)の場合、基準体長が6cmなのでインデックスは0となって、ばらつきテーブルの0番目の値「2H」が、ばらつき量(の絶対値)の最大値となります。

実際のばらつき量は「ーばらつき量(の絶対値)の最大値~+ばらつき量(の絶対値)の最大値」の範囲内の値となり、これは乱数によって選ばれます。
体長は基準体長にばらつき量を加味した値となります。
イトモロコちゃんの場合だとばらつきの範囲は±2cmとなり、体長は4~8cmとなります。

ところで、現在のフィールドが幻湖のとき((C9D7H)=4Hのとき)は、この体長にさらにボーナス値が加わることになります。
(わたしはこれを「幻湖ボーナス」と呼んでいます。(笑))
ボーナス値にもばらつきがあり、通常のばらつき量とは別にボーナス値のばらつき量(の最大値)を定義するテーブルが定義されていて、そこでは次のように5バイトが並んでいます。
 DB 02H,03H,05H,08H,0CH
アクセス用のインデックスは、通常のばらつき量を求めた際のインデックスと同じ値です。
なお、ボーナス値は体長に対して足されるのみで引かれることはありません。
よって、幻湖で釣れるイトモロコちゃんの体長は6~10cmとなります。

ちなみに、体長が(幻湖ではなく、通常の)最大値のとき、またそのときに限って大物と判定されます。
したがって、幻湖でのみ釣れるサイズの魚は、いくら大きくても大物とは判定されません。
それどころか、幻湖の大物サイズは魚の種類に関わらず67cmで固定となります。
これは不具合のようにも感じられますが、幻湖で釣った場合には大物判定を行わず、釣りメモの最大サイズだけが更新されるため表面上の問題は起こりません。



大物が釣れる確率
大物と判定される条件については上述した通りですが、ここでは大物が釣れる確率について述べます。
ただし、幻湖ボーナスについては考えないものとします。

ばらつきテーブルのアクセス用インデックスは基準体長によって一意に決まります。
そして、取得したばらつき量(の絶対値)の最大値から実際のばらつき量を求める際に乱数が用いられています。
以下の説明では、「ばらつき量の絶対値の最大値」のことを「絶対最大ばらつき量」と表記することにします。

絶対最大ばらつき量から実際のばらつき量を算出する手続きは次のようになっています。
  1. 絶対最大ばらつき量に1を足した値をBCレジスタへ格納する。
  2. 乱数値((C96DH))を取得してAレジスタへ格納する。
  3. HL = BC i = 0 7 2 i A i
    とする。ただし、Ai はAレジスタのビットiのことであり、0または1のいずれかの値を取る。
  4. 乱数値((C96DH))を更新する。
  5. (C96DH)のビット4が0のときはHレジスタの符号を反転する。
    1のときは反転しない。
  6. ここで、Hレジスタの中身が実際の(符号つきの)ばらつき量となっている。
この手続きのあとで基準体長にHレジスタの値を足せば釣り上げた魚の体長となります。

たとえばイトモロコちゃんの場合だと、絶対最大ばらつき量は2なので、「大物が釣れる」ことは「実際のばらつき量が+2となる」ことと同値です。
そして「実際のばらつき量が+2となる」ことは「乱数値が171以上であり、かつ符号反転が行われない」ことと同値です。
よって、イトモロコちゃんの大物が釣れる確率は85/512≈16.6%となります。
ただしこの確率は、乱数値のどの値が選ばれる確率も等しいということが前提となっていることに注意してください。

ちなみに、かわのぬしの大物が釣れる確率は、13/512≈2.5%で、イトモロコちゃんよりも大物が釣れにくくなっています。
これはぬしだから特別に大物が釣りにくいというわけではなく、基準体長が61cmより大きい魚はすべて大物が釣れる確率は2.5%となります。



現在フィールドに存在する魚データの初期化ルーチン
現在のフィールド内に存在する魚の情報(魚の種類(ID)、初期座標、個体数)は宿屋に泊まると初期化(生成)されます。
この初期化は3:490CH~のルーチンで行われます。

生息する魚の種類(ID)と初期座標の基準値、初期座標のばらつき量は3:508BH~のROMエリヤーにフィールド別のテーブル形式で定義されています。
(以降、このテーブルデータを「魚テーブル」と呼びます。)
魚テーブルの先頭アドレスは、フィールド別にそれぞれ次のようになっています。
 渓流    3:508BH~
 上流湖   3:50CCH~
 清流    3:5155H~
 下流湖   3:51E6H~
 幻湖    3:52BFH~
魚テーブル内の情報1件あたりの構成は次のようになっています。

以下の表のアドレスは、魚テーブルの各データの先頭アドレスを基準とした相対アドレスです。

アドレス 内容
+0H 初期X座標の基準値を16で割った値
+1H 初期Y座標の基準値を16で割った値
+2H 初期X座標のばらつき量の標準値
+3H 初期Y座標のばらつき量の標準値
+4H 魚のID
+5H 0H
+6H 0H
+7H 0H

この魚テーブルを元にフィールド内に存在する実際の魚の情報が生成されて、CC0BH~のRAMエリヤーに格納されます。
(以降、このRAM領域内に格納されているデータを「魚データ」と呼びます。)
魚1匹あたり8バイトで構成されていて、詳しい構成は次のようになっています。

以下の表のアドレスは、各魚データ(1匹分)の先頭アドレスを基準とした相対アドレスです。

アドレス 内容
+0H 魚の移動情報(LOW)
+1H 魚の移動情報(HIGH)
+2H X座標(LOW)
+3H X座標(HIGH)
+4H Y座標(LOW)
+5H Y座標(HIGH)
+6H FFH
+7H FFH

魚テーブル内の情報1件に対して、6件の魚データが生成されます。
初期生成ルーチンの本質的な流れは次のようになっています。

   

上図のうち、「(HL)~(HL+3H)の値を元に魚の初期座標および移動情報を算出~」の詳細は少々ややこしいのですが、結論だけを書くと次のようになっています。
ただし、mod はモジュロ演算を表しています。
なお、魚データ(RAMエリヤー)側では個別の魚データがIDを保持していませんが、魚テーブル内の1件の情報につき6匹分の魚データが生成され、またその生成方式は上図の通りですから、
各「魚データ」の先頭アドレスからCC0BHを引いて8で割ったときの商・・・これを「魚番号」と呼ぶことにする――を6で割ったときの商をインデックスとして、魚テーブルからIDを参照することができます。
実際に魚のアタリ判定では各魚データからそのようにして魚のIDを取得しています。



画面範囲内に存在する魚配列の初期化ルーチン
(CBEDH)~の12バイトには画面範囲内に存在する魚の魚番号が格納されています。(以降、この領域のことを「魚番号配列」と呼びます。)
ただし、「魚番号」は「魚のID」とは異なる概念です。
この魚番号配列に含まれている魚番号に対応する魚のみが移動制御の対象となって画面内を遊泳し、釣り上げることができます。
魚番号配列の初期化は、仕掛けのキャスト直後に3:47E7H~のサブルーチンの中で行われます。

RAMエリヤー内の各「魚データ」に対応する魚番号は、アドレスの小さい方から順に0、1、2、・・・、となりますが、これを一般にiと表し、魚番号iの魚のX座標をXi、Y座標をYiとそれぞれ表すことにすると、
の両条件を同時に満たすことが、魚番号iの魚が画面内に存在するための必要条件となります。
描画画面範囲の左端のX座標は(FFB5H)に、上端のY座標は(FFB9H)に格納されています。
また、ゲームボーイの画面の大きさは横160×縦144ドットになっていますので、上記の条件は次の表現と同値です。
ただし、これは画面内に存在するための必要条件ですが、十分条件ではありません。
十分性を主張するためには次の2つの条件をも同時に満たさなければなりません。
魚番号iの魚の魚データの先頭アドレスの中身の値がFFHのとき、その魚は既に釣り上げられたor逃げられたあとであり、フィールド内には存在しません。
FFHではないときはフィールド内に存在します。

魚番号配列には最大で12件の魚番号が格納されます。
そのため、たとえ座標に関する条件を満たしていても魚番号配列に魚番号が12件追加されていると、それ以上追加されることはありません。
つまり画面範囲内に同時に出現する魚は最大で12尾となります。
以上の4条件を同時に満たすことが、魚番号iの魚が画面内に存在するための必要十分条件となります。

魚番号iの魚が画面内に存在するとき、魚番号配列にiを格納します。
この手続きをすべてのiに対して行って画面内に存在する魚の一覧を決定します。



魚のアタリ判定ルーチン
3:4CAFH~のルーチンにおいて、魚番号配列内に格納されている魚番号に対応する各魚に対して、8フレーム(約133ms)に1度の定間隔で仕掛けとの"アタリ判定"を取っています。
細かい部分については省略しますが、本質的には次の4つの条件をすべて満たすとき、またそのときに限って対象の魚がかかります(ピピ音が鳴って魚が仕掛けのまわりを回って仕掛けが沈む)。
上3つの条件は満たしているものの4つ目の仕掛けに関する条件を満たしていないときは、ピピ音は鳴りますが魚はかかりません。
仕掛けの条件が合致していて、かつ魚が仕掛けの近くにいるにも関わらず魚がかからない場合は、確率による条件で弾かれています。
仕掛けの条件では、合致するか合致しないかのどちらかであり、「食いつきやすさ」のような違いはありません。

仕掛けの合致条件は魚のIDごとに決まっていて、215DH~のテーブルに定義されています。
このテーブルデータでは魚1種類あたりの情報が4バイトで次のように構成されています。

以下の表のアドレスは、各魚の情報の先頭アドレスを基準とした相対アドレスです。

アドレス 内容
+0H 釣れるエサ
+1H 釣れるルアー
+2H 釣れるフライ
+3H 基準体長

現在の釣りの種類がウキ釣りor投げ釣りならばエサの条件が、ルアー釣りならばルアーの条件が、フライ釣りならばフライの条件がそれぞれ適用されます。
現在の釣りの種類は(C9EAH)に格納されていて、0:ウキ釣り/1:投げ釣り/2:ルアー釣り/3:フライ釣り となっています。
各条件は1バイトで、それぞれのビットが食いつく/食いつかないの判定条件を正論理で表しています。
エサの判定条件におけるビット割り当ては次のようになっています。

[7] [6] [5] [4] [3] [2] [1] [0]
ミミズ サシ こんちゅう アカムシ カワムシ ネリエ こざかな カエル

ルアーの判定条件におけるビット割り当ては次のようになっています。

[7] [6] [5] [4] [3] [2] [1] [0]
スピナー スピナーベイト ミノー クランクベイト ホッパー スプーン ワーム カエル

フライの判定条件におけるビット割り当ては次のようになっています。

[7] [6] [5] [4] [3] [2] [1] [0]
メイフライW メイフライD カディスW カディスD ディプテラW ディプテラD ストーンフライW ストーンフライD

たとえばイトモロコちゃん(ID=16H)の場合、テーブルデータには9CH、82H、AAH、06Hの4バイトが定義されていて、左から順にエサの判定条件、ルアーの判定条件、フライの判定条件、基準体長となっています。
仮にエサの判定条件について見てみると、9CH=10011100Bとなっていて、正論理ですから、「ミミズ」「アカムシ」「カワムシ」「ネリエ」には食いつき、これら以外のエサには食いつかない、という具合になっています。



ファイト中の魚の挙動について
ファイト中の魚の挙動に関するルーチンはバンク6の中にまとめられています。
ファイト中に魚が仕掛けに食いついているとき、魚が仕掛けを左に引っ張って逃げようとする「抵抗状態」と、魚が静止していてプレイヤーが右に引っ張ることのできる「安静状態」とを交互に繰り返します。
これらの状態間の遷移は、(CBBBH)~(CBBCH)に格納されている値(以降、これを「タイマー値」と呼びます。)がゼロになったときか、プレイヤーがBボタン(Aボタンでも同様)から指を離したときに行われます。

Bボタンの押下状況による状態遷移は6:4B44H~のサブルーチンの中で行われ、次のような流れになっています。

   

上図ではタイマー値の値が変化する箇所を赤背景にしています。
このサブルーチンは毎フレーム呼び出されます。
前フレームでプレイヤーが引っ張っていたかどうか(プレイヤーの引っ張りフラグ)と現フレーム時におけるBボタンの押下状況によって、大別して4つのルートに分岐します。
なお、ファイト中の状態に関する各種のフラグは(CBB9H)に格納されています。
(CB99H)のビット割り当ては次のようになっています。

ビット 内容
[0] 魚の状態(1:安静状態/0:抵抗中)
[6] 魚に逃げられたあとかどうか
(1:逃げられた/0:逃げられていない)
[7] プレイヤーの引っ張りフラグ
(1:引っ張っている/0:引っ張っていない)


前フレームでプレイヤーが引っ張っておらず、現フレームでも引っ張っていない(Bボタンが押されていない)ときは、このルーチン内では何も処理されません。
前フレームでプレイヤーが引っ張っていなかったものの、現フレームでBボタンが押されたとき、プレイヤーの引っ張りフラグを立てて((CBB9H)[7]←1)、「ピピ限界時間>0?」の判定を行います。
「ピピ限界時間」はわたしの造語ですが、これは「魚の抵抗中にBボタンを押し続けていられる時間(カウンター)」のことを指しています。
この値は(CBCDH)に格納されています。
魚の抵抗中にBボタンを押して魚を引っ張ると「ピピーーー!!」という音が鳴りますが、このままピピ限界時間までずっとBボタンを離さずに押し続けていると魚に逃げられてしまいます。
ピピ音が鳴っているときにBボタンを押し続けている時間(カウンター)は(CBCBH)に格納されています。
(CBCDH)≧0ですが、ハリの大きさが魚に合っていないとき、またそのときに限り、(CBCDH)=ピピ限界時間=0となります。
ハリの大きさが合っていないときに引っ張ると、魚は問答無用で即逃げます。
一般にピピ限界時間は魚の大きさ(対象の魚の基準体長を10で割ったときの商)とハリの大きさによってのみ一意に決まり、次の表中の値となります。(6:45A2H~)
ただし、この表における値はすべて10進数で表記しています。

/10商  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
ハリ小 40 50 60 40  0  0  0  0  0  0  0  0  0  0  0  0
ハリ中  0  0  0 40 50 60 50 40  0  0  0  0  0  0  0  0
ハリ大  0  0  0  0  0  0 40 50 60 50 40 40 40 40 40 40


たとえば、イトモロコちゃんの場合だと基準体長が6(cm)なので、10で割ったときの商は0ですから、ハリ小のときのピピ限界時間は40となります。
ハリ中やハリ大のときは0となり、ハリが合わないということになりますので、イトモロコちゃんは即逃げてしまいます。
(そういうところも可愛いです。(*´ω`*))
ちなみに、ルアー釣りorケバリ釣りのときのピピ限界時間は一律で60となります。

ルート分岐の話に戻ります。
ピピ限界時間>0のとき、ピピ音が鳴っているときにBボタンを押し続けている時間((CBCBH))を0に初期化し、魚が抵抗中ならばピピ音が鳴った回数((CBE0H))をインクリメントします。
魚が安静状態ならばピピ音は鳴らず、プレイヤーが魚を右に引っ張ることになります。
なお、ピピ音が鳴った回数が8以上になると魚とのファイトが終わったときにハリやルアーが壊れてしまいます。

前フレームでプレイヤーが引っ張っていて、現フレームでも引っ張っている(Bボタンが押されている)とき、魚が安静状態ならばこのルーチン内では何も処理をせずに魚を引っ張り続けます。
魚が抵抗中のとき、(CBCBH)<ピピ限界時間ならば(CBCBH)をインクリメントします。
(CBCB0H)≧ピピ限界時間ならば魚は仕掛けから離れて逃げてしまいます。

前フレームでプレイヤーが引っ張っていたものの、現フレームでは引っ張っていない(Bボタンが離された)とき、プレイヤーの引っ張りフラグを解除し、魚は抵抗状態へ遷移します。
続いてタイマー値の設定が行われます。
設定されるタイマー値は正の値で、単調減少しますので、設定される値が小さければ抵抗時間は短くなってあまり遠くまで仕掛けを持って行かれずに済みます。
設定される値が大きくなるほど抵抗時間は長くなり、遠くまで仕掛けを持って行かれて逃げられる可能性が高くなります。
この設定される値・・・「抵抗時間」の基準値は6:45D2H~のテーブルに魚のIDごとに定義されています。
抵抗時間には学習値が存在し、(CBC5H)~(CBC6H)にリトルエンディアンで格納されています。
抵抗時間の学習値は「基準値」で初期化されていて次第に減少してゆきます。(詳しくは後述します。)
学習値の最小値は「基準値/2」であり、抵抗時間の決定アルゴリズムは上のフローチャートに示した通りです。
抵抗時間(と後述する安静時間)については 魚のID一覧 にまとめました。

タイマー値が設定されたあとで魚の移動速度を決定していますが、この値は符号つきであるため、FD00HとFE00Hとでは、FD00Hの方が絶対値が大きくなります。
移動速度(の絶対値)は小さいほどあまり遠くまで仕掛けを持って行かれずに済みますので、ピピ音が鳴ったら早めにBボタンを離すことが重要です。


次に、タイマー値がゼロになったときの状態遷移について説明します。
タイマー値のデクリメントおよびゼロになったときの状態遷移は6:527BH~のサブルーチンの中で行われ、次のような流れになっています。

   

タイマー値がゼロになったとき、安静状態ならば――つまり安静状態が終わった直後ならば、抵抗時間と移動速度を決定し、抵抗状態へ遷移します。
プレイヤーが引っ張っているときに抵抗状態に遷移した場合はピピ音が鳴ります。
抵抗時間(タイマー値)は1/4の確率で基準値×2となり(最長抵抗時間)、これまた1/4の確率で基準値/2となります(最短抵抗時間)。
残りの1/2の確率でタイマー値=学習値となります。
学習値が基準値/2より大きければ学習値から16を引いた値を新たな学習値として更新し、タイマー値=更新後の学習値となります。
学習値が基準値/2以下のときは学習値の更新は行われません。
つまり、抵抗時間の学習値は次第に減少してゆき、下限は「抵抗時間の基準値/2」です(厳密には「抵抗時間の基準値/2ー15」)。

タイマー値がゼロになったときに抵抗状態ならば――つまり抵抗状態が終わった直後ならば、安静時間を決定し(移動速度は0)、安静状態へ遷移します。
安静時間(タイマー値)は1/4の確率で基準値/2となります(最短安静時間)。
安静時間の基準値は6:45D2H~のテーブルに魚のIDごとに定義されていて、抵抗時間の基準値とは別の値です(たまたま同じ値となる場合はある)。
残りの3/4の確率でタイマー値=学習値となります。
抵抗時間と同様に安静時間にも学習値が存在し、(CBC3H)~(CBC4H)にリトルエンディアンで格納されています。
(安静時間の)学習値が基準値×2未満のときは学習値に16を足した値を新たな学習値として更新し、タイマー値=更新後の学習値となります。
学習値が基準値×2より大きいときは学習値の更新は行われません。
つまり、安静時間の学習値は次第に増加してゆき、上限(最長安静時間)は「安静時間の基準値×2」です(厳密には「安静時間の基準値×2+16」)。

なお、このサブルーチン(6:527BH~)は魚の静止中(安静状態かつプレイヤーが引っ張っていないとき)は毎フレーム呼び出されますが、プレイヤーが引っ張っているときは4フレームに1度しか呼び出されません。
したがって、たとえば安静時間が0.75秒だとしても、魚が静止してからすぐにBボタンを押せば3秒間引っ張ることができます。



フィールドにおける魚の生成時の座標範囲マップ
魚の生成時の座標範囲をマップ上にまとめてみました。

魚の分布マップ