core: Allow MorphShapes and Texts to be replaced via PlaceObject

PR #4540 allowed shapes to replace their inner art handles via
replace PlaceObject tags. This can also happen for morph shapes and
static texts.

Add MorphShape::replace_with and Text::repalce_with. This fixes the
incorrect art in main menu buttons of Super Mario 63.
This commit is contained in:
Mike Welsh 2021-06-09 22:59:35 -07:00
parent 5135b14023
commit 5462b8a522
4 changed files with 47 additions and 4 deletions

View File

@ -147,9 +147,8 @@ 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.
// Static assets like graphics can replace themselves via a PlaceObject tag with PlaceObjectAction::Replace.
// This does not create a new instance, but instead swaps out the underlying static data to point to the new art.
if let Some(new_graphic) = context
.library
.library_for_movie_mut(self.movie().unwrap())

View File

@ -55,6 +55,18 @@ impl<'gc> TDisplayObject<'gc> for MorphShape<'gc> {
Some(*self)
}
fn replace_with(&self, context: &mut UpdateContext<'_, 'gc, '_>, id: CharacterId) {
if let Some(new_morph_shape) = context
.library
.library_for_movie_mut(self.movie().unwrap())
.get_morph_shape(id)
{
self.0.write(context.gc_context).static_data = new_morph_shape.0.read().static_data;
} else {
log::warn!("PlaceObject: expected morph shape at character ID {}", id);
}
}
fn run_frame(&self, _context: &mut UpdateContext) {
// Noop
}

View File

@ -65,6 +65,18 @@ impl<'gc> TDisplayObject<'gc> for Text<'gc> {
Some(self.0.read().static_data.swf.clone())
}
fn replace_with(&self, context: &mut UpdateContext<'_, 'gc, '_>, id: CharacterId) {
if let Some(new_text) = context
.library
.library_for_movie_mut(self.movie().unwrap())
.get_text(id)
{
self.0.write(context.gc_context).static_data = new_text.0.read().static_data;
} else {
log::warn!("PlaceObject: expected text at character ID {}", id);
}
}
fn run_frame(&self, _context: &mut UpdateContext) {
// Noop
}

View File

@ -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, Graphic, TDisplayObject};
use crate::display_object::{Bitmap, Graphic, MorphShape, TDisplayObject, Text};
use crate::font::{Font, FontDescriptor};
use crate::prelude::*;
use crate::tag_utils::SwfMovie;
@ -297,6 +297,16 @@ impl<'gc> MovieLibrary<'gc> {
}
}
/// Returns the `MorphShape` with the given character ID.
/// Returns `None` if the ID does not exist or is not a `MorphShape`.
pub fn get_morph_shape(&self, id: CharacterId) -> Option<MorphShape<'gc>> {
if let Some(&Character::MorphShape(morph_shape)) = self.characters.get(&id) {
Some(morph_shape)
} else {
None
}
}
pub fn get_sound(&self, id: CharacterId) -> Option<SoundHandle> {
if let Some(Character::Sound(sound)) = self.characters.get(&id) {
Some(*sound)
@ -305,6 +315,16 @@ impl<'gc> MovieLibrary<'gc> {
}
}
/// Returns the `Text` with the given character ID.
/// Returns `None` if the ID does not exist or is not a `Text`.
pub fn get_text(&self, id: CharacterId) -> Option<Text<'gc>> {
if let Some(&Character::Text(text)) = self.characters.get(&id) {
Some(text)
} else {
None
}
}
pub fn set_jpeg_tables(&mut self, data: Vec<u8>) {
if self.jpeg_tables.is_some() {
// SWF spec says there should only be one JPEGTables tag.