Compare commits

...

53 commits

Author SHA1 Message Date
yuni 838b8b7fd6 bump version to v0.7.3 2024-04-20 04:58:11 +02:00
yuni 44e4c5493a move starting position closer into the ring 2024-04-20 04:57:18 +02:00
yuni 115cd1b46d tweak key bindings 2024-04-20 04:49:46 +02:00
yuni 0d9bf25f52 make map movement keys much more intuitive 2024-04-20 04:39:48 +02:00
yuni c79320d072 implement moving with AWSD in map. this allows changing target! 2024-04-20 04:19:30 +02:00
yuni 973506d335 fix rotation of 4 galileian moons 2024-04-20 03:51:04 +02:00
yuni 2c44d89c53 fix the bus dropping you off right into the sun /o\ 2024-04-20 03:27:20 +02:00
yuni 7e56f1f07b display pronouns of targets 2024-04-20 03:07:28 +02:00
yuni e6ca1c5b50 make pronouns optional (tā tāāā!) 2024-04-20 02:48:55 +02:00
yuni dba6c4183a hide orbital circles when AR is off 2024-04-20 02:37:00 +02:00
yuni e8eb7a77a1 reduced starting map zoom level 2024-04-20 02:36:45 +02:00
yuni 61986d2f43 slower map zoom 2024-04-20 02:33:37 +02:00
yuni d0acc6988f add orbital inclinations 2024-04-20 02:26:04 +02:00
yuni 1d220c79cc add orbital rings for all 8 planets of the sun + pluto 2024-04-20 02:16:17 +02:00
yuni d8722a4f98 add orbit rings in map for jupiter and its moons 2024-04-20 02:09:04 +02:00
yuni 8e04cddda5 add the remaining moons: Metis, Adrastea, Amalthea 2024-04-20 02:05:51 +02:00
yuni d2744d7f16 cleanup 2024-04-20 02:04:34 +02:00
yuni f1a7781fa2 properly reset generic asteroids on death 2024-04-20 00:47:51 +02:00
yuni 8c3eb72994 remove unneeded png dependency 2024-04-20 00:47:13 +02:00
yuni f9ac1c0275 update paths in install.sh 2024-04-20 00:31:00 +02:00
yuni cf71a23aec embed icon into OutFly.exe 2024-04-20 00:16:50 +02:00
yuni 6a1dadfd3a add windows-specific build files in build/windows/ 2024-04-19 23:22:30 +02:00
yuni 00d042b56c move linux-specific build files to build/linux/ 2024-04-19 23:22:16 +02:00
yuni 917d130cf8 add install.sh 2024-04-19 23:19:14 +02:00
yuni 965c7ef652 moved build/install-related files to /build/ 2024-04-19 23:17:19 +02:00
yuni 42a3577c57 enable scrolling the map with the mouse wheel 2024-04-19 22:54:27 +02:00
yuni ed1ef1bb1f fix asteroids spawing inside the sun rather than around jupiter 2024-04-19 22:41:16 +02:00
yuni 5c29681ee3 position cheats now teleport you relative to identified objects 2024-04-19 22:30:15 +02:00
yuni e3ff386011 keep track of positions of IDed objects, for easy rel. position calc. 2024-04-19 22:23:25 +02:00
yuni 1d73cf8367 add outfly.desktop file, rename icon.png to outfly.png 2024-04-19 20:16:55 +02:00
yuni bf3c8d5b44 add Mastodon link 2024-04-19 17:54:25 +02:00
yuni 0044f50e68 better starting zoom level 2024-04-19 04:41:07 +02:00
yuni ed6187d996 restore jupiter's ring 2024-04-19 04:18:45 +02:00
yuni 513e3d89ef center map around targeted object 2024-04-19 03:54:17 +02:00
yuni 7d85a93449 restore previous night sky appearance, tho with real star distances 2024-04-19 03:42:59 +02:00
yuni 8c0af0e467 add star radius calculation based on stefan-boltzman law 2024-04-19 03:39:57 +02:00
yuni 70a602de5c enforce min/max zoom levels 2024-04-19 03:38:32 +02:00
yuni 7d063209fa add some natural constants 2024-04-19 03:38:13 +02:00
yuni ea26711806 place stars at their actual xyz coordinate. (try zooming out in map 🤯) 2024-04-19 00:43:12 +02:00
yuni fca8251f27 place sun separately of the other stars, for better placement in map 2024-04-18 23:39:34 +02:00
yuni 0c2c295f6b place stars ~105 lightyears away from the player, should be enough... 2024-04-18 23:14:27 +02:00
yuni 8cd970a930 fix selectagon orientation in map mode 2024-04-18 23:05:29 +02:00
yuni 321fccb9c4 update .gitignore 2024-04-18 22:48:54 +02:00
yuni 336527c4c0 fix AR flicker when moving 2024-04-18 22:48:30 +02:00
yuni a14e295007 tweak map parameters 2024-04-18 22:48:21 +02:00
yuni 463745eabb add map mode 🥰 2024-04-18 21:25:41 +02:00
yuni f169ceac8f show the game version in the HUD 2024-04-18 03:56:32 +02:00
yuni 5d89043ef2 bump version to v0.7.2 2024-04-17 15:25:20 +02:00
yuni 81b09d183a boost interaction distance 2024-04-17 15:24:00 +02:00
yuni a6e6dac3d8 reposition Lum 2024-04-17 15:15:24 +02:00
yuni 919b801832 fix dying from entering vehicles 2024-04-17 13:55:39 +02:00
yuni c9e271184c update mouse sensitivity after the recent collider/mass change 2024-04-17 13:55:30 +02:00
yuni 1b0209f38b toggle skybox off. don't like the result 2024-04-17 04:08:44 +02:00
25 changed files with 16301 additions and 15782 deletions

2
.gitignore vendored
View file

@ -3,4 +3,4 @@ assets/tmp
assets/external assets/external
*.blend1 *.blend1
extra extra
outfly_*.zip outfly_v*

118
Cargo.lock generated
View file

@ -72,12 +72,6 @@ dependencies = [
"winit", "winit",
] ]
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]] [[package]]
name = "ahash" name = "ahash"
version = "0.8.11" version = "0.8.11"
@ -1488,15 +1482,6 @@ dependencies = [
"windows 0.54.0", "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]] [[package]]
name = "crossbeam-channel" name = "crossbeam-channel"
version = "0.5.12" version = "0.5.12"
@ -1600,6 +1585,19 @@ version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" 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]] [[package]]
name = "encase" name = "encase"
version = "0.7.0" version = "0.7.0"
@ -1720,15 +1718,6 @@ version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" 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]] [[package]]
name = "file-id" name = "file-id"
version = "0.2.1" version = "0.2.1"
@ -1756,16 +1745,6 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" 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]] [[package]]
name = "foreign-types" name = "foreign-types"
version = "0.5.0" version = "0.5.0"
@ -2086,7 +2065,6 @@ dependencies = [
"color_quant", "color_quant",
"jpeg-decoder", "jpeg-decoder",
"num-traits", "num-traits",
"png",
] ]
[[package]] [[package]]
@ -2393,16 +2371,6 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 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]] [[package]]
name = "mio" name = "mio"
version = "0.8.11" version = "0.8.11"
@ -2769,11 +2737,12 @@ dependencies = [
[[package]] [[package]]
name = "outfly" name = "outfly"
version = "0.7.1" version = "0.7.3"
dependencies = [ dependencies = [
"bevy", "bevy",
"bevy_embedded_assets", "bevy_embedded_assets",
"bevy_xpbd_3d", "bevy_xpbd_3d",
"embed-resource",
"fastrand", "fastrand",
"regex", "regex",
"serde", "serde",
@ -2913,19 +2882,6 @@ version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" 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]] [[package]]
name = "polling" name = "polling"
version = "3.5.0" version = "3.5.0"
@ -3309,12 +3265,6 @@ dependencies = [
"wide", "wide",
] ]
[[package]]
name = "simd-adler32"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.4.9" version = "0.4.9"
@ -3542,6 +3492,15 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "toml"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "toml_datetime" name = "toml_datetime"
version = "0.6.5" version = "0.6.5"
@ -3716,6 +3675,26 @@ version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 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]] [[package]]
name = "walkdir" name = "walkdir"
version = "2.5.0" version = "2.5.0"
@ -4416,6 +4395,15 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "winreg"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
dependencies = [
"winapi",
]
[[package]] [[package]]
name = "x11-dl" name = "x11-dl"
version = "2.21.0" version = "2.21.0"

View file

@ -1,6 +1,6 @@
[package] [package]
name = "outfly" name = "outfly"
version = "0.7.1" version = "0.7.3"
edition = "2021" edition = "2021"
homepage = "https://codeberg.org/hut/outfly" homepage = "https://codeberg.org/hut/outfly"
repository = "https://codeberg.org/hut/outfly" repository = "https://codeberg.org/hut/outfly"
@ -8,16 +8,20 @@ categories = ["game", "aerospace", "simulation"]
keywords = ["game", "space", "3d"] keywords = ["game", "space", "3d"]
license = "GPL-3.0-only" license = "GPL-3.0-only"
rust-version = "1.76.0" rust-version = "1.76.0"
build = "build/build.rs"
[dependencies] [dependencies]
regex = "1" 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", "png", "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", "tonemapping_luts", "vorbis"]}
bevy_xpbd_3d = { version = "0.4.2", default-features = false, features = ["3d", "f64", "parry-f64", "parallel", "async-collider"] } bevy_xpbd_3d = { version = "0.4.2", default-features = false, features = ["3d", "f64", "parry-f64", "parallel", "async-collider"] }
bevy_embedded_assets = "0.10.2" bevy_embedded_assets = "0.10.2"
fastrand = "2.0" fastrand = "2.0"
serde = "1.0" serde = "1.0"
serde_yaml = "0.9" serde_yaml = "0.9"
[build-dependencies]
embed-resource = "1.6.3" # embedding of .exe metadata
[features] [features]
default = ["x11"] default = ["x11"]
dev = ["bevy/dynamic_linking", "bevy/file_watcher"] dev = ["bevy/dynamic_linking", "bevy/file_watcher"]

View file

@ -21,6 +21,7 @@ Links:
- [Source code](https://codeberg.org/hut/outfly) - [Source code](https://codeberg.org/hut/outfly)
- [itch.io page](https://wholesomevacuum.itch.io/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) - [Chatroom](https://matrix.to/#/#outfly:pub.solar)
![screenshot](doc/images/screenshot2.jpg) ![screenshot](doc/images/screenshot2.jpg)
@ -42,9 +43,10 @@ Links:
- Tab: Toggle HUD/AR - Tab: Toggle HUD/AR
- F11: Toggle fullscreen - F11: Toggle fullscreen
- F: Toggle 3rd person view - F: Toggle 3rd person view
- M: Toggle map
- Y: Toggle rotation stabilizer - Y: Toggle rotation stabilizer
- T: Toggle music - F4: Toggle music
- M: Toggle sound effects - F3: Toggle sound effects
- Cheats - Cheats
- G: Toggle god mode / cheats - G: Toggle god mode / cheats
- V/B: Impossible acceleration forward/backward - V/B: Impossible acceleration forward/backward
@ -155,6 +157,10 @@ python -m http.server -d wasm
# Changelog # 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.1: Much nicer HUD
- v0.7.0: - v0.7.0:
- Overhaul conversation system, now defined in YAML files - Overhaul conversation system, now defined in YAML files

BIN
assets/models/orbitring.glb Normal file

Binary file not shown.

7
build/build.rs Normal file
View file

@ -0,0 +1,7 @@
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");
}
}

View file

@ -31,55 +31,85 @@ PATH = "extra/hygdata_v41.csv"
# Meissa (orion's head, 8th brightest star in orion) = 3.33 # Meissa (orion's head, 8th brightest star in orion) = 3.33
MAX_APPARENT_MAGNITUDE = 7.0 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("// 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("// 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("// License: CC BY-SA 4.0: https://creativecommons.org/licenses/by-sa/4.0/")
print("// Each star's values: (x, y, z, magnitude, color index, distance, name)") print("// Each star's values: (x, y, z, magnitude, absolute magnitude, color index, name)")
print("[") print("[")
def render(ra, dec, mag, ci, dist, name): def render(i, x, y, z, ra, dec, mag, absmag, ci, dist, name):
# Takes ra/deg in degrees # Takes ra/deg in degrees
ra = float(ra) ra = float(ra)
dec = float(dec) dec = float(dec)
mag = float(mag) mag = float(mag)
x, y, z = float(x), float(y), float(z)
name = re.sub(r'\s+', ' ', name) name = re.sub(r'\s+', ' ', name)
if name == 'Sol':
return
distance = 1.0 #radius = star_radius(float(ci), float(absmag))
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 # distance = 1.0
x, y, z = x, z, y # 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
#brightness = 2.512 ** (0 - mag) #brightness = 2.512 ** (0 - mag)
print(f'({x:.04},{y:.04},{z:.04},{mag:.04},{ci},{dist},"{name}"),') print(f'({x:.04},{y:.04},{z:.04},{mag:.04},{absmag:.04},{ci},"{name}"),')
def clean_name(string): def clean_name(string):
return "".join([c for c in string if c.isalnum() or c in ' -_']) 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 total = 0
count = 0 count = 0
count_extra = 0 count_extra = 0
entries = [] entries = []
with open(PATH, "r", encoding="utf-8") as f: with open(PATH, "r", encoding="utf-8") as f:
for entry in csv.DictReader(f): for index, entry in enumerate(csv.DictReader(f)):
entries.append(entry) entries.append((index, entry))
entries.sort(key=lambda entry: float(entry['mag'])) entries.sort(key=lambda entry: float(entry[1]['mag']))
for entry in entries: for index, entry in entries:
total += 1 total += 1
ra = entry['ra'] ra = entry['ra']
dec = entry['dec'] dec = entry['dec']
x = entry['x']
y = entry['y']
z = entry['z']
mag = entry['mag'] mag = entry['mag']
absmag = entry['absmag']
ci = entry['ci'] ci = entry['ci']
dist = entry['dist'] dist = entry['dist']
name = clean_name(entry['proper']) name = clean_name(entry['proper'])
@ -92,7 +122,7 @@ for entry in entries:
continue continue
if float(mag) > MAX_APPARENT_MAGNITUDE: if float(mag) > MAX_APPARENT_MAGNITUDE:
continue continue
render(ra, dec, mag, ci, dist, name) render(index, x, y, z, ra, dec, mag, absmag, ci, dist, name)
count += 1 count += 1
#for entry in CUSTOM_ENTRIES: #for entry in CUSTOM_ENTRIES:
# render(entry[0], entry[1], entry[2], entry[3], entry[4]) # render(entry[0], entry[1], entry[2], entry[3], entry[4])

7
build/linux/install.sh Executable file
View file

@ -0,0 +1,7 @@
#!/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"

View file

@ -0,0 +1,9 @@
[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

View file

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View file

@ -21,10 +21,10 @@ SRCPATH="outfly_v$VERSION"
mkdir "$SRCPATH" mkdir "$SRCPATH"
cp ../README.md "$SRCPATH" cp ../README.md "$SRCPATH"
cp ../target/x86_64-pc-windows-gnu/release/outfly.exe "$SRCPATH" cp ../target/x86_64-pc-windows-gnu/release/outfly.exe "$SRCPATH"/OutFly.exe
zip -v -r -9 ../"outfly_v${VERSION}_windows.zip" "$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" cp ../target/x86_64-unknown-linux-gnu/release/outfly "$SRCPATH"
zip -v -r -9 ../"outfly_v${VERSION}_linux.zip" "$SRCPATH" zip -v -r -9 ../"outfly_v${VERSION}_linux.zip" "$SRCPATH"

1
build/windows/icon.rc Normal file
View file

@ -0,0 +1 @@
app_icon ICON "outfly.ico"

BIN
build/windows/outfly.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View file

@ -3,10 +3,11 @@ use bevy_xpbd_3d::prelude::*;
use bevy::scene::SceneInstance; use bevy::scene::SceneInstance;
use bevy::math::DVec3; use bevy::math::DVec3;
use crate::{actor, audio, camera, chat, commands, effects, hud, nature, var, world}; use crate::{actor, audio, camera, chat, commands, effects, hud, nature, var, world};
use std::collections::HashMap;
pub const ENGINE_SPEED_FACTOR: f32 = 30.0; pub const ENGINE_SPEED_FACTOR: f32 = 30.0;
const MAX_TRANSMISSION_DISTANCE: f32 = 100.0; const MAX_TRANSMISSION_DISTANCE: f32 = 100.0;
const MAX_INTERACT_DISTANCE: f32 = 40.0; const MAX_INTERACT_DISTANCE: f32 = 50.0;
pub struct ActorPlugin; pub struct ActorPlugin;
impl Plugin for ActorPlugin { impl Plugin for ActorPlugin {
@ -27,9 +28,11 @@ impl Plugin for ActorPlugin {
)); ));
app.add_systems(PostUpdate, ( app.add_systems(PostUpdate, (
handle_vehicle_enter_exit, handle_vehicle_enter_exit,
update_id2pos,
)); ));
app.add_event::<VehicleEnterExitEvent>(); app.add_event::<VehicleEnterExitEvent>();
app.add_event::<PlayerDiesEvent>(); app.add_event::<PlayerDiesEvent>();
app.insert_resource(Id2Pos(HashMap::new()));
} }
} }
@ -110,10 +113,13 @@ impl Default for ExperiencesGForce { fn default() -> Self { Self {
#[derive(Component)] pub struct Player; // Attached to the suit of the player #[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 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 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 ActorEnteringVehicle;
#[derive(Component)] pub struct ActorVehicleBeingEntered; #[derive(Component)] pub struct ActorVehicleBeingEntered;
#[derive(Component)] pub struct WantsMaxRotation(pub f64); #[derive(Component)] pub struct WantsMaxRotation(pub f64);
#[derive(Component)] pub struct WantsMaxVelocity(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)] #[derive(Component)]
pub struct LifeForm { pub struct LifeForm {
@ -326,6 +332,7 @@ pub fn handle_vehicle_enter_exit(
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::EnterVehicle)); ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::EnterVehicle));
commands.entity(driver).remove::<PlayerCamera>(); commands.entity(driver).remove::<PlayerCamera>();
commands.entity(driver).remove::<Collider>(); commands.entity(driver).remove::<Collider>();
commands.entity(driver).insert(JustNowEnteredVehicle);
commands.entity(vehicle).insert(PlayerCamera); commands.entity(vehicle).insert(PlayerCamera);
commands.entity(vehicle).insert(PlayerDrivesThis); commands.entity(vehicle).insert(PlayerDrivesThis);
} }
@ -421,6 +428,7 @@ fn handle_player_death(
q_noscenes: Query<Entity, (With<world::DespawnOnPlayerDeath>, Without<SceneInstance>)>, q_noscenes: Query<Entity, (With<world::DespawnOnPlayerDeath>, Without<SceneInstance>)>,
ew_spawn: EventWriter<commands::SpawnEvent>, ew_spawn: EventWriter<commands::SpawnEvent>,
mut scene_spawner: ResMut<SceneSpawner>, mut scene_spawner: ResMut<SceneSpawner>,
mut active_asteroids: ResMut<world::ActiveAsteroids>,
mut ew_sfx: EventWriter<audio::PlaySfxEvent>, mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
mut ew_effect: EventWriter<effects::SpawnEffectEvent>, mut ew_effect: EventWriter<effects::SpawnEffectEvent>,
mut log: ResMut<hud::Log>, mut log: ResMut<hud::Log>,
@ -431,6 +439,7 @@ fn handle_player_death(
return; return;
} }
settings.reset_player_settings(); settings.reset_player_settings();
active_asteroids.0.clear();
for entity in &q_noscenes { for entity in &q_noscenes {
cmd.entity(entity).despawn(); cmd.entity(entity).despawn();
} }
@ -519,3 +528,13 @@ 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);
}
}

View file

@ -137,12 +137,12 @@ pub fn toggle_bgm(
mut evwriter_sfx: EventWriter<PlaySfxEvent>, mut evwriter_sfx: EventWriter<PlaySfxEvent>,
mut settings: ResMut<var::Settings>, mut settings: ResMut<var::Settings>,
) { ) {
if keyboard_input.just_pressed(KeyCode::KeyT) { if keyboard_input.just_pressed(settings.key_toggle_music) {
settings.mute_music ^= true; settings.mute_music ^= true;
evwriter_sfx.send(PlaySfxEvent(Sfx::Click)); evwriter_sfx.send(PlaySfxEvent(Sfx::Click));
evwriter_toggle.send(ToggleMusicEvent()); evwriter_toggle.send(ToggleMusicEvent());
} }
if keyboard_input.just_pressed(KeyCode::KeyM) { if keyboard_input.just_pressed(settings.key_toggle_sfx) {
settings.mute_sfx ^= true; settings.mute_sfx ^= true;
evwriter_sfx.send(PlaySfxEvent(Sfx::Click)); evwriter_sfx.send(PlaySfxEvent(Sfx::Click));
evwriter_toggle.send(ToggleMusicEvent()); evwriter_toggle.send(ToggleMusicEvent());

View file

@ -1,5 +1,5 @@
use bevy::prelude::*; use bevy::prelude::*;
use bevy::input::mouse::MouseMotion; use bevy::input::mouse::{MouseMotion, MouseWheel};
use bevy::window::PrimaryWindow; use bevy::window::PrimaryWindow;
use bevy::core_pipeline::bloom::{BloomCompositeMode, BloomSettings}; use bevy::core_pipeline::bloom::{BloomCompositeMode, BloomSettings};
use bevy::core_pipeline::tonemapping::Tonemapping; use bevy::core_pipeline::tonemapping::Tonemapping;
@ -8,6 +8,7 @@ use bevy::transform::TransformSystem;
use bevy::math::{DVec3, DQuat}; use bevy::math::{DVec3, DQuat};
use bevy_xpbd_3d::prelude::*; use bevy_xpbd_3d::prelude::*;
use std::f32::consts::PI; use std::f32::consts::PI;
use std::f64::consts::PI as PI64;
use crate::{actor, audio, hud, var}; use crate::{actor, audio, hud, var};
pub struct CameraPlugin; pub struct CameraPlugin;
@ -16,15 +17,50 @@ impl Plugin for CameraPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_systems(Startup, setup_camera); app.add_systems(Startup, setup_camera);
app.add_systems(Update, handle_input); 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(Update, manage_player_actor.after(handle_input));
app.add_systems(PostUpdate, sync_camera_to_player app.add_systems(PostUpdate, sync_camera_to_player
.after(PhysicsSet::Sync) .after(PhysicsSet::Sync)
.after(apply_input_to_player) .after(apply_input_to_player)
.before(TransformSystem::TransformPropagate)); .before(TransformSystem::TransformPropagate));
app.add_systems(Update, update_map_camera);
app.add_systems(Update, update_fov); app.add_systems(Update, update_fov);
app.add_systems(PostUpdate, apply_input_to_player app.add_systems(PostUpdate, apply_input_to_player
.after(PhysicsSet::Sync) .after(PhysicsSet::Sync)
.before(TransformSystem::TransformPropagate)); .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),
}
} }
} }
@ -72,7 +108,7 @@ pub fn sync_camera_to_player(
mut q_camera: Query<&mut Transform, (With<Camera>, Without<actor::PlayerCamera>)>, mut q_camera: Query<&mut Transform, (With<Camera>, Without<actor::PlayerCamera>)>,
q_playercam: Query<(&actor::Actor, &Transform), (With<actor::PlayerCamera>, Without<Camera>)>, q_playercam: Query<(&actor::Actor, &Transform), (With<actor::PlayerCamera>, Without<Camera>)>,
) { ) {
if q_camera.is_empty() || q_playercam.is_empty() { if settings.map_active || q_camera.is_empty() || q_playercam.is_empty() {
return; return;
} }
let mut camera_transform = q_camera.get_single_mut().unwrap(); let mut camera_transform = q_camera.get_single_mut().unwrap();
@ -90,6 +126,105 @@ 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( pub fn update_fov(
q_player: Query<&actor::ExperiencesGForce, With<actor::Player>>, q_player: Query<&actor::ExperiencesGForce, With<actor::Player>>,
mouse_input: Res<ButtonInput<MouseButton>>, mouse_input: Res<ButtonInput<MouseButton>>,
@ -117,11 +252,16 @@ pub fn update_fov(
pub fn handle_input( pub fn handle_input(
keyboard_input: Res<ButtonInput<KeyCode>>, keyboard_input: Res<ButtonInput<KeyCode>>,
mut settings: ResMut<var::Settings>, mut settings: ResMut<var::Settings>,
mut mapcam: ResMut<MapCam>,
mut ew_sfx: EventWriter<audio::PlaySfxEvent>, mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
) { ) {
if keyboard_input.just_pressed(settings.key_camera) { if keyboard_input.just_pressed(settings.key_camera) {
settings.third_person ^= true; 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) { if keyboard_input.just_pressed(settings.key_rotation_stabilizer) {
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Click)); ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Click));
settings.rotation_stabilizer_active ^= true; settings.rotation_stabilizer_active ^= true;
@ -129,20 +269,21 @@ pub fn handle_input(
} }
fn manage_player_actor( fn manage_player_actor(
mut commands: Commands,
settings: Res<var::Settings>, settings: Res<var::Settings>,
mut q_playercam: Query<&mut Visibility, With<actor::PlayerCamera>>, mut q_playercam: Query<&mut Visibility, With<actor::PlayerCamera>>,
mut q_hiddenplayer: Query<(&mut Visibility, &mut Position, &mut Rotation, &mut LinearVelocity, &mut AngularVelocity), (With<actor::Player>, Without<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>)>,
q_ride: Query<(&Transform, &Position, &Rotation, &LinearVelocity, &AngularVelocity), (With<actor::PlayerDrivesThis>, Without<actor::Player>)>, q_ride: Query<(&Transform, &Position, &Rotation, &LinearVelocity, &AngularVelocity), (With<actor::PlayerDrivesThis>, Without<actor::Player>)>,
) { ) {
for mut vis in &mut q_playercam { for mut vis in &mut q_playercam {
if settings.third_person { if settings.third_person || settings.map_active {
*vis = Visibility::Inherited; *vis = Visibility::Inherited;
} }
else { else {
*vis = Visibility::Hidden; *vis = Visibility::Hidden;
} }
} }
for (mut vis, mut pos, mut rot, mut v, mut angv) in &mut q_hiddenplayer { for (entity, mut vis, mut pos, mut rot, mut v, mut angv, mut gforce, entering) in &mut q_hiddenplayer {
// If we are riding a vehicle, place the player at the position where // If we are riding a vehicle, place the player at the position where
// it would be after exiting the vehicle. // it would be after exiting the vehicle.
// I would rather place it in the center of the vehicle, but at the time // I would rather place it in the center of the vehicle, but at the time
@ -154,6 +295,13 @@ fn manage_player_actor(
rot.0 = ride_rot.0 * DQuat::from_array([-1.0, 0.0, 0.0, 0.0]); rot.0 = ride_rot.0 * DQuat::from_array([-1.0, 0.0, 0.0, 0.0]);
*v = ride_v.clone(); *v = ride_v.clone();
*angv = ride_angv.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>();
}
} }
} }
} }
@ -179,6 +327,9 @@ pub fn apply_input_to_player(
Option<&actor::PlayerDrivesThis>, Option<&actor::PlayerDrivesThis>,
), (With<actor::PlayerCamera>, Without<Camera>)>, ), (With<actor::PlayerCamera>, Without<Camera>)>,
) { ) {
if settings.map_active {
return;
}
let dt = time.delta_seconds(); let dt = time.delta_seconds();
let mut play_thruster_sound = false; let mut play_thruster_sound = false;
let mut axis_input: DVec3 = DVec3::ZERO; let mut axis_input: DVec3 = DVec3::ZERO;
@ -389,6 +540,39 @@ 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 // Find the closest world object that the player is looking at
#[inline] #[inline]
pub fn find_closest_target<TargetSpecifier>( pub fn find_closest_target<TargetSpecifier>(
@ -406,10 +590,10 @@ pub fn find_closest_target<TargetSpecifier>(
// not on the player mesh but on the camera, which doesn't have a position. // 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( let (angular_diameter, angle, distance) = calc_angular_diameter_known_target_vector(
trans, camera_transform, &target_vector); trans, camera_transform, &target_vector);
let distance_to_surface = distance - trans.scale.x;
if angle <= angular_diameter.clamp(0.001, PI) { if angle <= angular_diameter.clamp(0.001, PI) {
// It's in the field of view! // It's in the field of view!
//commands.entity(entity).insert(IsTargeted); //commands.entity(entity).insert(IsTargeted);
let distance_to_surface = distance - trans.scale.x;
if distance_to_surface < closest_distance { if distance_to_surface < closest_distance {
closest_distance = distance_to_surface; closest_distance = distance_to_surface;
closest_entity = Some(entity); closest_entity = Some(entity);

View file

@ -778,6 +778,7 @@ pub fn handle_chat_scripts(
mut q_playercam: Query<(&mut Position, &mut LinearVelocity), With<actor::PlayerCamera>>, mut q_playercam: Query<(&mut Position, &mut LinearVelocity), With<actor::PlayerCamera>>,
mut ew_sfx: EventWriter<audio::PlaySfxEvent>, mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
mut ew_effect: EventWriter<effects::SpawnEffectEvent>, mut ew_effect: EventWriter<effects::SpawnEffectEvent>,
id2pos: Res<actor::Id2Pos>,
) { ) {
for script in er_chatscript.read() { for script in er_chatscript.read() {
// Parse the script string // Parse the script string
@ -834,19 +835,20 @@ pub fn handle_chat_scripts(
} }
else { else {
if let Ok((mut pos, mut v)) = q_playercam.get_single_mut() { if let Ok((mut pos, mut v)) = q_playercam.get_single_mut() {
if param1 == "oscillation".to_string() { let busstop = match param1 {
*pos = Position(DVec3::new(-184968e3, 149410e3, -134273e3)); "serenity" => Some("busstop"),
v.0 = DVec3::ZERO; "oscillation" => Some("busstop2"),
} "metisprime" => Some("busstop3"),
else if param1 == "metisprime".to_string() { _ => None
*pos = Position(DVec3::new(27643e3, -47e3, -124434e3)); };
v.0 = DVec3::ZERO; if let Some(station) = busstop {
} if let Some(target) = id2pos.0.get(&station.to_string()) {
else if param1 == "serenity".to_string() { pos.0 = *target + DVec3::new(0.0, -1000.0, 0.0);
*pos = Position(DVec3::new(-121095e3, 582e3, -190816e3)); v.0 = DVec3::ZERO;
v.0 = DVec3::ZERO; } else {
} error!("Could not determine position of actor with ID: '{}'", station);
else { }
} else {
error!("Invalid destination for cryotrip chat script: '{}'", param1); error!("Invalid destination for cryotrip chat script: '{}'", param1);
} }
} }

View file

@ -3,11 +3,10 @@ extern crate regex;
use bevy::prelude::*; use bevy::prelude::*;
use bevy_xpbd_3d::prelude::*; use bevy_xpbd_3d::prelude::*;
use bevy::math::DVec3; use bevy::math::DVec3;
use crate::{actor, chat, hud, nature, world}; use crate::{actor, camera, chat, hud, nature, shading, world};
use regex::Regex; use regex::Regex;
use std::f32::consts::PI; use std::f32::consts::PI;
use std::f64::consts::PI as PI64; use std::f64::consts::PI as PI64;
use std::collections::HashMap;
pub struct CommandsPlugin; pub struct CommandsPlugin;
impl Plugin for CommandsPlugin { impl Plugin for CommandsPlugin {
@ -39,12 +38,13 @@ struct ParserState {
// Actor fields // Actor fields
id: String, id: String,
pos: DVec3, pos: DVec3,
model: String, relative_to: Option<String>,
model: Option<String>,
model_scale: f32, model_scale: f32,
rotation: Quat, rotation: Quat,
velocity: DVec3, velocity: DVec3,
angular_momentum: DVec3, angular_momentum: DVec3,
pronoun: String, pronoun: Option<String>,
is_sphere: bool, is_sphere: bool,
is_player: bool, is_player: bool,
is_lifeform: bool, is_lifeform: bool,
@ -53,7 +53,9 @@ struct ParserState {
is_vehicle: bool, is_vehicle: bool,
is_clickable: bool, is_clickable: bool,
is_targeted_on_startup: bool, is_targeted_on_startup: bool,
is_sun: bool,
has_physics: bool, has_physics: bool,
has_ring: bool,
wants_maxrotation: Option<f64>, wants_maxrotation: Option<f64>,
wants_maxvelocity: Option<f64>, wants_maxvelocity: Option<f64>,
collider_is_mesh: bool, collider_is_mesh: bool,
@ -72,6 +74,7 @@ struct ParserState {
light_brightness: f32, light_brightness: f32,
light_color: Option<Color>, light_color: Option<Color>,
ar_model: Option<String>, ar_model: Option<String>,
show_only_in_map_at_distance: Option<(f64, String)>,
} }
impl Default for ParserState { impl Default for ParserState {
fn default() -> Self { fn default() -> Self {
@ -84,12 +87,13 @@ impl Default for ParserState {
id: "".to_string(), id: "".to_string(),
pos: DVec3::new(0.0, 0.0, 0.0), pos: DVec3::new(0.0, 0.0, 0.0),
model: "".to_string(), relative_to: None,
model: None,
model_scale: 1.0, model_scale: 1.0,
rotation: Quat::IDENTITY, rotation: Quat::IDENTITY,
velocity: DVec3::splat(0.0), velocity: DVec3::splat(0.0),
angular_momentum: DVec3::new(0.03, 0.3, 0.09), angular_momentum: DVec3::new(0.03, 0.3, 0.09),
pronoun: "they/them".to_string(), pronoun: None,
is_sphere: false, is_sphere: false,
is_player: false, is_player: false,
is_lifeform: false, is_lifeform: false,
@ -98,7 +102,9 @@ impl Default for ParserState {
is_vehicle: false, is_vehicle: false,
is_clickable: true, is_clickable: true,
is_targeted_on_startup: false, is_targeted_on_startup: false,
is_sun: false,
has_physics: true, has_physics: true,
has_ring: false,
wants_maxrotation: None, wants_maxrotation: None,
wants_maxvelocity: None, wants_maxvelocity: None,
collider_is_mesh: false, collider_is_mesh: false,
@ -117,6 +123,7 @@ impl Default for ParserState {
light_brightness: 0.0, light_brightness: 0.0,
light_color: None, light_color: None,
ar_model: None, ar_model: None,
show_only_in_map_at_distance: None,
} }
} }
} }
@ -131,7 +138,6 @@ pub fn load_defs(
let mut state = ParserState::default(); let mut state = ParserState::default();
let mut command; let mut command;
let mut parameters; let mut parameters;
let mut id2pos: HashMap<String, DVec3> = HashMap::new();
let mut line_nr = -1; let mut line_nr = -1;
while let Some(line) = lines.next() { while let Some(line) = lines.next() {
@ -177,7 +183,21 @@ pub fn load_defs(
ew_spawn.send(SpawnEvent(state)); ew_spawn.send(SpawnEvent(state));
state = ParserState::default(); state = ParserState::default();
state.class = DefClass::Actor; state.class = DefClass::Actor;
state.model = model.to_string(); 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;
if let (Ok(x_float), Ok(y_float), Ok(z_float)) = if let (Ok(x_float), Ok(y_float), Ok(z_float)) =
(x.parse::<f64>(), y.parse::<f64>(), z.parse::<f64>()) { (x.parse::<f64>(), y.parse::<f64>(), z.parse::<f64>()) {
state.pos = DVec3::new(x_float, y_float, z_float); state.pos = DVec3::new(x_float, y_float, z_float);
@ -189,18 +209,7 @@ pub fn load_defs(
} }
} }
["relativeto", id] => { ["relativeto", id] => {
// NOTE: call this command before "id", otherwise actors that state.relative_to = Some(id.to_string());
// 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] => { ["orbit", radius_str, phase_str] => {
if let (Ok(r), Ok(phase)) = (radius_str.parse::<f64>(), phase_str.parse::<f64>()) { if let (Ok(r), Ok(phase)) = (radius_str.parse::<f64>(), phase_str.parse::<f64>()) {
@ -221,7 +230,6 @@ pub fn load_defs(
} }
["id", id] => { ["id", id] => {
state.id = id.to_string(); state.id = id.to_string();
id2pos.insert(state.id.clone(), state.pos.clone());
} }
["alive", "yes"] => { ["alive", "yes"] => {
state.is_alive = true; state.is_alive = true;
@ -237,6 +245,13 @@ pub fn load_defs(
["moon", "yes"] => { ["moon", "yes"] => {
state.model_scale *= 3.0; state.model_scale *= 3.0;
} }
["sun", "yes"] => {
state.is_sun = true;
state.model_scale *= 5.0;
}
["ring", "yes"] => {
state.has_ring = true;
}
["oxygen", amount] => { ["oxygen", amount] => {
if let Ok(amount) = amount.parse::<f32>() { if let Ok(amount) = amount.parse::<f32>() {
state.is_lifeform = true; state.is_lifeform = true;
@ -249,7 +264,7 @@ pub fn load_defs(
} }
} }
["pronoun", pronoun] => { ["pronoun", pronoun] => {
state.pronoun = pronoun.to_string(); state.pronoun = Some(pronoun.to_string());
} }
["chatid", chat] => { ["chatid", chat] => {
state.chat = chat.to_string(); state.chat = chat.to_string();
@ -428,6 +443,15 @@ pub fn load_defs(
["targeted", "yes"] => { ["targeted", "yes"] => {
state.is_targeted_on_startup = true; 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(",")); error!("No match for [{}]", parts.join(","));
} }
@ -442,10 +466,26 @@ fn spawn_entities(
asset_server: Res<AssetServer>, asset_server: Res<AssetServer>,
mut meshes: ResMut<Assets<Mesh>>, mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>, mut materials: ResMut<Assets<StandardMaterial>>,
mut materials_jupiter: ResMut<Assets<shading::JupitersRing>>,
mut id2pos: ResMut<actor::Id2Pos>,
) { ) {
for state_wrapper in er_spawn.read() { for state_wrapper in er_spawn.read() {
let state = &state_wrapper.0; let state = &state_wrapper.0;
if state.class == DefClass::Actor { 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 actor_entity;
{ {
let mut actor = commands.spawn_empty(); let mut actor = commands.spawn_empty();
@ -458,13 +498,17 @@ fn spawn_entities(
actor.insert(SleepingDisabled); actor.insert(SleepingDisabled);
actor.insert(world::DespawnOnPlayerDeath); actor.insert(world::DespawnOnPlayerDeath);
actor.insert(actor::HitPoints::default()); actor.insert(actor::HitPoints::default());
actor.insert(Position::from(state.pos)); actor.insert(Position::from(relative_pos));
actor.insert(Rotation::from(state.rotation)); actor.insert(Rotation::from(state.rotation));
if state.is_sphere { if state.is_sphere {
let sphere_texture_handle: Handle<Image> = asset_server.load(format!("textures/{}.jpg", state.model)); let sphere_texture_handle = if let Some(model) = &state.model {
Some(asset_server.load(format!("textures/{}.jpg", model)))
} else {
None
};
let sphere_handle = meshes.add(Sphere::new(1.0).mesh().uv(128, 128)); let sphere_handle = meshes.add(Sphere::new(1.0).mesh().uv(128, 128));
let sphere_material_handle = materials.add(StandardMaterial { let sphere_material_handle = materials.add(StandardMaterial {
base_color_texture: Some(sphere_texture_handle.clone()), base_color_texture: sphere_texture_handle,
perceptual_roughness: 1.0, perceptual_roughness: 1.0,
metallic: 0.0, metallic: 0.0,
..default() ..default()
@ -478,13 +522,13 @@ fn spawn_entities(
}, },
..default() ..default()
}); });
} else { } else if let Some(model) = &state.model {
actor.insert(SceneBundle { actor.insert(SceneBundle {
transform: Transform { transform: Transform {
scale: Vec3::splat(state.model_scale), scale: Vec3::splat(state.model_scale),
..default() ..default()
}, },
scene: asset_server.load(world::asset_name_to_path(state.model.as_str())), scene: asset_server.load(world::asset_name_to_path(model.as_str())),
..default() ..default()
}); });
} }
@ -524,9 +568,23 @@ fn spawn_entities(
actor.insert(actor::Player); actor.insert(actor::Player);
actor.insert(actor::PlayerCamera); 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 { if state.is_targeted_on_startup {
actor.insert(hud::IsTargeted); 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 { if state.is_player || state.is_vehicle {
// used to apply mouse movement to actor rotation // used to apply mouse movement to actor rotation
actor.insert(ExternalTorque::ZERO.with_persistence(false)); actor.insert(ExternalTorque::ZERO.with_persistence(false));
@ -544,6 +602,7 @@ fn spawn_entities(
if state.is_clickable { if state.is_clickable {
actor.insert(hud::IsClickable { actor.insert(hud::IsClickable {
name: state.name.clone(), name: state.name.clone(),
pronoun: state.pronoun.clone(),
..default() ..default()
}); });
} }
@ -565,12 +624,16 @@ fn spawn_entities(
..default() ..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() { if !state.chat.is_empty() {
actor.insert(chat::Talker { actor.insert(chat::Talker {
actor_id: state.id.clone(), actor_id: state.id.clone(),
chat_name: state.chat.clone(), chat_name: state.chat.clone(),
name: state.name.clone(), name: state.name.clone(),
pronoun: Some(state.pronoun.clone()), pronoun: state.pronoun.clone(),
talking_speed: 1.0, talking_speed: 1.0,
}); });
} }
@ -612,6 +675,25 @@ 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())),
));
}
} }
} }
} }

View file

@ -3,10 +3,11 @@ C: Impossibly instant stopping [CHEAT]
Shift+V/B: Same as V/B, but a thousand times faster [CHEAT] Shift+V/B: Same as V/B, but a thousand times faster [CHEAT]
V/B: Impossible acceleration forward/backward [CHEAT] V/B: Impossible acceleration forward/backward [CHEAT]
G: Toggle god mode / cheats [CHEAT] G: Toggle god mode / cheats [CHEAT]
M: Toggle sound effects M: Toggle map
T: Toggle music
Y: Toggle rotation stabilizer Y: Toggle rotation stabilizer
F: Toggle 3rd person view F: Toggle 3rd person view
F3: Toggle sound effects
F4: Toggle music
F11: Toggle fullscreen F11: Toggle fullscreen
Tab: Toggle HUD + Augmented Reality Tab: Toggle HUD + Augmented Reality
Right click: Zoom [AUGMENTED REALITY ONLY] Right click: Zoom [AUGMENTED REALITY ONLY]

File diff suppressed because it is too large Load diff

View file

@ -1,16 +1,128 @@
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 actor 0 0 0 jupiter
relativeto sol
orbit 778479000e3 0.5
id jupiter id jupiter
name Jupiter name Jupiter
scale 71492e3 scale 71492e3
sphere yes sphere yes
ring yes
physics off physics off
rotationx -0.50 rotationx -0.50
rotationz -0.28 rotationz -0.28
angularmomentum 30 30 30 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 actor 0 593051 0 suit
relativeto jupiter relativeto jupiter
orbit 226000e3 0.66 orbit 224000e3 0.66
player yes player yes
id player id player
scale 2 scale 2
@ -37,10 +149,12 @@ actor 10 -30 20 MeteorAceGT
actor 0 0 0 io actor 0 0 0 io
name Io name Io
id io
relativeto jupiter relativeto jupiter
orbit 421700e3 0.65 orbit 421700e3 0.65
scale 1822e3 scale 1822e3
rotationy -0.40 rotationy -0.40
rotationx -0.50
angularmomentum 0 0.0001 0 angularmomentum 0 0.0001 0
sphere yes sphere yes
moon yes moon yes
@ -48,10 +162,12 @@ actor 0 0 0 io
actor 0 0 0 europa actor 0 0 0 europa
name Europa name Europa
id europa
relativeto jupiter relativeto jupiter
orbit 670900e3 0.35 orbit 670900e3 0.35
scale 1561e3 scale 1561e3
rotationy 0.20 rotationy 0.20
rotationx -0.50
angularmomentum 0 0.0001 0 angularmomentum 0 0.0001 0
sphere yes sphere yes
moon yes moon yes
@ -59,10 +175,12 @@ actor 0 0 0 europa
actor 0 0 0 ganymede actor 0 0 0 ganymede
name Ganymede name Ganymede
id ganymede
relativeto jupiter relativeto jupiter
orbit 1070400e3 0.93 orbit 1070400e3 0.93
scale 2634e3 scale 2634e3
rotationy -0.40 rotationy -0.40
rotationx -0.50
angularmomentum 0 0.0001 0 angularmomentum 0 0.0001 0
sphere yes sphere yes
moon yes moon yes
@ -70,10 +188,12 @@ actor 0 0 0 ganymede
actor 0 0 0 callisto actor 0 0 0 callisto
name Callisto name Callisto
id callisto
relativeto jupiter relativeto jupiter
orbit 1882700e3 0.45 orbit 1882700e3 0.45
scale 2410e3 scale 2410e3
rotationy -0.40 rotationy -0.40
rotationx -0.50
angularmomentum 0 0.0001 0 angularmomentum 0 0.0001 0
sphere yes sphere yes
moon yes moon yes
@ -87,6 +207,30 @@ actor 0 0 0 moonlet
scale 50e3 scale 50e3
angularmomentum 0 0.025 0 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 actor 3000 0 0 moonlet
name Moonlet name Moonlet
collider mesh collider mesh
@ -95,15 +239,16 @@ actor 3000 0 0 moonlet
scale 500 scale 500
angularmomentum 0 0.015 0 angularmomentum 0 0.015 0
actor 220 -2400 410 asteroid_lum actor -8200 -4400 -8100 asteroid_lum
relativeto player relativeto player
name Lum name Lum
id Lum id Lum
collider mesh collider mesh
density 10000000000 density 10000000000
scale 300 scale 300
rotationy 0.82
angularmomentum 0 0.015 0 angularmomentum 0 0.015 0
actor -80 0 0 lightorb actor 70 30 30 lightorb
relativeto Lum relativeto Lum
name "Light Orb" name "Light Orb"
scale 0.3 scale 0.3
@ -198,6 +343,7 @@ actor -3300 10 0 pizzeria
rotationy -0.7 rotationy -0.7
scale 3 scale 3
chatid SubduedClippy chatid SubduedClippy
pronoun it
actor -45 -4 -4 suit actor -45 -4 -4 suit
relativeto pizzeria relativeto pizzeria
@ -233,11 +379,12 @@ actor 60 -15 -40 suit
actor -300 0 40 suit actor -300 0 40 suit
relativeto player relativeto player
id Drifter id Drifter
name "Drifter" name "梓涵"
chatid Drifter chatid Drifter
oxygen 0.08 oxygen 0.08
scale 2 scale 2
collider handcrafted collider handcrafted
pronoun she
actor 100 -18000 2000 "orb_busstop" actor 100 -18000 2000 "orb_busstop"
relativeto player relativeto player
@ -258,6 +405,7 @@ actor 100 -18000 2000 "orb_busstop"
rotationy -0.5 rotationy -0.5
scale 3 scale 3
chatid ClippyTransSerenity chatid ClippyTransSerenity
pronoun it
actor 40 10 40 "orb_busstop" actor 40 10 40 "orb_busstop"
name "Light Orb" name "Light Orb"
relativeto busstopclippy relativeto busstopclippy
@ -283,6 +431,7 @@ actor 100 -18000 2000 "orb_busstop"
scale 2 scale 2
collider capsule 1 0.5 collider capsule 1 0.5
chatid NPCinCryoStasis chatid NPCinCryoStasis
pronoun he
actor -184971e3 149410e3 -134273e3 "orb_busstop" actor -184971e3 149410e3 -134273e3 "orb_busstop"
relativeto jupiter relativeto jupiter
@ -303,6 +452,7 @@ actor -184971e3 149410e3 -134273e3 "orb_busstop"
rotationy -0.5 rotationy -0.5
scale 3 scale 3
chatid ClippyTransOscillation chatid ClippyTransOscillation
pronoun it
actor 40 10 40 "orb_busstop" actor 40 10 40 "orb_busstop"
name "Light Orb" name "Light Orb"
relativeto busstopclippy2 relativeto busstopclippy2
@ -339,6 +489,7 @@ actor 27643e3 -44e3 -124434e3 "orb_busstop"
rotationy -0.5 rotationy -0.5
scale 3 scale 3
chatid ClippyTransMetis chatid ClippyTransMetis
pronoun it
actor 40 10 40 "orb_busstop" actor 40 10 40 "orb_busstop"
name "Light Orb" name "Light Orb"
relativeto busstopclippy3 relativeto busstopclippy3

View file

@ -25,11 +25,13 @@ impl Plugin for HudPlugin {
app.add_systems(Startup, setup); app.add_systems(Startup, setup);
app.add_systems(Update, ( app.add_systems(Update, (
update_hud, update_hud,
update_ar_overlays,
handle_input, handle_input,
handle_target_event, handle_target_event,
)); ));
app.add_systems(PostUpdate, ( app.add_systems(PostUpdate, (
update_ar_overlays
.after(world::position_to_transform)
.in_set(sync::SyncSet::PositionToTransform),
update_target_selectagon update_target_selectagon
.after(PhysicsSet::Sync) .after(PhysicsSet::Sync)
.after(camera::apply_input_to_player) .after(camera::apply_input_to_player)
@ -109,10 +111,12 @@ impl Message {
#[derive(Component)] #[derive(Component)]
pub struct IsClickable { pub struct IsClickable {
pub name: Option<String>, pub name: Option<String>,
pub pronoun: Option<String>,
pub distance: Option<f64>, pub distance: Option<f64>,
} }
impl Default for IsClickable { fn default() -> Self { Self { impl Default for IsClickable { fn default() -> Self { Self {
name: None, name: None,
pronoun: None,
distance: None, distance: None,
}}} }}}
@ -204,9 +208,10 @@ fn setup(
}; };
// Add Statistics HUD // Add Statistics HUD
let version = &settings.version;
let mut bundle_fps = TextBundle::from_sections([ let mut bundle_fps = TextBundle::from_sections([
TextSection::new("", style.clone()), TextSection::new("", style.clone()),
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()),
TextSection::new("", style.clone()), TextSection::new("", style.clone()),
@ -486,7 +491,12 @@ fn update_hud(
}; };
let speed_readable = nature::readable_distance(speed); let speed_readable = nature::readable_distance(speed);
let target_name = clickable.name.clone().unwrap_or("Unnamed".to_string()); let target_name = clickable.name.clone().unwrap_or("Unnamed".to_string());
text.sections[15].value = format!("\n\nTarget: {target_name}\nDistance: {distance}\nΔv {speed_readable}/s"); 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");
} }
else { else {
text.sections[15].value = "".to_string(); text.sections[15].value = "".to_string();
@ -706,7 +716,7 @@ fn update_target_selectagon(
} }
selectagon_trans.translation = target_trans.translation; selectagon_trans.translation = target_trans.translation;
selectagon_trans.scale = target_trans.scale; selectagon_trans.scale = target_trans.scale;
selectagon_trans.rotation = Quat::from_rotation_arc(Vec3::Z, (-selectagon_trans.translation).normalize()); selectagon_trans.look_at(camera_trans.translation, Vec3::X);
// Enlarge Selectagon to a minimum angular diameter // Enlarge Selectagon to a minimum angular diameter
let (angular_diameter, _, _) = camera::calc_angular_diameter( let (angular_diameter, _, _) = camera::calc_angular_diameter(

View file

@ -12,6 +12,10 @@ pub const PARSEC2METER: f64 = 3.0857e16;
pub const DIST_JUPTER_SUN: f64 = 778479.0e6; pub const DIST_JUPTER_SUN: f64 = 778479.0e6;
pub const EARTH_GRAVITY: f32 = 9.81; 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) // 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"); pub const STARS: &[(f32, f32, f32, f32, f32, f32, &str)] = &include!("data/stars.in");

View file

@ -18,6 +18,7 @@ pub const DEFAULT_CHAT_SPEED: f32 = 10.0;
pub struct Settings { pub struct Settings {
pub dev_mode: bool, pub dev_mode: bool,
pub god_mode: bool, pub god_mode: bool,
pub version: String,
pub mute_sfx: bool, pub mute_sfx: bool,
pub mute_music: bool, pub mute_music: bool,
pub volume_sfx: u8, pub volume_sfx: u8,
@ -40,11 +41,17 @@ pub struct Settings {
pub hud_color_choices: Color, pub hud_color_choices: Color,
pub chat_speed: f32, pub chat_speed: f32,
pub hud_active: bool, pub hud_active: bool,
pub map_active: bool,
pub is_zooming: bool, pub is_zooming: bool,
pub third_person: bool, pub third_person: bool,
pub rotation_stabilizer_active: bool, pub rotation_stabilizer_active: bool,
pub key_selectobject: MouseButton, pub key_selectobject: MouseButton,
pub key_zoom: 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_togglehud: KeyCode,
pub key_exit: KeyCode, pub key_exit: KeyCode,
pub key_restart: KeyCode, pub key_restart: KeyCode,
@ -69,6 +76,8 @@ pub struct Settings {
pub key_mouseright: KeyCode, pub key_mouseright: KeyCode,
pub key_rotateleft: KeyCode, pub key_rotateleft: KeyCode,
pub key_rotateright: KeyCode, pub key_rotateright: KeyCode,
pub key_toggle_sfx: KeyCode,
pub key_toggle_music: KeyCode,
pub key_reply1: KeyCode, pub key_reply1: KeyCode,
pub key_reply2: KeyCode, pub key_reply2: KeyCode,
pub key_reply3: KeyCode, pub key_reply3: KeyCode,
@ -110,15 +119,21 @@ impl Default for Settings {
default_mute_music = false; default_mute_music = false;
dev_mode = false; dev_mode = false;
} }
let version = if let Some(version) = option_env!("CARGO_PKG_VERSION") {
version.to_string()
} else {
"13.37".to_string()
};
Settings { Settings {
dev_mode, dev_mode,
god_mode: false, god_mode: false,
version,
mute_sfx: default_mute_sfx, mute_sfx: default_mute_sfx,
mute_music: default_mute_music, mute_music: default_mute_music,
volume_sfx: 100, volume_sfx: 100,
volume_music: 100, volume_music: 100,
mouse_sensitivity: 0.7, mouse_sensitivity: 0.4,
fov: 50.0, fov: 50.0,
fov_highspeed: 25.0, fov_highspeed: 25.0,
zoom_fov: 15.0, zoom_fov: 15.0,
@ -136,11 +151,17 @@ impl Default for Settings {
hud_color_choices: Color::rgb(0.45, 0.45, 0.45), hud_color_choices: Color::rgb(0.45, 0.45, 0.45),
chat_speed: DEFAULT_CHAT_SPEED * if dev_mode { 2.5 } else { 1.0 }, chat_speed: DEFAULT_CHAT_SPEED * if dev_mode { 2.5 } else { 1.0 },
hud_active: false, hud_active: false,
map_active: false,
is_zooming: false, is_zooming: false,
third_person: false, third_person: false,
rotation_stabilizer_active: true, rotation_stabilizer_active: true,
key_selectobject: MouseButton::Left, key_selectobject: MouseButton::Left,
key_zoom: MouseButton::Right, 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_togglehud: KeyCode::Tab,
key_exit: KeyCode::Escape, key_exit: KeyCode::Escape,
key_restart: KeyCode::F7, key_restart: KeyCode::F7,
@ -165,6 +186,8 @@ impl Default for Settings {
key_mouseright: KeyCode::KeyL, key_mouseright: KeyCode::KeyL,
key_rotateleft: KeyCode::KeyU, key_rotateleft: KeyCode::KeyU,
key_rotateright: KeyCode::KeyO, key_rotateright: KeyCode::KeyO,
key_toggle_sfx: KeyCode::F3,
key_toggle_music: KeyCode::F4,
key_reply1: KeyCode::Digit1, key_reply1: KeyCode::Digit1,
key_reply2: KeyCode::Digit2, key_reply2: KeyCode::Digit2,
key_reply3: KeyCode::Digit3, key_reply3: KeyCode::Digit3,

View file

@ -15,6 +15,7 @@ const RING_THICKNESS: f64 = 8.0e6;
const STARS_MAX_MAGNITUDE: f32 = 5.5; // max 7.0, see generate_starchart.py const STARS_MAX_MAGNITUDE: f32 = 5.5; // max 7.0, see generate_starchart.py
const CENTER_WORLD_ON_PLAYER: bool = true; const CENTER_WORLD_ON_PLAYER: bool = true;
const SKYBOX: bool = false;
const ASTEROID_SPAWN_STEP: f64 = 500.0; const ASTEROID_SPAWN_STEP: f64 = 500.0;
const ASTEROID_VIEW_RADIUS: f64 = 3000.0; const ASTEROID_VIEW_RADIUS: f64 = 3000.0;
@ -38,6 +39,7 @@ pub fn asset_name_to_path(name: &str) -> &'static str {
"pizzeria" => "models/pizzeria2.glb#Scene0", "pizzeria" => "models/pizzeria2.glb#Scene0",
"pizzasign" => "models/pizzasign.glb#Scene0", "pizzasign" => "models/pizzasign.glb#Scene0",
"selectagon" => "models/selectagon.glb#Scene0", "selectagon" => "models/selectagon.glb#Scene0",
"orbitring" => "models/orbitring.glb#Scene0",
"clippy" => "models/clippy.glb#Scene0", "clippy" => "models/clippy.glb#Scene0",
"clippy_ar" => "models/clippy_ar.glb#Scene0", "clippy_ar" => "models/clippy_ar.glb#Scene0",
"whale" => "models/whale.glb#Scene0", "whale" => "models/whale.glb#Scene0",
@ -57,7 +59,6 @@ impl Plugin for WorldPlugin {
app.insert_resource(Gravity(DVec3::splat(0.0))); app.insert_resource(Gravity(DVec3::splat(0.0)));
app.insert_resource(AsteroidUpdateTimer( app.insert_resource(AsteroidUpdateTimer(
Timer::from_seconds(ASTEROID_UPDATE_INTERVAL, TimerMode::Repeating))); Timer::from_seconds(ASTEROID_UPDATE_INTERVAL, TimerMode::Repeating)));
app.insert_resource(AsteroidDatabase(Vec::new()));
app.insert_resource(ActiveAsteroids(HashMap::new())); app.insert_resource(ActiveAsteroids(HashMap::new()));
app.add_event::<DespawnEvent>(); app.add_event::<DespawnEvent>();
@ -76,15 +77,14 @@ impl Plugin for WorldPlugin {
} }
#[derive(Resource)] struct AsteroidUpdateTimer(Timer); #[derive(Resource)] struct AsteroidUpdateTimer(Timer);
#[derive(Resource)] struct AsteroidDatabase(Vec<AsteroidData>); #[derive(Resource)] pub struct ActiveAsteroids(pub HashMap<I64Vec3, AsteroidData>);
#[derive(Resource)] struct ActiveAsteroids(HashMap<I64Vec3, AsteroidData>);
#[derive(Resource)] struct AsteroidModel1(Handle<Scene>); #[derive(Resource)] struct AsteroidModel1(Handle<Scene>);
#[derive(Resource)] struct AsteroidModel2(Handle<Scene>); #[derive(Resource)] struct AsteroidModel2(Handle<Scene>);
#[derive(Component)] struct Asteroid; #[derive(Component)] struct Asteroid;
#[derive(Component)] pub struct DespawnOnPlayerDeath; #[derive(Component)] pub struct DespawnOnPlayerDeath;
struct AsteroidData { pub struct AsteroidData {
entity: Entity, entity: Entity,
//viewdistance: f64, //viewdistance: f64,
} }
@ -103,7 +103,6 @@ pub fn setup(
mut commands: Commands, mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>, mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>, mut materials: ResMut<Assets<StandardMaterial>>,
mut materials_jupiter: ResMut<Assets<shading::JupitersRing>>,
mut materials_skybox: ResMut<Assets<shading::SkyBox>>, mut materials_skybox: ResMut<Assets<shading::SkyBox>>,
asset_server: Res<AssetServer>, asset_server: Res<AssetServer>,
) { ) {
@ -112,113 +111,90 @@ pub fn setup(
commands.insert_resource(AsteroidModel2(asset_server.load(ASSET_ASTEROID2))); commands.insert_resource(AsteroidModel2(asset_server.load(ASSET_ASTEROID2)));
// Generate starmap // Generate starmap
let sphere_handle = meshes.add(Circle::new(1.0)); let sphere_handle = meshes.add(Sphere::new(1.0).mesh().uv(16, 16));
let mut starcount = 0; let mut starcount = 0;
for star in nature::STARS { for (index, star) in nature::STARS.iter().enumerate() {
let mag = star.3; let (x, y, z, mag, _absmag, color_index, name) = *star;
if mag > STARS_MAX_MAGNITUDE { if mag > STARS_MAX_MAGNITUDE {
continue; continue;
} }
let is_sun = mag < -20.0;
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 mag = mag.min(6.0); let mag = mag.min(6.0);
let scale_color = {|color: f32|
if is_sun {
color * 13.0f32
} else {
color * (0.0659663 * mag * mag - 1.09862 * mag + 4.3)
}
};
let scale_size = {|mag: f32| let scale_size = {|mag: f32|
if is_sun { scale_factor * (0.230299 * mag * mag - 3.09013 * mag + 15.1782)
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 scale = scale_size(mag);
let scale_color = {|color: f32|
1.2 * 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 star_color_handle = materials.add(StandardMaterial { let star_color_handle = materials.add(StandardMaterial {
base_color: Color::rgb(scale_color(r), scale_color(g), scale_color(b)), base_color: Color::rgb(scale_color(r), scale_color(g), scale_color(b)),
unlit: true, unlit: true,
..default() ..default()
}); });
let mesh_distance = 1e9; let name = if name.is_empty() {
let starchart_distance = if is_sun { format!("Uncharted Star #{index}")
Some(nature::DIST_JUPTER_SUN) } else {
} else if star.5 >= 100000.0 { name.to_string()
};
let distance = if pos.length() > 1e21 {
None None
} else { } else {
Some(nature::PARSEC2METER * star.5 as f64) Some(pos.length())
}; };
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(( commands.spawn((
Star, Star,
hud::IsClickable { hud::IsClickable {
name: Some(name), name: Some(name),
distance: starchart_distance, distance,
..default()
}, },
PbrBundle { PbrBundle {
mesh: sphere_handle.clone(), mesh: sphere_handle.clone(),
material: star_color_handle, material: star_color_handle,
transform: Transform { transform: Transform {
translation, translation: pos_render.as_vec3(),
rotation, scale: Vec3::splat(scale as f32),
scale: Vec3::splat(scale_size(mag)), ..default()
}, },
..default() ..default()
} },
)); ));
starcount += 1; starcount += 1;
} }
info!("Generated {starcount} stars"); info!("Generated {starcount} stars");
// Add shaded skybox // Add shaded skybox
//let mut mesh = Mesh::from(Sphere::new(1e9).mesh().uv(50, 50)); if SKYBOX {
let mut mesh = Mesh::from(Sphere::new(1e10).mesh().uv(5, 5)); let mut mesh = Mesh::from(Sphere::new(1e10).mesh().uv(5, 5));
//let mut mesh = Mesh::from(Cuboid::from_size(Vec3::splat(2e10))); //let mut mesh = Mesh::from(Cuboid::from_size(Vec3::splat(2e10)));
if let Some(Indices::U32(indices)) = mesh.indices_mut() { if let Some(Indices::U32(indices)) = mesh.indices_mut() {
// Reverse the order of each triangle to avoid backface culling // Reverse the order of each triangle to avoid backface culling
for slice in indices.chunks_mut(3) { for slice in indices.chunks_mut(3) {
slice.reverse(); slice.reverse();
}
} }
} commands.spawn(MaterialMeshBundle {
commands.spawn((
MaterialMeshBundle {
mesh: meshes.add(mesh), mesh: meshes.add(mesh),
material: materials_skybox.add(shading::SkyBox {}), material: materials_skybox.add(shading::SkyBox {}),
transform: Transform::from_translation(Vec3::new(0.0, 0.0, 0.0)), transform: Transform::from_translation(Vec3::new(0.0, 0.0, 0.0)),
..default() ..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( fn spawn_despawn_asteroids(
@ -232,16 +208,24 @@ fn spawn_despawn_asteroids(
asteroid2_handle: Res<AsteroidModel2>, asteroid2_handle: Res<AsteroidModel2>,
mut q_asteroid: Query<(Entity, &SceneInstance), With<Asteroid>>, mut q_asteroid: Query<(Entity, &SceneInstance), With<Asteroid>>,
mut last_player_cell: Local<I64Vec3>, mut last_player_cell: Local<I64Vec3>,
id2pos: Res<actor::Id2Pos>,
) { ) {
if !timer.0.tick(time.delta()).just_finished() || q_player.is_empty() { if !timer.0.tick(time.delta()).just_finished() || q_player.is_empty() {
//if q_player.is_empty() { //if q_player.is_empty() {
return; 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 player = q_player.get_single().unwrap();
let fromjupiter = player.0 - jupiter_pos;
let player_cell = I64Vec3::new( let player_cell = I64Vec3::new(
(player.x / ASTEROID_SPAWN_STEP).round() as i64, (fromjupiter.x / ASTEROID_SPAWN_STEP).round() as i64,
(player.y / ASTEROID_SPAWN_STEP).round() as i64, (fromjupiter.y / ASTEROID_SPAWN_STEP).round() as i64,
(player.z / ASTEROID_SPAWN_STEP).round() as i64, (fromjupiter.z / ASTEROID_SPAWN_STEP).round() as i64,
); );
if *last_player_cell == player_cell { if *last_player_cell == player_cell {
return; return;
@ -285,13 +269,13 @@ fn spawn_despawn_asteroids(
} }
// Density based on the radius alone // Density based on the radius alone
let radius_plane = (player.x * player.x + player.z * player.z).sqrt(); let radius_plane = (fromjupiter.x * fromjupiter.x + fromjupiter.z * fromjupiter.z).sqrt();
let density_r = nature::ring_density((radius_plane / 1e6) as f32); let density_r = nature::ring_density((radius_plane / 1e6) as f32);
if density_r < 0.001 { if density_r < 0.001 {
return; return;
} }
// Density based on radius and the vertical distance to the ring // Density based on radius and the vertical distance to the ring
let normalized_distance = player.y / (RING_THICKNESS / 2.0); let normalized_distance = fromjupiter.y / (RING_THICKNESS / 2.0);
let density = density_r * (-4.0 * normalized_distance.powf(2.0)).exp() as f32; let density = density_r * (-4.0 * normalized_distance.powf(2.0)).exp() as f32;
if density < 0.001 { if density < 0.001 {
return; return;
@ -342,7 +326,7 @@ fn spawn_despawn_asteroids(
//let max_viewdist = ASTEROID_VIEW_RADIUS / ASTEROID_SPAWN_STEP; //let max_viewdist = ASTEROID_VIEW_RADIUS / ASTEROID_SPAWN_STEP;
let wobble = ASTEROID_SPAWN_STEP * 0.5; let wobble = ASTEROID_SPAWN_STEP * 0.5;
let pos = DVec3::new( let pos = jupiter_pos + DVec3::new(
origin.x as f64 * ASTEROID_SPAWN_STEP + wobble * rand_x * 2.0 - 1.0, 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.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, origin.z as f64 * ASTEROID_SPAWN_STEP + wobble * rand_z * 2.0 - 1.0,
@ -358,6 +342,7 @@ fn spawn_despawn_asteroids(
Rotation::from(Quat::from_rotation_y(-PI / 3.)), Rotation::from(Quat::from_rotation_y(-PI / 3.)),
Position::new(pos), Position::new(pos),
Asteroid, Asteroid,
DespawnOnPlayerDeath,
)); ));
let model = match class { let model = match class {
0 => asteroid1_handle.0.clone(), 0 => asteroid1_handle.0.clone(),
@ -400,6 +385,7 @@ fn handle_cheats(
q_target: Query<(&Transform, &Position, &LinearVelocity), (With<hud::IsTargeted>, Without<actor::PlayerCamera>)>, q_target: Query<(&Transform, &Position, &LinearVelocity), (With<hud::IsTargeted>, Without<actor::PlayerCamera>)>,
mut ew_playerdies: EventWriter<actor::PlayerDiesEvent>, mut ew_playerdies: EventWriter<actor::PlayerDiesEvent>,
mut settings: ResMut<var::Settings>, mut settings: ResMut<var::Settings>,
id2pos: Res<actor::Id2Pos>,
mut ew_sfx: EventWriter<audio::PlaySfxEvent>, mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
) { ) {
if q_player.is_empty() || q_life.is_empty() { if q_player.is_empty() || q_life.is_empty() {
@ -447,16 +433,22 @@ fn handle_cheats(
} }
if key_input.just_pressed(settings.key_cheat_pizza) { if key_input.just_pressed(settings.key_cheat_pizza) {
pos.0 = DVec3::new(-121100218.0, 593057.0, -190818113.0); if let Some(target) = id2pos.0.get(&"pizzeria".to_string()) {
gforce.ignore_gforce_seconds = 1.0; pos.0 = *target + DVec3::new(-60.0, 0.0, 0.0);
gforce.ignore_gforce_seconds = 1.0;
}
} }
if key_input.just_pressed(settings.key_cheat_farview1) { if key_input.just_pressed(settings.key_cheat_farview1) {
pos.0 = DVec3::new(27643e3, -47e3, -124434e3); if let Some(target) = id2pos.0.get(&"busstop2".to_string()) {
gforce.ignore_gforce_seconds = 1.0; pos.0 = *target + DVec3::new(0.0, -1000.0, 0.0);
gforce.ignore_gforce_seconds = 1.0;
}
} }
if key_input.just_pressed(settings.key_cheat_farview2) { if key_input.just_pressed(settings.key_cheat_farview2) {
pos.0 = DVec3::new(-184968e3, 149410e3, -134273e3); if let Some(target) = id2pos.0.get(&"busstop3".to_string()) {
gforce.ignore_gforce_seconds = 1.0; pos.0 = *target + DVec3::new(0.0, -1000.0, 0.0);
gforce.ignore_gforce_seconds = 1.0;
}
} }
if key_input.pressed(settings.key_cheat_adrenaline_zero) { if key_input.pressed(settings.key_cheat_adrenaline_zero) {
lifeform.adrenaline = 0.0; lifeform.adrenaline = 0.0;