diff --git a/core/src/avm1/activation.rs b/core/src/avm1/activation.rs index 667d0b2482da..2c674211d786 100644 --- a/core/src/avm1/activation.rs +++ b/core/src/avm1/activation.rs @@ -652,10 +652,8 @@ impl<'a, 'gc> Activation<'a, 'gc> { let start_clip = self.target_clip_or_root(); let source_clip = self.resolve_target_display_object(start_clip, source, true)?; - if let Some(movie_clip) = source_clip.and_then(|o| o.as_movie_clip()) { - globals::movie_clip::clone_sprite(movie_clip, self.context, target, depth, None); - } else { - avm_warn!(self, "CloneSprite: Source is not a movie clip"); + if let Some(source_clip) = source_clip { + globals::movie_clip::clone_sprite(source_clip, self.context, target, depth, None); } Ok(FrameControl::Continue) diff --git a/core/src/avm1/globals/movie_clip.rs b/core/src/avm1/globals/movie_clip.rs index d591d820a03d..9e9eb543f352 100644 --- a/core/src/avm1/globals/movie_clip.rs +++ b/core/src/avm1/globals/movie_clip.rs @@ -931,7 +931,13 @@ fn duplicate_movie_clip<'gc>( // `duplicateMovieClip` method uses biased depth compared to `CloneSprite`. let depth = depth.wrapping_add(AVM_DEPTH_BIAS); - let new_clip = clone_sprite(movie_clip, activation.context, name, depth, init_object); + let new_clip = clone_sprite( + movie_clip.as_displayobject(), + activation.context, + name, + depth, + init_object, + ); // On SWF<6 undefined is returned. if activation.swf_version() < 6 { @@ -943,14 +949,19 @@ fn duplicate_movie_clip<'gc>( .map_or(Value::Undefined, |o| o.into())) } +/// Clone the given display object with `CloneSprite` semantics. +/// +/// It can be inferred from the docs that `duplicateMovieClip()` works only +/// with MovieClips, but that's not the case. It works with all display +/// objects (including buttons and text). pub fn clone_sprite<'gc>( - movie_clip: MovieClip<'gc>, + sprite: DisplayObject<'gc>, context: &mut UpdateContext<'gc>, target: AvmString<'gc>, depth: Depth, init_object: Option>, -) -> Option> { - let Some(parent) = movie_clip.avm1_parent().and_then(|o| o.as_movie_clip()) else { +) -> Option> { + let Some(parent) = sprite.avm1_parent().and_then(|o| o.as_movie_clip()) else { // Can't duplicate the root! return None; }; @@ -962,39 +973,56 @@ pub fn clone_sprite<'gc>( } let movie = parent.movie(); - let new_clip = if movie_clip.id() != 0 { + let cloned_sprite = if sprite.id() != 0 { // Clip from SWF; instantiate a new copy. let library = context.library.library_for_movie(movie).unwrap(); library - .instantiate_by_id(movie_clip.id(), context.gc()) - .unwrap() - .as_movie_clip() + .instantiate_by_id(sprite.id(), context.gc()) .unwrap() + } else if sprite.as_movie_clip().is_some() { + // Dynamically created MovieClip; create a new empty movie clip. + MovieClip::new(movie, context.gc()).as_displayobject() + } else if let Some(et) = sprite.as_edit_text() { + // Dynamically created TextField; create a new text field. + EditText::new( + context, + movie, + et.x().to_pixels(), + et.y().to_pixels(), + et.width(), + et.height(), + ) + .as_displayobject() } else { - // Dynamically created clip; create a new empty movie clip. - MovieClip::new(movie, context.gc()) + return None; }; - new_clip.set_placed_by_avm1_script(true); + cloned_sprite.set_placed_by_avm1_script(true); // Set name and attach to parent. - new_clip.set_name(context.gc(), target); - parent.replace_at_depth(context, new_clip.into(), depth); + cloned_sprite.set_name(context.gc(), target); + parent.replace_at_depth(context, cloned_sprite, depth); // Copy display properties from previous clip to new clip. - new_clip.set_matrix(movie_clip.base().matrix()); - new_clip.set_color_transform(movie_clip.base().color_transform()); + cloned_sprite.set_matrix(sprite.base().matrix()); + cloned_sprite.set_color_transform(sprite.base().color_transform()); - new_clip.init_clip_event_handlers(movie_clip.clip_actions().into()); + // Copy properties of MovieClip + if let (Some(cloned_sprite), Some(sprite)) = + (cloned_sprite.as_movie_clip(), sprite.as_movie_clip()) + { + cloned_sprite.init_clip_event_handlers(sprite.clip_actions().into()); - if let Some(drawing) = movie_clip.drawing().as_deref().cloned() { - *new_clip.drawing_mut() = drawing; + if let Some(drawing) = sprite.drawing().as_deref().cloned() { + *cloned_sprite.drawing_mut() = drawing; + } } + // TODO: Any other properties we should copy...? // Definitely not Object properties. - new_clip.post_instantiation(context, init_object, Instantiator::Avm1, true); + cloned_sprite.post_instantiation(context, init_object, Instantiator::Avm1, true); - Some(new_clip) + Some(cloned_sprite) } fn get_bytes_loaded<'gc>( diff --git a/tests/tests/swfs/avm1/clone_sprite_types/output.ruffle.txt b/tests/tests/swfs/avm1/clone_sprite_types/output.ruffle.txt new file mode 100644 index 000000000000..1005b95bbd53 --- /dev/null +++ b/tests/tests/swfs/avm1/clone_sprite_types/output.ruffle.txt @@ -0,0 +1,24 @@ +Timeline objects: +_level0.timeline_button +_level0.timeline_edittext +_level0 +_level0.timeline_sprite +_level0 +_level0 +undefined +_level0.timeline_video +Timeline clones: +_level0.timeline_button_2 +_level0.timeline_edittext_2 +undefined +_level0.timeline_sprite_2 +undefined +undefined +undefined +_level0.timeline_video_2 +Script objects: +_level0.script_movieclip +_level0.script_textfield +Script clones: +_level0.script_movieclip_2 +_level0.script_textfield_2 diff --git a/tests/tests/swfs/avm1/clone_sprite_types/output.txt b/tests/tests/swfs/avm1/clone_sprite_types/output.txt new file mode 100644 index 000000000000..1b9cd6935028 --- /dev/null +++ b/tests/tests/swfs/avm1/clone_sprite_types/output.txt @@ -0,0 +1,24 @@ +Timeline objects: +_level0.timeline_button +_level0.timeline_edittext +_level0 +_level0.timeline_sprite +_level0 +_level0 +_level0 +_level0.timeline_video +Timeline clones: +_level0.timeline_button_2 +_level0.timeline_edittext_2 +undefined +_level0.timeline_sprite_2 +undefined +undefined +undefined +_level0.timeline_video_2 +Script objects: +_level0.script_movieclip +_level0.script_textfield +Script clones: +_level0.script_movieclip_2 +_level0.script_textfield_2 diff --git a/tests/tests/swfs/avm1/clone_sprite_types/test.as b/tests/tests/swfs/avm1/clone_sprite_types/test.as new file mode 100644 index 000000000000..4836ff01e6ff --- /dev/null +++ b/tests/tests/swfs/avm1/clone_sprite_types/test.as @@ -0,0 +1,42 @@ +trace("Timeline objects:"); +trace(timeline_button); +trace(timeline_edittext); +trace(timeline_statictext); +trace(timeline_sprite); +trace(timeline_shape); +trace(timeline_morphshape); +trace(timeline_image); +trace(timeline_video); + +duplicateMovieClip(timeline_button, "timeline_button_2", 101); +duplicateMovieClip(timeline_edittext, "timeline_edittext_2", 102); +duplicateMovieClip(timeline_statictext, "timeline_statictext_2", 103); +duplicateMovieClip(timeline_sprite, "timeline_sprite_2", 104); +duplicateMovieClip(timeline_shape, "timeline_shape_2", 105); +duplicateMovieClip(timeline_morphshape, "timeline_morphshape_2", 106); +duplicateMovieClip(timeline_image, "timeline_image_2", 107); +duplicateMovieClip(timeline_video, "timeline_video_2", 108); + +trace("Timeline clones:"); +trace(timeline_button_2); +trace(timeline_edittext_2); +trace(timeline_statictext_2); +trace(timeline_sprite_2); +trace(timeline_shape_2); +trace(timeline_morphshape_2); +trace(timeline_image_2); +trace(timeline_video_2); + +createEmptyMovieClip("script_movieclip", 201); +createTextField("script_textfield", 202, 0, 0, 10, 10); + +trace("Script objects:"); +trace(script_movieclip); +trace(script_textfield); + +duplicateMovieClip(script_movieclip, "script_movieclip_2", 301); +duplicateMovieClip(script_textfield, "script_textfield_2", 302); + +trace("Script clones:"); +trace(script_movieclip_2); +trace(script_textfield_2); diff --git a/tests/tests/swfs/avm1/clone_sprite_types/test.swf b/tests/tests/swfs/avm1/clone_sprite_types/test.swf new file mode 100644 index 000000000000..a994345308e6 Binary files /dev/null and b/tests/tests/swfs/avm1/clone_sprite_types/test.swf differ diff --git a/tests/tests/swfs/avm1/clone_sprite_types/test.toml b/tests/tests/swfs/avm1/clone_sprite_types/test.toml new file mode 100644 index 000000000000..29f3cef79022 --- /dev/null +++ b/tests/tests/swfs/avm1/clone_sprite_types/test.toml @@ -0,0 +1,2 @@ +num_ticks = 1 +known_failure = true