avm2: Implement `MovieClip.currentLabels`

This commit is contained in:
David Wendt 2020-09-17 20:58:36 -04:00 committed by Mike Welsh
parent 77a86aef9b
commit de186ed5f3
7 changed files with 95 additions and 2 deletions

View File

@ -54,6 +54,7 @@ pub struct SystemPrototypes<'gc> {
pub uint: Object<'gc>,
pub namespace: Object<'gc>,
pub array: Object<'gc>,
pub framelabel: Object<'gc>,
}
impl<'gc> SystemPrototypes<'gc> {
@ -81,6 +82,7 @@ impl<'gc> SystemPrototypes<'gc> {
uint: empty,
namespace: empty,
array: empty,
framelabel: empty,
}
}
}
@ -377,7 +379,13 @@ pub fn load_player_globals<'gc>(activation: &mut Activation<'_, 'gc, '_>) -> Res
flash::display::movieclip::create_class(activation.context.gc_context),
implicit_deriver,
)?;
class(
activation
.context
.avm2
.system_prototypes
.as_mut()
.unwrap()
.framelabel = class(
activation,
gs,
flash::display::framelabel::create_class(activation.context.gc_context),

View File

@ -1,10 +1,12 @@
//! `flash.display.MovieClip` builtin/prototype
use crate::avm2::activation::Activation;
use crate::avm2::array::ArrayStorage;
use crate::avm2::class::Class;
use crate::avm2::globals::flash::display::framelabel;
use crate::avm2::method::Method;
use crate::avm2::names::{Namespace, QName};
use crate::avm2::object::{Object, TObject};
use crate::avm2::object::{ArrayObject, Object, TObject};
use crate::avm2::string::AvmString;
use crate::avm2::traits::Trait;
use crate::avm2::value::Value;
@ -120,6 +122,45 @@ pub fn current_label<'gc>(
Ok(Value::Undefined)
}
/// Implements `currentLabels`.
pub fn current_labels<'gc>(
activation: &mut Activation<'_, 'gc, '_>,
this: Option<Object<'gc>>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> {
if let Some(mc) = this
.and_then(|o| o.as_display_object())
.and_then(|dobj| dobj.as_movie_clip())
{
let mut frame_labels = Vec::new();
let frame_label_proto = activation.context.avm2.prototypes().framelabel;
let current_scene_start = mc
.current_scene()
.map(|(_, frame)| frame.saturating_sub(1))
.unwrap_or(0);
for (name, frame) in mc.current_labels() {
let name: Value<'gc> = AvmString::new(activation.context.gc_context, name).into();
let local_frame = frame - current_scene_start;
let frame_label =
frame_label_proto.construct(activation, &[name.clone(), local_frame.into()])?;
framelabel::instance_init(activation, Some(frame_label), &[name, local_frame.into()])?;
frame_labels.push(Some(frame_label.into()));
}
return Ok(ArrayObject::from_array(
ArrayStorage::from_storage(frame_labels),
activation.context.avm2.prototypes().array,
activation.context.gc_context,
)
.into());
}
Ok(Value::Undefined)
}
/// Implements `framesLoaded`.
pub fn frames_loaded<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
@ -379,6 +420,11 @@ pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>
Method::from_builtin(current_label),
));
write.define_instance_trait(Trait::from_getter(
QName::new(Namespace::package(""), "currentLabels"),
Method::from_builtin(current_labels),
));
write.define_instance_trait(Trait::from_getter(
QName::new(Namespace::package(""), "framesLoaded"),
Method::from_builtin(frames_loaded),

View File

@ -681,6 +681,36 @@ impl<'gc> MovieClip<'gc> {
best.map(|(s, fnum)| (s.to_string(), fnum))
}
/// Yield a list of labels and frame-nubmers in the current scene.
///
/// Labels are returned sorted by frame number.
pub fn current_labels(self) -> Vec<(String, FrameNumber)> {
let read = self.0.read();
let current_scene = self.current_scene();
let next_scene = self.next_scene();
let mut values: Vec<(String, FrameNumber)> = read
.static_data
.frame_labels
.iter()
.filter(|(_label, frame)| {
current_scene
.as_ref()
.map(|(_, scene_start)| **frame >= *scene_start)
.unwrap_or(false)
&& next_scene
.as_ref()
.map(|(_, scene_start)| **frame < *scene_start)
.unwrap_or(true)
})
.map(|(label, frame)| (label.clone(), *frame))
.collect();
values.sort_unstable_by(|(_, framea), (_, frameb)| framea.cmp(frameb));
values
}
pub fn total_frames(self) -> FrameNumber {
self.0.read().static_data.total_frames
}

View File

@ -394,6 +394,7 @@ swf_tests! {
(as3_movieclip_prev_scene, "avm2/movieclip_prev_scene", 5),
(as3_movieclip_next_scene, "avm2/movieclip_next_scene", 5),
(as3_framelabel_constr, "avm2/framelabel_constr", 5),
(as3_movieclip_currentlabels, "avm2/movieclip_currentlabels", 5),
}
// TODO: These tests have some inaccuracies currently, so we use approx_eq to test that numeric values are close enough.

View File

@ -0,0 +1,8 @@
//(contents of this.currentLabels)
1
frame1
3
frame3
//(contents of this.currentLabels in scene 2)
1
frame4

Binary file not shown.

Binary file not shown.