Raal's Tome of Destruction

主にroguelikeのことを書くブログです

解析記事 - 遠距離スタブ

この記事は Roguelike Advent Calendar 2014 の15日目の記事です。

DCSSでは最近、遠距離攻撃に不意打ちが実装されました。しかし良く分からんぞ詳細どうなの、ということで解説記事です。
これはソース読まな分からんわ、という感じですね。

Crawlの不意打ちとは

Crawlでは睡眠、混乱、石化などの状態異常の敵を攻撃したとき、適切なスキルを保有していると大ダメージを与える機会が得られます。
従来これは近接戦闘のみに適用され、しかも短剣類以外のダメージボーナスは微々たるものでした。
隣接せずに放つ遠距離攻撃ではあまりにも容易に睡眠スタブが発生するので強すぎるとの判断だったわけです。
しかし0.15にて吹き矢のみとはいえこれが解禁されてしまいました。気になります。
とりあえず、2014/12/15時点で0.16trunk最新版のソースを解析することにします。

ソースコードの取得について

こういう話をするならソースコードがなければ始まりません。
DCSS公式はgitリポジトリを公開しています。
とりあえず今見るだけなら以下リンクから。
https://gitorious.org/crawl/crawl/source/d1cc993324d235f6b55e8b23e9b608a29312edbf:crawl-ref/source
なお、めんどくせえから結論だけ見るって人は読み飛ばして結論を見てください。

見るべき場所

ずばりranged_attack.ccのattack()がメインの処理です。
今回みるべきスタブ関連の箇所をざっくり抽出すると

    • player_stab_check()を実行
    • もしスタブ判定を通れば必中にして盾防御不可にする
    • 必中になってるので諸々のチェックをスルーして当たる
    • handle_phase_hit()を実行
    • その後の処理がなんかいっぱいある

要はplayer_stab_check()とhandle_phase_hit()を見ればいいことが分かります。
両方ともranged_attack.cc内にあります。

ranged_attack::player_stab_check()

player_good_stab()であればattackクラスのplayer_stab_check()を実行します。
それで不意打ち判定にならなかった場合、Blowgun of Assassinを装備していれば1/3で不意打ち扱いとします(Blowgun of Assassinは0.14で廃止されていることに注意してください)

player_good_stab()はblowgunからneedleを発射していればtrueを返します。

attack::player_stab_check()

attackクラスのplayer_stab_check()はattack.ccを参照します。
ざっくり言うと、状態異常によって不意打ちのパワーを決定する関数で、近接攻撃と共用です。
stab_bonusという変数が不意打ちの倍率を決定します。小さいほど強い不意打ちになります。

    • 睡眠 麻痺 → stab_bonus=1 必中
    • ネット 石化中 石化済 → stab_bonus=2
    • 透明(dazzによる盲目もここ) 混乱 恐怖 隷属 → stab_bonus=4
    • 余所見 → stab_bonus=6

必中でなければ不意打ち発生のチェックを成功させる必要があります。
成功率は((武器スキル+隠密スキル)/2+器用+1) / 100。
ただし透明不意打ちは分母が90になります。

handle_phase_hit()

スタブ強度を決めたのでranged_attack.ccに戻ってきました。
なんやかんやあって当たった後の処理を追っていきます。
スタブが発生するのは針だけだと分かっているので、針の処理を重点的に見ていきます。

    • dur = blowgun_duration_roll(針の属性)
    • stab = player_stab(dur)
    • damage_done = dur + (stab - dur) / 10
    • apply_damage_brand() (火炎とかの属性エゴ弾の処理。関係ないので省略)
    • apply_missile_brand()
    • behaviour_event(恐怖したり敵対したり、モンスターの状態がなんか変わる。省略)

他の武器に関するところやメッセージ出すだけの処理を端折るとこんな感じです。
見るべきは

    • blowgun_duration_roll()
    • player_stab()
    • apply_missile_brand()

この3つです。

blowgun_duration_roll()

吹き矢の属性によって効果ターンを決めています。

    • 毒=5+1d8
    • クラーレ=2
    • それ以外=4+1d(投擲スキル+blowgunの修正値)

player_stab

attack.ccにあります。
damageが1点未満なら1点にしてplayer_stab_weapon_bonusに丸投げします。

player_stab_weapon_bonus

attack.ccにあります。
不意打ちのダメージ増加量を決定します。

    • stab_skill = (武器スキル + 隠密スキル) * 50
    • stab_bonus = 上で決めた奴
    • good_stabであれば(針や短剣等)大ダメージを与える
      • bonus = 器用 * (stab_skill + 100) / 1000 ただしダガーは2倍
      • bonusを適当に減衰させる。上限30。
      • damage += bonus
      • damage *= 10 + (stab_skill / 100 / stab_bonus)
      • damage /= 10
    • 共通処理(good_stabであってもこの処理を実行することに注意)
      • damage *= 12 + (stab_skill / 100 / stab_bonus)
      • damage /= 12

apply_missile_brand

ranged_attack.ccにあります。

      • なんやかんやあってdamage_doneだけ毒を増加させる
    • クラーレ
      • curare_actorに処理を丸投げ
    • その他
      • blowgun_checkに成功したら対応する状態異常をdamage_doneターン入れる。

blowgun_check

ranged_attack.ccにあります。
無生物とアンデッドが相手なら必ず失敗します。デーモンは無生物ではないことに注意してください。

    • 不意打ち成功時、無条件成功。
    • モンスターHDが14以下のとき、3%で無条件成功。
    • resist_roll = 1 + 1d(4+投擲スキル+blowgunの修正値)
    • resist_roll >= モンスターHD で成功

curare_actor

beam.ccにあります。
モンスターが対象のとき、_curare_hits_monsterに処理を丸投げします。
変数levelsには前述damage_doneが格納されていることに注意してください。

_curare_hits_monster

beam.ccにあります。
ようやく辿り着きました。本命の処理です。

    • 毒を強度levelsで入れます。
    • asphyx耐性がない場合、(levels)d6のダメージを入れ、呼吸困難状態にします。
    • slowを入れます。levelsが高いほどslowが長くなります。

結論

    • 吹き矢以外の遠距離は不意打ちなし
    • 睡眠以外の不意打ちも全部アリアリ
    • 不意打ち成功すれば状態異常が確実に入る
    • 短剣相当の不意打ちで計算するが、増加は1/10になる
    • 増加した値はダメージではなく状態異常ターンの増加に使われる
    • ただしcurareの窒息ダメージは計算式が別なので必殺の一撃になりうる

例えば短剣で200点入る不意打ちだとして、針だと状態異常が必中になり持続が20ターン程度増えます。
必中の麻痺混乱は非常に強く、短剣不意打ちに繋がる貴重な遠距離攻撃として重宝しそうです。
またcurareだと窒息ダメージが通常2d6点のところ、20d6点くらいになり柔らかいモンスターを即死させます。
上級エルフや上級ドラコニアンなどへの有効打として期待できます。

まとめ

ソースコード解析楽しい。dcssは比較的読みやすいので皆で読みましょう。