特殊イベントの書き方

ここでは、通常の会話イベント以外の特殊なイベントの書き方をまとめます。

ページ概要

  • 対象ファイル: events.json, events/*.json
  • 役割: 「約束(すっぽかし判定)」「クロスオーバー」「アンロック」など、条件/状態と組み合わせた実装パターンをまとめます。
  • どこで使われるか: イベント発火判定時(required_conditions)、イベント実行時(フラグ/変数/アンロック)
  • 最小構成: 約束フラグ+約束日変数を立てる(このページの「約束を立てる」)
  • よくあるミス: フラグ/変数の命名揺れ、日付をintで入れていない、完了フラグの付け忘れ

すっぽかし(約束)

約束(デートの取り決め)→当日のイベント発火→完了/未完了(すっぽかし)の分岐、という流れは「フラグ」と「変数」を組み合わせて実装します。

このページでは次の2パターンを紹介します。

  • (推奨)日付変数で当日発火: day_equals_variable を使って「変数に入っている日だけ発生」を書く(公式MODの方式)

1) 約束を立てる(イベント内)

フラグ: {characterId}_date_promised_{locationId}

変数: {characterId}_promise_{locationId}_day(約束日。int)

{
  "scene_id": "s01",
  "type": "dialogue",
  "character": "my_char",
  "text_ja": "明日、カフェで会おう。",
  "text_en": "Let's meet at the cafe tomorrow.",
  "flags_set": ["my_char_date_promised_cafe"],
  "variables": { "my_char_promise_cafe_day": 2 }
}

2) 約束を達成した印(デート完了フラグ)

フラグ: {characterId}_date_completed_{locationId}

{
  "scene_id": "s99",
  "type": "end",
  "flags_set": ["my_char_date_completed_cafe"]
}

約束日を過ぎても完了フラグが無い場合、システムは「すっぽかし」と判定できます。

3) 約束日にイベントを発生させる(events.json側)

day_equals_variable を使うと「変数に入れた日だけ発生」を書けます。

{
  "id": "promise_day_event",
  "title_ja": "約束の日",
  "title_en": "Promise Day",
  "description_ja": "",
  "description_en": "",
  "type": "story",
  "required_conditions": {
    "day_equals_variable": "my_char_promise_cafe_day",
    "required_flags": ["my_char_date_promised_cafe"],
    "forbidden_flags": ["my_char_date_completed_cafe"],
    "time": ["dayEnd"]
  },
  "script_file": "events/promise_day_event.json",
  "once_only": true,
  "priority": 50
}

※発生タイミングは、一日の終わりの特殊な時間帯「dayEnd」が推奨されます

公式MOD例(はるか): 約束 → dayEndすっぽかし電話(type: special)

A) 約束を立てる(スクリプト側)

{
  "scene_id": "scene_017",
  "type": "end",
  "flags_set": ["haruka_male_date_promised_cafe"],
  "variables": { "haruka_promise_cafe_day": 21 }
}

B) すっぽかし電話イベント定義(events.json側)

{
  "id": "haruka_male_dating_date_standup_cafe",
  "title_ja": "カフェデートすっぽかし電話",
  "type": "special",
  "required_conditions": {
    "day_equals_variable": "haruka_promise_cafe_day",
    "time": ["dayEnd"],
    "required_flags": ["haruka_male_date_promised_cafe"],
    "forbidden_flags": ["haruka_male_date_completed_cafe"]
  },
  "script_file": "events/male_dating_date_standup_cafe.json",
  "once_only": true,
  "priority": 100
}

C) すっぽかしのペナルティ(スクリプト側)

{
  "scene_id": "scene_016",
  "type": "end",
  "stat_changes": { "affection": -20, "intimacy": -15 }
}

クロスオーバー(複数キャラクター連動)

クロスオーバーは「別MOD/別キャラが存在する時だけ発生するイベント」です。主に2種類の条件を使い分けます。

  • required_mods: MODが“読み込まれている”ことだけをチェック(存在確認)
  • other_characters_enabled: キャラクターが“有効化(=アンロック済み)”であることをチェック(実際に登場させたい時)

required_mods(存在確認)

"required_conditions": {
  "required_mods": ["shouya_tada"],
  "required_flags": ["haruka_male_met", "shouya_male_met"]
}

other_characters_enabled(アンロック済みチェック)

相手キャラがアンロックされていないとイベントが発生しない、という“ゲーム内状態”まで含めて制御したい場合はこちらを使います。

"required_conditions": {
  "other_characters_enabled": ["char_a", "char_b"]
}

イベント本体では character_show を使うと複数人の立ち絵管理がしやすいです。

アンロック(キャラクター解放)

アンロックは2通りで実装できます。

A) unlock_character シーン

{
  "scene_id": "s50",
  "type": "unlock_character",
  "characters": ["new_character_id"]
}

B) unlock_characters フィールド(dialogue/choice 等)

{
  "scene_id": "s51",
  "type": "dialogue",
  "character": "player",
  "text_ja": "ということで、彼女を紹介してもらった。",
  "text_en": "So I introduced her to you.",
  "unlock_characters": ["new_character_id"]
}

補足: unlock_charactersdialogue/choice/end で利用できます。また、専用の unlock_character シーンタイプも利用できます(複数アンロック可)。

クロスオーバーの other_characters_enabled は「アンロック済み」でないと通らないため、必要に応じて先にアンロックを挟んでください。

バッティング(鉢合わせ)

バッティングは、同じ場所・同じタイミングに複数キャラクターのイベント候補がある時に発生する“競合処理”です。

  • イベント定義: type: "batting" を使う
  • 実行タイミング: 通常候補としては選ばれず、競合が発生した時だけシステムが追加で探索します
  • 関係性条件: required_relationship で柔軟に指定可能。公式MODでは ["bestFriend", "dating"] に設定されていますが、MOD作成者は自由に設定できます

公式MOD例(はるか): batting定義とスクリプト

公式MODでは「学校内/学校外」で別のバッティングイベントを用意し、場所条件で出し分けています。

{
  "id": "haruka_male_batting_school",
  "title_ja": "バッティング(学校内)",
  "type": "batting",
  "required_conditions": {
    "locations": ["school_classroom", "school_hallway_1"],
    "required_flags": ["haruka_male_met"]
  },
  "script_file": "events/male_batting_school.json",
  "once_only": false,
  "priority": 100
}

バッティングスクリプト例(ペナルティ)

{
  "scene_id": "scene_004",
  "type": "end",
  "stat_changes": { "affection": -10, "intimacy": -5 }
}

バッティングイベントのキャンセル

イベント内で以下のいずれかの条件を満たすと、バッティングイベントの発生がキャンセルされます。

自動キャンセル(location_change)

type: "location_change" シーンを含むイベントでは、パラメータ指定不要で自動的にバッティングイベントがキャンセルされます。

{
  "scene_id": "scene_015",
  "type": "location_change",
  "location": "school_classroom"
}

これは、場所が変わった時点でバッティング(鉢合わせ)が発生しないのが自然なためです。

明示的キャンセル(batting_cancel)

type: "end" シーンに batting_cancel: true を指定すると、バッティングイベントをキャンセルできます。

{
  "scene_id": "scene_037",
  "type": "end",
  "batting_cancel": true,
  "flags_set": ["some_event_done"],
  "stat_changes": {
    "affection": 5
  }
}

使用例:

  • 重要なストーリーイベントの後(感情的な余韻を残すため)
  • キャラクターが別の場所に移動したことが暗示される場合
  • イベント内容的にバッティングが不自然な場合

補足

バッティングがキャンセルされた場合、メインイベント完了後に時間が自動的に進みます。通常はバッティングイベントの最後で時間が進むため、キャンセル時もタイミングを合わせる必要があるためです。

関連