use alloc::boxed::Box;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use crate::behavior::value::{Arith, Cmp, Val};
use crate::components::{Behavior, CueKind, StoryPlayback};
use crate::ecs::{AudioClipHandle, TracePath, asset_id::AssetId};
#[derive(Debug)]
pub enum CExpr {
Lit(Val),
Var(u16),
Local(u16),
Bind(u16),
Named(AssetId),
SelfEntity,
Dt,
Elapsed,
Position(Box<CExpr>),
Distance(Box<CExpr>, Box<CExpr>),
Alive(Box<CExpr>),
First(u16),
Count(u16),
Arith(Arith, Box<CExpr>, Box<CExpr>),
Normalize(Box<CExpr>),
Compare(Cmp, Box<CExpr>, Box<CExpr>),
All(Vec<CExpr>),
Any(Vec<CExpr>),
Not(Box<CExpr>),
Never,
}
#[derive(Debug)]
pub struct CNode {
pub id: u32,
pub op: COp,
}
#[derive(Debug)]
pub enum COp {
If {
cond: CExpr,
then: Vec<CNode>,
otherwise: Vec<CNode>,
},
ForEach {
query: u16,
bind: u16,
body: Vec<CNode>,
},
Let {
bind: u16,
value: CExpr,
},
SetVar {
slot: u16,
value: CExpr,
add: bool,
},
SetLocal {
slot: u16,
value: CExpr,
add: bool,
},
SetTransform {
entity: CExpr,
position: Option<CExpr>,
rotation_deg: Option<CExpr>,
scale: Option<CExpr>,
},
Spawn {
template: AssetId,
position: [f32; 3],
rotation_deg: [f32; 3],
scale: [f32; 3],
lifetime: f32,
bind: Option<u16>,
},
Despawn(CExpr),
Reparent {
child: CExpr,
parent: Option<CExpr>,
},
Visible(CExpr, bool),
Sound {
clip: AudioClipHandle,
kind: CueKind,
volume: f32,
},
Scene {
scene: AssetId,
transition: String,
},
Screen(AssetId),
Story(StoryPlayback),
Save,
Never,
}
#[derive(Debug)]
pub struct Program {
pub def: Behavior,
pub scope: Vec<u8>,
pub local_inits: Vec<Val>,
pub queries: Vec<Vec<u8>>,
pub body: Vec<CNode>,
pub paths: Vec<TracePath>,
pub bindings: usize,
}
impl Program {
pub fn is_scoped(&self) -> bool {
!self.scope.is_empty()
}
}
#[derive(Debug, Default)]
pub struct VarTable {
names: Vec<String>,
inits: Vec<Val>,
}
impl VarTable {
pub fn declare(&mut self, name: &str, init: Val) {
if self.slot_of(name).is_some() {
return;
}
self.names.push(name.to_string());
self.inits.push(init);
}
pub(crate) fn intern(&mut self, name: &str) -> u16 {
if let Some(i) = self.slot_of(name) {
return i;
}
self.declare(name, Val::Int(0));
(self.names.len() - 1) as u16
}
pub fn slot_of(&self, name: &str) -> Option<u16> {
self.names.iter().position(|n| n == name).map(|i| i as u16)
}
pub fn names(&self) -> &[String] {
&self.names
}
pub fn initial(&self) -> Vec<Val> {
self.inits.clone()
}
pub fn init_of(&self, name: &str) -> Option<Val> {
self.slot_of(name)
.and_then(|slot| self.inits.get(slot as usize))
.copied()
}
}