outfly/src/actor.rs

282 lines
7.5 KiB
Rust
Raw Normal View History

2024-03-17 22:49:50 +00:00
use bevy::prelude::*;
use crate::{nature, settings, actor, audio, hud};
const MIN_INTERACT_DISTANCE: f32 = 30.0;
2024-03-19 04:38:11 +00:00
const ASSET_CONVERSATIONS: &str = "scenes/conversations.scn.ron";
2024-03-17 22:49:50 +00:00
pub struct ActorPlugin;
impl Plugin for ActorPlugin {
fn build(&self, app: &mut App) {
2024-03-19 04:38:11 +00:00
app.add_systems(Startup, setup);
app.register_type::<ChatBranch>();
app.register_type::<ChatChoice>();
2024-03-17 22:49:50 +00:00
app.add_systems(FixedUpdate, update);
app.add_systems(Update, (
2024-03-19 04:38:11 +00:00
handle_new_conversations,
handle_conversations,
handle_input,
));
app.add_event::<StartConversationEvent>();
2024-03-17 22:49:50 +00:00
}
}
#[derive(Event)]
pub struct StartConversationEvent {
pub conv: String
}
2024-03-17 22:49:50 +00:00
#[derive(Component)]
pub struct Actor {
pub hp: f32,
pub m: f32, // mass
pub v: Vec3, // velocity
// TODO: rotation
}
impl Default for Actor {
fn default() -> Self {
Self {
hp: 100.0,
m: 100.0,
v: Vec3::ZERO,
}
}
}
#[derive(Component)] pub struct Player;
#[derive(Component)] pub struct PlayerInConversation;
#[derive(Component)] pub struct InConversationWithPlayer;
2024-03-19 04:38:11 +00:00
#[derive(Component, Reflect, Default)]
#[reflect(Component)]
pub struct ChatBranch {
pub id: String,
pub name: String,
pub label: String,
2024-03-19 05:01:17 +00:00
pub delay: f64,
2024-03-19 04:38:11 +00:00
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,
2024-03-19 05:01:17 +00:00
pub timer: f64,
2024-03-19 04:38:11 +00:00
}
#[derive(Component)]
pub struct Talker {
pub conv: String,
}
2024-03-17 22:49:50 +00:00
#[derive(Component)]
pub struct LifeForm {
pub adrenaline: f32,
pub adrenaline_baseline: f32,
pub adrenaline_jolt: f32,
}
impl Default for LifeForm { fn default() -> Self { Self {
adrenaline: 0.3,
adrenaline_baseline: 0.3,
adrenaline_jolt: 0.0,
}}}
#[derive(Component)]
pub struct Suit {
pub oxygen: f32,
pub power: f32,
pub oxygen_max: f32,
pub power_max: f32,
}
impl Default for Suit { fn default() -> Self { SUIT_SIMPLE } }
const SUIT_SIMPLE: Suit = Suit {
power: 1e5,
power_max: 1e5,
2024-03-18 22:53:52 +00:00
oxygen: nature::OXY_D,
oxygen_max: nature::OXY_D,
2024-03-17 22:49:50 +00:00
};
2024-03-19 04:38:11 +00:00
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);
//}
2024-03-17 22:49:50 +00:00
pub fn update(
time: Res<Time>,
mut query: Query<(&mut LifeForm, &mut Suit)>,
) {
let d = time.delta_seconds();
for (mut lifeform, mut suit) in query.iter_mut() {
if lifeform.adrenaline_jolt.abs() > 1e-3 {
lifeform.adrenaline_jolt *= 0.99;
}
else {
lifeform.adrenaline_jolt = 0.0
}
lifeform.adrenaline = (lifeform.adrenaline - 0.0001 + lifeform.adrenaline_jolt * 0.01).clamp(0.0, 1.0);
2024-03-18 22:53:52 +00:00
suit.oxygen = (suit.oxygen - nature::OXY_S*d).clamp(0.0, 1.0);
2024-03-17 22:49:50 +00:00
}
}
pub fn handle_input(
keyboard_input: Res<ButtonInput<KeyCode>>,
settings: ResMut<settings::Settings>,
query: Query<(&Talker, &Transform)>,
player: Query<&Transform, With<actor::Player>>,
mut ew_conv: EventWriter<StartConversationEvent>,
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
)
{
if keyboard_input.just_pressed(settings.key_interact) {
let mindist = MIN_INTERACT_DISTANCE * MIN_INTERACT_DISTANCE;
if let Ok(player) = player.get_single() {
for (talker, transform) in &query {
if transform.translation.distance_squared(player.translation) <= mindist {
ew_conv.send(StartConversationEvent{conv: talker.conv.clone()});
2024-03-19 04:38:11 +00:00
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Ping));
break;
}
}
}
}
}
2024-03-19 04:38:11 +00:00
pub fn handle_new_conversations(
mut commands: Commands,
mut er_conv: EventReader<StartConversationEvent>,
2024-03-19 05:01:17 +00:00
time: Res<Time>,
) {
2024-03-19 04:38:11 +00:00
for _my_event in er_conv.read() {
commands.spawn(Chat {
id: "hialien".to_string(),
label: "INIT".to_string(),
2024-03-19 05:01:17 +00:00
timer: time.elapsed_seconds_f64(),
2024-03-19 04:38:11 +00:00
});
break;
}
}
2024-03-19 04:38:11 +00:00
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>,
2024-03-19 05:01:17 +00:00
time: Res<Time>,
2024-03-19 04:38:11 +00:00
chat_branches: Query<&ChatBranch>, // TODO: use Table for faster iteration?
//chat_choices: Query<&ChatChoice>,
) {
2024-03-19 05:01:17 +00:00
let now = time.elapsed_seconds_f64();
2024-03-19 04:38:11 +00:00
for (entity, mut chat) in &mut q_conv {
if chat.label == "EXIT" {
debug!("Despawning chat.");
commands.entity(entity).despawn();
continue;
}
2024-03-19 05:01:17 +00:00
if now < chat.timer {
continue;
}
2024-03-19 04:38:11 +00:00
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));
}
2024-03-19 05:01:17 +00:00
info!("<chat.timer={:.2}, branch.delay={:.2}, epoch={:.2}", chat.timer, branch.delay, now);
chat.timer = now + branch.delay;
info!(">chat.timer={:.2}, branch.delay={:.2}, epoch={:.2}", chat.timer, branch.delay, now);
2024-03-19 04:38:11 +00:00
}
}
2024-03-17 22:49:50 +00:00
//pub enum SuitSystemHandler {
// Heat,
// None,
//}
//#[derive(Component)]
//pub struct SuitSystem {
// pub name: String,
// pub active: bool,
// pub power: f32,
// pub handler: SuitSystemHandler,
//}
//
//impl Default for SuitSystem {
// fn default() -> Self {
// Self {
// name: "Untitled".to_string(),
// active: true,
// power: 0.0,
// handler: SuitSystemHandler::None,
// }
// }
//}
//pub fn setup(
// mut commands: Commands,
// settings: Res<settings::Settings>,
//) {
// commands.spawn((
// Player,
// SuitSystem {
// name: "HUD".to_string(),
// active: settings.hud_active,
// power: -0.05,
// ..default()
// },
// SuitSystem {
// name: "Heater".to_string(),
// handler: SuitSystemHandler::Heat,
// ..default()
// }
// ));
//}