use nightshade::prelude::*;
use nightshade::shell::{Command, ShellState, shell_retained_ui};
pub enum CutsceneCommand {
AddPoint,
Clear,
Save(String),
Load(String),
Preview,
Stop,
}
#[derive(Default)]
pub struct EditorShellContext {
pub cutscene_commands: Vec<CutsceneCommand>,
}
pub fn new_shell() -> ShellState<EditorShellContext> {
let mut shell = ShellState::new(EditorShellContext::default());
shell.register_builtin_commands();
shell.registry.register(Command {
name: "cutscene",
description: "Author a camera-path cutscene with draggable waypoints",
usage: "cutscene <point|clear|save <name>|load <name>|preview|stop>",
execute: cutscene_command,
});
shell.registry.register(Command {
name: "teleport",
description: "Teleport the play-mode player to the first entity with a tag",
usage: "teleport <tag>",
execute: teleport_command,
});
shell
}
fn teleport_command(args: &[&str], world: &mut World, _context: &mut EditorShellContext) -> String {
let Some(tag) = args.first() else {
return "Usage: teleport <tag>".to_string();
};
let target = world
.resources
.entities
.tags
.iter()
.find(|(_, tags)| tags.iter().any(|entry| entry == tag))
.map(|(entity, _)| *entity)
.and_then(|entity| world.core.get_global_transform(entity))
.map(|transform| transform.translation());
let Some(position) = target else {
return format!("No entity tagged '{tag}'");
};
let Some(player) = world
.core
.query_entities(nightshade::ecs::world::CHARACTER_CONTROLLER)
.next()
else {
return "No player in the level (enter play mode first)".to_string();
};
if let Some(transform) = world.core.get_local_transform_mut(player) {
transform.translation = position + Vec3::new(0.0, 0.6, 0.0);
}
mark_local_transform_dirty(world, player);
nightshade::ecs::physics::commands::reset_physics_interpolation(world, player);
if let Some(controller) = world.core.get_character_controller_mut(player) {
controller.velocity = Vec3::zeros();
}
format!("Teleported player to '{tag}'")
}
fn cutscene_command(args: &[&str], _world: &mut World, context: &mut EditorShellContext) -> String {
match args.first().copied() {
Some("point") | Some("add") => {
context.cutscene_commands.push(CutsceneCommand::AddPoint);
"Added a waypoint from the current view".to_string()
}
Some("clear") => {
context.cutscene_commands.push(CutsceneCommand::Clear);
"Cleared the camera path".to_string()
}
Some("save") => match args.get(1) {
Some(name) => {
context
.cutscene_commands
.push(CutsceneCommand::Save((*name).to_string()));
format!("Saving cutscene '{name}'")
}
None => "Usage: cutscene save <name>".to_string(),
},
Some("load") => match args.get(1) {
Some(name) => {
context
.cutscene_commands
.push(CutsceneCommand::Load((*name).to_string()));
format!("Loading cutscene '{name}'")
}
None => "Usage: cutscene load <name>".to_string(),
},
Some("preview") | Some("play") => {
context.cutscene_commands.push(CutsceneCommand::Preview);
"Previewing the cutscene".to_string()
}
Some("stop") => {
context.cutscene_commands.push(CutsceneCommand::Stop);
"Stopped the preview".to_string()
}
_ => "Usage: cutscene <point|clear|save <name>|load <name>|preview|stop>".to_string(),
}
}
pub fn run(shell: &mut ShellState<EditorShellContext>, world: &mut World) {
if shell.visible {
let alt_held = world
.resources
.input
.keyboard
.is_key_pressed(KeyCode::AltLeft)
|| world
.resources
.input
.keyboard
.is_key_pressed(KeyCode::AltRight);
if !alt_held {
for character in world.resources.input.keyboard.frame_chars.clone() {
if !character.is_control() {
shell.input_buffer.push(character);
}
}
}
}
let delta_time = world.resources.window.timing.delta_time;
shell.update_animation(delta_time);
shell_retained_ui(shell, world);
}
pub fn handle_key(
shell: &mut ShellState<EditorShellContext>,
world: &mut World,
key_code: KeyCode,
key_state: ElementState,
) {
let pressed = key_state == ElementState::Pressed;
if pressed {
let alt_pressed = world
.resources
.input
.keyboard
.is_key_pressed(KeyCode::AltLeft)
|| world
.resources
.input
.keyboard
.is_key_pressed(KeyCode::AltRight);
if key_code == KeyCode::KeyC && alt_pressed {
shell.toggle();
return;
}
}
if shell.visible {
shell.handle_key(key_code, pressed);
}
}
pub fn is_capturing_input(shell: &ShellState<EditorShellContext>) -> bool {
shell.visible || shell.dragging_resize || shell.dragging_scrollbar
}