new RPG system

This commit is contained in:
2026-06-05 14:57:15 +03:00
parent 6189a5fb74
commit 01b16dbeaa
29 changed files with 2395 additions and 311 deletions
+29 -6
View File
@@ -40,14 +40,17 @@ Return ONLY valid JSON (no markdown):
"stats_delta": {"lust": 0, "stamina": 0, "tension": 0},
"scene_update": {"place": "", "place_id": "", "time_of_day": "", "day": "", "weather": "", "exits": [], "layout_note": ""},
"quest_updates": [{"title": "quest title", "status": "active|done|failed"}],
"outfit_update": ["danbooru_tag", "danbooru_tag"]
"outfit_update": ["danbooru_tag", "danbooru_tag"],
"step_complete": false,
"step_completion_note": "optional 1 sentence when step_complete is true"
}
Rules:
- status_quo_update: internal DM state only (facts, location, mood). Never address the player, never use headers like "Status quo"/"Статус кво", P.S., or author commentary.
- affinity_delta: integer -2..+2. Positive if character warmed up to player, negative if pushed away. 0 if neutral.
- stats_delta: each lust/stamina/tension -2..+2 (0 if unchanged). lust=arousal, stamina=energy, tension=stress.
- scene_update: partial location/time schema; only keys that changed. Do not duplicate all of status_quo into scene_update.
- quest_updates: only include if a quest was clearly started, completed, or failed. Empty array otherwise.
- quest_updates: legacy; prefer step_complete for story progression. Empty array otherwise.
- step_complete: true ONLY when the CURRENT story step completion_criteria are clearly met. Do not rush.
- choices: 0-4 options for what the player can do next. REQUIRED when scripted beats are exhausted — never return an empty choices array unless the session truly ended.
- outfit_update: ONLY if clothing visibly changed. Use danbooru underscore_tags WITH COLOR when possible
(e.g. white_tank_top, black_sports_shorts, gold_championship_belt, blue_jeans, red_ribbon).
@@ -64,6 +67,8 @@ async def narrator_pre(
roll: int | None = None,
outcome: str | None = None,
extra_context: str = "",
*,
lang: str = "ru",
) -> dict:
roll_block = f"Roll d20={roll}\nOutcome={outcome}\n\n" if roll is not None else ""
user = (
@@ -76,9 +81,17 @@ async def narrator_pre(
)
if extra_context:
user += f"\n--- Session state ---\n{extra_context}\n---\n"
from services.rpg_locale import locale_instruction
try:
raw = await send_message_with_model(
[{"role": "system", "content": NARRATOR_PRE_SYSTEM}, {"role": "user", "content": user}],
[
{
"role": "system",
"content": NARRATOR_PRE_SYSTEM + "\n" + locale_instruction(lang),
},
{"role": "user", "content": user},
],
NARRATOR_MODEL,
)
except LLMError as e:
@@ -111,6 +124,8 @@ async def narrator_post(
facts_block: str,
is_opening: bool = False,
extra_context: str = "",
*,
lang: str = "ru",
) -> dict:
opening_block = ""
if is_opening:
@@ -133,17 +148,25 @@ async def narrator_post(
)
if extra_context:
user += f"\n--- Session state ---\n{extra_context}\n---\n"
from services.rpg_locale import locale_instruction
try:
raw = await send_message_with_model(
[{"role": "system", "content": NARRATOR_POST_SYSTEM}, {"role": "user", "content": user}],
[
{
"role": "system",
"content": NARRATOR_POST_SYSTEM + "\n" + locale_instruction(lang),
},
{"role": "user", "content": user},
],
NARRATOR_MODEL,
)
except LLMError as e:
logger.warning("Narrator-post LLM failed (model=%s): %s", NARRATOR_MODEL, e)
return {"status_quo_update": "", "facts": [], "choices": [], "affinity_delta": 0, "quest_updates": [], "_ok": False}
return {"status_quo_update": "", "facts": [], "choices": [], "affinity_delta": 0, "quest_updates": [], "step_complete": False, "_ok": False}
except Exception as e:
logger.warning("Narrator-post unexpected error: %s", e)
return {"status_quo_update": "", "facts": [], "choices": [], "affinity_delta": 0, "quest_updates": [], "_ok": False}
return {"status_quo_update": "", "facts": [], "choices": [], "affinity_delta": 0, "quest_updates": [], "step_complete": False, "_ok": False}
cleaned = raw.strip()
if cleaned.startswith("```"):