フラグシステム仕様

ページ概要

  • 対象ファイル: events.json, events/*.json
  • 役割: flags_set/flags_unset でフラグを更新し、required_conditions.required_flags/forbidden_flags でイベント発生や分岐条件に使います。
  • どこで使われるか: イベント実行時(フラグ更新)、イベント発火判定時/条件分岐時(フラグ評価)
  • 最小構成: フラグを1つ立てる(このページの「フラグの設定方法」)
  • よくあるミス: 条件JSONの形(required_conditions)、命名衝突、解除漏れ

概要

フラグシステムは、ゲーム内で発生したイベントや選択肢の結果を記録し、後続のイベント発生条件として利用する仕組みです。

フラグの種類

1. カスタムフラグ

MOD作成者が自由に定義できるフラグ。イベントの進行状況や選択肢の結果を記録します。

例:

  • sakura_name_known - さくらの名前を知った
  • visited_cafe_with_haruka - はるかとカフェに行った
  • promised_to_meet - 会う約束をした
  • confession_accepted - 告白が成功した

2. システムフラグ

システムが自動的に設定するフラグ。特定の状況を表します。

バッティングフラグ:

  • batting_with_{character_id} - 特定のキャラクターとバッティングした
  • batting_at_{location_id} - 特定の場所でバッティングした

関係性フラグ:

  • relationship_{character_id}_{relationship_type} - 関係性が変化した
    • 例: relationship_sakura_dating - さくらと恋人になった

補足:

  • 「一度しか起こしたくない」イベントは、イベント定義側の once_only(一度だけ)と、必要ならフラグを組み合わせて管理します。

3. グローバルフラグ

全キャラクター共通で使用できるフラグ。

例:

  • player_confession_successful - プレイヤーが誰かに告白成功した
  • christmas_event_happened - クリスマスイベントが発生した
  • exam_period - 試験期間中

フラグの設定方法

1. イベントスクリプト内で設定

Dialogue シーンで設定:

{
  "scene_id": "scene_005",
  "type": "dialogue",
  "character": "sakura_tanaka",
  "portrait": "happy",
  "text": "私、田中さくらって言うの。よろしくね!",
  "flags_set": ["sakura_name_known", "first_meeting_completed"],
  "flags_unset": ["sakura_name_unknown"]
}

Choice シーンで設定:

{
  "scene_id": "scene_010",
  "type": "choice",
  "text": "告白しますか?",
  "choices": [
    {
      "choice_id": "choice_001",
      "text": "告白する",
      "stat_changes": {
        "affection": 10
      },
      "flags_set": ["confession_attempted"],
      "next_scene": "scene_011"
    },
    {
      "choice_id": "choice_002",
      "text": "やめておく",
      "flags_set": ["confession_delayed"],
      "next_scene": "scene_020"
    }
  ]
}

2. プレイヤー独白シーンで設定

{
  "scene_id": "scene_012",
  "type": "dialogue",
  "character": "player",
  "text_ja": "(よし、気持ちを伝えることができた!)",
  "text_en": "(Alright, I was able to express my feelings!)",
  "stat_changes": {
    "affection": 15
  },
  "flags_set": ["confession_successful", "relationship_dating"],
  "flags_unset": ["confession_attempted"]
}

フラグの使用方法

1. イベント発生条件として使用

必須フラグ(required_flags):

指定したフラグが全て設定されている場合のみイベントが発生します。

{
  "id": "second_meeting",
  "title": "2回目の出会い",
  "type": "story",
  "required_conditions": {
    "day": [{"operator": ">=", "value": 3}],
    "required_flags": ["sakura_name_known", "first_meeting_completed"]
  },
  "script_file": "events/second_meeting.json",
  "once_only": true,
  "priority": 90
}

禁止フラグ(forbidden_flags):

指定したフラグが1つも設定されていない場合のみイベントが発生します。

{
  "id": "alternative_meeting",
  "title": "別の出会い方",
  "type": "story",
  "required_conditions": {
    "day": [{"operator": ">=", "value": 5}],
    "forbidden_flags": ["first_meeting_completed", "bad_ending_triggered"]
  },
  "script_file": "events/alternative_meeting.json",
  "once_only": true,
  "priority": 80
}

2. 複合条件の例

フラグ + ステータス + 場所:

{
  "id": "special_cafe_date",
  "title": "特別なカフェデート",
  "type": "date",
  "required_conditions": {
    "day": [{"operator": ">=", "value": 14}],
    "locations": ["cafe"],
    "stats": {
      "affection": {"operator": ">=", "value": 50},
      "intimacy": {"operator": ">=", "value": 40}
    },
    "required_relationship": "friend",
    "required_flags": ["visited_cafe_together", "promised_next_date"],
    "forbidden_flags": ["relationship_broken", "bad_ending_triggered"]
  },
  "script_file": "events/special_cafe_date.json",
  "once_only": true,
  "priority": 95
}

フラグ条件の評価ロジック

イベント条件は以下の優先順序で評価されます:

  1. 日数条件 - 現在の日数が条件を満たすかチェック
  2. 時間帯条件 - 現在の時間帯が許可された時間帯に含まれるかチェック
  3. 場所条件 - 現在の場所が許可された場所に含まれるかチェック
  4. ステータス条件 - 各ステータス値が条件を満たすかチェック
  5. 関係性条件 - 現在の関係性が条件と一致するかチェック
  6. 必須フラグ条件(AND) - すべてのフラグが設定されているかチェック
  7. 禁止フラグ条件(NOR) - いずれのフラグも設定されていないかチェック
  8. クロスオーバー条件 - 他キャラクターが有効化されているかチェック

すべての条件を満たした場合のみ、イベントが発生候補となります。

フラグ命名規則

形式:

  • スネークケース: flag_name_example
  • 小文字のみ: 大文字は使用しない
  • 英数字とアンダースコア: 特殊文字は使用しない

命名パターン:

  1. 状態を表すフラグ:
    • {キャラクターID}_{状態}
    • 例: sakura_name_known, haruka_jealous
  2. イベント完了フラグ:
    • {イベント名}_completed または {イベント名}_done
    • 例: first_meeting_completed, confession_done
  3. 選択肢の結果フラグ:
    • {選択内容}_{結果}
    • 例: promised_to_meet, ignored_phone_call
  4. 場所訪問フラグ:
    • visited_{場所名}
    • 例: visited_cafe, visited_park_with_sakura
  5. 関係性フラグ:
    • {関係性}_{キャラクターID}
    • 例: dating_sakura, friends_with_haruka
  6. システムフラグ:
    • batting_with_{キャラクターID}
    • batting_at_{場所ID}
    • 例: batting_with_sakura, batting_at_cafe

NG例

  • Flag1, Flag2 - 意味が不明
  • SakuraNameKnown - キャメルケース(スネークケース推奨)
  • さくら_名前_知っている - 日本語(英語推奨)
  • sakura-name-known - ハイフン(アンダースコア推奨)
  • sakura.name.known - ドット(アンダースコア推奨)

OK例

  • sakura_name_known - 明確でスネークケース
  • first_meeting_completed - イベント完了を表す
  • promised_next_date - 選択肢の結果
  • visited_cafe_together - 場所訪問 + 状況

フラグ管理のベストプラクティス

1. フラグは明確な名前をつける

// ❌ 悪い例
"flags_set": ["flag1", "flag2"]

// ✅ 良い例
"flags_set": ["sakura_name_known", "first_meeting_completed"]

2. フラグは一貫性を保つ

同じ意味のフラグに異なる名前をつけない。

// ❌ 悪い例
"required_flags": ["sakura_met", "haruka_first_meeting_done"]

// ✅ 良い例
"required_flags": ["sakura_first_meeting_completed", "haruka_first_meeting_completed"]

3. フラグは必要最小限にする

ステータスで代用できる場合はフラグを使わない。

// ❌ 悪い例(ステータスで判定できる)
"required_flags": ["affection_over_50"]

// ✅ 良い例(ステータスで判定)
"affection": {"operator": ">=", "value": 50}

4. フラグの設定と解除はペアで考える

{
  "scene_id": "scene_001",
  "type": "dialogue",
  "character": "sakura_tanaka",
  "text": "私の名前は田中さくらよ!",
  "flags_set": ["sakura_name_known"],
  "flags_unset": ["sakura_name_unknown"]
}

5. フラグのドキュメント化

MOD内に flags.md を作成し、使用するフラグを一覧化する。

# フラグ一覧

## ストーリーフラグ

- `sakura_name_known` - さくらの名前を知った
- `first_meeting_completed` - 初めて会った
- `confession_attempted` - 告白を試みた
- `confession_successful` - 告白成功

## バッティングフラグ

- `batting_with_haruka` - はるかとバッティングした
- `batting_at_cafe` - カフェでバッティングした

実装例

例1: 分岐するストーリー

イベント定義:

{
  "events": [
    {
      "id": "first_meeting",
      "title": "初めての出会い",
      "type": "story",
      "required_conditions": {
        "day": [{"operator": "==", "value": 1}]
      },
      "script_file": "events/first_meeting.json",
      "once_only": true,
      "priority": 100
    },
    {
      "id": "friendly_route_start",
      "title": "友達ルート開始",
      "type": "story",
      "required_conditions": {
        "day": [{"operator": ">=", "value": 3}],
        "stats": {
          "affection": {"operator": ">=", "value": 20}
        },
        "required_flags": ["first_meeting_completed", "chose_friendly_option"],
        "forbidden_flags": ["chose_romantic_option"]
      },
      "script_file": "events/friendly_route_start.json",
      "once_only": true,
      "priority": 90
    },
    {
      "id": "romantic_route_start",
      "title": "恋愛ルート開始",
      "type": "story",
      "required_conditions": {
        "day": [{"operator": ">=", "value": 3}],
        "stats": {
          "affection": {"operator": ">=", "value": 30}
        },
        "required_flags": ["first_meeting_completed", "chose_romantic_option"],
        "forbidden_flags": ["chose_friendly_option"]
      },
      "script_file": "events/romantic_route_start.json",
      "once_only": true,
      "priority": 90
    }
  ]
}

イベントスクリプト(first_meeting.json):

{
  "event_id": "first_meeting",
  "scenes": [
    {
      "scene_id": "scene_001",
      "type": "dialogue",
      "character": "sakura_tanaka",
      "portrait": "happy",
      "text_ja": "こんにちは!初めまして!",
      "text_en": "Hello! Nice to meet you!"
    },
    {
      "scene_id": "scene_002",
      "type": "choice",
      "text_ja": "どう返事する?",
      "text_en": "How do you respond?",
      "choices": [
        {
          "choice_id": "choice_001",
          "text_ja": "よろしくね!友達になろう!",
          "text_en": "Nice to meet you! Let's be friends!",
          "stat_changes": {
            "affection": 5,
            "intimacy": 3
          },
          "flags_set": ["chose_friendly_option"],
          "next_scene": "scene_003"
        },
        {
          "choice_id": "choice_002",
          "text_ja": "素敵だね...もっと話したいな",
          "text_en": "You're lovely... I'd like to talk more",
          "stat_changes": {
            "affection": 8,
            "intimacy": 2
          },
          "flags_set": ["chose_romantic_option"],
          "next_scene": "scene_004"
        }
      ]
    },
    {
      "scene_id": "scene_003",
      "type": "dialogue",
      "character": "sakura_tanaka",
      "portrait": "happy",
      "text_ja": "うん!友達になろう!",
      "text_en": "Yeah! Let's be friends!",
      "flags_set": ["first_meeting_completed"],
      "next_scene": "scene_999"
    },
    {
      "scene_id": "scene_004",
      "type": "dialogue",
      "character": "sakura_tanaka",
      "portrait": "shy",
      "text_ja": "え...?ありがとう...私も話したいな",
      "text_en": "Huh...? Thanks... I'd like to talk too",
      "flags_set": ["first_meeting_completed"],
      "next_scene": "scene_999"
    },
    {
      "scene_id": "scene_999",
      "type": "end"
    }
  ]
}

例2: フラグによる条件分岐イベント

告白イベント:

{
  "id": "confession_event",
  "title": "告白イベント",
  "type": "story",
  "required_conditions": {
    "day": [{"operator": ">=", "value": 30}],
    "stats": {
      "affection": {"operator": ">=", "value": 70},
      "intimacy": {"operator": ">=", "value": 60}
    },
    "required_relationship": "friend",
    "required_flags": ["romantic_route_started", "promised_to_meet_again"],
    "forbidden_flags": ["confession_rejected", "bad_ending_triggered"]
  },
  "script_file": "events/confession.json",
  "once_only": true,
  "priority": 100
}

例3: バッティングイベントのフラグ使用

バッティングイベント定義:

{
  "id": "batting_jealous",
  "title": "嫉妬",
  "type": "batting",
  "required_conditions": {
    "required_flags": ["batting_with_sakura"],
    "required_relationship": "dating",
    "stats": {
      "intimacy": {"operator": ">=", "value": 50}
    }
  },
  "script_file": "events/batting_jealous.json",
  "once_only": false,
  "priority": 90
}

例4: クロスオーバーイベントのフラグ使用

{
  "id": "crossover_shopping",
  "title": "3人でショッピング",
  "type": "special",
  "required_conditions": {
    "day": [{"operator": ">=", "value": 20}],
    "day_of_week": ["saturday", "sunday"],
    "locations": ["shopping_mall"],
    "other_characters_enabled": ["sakura_tanaka", "haruka_sato"],
    "required_flags": [
      "sakura_friend",
      "haruka_friend",
      "both_characters_met"
    ],
    "forbidden_flags": ["dating_someone"]
  },
  "script_file": "events/crossover_shopping.json",
  "once_only": true,
  "priority": 95
}

条件分岐シーンでのフラグ使用

条件分岐シーン(type: conditional)では、フラグやステータス、日数などの条件に基づいて自動的にシーンを分岐できます。

基本的な使用例

{
  "scene_id": "scene_check_flag",
  "type": "conditional",
  "branches": [
    {
      "conditions": {
        "required_flags": ["haruka_fully_opened"]
      },
      "next_scene": "scene_deep_conversation"
    }
  ],
  "default_next_scene": "scene_normal_conversation"
}

他のキャラクターのフラグを参照

フラグはグローバルに保存されているため、他のキャラクターのフラグも参照できます:

{
  "scene_id": "scene_check_other_character",
  "type": "conditional",
  "branches": [
    {
      "conditions": {
        "required_flags": ["sakura_name_known", "haruka_fully_opened"]
      },
      "next_scene": "scene_both_characters_known"
    },
    {
      "conditions": {
        "required_flags": ["sakura_name_known"]
      },
      "next_scene": "scene_only_sakura_known"
    }
  ],
  "default_next_scene": "scene_neither_known"
}

フラグとステータスの複合条件

{
  "scene_id": "scene_complex_branch",
  "type": "conditional",
  "branches": [
    {
      "conditions": {
        "required_flags": ["confession_accepted"],
        "stats": {
          "affection": {"operator": ">=", "value": 90}
        }
      },
      "next_scene": "scene_romantic_ending"
    },
    {
      "conditions": {
        "required_flags": ["confession_accepted"]
      },
      "next_scene": "scene_dating_route"
    }
  ],
  "default_next_scene": "scene_friendship_route"
}

禁止フラグの使用

{
  "scene_id": "scene_check_forbidden_flags",
  "type": "conditional",
  "branches": [
    {
      "conditions": {
        "forbidden_flags": ["bad_ending_triggered", "relationship_broken"]
      },
      "next_scene": "scene_good_route"
    }
  ],
  "default_next_scene": "scene_bad_route"
}

条件の書き方(required_conditions)は イベント発生条件(required_conditions)、イベント内分岐(conditional)は イベントスクリプトについて を参照してください。

まとめ

フラグシステムは、イベント分岐やストーリー進行の制御に不可欠な機能です。

重要なポイント:

  1. 明確な命名規則 - スネークケースで意味が分かる名前
  2. 必要最小限のフラグ - ステータスで代用できる場合は使わない
  3. ドキュメント化 - 使用するフラグを一覧化する
  4. required_flags と forbidden_flags - 組み合わせて複雑な条件を作る
  5. flags_set と flags_unset - イベント内で適切に設定・解除する
  6. 条件分岐シーン - フラグやステータスに基づく自動分岐を活用する

次のステップ:

  • フラグシステムの実例を作成
  • フラグ管理UIの実装