|
6 |
GameFSMの改良 (25) |
次に原作にもある「化石」という症状をインプリしてもらいました。
仕様: 化石の再現 隊列がシールド帯まで降下した状態で、左端での下降パス中に自弾が右端シールドに命中すると、これを個体命中と誤認する。シールド内の弾の位置に敵爆発音と敵爆発マークを発生させ、マーク消去時にシールドへ 16×8 の穴を開ける一方、実際に死ぬのは弾の真上とは別の列の最前列個体である。その個体の画像は消去されないまま画面に残り、以後は敵として撃てず、隊列だけが先へ進む。
発症条件の幾何定数 — 隊列の降下判定しきい値と、右端シールドの x 範囲。
`ifdef FOSSIL_BUG
`define FOSSIL_BUG_Y 176 // 原作バグ発症条件: 隊列がシールド上端(192)の1ピッチ以内
`define FOSSIL_BUG_X 182 // これ以右のシールド命中で発症(=右端シールド全体、x=182〜204)
`endif
爆発マークの描画・消去位置を保持する — 犠牲個体の座標から切り離すことで「マークと犠牲が別の場所」という原作バグの本質を表現可能にする。
`ifdef FOSSIL_BUG
Reg#(UInt#(8)) expl_x <- mkRegU, // インベーダ爆発マークの描画・消去位置
expl_y <- mkRegU; // (バグ再現時は個体位置とマーク位置が食い違う)
`endif
常キル時にマーク位置=個体位置を expl_x/expl_y へラッチする(挙動は従来と同一の中立改造)。
`ifdef FOSSIL_BUG
eraseArea(
expl_x,
expl_y,
16, 8);
`else
eraseArea(
inv_x[gx][gy],
inv_y[gx][gy],
16, 8);
`endif
マーク消去を個体座標ではなく expl_x/expl_y に対して行う(バグ時はシールド内を消して穴を開ける)。
`ifdef FOSSIL_BUG
return (seq
action
alien_timer <= 1;
expl_x <= inv_x[gx][gy]; // マーク位置=個体位置(通常キル)
expl_y <= inv_y[gx][gy];
endaction
copyArea(96, colorAtY(inv_y[gx][gy])*16+16,
inv_x[gx][gy], inv_y[gx][gy],
16, 8);
endseq);
`else
return (seq
alien_timer <= 1;
copyArea(96, colorAtY(inv_y[gx][gy])*16+16,
inv_x[gx][gy], inv_y[gx][gy],
16, 8);
endseq);
`endif
発症4条件成立時のシールド命中を誤認キル化する本体 — マークと音は弾の位置、死ぬのは左端側の生存列の最前列、絵は残って化石になる。
`ifdef FOSSIL_BUG
endseq else if (fbase && ymax >= `FOSSIL_BUG_Y
&& inv_move == MoveLeftDown
&& bullet_x >= `FOSSIL_BUG_X) seq
// ---- 原作バグの再現(希少条件版) ----
// 右→左で来て左端で下降中(=MoveLeftDown、隊列スキュー中)、
// かつ隊列がシールド帯まで降りた状態で右端シールドを撃つと、
// 原作の座標→個体逆算の破綻(左下原点からの列計算オーバー
// フローが隣行へ回り込む)を模して、左端の生存個体が誤って
// 殺される。マークと消去は弾の位置、犠牲の絵は左端に無傷で
// 残り、隊列に取り残される(化石)。
fhit_alien <= False;
for (gx <= 0; gx < `InvCols; gx <= gx + 1) seq
if (inv_s[gx][inv_lead_y[gx]]) seq // 左端の生存列を探す
fhit_alien <= True;
break;
endseq
endseq
if (fhit_alien) seq
gy <= inv_lead_y[gx];
erasePlayerBullet(bullet_x, bullet_y);
inv_s[gx][gy] <= False;
inv_no <= inv_no - 1;
updateColumnFrontY();
playSound(SND_ALIEN_EXPLODE);
action // マークは弾の位置に描く
alien_timer <= 1;
expl_x <= bullet_x - 8;
expl_y <= bullet_y;
endaction
copyArea(96, colorAtY(bullet_y)*16+16,
bullet_x - 8, bullet_y, 16, 8);
action
score <= score + inv_score[gy];
bscore <= bscore + inv_score[gy];
endaction
endseq else seq
erasePlayerBullet(bullet_x, bullet_y); // 生存列なし: 通常のシールド命中
explodePlayerBullet(35, bullet_x, bullet_y);
bullet_timer <= 1;
endseq
`endif