Compare commits
145 Commits
stephan/he
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| a7239b3e83 | |||
| 16b08e2547 | |||
| 6ed54dfdc7 | |||
|
|
dd01b03526 | ||
|
|
da5c0f8fb7 | ||
| 375b8a5b46 | |||
| 89e6102b06 | |||
| e22fa8d134 | |||
| 181b617620 | |||
| 56ca801992 | |||
| 16cd95ae02 | |||
| fcb13eed31 | |||
|
|
0735c429ca | ||
|
|
c3c5ae6dfb | ||
|
|
cc7e2aae70 | ||
|
|
dbcd822b50 | ||
| ac8c834f2f | |||
| f35275ab9f | |||
| 3901ee1174 | |||
|
|
7d280af821 | ||
| 7ea9046414 | |||
|
|
f6fa9ce1e4 | ||
| a4488cc072 | |||
| 14a307f29a | |||
| 5278bc9d1f | |||
| c34fe0c90c | |||
|
|
8132203653 | ||
| 7b233f2220 | |||
| 1eb88cc1a6 | |||
| 83729e2699 | |||
| 51596bd331 | |||
|
|
7cfae285ed | ||
| c80129dac1 | |||
| 380bf896ea | |||
| b7414f146a | |||
| e4c8df8a57 | |||
| 7088d863dd | |||
|
|
d93b38d1c8 | ||
|
|
e7ebff2029 | ||
| e044558a93 | |||
| abf037baa3 | |||
|
|
65663f682f | ||
|
|
668ed93475 | ||
|
|
b177c880e3 | ||
| 29db17e11a | |||
| da5fd201b1 | |||
|
|
4e169c1506 | ||
| 5c0e78cb8a | |||
| 456d6ec92a | |||
| 0a3b5253d1 | |||
| 845cae4389 | |||
|
|
ff52258ad2 | ||
| 7a5d2e6914 | |||
|
|
432f245140 | ||
|
|
0649a3e5b9 | ||
| 072badcc14 | |||
| 434184f872 | |||
| 18a799b151 | |||
| f24736ed73 | |||
| 19b4dcc73f | |||
|
|
b83e506a4d | ||
| ad1b7446e1 | |||
| 7cc215ebd8 | |||
|
|
f8540b01e0 | ||
| 5d2ac11ebd | |||
|
|
11684b80a9 | ||
|
|
8f24f4e03a | ||
| adaa9cab30 | |||
| f24cab34b4 | |||
| cf11f4c191 | |||
| 90d5b874ca | |||
| 00455bf212 | |||
| 541875d70c | |||
| fcdfdf07ae | |||
| 8bc3e5a056 | |||
|
|
a16ee231cc | ||
|
|
a07dfb3840 | ||
|
|
fb4c6f501c | ||
|
|
83c59519e5 | ||
|
|
d582313013 | ||
|
|
2f5d154d26 | ||
|
|
7f6c00b5d6 | ||
|
|
c650924d68 | ||
|
|
0bd3fb0e80 | ||
| d55f651a12 | |||
| 84c3efc6c2 | |||
|
|
fc81e0ad21 | ||
|
|
fdee4cf37c | ||
|
|
4c23288511 | ||
|
|
0e8b0bb677 | ||
| d4bea5a7ac | |||
| 013eb9e6b1 | |||
| 16052fc61d | |||
|
|
78b09b33d6 | ||
| 691b9eed33 | |||
| f993c6f7de | |||
| 132db8dbe8 | |||
| 6bfdcf37fc | |||
|
|
7cb9a33f79 | ||
|
|
7996d632f7 | ||
| 5d4c7630ef | |||
| f6e94cfd32 | |||
| 9aed0e9381 | |||
|
|
b93c0e4d96 | ||
|
|
5d00cede94 | ||
| 63dea78c4f | |||
| 9a1d4648c0 | |||
| f45a0bdc1b | |||
| 894f4b1214 | |||
| 05d32c943d | |||
|
|
6e23d12655 | ||
| 2add5f627b | |||
| c1bf090247 | |||
| d04c5eb84f | |||
|
|
376ae8c770 | ||
| b5d15187dd | |||
|
|
8cb7e46d97 | ||
| 4cb7167e09 | |||
|
|
3997beee03 | ||
| 1aa83320b6 | |||
| 7700d4bef9 | |||
| 241b96fcc7 | |||
|
|
28ad53fd68 | ||
| a8f2116804 | |||
| 66cbff654f | |||
|
|
ae18353163 | ||
| 99eb0bf9c8 | |||
|
|
71d43767bd | ||
|
|
e1abb02cb8 | ||
|
|
a2ea917c1e | ||
| 580419e823 | |||
| 4116c4a939 | |||
| 80725e0e84 | |||
| 59c5b1f808 | |||
| 69e112082a | |||
| 703600060b | |||
| 00c2c08f06 | |||
| 8da601e557 | |||
| e9f78f25ff | |||
| 6e8e995da5 | |||
| 67320b3be4 | |||
| fb1c004f8c | |||
| b661844107 | |||
| bdb0cdd04a | |||
| 15059cc6ce |
2
.cargo/config.toml
Normal file
2
.cargo/config.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
[env]
|
||||
BEVY_ASSET_ROOT = { value = "", relative = true }
|
||||
59
.github/actions/discord-webhook/action.yml
vendored
Normal file
59
.github/actions/discord-webhook/action.yml
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
name: "Discord Webhook"
|
||||
description: "Send a message to Discord via webhook using curl"
|
||||
inputs:
|
||||
webhook_url:
|
||||
description: "Discord webhook URL"
|
||||
required: true
|
||||
message:
|
||||
description: "Message to send"
|
||||
required: true
|
||||
username:
|
||||
description: "Override the default username of the webhook"
|
||||
required: false
|
||||
default: ""
|
||||
avatar_url:
|
||||
description: "Override the default avatar of the webhook"
|
||||
required: false
|
||||
default: ""
|
||||
color:
|
||||
description: "Embed color (decimal number)"
|
||||
required: false
|
||||
default: ""
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Send Discord notification
|
||||
shell: bash
|
||||
run: |
|
||||
MESSAGE='${{ inputs.message }}'
|
||||
|
||||
if [ -n "${{ inputs.color }}" ]; then
|
||||
# Send as embed with color
|
||||
PAYLOAD=$(jq -n \
|
||||
--arg content "$MESSAGE" \
|
||||
--arg username "${{ inputs.username }}" \
|
||||
--arg avatar "${{ inputs.avatar_url }}" \
|
||||
--arg color "${{ inputs.color }}" \
|
||||
'{
|
||||
content: (if $content != "" then $content else null end),
|
||||
username: (if $username != "" then $username else null end),
|
||||
avatar_url: (if $avatar != "" then $avatar else null end),
|
||||
embeds: (if $color != "" then [{description: $content, color: ($color | tonumber)}] else null end)
|
||||
} | with_entries(select(.value != null))')
|
||||
else
|
||||
# Send as simple message
|
||||
PAYLOAD=$(jq -n \
|
||||
--arg content "$MESSAGE" \
|
||||
--arg username "${{ inputs.username }}" \
|
||||
--arg avatar "${{ inputs.avatar_url }}" \
|
||||
'{
|
||||
content: $content,
|
||||
username: (if $username != "" then $username else null end),
|
||||
avatar_url: (if $avatar != "" then $avatar else null end)
|
||||
} | with_entries(select(.value != null))')
|
||||
fi
|
||||
|
||||
curl -H "Content-Type: application/json" \
|
||||
-d "$PAYLOAD" \
|
||||
"${{ inputs.webhook_url }}"
|
||||
16
.github/workflows/archive_steamos.yml
vendored
16
.github/workflows/archive_steamos.yml
vendored
@@ -15,22 +15,22 @@ jobs:
|
||||
toolchain: stable
|
||||
components: clippy, rustfmt
|
||||
|
||||
- name: "Install and cache dependencies (Linux)"
|
||||
uses: awalsh128/cache-apt-pkgs-action@latest
|
||||
with:
|
||||
packages: libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev
|
||||
version: 1.0
|
||||
- name: install linux dependencies
|
||||
if: runner.os == 'linux'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cargo build --release --locked --target=x86_64-unknown-linux-gnu
|
||||
cargo build --release --locked --target=x86_64-unknown-linux-gnu --bin hedz_reloaded
|
||||
|
||||
- name: Archive
|
||||
run: |
|
||||
cp target/x86_64-unknown-linux-gnu/release/hedz_reloaded hedz_reloaded
|
||||
cp target/x86_64-unknown-linux-gnu/release/hedz_reloaded ./
|
||||
tar -czf steamos.tar.gz hedz_reloaded
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: christopherhx/gitea-upload-artifact@v4
|
||||
with:
|
||||
name: steamos.tar.gz
|
||||
path: ./steamos.tar.gz
|
||||
|
||||
15
.github/workflows/archive_win.yml
vendored
15
.github/workflows/archive_win.yml
vendored
@@ -20,23 +20,22 @@ jobs:
|
||||
run: |
|
||||
which cargo-xwin || cargo install --locked cargo-xwin
|
||||
|
||||
- name: "Install and cache dependencies (Windows)"
|
||||
uses: awalsh128/cache-apt-pkgs-action@latest
|
||||
with:
|
||||
packages: lld clang cmake ninja-build
|
||||
version: 1.0
|
||||
- name: install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y lld clang cmake ninja-build
|
||||
|
||||
- name: Build binaries (Windows)
|
||||
run: |
|
||||
cargo xwin build --locked --release --target=x86_64-pc-windows-msvc
|
||||
cargo xwin build --locked --release --target=x86_64-pc-windows-msvc --bin hedz_reloaded
|
||||
|
||||
- name: Archive
|
||||
run: |
|
||||
ls -lisa target/x86_64-pc-windows-msvc/release/
|
||||
cp target/x86_64-pc-windows-msvc/release/hedz_reloaded.exe hedz_reloaded.exe
|
||||
cp target/x86_64-pc-windows-msvc/release/hedz_reloaded.exe ./
|
||||
tar -czf win.tar.gz hedz_reloaded.exe
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: christopherhx/gitea-upload-artifact@v4
|
||||
with:
|
||||
name: win.tar.gz
|
||||
path: ./win.tar.gz
|
||||
|
||||
17
.github/workflows/ci.yml
vendored
17
.github/workflows/ci.yml
vendored
@@ -14,20 +14,17 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cargo-bins/cargo-binstall@main
|
||||
|
||||
if: runner.os == 'linux'
|
||||
- uses: extractions/setup-just@v1
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: stable
|
||||
components: clippy, rustfmt
|
||||
|
||||
- name: "Install and cache dependencies (Linux)"
|
||||
- name: install linux dependencies
|
||||
if: runner.os == 'linux'
|
||||
uses: awalsh128/cache-apt-pkgs-action@latest
|
||||
with:
|
||||
packages: libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev
|
||||
version: 1.0
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev
|
||||
|
||||
- name: Formatting
|
||||
run: |
|
||||
@@ -39,8 +36,10 @@ jobs:
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cargo build
|
||||
cargo build --bin hedz_reloaded
|
||||
cargo build --bin hedz_reloaded_server --no-default-features
|
||||
|
||||
- name: Tests
|
||||
run: |
|
||||
cargo test
|
||||
cargo test --lib hedz_reloaded
|
||||
cargo test --lib hedz_reloaded --no-default-features
|
||||
|
||||
26
.github/workflows/ci_debug.yml
vendored
26
.github/workflows/ci_debug.yml
vendored
@@ -15,15 +15,19 @@ jobs:
|
||||
toolchain: stable
|
||||
components: clippy, rustfmt
|
||||
|
||||
- name: "Install and cache dependencies (Linux)"
|
||||
uses: awalsh128/cache-apt-pkgs-action@latest
|
||||
with:
|
||||
packages: libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev
|
||||
version: 1.0
|
||||
|
||||
- name: Build
|
||||
- name: install linux dependencies
|
||||
if: runner.os == 'linux'
|
||||
run: |
|
||||
cargo build --locked --target=x86_64-unknown-linux-gnu
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev
|
||||
|
||||
- name: Build client
|
||||
run: |
|
||||
cargo build --locked --target=x86_64-unknown-linux-gnu --bin hedz_reloaded
|
||||
|
||||
- name: Build server
|
||||
run: |
|
||||
cargo build --locked --target=x86_64-unknown-linux-gnu --bin hedz_reloaded_server --no-default-features
|
||||
|
||||
- name: Lints
|
||||
run: |
|
||||
@@ -31,10 +35,10 @@ jobs:
|
||||
|
||||
- name: Archive
|
||||
run: |
|
||||
cp target/x86_64-unknown-linux-gnu/debug/hedz_reloaded hedz_reloaded
|
||||
tar -czf steamos-debug.tar.gz hedz_reloaded
|
||||
cp target/x86_64-unknown-linux-gnu/debug/hedz_reloaded target/x86_64-unknown-linux-gnu/debug/hedz_reloaded_server ./
|
||||
tar -czf steamos-debug.tar.gz hedz_reloaded hedz_reloaded_server
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: christopherhx/gitea-upload-artifact@v4
|
||||
with:
|
||||
name: steamos-debug.tar.gz
|
||||
path: ./steamos-debug.tar.gz
|
||||
|
||||
43
.github/workflows/steam_alpha.yml
vendored
43
.github/workflows/steam_alpha.yml
vendored
@@ -22,17 +22,17 @@ jobs:
|
||||
|
||||
- name: Build (lipo)
|
||||
run: |
|
||||
cargo build --release --target=x86_64-apple-darwin
|
||||
cargo build --release --target=aarch64-apple-darwin
|
||||
cargo build --release --target=x86_64-apple-darwin --bin hedz_reloaded
|
||||
cargo build --release --target=aarch64-apple-darwin --bin hedz_reloaded
|
||||
lipo -create -output target/release/hedz_reloaded target/aarch64-apple-darwin/release/hedz_reloaded target/x86_64-apple-darwin/release/hedz_reloaded
|
||||
|
||||
- name: Archive
|
||||
run: |
|
||||
cp target/release/hedz_reloaded ./hedz_reloaded
|
||||
cp target/release/hedz_reloaded ./
|
||||
tar -czf hedz-macos.tar.gz hedz_reloaded
|
||||
ls -lisah hedz-macos.tar.gz
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: christopherhx/gitea-upload-artifact@v4
|
||||
with:
|
||||
name: hedz-macos
|
||||
path: ./hedz-macos.tar.gz
|
||||
@@ -80,10 +80,10 @@ jobs:
|
||||
id: vars
|
||||
run: |
|
||||
echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT
|
||||
echo "branch=$(git branch --show-current)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Discord notification
|
||||
uses: appleboy/discord-action@v1.2.0
|
||||
uses: ./.github/actions/discord-webhook
|
||||
with:
|
||||
webhook_url: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
message: New macos release (`${{ steps.vars.outputs.branch }}`) [`${{ steps.vars.outputs.sha_short }}`]
|
||||
@@ -103,7 +103,7 @@ jobs:
|
||||
uses: awalsh128/cache-apt-pkgs-action@latest
|
||||
with:
|
||||
packages: libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev
|
||||
version: 1.0
|
||||
version: 2
|
||||
|
||||
- uses: leafwing-studios/cargo-cache@v2
|
||||
with:
|
||||
@@ -111,14 +111,14 @@ jobs:
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cargo build --locked --release --target=x86_64-unknown-linux-gnu
|
||||
cargo build --locked --release --target=x86_64-unknown-linux-gnu --bin hedz_reloaded
|
||||
|
||||
- name: Archive
|
||||
run: |
|
||||
cp target/x86_64-unknown-linux-gnu/release/hedz_reloaded hedz_reloaded
|
||||
cp target/x86_64-unknown-linux-gnu/release/hedz_reloaded ./
|
||||
tar -czf steamos.tar.gz hedz_reloaded
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: christopherhx/gitea-upload-artifact@v4
|
||||
with:
|
||||
name: steamos.tar.gz
|
||||
path: ./steamos.tar.gz
|
||||
@@ -126,7 +126,7 @@ jobs:
|
||||
- name: Copy Binary for SteamOS
|
||||
run: |
|
||||
mkdir -p build/steamos/content
|
||||
cp target/x86_64-unknown-linux-gnu/release/hedz_reloaded build/steamos/content/hedz_reloaded
|
||||
cp target/x86_64-unknown-linux-gnu/release/hedz_reloaded build/steamos/content/
|
||||
|
||||
- name: Install SteamCMD
|
||||
run: |
|
||||
@@ -161,10 +161,10 @@ jobs:
|
||||
id: vars
|
||||
run: |
|
||||
echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT
|
||||
echo "branch=$(git branch --show-current)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Discord notification
|
||||
uses: appleboy/discord-action@v1.2.0
|
||||
uses: ./.github/actions/discord-webhook
|
||||
with:
|
||||
webhook_url: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
message: New steamos release (`${{ steps.vars.outputs.branch }}`) [`${{ steps.vars.outputs.sha_short }}`]
|
||||
@@ -185,20 +185,19 @@ jobs:
|
||||
run: |
|
||||
which cargo-xwin || cargo install --locked cargo-xwin
|
||||
|
||||
- name: "Install and cache dependencies (Windows)"
|
||||
uses: awalsh128/cache-apt-pkgs-action@latest
|
||||
with:
|
||||
packages: lld clang cmake ninja-build
|
||||
version: 1.0
|
||||
- name: install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y lld clang cmake ninja-build
|
||||
|
||||
- name: Build binaries (Windows)
|
||||
run: |
|
||||
cargo xwin build --locked --release --target=x86_64-pc-windows-msvc
|
||||
cargo xwin build --locked --release --target=x86_64-pc-windows-msvc --bin hedz_reloaded
|
||||
|
||||
- name: Move binary
|
||||
run: |
|
||||
ls -lisa target/x86_64-pc-windows-msvc/release/
|
||||
cp target/x86_64-pc-windows-msvc/release/hedz_reloaded.exe build/win/hedz_reloaded.exe
|
||||
cp target/x86_64-pc-windows-msvc/release/hedz_reloaded.exe build/win/
|
||||
|
||||
- uses: ./.github/actions/steamcmd
|
||||
with:
|
||||
@@ -217,10 +216,10 @@ jobs:
|
||||
id: vars
|
||||
run: |
|
||||
echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT
|
||||
echo "branch=$(git branch --show-current)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Discord notification
|
||||
uses: appleboy/discord-action@v1.2.0
|
||||
uses: ./.github/actions/discord-webhook
|
||||
with:
|
||||
webhook_url: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
message: New windows release (`${{ steps.vars.outputs.branch }}`) [`${{ steps.vars.outputs.sha_short }}`]
|
||||
|
||||
2
.rustfmt.toml
Normal file
2
.rustfmt.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
imports_granularity = "Crate"
|
||||
group_imports = "One"
|
||||
3969
Cargo.lock
generated
3969
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
103
Cargo.toml
103
Cargo.toml
@@ -1,17 +1,9 @@
|
||||
[package]
|
||||
name = "hedz_reloaded"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
build = "build.rs"
|
||||
[workspace]
|
||||
resolver = "3"
|
||||
members = ["crates/*"]
|
||||
|
||||
[profile.dev.package."*"]
|
||||
opt-level = 3
|
||||
|
||||
[features]
|
||||
dbg = ["avian3d/debug-plugin", "dep:bevy-inspector-egui"]
|
||||
|
||||
[dependencies]
|
||||
avian3d = { version = "0.3", default-features = false, features = [
|
||||
[workspace.dependencies]
|
||||
avian3d = { version = "0.4.0", default-features = false, features = [
|
||||
"3d",
|
||||
"f32",
|
||||
"parry-f32",
|
||||
@@ -19,29 +11,72 @@ avian3d = { version = "0.3", default-features = false, features = [
|
||||
"bevy_scene",
|
||||
"bevy_picking", # todo: Consider if this one is necessary
|
||||
"parallel",
|
||||
"serialize",
|
||||
] }
|
||||
bevy = { version = "0.17.0", default-features = false, features = [
|
||||
"animation",
|
||||
"async_executor",
|
||||
"bevy_asset",
|
||||
"bevy_color",
|
||||
"bevy_core_pipeline",
|
||||
"bevy_gilrs",
|
||||
"bevy_gizmos",
|
||||
"bevy_gltf",
|
||||
"bevy_input_focus",
|
||||
"bevy_log",
|
||||
"bevy_mesh_picking_backend",
|
||||
"bevy_pbr",
|
||||
"bevy_picking",
|
||||
"bevy_render",
|
||||
"bevy_scene",
|
||||
"bevy_sprite",
|
||||
"bevy_sprite_picking_backend",
|
||||
"bevy_state",
|
||||
"bevy_text",
|
||||
"bevy_ui",
|
||||
"bevy_ui_render",
|
||||
"bevy_ui_picking_backend",
|
||||
"default_font",
|
||||
"hdr",
|
||||
"multi_threaded",
|
||||
"png",
|
||||
"reflect_auto_register",
|
||||
"smaa_luts",
|
||||
"std",
|
||||
"sysinfo_plugin",
|
||||
"tonemapping_luts",
|
||||
"vorbis",
|
||||
"webgl2",
|
||||
"zstd_rust",
|
||||
"track_location",
|
||||
] }
|
||||
bevy-inspector-egui = "0.34"
|
||||
bevy-persistent = { version = "0.9", features = ["ron"] }
|
||||
bevy-steamworks = "0.15.0"
|
||||
bevy_asset_loader = "=0.24.0-rc.1"
|
||||
bevy_ballistic = { git = "https://github.com/rustunit/bevy_ballistic.git", rev = "b08ffec" }
|
||||
bevy_common_assets = { version = "0.14.0", features = ["ron"] }
|
||||
bevy_debug_log = { git = "https://github.com/rustunit/bevy_debug_log.git", rev = "86051a0" }
|
||||
bevy_replicon = "0.37.1"
|
||||
# TODO: i dont think we need this in dedicated server mode
|
||||
bevy_replicon_renet = { version = "0.13.0", features = ["renet_steam"] }
|
||||
bevy_sprite3d = "7.0.0"
|
||||
bevy_trenchbroom = { version = "0.10", default-features = false, features = [
|
||||
"physics-integration",
|
||||
] }
|
||||
bevy_trenchbroom_avian = "0.10"
|
||||
clap = { version = "=4.5.47", features = ["derive"] }
|
||||
dirs = "6.0.0"
|
||||
happy_feet = { git = "https://github.com/PROMETHIA-27/happy_feet.git", rev = "e3a4660e0b68f9bf4d6facb0fb4d93f2d7848b27", features = [
|
||||
"serde",
|
||||
] }
|
||||
bevy = { version = "0.16.0", features = ["track_location"] }
|
||||
bevy_trenchbroom = { version = "0.8.0", features = ["avian"] }
|
||||
nil = "0.14.0"
|
||||
bevy_asset_loader = "0.23.0-rc.3"
|
||||
bevy_sprite3d = "5.0.0"
|
||||
rand = "=0.8.5"
|
||||
bevy-inspector-egui = { version = "0.31", optional = true }
|
||||
bevy-steamworks = "0.13.0"
|
||||
steamworks = "0.11"
|
||||
bevy_ballistic = "0.4.0"
|
||||
bevy-ui-gradients = "0.4.0"
|
||||
bevy_debug_log = "0.6.0"
|
||||
bevy_common_assets = { version = "0.13.0", features = ["ron"] }
|
||||
serde = { version = "1.0.219", features = ["derive"] }
|
||||
ron = "0.8"
|
||||
serde = { version = "1.0.219", features = ["derive"] }
|
||||
shared = { path = "crates/shared" }
|
||||
# TODO: i dont think we need this in dedicated server mode
|
||||
steamworks = "0.12"
|
||||
|
||||
[build-dependencies]
|
||||
vergen-gitcl = "1.0"
|
||||
|
||||
[lints.clippy]
|
||||
too_many_arguments = "allow"
|
||||
type_complexity = "allow"
|
||||
|
||||
[patch.crates-io]
|
||||
bevy-steamworks = { git = "https://github.com/extrawurst/bevy_steamworks.git", branch = "derive-debug-event" }
|
||||
[profile.dev.package."*"]
|
||||
opt-level = 3
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
([
|
||||
/*00*/(key:"angry demonstrator", ability:Thrown, aps:2, ammo:10, range:90, damage:25, projectile:"molotov"),
|
||||
/*01*/(key:"carnival knife thrower", range:60, ammo:5),
|
||||
/*02*/(key:"chicago gangster", ability:Gun, ammo:25, range:60),
|
||||
/*00*/(key:"angry demonstrator", ability:Thrown, aps:2, ammo:10, range:90, damage:25, projectile:"molotov", interrupt_shoot:false),
|
||||
/*01*/(key:"carnival knife thrower", ability:Arrow, range:60, ammo:5),
|
||||
/*02*/(key:"chicago gangster", ability:Gun, aps:7.4, ammo:25, range:60, damage:12),
|
||||
/*03*/(key:"commando", ability:Gun, aps:7.4, ammo:26, range:60, damage:12),
|
||||
/*04*/(key:"field medic", ability:Medic, aps:20),
|
||||
/*05*/(key:"geisha"),
|
||||
/*05*/(key:"geisha", ability:Curver, range:60, ammo:10, damage:25, projectile:"fan"),
|
||||
/*06*/(key:"goblin", ability:Arrow, aps:2, ammo:5, range:90, damage:50),
|
||||
/*07*/(key:"green grocer", range:60, ammo:10, damage:25),
|
||||
/*08*/(key:"highland hammer thrower", ability:Thrown, aps:2, ammo:10, damage:25, range:80, projectile:"hammer"),
|
||||
/*09*/(key:"legionnaire", ability:Gun, aps:1.5, ammo:25, range:60, damage:13),
|
||||
/*10*/(key:"mig pilot", ability:Missile, ammo:5, range:60, damage:100, controls:Plane),
|
||||
/*11*/(key:"nanny", ability:Thrown, range:60),
|
||||
/*12*/(key:"panic attack"),
|
||||
/*13*/(key:"salty sea dog"),
|
||||
/*14*/(key:"snow plough operator"),
|
||||
/*15*/(key:"soldier ant"),
|
||||
/*07*/(key:"green grocer", ability:Curver, range:60, ammo:10, damage:25, projectile:"carrot"),
|
||||
/*08*/(key:"highland hammer thrower", ability:Thrown, aps:2, ammo:10, damage:25, range:80, projectile:"hammer", interrupt_shoot:false),
|
||||
/*09*/(key:"legionnaire", ability:Gun, aps:2, ammo:25, range:60, damage:13, shoot_offset:0.1667),
|
||||
/*10*/(key:"mig pilot", ability:Missile, ammo:5, range:60, damage:100, controls:Plane, interrupt_shoot:false),
|
||||
/*11*/(key:"nanny", ability:None, ammo:5, range:60, projectile:"handbag"),
|
||||
/*12*/(key:"panic attack", ability:Turbo),
|
||||
/*13*/(key:"salty sea dog", ability:None),
|
||||
/*14*/(key:"snow plough operator", ability:None),
|
||||
/*15*/(key:"soldier ant", ability:None),
|
||||
/*16*/(key:"super market shopper", ability:Thrown, ammo:10, range:80, damage:17, projectile:"handbag"),
|
||||
/*17*/(key:"troll", ability:Thrown, ammo:10, range:80, damage:17),
|
||||
/*17*/(key:"troll", ability:Thrown, ammo:10, range:80, damage:17, projectile:"club"),
|
||||
])
|
||||
|
||||
@@ -16,12 +16,12 @@
|
||||
}
|
||||
// brush 1
|
||||
{
|
||||
( -256 -832 2320 ) ( -256 -768 272 ) ( -256 -768 2320 ) TinyTexPack2/Brick/Brick_20_window [ 0 -1 0 0 ] [ 0 0 -1 16 ] 270 1 1
|
||||
( 768 -832 2320 ) ( -256 -832 272 ) ( -256 -832 2320 ) TinyTexPack2/Brick/Brick_20_window [ 1 0 0 0 ] [ 0 0 -1 16 ] 270 1 1
|
||||
( 768 -768 272 ) ( -256 -832 272 ) ( 768 -832 272 ) TinyTexPack2/Brick/Brick_20_window [ -1 0 0 0 ] [ 0 -1 0 0 ] 270 1 1
|
||||
( 768 -768 2320 ) ( -256 -832 2320 ) ( -256 -768 2320 ) TinyTexPack2/Brick/Brick_20_window [ 1 0 0 0 ] [ 0 -1 0 0 ] 270 1 1
|
||||
( 768 -768 2320 ) ( -256 -768 272 ) ( 768 -768 272 ) TinyTexPack2/Brick/Brick_20_window [ -1 0 0 0 ] [ 0 0 -1 16 ] 270 1 1
|
||||
( 768 -768 2320 ) ( 768 -832 272 ) ( 768 -832 2320 ) TinyTexPack2/Brick/Brick_20_window [ 0 1 0 0 ] [ 0 0 -1 16 ] 270 1 1
|
||||
( -256 -832 2320 ) ( -256 -768 272 ) ( -256 -768 2320 ) TinyTexPack2/Brick/Brick_20_window [ 0 -1 0 0 ] [ 0 0 -1 16 ] 90 1 1
|
||||
( 768 -832 2320 ) ( -256 -832 272 ) ( -256 -832 2320 ) TinyTexPack2/Brick/Brick_20_window [ 1 0 0 0 ] [ 0 0 -1 16 ] 90 1 1
|
||||
( 768 -768 272 ) ( -256 -832 272 ) ( 768 -832 272 ) TinyTexPack2/Brick/Brick_20_window [ -1 0 0 0 ] [ 0 -1 0 0 ] 90 1 1
|
||||
( 768 -768 2320 ) ( -256 -832 2320 ) ( -256 -768 2320 ) TinyTexPack2/Brick/Brick_20_window [ 1 0 0 0 ] [ 0 -1 0 0 ] 90 1 1
|
||||
( 768 -768 2320 ) ( -256 -768 272 ) ( 768 -768 272 ) TinyTexPack2/Brick/Brick_20_window [ -1 0 0 0 ] [ 0 0 -1 16 ] 90 1 1
|
||||
( 768 -768 2320 ) ( 768 -832 272 ) ( 768 -832 2320 ) TinyTexPack2/Brick/Brick_20_window [ 0 1 0 0 ] [ 0 0 -1 16 ] 90 1 1
|
||||
}
|
||||
// brush 2
|
||||
{
|
||||
@@ -1074,7 +1074,7 @@
|
||||
// brush 119
|
||||
{
|
||||
( -5376 3584 -1440 ) ( -5376 3584 -1439 ) ( -5376 3583 -1440 ) airlock/airlock-wall-outside [ -1.9967346175427393e-16 -1 0 64 ] [ 0 0 -1 -64 ] 180 1 1
|
||||
( -5376 3515.4285714285716 -1440 ) ( -5377 3515.4285714285716 -1440 ) ( -5376 3515.4285714285716 -1439 ) airlock/airlock-wall [ -1 1.9967346175427393e-16 0 0 ] [ 0 0 -1 -64 ] 180 1 1
|
||||
( -5376 3392 -1440 ) ( -5377 3392 -1440 ) ( -5376 3392 -1439 ) airlock/airlock-wall [ -1 1.9967346175427393e-16 0 0 ] [ 0 0 -1 -64 ] 180 1 1
|
||||
( -5344 3968 -1472 ) ( -5345 3968 -1472 ) ( -5344 3967 -1472 ) airlock/airlock-wall [ -1 0 0 0 ] [ 0 -1 0 64 ] 180 1 1
|
||||
( -5312 3584 -1248 ) ( -5376 3680 -1472 ) ( -5312 3680 -1472 ) airlock/airlock-wall-outside [ -1 0 0 0 ] [ 0 0.39391929857916613 -0.9191450300180586 33.614258 ] 180 1 1
|
||||
( -5312 3968 -1472 ) ( -5312 3967 -1472 ) ( -5312 3968 -1471 ) airlock/airlock-wall [ 1.9967346175427393e-16 1 0 -64 ] [ 0 0 -1 -64 ] 180 1 1
|
||||
@@ -1082,8 +1082,8 @@
|
||||
// brush 120
|
||||
{
|
||||
( -5376 3584 -1440 ) ( -5376 3584 -1439 ) ( -5376 3583 -1440 ) airlock/airlock-wall-outside [ -1.9967346175427393e-16 -1 0 64 ] [ 0 0 -1 -64 ] 180 1 1
|
||||
( -5376 3515.4285714285716 -1440 ) ( -5377 3515.4285714285716 -1440 ) ( -5376 3515.4285714285716 -1439 ) airlock/airlock-wall [ -1 1.9967346175427393e-16 0 0 ] [ 0 0 -1 -64 ] 180 1 1
|
||||
( -5312 3872 -1024 ) ( -5312 3680 -1024 ) ( -5440 3680 -1024 ) airlock/airlock-wall [ 1 0 0 0 ] [ 0 -1 0 64 ] 180 1 1
|
||||
( -5376 3392 -1440 ) ( -5377 3392 -1440 ) ( -5376 3392 -1439 ) airlock/airlock-wall [ -1 1.9967346175427393e-16 0 0 ] [ 0 0 -1 -64 ] 180 1 1
|
||||
( -5312 3872 -704 ) ( -5312 3680 -704 ) ( -5440 3680 -704 ) airlock/airlock-wall [ 1 0 0 0 ] [ 0 -1 0 64 ] 180 1 1
|
||||
( -5312 3680 -1024 ) ( -5376 3584 -1248 ) ( -5312 3584 -1248 ) airlock/airlock-wall-outside [ -1 7.105427357601002e-15 0 0 ] [ -2.7989649608114045e-15 -0.39391929857916613 -0.9191450300180586 84.0361 ] 180 1 1
|
||||
( -5312 3968 -1472 ) ( -5312 3967 -1472 ) ( -5312 3968 -1471 ) airlock/airlock-wall [ 1.9967346175427393e-16 1 0 -64 ] [ 0 0 -1 -64 ] 180 1 1
|
||||
}
|
||||
@@ -1091,8 +1091,8 @@
|
||||
{
|
||||
( -5376 3584 -1440 ) ( -5376 3584 -1439 ) ( -5376 3583 -1440 ) airlock/airlock-wall-outside [ -1.9967346175427393e-16 -1 0 64 ] [ 0 0 -1 -64 ] 180 1 1
|
||||
( -5312 3968 -1248 ) ( -5440 3872 -1024 ) ( -5312 3872 -1024 ) airlock/airlock-wall-outside [ 1 0 0 0 ] [ 0 0.3939192985791677 -0.9191450300180579 33.614502 ] 180 1 1
|
||||
( -5312 3872 -1024 ) ( -5312 3680 -1024 ) ( -5440 3680 -1024 ) airlock/airlock-wall [ 1 0 0 0 ] [ 0 -1 0 64 ] 180 1 1
|
||||
( -5344 4032 -1472 ) ( -5344 4032 -1471 ) ( -5345 4032 -1472 ) airlock/airlock-wall [ 1 -1.9967346175427393e-16 0 0 ] [ 0 0 -1 -64 ] 180 1 1
|
||||
( -5312 3872 -704 ) ( -5312 3680 -704 ) ( -5440 3680 -704 ) airlock/airlock-wall [ 1 0 0 0 ] [ 0 -1 0 64 ] 180 1 1
|
||||
( -5344 4096 -1472 ) ( -5344 4096 -1471 ) ( -5345 4096 -1472 ) airlock/airlock-wall [ 1 -1.9967346175427393e-16 0 0 ] [ 0 0 -1 -64 ] 180 1 1
|
||||
( -5312 3968 -1472 ) ( -5312 3967 -1472 ) ( -5312 3968 -1471 ) airlock/airlock-wall [ 1.9967346175427393e-16 1 0 -64 ] [ 0 0 -1 -64 ] 180 1 1
|
||||
}
|
||||
// brush 122
|
||||
@@ -1100,9 +1100,125 @@
|
||||
( -5376 3584 -1440 ) ( -5376 3584 -1439 ) ( -5376 3583 -1440 ) airlock/airlock-wall-outside [ -1.9967346175427393e-16 -1 0 64 ] [ 0 0 -1 -64 ] 180 1 1
|
||||
( -5312 3872 -1472 ) ( -5376 3968 -1248 ) ( -5312 3968 -1248 ) airlock/airlock-wall-outside [ 1 0 0 0 ] [ 0 -0.3939192985791677 -0.9191450300180579 84.03613 ] 180 1 1
|
||||
( -5344 3968 -1472 ) ( -5345 3968 -1472 ) ( -5344 3967 -1472 ) airlock/airlock-wall [ -1 0 0 0 ] [ 0 -1 0 64 ] 180 1 1
|
||||
( -5344 4032 -1472 ) ( -5344 4032 -1471 ) ( -5345 4032 -1472 ) airlock/airlock-wall [ 1 -1.9967346175427393e-16 0 0 ] [ 0 0 -1 -64 ] 180 1 1
|
||||
( -5344 4096 -1472 ) ( -5344 4096 -1471 ) ( -5345 4096 -1472 ) airlock/airlock-wall [ 1 -1.9967346175427393e-16 0 0 ] [ 0 0 -1 -64 ] 180 1 1
|
||||
( -5312 3968 -1472 ) ( -5312 3967 -1472 ) ( -5312 3968 -1471 ) airlock/airlock-wall [ 1.9967346175427393e-16 1 0 -64 ] [ 0 0 -1 -64 ] 180 1 1
|
||||
}
|
||||
// brush 123
|
||||
{
|
||||
( -5376 3392 -1472 ) ( -5568 3392 -1600 ) ( -5568 4096 -1600 ) canals [ -0.7071067811865474 0 -0.7071067811865477 0 ] [ 0 -1 0 0 ] 0 1 1
|
||||
( -5376 3392 -1600 ) ( -5568 3392 -1600 ) ( -5376 3392 -1472 ) canals [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
|
||||
( -5568 4096 -1600 ) ( -5568 3392 -1600 ) ( -5376 3392 -1600 ) canals [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
|
||||
( -5376 4096 -1472 ) ( -5568 4096 -1600 ) ( -5376 4096 -1600 ) canals [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
|
||||
( -5376 3392 -1472 ) ( -5376 4096 -1472 ) ( -5376 4096 -1600 ) canals [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1
|
||||
}
|
||||
// brush 124
|
||||
{
|
||||
( -7168 3392 -1600 ) ( -7168 3392 -1599 ) ( -7168 3391 -1600 ) canals [ 1.9967346175427393e-16 1 0 0 ] [ 0 0 -1 0 ] 0 1 1
|
||||
( -4416 3328 -1536 ) ( -4416 3328 -1535 ) ( -4415 3328 -1536 ) canals [ -1 1.9967346175427393e-16 0 0 ] [ 0 0 -1 0 ] 0 1 1
|
||||
( -4480 3392 -1600 ) ( -4480 3391 -1600 ) ( -4479 3392 -1600 ) canals [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
|
||||
( -4416 3328 -768 ) ( -4415 3328 -768 ) ( -4416 3327 -768 ) canals [ 1 0 0 0 ] [ 0 -1 0 0 ] 90 1 1
|
||||
( -4480 3392 -1600 ) ( -4479 3392 -1600 ) ( -4480 3392 -1599 ) canals [ 1 -1.9967346175427393e-16 0 0 ] [ 0 0 -1 0 ] 0 1 1
|
||||
( -5376 3328 -1536 ) ( -5376 3327 -1536 ) ( -5376 3328 -1535 ) canals [ -1.9967346175427393e-16 -1 0 0 ] [ 0 0 -1 0 ] 180 1 1
|
||||
}
|
||||
// brush 125
|
||||
{
|
||||
( -7168 4160 -1600 ) ( -7168 4160 -1599 ) ( -7168 4159 -1600 ) canals [ 1.9967346175427393e-16 1 0 0 ] [ 0 0 -1 0 ] 0 1 1
|
||||
( -4416 4096 -1536 ) ( -4416 4096 -1535 ) ( -4415 4096 -1536 ) canals [ -1 1.9967346175427393e-16 0 0 ] [ 0 0 -1 0 ] 0 1 1
|
||||
( -4480 4160 -1600 ) ( -4480 4159 -1600 ) ( -4479 4160 -1600 ) canals [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
|
||||
( -4416 4096 -768 ) ( -4415 4096 -768 ) ( -4416 4095 -768 ) canals [ 1 0 0 0 ] [ 0 -1 0 0 ] 90 1 1
|
||||
( -4480 4160 -1600 ) ( -4479 4160 -1600 ) ( -4480 4160 -1599 ) canals [ 1 -1.9967346175427393e-16 0 0 ] [ 0 0 -1 0 ] 0 1 1
|
||||
( -5376 4096 -1536 ) ( -5376 4095 -1536 ) ( -5376 4096 -1535 ) canals [ -1.9967346175427393e-16 -1 0 0 ] [ 0 0 -1 0 ] 180 1 1
|
||||
}
|
||||
// brush 126
|
||||
{
|
||||
( -7136 3392 -1664 ) ( -7136 3393 -1664 ) ( -7136 3392 -1663 ) canals [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1
|
||||
( -5696 3392 -1664 ) ( -5696 3392 -1663 ) ( -5695 3392 -1664 ) canals [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
|
||||
( -5696 3392 -1664 ) ( -5695 3392 -1664 ) ( -5696 3393 -1664 ) canals [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
|
||||
( -5568 4096 -1600 ) ( -5568 4097 -1600 ) ( -5567 4096 -1600 ) canals [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
|
||||
( -5568 4096 -1600 ) ( -5567 4096 -1600 ) ( -5568 4096 -1599 ) canals [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
|
||||
( -5568 4096 -1600 ) ( -5568 4096 -1599 ) ( -5568 4097 -1600 ) canals [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1
|
||||
}
|
||||
// brush 127
|
||||
{
|
||||
( -6496 3392 -768 ) ( -6496 3393 -768 ) ( -6496 3392 -767 ) canals [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1
|
||||
( -5504 3392 -768 ) ( -5504 3392 -767 ) ( -5503 3392 -768 ) canals [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
|
||||
( -5504 3392 -768 ) ( -5503 3392 -768 ) ( -5504 3393 -768 ) canals [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
|
||||
( -5376 4096 -704 ) ( -5376 4097 -704 ) ( -5375 4096 -704 ) canals [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
|
||||
( -5376 4096 -704 ) ( -5375 4096 -704 ) ( -5376 4096 -703 ) canals [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
|
||||
( -5376 4096 -704 ) ( -5376 4096 -703 ) ( -5376 4097 -704 ) canals [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1
|
||||
}
|
||||
// brush 128
|
||||
{
|
||||
( -6560 4096 -304 ) ( -6560 4097 -304 ) ( -6560 4096 -303 ) canals [ 4.812271809458903e-16 -0.9999999999999999 0 64 ] [ 0 0 -1 16 ] 270 1 1
|
||||
( -6496 3392 -368 ) ( -6497 3392 -368 ) ( -6496 3392 -367 ) canals [ 1.0000000000000007 -5.456456664689579e-16 0 128 ] [ 0 0 -1 16 ] 180 1 1
|
||||
( -6496 3648 -768 ) ( -6496 3649 -768 ) ( -6497 3648 -768 ) canals [ 4.812271809458903e-16 -0.9999999999999999 0 64 ] [ 1.0000000000000007 -5.456456664689579e-16 0 128 ] 180 1 1
|
||||
( -6688 4096 -304 ) ( -6689 4096 -304 ) ( -6688 4097 -304 ) canals [ -4.812271809458903e-16 0.9999999999999999 0 -64 ] [ 1.0000000000000007 -5.456456664689579e-16 0 128 ] 270 1 1
|
||||
( -6688 4096 -304 ) ( -6688 4096 -303 ) ( -6689 4096 -304 ) canals [ -1.0000000000000007 5.456456664689579e-16 0 -128 ] [ 0 0 -1 16 ] 0 1 1
|
||||
( -6496 3648 -368 ) ( -6496 3648 -367 ) ( -6496 3649 -368 ) canals [ -4.812271809458903e-16 0.9999999999999999 0 -64 ] [ 0 0 -1 16 ] 0 1 1
|
||||
}
|
||||
// brush 129
|
||||
{
|
||||
( -7296 4096 -304 ) ( -7296 4097 -304 ) ( -7296 4096 -303 ) tile [ 0 -1 0 0 ] [ 0 0 -1 16 ] 270 1 1
|
||||
( -8544 4096 -304 ) ( -8544 4096 -303 ) ( -8543 4096 -304 ) tile [ 1 0 0 0 ] [ 0 0 -1 16 ] 270 1 1
|
||||
( -8544 4096 -304 ) ( -8543 4096 -304 ) ( -8544 4097 -304 ) tile [ -1 0 0 0 ] [ 0 -1 0 0 ] 270 1 1
|
||||
( -7648 4544 -240 ) ( -7648 4545 -240 ) ( -7647 4544 -240 ) tile [ 1 0 0 0 ] [ 0 -1 0 0 ] 270 1 1
|
||||
( -7648 4224 -240 ) ( -7647 4224 -240 ) ( -7648 4224 -239 ) tile [ -1 0 0 0 ] [ 0 0 -1 16 ] 180 1 1
|
||||
( -6432 4544 -240 ) ( -6432 4544 -239 ) ( -6432 4545 -240 ) tile [ 0 1 0 0 ] [ 0 0 -1 16 ] 270 1 1
|
||||
}
|
||||
// brush 130
|
||||
{
|
||||
( -7232 4096 -304 ) ( -7232 4097 -304 ) ( -7232 4096 -303 ) canals [ 4.812271809458903e-16 -0.9999999999999999 0 64 ] [ 0 0 -1 16 ] 270 1 1
|
||||
( -7168 3392 -368 ) ( -7169 3392 -368 ) ( -7168 3392 -367 ) canals [ 1.0000000000000007 -5.456456664689579e-16 0 128 ] [ 0 0 -1 16 ] 180 1 1
|
||||
( -7168 3648 -1600 ) ( -7168 3649 -1600 ) ( -7169 3648 -1600 ) canals [ 4.812271809458903e-16 -0.9999999999999999 0 64 ] [ 1.0000000000000007 -5.456456664689579e-16 0 128 ] 180 1 1
|
||||
( -7360 4096 -304 ) ( -7361 4096 -304 ) ( -7360 4097 -304 ) canals [ -4.812271809458903e-16 0.9999999999999999 0 -64 ] [ 1.0000000000000007 -5.456456664689579e-16 0 128 ] 270 1 1
|
||||
( -7360 4096 -304 ) ( -7360 4096 -303 ) ( -7361 4096 -304 ) canals [ -1.0000000000000007 5.456456664689579e-16 0 -128 ] [ 0 0 -1 16 ] 0 1 1
|
||||
( -7168 3648 -368 ) ( -7168 3648 -367 ) ( -7168 3649 -368 ) canals [ -4.812271809458903e-16 0.9999999999999999 0 -64 ] [ 0 0 -1 16 ] 0 1 1
|
||||
}
|
||||
// brush 131
|
||||
{
|
||||
( -7296 3264 -304 ) ( -7296 3265 -304 ) ( -7296 3264 -303 ) tile [ 0 -1 0 0 ] [ 0 0 -1 16 ] 270 1 1
|
||||
( -8544 3136 -304 ) ( -8544 3136 -303 ) ( -8543 3136 -304 ) tile [ 1 0 0 0 ] [ 0 0 -1 16 ] 270 1 1
|
||||
( -8544 3264 -304 ) ( -8543 3264 -304 ) ( -8544 3265 -304 ) tile [ -1 0 0 0 ] [ 0 -1 0 0 ] 270 1 1
|
||||
( -7648 3712 -240 ) ( -7648 3713 -240 ) ( -7647 3712 -240 ) tile [ 1 0 0 0 ] [ 0 -1 0 0 ] 270 1 1
|
||||
( -7648 3392 -240 ) ( -7647 3392 -240 ) ( -7648 3392 -239 ) tile [ -1 0 0 0 ] [ 0 0 -1 16 ] 180 1 1
|
||||
( -6304 3712 -240 ) ( -6304 3712 -239 ) ( -6304 3713 -240 ) tile [ 0 1 0 0 ] [ 0 0 -1 16 ] 270 1 1
|
||||
}
|
||||
// brush 132
|
||||
{
|
||||
( -6560 5344 -304 ) ( -6560 5344 -303 ) ( -6560 5343 -304 ) tile [ 1.9967346175427393e-16 1 0 0 ] [ 0 0 -1 16 ] 270 1 1
|
||||
( -6112 3392 -240 ) ( -6112 3392 -239 ) ( -6111 3392 -240 ) tile [ -1 1.9967346175427393e-16 0 0 ] [ 0 0 -1 16 ] 270 1 1
|
||||
( -6560 5344 -304 ) ( -6560 5343 -304 ) ( -6559 5344 -304 ) tile [ -1 0 0 0 ] [ 0 -1 0 0 ] 270 1 1
|
||||
( -6112 4448 -240 ) ( -6111 4448 -240 ) ( -6112 4447 -240 ) tile [ 1 0 0 0 ] [ 0 -1 0 0 ] 270 1 1
|
||||
( -6560 4096 -304 ) ( -6559 4096 -304 ) ( -6560 4096 -303 ) tile [ 1 -1.9967346175427393e-16 0 0 ] [ 0 0 -1 16 ] 270 1 1
|
||||
( -6304 4448 -240 ) ( -6304 4447 -240 ) ( -6304 4448 -239 ) tile [ -1.9967346175427393e-16 -1 0 0 ] [ 0 0 -1 16 ] 180 1 1
|
||||
}
|
||||
// brush 133
|
||||
{
|
||||
( -7296 5344 -304 ) ( -7296 5344 -303 ) ( -7296 5343 -304 ) tile [ 1.9967346175427393e-16 1 0 0 ] [ 0 0 -1 16 ] 270 1 1
|
||||
( -6848 3392 -240 ) ( -6848 3392 -239 ) ( -6847 3392 -240 ) tile [ -1 1.9967346175427393e-16 0 0 ] [ 0 0 -1 16 ] 270 1 1
|
||||
( -7296 5344 -304 ) ( -7296 5343 -304 ) ( -7295 5344 -304 ) tile [ -1 0 0 0 ] [ 0 -1 0 0 ] 270 1 1
|
||||
( -6848 4448 -240 ) ( -6847 4448 -240 ) ( -6848 4447 -240 ) tile [ 1 0 0 0 ] [ 0 -1 0 0 ] 270 1 1
|
||||
( -7296 4096 -304 ) ( -7295 4096 -304 ) ( -7296 4096 -303 ) tile [ 1 -1.9967346175427393e-16 0 0 ] [ 0 0 -1 16 ] 270 1 1
|
||||
( -7168 4448 -240 ) ( -7168 4447 -240 ) ( -7168 4448 -239 ) tile [ -1.9967346175427393e-16 -1 0 0 ] [ 0 0 -1 16 ] 180 1 1
|
||||
}
|
||||
// brush 134
|
||||
{
|
||||
( -7168 3328 -368 ) ( -7168 3329 -368 ) ( -7168 3328 -367 ) canals [ 6.762803511326634e-16 1.0000000000000007 0 128 ] [ 0 0 -1 16 ] 180 1 1
|
||||
( -7008 3328 -368 ) ( -7008 3328 -367 ) ( -7007 3328 -368 ) canals [ -0.9999999999999998 -2.9040269150165053e-16 0 -64 ] [ 0 0 -1 16 ] 0 1 1
|
||||
( -7008 3328 -768 ) ( -7007 3328 -768 ) ( -7008 3329 -768 ) canals [ 4.812271809458903e-16 -0.9999999999999999 0 64 ] [ 1.0000000000000007 -5.456456664689579e-16 0 128 ] 180 1 1
|
||||
( -6560 3520 -304 ) ( -6560 3521 -304 ) ( -6559 3520 -304 ) canals [ -4.812271809458903e-16 0.9999999999999999 0 -64 ] [ 1.0000000000000007 -5.456456664689579e-16 0 128 ] 270 1 1
|
||||
( -6560 3392 -304 ) ( -6559 3392 -304 ) ( -6560 3392 -303 ) canals [ 0.9999999999999998 2.9040269150165053e-16 0 64 ] [ 0 0 -1 16 ] 270 1 1
|
||||
( -6560 3520 -304 ) ( -6560 3520 -303 ) ( -6560 3521 -304 ) canals [ -6.762803511326634e-16 -1.0000000000000007 0 -128 ] [ 0 0 -1 16 ] 0 1 1
|
||||
}
|
||||
// brush 135
|
||||
{
|
||||
( -7168 4096 -368 ) ( -7168 4097 -368 ) ( -7168 4096 -367 ) canals [ 6.762803511326634e-16 1.0000000000000007 0 128 ] [ 0 0 -1 16 ] 180 1 1
|
||||
( -7008 4096 -368 ) ( -7008 4096 -367 ) ( -7007 4096 -368 ) canals [ -0.9999999999999998 -2.9040269150165053e-16 0 -64 ] [ 0 0 -1 16 ] 0 1 1
|
||||
( -7008 4096 -768 ) ( -7007 4096 -768 ) ( -7008 4097 -768 ) canals [ 4.812271809458903e-16 -0.9999999999999999 0 64 ] [ 1.0000000000000007 -5.456456664689579e-16 0 128 ] 180 1 1
|
||||
( -6560 4288 -304 ) ( -6560 4289 -304 ) ( -6559 4288 -304 ) canals [ -4.812271809458903e-16 0.9999999999999999 0 -64 ] [ 1.0000000000000007 -5.456456664689579e-16 0 128 ] 270 1 1
|
||||
( -6560 4160 -304 ) ( -6559 4160 -304 ) ( -6560 4160 -303 ) canals [ 0.9999999999999998 2.9040269150165053e-16 0 64 ] [ 0 0 -1 16 ] 270 1 1
|
||||
( -6560 4288 -304 ) ( -6560 4288 -303 ) ( -6560 4289 -304 ) canals [ -6.762803511326634e-16 -1.0000000000000007 0 -128 ] [ 0 0 -1 16 ] 0 1 1
|
||||
}
|
||||
}
|
||||
// entity 1
|
||||
{
|
||||
@@ -1113,10 +1229,11 @@
|
||||
{
|
||||
"classname" "enemy_spawn"
|
||||
"origin" "648 -680 -232"
|
||||
"angles" "0 -90 0"
|
||||
"angles" "0 90 0"
|
||||
"head" "field medic"
|
||||
"key" "fence_gate"
|
||||
"disable_ai" "true"
|
||||
"spawn_order" "0"
|
||||
}
|
||||
// entity 3
|
||||
{
|
||||
@@ -1128,8 +1245,9 @@
|
||||
"classname" "enemy_spawn"
|
||||
"origin" "3256 248 -232"
|
||||
"head" "field medic"
|
||||
"angles" "0 0 0"
|
||||
"angles" "0 180 0"
|
||||
"disable_ai" "true"
|
||||
"spawn_order" "1"
|
||||
}
|
||||
// entity 5
|
||||
{
|
||||
@@ -1384,16 +1502,18 @@
|
||||
{
|
||||
"classname" "enemy_spawn"
|
||||
"origin" "2568 4504 -232"
|
||||
"angles" "0 180 0"
|
||||
"angles" "0 0 0"
|
||||
"head" "super market shopper"
|
||||
"spawn_order" "2"
|
||||
}
|
||||
// entity 24
|
||||
{
|
||||
"classname" "enemy_spawn"
|
||||
"origin" "648 6760 -232"
|
||||
"angles" "0 90 0"
|
||||
"angles" "0 -90 0"
|
||||
"head" "green grocer"
|
||||
"key" "fence_shaft"
|
||||
"spawn_order" "3"
|
||||
}
|
||||
// entity 25
|
||||
{
|
||||
@@ -1597,3 +1717,189 @@
|
||||
"origin" "2712 5240 792"
|
||||
"head_id" "13"
|
||||
}
|
||||
// entity 45
|
||||
{
|
||||
"classname" "enemy_spawn"
|
||||
"origin" "-184 -584 -232"
|
||||
"angles" "0 0 0"
|
||||
"head" "commando"
|
||||
"key" ""
|
||||
"disable_ai" "true"
|
||||
}
|
||||
// entity 46
|
||||
{
|
||||
"classname" "enemy_spawn"
|
||||
"origin" "-184 -504 -232"
|
||||
"angles" "0 0 0"
|
||||
"head" "angry demonstrator"
|
||||
"key" ""
|
||||
"disable_ai" "true"
|
||||
}
|
||||
// entity 47
|
||||
{
|
||||
"classname" "enemy_spawn"
|
||||
"origin" "-184 -440 -232"
|
||||
"angles" "0 0 0"
|
||||
"head" "goblin"
|
||||
"key" ""
|
||||
"disable_ai" "true"
|
||||
}
|
||||
// entity 48
|
||||
{
|
||||
"classname" "enemy_spawn"
|
||||
"origin" "-184 -360 -232"
|
||||
"angles" "0 0 0"
|
||||
"head" "green grocer"
|
||||
"key" ""
|
||||
"disable_ai" "true"
|
||||
}
|
||||
// entity 49
|
||||
{
|
||||
"classname" "enemy_spawn"
|
||||
"origin" "-184 -280 -232"
|
||||
"angles" "0 0 0"
|
||||
"head" "highland hammer thrower"
|
||||
"key" ""
|
||||
"disable_ai" "true"
|
||||
}
|
||||
// entity 50
|
||||
{
|
||||
"classname" "enemy_spawn"
|
||||
"origin" "-184 -184 -232"
|
||||
"angles" "0 0 0"
|
||||
"head" "legionnaire"
|
||||
"key" ""
|
||||
"disable_ai" "true"
|
||||
}
|
||||
// entity 51
|
||||
{
|
||||
"classname" "enemy_spawn"
|
||||
"origin" "-184 -104 -232"
|
||||
"angles" "0 0 0"
|
||||
"head" "mig pilot"
|
||||
"key" ""
|
||||
"disable_ai" "true"
|
||||
}
|
||||
// entity 52
|
||||
{
|
||||
"classname" "enemy_spawn"
|
||||
"origin" "-184 -8 -232"
|
||||
"angles" "0 0 0"
|
||||
"head" "soldier ant"
|
||||
"key" ""
|
||||
"disable_ai" "true"
|
||||
}
|
||||
// entity 53
|
||||
{
|
||||
"classname" "enemy_spawn"
|
||||
"origin" "-184 -680 -232"
|
||||
"angles" "0 0 0"
|
||||
"head" "super market shopper"
|
||||
"key" ""
|
||||
"disable_ai" "true"
|
||||
}
|
||||
// entity 54
|
||||
{
|
||||
"classname" "enemy_spawn"
|
||||
"origin" "-184 88 -232"
|
||||
"angles" "0 0 0"
|
||||
"head" "nanny"
|
||||
"key" ""
|
||||
"disable_ai" "true"
|
||||
}
|
||||
// entity 55
|
||||
{
|
||||
"classname" "enemy_spawn"
|
||||
"origin" "-184 184 -232"
|
||||
"angles" "0 0 0"
|
||||
"head" "chicago gangster"
|
||||
"key" ""
|
||||
"disable_ai" "true"
|
||||
}
|
||||
// entity 56
|
||||
{
|
||||
"classname" "enemy_spawn"
|
||||
"origin" "-184 264 -232"
|
||||
"angles" "0 0 0"
|
||||
"head" "carnival knife thrower"
|
||||
"key" ""
|
||||
"disable_ai" "true"
|
||||
}
|
||||
// entity 57
|
||||
{
|
||||
"classname" "enemy_spawn"
|
||||
"origin" "-184 344 -232"
|
||||
"angles" "0 0 0"
|
||||
"head" "snow plough operator"
|
||||
"key" ""
|
||||
"disable_ai" "true"
|
||||
}
|
||||
// entity 58
|
||||
{
|
||||
"classname" "enemy_spawn"
|
||||
"origin" "-184 424 -232"
|
||||
"angles" "0 0 0"
|
||||
"head" "salty sea dog"
|
||||
"key" ""
|
||||
"disable_ai" "true"
|
||||
}
|
||||
// entity 59
|
||||
{
|
||||
"classname" "enemy_spawn"
|
||||
"origin" "-184 504 -232"
|
||||
"angles" "0 0 0"
|
||||
"head" "geisha"
|
||||
"key" ""
|
||||
"disable_ai" "true"
|
||||
}
|
||||
// entity 60
|
||||
{
|
||||
"classname" "platform"
|
||||
"target" "elevator_02_target"
|
||||
// brush 0
|
||||
{
|
||||
( -7168 3840 -2720 ) ( -7168 3841 -2720 ) ( -7168 3840 -2719 ) canals [ 0 -1 0 0 ] [ 0 0 -1 -96 ] 90 1 1
|
||||
( -7136 3392 -2720 ) ( -7136 3392 -2719 ) ( -7135 3392 -2720 ) canals [ 1 0 0 0 ] [ 0 0 -1 -96 ] 90 1 1
|
||||
( -7136 3840 -2720 ) ( -7135 3840 -2720 ) ( -7136 3841 -2720 ) canals [ -1 0 0 0 ] [ 0 -1 0 0 ] 90 1 1
|
||||
( -6496 4032 -1408 ) ( -6496 4033 -1408 ) ( -6495 4032 -1408 ) canals [ 1 0 0 0 ] [ 0 -1 0 0 ] 90 1 1
|
||||
( -6496 4096 -2656 ) ( -6495 4096 -2656 ) ( -6496 4096 -2655 ) canals [ -1 0 0 0 ] [ 0 0 -1 -96 ] 90 1 1
|
||||
( -6560 4032 -2656 ) ( -6560 4032 -2655 ) ( -6560 4033 -2656 ) canals [ 0 1 0 0 ] [ 0 0 -1 -96 ] 90 1 1
|
||||
}
|
||||
// brush 1
|
||||
{
|
||||
( -6848 3712 -1408 ) ( -6848 3713 -1408 ) ( -6848 3712 -1407 ) origin [ 0 -1 0 0 ] [ 0 0 -1 0 ] 270 1 1
|
||||
( -6848 3712 -1408 ) ( -6848 3712 -1407 ) ( -6847 3712 -1408 ) origin [ 1 0 0 0 ] [ 0 0 -1 0 ] 270 1 1
|
||||
( -6848 3712 -1408 ) ( -6847 3712 -1408 ) ( -6848 3713 -1408 ) origin [ -1 0 0 0 ] [ 0 -1 0 0 ] 270 1 1
|
||||
( -6784 3776 -1376 ) ( -6784 3777 -1376 ) ( -6783 3776 -1376 ) origin [ 1 0 0 0 ] [ 0 -1 0 0 ] 270 1 1
|
||||
( -6784 3776 -1376 ) ( -6783 3776 -1376 ) ( -6784 3776 -1375 ) origin [ -1 0 0 0 ] [ 0 0 -1 0 ] 270 1 1
|
||||
( -6784 3776 -1376 ) ( -6784 3776 -1375 ) ( -6784 3777 -1376 ) origin [ 0 1 0 0 ] [ 0 0 -1 0 ] 270 1 1
|
||||
}
|
||||
}
|
||||
// entity 61
|
||||
{
|
||||
"classname" "platform_target"
|
||||
"origin" "-6824 3736 -264"
|
||||
"targetname" "elevator_02_target"
|
||||
}
|
||||
// entity 62
|
||||
{
|
||||
"classname" "water"
|
||||
// brush 0
|
||||
{
|
||||
( -7296 6976 -1632 ) ( -7296 6977 -1632 ) ( -7296 6976 -1631 ) water [ 0 -1 0 0 ] [ 0 0 -1 -32 ] 180 1 1
|
||||
( -6080 3264 -1632 ) ( -6080 3264 -1631 ) ( -6079 3264 -1632 ) water [ 1 0 0 0 ] [ 0 0 -1 -32 ] 90 1 1
|
||||
( -6080 6976 -1632 ) ( -6079 6976 -1632 ) ( -6080 6977 -1632 ) water [ -1 0 0 0 ] [ 0 -1 0 0 ] 270 1 1
|
||||
( -5568 7360 -1568 ) ( -5568 7361 -1568 ) ( -5567 7360 -1568 ) water [ 1 0 0 0 ] [ 0 -1 0 0 ] 270 1 1
|
||||
( -5568 4224 -1568 ) ( -5567 4224 -1568 ) ( -5568 4224 -1567 ) water [ -1 0 0 0 ] [ 0 0 -1 -32 ] 270 1 1
|
||||
( -5504 7360 -1568 ) ( -5504 7360 -1567 ) ( -5504 7361 -1568 ) water [ 0 1 0 0 ] [ 0 0 -1 -32 ] 270 1 1
|
||||
}
|
||||
}
|
||||
// entity 63
|
||||
{
|
||||
"classname" "enemy_spawn"
|
||||
"origin" "-184 584 -232"
|
||||
"angles" "0 0 0"
|
||||
"head" "troll"
|
||||
"key" ""
|
||||
"disable_ai" "true"
|
||||
}
|
||||
|
||||
BIN
assets/models/beaming.glb
Normal file
BIN
assets/models/beaming.glb
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
assets/models/characters/carnival knife thrower.glb
Normal file
BIN
assets/models/characters/carnival knife thrower.glb
Normal file
Binary file not shown.
BIN
assets/models/characters/chicago gangster.glb
Normal file
BIN
assets/models/characters/chicago gangster.glb
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
assets/models/characters/geisha.glb
Normal file
BIN
assets/models/characters/geisha.glb
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
assets/models/characters/highland hammer thrower.glb
Normal file
BIN
assets/models/characters/highland hammer thrower.glb
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
assets/models/characters/nanny.glb
Normal file
BIN
assets/models/characters/nanny.glb
Normal file
Binary file not shown.
BIN
assets/models/characters/salty sea dog.glb
Normal file
BIN
assets/models/characters/salty sea dog.glb
Normal file
Binary file not shown.
BIN
assets/models/characters/snow plough operator.glb
Normal file
BIN
assets/models/characters/snow plough operator.glb
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
assets/models/characters/troll.glb
Normal file
BIN
assets/models/characters/troll.glb
Normal file
Binary file not shown.
Binary file not shown.
BIN
assets/models/head_drops/aa_gun.glb
Normal file
BIN
assets/models/head_drops/aa_gun.glb
Normal file
Binary file not shown.
BIN
assets/models/head_drops/airstrike.glb
Normal file
BIN
assets/models/head_drops/airstrike.glb
Normal file
Binary file not shown.
BIN
assets/models/head_drops/arrow.glb
Normal file
BIN
assets/models/head_drops/arrow.glb
Normal file
Binary file not shown.
BIN
assets/models/head_drops/automobile.glb
Normal file
BIN
assets/models/head_drops/automobile.glb
Normal file
Binary file not shown.
BIN
assets/models/head_drops/boat.glb
Normal file
BIN
assets/models/head_drops/boat.glb
Normal file
Binary file not shown.
BIN
assets/models/head_drops/bouncer.glb
Normal file
BIN
assets/models/head_drops/bouncer.glb
Normal file
Binary file not shown.
BIN
assets/models/head_drops/cell.glb
Normal file
BIN
assets/models/head_drops/cell.glb
Normal file
Binary file not shown.
BIN
assets/models/head_drops/curver.glb
Normal file
BIN
assets/models/head_drops/curver.glb
Normal file
Binary file not shown.
BIN
assets/models/head_drops/dropped.glb
Normal file
BIN
assets/models/head_drops/dropped.glb
Normal file
Binary file not shown.
BIN
assets/models/head_drops/far_sight.glb
Normal file
BIN
assets/models/head_drops/far_sight.glb
Normal file
Binary file not shown.
BIN
assets/models/head_drops/flamer.glb
Normal file
BIN
assets/models/head_drops/flamer.glb
Normal file
Binary file not shown.
BIN
assets/models/head_drops/glider.glb
Normal file
BIN
assets/models/head_drops/glider.glb
Normal file
Binary file not shown.
BIN
assets/models/head_drops/guided_missile.glb
Normal file
BIN
assets/models/head_drops/guided_missile.glb
Normal file
Binary file not shown.
BIN
assets/models/head_drops/gun.glb
Normal file
BIN
assets/models/head_drops/gun.glb
Normal file
Binary file not shown.
BIN
assets/models/head_drops/hover.glb
Normal file
BIN
assets/models/head_drops/hover.glb
Normal file
Binary file not shown.
BIN
assets/models/head_drops/invisiblity.glb
Normal file
BIN
assets/models/head_drops/invisiblity.glb
Normal file
Binary file not shown.
BIN
assets/models/head_drops/laser.glb
Normal file
BIN
assets/models/head_drops/laser.glb
Normal file
Binary file not shown.
BIN
assets/models/head_drops/medic.glb
Normal file
BIN
assets/models/head_drops/medic.glb
Normal file
Binary file not shown.
BIN
assets/models/head_drops/mine.glb
Normal file
BIN
assets/models/head_drops/mine.glb
Normal file
Binary file not shown.
BIN
assets/models/head_drops/missile.glb
Normal file
BIN
assets/models/head_drops/missile.glb
Normal file
Binary file not shown.
BIN
assets/models/head_drops/motorbike.glb
Normal file
BIN
assets/models/head_drops/motorbike.glb
Normal file
Binary file not shown.
BIN
assets/models/head_drops/none.glb
Normal file
BIN
assets/models/head_drops/none.glb
Normal file
Binary file not shown.
BIN
assets/models/head_drops/pet_attack.glb
Normal file
BIN
assets/models/head_drops/pet_attack.glb
Normal file
Binary file not shown.
BIN
assets/models/head_drops/plane.glb
Normal file
BIN
assets/models/head_drops/plane.glb
Normal file
Binary file not shown.
BIN
assets/models/head_drops/power_jump.glb
Normal file
BIN
assets/models/head_drops/power_jump.glb
Normal file
Binary file not shown.
BIN
assets/models/head_drops/power_shield.glb
Normal file
BIN
assets/models/head_drops/power_shield.glb
Normal file
Binary file not shown.
BIN
assets/models/head_drops/quake.glb
Normal file
BIN
assets/models/head_drops/quake.glb
Normal file
Binary file not shown.
BIN
assets/models/head_drops/shield.glb
Normal file
BIN
assets/models/head_drops/shield.glb
Normal file
Binary file not shown.
BIN
assets/models/head_drops/spray.glb
Normal file
BIN
assets/models/head_drops/spray.glb
Normal file
Binary file not shown.
BIN
assets/models/head_drops/sprouter.glb
Normal file
BIN
assets/models/head_drops/sprouter.glb
Normal file
Binary file not shown.
BIN
assets/models/head_drops/swarm.glb
Normal file
BIN
assets/models/head_drops/swarm.glb
Normal file
Binary file not shown.
BIN
assets/models/head_drops/teleport.glb
Normal file
BIN
assets/models/head_drops/teleport.glb
Normal file
Binary file not shown.
BIN
assets/models/head_drops/thrown.glb
Normal file
BIN
assets/models/head_drops/thrown.glb
Normal file
Binary file not shown.
BIN
assets/models/head_drops/thruster.glb
Normal file
BIN
assets/models/head_drops/thruster.glb
Normal file
Binary file not shown.
BIN
assets/models/head_drops/turbo.glb
Normal file
BIN
assets/models/head_drops/turbo.glb
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
assets/models/projectiles/carrot.glb
Normal file
BIN
assets/models/projectiles/carrot.glb
Normal file
Binary file not shown.
BIN
assets/models/projectiles/club.glb
Normal file
BIN
assets/models/projectiles/club.glb
Normal file
Binary file not shown.
BIN
assets/models/projectiles/fan.glb
Normal file
BIN
assets/models/projectiles/fan.glb
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
54
crates/hedz_reloaded/Cargo.toml
Normal file
54
crates/hedz_reloaded/Cargo.toml
Normal file
@@ -0,0 +1,54 @@
|
||||
[package]
|
||||
name = "hedz_reloaded"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
build = "build.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "hedz_reloaded_server"
|
||||
|
||||
[features]
|
||||
default = ["client"]
|
||||
client = [
|
||||
"bevy/bevy_audio",
|
||||
"bevy/bevy_window",
|
||||
# depend on `winit`
|
||||
"bevy/bevy_winit",
|
||||
"bevy/x11",
|
||||
"bevy/custom_cursor",
|
||||
"bevy_replicon/client",
|
||||
"bevy_replicon_renet/client",
|
||||
"bevy_trenchbroom/client",
|
||||
]
|
||||
dbg = ["avian3d/debug-plugin", "bevy/debug", "dep:bevy-inspector-egui"]
|
||||
|
||||
[dependencies]
|
||||
avian3d = { workspace = true }
|
||||
bevy = { workspace = true }
|
||||
bevy-inspector-egui = { workspace = true, optional = true }
|
||||
bevy-persistent = { workspace = true }
|
||||
bevy-steamworks = { workspace = true }
|
||||
bevy_asset_loader = { workspace = true }
|
||||
bevy_ballistic = { workspace = true }
|
||||
bevy_common_assets = { workspace = true }
|
||||
bevy_debug_log = { workspace = true }
|
||||
bevy_replicon = { workspace = true }
|
||||
bevy_replicon_renet = { workspace = true }
|
||||
bevy_sprite3d = { workspace = true }
|
||||
bevy_trenchbroom = { workspace = true }
|
||||
bevy_trenchbroom_avian = { workspace = true }
|
||||
clap = { workspace = true }
|
||||
dirs = { workspace = true }
|
||||
happy_feet = { workspace = true }
|
||||
nil = { workspace = true }
|
||||
rand = { workspace = true }
|
||||
ron = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
steamworks = { workspace = true }
|
||||
|
||||
[build-dependencies]
|
||||
vergen-gitcl = "1.0"
|
||||
|
||||
[lints.clippy]
|
||||
too_many_arguments = "allow"
|
||||
type_complexity = "allow"
|
||||
@@ -1,13 +1,15 @@
|
||||
use super::TriggerArrow;
|
||||
use crate::{
|
||||
GameState, billboards::Billboard, global_observer, heads_database::HeadsDatabase,
|
||||
hitpoints::Hit, loading_assets::GameAssets, physics_layers::GameLayer, sounds::PlaySound,
|
||||
utils::sprite_3d_animation::AnimationTimer,
|
||||
GameState, global_observer,
|
||||
heads_database::HeadsDatabase,
|
||||
hitpoints::Hit,
|
||||
loading_assets::GameAssets,
|
||||
physics_layers::GameLayer,
|
||||
utils::{Billboard, sprite_3d_animation::AnimationTimer},
|
||||
};
|
||||
use avian3d::prelude::*;
|
||||
use bevy::{pbr::NotShadowCaster, prelude::*};
|
||||
use bevy_sprite3d::{Sprite3dBuilder, Sprite3dParams};
|
||||
use std::f32::consts::PI;
|
||||
use bevy::{light::NotShadowCaster, prelude::*};
|
||||
use bevy_sprite3d::Sprite3d;
|
||||
|
||||
#[derive(Component)]
|
||||
struct ArrowProjectile {
|
||||
@@ -27,25 +29,26 @@ pub fn plugin(app: &mut App) {
|
||||
global_observer!(app, on_trigger_arrow);
|
||||
}
|
||||
|
||||
fn setup(mut commands: Commands, assets: Res<GameAssets>, mut sprite_params: Sprite3dParams) {
|
||||
fn setup(mut commands: Commands, assets: Res<GameAssets>, asset_server: Res<AssetServer>) {
|
||||
let layout = TextureAtlasLayout::from_grid(UVec2::splat(256), 7, 6, None, None);
|
||||
let texture_atlas_layout = sprite_params.atlas_layouts.add(layout);
|
||||
let layout = asset_server.add(layout);
|
||||
|
||||
commands.insert_resource(ShotAssets {
|
||||
image: assets.impact_atlas.clone(),
|
||||
layout: texture_atlas_layout,
|
||||
layout,
|
||||
});
|
||||
}
|
||||
|
||||
fn on_trigger_arrow(
|
||||
trigger: Trigger<TriggerArrow>,
|
||||
trigger: On<TriggerArrow>,
|
||||
mut commands: Commands,
|
||||
query_transform: Query<&Transform>,
|
||||
heads_db: Res<HeadsDatabase>,
|
||||
) {
|
||||
let state = trigger.0;
|
||||
|
||||
commands.trigger(PlaySound::Crossbow);
|
||||
#[cfg(feature = "client")]
|
||||
commands.trigger(crate::protocol::PlaySound::Crossbow);
|
||||
|
||||
let rotation = if let Some(target) = state.target {
|
||||
let t = query_transform
|
||||
@@ -55,14 +58,18 @@ fn on_trigger_arrow(
|
||||
.looking_at(t.translation, Vec3::Y)
|
||||
.rotation
|
||||
} else {
|
||||
state.rot.mul_quat(Quat::from_rotation_y(PI))
|
||||
state.rot()
|
||||
};
|
||||
|
||||
let mut t = Transform::from_translation(state.pos).with_rotation(rotation);
|
||||
t.translation += t.forward().as_vec3() * 2.;
|
||||
let mut transform = Transform::from_translation(state.pos).with_rotation(rotation);
|
||||
transform.translation += transform.forward().as_vec3() * 2.;
|
||||
|
||||
let damage = heads_db.head_stats(state.head).damage;
|
||||
commands.spawn((Name::new("projectile-arrow"), ArrowProjectile { damage }, t));
|
||||
commands.spawn((
|
||||
Name::new("projectile-arrow"),
|
||||
ArrowProjectile { damage },
|
||||
transform,
|
||||
));
|
||||
}
|
||||
|
||||
fn update(
|
||||
@@ -70,7 +77,6 @@ fn update(
|
||||
query: Query<(Entity, &Transform, &ArrowProjectile)>,
|
||||
spatial_query: SpatialQuery,
|
||||
assets: Res<ShotAssets>,
|
||||
mut sprite_params: Sprite3dParams,
|
||||
) {
|
||||
for (e, t, arrow) in query.iter() {
|
||||
let filter = SpatialQueryFilter::from_mask(LayerMask(
|
||||
@@ -85,28 +91,29 @@ fn update(
|
||||
&ShapeCastConfig::from_max_distance(150.),
|
||||
&filter,
|
||||
) {
|
||||
cmds.entity(first_hit.entity).trigger(Hit {
|
||||
cmds.trigger(Hit {
|
||||
damage: arrow.damage,
|
||||
entity: first_hit.entity,
|
||||
});
|
||||
|
||||
cmds.spawn(
|
||||
Sprite3dBuilder {
|
||||
image: assets.image.clone(),
|
||||
cmds.spawn((
|
||||
Sprite3d {
|
||||
pixels_per_metre: 128.,
|
||||
alpha_mode: AlphaMode::Blend,
|
||||
unlit: true,
|
||||
..default()
|
||||
}
|
||||
.bundle_with_atlas(
|
||||
&mut sprite_params,
|
||||
TextureAtlas {
|
||||
},
|
||||
Sprite {
|
||||
image: assets.image.clone(),
|
||||
texture_atlas: Some(TextureAtlas {
|
||||
layout: assets.layout.clone(),
|
||||
index: 0,
|
||||
},
|
||||
),
|
||||
)
|
||||
}),
|
||||
..default()
|
||||
},
|
||||
))
|
||||
.insert((
|
||||
Billboard,
|
||||
Billboard::All,
|
||||
Transform::from_translation(first_hit.point1),
|
||||
NotShadowCaster,
|
||||
AnimationTimer::new(Timer::from_seconds(0.005, TimerMode::Repeating)),
|
||||
206
crates/hedz_reloaded/src/abilities/curver.rs
Normal file
206
crates/hedz_reloaded/src/abilities/curver.rs
Normal file
@@ -0,0 +1,206 @@
|
||||
use crate::{
|
||||
GameState,
|
||||
abilities::{BuildExplosionSprite, ProjectileId, TriggerCurver},
|
||||
heads_database::HeadsDatabase,
|
||||
hitpoints::Hit,
|
||||
physics_layers::GameLayer,
|
||||
tb_entities::EnemySpawn,
|
||||
utils::global_observer,
|
||||
};
|
||||
use avian3d::prelude::*;
|
||||
use bevy::prelude::*;
|
||||
use bevy_replicon::prelude::{Replicated, SendMode, ServerTriggerExt, ToClients};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
const MAX_SHOT_AGES: f32 = 15.;
|
||||
|
||||
#[derive(Component, Reflect, Deserialize, Serialize)]
|
||||
#[reflect(Component)]
|
||||
pub struct CurverProjectile {
|
||||
time: f32,
|
||||
damage: u32,
|
||||
projectile: String,
|
||||
}
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
app.register_type::<CurverProjectile>();
|
||||
|
||||
app.add_systems(
|
||||
Update,
|
||||
(shot_collision, enemy_hit).run_if(in_state(GameState::Playing)),
|
||||
);
|
||||
#[cfg(feature = "client")]
|
||||
app.add_systems(Update, shot_visuals.run_if(in_state(GameState::Playing)));
|
||||
app.add_systems(
|
||||
FixedUpdate,
|
||||
(update, timeout).run_if(in_state(GameState::Playing)),
|
||||
);
|
||||
|
||||
global_observer!(app, on_trigger_curver);
|
||||
}
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
fn shot_visuals(
|
||||
mut commands: Commands,
|
||||
query: Query<(Entity, &CurverProjectile), Added<CurverProjectile>>,
|
||||
) {
|
||||
for (entity, projectile) in query.iter() {
|
||||
if commands.get_entity(entity).is_ok() {
|
||||
let child = commands
|
||||
.spawn((
|
||||
crate::utils::auto_rotate::AutoRotation(
|
||||
Quat::from_rotation_x(0.4) * Quat::from_rotation_z(0.3),
|
||||
),
|
||||
crate::protocol::GltfSceneRoot::Projectile(projectile.projectile.clone()),
|
||||
))
|
||||
.id();
|
||||
|
||||
commands.entity(entity).add_child(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn on_trigger_curver(
|
||||
trigger: On<TriggerCurver>,
|
||||
mut commands: Commands,
|
||||
query_transform: Query<&Transform>,
|
||||
time: Res<Time>,
|
||||
heads_db: Res<HeadsDatabase>,
|
||||
) {
|
||||
let state = trigger.event().0;
|
||||
|
||||
let rotation = if let Some(target) = state.target {
|
||||
let t = query_transform
|
||||
.get(target)
|
||||
.expect("target must have transform");
|
||||
Transform::from_translation(state.pos)
|
||||
.looking_at(t.translation, Vec3::Y)
|
||||
.rotation
|
||||
} else {
|
||||
state.rot()
|
||||
};
|
||||
|
||||
let head = heads_db.head_stats(state.head);
|
||||
|
||||
let mut transform = Transform::from_translation(state.pos).with_rotation(rotation);
|
||||
transform.translation += transform.forward().as_vec3() * 2.0;
|
||||
|
||||
let id = commands
|
||||
.spawn((
|
||||
Name::new("projectile-curver"),
|
||||
CurverProjectile {
|
||||
time: time.elapsed_secs(),
|
||||
damage: head.damage,
|
||||
projectile: head.projectile.clone(),
|
||||
},
|
||||
Collider::capsule_endpoints(0.5, Vec3::new(0., 0., 1.), Vec3::new(0., 0., -1.)),
|
||||
CollisionLayers::new(
|
||||
LayerMask(GameLayer::Projectile.to_bits()),
|
||||
LayerMask(state.target_layer.to_bits() | GameLayer::Level.to_bits()),
|
||||
),
|
||||
Sensor,
|
||||
RigidBody::Kinematic,
|
||||
CollisionEventsEnabled,
|
||||
Visibility::default(),
|
||||
transform,
|
||||
Replicated,
|
||||
ProjectileId(state.trigger_id),
|
||||
))
|
||||
.id();
|
||||
|
||||
debug!(id=?id, trigger_id = state.trigger_id, "Curver");
|
||||
}
|
||||
|
||||
fn enemy_hit(
|
||||
mut commands: Commands,
|
||||
mut collision_message_reader: MessageReader<CollisionStart>,
|
||||
query_shot: Query<&CurverProjectile>,
|
||||
query_npc: Query<&EnemySpawn>,
|
||||
) {
|
||||
for CollisionStart {
|
||||
collider1: e1,
|
||||
collider2: e2,
|
||||
..
|
||||
} in collision_message_reader.read()
|
||||
{
|
||||
if !query_shot.contains(*e1) && !query_shot.contains(*e2) {
|
||||
continue;
|
||||
}
|
||||
if !query_npc.contains(*e1) && !query_npc.contains(*e2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let (enemy_entity, projectile) = if query_npc.contains(*e1) {
|
||||
(*e1, query_shot.get(*e2))
|
||||
} else {
|
||||
(*e2, query_shot.get(*e1))
|
||||
};
|
||||
|
||||
if let Ok(projectile) = projectile {
|
||||
let damage = projectile.damage;
|
||||
commands
|
||||
.entity(enemy_entity)
|
||||
.trigger(|entity| Hit { entity, damage });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update(mut query: Query<&mut Transform, With<CurverProjectile>>) {
|
||||
for mut transform in query.iter_mut() {
|
||||
let forward = transform.forward();
|
||||
transform.translation += forward * 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
fn timeout(mut commands: Commands, query: Query<(Entity, &CurverProjectile)>, time: Res<Time>) {
|
||||
let current_time = time.elapsed_secs();
|
||||
|
||||
for (e, CurverProjectile { time, .. }) in query.iter() {
|
||||
if current_time > time + MAX_SHOT_AGES {
|
||||
commands.entity(e).despawn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn shot_collision(
|
||||
mut commands: Commands,
|
||||
mut collision_message_reader: MessageReader<CollisionStart>,
|
||||
query_shot: Query<&Transform, With<CurverProjectile>>,
|
||||
sensors: Query<(), With<Sensor>>,
|
||||
) {
|
||||
for CollisionStart {
|
||||
collider1: e1,
|
||||
collider2: e2,
|
||||
..
|
||||
} in collision_message_reader.read()
|
||||
{
|
||||
if !query_shot.contains(*e1) && !query_shot.contains(*e2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if sensors.contains(*e1) && sensors.contains(*e2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let shot_entity = if query_shot.contains(*e1) { *e1 } else { *e2 };
|
||||
|
||||
let Ok(shot_pos) = query_shot.get(shot_entity).map(|t| t.translation) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if let Ok(mut entity) = commands.get_entity(shot_entity) {
|
||||
entity.try_despawn();
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
commands.server_trigger(ToClients {
|
||||
message: BuildExplosionSprite {
|
||||
pos: shot_pos,
|
||||
pixels_per_meter: 128.,
|
||||
time: 0.01,
|
||||
},
|
||||
mode: SendMode::Broadcast,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,19 @@
|
||||
use super::TriggerGun;
|
||||
use crate::{
|
||||
GameState, billboards::Billboard, global_observer, heads_database::HeadsDatabase,
|
||||
hitpoints::Hit, loading_assets::GameAssets, physics_layers::GameLayer, sounds::PlaySound,
|
||||
tb_entities::EnemySpawn, utils::sprite_3d_animation::AnimationTimer,
|
||||
GameState,
|
||||
abilities::ProjectileId,
|
||||
global_observer,
|
||||
heads_database::HeadsDatabase,
|
||||
hitpoints::Hit,
|
||||
loading_assets::GameAssets,
|
||||
physics_layers::GameLayer,
|
||||
tb_entities::EnemySpawn,
|
||||
utils::{Billboard, sprite_3d_animation::AnimationTimer},
|
||||
};
|
||||
use avian3d::prelude::*;
|
||||
use bevy::{
|
||||
input::gamepad::{GamepadRumbleIntensity, GamepadRumbleRequest},
|
||||
pbr::NotShadowCaster,
|
||||
prelude::*,
|
||||
};
|
||||
use bevy_sprite3d::{Sprite3dBuilder, Sprite3dParams};
|
||||
use std::{f32::consts::PI, time::Duration};
|
||||
use bevy::{light::NotShadowCaster, prelude::*};
|
||||
use bevy_replicon::prelude::Replicated;
|
||||
use bevy_sprite3d::Sprite3d;
|
||||
|
||||
#[derive(Component)]
|
||||
struct GunProjectile {
|
||||
@@ -41,9 +43,9 @@ pub fn plugin(app: &mut App) {
|
||||
global_observer!(app, on_trigger_gun);
|
||||
}
|
||||
|
||||
fn setup(mut commands: Commands, assets: Res<GameAssets>, mut sprite_params: Sprite3dParams) {
|
||||
fn setup(mut commands: Commands, assets: Res<GameAssets>, asset_server: Res<AssetServer>) {
|
||||
let layout = TextureAtlasLayout::from_grid(UVec2::splat(256), 7, 6, None, None);
|
||||
let texture_atlas_layout = sprite_params.atlas_layouts.add(layout);
|
||||
let texture_atlas_layout = asset_server.add(layout);
|
||||
|
||||
commands.insert_resource(ShotAssets {
|
||||
image: assets.impact_atlas.clone(),
|
||||
@@ -53,12 +55,17 @@ fn setup(mut commands: Commands, assets: Res<GameAssets>, mut sprite_params: Spr
|
||||
|
||||
fn enemy_hit(
|
||||
mut commands: Commands,
|
||||
mut collision_event_reader: EventReader<CollisionStarted>,
|
||||
mut collision_message_reader: MessageReader<CollisionStart>,
|
||||
query_shot: Query<&GunProjectile>,
|
||||
query_npc: Query<&EnemySpawn>,
|
||||
heads_db: Res<HeadsDatabase>,
|
||||
) {
|
||||
for CollisionStarted(e1, e2) in collision_event_reader.read() {
|
||||
for CollisionStart {
|
||||
collider1: e1,
|
||||
collider2: e2,
|
||||
..
|
||||
} in collision_message_reader.read()
|
||||
{
|
||||
if !query_shot.contains(*e1) && !query_shot.contains(*e2) {
|
||||
continue;
|
||||
}
|
||||
@@ -74,31 +81,25 @@ fn enemy_hit(
|
||||
|
||||
if let Ok(head) = projectile.map(|p| p.owner_head) {
|
||||
let damage = heads_db.head_stats(head).damage;
|
||||
commands.entity(enemy_entity).trigger(Hit { damage });
|
||||
commands.trigger(Hit {
|
||||
damage,
|
||||
entity: enemy_entity,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn on_trigger_gun(
|
||||
trigger: Trigger<TriggerGun>,
|
||||
trigger: On<TriggerGun>,
|
||||
mut commands: Commands,
|
||||
query_transform: Query<&Transform>,
|
||||
time: Res<Time>,
|
||||
mut gizmo_assets: ResMut<Assets<GizmoAsset>>,
|
||||
gamepads: Query<(Entity, &Gamepad)>,
|
||||
mut rumble_requests: EventWriter<GamepadRumbleRequest>,
|
||||
) {
|
||||
let state = trigger.0;
|
||||
|
||||
commands.trigger(PlaySound::Gun);
|
||||
|
||||
for (e, _) in gamepads.iter() {
|
||||
rumble_requests.write(GamepadRumbleRequest::Add {
|
||||
gamepad: e,
|
||||
duration: Duration::from_secs_f32(0.5),
|
||||
intensity: GamepadRumbleIntensity::MAX,
|
||||
});
|
||||
}
|
||||
#[cfg(feature = "client")]
|
||||
commands.trigger(crate::protocol::PlaySound::Gun);
|
||||
|
||||
let rotation = if let Some(t) = state
|
||||
.target
|
||||
@@ -108,11 +109,11 @@ fn on_trigger_gun(
|
||||
.looking_at(t.translation, Vec3::Y)
|
||||
.rotation
|
||||
} else {
|
||||
state.rot.mul_quat(Quat::from_rotation_y(PI))
|
||||
state.rot()
|
||||
};
|
||||
|
||||
let mut t = Transform::from_translation(state.pos).with_rotation(rotation);
|
||||
t.translation += t.forward().as_vec3() * 2.0;
|
||||
let mut transform = Transform::from_translation(state.pos).with_rotation(rotation);
|
||||
transform.translation += transform.forward().as_vec3() * 2.0;
|
||||
|
||||
commands.spawn((
|
||||
Name::new("projectile-gun"),
|
||||
@@ -128,7 +129,9 @@ fn on_trigger_gun(
|
||||
Sensor,
|
||||
CollisionEventsEnabled,
|
||||
Visibility::default(),
|
||||
t,
|
||||
transform,
|
||||
Replicated,
|
||||
ProjectileId(state.trigger_id),
|
||||
Children::spawn(Spawn(Gizmo {
|
||||
handle: gizmo_assets.add({
|
||||
let mut g = GizmoAsset::default();
|
||||
@@ -163,16 +166,25 @@ fn timeout(mut commands: Commands, query: Query<(Entity, &GunProjectile)>, time:
|
||||
|
||||
fn shot_collision(
|
||||
mut commands: Commands,
|
||||
mut collision_event_reader: EventReader<CollisionStarted>,
|
||||
mut collision_message_reader: MessageReader<CollisionStart>,
|
||||
query_shot: Query<(&GunProjectile, &Transform)>,
|
||||
sensors: Query<(), With<Sensor>>,
|
||||
assets: Res<ShotAssets>,
|
||||
mut sprite_params: Sprite3dParams,
|
||||
) {
|
||||
for CollisionStarted(e1, e2) in collision_event_reader.read() {
|
||||
for CollisionStart {
|
||||
collider1: e1,
|
||||
collider2: e2,
|
||||
..
|
||||
} in collision_message_reader.read()
|
||||
{
|
||||
if !query_shot.contains(*e1) && !query_shot.contains(*e2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if sensors.contains(*e1) && sensors.contains(*e2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let shot_entity = if query_shot.contains(*e1) { *e1 } else { *e2 };
|
||||
|
||||
if let Ok(mut entity) = commands.get_entity(shot_entity) {
|
||||
@@ -189,18 +201,21 @@ fn shot_collision(
|
||||
};
|
||||
|
||||
commands
|
||||
.spawn(
|
||||
Sprite3dBuilder {
|
||||
image: assets.image.clone(),
|
||||
.spawn((
|
||||
Sprite3d {
|
||||
pixels_per_metre: 128.,
|
||||
alpha_mode: AlphaMode::Blend,
|
||||
unlit: true,
|
||||
..default()
|
||||
}
|
||||
.bundle_with_atlas(&mut sprite_params, texture_atlas),
|
||||
)
|
||||
},
|
||||
Sprite {
|
||||
image: assets.image.clone(),
|
||||
texture_atlas: Some(texture_atlas),
|
||||
..default()
|
||||
},
|
||||
))
|
||||
.insert((
|
||||
Billboard,
|
||||
Billboard::All,
|
||||
Transform::from_translation(shot_pos),
|
||||
NotShadowCaster,
|
||||
AnimationTimer::new(Timer::from_seconds(0.01, TimerMode::Repeating)),
|
||||
@@ -1,15 +1,21 @@
|
||||
use bevy::prelude::*;
|
||||
|
||||
use crate::{
|
||||
GameState, global_observer, heads::ActiveHeads, heads_database::HeadsDatabase,
|
||||
hitpoints::Hitpoints, loading_assets::AudioAssets,
|
||||
hitpoints::Hitpoints,
|
||||
};
|
||||
use bevy::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Healing(pub Entity);
|
||||
#[derive(Component, Serialize, Deserialize, PartialEq)]
|
||||
pub struct Healing;
|
||||
|
||||
#[derive(Event, Debug)]
|
||||
pub enum HealingStateChanged {
|
||||
#[derive(Clone, EntityEvent, Debug, Serialize, Deserialize)]
|
||||
pub struct HealingStateChanged {
|
||||
pub entity: Entity,
|
||||
pub state: HealingState,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum HealingState {
|
||||
Started,
|
||||
Stopped,
|
||||
}
|
||||
@@ -21,30 +27,24 @@ pub fn plugin(app: &mut App) {
|
||||
}
|
||||
|
||||
fn on_heal_start_stop(
|
||||
trigger: Trigger<HealingStateChanged>,
|
||||
trigger: On<HealingStateChanged>,
|
||||
mut cmds: Commands,
|
||||
assets: Res<AudioAssets>,
|
||||
query: Query<&Healing>,
|
||||
) {
|
||||
if matches!(trigger.event(), HealingStateChanged::Started) {
|
||||
let e = cmds
|
||||
.spawn((
|
||||
Name::new("sfx-heal"),
|
||||
AudioPlayer::new(assets.healing.clone()),
|
||||
PlaybackSettings {
|
||||
mode: bevy::audio::PlaybackMode::Loop,
|
||||
..Default::default()
|
||||
},
|
||||
))
|
||||
.id();
|
||||
cmds.entity(trigger.target())
|
||||
.add_child(e)
|
||||
.insert(Healing(e));
|
||||
} else {
|
||||
if let Ok(healing) = query.single() {
|
||||
cmds.entity(healing.0).despawn();
|
||||
if matches!(trigger.event().state, HealingState::Started) {
|
||||
if query.contains(trigger.event().entity) {
|
||||
// already healing, just ignore
|
||||
return;
|
||||
}
|
||||
cmds.entity(trigger.target()).remove::<Healing>();
|
||||
|
||||
cmds.entity(trigger.event().entity).insert(Healing);
|
||||
} else {
|
||||
if !query.contains(trigger.event().entity) {
|
||||
// Not healing, just ignore
|
||||
return;
|
||||
}
|
||||
|
||||
cmds.entity(trigger.event().entity).remove::<Healing>();
|
||||
}
|
||||
}
|
||||
|
||||
156
crates/hedz_reloaded/src/abilities/missile.rs
Normal file
156
crates/hedz_reloaded/src/abilities/missile.rs
Normal file
@@ -0,0 +1,156 @@
|
||||
use super::TriggerMissile;
|
||||
use crate::{
|
||||
GameState,
|
||||
abilities::{ExplodingProjectile, ExplodingProjectileSet, ProjectileId},
|
||||
heads_database::HeadsDatabase,
|
||||
physics_layers::GameLayer,
|
||||
protocol::{GltfSceneRoot, PlaySound},
|
||||
utils::{global_observer, trail::SpawnTrail},
|
||||
};
|
||||
use avian3d::prelude::*;
|
||||
use bevy::prelude::*;
|
||||
use bevy_replicon::prelude::Replicated;
|
||||
use std::f32::consts::PI;
|
||||
|
||||
const MAX_SHOT_AGES: f32 = 15.;
|
||||
const MISSLE_SPEED: f32 = 3.;
|
||||
|
||||
#[derive(Component, Reflect)]
|
||||
#[reflect(Component)]
|
||||
struct MissileProjectile {
|
||||
time: f32,
|
||||
damage: u32,
|
||||
}
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
app.add_systems(
|
||||
FixedUpdate,
|
||||
shot_collision.in_set(ExplodingProjectileSet::Mark),
|
||||
);
|
||||
app.add_systems(
|
||||
FixedUpdate,
|
||||
(update, timeout).run_if(in_state(GameState::Playing)),
|
||||
);
|
||||
|
||||
global_observer!(app, on_trigger_missile);
|
||||
}
|
||||
|
||||
fn on_trigger_missile(
|
||||
trigger: On<TriggerMissile>,
|
||||
mut commands: Commands,
|
||||
query_transform: Query<&Transform>,
|
||||
time: Res<Time>,
|
||||
heads_db: Res<HeadsDatabase>,
|
||||
) {
|
||||
let state = trigger.event().0;
|
||||
|
||||
let rotation = if let Some(target) = state.target {
|
||||
let t = query_transform
|
||||
.get(target)
|
||||
.expect("target must have transform");
|
||||
Transform::from_translation(state.pos)
|
||||
.looking_at(t.translation, Vec3::Y)
|
||||
.rotation
|
||||
} else {
|
||||
state.rot()
|
||||
};
|
||||
|
||||
let head = heads_db.head_stats(state.head);
|
||||
|
||||
let mut transform = Transform::from_translation(state.pos).with_rotation(rotation);
|
||||
transform.translation += transform.forward().as_vec3() * 2.0;
|
||||
|
||||
let id = commands
|
||||
.spawn((
|
||||
Name::new("projectile-missile"),
|
||||
MissileProjectile {
|
||||
time: time.elapsed_secs(),
|
||||
damage: head.damage,
|
||||
},
|
||||
SpawnTrail::new(
|
||||
12,
|
||||
LinearRgba::rgb(1., 0.0, 0.),
|
||||
LinearRgba::rgb(0.9, 0.9, 0.),
|
||||
10.,
|
||||
)
|
||||
.init_with_pos(),
|
||||
Collider::capsule_endpoints(0.4, Vec3::new(0., 0., 2.), Vec3::new(0., 0., -2.)),
|
||||
CollisionLayers::new(
|
||||
LayerMask(GameLayer::Projectile.to_bits()),
|
||||
LayerMask(state.target_layer.to_bits() | GameLayer::Level.to_bits()),
|
||||
),
|
||||
Sensor,
|
||||
RigidBody::Kinematic,
|
||||
CollisionEventsEnabled,
|
||||
Visibility::default(),
|
||||
transform,
|
||||
Replicated,
|
||||
ProjectileId(state.trigger_id),
|
||||
children![(
|
||||
Transform::from_rotation(Quat::from_rotation_x(PI / 2.).inverse()),
|
||||
GltfSceneRoot::Projectile("missile".to_string()),
|
||||
Replicated
|
||||
)],
|
||||
))
|
||||
.id();
|
||||
|
||||
debug!(id=?id, trigger_id = state.trigger_id, "Missile");
|
||||
}
|
||||
|
||||
fn update(mut query: Query<&mut Transform, With<MissileProjectile>>) {
|
||||
for mut transform in query.iter_mut() {
|
||||
let forward = transform.forward();
|
||||
transform.translation += forward * MISSLE_SPEED;
|
||||
}
|
||||
}
|
||||
|
||||
fn timeout(mut commands: Commands, query: Query<(Entity, &MissileProjectile)>, time: Res<Time>) {
|
||||
let current_time = time.elapsed_secs();
|
||||
|
||||
for (e, MissileProjectile { time, .. }) in query.iter() {
|
||||
if current_time > time + MAX_SHOT_AGES {
|
||||
commands.entity(e).despawn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn shot_collision(
|
||||
mut commands: Commands,
|
||||
mut collision_message_reader: MessageReader<CollisionStart>,
|
||||
query_shot: Query<(&MissileProjectile, &Transform)>,
|
||||
sensors: Query<(), With<Sensor>>,
|
||||
) {
|
||||
for CollisionStart {
|
||||
collider1: e1,
|
||||
collider2: e2,
|
||||
..
|
||||
} in collision_message_reader.read()
|
||||
{
|
||||
if !query_shot.contains(*e1) && !query_shot.contains(*e2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if sensors.contains(*e1) && sensors.contains(*e2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let shot_entity = if query_shot.contains(*e1) { *e1 } else { *e2 };
|
||||
|
||||
let Ok((shot_pos, damage)) = query_shot
|
||||
.get(shot_entity)
|
||||
.map(|(projectile, t)| (t.translation, projectile.damage))
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
commands.entity(shot_entity).insert(ExplodingProjectile {
|
||||
sound: PlaySound::MissileExplosion,
|
||||
damage,
|
||||
position: shot_pos,
|
||||
radius: 6.0,
|
||||
animation: true,
|
||||
anim_pixels_per_meter: 16.0,
|
||||
anim_time: 0.01,
|
||||
});
|
||||
}
|
||||
}
|
||||
351
crates/hedz_reloaded/src/abilities/mod.rs
Normal file
351
crates/hedz_reloaded/src/abilities/mod.rs
Normal file
@@ -0,0 +1,351 @@
|
||||
pub mod arrow;
|
||||
pub mod curver;
|
||||
pub mod gun;
|
||||
pub mod healing;
|
||||
pub mod missile;
|
||||
pub mod thrown;
|
||||
|
||||
use crate::{
|
||||
GameState,
|
||||
aim::AimTarget,
|
||||
character::CharacterHierarchy,
|
||||
control::Inputs,
|
||||
global_observer,
|
||||
heads::ActiveHeads,
|
||||
heads_database::HeadsDatabase,
|
||||
loading_assets::GameAssets,
|
||||
physics_layers::GameLayer,
|
||||
player::Player,
|
||||
protocol::PlaySound,
|
||||
utils::{Billboard, explosions::Explosion, sprite_3d_animation::AnimationTimer},
|
||||
};
|
||||
use bevy::{light::NotShadowCaster, prelude::*};
|
||||
use bevy_replicon::prelude::{SendMode, ServerTriggerExt, Signature, ToClients};
|
||||
use bevy_sprite3d::Sprite3d;
|
||||
pub use healing::Healing;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Reflect, Default, Serialize, Deserialize)]
|
||||
pub enum HeadAbility {
|
||||
#[default]
|
||||
None,
|
||||
Arrow,
|
||||
Thrown,
|
||||
Gun,
|
||||
Missile,
|
||||
Medic,
|
||||
Curver,
|
||||
Boat,
|
||||
Turbo,
|
||||
Spray,
|
||||
}
|
||||
|
||||
#[derive(Debug, Reflect, Clone, Copy)]
|
||||
pub struct TriggerData {
|
||||
target: Option<Entity>,
|
||||
dir: Dir3,
|
||||
pos: Vec3,
|
||||
target_layer: GameLayer,
|
||||
head: usize,
|
||||
trigger_id: usize,
|
||||
}
|
||||
|
||||
impl TriggerData {
|
||||
pub fn new(
|
||||
target: Option<Entity>,
|
||||
dir: Dir3,
|
||||
pos: Vec3,
|
||||
target_layer: GameLayer,
|
||||
head: usize,
|
||||
trigger_id: usize,
|
||||
) -> Self {
|
||||
Self {
|
||||
target,
|
||||
dir,
|
||||
pos,
|
||||
target_layer,
|
||||
head,
|
||||
trigger_id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rot(&self) -> Quat {
|
||||
// as it turns out, `glam` comes with some `looking_to` functions for left and right handed coordinate systems, but it seems like they're wrong?
|
||||
// at the very least they give some very odd results and the cross multiplications inside all look backwards compared to what bevy transforms do.
|
||||
Transform::default().looking_to(self.dir, Vec3::Y).rotation
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Event, Reflect)]
|
||||
pub struct TriggerGun(pub TriggerData);
|
||||
#[derive(Event, Reflect)]
|
||||
pub struct TriggerArrow(pub TriggerData);
|
||||
#[derive(Event, Reflect)]
|
||||
pub struct TriggerThrow(pub TriggerData);
|
||||
#[derive(Event, Reflect)]
|
||||
pub struct TriggerMissile(pub TriggerData);
|
||||
#[derive(Event, Reflect)]
|
||||
pub struct TriggerCurver(pub TriggerData);
|
||||
|
||||
#[derive(Component, Default, Reflect)]
|
||||
#[reflect(Component)]
|
||||
pub struct PlayerTriggerState {
|
||||
next_trigger_timestamp: f32,
|
||||
projectile_count: usize,
|
||||
}
|
||||
|
||||
#[derive(Component, Reflect, Deserialize, Serialize, Hash)]
|
||||
#[reflect(Component)]
|
||||
#[require(Signature::of::<ProjectileId>())]
|
||||
pub struct ProjectileId(pub usize);
|
||||
|
||||
#[derive(Component)]
|
||||
#[component(storage = "SparseSet")]
|
||||
#[allow(dead_code)]
|
||||
pub struct ExplodingProjectile {
|
||||
sound: PlaySound,
|
||||
damage: u32,
|
||||
position: Vec3,
|
||||
radius: f32,
|
||||
animation: bool,
|
||||
anim_pixels_per_meter: f32,
|
||||
anim_time: f32,
|
||||
}
|
||||
|
||||
// TODO: move explosions into separate modul
|
||||
#[derive(SystemSet, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum ExplodingProjectileSet {
|
||||
Mark,
|
||||
Explode,
|
||||
}
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
app.add_plugins(gun::plugin);
|
||||
app.add_plugins(thrown::plugin);
|
||||
app.add_plugins(arrow::plugin);
|
||||
app.add_plugins(missile::plugin);
|
||||
app.add_plugins(healing::plugin);
|
||||
app.add_plugins(curver::plugin);
|
||||
|
||||
app.configure_sets(
|
||||
FixedUpdate,
|
||||
ExplodingProjectileSet::Explode
|
||||
.after(ExplodingProjectileSet::Mark)
|
||||
.run_if(in_state(GameState::Playing)),
|
||||
);
|
||||
app.add_systems(OnEnter(GameState::Playing), setup);
|
||||
|
||||
app.add_systems(
|
||||
FixedUpdate,
|
||||
(update, update_heal_ability)
|
||||
.chain()
|
||||
.run_if(in_state(GameState::Playing)),
|
||||
);
|
||||
|
||||
app.add_systems(
|
||||
FixedUpdate,
|
||||
explode_projectiles.in_set(ExplodingProjectileSet::Explode),
|
||||
);
|
||||
|
||||
global_observer!(app, build_explosion_sprite);
|
||||
}
|
||||
|
||||
fn explode_projectiles(mut commands: Commands, query: Query<(Entity, &ExplodingProjectile)>) {
|
||||
for (shot_entity, projectile) in query.iter() {
|
||||
debug!(id=?shot_entity, "Projectile explosion");
|
||||
|
||||
if let Ok(mut entity) = commands.get_entity(shot_entity) {
|
||||
entity.try_despawn();
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
commands.server_trigger(ToClients {
|
||||
mode: SendMode::Broadcast,
|
||||
message: projectile.sound.clone(),
|
||||
});
|
||||
|
||||
commands.trigger(Explosion {
|
||||
damage: projectile.damage,
|
||||
position: projectile.position,
|
||||
//TODO: should be around 1 grid in distance
|
||||
radius: projectile.radius,
|
||||
});
|
||||
|
||||
//TODO: support different impact animations
|
||||
if projectile.animation {
|
||||
commands.server_trigger(ToClients {
|
||||
mode: SendMode::Broadcast,
|
||||
message: BuildExplosionSprite {
|
||||
pos: projectile.position,
|
||||
pixels_per_meter: projectile.anim_pixels_per_meter,
|
||||
time: projectile.anim_time,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update(
|
||||
mut commands: Commands,
|
||||
mut query: Query<
|
||||
(
|
||||
Entity,
|
||||
&mut ActiveHeads,
|
||||
&mut PlayerTriggerState,
|
||||
&AimTarget,
|
||||
&Inputs,
|
||||
),
|
||||
With<Player>,
|
||||
>,
|
||||
query_transform: Query<&Transform>,
|
||||
heads_db: Res<HeadsDatabase>,
|
||||
time: Res<Time>,
|
||||
character: CharacterHierarchy,
|
||||
) {
|
||||
for (player, mut active_heads, mut trigger_state, target, inputs) in query.iter_mut() {
|
||||
if inputs.trigger && trigger_state.next_trigger_timestamp < time.elapsed_secs() {
|
||||
let Some(state) = active_heads.current() else {
|
||||
return;
|
||||
};
|
||||
|
||||
if !state.has_ammo() {
|
||||
return;
|
||||
}
|
||||
|
||||
let target = if let Some(target) = target.0
|
||||
&& query_transform.get(target).is_ok()
|
||||
{
|
||||
Some(target)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let Some(projectile_origin) = character
|
||||
.projectile_origin(player)
|
||||
.map(|origin| origin.translation())
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let head = heads_db.head_stats(state.head);
|
||||
|
||||
if matches!(head.ability, HeadAbility::None | HeadAbility::Medic) {
|
||||
return;
|
||||
}
|
||||
|
||||
active_heads.use_ammo(time.elapsed_secs());
|
||||
|
||||
trigger_state.next_trigger_timestamp = time.elapsed_secs() + (1. / head.aps);
|
||||
trigger_state.projectile_count += 1;
|
||||
|
||||
let trigger_state = TriggerData {
|
||||
dir: Dir3::try_from(inputs.look_dir).unwrap_or(Dir3::NEG_Z),
|
||||
pos: projectile_origin,
|
||||
target,
|
||||
target_layer: GameLayer::Npc,
|
||||
head: state.head,
|
||||
trigger_id: trigger_state.projectile_count,
|
||||
};
|
||||
|
||||
match head.ability {
|
||||
HeadAbility::Thrown => commands.trigger(TriggerThrow(trigger_state)),
|
||||
HeadAbility::Gun => commands.trigger(TriggerGun(trigger_state)),
|
||||
HeadAbility::Missile => commands.trigger(TriggerMissile(trigger_state)),
|
||||
HeadAbility::Arrow => commands.trigger(TriggerArrow(trigger_state)),
|
||||
HeadAbility::Curver => commands.trigger(TriggerCurver(trigger_state)),
|
||||
_ => panic!("Unhandled head ability"),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update_heal_ability(
|
||||
mut commands: Commands,
|
||||
players: Query<(Entity, &ActiveHeads, Ref<Inputs>), With<Player>>,
|
||||
heads_db: Res<HeadsDatabase>,
|
||||
) {
|
||||
for (player, active_heads, inputs) in players.iter() {
|
||||
if inputs.is_changed() {
|
||||
let Some(state) = active_heads.current() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let head = heads_db.head_stats(state.head);
|
||||
|
||||
if !matches!(head.ability, HeadAbility::Medic) {
|
||||
return;
|
||||
}
|
||||
|
||||
use crate::abilities::healing::HealingState;
|
||||
if inputs.trigger {
|
||||
use crate::abilities::healing::HealingStateChanged;
|
||||
|
||||
commands.trigger(HealingStateChanged {
|
||||
state: HealingState::Started,
|
||||
entity: player,
|
||||
});
|
||||
} else {
|
||||
use crate::abilities::healing::HealingStateChanged;
|
||||
|
||||
commands.trigger(HealingStateChanged {
|
||||
state: HealingState::Stopped,
|
||||
entity: player,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
struct ShotAssets {
|
||||
image: Handle<Image>,
|
||||
layout: Handle<TextureAtlasLayout>,
|
||||
}
|
||||
|
||||
fn setup(mut commands: Commands, assets: Res<GameAssets>, asset_server: Res<AssetServer>) {
|
||||
let layout = TextureAtlasLayout::from_grid(UVec2::splat(256), 7, 6, None, None);
|
||||
let texture_atlas_layout = asset_server.add(layout);
|
||||
|
||||
commands.insert_resource(ShotAssets {
|
||||
image: assets.impact_atlas.clone(),
|
||||
layout: texture_atlas_layout,
|
||||
});
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Event, Serialize, Deserialize, PartialEq)]
|
||||
pub struct BuildExplosionSprite {
|
||||
pos: Vec3,
|
||||
pixels_per_meter: f32,
|
||||
time: f32,
|
||||
}
|
||||
|
||||
fn build_explosion_sprite(
|
||||
trigger: On<BuildExplosionSprite>,
|
||||
mut commands: Commands,
|
||||
assets: Res<ShotAssets>,
|
||||
) {
|
||||
commands.spawn((
|
||||
Transform::from_translation(trigger.event().pos),
|
||||
Sprite3d {
|
||||
pixels_per_metre: trigger.event().pixels_per_meter,
|
||||
alpha_mode: AlphaMode::Blend,
|
||||
unlit: true,
|
||||
..default()
|
||||
},
|
||||
Sprite {
|
||||
image: assets.image.clone(),
|
||||
texture_atlas: Some(TextureAtlas {
|
||||
layout: assets.layout.clone(),
|
||||
index: 0,
|
||||
}),
|
||||
..default()
|
||||
},
|
||||
Billboard::All,
|
||||
NotShadowCaster,
|
||||
AnimationTimer::new(Timer::from_seconds(
|
||||
trigger.event().time,
|
||||
TimerMode::Repeating,
|
||||
)),
|
||||
));
|
||||
}
|
||||
154
crates/hedz_reloaded/src/abilities/thrown.rs
Normal file
154
crates/hedz_reloaded/src/abilities/thrown.rs
Normal file
@@ -0,0 +1,154 @@
|
||||
use super::TriggerThrow;
|
||||
use crate::{
|
||||
GameState,
|
||||
abilities::{ExplodingProjectile, ExplodingProjectileSet, ProjectileId},
|
||||
heads_database::HeadsDatabase,
|
||||
physics_layers::GameLayer,
|
||||
protocol::PlaySound,
|
||||
utils::global_observer,
|
||||
};
|
||||
use avian3d::prelude::*;
|
||||
use bevy::prelude::*;
|
||||
use bevy_ballistic::launch_velocity;
|
||||
use bevy_replicon::prelude::Replicated;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Component, Serialize, Deserialize, PartialEq)]
|
||||
pub struct ThrownProjectile {
|
||||
impact_animation: bool,
|
||||
damage: u32,
|
||||
projectile: String,
|
||||
}
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
app.add_systems(
|
||||
FixedUpdate,
|
||||
shot_collision
|
||||
.in_set(ExplodingProjectileSet::Mark)
|
||||
.run_if(in_state(GameState::Playing)),
|
||||
);
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
app.add_systems(Update, shot_visuals.run_if(in_state(GameState::Playing)));
|
||||
|
||||
global_observer!(app, on_trigger_thrown);
|
||||
}
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
fn shot_visuals(
|
||||
mut commands: Commands,
|
||||
query: Query<(Entity, &ThrownProjectile), Added<ThrownProjectile>>,
|
||||
) {
|
||||
for (entity, thrown) in query.iter() {
|
||||
commands.entity(entity).try_insert((
|
||||
crate::utils::auto_rotate::AutoRotation(
|
||||
Quat::from_rotation_x(0.4) * Quat::from_rotation_z(0.3),
|
||||
),
|
||||
crate::protocol::GltfSceneRoot::Projectile(thrown.projectile.clone()),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
fn on_trigger_thrown(
|
||||
trigger: On<TriggerThrow>,
|
||||
mut commands: Commands,
|
||||
query_transform: Query<&Transform>,
|
||||
heads_db: Res<HeadsDatabase>,
|
||||
) {
|
||||
let state = trigger.event().0;
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
commands.trigger(PlaySound::Throw);
|
||||
|
||||
const SPEED: f32 = 35.;
|
||||
|
||||
let pos = state.pos;
|
||||
|
||||
let vel = if let Some(target) = state.target
|
||||
&& let Ok(t) = query_transform.get(target)
|
||||
{
|
||||
launch_velocity(pos, t.translation, SPEED, 9.81)
|
||||
.map(|(low, _)| low)
|
||||
.unwrap()
|
||||
} else {
|
||||
((state.dir.as_vec3() * 2.0) + Vec3::Y).normalize() * SPEED
|
||||
};
|
||||
|
||||
let head = heads_db.head_stats(state.head);
|
||||
|
||||
//TODO: projectile db?
|
||||
let explosion_animation = !matches!(state.head, 8 | 16);
|
||||
|
||||
let id = commands
|
||||
.spawn((
|
||||
Transform::from_translation(pos),
|
||||
Name::new("projectile-thrown"),
|
||||
ThrownProjectile {
|
||||
impact_animation: explosion_animation,
|
||||
damage: head.damage,
|
||||
projectile: head.projectile.clone(),
|
||||
},
|
||||
Collider::sphere(0.4),
|
||||
CollisionLayers::new(
|
||||
LayerMask(GameLayer::Projectile.to_bits()),
|
||||
LayerMask(state.target_layer.to_bits() | GameLayer::Level.to_bits()),
|
||||
),
|
||||
RigidBody::Dynamic,
|
||||
CollisionEventsEnabled,
|
||||
Mass(0.01),
|
||||
LinearVelocity(vel),
|
||||
Visibility::default(),
|
||||
Sensor,
|
||||
Replicated,
|
||||
ProjectileId(state.trigger_id),
|
||||
))
|
||||
.id();
|
||||
|
||||
debug!(id=?id, trigger_id = state.trigger_id, "Thrown");
|
||||
}
|
||||
|
||||
fn shot_collision(
|
||||
mut commands: Commands,
|
||||
mut collision_message_reader: MessageReader<CollisionStart>,
|
||||
query_shot: Query<(&ThrownProjectile, &Transform)>,
|
||||
sensors: Query<(), With<Sensor>>,
|
||||
) {
|
||||
for CollisionStart {
|
||||
collider1: e1,
|
||||
collider2: e2,
|
||||
..
|
||||
} in collision_message_reader.read()
|
||||
{
|
||||
if !query_shot.contains(*e1) && !query_shot.contains(*e2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if sensors.contains(*e1) && sensors.contains(*e2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let shot_entity = if query_shot.contains(*e1) { *e1 } else { *e2 };
|
||||
|
||||
let Ok((shot_pos, animation, damage)) =
|
||||
query_shot.get(shot_entity).map(|(projectile, t)| {
|
||||
(
|
||||
t.translation,
|
||||
projectile.impact_animation,
|
||||
projectile.damage,
|
||||
)
|
||||
})
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
commands.entity(shot_entity).insert(ExplodingProjectile {
|
||||
sound: PlaySound::ThrowHit,
|
||||
damage,
|
||||
position: shot_pos,
|
||||
radius: 5.0,
|
||||
animation,
|
||||
anim_pixels_per_meter: 32.0,
|
||||
anim_time: 0.02,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
use bevy::prelude::*;
|
||||
|
||||
use crate::{
|
||||
GameState,
|
||||
abilities::{HeadAbility, TriggerData, TriggerThrow},
|
||||
@@ -8,6 +6,7 @@ use crate::{
|
||||
heads_database::HeadsDatabase,
|
||||
player::Player,
|
||||
};
|
||||
use bevy::prelude::*;
|
||||
|
||||
#[derive(Component, Reflect)]
|
||||
#[reflect(Component)]
|
||||
@@ -128,7 +127,7 @@ fn rotate(agent: Query<(Entity, &Engage)>, mut transform: Query<&mut Transform>)
|
||||
};
|
||||
|
||||
// Get the direction vector from the current position to the target
|
||||
let direction = (target_pos - agent_transform.translation).normalize();
|
||||
let direction = (agent_transform.translation - target_pos).normalize();
|
||||
|
||||
// Project the direction onto the XZ plane by zeroing out the Y component
|
||||
let xz_direction = Vec3::new(direction.x, 0.0, direction.z).normalize();
|
||||
@@ -164,15 +163,14 @@ fn engage_and_throw(
|
||||
if can_shoot_again && npc_head.has_ammo() {
|
||||
npc.use_ammo(time.elapsed_secs());
|
||||
|
||||
let dir = t.forward();
|
||||
|
||||
commands.trigger(TriggerThrow(TriggerData::new(
|
||||
target.0,
|
||||
dir,
|
||||
t.rotation,
|
||||
t.forward(),
|
||||
t.translation,
|
||||
crate::physics_layers::GameLayer::Player,
|
||||
npc_head.head,
|
||||
// TODO: we probably need to make sure the ai's projectile does not get deduped, zero should not be used by anyone though
|
||||
0,
|
||||
)));
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,34 @@
|
||||
mod marker;
|
||||
mod target_ui;
|
||||
|
||||
use crate::{
|
||||
GameState,
|
||||
control::Inputs,
|
||||
head::ActiveHead,
|
||||
heads_database::HeadsDatabase,
|
||||
hitpoints::Hitpoints,
|
||||
physics_layers::GameLayer,
|
||||
player::{Player, PlayerBodyMesh},
|
||||
player::{LocalPlayer, Player},
|
||||
tb_entities::EnemySpawn,
|
||||
};
|
||||
use avian3d::prelude::*;
|
||||
use bevy::prelude::*;
|
||||
use marker::MarkerEvent;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::f32::consts::PI;
|
||||
|
||||
#[derive(Component, Reflect, Default, Deref)]
|
||||
#[derive(Component, Reflect, Default, Deref, PartialEq, Serialize, Deserialize)]
|
||||
#[reflect(Component)]
|
||||
pub struct AimTarget(pub Option<Entity>);
|
||||
|
||||
#[derive(Component, Reflect)]
|
||||
#[derive(Component, Reflect, PartialEq, Serialize, Deserialize)]
|
||||
#[reflect(Component)]
|
||||
#[require(AimTarget)]
|
||||
pub struct AimState {
|
||||
pub range: f32,
|
||||
pub max_angle: f32,
|
||||
pub spawn_marker: bool,
|
||||
}
|
||||
|
||||
#[derive(Event)]
|
||||
pub enum MarkerEvent {
|
||||
Spawn(Entity),
|
||||
Despawn,
|
||||
}
|
||||
|
||||
impl Default for AimState {
|
||||
@@ -32,26 +36,20 @@ impl Default for AimState {
|
||||
Self {
|
||||
range: 80.,
|
||||
max_angle: PI / 8.,
|
||||
spawn_marker: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
app.add_plugins(target_ui::plugin);
|
||||
app.add_plugins(marker::plugin);
|
||||
app.register_type::<AimState>();
|
||||
app.register_type::<AimTarget>();
|
||||
|
||||
app.register_required_components::<ActiveHead, AimState>();
|
||||
|
||||
app.add_systems(
|
||||
Update,
|
||||
(update_player_aim, update_npc_aim, head_change).run_if(in_state(GameState::Playing)),
|
||||
);
|
||||
app.add_systems(Update, add_aim);
|
||||
}
|
||||
|
||||
fn add_aim(mut commands: Commands, query: Query<Entity, Added<ActiveHead>>) {
|
||||
for e in query.iter() {
|
||||
commands.entity(e).insert(AimState::default());
|
||||
}
|
||||
}
|
||||
|
||||
fn head_change(
|
||||
@@ -68,63 +66,69 @@ fn head_change(
|
||||
|
||||
fn update_player_aim(
|
||||
mut commands: Commands,
|
||||
potential_targets: Query<(Entity, &Transform), With<EnemySpawn>>,
|
||||
player_rot: Query<(&Transform, &GlobalTransform), With<PlayerBodyMesh>>,
|
||||
mut player_aim: Query<(&AimState, &mut AimTarget), With<Player>>,
|
||||
potential_targets: Query<(Entity, &Transform), With<Hitpoints>>,
|
||||
mut player_aim: Query<
|
||||
(
|
||||
Entity,
|
||||
&AimState,
|
||||
&mut AimTarget,
|
||||
&GlobalTransform,
|
||||
&Inputs,
|
||||
Has<LocalPlayer>,
|
||||
),
|
||||
With<Player>,
|
||||
>,
|
||||
spatial_query: SpatialQuery,
|
||||
) {
|
||||
let Some((state, mut aim_target)) = player_aim.iter_mut().next() else {
|
||||
return;
|
||||
};
|
||||
for (player, state, mut aim_target, global_tf, inputs, is_local) in player_aim.iter_mut() {
|
||||
let (player_pos, player_forward) = (global_tf.translation(), inputs.look_dir);
|
||||
|
||||
let Some((player_pos, player_forward)) = player_rot
|
||||
.iter()
|
||||
.next()
|
||||
.map(|(t, global)| (global.translation(), t.forward()))
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let mut new_target = None;
|
||||
let mut target_distance = f32::MAX;
|
||||
|
||||
let mut new_target = None;
|
||||
let mut target_distance = f32::MAX;
|
||||
|
||||
for (e, t) in potential_targets.iter() {
|
||||
let delta = player_pos - t.translation;
|
||||
|
||||
let distance = delta.length();
|
||||
|
||||
if distance > state.range {
|
||||
continue;
|
||||
}
|
||||
|
||||
let angle = player_forward.angle_between(delta.normalize());
|
||||
|
||||
if angle < state.max_angle && distance < target_distance {
|
||||
if !line_of_sight(&spatial_query, player_pos, delta, distance) {
|
||||
for (e, t) in potential_targets.iter() {
|
||||
if e == player {
|
||||
continue;
|
||||
}
|
||||
|
||||
new_target = Some(e);
|
||||
target_distance = distance;
|
||||
}
|
||||
}
|
||||
let delta = t.translation - player_pos;
|
||||
|
||||
if let Some(e) = &aim_target.0 {
|
||||
if commands.get_entity(*e).is_err() {
|
||||
let distance = delta.length();
|
||||
|
||||
if distance > state.range {
|
||||
continue;
|
||||
}
|
||||
|
||||
let angle = player_forward.angle_between(delta.normalize());
|
||||
|
||||
if angle < state.max_angle && distance < target_distance {
|
||||
if !line_of_sight(&spatial_query, player_pos, delta, distance) {
|
||||
continue;
|
||||
}
|
||||
|
||||
new_target = Some(e);
|
||||
target_distance = distance;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(e) = &aim_target.0
|
||||
&& commands.get_entity(*e).is_err()
|
||||
{
|
||||
aim_target.0 = None;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if new_target != aim_target.0 {
|
||||
if state.spawn_marker {
|
||||
if let Some(target) = new_target {
|
||||
commands.trigger(MarkerEvent::Spawn(target));
|
||||
} else {
|
||||
commands.trigger(MarkerEvent::Despawn);
|
||||
if new_target != aim_target.0 {
|
||||
if is_local {
|
||||
if let Some(target) = new_target {
|
||||
commands.trigger(MarkerEvent::Spawn(target));
|
||||
} else {
|
||||
commands.trigger(MarkerEvent::Despawn);
|
||||
}
|
||||
}
|
||||
|
||||
aim_target.0 = new_target;
|
||||
}
|
||||
aim_target.0 = new_target;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +145,7 @@ fn update_npc_aim(
|
||||
let mut target_distance = f32::MAX;
|
||||
|
||||
for (e, t) in potential_targets.iter() {
|
||||
let delta = pos - t.translation;
|
||||
let delta = t.translation - pos;
|
||||
|
||||
let distance = delta.length();
|
||||
|
||||
@@ -161,11 +165,11 @@ fn update_npc_aim(
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(e) = &aim_target.0 {
|
||||
if commands.get_entity(*e).is_err() {
|
||||
aim_target.0 = None;
|
||||
return;
|
||||
}
|
||||
if let Some(e) = &aim_target.0
|
||||
&& commands.get_entity(*e).is_err()
|
||||
{
|
||||
aim_target.0 = None;
|
||||
return;
|
||||
}
|
||||
|
||||
if new_target != aim_target.0 {
|
||||
@@ -182,9 +186,9 @@ fn line_of_sight(
|
||||
) -> bool {
|
||||
if let Some(_hit) = spatial_query.cast_shape(
|
||||
&Collider::sphere(0.1),
|
||||
player_pos + -delta.normalize() + (Vec3::Y * 2.),
|
||||
player_pos + delta.normalize() + (Vec3::Y * 2.),
|
||||
Quat::default(),
|
||||
Dir3::new(-delta).unwrap(),
|
||||
Dir3::new(delta).unwrap(),
|
||||
&ShapeCastConfig {
|
||||
max_distance: distance * 0.98,
|
||||
compute_contact_on_penetration: false,
|
||||
184
crates/hedz_reloaded/src/animation.rs
Normal file
184
crates/hedz_reloaded/src/animation.rs
Normal file
@@ -0,0 +1,184 @@
|
||||
use crate::{
|
||||
GameState, character::CharacterAnimations, head::ActiveHead, heads_database::HeadsDatabase,
|
||||
};
|
||||
use bevy::{animation::RepeatAnimation, ecs::query::QueryData, prelude::*};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::Duration;
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
app.register_type::<AnimationFlags>();
|
||||
|
||||
app.add_systems(
|
||||
Update,
|
||||
update_animation.run_if(in_state(GameState::Playing)),
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Component, Default, Reflect, PartialEq, Serialize, Deserialize)]
|
||||
#[reflect(Component)]
|
||||
#[require(AnimationFlagCache)]
|
||||
pub struct AnimationFlags {
|
||||
pub any_direction: bool,
|
||||
pub jumping: bool,
|
||||
pub jump_count: u8,
|
||||
pub shooting: bool,
|
||||
pub restart_shooting: bool,
|
||||
pub hit: bool,
|
||||
}
|
||||
|
||||
#[derive(Component, Default, Reflect)]
|
||||
#[reflect(Component)]
|
||||
pub struct AnimationFlagCache {
|
||||
pub jump_count: u8,
|
||||
}
|
||||
|
||||
#[derive(QueryData)]
|
||||
#[query_data(mutable)]
|
||||
pub struct AnimationController {
|
||||
pub transitions: &'static mut AnimationTransitions,
|
||||
pub player: &'static mut AnimationPlayer,
|
||||
}
|
||||
|
||||
impl AnimationController {
|
||||
pub fn play_inner(
|
||||
player: &mut AnimationPlayer,
|
||||
transitions: &mut AnimationTransitions,
|
||||
animation: AnimationNodeIndex,
|
||||
transition: Duration,
|
||||
repeat: RepeatAnimation,
|
||||
) {
|
||||
transitions
|
||||
.play(player, animation, transition)
|
||||
.set_repeat(repeat);
|
||||
}
|
||||
}
|
||||
|
||||
impl AnimationControllerItem<'_, '_> {
|
||||
pub fn play(
|
||||
&mut self,
|
||||
animation: AnimationNodeIndex,
|
||||
transition: Duration,
|
||||
repeat: RepeatAnimation,
|
||||
) {
|
||||
AnimationController::play_inner(
|
||||
&mut self.player,
|
||||
&mut self.transitions,
|
||||
animation,
|
||||
transition,
|
||||
repeat,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn is_playing(&self, index: AnimationNodeIndex) -> bool {
|
||||
self.player.is_playing_animation(index)
|
||||
}
|
||||
}
|
||||
|
||||
const DEFAULT_TRANSITION_DURATION: Duration = Duration::ZERO;
|
||||
|
||||
fn update_animation(
|
||||
mut animated: Query<(AnimationController, &CharacterAnimations)>,
|
||||
mut character: Query<(&ActiveHead, &mut AnimationFlags, &mut AnimationFlagCache)>,
|
||||
headdb: Res<HeadsDatabase>,
|
||||
) {
|
||||
for (mut controller, anims) in animated.iter_mut() {
|
||||
let (head, mut flags, mut cache) = character.get_mut(anims.of_character).unwrap();
|
||||
let head = headdb.head_stats(head.0);
|
||||
|
||||
let is_playing_shoot = anims.shoot.is_some()
|
||||
&& controller.is_playing(anims.shoot.unwrap())
|
||||
&& !controller
|
||||
.player
|
||||
.animation(anims.shoot.unwrap())
|
||||
.unwrap()
|
||||
.is_finished();
|
||||
let is_playing_run_shoot = anims.run_shoot.is_some()
|
||||
&& controller.is_playing(anims.run_shoot.unwrap())
|
||||
&& !controller
|
||||
.player
|
||||
.animation(anims.run_shoot.unwrap())
|
||||
.unwrap()
|
||||
.is_finished();
|
||||
let wait_for_shoot = !head.interrupt_shoot && (is_playing_shoot || is_playing_run_shoot);
|
||||
if wait_for_shoot {
|
||||
return;
|
||||
} else if flags.shooting && flags.any_direction && anims.run_shoot.is_some() {
|
||||
if !controller.is_playing(anims.run_shoot.unwrap()) {
|
||||
controller.play(
|
||||
anims.run_shoot.unwrap(),
|
||||
DEFAULT_TRANSITION_DURATION,
|
||||
RepeatAnimation::Never,
|
||||
);
|
||||
}
|
||||
if controller
|
||||
.player
|
||||
.animation(anims.run_shoot.unwrap())
|
||||
.unwrap()
|
||||
.is_finished()
|
||||
|| flags.restart_shooting
|
||||
{
|
||||
controller
|
||||
.player
|
||||
.animation_mut(anims.run_shoot.unwrap())
|
||||
.unwrap()
|
||||
.replay();
|
||||
|
||||
flags.restart_shooting = false;
|
||||
}
|
||||
} else if flags.shooting && anims.shoot.is_some() {
|
||||
if !controller.is_playing(anims.shoot.unwrap()) {
|
||||
controller.play(
|
||||
anims.shoot.unwrap(),
|
||||
DEFAULT_TRANSITION_DURATION,
|
||||
RepeatAnimation::Never,
|
||||
);
|
||||
}
|
||||
if controller
|
||||
.player
|
||||
.animation(anims.shoot.unwrap())
|
||||
.unwrap()
|
||||
.is_finished()
|
||||
|| flags.restart_shooting
|
||||
{
|
||||
controller
|
||||
.player
|
||||
.animation_mut(anims.shoot.unwrap())
|
||||
.unwrap()
|
||||
.replay();
|
||||
|
||||
flags.restart_shooting = false;
|
||||
}
|
||||
} else if flags.hit {
|
||||
if !controller.is_playing(anims.hit) {
|
||||
controller.play(
|
||||
anims.hit,
|
||||
DEFAULT_TRANSITION_DURATION,
|
||||
RepeatAnimation::Never,
|
||||
);
|
||||
}
|
||||
} else if flags.jumping {
|
||||
if !controller.is_playing(anims.jump) || flags.jump_count != cache.jump_count {
|
||||
controller.play(
|
||||
anims.jump,
|
||||
DEFAULT_TRANSITION_DURATION,
|
||||
RepeatAnimation::Never,
|
||||
);
|
||||
cache.jump_count = flags.jump_count;
|
||||
}
|
||||
} else if flags.any_direction {
|
||||
if !controller.player.is_playing_animation(anims.run) {
|
||||
controller.play(
|
||||
anims.run,
|
||||
DEFAULT_TRANSITION_DURATION,
|
||||
RepeatAnimation::Forever,
|
||||
);
|
||||
}
|
||||
} else if !controller.is_playing(anims.idle) {
|
||||
controller.play(
|
||||
anims.idle,
|
||||
DEFAULT_TRANSITION_DURATION,
|
||||
RepeatAnimation::Forever,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user