Add syntax enhancements for more expressive behavior trees and relationships.
Action parameters:
- Support typed/positional parameters: WaitDuration(2.0)
- Support named parameters: SetValue(field: value)
- Enable inline values without field names
Repeater decorator:
- Add * { node } syntax for repeating behavior nodes
- Maps to Decorator("repeat", node)
Named participant blocks:
- Replace self/other blocks with named participant syntax
- Support multi-party relationships
- Example: Alice { role: seeker } instead of self { role: seeker }
Schedule block syntax:
- Require braces for schedule blocks to support narrative fields
- Update tests to use new syntax: 04:00 -> 06:00: Activity { }
230 lines
6.4 KiB
Rust
230 lines
6.4 KiB
Rust
//! Public types for resolved Storybook entities
|
|
//!
|
|
//! These types represent fully resolved, validated entities after the
|
|
//! resolution pipeline completes. Unlike the AST types which represent
|
|
//! raw parsed syntax, these types:
|
|
//! - Have all cross-references resolved
|
|
//! - Have all overrides applied
|
|
//! - Have passed semantic validation
|
|
//! - Are ready for consumption by the game engine
|
|
|
|
use std::collections::HashMap;
|
|
|
|
use crate::syntax::ast::{
|
|
BehaviorNode,
|
|
Participant,
|
|
ProseBlock,
|
|
Span,
|
|
Time,
|
|
Transition,
|
|
Value,
|
|
};
|
|
|
|
/// A fully resolved Storybook project
|
|
#[derive(Debug, Clone)]
|
|
pub struct ResolvedFile {
|
|
pub declarations: Vec<ResolvedDeclaration>,
|
|
}
|
|
|
|
/// A resolved top-level declaration
|
|
#[derive(Debug, Clone)]
|
|
pub enum ResolvedDeclaration {
|
|
Character(ResolvedCharacter),
|
|
Template(ResolvedTemplate),
|
|
LifeArc(ResolvedLifeArc),
|
|
Schedule(ResolvedSchedule),
|
|
Behavior(ResolvedBehavior),
|
|
Institution(ResolvedInstitution),
|
|
Relationship(ResolvedRelationship),
|
|
Location(ResolvedLocation),
|
|
Species(ResolvedSpecies),
|
|
Enum(ResolvedEnum),
|
|
}
|
|
|
|
/// A character with all templates applied and references resolved
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
pub struct ResolvedCharacter {
|
|
pub name: String,
|
|
pub species: Option<String>,
|
|
pub fields: HashMap<String, Value>,
|
|
pub prose_blocks: HashMap<String, ProseBlock>,
|
|
pub span: Span,
|
|
}
|
|
|
|
/// A template definition (before instantiation)
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
pub struct ResolvedTemplate {
|
|
pub name: String,
|
|
pub fields: HashMap<String, Value>,
|
|
pub span: Span,
|
|
}
|
|
|
|
/// A life arc with validated state transitions
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
pub struct ResolvedLifeArc {
|
|
pub name: String,
|
|
pub states: Vec<ResolvedArcState>,
|
|
pub span: Span,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
pub struct ResolvedArcState {
|
|
pub name: String,
|
|
pub transitions: Vec<Transition>,
|
|
pub span: Span,
|
|
}
|
|
|
|
/// A schedule with validated non-overlapping blocks
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
pub struct ResolvedSchedule {
|
|
pub name: String,
|
|
pub blocks: Vec<ResolvedScheduleBlock>,
|
|
pub span: Span,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
pub struct ResolvedScheduleBlock {
|
|
pub activity: String,
|
|
pub start: Time,
|
|
pub end: Time,
|
|
pub span: Span,
|
|
}
|
|
|
|
/// A behavior tree with validated actions
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
pub struct ResolvedBehavior {
|
|
pub name: String,
|
|
pub root: BehaviorNode,
|
|
pub span: Span,
|
|
}
|
|
|
|
/// An institution with resolved member references
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
pub struct ResolvedInstitution {
|
|
pub name: String,
|
|
pub fields: HashMap<String, Value>,
|
|
pub span: Span,
|
|
}
|
|
|
|
/// A bidirectional relationship with merged self/other blocks
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
pub struct ResolvedRelationship {
|
|
pub name: String,
|
|
pub participants: Vec<Participant>,
|
|
pub fields: HashMap<String, Value>,
|
|
pub span: Span,
|
|
}
|
|
|
|
/// A location definition
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
pub struct ResolvedLocation {
|
|
pub name: String,
|
|
pub fields: HashMap<String, Value>,
|
|
pub span: Span,
|
|
}
|
|
|
|
/// A species definition
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
pub struct ResolvedSpecies {
|
|
pub name: String,
|
|
pub fields: HashMap<String, Value>,
|
|
pub span: Span,
|
|
}
|
|
|
|
/// An enum definition with variants
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
pub struct ResolvedEnum {
|
|
pub name: String,
|
|
pub variants: Vec<String>,
|
|
pub span: Span,
|
|
}
|
|
|
|
impl ResolvedFile {
|
|
/// Get all characters in the file
|
|
pub fn characters(&self) -> impl Iterator<Item = &ResolvedCharacter> {
|
|
self.declarations.iter().filter_map(|decl| match decl {
|
|
| ResolvedDeclaration::Character(c) => Some(c),
|
|
| _ => None,
|
|
})
|
|
}
|
|
|
|
/// Get all relationships in the file
|
|
pub fn relationships(&self) -> impl Iterator<Item = &ResolvedRelationship> {
|
|
self.declarations.iter().filter_map(|decl| match decl {
|
|
| ResolvedDeclaration::Relationship(r) => Some(r),
|
|
| _ => None,
|
|
})
|
|
}
|
|
|
|
/// Get all institutions in the file
|
|
pub fn institutions(&self) -> impl Iterator<Item = &ResolvedInstitution> {
|
|
self.declarations.iter().filter_map(|decl| match decl {
|
|
| ResolvedDeclaration::Institution(i) => Some(i),
|
|
| _ => None,
|
|
})
|
|
}
|
|
|
|
/// Get all schedules in the file
|
|
pub fn schedules(&self) -> impl Iterator<Item = &ResolvedSchedule> {
|
|
self.declarations.iter().filter_map(|decl| match decl {
|
|
| ResolvedDeclaration::Schedule(s) => Some(s),
|
|
| _ => None,
|
|
})
|
|
}
|
|
|
|
/// Get all behavior trees in the file
|
|
pub fn behaviors(&self) -> impl Iterator<Item = &ResolvedBehavior> {
|
|
self.declarations.iter().filter_map(|decl| match decl {
|
|
| ResolvedDeclaration::Behavior(b) => Some(b),
|
|
| _ => None,
|
|
})
|
|
}
|
|
|
|
/// Get all life arcs in the file
|
|
pub fn life_arcs(&self) -> impl Iterator<Item = &ResolvedLifeArc> {
|
|
self.declarations.iter().filter_map(|decl| match decl {
|
|
| ResolvedDeclaration::LifeArc(la) => Some(la),
|
|
| _ => None,
|
|
})
|
|
}
|
|
|
|
/// Get all locations in the file
|
|
pub fn locations(&self) -> impl Iterator<Item = &ResolvedLocation> {
|
|
self.declarations.iter().filter_map(|decl| match decl {
|
|
| ResolvedDeclaration::Location(l) => Some(l),
|
|
| _ => None,
|
|
})
|
|
}
|
|
|
|
/// Get all species in the file
|
|
pub fn species(&self) -> impl Iterator<Item = &ResolvedSpecies> {
|
|
self.declarations.iter().filter_map(|decl| match decl {
|
|
| ResolvedDeclaration::Species(s) => Some(s),
|
|
| _ => None,
|
|
})
|
|
}
|
|
|
|
/// Get all enums in the file
|
|
pub fn enums(&self) -> impl Iterator<Item = &ResolvedEnum> {
|
|
self.declarations.iter().filter_map(|decl| match decl {
|
|
| ResolvedDeclaration::Enum(e) => Some(e),
|
|
| _ => None,
|
|
})
|
|
}
|
|
|
|
/// Find a character by name
|
|
pub fn find_character(&self, name: &str) -> Option<&ResolvedCharacter> {
|
|
self.characters().find(|c| c.name == name)
|
|
}
|
|
|
|
/// Find a relationship by name
|
|
pub fn find_relationship(&self, name: &str) -> Option<&ResolvedRelationship> {
|
|
self.relationships().find(|r| r.name == name)
|
|
}
|
|
|
|
/// Find an institution by name
|
|
pub fn find_institution(&self, name: &str) -> Option<&ResolvedInstitution> {
|
|
self.institutions().find(|i| i.name == name)
|
|
}
|
|
}
|