use std::{collections::HashMap, fmt, rc::Rc};
use crate::engine::d2::{
swf::{Library, MovieSprite},
util::{Disposable, Value},
Component, Entity,
};
use super::Symbol;
#[derive(Default, Clone)]
pub struct MoviePlayer {
pub inner: Component,
pub movie: Option<MovieSprite>,
pub looping: bool,
library: Library,
root: Entity,
oneshot_sprite: Option<MovieSprite>,
looping_sprite: Option<MovieSprite>,
decorator: Option<Rc<dyn Fn(MovieSprite)>>,
cache: Option<HashMap<String, MovieSprite>>,
}
impl MoviePlayer {
pub fn new(lib: Library) -> Self {
Self {
inner: Default::default(),
movie: None,
looping: false,
library: lib,
root: Entity::new(),
looping_sprite: None,
oneshot_sprite: None,
decorator: None,
cache: Some(HashMap::new()),
}
}
pub fn set_cache(&mut self, cache: bool) -> &Self {
self.cache = if cache { Some(HashMap::new()) } else { None };
self
}
pub fn set_decorator(&mut self, decorator: Option<Rc<dyn Fn(MovieSprite)>>) -> &Self {
self.decorator = decorator;
self
}
pub fn play(&mut self, name: String, restart: bool) -> &Self {
assert!(
self.looping_sprite.is_some(),
"A loop must be started before the first call to play()"
);
let should_update = self
.oneshot_sprite
.as_ref()
.map(|sprite| sprite.symbol.name().map(|x| x != name).unwrap_or_default())
.unwrap_or_default();
if restart || should_update {
self.oneshot_sprite = self.play_from_cache(name);
}
self
}
pub fn play_with_loop(&mut self, name: String, restart: bool) -> &Self {
let should_update = self
.looping_sprite
.as_ref()
.map(|sprite| sprite.symbol.name().map(|x| x != name).unwrap_or_default())
.unwrap_or_default();
if restart || should_update {
self.oneshot_sprite = None;
self.looping_sprite = self.play_from_cache(name);
}
self
}
pub fn on_added(&self) {
if let Some(owner) = self.inner.owner {
owner.entity().add_child(self.root.clone(), true, None);
}
}
pub fn on_removed(&mut self) {
self.root.dispose();
self.oneshot_sprite = None;
self.looping_sprite = None;
self.movie = None;
}
pub fn on_update(&mut self, dt: f32) {
if let Some(ref oneshot_sprite) = self.oneshot_sprite {
if oneshot_sprite.position() + dt > oneshot_sprite.symbol.duration {
self.oneshot_sprite = None;
self.set_current(self.looping_sprite.clone());
}
}
}
fn play_from_cache(&mut self, name: String) -> Option<MovieSprite> {
match self.cache {
Some(ref mut cache) => {
match cache.get_mut(&name) {
Some(sprite) => {
sprite.set_position(0.0);
self.root.add(sprite.inner.component.clone());
self.movie = Some(sprite.clone());
return Some(sprite.clone());
}
None => {
if let Some(ref sprite) = self.library.create_movie(name.clone(), true) {
if let Some(ref decorator) = self.decorator {
(decorator)(sprite.clone());
}
cache.insert(name, sprite.clone());
self.set_current(Some(sprite.clone()));
Some(sprite.clone())
} else {
None
}
}
}
}
None => {
self.create_movie(name).map(|ref sprite| {
self.set_current(Some(sprite.clone()));
sprite.clone()
})
}
}
}
fn create_movie(&self, name: String) -> Option<MovieSprite> {
unimplemented!()
}
fn looping(&self) -> bool {
self.oneshot_sprite.is_none() && self.looping_sprite.is_some()
}
fn set_current(&mut self, current: Option<MovieSprite>) {
if let Some(ref sprite) = current {
self.root.add(sprite.inner.component.clone());
}
self.movie = current;
}
}
impl PartialEq for MoviePlayer {
fn eq(&self, other: &Self) -> bool {
self.root == other.root
}
}
impl fmt::Debug for MoviePlayer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MoviePlayer")
.finish()
}
}
impl AsRef<Component> for MoviePlayer {
fn as_ref(&self) -> &Component {
&self.inner
}
}