Implement `addFrameScript`.
This commit is contained in:
parent
664152d739
commit
d991c49774
|
@ -4,9 +4,11 @@ use crate::avm2::activation::Activation;
|
||||||
use crate::avm2::class::Class;
|
use crate::avm2::class::Class;
|
||||||
use crate::avm2::method::Method;
|
use crate::avm2::method::Method;
|
||||||
use crate::avm2::names::{Namespace, QName};
|
use crate::avm2::names::{Namespace, QName};
|
||||||
use crate::avm2::object::Object;
|
use crate::avm2::object::{Object, TObject};
|
||||||
|
use crate::avm2::traits::Trait;
|
||||||
use crate::avm2::value::Value;
|
use crate::avm2::value::Value;
|
||||||
use crate::avm2::Error;
|
use crate::avm2::Error;
|
||||||
|
use crate::display_object::TDisplayObject;
|
||||||
use gc_arena::{GcCell, MutationContext};
|
use gc_arena::{GcCell, MutationContext};
|
||||||
|
|
||||||
/// Implements `flash.display.MovieClip`'s instance constructor.
|
/// Implements `flash.display.MovieClip`'s instance constructor.
|
||||||
|
@ -27,13 +29,44 @@ pub fn class_init<'gc>(
|
||||||
Ok(Value::Undefined)
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Implements `addFrameScript`, an undocumented method of `MovieClip` used to
|
||||||
|
/// specify what methods of a clip's class run on which frames.
|
||||||
|
pub fn add_frame_script<'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())
|
||||||
|
{
|
||||||
|
for (frame_id, callable) in args.chunks_exact(2).map(|s| (s[0].clone(), s[1].clone())) {
|
||||||
|
let frame_id = frame_id.coerce_to_u32(activation)? as u16;
|
||||||
|
let callable = callable.coerce_to_object(activation)?;
|
||||||
|
|
||||||
|
mc.register_frame_script(frame_id, callable, &mut activation.context);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log::error!("Attempted to add frame scripts to non-MovieClip this!");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Value::Undefined)
|
||||||
|
}
|
||||||
|
|
||||||
/// Construct `MovieClip`'s class.
|
/// Construct `MovieClip`'s class.
|
||||||
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
|
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
|
||||||
Class::new(
|
let class = Class::new(
|
||||||
QName::new(Namespace::package("flash.display"), "MovieClip"),
|
QName::new(Namespace::package("flash.display"), "MovieClip"),
|
||||||
Some(QName::new(Namespace::package("flash.display"), "Sprite").into()),
|
Some(QName::new(Namespace::package("flash.display"), "Sprite").into()),
|
||||||
Method::from_builtin(instance_init),
|
Method::from_builtin(instance_init),
|
||||||
Method::from_builtin(class_init),
|
Method::from_builtin(class_init),
|
||||||
mc,
|
mc,
|
||||||
)
|
);
|
||||||
|
|
||||||
|
class.write(mc).define_instance_trait(Trait::from_method(
|
||||||
|
QName::new(Namespace::package(""), "addFrameScript"),
|
||||||
|
Method::from_builtin(add_frame_script),
|
||||||
|
));
|
||||||
|
|
||||||
|
class
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,12 +54,27 @@ pub struct MovieClipData<'gc> {
|
||||||
children: BTreeMap<Depth, DisplayObject<'gc>>,
|
children: BTreeMap<Depth, DisplayObject<'gc>>,
|
||||||
object: Option<AvmObject<'gc>>,
|
object: Option<AvmObject<'gc>>,
|
||||||
clip_actions: Vec<ClipAction>,
|
clip_actions: Vec<ClipAction>,
|
||||||
|
frame_scripts: Vec<Avm2FrameScript<'gc>>,
|
||||||
has_button_clip_event: bool,
|
has_button_clip_event: bool,
|
||||||
flags: EnumSet<MovieClipFlags>,
|
flags: EnumSet<MovieClipFlags>,
|
||||||
avm_constructor: Option<AvmObject<'gc>>,
|
avm_constructor: Option<AvmObject<'gc>>,
|
||||||
drawing: Drawing,
|
drawing: Drawing,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe impl<'gc> Collect for MovieClipData<'gc> {
|
||||||
|
#[inline]
|
||||||
|
fn trace(&self, cc: gc_arena::CollectionContext) {
|
||||||
|
for child in self.children.values() {
|
||||||
|
child.trace(cc);
|
||||||
|
}
|
||||||
|
self.base.trace(cc);
|
||||||
|
self.static_data.trace(cc);
|
||||||
|
self.object.trace(cc);
|
||||||
|
self.avm_constructor.trace(cc);
|
||||||
|
self.frame_scripts.trace(cc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'gc> MovieClip<'gc> {
|
impl<'gc> MovieClip<'gc> {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn new(swf: SwfSlice, gc_context: MutationContext<'gc, '_>) -> Self {
|
pub fn new(swf: SwfSlice, gc_context: MutationContext<'gc, '_>) -> Self {
|
||||||
|
@ -74,6 +89,7 @@ impl<'gc> MovieClip<'gc> {
|
||||||
children: BTreeMap::new(),
|
children: BTreeMap::new(),
|
||||||
object: None,
|
object: None,
|
||||||
clip_actions: Vec::new(),
|
clip_actions: Vec::new(),
|
||||||
|
frame_scripts: Vec::new(),
|
||||||
has_button_clip_event: false,
|
has_button_clip_event: false,
|
||||||
flags: EnumSet::empty(),
|
flags: EnumSet::empty(),
|
||||||
avm_constructor: None,
|
avm_constructor: None,
|
||||||
|
@ -108,6 +124,7 @@ impl<'gc> MovieClip<'gc> {
|
||||||
children: BTreeMap::new(),
|
children: BTreeMap::new(),
|
||||||
object: None,
|
object: None,
|
||||||
clip_actions: Vec::new(),
|
clip_actions: Vec::new(),
|
||||||
|
frame_scripts: Vec::new(),
|
||||||
has_button_clip_event: false,
|
has_button_clip_event: false,
|
||||||
flags: MovieClipFlags::Playing.into(),
|
flags: MovieClipFlags::Playing.into(),
|
||||||
avm_constructor: None,
|
avm_constructor: None,
|
||||||
|
@ -1276,6 +1293,47 @@ impl<'gc> MovieClip<'gc> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn register_frame_script(
|
||||||
|
self,
|
||||||
|
frame_id: FrameNumber,
|
||||||
|
callable: Avm2Object<'gc>,
|
||||||
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
|
) {
|
||||||
|
let mut write = self.0.write(context.gc_context);
|
||||||
|
|
||||||
|
write
|
||||||
|
.frame_scripts
|
||||||
|
.push(Avm2FrameScript { frame_id, callable });
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_frame_scripts(self, frame_id: FrameNumber, context: &mut UpdateContext<'_, 'gc, '_>) {
|
||||||
|
let mut index = 0;
|
||||||
|
let mut read = self.0.read();
|
||||||
|
|
||||||
|
let avm2_object = read.object.and_then(|o| o.as_avm2_object().ok());
|
||||||
|
|
||||||
|
if let Some(avm2_object) = avm2_object {
|
||||||
|
while let Some(fs) = read.frame_scripts.get(index) {
|
||||||
|
if fs.frame_id == frame_id {
|
||||||
|
let callable = fs.callable;
|
||||||
|
|
||||||
|
drop(read);
|
||||||
|
|
||||||
|
let mut activation = Avm2Activation::from_nothing(context.reborrow());
|
||||||
|
if let Err(e) = callable.call(Some(avm2_object), &[], &mut activation, None) {
|
||||||
|
log::error!("Error in script on frame {}: {}", frame_id, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
read = self.0.read();
|
||||||
|
}
|
||||||
|
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log::error!("Attempted to run AVM2 frame scripts on an AVM1 MovieClip.");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
|
impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
|
||||||
|
@ -1322,6 +1380,18 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
|
||||||
ClipEvent::Load,
|
ClipEvent::Load,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self
|
||||||
|
.0
|
||||||
|
.read()
|
||||||
|
.object
|
||||||
|
.map(|o| o.is_avm2_object())
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
let frame_id = self.0.read().current_frame;
|
||||||
|
|
||||||
|
self.run_frame_scripts(frame_id, context);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&self, context: &mut RenderContext<'_, 'gc>) {
|
fn render(&self, context: &mut RenderContext<'_, 'gc>) {
|
||||||
|
@ -1529,19 +1599,6 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<'gc> Collect for MovieClipData<'gc> {
|
|
||||||
#[inline]
|
|
||||||
fn trace(&self, cc: gc_arena::CollectionContext) {
|
|
||||||
for child in self.children.values() {
|
|
||||||
child.trace(cc);
|
|
||||||
}
|
|
||||||
self.base.trace(cc);
|
|
||||||
self.static_data.trace(cc);
|
|
||||||
self.object.trace(cc);
|
|
||||||
self.avm_constructor.trace(cc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'gc> MovieClipData<'gc> {
|
impl<'gc> MovieClipData<'gc> {
|
||||||
/// Replace the current MovieClipData with a completely new SwfMovie.
|
/// Replace the current MovieClipData with a completely new SwfMovie.
|
||||||
///
|
///
|
||||||
|
|
Loading…
Reference in New Issue