Compare commits
No commits in common. "838b8b7fd6908e7198d667f1233bc7f851801662" and "6d12033e2342a4d04c18e4c1558cd0efaadf7d66" have entirely different histories.
838b8b7fd6
...
6d12033e23
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -3,4 +3,4 @@ assets/tmp
|
|||
assets/external
|
||||
*.blend1
|
||||
extra
|
||||
outfly_v*
|
||||
outfly_*.zip
|
||||
|
|
118
Cargo.lock
generated
118
Cargo.lock
generated
|
@ -72,6 +72,12 @@ dependencies = [
|
|||
"winit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.11"
|
||||
|
@ -1482,6 +1488,15 @@ dependencies = [
|
|||
"windows 0.54.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.12"
|
||||
|
@ -1585,19 +1600,6 @@ version = "1.10.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
|
||||
|
||||
[[package]]
|
||||
name = "embed-resource"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e62abb876c07e4754fae5c14cafa77937841f01740637e17d78dc04352f32a5e"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"rustc_version",
|
||||
"toml",
|
||||
"vswhom",
|
||||
"winreg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encase"
|
||||
version = "0.7.0"
|
||||
|
@ -1718,6 +1720,15 @@ version = "2.0.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984"
|
||||
|
||||
[[package]]
|
||||
name = "fdeflate"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645"
|
||||
dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "file-id"
|
||||
version = "0.2.1"
|
||||
|
@ -1745,6 +1756,16 @@ version = "0.4.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.5.0"
|
||||
|
@ -2065,6 +2086,7 @@ dependencies = [
|
|||
"color_quant",
|
||||
"jpeg-decoder",
|
||||
"num-traits",
|
||||
"png",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2371,6 +2393,16 @@ version = "0.2.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
|
||||
dependencies = [
|
||||
"adler",
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.11"
|
||||
|
@ -2737,12 +2769,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "outfly"
|
||||
version = "0.7.3"
|
||||
version = "0.7.1"
|
||||
dependencies = [
|
||||
"bevy",
|
||||
"bevy_embedded_assets",
|
||||
"bevy_xpbd_3d",
|
||||
"embed-resource",
|
||||
"fastrand",
|
||||
"regex",
|
||||
"serde",
|
||||
|
@ -2882,6 +2913,19 @@ version = "0.3.30"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.17.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"crc32fast",
|
||||
"fdeflate",
|
||||
"flate2",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "polling"
|
||||
version = "3.5.0"
|
||||
|
@ -3265,6 +3309,12 @@ dependencies = [
|
|||
"wide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.9"
|
||||
|
@ -3492,15 +3542,6 @@ version = "0.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.5"
|
||||
|
@ -3675,26 +3716,6 @@ version = "0.9.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "vswhom"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"vswhom-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vswhom-sys"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3b17ae1f6c8a2b28506cd96d412eebf83b4a0ff2cbefeeb952f2f9dfa44ba18"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.5.0"
|
||||
|
@ -4395,15 +4416,6 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "x11-dl"
|
||||
version = "2.21.0"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "outfly"
|
||||
version = "0.7.3"
|
||||
version = "0.7.1"
|
||||
edition = "2021"
|
||||
homepage = "https://codeberg.org/hut/outfly"
|
||||
repository = "https://codeberg.org/hut/outfly"
|
||||
|
@ -8,20 +8,16 @@ categories = ["game", "aerospace", "simulation"]
|
|||
keywords = ["game", "space", "3d"]
|
||||
license = "GPL-3.0-only"
|
||||
rust-version = "1.76.0"
|
||||
build = "build/build.rs"
|
||||
|
||||
[dependencies]
|
||||
regex = "1"
|
||||
bevy = { version = "0.13.2", default-features = false, features = ["jpeg", "bevy_asset", "bevy_audio", "bevy_scene", "bevy_winit", "bevy_core_pipeline", "bevy_pbr", "bevy_gltf", "bevy_render", "bevy_text", "bevy_ui", "multi-threaded", "tonemapping_luts", "vorbis"]}
|
||||
bevy = { version = "0.13.2", default-features = false, features = ["jpeg", "bevy_asset", "bevy_audio", "bevy_scene", "bevy_winit", "bevy_core_pipeline", "bevy_pbr", "bevy_gltf", "bevy_render", "bevy_text", "bevy_ui", "multi-threaded", "png", "tonemapping_luts", "vorbis"]}
|
||||
bevy_xpbd_3d = { version = "0.4.2", default-features = false, features = ["3d", "f64", "parry-f64", "parallel", "async-collider"] }
|
||||
bevy_embedded_assets = "0.10.2"
|
||||
fastrand = "2.0"
|
||||
serde = "1.0"
|
||||
serde_yaml = "0.9"
|
||||
|
||||
[build-dependencies]
|
||||
embed-resource = "1.6.3" # embedding of .exe metadata
|
||||
|
||||
[features]
|
||||
default = ["x11"]
|
||||
dev = ["bevy/dynamic_linking", "bevy/file_watcher"]
|
||||
|
|
10
README.md
10
README.md
|
@ -21,7 +21,6 @@ Links:
|
|||
|
||||
- [Source code](https://codeberg.org/hut/outfly)
|
||||
- [itch.io page](https://wholesomevacuum.itch.io/outfly)
|
||||
- [Game updates on Mastodon](https://mastodon.gamedev.place/@outfly)
|
||||
- [Chatroom](https://matrix.to/#/#outfly:pub.solar)
|
||||
|
||||
![screenshot](doc/images/screenshot2.jpg)
|
||||
|
@ -43,10 +42,9 @@ Links:
|
|||
- Tab: Toggle HUD/AR
|
||||
- F11: Toggle fullscreen
|
||||
- F: Toggle 3rd person view
|
||||
- M: Toggle map
|
||||
- Y: Toggle rotation stabilizer
|
||||
- F4: Toggle music
|
||||
- F3: Toggle sound effects
|
||||
- T: Toggle music
|
||||
- M: Toggle sound effects
|
||||
- Cheats
|
||||
- G: Toggle god mode / cheats
|
||||
- V/B: Impossible acceleration forward/backward
|
||||
|
@ -157,10 +155,6 @@ python -m http.server -d wasm
|
|||
|
||||
# Changelog
|
||||
|
||||
- v0.7.3: Implement map. You can now zoom out ALL THE WAY
|
||||
- v0.7.2:
|
||||
- Implement colliders based on object shape
|
||||
- Add "The Whale" vehicle around bus station Metis Prime
|
||||
- v0.7.1: Much nicer HUD
|
||||
- v0.7.0:
|
||||
- Overhaul conversation system, now defined in YAML files
|
||||
|
|
Binary file not shown.
|
@ -1,7 +0,0 @@
|
|||
fn main() {
|
||||
let target = std::env::var("TARGET").unwrap();
|
||||
if target.contains("windows") {
|
||||
println!("cargo:warning=Embedding Windows Icon");
|
||||
embed_resource::compile("build/windows/icon.rc");
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
#!/bin/sh
|
||||
# usage: run this script from the project root, like this: build/install.sh [rootdir]
|
||||
|
||||
rootdir="${1:-}"
|
||||
install -Dm755 "target/release/outfly" "$rootdir/usr/bin/outfly"
|
||||
install -Dm644 "build/linux/outfly.png" "$rootdir/usr/share/pixmaps/outfly.png"
|
||||
install -Dm644 "build/linux/outfly.desktop" "$rootdir/usr/share/applications/outfly.desktop"
|
|
@ -1,9 +0,0 @@
|
|||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=OutFly
|
||||
GenericName=Space Game
|
||||
Comment=A breathtaking 3D space game in the rings of Jupiter
|
||||
Icon=outfly
|
||||
Categories=Game;Simulation;
|
||||
Exec=outfly
|
||||
Terminal=false
|
|
@ -1 +0,0 @@
|
|||
app_icon ICON "outfly.ico"
|
Binary file not shown.
Before Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
@ -31,85 +31,55 @@ PATH = "extra/hygdata_v41.csv"
|
|||
# Meissa (orion's head, 8th brightest star in orion) = 3.33
|
||||
MAX_APPARENT_MAGNITUDE = 7.0
|
||||
|
||||
SOL_ABSMAG = 4.83
|
||||
SOL_RADIUS = 696.3e6
|
||||
SOL_LUMINOSITY = 3.828e26
|
||||
STEFAN_BOLTZMANN_CONSTANT = 5.670374419e-8
|
||||
|
||||
print("// This file was autogenerated by \"genrate_starchart.py\" using data from the")
|
||||
print("// HYG database: https://github.com/astronexus/HYG-Database/tree/main/hyg")
|
||||
print("// License: CC BY-SA 4.0: https://creativecommons.org/licenses/by-sa/4.0/")
|
||||
print("// Each star's values: (x, y, z, magnitude, absolute magnitude, color index, name)")
|
||||
print("// Each star's values: (x, y, z, magnitude, color index, distance, name)")
|
||||
print("[")
|
||||
|
||||
|
||||
def render(i, x, y, z, ra, dec, mag, absmag, ci, dist, name):
|
||||
def render(ra, dec, mag, ci, dist, name):
|
||||
# Takes ra/deg in degrees
|
||||
ra = float(ra)
|
||||
dec = float(dec)
|
||||
mag = float(mag)
|
||||
x, y, z = float(x), float(y), float(z)
|
||||
name = re.sub(r'\s+', ' ', name)
|
||||
if name == 'Sol':
|
||||
return
|
||||
|
||||
#radius = star_radius(float(ci), float(absmag))
|
||||
distance = 1.0
|
||||
ra_radians = math.radians(ra * 15) # ra is in [0, 24], multiplying by 15 gives degrees in [0, 360]
|
||||
dec_radians = math.radians(dec)
|
||||
#print(f"ra_radians={ra_radians}, dec_radians={dec_radians}, dec={dec}, ra={ra}", file=sys.stderr)
|
||||
x = distance * math.cos(dec_radians) * math.cos(-ra_radians)
|
||||
y = distance * math.cos(dec_radians) * math.sin(-ra_radians)
|
||||
z = distance * math.sin(dec_radians)
|
||||
|
||||
# distance = 1.0
|
||||
# ra_radians = math.radians(ra * 15) # ra is in [0, 24], multiplying by 15 gives degrees in [0, 360]
|
||||
# dec_radians = math.radians(dec)
|
||||
# #print(f"ra_radians={ra_radians}, dec_radians={dec_radians}, dec={dec}, ra={ra}", file=sys.stderr)
|
||||
# x = distance * math.cos(dec_radians) * math.cos(-ra_radians)
|
||||
# y = distance * math.cos(dec_radians) * math.sin(-ra_radians)
|
||||
# z = distance * math.sin(dec_radians)
|
||||
#
|
||||
# # Correct for differences in coordinate system axes
|
||||
# x, y, z = x, z, y
|
||||
# Correct for differences in coordinate system axes
|
||||
x, y, z = x, z, y
|
||||
|
||||
#brightness = 2.512 ** (0 - mag)
|
||||
|
||||
print(f'({x:.04},{y:.04},{z:.04},{mag:.04},{absmag:.04},{ci},"{name}"),')
|
||||
print(f'({x:.04},{y:.04},{z:.04},{mag:.04},{ci},{dist},"{name}"),')
|
||||
|
||||
|
||||
def clean_name(string):
|
||||
return "".join([c for c in string if c.isalnum() or c in ' -_'])
|
||||
|
||||
|
||||
def star_radius(color_index, absolute_magnitude):
|
||||
# Convert color index to temperature (using Ballesteros' formula approximation for B-V)
|
||||
temp = 4600 * ((1 / (0.92 * color_index + 1.7)) + (1 / (0.92 * color_index + 0.62)))
|
||||
|
||||
# Convert absolute magnitude to luminosity (in solar luminosities)
|
||||
lum = 10 ** (0.4 * (SOL_ABSMAG - absolute_magnitude))
|
||||
|
||||
# Calculate the radius using Stefan-Boltzmann law (output should be in meters)
|
||||
radius = math.sqrt(lum * SOL_LUMINOSITY / (4 * math.pi * STEFAN_BOLTZMANN_CONSTANT * temp ** 4)) # Luminosity in watts for the Sun ~ 3.828e26
|
||||
|
||||
# Convert radius from meters to solar radii (1 solar radius ≈ 6.96e8 meters)
|
||||
radius_solar = radius / SOL_RADIUS
|
||||
|
||||
return radius_solar
|
||||
|
||||
|
||||
total = 0
|
||||
count = 0
|
||||
count_extra = 0
|
||||
entries = []
|
||||
with open(PATH, "r", encoding="utf-8") as f:
|
||||
for index, entry in enumerate(csv.DictReader(f)):
|
||||
entries.append((index, entry))
|
||||
for entry in csv.DictReader(f):
|
||||
entries.append(entry)
|
||||
|
||||
entries.sort(key=lambda entry: float(entry[1]['mag']))
|
||||
entries.sort(key=lambda entry: float(entry['mag']))
|
||||
|
||||
for index, entry in entries:
|
||||
for entry in entries:
|
||||
total += 1
|
||||
ra = entry['ra']
|
||||
dec = entry['dec']
|
||||
x = entry['x']
|
||||
y = entry['y']
|
||||
z = entry['z']
|
||||
mag = entry['mag']
|
||||
absmag = entry['absmag']
|
||||
ci = entry['ci']
|
||||
dist = entry['dist']
|
||||
name = clean_name(entry['proper'])
|
||||
|
@ -122,7 +92,7 @@ for index, entry in entries:
|
|||
continue
|
||||
if float(mag) > MAX_APPARENT_MAGNITUDE:
|
||||
continue
|
||||
render(index, x, y, z, ra, dec, mag, absmag, ci, dist, name)
|
||||
render(ra, dec, mag, ci, dist, name)
|
||||
count += 1
|
||||
#for entry in CUSTOM_ENTRIES:
|
||||
# render(entry[0], entry[1], entry[2], entry[3], entry[4])
|
|
@ -21,10 +21,10 @@ SRCPATH="outfly_v$VERSION"
|
|||
mkdir "$SRCPATH"
|
||||
|
||||
cp ../README.md "$SRCPATH"
|
||||
cp ../target/x86_64-pc-windows-gnu/release/outfly.exe "$SRCPATH"/OutFly.exe
|
||||
cp ../target/x86_64-pc-windows-gnu/release/outfly.exe "$SRCPATH"
|
||||
zip -v -r -9 ../"outfly_v${VERSION}_windows.zip" "$SRCPATH"
|
||||
|
||||
rm "$SRCPATH"/OutFly.exe
|
||||
rm "$SRCPATH"/outfly.exe
|
||||
cp ../target/x86_64-unknown-linux-gnu/release/outfly "$SRCPATH"
|
||||
zip -v -r -9 ../"outfly_v${VERSION}_linux.zip" "$SRCPATH"
|
||||
|
21
src/actor.rs
21
src/actor.rs
|
@ -3,11 +3,10 @@ use bevy_xpbd_3d::prelude::*;
|
|||
use bevy::scene::SceneInstance;
|
||||
use bevy::math::DVec3;
|
||||
use crate::{actor, audio, camera, chat, commands, effects, hud, nature, var, world};
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub const ENGINE_SPEED_FACTOR: f32 = 30.0;
|
||||
const MAX_TRANSMISSION_DISTANCE: f32 = 100.0;
|
||||
const MAX_INTERACT_DISTANCE: f32 = 50.0;
|
||||
const MAX_INTERACT_DISTANCE: f32 = 40.0;
|
||||
|
||||
pub struct ActorPlugin;
|
||||
impl Plugin for ActorPlugin {
|
||||
|
@ -28,11 +27,9 @@ impl Plugin for ActorPlugin {
|
|||
));
|
||||
app.add_systems(PostUpdate, (
|
||||
handle_vehicle_enter_exit,
|
||||
update_id2pos,
|
||||
));
|
||||
app.add_event::<VehicleEnterExitEvent>();
|
||||
app.add_event::<PlayerDiesEvent>();
|
||||
app.insert_resource(Id2Pos(HashMap::new()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,13 +110,10 @@ impl Default for ExperiencesGForce { fn default() -> Self { Self {
|
|||
#[derive(Component)] pub struct Player; // Attached to the suit of the player
|
||||
#[derive(Component)] pub struct PlayerDrivesThis; // Attached to the entered vehicle
|
||||
#[derive(Component)] pub struct PlayerCamera; // Attached to the actor to use as point of view
|
||||
#[derive(Component)] pub struct JustNowEnteredVehicle;
|
||||
#[derive(Component)] pub struct ActorEnteringVehicle;
|
||||
#[derive(Component)] pub struct ActorVehicleBeingEntered;
|
||||
#[derive(Component)] pub struct WantsMaxRotation(pub f64);
|
||||
#[derive(Component)] pub struct WantsMaxVelocity(pub f64);
|
||||
#[derive(Component)] pub struct Identifier(pub String);
|
||||
#[derive(Resource)] pub struct Id2Pos(pub HashMap<String, DVec3>);
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct LifeForm {
|
||||
|
@ -332,7 +326,6 @@ pub fn handle_vehicle_enter_exit(
|
|||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::EnterVehicle));
|
||||
commands.entity(driver).remove::<PlayerCamera>();
|
||||
commands.entity(driver).remove::<Collider>();
|
||||
commands.entity(driver).insert(JustNowEnteredVehicle);
|
||||
commands.entity(vehicle).insert(PlayerCamera);
|
||||
commands.entity(vehicle).insert(PlayerDrivesThis);
|
||||
}
|
||||
|
@ -428,7 +421,6 @@ fn handle_player_death(
|
|||
q_noscenes: Query<Entity, (With<world::DespawnOnPlayerDeath>, Without<SceneInstance>)>,
|
||||
ew_spawn: EventWriter<commands::SpawnEvent>,
|
||||
mut scene_spawner: ResMut<SceneSpawner>,
|
||||
mut active_asteroids: ResMut<world::ActiveAsteroids>,
|
||||
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
||||
mut ew_effect: EventWriter<effects::SpawnEffectEvent>,
|
||||
mut log: ResMut<hud::Log>,
|
||||
|
@ -439,7 +431,6 @@ fn handle_player_death(
|
|||
return;
|
||||
}
|
||||
settings.reset_player_settings();
|
||||
active_asteroids.0.clear();
|
||||
for entity in &q_noscenes {
|
||||
cmd.entity(entity).despawn();
|
||||
}
|
||||
|
@ -528,13 +519,3 @@ fn handle_gforce(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update_id2pos(
|
||||
mut id2pos: ResMut<Id2Pos>,
|
||||
q_id: Query<(&Position, &Identifier)>,
|
||||
) {
|
||||
id2pos.0.clear();
|
||||
for (pos, id) in &q_id {
|
||||
id2pos.0.insert(id.0.clone(), pos.0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -137,12 +137,12 @@ pub fn toggle_bgm(
|
|||
mut evwriter_sfx: EventWriter<PlaySfxEvent>,
|
||||
mut settings: ResMut<var::Settings>,
|
||||
) {
|
||||
if keyboard_input.just_pressed(settings.key_toggle_music) {
|
||||
if keyboard_input.just_pressed(KeyCode::KeyT) {
|
||||
settings.mute_music ^= true;
|
||||
evwriter_sfx.send(PlaySfxEvent(Sfx::Click));
|
||||
evwriter_toggle.send(ToggleMusicEvent());
|
||||
}
|
||||
if keyboard_input.just_pressed(settings.key_toggle_sfx) {
|
||||
if keyboard_input.just_pressed(KeyCode::KeyM) {
|
||||
settings.mute_sfx ^= true;
|
||||
evwriter_sfx.send(PlaySfxEvent(Sfx::Click));
|
||||
evwriter_toggle.send(ToggleMusicEvent());
|
||||
|
|
196
src/camera.rs
196
src/camera.rs
|
@ -1,5 +1,5 @@
|
|||
use bevy::prelude::*;
|
||||
use bevy::input::mouse::{MouseMotion, MouseWheel};
|
||||
use bevy::input::mouse::MouseMotion;
|
||||
use bevy::window::PrimaryWindow;
|
||||
use bevy::core_pipeline::bloom::{BloomCompositeMode, BloomSettings};
|
||||
use bevy::core_pipeline::tonemapping::Tonemapping;
|
||||
|
@ -8,7 +8,6 @@ use bevy::transform::TransformSystem;
|
|||
use bevy::math::{DVec3, DQuat};
|
||||
use bevy_xpbd_3d::prelude::*;
|
||||
use std::f32::consts::PI;
|
||||
use std::f64::consts::PI as PI64;
|
||||
use crate::{actor, audio, hud, var};
|
||||
|
||||
pub struct CameraPlugin;
|
||||
|
@ -17,50 +16,15 @@ impl Plugin for CameraPlugin {
|
|||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(Startup, setup_camera);
|
||||
app.add_systems(Update, handle_input);
|
||||
app.add_systems(Update, update_map_only_object_visibility);
|
||||
app.add_systems(Update, manage_player_actor.after(handle_input));
|
||||
app.add_systems(PostUpdate, sync_camera_to_player
|
||||
.after(PhysicsSet::Sync)
|
||||
.after(apply_input_to_player)
|
||||
.before(TransformSystem::TransformPropagate));
|
||||
app.add_systems(Update, update_map_camera);
|
||||
app.add_systems(Update, update_fov);
|
||||
app.add_systems(PostUpdate, apply_input_to_player
|
||||
.after(PhysicsSet::Sync)
|
||||
.before(TransformSystem::TransformPropagate));
|
||||
app.insert_resource(MapCam::default());
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct ShowOnlyInMap {
|
||||
pub min_distance: f64,
|
||||
pub distance_to_id: String,
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
pub struct MapCam {
|
||||
pub initialized: bool,
|
||||
pub zoom_level: f64,
|
||||
pub target_zoom_level: f64,
|
||||
pub pitch: f64,
|
||||
pub yaw: f64,
|
||||
pub offset_x: f64,
|
||||
pub offset_z: f64,
|
||||
pub center: DVec3,
|
||||
}
|
||||
impl Default for MapCam {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
initialized: false,
|
||||
zoom_level: 2.0,
|
||||
target_zoom_level: 10.0,
|
||||
pitch: PI64 * 0.3,
|
||||
yaw: 0.0,
|
||||
offset_x: 0.0,
|
||||
offset_z: 0.0,
|
||||
center: DVec3::new(0.0, 0.0, 0.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -108,7 +72,7 @@ pub fn sync_camera_to_player(
|
|||
mut q_camera: Query<&mut Transform, (With<Camera>, Without<actor::PlayerCamera>)>,
|
||||
q_playercam: Query<(&actor::Actor, &Transform), (With<actor::PlayerCamera>, Without<Camera>)>,
|
||||
) {
|
||||
if settings.map_active || q_camera.is_empty() || q_playercam.is_empty() {
|
||||
if q_camera.is_empty() || q_playercam.is_empty() {
|
||||
return;
|
||||
}
|
||||
let mut camera_transform = q_camera.get_single_mut().unwrap();
|
||||
|
@ -126,105 +90,6 @@ pub fn sync_camera_to_player(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn update_map_camera(
|
||||
settings: Res<var::Settings>,
|
||||
mut mapcam: ResMut<MapCam>,
|
||||
mut q_camera: Query<&mut Transform, (With<Camera>, Without<actor::PlayerCamera>)>,
|
||||
q_playercam: Query<&Transform, (With<actor::PlayerCamera>, Without<Camera>)>,
|
||||
q_target: Query<&Transform, (With<hud::IsTargeted>, Without<Camera>, Without<actor::PlayerCamera>)>,
|
||||
q_target_changed: Query<(), Changed<hud::IsTargeted>>,
|
||||
mut mouse_events: EventReader<MouseMotion>,
|
||||
mut er_mousewheel: EventReader<MouseWheel>,
|
||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||
) {
|
||||
if !settings.map_active || q_camera.is_empty() || q_playercam.is_empty() {
|
||||
return;
|
||||
}
|
||||
let mut camera_transform = q_camera.get_single_mut().unwrap();
|
||||
let player_transform = q_playercam.get_single().unwrap();
|
||||
let target = if let Ok(target) = q_target.get_single() {
|
||||
target
|
||||
} else {
|
||||
player_transform
|
||||
};
|
||||
|
||||
// Get mouse movement
|
||||
let mut mouse_delta = Vec2::ZERO;
|
||||
for mouse_event in mouse_events.read() {
|
||||
mouse_delta += mouse_event.delta;
|
||||
}
|
||||
// NOTE: we need to subtract a bit from PI/2, otherwise the "up"
|
||||
// direction parameter for the Transform.look_at function is ambiguous
|
||||
// at the extreme values and the orientation will flicker back/forth.
|
||||
let epsilon = 0.001;
|
||||
let min_zoom: f64 = target.scale.x as f64 * 2.0;
|
||||
let max_zoom: f64 = 17e18; // at this point, camera starts glitching
|
||||
mapcam.pitch = (mapcam.pitch + mouse_delta.y as f64 / 180.0 * settings.mouse_sensitivity as f64).clamp(-PI64 / 2.0 + epsilon, PI64 / 2.0 - epsilon);
|
||||
mapcam.yaw += mouse_delta.x as f64 / 180.0 * settings.mouse_sensitivity as f64;
|
||||
|
||||
// Reset movement offset if target changes
|
||||
if !q_target_changed.is_empty() {
|
||||
mapcam.offset_x = 0.0;
|
||||
mapcam.offset_z = 0.0;
|
||||
}
|
||||
|
||||
// Get keyboard movement
|
||||
let mut offset_x: f64 = 0.0;
|
||||
let mut offset_z: f64 = 0.0;
|
||||
if keyboard_input.pressed(settings.key_forward) {
|
||||
offset_x -= 1.0;
|
||||
}
|
||||
if keyboard_input.pressed(settings.key_back) {
|
||||
offset_x += 1.0;
|
||||
}
|
||||
if keyboard_input.pressed(settings.key_right) {
|
||||
offset_z -= 1.0;
|
||||
}
|
||||
if keyboard_input.pressed(settings.key_left) {
|
||||
offset_z += 1.0;
|
||||
}
|
||||
|
||||
// Update zoom level
|
||||
if !mapcam.initialized {
|
||||
let factor: f64 = if target == player_transform { 7.0 } else { 1.0 };
|
||||
mapcam.target_zoom_level *= target.scale.x as f64 * factor;
|
||||
mapcam.zoom_level *= target.scale.x as f64 * factor;
|
||||
mapcam.initialized = true;
|
||||
}
|
||||
let mut change_zoom: f64 = 0.0;
|
||||
if keyboard_input.pressed(settings.key_map_zoom_out) {
|
||||
change_zoom += 0.5;
|
||||
}
|
||||
if keyboard_input.pressed(settings.key_map_zoom_in) {
|
||||
change_zoom -= 0.5;
|
||||
}
|
||||
for wheel_event in er_mousewheel.read() {
|
||||
change_zoom -= wheel_event.y as f64 * 3.0;
|
||||
}
|
||||
mapcam.target_zoom_level = (mapcam.target_zoom_level * 1.1f64.powf(change_zoom)).clamp(min_zoom, max_zoom);
|
||||
let zoom_speed = 0.05; // should be between 0.0001 (slow) and 1.0 (instant)
|
||||
mapcam.zoom_level = (zoom_speed * mapcam.target_zoom_level + (1.0 - zoom_speed) * mapcam.zoom_level).clamp(min_zoom, max_zoom);
|
||||
|
||||
// Update point of view
|
||||
let pov_rotation = DQuat::from_euler(EulerRot::XYZ, 0.0, mapcam.yaw as f64, mapcam.pitch as f64);
|
||||
let offset = DVec3::new(mapcam.offset_x, 0.0, mapcam.offset_z);
|
||||
let point_of_view = offset + pov_rotation * (mapcam.zoom_level as f64 * DVec3::new(1.0, 0.0, 0.0));
|
||||
|
||||
// Update movement offset
|
||||
let mut direction = pov_rotation * DVec3::new(offset_x, 0.0, offset_z);
|
||||
let speed = direction.length();
|
||||
direction.y = 0.0;
|
||||
let direction = speed * direction.normalize_or_zero();
|
||||
|
||||
mapcam.offset_x += 0.01 * (direction.x * mapcam.zoom_level);
|
||||
mapcam.offset_z += 0.01 * (direction.z * mapcam.zoom_level);
|
||||
|
||||
// Apply updates to camera
|
||||
mapcam.center = target.translation.as_dvec3() + offset;
|
||||
camera_transform.translation = target.translation + point_of_view.as_vec3();
|
||||
camera_transform.look_at(mapcam.center.as_vec3(), Vec3::Y);
|
||||
}
|
||||
|
||||
pub fn update_fov(
|
||||
q_player: Query<&actor::ExperiencesGForce, With<actor::Player>>,
|
||||
mouse_input: Res<ButtonInput<MouseButton>>,
|
||||
|
@ -252,16 +117,11 @@ pub fn update_fov(
|
|||
pub fn handle_input(
|
||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||
mut settings: ResMut<var::Settings>,
|
||||
mut mapcam: ResMut<MapCam>,
|
||||
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_map) {
|
||||
settings.map_active ^= true;
|
||||
*mapcam = MapCam::default();
|
||||
}
|
||||
if keyboard_input.just_pressed(settings.key_rotation_stabilizer) {
|
||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Click));
|
||||
settings.rotation_stabilizer_active ^= true;
|
||||
|
@ -269,21 +129,20 @@ pub fn handle_input(
|
|||
}
|
||||
|
||||
fn manage_player_actor(
|
||||
mut commands: Commands,
|
||||
settings: Res<var::Settings>,
|
||||
mut q_playercam: Query<&mut Visibility, With<actor::PlayerCamera>>,
|
||||
mut q_hiddenplayer: Query<(Entity, &mut Visibility, &mut Position, &mut Rotation, &mut LinearVelocity, &mut AngularVelocity, Option<&mut actor::ExperiencesGForce>, Option<&actor::JustNowEnteredVehicle>), (With<actor::Player>, Without<actor::PlayerCamera>)>,
|
||||
mut q_hiddenplayer: Query<(&mut Visibility, &mut Position, &mut Rotation, &mut LinearVelocity, &mut AngularVelocity), (With<actor::Player>, Without<actor::PlayerCamera>)>,
|
||||
q_ride: Query<(&Transform, &Position, &Rotation, &LinearVelocity, &AngularVelocity), (With<actor::PlayerDrivesThis>, Without<actor::Player>)>,
|
||||
) {
|
||||
for mut vis in &mut q_playercam {
|
||||
if settings.third_person || settings.map_active {
|
||||
if settings.third_person {
|
||||
*vis = Visibility::Inherited;
|
||||
}
|
||||
else {
|
||||
*vis = Visibility::Hidden;
|
||||
}
|
||||
}
|
||||
for (entity, mut vis, mut pos, mut rot, mut v, mut angv, mut gforce, entering) in &mut q_hiddenplayer {
|
||||
for (mut vis, mut pos, mut rot, mut v, mut angv) in &mut q_hiddenplayer {
|
||||
// If we are riding a vehicle, place the player at the position where
|
||||
// it would be after exiting the vehicle.
|
||||
// I would rather place it in the center of the vehicle, but at the time
|
||||
|
@ -295,13 +154,6 @@ fn manage_player_actor(
|
|||
rot.0 = ride_rot.0 * DQuat::from_array([-1.0, 0.0, 0.0, 0.0]);
|
||||
*v = ride_v.clone();
|
||||
*angv = ride_angv.clone();
|
||||
|
||||
// I really don't want people to die from the g-forces of entering
|
||||
// vehicles at high relative speed, even though they probably should.
|
||||
if let (Some(gforce), Some(_)) = (&mut gforce, entering) {
|
||||
gforce.last_linear_velocity = v.0;
|
||||
commands.entity(entity).remove::<actor::JustNowEnteredVehicle>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -327,9 +179,6 @@ pub fn apply_input_to_player(
|
|||
Option<&actor::PlayerDrivesThis>,
|
||||
), (With<actor::PlayerCamera>, Without<Camera>)>,
|
||||
) {
|
||||
if settings.map_active {
|
||||
return;
|
||||
}
|
||||
let dt = time.delta_seconds();
|
||||
let mut play_thruster_sound = false;
|
||||
let mut axis_input: DVec3 = DVec3::ZERO;
|
||||
|
@ -540,39 +389,6 @@ pub fn apply_input_to_player(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn update_map_only_object_visibility(
|
||||
settings: Res<var::Settings>,
|
||||
q_camera: Query<&Transform, With<Camera>>,
|
||||
q_player: Query<&Position, With<actor::PlayerCamera>>,
|
||||
mut q_onlyinmap: Query<(&mut Visibility, &ShowOnlyInMap), Without<Camera>>,
|
||||
id2pos: Res<actor::Id2Pos>,
|
||||
) {
|
||||
if q_camera.is_empty() || q_player.is_empty() {
|
||||
return;
|
||||
}
|
||||
let cam: &Transform = q_camera.get_single().unwrap();
|
||||
let player_pos: &Position = q_player.get_single().unwrap();
|
||||
let cam_pos: Vec3 = cam.translation + player_pos.as_vec3();
|
||||
for (mut vis, onlyinmap) in &mut q_onlyinmap {
|
||||
if settings.map_active && settings.hud_active {
|
||||
if let Some(pos) = id2pos.0.get(&onlyinmap.distance_to_id) {
|
||||
let dist = cam_pos.distance(pos.as_vec3());
|
||||
if dist >= onlyinmap.min_distance as f32 {
|
||||
*vis = Visibility::Inherited;
|
||||
}
|
||||
else {
|
||||
*vis = Visibility::Hidden;
|
||||
}
|
||||
} else {
|
||||
error!("Failed get position of actor ID '{}'", &onlyinmap.distance_to_id);
|
||||
*vis = Visibility::Hidden;
|
||||
}
|
||||
} else {
|
||||
*vis = Visibility::Hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find the closest world object that the player is looking at
|
||||
#[inline]
|
||||
pub fn find_closest_target<TargetSpecifier>(
|
||||
|
@ -590,10 +406,10 @@ pub fn find_closest_target<TargetSpecifier>(
|
|||
// not on the player mesh but on the camera, which doesn't have a position.
|
||||
let (angular_diameter, angle, distance) = calc_angular_diameter_known_target_vector(
|
||||
trans, camera_transform, &target_vector);
|
||||
let distance_to_surface = distance - trans.scale.x;
|
||||
if angle <= angular_diameter.clamp(0.001, PI) {
|
||||
// It's in the field of view!
|
||||
//commands.entity(entity).insert(IsTargeted);
|
||||
let distance_to_surface = distance - trans.scale.x;
|
||||
if distance_to_surface < closest_distance {
|
||||
closest_distance = distance_to_surface;
|
||||
closest_entity = Some(entity);
|
||||
|
|
28
src/chat.rs
28
src/chat.rs
|
@ -778,7 +778,6 @@ pub fn handle_chat_scripts(
|
|||
mut q_playercam: Query<(&mut Position, &mut LinearVelocity), With<actor::PlayerCamera>>,
|
||||
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
||||
mut ew_effect: EventWriter<effects::SpawnEffectEvent>,
|
||||
id2pos: Res<actor::Id2Pos>,
|
||||
) {
|
||||
for script in er_chatscript.read() {
|
||||
// Parse the script string
|
||||
|
@ -835,20 +834,19 @@ pub fn handle_chat_scripts(
|
|||
}
|
||||
else {
|
||||
if let Ok((mut pos, mut v)) = q_playercam.get_single_mut() {
|
||||
let busstop = match param1 {
|
||||
"serenity" => Some("busstop"),
|
||||
"oscillation" => Some("busstop2"),
|
||||
"metisprime" => Some("busstop3"),
|
||||
_ => None
|
||||
};
|
||||
if let Some(station) = busstop {
|
||||
if let Some(target) = id2pos.0.get(&station.to_string()) {
|
||||
pos.0 = *target + DVec3::new(0.0, -1000.0, 0.0);
|
||||
v.0 = DVec3::ZERO;
|
||||
} else {
|
||||
error!("Could not determine position of actor with ID: '{}'", station);
|
||||
}
|
||||
} else {
|
||||
if param1 == "oscillation".to_string() {
|
||||
*pos = Position(DVec3::new(-184968e3, 149410e3, -134273e3));
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
138
src/commands.rs
138
src/commands.rs
|
@ -3,10 +3,11 @@ extern crate regex;
|
|||
use bevy::prelude::*;
|
||||
use bevy_xpbd_3d::prelude::*;
|
||||
use bevy::math::DVec3;
|
||||
use crate::{actor, camera, chat, hud, nature, shading, world};
|
||||
use crate::{actor, chat, hud, nature, world};
|
||||
use regex::Regex;
|
||||
use std::f32::consts::PI;
|
||||
use std::f64::consts::PI as PI64;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub struct CommandsPlugin;
|
||||
impl Plugin for CommandsPlugin {
|
||||
|
@ -38,13 +39,12 @@ struct ParserState {
|
|||
// Actor fields
|
||||
id: String,
|
||||
pos: DVec3,
|
||||
relative_to: Option<String>,
|
||||
model: Option<String>,
|
||||
model: String,
|
||||
model_scale: f32,
|
||||
rotation: Quat,
|
||||
velocity: DVec3,
|
||||
angular_momentum: DVec3,
|
||||
pronoun: Option<String>,
|
||||
pronoun: String,
|
||||
is_sphere: bool,
|
||||
is_player: bool,
|
||||
is_lifeform: bool,
|
||||
|
@ -53,9 +53,7 @@ struct ParserState {
|
|||
is_vehicle: bool,
|
||||
is_clickable: bool,
|
||||
is_targeted_on_startup: bool,
|
||||
is_sun: bool,
|
||||
has_physics: bool,
|
||||
has_ring: bool,
|
||||
wants_maxrotation: Option<f64>,
|
||||
wants_maxvelocity: Option<f64>,
|
||||
collider_is_mesh: bool,
|
||||
|
@ -74,7 +72,6 @@ struct ParserState {
|
|||
light_brightness: f32,
|
||||
light_color: Option<Color>,
|
||||
ar_model: Option<String>,
|
||||
show_only_in_map_at_distance: Option<(f64, String)>,
|
||||
}
|
||||
impl Default for ParserState {
|
||||
fn default() -> Self {
|
||||
|
@ -87,13 +84,12 @@ impl Default for ParserState {
|
|||
|
||||
id: "".to_string(),
|
||||
pos: DVec3::new(0.0, 0.0, 0.0),
|
||||
relative_to: None,
|
||||
model: None,
|
||||
model: "".to_string(),
|
||||
model_scale: 1.0,
|
||||
rotation: Quat::IDENTITY,
|
||||
velocity: DVec3::splat(0.0),
|
||||
angular_momentum: DVec3::new(0.03, 0.3, 0.09),
|
||||
pronoun: None,
|
||||
pronoun: "they/them".to_string(),
|
||||
is_sphere: false,
|
||||
is_player: false,
|
||||
is_lifeform: false,
|
||||
|
@ -102,9 +98,7 @@ impl Default for ParserState {
|
|||
is_vehicle: false,
|
||||
is_clickable: true,
|
||||
is_targeted_on_startup: false,
|
||||
is_sun: false,
|
||||
has_physics: true,
|
||||
has_ring: false,
|
||||
wants_maxrotation: None,
|
||||
wants_maxvelocity: None,
|
||||
collider_is_mesh: false,
|
||||
|
@ -123,7 +117,6 @@ impl Default for ParserState {
|
|||
light_brightness: 0.0,
|
||||
light_color: None,
|
||||
ar_model: None,
|
||||
show_only_in_map_at_distance: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -138,6 +131,7 @@ pub fn load_defs(
|
|||
let mut state = ParserState::default();
|
||||
let mut command;
|
||||
let mut parameters;
|
||||
let mut id2pos: HashMap<String, DVec3> = HashMap::new();
|
||||
|
||||
let mut line_nr = -1;
|
||||
while let Some(line) = lines.next() {
|
||||
|
@ -183,21 +177,7 @@ pub fn load_defs(
|
|||
ew_spawn.send(SpawnEvent(state));
|
||||
state = ParserState::default();
|
||||
state.class = DefClass::Actor;
|
||||
state.model = Some(model.to_string());
|
||||
if let (Ok(x_float), Ok(y_float), Ok(z_float)) =
|
||||
(x.parse::<f64>(), y.parse::<f64>(), z.parse::<f64>()) {
|
||||
state.pos = DVec3::new(x_float, y_float, z_float);
|
||||
}
|
||||
else {
|
||||
error!("Can't parse coordinates as floats in def: {line}");
|
||||
state = ParserState::default();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
["actor", x, y, z] => {
|
||||
ew_spawn.send(SpawnEvent(state));
|
||||
state = ParserState::default();
|
||||
state.class = DefClass::Actor;
|
||||
state.model = model.to_string();
|
||||
if let (Ok(x_float), Ok(y_float), Ok(z_float)) =
|
||||
(x.parse::<f64>(), y.parse::<f64>(), z.parse::<f64>()) {
|
||||
state.pos = DVec3::new(x_float, y_float, z_float);
|
||||
|
@ -209,7 +189,18 @@ pub fn load_defs(
|
|||
}
|
||||
}
|
||||
["relativeto", id] => {
|
||||
state.relative_to = Some(id.to_string());
|
||||
// NOTE: call this command before "id", otherwise actors that
|
||||
// set their position relative to this actor will get the wrong offset
|
||||
// TODO: fix the above
|
||||
match id2pos.get(&id.to_string()) {
|
||||
Some(pos) => {
|
||||
state.pos += *pos;
|
||||
}
|
||||
None => {
|
||||
error!("Specified `relativeto` command but could not find id `{id}`");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
["orbit", radius_str, phase_str] => {
|
||||
if let (Ok(r), Ok(phase)) = (radius_str.parse::<f64>(), phase_str.parse::<f64>()) {
|
||||
|
@ -230,6 +221,7 @@ pub fn load_defs(
|
|||
}
|
||||
["id", id] => {
|
||||
state.id = id.to_string();
|
||||
id2pos.insert(state.id.clone(), state.pos.clone());
|
||||
}
|
||||
["alive", "yes"] => {
|
||||
state.is_alive = true;
|
||||
|
@ -245,13 +237,6 @@ pub fn load_defs(
|
|||
["moon", "yes"] => {
|
||||
state.model_scale *= 3.0;
|
||||
}
|
||||
["sun", "yes"] => {
|
||||
state.is_sun = true;
|
||||
state.model_scale *= 5.0;
|
||||
}
|
||||
["ring", "yes"] => {
|
||||
state.has_ring = true;
|
||||
}
|
||||
["oxygen", amount] => {
|
||||
if let Ok(amount) = amount.parse::<f32>() {
|
||||
state.is_lifeform = true;
|
||||
|
@ -264,7 +249,7 @@ pub fn load_defs(
|
|||
}
|
||||
}
|
||||
["pronoun", pronoun] => {
|
||||
state.pronoun = Some(pronoun.to_string());
|
||||
state.pronoun = pronoun.to_string();
|
||||
}
|
||||
["chatid", chat] => {
|
||||
state.chat = chat.to_string();
|
||||
|
@ -443,15 +428,6 @@ pub fn load_defs(
|
|||
["targeted", "yes"] => {
|
||||
state.is_targeted_on_startup = true;
|
||||
}
|
||||
["only_in_map_at_dist", value, id] => {
|
||||
if let Ok(value_float) = value.parse::<f64>() {
|
||||
state.show_only_in_map_at_distance = Some((value_float, id.to_string()));
|
||||
}
|
||||
else {
|
||||
error!("Can't parse float: {line}");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
error!("No match for [{}]", parts.join(","));
|
||||
}
|
||||
|
@ -466,26 +442,10 @@ fn spawn_entities(
|
|||
asset_server: Res<AssetServer>,
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||
mut materials_jupiter: ResMut<Assets<shading::JupitersRing>>,
|
||||
mut id2pos: ResMut<actor::Id2Pos>,
|
||||
) {
|
||||
for state_wrapper in er_spawn.read() {
|
||||
let state = &state_wrapper.0;
|
||||
if state.class == DefClass::Actor {
|
||||
let relative_pos = if let Some(id) = &state.relative_to {
|
||||
match id2pos.0.get(&id.to_string()) {
|
||||
Some(pos) => {
|
||||
state.pos + *pos
|
||||
}
|
||||
None => {
|
||||
error!("Specified `relativeto` command but could not find id `{id}`");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
state.pos
|
||||
};
|
||||
|
||||
let actor_entity;
|
||||
{
|
||||
let mut actor = commands.spawn_empty();
|
||||
|
@ -498,17 +458,13 @@ fn spawn_entities(
|
|||
actor.insert(SleepingDisabled);
|
||||
actor.insert(world::DespawnOnPlayerDeath);
|
||||
actor.insert(actor::HitPoints::default());
|
||||
actor.insert(Position::from(relative_pos));
|
||||
actor.insert(Position::from(state.pos));
|
||||
actor.insert(Rotation::from(state.rotation));
|
||||
if state.is_sphere {
|
||||
let sphere_texture_handle = if let Some(model) = &state.model {
|
||||
Some(asset_server.load(format!("textures/{}.jpg", model)))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let sphere_texture_handle: Handle<Image> = asset_server.load(format!("textures/{}.jpg", state.model));
|
||||
let sphere_handle = meshes.add(Sphere::new(1.0).mesh().uv(128, 128));
|
||||
let sphere_material_handle = materials.add(StandardMaterial {
|
||||
base_color_texture: sphere_texture_handle,
|
||||
base_color_texture: Some(sphere_texture_handle.clone()),
|
||||
perceptual_roughness: 1.0,
|
||||
metallic: 0.0,
|
||||
..default()
|
||||
|
@ -522,13 +478,13 @@ fn spawn_entities(
|
|||
},
|
||||
..default()
|
||||
});
|
||||
} else if let Some(model) = &state.model {
|
||||
} else {
|
||||
actor.insert(SceneBundle {
|
||||
transform: Transform {
|
||||
scale: Vec3::splat(state.model_scale),
|
||||
..default()
|
||||
},
|
||||
scene: asset_server.load(world::asset_name_to_path(model.as_str())),
|
||||
scene: asset_server.load(world::asset_name_to_path(state.model.as_str())),
|
||||
..default()
|
||||
});
|
||||
}
|
||||
|
@ -568,23 +524,9 @@ fn spawn_entities(
|
|||
actor.insert(actor::Player);
|
||||
actor.insert(actor::PlayerCamera);
|
||||
}
|
||||
if state.is_sun {
|
||||
let (r, g, b) = nature::star_color_index_to_rgb(0.656);
|
||||
actor.insert(materials.add(StandardMaterial {
|
||||
base_color: Color::rgb(r, g, b) * 13.0,
|
||||
unlit: true,
|
||||
..default()
|
||||
}));
|
||||
}
|
||||
if state.is_targeted_on_startup {
|
||||
actor.insert(hud::IsTargeted);
|
||||
}
|
||||
if let Some((mindist, id)) = &state.show_only_in_map_at_distance {
|
||||
actor.insert(camera::ShowOnlyInMap {
|
||||
min_distance: *mindist,
|
||||
distance_to_id: id.clone()
|
||||
});
|
||||
}
|
||||
if state.is_player || state.is_vehicle {
|
||||
// used to apply mouse movement to actor rotation
|
||||
actor.insert(ExternalTorque::ZERO.with_persistence(false));
|
||||
|
@ -602,7 +544,6 @@ fn spawn_entities(
|
|||
if state.is_clickable {
|
||||
actor.insert(hud::IsClickable {
|
||||
name: state.name.clone(),
|
||||
pronoun: state.pronoun.clone(),
|
||||
..default()
|
||||
});
|
||||
}
|
||||
|
@ -624,16 +565,12 @@ fn spawn_entities(
|
|||
..default()
|
||||
});
|
||||
}
|
||||
if !state.id.is_empty() {
|
||||
actor.insert(actor::Identifier(state.id.clone()));
|
||||
id2pos.0.insert(state.id.clone(), relative_pos);
|
||||
}
|
||||
if !state.chat.is_empty() {
|
||||
actor.insert(chat::Talker {
|
||||
actor_id: state.id.clone(),
|
||||
chat_name: state.chat.clone(),
|
||||
name: state.name.clone(),
|
||||
pronoun: state.pronoun.clone(),
|
||||
pronoun: Some(state.pronoun.clone()),
|
||||
talking_speed: 1.0,
|
||||
});
|
||||
}
|
||||
|
@ -675,25 +612,6 @@ fn spawn_entities(
|
|||
},
|
||||
));
|
||||
}
|
||||
|
||||
if state.has_ring {
|
||||
commands.spawn((
|
||||
world::DespawnOnPlayerDeath,
|
||||
MaterialMeshBundle {
|
||||
mesh: meshes.add(Mesh::from(Cylinder::new(nature::JUPITER_RING_RADIUS as f32, 1.0))),
|
||||
material: materials_jupiter.add(shading::JupitersRing {
|
||||
alpha_mode: AlphaMode::Blend,
|
||||
ring_radius: nature::JUPITER_RING_RADIUS as f32,
|
||||
jupiter_radius: nature::JUPITER_RADIUS as f32,
|
||||
}),
|
||||
transform: Transform::from_translation(relative_pos.as_vec3()),
|
||||
..default()
|
||||
},
|
||||
Position::new(relative_pos),
|
||||
Rotation::from(Quat::IDENTITY),
|
||||
//Rotation::from(Quat::from_rotation_x(-0.3f32.to_radians())),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,11 +3,10 @@ C: Impossibly instant stopping [CHEAT]
|
|||
Shift+V/B: Same as V/B, but a thousand times faster [CHEAT]
|
||||
V/B: Impossible acceleration forward/backward [CHEAT]
|
||||
G: Toggle god mode / cheats [CHEAT]
|
||||
M: Toggle map
|
||||
M: Toggle sound effects
|
||||
T: Toggle music
|
||||
Y: Toggle rotation stabilizer
|
||||
F: Toggle 3rd person view
|
||||
F3: Toggle sound effects
|
||||
F4: Toggle music
|
||||
F11: Toggle fullscreen
|
||||
Tab: Toggle HUD + Augmented Reality
|
||||
Right click: Zoom [AUGMENTED REALITY ONLY]
|
||||
|
|
31089
src/data/stars.in
31089
src/data/stars.in
File diff suppressed because it is too large
Load diff
159
src/defs.txt
159
src/defs.txt
|
@ -1,128 +1,16 @@
|
|||
actor 0 0 0
|
||||
id sol
|
||||
name Sol
|
||||
scale 696300e3
|
||||
sphere yes
|
||||
sun yes
|
||||
physics off
|
||||
actor 0 0 0 orbitring
|
||||
scale 57.91e9
|
||||
rotationz 0.0353
|
||||
only_in_map_at_dist 1e10 jupiter
|
||||
clickable no
|
||||
physics off
|
||||
actor 0 0 0 orbitring
|
||||
scale 108.21e9
|
||||
rotationz 0.0119
|
||||
only_in_map_at_dist 1e10 jupiter
|
||||
clickable no
|
||||
physics off
|
||||
actor 0 0 0 orbitring
|
||||
scale 149.598023e9
|
||||
rotationz 0.0088
|
||||
only_in_map_at_dist 1e10 jupiter
|
||||
clickable no
|
||||
physics off
|
||||
actor 0 0 0 orbitring
|
||||
scale 227.939366e9
|
||||
rotationz 0.0091
|
||||
only_in_map_at_dist 1e10 jupiter
|
||||
clickable no
|
||||
physics off
|
||||
actor 0 0 0 orbitring
|
||||
scale 778.479e9
|
||||
only_in_map_at_dist 1e10 jupiter
|
||||
clickable no
|
||||
physics off
|
||||
actor 0 0 0 orbitring
|
||||
scale 1433.53e9
|
||||
rotationz 0.0052
|
||||
only_in_map_at_dist 1e10 jupiter
|
||||
clickable no
|
||||
physics off
|
||||
actor 0 0 0 orbitring
|
||||
scale 2870.972e9
|
||||
rotationz 0.0055
|
||||
only_in_map_at_dist 1e10 jupiter
|
||||
clickable no
|
||||
physics off
|
||||
actor 0 0 0 orbitring
|
||||
scale 4500e9
|
||||
rotationz 0.0041
|
||||
only_in_map_at_dist 1e10 jupiter
|
||||
clickable no
|
||||
physics off
|
||||
actor 0 0 0 orbitring
|
||||
scale 5906.38e9
|
||||
rotationz 0.0953
|
||||
only_in_map_at_dist 1e10 jupiter
|
||||
clickable no
|
||||
physics off
|
||||
|
||||
actor 0 0 0 jupiter
|
||||
relativeto sol
|
||||
orbit 778479000e3 0.5
|
||||
id jupiter
|
||||
name Jupiter
|
||||
scale 71492e3
|
||||
sphere yes
|
||||
ring yes
|
||||
physics off
|
||||
rotationx -0.50
|
||||
rotationz -0.28
|
||||
angularmomentum 30 30 30
|
||||
actor 0 0 0 orbitring
|
||||
relativeto jupiter
|
||||
scale 128000e3
|
||||
only_in_map_at_dist 1e7 metis
|
||||
clickable no
|
||||
physics off
|
||||
actor 0 0 0 orbitring
|
||||
relativeto jupiter
|
||||
scale 129000e3
|
||||
only_in_map_at_dist 1e7 adrastea
|
||||
clickable no
|
||||
physics off
|
||||
actor 0 0 0 orbitring
|
||||
relativeto jupiter
|
||||
scale 181365.84e3
|
||||
only_in_map_at_dist 1e7 amalthea
|
||||
clickable no
|
||||
physics off
|
||||
actor 0 0 0 orbitring
|
||||
relativeto jupiter
|
||||
scale 221900e3
|
||||
only_in_map_at_dist 1e7 thebe
|
||||
clickable no
|
||||
physics off
|
||||
actor 0 0 0 orbitring
|
||||
relativeto jupiter
|
||||
scale 421700e3
|
||||
only_in_map_at_dist 1e8 io
|
||||
clickable no
|
||||
physics off
|
||||
actor 0 0 0 orbitring
|
||||
relativeto jupiter
|
||||
scale 670900e3
|
||||
only_in_map_at_dist 1e8 europa
|
||||
clickable no
|
||||
physics off
|
||||
actor 0 0 0 orbitring
|
||||
relativeto jupiter
|
||||
scale 1070400e3
|
||||
only_in_map_at_dist 1e8 ganymede
|
||||
clickable no
|
||||
physics off
|
||||
actor 0 0 0 orbitring
|
||||
relativeto jupiter
|
||||
scale 1882700e3
|
||||
only_in_map_at_dist 1e8 callisto
|
||||
clickable no
|
||||
physics off
|
||||
|
||||
actor 0 593051 0 suit
|
||||
relativeto jupiter
|
||||
orbit 224000e3 0.66
|
||||
orbit 226000e3 0.66
|
||||
player yes
|
||||
id player
|
||||
scale 2
|
||||
|
@ -149,12 +37,10 @@ actor 10 -30 20 MeteorAceGT
|
|||
|
||||
actor 0 0 0 io
|
||||
name Io
|
||||
id io
|
||||
relativeto jupiter
|
||||
orbit 421700e3 0.65
|
||||
scale 1822e3
|
||||
rotationy -0.40
|
||||
rotationx -0.50
|
||||
angularmomentum 0 0.0001 0
|
||||
sphere yes
|
||||
moon yes
|
||||
|
@ -162,12 +48,10 @@ actor 0 0 0 io
|
|||
|
||||
actor 0 0 0 europa
|
||||
name Europa
|
||||
id europa
|
||||
relativeto jupiter
|
||||
orbit 670900e3 0.35
|
||||
scale 1561e3
|
||||
rotationy 0.20
|
||||
rotationx -0.50
|
||||
angularmomentum 0 0.0001 0
|
||||
sphere yes
|
||||
moon yes
|
||||
|
@ -175,12 +59,10 @@ actor 0 0 0 europa
|
|||
|
||||
actor 0 0 0 ganymede
|
||||
name Ganymede
|
||||
id ganymede
|
||||
relativeto jupiter
|
||||
orbit 1070400e3 0.93
|
||||
scale 2634e3
|
||||
rotationy -0.40
|
||||
rotationx -0.50
|
||||
angularmomentum 0 0.0001 0
|
||||
sphere yes
|
||||
moon yes
|
||||
|
@ -188,12 +70,10 @@ actor 0 0 0 ganymede
|
|||
|
||||
actor 0 0 0 callisto
|
||||
name Callisto
|
||||
id callisto
|
||||
relativeto jupiter
|
||||
orbit 1882700e3 0.45
|
||||
scale 2410e3
|
||||
rotationy -0.40
|
||||
rotationx -0.50
|
||||
angularmomentum 0 0.0001 0
|
||||
sphere yes
|
||||
moon yes
|
||||
|
@ -207,30 +87,6 @@ actor 0 0 0 moonlet
|
|||
scale 50e3
|
||||
angularmomentum 0 0.025 0
|
||||
|
||||
actor 0 0 0 moonlet
|
||||
name Metis
|
||||
relativeto jupiter
|
||||
id metis
|
||||
orbit 128000e3 0.8
|
||||
scale 21.5e3
|
||||
angularmomentum 0 0.025 0
|
||||
|
||||
actor 0 0 0 moonlet
|
||||
name Adrastea
|
||||
relativeto jupiter
|
||||
id adrastea
|
||||
orbit 129000e3 0.5
|
||||
scale 8.2e3
|
||||
angularmomentum 0 0.025 0
|
||||
|
||||
actor 0 0 0 moonlet
|
||||
name Amalthea
|
||||
relativeto jupiter
|
||||
id amalthea
|
||||
orbit 181365.84e3 0.2
|
||||
scale 83.5e3
|
||||
angularmomentum 0 0.025 0
|
||||
|
||||
actor 3000 0 0 moonlet
|
||||
name Moonlet
|
||||
collider mesh
|
||||
|
@ -239,16 +95,15 @@ actor 3000 0 0 moonlet
|
|||
scale 500
|
||||
angularmomentum 0 0.015 0
|
||||
|
||||
actor -8200 -4400 -8100 asteroid_lum
|
||||
actor 220 -2400 410 asteroid_lum
|
||||
relativeto player
|
||||
name Lum
|
||||
id Lum
|
||||
collider mesh
|
||||
density 10000000000
|
||||
scale 300
|
||||
rotationy 0.82
|
||||
angularmomentum 0 0.015 0
|
||||
actor 70 30 30 lightorb
|
||||
actor -80 0 0 lightorb
|
||||
relativeto Lum
|
||||
name "Light Orb"
|
||||
scale 0.3
|
||||
|
@ -343,7 +198,6 @@ actor -3300 10 0 pizzeria
|
|||
rotationy -0.7
|
||||
scale 3
|
||||
chatid SubduedClippy
|
||||
pronoun it
|
||||
|
||||
actor -45 -4 -4 suit
|
||||
relativeto pizzeria
|
||||
|
@ -379,12 +233,11 @@ actor 60 -15 -40 suit
|
|||
actor -300 0 40 suit
|
||||
relativeto player
|
||||
id Drifter
|
||||
name "梓涵"
|
||||
name "Drifter"
|
||||
chatid Drifter
|
||||
oxygen 0.08
|
||||
scale 2
|
||||
collider handcrafted
|
||||
pronoun she
|
||||
|
||||
actor 100 -18000 2000 "orb_busstop"
|
||||
relativeto player
|
||||
|
@ -405,7 +258,6 @@ actor 100 -18000 2000 "orb_busstop"
|
|||
rotationy -0.5
|
||||
scale 3
|
||||
chatid ClippyTransSerenity
|
||||
pronoun it
|
||||
actor 40 10 40 "orb_busstop"
|
||||
name "Light Orb"
|
||||
relativeto busstopclippy
|
||||
|
@ -431,7 +283,6 @@ actor 100 -18000 2000 "orb_busstop"
|
|||
scale 2
|
||||
collider capsule 1 0.5
|
||||
chatid NPCinCryoStasis
|
||||
pronoun he
|
||||
|
||||
actor -184971e3 149410e3 -134273e3 "orb_busstop"
|
||||
relativeto jupiter
|
||||
|
@ -452,7 +303,6 @@ actor -184971e3 149410e3 -134273e3 "orb_busstop"
|
|||
rotationy -0.5
|
||||
scale 3
|
||||
chatid ClippyTransOscillation
|
||||
pronoun it
|
||||
actor 40 10 40 "orb_busstop"
|
||||
name "Light Orb"
|
||||
relativeto busstopclippy2
|
||||
|
@ -489,7 +339,6 @@ actor 27643e3 -44e3 -124434e3 "orb_busstop"
|
|||
rotationy -0.5
|
||||
scale 3
|
||||
chatid ClippyTransMetis
|
||||
pronoun it
|
||||
actor 40 10 40 "orb_busstop"
|
||||
name "Light Orb"
|
||||
relativeto busstopclippy3
|
||||
|
|
18
src/hud.rs
18
src/hud.rs
|
@ -25,13 +25,11 @@ impl Plugin for HudPlugin {
|
|||
app.add_systems(Startup, setup);
|
||||
app.add_systems(Update, (
|
||||
update_hud,
|
||||
update_ar_overlays,
|
||||
handle_input,
|
||||
handle_target_event,
|
||||
));
|
||||
app.add_systems(PostUpdate, (
|
||||
update_ar_overlays
|
||||
.after(world::position_to_transform)
|
||||
.in_set(sync::SyncSet::PositionToTransform),
|
||||
update_target_selectagon
|
||||
.after(PhysicsSet::Sync)
|
||||
.after(camera::apply_input_to_player)
|
||||
|
@ -111,12 +109,10 @@ impl Message {
|
|||
#[derive(Component)]
|
||||
pub struct IsClickable {
|
||||
pub name: Option<String>,
|
||||
pub pronoun: Option<String>,
|
||||
pub distance: Option<f64>,
|
||||
}
|
||||
impl Default for IsClickable { fn default() -> Self { Self {
|
||||
name: None,
|
||||
pronoun: None,
|
||||
distance: None,
|
||||
}}}
|
||||
|
||||
|
@ -208,10 +204,9 @@ fn setup(
|
|||
};
|
||||
|
||||
// Add Statistics HUD
|
||||
let version = &settings.version;
|
||||
let mut bundle_fps = TextBundle::from_sections([
|
||||
TextSection::new("", style.clone()),
|
||||
TextSection::new(format!(" OutFlyOS v{version} ⚡ "), style.clone()),
|
||||
TextSection::new(" ⚡ ", style.clone()),
|
||||
TextSection::new("", style.clone()),
|
||||
TextSection::new(" ☣ ", style.clone()),
|
||||
TextSection::new("", style.clone()),
|
||||
|
@ -491,12 +486,7 @@ fn update_hud(
|
|||
};
|
||||
let speed_readable = nature::readable_distance(speed);
|
||||
let target_name = clickable.name.clone().unwrap_or("Unnamed".to_string());
|
||||
let pronoun = if let Some(pronoun) = &clickable.pronoun {
|
||||
format!("Pronoun: {pronoun}\n")
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
text.sections[15].value = format!("\n\nTarget: {target_name}\n{pronoun}Distance: {distance}\nΔv {speed_readable}/s");
|
||||
text.sections[15].value = format!("\n\nTarget: {target_name}\nDistance: {distance}\nΔv {speed_readable}/s");
|
||||
}
|
||||
else {
|
||||
text.sections[15].value = "".to_string();
|
||||
|
@ -716,7 +706,7 @@ fn update_target_selectagon(
|
|||
}
|
||||
selectagon_trans.translation = target_trans.translation;
|
||||
selectagon_trans.scale = target_trans.scale;
|
||||
selectagon_trans.look_at(camera_trans.translation, Vec3::X);
|
||||
selectagon_trans.rotation = Quat::from_rotation_arc(Vec3::Z, (-selectagon_trans.translation).normalize());
|
||||
|
||||
// Enlarge Selectagon to a minimum angular diameter
|
||||
let (angular_diameter, _, _) = camera::calc_angular_diameter(
|
||||
|
|
|
@ -12,10 +12,6 @@ pub const PARSEC2METER: f64 = 3.0857e16;
|
|||
pub const DIST_JUPTER_SUN: f64 = 778479.0e6;
|
||||
pub const EARTH_GRAVITY: f32 = 9.81;
|
||||
|
||||
pub const SOL_RADIUS: f64 = 696_300_000.0;
|
||||
pub const JUPITER_RADIUS: f64 = 71_492_000.0;
|
||||
pub const JUPITER_RING_RADIUS: f64 = 229_000_000.0;
|
||||
|
||||
// Each star's values: (x, y, z, magnitude, color index, distance, name)
|
||||
pub const STARS: &[(f32, f32, f32, f32, f32, f32, &str)] = &include!("data/stars.in");
|
||||
|
||||
|
|
25
src/var.rs
25
src/var.rs
|
@ -18,7 +18,6 @@ pub const DEFAULT_CHAT_SPEED: f32 = 10.0;
|
|||
pub struct Settings {
|
||||
pub dev_mode: bool,
|
||||
pub god_mode: bool,
|
||||
pub version: String,
|
||||
pub mute_sfx: bool,
|
||||
pub mute_music: bool,
|
||||
pub volume_sfx: u8,
|
||||
|
@ -41,17 +40,11 @@ pub struct Settings {
|
|||
pub hud_color_choices: Color,
|
||||
pub chat_speed: f32,
|
||||
pub hud_active: bool,
|
||||
pub map_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_map: KeyCode,
|
||||
pub key_map_zoom_out: KeyCode,
|
||||
pub key_map_zoom_in: KeyCode,
|
||||
//pub key_map_zoom_out_wheel: MouseButton,
|
||||
//pub key_map_zoom_in_wheel: MouseButton,
|
||||
pub key_togglehud: KeyCode,
|
||||
pub key_exit: KeyCode,
|
||||
pub key_restart: KeyCode,
|
||||
|
@ -76,8 +69,6 @@ pub struct Settings {
|
|||
pub key_mouseright: KeyCode,
|
||||
pub key_rotateleft: KeyCode,
|
||||
pub key_rotateright: KeyCode,
|
||||
pub key_toggle_sfx: KeyCode,
|
||||
pub key_toggle_music: KeyCode,
|
||||
pub key_reply1: KeyCode,
|
||||
pub key_reply2: KeyCode,
|
||||
pub key_reply3: KeyCode,
|
||||
|
@ -119,21 +110,15 @@ impl Default for Settings {
|
|||
default_mute_music = false;
|
||||
dev_mode = false;
|
||||
}
|
||||
let version = if let Some(version) = option_env!("CARGO_PKG_VERSION") {
|
||||
version.to_string()
|
||||
} else {
|
||||
"13.37".to_string()
|
||||
};
|
||||
|
||||
Settings {
|
||||
dev_mode,
|
||||
god_mode: false,
|
||||
version,
|
||||
mute_sfx: default_mute_sfx,
|
||||
mute_music: default_mute_music,
|
||||
volume_sfx: 100,
|
||||
volume_music: 100,
|
||||
mouse_sensitivity: 0.4,
|
||||
mouse_sensitivity: 0.7,
|
||||
fov: 50.0,
|
||||
fov_highspeed: 25.0,
|
||||
zoom_fov: 15.0,
|
||||
|
@ -151,17 +136,11 @@ impl Default for Settings {
|
|||
hud_color_choices: Color::rgb(0.45, 0.45, 0.45),
|
||||
chat_speed: DEFAULT_CHAT_SPEED * if dev_mode { 2.5 } else { 1.0 },
|
||||
hud_active: false,
|
||||
map_active: false,
|
||||
is_zooming: false,
|
||||
third_person: false,
|
||||
rotation_stabilizer_active: true,
|
||||
key_selectobject: MouseButton::Left,
|
||||
key_zoom: MouseButton::Right,
|
||||
key_map: KeyCode::KeyM,
|
||||
key_map_zoom_out: KeyCode::ShiftLeft,
|
||||
key_map_zoom_in: KeyCode::ControlLeft,
|
||||
//key_map_zoom_out_wheel: KeyCode::Shift,
|
||||
//key_map_zoom_in_wheel: KeyCode::Shift,
|
||||
key_togglehud: KeyCode::Tab,
|
||||
key_exit: KeyCode::Escape,
|
||||
key_restart: KeyCode::F7,
|
||||
|
@ -186,8 +165,6 @@ impl Default for Settings {
|
|||
key_mouseright: KeyCode::KeyL,
|
||||
key_rotateleft: KeyCode::KeyU,
|
||||
key_rotateright: KeyCode::KeyO,
|
||||
key_toggle_sfx: KeyCode::F3,
|
||||
key_toggle_music: KeyCode::F4,
|
||||
key_reply1: KeyCode::Digit1,
|
||||
key_reply2: KeyCode::Digit2,
|
||||
key_reply3: KeyCode::Digit3,
|
||||
|
|
166
src/world.rs
166
src/world.rs
|
@ -15,7 +15,6 @@ const RING_THICKNESS: f64 = 8.0e6;
|
|||
const STARS_MAX_MAGNITUDE: f32 = 5.5; // max 7.0, see generate_starchart.py
|
||||
|
||||
const CENTER_WORLD_ON_PLAYER: bool = true;
|
||||
const SKYBOX: bool = false;
|
||||
|
||||
const ASTEROID_SPAWN_STEP: f64 = 500.0;
|
||||
const ASTEROID_VIEW_RADIUS: f64 = 3000.0;
|
||||
|
@ -39,7 +38,6 @@ pub fn asset_name_to_path(name: &str) -> &'static str {
|
|||
"pizzeria" => "models/pizzeria2.glb#Scene0",
|
||||
"pizzasign" => "models/pizzasign.glb#Scene0",
|
||||
"selectagon" => "models/selectagon.glb#Scene0",
|
||||
"orbitring" => "models/orbitring.glb#Scene0",
|
||||
"clippy" => "models/clippy.glb#Scene0",
|
||||
"clippy_ar" => "models/clippy_ar.glb#Scene0",
|
||||
"whale" => "models/whale.glb#Scene0",
|
||||
|
@ -59,6 +57,7 @@ impl Plugin for WorldPlugin {
|
|||
app.insert_resource(Gravity(DVec3::splat(0.0)));
|
||||
app.insert_resource(AsteroidUpdateTimer(
|
||||
Timer::from_seconds(ASTEROID_UPDATE_INTERVAL, TimerMode::Repeating)));
|
||||
app.insert_resource(AsteroidDatabase(Vec::new()));
|
||||
app.insert_resource(ActiveAsteroids(HashMap::new()));
|
||||
app.add_event::<DespawnEvent>();
|
||||
|
||||
|
@ -77,14 +76,15 @@ impl Plugin for WorldPlugin {
|
|||
}
|
||||
|
||||
#[derive(Resource)] struct AsteroidUpdateTimer(Timer);
|
||||
#[derive(Resource)] pub struct ActiveAsteroids(pub HashMap<I64Vec3, AsteroidData>);
|
||||
#[derive(Resource)] struct AsteroidDatabase(Vec<AsteroidData>);
|
||||
#[derive(Resource)] struct ActiveAsteroids(HashMap<I64Vec3, AsteroidData>);
|
||||
#[derive(Resource)] struct AsteroidModel1(Handle<Scene>);
|
||||
#[derive(Resource)] struct AsteroidModel2(Handle<Scene>);
|
||||
|
||||
#[derive(Component)] struct Asteroid;
|
||||
#[derive(Component)] pub struct DespawnOnPlayerDeath;
|
||||
|
||||
pub struct AsteroidData {
|
||||
struct AsteroidData {
|
||||
entity: Entity,
|
||||
//viewdistance: f64,
|
||||
}
|
||||
|
@ -103,6 +103,7 @@ pub fn setup(
|
|||
mut commands: Commands,
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||
mut materials_jupiter: ResMut<Assets<shading::JupitersRing>>,
|
||||
mut materials_skybox: ResMut<Assets<shading::SkyBox>>,
|
||||
asset_server: Res<AssetServer>,
|
||||
) {
|
||||
|
@ -111,90 +112,113 @@ pub fn setup(
|
|||
commands.insert_resource(AsteroidModel2(asset_server.load(ASSET_ASTEROID2)));
|
||||
|
||||
// Generate starmap
|
||||
let sphere_handle = meshes.add(Sphere::new(1.0).mesh().uv(16, 16));
|
||||
let sphere_handle = meshes.add(Circle::new(1.0));
|
||||
let mut starcount = 0;
|
||||
for (index, star) in nature::STARS.iter().enumerate() {
|
||||
let (x, y, z, mag, _absmag, color_index, name) = *star;
|
||||
for star in nature::STARS {
|
||||
let mag = star.3;
|
||||
if mag > STARS_MAX_MAGNITUDE {
|
||||
continue;
|
||||
}
|
||||
|
||||
let (r, g, b) = nature::star_color_index_to_rgb(color_index);
|
||||
let mut pos = DVec3::new(x as f64, z as f64, -y as f64) * nature::PARSEC2METER;
|
||||
if pos.length() > 1e21 {
|
||||
pos *= 0.002;
|
||||
}
|
||||
let pos_render = pos * 1.0;
|
||||
let scale_factor = 1e-4 * pos_render.length() as f32; // from experimentation
|
||||
|
||||
let is_sun = mag < -20.0;
|
||||
let mag = mag.min(6.0);
|
||||
let scale_size = {|mag: f32|
|
||||
scale_factor * (0.230299 * mag * mag - 3.09013 * mag + 15.1782)
|
||||
};
|
||||
let scale = scale_size(mag);
|
||||
|
||||
let scale_color = {|color: f32|
|
||||
1.2 * color * (0.0659663 * mag * mag - 1.09862 * mag + 4.3)
|
||||
if is_sun {
|
||||
color * 13.0f32
|
||||
} else {
|
||||
color * (0.0659663 * mag * mag - 1.09862 * mag + 4.3)
|
||||
}
|
||||
};
|
||||
//let scale = translation.length().powf(0.84);
|
||||
//pos_render.length().powf(0.64)
|
||||
//(radius as f64 * nature::SOL_RADIUS).powf(0.02) as f32 *
|
||||
|
||||
let scale_size = {|mag: f32|
|
||||
if is_sun {
|
||||
40000.0f32
|
||||
} else {
|
||||
1000.0 * (0.230299 * mag * mag - 3.09013 * mag + 15.1782)
|
||||
} * 100.0
|
||||
};
|
||||
let (r, g, b) = nature::star_color_index_to_rgb(star.4);
|
||||
let star_color_handle = materials.add(StandardMaterial {
|
||||
base_color: Color::rgb(scale_color(r), scale_color(g), scale_color(b)),
|
||||
unlit: true,
|
||||
..default()
|
||||
});
|
||||
let name = if name.is_empty() {
|
||||
format!("Uncharted Star #{index}")
|
||||
} else {
|
||||
name.to_string()
|
||||
};
|
||||
let distance = if pos.length() > 1e21 {
|
||||
let mesh_distance = 1e9;
|
||||
let starchart_distance = if is_sun {
|
||||
Some(nature::DIST_JUPTER_SUN)
|
||||
} else if star.5 >= 100000.0 {
|
||||
None
|
||||
} else {
|
||||
Some(pos.length())
|
||||
Some(nature::PARSEC2METER * star.5 as f64)
|
||||
};
|
||||
|
||||
let name = if star.6.is_empty() {
|
||||
"Uncharted Star".to_string()
|
||||
} else {
|
||||
star.6.to_string()
|
||||
};
|
||||
let translation = Vec3::new(
|
||||
mesh_distance * star.0,
|
||||
mesh_distance * star.1,
|
||||
mesh_distance * star.2,
|
||||
);
|
||||
let rotation = Quat::from_rotation_arc(Vec3::Z, (-translation).normalize());
|
||||
commands.spawn((
|
||||
Star,
|
||||
hud::IsClickable {
|
||||
name: Some(name),
|
||||
distance,
|
||||
..default()
|
||||
distance: starchart_distance,
|
||||
},
|
||||
PbrBundle {
|
||||
mesh: sphere_handle.clone(),
|
||||
material: star_color_handle,
|
||||
transform: Transform {
|
||||
translation: pos_render.as_vec3(),
|
||||
scale: Vec3::splat(scale as f32),
|
||||
..default()
|
||||
translation,
|
||||
rotation,
|
||||
scale: Vec3::splat(scale_size(mag)),
|
||||
},
|
||||
..default()
|
||||
},
|
||||
}
|
||||
));
|
||||
starcount += 1;
|
||||
}
|
||||
info!("Generated {starcount} stars");
|
||||
|
||||
// Add shaded skybox
|
||||
if SKYBOX {
|
||||
let mut mesh = Mesh::from(Sphere::new(1e10).mesh().uv(5, 5));
|
||||
//let mut mesh = Mesh::from(Cuboid::from_size(Vec3::splat(2e10)));
|
||||
if let Some(Indices::U32(indices)) = mesh.indices_mut() {
|
||||
// Reverse the order of each triangle to avoid backface culling
|
||||
for slice in indices.chunks_mut(3) {
|
||||
slice.reverse();
|
||||
}
|
||||
//let mut mesh = Mesh::from(Sphere::new(1e9).mesh().uv(50, 50));
|
||||
let mut mesh = Mesh::from(Sphere::new(1e10).mesh().uv(5, 5));
|
||||
//let mut mesh = Mesh::from(Cuboid::from_size(Vec3::splat(2e10)));
|
||||
if let Some(Indices::U32(indices)) = mesh.indices_mut() {
|
||||
// Reverse the order of each triangle to avoid backface culling
|
||||
for slice in indices.chunks_mut(3) {
|
||||
slice.reverse();
|
||||
}
|
||||
commands.spawn(MaterialMeshBundle {
|
||||
}
|
||||
commands.spawn((
|
||||
MaterialMeshBundle {
|
||||
mesh: meshes.add(mesh),
|
||||
material: materials_skybox.add(shading::SkyBox {}),
|
||||
transform: Transform::from_translation(Vec3::new(0.0, 0.0, 0.0)),
|
||||
..default()
|
||||
});
|
||||
}
|
||||
},
|
||||
Position::from_xyz(0.0, 0.0, 0.0),
|
||||
Rotation::from(Quat::IDENTITY),
|
||||
));
|
||||
|
||||
// Add shaded ring
|
||||
let ring_radius = 229_000_000.0;
|
||||
let jupiter_radius = 71_492_000.0;
|
||||
commands.spawn((
|
||||
MaterialMeshBundle {
|
||||
mesh: meshes.add(Mesh::from(Cylinder::new(ring_radius, 1.0))),
|
||||
material: materials_jupiter.add(shading::JupitersRing {
|
||||
alpha_mode: AlphaMode::Blend,
|
||||
ring_radius: ring_radius,
|
||||
jupiter_radius: jupiter_radius,
|
||||
}),
|
||||
..default()
|
||||
},
|
||||
Position::from_xyz(0.0, 0.0, 0.0),
|
||||
Rotation::from(Quat::IDENTITY),
|
||||
//Rotation::from(Quat::from_rotation_x(-0.3f32.to_radians())),
|
||||
));
|
||||
}
|
||||
|
||||
fn spawn_despawn_asteroids(
|
||||
|
@ -208,24 +232,16 @@ fn spawn_despawn_asteroids(
|
|||
asteroid2_handle: Res<AsteroidModel2>,
|
||||
mut q_asteroid: Query<(Entity, &SceneInstance), With<Asteroid>>,
|
||||
mut last_player_cell: Local<I64Vec3>,
|
||||
id2pos: Res<actor::Id2Pos>,
|
||||
) {
|
||||
if !timer.0.tick(time.delta()).just_finished() || q_player.is_empty() {
|
||||
//if q_player.is_empty() {
|
||||
return;
|
||||
}
|
||||
let jupiter_pos = if let Some(jupiter_pos) = id2pos.0.get(&"jupiter".to_string()) {
|
||||
*jupiter_pos
|
||||
} else {
|
||||
error!("Can't spawn asteroids because Jupiter's position can not be determined");
|
||||
return;
|
||||
};
|
||||
let player = q_player.get_single().unwrap();
|
||||
let fromjupiter = player.0 - jupiter_pos;
|
||||
let player_cell = I64Vec3::new(
|
||||
(fromjupiter.x / ASTEROID_SPAWN_STEP).round() as i64,
|
||||
(fromjupiter.y / ASTEROID_SPAWN_STEP).round() as i64,
|
||||
(fromjupiter.z / ASTEROID_SPAWN_STEP).round() as i64,
|
||||
(player.x / ASTEROID_SPAWN_STEP).round() as i64,
|
||||
(player.y / ASTEROID_SPAWN_STEP).round() as i64,
|
||||
(player.z / ASTEROID_SPAWN_STEP).round() as i64,
|
||||
);
|
||||
if *last_player_cell == player_cell {
|
||||
return;
|
||||
|
@ -269,13 +285,13 @@ fn spawn_despawn_asteroids(
|
|||
}
|
||||
|
||||
// Density based on the radius alone
|
||||
let radius_plane = (fromjupiter.x * fromjupiter.x + fromjupiter.z * fromjupiter.z).sqrt();
|
||||
let radius_plane = (player.x * player.x + player.z * player.z).sqrt();
|
||||
let density_r = nature::ring_density((radius_plane / 1e6) as f32);
|
||||
if density_r < 0.001 {
|
||||
return;
|
||||
}
|
||||
// Density based on radius and the vertical distance to the ring
|
||||
let normalized_distance = fromjupiter.y / (RING_THICKNESS / 2.0);
|
||||
let normalized_distance = player.y / (RING_THICKNESS / 2.0);
|
||||
let density = density_r * (-4.0 * normalized_distance.powf(2.0)).exp() as f32;
|
||||
if density < 0.001 {
|
||||
return;
|
||||
|
@ -326,7 +342,7 @@ fn spawn_despawn_asteroids(
|
|||
|
||||
//let max_viewdist = ASTEROID_VIEW_RADIUS / ASTEROID_SPAWN_STEP;
|
||||
let wobble = ASTEROID_SPAWN_STEP * 0.5;
|
||||
let pos = jupiter_pos + DVec3::new(
|
||||
let pos = DVec3::new(
|
||||
origin.x as f64 * ASTEROID_SPAWN_STEP + wobble * rand_x * 2.0 - 1.0,
|
||||
origin.y as f64 * ASTEROID_SPAWN_STEP + wobble * rand_y * 2.0 - 1.0,
|
||||
origin.z as f64 * ASTEROID_SPAWN_STEP + wobble * rand_z * 2.0 - 1.0,
|
||||
|
@ -342,7 +358,6 @@ fn spawn_despawn_asteroids(
|
|||
Rotation::from(Quat::from_rotation_y(-PI / 3.)),
|
||||
Position::new(pos),
|
||||
Asteroid,
|
||||
DespawnOnPlayerDeath,
|
||||
));
|
||||
let model = match class {
|
||||
0 => asteroid1_handle.0.clone(),
|
||||
|
@ -385,7 +400,6 @@ fn handle_cheats(
|
|||
q_target: Query<(&Transform, &Position, &LinearVelocity), (With<hud::IsTargeted>, Without<actor::PlayerCamera>)>,
|
||||
mut ew_playerdies: EventWriter<actor::PlayerDiesEvent>,
|
||||
mut settings: ResMut<var::Settings>,
|
||||
id2pos: Res<actor::Id2Pos>,
|
||||
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
||||
) {
|
||||
if q_player.is_empty() || q_life.is_empty() {
|
||||
|
@ -433,22 +447,16 @@ fn handle_cheats(
|
|||
}
|
||||
|
||||
if key_input.just_pressed(settings.key_cheat_pizza) {
|
||||
if let Some(target) = id2pos.0.get(&"pizzeria".to_string()) {
|
||||
pos.0 = *target + DVec3::new(-60.0, 0.0, 0.0);
|
||||
gforce.ignore_gforce_seconds = 1.0;
|
||||
}
|
||||
pos.0 = DVec3::new(-121100218.0, 593057.0, -190818113.0);
|
||||
gforce.ignore_gforce_seconds = 1.0;
|
||||
}
|
||||
if key_input.just_pressed(settings.key_cheat_farview1) {
|
||||
if let Some(target) = id2pos.0.get(&"busstop2".to_string()) {
|
||||
pos.0 = *target + DVec3::new(0.0, -1000.0, 0.0);
|
||||
gforce.ignore_gforce_seconds = 1.0;
|
||||
}
|
||||
pos.0 = DVec3::new(27643e3, -47e3, -124434e3);
|
||||
gforce.ignore_gforce_seconds = 1.0;
|
||||
}
|
||||
if key_input.just_pressed(settings.key_cheat_farview2) {
|
||||
if let Some(target) = id2pos.0.get(&"busstop3".to_string()) {
|
||||
pos.0 = *target + DVec3::new(0.0, -1000.0, 0.0);
|
||||
gforce.ignore_gforce_seconds = 1.0;
|
||||
}
|
||||
pos.0 = DVec3::new(-184968e3, 149410e3, -134273e3);
|
||||
gforce.ignore_gforce_seconds = 1.0;
|
||||
}
|
||||
if key_input.pressed(settings.key_cheat_adrenaline_zero) {
|
||||
lifeform.adrenaline = 0.0;
|
||||
|
|
Loading…
Reference in a new issue