# 機能設計書 91-キーバインド設定

## 概要

本ドキュメントは、K-Shoot MANIAのオプション設定画面（ページ4: キーバインド設定）において、BT/FX/レーザー/Start/Backの各キーをキーボードおよびジョイスティックに割り当てる機能の設計を記述する。

### 本機能の処理概要

本機能は、ゲームプレイに使用するBTボタン4つ、FXボタン2つ、レーザーL/R、Startキー、Backキーの各操作入力を、キーボードの任意のキーまたはジョイスティックの任意のボタンに割り当てる機能である。

**業務上の目的・背景**：K-Shoot MANIAは音楽ゲームであり、プレイヤーごとに異なる入力デバイスや好みのキー配置が存在する。専用コントローラー(SVSE5等)やキーボード、ジョイスティックなど多様なデバイスに対応するため、キーバインドのカスタマイズが不可欠である。キーボード2セット(key/key2)およびジョイスティック2台(joykey.0/joykey.1)の合計4デバイス分のバインド設定をサポートする。

**機能の利用シーン**：オプション設定画面の第4ページ(pagenow=3)で利用される。プレイヤーが初回起動時にキーバインドを設定する場合や、デバイスを変更した際にキー配置を調整する場合に使用される。

**主要な処理内容**：
1. オプション画面のキーバインドページ表示(pagenow=3)
2. 対象キーの選択(selnow変数による選択状態管理)
3. Enterキーでバインド待ち状態(selnow+=100)に遷移
4. キーボード入力(confkey)またはジョイスティックボタン入力の検出
5. 検出されたキーコードをkey/key2/joykey配列に格納
6. キー重複検出(keyovl/keyovl2/joykeyovl配列)
7. Spaceキーによるキーバインド解除(値を-1に設定)
8. 設定値のconfig.iniへの保存

**関連システム・外部連携**：config.iniファイルへの読み書きを通じて設定値を永続化する。Win32 APIのonkeyイベント(confkey変数)を利用してキーボードのキーコードを取得する。

**権限による制御**：特になし。全プレイヤーが同一のキーバインド設定画面を利用できる。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 8 | オプション設定画面 | 主画面 | pagenow=3でのBT/FX/レーザー/Start/Backの各キーをキーボード・ジョイスティックに割り当て |

## 機能種別

設定変更 / ユーザーインタラクション

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| confkey | int | Yes | onkeyイベントで取得されたキーコード(wparam) | Enter(13)・方向キー(37-40)は除外される |
| joystat | int配列 | No | ジョイスティックボタン状態(前フレームとの差分で検出) | ボタン番号0-31の範囲 |
| ckey | int | Yes | 現在のデバイス選択(0:キーボード1, 1:キーボード2, 2:ジョイスティック1, 3:ジョイスティック2) | 0-3の範囲 |
| selnow | int | Yes | 現在の選択位置(0-11:通常選択, 100+:バインド待ち, 200+:レーザー2つ目バインド待ち) | 範囲チェックあり |

### 入力データソース

- キーボード入力: onkeyイベント経由でconfkey変数に格納(option.hsp 59行目)
- ジョイスティック入力: joystat配列を毎フレームポーリング(getjoystatサブルーチン)
- デバイス選択: ckey変数(0-3)を左右キーで切替(option.hsp 325-326, 476行目)

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| key | int配列(14要素) | キーボード1のキーバインド設定 |
| key2 | int配列(14要素) | キーボード2のキーバインド設定 |
| joykey | int配列(2x14要素) | ジョイスティック1/2のキーバインド設定 |

### 出力先

- メモリ上: key/key2/joykey配列
- ファイル: config.iniに「key=」「key2=」「joy=」「joy2=」形式で保存

## 処理フロー

### 処理シーケンス

```
1. オプション画面でpagenow=3(キーバインドページ)を表示
   └─ BT(0-3)/FX(4-5)/レーザーL/R(6-7)/スタートボタン(8)/START(10)/BACK(11)を表示
2. 上下左右キーでselnowを移動し、対象のキーを選択
   └─ pagenow=3時は特殊なナビゲーション(ボタン配置に合わせた移動)
3. Enterキーで選択中のキーをバインド待ち状態(selnow+=100)にする
   └─ 画面上で黄色枠に変化
4. バインド待ち状態でキーボード/ジョイスティックの入力を検出
   └─ キーボード: onkeyイベントのconfkey値を使用
   └─ ジョイスティック: joystat配列の前フレーム差分を使用
5. 検出されたキーコードをkey/key2/joykey配列に格納
   └─ レーザーの場合は2段階(selnow→selnow+100→selnow+200)
6. キー重複チェック(keyovl/keyovl2/joykeyovl)を実行
   └─ 重複キーは赤色太字で表示
7. Spaceキーでバインドを解除(-1に設定)
   └─ option.hsp 79-118行目
8. Escキーでオプションメニューに戻り、*saveconfigでconfig.iniに保存
```

### フローチャート

```mermaid
flowchart TD
    A[キーバインド画面表示] --> B{selnow < 100?}
    B -->|Yes: 通常選択状態| C[上下左右でキー選択]
    C --> D{Enterキー?}
    D -->|Yes| E[selnow += 100: バインド待ち]
    D -->|No| F{Spaceキー?}
    F -->|Yes| G[選択キーのバインド解除: -1]
    F -->|No| C
    B -->|No: バインド待ち状態| H{キーボード入力?}
    H -->|Yes| I[confkeyの値をkey/key2配列に格納]
    H -->|No| J{ジョイスティック入力?}
    J -->|Yes| K[ボタン番号をjoykey配列に格納]
    J -->|No| H
    I --> L{レーザーの2つ目?}
    K --> L
    L -->|Yes: selnow < 200| M[selnow += 100: 2つ目待ち]
    L -->|No| N[selnow -= 100: 通常状態に戻る]
    M --> H
    N --> O[キー重複チェック]
    O --> C
    G --> C
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-91-01 | 除外キー | Enter(13)および方向キー(37-40)はバインド対象外 | バインド待ち状態でのキーコード検出時 |
| BR-91-02 | IMEキー変換 | confkey=229/243/244(IME関連)の場合、キーコード28(変換)に置換 | ckey<2(キーボード)の場合 |
| BR-91-03 | START/BACKキー固定 | key.11(Enter)、key.12(Esc)、key.13(F11)はキーボード1では常に固定 | config読み込み後(kshootmania.hsp 710-712行目) |
| BR-91-04 | レーザー2段バインド | レーザーL/R(selnow=6,7)はL/Rそれぞれに2つのキー(6+cnt*2, 7+cnt*2)を割り当て | レーザー選択時 |
| BR-91-05 | キー重複警告 | 同一デバイスまたは異なるデバイス間で同一キーコードが割り当てられた場合、赤色表示で警告 | 全キーに対して毎フレームチェック |
| BR-91-06 | STARTキー制限 | キーボード2(ckey=1)のSTARTキーではEnter(13)/Space(32)/F11(122)は割り当て不可 | selnow=110-112でckey=1の場合 |

### 計算ロジック

- キーバインド配列のインデックス: BT-A=0, BT-B=1, BT-C=2, BT-D=3, FX-L=4, FX-R=5, レーザーL左=6, レーザーL右=7, レーザーR左=8, レーザーR右=9, FXボタン=10, START=11, BACK=12, F11=13
- ジョイスティックのボタン識別: デバイスID*32+ボタン番号

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

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

本機能ではデータベースは使用しない。設定はconfig.iniファイルに保存される。

| 操作 | 対象ファイル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 設定読み込み | config.ini | READ | 起動時にkey/key2/joy/joy2の行を解析 |
| 設定保存 | config.ini | WRITE | オプション画面終了時にキーバインドを保存 |

### テーブル別操作詳細

#### config.ini キーバインド関連エントリ

| 操作 | 項目 | 値の形式 | 備考 |
|-----|------|---------|------|
| READ/WRITE | key | カンマ区切り14要素の整数 | キーボード1のキーバインド |
| READ/WRITE | key2 | カンマ区切り14要素の整数 | キーボード2のキーバインド |
| READ/WRITE | joy | カンマ区切り14要素の整数 | ジョイスティック1のキーバインド |
| READ/WRITE | joy2 | カンマ区切り14要素の整数 | ジョイスティック2のキーバインド |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| N/A | キー重複 | 同一キーコードが複数のバインドに割り当てられた場合 | 赤色太字表示で視覚的に警告（動作は許容） |
| N/A | 未割当 | key値が-1の場合 | 「x」記号を表示し、未割当を示す |

### リトライ仕様

特になし。

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

ファイルベースの設定保存であり、トランザクション管理は行われない。オプション画面終了時に*saveconfigサブルーチンで一括保存する。

## パフォーマンス要件

特になし。オプション画面はリアルタイム性の要求が低い。

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

特になし。ローカルファイル(config.ini)への読み書きのみ。

## 備考

- キーボード1のkey.11(START=Enter)、key.12(BACK=Esc)、key.13(F11)は固定値として上書きされるため、config.iniの値に関わらず変更不可
- デフォルトキーバインド(キーボード1): S,D,K,L,変換,無変換,Q,W,O,P,Space,Enter,Esc,F11
- デフォルトキーバインド(キーボード2): H,J,F,G,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
- getkeylabel/getkeylabel2関数でキーコードを表示用ラベルに変換(option.hsp 1209-1386行目)

---

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

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

### 推奨読解順序

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

キーバインド設定で使用される配列変数の構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | kshootmania.hsp | `kshootmania.hsp` | key配列(14要素)のインデックス定義(684-712行目): 0-3=BT, 4-5=FX, 6-9=レーザー, 10=FXボタン, 11=START, 12=BACK, 13=F11 |
| 1-2 | kshootmania.hsp | `kshootmania.hsp` | key2配列(14要素)のデフォルト値(699-708行目) |
| 1-3 | kshootmania.hsp | `kshootmania.hsp` | joykey配列(2x14要素)の構造(713-736行目) |

**読解のコツ**: HSP3ではdim命令で整数配列を定義する。key配列の値はWin32仮想キーコードに対応する。-1は未割当を意味する。

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

オプション画面の更新処理がエントリーポイントとなる。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | option.hsp | `src/scene/option.hsp` | on_update_optionが毎フレーム呼び出されるメイン処理 |

**主要処理フロー**:
1. **1-63行目**: 初期化処理（画像読み込み、BGM再生開始）
2. **59行目**: `onkey gosub *confkeshu` でキーボードイベントハンドラ登録
3. **72-120行目**: Spaceキーによるキーバインド解除処理
4. **749-1059行目**: pagenow=3(キーバインドページ)の描画処理
5. **1167-1185行目**: Enterキーによるバインド待ち状態の切替

#### Step 3: キーバインド入力検出を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | option.hsp | `src/scene/option.hsp` | キーボード入力検出(751-781行目): confkey変数を使用したキーコード取得 |
| 3-2 | option.hsp | `src/scene/option.hsp` | ジョイスティック入力検出(783-807行目): joystat配列の前フレーム差分チェック |
| 3-3 | option.hsp | `src/scene/option.hsp` | キー重複チェック(808-843行目): keyovl/keyovl2/joykeyovl配列の構築 |

**主要処理フロー**:
- **751-753行目**: confkeyのバリデーション（Enter/方向キー除外、IMEキー変換）
- **757-761行目**: BT/FXキーのバインド処理(selnow<=105またはselnow=108)
- **762-767行目**: START/BACKキーのバインド処理(selnow>=110)
- **769-779行目**: レーザーキーの2段階バインド処理

#### Step 4: 設定の永続化を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | kshootmania.hsp | `kshootmania.hsp` | *saveconfig サブルーチン(1299-1500行目): config.iniへの保存処理 |
| 4-2 | kshootmania.hsp | `kshootmania.hsp` | config.ini読み込み(416-681行目): key/key2/joy/joy2の解析 |

**主要処理フロー**:
- **1368-1371行目**: key/key2配列をカンマ区切りで文字列化して保存
- **1440-1443行目**: joykey配列をカンマ区切りで文字列化して保存

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

```
kshootmania.hsp (メインループ)
    |
    +-- on_update_option (option.hsp)
    |       |
    |       +-- *confkeshu (kshootmania.hsp 1502行目)
    |       |       └── onkeyイベントハンドラ: confkey=wparam
    |       |
    |       +-- *getjoystat (ジョイスティック状態取得)
    |       |
    |       +-- キーバインド入力検出 (option.hsp 751-807行目)
    |       |       +-- キーボード入力 → key/key2配列更新
    |       |       └── ジョイスティック入力 → joykey配列更新
    |       |
    |       +-- キー重複チェック (option.hsp 808-843行目)
    |       |       +-- keyovl配列構築
    |       |       +-- keyovl2配列構築
    |       |       └── joykeyovl配列構築
    |       |
    |       +-- 描画処理 (option.hsp 844-1059行目)
    |       |       +-- BT/FXキー表示
    |       |       +-- レーザーキー表示
    |       |       +-- START/BACKキー表示
    |       |       └── getkeylabel() → キーコードをラベルに変換
    |       |
    |       └── Escキー → *saveconfig → config.ini保存
    |
    +-- *saveconfig (kshootmania.hsp 1299行目)
            └── config.iniにkey/key2/joy/joy2を保存
```

### データフロー図

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

キーボードイベント ──────> confkey変数 ──────> key/key2配列
(onkey)                                        |
                                               v
ジョイスティック ────────> joystat配列 ─────> joykey配列
(ポーリング)                                    |
                                               v
                                        重複チェック ──> keyovl/keyovl2/joykeyovl
                                               |
                                               v
                                        画面描画 ────> オプション画面表示
                                               |
                                               v
                                        *saveconfig ──> config.ini
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| option.hsp | `src/scene/option.hsp` | ソース | キーバインド設定のメインロジック・描画処理 |
| kshootmania.hsp | `kshootmania.hsp` | ソース | config.iniの読み込み・保存、key/key2/joykey配列の初期化 |
| ksmcore.as | `src/ksmcore.as` | ソース | StartsWith関数の定義（config.ini解析に使用） |
| config.ini | `config.ini` | 設定ファイル | キーバインド設定の永続化先 |
