ワープをキャンセルしたときのカーソルの不具合について(原作バグ)

トラキア776にはワープ/リワープの操作をキャンセルしたときにカーソルの内部値が狂うバグがあります。これの動作原理や、ロムハックでの修正方法について記載します。

ワープのカーソルバグとは何か

ワープ/リワープでの飛ばし先を指定するカーソル操作をキャンセルしたときに、カーソル位置の内部値が上書きされるバグです。ワープやリワープでカーソルを該当座標に合わせてキャンセルすると「秘密店」コマンドが出るバグとして有名なやつです。

内部的には、操作中のユニットのX座標・Y座標、カーソルのX座標・Y座標、そしてそれらをプログラムが扱いやすくした座標値をメモリに持っています。これらは行動コマンドを表示する際には一致していることを期待して各処理が実装されていますが、ワープの操作をキャンセルすると、カーソル位置が上書きされたままになっているため、乖離してしまいます。

ユニットのコマンドの表示・非表示は、操作中のユニットの移動先(各ユニットが持っている座標)で判定されますが、店などのイベント内容を参照する際はカーソル位置をもとにしているため、上記の乖離が発生すると誤った内容を読み出してしまう問題がありました。

不具合の実演動画(日本語音声、英語字幕あり)

この動画では、リモート訪問バグ、宝箱訪問バグ、リモート開錠バグについて実演しています。発生する原因はどれも同じです。

修正内容の概要

ユニットの行動コマンドの表示処理をフックし、座標リセット処理を追加すると、この不具合は発生しなくなります。

既存処理の変更

各コードは Asar の構文に従っています。

行動コマンド定義読み出しをフック

org $878391
	jsl l_878358_sub
warnpc $878396
padbyte $ea : pad $878396

追加処理

以下の追加処理は適当な空き領域に入れます。

座標リセット処理の呼び出し

l_878358_sub:
	lda.w	#$8000	; 
	sta	$2F	; 
	jsl reset_map_cursor_pos
	jsl reset_map_cursor_coordinate_value
	rtl

カーソル位置をリセットする

reset_map_cursor_pos:
	sep	#$20	; A:8
	lda	$0E72	; Active unit X coordinate
	sta	$0E4D	; Map cursor X coordinate
	lda	$0E73	; Active unit Y coordinate
	sta	$0E4F	; Map cursor Y coordinate
	rep	#$20	; A:16
	rtl		;

座標値の再計算

reset_map_cursor_coordinate_value:
	phx		; 
	lda	$0E4F	; Map cursor Y coordinate
	asl	A	; 
	tax		; 
	lda	$5142,X	; Map Y direction, Each row coordinate origin
	clc		; 
	adc	$0E4D	; Map cursor X coordinate
	plx		; 
	sta	$0E4B	; Map cursor coordinate value
	rtl