core: PlaceObjectAction::Replace swaps out graphic handles
A `PlaceObjectAction::Replace` signals that a shape should be swapped with a different shape. Previously we instantiated a completely new `Graphic`, but this is incorrect; instead the underlying shape handle should be swapped out, but the outer object remains. This is visible in AVM2 where you can access `Shape` as a normal display object.
This commit is contained in:
parent
5acc476789
commit
b1318ecb01
|
@ -1114,23 +1114,10 @@ pub trait TDisplayObject<'gc>:
|
|||
}
|
||||
}
|
||||
|
||||
fn copy_display_properties_from(
|
||||
&self,
|
||||
gc_context: MutationContext<'gc, '_>,
|
||||
other: DisplayObject<'gc>,
|
||||
) {
|
||||
self.set_matrix(gc_context, &*other.matrix());
|
||||
self.set_color_transform(gc_context, &*other.color_transform());
|
||||
self.set_clip_depth(gc_context, other.clip_depth());
|
||||
self.set_name(gc_context, &*other.name());
|
||||
if let (Some(mut me), Some(other)) = (self.as_morph_shape(), other.as_morph_shape()) {
|
||||
me.set_ratio(gc_context, other.ratio());
|
||||
}
|
||||
// onEnterFrame actions only apply to movie clips.
|
||||
if let (Some(me), Some(other)) = (self.as_movie_clip(), other.as_movie_clip()) {
|
||||
me.set_clip_actions(gc_context, other.clip_actions().iter().cloned().collect());
|
||||
}
|
||||
// TODO: More in here eventually.
|
||||
/// Called when this object should be replaced by a PlaceObject tag.
|
||||
#[allow(unused_variables)]
|
||||
fn replace_with(&self, context: &mut UpdateContext<'_, 'gc, '_>, id: CharacterId) {
|
||||
// Noop for most symbols; only shapes can replace their innards with another graphic.
|
||||
}
|
||||
|
||||
fn object(&self) -> Avm1Value<'gc> {
|
||||
|
|
|
@ -146,6 +146,21 @@ impl<'gc> TDisplayObject<'gc> for Graphic<'gc> {
|
|||
}
|
||||
}
|
||||
|
||||
fn replace_with(&self, context: &mut UpdateContext<'_, 'gc, '_>, id: CharacterId) {
|
||||
// Only a graphic can replace themselves with another graphic via a Replace PlaceObject tag.
|
||||
// This does not create a new graphic instance, but instead swaps out the underlying handle to point to
|
||||
// the new art.
|
||||
if let Some(new_graphic) = context
|
||||
.library
|
||||
.library_for_movie_mut(self.movie().unwrap())
|
||||
.get_graphic(id)
|
||||
{
|
||||
self.0.write(context.gc_context).static_data = new_graphic.0.read().static_data;
|
||||
} else {
|
||||
log::warn!("PlaceObject: expected graphic at character ID {}", id);
|
||||
}
|
||||
}
|
||||
|
||||
fn run_frame(&self, _context: &mut UpdateContext) {
|
||||
// Noop
|
||||
}
|
||||
|
|
|
@ -1092,14 +1092,12 @@ impl<'gc> MovieClip<'gc> {
|
|||
}
|
||||
|
||||
/// Instantiate a given child object on the timeline at a given depth.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn instantiate_child(
|
||||
self,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
id: CharacterId,
|
||||
depth: Depth,
|
||||
place_object: &swf::PlaceObject,
|
||||
copy_previous_properties: bool,
|
||||
) -> Option<DisplayObject<'gc>> {
|
||||
match context
|
||||
.library
|
||||
|
@ -1122,11 +1120,7 @@ impl<'gc> MovieClip<'gc> {
|
|||
} else {
|
||||
child.set_place_frame(context.gc_context, self.current_frame());
|
||||
}
|
||||
if copy_previous_properties {
|
||||
if let Some(prev_child) = prev_child {
|
||||
child.copy_display_properties_from(context.gc_context, prev_child);
|
||||
}
|
||||
}
|
||||
|
||||
// Run first frame.
|
||||
child.apply_place_object(context, self.movie(), place_object);
|
||||
child.construct_frame(context);
|
||||
|
@ -1317,7 +1311,6 @@ impl<'gc> MovieClip<'gc> {
|
|||
params.id(),
|
||||
params.depth(),
|
||||
¶ms.place_object,
|
||||
params.modifies_original_item(),
|
||||
) {
|
||||
// Set the place frame to the frame where the object *would* have been placed.
|
||||
child.set_place_frame(context.gc_context, params.frame);
|
||||
|
@ -3114,28 +3107,28 @@ impl<'gc, 'a> MovieClip<'gc> {
|
|||
}?;
|
||||
use swf::PlaceObjectAction;
|
||||
match place_object.action {
|
||||
PlaceObjectAction::Place(id) | PlaceObjectAction::Replace(id) => {
|
||||
if let Some(child) = self.instantiate_child(
|
||||
context,
|
||||
id,
|
||||
place_object.depth.into(),
|
||||
&place_object,
|
||||
matches!(place_object.action, PlaceObjectAction::Replace(_)),
|
||||
) {
|
||||
child
|
||||
PlaceObjectAction::Place(id) => {
|
||||
self.instantiate_child(context, id, place_object.depth.into(), &place_object);
|
||||
}
|
||||
PlaceObjectAction::Replace(id) => {
|
||||
if let Some(child) = self.child_by_depth(place_object.depth.into()) {
|
||||
child.replace_with(context, id);
|
||||
child.apply_place_object(context, self.movie(), &place_object);
|
||||
if child.avm_type() == AvmType::Avm2 {
|
||||
// In AVM2 instantiation happens before frame advance so we
|
||||
// have to special-case that
|
||||
child.set_place_frame(context.gc_context, self.current_frame() + 1);
|
||||
} else {
|
||||
return Ok(());
|
||||
child.set_place_frame(context.gc_context, self.current_frame());
|
||||
}
|
||||
}
|
||||
}
|
||||
PlaceObjectAction::Modify => {
|
||||
if let Some(child) = self.child_by_depth(place_object.depth.into()) {
|
||||
child.apply_place_object(context, self.movie(), &place_object);
|
||||
child
|
||||
} else {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -3373,14 +3366,6 @@ impl<'a> GotoPlaceObject<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn modifies_original_item(&self) -> bool {
|
||||
matches!(
|
||||
&self.place_object.action,
|
||||
swf::PlaceObjectAction::Replace(_)
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn depth(&self) -> Depth {
|
||||
self.place_object.depth.into()
|
||||
|
|
|
@ -3,7 +3,7 @@ use crate::avm1::property_map::PropertyMap as Avm1PropertyMap;
|
|||
use crate::avm2::{Domain as Avm2Domain, Object as Avm2Object};
|
||||
use crate::backend::audio::SoundHandle;
|
||||
use crate::character::Character;
|
||||
use crate::display_object::{Bitmap, TDisplayObject};
|
||||
use crate::display_object::{Bitmap, Graphic, TDisplayObject};
|
||||
use crate::font::{Font, FontDescriptor};
|
||||
use crate::prelude::*;
|
||||
use crate::tag_utils::SwfMovie;
|
||||
|
@ -287,6 +287,16 @@ impl<'gc> MovieLibrary<'gc> {
|
|||
self.fonts.get(&descriptor).copied()
|
||||
}
|
||||
|
||||
/// Returns the graphic with the given character ID.
|
||||
/// Returns `None` if the ID does not exist or is not a `Graphic`.
|
||||
pub fn get_graphic(&self, id: CharacterId) -> Option<Graphic<'gc>> {
|
||||
if let Some(&Character::Graphic(graphic)) = self.characters.get(&id) {
|
||||
Some(graphic)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_sound(&self, id: CharacterId) -> Option<SoundHandle> {
|
||||
if let Some(Character::Sound(sound)) = self.characters.get(&id) {
|
||||
Some(*sound)
|
||||
|
|
Loading…
Reference in New Issue