//! 视觉小说模板的纯领域模型。 //! //! 本 crate 只负责草稿校验、运行时 step 解析、状态推进、历史重生成边界 //! 和平台统一存档状态构造;HTTP、SpacetimeDB、LLM、OSS 均由外层 adapter 处理。 use serde::{Deserialize, Serialize}; #[cfg(feature = "spacetime-types")] use spacetimedb::SpacetimeType; use std::collections::BTreeMap; pub const VISUAL_NOVEL_PROFILE_ID_PREFIX: &str = "vn-profile-"; pub const VISUAL_NOVEL_RUN_ID_PREFIX: &str = "vn-run-"; pub const VISUAL_NOVEL_HISTORY_ID_PREFIX: &str = "vn-history-"; pub const VISUAL_NOVEL_DEFAULT_MAX_HISTORY_ENTRIES: u32 = 80; pub const VISUAL_NOVEL_DEFAULT_MAX_ASSISTANT_STEP_COUNT_PER_TURN: u32 = 8; pub const VISUAL_NOVEL_MIN_INITIAL_CHOICE_COUNT: usize = 2; pub const VISUAL_NOVEL_MAX_INITIAL_CHOICE_COUNT: usize = 4; #[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum VisualNovelSourceMode { Idea, Document, Blank, } #[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum VisualNovelCharacterRole { Protagonist, Main, Supporting, Antagonist, Background, } #[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum VisualNovelAssetSource { PlatformAsset, Generated, External, } #[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum VisualNovelSceneAvailability { Opening, Always, PhaseLocked, } #[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum VisualNovelAttributePanelMode { Off, PlatformWhitelist, TemplateConfig, } #[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum VisualNovelValidationSeverity { Error, Warning, } #[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum VisualNovelRunMode { Test, Play, } #[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum VisualNovelRunStatus { Active, Completed, Failed, } #[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum VisualNovelRuntimeActionKind { Choice, FreeText, Continue, } #[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum VisualNovelTransitionKind { Fade, Cut, Flash, None, } #[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum VisualNovelHistorySource { Player, Assistant, System, } #[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(untagged)] pub enum VisualNovelFlagValue { String(String), Number(f64), Bool(bool), } #[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct VisualNovelValidationIssue { pub issue_id: String, pub code: String, pub severity: VisualNovelValidationSeverity, pub path: String, pub message: String, } #[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct VisualNovelChoiceDraft { pub choice_id: String, pub text: String, pub action_hint: Option, } #[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct VisualNovelCharacterImageAsset { pub asset_id: String, pub image_src: String, pub expression: Option, pub source: VisualNovelAssetSource, } #[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct VisualNovelWorldDraft { pub title: String, pub summary: String, pub background: String, pub premise: String, pub literary_style: String, pub player_role: String, pub default_tone: String, } #[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct VisualNovelCharacterDraft { pub character_id: String, pub name: String, pub gender: Option, pub role: VisualNovelCharacterRole, pub appearance: String, pub personality: String, pub tone: String, pub background: String, pub relationship_to_player: String, pub image_assets: Vec, pub default_expression: Option, pub is_player_visible: bool, } #[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct VisualNovelSceneDraft { pub scene_id: String, pub name: String, pub description: String, pub background_image_src: Option, pub music_src: Option, pub ambient_sound_src: Option, pub availability: VisualNovelSceneAvailability, pub phase_ids: Vec, } #[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct VisualNovelStoryPhaseDraft { pub phase_id: String, pub title: String, pub goal: String, pub summary: String, pub entry_condition: String, pub exit_condition: String, pub scene_ids: Vec, pub character_ids: Vec, pub suggested_choices: Vec, } #[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct VisualNovelOpeningDraft { pub scene_id: Option, pub narration: String, pub speaker_character_id: Option, pub first_dialogue: Option, pub initial_choices: Vec, } #[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct VisualNovelRuntimeConfigDraft { pub text_mode_enabled: bool, pub default_text_mode: bool, pub max_history_entries: u32, pub max_assistant_step_count_per_turn: u32, pub allow_free_text_action: bool, pub allow_history_regeneration: bool, pub attribute_panel_mode: VisualNovelAttributePanelMode, pub save_archive_enabled: bool, } #[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct VisualNovelResultDraft { pub profile_id: Option, pub work_title: String, pub work_description: String, pub work_tags: Vec, pub cover_image_src: Option, pub source_mode: VisualNovelSourceMode, pub source_asset_ids: Vec, pub world: VisualNovelWorldDraft, pub characters: Vec, pub scenes: Vec, pub story_phases: Vec, pub opening: VisualNovelOpeningDraft, pub runtime_config: VisualNovelRuntimeConfigDraft, pub publish_ready: bool, pub validation_issues: Vec, pub updated_at: String, } #[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct VisualNovelWorkProfile { pub profile_id: String, pub work_title: String, pub work_description: String, pub work_tags: Vec, pub cover_image_src: Option, pub source_mode: VisualNovelSourceMode, pub draft: VisualNovelResultDraft, } #[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde( tag = "type", rename_all = "snake_case", rename_all_fields = "camelCase" )] pub enum VisualNovelRuntimeStep { SceneChange { scene_id: String, background_image_src: Option, music_src: Option, }, Narration { text: String, }, Dialogue { character_id: String, character_name: String, expression: Option, text: String, }, Transition { transition_kind: VisualNovelTransitionKind, text: Option, }, Choice { choices: Vec, }, Flag { key: String, value: VisualNovelFlagValue, }, Metric { key: String, delta: f64, }, } #[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct VisualNovelHistoryEntry { pub entry_id: String, pub run_id: String, pub turn_index: u32, pub source: VisualNovelHistorySource, pub action_text: Option, pub steps: Vec, pub snapshot_before_hash: Option, pub snapshot_after_hash: Option, pub created_at: String, } #[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct VisualNovelRunSnapshot { pub run_id: String, pub owner_user_id: String, pub profile_id: String, pub mode: VisualNovelRunMode, pub status: VisualNovelRunStatus, pub current_scene_id: Option, pub current_phase_id: Option, pub visible_character_ids: Vec, pub flags: BTreeMap, pub metrics: BTreeMap, pub history: Vec, pub available_choices: Vec, pub text_mode_enabled: bool, pub created_at: String, pub updated_at: String, } #[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct VisualNovelRuntimeAction { pub action_kind: VisualNovelRuntimeActionKind, pub choice_id: Option, pub text: Option, pub client_event_id: String, } #[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct VisualNovelSaveArchiveState { pub runtime_kind: String, pub profile_id: String, pub run_id: String, pub current_scene_id: Option, pub current_phase_id: Option, pub history_cursor: u32, pub snapshot_hash: Option, } impl Default for VisualNovelRuntimeConfigDraft { fn default() -> Self { Self { text_mode_enabled: true, default_text_mode: false, max_history_entries: VISUAL_NOVEL_DEFAULT_MAX_HISTORY_ENTRIES, max_assistant_step_count_per_turn: VISUAL_NOVEL_DEFAULT_MAX_ASSISTANT_STEP_COUNT_PER_TURN, allow_free_text_action: true, allow_history_regeneration: true, attribute_panel_mode: VisualNovelAttributePanelMode::Off, save_archive_enabled: true, } } }