Compare commits
33 commits
939f03b95d
...
86ce38eda1
Author | SHA1 | Date | |
---|---|---|---|
yuni | 86ce38eda1 | ||
yuni | 629f6ac900 | ||
yuni | b2e6ba2b77 | ||
yuni | ef0ada9dac | ||
yuni | 053f2827dd | ||
yuni | cb698cc3b2 | ||
yuni | 3676fd444c | ||
yuni | d0df5c5dbd | ||
yuni | 9ea79ff80f | ||
yuni | 0c31055422 | ||
yuni | eb481edc8e | ||
yuni | 2b74b50563 | ||
yuni | 0d312edeee | ||
yuni | dc1037e5a3 | ||
yuni | a3ea057994 | ||
yuni | 9954c19d2a | ||
yuni | a6f6b8b582 | ||
yuni | db3545e9a3 | ||
yuni | abaed74424 | ||
yuni | 5f56d63d32 | ||
yuni | 86c2c5e410 | ||
yuni | cd1f8c18cf | ||
yuni | 860d7f8d4b | ||
yuni | 8b2debfa37 | ||
yuni | d064680d60 | ||
yuni | 817a5e2a96 | ||
yuni | 10d65f8c02 | ||
yuni | 96b8ed22b2 | ||
yuni | e4c3eccb02 | ||
yuni | b3d63301cc | ||
yuni | c7b4216c57 | ||
yuni | 8c51e67228 | ||
yuni | b8cabe7ac1 |
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -3,3 +3,4 @@ assets/tmp
|
|||
assets/external
|
||||
*.blend1
|
||||
extra
|
||||
outfly_*.zip
|
||||
|
|
20
COMMENTARY.md
Normal file
20
COMMENTARY.md
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Developer Commentary
|
||||
## Clippy Convenience Companion
|
||||
|
||||
Clippy™ Convenience Companion is a self-assembling, self-replicating, highly modular being that emerges from the collection of individual Clippy™ cubes.
|
||||
|
||||
These cubes are interchangeable, solar panel plated, driven by strong reaction wheels, and connect to each other through powerful magnets along the surface. A sensor array picks up various EM frequencies and a camera image through the plating. There are no moving parts on the outside. Individual cubes are able to transfer electricity to connected cubes, as well as transmit information wirelessly.
|
||||
|
||||
The shape of a Clippy™ can change quickly to solve a variety of space problems. Off-the-shelf plug-and-play Clippy™ modules further enhance their agency with additional tools, such as propulsion modules, sensors, or manipulators.
|
||||
|
||||
A Clippy™ Convenience Companion will broadcast an Augmented Reality overlay that resembles a face, in the somewhat antiquated "kaomoji" style of the late 1900's, typically a friendly face to instill trust and kindness in viewers. How a unified persona with aligned intentions emerges from the interactions of the individual Clippy™ cubes is, as of writing, still subject to debate.
|
||||
|
||||
Clippy™ is inspired by [self-assembling cube robots](https://www.youtube.com/watch?v=hI5UDKaWJOo), by the [paperclip maximizer thought experiment](https://en.wikipedia.org/w/index.php?title=Instrumental_convergence&oldid=1210129297#Paperclip_maximizer) (which is also the source of the name), by [Star Gate's Replicators](https://stargate.fandom.com/wiki/Replicator), and finally, by [Fallout New Vegas' "Yes Man" robot](https://fallout.fandom.com/wiki/Yes_Man).
|
||||
|
||||
## MeteorAceGT™ Sports Racing Capsule
|
||||
|
||||
This icosahedral sports spacecraft is designed for maximum stability and maneuverability at high velocities, not only in vacuum but also in thick atmospheres. Strong reaction wheels allow perfect control of spacecraft rotation, and allow you to land the spacecraft safely on any planet or moon. Powerful nuclear-powered electromagnets shield from ionized particle radiation, allowing you to surf the clouds of Jupiter safely, with the spherical shape providing maximum structural integrity.
|
||||
|
||||
The excessive forward thrust will get many pilots to pass out, while using close to zero propellant thanks to the advanced ion engine array. No dedicated backward thrusters are available for structural integrity in atmospheres. This is compensated by the sideways thrusters, which are mildly inclined to the forward direction, providing a small amount of backward thrust.
|
||||
|
||||
The design of this racing capsule was inspired [Shotaro Kaneda's motorbike from Akira](https://akira.fandom.com/wiki/Shotaro_Kaneda%27s_Bike) and by the [orbital module of the Soyuz spacecraft](https://en.wikipedia.org/w/index.php?title=Soyuz_%28spacecraft%29&oldid=1218141107#Orbital_module).
|
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -2769,7 +2769,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "outfly"
|
||||
version = "0.6.0"
|
||||
version = "0.6.1"
|
||||
dependencies = [
|
||||
"bevy",
|
||||
"bevy_embedded_assets",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "outfly"
|
||||
version = "0.6.0"
|
||||
version = "0.6.1"
|
||||
edition = "2021"
|
||||
homepage = "https://codeberg.org/hut/outfly"
|
||||
repository = "https://codeberg.org/hut/outfly"
|
||||
|
|
|
@ -32,6 +32,7 @@ Links:
|
|||
- R: Rotate (hold & move mouse)
|
||||
- E: Interact: Talk to people, enter vehicles
|
||||
- Q: Exit vehicle
|
||||
- F7: Restart game
|
||||
- JKULIO: Mouseless camera rotation
|
||||
- Augmented Reality: (toggle with Tab)
|
||||
- Left click: Target objects
|
||||
|
@ -40,6 +41,7 @@ Links:
|
|||
- Tab: Toggle HUD/AR
|
||||
- F11: Toggle fullscreen
|
||||
- F: Toggle 3rd person view
|
||||
- Y: Toggle rotation stabilizer
|
||||
- T: Toggle music
|
||||
- M: Toggle sound effects
|
||||
- Cheats
|
||||
|
@ -151,6 +153,10 @@ python -m http.server -d wasm
|
|||
|
||||
# Changelog
|
||||
|
||||
- v0.6.1:
|
||||
- Implement free public transport with 3 bus stations
|
||||
- Implement Clippy™ Convenience Companion drone
|
||||
- Implement augmented reality overlays on top of NPC appearances
|
||||
- v0.6.0:
|
||||
- Implement zooming with right click (AR only)
|
||||
- Implement targeting objects with left click (AR only)
|
||||
|
|
BIN
assets/models/clippy.glb
Normal file
BIN
assets/models/clippy.glb
Normal file
Binary file not shown.
BIN
assets/models/clippy_ar.glb
Normal file
BIN
assets/models/clippy_ar.glb
Normal file
Binary file not shown.
BIN
assets/models/orb_busstop.glb
Normal file
BIN
assets/models/orb_busstop.glb
Normal file
Binary file not shown.
Binary file not shown.
BIN
assets/models/suit_ar_chefhat.glb
Normal file
BIN
assets/models/suit_ar_chefhat.glb
Normal file
Binary file not shown.
62
src/actor.rs
62
src/actor.rs
|
@ -33,7 +33,20 @@ impl Plugin for ActorPlugin {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Event)] pub struct PlayerDiesEvent;
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum DamageType {
|
||||
Unknown,
|
||||
Mental,
|
||||
Trauma,
|
||||
Asphyxiation,
|
||||
//Poison,
|
||||
//Radiation,
|
||||
//Freeze,
|
||||
//Burn,
|
||||
}
|
||||
|
||||
#[derive(Event)] pub struct PlayerDiesEvent(pub DamageType);
|
||||
|
||||
#[derive(Event)]
|
||||
pub struct VehicleEnterExitEvent {
|
||||
vehicle: Entity,
|
||||
|
@ -63,6 +76,7 @@ pub struct HitPoints {
|
|||
pub current: f32,
|
||||
pub max: f32,
|
||||
pub damage: f32,
|
||||
pub damagetype: DamageType,
|
||||
}
|
||||
impl Default for HitPoints {
|
||||
fn default() -> Self {
|
||||
|
@ -70,6 +84,7 @@ impl Default for HitPoints {
|
|||
current: 100.0,
|
||||
max: 100.0,
|
||||
damage: 0.0,
|
||||
damagetype: DamageType::Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -211,6 +226,7 @@ pub fn update_physics_lifeforms(
|
|||
suit.oxygen = (suit.oxygen - oxygen_drain*d).clamp(0.0, suit.oxygen_max);
|
||||
if suit.oxygen <= 0.0 {
|
||||
hp.damage += 1.0 * d;
|
||||
hp.damagetype = DamageType::Asphyxiation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -218,13 +234,14 @@ pub fn update_physics_lifeforms(
|
|||
pub fn handle_input(
|
||||
mut commands: Commands,
|
||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||
settings: ResMut<settings::Settings>,
|
||||
mut settings: ResMut<settings::Settings>,
|
||||
q_talker: Query<(&chat::Talker, &Transform), (Without<actor::Player>, Without<Camera>)>,
|
||||
player: Query<Entity, With<actor::Player>>,
|
||||
q_camera: Query<&Transform, With<Camera>>,
|
||||
q_vehicles: Query<(Entity, &Transform), (With<actor::Vehicle>, Without<actor::Player>, Without<Camera>)>,
|
||||
mut ew_conv: EventWriter<chat::StartConversationEvent>,
|
||||
mut ew_vehicle: EventWriter<VehicleEnterExitEvent>,
|
||||
mut ew_playerdies: EventWriter<PlayerDiesEvent>,
|
||||
q_player_drives: Query<Entity, With<PlayerDrivesThis>>,
|
||||
) {
|
||||
if q_camera.is_empty() || player.is_empty() {
|
||||
|
@ -278,6 +295,10 @@ pub fn handle_input(
|
|||
break;
|
||||
}
|
||||
}
|
||||
else if keyboard_input.just_pressed(settings.key_restart) {
|
||||
settings.god_mode = false;
|
||||
ew_playerdies.send(PlayerDiesEvent(DamageType::Mental));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_vehicle_enter_exit(
|
||||
|
@ -403,12 +424,13 @@ fn handle_player_death(
|
|||
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
||||
mut ew_effect: EventWriter<effects::SpawnEffectEvent>,
|
||||
mut log: ResMut<hud::Log>,
|
||||
settings: Res<settings::Settings>,
|
||||
mut settings: ResMut<settings::Settings>,
|
||||
) {
|
||||
for _ in er_playerdies.read() {
|
||||
for death in er_playerdies.read() {
|
||||
if settings.god_mode {
|
||||
return;
|
||||
}
|
||||
settings.reset_player_settings();
|
||||
for entity in &q_noscenes {
|
||||
cmd.entity(entity).despawn();
|
||||
}
|
||||
|
@ -418,11 +440,30 @@ fn handle_player_death(
|
|||
}
|
||||
log.clear();
|
||||
//cmd.run_system(commands::load_defs); // why is it so complicated to get SystemId?
|
||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::WakeUp));
|
||||
ew_effect.send(effects::SpawnEffectEvent {
|
||||
class: effects::Effects::FadeIn(Color::MAROON),
|
||||
duration: 1.0,
|
||||
});
|
||||
|
||||
match death.0 {
|
||||
DamageType::Mental => {
|
||||
ew_effect.send(effects::SpawnEffectEvent {
|
||||
class: effects::Effects::FadeIn(Color::BLACK),
|
||||
duration: 4.0,
|
||||
});
|
||||
}
|
||||
DamageType::Asphyxiation => {
|
||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::WakeUp));
|
||||
ew_effect.send(effects::SpawnEffectEvent {
|
||||
class: effects::Effects::FadeIn(Color::BLACK),
|
||||
duration: 1.0,
|
||||
});
|
||||
}
|
||||
DamageType::Trauma | _ => {
|
||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::WakeUp));
|
||||
ew_effect.send(effects::SpawnEffectEvent {
|
||||
class: effects::Effects::FadeIn(Color::MAROON),
|
||||
duration: 1.0,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
commands::load_defs(ew_spawn);
|
||||
return;
|
||||
}
|
||||
|
@ -439,7 +480,7 @@ fn handle_damage(
|
|||
hp.current -= hp.damage;
|
||||
}
|
||||
if hp.current <= 0.0 {
|
||||
ew_playerdies.send(PlayerDiesEvent);
|
||||
ew_playerdies.send(PlayerDiesEvent(hp.damagetype));
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -464,6 +505,7 @@ fn handle_gforce(
|
|||
}
|
||||
if gforce.gforce > gforce.damage_threshold {
|
||||
hp.damage += (gforce.gforce - gforce.damage_threshold).powf(2.0) / 3000.0;
|
||||
hp.damagetype = DamageType::Trauma;
|
||||
}
|
||||
|
||||
if gforce.visual_effect > 0.0001 {
|
||||
|
|
|
@ -105,7 +105,7 @@ pub fn update_fov(
|
|||
settings.is_zooming = true;
|
||||
}
|
||||
} else {
|
||||
fov = (gforce.visual_effect * settings.fov_highspeed + settings.fov).to_radians();
|
||||
fov = (gforce.visual_effect.clamp(0.0, 1.0) * settings.fov_highspeed + settings.fov).to_radians();
|
||||
if settings.is_zooming {
|
||||
settings.is_zooming = false;
|
||||
}
|
||||
|
@ -117,10 +117,15 @@ pub fn update_fov(
|
|||
pub fn handle_input(
|
||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||
mut settings: ResMut<settings::Settings>,
|
||||
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
||||
) {
|
||||
if keyboard_input.just_pressed(settings.key_camera) {
|
||||
settings.third_person ^= true;
|
||||
}
|
||||
if keyboard_input.just_pressed(settings.key_rotation_stabilizer) {
|
||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Click));
|
||||
settings.rotation_stabilizer_active ^= true;
|
||||
}
|
||||
}
|
||||
|
||||
fn manage_player_actor(
|
||||
|
@ -157,7 +162,7 @@ fn manage_player_actor(
|
|||
pub fn apply_input_to_player(
|
||||
time: Res<Time>,
|
||||
settings: Res<settings::Settings>,
|
||||
mut windows: Query<&mut Window, With<PrimaryWindow>>,
|
||||
windows: Query<&Window, With<PrimaryWindow>>,
|
||||
mut mouse_events: EventReader<MouseMotion>,
|
||||
key_input: Res<ButtonInput<KeyCode>>,
|
||||
thruster_sound_controller: Query<&AudioSink, With<audio::ComponentThrusterSound>>,
|
||||
|
@ -171,16 +176,23 @@ pub fn apply_input_to_player(
|
|||
&mut AngularVelocity,
|
||||
&mut LinearVelocity,
|
||||
&mut ExternalTorque,
|
||||
Option<&actor::PlayerDrivesThis>,
|
||||
), (With<actor::PlayerCamera>, Without<Camera>)>,
|
||||
) {
|
||||
let dt = time.delta_seconds();
|
||||
let mut play_thruster_sound = false;
|
||||
let mut axis_input: DVec3 = DVec3::ZERO;
|
||||
|
||||
let window_result = windows.get_single_mut();
|
||||
let (win_res_x, win_res_y): (f32, f32);
|
||||
let mut focused = true;
|
||||
if window_result.is_ok() {
|
||||
focused = window_result.unwrap().focused;
|
||||
if let Ok(window) = &windows.get_single() {
|
||||
focused = window.focused;
|
||||
win_res_x = window.resolution.width();
|
||||
win_res_y = window.resolution.height();
|
||||
}
|
||||
else {
|
||||
win_res_x = 1920.0;
|
||||
win_res_y = 1050.0;
|
||||
}
|
||||
|
||||
let target_v: DVec3 = if let Ok(target) = q_target.get_single() {
|
||||
|
@ -189,7 +201,7 @@ pub fn apply_input_to_player(
|
|||
DVec3::splat(0.0)
|
||||
};
|
||||
|
||||
if let Ok((player_transform, mut engine, mut angularvelocity, mut v, mut torque)) = q_playercam.get_single_mut() {
|
||||
if let Ok((player_transform, mut engine, mut angularvelocity, mut v, mut torque, bike)) = q_playercam.get_single_mut() {
|
||||
// Handle key input
|
||||
if focused {
|
||||
if key_input.pressed(settings.key_forward) {
|
||||
|
@ -288,20 +300,24 @@ pub fn apply_input_to_player(
|
|||
}
|
||||
if mouse_delta != Vec2::ZERO {
|
||||
if key_input.pressed(settings.key_rotate) {
|
||||
pitch_yaw_rot[2] += mouse_delta.x;
|
||||
pitch_yaw_rot[2] += 1000.0 * mouse_delta.x / win_res_x;
|
||||
} else {
|
||||
pitch_yaw_rot[0] += mouse_delta.y;
|
||||
pitch_yaw_rot[1] -= mouse_delta.x;
|
||||
pitch_yaw_rot[0] += 1000.0 * mouse_delta.y / win_res_y;
|
||||
pitch_yaw_rot[1] -= 1000.0 * mouse_delta.x / win_res_x;
|
||||
}
|
||||
}
|
||||
|
||||
let angular_slowdown: f64 = (2.0 - engine.reaction_wheels.powf(0.01).clamp(1.001, 1.1)) as f64;
|
||||
let angular_slowdown: f64 = if settings.rotation_stabilizer_active {
|
||||
(2.0 - engine.reaction_wheels.powf(0.01).clamp(1.001, 1.1)) as f64
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
if pitch_yaw_rot.length_squared() > 1.0e-18 {
|
||||
play_reactionwheel_sound = true;
|
||||
pitch_yaw_rot *= settings.mouse_sensitivity * sensitivity_factor * engine.reaction_wheels;
|
||||
torque.apply_torque(DVec3::from(
|
||||
player_transform.rotation * Vec3::new(
|
||||
pitch_yaw_rot[0] * 2.0,
|
||||
pitch_yaw_rot[0],
|
||||
pitch_yaw_rot[1],
|
||||
pitch_yaw_rot[2])));
|
||||
angularvelocity.0 *= angular_slowdown.clamp(0.97, 1.0) as f64;
|
||||
|
@ -320,7 +336,7 @@ pub fn apply_input_to_player(
|
|||
let volume = sink.volume();
|
||||
let speed = sink.speed();
|
||||
let action = pitch_yaw_rot.length_squared().powf(0.2) * 0.0005;
|
||||
if play_reactionwheel_sound && !settings.mute_sfx {
|
||||
if play_reactionwheel_sound && !settings.mute_sfx && bike.is_some() {
|
||||
sink.set_volume((volume + action).clamp(0.0, 1.0));
|
||||
sink.set_speed((speed + action * 0.2).clamp(0.2, 0.5));
|
||||
sink.play()
|
||||
|
|
55
src/chat.rs
55
src/chat.rs
|
@ -1,5 +1,7 @@
|
|||
use bevy::prelude::*;
|
||||
use crate::{actor, audio, hud, settings, world};
|
||||
use bevy_xpbd_3d::prelude::*;
|
||||
use bevy::math::DVec3;
|
||||
use crate::{actor, audio, hud, settings, world, effects};
|
||||
|
||||
pub struct ChatPlugin;
|
||||
impl Plugin for ChatPlugin {
|
||||
|
@ -300,12 +302,15 @@ pub fn handle_conversations(
|
|||
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), With<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() {
|
||||
match script.name.as_str() {
|
||||
"refilloxygen" => if let Ok(mut amount) = script.param.parse::<f32>() {
|
||||
for (mut _actor, mut suit) in q_player.iter_mut() {
|
||||
for (_, mut suit, _) in q_player.iter_mut() {
|
||||
if script.param2.is_empty() {
|
||||
suit.oxygen = (suit.oxygen + amount).clamp(0.0, suit.oxygen_max);
|
||||
}
|
||||
|
@ -334,7 +339,49 @@ pub fn handle_chat_scripts(
|
|||
} else {
|
||||
error!("Invalid parameter for command `{}`: `{}`", script.name, script.param);
|
||||
}
|
||||
_ => {}
|
||||
"cryotrip" => {
|
||||
if script.param.is_empty() {
|
||||
error!("Chat script cryotrip needs a parameter");
|
||||
}
|
||||
else {
|
||||
if let Ok((mut pos, mut v)) = q_playercam.get_single_mut() {
|
||||
if script.param == "oscillation".to_string() {
|
||||
*pos = Position(DVec3::new(147e6, 165e6, 336e6));
|
||||
v.0 = DVec3::ZERO;
|
||||
}
|
||||
else if script.param == "metisprime".to_string() {
|
||||
*pos = Position(DVec3::new(27643e3, -47e3, -124434e3));
|
||||
v.0 = DVec3::ZERO;
|
||||
}
|
||||
else if script.param == "serenity".to_string() {
|
||||
*pos = Position(DVec3::new(-121095e3, 582e3, -190816e3));
|
||||
v.0 = DVec3::ZERO;
|
||||
}
|
||||
else {
|
||||
error!("Invalid destination for cryotrip chat script: '{}'", script.param);
|
||||
}
|
||||
}
|
||||
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,
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
let script_name = &script.name;
|
||||
error!("Error, undefined chat script {script_name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,12 +61,13 @@ struct ParserState {
|
|||
warmup_seconds: f32,
|
||||
engine_type: actor::EngineType,
|
||||
oxygen: f32,
|
||||
mass: f64,
|
||||
density: f64,
|
||||
collider: Collider,
|
||||
camdistance: f32,
|
||||
suit_integrity: f32,
|
||||
light_brightness: f32,
|
||||
light_color: Option<Color>,
|
||||
ar_model: Option<String>,
|
||||
|
||||
// Chat fields
|
||||
delay: f64,
|
||||
|
@ -79,6 +80,7 @@ struct ParserState {
|
|||
script: String,
|
||||
script_parameter: String,
|
||||
script_parameter2: String,
|
||||
sound: Option<String>,
|
||||
}
|
||||
impl Default for ParserState {
|
||||
fn default() -> Self {
|
||||
|
@ -115,12 +117,13 @@ impl Default for ParserState {
|
|||
warmup_seconds: default_engine.warmup_seconds,
|
||||
engine_type: default_engine.engine_type,
|
||||
oxygen: nature::OXY_D,
|
||||
mass: 1.0,
|
||||
density: 100.0,
|
||||
collider: Collider::sphere(1.0),
|
||||
camdistance: default_actor.camdistance,
|
||||
suit_integrity: 1.0,
|
||||
light_brightness: 0.0,
|
||||
light_color: None,
|
||||
ar_model: None,
|
||||
|
||||
delay: 0.0,
|
||||
text: "".to_string(),
|
||||
|
@ -132,6 +135,7 @@ impl Default for ParserState {
|
|||
script: "".to_string(),
|
||||
script_parameter: "".to_string(),
|
||||
script_parameter2: "".to_string(),
|
||||
sound: Some("chat".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -144,6 +148,10 @@ impl ParserState {
|
|||
self.level = default.level;
|
||||
self.text = default.text;
|
||||
self.is_choice = default.is_choice;
|
||||
self.script = default.script;
|
||||
self.script_parameter = default.script_parameter;
|
||||
self.script_parameter2 = default.script_parameter2;
|
||||
self.sound = default.sound;
|
||||
}
|
||||
fn as_chatbranch(&self) -> chat::ChatBranch {
|
||||
return chat::ChatBranch {
|
||||
|
@ -151,7 +159,7 @@ impl ParserState {
|
|||
name: self.name.clone().unwrap_or("".to_string()),
|
||||
label: self.label.clone(),
|
||||
delay: self.delay.clone(),
|
||||
sound: if self.is_choice { "".to_string() } else { "chat".to_string() },
|
||||
sound: if self.is_choice || self.sound.is_none() { "".to_string() } else { "chat".to_string() },
|
||||
level: self.level.clone(),
|
||||
reply: if self.is_choice { "".to_string() } else { self.text.clone() },
|
||||
choice: if self.is_choice { self.text.clone() } else { "".to_string() },
|
||||
|
@ -226,6 +234,8 @@ pub fn load_defs(
|
|||
}
|
||||
}
|
||||
["relativeto", id] => {
|
||||
// NOTE: call this command before "id", otherwise actors that
|
||||
// set their position relative to this actor will get the wrong offset
|
||||
match id2pos.get(&id.to_string()) {
|
||||
Some(pos) => {
|
||||
state.pos += *pos;
|
||||
|
@ -371,9 +381,9 @@ pub fn load_defs(
|
|||
continue;
|
||||
}
|
||||
}
|
||||
["mass", value] => {
|
||||
["density", value] => {
|
||||
if let Ok(value_float) = value.parse::<f64>() {
|
||||
state.mass = value_float;
|
||||
state.density = value_float;
|
||||
}
|
||||
else {
|
||||
error!("Can't parse float: {line}");
|
||||
|
@ -453,6 +463,9 @@ pub fn load_defs(
|
|||
continue;
|
||||
}
|
||||
}
|
||||
["armodel", asset_name] => {
|
||||
state.ar_model = Some(asset_name.to_string());
|
||||
}
|
||||
|
||||
// Parsing chats
|
||||
["chat", chat_name] => {
|
||||
|
@ -537,6 +550,11 @@ pub fn load_defs(
|
|||
debug!("Registering level: {}", level);
|
||||
state.level = level.to_string();
|
||||
}
|
||||
["script", scriptname] => {
|
||||
state.script = scriptname.to_string();
|
||||
state.script_parameter = "".to_string();
|
||||
state.script_parameter2 = "".to_string();
|
||||
}
|
||||
["script", scriptname, parameter] => {
|
||||
state.script = scriptname.to_string();
|
||||
state.script_parameter = parameter.to_string();
|
||||
|
@ -547,6 +565,9 @@ pub fn load_defs(
|
|||
state.script_parameter = parameter.to_string();
|
||||
state.script_parameter2 = parameter2.to_string();
|
||||
}
|
||||
["sound", "none"] => {
|
||||
state.sound = None;
|
||||
}
|
||||
_ => {
|
||||
error!("No match for [{}]", parts.join(","));
|
||||
}
|
||||
|
@ -571,6 +592,8 @@ fn spawn_entities(
|
|||
}
|
||||
}
|
||||
else if state.class == DefClass::Actor {
|
||||
let actor_entity;
|
||||
{
|
||||
let mut actor = commands.spawn_empty();
|
||||
actor.insert(actor::Actor {
|
||||
id: state.id.clone(),
|
||||
|
@ -614,11 +637,10 @@ fn spawn_entities(
|
|||
|
||||
// Physics Parameters
|
||||
if state.has_physics {
|
||||
let fix_scale: f64 = 1.0 / (state.model_scale as f64).powf(3.0);
|
||||
actor.insert(RigidBody::Dynamic);
|
||||
actor.insert(LinearVelocity(state.velocity));
|
||||
actor.insert(AngularVelocity(state.angular_momentum));
|
||||
actor.insert(ColliderDensity((state.mass * fix_scale) as f64));
|
||||
actor.insert(ColliderDensity(state.density));
|
||||
if state.collider_is_mesh {
|
||||
actor.insert(AsyncSceneCollider::new(Some(
|
||||
ComputedCollider::TriMesh
|
||||
|
@ -699,6 +721,25 @@ fn spawn_entities(
|
|||
..default()
|
||||
});
|
||||
}
|
||||
if let Some(_) = state.ar_model {
|
||||
actor.insert(hud::AugmentedRealityOverlayBroadcaster);
|
||||
}
|
||||
actor_entity = actor.id();
|
||||
}
|
||||
|
||||
if let Some(ar_asset_name) = &state.ar_model {
|
||||
commands.spawn((
|
||||
hud::AugmentedRealityOverlay {
|
||||
owner: actor_entity,
|
||||
},
|
||||
world::DespawnOnPlayerDeath,
|
||||
SceneBundle {
|
||||
scene: asset_server.load(world::asset_name_to_path(ar_asset_name)),
|
||||
visibility: Visibility::Hidden,
|
||||
..default()
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
222
src/defs.txt
222
src/defs.txt
|
@ -13,13 +13,12 @@ actor 0 593051 0 suit
|
|||
orbit 226000e3 0.66
|
||||
player yes
|
||||
id player
|
||||
mass 200.0
|
||||
scale 1
|
||||
scale 2
|
||||
oxygen 0.008
|
||||
health 0.3
|
||||
angularmomentum 0 0 0
|
||||
collider capsule 2 1
|
||||
thrust 1.2 1 1 300 1.5
|
||||
collider capsule 1 0.5
|
||||
thrust 1.2 1 1 400 1.5
|
||||
rotationy 0.65
|
||||
engine monopropellant
|
||||
|
||||
|
@ -28,11 +27,11 @@ actor 10 -30 20 MeteorAceGT
|
|||
relativeto player
|
||||
scale 5
|
||||
vehicle yes
|
||||
thrust 24.5 4.8 3.3 200000 3
|
||||
thrust 24.5 4.8 3.3 500000 3
|
||||
engine ion
|
||||
collider sphere 1.5
|
||||
collider sphere 1
|
||||
camdistance 50
|
||||
mass 3000
|
||||
density 200
|
||||
angularmomentum 0.1 0.1 0.3
|
||||
|
||||
actor 0 0 0 io
|
||||
|
@ -85,14 +84,12 @@ actor 0 0 0 moonlet
|
|||
id thebe
|
||||
orbit 221900e3 0.66
|
||||
scale 50e3
|
||||
mass 430e15
|
||||
angularmomentum 0 0.025 0
|
||||
|
||||
actor 3000 0 0 moonlet
|
||||
name Moonlet
|
||||
relativeto player
|
||||
scale 500
|
||||
mass 10000000
|
||||
angularmomentum 0 0.15 0
|
||||
|
||||
actor -200 -110 1000 satellite
|
||||
|
@ -105,13 +102,13 @@ actor -200 -110 1000 satellite
|
|||
collider capsule 7.5 1
|
||||
rotationy 0.5
|
||||
angularmomentum 0 0 0
|
||||
mass 10
|
||||
density 0.01
|
||||
|
||||
actor 1000 20 300 monolith
|
||||
name "Mysterious Monolith 1"
|
||||
relativeto player
|
||||
scale 2
|
||||
mass 1000
|
||||
density 300
|
||||
rotationx 0.5
|
||||
wants maxrotation 0.01
|
||||
angularmomentum 0.0 0.0 0.01
|
||||
|
@ -121,7 +118,7 @@ actor 10000 2000 -3500 monolith
|
|||
name "Mysterious Monolith 2"
|
||||
relativeto player
|
||||
scale 2
|
||||
mass 1000
|
||||
density 300
|
||||
rotationx 0.5
|
||||
wants maxrotation 0.01
|
||||
angularmomentum 0.0 0.0 0.01
|
||||
|
@ -131,7 +128,7 @@ actor -8000 -1000 -100 monolith
|
|||
name "Mysterious Monolith 3"
|
||||
relativeto player
|
||||
scale 2
|
||||
mass 1000
|
||||
density 300
|
||||
rotationx 0.5
|
||||
wants maxrotation 0.01
|
||||
angularmomentum 0.0 0.0 0.01
|
||||
|
@ -142,7 +139,6 @@ actor -3300 10 0 pizzeria
|
|||
relativeto player
|
||||
id pizzeria
|
||||
scale 40
|
||||
mass 1000000
|
||||
rotationy 0.30
|
||||
angularmomentum 0 0 0
|
||||
actor -120 0 20 MeteorAceGT
|
||||
|
@ -150,17 +146,17 @@ actor -3300 10 0 pizzeria
|
|||
relativeto pizzeria
|
||||
scale 5
|
||||
vehicle yes
|
||||
thrust 70 13.7 9.4 200000 20
|
||||
thrust 24.5 4.8 3.3 500000 3
|
||||
engine ion
|
||||
collider sphere 1.5
|
||||
collider sphere 1
|
||||
camdistance 50
|
||||
mass 3000
|
||||
density 200
|
||||
angularmomentum 0 0 0.2
|
||||
actor -100 63 -13 pizzasign
|
||||
name "Pizzeria Sign"
|
||||
relativeto pizzeria
|
||||
scale 20
|
||||
mass 200
|
||||
density 200
|
||||
rotationy 0.45
|
||||
angularmomentum 0 0 0
|
||||
actor -16 -10 0 lightorb
|
||||
|
@ -173,13 +169,29 @@ actor -3300 10 0 pizzeria
|
|||
relativeto pizzeria
|
||||
scale 0.5
|
||||
light FF8F4A 1000000
|
||||
actor -33 0 4 clippy
|
||||
name "Clippy™ Convenience Companion"
|
||||
relativeto pizzeria
|
||||
armodel clippy_ar
|
||||
angularmomentum 0 0 0
|
||||
wants maxrotation 0
|
||||
wants maxvelocity 0
|
||||
thrust 15 6 3 400 0.5
|
||||
rotationy -0.5
|
||||
scale 3
|
||||
chatid pizzaclippy
|
||||
chat pizzaclippy
|
||||
name "Clippy™ Convenience Companion"
|
||||
msg 4 INIT EXIT "At your service!"
|
||||
|
||||
actor -35 0 0 suit
|
||||
relativeto pizzeria
|
||||
name "Space Pizza™"
|
||||
chatid pizzeria
|
||||
armodel suit_ar_chefhat
|
||||
alive yes
|
||||
mass 200.0
|
||||
collider capsule 2 1
|
||||
scale 2
|
||||
collider capsule 1 0.5
|
||||
thrust 1.2 1 1 10 1.5
|
||||
wants maxrotation 0
|
||||
wants maxvelocity 0
|
||||
|
@ -193,7 +205,7 @@ actor -3300 10 0 pizzeria
|
|||
msg 50 order special "Would you like to order today's special?"
|
||||
choice 3 special whatsthespecial "What's the special?"
|
||||
msg 4 whatsthespecial pineapple "Suspicious Spacefunghi"
|
||||
msg 4 pineapple smoothie "With free pineapple imiation"
|
||||
msg 4 pineapple smoothie "With free pineapple imitation"
|
||||
msg 7 smoothie tube "Our pizza smoothies are freshly blended every day"
|
||||
choice 3 tube wtftube "Wait... pizza smoothie?"
|
||||
msg 6 wtftube anyway "Huh? Of course, smoothie! How else do you want to get that pizza down your spacesuit feeding tube?"
|
||||
|
@ -236,8 +248,8 @@ actor 60 -15 -40 suit
|
|||
name Icarus
|
||||
chatid hi_icarus
|
||||
alive yes
|
||||
mass 200.0
|
||||
collider capsule 2 1
|
||||
scale 2
|
||||
collider capsule 1 0.5
|
||||
angularmomentum 0.4 0.2 0.1
|
||||
rotationy 0.6
|
||||
rotationx 1
|
||||
|
@ -336,8 +348,8 @@ actor -300 0 40 suit
|
|||
name "Drifter"
|
||||
chatid drifter
|
||||
oxygen 0.08
|
||||
mass 200.0
|
||||
collider capsule 2 1
|
||||
scale 2
|
||||
collider capsule 1 0.5
|
||||
chat drifter
|
||||
name "Drifter"
|
||||
msg 2 INIT dead "Error: No response"
|
||||
|
@ -349,6 +361,166 @@ actor -300 0 40 suit
|
|||
script refilloxygen 1 drifter
|
||||
msg 0 outcold EXIT ""
|
||||
|
||||
actor 100 -18000 2000 "orb_busstop"
|
||||
relativeto player
|
||||
id "busstop"
|
||||
name "StarTrans Bus Stop: Serenity Station"
|
||||
scale 100
|
||||
wants maxrotation 0
|
||||
wants maxvelocity 0
|
||||
actor 120 864 150 clippy
|
||||
relativeto "busstop"
|
||||
id "busstopclippy"
|
||||
name "StarTrans Clippy™"
|
||||
armodel clippy_ar
|
||||
angularmomentum 0 0 0
|
||||
wants maxrotation 0
|
||||
wants maxvelocity 0
|
||||
thrust 15 6 3 400 0.5
|
||||
rotationy -0.5
|
||||
scale 3
|
||||
chatid "busstopclippy"
|
||||
chat "busstopclippy"
|
||||
name "StarTrans Clippy™"
|
||||
msg 2 INIT question "Welcome at StarTrans Cargo Services!"
|
||||
msg 2 question wait "Ready for a trip? Available bus stops: Oscillation Station, Metis Prime Station"
|
||||
msg 40 wait answer ""
|
||||
sound none
|
||||
choice 2 answer how1 "Bus stop?! How does this work?"
|
||||
msg 6 how1 how2 "StarTrans Cargo Services is the most convenient way to travel the vast distances of space."
|
||||
msg 6 how2 how3 "Just activate your suit's built-in cryostasis. A StarTrans carrier will pick you up and you will wake up at your destination in the blink of an eye."
|
||||
msg 40 how3 answer "Of course we will supply you with free oxygen and ensure your safety."
|
||||
choice 2 answer vehicle "Can I take a spacecraft with me?"
|
||||
msg 40 vehicle answer "Absolutely."
|
||||
choice 1 answer stopA1 "Take me to Oscillation Station, please."
|
||||
msg 5 stopA1 stopA2 "StarTrans wishes you a pleasant journey."
|
||||
script cryofadeout
|
||||
msg 0 stopA2 EXIT ""
|
||||
script cryotrip oscillation
|
||||
sound none
|
||||
choice 1 answer stopB1 "Take me to Metis Prime Station, please."
|
||||
msg 5 stopB1 stopB2 "StarTrans wishes you a pleasant journey."
|
||||
script cryofadeout
|
||||
msg 0 stopB2 EXIT ""
|
||||
script cryotrip metisprime
|
||||
choice 1 answer stopC1 "Take me to Serenity Station, please."
|
||||
msg 5 stopC1 stopC2 "StarTrans wishes you a pleasant journey."
|
||||
script cryofadeout
|
||||
msg 0 stopC2 EXIT ""
|
||||
script cryotrip serenity
|
||||
choice 1 answer oxy "Can you please fill up my oxygen tank without taking me anywhere?"
|
||||
msg 2 oxy EXIT "Acceptable."
|
||||
script refilloxygen 1000
|
||||
sound none
|
||||
choice 2 answer bye "No, thank you."
|
||||
msg 2 bye EXIT "Feel free to come back any time."
|
||||
msg 0 answer EXIT "Connection terminated."
|
||||
actor 40 10 40 "orb_busstop"
|
||||
name "Light Orb"
|
||||
relativeto busstopclippy
|
||||
light "47FF00" 1000000
|
||||
actor 30 60 -10 "orb_busstop"
|
||||
name "Light Orb"
|
||||
relativeto busstopclippy
|
||||
light "47FF00" 1000000
|
||||
actor -10 -60 20 "orb_busstop"
|
||||
name "Light Orb"
|
||||
relativeto busstopclippy
|
||||
light "47FF00" 1000000
|
||||
actor -40 20 30 "orb_busstop"
|
||||
name "Light Orb"
|
||||
relativeto busstopclippy
|
||||
light "47FF00" 1000000
|
||||
actor 8 2 0 suit
|
||||
relativeto "busstopclippy"
|
||||
name "Rudy"
|
||||
wants maxrotation 0.2
|
||||
wants maxvelocity 0
|
||||
thrust 1.2 1 1 400 1.5
|
||||
scale 2
|
||||
collider capsule 1 0.5
|
||||
chatid "busstop1clippynpc1"
|
||||
chat "busstop1clippynpc1"
|
||||
name "Rudy"
|
||||
msg 3 INIT cryo "Error: No response"
|
||||
lvl info
|
||||
msg 0 cryo EXIT "Lifeform in cryostasis detected."
|
||||
lvl info
|
||||
|
||||
actor 147002e3 165001e3 336e6 "orb_busstop"
|
||||
relativeto jupiter
|
||||
id "busstop2"
|
||||
name "StarTrans Bus Station 'Oscillation Station'"
|
||||
scale 100
|
||||
wants maxrotation 0
|
||||
wants maxvelocity 0
|
||||
actor 120 864 150 clippy
|
||||
relativeto "busstop2"
|
||||
id "busstopclippy2"
|
||||
name "StarTrans Clippy™"
|
||||
armodel clippy_ar
|
||||
angularmomentum 0 0 0
|
||||
wants maxrotation 0
|
||||
wants maxvelocity 0
|
||||
thrust 15 6 3 400 0.5
|
||||
rotationy -0.5
|
||||
scale 3
|
||||
chatid "busstopclippy"
|
||||
chat "busstopclippy"
|
||||
actor 40 10 40 "orb_busstop"
|
||||
name "Light Orb"
|
||||
relativeto busstopclippy2
|
||||
light "47FF00" 1000000
|
||||
actor 30 60 -10 "orb_busstop"
|
||||
name "Light Orb"
|
||||
relativeto busstopclippy2
|
||||
light "47FF00" 1000000
|
||||
actor -10 -60 20 "orb_busstop"
|
||||
name "Light Orb"
|
||||
relativeto busstopclippy2
|
||||
light "47FF00" 1000000
|
||||
actor -40 20 30 "orb_busstop"
|
||||
name "Light Orb"
|
||||
relativeto busstopclippy2
|
||||
light "47FF00" 1000000
|
||||
|
||||
actor 27643e3 -44e3 -124434e3 "orb_busstop"
|
||||
relativeto jupiter
|
||||
id "busstop3"
|
||||
name "StarTrans Bus Station 'Metis Prime'"
|
||||
scale 100
|
||||
wants maxrotation 0
|
||||
wants maxvelocity 0
|
||||
actor 120 864 150 clippy
|
||||
relativeto "busstop3"
|
||||
id "busstopclippy3"
|
||||
name "StarTrans Clippy™"
|
||||
armodel clippy_ar
|
||||
angularmomentum 0 0 0
|
||||
wants maxrotation 0
|
||||
wants maxvelocity 0
|
||||
thrust 15 6 3 400 0.5
|
||||
rotationy -0.5
|
||||
scale 3
|
||||
chatid "busstopclippy"
|
||||
chat "busstopclippy"
|
||||
actor 40 10 40 "orb_busstop"
|
||||
name "Light Orb"
|
||||
relativeto busstopclippy3
|
||||
light "47FF00" 1000000
|
||||
actor 30 60 -10 "orb_busstop"
|
||||
name "Light Orb"
|
||||
relativeto busstopclippy3
|
||||
light "47FF00" 1000000
|
||||
actor -10 -60 20 "orb_busstop"
|
||||
name "Light Orb"
|
||||
relativeto busstopclippy3
|
||||
light "47FF00" 1000000
|
||||
actor -40 20 30 "orb_busstop"
|
||||
name "Light Orb"
|
||||
relativeto busstopclippy3
|
||||
light "47FF00" 1000000
|
||||
|
||||
chat error
|
||||
name ERROR
|
||||
msg 0 INIT EXIT "Unspecified conversation ID"
|
||||
|
|
|
@ -8,7 +8,8 @@ impl Plugin for EffectsPlugin {
|
|||
app.add_systems(Startup, setup);
|
||||
app.add_systems(Startup, spawn_effects.after(setup).after(camera::setup_camera));
|
||||
app.add_systems(Update, spawn_effects);
|
||||
app.add_systems(Update, update_fadeblack);
|
||||
app.add_systems(Update, update_fadein);
|
||||
app.add_systems(Update, update_fadeout);
|
||||
// Blackout disabled for now
|
||||
//app.add_systems(Update, update_blackout);
|
||||
app.add_event::<SpawnEffectEvent>();
|
||||
|
@ -18,12 +19,14 @@ impl Plugin for EffectsPlugin {
|
|||
#[derive(Clone)]
|
||||
pub enum Effects {
|
||||
FadeIn(Color),
|
||||
FadeOut(Color),
|
||||
}
|
||||
|
||||
// Blackout disabled for now
|
||||
//#[derive(Component)] pub struct BlackOutOverlay;
|
||||
|
||||
#[derive(Component)] pub struct FadeBlack;
|
||||
#[derive(Component)] pub struct FadeIn;
|
||||
#[derive(Component)] pub struct FadeOut;
|
||||
#[derive(Component)]
|
||||
pub struct Effect {
|
||||
pub class: Effects,
|
||||
|
@ -76,7 +79,7 @@ pub fn spawn_effects(
|
|||
duration: effect.duration,
|
||||
start_time: now,
|
||||
},
|
||||
FadeBlack,
|
||||
FadeIn,
|
||||
NodeBundle {
|
||||
style: Style {
|
||||
width: Val::Vw(100.0),
|
||||
|
@ -91,14 +94,36 @@ pub fn spawn_effects(
|
|||
},
|
||||
));
|
||||
},
|
||||
Effects::FadeOut(color) => {
|
||||
commands.spawn((
|
||||
Effect {
|
||||
class: effect.class.clone(),
|
||||
duration: effect.duration,
|
||||
start_time: now,
|
||||
},
|
||||
FadeOut,
|
||||
NodeBundle {
|
||||
style: Style {
|
||||
width: Val::Vw(100.0),
|
||||
height: Val::Vh(100.0),
|
||||
position_type: PositionType::Absolute,
|
||||
top: Val::Px(0.0),
|
||||
left: Val::Px(0.0),
|
||||
..default()
|
||||
},
|
||||
background_color: color.with_a(0.0).into(),
|
||||
..default()
|
||||
},
|
||||
));
|
||||
},
|
||||
//_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_fadeblack(
|
||||
pub fn update_fadein(
|
||||
mut commands: Commands,
|
||||
mut q_effect: Query<(Entity, &Effect, &mut BackgroundColor), With<FadeBlack>>,
|
||||
mut q_effect: Query<(Entity, &Effect, &mut BackgroundColor), With<FadeIn>>,
|
||||
time: Res<Time>,
|
||||
) {
|
||||
for (entity, effect, mut bgcolor) in &mut q_effect {
|
||||
|
@ -112,6 +137,22 @@ pub fn update_fadeblack(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn update_fadeout(
|
||||
mut commands: Commands,
|
||||
mut q_effect: Query<(Entity, &Effect, &mut BackgroundColor), With<FadeOut>>,
|
||||
time: Res<Time>,
|
||||
) {
|
||||
for (entity, effect, mut bgcolor) in &mut q_effect {
|
||||
let now = time.elapsed_seconds_f64();
|
||||
if effect.start_time + effect.duration < now {
|
||||
commands.entity(entity).despawn();
|
||||
continue;
|
||||
}
|
||||
let alpha = ((now - effect.start_time) / effect.duration).clamp(0.0, 1.0);
|
||||
bgcolor.0.set_a(alpha as f32);
|
||||
}
|
||||
}
|
||||
|
||||
// Blackout disabled for now
|
||||
//pub fn update_blackout(
|
||||
// mut q_effect: Query<&mut BackgroundColor, With<BlackOutOverlay>>,
|
||||
|
|
63
src/hud.rs
63
src/hud.rs
|
@ -24,6 +24,7 @@ impl Plugin for HudPlugin {
|
|||
app.add_systems(Startup, setup);
|
||||
app.add_systems(Update, (
|
||||
update_hud,
|
||||
update_ar_overlays,
|
||||
handle_input,
|
||||
handle_target_event,
|
||||
));
|
||||
|
@ -33,6 +34,9 @@ impl Plugin for HudPlugin {
|
|||
.after(camera::apply_input_to_player)
|
||||
.before(TransformSystem::TransformPropagate),
|
||||
));
|
||||
app.insert_resource(AugmentedRealityState {
|
||||
overlays_visible: false,
|
||||
});
|
||||
app.insert_resource(Log {
|
||||
logs: VecDeque::with_capacity(LOG_MAX),
|
||||
needs_rerendering: true,
|
||||
|
@ -52,6 +56,17 @@ impl Plugin for HudPlugin {
|
|||
#[derive(Component)] struct Selectagon;
|
||||
#[derive(Component)] pub struct IsTargeted;
|
||||
|
||||
#[derive(Resource)]
|
||||
pub struct AugmentedRealityState {
|
||||
pub overlays_visible: bool,
|
||||
}
|
||||
|
||||
#[derive(Component)] pub struct AugmentedRealityOverlayBroadcaster;
|
||||
#[derive(Component)]
|
||||
pub struct AugmentedRealityOverlay {
|
||||
pub owner: Entity,
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
struct FPSUpdateTimer(Timer);
|
||||
|
||||
|
@ -465,6 +480,14 @@ fn update_hud(
|
|||
}
|
||||
}
|
||||
|
||||
let dev_speed = if settings.dev_mode {
|
||||
let x = pos.x;
|
||||
let y = pos.y;
|
||||
let z = pos.z;
|
||||
format!("\n{x:.0}\n{y:.0}\n{z:.0}")
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
let gforce = gforce.gforce;
|
||||
if let Ok((clickable, _, target_v_maybe)) = q_target.get_single() {
|
||||
let distance = if dist_scalar.is_nan() {
|
||||
|
@ -481,12 +504,12 @@ fn update_hud(
|
|||
};
|
||||
let speed_readable = nature::readable_distance(speed);
|
||||
let target_name = clickable.name.clone().unwrap_or("Unnamed".to_string());
|
||||
text.sections[14].value = format!("\n\nTarget: {target_name}\nDistance: {distance}\nΔv {speed_readable}/s + {gforce:.1}g");
|
||||
text.sections[14].value = format!("\n\nTarget: {target_name}\nDistance: {distance}\nΔv {speed_readable}/s + {gforce:.1}g{dev_speed}");
|
||||
}
|
||||
else {
|
||||
let speed = cam_v.length();
|
||||
let speed_readable = nature::readable_distance(speed);
|
||||
text.sections[14].value = format!("\nv {speed_readable}/s + {gforce:.1}g");
|
||||
text.sections[14].value = format!("\nv {speed_readable}/s + {gforce:.1}g{dev_speed}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -645,3 +668,39 @@ fn update_target_selectagon(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update_ar_overlays (
|
||||
q_owners: Query<(Entity, &Transform, &Visibility), (With<AugmentedRealityOverlayBroadcaster>, Without<AugmentedRealityOverlay>)>,
|
||||
mut q_overlays: Query<(&mut Transform, &mut Visibility, &mut AugmentedRealityOverlay)>,
|
||||
settings: ResMut<settings::Settings>,
|
||||
mut state: ResMut<AugmentedRealityState>,
|
||||
) {
|
||||
let (need_activate, need_clean, need_update);
|
||||
if settings.hud_active {
|
||||
need_activate = !state.overlays_visible;
|
||||
need_clean = false;
|
||||
}
|
||||
else {
|
||||
need_activate = false;
|
||||
need_clean = state.overlays_visible;
|
||||
}
|
||||
need_update = settings.hud_active;
|
||||
state.overlays_visible = settings.hud_active;
|
||||
|
||||
if need_update || need_clean || need_activate {
|
||||
for (mut trans, mut vis, ar) in &mut q_overlays {
|
||||
for (owner_id, owner_trans, owner_vis) in &q_owners {
|
||||
if owner_id == ar.owner {
|
||||
*trans = *owner_trans;
|
||||
if need_clean {
|
||||
*vis = Visibility::Hidden;
|
||||
}
|
||||
else {
|
||||
*vis = *owner_vis;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ fn setup(
|
|||
|
||||
fn handle_input(
|
||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||
mut settings: ResMut<settings::Settings>,
|
||||
settings: Res<settings::Settings>,
|
||||
mut app_exit_events: ResMut<Events<bevy::app::AppExit>>,
|
||||
mut windows: Query<&mut Window, With<PrimaryWindow>>,
|
||||
) {
|
||||
|
@ -93,7 +93,4 @@ fn handle_input(
|
|||
}
|
||||
}
|
||||
}
|
||||
if keyboard_input.just_pressed(settings.key_restart) {
|
||||
settings.reset();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ pub struct Settings {
|
|||
pub hud_active: bool,
|
||||
pub is_zooming: bool,
|
||||
pub third_person: bool,
|
||||
pub rotation_stabilizer_active: bool,
|
||||
pub key_selectobject: MouseButton,
|
||||
pub key_zoom: MouseButton,
|
||||
pub key_togglehud: KeyCode,
|
||||
|
@ -37,6 +38,7 @@ pub struct Settings {
|
|||
pub key_vehicle: KeyCode,
|
||||
pub key_camera: KeyCode,
|
||||
pub key_rotate: KeyCode,
|
||||
pub key_rotation_stabilizer: KeyCode,
|
||||
pub key_mouseup: KeyCode,
|
||||
pub key_mousedown: KeyCode,
|
||||
pub key_mouseleft: KeyCode,
|
||||
|
@ -101,11 +103,12 @@ impl Default for Settings {
|
|||
hud_active: false,
|
||||
is_zooming: false,
|
||||
third_person: false,
|
||||
rotation_stabilizer_active: true,
|
||||
key_selectobject: MouseButton::Left,
|
||||
key_zoom: MouseButton::Right,
|
||||
key_togglehud: KeyCode::Tab,
|
||||
key_exit: KeyCode::Escape,
|
||||
key_restart: KeyCode::F12,
|
||||
key_restart: KeyCode::F7,
|
||||
key_fullscreen: KeyCode::F11,
|
||||
key_forward: KeyCode::KeyW,
|
||||
key_back: KeyCode::KeyS,
|
||||
|
@ -119,6 +122,7 @@ impl Default for Settings {
|
|||
key_vehicle: KeyCode::KeyQ,
|
||||
key_camera: KeyCode::KeyF,
|
||||
key_rotate: KeyCode::KeyR,
|
||||
key_rotation_stabilizer: KeyCode::KeyY,
|
||||
key_mouseup: KeyCode::KeyI,
|
||||
key_mousedown: KeyCode::KeyK,
|
||||
key_mouseleft: KeyCode::KeyJ,
|
||||
|
@ -139,23 +143,32 @@ impl Default for Settings {
|
|||
key_cheat_stop: KeyCode::KeyC,
|
||||
key_cheat_speed: KeyCode::KeyV,
|
||||
key_cheat_speed_backward: KeyCode::KeyB,
|
||||
key_cheat_pizza: KeyCode::F8,
|
||||
key_cheat_farview1: KeyCode::F9,
|
||||
key_cheat_farview2: KeyCode::F10,
|
||||
key_cheat_pizza: KeyCode::F9,
|
||||
key_cheat_farview1: KeyCode::F10,
|
||||
key_cheat_farview2: KeyCode::F12,
|
||||
key_cheat_adrenaline_zero: KeyCode::F5,
|
||||
key_cheat_adrenaline_mid: KeyCode::F6,
|
||||
key_cheat_adrenaline_max: KeyCode::F7,
|
||||
key_cheat_adrenaline_max: KeyCode::F8,
|
||||
key_cheat_die: KeyCode::KeyZ,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
#[allow(dead_code)]
|
||||
pub fn reset(&mut self) {
|
||||
println!("Resetting settings!");
|
||||
*self = Self::default();
|
||||
}
|
||||
|
||||
pub fn reset_player_settings(&mut self) {
|
||||
println!("Resetting player settings!");
|
||||
let default = Self::default();
|
||||
self.rotation_stabilizer_active = default.rotation_stabilizer_active;
|
||||
self.third_person = default.third_person;
|
||||
self.is_zooming = default.is_zooming;
|
||||
}
|
||||
|
||||
pub fn get_reply_keys(&self) -> [KeyCode; 10] {
|
||||
return [
|
||||
self.key_reply1,
|
||||
|
|
|
@ -24,16 +24,21 @@ const ASSET_ASTEROID2: &str = "models/asteroid2.glb#Scene0";
|
|||
pub fn asset_name_to_path(name: &str) -> &'static str {
|
||||
match name {
|
||||
"suit" => "models/suit.glb#Scene0",
|
||||
"suit_ar_chefhat" => "models/suit_ar_chefhat.glb#Scene0",
|
||||
"asteroid1" => ASSET_ASTEROID1,
|
||||
"asteroid2" => ASSET_ASTEROID2,
|
||||
"moonlet" => "models/moonlet.glb#Scene0",
|
||||
"monolith" => "models/monolith_neon.glb#Scene0",
|
||||
"lightorb" => "models/lightorb.glb#Scene0",
|
||||
"orb_busstop" => "models/orb_busstop.glb#Scene0",
|
||||
"orb_busstop_dim" => "models/orb_busstop_dim.glb#Scene0",
|
||||
"MeteorAceGT" => "models/MeteorAceGT.glb#Scene0",
|
||||
"satellite" => "models/satellite.glb#Scene0",
|
||||
"pizzeria" => "models/pizzeria2.glb#Scene0",
|
||||
"pizzasign" => "models/pizzasign.glb#Scene0",
|
||||
"selectagon" => "models/selectagon.glb#Scene0",
|
||||
"clippy" => "models/clippy.glb#Scene0",
|
||||
"clippy_ar" => "models/clippy_ar.glb#Scene0",
|
||||
_ => "models/error.glb#Scene0",
|
||||
}
|
||||
}
|
||||
|
@ -441,7 +446,8 @@ fn handle_cheats(
|
|||
lifeform.adrenaline = 1.0;
|
||||
}
|
||||
if key_input.just_pressed(settings.key_cheat_die) {
|
||||
ew_playerdies.send(actor::PlayerDiesEvent);
|
||||
settings.god_mode = false;
|
||||
ew_playerdies.send(actor::PlayerDiesEvent(actor::DamageType::Trauma));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue