# 通知設計書 5-お気に入り管理ダイアログ通知

## 概要

本ドキュメントは、K-Shoot MANIAの選曲画面における曲のお気に入りカテゴリ追加・削除時の確認ダイアログ通知の設計を記述する。dialognowフラグによる状態管理と、お気に入りファイル（.fav）の操作を伴うカスタムダイアログUIについて、トリガー条件、UI仕様、処理フローを網羅的に定義する。

### 本通知の処理概要

本通知は、選曲画面でプレイヤーが曲をお気に入りカテゴリに追加または削除する際に表示されるカスタムダイアログUIである。OS標準のdialog命令ではなく、ゲーム画面上に描画されるカスタムUI（Image 119/120/121）で実装されており、ゲームのコントローラー操作で確認・キャンセルが可能である。

**業務上の目的・背景**：K-Shoot MANIAは大量のユーザー作成譜面をサポートしているため、プレイヤーが好みの曲を素早くアクセスできるお気に入り機能が重要である。お気に入りの追加・削除はプレイヤーのライブラリ管理に直結する操作であるため、誤操作を防止する確認ダイアログを提供している。

**通知の送信タイミング**：選曲画面でスタートボタン（Enter/ジョイスティックボタン11）を750ms以上長押しした際に発動する。通常のカテゴリ外（dir先頭が"?"でない）ではお気に入り追加ダイアログ（dialognow=1）、お気に入りカテゴリ内（dir先頭が"?"）ではお気に入り削除ダイアログ（dialognow=2）が表示される。

**通知の受信者**：選曲画面を操作しているプレイヤー。

**通知内容の概要**：お気に入り追加時はFavoriteカテゴリの選択UI、削除時はYes/No確認UIを表示する。

**期待されるアクション**：追加時はFavoriteカテゴリ番号を上下キーで選択しEnterで確定、削除時は左右キーでYes/Noを選択しEnterで確定する。ESCキーでキャンセルも可能。

## 通知種別

アプリ内通知（カスタムダイアログUI、ゲーム画面上のオーバーレイ表示）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（ダイアログ表示中は選曲操作がブロックされる） |
| 優先度 | 中（ユーザー任意の操作） |
| リトライ | なし（確定後に即座に反映される） |

### 送信先決定ロジック

ダイアログはゲーム画面の中央にオーバーレイ表示される。背景はアルファブレンド（gmode 6, alpha=32）で半透明化される。

## 通知テンプレート

### メール通知の場合

本通知はゲーム内カスタムUIのため、メール関連項目は該当しない。

| 項目 | 内容 |
|-----|------|
| 送信元アドレス | N/A |
| 送信元名称 | N/A |
| 件名 | N/A |
| 形式 | カスタムダイアログUI（画像ベース） |

### 本文テンプレート

お気に入り追加ダイアログ（dialognow=1）:
- Image 119: ダイアログ背景画像（288x96px相当）
- Image 120: Favoriteカテゴリ番号選択UI（33x33px、favselnowの値で画像切り替え）
- 表示位置: 画面中央（scrsize_w/2-getsize(288)/2, scrsize_h/2-getsize(96)/2）

お気に入り削除ダイアログ（dialognow=2）:
- Image 121: 削除確認ダイアログ画像（288x96px相当、favselnow2でYes/No切り替え）
- 表示位置: 画面中央（scrsize_w/2-getsize(288)/2, scrsize_h/2-getsize(96)/2）

### 添付ファイル

| ファイル名 | 形式 | 条件 | 説明 |
|----------|------|------|------|
| N/A | N/A | N/A | カスタムUIのため添付ファイルなし |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| dialognow | ダイアログ状態（0=非表示, 1=追加確認, 2=削除確認） | ユーザー操作から設定 | Yes |
| favselnow | お気に入りカテゴリ番号（0-based） | 上下キー操作で変更 | Yes |
| favselnow2 | 削除確認の選択状態（0=No, 1=Yes） | 左右キー操作で変更 | Yes |
| favselnum | お気に入りカテゴリの総数（最大9） | Favorite{n}.favファイルの存在数 | Yes |
| minfostr.selnow.levm.0 | 現在選択中の曲のパス | 選曲リスト | Yes |
| dir | 現在のディレクトリパス | 選曲画面のナビゲーション状態 | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| 画面操作 | スタートボタン750ms長押し | dialognow=0, stat2ap=1（スタートボタン押下中）, stat2a=1, 曲データあり（minfo.selnow.levm.1>=1）, 選曲画面がアクティブ | select.hsp:1735-1751 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| dialognow!=0 | 既にダイアログが表示されている場合 |
| minfo.selnow.levm.1<1 | 曲データがない（レベルデータ未設定）場合 |
| timenow-stat2astartt<=750 | スタートボタン長押し時間が750ms未満 |
| ginfo2()!=0 | ウィンドウが非アクティブの場合 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[選曲画面メインループ] --> B{スタートボタン750ms長押し?}
    B -->|No| A
    B -->|Yes| C{現在のディレクトリ}
    C -->|通常カテゴリ| D[dialognow=1: 追加ダイアログ表示]
    C -->|お気に入りカテゴリ| E[dialognow=2: 削除ダイアログ表示]
    D --> F[Favoriteカテゴリ数カウント]
    F --> G[favselnow初期化]
    G --> H[追加ダイアログ操作ループ]
    H --> I{入力}
    I -->|上キー| J[favselnow--]
    I -->|下キー| K[favselnow++]
    I -->|Enter| L[お気に入り追加処理]
    I -->|ESC| M[dialognow=0: キャンセル]
    J --> H
    K --> H
    L --> N[Favorite{n}.favに曲パス追記]
    N --> O[dialognow=0]
    E --> P[favselnow2=0初期化]
    P --> Q[削除ダイアログ操作ループ]
    Q --> R{入力}
    R -->|左/右キー| S[favselnow2トグル]
    R -->|Enter| T{favselnow2=1?}
    R -->|ESC| U[dialognow=0: キャンセル]
    S --> Q
    T -->|Yes| V[お気に入り削除処理]
    T -->|No| W[dialognow=0]
    V --> X[.favファイルから曲パス削除]
    X --> Y[曲リスト更新]
    Y --> Z[dialognow=0]
    M --> A
    O --> A
    U --> A
    W --> A
    Z --> A
```

## データベース参照・更新仕様

### 参照テーブル一覧

本通知はデータベースではなく、ファイルシステム上の.favファイルを使用する。

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| Favorite{n}.fav | お気に入りカテゴリファイル（n=1〜9） | songs/ディレクトリに配置。1行1曲パスの形式 |

### テーブル別参照項目詳細

#### Favorite{n}.fav

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| 各行（曲パス文字列） | お気に入り登録済み曲の判定 | 全行を走査して現在選択中の曲パスと一致するか確認 |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| Favorite{n}.fav | 追記（追加時）/ 行削除（削除時） | お気に入り曲パスの追加・削除 |

#### Favorite{n}.fav更新詳細

| 操作 | 項目（カラム名） | 更新値 | 備考 |
|-----|-----------------|-------|------|
| 追記 | 新規行 | minfostr.selnow.levm.0（バックスラッシュをスラッシュに置換） | 重複チェック後に追加 |
| 削除 | 一致行 | （行削除） | 曲パスが一致する行をnotedel命令で削除 |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| .favファイル未存在 | 初回追加時にFavorite{n}.favが存在しない | 空ファイルとして扱い、新規追記を行う |
| 重複追加 | 同じ曲パスが既に登録済み | f=0フラグで重複を検出し、追記をスキップ |
| .favファイル空化 | 最後の1曲を削除した場合 | notemax=0検出時にdir=""でカテゴリから離脱 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | N/A（即座に反映される操作） |
| リトライ間隔 | N/A |
| リトライ対象エラー | N/A |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし。選曲画面表示中のみ操作可能。

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

- .favファイルにはファイルパスが平文で保存される
- パス区切り文字はバックスラッシュからスラッシュに置換される（`replace(buf,"\\\\","/")` select.hsp:2340）
- ファイル書き込みにnotesave命令を使用しており、ファイルシステムのアクセス権限に依存する

## 備考

- dialognowフラグの状態遷移: 0（非表示）→ 1（追加確認）または 2（削除確認）→ 0（操作完了またはキャンセル）
- お気に入りカテゴリは最大9個（Favorite1.fav〜Favorite9.fav）
- ダイアログ表示中はdialognow!=0により選曲操作（曲選択、レベル切替、プレイヤー切替等）がすべてブロックされる
- お気に入り追加後にvalldir=1かつ初回追加時（stat3=0）は曲リストが自動更新される
- お気に入り削除後は曲リストが自動更新され、削除された曲の次の曲が選択される
- ダイアログ表示中の操作キー: 追加時は上下キー/BT-C/BT-B+ジョイスティック、削除時は左右キー/BT-A/BT-D+ジョイスティック
- 背景は半透明オーバーレイ（gmode 6, alpha=32, 白色）で暗転させる

---

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

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

### 推奨読解順序

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

お気に入り管理に関わる変数と.favファイルの構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | select.hsp | `src/scene/select.hsp` | 52-53行目: `favselnow=0`, `dialognow=0` - ダイアログ状態変数の初期化 |

**読解のコツ**: HSP3の`notesel`/`noteload`/`notesave`/`noteget`/`notedel`命令群はテキストファイルの行操作を行う。`notesel buf`でバッファを選択し、`noteload`でファイル読み込み、`notemax`で行数取得、`noteget`で行取得、`notedel`で行削除、`notesave`でファイル保存を行う。

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

ダイアログのトリガーとなるスタートボタン長押しの検出部分を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | select.hsp | `src/scene/select.hsp` | 1734-1751行目: スタートボタン750ms長押し検出とdialognow設定 |

**主要処理フロー**:
1. **1734行目**: `stat2ap=0`かつ`stat2a=1`でスタートボタン長押し開始時刻を記録（`stat2astartt=timenow`）
2. **1735行目**: `stat2ap=1`かつ`stat2a=1`かつ`minfo.selnow.levm.1>=1`かつ`timenow-stat2astartt>750`で長押し判定成立
3. **1739-1740行目**: `dir`が"?"で始まらない場合は`dialognow=1`（追加モード）
4. **1741-1747行目**: Favoriteカテゴリ数のカウント（`exist dir_default+"\\songs\\Favorite"+cnt+".fav"`を9回ループ）
5. **1748-1749行目**: `dir`が"?"で始まる場合は`dialognow=2`（削除モード）

#### Step 3: お気に入り追加ダイアログの処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | select.hsp | `src/scene/select.hsp` | 2303-2354行目: お気に入り追加ダイアログの入力処理 |
| 3-2 | select.hsp | `src/scene/select.hsp` | 2447-2456行目: お気に入り追加ダイアログの描画処理 |

**主要処理フロー**:
- **2305-2312行目**: 上キー/BT-C/ジョイスティックでfavselnowをデクリメント（循環）
- **2313-2320行目**: 下キー/BT-B/ジョイスティックでfavselnowをインクリメント（循環）
- **2321-2348行目**: Enterキーでお気に入り追加実行（重複チェック → .favファイルに追記 → 曲リスト更新）
- **2350-2353行目**: ESCキーでダイアログ閉じる（dialognow=0）

#### Step 4: お気に入り削除ダイアログの処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | select.hsp | `src/scene/select.hsp` | 2355-2444行目: お気に入り削除ダイアログの入力処理 |
| 4-2 | select.hsp | `src/scene/select.hsp` | 2457-2465行目: お気に入り削除ダイアログの描画処理 |

**主要処理フロー**:
- **2357-2366行目**: 左右キーでfavselnow2をトグル（0=No, 1=Yes）
- **2370-2438行目**: Enterキーで削除実行（favselnow2=1の場合、.favファイルから該当曲を削除 → 曲リスト更新）
- **2440-2443行目**: ESCキーでダイアログ閉じる（dialognow=0）

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

```
start_select (select.hsp:1)
    |
    +-- [メインループ] (select.hsp:154以降)
    |       |
    |       +-- [スタートボタン長押し検出] (select.hsp:1734-1751)
    |       |       +-- dialognow=1 : 追加ダイアログ表示
    |       |       +-- dialognow=2 : 削除ダイアログ表示
    |       |
    |       +-- [追加ダイアログ処理] (select.hsp:2303-2354)
    |       |       +-- favselnow操作 : カテゴリ選択
    |       |       +-- Enter確定 : Favorite{n}.fav追記
    |       |       |       +-- noteload : .favファイル読み込み
    |       |       |       +-- 重複チェック
    |       |       |       +-- notesave : .favファイル保存
    |       |       |       +-- gosub *refreshfilelist : 曲リスト更新
    |       |       +-- ESCキャンセル : dialognow=0
    |       |
    |       +-- [削除ダイアログ処理] (select.hsp:2355-2444)
    |       |       +-- favselnow2操作 : Yes/No選択
    |       |       +-- Enter確定(Yes) : .favファイルから行削除
    |       |       |       +-- noteload : .favファイル読み込み
    |       |       |       +-- notedel : 該当行削除
    |       |       |       +-- notesave : .favファイル保存
    |       |       |       +-- gosub *refreshfilelist : 曲リスト更新
    |       |       +-- ESCキャンセル : dialognow=0
    |       |
    |       +-- [ダイアログ描画] (select.hsp:2447-2465)
    |               +-- gmode 6 : 半透明背景
    |               +-- alCopyImageToScreen 119/120/121 : ダイアログ画像描画
```

### データフロー図

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

スタートボタン
  750ms長押し ──────────▶ dialognow設定 ──────────▶ ダイアログ表示
                           (select.hsp:1734)

ユーザー操作
  上下/左右キー ────────▶ favselnow/favselnow2 ──▶ ダイアログ内選択変更
  Enterキー ────────────▶ お気に入り追加/削除
                              |
                              v
Favorite{n}.fav ◀────── noteload/notesave ────▶ Favorite{n}.fav（更新）
(songs/ディレクトリ)        (select.hsp:2329-2341)

曲リスト更新
  gosub *refreshfilelist ──▶ 選曲画面の曲リスト再構築
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| select.hsp | `src/scene/select.hsp` | ソース | 選曲画面全体。ダイアログのトリガー検出、追加/削除処理、描画処理 |
| kshootmania.hsp | `kshootmania.hsp` | ソース | メインスクリプト。SELSLIDER_MAX定数等の定義 |
| Favorite{n}.fav | `songs/Favorite{n}.fav` | データ | お気に入りカテゴリファイル（1行1曲パス、最大9ファイル） |
