diff --git a/docs/Animation.md b/docs/Animation.md index c89af4406..78420b837 100644 --- a/docs/Animation.md +++ b/docs/Animation.md @@ -2,6 +2,10 @@ Perro animation has two resource files and two scene nodes. +Sigils: +- `$` => value var define/use. +- `@` => node ref (scene key / NodeID target), in scenes + animation bindings. + - `.panim`: one animation clip. - `.panimtree`: one animation graph. - `AnimationPlayer`: plays one `.panim`. @@ -53,7 +57,7 @@ Use `AnimationPlayer` when one clip drives one set of bindings. [IdlePlayer] [AnimationPlayer] animation = "res://animations/idle.panim" - bindings = { Hero = Hero } + bindings = { Hero = @Hero } speed = 1.0 paused = false playback = loop @@ -204,9 +208,9 @@ The scene node supplies: [AnimationTree] tree = "res://animations/player.panimtree" animations = [ - { animation = "res://animations/idle.panim", bindings = { Hero = Hero }, playback = loop, speed = 1.0, paused = false }, - { animation = "res://animations/run.panim", bindings = { Hero = Hero }, playback = loop, speed = 1.0, paused = false }, - { animation = "res://animations/aim.panim", bindings = { Hero = Hero }, playback = boomerang, speed = 1.0, paused = false }, + { animation = "res://animations/idle.panim", bindings = { Hero = @Hero }, playback = loop, speed = 1.0, paused = false }, + { animation = "res://animations/run.panim", bindings = { Hero = @Hero }, playback = loop, speed = 1.0, paused = false }, + { animation = "res://animations/aim.panim", bindings = { Hero = @Hero }, playback = boomerang, speed = 1.0, paused = false }, ] speed = 1.0 paused = false diff --git a/docs/resources/panimtree.md b/docs/resources/panimtree.md index fcb51b60c..86a14e87c 100644 --- a/docs/resources/panimtree.md +++ b/docs/resources/panimtree.md @@ -7,6 +7,10 @@ The `.panimtree` owns slot names and graph connections. The scene `AnimationTree` node owns runtime clip IDs and per-slot bindings. Slot bindings connect `.panim` object names to scene node keys. +Sigils in scene-side bindings: +- `$` => value vars. +- `@` => node refs (`NodeID` targets), same as scene parent/root refs. + ## Example ```ini @@ -195,9 +199,9 @@ Scene node template: speed = 1.0 paused = false animations = [ - { animation = "res://animations/idle.panim", bindings = { Hero = PlayerRoot }, playback = loop, speed = 1.0, paused = false }, - { animation = "res://animations/run.panim", bindings = { Hero = PlayerRoot }, playback = loop, speed = 1.0, paused = false }, - { animation = "res://animations/aim.panim", bindings = { Hero = PlayerRoot }, playback = boomerang, speed = 1.0, paused = false }, + { animation = "res://animations/idle.panim", bindings = { Hero = @PlayerRoot }, playback = loop, speed = 1.0, paused = false }, + { animation = "res://animations/run.panim", bindings = { Hero = @PlayerRoot }, playback = loop, speed = 1.0, paused = false }, + { animation = "res://animations/aim.panim", bindings = { Hero = @PlayerRoot }, playback = boomerang, speed = 1.0, paused = false }, ] [/AnimationTree] [/AnimTree] @@ -251,9 +255,9 @@ Bind it to a real scene node in `animations`: [AnimationTree] tree = "res://animations/player.panimtree" animations = [ - { animation = "res://animations/idle.panim", bindings = { Hero = PlayerRoot }, playback = loop }, - { animation = "res://animations/run.panim", bindings = { Hero = PlayerRoot }, playback = loop }, - { animation = "res://animations/aim.panim", bindings = { Hero = PlayerRoot }, playback = boomerang }, + { animation = "res://animations/idle.panim", bindings = { Hero = @PlayerRoot }, playback = loop }, + { animation = "res://animations/run.panim", bindings = { Hero = @PlayerRoot }, playback = loop }, + { animation = "res://animations/aim.panim", bindings = { Hero = @PlayerRoot }, playback = boomerang }, ] [/AnimationTree] [/anim_tree] @@ -362,9 +366,9 @@ Scene: [AnimationTree] tree = "res://animations/player.panimtree" animations = [ - { animation = "res://animations/idle.panim", bindings = { Hero = PlayerRoot }, playback = loop, speed = 1.0 }, - { animation = "res://animations/run.panim", bindings = { Hero = PlayerRoot }, playback = loop, speed = 1.0 }, - { animation = "res://animations/aim.panim", bindings = { Hero = PlayerRoot }, playback = boomerang, speed = 0.8 }, + { animation = "res://animations/idle.panim", bindings = { Hero = @PlayerRoot }, playback = loop, speed = 1.0 }, + { animation = "res://animations/run.panim", bindings = { Hero = @PlayerRoot }, playback = loop, speed = 1.0 }, + { animation = "res://animations/aim.panim", bindings = { Hero = @PlayerRoot }, playback = boomerang, speed = 0.8 }, ] [/AnimationTree] [/AnimTree] diff --git a/docs/scripting/contexts/resource_modules/animations.md b/docs/scripting/contexts/resource_modules/animations.md index 840b0203c..d5bb6c846 100644 --- a/docs/scripting/contexts/resource_modules/animations.md +++ b/docs/scripting/contexts/resource_modules/animations.md @@ -53,5 +53,5 @@ Binding note: - `ClipObjectName` is the object key declared in `.panim [Objects]` (without `@`). - Bind each clip object to a runtime node with the expected node type for that track data. -- scene authoring bindings use map entries: `{ ClipObjectName = SceneKey }` or `{ "ClipObjectName": SceneKey }`. +- scene authoring bindings use map entries: `{ ClipObjectName = @SceneKey }` or `{ "ClipObjectName": @SceneKey }`. diff --git a/docs/scripting/contexts/runtime_modules/animations.md b/docs/scripting/contexts/runtime_modules/animations.md index d05dda401..a5d08ef81 100644 --- a/docs/scripting/contexts/runtime_modules/animations.md +++ b/docs/scripting/contexts/runtime_modules/animations.md @@ -101,7 +101,7 @@ let _ = anim_player_play!(ctx, animation_player_id); - `animation = "res://animations/clip.panim"` - `bindings = [{ Hero = HeroNode }, { Weapon = WeaponNode }]` - `bindings = [{ "Hero": HeroNode }, { "Weapon": WeaponNode }]` -- bindings are map entries: `AnimationObject -> SceneKey` +- bindings are map entries: `AnimationObject -> @SceneKey` - scene key (`HeroNode`) is resolved to runtime `NodeID` during scene merge - `speed = 1.0` - `paused = true|false` @@ -125,4 +125,3 @@ Example scene-key binding: ``` Scripts can override or update these values at runtime. - diff --git a/docs/scripting/nodes.md b/docs/scripting/nodes.md index b4cdc961e..68d76b893 100644 --- a/docs/scripting/nodes.md +++ b/docs/scripting/nodes.md @@ -240,14 +240,14 @@ Example: sword in hand. [/Character] [CharacterSkeleton] -parent = Character +parent = @Character [Skeleton3D] skeleton = "res://characters/knight.glb:skeleton[0]" [/Skeleton3D] [/CharacterSkeleton] [CharacterMesh] -parent = Character +parent = @Character [MeshInstance3D] mesh = "res://characters/knight.glb:mesh[0]" skeleton = "CharacterSkeleton" @@ -255,7 +255,7 @@ parent = Character [/CharacterMesh] [RightHandSocket] -parent = Character +parent = @Character [BoneAttachment3D] skeleton = "CharacterSkeleton" bone = 15 @@ -269,7 +269,7 @@ parent = Character [/RightHandSocket] [Sword] -parent = RightHandSocket +parent = @RightHandSocket [MeshInstance3D] mesh = "res://weapons/sword.glb:mesh[0]" material = "res://weapons/sword.pmat" diff --git a/docs/scripting/scene_node_templates.md b/docs/scripting/scene_node_templates.md index 7ed8dbd81..97879db92 100644 --- a/docs/scripting/scene_node_templates.md +++ b/docs/scripting/scene_node_templates.md @@ -5,7 +5,7 @@ Every node template lists the fields it exposes, including fields that default t Conventions used below: -- `parent = PARENTKEY` is a placeholder parent node key (for example `@root` or another node name). +- `parent = @PARENTKEY` is a placeholder parent node key (for example `@root` or another node name). - `script = "res://path/to/script.rs"` is an example script path. - `res://path/to/...` placeholders show expected path shape. @@ -13,7 +13,7 @@ General wrapper (it might look like this): ```text [name] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [Type] @@ -29,7 +29,7 @@ script = "res://path/to/script.rs" ```text [node] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [Node] [/Node] @@ -48,7 +48,7 @@ Think of it as: ### Example ```text -@root = Main +$root = @Main [Main] root_of = "res://shared/player_base.scn" @@ -63,7 +63,7 @@ script_vars = { [/Main] [ExtraHat] -parent = Main +parent = @Main [Sprite2D] texture = "res://cosmetics/hat.png" [Node2D] @@ -109,7 +109,7 @@ root_of = "res://shared/player_base.scn" ```text [node2d] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [Node2D] position = (0, 0) @@ -121,7 +121,7 @@ script = "res://path/to/script.rs" [/node2d] [sprite2d] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [Sprite2D] texture = "res://path/to/texture.png" @@ -136,7 +136,7 @@ script = "res://path/to/script.rs" [/sprite2d] [camera2d] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [Camera2D] zoom = 0.0 @@ -153,7 +153,7 @@ script = "res://path/to/script.rs" [/camera2d] [collision_shape_2d] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [CollisionShape2D] shape = { type = quad width = 1.0 height = 1.0 } @@ -168,7 +168,7 @@ script = "res://path/to/script.rs" [/collision_shape_2d] [static_body_2d] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [StaticBody2D] enabled = true @@ -186,7 +186,7 @@ script = "res://path/to/script.rs" [/static_body_2d] [rigid_body_2d] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [RigidBody2D] enabled = true @@ -212,7 +212,7 @@ script = "res://path/to/script.rs" [/rigid_body_2d] [area2d] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [Area2D] enabled = true @@ -231,7 +231,7 @@ script = "res://path/to/script.rs" ```text [node3d] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [Node3D] position = (0, 0, 0) @@ -242,7 +242,7 @@ script = "res://path/to/script.rs" [/node3d] [mesh_instance_3d] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [MeshInstance3D] mesh = "res://path/to/model.glb:mesh[0]" @@ -272,7 +272,7 @@ script = "res://path/to/script.rs" [/mesh_instance_3d] [multi_mesh_instance_3d] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [MultiMeshInstance3D] mesh = "res://path/to/model.glb:mesh[0]" @@ -311,7 +311,7 @@ script = "res://path/to/script.rs" [/multi_mesh_instance_3d] [camera3d] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [Camera3D] zoom = 0.0 @@ -340,7 +340,7 @@ script = "res://path/to/script.rs" [/camera3d] [collision_shape_3d] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [CollisionShape3D] shape = { type = cube, size = (1, 1, 1) } @@ -357,7 +357,7 @@ script = "res://path/to/script.rs" [/collision_shape_3d] [static_body_3d] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [StaticBody3D] enabled = true @@ -374,7 +374,7 @@ script = "res://path/to/script.rs" [/static_body_3d] [rigid_body_3d] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [RigidBody3D] enabled = true @@ -399,7 +399,7 @@ script = "res://path/to/script.rs" [/rigid_body_3d] [area3d] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [Area3D] enabled = true @@ -413,7 +413,7 @@ script = "res://path/to/script.rs" [/area3d] [skeleton3d] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [Skeleton3D] skeleton = "res://path/to/model.glb:skeleton[0]" @@ -427,7 +427,7 @@ script = "res://path/to/script.rs" [/skeleton3d] [bone_attachment_3d] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [BoneAttachment3D] skeleton = "SkeletonNodeName" @@ -443,7 +443,7 @@ script = "res://path/to/script.rs" [/bone_attachment_3d] [particle_emitter_3d] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [ParticleEmitter3D] active = true @@ -479,7 +479,7 @@ UI templates use ratio-only sizing. ```text [ui_box] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [UiBox] visible = true @@ -506,7 +506,7 @@ script = "res://path/to/script.rs" [/ui_box] [ui_panel] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [UiPanel] fill = (0.11, 0.12, 0.14, 0.92) @@ -539,7 +539,7 @@ script = "res://path/to/script.rs" [/ui_panel] [ui_button] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [UiButton] disabled = false @@ -582,7 +582,7 @@ script = "res://path/to/script.rs" [/ui_button] [ui_label] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [UiLabel] text = "" @@ -620,7 +620,7 @@ script = "res://path/to/script.rs" [/ui_label] [ui_text_box] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [UiTextBox] text = "" @@ -669,7 +669,7 @@ script = "res://path/to/script.rs" [/ui_text_box] [ui_text_block] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [UiTextBlock] text = "" @@ -718,7 +718,7 @@ script = "res://path/to/script.rs" [/ui_text_block] [ui_layout] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [UiLayout] mode = "h" @@ -752,7 +752,7 @@ script = "res://path/to/script.rs" [/ui_layout] [ui_hlayout] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [UiHLayout] spacing = 0.0 @@ -785,7 +785,7 @@ script = "res://path/to/script.rs" [/ui_hlayout] [ui_vlayout] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [UiVLayout] spacing = 0.0 @@ -818,7 +818,7 @@ script = "res://path/to/script.rs" [/ui_vlayout] [ui_grid] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [UiGrid] columns = 1 @@ -850,7 +850,7 @@ script = "res://path/to/script.rs" [/ui_grid] [ui_tree_list] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [UiTreeList] # roots, branches, and collapsed are usually set from script with NodeID values. @@ -890,14 +890,14 @@ Use it for socket nodes, like a sword in a hand. ```text [CharacterSkeleton] -parent = Character +parent = @Character [Skeleton3D] skeleton = "res://characters/hero.glb:skeleton[0]" [/Skeleton3D] [/CharacterSkeleton] [RightHandSocket] -parent = Character +parent = @Character [BoneAttachment3D] skeleton = "CharacterSkeleton" bone = 15 @@ -905,7 +905,7 @@ parent = Character [/RightHandSocket] [Sword] -parent = RightHandSocket +parent = @RightHandSocket [MeshInstance3D] mesh = "res://weapons/sword.glb:mesh[0]" material = "res://weapons/sword.pmat" @@ -923,7 +923,7 @@ parent = RightHandSocket ```text [ambient_light_3d] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [AmbientLight3D] color = (1, 1, 1) @@ -934,7 +934,7 @@ script = "res://path/to/script.rs" [/ambient_light_3d] [sky3d] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [Sky3D] day_colors = [ @@ -972,7 +972,7 @@ script = "res://path/to/script.rs" [/sky3d] [ray_light_3d] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [RayLight3D] color = (1, 1, 1) @@ -990,7 +990,7 @@ script = "res://path/to/script.rs" [/ray_light_3d] [point_light_3d] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [PointLight3D] color = (1, 1, 1) @@ -1008,7 +1008,7 @@ script = "res://path/to/script.rs" [/point_light_3d] [spot_light_3d] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [SpotLight3D] color = (1, 1, 1) @@ -1028,7 +1028,7 @@ script = "res://path/to/script.rs" [/spot_light_3d] [animation_player] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [AnimationPlayer] animation = "res://path/to/clip.panim" @@ -1040,14 +1040,14 @@ script = "res://path/to/script.rs" [/animation_player] [animation_tree] -parent = PARENTKEY +parent = @PARENTKEY script = "res://path/to/script.rs" [AnimationTree] tree = "res://path/to/tree.panimtree" animations = [ - { animation = "res://path/to/idle.panim", bindings = { Hero = PlayerRoot }, playback = loop, speed = 1.0, paused = false }, - { animation = "res://path/to/run.panim", bindings = { Hero = PlayerRoot }, playback = loop, speed = 1.0, paused = false }, - { animation = "res://path/to/aim.panim", bindings = { Hero = PlayerRoot }, playback = boomerang, speed = 1.0, paused = false }, + { animation = "res://path/to/idle.panim", bindings = { Hero = @PlayerRoot }, playback = loop, speed = 1.0, paused = false }, + { animation = "res://path/to/run.panim", bindings = { Hero = @PlayerRoot }, playback = loop, speed = 1.0, paused = false }, + { animation = "res://path/to/aim.panim", bindings = { Hero = @PlayerRoot }, playback = boomerang, speed = 1.0, paused = false }, ] speed = 1.0 paused = false diff --git a/perro_editor/res/editor.scn b/perro_editor/res/editor.scn index a0689f9fe..5f84f019f 100644 --- a/perro_editor/res/editor.scn +++ b/perro_editor/res/editor.scn @@ -1,4 +1,4 @@ -@root = preview_canvas +$root = @preview_canvas [preview_canvas] script = "res://scripts/editor.rs" diff --git a/perro_editor/res/proj_manager.scn b/perro_editor/res/proj_manager.scn index 0b4ce1685..5c66bc264 100644 --- a/perro_editor/res/proj_manager.scn +++ b/perro_editor/res/proj_manager.scn @@ -1,9 +1,9 @@ -@col_bg = "#101318" -@col_panel = "#202733" -@col_stroke = "#8FB3E8" -@col_text = "#E9EEF6" +$col_bg = "#101318" +$col_panel = "#202733" +$col_stroke = "#8FB3E8" +$col_text = "#E9EEF6" -@root = project_manager +$root = @project_manager [project_manager] name = "ProjectManager" @@ -12,27 +12,27 @@ name = "ProjectManager" position_ratio = (0.5, 0.5) pivot_ratio = (0.5, 0.5) size_ratio = (1.0, 1.0) - fill = @col_bg - stroke = @col_bg + fill = $col_bg + stroke = $col_bg stroke_width = 0 [/UiPanel] [/project_manager] [test_panel] -parent = @root +parent = @project_manager [UiPanel] anchor = "center" position_ratio = (0.5, 0.5) pivot_ratio = (0.5, 0.5) size_ratio = (0.35, 0.25) - fill = @col_panel - stroke = @col_stroke + fill = $col_panel + stroke = $col_stroke stroke_width = 1 [/UiPanel] [/test_panel] [test_label] -parent = test_panel +parent = @test_panel [UiLabel] anchor = "center" position_ratio = (0.5, 0.5) @@ -40,6 +40,6 @@ parent = test_panel size_ratio = (1.0, 1.0) text = "CENTER UI TEST" text_size_ratio = 0.22 - color = @col_text + color = $col_text [/UiLabel] [/test_label] diff --git a/perro_source/core/perro_animation/src/panim/events.rs b/perro_source/core/perro_animation/src/panim/events.rs index 9b2d5537e..bbbd2f877 100644 --- a/perro_source/core/perro_animation/src/panim/events.rs +++ b/perro_source/core/perro_animation/src/panim/events.rs @@ -184,16 +184,11 @@ fn is_ident(value: &str) -> bool { } fn parse_scene_value_with_refs(value: &str, line_no: usize) -> Result { - match parse_scene_value(value, line_no) { - Ok(parsed) => Ok(parsed), - Err(_) => { - if !value.contains('@') { - return parse_scene_value(value, line_no); - } - let rewritten = rewrite_reference_tokens(value); - parse_scene_value(&rewritten, line_no) - } + if !value.contains('@') { + return parse_scene_value(value, line_no); } + let rewritten = rewrite_reference_tokens(value); + parse_scene_value(&rewritten, line_no).or_else(|_| parse_scene_value(value, line_no)) } fn rewrite_reference_tokens(src: &str) -> String { @@ -256,4 +251,3 @@ fn rewrite_reference_tokens(src: &str) -> String { out } - diff --git a/perro_source/runtime_project/perro_scene/src/lexer.rs b/perro_source/runtime_project/perro_scene/src/lexer.rs index eadd568fc..3b48dbc70 100644 --- a/perro_source/runtime_project/perro_scene/src/lexer.rs +++ b/perro_source/runtime_project/perro_scene/src/lexer.rs @@ -5,6 +5,7 @@ pub enum Token { String(String), At, // @ + Dollar, // $ Equals, // = Comma, // , LParen, // ( @@ -67,6 +68,7 @@ impl<'a> Lexer<'a> { match c { '@' => Token::At, + '$' => Token::Dollar, '=' => Token::Equals, ',' => Token::Comma, '(' => Token::LParen, diff --git a/perro_source/runtime_project/perro_scene/src/parser.rs b/perro_source/runtime_project/perro_scene/src/parser.rs index e202c8645..37e2cb915 100644 --- a/perro_source/runtime_project/perro_scene/src/parser.rs +++ b/perro_source/runtime_project/perro_scene/src/parser.rs @@ -54,7 +54,7 @@ impl<'a> Parser<'a> { pub(crate) fn collect_var_entries(mut self) -> Vec<(String, SceneValue)> { let mut vars = Vec::new(); while self.current != Token::Eof { - if self.current == Token::At { + if self.current == Token::Dollar { self.advance(); let name = self.expect_ident(); if self.current == Token::Equals { @@ -84,13 +84,19 @@ impl<'a> Parser<'a> { SceneValue::Str(Cow::Owned(v)) } - Token::At => { + Token::Dollar => { self.advance(); let name = self.expect_ident(); self.vars .get(&name) .cloned() - .unwrap_or_else(|| panic!("Unknown variable @{name}")) + .unwrap_or_else(|| panic!("Unknown variable ${name}")) + } + + Token::At => { + self.advance(); + let key = self.expect_ident(); + SceneValue::Key(SceneValueKey::from(key)) } Token::Ident(name) => { @@ -302,22 +308,22 @@ impl<'a> Parser<'a> { tags.push(s.clone()); self.advance(); } - Token::At => { + Token::Dollar => { self.advance(); let name = self.expect_ident(); let resolved = self .vars .get(&name) .cloned() - .unwrap_or_else(|| panic!("Unknown variable @{name}")); + .unwrap_or_else(|| panic!("Unknown variable ${name}")); match resolved { SceneValue::Str(tag) => tags.push(tag.to_string()), SceneValue::Key(key) => tags.push(key.to_string()), - _ => panic!("tags variable @{name} must resolve to a string or key"), + _ => panic!("tags variable ${name} must resolve to a string or key"), } } other => { - panic!("tags entries must be strings, identifiers, or @vars; got {other:?}") + panic!("tags entries must be strings, identifiers, or $vars; got {other:?}") } } @@ -345,23 +351,19 @@ impl<'a> Parser<'a> { while self.current != Token::Eof { match self.current { - Token::At => { + Token::Dollar => { self.advance(); let name = self.expect_ident(); self.expect(Token::Equals); if name == "root" { - match &self.current { - Token::Ident(key) => { - let k = key.clone(); - self.advance(); - root_name = Some(k.clone()); - self.vars.insert( - "root".to_string(), - SceneValue::Key(SceneValueKey::from(k)), - ); + match self.parse_value() { + SceneValue::Key(k) => { + let key = k.to_string(); + root_name = Some(key.clone()); + self.vars.insert("root".to_string(), SceneValue::Key(k)); } - _ => panic!("root must be a scene key"), + _ => panic!("root must be a node ref like @Main"), } } else { let value = self.parse_value(); @@ -411,7 +413,7 @@ impl<'a> Parser<'a> { "parent" => { parent = Some(match v { SceneValue::Key(k) => k.to_string(), - _ => panic!("parent must be a key"), + _ => panic!("parent must be a node ref like @Parent"), }) } "script" => match v { @@ -614,7 +616,7 @@ mod tests { #[test] fn parser_keeps_script_path_string() { - let src = "@root = main\n\n[main]\nscript = \"dlc://test/scripts/script.rs\"\n[/main]\n"; + let src = "$root = main\n\n[main]\nscript = \"dlc://test/scripts/script.rs\"\n[/main]\n"; let scene = Parser::new(src).parse_scene(); let node = &scene.nodes[0]; assert_eq!(node.script.as_deref(), Some("dlc://test/scripts/script.rs")); @@ -622,7 +624,7 @@ mod tests { #[test] fn parser_keeps_root_of_path_string() { - let src = "@root = main\n\n[main]\nroot_of = \"dlc://test/scenes/main.scn\"\n[/main]\n"; + let src = "$root = main\n\n[main]\nroot_of = \"dlc://test/scenes/main.scn\"\n[/main]\n"; let scene = Parser::new(src).parse_scene(); let node = &scene.nodes[0]; assert_eq!(node.root_of.as_deref(), Some("dlc://test/scenes/main.scn")); diff --git a/perro_source/runtime_project/perro_scene/src/scene_doc.rs b/perro_source/runtime_project/perro_scene/src/scene_doc.rs index fd0fc2ad0..34bc47ca6 100644 --- a/perro_source/runtime_project/perro_scene/src/scene_doc.rs +++ b/perro_source/runtime_project/perro_scene/src/scene_doc.rs @@ -98,13 +98,14 @@ impl<'a> SceneDocWriter<'a> { fn write(&self) -> String { let mut out = String::new(); if let Some(root) = &self.doc.scene.root { - out.push_str("@root = "); + out.push_str("$root = "); + out.push('@'); out.push_str(self.doc.scene.key_name_or_id(*root).as_ref()); out.push('\n'); } for (name, value) in self.doc.vars.iter() { - out.push('@'); + out.push('$'); out.push_str(name.as_ref()); out.push_str(" = "); self.write_value(value, &mut out, 0, false); @@ -121,7 +122,7 @@ impl<'a> SceneDocWriter<'a> { if self.doc.vars.iter().any(|(var, _)| var.as_ref() == name) { continue; } - out.push('@'); + out.push('$'); out.push_str(name); out.push_str(" = "); out.push_str(value); @@ -166,6 +167,7 @@ impl<'a> SceneDocWriter<'a> { } if let Some(parent) = &node.parent { out.push_str("parent = "); + out.push('@'); out.push_str(self.doc.scene.key_name_or_id(*parent).as_ref()); out.push('\n'); } @@ -224,7 +226,7 @@ impl<'a> SceneDocWriter<'a> { if dedupe { let key = value_key(value); if let Some(var) = self.value_vars.get(&key) { - out.push('@'); + out.push('$'); out.push_str(var); return; } diff --git a/perro_source/runtime_project/perro_scene/tests/unit/lib_tests.rs b/perro_source/runtime_project/perro_scene/tests/unit/lib_tests.rs index 3d1be8e15..d5a90dd59 100644 --- a/perro_source/runtime_project/perro_scene/tests/unit/lib_tests.rs +++ b/perro_source/runtime_project/perro_scene/tests/unit/lib_tests.rs @@ -11,7 +11,7 @@ fn find_node<'a>(scene: &'a Scene, key: &str) -> &'a SceneNodeEntry { #[test] fn parse_basic_scene() { let src = r#" - @root = main + $root = @main [main] name = "Root Node" @@ -21,7 +21,7 @@ fn parse_basic_scene() { [/main] [player] - parent = @root + parent = @main [Sprite2D] texture = "res://player.png" [/Sprite2D] @@ -43,12 +43,12 @@ fn parse_basic_scene() { #[test] fn parse_object_literal() { let src = r#" - @root = main - @mat = { roughness: 1.0, metallic: 0.2 } + $root = @main + $mat = { roughness: 1.0, metallic: 0.2 } [main] [MeshInstance3D] - material = @mat + material = $mat [/MeshInstance3D] [/main] "#; @@ -161,7 +161,7 @@ fn parse_script_clear_options() { #[test] fn parse_root_of_without_type_block() { let src = r#" - @root = main + $root = @main [main] root_of = "res://base.scn" [/main] @@ -176,7 +176,7 @@ fn parse_root_of_without_type_block() { #[test] fn parse_header_only_node_without_type_block_defaults_to_node() { let src = r#" - @root = root + $root = @root [relationship_manager] parent = @root script = "res://scripts/relationship_manager.rs" @@ -206,8 +206,8 @@ fn parse_header_only_node_without_type_block_defaults_to_node() { #[test] fn scene_doc_writes_valid_scene_and_syncs_children() { let src = r#" - @root = root - @shared = { color: (1, 0, 0, 1), roughness: 0.5 } + $root = @root + $shared = { color: (1, 0, 0, 1), roughness: 0.5 } [root] [Node] @@ -215,9 +215,9 @@ fn scene_doc_writes_valid_scene_and_syncs_children() { [/root] [child] - parent = root + parent = @root [MeshInstance3D] - material = @shared + material = $shared [/MeshInstance3D] [/child] "#; @@ -245,7 +245,7 @@ fn scene_doc_writes_valid_scene_and_syncs_children() { #[test] fn scene_doc_deduplicates_repeated_values() { let src = r#" - @root = a + $root = @a [a] [MeshInstance3D] @@ -263,9 +263,9 @@ fn scene_doc_deduplicates_repeated_values() { let doc = Parser::new(src).parse_scene_doc(); let text = doc.to_text(); assert!( - text.contains("@var1 = { roughness: 1.0, metallic: 0.2, color: (1.0, 1.0, 1.0, 1.0) }") + text.contains("$var1 = { roughness: 1.0, metallic: 0.2, color: (1.0, 1.0, 1.0, 1.0) }") ); - assert_eq!(text.matches("material = @var1").count(), 2); + assert_eq!(text.matches("material = $var1").count(), 2); let reparsed = Parser::new(&text).parse_scene(); assert_eq!(reparsed.nodes.len(), 2); }