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
+23 -42
View File
@@ -1,6 +1,12 @@
"""Shared context blocks for RPG narrator / plot LLM calls."""
from services.rpg_plot import count_active_quests
import json
from services.rpg_story import (
format_step_for_narrator,
normalize_story_arc,
step_progress,
)
def format_narrator_context(
@@ -8,46 +14,21 @@ def format_narrator_context(
quests: list | None,
status_quo: str = "",
) -> str:
parts: list[str] = []
arc = arc or {}
beats = arc.get("beats") or []
if not isinstance(beats, list):
beats = []
arc = normalize_story_arc(arc or {}) if arc else {}
return format_step_for_narrator(arc, quests, status_quo)
parts.append(f"Plot phase: {arc.get('phase', 'opening')}. Scripted beats left: {len(beats)}.")
if not beats:
parts.append(
"IMPORTANT: Scripted beats are EXHAUSTED (quests may already be done). "
"The story must CONTINUE — do not stall. "
"Always return 2-4 meaningful choices for the player's next actions. "
"You may add quest_updates with status 'active' for NEW optional threads. "
"Do NOT re-activate quests the player already completed unless they explicitly revisit that thread."
)
elif count_active_quests(quests) == 0:
pending = [
(b.get("title") or b.get("id") or "beat")
for b in beats[:3]
if isinstance(b, dict)
]
parts.append(
"IMPORTANT: No active quests but scripted beats remain — arc was likely desynced. "
"The engine will inject the next beat; prefer choices that fit pending beats: "
+ ", ".join(pending)
+ ". Do NOT treat the arc as finished."
)
hint = (arc.get("next_beat_hint") or "").strip()
if hint:
parts.append(f"Arc hint: {hint}")
if quests:
parts.append("Quest log:")
for q in quests:
parts.append(f" [{q.get('status', 'active')}] {q.get('title', '')}")
else:
parts.append("Quest log: (empty)")
sq = (status_quo or "").strip()
if sq:
parts.append(f"Status quo: {sq[:400]}")
return "\n".join(parts)
def format_arc_summary_for_runtime(arc: dict | None) -> str:
arc = normalize_story_arc(arc or {}) if arc else {}
if not arc:
return ""
cur, total = step_progress(arc)
return json.dumps(
{
"title": arc.get("title"),
"global_story": (arc.get("global_story") or "")[:300],
"step": f"{cur}/{total}",
"status": arc.get("status", "active"),
},
ensure_ascii=False,
)