してログ

タイヤ交換ついてでに、金額合わせに買っておいたタイヤマーカーペンを使って、ホワイトレターを入れてみました。ペン先が結構太いので慣れるまでが大変でしたが、文字のエッジ部分は書くというよりは撫でるように色を乗せていく感じにしたら、それほど難しくはありませんでした。書く前に十分ペン先を突いて染み出させて、試し書きして量を調節しておくのがポイントかなと思います。

今回、初めて入れてみましたが、見た目の仕上がりもなかなか良いし、運転するのが楽しくなるドレスアップだと思いました。履いているタイヤだと書くには難しいので、タイヤ交換のこの機会に入れてみてはいかがでしょうか。

表ゼビウス

アーケード版ゼビウスでは自動難易度設定が導入され、プレイする度に内容が異なるようになっています。これが効果的に機能しているかは微妙ですが、「完全にパターン化できないがある程度調整可能」といった独特のプレイ感覚になっています。詳しい説明は他所に譲るとして、これには 0~127 で定義される攻撃パターンテーブルを指すポインタを増減させることによって実現しています。

先日気づいたことに「偶数番の攻撃コードだけが使用されているのでは無いか」ということがあります。理由は単純で、この増減の幅がひとつを除いて偶数になるためです。奇数になるのは、マップ上の決められたチェックポイント通過時に「出撃1機あたりのスコアが1万点以下(厳密には少し違う)でスコアの千の位が奇数のとき」だけになります。普通にプレイしている限り、この条件に該当するのは無いに等しく、結果として偶数攻撃コードの「表ゼビウス」が進行して行きます。

ポインタの増減

マップの進行による増加と、プレイヤーの行動による減少によってゲームが進行していきます(表は難易度がノーマル設定の場合)。

イベント増減値
空中物出現時(固定以外)2増加
決められたチェックポイントを通過するプレイ内容によって最大16増加
ゾルバクを破壊する2減少
残機を失う16減少

だいたいエリア2序盤付近で2万点を超え、ある程度上達するとミスもしなくなるため、以降のチェックポイントでは毎回最大値(16)増加することになります。また、チェックポイントはエリア1で4箇所、エリア2で0箇所設定されていますが、エリア1の設定箇所は2個ずつ連続していて「奇数判定されても×2で偶数」になってしまいます。ただし、ごく稀に連続したチェックポイントの間でスコアが変動した場合のみ奇数になることはあり得ます。

裏ゼビウスへの入り方

意図的にプレイしてチェックポイントで奇数判定させれば、奇数番の攻撃コードで進行する「裏ゼビウス」に移行できます。エリア1は意図的にこれを行うのは難しく、エリア2は設定されていないため、次に設定されているエリア3のスペシャルフラッグゾーン付近で調整します。

  • エリア3のスペシャルフラッグゾーン時点でのスコアを9、7、5、3、1千点代の何れかに調整する
  • 例えば空中物ノーショットで、エリア1のスペシャルフラッグ、エリア2までのソル全出現で9千点
  • スペシャルフラッグゾーン直後の空中物が出現すれば、奇数判定済みなのでスぺフラも空中物も普通に撃って大丈夫
  • これ以降普通にプレイすれば「裏ゼビウス」のまま維持される

表と裏の違い

ほとんど違いはありませんが、いくつかの攻撃テーブルで敵機の数が1つ多く設定されています。良く分かるのがブラグザカートで、表ゼビウスでは1個、裏ゼビウスでは2個ずつ出現します。また、テラジの最大機数が1機多い5機となっており、総じて裏ゼビウスのほうが難易度が高くなります。詳しくは、以下の攻撃テーブル表で確認してください。

総攻撃 A3 も

奇数コードなので A3 は当初出せないと思いましたが、裏ゼビウスに移行してからエリア12まで行くことで出すことに成功しました。研究され尽くされていると思っていましたが、まだまだ色々とありますね。

追記:ファミコン版でも確認

ファミコン版ゼビウスでも同様のプレイをすることで、出現テーブル上のブラグザカートが2個ずつ出現するのを確認できました。この部分はアーケードの出現テーブルとロジックを忠実に移植しているようです。

またファミコン版はスコアに1000万の桁が存在しますが、チェックポイントでは100万以下の桁を計算に使用しているようです。このため、1000万点の超えた直後のチェックポイントで偶数奇数のレーンチェンジが起こる可能性があり、これが1000万点ごとに繰り返されると思われます。

追記:通常プレイで奇数パターンを確認

偶然に奇数パターンになったプレイの動画が撮れたので YouTube にアップロードしました。条件は推測していたので検証してみたところ、推測どおりの内容を確認できて確信できました。

奇数パターンに移行する可能性があるのはエリア1の2箇所で、ここはチェックポイントが間髪入れずに2個連続しています。スクロール位置がぴったり図の画面になった瞬間に、千の位の偶奇が変化すると奇数パターンに移行します。他の箇所では点数が高くなりすぎて、常に+16(最大値)と判定されるため、意識してプレイしない限り奇数パターンには移行できないと思われます。

動画で移行したと思われるポイント
動画で移行したと思われるポイント
別のチェックポイント連続箇所
別のチェックポイント連続箇所
左と右のスコアでそれぞれチェックポイントの判定が行われたと推定
左と右のスコアでそれぞれチェックポイントの判定が行われたと推定

[ゼビウス] AC版ゼビウスに総攻撃がプログラムされていた (6) からのつづき)

調整方法の早見表

エリア11の攻撃パターンの組み合わせから、エリア12で必要なテーブル調整方法をまとめてみました。なお、通常プレイでは偶数コード進行になるため奇数コードは除いています(奇数コードは参考資料を参照)。

追記:2パターンある場合があったので表を修正しました。

他のエリアでも再現可能

難易度設定によって、ミス時の減少値、空中物出現時の増加値が変わってくるため、HARD や HARDEST では他にも可能なエリアがありました。ただし、エリア3は HARDEST+5機設定でないと難しい(チェックポイント通過時の判定を最大にするために点稼ぎも必要)です。

難易度設定空中物出現時
(固定を除く)
チェックポイント
通過時(最大)
ゾルバク破壊再スタート時
(ミス)
EASY0+16-2-24
NORMAL+2+16-2-16
HARD+6+16-2-8
HARDEST+16+16-20
  • 空中物出現時に128以上かどうか判定されます

仕込み時の進行ポイントの目安

各進行ポイントまで進んでミスした場合、難易度設定毎に攻撃ポインタは下記のように変化します。

エリア進行ポイントEASYNORMALHARDHARDEST
エリア3スペシャルフラッグゾーンまで進行-80+8+16
エリア12斜めに道路が交差するあたりまで進行+8+16+24+32
エリア13埠頭の手前のゾルバク×4まで進行-80+8+16

注意など

ご自分の所有する基板、エミュレータ実装の市販ゲームなどでお試しください。ゲームセンターなどでは迷惑が掛かるのでやってはいけません。

追記:発動確認環境

エミュレータ実装のゼビウスであれば発動可能なので、逆にエミュレータなのか確認に使えると思います。最も移植精度の高いナムクラ、PS 3D/G+ は発動しないので移植作品だと分かります。

  • アーケード基板
  • NAMCO HISTORY VOL.1 (Window95/98)
  • SUPER1500 ゼビウス (Windows95/98)
  • ULTRA2000 ゼビウス (Windows95/98)

[ゼビウス] AC版ゼビウスに総攻撃がプログラムされていた (5) からのつづき)

前回の検証で終わったはずでしたが、なんとプログラムの解析結果から未改造のゼビウスで総攻撃が発動する可能性があることが判明しました。ただし意図されたものでは無く、コード上のバグを利用して攻撃テーブルポインタをオーバーランさせることによって可能になります。

条件が成立するマップを探したところ1箇所だけ合致するポイントを発見しました。それがエリア12の冒頭部分で、ここは都合のいいことにテーブル調整が安定して行える正にうってつけのポイントでした。ほとんど同じ動きを最低5回繰り返す必要があるので、意図せず遭遇した人はいないと思いますが、まったく無いとも言えません。先日検証した巨大バキュラバグよりも、実は再現するのは簡単だと思います。

仕込み方法

再現条件は下記の通りで、普通にプレイしてフラグを立てることが可能です。攻撃パターンのチャートがあれば、テーブルの数値を頭で計算しながら調整できます。

  1. エリア12まで行く(残機はある程度必要です)
  2. エリア12の開始時に、空中物のテーブルポインタを調整しておく(直前のエリア11で、テラジ×3、またはテラジ×4が理想)
  3. エリア12のゾルバクを破壊しないでバーラ4つの右側の道路を降りてくるドモグラムの弾(タイミングの目安なので別のドモグラムのでも良い)に当たってミスする
  4. この手順を5回繰り返す(テラジ以外は後述の回数で)
  5. 6回目はそのまま4本ソルのところまで行くと総攻撃が始まるはずです(失敗するとリセットが掛かるか、何も起こらない)

テーブル調整はテラジが一番分かりやすいのですが、他にも下記のような空中物を目安にすることができます。カッコ内はエリア12でミスをする回数を表しています。

  • ジアラ(6回)
  • カピ(7回)
  • タルケン+ジアラ(8回)

※目安なのでゾルバク調整が必要な場合があります(それぞれ4~6パターン同じ機種の数違いが続くのでポインタの数値を確定できません/上記でだいたい40%~50%の成功率です)

追記:追記のほうに書いた「ザカート>ジアラ」で調整するのが確実です。 仕込み完了後に操作可能なリプレイファイルも用意しました。

具体的な調整方法

これはだいたいの目安なので、ネットで攻撃パターンのチャートを探して(時間が出来たら私も資料作りますが)ください。具体的な手順は、エリア11の攻撃パターンをよく見ておいて、今どこにいるか把握します。エリア11の最後の攻撃が今いるポイントになるので、その数値を基準に下記の計算をしながら調整します。

  • エリア12のバーラ4基(の先の道路が交わる部分が画面に出る)まで進むとプラス32
  • ミスでマイナス16
  • ゾルバク破壊で1基マイナス2
  • 攻撃テーブルによる空中物の出現でプラス2
    (ただし空中物の出現時に128以上かどうかチェックされるので仕込み中は出さないようにしよう)

これをうまく使って、エリア12スタート時に攻撃テーブルの数値を112~113に調整できれば勝ち確定です。この状態で上のプレイを1回行うごとにポインタをオーバーランさせ、6回目にそのまま川まで進むと総攻撃が始まります(調整に失敗した場合はリセット)。ただし、発動できても川の手前のごく僅かの時間だけで、川に差し掛かると通常攻撃に戻ってしまいます。

参考画像
参考画像

エリア12のみでテーブル調整(未検証/理論上可能)

残機があればエリア12の川直前の空中物で調整する方法があります。この空中物の種類がテラジ、カピ、タルケン+ジアラのときミスした場合、上記の回数+1回繰り返すと仕込みのラインに乗ります。もし違う空中物の場合は、ゾルバクを少し倒すなりして調整してください。ジアラを外したのは、攻撃パターンのグループが2つあり特定ができないからです。

何が起きているか

からくりは「攻撃テーブルポインタを2加算>128以上になったら64減算する>ポインタに対応する空中物を出現させる」というロジックになっているためです。つまり元のポインタが128未満であることしか考慮していないことを利用しています。更に、得点とミス回数から16加算する処理のバグ(範囲外になってもチェックしていない)を組み合わせることにより、次の空中物が出るときに総攻撃のパターンコード(162~163)を指すよう調整しています。

エリア12の序盤では、バグのあるこの処理が2回実行されるため、攻撃テーブルポインタが32進みます。ミスしたときは単純に16減算しているのみなので、1回のプレイでプラス16進むことになります。従って、(仕込み5回分で)112+16×5=192、(6回目のプレイで)192+32-64+2=162(総攻撃パターンコード0xA2)となります。

なお、ハードやベリーハードの場合はまだ可能性のあるエリアがあるかも知れませんし、テーブル調整がしやすいかも知れません。こちらはまだ想像の範囲ですが、きちんと調べれば入り口の間口は広がるかも知れません。

とりあえず、総攻撃への入り口はエリア12にありました、というご報告でした。動画を作成していますので、できしだい追記&アップロード致します。

追記1

下記に動画をアップしました。

この動画ではエリア11の攻撃が「ザカート>ジアラ」だったのでポインタが104か105で確定できました。(上の目安で書いたジアラの少し後に出てくる2パターン連続しているジアラと特定)そのため、エリア12のゾルバクで調整を入れて必ず発動するよう調整しています。また、エリア11を繰り上げで抜けているので1回分多く仕込み作業をしています。ここら辺はややこしいので早見表を作っておくと成功率が上がると思います。

また想定外ですが仕込み中、変なところでスぺフラ効果音みたいなのが鳴っています。これ、コード上鳴りそうもないのに、どうしてなのかはまったく分かりません。

エリア12が総攻撃への入り口だった
エリア12が総攻撃への入り口だった

追記2

仕込み完了直後にプレイ可能になるリプレイファイルを作りましたので試せる方はご利用ください。

だんだん慣れて成功率が上がってきました。エリア11の斜めの地上物から2回空中物が来ますが、ここが「ザカート>ジアラ」となった場合は、仕込み×6回、ゾルバク×4個破壊、これで仕込み完了になります。もし、動画やリプレイファイルのように繰り上げでエリア12に来た場合は、仕込み×7回、ゾルバク×4個破壊、になります。

リプレイファイルでは、カウントを間違えて5個破壊していますが、恐らく1回目の仕込みの際に、若干スクロールさせすぎて空中物の+2が入ってしまい帳尻が合ったと思われます。もう少し早めに当たった方が成功率が上がると思います。

追記3

A3 を出そうと思って少し気づいたことを書いときます。この出現テーブルポインタですが、加算も減算も偶数で難易度設定変えてもそれは変わらないと思います。そうだとすると、奇数には絶対にならないため A3 は出せないばかりか、出現テーブルの半分は実際には使われていないっぽいです。

追記の追記:チェックポイントで得点とミス数から出現テーブルポインタの加算値を算出しますが、その際に奇数になることがありました。 ただ、ゲーム開始の早いうちでないと以降ほぼ確実に +16 の判定になるっぽい(後ほど検証します)。 また、奇数になっているのはテーブル上のブラグザカートが ×2 になることで判断できます。

固定の空中物としてコードを直接指定されれば別ですが、それでも多くの使われない攻撃パターンがあると思われます。加算や減算の数値を半分にすれば、出現テーブルを倍使えるので非常にもったいないと思います。

おかげで A3 がちょっと難しそうですが、B2 であれば仕込み回数をもう1回増やすだけなので、後で挑戦してみようと思います追記2のリプレイファイルから仕込みを1回追加すると B2 の攻撃になります(動画も追加しておきました)。

あと、先ほどの偶数奇数の話に戻ると「ザカート>ジアラ」と来た場合は仕込み方法がひとつしかありません。出現テーブル上は、同じジアラの攻撃が2連続定義されていますが、片方が奇数になるためポインタが 104 であることが確定するからです。テラジや他の機体でも編隊の機数によって、仕込み回数とゾルバク破壊個数が確定的に言えそうです。

更に改造のアイディアも。ということはですよ、何らかのトリガーで奇数にレーンチェンジできれば、出現テーブルが2本できますよね。ゼビウスアレンジメントのように、出現テーブルを2つ用意してプレイヤーを苦しめることができますね。

追記4:調整の早見表

攻撃順から起点攻撃コードを特定できるものを早見表にしてみました。繰り上げでエリア12に来た場合は、仕込み回数を+1してください。

エリア11の攻撃パターン起点攻撃コード仕込み回数ゾルバク破壊数
ザカート > ジアラ0x68 (104)64
ジアラ > トーロイド0x6A (106)65
テラジ×2 > テラジ×30x70 (112)50
テラジ×3 > テラジ×40x72 (114)51
テラジ > トーロイド0x74 (116)52
発動後に再チャレンジしたい場合

A2 の発動後はテーブルポインタが 146(0x92)になっているので、ゾルバク1個破壊することでもう一度ラインに乗せることができます。ラインに乗ったら、また仕込み3回行うと再びチャレンジできる状態になります。ただし、川付近まで進んだ場合は仕込みが壊れているので諦めましょう。

また、16進で考えたほうが分かりやすいです。まず、ラインに乗せた状態は16進で1桁目がゼロの場合になりますので、ゾルバクを使って乗せてください。仕込み1回で +0x10 進むので 0x90 → 0xA0 → 0xB0 → 0xC0 と仕込みを繰り返し、出撃の際に 0xC0 になっていれば条件が成立しています。

[ゼビウス] AC版ゼビウスに総攻撃がプログラムされていた (7) につづく)

ROM チェックエラー
ROM チェックエラー

今まで ROM チェックを無視して結果判定のところだけ NG に分岐しないよう改造してたんですけど、それをちゃんとチェックが通るようにしよう、ということで重い腰を上げてみた。

ROM データの最終バイトにチェックサム調整の数値が書き込まれているのは気づいていたので、恐らく総和の下位8ビットがゼロになるよう調整してあるんだろうと察しはついていました。実際に計算してみるとその通りだったので、チェックサムを計算してこの調整バイトに書き込んで終わり、と思ったのになぜかチェックが通らない。調べてみると、チェックサムを計算した計算結果が並ぶメモリがあって、そこの恐らく XVI6 に当たる部分がゼロになっていませんでした。でも、こちらのエクセルの計算結果はゼロになります。

プログラムを追ってようやく分かったのは、なんとサブ CPU にマップされる ROM はチェックサムを計算していない、ということです。データの中でたまたまゼロになっている1バイトのアドレスを、ただ計算結果のメモリにコピーしてるだけ! そんなの有りですか? 時間を返して欲しいです。

だから、プログラム内のたまたまゼロ値であるアドレスは変わりようがないので、XVI5 についてはチェックサムは関係なくてもパスします。しかし、出現テーブルの XVI6 は改変したら大きくずれて参照先アドレスがゼロ値でなくなり、チェックサムが合っていても ROM エラーとなります。しかも、ROM 5 という表示でいかにも XVI5 がエラーというふうなのですが、XVI6 がエラーというトラップもありました。

散々苦労しましたが、最初の ROM チェックを無視するというコードが正解でした。同時にプログラムの ROM を改造する場合は、最終バイトまで心置きなく使っても大丈夫なことが判明しました。たぶん、サブ CPU のほうはメンドクサイからそうしたのか、チェック完了のタイミングが CPU 同士で取るのが大変だから、とかそういう理由でしょうね。

ちなみに、エリア8の中盤で右側道路に3台のドモグラムが下りてくるシーンがあると思いますが、そのうち真ん中の1台のデータがそれです。このドモグラムを弄るか、この前のエリアに何か追加してずれてしまうと ROM エラーになります。

再現できたガル・バキュラ
再現できたガル・バキュラ

デモプレイに画面が切り替わる瞬間にクレジットを投入した際、ソルバルウなどが画面に残った状態で PUSH START BUTTON の画面になることが、このバグが成立した状態になります。ただし、状況によっては目立った変化が無い動画もあります、何れにしてもこれが成立していれば、スタート直後に横一列にバキュラが並んだ状態で出現します。こちらのページにそのリプレイファイルがあったので、今回はこのバグが成立する条件を探ってみました。

まず、大量のバキュラが出る原因はすぐに分かりました。バキュラのキャラクターコードが 0x01 で、これが地上物、空中物、バキュラ、スパリオなどのワークメモリすべて埋め尽くされるために起きています。これは総攻撃などと同じ原理で、どのワークに入れても指定したコードのキャラクタが出現するのと一緒です。そのため、空中物に入れられたバキュラは破壊可能で、得点テーブルも 0x01 で埋められているので対応した得点が加算されます。

問題はなぜこのような現象(0x01 で埋め尽くされる)が起きるかですが、やはりメインとサブの CPU が関係していました。画面モードが切り替わった直後にメイン CPU がワークメモリを 0x00 でクリアしています。原因はそのゼロクリアのし方が下記のようになっているためです。

該当箇所のコード(XVI4 の先頭から)
  1. クリアする先頭アドレスに 0x00 を書き込む
  2. ブロック転送命令で1バイト前のアドレスの値を指定回数コピーする
ld   hl,$7900
ld   de,$7901
ld   bc,$06FF
ld   (hl),$00
ldir

このコードはシングル CPU であれば問題は起こりませんが、このブロック転送の最中にサブ CPU が介入してキャラクタの状態を表すワーク領域に 0x01 を書き込むと、意図しない値がコピーされてしまいます。具体的には、ひとつ前のアドレスをメイン CPU が読み出す直前に、サブ CPU がそのアドレスに 0x01 を書き込んだ場合です。本来は先頭に書き込んだ 0x00 がバケツリレーのようにコピーされるはずが、途中から 0x01 に差し替えられてしまい、以降の領域のすべてが 0x01 で埋め尽くされることによります。なお、画面にスプライトが残る残らないの違いは、どの場所をクリアできてどの場所がクリアできなかったかの違いで起きていると思われます。

この条件が成立するのはかなりシビアであり、狙って出すのはかなり難しいと考えます。それはデモ画面に切り替わった直後『サブ CPU がキャラクタの状態のワーク領域に 0x01 を書き込む直前』にクレジットを投入する、というタイミングと思われますが、クロック単位のシビアなタイミングが予想されます。

状況の再現

ロジックが分かったので再現して確かめてみました。前述のコードの先頭に書き込む 0x00 を 0x01 に変更しただけで再現できました。ただし、強制的にやっている関係でミスしても元に戻りません。

ld   hl,$7900
ld   de,$7901
ld   bc,$06FF
ld   (hl),$01
ldir

ゼビウスの他のバグと根っこは同じ

ゼビウスってこれ系のバグが多いですよね。エリア繰り上げワープ然り、森の地上物さん然り、CPU 間でメモリを共有しているにも関わらず、タイミングが取れてないから意図しないデータ参照や書き込みが起きてしまう。もしかしたらまだまだレアな怪奇現象が起こるかも知れません。

追記:大量得点の件

破壊可能なバキュラから大量の得点が得られる点については、得点インデックスが3バイトの二進化十進数で定義されているためです。インデックス値としては3の倍数が期待されますが、そこに 0x01 というイレギュラーなデータが入り、1バイトずれた3バイトを参照してしまいます。その結果、本来20点であるはずのデータが20万点として加算されてしまうようです。

古い MAME をビルドする必要があったので調べた結果を共有しておきます。最近のバージョンはデバッガが使える状態になっていますが、古いバージョンはデバッガをオンにしてビルドしないと使えないためその必要がありました。ただ、あまりに古いのでビルドツールを見つけるのに苦労して探し回った結果、下記の掲示板にまとめがありましたのでリンクを張っておきます。

今回は 0.93 をビルドしたいので "MinGW-3.2.0-rc-3.exe + mingw-over-092.zip (0.92 - 0.104u1)" のリンクより2つダウンロードしました。作業用の WindowsXP にそれらをコピーしインストールします(Windows10 でビルドできるかは試していません)。

ビルド手順

※バックスラッシュは小文字の¥マークです

  1. MinGW-3.2.0-rc-3.exe を実行し解凍先を指定します(今回は C:\mame\MinGW にしました)
  2. mingw-over-092.zip を解凍して C:\mame\MinGW に上書きコピーします(ディレクトリ構成そのままコピーすればOK)
  3. mame のソースをダウンロードして C:\mame\mame093s に解凍しておきます
  4. ファイル名を指定して実行で cmd を入力し OK ボタン(コマンドプロンプトが開きます)
  5. カレントディレクトリを変更します
    cd \mame\mame093s
  6. ビルドツールへのパスを通します(何度も使う場合はバッチファイルにします)
    set PATH=C:\mame\MinGW\bin;C:\mame\MinGW\mingw32\bin;%PATH%
  7. ビルドを実行します
    make

デバッガをオンにしてビルドする

MAME のソースコードのルートにある makefile をテキストエディタで開き、DEBUG=1 のコメントアウトを取って(行頭の # を取る)ビルドするだけです。ビルドが完了すると mame.exe と別に mamed.exe ができ、こちらがデバッガ内臓 MAME になっています。デバッガをオンにして起動するには、下記のようにオプションを指定します。

mamed.exe -window -debug [ROMNAME]

試したバージョン(0.93)ではエラーで落ちる

試したバージョン MAME0.93 ではエラーで落ちる事象が見られました。エミュレーション動作中にデバッガのコマンドを実行すると ACCESS VIOLATION で落ちます。このバージョンだけなのか、その他何が原因なのかは分かりません。

ただし、起動直後の停止状態のときであれば、デバッガのコマンド設定が通るのでそこでブレークポイント等を設定してなんとか使えています。最新の MAME で適当なブレークポイントを探しておき、0.93 の開始直後に設定して止まったところで、ステップ実行その他のコマンドを実行するという、非常に面倒な状況で使うしかありません。一応止まった状態からは、trace、dasm など使っても落ちません。

OpenLayers2 と OpenLayers3 以降とではだいぶ構成が違うので慣れるまでが大変です。

イベントハンドラの代替案2つ

Feature にはクリックイベントが無いようです。似たような動作をしたい場合は、マップオブジェクトのクリックイベントで行えます。以下の例では、クリックした座標から forEachFeatureAtPixels を使ってヒットした Feature をすべてコンソールに出力しています。

map.on('click',function(e){
	map.forEachFeatureAtPixel(e.pixel,function(feature,layer){
		console.log(feature);
	})
});

セレクトツール(インタラクションというらしいが)で良ければ、下記のようなコードでも実現できます。こちらの場合、選択時のスタイルも設定しておくか、選択されたら即解除するような処理を入れておかないとおかしな動作に見えると思います。

var selectSingleClick = new ol.interaction.Select();
map.addInteraction(selectSingleClick);
selectSingleClick.on('select',function(e) {
	console.log(e.target.getFeatures().getLength());
});

ポップアップ(吹き出し)の作り方

OpenLayers2 のときあった OpenLayers.Popup.FramedCloud も無いみたいです。CSS で吹き出し形状にするのって、after などの疑似要素やボーダーの変態活用が絡むのであまりやりたくない。標準のポップアップくらい用意しておいてくれよと思います。

枠だけで良いなら下記のように jQuery でスタイルも埋め込んでやれば CSS 要りません。ただし地図においては、三角形がないとどこを示しているポップアップなのか分からなくなるので、素直に CSS に定義したほうが良さそうです。

popup_container =
	$('<div>')
	.css({
		'position':'absolute',
		'background-color':'white',
		'box-shadow':'0 1px 4px rgba(0,0,0,0.2)',
		'padding':'15px',
		'border-radius':'10px',
		'border':'1px solid #cccccc',
		'bottom':'12px',
		'left':'-50px',
		'min-width':'280px'
	})
	.append($('<div>',{class:'pp_content'}));

popup_overlay = new ol.Overlay({
	element: popup_container[0],
	autoPan: true,
	autoPanAnimation: {
		duration: 250
	}
});

マップのクリックイベントハンドラを下記のように書けばポップアップを表示できます。

map.on('click',function(e){
	map.forEachFeatureAtPixel(e.pixel,function(feature,layer){
		var geom = feature.getGeometry();
		var coord = geom.getCoordinates();
		popup_overlay.setPosition(coord);
		var obj = $(popup_overlay.element);
		obj.find('.pp_content').text(feature.getProperties()...);
	})
});

ポップアップを閉じるには下記のようにします。

popup_overlay.setPosition(undefined);

[ゼビウス] AC版ゼビウスに総攻撃がプログラムされていた (4) からのつづき)

今回はロマン溢れる記事から一転、現実解的な内容になりますので、よろしくお願いします。インパクトある物量攻撃が起こる原因について少し見て行きたいと思います。

複合攻撃の仕組み

空中物が出現するとワークメモリに、出現機数とキャラクタ出現テーブルの位置がロードされます。例えば弾なしトーロイドを3機出現させる場合「0x03 0x01」になります。同時出現は6機までなので、1バイト目は 0x01~0x06 の範囲、2バイト目は 0x01~0x73 くらいの値を取ります。

キャラクタ出現テーブルは表のように定義されており、先の例だと1番目から6機分を出現させる指定になります。複合攻撃にしたい場合は、キャラクタの切り替わる境界を使うことで実現しています。例えば、弾なしトーロイド×3+弾ありトーロイド×3を出したい場合「0x06 0x04」と指定すれば良いことになります。

おなじみの、ジアラ×2+タルケン×3~4の組み合わせもジアラとタルケンが切り替わる境界のところで、「0x05 0x17」か「0x06 0x17」と指定します。機数の違いのみでタルケンを3機にしたり4機にしたりできる、とても巧妙な作りです。

+0+1+2+3+4+5
0x01トーロイド(弾なし)トーロイド(弾なし)トーロイド(弾なし)トーロイド(弾なし)トーロイド(弾なし)トーロイド(弾なし)
0x07トーロイド(弾あり)トーロイド(弾あり)トーロイド(弾あり)トーロイド(弾あり)トーロイド(弾あり)トーロイド(弾あり)
0x0Dジアラ(弾あり)ジアラ(弾あり)ジアラ(弾あり)ジアラ(弾あり)ジアラ(弾あり)ジアラ(弾あり)
0x13ジアラ(弾なし)ジアラ(弾なし)ジアラ(弾なし)ジアラ(弾なし)ジアラ(弾なし)ジアラ(弾なし)
0x19タルケンタルケンタルケンタルケンタルケンタルケン
出現テーブルの冒頭部分

総攻撃はバッファオーバーランか

出現テーブルに従って機数分の空中物ワークへキャラクタが転送されると、キャラクタに対応するプログラムが紐づいて攻撃が始まります。また同様に、地上物は地上物用ワーク、バキュラはバキュラ用ワークを持っていて、出現テーブルに従ってセットされたりクリアされたりしながらエリアが進行していきます。

これらのワークはメモリ上で隣接しているため、例えばボザログラム(ワークを5個使用する)を11番目以降(地上物のワークは14個まで)に設定してしまうとバキュラ用ワークにはみ出します。それぞれのワーク領域では、自機に当たる・当たらない、ザッパーに当たる・当たらない、ブラスターに当たる・当たらない、の属性になっているので、このボザログラムはバキュラバリアを装備し、かつ自機との当たり判定も持つことになります。

ここで面白いのは、本来とは異なるワーク領域へキャラクタをロードさせてもそのキャラクタのプログラム(動き)はそのままな点です。左記のボザログラムはスクロールに同期して移動し、見た目上は地上物としてふるまいます。

総攻撃に話を戻しましょう。今までの前置きを踏まえると「空中物のワーク領域からオーバーランさせて、地上物やバキュラのワーク領域にロードさせた状態なのでは無いのか」という話が見えてきます。

実際に検証してみます

1番目のトーロイドを起点に実際にオーバーランさせて検証してみたいと思います。敵出現テーブルのメモリにブレークポイント入れて、読みだされた後の動きを追ってみると、XVI5 の 0x04D8~0x054B あたりまでが空中物の組み合わせを定義しているテーブルと分かりました(複合攻撃のところで書いた2バイト単位が羅列されています)。

このテーブルの機数のところを書き換えてオーバーランを起こさせます。しかし、適当に数を増やしてみても6機以上は出て来ず、極端に増やすとリセットが掛かりました。見当が外れたのかと思いましたが、71機以上設定すると地上物のワークを使い始めると判明。トーロイド起点だとカピ(71機目)からブラグザカート(87機目)までが地上物扱いになりました。

これ以降109機くらいまではバキュラのワークに配置され、バキュラバリアが付いた状態になります。

更に増やすとスパリオのワークに行くらしく、幻影弾が出るようになります。これ以上増やすとキャラクタのテーブルも終わるため、リセットや halt してしまいます。

0xA2、0xB2、0xA3 では何が起きているか

理由は分かりませんが上のキャラテーブルの続きではなく、XVI5 の 0x03D8~0x04D7 を参照していました。実は 0x80 以降の攻撃パターンは、0xE1 のジアラ+ギドスパリオぐらいしか使用されておらず、なぜこうなっているのか良くわかっていません。キャラテーブルに空きもあるし入れられると思うですけどね、謎です。

それぞれの参照先で何が定義されているか調べてみると下記の通りでした。

攻撃コード参照先(XVI5)定義(機数・起点キャラ)
0xA20x041C0xA7 0xED
0xB20x043C0xC1 0xC9
0xA30x041E0x52 0x19

うーむ。上2つは想像してたのとぜんぜん違いますね。これだとリセットコースなんですが、さっき書き換えてテストしていた部分に入れてみると A2, B2 と同じ攻撃が出て来ました。この A2 の定義だと範囲外のキャラを167機出現させるという指定であり、リセットが掛かってもおかしくありません。これでよく動いてるな、というのが心証です。

追記:0x80~0xFF までの参照で実際有効なのは 0xE0~0xFF(XVI5 の 0x0498~0x04D7)です。 残念ながらプログラムコードの一部を拾ってしてしまい、バグって総攻撃に至っているというのが事の真相のようです。

残りの A3 については、25番目(0x19)のタルケンを指定して82機出現させると言った意味になります。関連するキャラテーブルを書き出すと下記の通り。

+0+1+2+3+4+5
0x19タルケンタルケンタルケンタルケンタルケンタルケン
0x1Fゾシー(乱)ゾシー(乱)ゾシー(乱)ゾシー(乱)ゾシー(乱)ゾシー(乱)
0x25トーロイド(弾)トーロイド(弾)ギドスパリオギドスパリオギドスパリオギドスパリオ
0x2Bギドスパリオギドスパリオゾシー(追撃)ゾシー(追撃)ゾシー(追撃)ゾシー(追撃)
0x31ゾシー(追撃)バックゾシーバックゾシーバックゾシーバックゾシーザカート(落下)
0x37ザカート(落下)ザカート(落下)ザカート(落下)ザカート(落下)ザカート(落下)ザカート(自狙)
0x3Dザカート(自狙)ザカート(自狙)ザカート(自狙)ザカート(自狙)ザカート(自狙)バックゾシー
0x43バックゾシーバックゾシーカピカピカピカピ
0x49カピカピザカート(自狙)ザカート(自狙)ザカート(自狙)テラジ
0x4Fテラジテラジテラジテラジテラジブラグザカート(自狙)
0x55ブラグザカート(自狙)ブラグザカート(自狙)ブラグザカート(自狙)ブラグザカート(自狙)ブラグザカート(自狙)ブラグザカート(自狙)
0x5Bブラグザカート(自狙)タルケンタルケンギドスパリオギドスパリオギドスパリオ
0x61ギドスパリオジアラ(弾)ジアラ(弾)カピカピギドスパリオ
0x67ギドスパリオギドスパリオギドスパリオテラジテラジザカート(自狙)

青字が通常の空中物として出ているタルケン6機、赤字が地上物扱いで出ているものになります。正直これ以外はメモリのゴミか無関係な参照かも知れませんが、こちらはキャラクタの区切りにきっちり設定してあり、キャラテーブル内の機数で設定され、かつ地上物ワークの範囲内に入れてある、という点を取って意図を感じてしまいます。

追記:地上物ワークまで間が空くのは、<地上物>~<バキュラ>~<スパリオ>~<空中物>~<未使用>の順に配置されているためでした(2バイト単位なので128機(うち有効なのが最大54機)で1周しています)。 細かく書くとバキュラとスパリオの間も、自機や照準など7機分の空きがあるようです。

結びに

果たして、意図したものなのか、単に無意味な参照だったのか、判断はお任せします。個人的にはロマンあるほうを主張しておきたいですね。

最後に、すごくインパクトのある発見で久しぶりにゼビウスで楽しめましたし、ここ最近プレイもして久方ぶりに1000万点を出しました。あと、消失ソルを1バイトで出す方法(今までは2バイトだった)とか、複合攻撃のコントロール方法、その他改造に役立つ知見が得られました。消失ソルの件は時間が出来たら記事にしますので、お楽しみに。

[ゼビウス] AC版ゼビウスに総攻撃がプログラムされていた (6) につづく)