implement conversation system
This commit is contained in:
parent
60370ad583
commit
bac0b59733
44
assets/scenes/conversations.scn.ron
Normal file
44
assets/scenes/conversations.scn.ron
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
(
|
||||||
|
resources: {},
|
||||||
|
entities: {
|
||||||
|
4294967296: (
|
||||||
|
components: {
|
||||||
|
"outfly::actor::ChatBranch": (
|
||||||
|
id: "hialien",
|
||||||
|
name: "Icarus",
|
||||||
|
label: "INIT",
|
||||||
|
delay: 0.0,
|
||||||
|
sound: "ping",
|
||||||
|
reply: "Requesting permission to communicate...",
|
||||||
|
goto: "requested",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
4294967297: (
|
||||||
|
components: {
|
||||||
|
"outfly::actor::ChatBranch": (
|
||||||
|
id: "hialien",
|
||||||
|
name: "Icarus",
|
||||||
|
label: "requested",
|
||||||
|
delay: 1.0,
|
||||||
|
sound: "chat",
|
||||||
|
reply: "Oh hey there, didn't even notice you! Was playing some VR game! What's up?",
|
||||||
|
goto: "reply1",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
4294967298: (
|
||||||
|
components: {
|
||||||
|
"outfly::actor::ChatBranch": (
|
||||||
|
id: "hialien",
|
||||||
|
name: "Icarus",
|
||||||
|
label: "reply1",
|
||||||
|
delay: 3.0,
|
||||||
|
sound: "chat",
|
||||||
|
reply: "Not so chatty, huh? That's ok. See you around.",
|
||||||
|
goto: "EXIT",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
)
|
114
src/actor.rs
114
src/actor.rs
|
@ -2,12 +2,17 @@ use bevy::prelude::*;
|
||||||
use crate::{nature, settings, actor, audio, hud};
|
use crate::{nature, settings, actor, audio, hud};
|
||||||
|
|
||||||
const MIN_INTERACT_DISTANCE: f32 = 30.0;
|
const MIN_INTERACT_DISTANCE: f32 = 30.0;
|
||||||
|
const ASSET_CONVERSATIONS: &str = "scenes/conversations.scn.ron";
|
||||||
|
|
||||||
pub struct ActorPlugin;
|
pub struct ActorPlugin;
|
||||||
impl Plugin for ActorPlugin {
|
impl Plugin for ActorPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
|
app.add_systems(Startup, setup);
|
||||||
|
app.register_type::<ChatBranch>();
|
||||||
|
app.register_type::<ChatChoice>();
|
||||||
app.add_systems(FixedUpdate, update);
|
app.add_systems(FixedUpdate, update);
|
||||||
app.add_systems(Update, (
|
app.add_systems(Update, (
|
||||||
|
handle_new_conversations,
|
||||||
handle_conversations,
|
handle_conversations,
|
||||||
handle_input,
|
handle_input,
|
||||||
));
|
));
|
||||||
|
@ -42,6 +47,34 @@ impl Default for Actor {
|
||||||
#[derive(Component)] pub struct PlayerInConversation;
|
#[derive(Component)] pub struct PlayerInConversation;
|
||||||
#[derive(Component)] pub struct InConversationWithPlayer;
|
#[derive(Component)] pub struct InConversationWithPlayer;
|
||||||
|
|
||||||
|
#[derive(Component, Reflect, Default)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
pub struct ChatBranch {
|
||||||
|
pub id: String,
|
||||||
|
pub name: String,
|
||||||
|
pub label: String,
|
||||||
|
pub delay: f32,
|
||||||
|
pub sound: String,
|
||||||
|
pub reply: String,
|
||||||
|
pub goto: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component, Reflect, Default)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
pub struct ChatChoice {
|
||||||
|
pub id: String,
|
||||||
|
pub label: String,
|
||||||
|
pub choice: String,
|
||||||
|
pub goto: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct Chat {
|
||||||
|
pub id: String,
|
||||||
|
pub label: String,
|
||||||
|
pub timer: f32,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct Talker {
|
pub struct Talker {
|
||||||
pub conv: String,
|
pub conv: String,
|
||||||
|
@ -75,6 +108,37 @@ const SUIT_SIMPLE: Suit = Suit {
|
||||||
oxygen_max: nature::OXY_D,
|
oxygen_max: nature::OXY_D,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub fn setup(
|
||||||
|
mut commands: Commands,
|
||||||
|
asset_server: Res<AssetServer>,
|
||||||
|
) {
|
||||||
|
commands.spawn(DynamicSceneBundle {
|
||||||
|
scene: asset_server.load(ASSET_CONVERSATIONS),
|
||||||
|
..default()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//#[allow(dead_code)]
|
||||||
|
//pub fn serialize(
|
||||||
|
// world: &mut World,
|
||||||
|
//) {
|
||||||
|
// let mut scene_world = World::new();
|
||||||
|
// scene_world.spawn(ChatBranch {
|
||||||
|
// id: "hialien".to_string(),
|
||||||
|
// name: "Icarus".to_string(),
|
||||||
|
// label: "INTERACT".to_string(),
|
||||||
|
// delay: 0.0,
|
||||||
|
// reply: "Requesting permission to communicate...".to_string(),
|
||||||
|
// goto: "requested".to_string(),
|
||||||
|
// });
|
||||||
|
// let type_registry = world.resource::<AppTypeRegistry>().clone();
|
||||||
|
// scene_world.insert_resource(type_registry);
|
||||||
|
// let type_registry = world.resource::<AppTypeRegistry>();
|
||||||
|
// let scene = DynamicScene::from_world(&scene_world);
|
||||||
|
// let serialized_scene = scene.serialize_ron(type_registry).unwrap();
|
||||||
|
// info!("{}", serialized_scene);
|
||||||
|
//}
|
||||||
|
|
||||||
pub fn update(
|
pub fn update(
|
||||||
time: Res<Time>,
|
time: Res<Time>,
|
||||||
mut query: Query<(&mut LifeForm, &mut Suit)>,
|
mut query: Query<(&mut LifeForm, &mut Suit)>,
|
||||||
|
@ -107,7 +171,7 @@ pub fn handle_input(
|
||||||
for (talker, transform) in &query {
|
for (talker, transform) in &query {
|
||||||
if transform.translation.distance_squared(player.translation) <= mindist {
|
if transform.translation.distance_squared(player.translation) <= mindist {
|
||||||
ew_conv.send(StartConversationEvent{conv: talker.conv.clone()});
|
ew_conv.send(StartConversationEvent{conv: talker.conv.clone()});
|
||||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::IncomingChatMessage));
|
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Ping));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,16 +179,58 @@ pub fn handle_input(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_conversations(
|
pub fn handle_new_conversations(
|
||||||
|
mut commands: Commands,
|
||||||
mut er_conv: EventReader<StartConversationEvent>,
|
mut er_conv: EventReader<StartConversationEvent>,
|
||||||
mut log: ResMut<hud::Log>,
|
mut log: ResMut<hud::Log>,
|
||||||
) {
|
) {
|
||||||
for my_event in er_conv.read() {
|
for _my_event in er_conv.read() {
|
||||||
log.chat(my_event.conv.clone(), "Alien".to_string());
|
log.info("Establishing connection with Alien".to_string());
|
||||||
|
commands.spawn(Chat {
|
||||||
|
id: "hialien".to_string(),
|
||||||
|
label: "INIT".to_string(),
|
||||||
|
timer: 0.0,
|
||||||
|
});
|
||||||
|
//log.chat(my_event.conv.clone(), "Alien".to_string());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn handle_conversations(
|
||||||
|
mut commands: Commands,
|
||||||
|
mut log: ResMut<hud::Log>,
|
||||||
|
mut q_conv: Query<(Entity, &mut Chat)>,
|
||||||
|
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
||||||
|
chat_branches: Query<&ChatBranch>, // TODO: use Table for faster iteration?
|
||||||
|
//chat_choices: Query<&ChatChoice>,
|
||||||
|
) {
|
||||||
|
for (entity, mut chat) in &mut q_conv {
|
||||||
|
if chat.label == "EXIT" {
|
||||||
|
debug!("Despawning chat.");
|
||||||
|
commands.entity(entity).despawn();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let branches: Vec<&ChatBranch> = chat_branches.iter()
|
||||||
|
.filter(|branch| branch.id == chat.id && branch.label == chat.label)
|
||||||
|
.collect();
|
||||||
|
if branches.len() != 1 {
|
||||||
|
error!("Expected 1 branch with ID '{}' and label '{}', but got {}! Aborting conversation.", chat.id, chat.label, branches.len());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let branch = branches[0];
|
||||||
|
log.chat(branch.reply.clone(), branch.name.clone());
|
||||||
|
if chat.label == "EXIT" {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
chat.label = branch.goto.clone();
|
||||||
|
if branch.sound != "" {
|
||||||
|
let sfx = audio::str2sfx(branch.sound.as_str());
|
||||||
|
ew_sfx.send(audio::PlaySfxEvent(sfx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//pub enum SuitSystemHandler {
|
//pub enum SuitSystemHandler {
|
||||||
// Heat,
|
// Heat,
|
||||||
|
|
20
src/audio.rs
20
src/audio.rs
|
@ -4,7 +4,8 @@ use crate::settings;
|
||||||
|
|
||||||
const ASSET_CLICK: &str = "sounds/click-button-140881-crop.ogg";
|
const ASSET_CLICK: &str = "sounds/click-button-140881-crop.ogg";
|
||||||
const ASSET_SWITCH: &str = "sounds/typosonic-typing-192811-crop.ogg";
|
const ASSET_SWITCH: &str = "sounds/typosonic-typing-192811-crop.ogg";
|
||||||
const ASSET_INCOMING_MESSAGE: &str = "tmp/multi-pop-2-188167.mp3.ogg";
|
const ASSET_INCOMING_MESSAGE: &str = "tmp/beep-6-96243.ogg";
|
||||||
|
const ASSET_PING: &str = "tmp/glitch-sound-fx-pack-04-118236.ogg";
|
||||||
const ASSET_RADIO: &str = "tmp/LP - Girls Go Wild (Official Music Video)-M7XRN0oHGIM.ogg";
|
const ASSET_RADIO: &str = "tmp/LP - Girls Go Wild (Official Music Video)-M7XRN0oHGIM.ogg";
|
||||||
const ASSET_BGM: &str = "tmp/FTL - Faster Than Light (2012) OST - 12 - Void (Explore)-edQw2yYXQJM.ogg";
|
const ASSET_BGM: &str = "tmp/FTL - Faster Than Light (2012) OST - 12 - Void (Explore)-edQw2yYXQJM.ogg";
|
||||||
|
|
||||||
|
@ -24,6 +25,8 @@ pub enum Sfx {
|
||||||
IncomingChatMessage,
|
IncomingChatMessage,
|
||||||
Click,
|
Click,
|
||||||
Switch,
|
Switch,
|
||||||
|
Ping,
|
||||||
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Event)] pub struct PlaySfxEvent(pub Sfx);
|
#[derive(Event)] pub struct PlaySfxEvent(pub Sfx);
|
||||||
|
@ -36,6 +39,7 @@ pub enum Sfx {
|
||||||
#[derive(Resource)] pub struct SoundClick(Handle<AudioSource>);
|
#[derive(Resource)] pub struct SoundClick(Handle<AudioSource>);
|
||||||
#[derive(Resource)] pub struct SoundSwitch(Handle<AudioSource>);
|
#[derive(Resource)] pub struct SoundSwitch(Handle<AudioSource>);
|
||||||
#[derive(Resource)] pub struct SoundIncomingMessage(Handle<AudioSource>);
|
#[derive(Resource)] pub struct SoundIncomingMessage(Handle<AudioSource>);
|
||||||
|
#[derive(Resource)] pub struct SoundPing(Handle<AudioSource>);
|
||||||
|
|
||||||
pub fn setup(
|
pub fn setup(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
|
@ -86,6 +90,7 @@ pub fn setup(
|
||||||
commands.insert_resource(SoundClick(asset_server.load(ASSET_CLICK)));
|
commands.insert_resource(SoundClick(asset_server.load(ASSET_CLICK)));
|
||||||
commands.insert_resource(SoundSwitch(asset_server.load(ASSET_SWITCH)));
|
commands.insert_resource(SoundSwitch(asset_server.load(ASSET_SWITCH)));
|
||||||
commands.insert_resource(SoundIncomingMessage(asset_server.load(ASSET_INCOMING_MESSAGE)));
|
commands.insert_resource(SoundIncomingMessage(asset_server.load(ASSET_INCOMING_MESSAGE)));
|
||||||
|
commands.insert_resource(SoundPing(asset_server.load(ASSET_PING)));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn toggle_bgm(
|
pub fn toggle_bgm(
|
||||||
|
@ -113,6 +118,7 @@ pub fn play_sfx(
|
||||||
sound_click: Res<SoundClick>,
|
sound_click: Res<SoundClick>,
|
||||||
sound_switch: Res<SoundSwitch>,
|
sound_switch: Res<SoundSwitch>,
|
||||||
sound_incoming_message: Res<SoundIncomingMessage>,
|
sound_incoming_message: Res<SoundIncomingMessage>,
|
||||||
|
sound_ping: Res<SoundPing>,
|
||||||
) {
|
) {
|
||||||
if settings.mute_sfx && !events_sfx.is_empty() {
|
if settings.mute_sfx && !events_sfx.is_empty() {
|
||||||
events_sfx.clear();
|
events_sfx.clear();
|
||||||
|
@ -123,12 +129,24 @@ pub fn play_sfx(
|
||||||
Sfx::Switch => sound_switch.0.clone(),
|
Sfx::Switch => sound_switch.0.clone(),
|
||||||
Sfx::Click => sound_click.0.clone(),
|
Sfx::Click => sound_click.0.clone(),
|
||||||
Sfx::IncomingChatMessage => sound_incoming_message.0.clone(),
|
Sfx::IncomingChatMessage => sound_incoming_message.0.clone(),
|
||||||
|
Sfx::Ping => sound_ping.0.clone(),
|
||||||
|
Sfx::None => sound_ping.0.clone(),
|
||||||
},
|
},
|
||||||
settings: PlaybackSettings::DESPAWN,
|
settings: PlaybackSettings::DESPAWN,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn str2sfx(sfx_label: &str) -> Sfx {
|
||||||
|
return match sfx_label {
|
||||||
|
"switch" => Sfx::Switch,
|
||||||
|
"click" => Sfx::Click,
|
||||||
|
"chat" => Sfx::IncomingChatMessage,
|
||||||
|
"ping" => Sfx::Ping,
|
||||||
|
_ => Sfx::None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn update_music(
|
pub fn update_music(
|
||||||
mut events: EventReader<ToggleMusicEvent>,
|
mut events: EventReader<ToggleMusicEvent>,
|
||||||
bgm_controller: Query<&AudioSink, With<ComponentBGM>>,
|
bgm_controller: Query<&AudioSink, With<ComponentBGM>>,
|
||||||
|
|
|
@ -7,7 +7,7 @@ use std::time::SystemTime;
|
||||||
|
|
||||||
const HUD_REFRESH_TIME: f32 = 0.5;
|
const HUD_REFRESH_TIME: f32 = 0.5;
|
||||||
const FONT: &str = "tmp/fonts/NotoSansSC-Thin.ttf";
|
const FONT: &str = "tmp/fonts/NotoSansSC-Thin.ttf";
|
||||||
const LOG_MAX: usize = 5;
|
const LOG_MAX: usize = 20;
|
||||||
const LOG_MAX_TIME_S: u64 = 20;
|
const LOG_MAX_TIME_S: u64 = 20;
|
||||||
|
|
||||||
pub struct HudPlugin;
|
pub struct HudPlugin;
|
||||||
|
|
Loading…
Reference in a new issue