diff --git a/src/chat.rs b/src/chat.rs index fe1701c..c032d05 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -1,7 +1,9 @@ use bevy::prelude::*; +use bevy::math::DVec3; +use bevy_xpbd_3d::prelude::*; use serde_yaml::Value; use serde::Deserialize; -use crate::{audio, hud, settings, world}; +use crate::{actor, audio, hud, settings, world, effects}; pub const CHATS: &[&str] = &[ include_str!("chats/serenity.yaml"), @@ -45,10 +47,12 @@ impl Plugin for ChatPlugin { handle_reply_keys.before(handle_chat_timer), handle_chat_timer.before(handle_chat_events), handle_new_conversations.before(handle_chat_events), - handle_chat_events, + handle_chat_events.before(handle_chat_scripts), + handle_chat_scripts, )); app.add_event::(); app.add_event::(); + app.add_event::(); app.insert_resource(ChatDB(Vec::new())); } } @@ -70,6 +74,33 @@ pub struct Choice { pub goto: ChatPos, } +#[derive(Component)] +#[derive(Clone)] +pub struct Talker { + pub conv_id: String, + pub name: Option, + pub pronoun: Option, + pub talking_speed: f32, +} + +#[derive(Event)] +pub struct StartConversationEvent { + pub talker: Talker, +} + +#[derive(Event)] +pub struct ChatScriptEvent(String); + +#[derive(Event)] +pub enum ChatEvent { + DespawnAllChoices, + DespawnAllChats, + SpawnMessage(String, hud::LogLevel), + SpawnChoice(String, usize, ChatPos), + RunScript(String), + //Script(String, String, String), +} + // This is the only place where any YAML interaction should be happening. #[derive(Resource)] pub struct ChatDB(Vec); @@ -230,6 +261,7 @@ impl ChatDB { let key = key.as_str(); match (key, value) { (Some(TOKEN_CHAT), _) => {} + (Some(TOKEN_LABEL), _) => {} (Some(TOKEN_MSG), Value::String(message)) => { event.send(ChatEvent::SpawnMessage( message.to_string(), hud::LogLevel::Chat)); @@ -242,6 +274,12 @@ impl ChatDB { event.send(ChatEvent::SpawnMessage( message.to_string(), hud::LogLevel::Warning)); } + (Some(TOKEN_SET), _) => {} + (Some(TOKEN_IF), _) => {} + (Some(TOKEN_GOTO), _) => {} + (Some(TOKEN_SCRIPT), Value::String(script)) => { + event.send(ChatEvent::RunScript(script)); + } _ => { } } @@ -288,29 +326,6 @@ impl ChatDB { } } -#[derive(Component)] -#[derive(Clone)] -pub struct Talker { - pub conv_id: String, - pub name: Option, - pub pronoun: Option, - pub talking_speed: f32, -} - -#[derive(Event)] -pub struct StartConversationEvent { - pub talker: Talker, -} - -#[derive(Event)] -pub enum ChatEvent { - DespawnAllChoices, - DespawnAllChats, - SpawnMessage(String, hud::LogLevel), - SpawnChoice(String, usize, ChatPos), - //Script(String, String, String), -} - pub fn load_chats(mut chatdb: ResMut) { for chat_yaml in CHATS { if chatdb.load_from_str(chat_yaml).is_err() { @@ -372,6 +387,7 @@ pub fn handle_chat_timer( pub fn handle_chat_events( mut commands: Commands, mut er_chatevent: EventReader, + mut ew_chatscript: EventWriter, mut log: ResMut, q_choices: Query>, mut q_chats: Query<(Entity, &mut Chat)>, @@ -420,6 +436,9 @@ pub fn handle_chat_events( )); chat.timer = now + CHOICE_TIMER / settings.chat_speed as f64; } + ChatEvent::RunScript(script) => { + ew_chatscript.send(ChatScriptEvent(script.clone())); + } } } } @@ -451,3 +470,101 @@ fn handle_reply_keys( selected_choice += 1; } } + +pub fn handle_chat_scripts( + mut er_chatscript: EventReader, + mut q_actor: Query<(&mut actor::Actor, &mut actor::Suit), Without>, + mut q_player: Query<(&mut actor::Actor, &mut actor::Suit, &mut actor::ExperiencesGForce), With>, + mut q_playercam: Query<(&mut Position, &mut LinearVelocity), With>, + mut ew_sfx: EventWriter, + mut ew_effect: EventWriter, +) { + for script in er_chatscript.read() { + // Parse the script string + let mut parts = script.0.split_whitespace(); + let name = parts.next(); + if name.is_none() { + error!("ChatScriptEvent should not contain a script name and its parameters, got empty String"); + return; + } + let name = name.unwrap(); + let param1 = parts.next().unwrap_or(""); + let param2 = parts.next().unwrap_or(""); + + // Process the script + match name { + "refilloxygen" => if let Ok(mut amount) = param1.to_string().parse::() { + for (_, mut suit, _) in q_player.iter_mut() { + if param2.is_empty() { + suit.oxygen = (suit.oxygen + amount).clamp(0.0, suit.oxygen_max); + } + else { + let mut found_other = false; + info!("param2={}", param2); + for (other_actor, mut other_suit) in q_actor.iter_mut() { + if !other_actor.id.is_empty() { + info!("ID={}", other_actor.id); + } + if other_actor.id == param2 { + found_other = true; + amount = amount + .clamp(0.0, other_suit.oxygen) + .clamp(0.0, suit.oxygen_max - suit.oxygen); + other_suit.oxygen = other_suit.oxygen - amount; + suit.oxygen = (suit.oxygen + amount).clamp(0.0, suit.oxygen_max); + break; + } + } + if !found_other { + error!("Script error: could not find actor with ID `{}`", param2); + } + } + } + } else { + error!("Invalid parameter for command `{}`: `{}`", name, param1); + } + "cryotrip" => { + if param1.is_empty() { + error!("Chat script cryotrip needs a parameter"); + } + else { + if let Ok((mut pos, mut v)) = q_playercam.get_single_mut() { + if param1 == "oscillation".to_string() { + *pos = Position(DVec3::new(147e6, 165e6, 336e6)); + v.0 = DVec3::ZERO; + } + else if param1 == "metisprime".to_string() { + *pos = Position(DVec3::new(27643e3, -47e3, -124434e3)); + v.0 = DVec3::ZERO; + } + else if param1 == "serenity".to_string() { + *pos = Position(DVec3::new(-121095e3, 582e3, -190816e3)); + v.0 = DVec3::ZERO; + } + else { + error!("Invalid destination for cryotrip chat script: '{}'", param1); + } + } + if let Ok((_, mut suit, mut gforce)) = q_player.get_single_mut() { + suit.oxygen = suit.oxygen_max; + gforce.ignore_gforce_seconds = 1.0; + } + ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::WakeUp)); + ew_effect.send(effects::SpawnEffectEvent { + class: effects::Effects::FadeIn(Color::CYAN), + duration: 1.0, + }); + } + } + "cryofadeout" => { + ew_effect.send(effects::SpawnEffectEvent { + class: effects::Effects::FadeOut(Color::CYAN), + duration: 5.1, + }); + } + _ => { + error!("Error, undefined chat script {name}"); + } + } + } +} diff --git a/src/defs.txt b/src/defs.txt index 499d56a..df4a8cd 100644 --- a/src/defs.txt +++ b/src/defs.txt @@ -213,7 +213,7 @@ actor 60 -15 -40 suit actor -300 0 40 suit relativeto player - id drifter + id Drifter name "Drifter" chatid Drifter oxygen 0.08