Compare commits
7 commits
ac44f4ee4f
...
7e7b8e7432
Author | SHA1 | Date | |
---|---|---|---|
yuni | 7e7b8e7432 | ||
yuni | 4f71f833fb | ||
yuni | b01823c641 | ||
yuni | dcb6e6e5a9 | ||
yuni | 904f7927eb | ||
yuni | 06a0bf3984 | ||
yuni | 5dcfbaf591 |
|
@ -1,3 +1,7 @@
|
|||
# v0.11.1
|
||||
|
||||
- Fixed radio stations
|
||||
|
||||
# v0.11.0
|
||||
|
||||
- Upgrade to Bevy 0.14.2, Rust 1.81
|
||||
|
|
BIN
assets/textures/exhaust.png
Normal file
BIN
assets/textures/exhaust.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
|
@ -46,6 +46,8 @@ We are not quite there yet, but this is what I'm aiming for:
|
|||
- No anxiety-causing features
|
||||
- No DRM
|
||||
- No paid features
|
||||
- All source code and assets are ["free" as defined by the FSF](https://www.gnu.org/licenses/license-list.html)
|
||||
- Obviously, space as a whole is not exactly wholesome. Space is a death trap. This should still be felt by the player, it's a space simulation after all.
|
||||
- Non-addictiveness
|
||||
- No features that manipulate the player into neglecting their life responsibilities in favor of the game
|
||||
- No artificially long game progression
|
||||
|
|
16
src/actor.rs
16
src/actor.rs
|
@ -599,8 +599,9 @@ fn handle_wants_maxvelocity(
|
|||
}
|
||||
|
||||
fn handle_wants_lookat(
|
||||
mut query: Query<(&Position, &mut Rotation, &Transform, &WantsToLookAt)>,
|
||||
mut query: Query<(&Position, &mut Rotation, &Transform, &WantsToLookAt), Without<Camera>>,
|
||||
q_playercam: Query<&Position, With<PlayerCamera>>,
|
||||
q_cam: Query<&Transform, With<Camera>>,
|
||||
id2pos: Res<game::Id2Pos>,
|
||||
) {
|
||||
let player_pos = if let Ok(player_pos) = q_playercam.get_single() {
|
||||
|
@ -608,17 +609,22 @@ fn handle_wants_lookat(
|
|||
} else {
|
||||
return;
|
||||
};
|
||||
let cam_pos = if let Ok(cam_trans) = q_cam.get_single() {
|
||||
cam_trans.translation.as_dvec3() + player_pos.0
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
// TODO: use ExternalTorque rather than hard-resetting the rotation
|
||||
for (pos, mut rot, trans, target_id) in &mut query {
|
||||
let target_pos = if target_id.0 == cmd::ID_SPECIAL_PLAYERCAM {
|
||||
player_pos
|
||||
let target_pos: DVec3 = if target_id.0 == cmd::ID_SPECIAL_PLAYERCAM {
|
||||
cam_pos
|
||||
} else if let Some(target_pos) = id2pos.0.get(&target_id.0) {
|
||||
target_pos
|
||||
*target_pos
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
rot.0 = look_at_quat(**pos, *target_pos, trans.up().as_dvec3());
|
||||
rot.0 = look_at_quat(**pos, target_pos, trans.up().as_dvec3());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -430,6 +430,7 @@ pub fn apply_input_to_player(
|
|||
(&Position, &LinearVelocity),
|
||||
(
|
||||
Without<hud::IsTargeted>,
|
||||
Without<visual::IsEffect>,
|
||||
Without<actor::PlayerCamera>,
|
||||
Without<actor::Player>,
|
||||
Without<actor::PlayerDrivesThis>,
|
||||
|
@ -447,6 +448,7 @@ pub fn apply_input_to_player(
|
|||
),
|
||||
(With<actor::PlayerCamera>, Without<Camera>),
|
||||
>,
|
||||
mut ew_effect: EventWriter<visual::SpawnEffectEvent>,
|
||||
) {
|
||||
if settings.map_active || !settings.in_control() {
|
||||
return;
|
||||
|
@ -564,6 +566,16 @@ pub fn apply_input_to_player(
|
|||
engine.current_warmup =
|
||||
(engine.current_warmup + dt / engine.warmup_seconds).clamp(0.0, 1.0);
|
||||
play_thruster_sound = true;
|
||||
|
||||
// Visual effect
|
||||
if acceleration_total.length_squared() > 1e-4 {
|
||||
let thruster_direction = acceleration_total.normalize();
|
||||
let thruster_v = v.0 - 10.0 * thruster_direction;
|
||||
ew_effect.send(visual::SpawnEffectEvent {
|
||||
duration: 2.0,
|
||||
class: visual::Effects::ThrusterParticle(pos.clone(), LinearVelocity::from(thruster_v)),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
engine.current_warmup =
|
||||
(engine.current_warmup - dt / engine.warmup_seconds).clamp(0.0, 1.0);
|
||||
|
|
|
@ -110,6 +110,10 @@ pub fn in_shadow(
|
|||
|
||||
pub fn look_at_quat(from: DVec3, to: DVec3, up: DVec3) -> DQuat {
|
||||
let direction = (to - from).normalize();
|
||||
let direction_len = direction.length_squared();
|
||||
if direction_len < 1e-4 || direction_len.is_nan() {
|
||||
return DQuat::IDENTITY;
|
||||
}
|
||||
let right = up.cross(direction).normalize();
|
||||
let corrected_up = direction.cross(right);
|
||||
|
||||
|
|
26
src/game.rs
26
src/game.rs
|
@ -12,7 +12,6 @@
|
|||
|
||||
use crate::prelude::*;
|
||||
use bevy::color::palettes::css;
|
||||
use bevy::pbr::ExtendedMaterial;
|
||||
use bevy::prelude::*;
|
||||
use bevy::scene::SceneInstance;
|
||||
use bevy::window::{PrimaryWindow, Window, WindowMode};
|
||||
|
@ -471,22 +470,23 @@ fn update_id2v(mut id2v: ResMut<Id2V>, q_id: Query<(&LinearVelocity, &actor::Ide
|
|||
fn debug(
|
||||
settings: Res<var::Settings>,
|
||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||
mut commands: Commands,
|
||||
mut extended_materials: ResMut<
|
||||
Assets<ExtendedMaterial<StandardMaterial, load::AsteroidSurface>>,
|
||||
>,
|
||||
// mut commands: Commands,
|
||||
// mut extended_materials: ResMut<
|
||||
// Assets<bevy::pbr::ExtendedMaterial<StandardMaterial, load::AsteroidSurface>>,
|
||||
// >,
|
||||
mut achievement_tracker: ResMut<var::AchievementTracker>,
|
||||
vars: Res<var::GameVars>,
|
||||
materials: Query<(Entity, Option<&Name>, &Handle<Mesh>)>,
|
||||
// materials: Query<(Entity, Option<&Name>, &Handle<Mesh>)>,
|
||||
) {
|
||||
if settings.dev_mode && keyboard_input.just_pressed(KeyCode::KeyP) {
|
||||
for (entity, _name, mesh) in &materials {
|
||||
dbg!(mesh);
|
||||
let mut entity = commands.entity(entity);
|
||||
entity.remove::<Handle<StandardMaterial>>();
|
||||
let material = extended_materials.add(load::AsteroidSurface::material());
|
||||
entity.insert(material);
|
||||
}
|
||||
|
||||
// for (entity, _name, mesh) in &materials {
|
||||
// dbg!(mesh);
|
||||
// let mut entity = commands.entity(entity);
|
||||
// entity.remove::<Handle<StandardMaterial>>();
|
||||
// let material = extended_materials.add(load::AsteroidSurface::material());
|
||||
// entity.insert(material);
|
||||
// }
|
||||
}
|
||||
if settings.dev_mode && keyboard_input.just_pressed(KeyCode::KeyN) {
|
||||
achievement_tracker.achieve_all();
|
||||
|
|
|
@ -475,7 +475,7 @@ pub fn update_menu(
|
|||
} else {
|
||||
&settings.radio_modes[0]
|
||||
};
|
||||
text.sections[i].value = format!("Radio: {station}\n");
|
||||
text.sections[i].value = format!("Speakers: {station}\n");
|
||||
}
|
||||
MenuAction::ToggleAR => {
|
||||
let onoff = bool2string(settings.hud_active);
|
||||
|
|
|
@ -177,6 +177,7 @@ impl Default for Settings {
|
|||
radio_modes: vec![
|
||||
// see also: settings.is_radio_playing()
|
||||
"Off".to_string(),
|
||||
"Space Wave Radio".to_string(),
|
||||
"Amplify outside recordings".to_string(),
|
||||
],
|
||||
volume_sfx: 100,
|
||||
|
|
121
src/visual.rs
121
src/visual.rs
|
@ -12,6 +12,7 @@
|
|||
|
||||
use crate::prelude::*;
|
||||
use bevy::prelude::*;
|
||||
use bevy_xpbd_3d::prelude::*;
|
||||
use std::time::Duration;
|
||||
|
||||
pub struct VisualPlugin;
|
||||
|
@ -26,6 +27,8 @@ impl Plugin for VisualPlugin {
|
|||
app.add_systems(Update, spawn_effects);
|
||||
app.add_systems(Update, update_fadein);
|
||||
app.add_systems(Update, update_fadeout);
|
||||
app.add_systems(Update, update_fade_material);
|
||||
app.add_systems(Update, update_grow);
|
||||
app.add_systems(Update, play_animations);
|
||||
// Blackout disabled for now
|
||||
//app.add_systems(Update, update_blackout);
|
||||
|
@ -37,15 +40,32 @@ impl Plugin for VisualPlugin {
|
|||
pub enum Effects {
|
||||
FadeIn(Color),
|
||||
FadeOut(Color),
|
||||
ThrusterParticle(Position, LinearVelocity),
|
||||
}
|
||||
|
||||
// Blackout disabled for now
|
||||
//#[derive(Component)] pub struct BlackOutOverlay;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct FadeIn;
|
||||
pub struct IsEffect;
|
||||
#[derive(Component)]
|
||||
pub struct FadeOut;
|
||||
pub struct FadeInSprite;
|
||||
#[derive(Component)]
|
||||
pub struct FadeOutSprite;
|
||||
#[derive(Component)]
|
||||
pub struct FadeMaterial {
|
||||
pub start_time: f64,
|
||||
pub duration: f64,
|
||||
pub value_start: f32,
|
||||
pub value_end: f32,
|
||||
}
|
||||
#[derive(Component)]
|
||||
pub struct Grow3DObject {
|
||||
pub start_time: f64,
|
||||
pub duration: f64,
|
||||
pub scale_start: f32,
|
||||
pub scale_end: f32,
|
||||
}
|
||||
#[derive(Component)]
|
||||
pub struct Effect {
|
||||
pub class: Effects,
|
||||
|
@ -109,6 +129,9 @@ pub fn setup(
|
|||
pub fn spawn_effects(
|
||||
mut commands: Commands,
|
||||
mut er_effect: EventReader<SpawnEffectEvent>,
|
||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
asset_server: Res<AssetServer>,
|
||||
time: Res<Time>,
|
||||
) {
|
||||
let now = time.elapsed_seconds_f64();
|
||||
|
@ -116,12 +139,13 @@ pub fn spawn_effects(
|
|||
match effect.class {
|
||||
Effects::FadeIn(color) => {
|
||||
commands.spawn((
|
||||
IsEffect,
|
||||
Effect {
|
||||
class: effect.class.clone(),
|
||||
duration: effect.duration,
|
||||
start_time: now,
|
||||
},
|
||||
FadeIn,
|
||||
FadeInSprite,
|
||||
NodeBundle {
|
||||
style: style_fullscreen(),
|
||||
background_color: color.into(),
|
||||
|
@ -131,12 +155,13 @@ pub fn spawn_effects(
|
|||
}
|
||||
Effects::FadeOut(color) => {
|
||||
commands.spawn((
|
||||
IsEffect,
|
||||
Effect {
|
||||
class: effect.class.clone(),
|
||||
duration: effect.duration,
|
||||
start_time: now,
|
||||
},
|
||||
FadeOut,
|
||||
FadeOutSprite,
|
||||
NodeBundle {
|
||||
style: style_fullscreen(),
|
||||
background_color: color.with_alpha(0.0).into(),
|
||||
|
@ -144,13 +169,49 @@ pub fn spawn_effects(
|
|||
},
|
||||
));
|
||||
}
|
||||
Effects::ThrusterParticle(pos, v) => {
|
||||
let texture = asset_server.load("textures/exhaust.png");
|
||||
commands.spawn((
|
||||
IsEffect,
|
||||
RigidBody::Kinematic,
|
||||
bevy::pbr::NotShadowCaster,
|
||||
pos,
|
||||
v,
|
||||
Grow3DObject {
|
||||
start_time: now,
|
||||
duration: effect.duration,
|
||||
scale_start: 1.0,
|
||||
scale_end: 20.0,
|
||||
},
|
||||
FadeMaterial {
|
||||
start_time: now,
|
||||
duration: effect.duration,
|
||||
value_start: 0.1,
|
||||
value_end: 0.0,
|
||||
},
|
||||
world::DespawnAt(now + effect.duration),
|
||||
world::DespawnOnPlayerDeath,
|
||||
actor::WantsToLookAt(cmd::ID_SPECIAL_PLAYERCAM.into()),
|
||||
PbrBundle {
|
||||
mesh: meshes.add(Mesh::from(Rectangle::new(0.2, 0.2))),
|
||||
material: materials.add(StandardMaterial {
|
||||
base_color_texture: Some(texture),
|
||||
perceptual_roughness: 1.0,
|
||||
metallic: 0.5,
|
||||
alpha_mode: AlphaMode::Blend,
|
||||
..Default::default()
|
||||
}),
|
||||
..default()
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_fadein(
|
||||
mut commands: Commands,
|
||||
mut q_effect: Query<(Entity, &Effect, &mut BackgroundColor), With<FadeIn>>,
|
||||
mut q_effect: Query<(Entity, &Effect, &mut BackgroundColor), With<FadeInSprite>>,
|
||||
time: Res<Time>,
|
||||
) {
|
||||
for (entity, effect, mut bgcolor) in &mut q_effect {
|
||||
|
@ -166,7 +227,7 @@ pub fn update_fadein(
|
|||
|
||||
pub fn update_fadeout(
|
||||
mut commands: Commands,
|
||||
mut q_effect: Query<(Entity, &Effect, &mut BackgroundColor), With<FadeOut>>,
|
||||
mut q_effect: Query<(Entity, &Effect, &mut BackgroundColor), With<FadeOutSprite>>,
|
||||
time: Res<Time>,
|
||||
) {
|
||||
for (entity, effect, mut bgcolor) in &mut q_effect {
|
||||
|
@ -180,6 +241,54 @@ pub fn update_fadeout(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn update_fade_material(
|
||||
mut commands: Commands,
|
||||
mut q_object: Query<(Entity, &Handle<StandardMaterial>, &FadeMaterial)>,
|
||||
time: Res<Time>,
|
||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||
) {
|
||||
let now = time.elapsed_seconds_f64();
|
||||
for (entity, material_handle, data) in q_object.iter_mut() {
|
||||
let end_time = data.start_time + data.duration;
|
||||
let material = materials.get_mut(material_handle).unwrap();
|
||||
if now > end_time {
|
||||
material.base_color.set_alpha(data.value_end);
|
||||
commands.entity(entity).remove::<FadeMaterial>();
|
||||
}
|
||||
else if now < data.start_time {
|
||||
material.base_color.set_alpha(data.value_start);
|
||||
}
|
||||
else {
|
||||
let progress = ((now - data.start_time) / data.duration) as f32;
|
||||
let value = data.value_start + progress * (data.value_end - data.value_start);
|
||||
material.base_color.set_alpha(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_grow(
|
||||
mut commands: Commands,
|
||||
mut q_object: Query<(Entity, &mut Transform, &Grow3DObject)>,
|
||||
time: Res<Time>,
|
||||
) {
|
||||
let now = time.elapsed_seconds_f64();
|
||||
for (entity, mut trans, data) in q_object.iter_mut() {
|
||||
let end_time = data.start_time + data.duration;
|
||||
if now > end_time {
|
||||
trans.scale = Vec3::splat(data.scale_end);
|
||||
commands.entity(entity).remove::<Grow3DObject>();
|
||||
}
|
||||
else if now < data.start_time {
|
||||
trans.scale = Vec3::splat(data.scale_start);
|
||||
}
|
||||
else {
|
||||
let progress = ((now - data.start_time) / data.duration) as f32;
|
||||
let scale = data.scale_start + progress * (data.scale_end - data.scale_start);
|
||||
trans.scale = Vec3::splat(scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn play_animations(
|
||||
mut commands: Commands,
|
||||
mut players: Query<(Entity, &mut AnimationPlayer), Added<AnimationPlayer>>,
|
||||
|
|
16
src/world.rs
16
src/world.rs
|
@ -38,6 +38,7 @@ impl Plugin for WorldPlugin {
|
|||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(Startup, setup);
|
||||
app.add_systems(Update, handle_respawn.run_if(on_event::<RespawnEvent>()));
|
||||
app.add_systems(Update, handle_despawn_at.run_if(any_with_component::<DespawnAt>));
|
||||
app.add_plugins(PhysicsPlugins::default());
|
||||
//app.add_plugins(PhysicsDebugPlugin::default());
|
||||
app.insert_resource(Gravity(DVec3::splat(0.0)));
|
||||
|
@ -65,6 +66,8 @@ struct Asteroid;
|
|||
pub struct Star;
|
||||
#[derive(Component)]
|
||||
pub struct DespawnOnPlayerDeath;
|
||||
#[derive(Component)]
|
||||
pub struct DespawnAt(pub f64);
|
||||
#[derive(Event)]
|
||||
pub struct RespawnEvent;
|
||||
|
||||
|
@ -360,6 +363,19 @@ fn handle_despawn_asteroids(
|
|||
}
|
||||
}
|
||||
|
||||
fn handle_despawn_at(
|
||||
mut commands: Commands,
|
||||
q_entity: Query<(Entity, &DespawnAt)>,
|
||||
time: Res<Time>,
|
||||
) {
|
||||
let now = time.elapsed_seconds_f64();
|
||||
for (entity, despawn_at) in &q_entity {
|
||||
if despawn_at.0 < now {
|
||||
commands.entity(entity).despawn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_respawn(
|
||||
ew_spawn: EventWriter<cmd::SpawnEvent>,
|
||||
mut achievement_tracker: ResMut<var::AchievementTracker>,
|
||||
|
|
Loading…
Reference in a new issue