├── src
├── game_state.rs
├── sky_box
│ ├── components.rs
│ └── systems.rs
├── debug_fps
│ ├── resources.rs
│ └── systems.rs
├── wind
│ └── resources.rs
├── focal_point
│ ├── resources.rs
│ └── systems.rs
├── stats
│ ├── resources.rs
│ └── systems.rs
├── camera
│ ├── resources.rs
│ └── systems.rs
├── ocean
│ ├── components.rs
│ ├── materials.rs
│ ├── resources.rs
│ └── systems.rs
├── orbiting_camera
│ ├── events.rs
│ ├── resources.rs
│ └── systems.rs
├── menu
│ ├── components.rs
│ └── systems.rs
├── utils.rs
├── game_state
│ └── states.rs
├── args
│ ├── run_conditions.rs
│ └── resources.rs
├── light.rs
├── wind.rs
├── player
│ ├── components.rs
│ └── systems.rs
├── widget_debug.rs
├── artillery
│ ├── resources.rs
│ ├── components.rs
│ └── systems.rs
├── instructions.rs
├── assets
│ ├── resources.rs
│ └── systems.rs
├── args.rs
├── controls.rs
├── orbiting_camera.rs
├── sync_test.rs
├── utils
│ ├── f32_extensions.rs
│ ├── linear_algebra.rs
│ ├── vec2_extensions.rs
│ ├── aerodynamics.rs
│ ├── hash.rs
│ └── water_mechanics.rs
├── debug_fps.rs
├── focal_point.rs
├── sky_box.rs
├── controls
│ └── components.rs
├── light
│ └── systems.rs
├── physics
│ ├── resources.rs
│ ├── bundles.rs
│ ├── components.rs
│ └── systems.rs
├── camera.rs
├── connection.rs
├── stats.rs
├── menu.rs
├── widget_debug
│ └── systems.rs
├── assets.rs
├── inputs.rs
├── instructions
│ └── systems.rs
├── inputs
│ └── systems.rs
├── artillery.rs
├── sync_test
│ └── systems.rs
├── player.rs
├── connection
│ └── systems.rs
├── physics.rs
├── main.rs
└── ocean.rs
├── .vscode
└── settings.json
├── assets
├── DISCLAIMER
├── models
│ ├── cannon_ball.glb
│ ├── medium_flag.glb
│ ├── medium_helm.glb
│ ├── medium_hull.glb
│ ├── medium_canon.glb
│ ├── raft_with_mast.glb
│ └── medium_pirate_sail.glb
├── fonts
│ └── the-bomb-regular.otf
└── shaders
│ ├── water_dynamics.wgsl
│ ├── ocean_material_bindings.wgsl
│ ├── ocean_material_prepass.wgsl
│ ├── ocean_material.wgsl
│ ├── not_working_merged_ocean_material_shader.wgsl
│ └── utils.wgsl
├── wasm
├── models
│ ├── cannon_ball.glb.meta
│ ├── medium_canon.glb.meta
│ ├── medium_flag.glb.meta
│ ├── medium_helm.glb.meta
│ ├── medium_hull.glb.meta
│ ├── pirate_flag.glb.meta
│ ├── medium_pirate_sail.glb.meta
│ └── raft_with_mast.glb.meta
├── fonts
│ └── the-bomb-regular.otf.meta
├── manifest.json
├── skyboxes
│ └── basic.png.meta
├── index.html
└── restart-audio-context.js
├── .idea
└── vcs.xml
├── LICENSE
├── justfile
├── Cargo.toml
├── README.md
├── .github
└── workflows
│ ├── ci.yaml
│ └── release.yaml
├── .gitignore
└── ABOUT_TEMPLATE.md
/src/game_state.rs:
--------------------------------------------------------------------------------
1 | pub mod states;
2 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "rust-analyzer.showUnlinkedFileNotification": false
3 | }
--------------------------------------------------------------------------------
/assets/DISCLAIMER:
--------------------------------------------------------------------------------
1 | Asset files come with their own licenses and may not be used outside this project.
--------------------------------------------------------------------------------
/src/sky_box/components.rs:
--------------------------------------------------------------------------------
1 | use bevy::prelude::*;
2 |
3 | #[derive(Component)]
4 | pub struct Sky;
5 |
--------------------------------------------------------------------------------
/src/debug_fps/resources.rs:
--------------------------------------------------------------------------------
1 | use bevy::prelude::*;
2 |
3 | #[derive(Component)]
4 | pub struct DebugFps;
5 |
--------------------------------------------------------------------------------
/src/wind/resources.rs:
--------------------------------------------------------------------------------
1 | use bevy::prelude::*;
2 |
3 | #[derive(Resource)]
4 | pub struct Wind(pub Vec3);
5 |
--------------------------------------------------------------------------------
/assets/models/cannon_ball.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/claudijo/pirate-sea-jam/HEAD/assets/models/cannon_ball.glb
--------------------------------------------------------------------------------
/assets/models/medium_flag.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/claudijo/pirate-sea-jam/HEAD/assets/models/medium_flag.glb
--------------------------------------------------------------------------------
/assets/models/medium_helm.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/claudijo/pirate-sea-jam/HEAD/assets/models/medium_helm.glb
--------------------------------------------------------------------------------
/assets/models/medium_hull.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/claudijo/pirate-sea-jam/HEAD/assets/models/medium_hull.glb
--------------------------------------------------------------------------------
/assets/models/medium_canon.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/claudijo/pirate-sea-jam/HEAD/assets/models/medium_canon.glb
--------------------------------------------------------------------------------
/assets/models/raft_with_mast.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/claudijo/pirate-sea-jam/HEAD/assets/models/raft_with_mast.glb
--------------------------------------------------------------------------------
/src/focal_point/resources.rs:
--------------------------------------------------------------------------------
1 | use bevy::prelude::*;
2 |
3 | #[derive(Resource)]
4 | pub struct FocalPoint(pub Vec3);
5 |
--------------------------------------------------------------------------------
/src/stats/resources.rs:
--------------------------------------------------------------------------------
1 | use bevy::prelude::*;
2 |
3 | #[derive(Resource)]
4 | pub struct NetworkStatsTimer(pub Timer);
5 |
--------------------------------------------------------------------------------
/assets/fonts/the-bomb-regular.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/claudijo/pirate-sea-jam/HEAD/assets/fonts/the-bomb-regular.otf
--------------------------------------------------------------------------------
/assets/models/medium_pirate_sail.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/claudijo/pirate-sea-jam/HEAD/assets/models/medium_pirate_sail.glb
--------------------------------------------------------------------------------
/src/camera/resources.rs:
--------------------------------------------------------------------------------
1 | use bevy::prelude::*;
2 |
3 | #[derive(Resource)]
4 | pub struct MainCamera {
5 | pub id: Entity,
6 | }
7 |
--------------------------------------------------------------------------------
/src/ocean/components.rs:
--------------------------------------------------------------------------------
1 | use bevy::prelude::*;
2 |
3 | #[derive(Component)]
4 | pub struct OceanTile {
5 | pub offset: Vec3,
6 | }
7 |
--------------------------------------------------------------------------------
/src/orbiting_camera/events.rs:
--------------------------------------------------------------------------------
1 | use bevy::prelude::*;
2 |
3 | #[derive(Event)]
4 | pub struct OrbitMotion {
5 | pub delta: Vec2,
6 | }
7 |
--------------------------------------------------------------------------------
/src/menu/components.rs:
--------------------------------------------------------------------------------
1 | use bevy::prelude::*;
2 |
3 | #[derive(Component)]
4 | pub struct StartGameButton;
5 |
6 | #[derive(Component)]
7 | pub struct StartMenuLayout;
8 |
--------------------------------------------------------------------------------
/src/utils.rs:
--------------------------------------------------------------------------------
1 | pub mod aerodynamics;
2 | pub mod f32_extensions;
3 | pub mod hash;
4 | pub mod linear_algebra;
5 | pub mod vec2_extensions;
6 | pub mod water_mechanics;
7 |
--------------------------------------------------------------------------------
/wasm/models/cannon_ball.glb.meta:
--------------------------------------------------------------------------------
1 | (
2 | meta_format_version: "1.0",
3 | asset: Load(
4 | loader: "bevy_gltf::loader::GltfLoader",
5 | settings: (),
6 | ),
7 | )
--------------------------------------------------------------------------------
/wasm/models/medium_canon.glb.meta:
--------------------------------------------------------------------------------
1 | (
2 | meta_format_version: "1.0",
3 | asset: Load(
4 | loader: "bevy_gltf::loader::GltfLoader",
5 | settings: (),
6 | ),
7 | )
--------------------------------------------------------------------------------
/wasm/models/medium_flag.glb.meta:
--------------------------------------------------------------------------------
1 | (
2 | meta_format_version: "1.0",
3 | asset: Load(
4 | loader: "bevy_gltf::loader::GltfLoader",
5 | settings: (),
6 | ),
7 | )
--------------------------------------------------------------------------------
/wasm/models/medium_helm.glb.meta:
--------------------------------------------------------------------------------
1 | (
2 | meta_format_version: "1.0",
3 | asset: Load(
4 | loader: "bevy_gltf::loader::GltfLoader",
5 | settings: (),
6 | ),
7 | )
--------------------------------------------------------------------------------
/wasm/models/medium_hull.glb.meta:
--------------------------------------------------------------------------------
1 | (
2 | meta_format_version: "1.0",
3 | asset: Load(
4 | loader: "bevy_gltf::loader::GltfLoader",
5 | settings: (),
6 | ),
7 | )
--------------------------------------------------------------------------------
/wasm/models/pirate_flag.glb.meta:
--------------------------------------------------------------------------------
1 | (
2 | meta_format_version: "1.0",
3 | asset: Load(
4 | loader: "bevy_gltf::loader::GltfLoader",
5 | settings: (),
6 | ),
7 | )
--------------------------------------------------------------------------------
/wasm/models/medium_pirate_sail.glb.meta:
--------------------------------------------------------------------------------
1 | (
2 | meta_format_version: "1.0",
3 | asset: Load(
4 | loader: "bevy_gltf::loader::GltfLoader",
5 | settings: (),
6 | ),
7 | )
--------------------------------------------------------------------------------
/wasm/models/raft_with_mast.glb.meta:
--------------------------------------------------------------------------------
1 | (
2 | meta_format_version: "1.0",
3 | asset: Load(
4 | loader: "bevy_gltf::loader::GltfLoader",
5 | settings: (),
6 | ),
7 | )
--------------------------------------------------------------------------------
/wasm/fonts/the-bomb-regular.otf.meta:
--------------------------------------------------------------------------------
1 | (
2 | meta_format_version: "1.0",
3 | asset: Load(
4 | loader: "bevy_text::font_loader::FontLoader",
5 | settings: (),
6 | ),
7 | )
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Bevy is dual-licensed under either
2 |
3 | * MIT License (docs/LICENSE-MIT or http://opensource.org/licenses/MIT)
4 | * Apache License, Version 2.0 (docs/LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
5 |
6 | at your option.
--------------------------------------------------------------------------------
/src/game_state/states.rs:
--------------------------------------------------------------------------------
1 | use bevy::prelude::*;
2 |
3 | #[derive(Clone, Debug, Default, Hash, Eq, States, PartialEq)]
4 | pub enum GameState {
5 | #[default]
6 | LoadingAssets,
7 | SplashScreen,
8 | Matchmaking,
9 | InGame,
10 | }
11 |
--------------------------------------------------------------------------------
/src/args/run_conditions.rs:
--------------------------------------------------------------------------------
1 | use crate::args::resources::Args;
2 | use bevy::prelude::*;
3 |
4 | pub fn sync_test_mode(args: Res) -> bool {
5 | args.sync_test
6 | }
7 |
8 | pub fn p2p_mode(args: Res) -> bool {
9 | !args.sync_test
10 | }
11 |
--------------------------------------------------------------------------------
/src/light.rs:
--------------------------------------------------------------------------------
1 | use crate::light::systems::spawn_light;
2 | use bevy::prelude::*;
3 |
4 | mod systems;
5 |
6 | pub struct LightPlugin;
7 |
8 | impl Plugin for LightPlugin {
9 | fn build(&self, app: &mut App) {
10 | app.add_systems(Startup, spawn_light);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/wind.rs:
--------------------------------------------------------------------------------
1 | use crate::wind::resources::Wind;
2 | use bevy::prelude::*;
3 |
4 | pub mod resources;
5 |
6 | pub struct WindPlugin;
7 |
8 | impl Plugin for WindPlugin {
9 | fn build(&self, app: &mut App) {
10 | app.insert_resource(Wind(Vec3::new(6., 0., 0.)));
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/player/components.rs:
--------------------------------------------------------------------------------
1 | use bevy::prelude::*;
2 |
3 | #[derive(Default, Reflect, Component, Clone, Copy)]
4 | #[reflect(Component)]
5 | pub struct Player {
6 | pub handle: usize,
7 | }
8 |
9 | #[derive(Component)]
10 | pub struct Wheel;
11 |
12 | #[derive(Component)]
13 | pub struct Flag;
14 |
--------------------------------------------------------------------------------
/wasm/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Pirate Sea Jam",
3 | "short_name": "Pirate Sea Jam",
4 | "start_url": ".",
5 | "display": "fullscreen",
6 | "orientation": "landscape",
7 | "background_color": "#ccc",
8 | "description": "Jam-sized pirate game prototype in the making. Written in Rust and Bevy. Made with ❤"
9 | }
--------------------------------------------------------------------------------
/wasm/skyboxes/basic.png.meta:
--------------------------------------------------------------------------------
1 | (
2 | meta_format_version: "1.0",
3 | asset: Load(
4 | loader: "bevy_render::texture::image_loader::ImageLoader",
5 | settings: (
6 | format: FromExtension,
7 | is_srgb: true,
8 | sampler: Default,
9 | ),
10 | ),
11 | )
--------------------------------------------------------------------------------
/justfile:
--------------------------------------------------------------------------------
1 | default: run
2 |
3 | build-wasm:
4 | cargo build --release --target wasm32-unknown-unknown --no-default-features
5 | wasm-bindgen --no-typescript --out-name bevy_game --out-dir wasm --target web target/wasm32-unknown-unknown/release/pirate-sea-jam.wasm
6 | cp -r assets wasm/
7 |
8 | run: build-wasm
9 | sfz -b 0.0.0.0 ./wasm
--------------------------------------------------------------------------------
/src/widget_debug.rs:
--------------------------------------------------------------------------------
1 | mod systems;
2 |
3 | use crate::widget_debug::systems::{debug_buoys, debug_particle};
4 | use bevy::prelude::*;
5 |
6 | pub struct WidgetDebugPlugin;
7 |
8 | impl Plugin for WidgetDebugPlugin {
9 | fn build(&self, app: &mut App) {
10 | app.add_systems(Update, (debug_buoys, debug_particle));
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/artillery/resources.rs:
--------------------------------------------------------------------------------
1 | use bevy::prelude::*;
2 | use std::collections::HashMap;
3 |
4 | #[derive(Resource)]
5 | pub struct StartAimArtilleryAnimationClips {
6 | pub handles: HashMap<&'static str, Handle>,
7 | }
8 |
9 | #[derive(Resource)]
10 | pub struct EndAimArtilleryAnimationClips {
11 | pub handles: HashMap<&'static str, Handle>,
12 | }
13 |
--------------------------------------------------------------------------------
/src/instructions.rs:
--------------------------------------------------------------------------------
1 | use crate::camera::systems::spawn_camera;
2 | use crate::instructions::systems::display_control_keys;
3 | use bevy::prelude::*;
4 |
5 | mod systems;
6 |
7 | pub struct InstructionsPlugin;
8 |
9 | impl Plugin for InstructionsPlugin {
10 | fn build(&self, app: &mut App) {
11 | app.add_systems(Startup, display_control_keys.after(spawn_camera));
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/assets/resources.rs:
--------------------------------------------------------------------------------
1 | use bevy::prelude::*;
2 | use std::collections::HashMap;
3 |
4 | #[derive(Resource, Default)]
5 | pub struct ModelAssets {
6 | pub scene_handles: HashMap<&'static str, Handle>,
7 | pub mesh_handles: HashMap<&'static str, Handle>,
8 | }
9 |
10 | #[derive(Resource, Default)]
11 | pub struct FontAssets {
12 | pub font_handles: HashMap<&'static str, Handle>,
13 | }
14 |
--------------------------------------------------------------------------------
/src/args.rs:
--------------------------------------------------------------------------------
1 | use crate::args::resources::Args;
2 | use bevy::app::{App, Plugin};
3 | use bevy::log::info;
4 | use clap::Parser;
5 |
6 | pub mod resources;
7 | pub mod run_conditions;
8 |
9 | pub struct ArgsPlugin;
10 |
11 | impl Plugin for ArgsPlugin {
12 | fn build(&self, app: &mut App) {
13 | let args = Args::parse();
14 | info!("{args:?}");
15 |
16 | app.insert_resource(args);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/artillery/components.rs:
--------------------------------------------------------------------------------
1 | use bevy::prelude::*;
2 |
3 | #[derive(Component, Clone, Copy, Default)]
4 | pub struct ArtilleryReady(pub bool);
5 |
6 | #[derive(Component, Clone, Copy, Default)]
7 | pub struct ArtilleryAiming(pub bool);
8 |
9 | #[derive(Component, Clone, Copy, Default)]
10 | pub struct Projectile;
11 |
12 | #[derive(Component, Clone, Copy, Default)]
13 | pub struct Artillery {
14 | pub muzzle_velocity: f32,
15 | pub is_aiming: bool,
16 | }
17 |
--------------------------------------------------------------------------------
/src/controls.rs:
--------------------------------------------------------------------------------
1 | use crate::controls::components::{checksum_wheel_turn_ratio, WheelTurnRatio};
2 | use bevy::prelude::*;
3 | use bevy_ggrs::GgrsApp;
4 |
5 | pub mod components;
6 |
7 | pub struct ShipPlugin;
8 |
9 | impl Plugin for ShipPlugin {
10 | fn build(&self, app: &mut App) {
11 | // Component candidates for roll back
12 | app.rollback_component_with_copy::();
13 |
14 | app.checksum_component::(checksum_wheel_turn_ratio);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/args/resources.rs:
--------------------------------------------------------------------------------
1 | use bevy::prelude::*;
2 |
3 | use clap::Parser;
4 |
5 | #[derive(Parser, Resource, Debug, Clone)]
6 | pub struct Args {
7 | // runs the game in sync test mode / local player mode
8 | #[clap(long, default_value = "true")]
9 | pub sync_test: bool,
10 |
11 | // Set to 2 for doing sync tests, 0 for optimized single player game
12 | #[clap(long, default_value = "0")]
13 | pub check_distance: usize,
14 |
15 | #[clap(long, default_value = "1")]
16 | pub num_players: usize,
17 | }
18 |
--------------------------------------------------------------------------------
/src/orbiting_camera.rs:
--------------------------------------------------------------------------------
1 | pub mod events;
2 | pub mod resources;
3 | mod systems;
4 |
5 | use crate::game_state::states::GameState;
6 | use crate::orbiting_camera::events::OrbitMotion;
7 | use crate::orbiting_camera::systems::orbit;
8 | use bevy::prelude::*;
9 |
10 | pub struct OrbitingCameraPlugin;
11 |
12 | impl Plugin for OrbitingCameraPlugin {
13 | fn build(&self, app: &mut App) {
14 | app.add_event::();
15 |
16 | app.add_systems(Update, orbit.run_if(in_state(GameState::InGame)));
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/sync_test.rs:
--------------------------------------------------------------------------------
1 | mod systems;
2 |
3 | use crate::args::run_conditions::sync_test_mode;
4 | use crate::game_state::states::GameState;
5 | use crate::sync_test::systems::start_sync_test_session;
6 | use bevy::prelude::*;
7 |
8 | pub struct SyncTestPlugin;
9 |
10 | impl Plugin for SyncTestPlugin {
11 | fn build(&self, app: &mut App) {
12 | app.add_systems(
13 | Update,
14 | start_sync_test_session
15 | .run_if(in_state(GameState::Matchmaking).and_then(sync_test_mode)),
16 | );
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/utils/f32_extensions.rs:
--------------------------------------------------------------------------------
1 | use bevy::math::FloatExt;
2 | use std::f32::consts::E;
3 |
4 | pub trait F32Ext {
5 | #[allow(dead_code)]
6 | fn damp(self, rhs: Self, lambda: f32, delta_time: f32) -> Self;
7 | }
8 | impl F32Ext for f32 {
9 | // lambda has range between `0` and infinity, will approach rhs
10 | // See https://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/
11 | fn damp(self, rhs: Self, lambda: f32, delta_time: f32) -> Self {
12 | self.lerp(rhs, 1. - E.powf(-lambda * delta_time))
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/debug_fps.rs:
--------------------------------------------------------------------------------
1 | use crate::camera::systems::spawn_camera;
2 | use crate::debug_fps::systems::{spawn_debug_fps, update_debug_fps};
3 | use bevy::diagnostic::FrameTimeDiagnosticsPlugin;
4 | use bevy::prelude::*;
5 |
6 | mod resources;
7 | mod systems;
8 |
9 | pub struct DebugFpsPlugin;
10 |
11 | impl Plugin for DebugFpsPlugin {
12 | fn build(&self, app: &mut App) {
13 | app.add_plugins(FrameTimeDiagnosticsPlugin);
14 |
15 | app.add_systems(Startup, spawn_debug_fps.after(spawn_camera));
16 |
17 | app.add_systems(Update, update_debug_fps);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/focal_point.rs:
--------------------------------------------------------------------------------
1 | pub mod resources;
2 | mod systems;
3 |
4 | use crate::focal_point::resources::FocalPoint;
5 | use crate::focal_point::systems::update_focal_point;
6 | use crate::game_state::states::GameState;
7 | use bevy::prelude::*;
8 |
9 | pub struct FocalPointPlugin;
10 |
11 | impl Plugin for FocalPointPlugin {
12 | fn build(&self, app: &mut App) {
13 | app.insert_resource(FocalPoint(Vec3::ZERO));
14 |
15 | app.add_systems(
16 | Update,
17 | update_focal_point.run_if(in_state(GameState::InGame)),
18 | );
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/sky_box.rs:
--------------------------------------------------------------------------------
1 | use crate::focal_point::resources::FocalPoint;
2 | use crate::sky_box::systems::{spawn_sky_box, sync_sky_box_center_offset};
3 | use bevy::app::{App, Plugin, Startup, Update};
4 | use bevy::prelude::*;
5 |
6 | mod components;
7 | mod systems;
8 |
9 | pub struct SkyBoxPlugin;
10 |
11 | impl Plugin for SkyBoxPlugin {
12 | fn build(&self, app: &mut App) {
13 | app.add_systems(Startup, spawn_sky_box);
14 |
15 | app.add_systems(
16 | Update,
17 | sync_sky_box_center_offset.run_if(resource_changed::),
18 | );
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/orbiting_camera/resources.rs:
--------------------------------------------------------------------------------
1 | use bevy::prelude::*;
2 | use std::f32::consts::FRAC_PI_2;
3 |
4 | #[derive(Component)]
5 | pub struct OrbitingCamera {
6 | pub radius: f32,
7 | pub pitch: f32,
8 | pub yaw: f32,
9 | pub min_pitch: f32,
10 | pub max_pitch: f32,
11 | }
12 |
13 | impl Default for OrbitingCamera {
14 | fn default() -> Self {
15 | OrbitingCamera {
16 | radius: 10.,
17 | pitch: 30_f32.to_radians(),
18 | yaw: 0.,
19 | min_pitch: 10_f32.to_radians(),
20 | max_pitch: FRAC_PI_2,
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/wasm/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
10 |
11 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/controls/components.rs:
--------------------------------------------------------------------------------
1 | use crate::utils::hash::hash_f32_number;
2 | use bevy::prelude::*;
3 |
4 | #[derive(Component, Reflect, Clone, Copy, Default)]
5 | #[reflect(Component)]
6 | pub struct WheelTurnRatio(pub f32);
7 |
8 | #[derive(Component, Reflect, Clone, Copy, Default)]
9 | #[reflect(Component)]
10 | pub struct SailTrimRatio(pub f32);
11 |
12 | #[derive(Component, Reflect, Clone, Copy, Default)]
13 | #[reflect(Component)]
14 | pub struct Controls {
15 | pub turn_action: i32,
16 | pub accelerate_action: i32,
17 | }
18 |
19 | pub fn checksum_wheel_turn_ratio(value: &WheelTurnRatio) -> u64 {
20 | hash_f32_number(value.0)
21 | }
22 |
--------------------------------------------------------------------------------
/src/focal_point/systems.rs:
--------------------------------------------------------------------------------
1 | use crate::focal_point::resources::FocalPoint;
2 | use crate::player::components::Player;
3 | use bevy::prelude::*;
4 | use bevy_ggrs::LocalPlayers;
5 |
6 | pub fn update_focal_point(
7 | player_query: Query<(&Player, &Transform)>,
8 | local_players: Res,
9 | mut focal_point: ResMut,
10 | ) {
11 | for (player, transform) in &player_query {
12 | // Ignore non-local players
13 | if !local_players.0.contains(&player.handle) {
14 | continue;
15 | }
16 |
17 | focal_point.0 = transform.translation;
18 | focal_point.0.y = 0.;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/light/systems.rs:
--------------------------------------------------------------------------------
1 | use bevy::prelude::*;
2 |
3 | pub fn spawn_light(mut commands: Commands) {
4 | // Sun
5 | commands.spawn(DirectionalLightBundle {
6 | directional_light: DirectionalLight {
7 | color: Color::rgb(0.98, 0.95, 0.82),
8 | shadows_enabled: true,
9 | ..default()
10 | },
11 | transform: Transform::from_xyz(0.0, 0.0, 0.0)
12 | .with_rotation(Quat::from_rotation_x(-40_f32.to_radians())),
13 | ..default()
14 | });
15 |
16 | // ambient light
17 | commands.insert_resource(AmbientLight {
18 | color: Color::rgb_u8(210, 220, 240),
19 | brightness: 160.,
20 | });
21 | }
22 |
--------------------------------------------------------------------------------
/src/physics/resources.rs:
--------------------------------------------------------------------------------
1 | use bevy::prelude::*;
2 |
3 | #[derive(Resource)]
4 | pub struct Gravity(pub Vec3);
5 |
6 | impl Default for Gravity {
7 | fn default() -> Self {
8 | Gravity(Vec3::NEG_Y * 15.)
9 | }
10 | }
11 |
12 | #[derive(Resource)]
13 | pub struct WaterDensity(pub f32);
14 |
15 | impl Default for WaterDensity {
16 | fn default() -> Self {
17 | // 1000 kg per cubic meter.
18 | WaterDensity(1000.)
19 | }
20 | }
21 |
22 | #[derive(Resource)]
23 | pub struct AirDensity(pub f32);
24 |
25 | impl Default for crate::physics::resources::AirDensity {
26 | fn default() -> Self {
27 | // 1.2 kg per cubic meter.
28 | AirDensity(1.2)
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/utils/linear_algebra.rs:
--------------------------------------------------------------------------------
1 | use bevy::math::Vec3;
2 |
3 | pub fn face_normal(a: [f32; 3], b: [f32; 3], c: [f32; 3]) -> [f32; 3] {
4 | let (a, b, c) = (Vec3::from(a), Vec3::from(b), Vec3::from(c));
5 | (b - a).cross(c - a).normalize().into()
6 | }
7 |
8 | pub fn is_facing(source_direction: Vec3, source_position: Vec3, target_position: Vec3) -> bool {
9 | (target_position - source_position).dot(source_direction) > 0.
10 | }
11 |
12 | pub fn angle_between_perpendicular(vector: Vec3, normal: Vec3) -> f32 {
13 | (vector.dot(normal) / vector.length()).asin()
14 | }
15 |
16 | pub fn perpendicular_to_projection_direction(vector: Vec3, normal: Vec3) -> Vec3 {
17 | normal - normal.project_onto(vector)
18 | }
19 |
--------------------------------------------------------------------------------
/src/camera.rs:
--------------------------------------------------------------------------------
1 | use crate::camera::systems::{
2 | grab_pointer, release_pointer, release_pointer_on_escape, spawn_camera,
3 | };
4 | use crate::game_state::states::GameState;
5 | use bevy::prelude::*;
6 |
7 | pub mod resources;
8 | pub mod systems;
9 |
10 | pub struct CameraPlugin;
11 |
12 | impl Plugin for CameraPlugin {
13 | fn build(&self, app: &mut App) {
14 | app.add_systems(Startup, spawn_camera);
15 |
16 | app.add_systems(OnEnter(GameState::InGame), grab_pointer);
17 | app.add_systems(OnExit(GameState::InGame), release_pointer);
18 |
19 | app.add_systems(
20 | Update,
21 | release_pointer_on_escape.run_if(in_state(GameState::InGame)),
22 | );
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/utils/vec2_extensions.rs:
--------------------------------------------------------------------------------
1 | use bevy::prelude::*;
2 | use std::f32::consts::E;
3 |
4 | pub trait Vec2Ext {
5 | #[allow(dead_code)]
6 | fn extend_with_y(self, y: f32) -> Vec3;
7 | #[allow(dead_code)]
8 | fn damp(self, rhs: Self, lambda: f32, delta_time: f32) -> Self;
9 | }
10 |
11 | impl Vec2Ext for Vec2 {
12 | fn extend_with_y(self, y: f32) -> Vec3 {
13 | Vec3::new(self.x, y, self.y)
14 | }
15 |
16 | // lambda has range between `0` and infinity, will approach rhs
17 | // See https://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/
18 | fn damp(self, rhs: Self, lambda: f32, delta_time: f32) -> Self {
19 | self.lerp(rhs, 1. - E.powf(-lambda * delta_time))
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/assets/shaders/water_dynamics.wgsl:
--------------------------------------------------------------------------------
1 | #define_import_path pirate_sea_jam::water_dynamics
2 |
3 | const PI: f32 = 3.14159265358979323846264338327950288;
4 | const GRAVITY: f32 = 10.;
5 |
6 | // `wave`: Vec4 containing direction x, direction z, steepness, wave_length
7 | fn gerstner_wave(wave: vec4, p: vec3, time: f32) -> vec3 {
8 | let steepness = wave.z;
9 | let wave_length = wave.w;
10 |
11 | let k: f32 = 2. * PI / wave_length;
12 | let c: f32 = sqrt(GRAVITY / k);
13 | let d: vec2 = normalize(wave.xy);
14 | let f: f32 = k * (dot(d, p.xz) - c * time);
15 | let a: f32 = steepness / k;
16 |
17 | return vec3(
18 | d.x * (a * cos(f)),
19 | a * sin(f),
20 | d.y * (a * cos(f))
21 | );
22 | }
--------------------------------------------------------------------------------
/assets/shaders/ocean_material_bindings.wgsl:
--------------------------------------------------------------------------------
1 | #define_import_path pirate_sea_jam::ocean_material_bindings
2 |
3 | const WAVES_COUNT: i32 = 4;
4 |
5 | struct OceanTilelSettings {
6 | tile_offset: vec3,
7 | tile_size: f32,
8 | quad_cell_size: f32,
9 | tier: u32,
10 | time_scale: f32,
11 | waves: array, WAVES_COUNT>,
12 | subdivision_count: u32,
13 | }
14 |
15 | struct OceanPosition {
16 | center_offset: vec3,
17 | }
18 |
19 | struct RollbackTime {
20 | elapsed_seconds: f32,
21 | padding: vec3,
22 | }
23 |
24 | @group(2) @binding(100)
25 | var settings: OceanTilelSettings;
26 |
27 | @group(2) @binding(101)
28 | var position: OceanPosition;
29 |
30 | @group(2) @binding(102)
31 | var time: RollbackTime;
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "pirate-sea-jam"
3 | version = "0.10.1"
4 | edition = "2021"
5 | license = "MIT OR Apache-2.0"
6 |
7 | [dependencies]
8 | bevy = "0.13.2"
9 | bevy_editor_pls = "0.8.1"
10 | bevy_ggrs = { version = "0.15.0", features = ["wasm-bindgen"] }
11 | bevy_matchbox = { version = "0.9.0", features = ["ggrs"] }
12 | clap = { version = "4.5.4", features = ["derive"] }
13 |
14 | # Enable a small amount of optimization in debug mode
15 | [profile.dev]
16 | opt-level = 1
17 |
18 | # Enable high optimizations for dependencies (incl. Bevy), but not for our code:
19 | [profile.dev.package."*"]
20 | opt-level = 3
21 |
22 | # Optimize for WASM builds for size (see https://bevy-cheatbook.github.io/platforms/wasm/size-opt.html)
23 | [profile.release]
24 | opt-level = 's'
25 | lto = "thin"
26 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Pirate Sea Jam
2 |
3 | Jam-sized pirate game prototype in the making. Written in Rust and Bevy. Made with ❤️
4 |
5 | Demo and devlog available at [https://claudijo.itch.io/pirate-sea-jam](https://claudijo.itch.io/pirate-sea-jam)
6 |
7 | ## Changelog and Feature List
8 | The following brief changelog includes main concepts and features
9 | * Sailing simulation and custom physics (v0.10) (Reintroduce "Firing cannons")
10 | * Render ocean using WebGPU shaders (v0.9) (Temporarily invalidate "Mobile friendly" and "Firing cannons")
11 | * Infinite ocean (v0.8)
12 | * Dynamic third-person camera (v0.7)
13 | * ~~Mobile friendly (v0.6)~~
14 | * Firing cannons (v0.5)
15 | * Player control (v0.4)
16 | * 3D models with textures (v0.3)
17 | * Buoyancy (v0.2)
18 | * Basic ocean tile with dynamic water waves (v0.1)
19 |
--------------------------------------------------------------------------------
/src/connection.rs:
--------------------------------------------------------------------------------
1 | use crate::args::run_conditions::p2p_mode;
2 | use crate::connection::systems::{start_matchbox_socket, wait_for_players};
3 | use crate::game_state::states::GameState;
4 | use bevy::prelude::*;
5 |
6 | pub mod systems;
7 |
8 | pub const MAX_PREDICTION: usize = 12;
9 | pub const FPS: usize = 60;
10 | pub const INPUT_DELAY: usize = 2;
11 |
12 | pub struct ConnectionPlugin;
13 |
14 | impl Plugin for ConnectionPlugin {
15 | fn build(&self, app: &mut App) {
16 | app.add_systems(
17 | OnEnter(GameState::Matchmaking),
18 | start_matchbox_socket.run_if(p2p_mode),
19 | );
20 |
21 | app.add_systems(
22 | Update,
23 | wait_for_players.run_if(in_state(GameState::Matchmaking).and_then(p2p_mode)),
24 | );
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/stats.rs:
--------------------------------------------------------------------------------
1 | use crate::args::run_conditions::p2p_mode;
2 | use crate::game_state::states::GameState;
3 | use crate::stats::resources::NetworkStatsTimer;
4 | use crate::stats::systems::{print_events, print_network_stats};
5 | use bevy::prelude::*;
6 |
7 | mod resources;
8 | mod systems;
9 |
10 | pub struct StatsPlugin;
11 |
12 | impl Plugin for StatsPlugin {
13 | fn build(&self, app: &mut App) {
14 | app.insert_resource(NetworkStatsTimer(Timer::from_seconds(
15 | 2.0,
16 | TimerMode::Repeating,
17 | )));
18 | app.add_systems(Update, print_events.run_if(in_state(GameState::InGame)));
19 |
20 | app.add_systems(
21 | Update,
22 | print_network_stats.run_if(in_state(GameState::InGame).and_then(p2p_mode)),
23 | );
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/menu.rs:
--------------------------------------------------------------------------------
1 | mod components;
2 | mod systems;
3 |
4 | use crate::game_state::states::GameState;
5 | use crate::menu::systems::{despawn_main_menu, handle_main_menu_interactions, spawn_main_menu};
6 | use bevy::prelude::*;
7 |
8 | pub struct MenuPlugin;
9 |
10 | pub const START_BUTTON_NORMAL: Color = Color::rgb(0.9, 0.45, 0.21);
11 | pub const START_BUTTON_HOVER: Color = Color::rgb(0.87, 0.36, 0.18);
12 |
13 | impl Plugin for MenuPlugin {
14 | fn build(&self, app: &mut App) {
15 | app.add_systems(OnEnter(GameState::SplashScreen), spawn_main_menu)
16 | .add_systems(OnExit(GameState::SplashScreen), despawn_main_menu)
17 | .add_systems(
18 | Update,
19 | handle_main_menu_interactions.run_if(in_state(GameState::SplashScreen)),
20 | );
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/widget_debug/systems.rs:
--------------------------------------------------------------------------------
1 | use crate::physics::components::{Buoy, Mass};
2 | use bevy::prelude::*;
3 |
4 | pub fn debug_buoys(buoy_query: Query<(&Buoy, &GlobalTransform)>, mut gizmos: Gizmos) {
5 | for (buoy, global_transform) in &buoy_query {
6 | gizmos.cuboid(
7 | Transform::from_matrix(global_transform.compute_matrix())
8 | .with_scale(Vec3::splat(buoy.max_depth * 2.)),
9 | Color::RED,
10 | )
11 | }
12 | }
13 |
14 | pub fn debug_particle(particle_query: Query<(&Mass, &GlobalTransform)>, mut gizmos: Gizmos) {
15 | for (mass, global_transform) in &particle_query {
16 | let (_, rotation, translation) = global_transform.to_scale_rotation_translation();
17 | gizmos.sphere(translation, rotation, mass.0 * 0.1, Color::PURPLE);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/physics/bundles.rs:
--------------------------------------------------------------------------------
1 | use crate::physics::components::{
2 | AngularDamping, AngularVelocity, ExternalForce, ExternalImpulse, ExternalTorque,
3 | ExternalTorqueImpulse, Inertia, LinearDamping, LinearVelocity, Mass,
4 | };
5 | use bevy::prelude::*;
6 |
7 | #[derive(Bundle, Default)]
8 | pub struct ParticleBundle {
9 | pub linear_velocity: LinearVelocity,
10 | pub external_force: ExternalForce,
11 | pub linear_damping: LinearDamping,
12 | pub external_impulse: ExternalImpulse,
13 | pub mass: Mass,
14 | }
15 |
16 | #[derive(Bundle, Default)]
17 | pub struct SpindleBundle {
18 | pub angular_velocity: AngularVelocity,
19 | pub external_torque: ExternalTorque,
20 | pub angular_damping: AngularDamping,
21 | pub external_torque_impulse: ExternalTorqueImpulse,
22 | pub inertia: Inertia,
23 | }
24 |
--------------------------------------------------------------------------------
/src/assets.rs:
--------------------------------------------------------------------------------
1 | pub mod resources;
2 | mod systems;
3 |
4 | use crate::assets::systems::{add_assets, check_assets_ready};
5 | use crate::game_state::states::GameState;
6 | use bevy::prelude::*;
7 |
8 | const MODEL_FILE_NAMES: [&str; 7] = [
9 | "medium_flag.glb",
10 | "medium_helm.glb",
11 | "medium_hull.glb",
12 | "medium_pirate_sail.glb",
13 | "medium_canon.glb",
14 | "raft_with_mast.glb",
15 | "cannon_ball.glb",
16 | ];
17 |
18 | const MESH_FILE_NAMES: [&str; 1] = ["medium_flag.glb"];
19 |
20 | const FONT_FILE_NAMES: [&str; 1] = ["the-bomb-regular.otf"];
21 |
22 | pub struct AssetsPlugin;
23 |
24 | impl Plugin for AssetsPlugin {
25 | fn build(&self, app: &mut App) {
26 | app.add_systems(OnEnter(GameState::LoadingAssets), add_assets);
27 |
28 | app.add_systems(
29 | Update,
30 | check_assets_ready.run_if(in_state(GameState::LoadingAssets)),
31 | );
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/inputs.rs:
--------------------------------------------------------------------------------
1 | use crate::inputs::systems::{read_local_inputs, read_mouse_input};
2 | use bevy::prelude::*;
3 | use bevy_ggrs::ggrs::InputStatus;
4 | use bevy_ggrs::ReadInputs;
5 |
6 | mod systems;
7 |
8 | // pub const INPUT_UP: u8 = 1 << 0;
9 | // pub const INPUT_DOWN: u8 = 1 << 1;
10 | pub const INPUT_LEFT: u8 = 1 << 2;
11 | pub const INPUT_RIGHT: u8 = 1 << 3;
12 | pub const INPUT_FIRE: u8 = 1 << 4;
13 |
14 | pub fn turn_action_from_input(input_and_status: (u8, InputStatus)) -> i32 {
15 | let input = match input_and_status.1 {
16 | InputStatus::Confirmed => input_and_status.0,
17 | InputStatus::Predicted => input_and_status.0,
18 | InputStatus::Disconnected => 0, // disconnected players do nothing
19 | };
20 |
21 | let mut turn: i32 = 0;
22 |
23 | if input & INPUT_RIGHT != 0 {
24 | turn += 1;
25 | }
26 | if input & INPUT_LEFT != 0 {
27 | turn -= 1;
28 | }
29 |
30 | turn
31 | }
32 |
33 | pub fn fire(input: u8) -> bool {
34 | input & INPUT_FIRE != 0
35 | }
36 |
37 | pub struct InputsPlugin;
38 |
39 | impl Plugin for InputsPlugin {
40 | fn build(&self, app: &mut App) {
41 | app.add_systems(ReadInputs, read_local_inputs);
42 |
43 | app.add_systems(Update, read_mouse_input);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/utils/aerodynamics.rs:
--------------------------------------------------------------------------------
1 | use crate::utils::linear_algebra::{
2 | angle_between_perpendicular, perpendicular_to_projection_direction,
3 | };
4 | use bevy::math::Vec3;
5 | use std::ops::Neg;
6 |
7 | // https://aviation.stackexchange.com/a/64637
8 | pub fn simple_lift_coefficient(angle_of_attack: f32) -> f32 {
9 | (angle_of_attack * 2.).sin()
10 | }
11 |
12 | // https://aviation.stackexchange.com/a/64637
13 | pub fn simple_drag_coefficient(angle_of_attack: f32) -> f32 {
14 | 1. - (angle_of_attack * 2.).cos()
15 | }
16 |
17 | // https://math.stackexchange.com/a/4890320/1306679
18 | pub fn scaled_lift_drag(relative_velocity: Vec3, surface_normal: Vec3) -> (Vec3, Vec3) {
19 | if relative_velocity.length().abs() <= f32::EPSILON {
20 | return (Vec3::ZERO, Vec3::ZERO);
21 | }
22 |
23 | let normal = if relative_velocity.dot(surface_normal) > 0. {
24 | surface_normal
25 | } else {
26 | surface_normal.neg()
27 | };
28 |
29 | let angle = angle_between_perpendicular(relative_velocity, normal);
30 |
31 | let lift = perpendicular_to_projection_direction(relative_velocity, normal).normalize()
32 | * simple_lift_coefficient(angle);
33 | let drag = relative_velocity.normalize() * simple_drag_coefficient(angle);
34 |
35 | (lift, drag)
36 | }
37 |
--------------------------------------------------------------------------------
/src/sky_box/systems.rs:
--------------------------------------------------------------------------------
1 | use crate::focal_point::resources::FocalPoint;
2 | use crate::ocean::OCEAN_TILE_SIZE;
3 | use crate::sky_box::components::Sky;
4 | use bevy::asset::Assets;
5 | use bevy::math::Vec3;
6 | use bevy::pbr::{NotShadowCaster, PbrBundle, StandardMaterial};
7 | use bevy::prelude::*;
8 |
9 | pub fn spawn_sky_box(
10 | mut commands: Commands,
11 | mut meshes: ResMut>,
12 | mut materials: ResMut>,
13 | ) {
14 | // Sky box
15 | commands.spawn((
16 | PbrBundle {
17 | mesh: meshes.add(Mesh::from(Cuboid::new(1., 1., 1.))),
18 | material: materials.add(StandardMaterial {
19 | base_color: Color::hex("a5cddf").unwrap(),
20 | unlit: true,
21 | cull_mode: None,
22 | ..default()
23 | }),
24 | transform: Transform::from_scale(Vec3::splat(OCEAN_TILE_SIZE * 9.)),
25 | ..default()
26 | },
27 | Sky,
28 | NotShadowCaster,
29 | ));
30 | }
31 |
32 | pub fn sync_sky_box_center_offset(
33 | focal_point: Res,
34 | mut sky_box_query: Query<&mut Transform, With>,
35 | ) {
36 | for mut transform in &mut sky_box_query {
37 | transform.translation = focal_point.0;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/debug_fps/systems.rs:
--------------------------------------------------------------------------------
1 | use crate::camera::resources::MainCamera;
2 | use crate::debug_fps::resources::DebugFps;
3 | use bevy::diagnostic::{DiagnosticsStore, FrameTimeDiagnosticsPlugin};
4 | use bevy::prelude::*;
5 |
6 | pub fn spawn_debug_fps(mut commands: Commands, main_camera: Res) {
7 | commands.spawn((
8 | // Seems to be required in dev builds since using editor plugin results in multiple
9 | // cameras
10 | TargetCamera(main_camera.id),
11 | TextBundle::from_section(
12 | "FPS: ??",
13 | TextStyle {
14 | font_size: 14.0,
15 | ..default()
16 | },
17 | )
18 | .with_style(Style {
19 | position_type: PositionType::Absolute,
20 | top: Val::Px(2.),
21 | left: Val::Px(2.),
22 | ..default()
23 | }),
24 | DebugFps,
25 | ));
26 | }
27 |
28 | pub fn update_debug_fps(
29 | diagnostics: Res,
30 | mut fps_text_query: Query<&mut Text, With>,
31 | ) {
32 | for mut text in &mut fps_text_query {
33 | if let Some(fps) = diagnostics.get(&FrameTimeDiagnosticsPlugin::FPS) {
34 | if let Some(value) = fps.average() {
35 | text.sections[0].value = format!("FPS: {value:.2}");
36 | }
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/assets/shaders/ocean_material_prepass.wgsl:
--------------------------------------------------------------------------------
1 | // Mainly copied from default StandardMaterial prepass shader and removed unused stuff
2 | // See https://github.com/bevyengine/bevy/blob/main/crates/bevy_pbr/src/prepass/prepass.wgsl
3 | // Possibly merge with corresponding main shader. Been tried using `#ifdef PREPASS_PIPELINE` directive without success.
4 |
5 | #import bevy_pbr::{
6 | mesh_functions::{mesh_position_local_to_clip, get_model_matrix},
7 | prepass_io::{Vertex, VertexOutput},
8 | }
9 |
10 | #import pirate_sea_jam::{
11 | water_dynamics::gerstner_wave,
12 | ocean_material_bindings,
13 | }
14 |
15 | @vertex
16 | fn vertex(in: Vertex) -> VertexOutput {
17 | let time = ocean_material_bindings::time.elapsed_seconds * ocean_material_bindings::settings.time_scale;
18 |
19 | var out: VertexOutput;
20 | var next_position = in.position;
21 |
22 | for (var i = 0; i < ocean_material_bindings::WAVES_COUNT; i += 1) {
23 | next_position += gerstner_wave(
24 | ocean_material_bindings::settings.waves[i],
25 | in.position + ocean_material_bindings::position.center_offset + ocean_material_bindings::settings.tile_offset,
26 | time
27 | );
28 | }
29 |
30 | var position = vec4(next_position, 1.);
31 |
32 | out.position = mesh_position_local_to_clip(
33 | get_model_matrix(in.instance_index),
34 | position
35 | );
36 |
37 | return out;
38 | }
--------------------------------------------------------------------------------
/src/instructions/systems.rs:
--------------------------------------------------------------------------------
1 | use crate::camera::resources::MainCamera;
2 | use bevy::prelude::*;
3 |
4 | pub fn display_control_keys(mut commands: Commands, main_camera: Res) {
5 | commands
6 | .spawn((
7 | // Seems to be required in dev builds since using editor plugin results in multiple
8 | // cameras
9 | TargetCamera(main_camera.id),
10 | NodeBundle {
11 | style: Style {
12 | width: Val::Percent(100.0),
13 | height: Val::Percent(100.0),
14 | align_items: AlignItems::Start,
15 | justify_content: JustifyContent::Center,
16 | padding: UiRect {
17 | left: Val::Px(16.),
18 | right: Val::Px(16.),
19 | top: Val::Px(16.),
20 | bottom: Val::Px(16.),
21 | },
22 | ..default()
23 | },
24 | ..default()
25 | },
26 | ))
27 | .with_children(|child_builder| {
28 | child_builder.spawn(TextBundle::from_section(
29 | "[A] turn port | [D] turn starboard | [Space] fire cannons | [Mouse] orbit camera",
30 | TextStyle {
31 | font_size: 18.0,
32 | color: Color::WHITE,
33 | ..default()
34 | },
35 | ));
36 | });
37 | }
38 |
--------------------------------------------------------------------------------
/src/inputs/systems.rs:
--------------------------------------------------------------------------------
1 | use crate::connection::systems::RollbackConfig;
2 | use crate::inputs::{INPUT_FIRE, INPUT_LEFT, INPUT_RIGHT};
3 | use crate::orbiting_camera::events::OrbitMotion;
4 | use bevy::input::mouse::MouseMotion;
5 | use bevy::prelude::*;
6 | use bevy::utils::HashMap;
7 | use bevy_ggrs::{LocalInputs, LocalPlayers};
8 |
9 | pub fn read_local_inputs(
10 | mut commands: Commands,
11 | keys: Res>,
12 | local_players: Res,
13 | ) {
14 | let mut local_inputs = HashMap::new();
15 |
16 | for handle in &local_players.0 {
17 | let mut input = 0u8;
18 | if keys.any_pressed([KeyCode::ArrowLeft, KeyCode::KeyA]) {
19 | input |= INPUT_LEFT
20 | }
21 | if keys.any_pressed([KeyCode::ArrowRight, KeyCode::KeyD]) {
22 | input |= INPUT_RIGHT;
23 | }
24 | if keys.any_pressed([KeyCode::Space, KeyCode::Enter]) {
25 | input |= INPUT_FIRE;
26 | }
27 |
28 | local_inputs.insert(*handle, input);
29 | }
30 |
31 | commands.insert_resource(LocalInputs::(local_inputs));
32 | }
33 |
34 | pub fn read_mouse_input(
35 | mut mouse_motion_event_reader: EventReader,
36 | mut orbit_motion_event_writer: EventWriter,
37 | ) {
38 | for mouse_motion_event in mouse_motion_event_reader.read() {
39 | orbit_motion_event_writer.send(OrbitMotion {
40 | delta: mouse_motion_event.delta,
41 | });
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/artillery.rs:
--------------------------------------------------------------------------------
1 | use crate::artillery::components::{ArtilleryAiming, ArtilleryReady};
2 | use crate::artillery::systems::{
3 | despawn_projectile, register_start_aim_artillery_animations,
4 | register_stop_aim_artillery_animations, start_aim_artillery, stop_aim_and_fire_artillery,
5 | };
6 | use crate::physics::systems::update_orientation;
7 | use bevy::prelude::*;
8 | use bevy_ggrs::{GgrsApp, GgrsSchedule};
9 |
10 | pub mod components;
11 | mod resources;
12 | mod systems;
13 |
14 | pub const PORT_BACK_CANNON_TAG: &str = "Port back cannon";
15 | pub const PORT_FRONT_CANNON_TAG: &str = "Port front cannon";
16 | pub const STARBOARD_BACK_CANNON_TAG: &str = "Starboard back cannon";
17 | pub const STARBOARD_FRONT_CANNON_TAG: &str = "Starboard front cannon";
18 |
19 | pub struct ArtilleryPlugin;
20 |
21 | impl Plugin for ArtilleryPlugin {
22 | fn build(&self, app: &mut App) {
23 | // Component candidates for roll back
24 | app.rollback_component_with_copy::();
25 | app.rollback_component_with_copy::();
26 |
27 | app.add_systems(
28 | Startup,
29 | (
30 | register_start_aim_artillery_animations,
31 | register_stop_aim_artillery_animations,
32 | ),
33 | );
34 |
35 | app.add_systems(
36 | GgrsSchedule,
37 | (
38 | start_aim_artillery.after(update_orientation),
39 | stop_aim_and_fire_artillery.after(start_aim_artillery),
40 | despawn_projectile,
41 | ),
42 | );
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/sync_test/systems.rs:
--------------------------------------------------------------------------------
1 | use crate::args::resources::Args;
2 | use crate::connection::systems::RollbackConfig;
3 | use crate::connection::{FPS, INPUT_DELAY, MAX_PREDICTION};
4 | use crate::game_state::states::GameState;
5 | use bevy::prelude::*;
6 | use bevy_ggrs::ggrs;
7 |
8 | pub fn start_sync_test_session(
9 | mut commands: Commands,
10 | mut next_state: ResMut>,
11 | args: Res,
12 | ) {
13 | info!("Starting synctest session");
14 | let mut session_builder = ggrs::SessionBuilder::::new()
15 | .with_num_players(args.num_players)
16 | .with_max_prediction_window(MAX_PREDICTION)
17 | .expect("prediction window can't be 0")
18 | .with_fps(FPS)
19 | .expect("FPS can't be 0")
20 | .with_input_delay(INPUT_DELAY)
21 | // GGRS will simulate a rollback every frame and re-simulate the last n states, where n is the given
22 | // check_distance. All expensive operations are skipped if the check distance is 0, enabling use of synctest
23 | // mode for general local play.
24 | .with_check_distance(args.check_distance);
25 |
26 | for i in 0..args.num_players {
27 | session_builder = session_builder
28 | .add_player(ggrs::PlayerType::Local, i)
29 | .expect("failed to add player");
30 | }
31 |
32 | let ggrs_session = session_builder
33 | .start_synctest_session()
34 | .expect("failed to start session");
35 |
36 | commands.insert_resource(bevy_ggrs::Session::SyncTest(ggrs_session));
37 | next_state.set(GameState::InGame);
38 | }
39 |
--------------------------------------------------------------------------------
/src/stats/systems.rs:
--------------------------------------------------------------------------------
1 | use crate::connection::systems::RollbackConfig;
2 | use crate::stats::resources::NetworkStatsTimer;
3 | use bevy::prelude::*;
4 | use bevy_ggrs::prelude::*;
5 |
6 | pub fn print_network_stats(
7 | time: Res