ステージデータ展開

順番前後しましたが、スクリプト内でのステージデータの展開について。

>9x9のフィールドデータ部分は、横3マスをASCII文字1Byteで表現しており、81マスで27文字使います。
>これに、敵と赤ポーションの数を示す1文字を付けた構造で、1ステージ分を正味28バイトの文字列データとします。
たとえば、スクリーンショットの面(Ver1.12では20面)の文字列データは "X@pZ@r@h@@B@PGP@B@@h@ZRrX@pL" です。
先頭から27バイト分を元に、以下のようなスクリプトで魔石オブジェクトとグランドマップへ展開しています。

memset mapg,255,EMAPNUMSZ
lpoke c1
repeat PLFNUM
	c1+:gosub *vardupc1
	e0t=(peek(mg,cnt/DTW)>>(cnt\DTW))&9,cnt+PLFOFSET+(cnt/PLFW)*PLFGAP,1 ; オブジェクトデータ登録
	gosub *posclc:emapg=(e0t=1) ; グランドマップ更新
loop

あらかじめ#constされている各シンボルは、以下のような意味と値になっています。
EMAPNUMSZ マップ配列のバイト数、15*11*4→660
PLFNUM プレイフィールドのマスの数 9*9→81
DTW ASCII文字1バイトで現わすマスの数 3
PLFOFSET マップにおけるプレイフィールド開始位置 15+1→16
PLFW プレイフィールドの幅 9
PLFGAP 2つの段のプレイフィールド間にある壁の厚さ 15-9→6


まず、memsetはグランドマップの中を255(&HFF)で埋めています。
配列は整数型なので、1要素4バイト分として考えると&HFFFFFFFF、つまり-1で埋まるのと同義です。
ループ内ではプレイフィールドのみ操作するので、81回ループしています。(壁は触りません)
c1をオブジェクトID(1〜82)として用いるので、ループ前にlpoke c1で0クリア、ループ最初でインクリメントしています。
おまじないのサブルーチンvardupc1をコールした後、オブジェクト情報の定義をします。
e0tは該当オブジェクトについての管理テーブル先頭要素で、ここからカンマ区切りで、マスのタイプ、位置、移動カウントを代入。
マスのタイプ(空白:0、魔法陣:1、紫魔石:8、赤魔石:9)は、文字列変数mgより該当する1バイトを参照し、シフト&マスク(9)で取得。
位置は、cntの値に<プレイフィールドの段数>×<壁の厚さ>とオフセットを加算した値。
オブジェクト情報に位置と移動カウントが入ったらposclcを呼び、魔石オブジェクトの下準備(現在位置のx、y情報登録など)を行います。
空き床や魔法陣についても構わずオブジェクト登録していますが、メインルーチンではタイプ8,9だけが魔石として扱われるので問題ありません。
posclcにてemapgに該当位置のグラウンドマップがdupされるので、タイプが1なら魔法陣(1)、それ以外は空き床(0)を代入します。


微々たる量ですがサイズが肥大化しないよう、配列添字の隠蔽と、計算式削減を念頭にスクリプトを書いています
要所に出てくるvardupc1,posclc(もしくはvardup,posclceg)などに共通化できる定型処理をまとめておき、マップ配列はできるだけdupされた変数が使えるようにしています。
シフトの計算については1マス1ビットシフトで済むタイプ値を採用し、式が大きくならないようにしています。
また、処理全体として1次元のデータとして扱うことで、ループ多重化も回避。