use std::collections::{HashMap, VecDeque};
use crate::ecs::cutscene::components::{Cutscene, CutsceneShot};
use crate::ecs::world::{Entity, World};
#[derive(Clone, Copy, Debug)]
pub struct CutsceneOverlay {
pub canvas: Entity,
pub title: Entity,
pub speaker: Entity,
pub dialogue: Entity,
pub dialogue_panel: Entity,
}
#[derive(Clone, Debug)]
pub struct CutsceneDirector {
pub active: Option<Cutscene>,
pub time: f32,
pub speed: f32,
pub playing: bool,
pub looping: bool,
pub finished: bool,
pub camera: Option<Entity>,
pub bindings: HashMap<String, Entity>,
pub overlay: Option<CutsceneOverlay>,
pub dialogue_characters_per_second: f32,
pub hold_on_finish: bool,
pub holding: bool,
pub queue: VecDeque<Cutscene>,
pub fired_markers: Vec<String>,
pub localization: HashMap<String, String>,
pub music_entity: Option<Entity>,
pub music_track: Option<String>,
pub sound_oneshots: Vec<(Entity, f32)>,
pub camera_smoothed: Option<CutsceneShot>,
pub marker_cursor: f32,
}
impl Default for CutsceneDirector {
fn default() -> Self {
Self {
active: None,
time: 0.0,
speed: 1.0,
playing: false,
looping: false,
finished: false,
camera: None,
bindings: HashMap::new(),
overlay: None,
dialogue_characters_per_second: 38.0,
hold_on_finish: false,
holding: false,
queue: VecDeque::new(),
fired_markers: Vec::new(),
localization: HashMap::new(),
music_entity: None,
music_track: None,
sound_oneshots: Vec::new(),
camera_smoothed: None,
marker_cursor: -1.0,
}
}
}
pub fn bind_cutscene_actor(world: &mut World, name: impl Into<String>, entity: Entity) {
world
.resources
.cutscene
.bindings
.insert(name.into(), entity);
}
pub fn set_cutscene_camera(world: &mut World, camera: Entity) {
world.resources.cutscene.camera = Some(camera);
}
pub fn play_cutscene(world: &mut World, cutscene: Cutscene) {
if world.resources.cutscene.camera.is_none() {
world.resources.cutscene.camera = world.resources.active_camera;
}
let director = &mut world.resources.cutscene;
director.active = Some(cutscene);
director.time = 0.0;
director.playing = true;
director.finished = false;
director.holding = false;
director.queue.clear();
director.marker_cursor = -1.0;
director.camera_smoothed = None;
director.fired_markers.clear();
if director.speed <= 0.0 {
director.speed = 1.0;
}
}
pub fn play_cutscene_reel(world: &mut World, cutscenes: impl IntoIterator<Item = Cutscene>) {
let mut iterator = cutscenes.into_iter();
let Some(first) = iterator.next() else {
return;
};
play_cutscene(world, first);
world.resources.cutscene.queue.extend(iterator);
}
pub fn queue_cutscene(world: &mut World, cutscene: Cutscene) {
world.resources.cutscene.queue.push_back(cutscene);
}
pub fn stop_cutscene(world: &mut World) {
let director = &mut world.resources.cutscene;
director.playing = false;
director.holding = false;
director.queue.clear();
}
pub fn pause_cutscene(world: &mut World) {
let director = &mut world.resources.cutscene;
if director.active.is_some() {
director.playing = false;
director.holding = true;
}
}
pub fn resume_cutscene(world: &mut World) {
let director = &mut world.resources.cutscene;
if director.active.is_some() {
director.playing = true;
director.holding = false;
director.finished = false;
}
}
pub fn seek_cutscene(world: &mut World, seconds: f32) {
let duration = world
.resources
.cutscene
.active
.as_ref()
.map(Cutscene::duration)
.unwrap_or(0.0);
let clamped = seconds.clamp(0.0, duration);
world.resources.cutscene.time = clamped;
world.resources.cutscene.marker_cursor = clamped;
world.resources.cutscene.camera_smoothed = None;
}
pub fn take_cutscene_markers(world: &mut World) -> Vec<String> {
std::mem::take(&mut world.resources.cutscene.fired_markers)
}
pub fn set_cutscene_localization(world: &mut World, table: HashMap<String, String>) {
world.resources.cutscene.localization = table;
}
pub fn cutscene_playing(world: &World) -> bool {
world.resources.cutscene.playing
}
pub fn cutscene_finished(world: &World) -> bool {
world.resources.cutscene.finished
}
pub fn cutscene_time(world: &World) -> f32 {
world.resources.cutscene.time
}
pub fn cutscene_progress(world: &World) -> f32 {
let director = &world.resources.cutscene;
match &director.active {
Some(cutscene) => {
let duration = cutscene.duration();
if duration <= 0.0 {
1.0
} else {
(director.time / duration).clamp(0.0, 1.0)
}
}
None => 0.0,
}
}