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(
|
/// Called when this object should be replaced by a PlaceObject tag.
|
||||||
&self,
|
#[allow(unused_variables)]
|
||||||
gc_context: MutationContext<'gc, '_>,
|
fn replace_with(&self, context: &mut UpdateContext<'_, 'gc, '_>, id: CharacterId) {
|
||||||
other: DisplayObject<'gc>,
|
// Noop for most symbols; only shapes can replace their innards with another graphic.
|
||||||
) {
|
|
||||||
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.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn object(&self) -> Avm1Value<'gc> {
|
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) {
|
fn run_frame(&self, _context: &mut UpdateContext) {
|
||||||
// Noop
|
// Noop
|
||||||
}
|
}
|
||||||
|
|
|
@ -1092,14 +1092,12 @@ impl<'gc> MovieClip<'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Instantiate a given child object on the timeline at a given depth.
|
/// Instantiate a given child object on the timeline at a given depth.
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
fn instantiate_child(
|
fn instantiate_child(
|
||||||
self,
|
self,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
id: CharacterId,
|
id: CharacterId,
|
||||||
depth: Depth,
|
depth: Depth,
|
||||||
place_object: &swf::PlaceObject,
|
place_object: &swf::PlaceObject,
|
||||||
copy_previous_properties: bool,
|
|
||||||
) -> Option<DisplayObject<'gc>> {
|
) -> Option<DisplayObject<'gc>> {
|
||||||
match context
|
match context
|
||||||
.library
|
.library
|
||||||
|
@ -1122,11 +1120,7 @@ impl<'gc> MovieClip<'gc> {
|
||||||
} else {
|
} else {
|
||||||
child.set_place_frame(context.gc_context, self.current_frame());
|
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.
|
// Run first frame.
|
||||||
child.apply_place_object(context, self.movie(), place_object);
|
child.apply_place_object(context, self.movie(), place_object);
|
||||||
child.construct_frame(context);
|
child.construct_frame(context);
|
||||||
|
@ -1317,7 +1311,6 @@ impl<'gc> MovieClip<'gc> {
|
||||||
params.id(),
|
params.id(),
|
||||||
params.depth(),
|
params.depth(),
|
||||||
¶ms.place_object,
|
¶ms.place_object,
|
||||||
params.modifies_original_item(),
|
|
||||||
) {
|
) {
|
||||||
// Set the place frame to the frame where the object *would* have been placed.
|
// Set the place frame to the frame where the object *would* have been placed.
|
||||||
child.set_place_frame(context.gc_context, params.frame);
|
child.set_place_frame(context.gc_context, params.frame);
|
||||||
|
@ -3114,28 +3107,28 @@ impl<'gc, 'a> MovieClip<'gc> {
|
||||||
}?;
|
}?;
|
||||||
use swf::PlaceObjectAction;
|
use swf::PlaceObjectAction;
|
||||||
match place_object.action {
|
match place_object.action {
|
||||||
PlaceObjectAction::Place(id) | PlaceObjectAction::Replace(id) => {
|
PlaceObjectAction::Place(id) => {
|
||||||
if let Some(child) = self.instantiate_child(
|
self.instantiate_child(context, id, place_object.depth.into(), &place_object);
|
||||||
context,
|
}
|
||||||
id,
|
PlaceObjectAction::Replace(id) => {
|
||||||
place_object.depth.into(),
|
if let Some(child) = self.child_by_depth(place_object.depth.into()) {
|
||||||
&place_object,
|
child.replace_with(context, id);
|
||||||
matches!(place_object.action, PlaceObjectAction::Replace(_)),
|
child.apply_place_object(context, self.movie(), &place_object);
|
||||||
) {
|
if child.avm_type() == AvmType::Avm2 {
|
||||||
child
|
// In AVM2 instantiation happens before frame advance so we
|
||||||
} else {
|
// have to special-case that
|
||||||
return Ok(());
|
child.set_place_frame(context.gc_context, self.current_frame() + 1);
|
||||||
|
} else {
|
||||||
|
child.set_place_frame(context.gc_context, self.current_frame());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PlaceObjectAction::Modify => {
|
PlaceObjectAction::Modify => {
|
||||||
if let Some(child) = self.child_by_depth(place_object.depth.into()) {
|
if let Some(child) = self.child_by_depth(place_object.depth.into()) {
|
||||||
child.apply_place_object(context, self.movie(), &place_object);
|
child.apply_place_object(context, self.movie(), &place_object);
|
||||||
child
|
|
||||||
} else {
|
|
||||||
return Ok(());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
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]
|
#[inline]
|
||||||
fn depth(&self) -> Depth {
|
fn depth(&self) -> Depth {
|
||||||
self.place_object.depth.into()
|
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::avm2::{Domain as Avm2Domain, Object as Avm2Object};
|
||||||
use crate::backend::audio::SoundHandle;
|
use crate::backend::audio::SoundHandle;
|
||||||
use crate::character::Character;
|
use crate::character::Character;
|
||||||
use crate::display_object::{Bitmap, TDisplayObject};
|
use crate::display_object::{Bitmap, Graphic, TDisplayObject};
|
||||||
use crate::font::{Font, FontDescriptor};
|
use crate::font::{Font, FontDescriptor};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::tag_utils::SwfMovie;
|
use crate::tag_utils::SwfMovie;
|
||||||
|
@ -287,6 +287,16 @@ impl<'gc> MovieLibrary<'gc> {
|
||||||
self.fonts.get(&descriptor).copied()
|
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> {
|
pub fn get_sound(&self, id: CharacterId) -> Option<SoundHandle> {
|
||||||
if let Some(Character::Sound(sound)) = self.characters.get(&id) {
|
if let Some(Character::Sound(sound)) = self.characters.get(&id) {
|
||||||
Some(*sound)
|
Some(*sound)
|
||||||
|
|
Loading…
Reference in New Issue