core: More goto fixes
This commit is contained in:
parent
ae80900f17
commit
a331c565b3
|
@ -1195,7 +1195,8 @@ impl<'gc> Avm1<'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn action_stop_sounds(&mut self, _context: &mut ActionContext) -> Result<(), Error> {
|
fn action_stop_sounds(&mut self, _context: &mut ActionContext) -> Result<(), Error> {
|
||||||
Err("Unimplemented action: StopSounds".into())
|
log::error!("Unimplemented action: StopSounds");
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn action_store_register(
|
fn action_store_register(
|
||||||
|
|
|
@ -354,8 +354,12 @@ impl<'gc> MovieClip<'gc> {
|
||||||
TagCode::PlaceObject4 => {
|
TagCode::PlaceObject4 => {
|
||||||
self.goto_place_object(reader, tag_len, 4, &mut goto_commands)
|
self.goto_place_object(reader, tag_len, 4, &mut goto_commands)
|
||||||
}
|
}
|
||||||
TagCode::RemoveObject => self.goto_remove_object(reader, 1, &mut goto_commands),
|
TagCode::RemoveObject => {
|
||||||
TagCode::RemoveObject2 => self.goto_remove_object(reader, 2, &mut goto_commands),
|
self.goto_remove_object(reader, 1, &mut goto_commands, is_rewind)
|
||||||
|
}
|
||||||
|
TagCode::RemoveObject2 => {
|
||||||
|
self.goto_remove_object(reader, 2, &mut goto_commands, is_rewind)
|
||||||
|
}
|
||||||
_ => Ok(()),
|
_ => Ok(()),
|
||||||
};
|
};
|
||||||
let _ = tag_utils::decode_tags(&mut reader, tag_callback, TagCode::ShowFrame);
|
let _ = tag_utils::decode_tags(&mut reader, tag_callback, TagCode::ShowFrame);
|
||||||
|
@ -369,7 +373,7 @@ impl<'gc> MovieClip<'gc> {
|
||||||
// but BTreeMap::retain does not exist.
|
// but BTreeMap::retain does not exist.
|
||||||
let mut children = std::mem::replace(&mut self.children, BTreeMap::new());
|
let mut children = std::mem::replace(&mut self.children, BTreeMap::new());
|
||||||
goto_commands.into_iter().for_each(|(depth, params)| {
|
goto_commands.into_iter().for_each(|(depth, params)| {
|
||||||
let (needs_run, child) = match children.get_mut(&depth).copied() {
|
let (was_instantiated, child) = match children.get_mut(&depth).copied() {
|
||||||
// For rewinds, if an object was created before the final frame,
|
// For rewinds, if an object was created before the final frame,
|
||||||
// it will exist on the final frame as well. Re-use this object
|
// it will exist on the final frame as well. Re-use this object
|
||||||
// instead of recreating.
|
// instead of recreating.
|
||||||
|
@ -391,13 +395,15 @@ impl<'gc> MovieClip<'gc> {
|
||||||
// Apply final delta to display pamareters.
|
// Apply final delta to display pamareters.
|
||||||
let child_node = child;
|
let child_node = child;
|
||||||
let mut child = child.write(context.gc_context);
|
let mut child = child.write(context.gc_context);
|
||||||
child.apply_place_object(params);
|
child.apply_place_object(params.place_object);
|
||||||
|
if was_instantiated {
|
||||||
// We must run newly created objects for one frame
|
// Set the placement frame for the new object to the frame
|
||||||
// to ensure they place any children objects.
|
// it is actually created on.
|
||||||
// TODO: This will probably move as our order-of-execution
|
child.set_place_frame(params.frame);
|
||||||
// becomes more accurate.
|
// We must run newly created objects for one frame
|
||||||
if needs_run {
|
// to ensure they place any children objects.
|
||||||
|
// TODO: This will probably move as our order-of-execution
|
||||||
|
// becomes more accurate.
|
||||||
context.active_clip = child_node;
|
context.active_clip = child_node;
|
||||||
child.run_frame(context);
|
child.run_frame(context);
|
||||||
context.active_clip = prev_active_clip;
|
context.active_clip = prev_active_clip;
|
||||||
|
@ -423,13 +429,15 @@ impl<'gc> MovieClip<'gc> {
|
||||||
// Apply final delta to display pamareters.
|
// Apply final delta to display pamareters.
|
||||||
let child_node = child;
|
let child_node = child;
|
||||||
let mut child = child.write(context.gc_context);
|
let mut child = child.write(context.gc_context);
|
||||||
child.apply_place_object(params);
|
child.apply_place_object(params.place_object);
|
||||||
|
|
||||||
// We must run newly created objects for one frame
|
|
||||||
// to ensure they place any children objects.
|
|
||||||
// TODO: This will probably move as our order-of-execution
|
|
||||||
// becomes more accurate.
|
|
||||||
if id != 0 {
|
if id != 0 {
|
||||||
|
// Set the placement frame for the new object to the frame
|
||||||
|
// it is actually created on.
|
||||||
|
child.set_place_frame(params.frame);
|
||||||
|
// We must run newly created objects for one frame
|
||||||
|
// to ensure they place any children objects.
|
||||||
|
// TODO: This will probably move as our order-of-execution
|
||||||
|
// becomes more accurate.
|
||||||
context.active_clip = child_node;
|
context.active_clip = child_node;
|
||||||
child.run_frame(context);
|
child.run_frame(context);
|
||||||
context.active_clip = prev_active_clip;
|
context.active_clip = prev_active_clip;
|
||||||
|
@ -449,9 +457,9 @@ impl<'gc> MovieClip<'gc> {
|
||||||
reader: &mut SwfStream<&'a [u8]>,
|
reader: &mut SwfStream<&'a [u8]>,
|
||||||
tag_len: usize,
|
tag_len: usize,
|
||||||
version: u8,
|
version: u8,
|
||||||
goto_commands: &mut fnv::FnvHashMap<Depth, swf::PlaceObject>,
|
goto_commands: &mut fnv::FnvHashMap<Depth, GotoPlaceObject>,
|
||||||
) -> DecodeResult {
|
) -> DecodeResult {
|
||||||
let mut place_object = if version == 1 {
|
let place_object = if version == 1 {
|
||||||
reader.read_place_object(tag_len)
|
reader.read_place_object(tag_len)
|
||||||
} else {
|
} else {
|
||||||
reader.read_place_object_2_or_3(version)
|
reader.read_place_object_2_or_3(version)
|
||||||
|
@ -459,10 +467,14 @@ impl<'gc> MovieClip<'gc> {
|
||||||
|
|
||||||
// We merge the deltas from this PlaceObject with the previous command.
|
// We merge the deltas from this PlaceObject with the previous command.
|
||||||
let depth = place_object.depth;
|
let depth = place_object.depth;
|
||||||
|
let mut goto_place = GotoPlaceObject {
|
||||||
|
frame: self.current_frame,
|
||||||
|
place_object,
|
||||||
|
};
|
||||||
goto_commands
|
goto_commands
|
||||||
.entry(depth)
|
.entry(depth)
|
||||||
.and_modify(|prev_place| prev_place.merge(&mut place_object))
|
.and_modify(|prev_place| prev_place.merge(&mut goto_place))
|
||||||
.or_insert(place_object);
|
.or_insert(goto_place);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -473,17 +485,23 @@ impl<'gc> MovieClip<'gc> {
|
||||||
&mut self,
|
&mut self,
|
||||||
reader: &mut SwfStream<&'a [u8]>,
|
reader: &mut SwfStream<&'a [u8]>,
|
||||||
version: u8,
|
version: u8,
|
||||||
goto_commands: &mut fnv::FnvHashMap<Depth, swf::PlaceObject>,
|
goto_commands: &mut fnv::FnvHashMap<Depth, GotoPlaceObject>,
|
||||||
|
is_rewind: bool,
|
||||||
) -> DecodeResult {
|
) -> DecodeResult {
|
||||||
let remove_object = if version == 1 {
|
let remove_object = if version == 1 {
|
||||||
reader.read_remove_object_1()
|
reader.read_remove_object_1()
|
||||||
} else {
|
} else {
|
||||||
reader.read_remove_object_2()
|
reader.read_remove_object_2()
|
||||||
}?;
|
}?;
|
||||||
// If this tag were to remove an object that existed before the goto,
|
|
||||||
// then we can remove that child right away.
|
|
||||||
goto_commands.remove(&remove_object.depth);
|
goto_commands.remove(&remove_object.depth);
|
||||||
self.children.remove(&remove_object.depth);
|
if !is_rewind {
|
||||||
|
// For fast-forwards, if this tag were to remove an object
|
||||||
|
// that existed before the goto, then we can remove that child right away.
|
||||||
|
// Don't do this for rewinds, because they conceptually
|
||||||
|
// start from an empty display list, and we also want to examine
|
||||||
|
// the old children to decide if they persist (place_frame <= goto_frame).
|
||||||
|
self.children.remove(&remove_object.depth);
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1151,7 +1169,11 @@ impl<'gc, 'a> MovieClip<'gc> {
|
||||||
context,
|
context,
|
||||||
id,
|
id,
|
||||||
place_object.depth,
|
place_object.depth,
|
||||||
place_object.modifies_original_item(),
|
if let PlaceObjectAction::Replace(_) = place_object.action {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
},
|
||||||
) {
|
) {
|
||||||
child
|
child
|
||||||
.write(context.gc_context)
|
.write(context.gc_context)
|
||||||
|
@ -1270,16 +1292,20 @@ unsafe impl<'gc> gc_arena::Collect for MovieClipStatic {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait PlaceObjectExt {
|
/// Stores the placement settings for display objects during a
|
||||||
fn id(&self) -> CharacterId;
|
/// goto command.
|
||||||
fn modifies_original_item(&self) -> bool;
|
#[derive(Debug)]
|
||||||
fn merge(&mut self, next: &mut swf::PlaceObject);
|
struct GotoPlaceObject {
|
||||||
|
/// The frame number that this character was first placed on.
|
||||||
|
frame: FrameNumber,
|
||||||
|
/// The display properties of the object.
|
||||||
|
place_object: swf::PlaceObject,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlaceObjectExt for swf::PlaceObject {
|
impl GotoPlaceObject {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn id(&self) -> CharacterId {
|
fn id(&self) -> CharacterId {
|
||||||
match &self.action {
|
match &self.place_object.action {
|
||||||
swf::PlaceObjectAction::Place(id) | swf::PlaceObjectAction::Replace(id) => *id,
|
swf::PlaceObjectAction::Place(id) | swf::PlaceObjectAction::Replace(id) => *id,
|
||||||
swf::PlaceObjectAction::Modify => 0,
|
swf::PlaceObjectAction::Modify => 0,
|
||||||
}
|
}
|
||||||
|
@ -1287,39 +1313,46 @@ impl PlaceObjectExt for swf::PlaceObject {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn modifies_original_item(&self) -> bool {
|
fn modifies_original_item(&self) -> bool {
|
||||||
if let swf::PlaceObjectAction::Replace(_) = &self.action {
|
if let swf::PlaceObjectAction::Replace(_) = &self.place_object.action {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn merge(&mut self, next: &mut swf::PlaceObject) {
|
fn merge(&mut self, next: &mut GotoPlaceObject) {
|
||||||
use swf::PlaceObjectAction;
|
use swf::PlaceObjectAction;
|
||||||
self.action = match (self.action, next.action) {
|
let cur_place = &mut self.place_object;
|
||||||
(prev, PlaceObjectAction::Modify) => prev,
|
let next_place = &mut next.place_object;
|
||||||
(_, next) => next,
|
match (cur_place.action, next_place.action) {
|
||||||
|
(cur, PlaceObjectAction::Modify) => {
|
||||||
|
cur_place.action = cur;
|
||||||
|
}
|
||||||
|
(_, new) => {
|
||||||
|
cur_place.action = new;
|
||||||
|
self.frame = next.frame;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
if next.matrix.is_some() {
|
if next_place.matrix.is_some() {
|
||||||
self.matrix = next.matrix.take();
|
cur_place.matrix = next_place.matrix.take();
|
||||||
}
|
}
|
||||||
if next.color_transform.is_some() {
|
if next_place.color_transform.is_some() {
|
||||||
self.color_transform = next.color_transform.take();
|
cur_place.color_transform = next_place.color_transform.take();
|
||||||
}
|
}
|
||||||
if next.ratio.is_some() {
|
if next_place.ratio.is_some() {
|
||||||
self.ratio = next.ratio.take();
|
cur_place.ratio = next_place.ratio.take();
|
||||||
}
|
}
|
||||||
if next.name.is_some() {
|
if next_place.name.is_some() {
|
||||||
self.name = next.name.take();
|
cur_place.name = next_place.name.take();
|
||||||
}
|
}
|
||||||
if next.clip_depth.is_some() {
|
if next_place.clip_depth.is_some() {
|
||||||
self.clip_depth = next.clip_depth.take();
|
cur_place.clip_depth = next_place.clip_depth.take();
|
||||||
}
|
}
|
||||||
if next.class_name.is_some() {
|
if next_place.class_name.is_some() {
|
||||||
self.class_name = next.class_name.take();
|
cur_place.class_name = next_place.class_name.take();
|
||||||
}
|
}
|
||||||
if next.background_color.is_some() {
|
if next_place.background_color.is_some() {
|
||||||
self.background_color = next.background_color.take();
|
cur_place.background_color = next_place.background_color.take();
|
||||||
}
|
}
|
||||||
// TODO: Other stuff.
|
// TODO: Other stuff.
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,8 +36,11 @@ swf_tests! {
|
||||||
(looping, "avm1/looping", 6),
|
(looping, "avm1/looping", 6),
|
||||||
(goto_advance1, "avm1/goto_advance1", 10),
|
(goto_advance1, "avm1/goto_advance1", 10),
|
||||||
(goto_advance2, "avm1/goto_advance2", 10),
|
(goto_advance2, "avm1/goto_advance2", 10),
|
||||||
|
(goto_both_ways1, "avm1/goto_both_ways1", 10),
|
||||||
|
(goto_both_ways2, "avm1/goto_both_ways2", 10),
|
||||||
(goto_rewind1, "avm1/goto_rewind1", 10),
|
(goto_rewind1, "avm1/goto_rewind1", 10),
|
||||||
(goto_rewind2, "avm1/goto_rewind2", 10),
|
(goto_rewind2, "avm1/goto_rewind2", 10),
|
||||||
|
(goto_rewind3, "avm1/goto_rewind3", 10),
|
||||||
(tell_target, "avm1/tell_target", 3),
|
(tell_target, "avm1/tell_target", 3),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
gotoAndPlay(3)
|
||||||
|
child frame 1
|
||||||
|
gotoAndStop(2)
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,3 @@
|
||||||
|
child frame 1
|
||||||
|
gotoAndPlay(5)
|
||||||
|
gotoAndStop(3)
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,2 @@
|
||||||
|
gotoAndPlay(4)
|
||||||
|
child frame 1
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue