# 機能設計書 30-レーザーノート判定

## 概要

本ドキュメントは、L/R独立のアナログ位置追跡・スラム判定・バッファリングによるタイミング補正を行うレーザー判定機能の設計を記述する。

### 本機能の処理概要

**業務上の目的・背景**：K-Shoot MANIAのレーザーノートは、BTノート・FXノートとは異なるアナログ入力（つまみ操作）による判定システムである。左右独立の2レーンのレーザーに対して、プレイヤーのカーソル位置がノートの目標位置に追従しているかをリアルタイムで判定する。スラム（急速な位置変化）には専用の判定ロジックが存在し、6種類のアナログ入力デバイスに対応している。

**機能の利用シーン**：play_update.hspの更新ループ内で毎フレーム呼び出される。prepareLaserRelatedValues（レーザー関連値準備）の後にprocessLaserNoteJudgment（レーザーノート判定）が実行される。

**主要な処理内容**：
1. レーザー関連値の準備（analognow, canalognow, panalognow等の位置計算）
2. フィルタータイプ更新（filtertype）
3. チルト（傾き）計算と線形補間
4. L/R独立のアナログカーソル移動処理（6種類のデバイス対応）
5. スラム判定（analoganj配列、10ms蓄積閾値）
6. 区間判定（analogj配列、位置誤差100以内でCRITICAL）
7. 自動ERROR判定（100ms超過で未判定スラムをERROR）
8. オートプレイモード（analogmode=mode_auto）での自動判定
9. ゲージ増減処理

**関連システム・外部連携**：play_game_logic.hspのspecifyJudgmentTargetOfLaserNotes関数で判定対象レーザーノートが事前に特定される。play_utils.hspのaddgauge/subgauge関数でゲージ増減が処理される。レーザー位置はオーディオエフェクト（Peaking filter等）のパラメータ制御にも使用される。

**権限による制御**：レーザーモード（analogmode: ON/OFF/HIDE/AUTO）により判定処理が変化する。effratetype（ゲージタイプ）やiscourse（コースモード）によりゲージ処理が異なる。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 5 | ゲームプレイ画面 | 主画面 | レーザーノートのリアルタイム判定処理 |

## 機能種別

計算処理 / リアルタイム判定

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| analog[n][9] | int配列 | Yes | アナログノートデータ（0:L/R, 1:開始拍, 2:長さ, 3:始点位置, 4:終点位置, 5:始点ノート, 6:開始時刻ms, 7:終了時刻ms, 8:スラムフラグ） | 譜面読み込み時に展開済み |
| analoganj[n][6] | int配列 | Yes | スラム判定ポイント（0:L/R, 1:ノート番号, 2:判定済みフラグ, 5:重み） | 譜面読み込み時に展開済み |
| analogj[n][5] | int配列 | Yes | 区間判定ポイント（0:L/R, 2:判定済みフラグ, 3:重み, 4:判定時刻） | 譜面読み込み時に展開済み |
| arstat[8][3] | int配列 | Yes | 判定対象ノート情報（arstat.6/7がL/R） | specifyJudgmentTargetOfLaserNotesで設定済み |
| analogpos[2] | int配列 | Yes | L/Rカーソル位置（0-1000） | 0-1000の範囲にクランプ |
| analoginput | int | Yes | 入力デバイス種別 | 0:KB, 1:PowerMate, 2:Mouse, 3:Stick, 4:Slider, 5:Z-Axis |
| analogmode | int | Yes | レーザーモード | ON/OFF/HIDE/AUTO |
| t | int | Yes | 現在の再生時間(ms) | - |
| analogdelay | int | Yes | アナログ判定遅延値(ms) | - |
| keystator[10] | int配列 | Yes | キー入力状態（キーボード入力時） | 0または1 |

### 入力データソース

キーボード/マウス/ジョイスティック/PowerMate等のアナログ入力デバイス、譜面データ配列

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| analognow[2] | int配列 | 判定用アナログ位置（L/R） |
| canalognow[2] | int配列 | カーソル表示用アナログ位置（L/R） |
| panalognow[2] | int配列 | エフェクト用アナログ位置（バッファリング付き） |
| analogfuture[2] | int配列 | 30ms先のアナログ位置（補正用） |
| analogpos[2] | int配列 | プレイヤーカーソル位置（L/R, 0-1000） |
| chainnow | int | 現在のチェーン数 |
| chainmax | int | 最大チェーン数 |
| critical | int | CRITICAL数 |
| errorn | int | ERROR数 |
| analogscore | int | レーザーノートスコア加算値 |
| gauge | int | ゲージ値 |
| tiltnow | int | 現在のチルト値 |
| ror | double | レーンの傾き角度 |

### 出力先

ゲーム内部変数（スコア計算・描画・エフェクト制御に使用）

## 処理フロー

### 処理シーケンス

```
1. prepareLaserRelatedValues（レーザー関連値準備）
   ├─ フィルタータイプ更新（filtertype, filtertype2）
   ├─ アナログ位置計算
   │   ├─ analognow: 判定用位置（analogdelay遅延考慮）
   │   ├─ canalognow: カーソル表示用位置
   │   ├─ panalognow: エフェクト用位置（バッファリング付き）
   │   └─ analogfuture: 30ms先の位置（補正判断用）
   ├─ ターン（方向転換）時刻の取得
   ├─ チルト値計算と線形補間
   └─ 傾き(ror)の算出と平滑化

2. processLaserNoteJudgment（レーザーノート判定）
   ├─ アナログデバイス終了処理（anaendt設定）
   ├─ 方向転換補正（anaturnt更新）
   ├─ NG判定補正（analogngcnt, analogokt等）
   ├─ 位置許容度計算（stat1: 入力デバイス・状況に応じた動的閾値）
   ├─ カーソル移動処理
   │   ├─ マウス/スライダー/スティック/Z軸入力（analoginput=2,3,4,5）
   │   │   ├─ デバイス固有の入力値取得
   │   │   ├─ カーソル位置更新（analogpos）
   │   │   └─ スラム判定（analoganj: 10ms蓄積で成功）
   │   └─ キーボード入力（analoginput=0）
   │       ├─ L(keystator.6/7), R(keystator.8/9)で左右操作
   │       ├─ 方向一致時にカーソル移動（速度: 600/1000 per tp）
   │       └─ スラム判定
   ├─ カーソル範囲クランプ（0-1000）
   ├─ 自動スラム判定（analogmode=mode_auto）
   ├─ スラムERROR判定（100ms超過）
   ├─ 区間判定（analogj配列）
   │   ├─ 位置誤差<100: CRITICAL
   │   ├─ 前フレーム位置考慮: CRITICAL（猶予あり）
   │   └─ それ以外: ERROR
   └─ OFFモード時のスピン自動処理
```

### フローチャート

```mermaid
flowchart TD
    A[processLaserNoteJudgment] --> B[アナログデバイス終了処理]
    B --> C[方向転換・NG判定補正]
    C --> D{analogmode=mode_on?}
    D -->|Yes| E{入力デバイス判別}
    E -->|Mouse/Slider/Stick/Z| F[デバイス固有入力処理]
    E -->|Keyboard| G[キーボード入力処理]
    F --> H[カーソル位置更新]
    G --> H
    H --> I[スラム判定: analoganj]
    I --> J{10ms蓄積?}
    J -->|Yes| K[CRITICAL: chain++, analogscore++]
    J -->|No| L[蓄積継続]
    D -->|No| M{mode_auto?}
    M -->|Yes| N[自動判定]
    N --> O[区間判定: analogj]
    K --> O
    L --> O
    O --> P{位置誤差<100?}
    P -->|Yes| Q[CRITICAL: addgauge]
    P -->|No| R{前フレーム補正?}
    R -->|Yes| Q
    R -->|No| S[ERROR: chainnow=0, subgauge]
    M -->|No| T[OFFモード処理]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-30-01 | 位置許容度 | カーソル位置とノート位置の差が100未満でCRITICAL | 区間判定時 |
| BR-30-02 | スラム蓄積閾値 | analoganjdamageが10ms以上蓄積でスラムCRITICAL | スラム判定時 |
| BR-30-03 | スラム判定窓 | ノート時刻との差が±60ms以内 | スラム判定時 |
| BR-30-04 | スラム自動ERROR | 100ms超過で未判定スラムをERROR | 全スラムノート |
| BR-30-05 | カーソル範囲 | analogposは0-1000の範囲にクランプ | 常時 |
| BR-30-06 | デバイス終了猶予 | アナログ区間終了時に150ms（マウス/スライダー時は350ms）の猶予 | デバイス終了時 |
| BR-30-07 | 前フレーム補正 | 現フレームでERROR条件でも前フレームで位置一致なら救済 | 区間判定時 |
| BR-30-08 | ゲージ条件 | HARDゲージ(effratetype<=0)かつ非コースモードかつゲージ残存時のみ判定処理 | 常時 |
| BR-30-09 | 自動判定 | analogmode=mode_autoでスラム・区間共に自動CRITICAL | オートプレイ時 |
| BR-30-10 | OFFモード | analogmode=mode_offではスピン演出のみ自動処理 | OFFモード時 |

### 計算ロジック

位置許容度（区間判定）:
```
abs(analogpos - analognow) < 100 + abs(analognow - analogfuture) / 24 * (analogfuture != -1)
```
- 基本許容度: 100（x20座標系、実座標で5.0相当）
- analogfutureが存在する場合、未来位置との差に応じて許容度が拡大

スラム判定成功条件（マウス/スライダー/スティック/Z軸入力時）:
```
analoganjdamage >= 10  (10ms蓄積)
かつ abs(analog.time - (t - analogdelay)) <= 60  (判定窓±60ms)
かつ 入力方向がスラム方向と一致
かつ カーソル位置がスラム目標に到達
```

キーボード入力時のカーソル速度:
- 通常移動: (t_tp) * 600 / 1000 per フレーム
- 反対方向の大きいずれ(>50): (t_tp) * 1350 / 1000 per フレーム
- 反対方向の小さいずれ(>10): (t_tp) * 400 / 1000 per フレーム

ゲージ減少（スラムERROR時）:
- `ngauge -= gaugemax * 2/100 * (10 + (weight-1)*5) / 10`
- `subgauge 4.0 * (1.0 + (weight-1)/2)`

ゲージ減少（区間ERROR時）:
- `ngauge -= gaugemax / 200 * (10 + (weight-1)*5) / 10`
- `subgauge 1.0 + (weight-1)/2`

## データベース操作仕様

本機能ではデータベース操作は発生しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------||
| - | スラムERROR | 100ms超過で未判定スラム | chainnow=0, errorn++, subgauge |
| - | 区間ERROR | カーソル位置が許容範囲外 | chainnow=0, errorn++, subgauge |
| - | ゲージ枯渇 | HARDゲージ使用時にgauge<=0 | endfl=1でゲーム終了フラグを設定 |

### リトライ仕様

リトライは行わない。

## トランザクション仕様

トランザクション管理は不要。

## パフォーマンス要件

毎フレーム（16ms間隔）で呼び出されるため、1フレーム以内に処理を完了する必要がある。prepareLaserRelatedValuesとprocessLaserNoteJudgmentの2つの関数が順番に実行される。

## セキュリティ考慮事項

特になし。

## 備考

- レーザーノートの判定はBT/FXノートと異なり、タイミング判定ではなく位置追従判定である。CRITICAL/ERRORの2段階判定（NEARは存在しない）。
- analognow（判定用位置）はanalogdelay分遅延した時刻で計算される。canalognow（カーソル表示用）は遅延なしの現在時刻で計算される。panalognow（エフェクト用）はttp2バッファリングによるタイミング補正付きで計算される。
- スラムノート（analog.n.2 <= UNIT_MEASURE_32 かつ analog.n.8=0）は急速な位置変化で、通常のアナログノートとは別の判定ロジック（蓄積方式）が適用される。
- レーザーノートのスコアはanalogscore変数に加算される（BTはnscore、FXはlscore）。最終スコア計算時に合算される。
- 6種類のアナログ入力デバイス: 0=キーボード、1=PowerMate、2=マウス、3=アナログスティック、4=スライダー、5=Z軸。各デバイスで判定窓や補正パラメータが異なる。

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: 判定対象レーザーノートの特定

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | play_game_logic.hsp | `src/scene/play/play_game_logic.hsp` | specifyJudgmentTargetOfLaserNotes関数 |

**主要処理フロー**:
1. **166行目**: specifyJudgmentTargetOfLaserNotes関数定義
2. **167行目**: analognum全体をループし判定対象を探索
3. **168行目**: 2100ms先までのノートを探索範囲とする
4. **174-179行目**: arstat[6]/arstat[7]にL/Rの判定対象ノート情報を設定（存在フラグ、ノート番号、始点ノートフラグ）

#### Step 2: レーザー関連値の準備

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | play_laser_note.hsp | `src/scene/play/play_laser_note.hsp` | prepareLaserRelatedValues関数 |

**主要処理フロー**:
1. **1行目**: prepareLaserRelatedValues関数定義
2. **2-19行目**: フィルタータイプ更新（filtertype: laserdelay0遅延付き、filtertype2: 遅延なし）
3. **21-28行目**: 各種アナログ位置変数の初期化（analognow, panalognow, canalognow, analogfuture等）
4. **30-82行目**: アナログノートループ - 各位置変数の計算
   - **32-35行目**: analogfuture計算（30ms先の位置）
   - **36-44行目**: panalognow計算（バッファリング付きエフェクト用位置）
   - **46-64行目**: canalognow計算（カーソル表示用位置）とro（傾き）計算
   - **74-81行目**: analognow計算（判定用位置、analogdelay遅延付き）
5. **88-97行目**: ターン（方向転換）時刻の取得
6. **100-150行目**: チルト計算（tiltnow, tiltrnow）と傾きキープ処理
7. **128-140行目**: 傾きの線形補間（speed制御による平滑化）

#### Step 3: レーザーノート判定処理

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | play_laser_note.hsp | `src/scene/play/play_laser_note.hsp` | processLaserNoteJudgment関数 |

**主要処理フロー**:
1. **158行目**: processLaserNoteJudgment関数定義
2. **160-170行目**: アナログデバイス終了処理（anaendt設定: 150ms猶予、マウス/スライダー時は350ms）
3. **172-177行目**: 方向転換補正（anaturnt更新、analogokt加算）
4. **178-204行目**: NG判定補正とOK判定許容度計算（stat1: 動的閾値）
5. **207-551行目**: デバイス別カーソル移動処理
   - **209-338行目**: マウス/スライダー/スティック/Z軸入力
   - **345-528行目**: キーボード入力（L: keystator.6/7、R: keystator.8/9）
6. **529-540行目**: カーソル範囲クランプ（0-1000）とノート開始時の初期位置設定
7. **552-582行目**: スラム自動判定（mode_auto）とスラム自動ERROR（100ms超過）
8. **590-633行目**: 区間判定（analogj配列）
   - **593行目**: 位置誤差<100でCRITICAL
   - **606行目**: 前フレーム補正によるCRITICAL救済
   - **618-627行目**: ERROR判定（chainnow=0, subgauge）
9. **637-647行目**: OFFモード時のスピン自動処理

#### Step 4: ゲージ増減処理

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | play_utils.hsp | `src/scene/play/play_utils.hsp` | addgauge/subgauge関数 |

**主要処理フロー**:
- **292-334行目**: addgauge関数 - ゲージタイプ別の増加処理
- **336-364行目**: subgauge関数 - ゲージタイプ別の減少処理

### プログラム呼び出し階層図

```
play_update (play_update.hsp)
    |
    +-- specifyJudgmentTargetOfLaserNotes (play_game_logic.hsp 166行目)
    |     +-- arstat[6]/arstat[7]にL/R判定対象ノート情報を設定
    |
    +-- prepareLaserRelatedValues (play_laser_note.hsp 1行目)
    |     +-- filtertype更新 (2-19行目)
    |     +-- analognow/canalognow/panalognow/analogfuture計算 (21-82行目)
    |     +-- ターン時刻取得 (88-97行目)
    |     +-- チルト計算 (100-150行目)
    |     +-- 傾き補間 (128-140行目)
    |
    +-- processLaserNoteJudgment (play_laser_note.hsp 158行目)
          +-- アナログデバイス終了処理 (160-170行目)
          +-- 方向転換・NG補正 (172-204行目)
          +-- デバイス別カーソル移動 (207-551行目)
          |     +-- Mouse/Slider/Stick/Z入力 (209-338行目)
          |     |     +-- スラム判定: analoganj蓄積>=10 (307-336行目)
          |     +-- Keyboard入力 (345-528行目)
          |           +-- L操作: keystator.6(左)/7(右) (347-435行目)
          |           +-- R操作: keystator.8(左)/9(右) (438-527行目)
          |           +-- スラム判定: analoganj (369-435行目)
          +-- スラム自動判定/ERROR (552-582行目)
          |     +-- mode_auto: 自動CRITICAL (554-570行目)
          |     +-- 100ms超過: 自動ERROR (571-581行目)
          +-- 区間判定 (590-633行目)
          |     +-- 位置誤差<100: CRITICAL + addgauge (593-604行目)
          |     +-- 前フレーム補正: CRITICAL + addgauge (606-617行目)
          |     +-- ERROR: chainnow=0, subgauge (618-627行目)
          +-- OFFモード スピン処理 (637-647行目)
```

### データフロー図

```
[入力]                       [処理]                              [出力]

アナログ入力 ---------> processLaserNoteJudgment
(KB/Mouse/Stick等)          |
                      デバイス別入力処理
                            |
arstat[6,7] ----------> カーソル位置更新(analogpos)
                            |
analog[] -------------> 位置計算
                      +-- analognow (判定用)
                      +-- canalognow (表示用)
                      +-- panalognow (エフェクト用)
                            |
                      スラム判定(analoganj)
                      +-- 蓄積>=10ms -------> CRITICAL
                      +-- 100ms超過 --------> ERROR
                            |
                      区間判定(analogj)
                      +-- 誤差<100 ---------> CRITICAL
                      +-- 前フレーム補正 ----> CRITICAL
                      +-- それ以外 ---------> ERROR
                            |
                      スコア・ゲージ更新 --> analogscore, gauge, chainnow
                            |
prepareLaserRelatedValues
                      +-- filtertype -------> エフェクト制御
                      +-- ror, tiltnow -----> レーン傾き描画
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| play_laser_note.hsp | `src/scene/play/play_laser_note.hsp` | ソース | レーザーノート判定メイン処理（1-648行目） |
| play_game_logic.hsp | `src/scene/play/play_game_logic.hsp` | ソース | 判定対象レーザーノート特定（166-184行目） |
| play_utils.hsp | `src/scene/play/play_utils.hsp` | ソース | addgauge/subgauge関数（292-364行目） |
| play_update.hsp | `src/scene/play/play_update.hsp` | ソース | 呼び出し元（60行目, 112行目, 115行目） |
| play_splash_screen.hsp | `src/scene/play/play_splash_screen.hsp` | ソース | デバイス初期化・キャリブレーション |
| option.hsp | `src/scene/option.hsp` | ソース | analoginput設定変更（271-280行目, 422-431行目） |
