免責事項
ゲームを編集している場合、その詳細をすべて揃える必要があります。 元のバージョンの断片が出て、私たちが入れた新しい意味と矛盾する場合、ゲームのバージョンを世界に見せることは愚かでしょう。 ゲームレベルの編集では、開発者が最初に投資したプロットのひねりを変更することはできません。
少し遅れて、プロットを修正します。
何を編集しますか?
先に進む前に、ゲームを再描画するように制限したいと思います。 実際、すべてを変更したい場合は、最初からゲームを書く方が簡単です。 しかし、当初の計画どおり、私はまだ古いベースを維持しながら、何か新しいことをしたいと思っています。 編集するものを決定します。
- 色。 NESバージョンは、すべてのレベルに同じタイプの退屈な色があるという点で、他のバージョンとは異なります。 法執行官の色のみが変わります。
- デモプレイ。 最初の部屋で少なくともレンガを交換するとすぐに、デモプレイの主人公は、盲目のウサギが壁を叩いたり、なくなった棚に登ろうとするのを好むでしょう。
- 「鏡面反射」 特定の条件が満たされ、厳密に定義されたレベルの厳密に定義された部屋でのみ表示されます。 何かを再構築すると、その場所から外れて表示されるか、まったく表示されず、ゲームのプロットが歪んでしまいます。
- ...マウスは8番目のレベルにあります:-)。 彼女は刑務所で自分自身を見つけたときに私たちを救うために王女の要求で走り、私たちにバーを明らかにします。
- 敵の種類。 3番目と12番目のレベル(オリジナルでは、12番目のレベルでは「リフレクション」ですが、NES開発者は少し節約しました)では、スケルトンに会います。
- トランジション。
- 最初のレベル。 プロットによれば、ヒーローはダンジョンに投げ込まれ、バーは彼の後ろで閉じられます。 したがって、彼は最初のレベルに現れ、その後すぐに脱出ルートが閉じます。
- 第6レベル。 主人公は奈落の底を飛び越えますが、陰湿な「反射」が火格子を閉じ、地下牢の深inに侵入します。 次のレベルへの非標準の移行があります。
- レベル7。 6番目のレベルで落ちると、7番目のレベルで上から表示されます。 非標準レベルのエントリ。
- 12番目と13番目のレベル。 ここで、開発者は、長い1つを2つの短いレベルに分割することにしました。 部屋から部屋へと移動し、次のレベルに到達します。
- 14番目のレベルには、次のレベルへの移行はありません
ゲームの精神を維持するために、ブロックのテキスト、音楽、タイプ、構成を元の形のままにすることにしました。 すべてのオブジェクトはかなり単純なデータへのポインターの配列に関連付けられているため、これは他のすべてと同じように簡単に変更できます。
ところで、私はその場所にマウスを置いたままにしました。
コード
ゲームの動的な要素は、以前のように単純なつぶしで簡単に変更することはできません。 したがって、コードを積極的に研究するときが来ました。 私たちはその要素のいくつかに精通しました。 それでは、ゲーム中に何が起こるか見てみましょう。
デバッガーで「ステップオーバー」ボタンを押すと、必然的にメインループに移動します。メインループのメインコンポーネントは次のようになります。
label_CC1A: $CC1A:20 F7 F2 JSR $F2F7 $CC1D:20 10 D0 JSR $D010 $CC20:20 1E 86 JSR $861E $CC23:20 00 CB JSR $CB00 $CC26:20 E8 A4 JSR $A4E8 $CC29:20 10 D0 JSR $D010 $CC2C:20 04 8B JSR $8B04 $CC2F:20 10 D0 JSR $D010 $CC32:20 F9 80 JSR $80F9 $CC35:20 FC D8 JSR $D8FC $CC38:20 DF BA JSR $BADF $CC3B:20 00 CB JSR $CB00 $CC3E:20 12 9F JSR $9F12 $CC41:20 DD A3 JSR $A3DD $CC44:4C 1A CC JMP $CC1A
このループの上には、部屋またはレベル間を移動するときに呼び出される多くのプロシージャの呼び出しもあります。 上記のコードにリストされている手順は、ゲームの状態を変更し、さまざまな種類のチェックも実行します。 これらのうち、以下は重要ではありません。
-$ F2F7-VBlank割り込みの特定の状態の変化を待っています。
-$ D010、$ CB00-対応する銀行にある手続きを呼び出す前に銀行を切り替える。
残りのすべてのプロシージャのコードは提供しません。すべてのブランチを考慮すると、さらに10個のポストが作成されるためです。 今興味のあるものだけをあげます。 私は擬似コードに翻訳しませんが、コメントをします。
A4E8。 彼は走っている!
このプロシージャを呼び出す前に、バンク#02-$ D010をオンにするプロシージャが呼び出されます。 セル$ FFF2に番号#02を書き込むことでこのバンクをオンにし、次の手順に進みます。
$A4E8:AD E4 04 LDA $04E4 = #$01 $A4EB:D0 13 BNE $A500 ;; $04E4. ;; 0, $A4ED:60 RTS ;; ... $A4EE:4C FF A4 JMP $A4FF $A4F1:A9 00 LDA #$00 $A4F3:8D E3 04 STA $04E3 = #$44 $A4F6:8D E0 04 STA $04E0 = #$76 $A4F9:8D E4 06 STA $06E4 = #$0A $A4FC:4C 14 DC JMP $DC14 ;; ;; , - . $A4FF:60 RTS ;; $DC14. ;; label_A500: $A500:AD 0A 04 LDA $040A = #$01 $A503:29 30 AND #$30 $A505:D0 EA BNE $A4F1 ;; , ;; $A507:AE E3 04 LDX $04E3 = #$44 ;; $04E3 $A50A:BD 0F A7 LDA $A70F,X @ $A763 = #$AA ;; ROM- , $04E3 $A50D:C9 FF CMP #$FF $A50F:F0 E0 BEQ $A4F1 ;; #FF, ;; $A511:CD E0 04 CMP $04E0 = #$76 ;; $04E0 $A514:D0 0E BNE $A524 ;; , ... (. ) $A516:A9 00 LDA #$00 ;; ... $A518:8D F4 04 STA $04F4 = #$00 $A51B:8D E0 04 STA $04E0 = #$76 $A51E:EE E3 04 INC $04E3 = #$44 ;; 2 ( ) $A521:EE E3 04 INC $04E3 = #$44 label_A524: $A524:BD 10 A7 LDA $A710,X @ $A764 = #$01 $A527:8D 0A 04 STA $040A = #$01 ;; ROM- $040A $A52A:EE E0 04 INC $04E0 = #$76 ;; $A52D:60 RTS ;;
したがって、ここには、いくつかの状態を持ついくつかのセル、インデックスを持つ別のセル、およびROMファイル内の特定の配列があります。 このコードは、フラグがセル$ 04E4に設定されている場合にのみ実行されることを示しています。 それを入れてみましょう。
ゲームを再開し、最初のレベルに入り、ユニットを$ 04E4に入れます。
2回目が通過し、...彼は自分で走ります ボタンを押す必要はありません。
デモのプレイを待つと、セル$ 04E4にすでに1つあることがわかります。デモ中にこのセルをリセットすると、デモのプレイが消え、コントロールが手に渡されます。 非常に便利です:デモはメインパートを渡し、その後、$ 04E4をリセットして自分でゲームを続行できます。
これで、ゲームに基づいて、コードを解析できます。 明らかに、$ DC14プロシージャにより、ゲームメニューが表示されます。 $ A70F配列には、それぞれ2バイトの構造体がいくつか格納され、#FFトークンで終わります。 さらに、最初のバイトは一定の時間間隔であり、その終わりにインデックスが増加し、次の構造の2番目のバイトがセル$ 040Aに転送されます。 彼はどういう意味ですか?
配列を見てみましょう:
64 00 . A0 01 . 28 00 . E6 84 . E6 02 ... FF
まず、#64 cuを期待します (従来の時間単位)、#A0 U.V.E。、#28などのように#FFマーカーまで。 最初のバイトペアの時間を#FEに変更すると、デモプレイでは、開始後長時間キャラクターがアイドル状態になっていることがわかります。 そこに#02を置くと、彼は左に曲がり、しばらくの間、壁にくり抜かれます。 したがって、2番目のバイトは、指定された時間間隔中に実行するアクションを記述します。 ゲームが決定されているため、特定のアクションのシーケンスを厳密に設定すれば十分であり、それ自体が「プレイ」されます。
実際、2番目のバイトはコントローラーのボタンを押すことの模倣であり、最初のビットはボタンを右にエンコードし、2番目のビットは左のボタンをエンコードします。 これらのビットの組み合わせにより、デモプレイ中のゲームパッドの擬似状態が決まります。 この配列を編集することにより、デモプレイを新しいレベルに調整できます。
セル$ 040Aの値で次に何が起こるかを追跡すると、$ 060Eにあるデータ構造に到達します。 次のように説明されています。
struct CHARACTER { char bCharType; unsigned short X; unsigned short Y; unsigned short ptrAction; char bDirection; char bActionIndex; char bPoseIndex; char bReserved[4]; };
この構造に到達すると、特定の構造へのポインターで構成される「アクション」の配列があり、それが画面上のスプライトディスプレイのシーケンスに展開されることがわかります。 「アクション」とは、キャラクターが画面上で実行することを意味します。実行、ジャンプ、しゃがみ、その他のアクションです。 同様に、「ポーズ」インデックスを持つすべて:彼が立っているか座っているかは、構造のこのメンバーによって決定されます。 将来的にはこの構造が必要になりますが、今のところ、レベルからレベルへの移行方法を見つける必要があります。
迷路を駆け抜ける
ここで、以前と同様に、開始点-セル$ 70があります。 レベルからレベルに移動すると、コードが表示されます(以前と同様に、セル$ 70に書き込む条件によってブレークポイントでキャッチします)。
$86F3:AD 35 07 LDA $0735 = #$00 $86F6:D0 0D BNE $8705 $86F8:A5 70 LDA $0070 = #$00 $86FA:18 CLC $86FB:69 01 ADC #$01 $86FD:C9 0E CMP #$0E $86FF:90 02 BCC $8703 $8701:A9 00 LDA #$00 $8703:85 70 STA $0070 = #$00 ;; <<< $8705:A9 00 LDA #$00 $8707:8D 01 20 STA $2001 = #$18 $870A:85 15 STA $0015 = #$18 $870C:4C 1D CB JMP $CB1D
ここで、ユニットがセル$ 70に追加され、最後のバンクに移動することがわかります。 さらに、チェックがあります。数値が13より大きい場合、この値をゼロにします。 つまり、出力をレベル14にすると、最初に戻ります。 これは、次のレベルに移行するための開始手順です。 次に、パスワードが表示され、次のレベルに進みます。 それがどこから呼び出されたのか見てみましょう。
ハードコード
x86アーキテクチャの場合と同様に、ここでは、JSR命令の引数を命令ポインターレジスタにプッシュし、リターンアドレスをスタックにプッシュすることにより、プロシージャが呼び出されます。 スタックには次のものがあります。
22, CC, ...
したがって、$ CC22の命令に先行する命令は次のように呼び出します。
$CC20:20 1E 86 JSR $861E
かさばり、すべてをトレースするのは面白くありません。 ブレークポイントを$ 70での書き込みから読み取りに切り替えて、デバッグを開始します。 最初に、おなじみの$ C0D5プロシージャで停止します-それは私たちにとって興味深いものではありませんが、次の停止でここに来ます:
$D0B8:A5 70 LDA $0070 = #$0B $D0BA:C9 0B CMP #$0B $D0BC:D0 0C BNE $D0CA $D0BE:A5 51 LDA $0051 = #$16 $D0C0:C9 16 CMP #$16 $D0C2:D0 06 BNE $D0CA $D0C4:20 10 D0 JSR $D010 $D0C7:4C F3 86 JMP $86F3
セル内の値は、番号#0B == 11(レベルは最初から番号付けされていることを思い出します)と比較され、次にセル$ 51から読み取ります。ここで、#16 = 22になります。レベルマップを見ると)、51ドルで、最後に検索しなかった部屋番号があります。 これらのチェックが実行されると、アドレス$ 86F3、つまり移行を開始する手順に進みます。 キャラクターが部屋を左に出ると、開発者はこの部屋から別のレベルに移動するためにコードを簡単に入力しました。
キャラクターが奈落の底に落ちたときに、6番目のレベルから7番目のレベルへの遷移を探す場合、同様のテストを見つけることができます。 つまり、CMP命令を編集するだけで十分であり、別の部屋にはこれらのプロパティがあります。 ちょうど条件が満たされる必要があります。次のレベルへの移行は、左(このチェックの場合)または転倒(2番目の場合)に行われます。
新しいレベルの起動コードを検討した結果、最初のレベルでは、キャラクターの座標もコードにハードコーディングされた値で上書きされるため、調査の開始時にレベルヘッダーを変更したときに移動できないことがわかります。 レベル13でのガードの敗北後、同様のチェックが実行され、道を開けることができます。 それらはすべて、同様の方法で非常に簡単に見つかります。
スケルトンをキャビネットから取り出し、壁をペイントします
エディターはほぼ完成です。 レベルからレベルへ、または部屋から部屋への移行モーメントを調べると、編集可能な残りのパラメーターが見つかります。 このようなデータを検索する方法はすでに検討されているため、ここでは詳しく説明しません。 一般的な説明のみを行い、最後の章の最後に、ゲームの構造とその説明に関するROMの変位のリストを示します。
0x13BEB: 00 00 24 00 00 00 00 00 00 00 00 24 00 00
ガードが描画されるタイルのセットのコードを含む配列(ガード自体またはスケルトン)。 この構造は、レベルのタイプを説明する構造に似ており、レベルの正常性の量を決定する構造に似ています。
パレットは、ポインタの2つの配列とパレット自体の実際の配列から構築されます。 パレット自体は、32バイトのシーケンスとしてPPUに送信される形式で正確に保存されます。 2番目の配列には7つのポインターしか含まれていないため、レベルに受け入れられるパレットが表示され、最初のポインターは6番目になります。 そして、最初の配列は(レベルの数に応じて)14個のポインターで構成され、それぞれが2番目の配列の1つまたは別の要素につながります。
ほぼ準備完了
この段階で、エディターのサンプルシェルを既にスケッチしました。細部は1つだけでした。 後で、ステータスバーのマジックナンバーの意味を説明します。
「リフレクション」と呼ばれる最後の5番目の章では、王子の反射を制御する方法を学びます。 ゲームのコースに実質的に影響を与えなかった二次的な要素から、それを主要なものの1つに変えることができます。