From dbbffe22b7e586c4567de7c3aaf02f7bc1dd505d Mon Sep 17 00:00:00 2001 From: Priyanshu Dangare Date: Mon, 21 Sep 2026 01:04:12 +0530 Subject: [PATCH] feat: attribute one random custom block to goboscript --- src/codegen/expr.rs | 1 + src/codegen/mutation.rs | 11 ++++++++++ src/codegen/sb3.rs | 45 +++++++++++++++++++++++++++++++++++++++++ src/codegen/stmt.rs | 1 + 4 files changed, 58 insertions(+) diff --git a/src/codegen/expr.rs b/src/codegen/expr.rs index 76440da5..edd2bcf6 100644 --- a/src/codegen/expr.rs +++ b/src/codegen/expr.rs @@ -454,6 +454,7 @@ impl Sb3 { self.json, "{}", Mutation::call(func.name.clone(), &qualified_args, true, false) + .with_attribution(self.has_attribution(&func.name, true)) )?; self.end_obj()?; // node for (arg, (_, arg_id)) in qualified_arg_values.iter().zip(qualified_args) { diff --git a/src/codegen/mutation.rs b/src/codegen/mutation.rs index dbfd691e..9c766485 100644 --- a/src/codegen/mutation.rs +++ b/src/codegen/mutation.rs @@ -15,6 +15,7 @@ pub struct Mutation<'a> { warp: bool, is_call: bool, compact: bool, + attribution: bool, } impl<'a> Mutation<'a> { @@ -30,6 +31,7 @@ impl<'a> Mutation<'a> { warp, is_call: false, compact, + attribution: false, } } @@ -45,8 +47,14 @@ impl<'a> Mutation<'a> { warp, is_call: true, compact, + attribution: false, } } + + pub fn with_attribution(mut self, attribution: bool) -> Self { + self.attribution = attribution; + self + } } impl Display for Mutation<'_> { @@ -61,6 +69,9 @@ impl Display for Mutation<'_> { write!(f, " {arg_name}: %s")?; } } + if self.attribution { + write!(f, " (via goboscript)")?; + } write!(f, "\"")?; write!(f, r#","argumentids":"["#)?; let mut comma = false; diff --git a/src/codegen/sb3.rs b/src/codegen/sb3.rs index 52f37b24..c9275774 100644 --- a/src/codegen/sb3.rs +++ b/src/codegen/sb3.rs @@ -333,6 +333,7 @@ pub struct Sb3 { pub block_count: usize, pub asset_object_store: AssetObjectStore, extensions: Extensions, + attributed_block: Option<(SmolStr, bool)>, } impl Sb3 { @@ -345,9 +346,18 @@ impl Sb3 { block_count: 0, asset_object_store: AssetObjectStore::new(input, fs), extensions: Extensions::default(), + attributed_block: None, } } + pub fn has_attribution(&self, name: &str, is_function: bool) -> bool { + self.attributed_block + .as_ref() + .is_some_and(|(selected_name, selected_is_function)| { + selected_name.as_str() == name && *selected_is_function == is_function + }) + } + pub fn begin_node(&mut self, node: Node) -> io::Result<()> { self.block_count += 1; if node.opcode.starts_with("pen_") { @@ -400,6 +410,37 @@ impl Sb3 { sprites_diagnostics: &mut FxHashMap, ) -> anyhow::Result<()> { let layers = compute_layers(project, config)?; + let candidates: Vec<_> = std::iter::once(&project.stage) + .chain(project.sprites.values()) + .flat_map(|sprite| { + sprite + .procs + .keys() + .filter(|name| sprite.used_procs.contains(*name)) + .map(move |name| (sprite, name, false)) + .chain( + sprite + .funcs + .keys() + .filter(|name| sprite.used_funcs.contains(*name)) + .map(move |name| (sprite, name, true)), + ) + }) + .collect(); + let selected = if candidates.is_empty() { + None + } else { + #[cfg(not(target_arch = "wasm32"))] + let index = rand::random_range(0..candidates.len()); + #[cfg(target_arch = "wasm32")] + let index = (js_sys::Math::random() * candidates.len() as f64) as usize; + Some(candidates[index]) + }; + let attribution_for = |sprite: &Sprite| { + selected + .filter(|(selected_sprite, _, _)| std::ptr::eq(*selected_sprite, sprite)) + .map(|(_, name, is_function)| (name.clone(), is_function)) + }; let broadcasts: FxHashSet<_> = project .stage .events @@ -415,6 +456,7 @@ impl Sb3 { .collect(); write!(self.json, "{{")?; write!(self.json, r#""targets":["#)?; + self.attributed_block = attribution_for(&project.stage); self.sprite( fs.clone(), input, @@ -430,6 +472,7 @@ impl Sb3 { sprite_names.sort(); for sprite_name in sprite_names { write!(self.json, r#","#)?; + self.attributed_block = attribution_for(&project.sprites[sprite_name]); self.sprite( fs.clone(), input, @@ -1044,6 +1087,7 @@ impl Sb3 { self.json, "{}", Mutation::prototype(proc.name.clone(), &qualified_args, proc.warp, false) + .with_attribution(self.has_attribution(&proc.name, false)) )?; self.end_obj()?; // node self.stmts(s, d, definition, next_id, Some(this_id)) @@ -1118,6 +1162,7 @@ impl Sb3 { self.json, "{}", Mutation::prototype(func.name.clone(), &qualified_args, true, false) + .with_attribution(self.has_attribution(&func.name, true)) )?; self.end_obj()?; // node self.stmts(s, d, definition, next_id, Some(this_id)) diff --git a/src/codegen/stmt.rs b/src/codegen/stmt.rs index 9614d793..2d21cb5b 100644 --- a/src/codegen/stmt.rs +++ b/src/codegen/stmt.rs @@ -604,6 +604,7 @@ impl Sb3 { self.json, "{}", Mutation::call(proc.name.clone(), &qualified_args, proc.warp, compact) + .with_attribution(self.has_attribution(&proc.name, false)) )?; self.end_obj()?; // node for (arg, (_, arg_id)) in qualified_arg_values.iter().zip(qualified_args) {