2019-04-26 21:11:29 +00:00
|
|
|
use crate::color_transform::ColorTransform;
|
2019-04-25 17:52:22 +00:00
|
|
|
use crate::display_object::{DisplayObject, DisplayObjectNode};
|
|
|
|
use crate::matrix::Matrix;
|
2019-04-27 01:55:06 +00:00
|
|
|
use crate::player::{RenderContext, UpdateContext};
|
2019-04-25 17:52:22 +00:00
|
|
|
use bacon_rajan_cc::{Cc, Trace, Tracer};
|
2019-04-27 01:55:06 +00:00
|
|
|
use log::info;
|
2019-04-25 17:52:22 +00:00
|
|
|
use std::cell::RefCell;
|
|
|
|
use std::collections::HashMap;
|
|
|
|
|
|
|
|
type Depth = i16;
|
|
|
|
type FrameNumber = u16;
|
|
|
|
|
|
|
|
pub struct MovieClip {
|
2019-04-26 03:27:44 +00:00
|
|
|
tag_stream_start: Option<u64>,
|
|
|
|
tag_stream_pos: u64,
|
2019-04-25 17:52:22 +00:00
|
|
|
is_playing: bool,
|
|
|
|
matrix: Matrix,
|
2019-04-26 21:11:29 +00:00
|
|
|
color_transform: ColorTransform,
|
2019-04-25 17:52:22 +00:00
|
|
|
current_frame: FrameNumber,
|
2019-04-26 03:27:44 +00:00
|
|
|
next_frame: FrameNumber,
|
2019-04-25 17:52:22 +00:00
|
|
|
total_frames: FrameNumber,
|
|
|
|
children: HashMap<Depth, Cc<RefCell<DisplayObjectNode>>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl MovieClip {
|
|
|
|
pub fn new() -> Cc<RefCell<MovieClip>> {
|
|
|
|
let clip = MovieClip {
|
2019-04-26 03:27:44 +00:00
|
|
|
tag_stream_start: None,
|
|
|
|
tag_stream_pos: 0,
|
2019-04-25 17:52:22 +00:00
|
|
|
is_playing: true,
|
|
|
|
matrix: Matrix::default(),
|
2019-04-26 21:11:29 +00:00
|
|
|
color_transform: Default::default(),
|
2019-04-26 03:27:44 +00:00
|
|
|
current_frame: 0,
|
|
|
|
next_frame: 1,
|
2019-04-25 17:52:22 +00:00
|
|
|
total_frames: 1,
|
|
|
|
children: HashMap::new(),
|
|
|
|
};
|
|
|
|
Cc::new(RefCell::new(clip))
|
|
|
|
}
|
|
|
|
|
2019-04-26 03:27:44 +00:00
|
|
|
pub fn new_with_data(tag_stream_start: u64, num_frames: u16) -> MovieClip {
|
|
|
|
info!("start: {} ", tag_stream_start);
|
2019-04-25 17:52:22 +00:00
|
|
|
MovieClip {
|
2019-04-26 03:27:44 +00:00
|
|
|
tag_stream_start: Some(tag_stream_start),
|
|
|
|
tag_stream_pos: tag_stream_start,
|
2019-04-25 17:52:22 +00:00
|
|
|
is_playing: true,
|
|
|
|
matrix: Matrix::default(),
|
2019-04-26 21:11:29 +00:00
|
|
|
color_transform: Default::default(),
|
2019-04-26 03:27:44 +00:00
|
|
|
current_frame: 0,
|
|
|
|
next_frame: 1,
|
2019-04-25 17:52:22 +00:00
|
|
|
total_frames: num_frames,
|
|
|
|
children: HashMap::new(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-26 03:27:44 +00:00
|
|
|
pub fn run_place_object(
|
|
|
|
children: &mut HashMap<Depth, Cc<RefCell<DisplayObjectNode>>>,
|
|
|
|
place_object: &swf::PlaceObject,
|
|
|
|
context: &mut UpdateContext,
|
|
|
|
) {
|
2019-04-25 17:52:22 +00:00
|
|
|
use swf::PlaceObjectAction;
|
2019-04-26 03:27:44 +00:00
|
|
|
let mut character = match place_object.action {
|
2019-04-25 17:52:22 +00:00
|
|
|
PlaceObjectAction::Place(id) => {
|
|
|
|
// TODO(Herschel): Behavior when character doesn't exist/isn't a DisplayObject?
|
2019-04-26 03:27:44 +00:00
|
|
|
let character =
|
2019-04-27 01:55:06 +00:00
|
|
|
if let Ok(character) = context.library.instantiate_display_object(id) {
|
2019-04-26 03:27:44 +00:00
|
|
|
Cc::new(RefCell::new(character))
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
};
|
2019-04-25 17:52:22 +00:00
|
|
|
|
|
|
|
// TODO(Herschel): Behavior when depth is occupied? (I think it replaces)
|
2019-04-26 03:27:44 +00:00
|
|
|
children.insert(place_object.depth, character.clone());
|
|
|
|
character
|
|
|
|
}
|
|
|
|
PlaceObjectAction::Modify => {
|
|
|
|
if let Some(child) = children.get(&place_object.depth) {
|
|
|
|
child.clone()
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
}
|
2019-04-25 17:52:22 +00:00
|
|
|
}
|
|
|
|
PlaceObjectAction::Replace(id) => {
|
2019-04-26 03:27:44 +00:00
|
|
|
let character =
|
2019-04-27 01:55:06 +00:00
|
|
|
if let Ok(character) = context.library.instantiate_display_object(id) {
|
2019-04-26 03:27:44 +00:00
|
|
|
Cc::new(RefCell::new(character))
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
};
|
2019-04-25 17:52:22 +00:00
|
|
|
|
2019-04-26 03:27:44 +00:00
|
|
|
children.insert(place_object.depth, character.clone());
|
|
|
|
character
|
2019-04-25 17:52:22 +00:00
|
|
|
}
|
2019-04-26 03:27:44 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let mut character = character.borrow_mut();
|
|
|
|
if let Some(matrix) = &place_object.matrix {
|
|
|
|
let m = matrix.clone();
|
|
|
|
character.set_matrix(Matrix::from(m));
|
2019-04-25 17:52:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl DisplayObject for MovieClip {
|
2019-04-26 03:27:44 +00:00
|
|
|
fn run_frame(&mut self, context: &mut UpdateContext) {
|
|
|
|
use swf::{read::SwfRead, Tag};
|
|
|
|
if self.tag_stream_start.is_some() {
|
|
|
|
context
|
|
|
|
.position_stack
|
|
|
|
.push(context.tag_stream.get_ref().position());
|
|
|
|
context
|
|
|
|
.tag_stream
|
|
|
|
.get_inner()
|
|
|
|
.set_position(self.tag_stream_pos);
|
|
|
|
|
|
|
|
while let Ok(Some(tag)) = context.tag_stream.read_tag() {
|
|
|
|
//trace!("mc: {:?}", tag);
|
2019-04-25 17:52:22 +00:00
|
|
|
match tag {
|
|
|
|
Tag::ShowFrame => break,
|
|
|
|
Tag::PlaceObject(place_object) => {
|
2019-04-26 03:27:44 +00:00
|
|
|
MovieClip::run_place_object(&mut self.children, &*place_object, context)
|
|
|
|
}
|
2019-04-27 01:55:06 +00:00
|
|
|
Tag::RemoveObject { depth, .. } => {
|
2019-04-26 03:27:44 +00:00
|
|
|
// TODO(Herschel): How does the character ID work for RemoveObject?
|
|
|
|
self.children.remove(&depth);
|
|
|
|
info!("REMOVE {} {}", depth, self.children.len());
|
2019-04-25 17:52:22 +00:00
|
|
|
}
|
2019-04-26 03:27:44 +00:00
|
|
|
|
|
|
|
Tag::JpegTables(_) => (),
|
|
|
|
Tag::SoundStreamHead(_) => (),
|
|
|
|
Tag::SoundStreamHead2(_) => (),
|
|
|
|
Tag::SoundStreamBlock(_) => (),
|
|
|
|
Tag::DoAction(_) => (),
|
|
|
|
_ => info!("Umimplemented tag: {:?}", tag),
|
2019-04-25 17:52:22 +00:00
|
|
|
}
|
|
|
|
}
|
2019-04-26 03:27:44 +00:00
|
|
|
self.tag_stream_pos = context.tag_stream.get_ref().position();
|
|
|
|
context
|
|
|
|
.tag_stream
|
|
|
|
.get_inner()
|
|
|
|
.set_position(context.position_stack.pop().unwrap());
|
2019-04-25 17:52:22 +00:00
|
|
|
|
|
|
|
// Advance frame number.
|
2019-04-26 03:27:44 +00:00
|
|
|
if self.next_frame < self.total_frames {
|
|
|
|
self.next_frame += 1;
|
2019-04-25 17:52:22 +00:00
|
|
|
} else {
|
2019-04-26 03:27:44 +00:00
|
|
|
self.next_frame = 1;
|
|
|
|
if let Some(start) = self.tag_stream_start {
|
|
|
|
self.tag_stream_pos = start;
|
|
|
|
}
|
2019-04-25 17:52:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO(Herschel): Verify order of execution for parent/children.
|
|
|
|
// Parent first? Children first? Sorted by depth?
|
|
|
|
for child in self.children.values() {
|
2019-04-26 03:27:44 +00:00
|
|
|
child.borrow_mut().run_frame(context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn update_frame_number(&mut self) {
|
|
|
|
self.current_frame = self.next_frame;
|
|
|
|
for child in self.children.values() {
|
|
|
|
child.borrow_mut().update_frame_number();
|
2019-04-25 17:52:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn render(&self, context: &mut RenderContext) {
|
|
|
|
context.matrix_stack.push(&self.matrix);
|
2019-04-26 21:11:29 +00:00
|
|
|
context.color_transform_stack.push(&self.color_transform);
|
2019-04-25 17:52:22 +00:00
|
|
|
|
2019-04-26 03:27:44 +00:00
|
|
|
let mut sorted_children: Vec<_> = self.children.iter().collect();
|
|
|
|
sorted_children.sort_by_key(|(depth, _)| *depth);
|
|
|
|
|
|
|
|
for child in sorted_children {
|
|
|
|
child.1.borrow_mut().render(context);
|
2019-04-25 17:52:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
context.matrix_stack.pop();
|
2019-04-26 21:11:29 +00:00
|
|
|
context.color_transform_stack.pop();
|
2019-04-25 17:52:22 +00:00
|
|
|
}
|
2019-04-26 03:27:44 +00:00
|
|
|
|
|
|
|
fn set_matrix(&mut self, matrix: Matrix) {
|
|
|
|
self.matrix = matrix;
|
|
|
|
}
|
2019-04-26 21:11:29 +00:00
|
|
|
|
|
|
|
fn set_color_transform(&mut self, color_transform: ColorTransform) {
|
|
|
|
self.color_transform = color_transform;
|
|
|
|
}
|
2019-04-25 17:52:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Trace for MovieClip {
|
|
|
|
fn trace(&mut self, tracer: &mut Tracer) {
|
|
|
|
for child in self.children.values_mut() {
|
|
|
|
child.trace(tracer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|