implement chat scripts
This commit is contained in:
parent
08ec42c043
commit
9176caa372
167
src/chat.rs
167
src/chat.rs
|
@ -1,7 +1,9 @@
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
use bevy::math::DVec3;
|
||||||
|
use bevy_xpbd_3d::prelude::*;
|
||||||
use serde_yaml::Value;
|
use serde_yaml::Value;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use crate::{audio, hud, settings, world};
|
use crate::{actor, audio, hud, settings, world, effects};
|
||||||
|
|
||||||
pub const CHATS: &[&str] = &[
|
pub const CHATS: &[&str] = &[
|
||||||
include_str!("chats/serenity.yaml"),
|
include_str!("chats/serenity.yaml"),
|
||||||
|
@ -45,10 +47,12 @@ impl Plugin for ChatPlugin {
|
||||||
handle_reply_keys.before(handle_chat_timer),
|
handle_reply_keys.before(handle_chat_timer),
|
||||||
handle_chat_timer.before(handle_chat_events),
|
handle_chat_timer.before(handle_chat_events),
|
||||||
handle_new_conversations.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::<StartConversationEvent>();
|
app.add_event::<StartConversationEvent>();
|
||||||
app.add_event::<ChatEvent>();
|
app.add_event::<ChatEvent>();
|
||||||
|
app.add_event::<ChatScriptEvent>();
|
||||||
app.insert_resource(ChatDB(Vec::new()));
|
app.insert_resource(ChatDB(Vec::new()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,6 +74,33 @@ pub struct Choice {
|
||||||
pub goto: ChatPos,
|
pub goto: ChatPos,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Talker {
|
||||||
|
pub conv_id: String,
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub pronoun: Option<String>,
|
||||||
|
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.
|
// This is the only place where any YAML interaction should be happening.
|
||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
pub struct ChatDB(Vec<Value>);
|
pub struct ChatDB(Vec<Value>);
|
||||||
|
@ -230,6 +261,7 @@ impl ChatDB {
|
||||||
let key = key.as_str();
|
let key = key.as_str();
|
||||||
match (key, value) {
|
match (key, value) {
|
||||||
(Some(TOKEN_CHAT), _) => {}
|
(Some(TOKEN_CHAT), _) => {}
|
||||||
|
(Some(TOKEN_LABEL), _) => {}
|
||||||
(Some(TOKEN_MSG), Value::String(message)) => {
|
(Some(TOKEN_MSG), Value::String(message)) => {
|
||||||
event.send(ChatEvent::SpawnMessage(
|
event.send(ChatEvent::SpawnMessage(
|
||||||
message.to_string(), hud::LogLevel::Chat));
|
message.to_string(), hud::LogLevel::Chat));
|
||||||
|
@ -242,6 +274,12 @@ impl ChatDB {
|
||||||
event.send(ChatEvent::SpawnMessage(
|
event.send(ChatEvent::SpawnMessage(
|
||||||
message.to_string(), hud::LogLevel::Warning));
|
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<String>,
|
|
||||||
pub pronoun: Option<String>,
|
|
||||||
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<ChatDB>) {
|
pub fn load_chats(mut chatdb: ResMut<ChatDB>) {
|
||||||
for chat_yaml in CHATS {
|
for chat_yaml in CHATS {
|
||||||
if chatdb.load_from_str(chat_yaml).is_err() {
|
if chatdb.load_from_str(chat_yaml).is_err() {
|
||||||
|
@ -372,6 +387,7 @@ pub fn handle_chat_timer(
|
||||||
pub fn handle_chat_events(
|
pub fn handle_chat_events(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut er_chatevent: EventReader<ChatEvent>,
|
mut er_chatevent: EventReader<ChatEvent>,
|
||||||
|
mut ew_chatscript: EventWriter<ChatScriptEvent>,
|
||||||
mut log: ResMut<hud::Log>,
|
mut log: ResMut<hud::Log>,
|
||||||
q_choices: Query<Entity, With<Choice>>,
|
q_choices: Query<Entity, With<Choice>>,
|
||||||
mut q_chats: Query<(Entity, &mut Chat)>,
|
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;
|
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;
|
selected_choice += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn handle_chat_scripts(
|
||||||
|
mut er_chatscript: EventReader<ChatScriptEvent>,
|
||||||
|
mut q_actor: Query<(&mut actor::Actor, &mut actor::Suit), Without<actor::Player>>,
|
||||||
|
mut q_player: Query<(&mut actor::Actor, &mut actor::Suit, &mut actor::ExperiencesGForce), With<actor::Player>>,
|
||||||
|
mut q_playercam: Query<(&mut Position, &mut LinearVelocity), With<actor::PlayerCamera>>,
|
||||||
|
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
||||||
|
mut ew_effect: EventWriter<effects::SpawnEffectEvent>,
|
||||||
|
) {
|
||||||
|
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::<f32>() {
|
||||||
|
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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -213,7 +213,7 @@ actor 60 -15 -40 suit
|
||||||
|
|
||||||
actor -300 0 40 suit
|
actor -300 0 40 suit
|
||||||
relativeto player
|
relativeto player
|
||||||
id drifter
|
id Drifter
|
||||||
name "Drifter"
|
name "Drifter"
|
||||||
chatid Drifter
|
chatid Drifter
|
||||||
oxygen 0.08
|
oxygen 0.08
|
||||||
|
|
Loading…
Reference in a new issue