391 lines
12 KiB
Rust
391 lines
12 KiB
Rust
//! 视觉小说模板的纯领域模型。
|
||
//!
|
||
//! 本 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<String>,
|
||
}
|
||
|
||
#[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<String>,
|
||
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<String>,
|
||
pub role: VisualNovelCharacterRole,
|
||
pub appearance: String,
|
||
pub personality: String,
|
||
pub tone: String,
|
||
pub background: String,
|
||
pub relationship_to_player: String,
|
||
pub image_assets: Vec<VisualNovelCharacterImageAsset>,
|
||
pub default_expression: Option<String>,
|
||
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<String>,
|
||
pub music_src: Option<String>,
|
||
pub ambient_sound_src: Option<String>,
|
||
pub availability: VisualNovelSceneAvailability,
|
||
pub phase_ids: Vec<String>,
|
||
}
|
||
|
||
#[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<String>,
|
||
pub character_ids: Vec<String>,
|
||
pub suggested_choices: Vec<String>,
|
||
}
|
||
|
||
#[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<String>,
|
||
pub narration: String,
|
||
pub speaker_character_id: Option<String>,
|
||
pub first_dialogue: Option<String>,
|
||
pub initial_choices: Vec<VisualNovelChoiceDraft>,
|
||
}
|
||
|
||
#[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<String>,
|
||
pub work_title: String,
|
||
pub work_description: String,
|
||
pub work_tags: Vec<String>,
|
||
pub cover_image_src: Option<String>,
|
||
pub source_mode: VisualNovelSourceMode,
|
||
pub source_asset_ids: Vec<String>,
|
||
pub world: VisualNovelWorldDraft,
|
||
pub characters: Vec<VisualNovelCharacterDraft>,
|
||
pub scenes: Vec<VisualNovelSceneDraft>,
|
||
pub story_phases: Vec<VisualNovelStoryPhaseDraft>,
|
||
pub opening: VisualNovelOpeningDraft,
|
||
pub runtime_config: VisualNovelRuntimeConfigDraft,
|
||
pub publish_ready: bool,
|
||
pub validation_issues: Vec<VisualNovelValidationIssue>,
|
||
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<String>,
|
||
pub cover_image_src: Option<String>,
|
||
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<String>,
|
||
music_src: Option<String>,
|
||
},
|
||
Narration {
|
||
text: String,
|
||
},
|
||
Dialogue {
|
||
character_id: String,
|
||
character_name: String,
|
||
expression: Option<String>,
|
||
text: String,
|
||
},
|
||
Transition {
|
||
transition_kind: VisualNovelTransitionKind,
|
||
text: Option<String>,
|
||
},
|
||
Choice {
|
||
choices: Vec<VisualNovelChoiceDraft>,
|
||
},
|
||
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<String>,
|
||
pub steps: Vec<VisualNovelRuntimeStep>,
|
||
pub snapshot_before_hash: Option<String>,
|
||
pub snapshot_after_hash: Option<String>,
|
||
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<String>,
|
||
pub current_phase_id: Option<String>,
|
||
pub visible_character_ids: Vec<String>,
|
||
pub flags: BTreeMap<String, VisualNovelFlagValue>,
|
||
pub metrics: BTreeMap<String, f64>,
|
||
pub history: Vec<VisualNovelHistoryEntry>,
|
||
pub available_choices: Vec<VisualNovelChoiceDraft>,
|
||
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<String>,
|
||
pub text: Option<String>,
|
||
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<String>,
|
||
pub current_phase_id: Option<String>,
|
||
pub history_cursor: u32,
|
||
pub snapshot_hash: Option<String>,
|
||
}
|
||
|
||
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,
|
||
}
|
||
}
|
||
}
|