avm1: Implement GetVariable/SetVariable
This commit is contained in:
parent
ecd9b18e90
commit
20ec170552
|
@ -75,6 +75,7 @@ impl Avm1 {
|
|||
Action::GetMember => self.action_get_member(context)?,
|
||||
Action::GetProperty => self.action_get_property(context)?,
|
||||
Action::GetTime => self.action_get_time(context)?,
|
||||
Action::GetVariable => self.action_get_variable(context)?,
|
||||
Action::GetUrl { url, target } => self.action_get_url(context, &url, &target)?,
|
||||
Action::GetUrl2 {
|
||||
send_vars_method,
|
||||
|
@ -120,6 +121,7 @@ impl Avm1 {
|
|||
Action::Return => self.action_return(context)?,
|
||||
Action::SetMember => self.action_set_member(context)?,
|
||||
Action::SetTarget(target) => self.action_set_target(context, &target)?,
|
||||
Action::SetVariable => self.action_set_variable(context)?,
|
||||
Action::StackSwap => self.action_stack_swap(context)?,
|
||||
Action::StartDrag => self.action_start_drag(context)?,
|
||||
Action::Stop => self.action_stop(context)?,
|
||||
|
@ -183,6 +185,25 @@ impl Avm1 {
|
|||
Some(cur_clip)
|
||||
}
|
||||
|
||||
pub fn resolve_slash_path_variable<'gc, 's>(
|
||||
start: DisplayNode<'gc>,
|
||||
root: DisplayNode<'gc>,
|
||||
path: &'s str,
|
||||
) -> Option<(DisplayNode<'gc>, &'s str)> {
|
||||
if !path.is_empty() {
|
||||
let mut var_iter = path.splitn(2, ':');
|
||||
match (var_iter.next(), var_iter.next()) {
|
||||
(Some(var_name), None) => return Some((start, var_name)),
|
||||
(Some(path), Some(var_name)) => if let Some(node) = Self::resolve_slash_path(start, root, path) {
|
||||
return Some((node, var_name));
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn push(&mut self, value: impl Into<Value>) {
|
||||
self.stack.push(value.into());
|
||||
}
|
||||
|
@ -476,6 +497,19 @@ impl Avm1 {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn action_get_variable(&mut self, context: &mut ActionContext) -> Result<(), Error> {
|
||||
// Flash 4-style variable
|
||||
let var_path = self.pop()?;
|
||||
if let Some((node, var_name)) = Self::resolve_slash_path_variable(context.active_clip, context.root, var_path.as_string()?) {
|
||||
if let Some(clip) = node.read().as_movie_clip() {
|
||||
self.push(clip.get_variable(var_name));
|
||||
}
|
||||
} else {
|
||||
self.push(Value::Undefined);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn action_get_url(
|
||||
&mut self,
|
||||
_context: &mut ActionContext,
|
||||
|
@ -504,11 +538,7 @@ impl Avm1 {
|
|||
fn action_goto_frame(&mut self, context: &mut ActionContext, frame: u16) -> Result<(), Error> {
|
||||
let mut display_object = context.active_clip.write(context.gc_context);
|
||||
let clip = display_object.as_movie_clip_mut().unwrap();
|
||||
if clip.playing() {
|
||||
clip.goto_frame(frame + 1, false);
|
||||
} else {
|
||||
clip.goto_frame(frame + 1, true);
|
||||
}
|
||||
clip.goto_frame(frame + 1, true);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -798,6 +828,18 @@ impl Avm1 {
|
|||
unimplemented!("Action::SetMember");
|
||||
}
|
||||
|
||||
fn action_set_variable(&mut self, context: &mut ActionContext) -> Result<(), Error> {
|
||||
// Flash 4-style variable
|
||||
let value = self.pop()?;
|
||||
let var_path = self.pop()?;
|
||||
if let Some((node, var_name)) = Self::resolve_slash_path_variable(context.active_clip, context.root, var_path.as_string()?) {
|
||||
if let Some(clip) = node.write(context.gc_context).as_movie_clip_mut() {
|
||||
clip.set_variable(var_name, value);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn action_set_target(
|
||||
&mut self,
|
||||
context: &mut ActionContext,
|
||||
|
@ -809,6 +851,9 @@ impl Avm1 {
|
|||
Avm1::resolve_slash_path(context.start_clip, context.root, target)
|
||||
{
|
||||
context.active_clip = clip;
|
||||
} else {
|
||||
log::warn!("SetTarget failed: {} not found", target);
|
||||
// TODO: Do we change active_clip to something? Undefined?
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1008,7 +1053,7 @@ type ObjectPtr = std::marker::PhantomData<()>;
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)]
|
||||
enum Value {
|
||||
pub enum Value {
|
||||
Undefined,
|
||||
Null,
|
||||
Bool(bool),
|
||||
|
|
|
@ -52,7 +52,9 @@ impl<'gc> Button<'gc> {
|
|||
for actions in &button.actions {
|
||||
if actions
|
||||
.conditions
|
||||
.contains(&swf::ButtonActionCondition::OverDownToOverUp)
|
||||
.contains(&swf::ButtonActionCondition::OverDownToOverUp) || actions
|
||||
.conditions
|
||||
.contains(&swf::ButtonActionCondition::OverUpToOverDown)
|
||||
{
|
||||
release_actions = actions.action_data.clone();
|
||||
}
|
||||
|
@ -123,7 +125,7 @@ impl<'gc> DisplayObject<'gc> for Button<'gc> {
|
|||
fn hit_test(&self, point: (Twips, Twips)) -> bool {
|
||||
//if self.world_bounds().contains(point) {
|
||||
for child in self.children_in_state(self.state).rev() {
|
||||
if child.read().hit_test(point) {
|
||||
if child.read().world_bounds().contains(point) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,10 +41,6 @@ impl<'gc> DisplayObject<'gc> for Graphic<'gc> {
|
|||
bounds
|
||||
}
|
||||
|
||||
fn hit_test(&self, point: (Twips, Twips)) -> bool {
|
||||
self.world_bounds().contains(point)
|
||||
}
|
||||
|
||||
fn run_frame(&mut self, _context: &mut UpdateContext) {
|
||||
// Noop
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::avm1;
|
||||
use crate::backend::audio::AudioStreamHandle;
|
||||
use crate::character::Character;
|
||||
use crate::color_transform::ColorTransform;
|
||||
|
@ -10,7 +11,7 @@ use crate::player::{RenderContext, UpdateContext};
|
|||
use crate::prelude::*;
|
||||
use crate::tag_utils::{self, DecodeResult, SwfStream};
|
||||
use crate::text::Text;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use swf::read::SwfRead;
|
||||
|
||||
type Depth = i16;
|
||||
|
@ -32,6 +33,7 @@ pub struct MovieClip<'gc> {
|
|||
audio_stream: Option<AudioStreamHandle>,
|
||||
|
||||
children: BTreeMap<Depth, DisplayNode<'gc>>,
|
||||
variables: HashMap<String, avm1::Value>,
|
||||
}
|
||||
|
||||
impl<'gc> MovieClip<'gc> {
|
||||
|
@ -49,6 +51,7 @@ impl<'gc> MovieClip<'gc> {
|
|||
audio_stream: None,
|
||||
audio_stream_info: None,
|
||||
children: BTreeMap::new(),
|
||||
variables: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,6 +74,7 @@ impl<'gc> MovieClip<'gc> {
|
|||
audio_stream_info: None,
|
||||
total_frames: num_frames,
|
||||
children: BTreeMap::new(),
|
||||
variables: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,7 +103,9 @@ impl<'gc> MovieClip<'gc> {
|
|||
}
|
||||
|
||||
pub fn goto_frame(&mut self, frame: FrameNumber, stop: bool) {
|
||||
self.goto_queue.push(frame);
|
||||
if frame != self.current_frame {
|
||||
self.goto_queue.push(frame);
|
||||
}
|
||||
|
||||
if stop {
|
||||
self.stop();
|
||||
|
@ -138,6 +144,7 @@ impl<'gc> MovieClip<'gc> {
|
|||
}
|
||||
|
||||
pub fn get_child_by_name(&self, name: &str) -> Option<&DisplayNode<'gc>> {
|
||||
// TODO: Make a HashMap from name -> child?
|
||||
self.children
|
||||
.values()
|
||||
.find(|child| child.read().name() == name)
|
||||
|
@ -195,6 +202,16 @@ impl<'gc> MovieClip<'gc> {
|
|||
self.goto_queue.clear();
|
||||
}
|
||||
|
||||
pub fn get_variable(&self, var_name: &str) -> avm1::Value {
|
||||
// TODO: Value should be Copy (and contain a Cow/GcCell for big objects)
|
||||
self.variables.get(var_name).unwrap_or(&avm1::Value::Undefined).clone()
|
||||
}
|
||||
|
||||
pub fn set_variable(&mut self, var_name: &str, value: avm1::Value) {
|
||||
// TODO: Cow for String values.
|
||||
self.variables.insert(var_name.to_owned(), value);
|
||||
}
|
||||
|
||||
fn reader<'a>(
|
||||
&self,
|
||||
context: &UpdateContext<'a, '_, '_>,
|
||||
|
@ -342,22 +359,15 @@ impl<'gc> DisplayObject<'gc> for MovieClip<'gc> {
|
|||
if child.read().hit_test(point) {
|
||||
return Some(*child);
|
||||
}
|
||||
}
|
||||
//}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn hit_test(&self, point: (Twips, Twips)) -> bool {
|
||||
//if self.world_bounds().contains(point) {
|
||||
for child in self.children.values().rev() {
|
||||
if child.read().hit_test(point) {
|
||||
return true;
|
||||
let button = child.read().pick(point);
|
||||
if button.is_some() {
|
||||
return button;
|
||||
}
|
||||
}
|
||||
//}
|
||||
|
||||
false
|
||||
None
|
||||
}
|
||||
|
||||
fn as_movie_clip(&self) -> Option<&crate::movie_clip::MovieClip<'gc>> {
|
||||
|
|
Loading…
Reference in New Issue