# 機能設計書 63-タイミング手動調整

## 概要

本ドキュメントは、K-Shoot MANIA のプレイ画面における判定タイミングの手動調整機能の設計を記述する。プレイ中に Ctrl+Alt+左右矢印キーを操作することで、判定のオフセット値（sync 変数）を変更し、音楽と譜面のズレを補正する。

### 本機能の処理概要

**業務上の目的・背景**：オーディオ出力のレイテンシはハードウェア環境によって異なるため、プレイヤーごとに判定タイミングの微調整が必要となる。本機能により、プレイ中にリアルタイムで判定オフセットを調整し、音楽と譜面の同期精度を向上させることができる。

**機能の利用シーン**：ゲームプレイ画面（SCENE_PLAY）において、プレイヤーが判定タイミングにズレを感じた際に Ctrl+Alt+左右矢印キーを押下する。Shift キーの有無で調整幅が変わる（Shift なし: 5ms、Shift あり: 1ms）。プレイ終了時に調整値を譜面ファイルに保存するかどうかをダイアログで確認できる。

**主要な処理内容**：
1. Ctrl+Alt+右矢印キーの押下検出で sync 値を増加（タイミングを遅らせる方向）
2. Ctrl+Alt+左矢印キーの押下検出で sync 値を減少（タイミングを早める方向）
3. Shift キーの追加押下で微調整モード（1ms刻み）に切替
4. 調整値はプレイ画面のUIに表示
5. プレイ終了後に sync!=0 の場合、譜面ファイルの offset 値への保存ダイアログを表示

**関連システム・外部連携**：譜面ファイル（.ksh）の `o=` パラメータへの書き込み。

**権限による制御**：特にない。全プレイヤーが同一の操作で利用可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 5 | ゲームプレイ画面 | 主画面 | processSyncValueManualChangeInput による Ctrl+Alt+矢印キーでのタイミング手動調整 |

## 機能種別

入力制御 / パラメータ調整

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| ctrl_key | int | Yes | Ctrl キー状態（0/1） | 仮想キーコード 17 |
| alt_key | int | Yes | Alt キー状態（0/1） | 仮想キーコード 18 |
| shift_key | int | Yes | Shift キー状態（0/1） | 仮想キーコード 16 |
| 右矢印キー | int | Yes | 右矢印キー状態（getkey_a で連射対応） | 仮想キーコード 39 |
| 左矢印キー | int | Yes | 左矢印キー状態 | 仮想キーコード 37 |
| ts | int | Yes | 入力間隔タイマー | ts>120 の間隔制限 |

### 入力データソース

キーボード入力（getkey / getkey_a 関数）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| sync | double | 判定タイミングのオフセット値（ms単位、正=遅延、負=早め） |

### 出力先

内部変数（sync）。プレイ終了時に譜面ファイルの `o=` パラメータに反映可能。

## 処理フロー

### 処理シーケンス

```
1. ウィンドウアクティブ判定 (ginfo2()=0)
   └─ 非アクティブ時は処理をスキップ
2. Shift/Ctrl/Alt キーの状態取得
3. 右矢印キーの検出（getkey_a: 連射対応）
4. sync 増加判定
   4-1. Ctrl+Alt+Shift+右矢印 (4キー全押し) AND ts>120 → sync += 1.0
   4-2. Ctrl+Alt+右矢印 (Shift なし) AND ts>120 → sync += 5.0
5. 左矢印キーの検出
6. sync 減少判定
   6-1. Ctrl+Alt+Shift+左矢印 (4キー全押し) AND ts>120 → sync -= 1.0
   6-2. Ctrl+Alt+左矢印 (Shift なし) AND ts>120 → sync -= 5.0
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B{ウィンドウアクティブ?}
    B -->|No| Z[return]
    B -->|Yes| C[Shift/Ctrl/Alt キー取得]
    C --> D[右矢印キー取得]
    D --> E{Ctrl+Alt+Shift+右=4?}
    E -->|Yes| F{ts>120?}
    F -->|Yes| G[sync += 1.0]
    F -->|No| H{Ctrl+Alt+右=3?}
    E -->|No| H
    H -->|Yes| I{ts>120?}
    I -->|Yes| J[sync += 5.0]
    I -->|No| K[左矢印キー取得]
    H -->|No| K
    G --> K
    J --> K
    K --> L{Ctrl+Alt+Shift+左=4?}
    L -->|Yes| M{ts>120?}
    M -->|Yes| N[sync -= 1.0]
    M -->|No| O{Ctrl+Alt+左=3?}
    L -->|No| O
    O -->|Yes| P{ts>120?}
    P -->|Yes| Q[sync -= 5.0]
    P -->|No| Z
    O -->|No| Z
    N --> Z
    Q --> Z
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-63-1 | 修飾キー必須 | Ctrl+Alt の同時押下が必須 | 常時 |
| BR-63-2 | 微調整モード | Shift を追加すると 1ms 刻みに変化 | Shift + Ctrl + Alt 同時押下 |
| BR-63-3 | 通常調整モード | Shift なしでは 5ms 刻み | Ctrl + Alt 同時押下 |
| BR-63-4 | 入力間隔制限 | ts>120ms の間隔制限あり | 常時 |
| BR-63-5 | 保存確認 | sync!=0 の場合、プレイ終了時に保存ダイアログ表示 | プレイ終了時 |
| BR-63-6 | 保存先 | 譜面ファイルの `o=` パラメータに `offset - int(sync)` を書き込む | 保存選択時 |

### 計算ロジック

保存時の計算: `新しいoffset = offset - int(sync)`

sync の符号: 正の値は判定を遅らせる方向、負の値は早める方向。UI 表示時は `-sync` として表示され、正の値が「+」付きで表示される。

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

### 操作別データベース影響一覧

| 操作 | 対象ファイル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 保存 | 譜面ファイル (.ksh) | UPDATE | `o=` パラメータの値を更新 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | ファイル書き込み失敗 | 譜面ファイルが読み取り専用 | HSP の noteload/notesave のエラーハンドリングに委任 |

### リトライ仕様

該当なし。

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

該当なし。譜面ファイルへの書き込みは単純なファイルI/O。

## パフォーマンス要件

毎フレーム呼ばれるが、修飾キーが押されていない場合は即座にリターンするため負荷は極小。

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

該当なし。

## 備考

- sync 値の範囲制限は設けられていない（理論上無限に増減可能）
- プレイ中の sync 値は画面左下に表示される（play_draw_frame.hsp 内の描画処理）
- 保存ダイアログはプレイ終了時の processEscKeyInput 関数内で表示される（176-236行目）
- コマンドライン起動時（iscmdline=0 以外）は保存先のパスが異なる

---

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

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

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | play_key_input.hsp | `src/scene/play/play_key_input.hsp` | sync 変数: double 型の判定オフセット値。正の値で判定を遅延方向にシフト |
| 1-2 | play_key_input.hsp | `src/scene/play/play_key_input.hsp` | offset 変数: 譜面ファイルの `o=` パラメータから読み込まれた初期オフセット値 |

**読解のコツ**: sync 変数は double 型で宣言されており、1.0 / 5.0 という浮動小数点数で加減算される。最終的に int(sync) に変換されてoffsetに適用される。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | play_key_input.hsp | `src/scene/play/play_key_input.hsp` | `processSyncValueManualChangeInput` 関数（91行目から定義）がエントリーポイント |

**主要処理フロー**:
1. **92行目**: `ginfo2()=0` でウィンドウアクティブ判定
2. **93-95行目**: Shift/Ctrl/Alt キーの状態取得
3. **96行目**: 右矢印キー取得（getkey_a で連射対応）
4. **97-98行目**: Ctrl+Alt+Shift+右矢印 → `sync += 1.0`
5. **99-100行目**: Ctrl+Alt+右矢印（Shift なし） → `sync += 5.0`
6. **102行目**: 左矢印キー取得
7. **103-104行目**: Ctrl+Alt+Shift+左矢印 → `sync -= 1.0`
8. **105-106行目**: Ctrl+Alt+左矢印（Shift なし） → `sync -= 5.0`

#### Step 3: 保存処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | play_key_input.hsp | `src/scene/play/play_key_input.hsp` | **176-236行目**: processEscKeyInput 内の保存ダイアログ処理。`int(sync)!=0` の場合に表示 |
| 3-2 | play_key_input.hsp | `src/scene/play/play_key_input.hsp` | **231行目**: `noteadd "o="+str(offset-int(sync))` で譜面ファイルに新しい offset 値を書き込み |

#### Step 4: UI表示を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | play_draw_frame.hsp | `src/scene/play/play_draw_frame.hsp` | **1057-1068行目**: sync値のUI表示処理。`-sync` の絶対値を数値画像で表示し、符号を±で表示 |

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

```
play_update (メインループ)
    |
    +-- processSyncValueManualChangeInput
    |       |
    |       +-- getkey (Shift/Ctrl/Alt)
    |       +-- getkey_a (右矢印)
    |       +-- getkey (左矢印)
    |       +-- sync += 1.0 / 5.0 / -1.0 / -5.0
    |
    +-- drawFrame
    |       |
    |       +-- sync値のUI表示（1057-1068行目）
    |
    +-- processEscKeyInput (プレイ終了時)
            |
            +-- 保存ダイアログ表示（176-236行目）
            +-- noteload / noteadd / notesave（譜面ファイル更新）
```

### データフロー図

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

キーボード入力         processSyncValueManualChangeInput
  Ctrl+Alt+←/→  ───>  ┌──────────────────────┐
  (+Shift で微調整)     |  sync 値の加減算      |      sync (ms)
                        |  ±1.0 / ±5.0         |  ──> UI表示
                        └──────────────────────┘      (play_draw_frame)

                       processEscKeyInput
                        ┌──────────────────────┐
  sync != 0     ───>   |  保存ダイアログ       |
                        |  offset - int(sync)   |  ──> 譜面ファイル (.ksh)
                        |  noteadd "o=..."      |      o= パラメータ更新
                        └──────────────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| play_key_input.hsp | `src/scene/play/play_key_input.hsp` | ソース | processSyncValueManualChangeInput 関数（91-109行目）、保存ダイアログ（176-236行目） |
| play_draw_frame.hsp | `src/scene/play/play_draw_frame.hsp` | ソース | sync 値の UI 表示（1057-1068行目） |
| play_game_logic.hsp | `src/scene/play/play_game_logic.hsp` | ソース | offset 変数の初期化 |
| play_chart_load.hsp | `src/scene/play/play_chart_load.hsp` | ソース | 譜面ファイルから `o=` パラメータの読み込み |
