├── src ├── utils │ └── mod.rs ├── states │ ├── mod.rs │ └── game_state.rs ├── plugins │ ├── objects │ │ ├── utils │ │ │ ├── mod.rs │ │ │ └── object_save.rs │ │ ├── resources │ │ │ ├── mod.rs │ │ │ └── objects_registry.rs │ │ ├── systems │ │ │ ├── mod.rs │ │ │ ├── unload_all.rs │ │ │ ├── spawn_object.rs │ │ │ └── user_grab.rs │ │ ├── components │ │ │ ├── objects │ │ │ │ ├── mod.rs │ │ │ │ ├── fire.rs │ │ │ │ ├── stump.rs │ │ │ │ ├── cactus.rs │ │ │ │ ├── spruce.rs │ │ │ │ ├── flax.rs │ │ │ │ └── tree.rs │ │ │ ├── items │ │ │ │ ├── log.rs │ │ │ │ ├── rock.rs │ │ │ │ ├── branch.rs │ │ │ │ ├── flax_item.rs │ │ │ │ ├── stone_axe.rs │ │ │ │ ├── coarse_string.rs │ │ │ │ ├── wooden_shovel.rs │ │ │ │ └── mod.rs │ │ │ ├── object_spawner.rs │ │ │ └── mod.rs │ │ └── mod.rs │ ├── world_generator │ │ ├── internal │ │ │ ├── mod.rs │ │ │ └── biomes │ │ │ │ ├── desert.rs │ │ │ │ ├── tundra.rs │ │ │ │ └── plains.rs │ │ └── mod.rs │ ├── craft │ │ ├── resources │ │ │ ├── crafts_registry │ │ │ │ ├── crafts │ │ │ │ │ ├── mod.rs │ │ │ │ │ └── simple.rs │ │ │ │ ├── craft.rs │ │ │ │ └── mod.rs │ │ │ └── mod.rs │ │ ├── components │ │ │ └── mod.rs │ │ ├── mod.rs │ │ └── systems │ │ │ └── mod.rs │ ├── tooltip │ │ ├── systems │ │ │ ├── mod.rs │ │ │ ├── redraw.rs │ │ │ └── upsert.rs │ │ ├── events │ │ │ └── mod.rs │ │ ├── components │ │ │ └── mod.rs │ │ ├── utils │ │ │ └── mod.rs │ │ ├── mod.rs │ │ └── resources │ │ │ └── mod.rs │ ├── inspector │ │ ├── systems │ │ │ ├── mod.rs │ │ │ ├── toggle.rs │ │ │ └── inspector │ │ │ │ ├── resources.rs │ │ │ │ ├── profiling │ │ │ │ ├── avg_samples.rs │ │ │ │ └── mod.rs │ │ │ │ ├── assets.rs │ │ │ │ ├── mod.rs │ │ │ │ └── entities.rs │ │ ├── resources │ │ │ └── mod.rs │ │ ├── components │ │ │ └── mod.rs │ │ └── mod.rs │ ├── loading │ │ ├── systems │ │ │ ├── assets_processors │ │ │ │ ├── mod.rs │ │ │ │ └── physics_object.rs │ │ │ ├── mod.rs │ │ │ ├── process_assets.rs │ │ │ └── load_assets.rs │ │ ├── mod.rs │ │ └── resources │ │ │ └── mod.rs │ ├── chunks │ │ ├── helpers │ │ │ ├── mod.rs │ │ │ ├── spawn_chunk.rs │ │ │ └── update_objects_parent.rs │ │ ├── systems │ │ │ ├── mod.rs │ │ │ ├── mine.rs │ │ │ ├── loading.rs │ │ │ ├── details.rs │ │ │ └── unload.rs │ │ ├── resources │ │ │ └── mod.rs │ │ ├── mod.rs │ │ └── components │ │ │ └── mod.rs │ ├── game_world │ │ ├── systems │ │ │ ├── mod.rs │ │ │ ├── create_world.rs │ │ │ ├── sun_to_player.rs │ │ │ ├── setup_world.rs │ │ │ ├── load_world.rs │ │ │ └── save.rs │ │ ├── components │ │ │ └── mod.rs │ │ ├── mod.rs │ │ └── resources │ │ │ └── meta.rs │ ├── ui │ │ ├── systems │ │ │ └── mod.rs │ │ └── mod.rs │ ├── player │ │ ├── resources │ │ │ ├── look_at.rs │ │ │ ├── mod.rs │ │ │ └── input_settings.rs │ │ ├── components │ │ │ ├── mod.rs │ │ │ └── save.rs │ │ ├── systems │ │ │ ├── spawn_item.rs │ │ │ ├── cursor.rs │ │ │ ├── mod.rs │ │ │ ├── look.rs │ │ │ ├── look_at.rs │ │ │ ├── input.rs │ │ │ └── movements.rs │ │ ├── events │ │ │ └── mod.rs │ │ └── mod.rs │ ├── mod.rs │ ├── static_mesh │ │ ├── mod.rs │ │ └── components │ │ │ └── mod.rs │ ├── physics │ │ ├── systems │ │ │ └── mod.rs │ │ └── mod.rs │ └── main_menu │ │ ├── mod.rs │ │ └── systems │ │ ├── mod.rs │ │ ├── load_game.rs │ │ └── new_game.rs ├── internal │ ├── color │ │ └── mod.rs │ ├── mod.rs │ ├── voxel │ │ ├── append_triangle.rs │ │ ├── voxel_types.rs │ │ ├── add_edge.rs │ │ └── mod.rs │ ├── chunks │ │ └── pointer.rs │ └── direction │ │ └── mod.rs ├── lib.rs └── main.rs ├── rust-toolchain.toml ├── .gitignore ├── assets ├── models │ ├── log.glb │ ├── fire.glb │ ├── flax.glb │ ├── rock.glb │ ├── stump.glb │ ├── tree.glb │ ├── branch.glb │ ├── cactus.glb │ ├── spruce.glb │ ├── flax-item.glb │ ├── stone-axe.glb │ ├── coarse-string.glb │ ├── spruce-snow.glb │ └── wooden_shovel.glb ├── fonts │ └── roboto.ttf └── textures │ └── crosshair.png ├── .gitmodules ├── Cargo.toml ├── README.md └── LICENSE-MIT /src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/states/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod game_state; 2 | -------------------------------------------------------------------------------- /src/plugins/objects/utils/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod object_save; 2 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" 3 | -------------------------------------------------------------------------------- /src/plugins/world_generator/internal/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod biomes; 2 | -------------------------------------------------------------------------------- /src/internal/color/mod.rs: -------------------------------------------------------------------------------- 1 | pub type Color = bevy::prelude::Color; 2 | -------------------------------------------------------------------------------- /src/plugins/objects/resources/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod objects_registry; 2 | -------------------------------------------------------------------------------- /src/plugins/craft/resources/crafts_registry/crafts/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod simple; 2 | -------------------------------------------------------------------------------- /src/plugins/tooltip/systems/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod redraw; 2 | pub mod upsert; 3 | -------------------------------------------------------------------------------- /src/plugins/inspector/systems/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod inspector; 2 | pub mod toggle; 3 | -------------------------------------------------------------------------------- /src/plugins/loading/systems/assets_processors/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod physics_object; 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .vscode 3 | .DS_Store 4 | trace-*.json 5 | logs 6 | saves 7 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod internal; 2 | pub mod plugins; 3 | pub mod states; 4 | pub mod utils; 5 | -------------------------------------------------------------------------------- /src/plugins/chunks/helpers/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod spawn_chunk; 2 | pub mod update_objects_parent; 3 | -------------------------------------------------------------------------------- /assets/models/log.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Defernus/primitive-engineering/HEAD/assets/models/log.glb -------------------------------------------------------------------------------- /src/plugins/objects/systems/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod spawn_object; 2 | pub mod unload_all; 3 | pub mod user_grab; 4 | -------------------------------------------------------------------------------- /assets/fonts/roboto.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Defernus/primitive-engineering/HEAD/assets/fonts/roboto.ttf -------------------------------------------------------------------------------- /assets/models/fire.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Defernus/primitive-engineering/HEAD/assets/models/fire.glb -------------------------------------------------------------------------------- /assets/models/flax.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Defernus/primitive-engineering/HEAD/assets/models/flax.glb -------------------------------------------------------------------------------- /assets/models/rock.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Defernus/primitive-engineering/HEAD/assets/models/rock.glb -------------------------------------------------------------------------------- /assets/models/stump.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Defernus/primitive-engineering/HEAD/assets/models/stump.glb -------------------------------------------------------------------------------- /assets/models/tree.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Defernus/primitive-engineering/HEAD/assets/models/tree.glb -------------------------------------------------------------------------------- /src/plugins/chunks/systems/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod details; 2 | pub mod loading; 3 | pub mod mine; 4 | pub mod unload; 5 | -------------------------------------------------------------------------------- /src/plugins/craft/resources/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod crafts_registry; 2 | 3 | pub const CRAFT_ZONE_RADIUS: f32 = 0.5; 4 | -------------------------------------------------------------------------------- /assets/models/branch.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Defernus/primitive-engineering/HEAD/assets/models/branch.glb -------------------------------------------------------------------------------- /assets/models/cactus.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Defernus/primitive-engineering/HEAD/assets/models/cactus.glb -------------------------------------------------------------------------------- /assets/models/spruce.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Defernus/primitive-engineering/HEAD/assets/models/spruce.glb -------------------------------------------------------------------------------- /src/internal/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod chunks; 2 | pub mod color; 3 | pub mod direction; 4 | pub mod pos; 5 | pub mod voxel; 6 | -------------------------------------------------------------------------------- /src/plugins/loading/systems/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod assets_processors; 2 | pub mod load_assets; 3 | pub mod process_assets; 4 | -------------------------------------------------------------------------------- /assets/models/flax-item.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Defernus/primitive-engineering/HEAD/assets/models/flax-item.glb -------------------------------------------------------------------------------- /assets/models/stone-axe.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Defernus/primitive-engineering/HEAD/assets/models/stone-axe.glb -------------------------------------------------------------------------------- /assets/models/coarse-string.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Defernus/primitive-engineering/HEAD/assets/models/coarse-string.glb -------------------------------------------------------------------------------- /assets/models/spruce-snow.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Defernus/primitive-engineering/HEAD/assets/models/spruce-snow.glb -------------------------------------------------------------------------------- /assets/models/wooden_shovel.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Defernus/primitive-engineering/HEAD/assets/models/wooden_shovel.glb -------------------------------------------------------------------------------- /assets/textures/crosshair.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Defernus/primitive-engineering/HEAD/assets/textures/crosshair.png -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "bevy_gltf_collider"] 2 | path = bevy_gltf_collider 3 | url = git@github.com:Defernus/bevy_gltf_collider.git 4 | -------------------------------------------------------------------------------- /src/plugins/game_world/systems/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod create_world; 2 | pub mod load_world; 3 | pub mod save; 4 | pub mod setup_world; 5 | pub mod sun_to_player; 6 | -------------------------------------------------------------------------------- /src/plugins/objects/components/objects/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod cactus; 2 | pub mod fire; 3 | pub mod flax; 4 | pub mod spruce; 5 | pub mod stump; 6 | pub mod tree; 7 | -------------------------------------------------------------------------------- /src/plugins/craft/components/mod.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | 3 | #[derive(Debug, Clone, Copy, Default, Component, Reflect, FromReflect)] 4 | #[reflect(Component)] 5 | pub struct CraftZoneComponent; 6 | -------------------------------------------------------------------------------- /src/plugins/inspector/resources/mod.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | 3 | #[derive(Default, Clone, Copy, Resource, Debug, Reflect, FromReflect)] 4 | #[reflect(Resource)] 5 | pub struct InspectorOpen(pub bool); 6 | -------------------------------------------------------------------------------- /src/plugins/ui/systems/mod.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | 3 | pub fn init_window(mut windows: ResMut) { 4 | let window = windows.get_primary_mut().unwrap(); 5 | 6 | window.set_maximized(true); 7 | } 8 | -------------------------------------------------------------------------------- /src/plugins/inspector/components/mod.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | 3 | #[derive(Debug, Clone, Copy, Default, Component, Hash, Reflect, FromReflect)] 4 | #[reflect(Component)] 5 | pub struct InspectorDisabled; 6 | 7 | #[derive(Component)] 8 | pub struct InspectorGroupChunks; 9 | -------------------------------------------------------------------------------- /src/plugins/game_world/components/mod.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | use bevy_inspector_egui::InspectorOptions; 3 | use bevy_reflect::Reflect; 4 | 5 | #[derive(Default, Debug, Clone, Copy, Reflect, FromReflect, Component, InspectorOptions)] 6 | #[reflect(Component)] 7 | pub struct WorldSun; 8 | -------------------------------------------------------------------------------- /src/plugins/player/resources/look_at.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | 3 | #[derive(Debug, Clone, Default, Copy, Resource)] 4 | pub struct PlayerLookAt { 5 | pub target: Option, 6 | pub distance: f32, 7 | pub origin: Vec3, 8 | pub dir: Vec3, 9 | pub position: Vec3, 10 | } 11 | -------------------------------------------------------------------------------- /src/plugins/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod chunks; 2 | pub mod craft; 3 | pub mod game_world; 4 | pub mod inspector; 5 | pub mod loading; 6 | pub mod main_menu; 7 | pub mod objects; 8 | pub mod physics; 9 | pub mod player; 10 | pub mod static_mesh; 11 | pub mod tooltip; 12 | pub mod ui; 13 | pub mod world_generator; 14 | -------------------------------------------------------------------------------- /src/plugins/inspector/systems/toggle.rs: -------------------------------------------------------------------------------- 1 | use crate::plugins::inspector::resources::InspectorOpen; 2 | use bevy::prelude::*; 3 | 4 | pub fn toggle_inspector_system(key: Res>, mut is_open: ResMut) { 5 | if key.just_pressed(KeyCode::Tab) { 6 | is_open.0 = !is_open.0; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/plugins/static_mesh/mod.rs: -------------------------------------------------------------------------------- 1 | use self::components::StaticMeshComponent; 2 | use bevy::prelude::*; 3 | 4 | pub mod components; 5 | 6 | pub struct StaticMeshPlugin; 7 | 8 | impl Plugin for StaticMeshPlugin { 9 | fn build(&self, app: &mut App) { 10 | app.register_type::(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/plugins/objects/systems/unload_all.rs: -------------------------------------------------------------------------------- 1 | use crate::plugins::objects::components::GameWorldObject; 2 | use bevy::prelude::*; 3 | 4 | pub fn unload_all_objects(mut commands: Commands, objects_q: Query>) { 5 | for object in objects_q.iter() { 6 | commands.entity(object).despawn_recursive(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/plugins/physics/systems/mod.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | use bevy_rapier3d::prelude::*; 3 | 4 | pub fn enable_physics(mut config: ResMut) { 5 | config.physics_pipeline_active = true; 6 | } 7 | 8 | pub fn disable_physics(mut config: ResMut) { 9 | config.physics_pipeline_active = false; 10 | } 11 | -------------------------------------------------------------------------------- /src/plugins/world_generator/mod.rs: -------------------------------------------------------------------------------- 1 | use self::resources::WorldGenerator; 2 | use bevy::prelude::*; 3 | 4 | pub mod internal; 5 | pub mod resources; 6 | 7 | pub struct WorldGeneratorPlugin; 8 | 9 | impl Plugin for WorldGeneratorPlugin { 10 | fn build(&self, app: &mut App) { 11 | app.insert_resource(WorldGenerator::default()); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/plugins/tooltip/events/mod.rs: -------------------------------------------------------------------------------- 1 | use super::{resources::TooltipType, utils::TooltipId}; 2 | use bevy::prelude::*; 3 | 4 | #[derive(Debug, Default, Clone, Reflect, FromReflect)] 5 | pub struct UpsertTooltipEvent { 6 | pub id: TooltipId, 7 | pub tooltip_type: TooltipType, 8 | pub text: String, 9 | pub parent: Option, 10 | pub position: Vec3, 11 | } 12 | -------------------------------------------------------------------------------- /src/plugins/ui/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::states::game_state::GameState; 2 | use bevy::prelude::*; 3 | 4 | use self::systems::init_window; 5 | 6 | mod systems; 7 | 8 | pub struct UiPlugin; 9 | 10 | impl Plugin for UiPlugin { 11 | fn build(&self, app: &mut App) { 12 | app.add_system_set(SystemSet::on_enter(GameState::AssetsLoading).with_system(init_window)); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/states/game_state.rs: -------------------------------------------------------------------------------- 1 | use bevy::reflect::Reflect; 2 | 3 | #[derive(Debug, Clone, Eq, PartialEq, Hash, Reflect)] 4 | #[derive(Default)] 5 | pub enum GameState { 6 | #[default] 7 | AssetsLoading, 8 | 9 | MenuMain, 10 | MenuNewGame, 11 | MenuLoadGame, 12 | MenuSettings, 13 | 14 | WorldLoading, 15 | WorldCreating, 16 | 17 | InGame, 18 | } 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/plugins/chunks/resources/mod.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | 3 | #[derive(Debug, Default, Clone, Resource, Reflect, FromReflect)] 4 | #[reflect(Resource)] 5 | pub struct ChunkLoadingEnabled(pub bool); 6 | 7 | #[derive(Debug, Default, Clone, Resource, Reflect, FromReflect)] 8 | #[reflect(Resource)] 9 | pub struct DebugChunkBorder { 10 | pub enabled: bool, 11 | } 12 | 13 | impl DebugChunkBorder { 14 | pub const ENABLED: Self = DebugChunkBorder { enabled: true }; 15 | } 16 | -------------------------------------------------------------------------------- /src/plugins/game_world/systems/create_world.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | plugins::game_world::resources::{meta::GameWorldMeta, GameWorld}, 3 | states::game_state::GameState, 4 | }; 5 | use bevy::prelude::*; 6 | 7 | pub fn start_world_creating(mut commands: Commands) { 8 | let world = GameWorld::new(); 9 | commands.insert_resource(world); 10 | } 11 | 12 | pub fn world_creating_progress(mut game_state: ResMut>, meta: Res) { 13 | meta.save_self(); 14 | 15 | game_state.set(GameState::InGame).unwrap(); 16 | } 17 | -------------------------------------------------------------------------------- /src/plugins/tooltip/components/mod.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | 3 | use super::utils::TooltipId; 4 | 5 | #[derive(Debug, Clone, Reflect, FromReflect, Component)] 6 | pub struct ToolTipComponent { 7 | pub id: TooltipId, 8 | } 9 | 10 | #[derive(Debug, Clone, Reflect, FromReflect, Component)] 11 | pub struct UiTooltip { 12 | /// In world tooltip entity 13 | pub entity: Entity, 14 | pub id: TooltipId, 15 | pub text: String, 16 | } 17 | 18 | impl UiTooltip { 19 | pub const WIDTH_PX: f32 = 200.0; 20 | pub const HEIGHT_PX: f32 = 100.0; 21 | } 22 | -------------------------------------------------------------------------------- /src/plugins/game_world/systems/sun_to_player.rs: -------------------------------------------------------------------------------- 1 | use crate::plugins::{game_world::components::WorldSun, player::components::PlayerComponent}; 2 | use bevy::prelude::*; 3 | 4 | pub fn move_sun_to_player( 5 | mut query: Query<&mut Transform, With>, 6 | player_query: Query<&Transform, (With, Without)>, 7 | ) { 8 | if let Some(player_transform) = player_query.iter().next() { 9 | for mut transform in query.iter_mut() { 10 | transform.translation = player_transform.translation; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/plugins/tooltip/utils/mod.rs: -------------------------------------------------------------------------------- 1 | use bevy_reflect::{FromReflect, Reflect}; 2 | 3 | #[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Reflect, FromReflect)] 4 | pub struct TooltipId(String); 5 | 6 | impl TooltipId { 7 | pub fn new(id: impl Into) -> Self { 8 | Self(id.into()) 9 | } 10 | 11 | pub fn as_str(&self) -> &str { 12 | &self.0 13 | } 14 | 15 | pub fn to_string(&self) -> String { 16 | self.0.clone() 17 | } 18 | } 19 | 20 | impl From for TooltipId 21 | where 22 | T: Into, 23 | { 24 | fn from(id: T) -> Self { 25 | Self::new(id) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/plugins/craft/mod.rs: -------------------------------------------------------------------------------- 1 | use self::{resources::crafts_registry::CraftsRegistry, systems::*}; 2 | use crate::states::game_state::GameState; 3 | use bevy::prelude::*; 4 | 5 | pub mod components; 6 | pub mod resources; 7 | mod systems; 8 | 9 | pub struct CraftPlugin; 10 | 11 | impl Plugin for CraftPlugin { 12 | fn build(&self, app: &mut App) { 13 | app.register_type::() 14 | .insert_resource(CraftsRegistry::new()) 15 | .add_system_set(SystemSet::on_update(GameState::InGame).with_system(craft_zone)) 16 | .add_system_set(SystemSet::on_enter(GameState::InGame).with_system(setup_craft_zone)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/plugins/player/components/mod.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | 3 | pub mod save; 4 | 5 | #[derive(Component, Debug, Clone, Copy, Default, Reflect, FromReflect)] 6 | #[reflect(Component)] 7 | pub struct PlayerComponent { 8 | pub velocity: Vec3, 9 | } 10 | 11 | #[derive(Component, Debug, Clone, Copy, Default, Reflect, FromReflect)] 12 | #[reflect(Component)] 13 | pub struct PlayerHand; 14 | 15 | #[derive(Component, Debug, Clone, Copy, Default, Reflect, FromReflect)] 16 | #[reflect(Component)] 17 | pub struct PlayerCameraComponent; 18 | 19 | #[derive(Component, Debug, Clone, Copy, Default, Reflect, FromReflect)] 20 | #[reflect(Component)] 21 | pub struct PlayerHeadComponent; 22 | -------------------------------------------------------------------------------- /src/plugins/loading/mod.rs: -------------------------------------------------------------------------------- 1 | use self::{ 2 | resources::GameAssets, 3 | systems::{load_assets::load_assets, process_assets::process_assets}, 4 | }; 5 | use crate::states::game_state::GameState; 6 | use bevy::prelude::*; 7 | 8 | pub mod resources; 9 | mod systems; 10 | 11 | pub struct LoadingPlugin; 12 | 13 | impl Plugin for LoadingPlugin { 14 | fn build(&self, app: &mut App) { 15 | app.init_resource::() 16 | .add_system_set(SystemSet::on_enter(GameState::AssetsLoading).with_system(load_assets)) 17 | .add_system_set( 18 | SystemSet::on_update(GameState::AssetsLoading) 19 | .with_system(process_assets.label("loading:process_assets")), 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/plugins/physics/mod.rs: -------------------------------------------------------------------------------- 1 | use self::systems::*; 2 | use crate::states::game_state::GameState; 3 | use bevy::prelude::*; 4 | use bevy_rapier3d::prelude::*; 5 | 6 | mod systems; 7 | 8 | pub struct PhysicsPlugin; 9 | impl Plugin for PhysicsPlugin { 10 | fn build(&self, app: &mut App) { 11 | app.add_plugin(RapierPhysicsPlugin::::default()) 12 | // .add_plugin(RapierDebugRenderPlugin::default()) 13 | .add_system_set(SystemSet::on_enter(GameState::InGame).with_system(enable_physics)) 14 | .add_system_set(SystemSet::on_exit(GameState::InGame).with_system(disable_physics)) 15 | .insert_resource(RapierConfiguration { 16 | physics_pipeline_active: false, 17 | ..Default::default() 18 | }); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "primitive-engineering" 3 | version = "0.1.0" 4 | edition = "2021" 5 | rust-version = "1.65" 6 | 7 | [workspace] 8 | resolver = "2" 9 | 10 | [profile.dev] 11 | opt-level = 1 12 | 13 | [profile.dev.package."*"] 14 | opt-level = 3 15 | 16 | [dependencies] 17 | bevy = { version = "0.9.1", features = ["dynamic"] } 18 | bevy_egui = "0.19.0" 19 | bevy-inspector-egui = "0.17.0" 20 | rand = "0.8.5" 21 | num-traits = "0.2.15" 22 | bevy_reflect = "0.9.1" 23 | strum_macros = "0.24" 24 | strum = { version = "0.24", features = ["derive"] } 25 | crossbeam-channel = "0.5.6" 26 | bevy_rapier3d = "0.20.0" 27 | noise = "0.8.2" 28 | lerp = { version = "0.4", features = ["derive"] } 29 | bevy_gltf_collider = { path = "./bevy_gltf_collider" } 30 | bincode = "1.3.3" 31 | serde = "1.0.152" 32 | serde_bytes = "0.11.9" 33 | zstd = "0.12.3" 34 | pariter = "0.5.1" 35 | -------------------------------------------------------------------------------- /src/plugins/main_menu/mod.rs: -------------------------------------------------------------------------------- 1 | use self::systems::{ 2 | load_game::load_game_system, 3 | main_menu, 4 | new_game::{init_new_game, new_game_system}, 5 | }; 6 | use crate::states::game_state::GameState; 7 | use bevy::prelude::*; 8 | 9 | mod systems; 10 | 11 | pub struct MainMenuPlugin; 12 | 13 | impl Plugin for MainMenuPlugin { 14 | fn build(&self, app: &mut App) { 15 | app.add_system_set(SystemSet::on_update(GameState::MenuMain).with_system(main_menu)) 16 | .add_system_set(SystemSet::on_enter(GameState::MenuNewGame).with_system(init_new_game)) 17 | .add_system_set( 18 | SystemSet::on_update(GameState::MenuNewGame).with_system(new_game_system), 19 | ) 20 | .add_system_set( 21 | SystemSet::on_update(GameState::MenuLoadGame).with_system(load_game_system), 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Primitive Engineering 2 | 3 | ## About 4 | 5 | Primitive Engineering is an open source sandbox game that focuses on the creation 6 | of machines and automation in infinite procedurally generated voxel world. 7 | 8 | ## Features 9 | 10 | * Infinite procedurally generated voxel world 11 | * Smooth voxels (using marching cubes algorithm) 12 | 13 | // TODO: add more 14 | 15 | ## TL;DR play 16 | 17 | Clone the repository and pull submodules: 18 | ``` 19 | git clone https://github.com/Defernus/primitive-engineering.git 20 | cd primitive-engineering 21 | git submodule update --init --recursive 22 | ``` 23 | 24 | Run the game: 25 | ``` 26 | cargo run --release 27 | ``` 28 | 29 | # License 30 | 31 | Primitive Engineering is distributed under the terms of both the MIT license and the 32 | Apache License (Version 2.0). 33 | 34 | See [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT). 35 | 36 | -------------------------------------------------------------------------------- /src/plugins/objects/mod.rs: -------------------------------------------------------------------------------- 1 | use self::{ 2 | resources::objects_registry::ObjectsRegistry, 3 | systems::{ 4 | spawn_object::spawn_object_system, unload_all::unload_all_objects, 5 | user_grab::use_grab_system, 6 | }, 7 | }; 8 | use crate::states::game_state::GameState; 9 | use bevy::prelude::*; 10 | 11 | pub mod components; 12 | pub mod resources; 13 | mod systems; 14 | pub mod utils; 15 | 16 | pub struct ObjectsPlugin; 17 | impl Plugin for ObjectsPlugin { 18 | fn build(&self, app: &mut App) { 19 | app.insert_resource(ObjectsRegistry::new()) 20 | .add_system_set(SystemSet::on_update(GameState::InGame).with_system(use_grab_system)) 21 | .add_system_set( 22 | SystemSet::on_update(GameState::InGame).with_system(spawn_object_system), 23 | ) 24 | .add_system_set(SystemSet::on_exit(GameState::InGame).with_system(unload_all_objects)); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/plugins/player/systems/spawn_item.rs: -------------------------------------------------------------------------------- 1 | use crate::plugins::{ 2 | inspector::components::InspectorDisabled, 3 | objects::components::{items::stone_axe::StoneAxeItem, GameWorldObjectTrait}, 4 | player::{components::PlayerCameraComponent, events::SpawnItemEvent}, 5 | }; 6 | use bevy::prelude::*; 7 | 8 | pub fn spawn_item( 9 | mut commands: Commands, 10 | mut spawn_item_e: EventReader, 11 | camera_q: Query<&GlobalTransform, With>, 12 | ) { 13 | for _ in spawn_item_e.iter() { 14 | let far = 1.0; 15 | 16 | let camera_transform = camera_q.single().compute_transform(); 17 | 18 | let pos = camera_transform.translation + camera_transform.forward() * far; 19 | 20 | commands.spawn(( 21 | StoneAxeItem.to_spawner(Transform::from_translation(pos)), 22 | Name::new("player_spawned_item"), 23 | InspectorDisabled, 24 | )); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/internal/voxel/append_triangle.rs: -------------------------------------------------------------------------------- 1 | use crate::{internal::color::Color, plugins::static_mesh::components::Vertex}; 2 | use bevy::prelude::Vec3; 3 | 4 | pub fn append_triangle( 5 | vertices: &mut Vec, 6 | scale: f32, 7 | color: Color, 8 | a: Vec3, 9 | b: Vec3, 10 | c: Vec3, 11 | ) -> Vec3 { 12 | let normal = (c - a).cross(b - a).normalize(); 13 | 14 | append_triangle_with_normal(vertices, scale, color, a, b, c, normal); 15 | 16 | normal 17 | } 18 | 19 | pub fn append_triangle_with_normal( 20 | vertices: &mut Vec, 21 | scale: f32, 22 | color: Color, 23 | a: Vec3, 24 | b: Vec3, 25 | c: Vec3, 26 | normal: Vec3, 27 | ) { 28 | vertices.push(Vertex { 29 | color, 30 | normal, 31 | pos: c * scale, 32 | }); 33 | vertices.push(Vertex { 34 | color, 35 | normal, 36 | pos: b * scale, 37 | }); 38 | vertices.push(Vertex { 39 | color, 40 | normal, 41 | pos: a * scale, 42 | }); 43 | } 44 | -------------------------------------------------------------------------------- /src/plugins/inspector/systems/inspector/resources.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | use bevy_egui::egui; 3 | use bevy_inspector_egui::bevy_inspector::ui_for_resources; 4 | 5 | #[derive(Default, Clone, Copy, PartialEq, Eq, Hash, Debug)] 6 | pub enum ResourcesInspectorTab { 7 | #[default] 8 | All, 9 | } 10 | 11 | #[derive(Resource, Default)] 12 | pub struct ResourcesInspectorState { 13 | pub tab_open: ResourcesInspectorTab, 14 | } 15 | 16 | pub fn resources_inspector(world: &mut World, ui: &mut egui::Ui) { 17 | let mut state = world 18 | .remove_resource::() 19 | .unwrap_or_default(); 20 | 21 | ui.horizontal(|ui| { 22 | ui.selectable_value(&mut state.tab_open, ResourcesInspectorTab::All, "main"); 23 | }); 24 | 25 | egui::ScrollArea::vertical() 26 | .auto_shrink([false, false]) 27 | .show(ui, |ui| match state.tab_open { 28 | ResourcesInspectorTab::All => ui_for_resources(world, ui), 29 | }); 30 | 31 | world.insert_resource(state); 32 | } 33 | -------------------------------------------------------------------------------- /src/plugins/main_menu/systems/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::states::game_state::GameState; 2 | use bevy::{app::AppExit, prelude::*}; 3 | use bevy_egui::{egui, EguiContext}; 4 | 5 | pub mod load_game; 6 | pub mod new_game; 7 | 8 | // !TODO:ui create menu 9 | pub fn main_menu( 10 | mut exit: EventWriter, 11 | mut game_state: ResMut>, 12 | mut egui_context: ResMut, 13 | ) { 14 | egui::Window::new("Main menu") 15 | .collapsible(false) 16 | .show(egui_context.ctx_mut(), |ui| { 17 | ui.vertical_centered(|ui| { 18 | if ui.button("Start new game").clicked() { 19 | game_state.set(GameState::MenuNewGame).unwrap(); 20 | } 21 | 22 | if ui.button("Load game").clicked() { 23 | game_state.set(GameState::MenuLoadGame).unwrap(); 24 | } 25 | 26 | if ui.button("Exit").clicked() { 27 | exit.send(AppExit); 28 | } 29 | }); 30 | }); 31 | } 32 | -------------------------------------------------------------------------------- /src/plugins/loading/systems/assets_processors/physics_object.rs: -------------------------------------------------------------------------------- 1 | use crate::plugins::loading::resources::PhysicsObject; 2 | use bevy::prelude::*; 3 | 4 | /// This system is responsible for loading physics objects from the assets. 5 | /// 6 | /// It will return `true` if the asset was loaded successfully or if field is not a [`PhysicsObject`]. 7 | /// It will return `false` if it still loading. 8 | pub fn process_physic_objects( 9 | field_name: &str, 10 | field: &mut dyn Reflect, 11 | scenes: &mut Assets, 12 | meshes: &mut Assets, 13 | ) -> bool { 14 | let obj = if let Some(obj) = field.downcast_mut::() { 15 | obj 16 | } else { 17 | return true; 18 | }; 19 | 20 | let scene = if let Some(scene) = scenes.get_mut(&obj.scene) { 21 | scene 22 | } else { 23 | return false; 24 | }; 25 | 26 | obj.colliders = bevy_gltf_collider::get_scene_colliders(meshes, &mut scene.world) 27 | .unwrap_or_else(|_| panic!("Failed to load colliders for {}", field_name)); 28 | 29 | true 30 | } 31 | -------------------------------------------------------------------------------- /src/plugins/tooltip/mod.rs: -------------------------------------------------------------------------------- 1 | use self::{ 2 | components::ToolTipComponent, 3 | events::UpsertTooltipEvent, 4 | resources::Tooltips, 5 | systems::{redraw::tooltip_redraw_system, upsert::handle_upsert_tooltip_system}, 6 | utils::TooltipId, 7 | }; 8 | use crate::states::game_state::GameState; 9 | use bevy::prelude::*; 10 | 11 | pub mod components; 12 | pub mod events; 13 | pub mod resources; 14 | pub mod systems; 15 | pub mod utils; 16 | 17 | pub struct TooltipPlugin; 18 | 19 | impl Plugin for TooltipPlugin { 20 | fn build(&self, app: &mut App) { 21 | app.register_type::() 22 | .register_type::() 23 | .register_type::() 24 | .insert_resource(Tooltips::default()) 25 | .add_event::() 26 | .add_system_set( 27 | SystemSet::on_update(GameState::InGame) 28 | .with_system(handle_upsert_tooltip_system) 29 | .with_system(tooltip_redraw_system.after(handle_upsert_tooltip_system)), 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/plugins/inspector/systems/inspector/profiling/avg_samples.rs: -------------------------------------------------------------------------------- 1 | use std::collections::LinkedList; 2 | 3 | pub struct AvgSamples { 4 | size: usize, 5 | samples: LinkedList, 6 | } 7 | 8 | impl Default for AvgSamples { 9 | fn default() -> Self { 10 | Self { 11 | samples: LinkedList::new(), 12 | size: 100, 13 | } 14 | } 15 | } 16 | 17 | impl AvgSamples { 18 | pub fn new(size: usize) -> Self { 19 | Self { 20 | samples: LinkedList::new(), 21 | size, 22 | } 23 | } 24 | 25 | pub fn update(&mut self, new_sample: f32) { 26 | self.samples.push_back(new_sample); 27 | if self.samples.len() > self.size { 28 | self.samples.pop_front(); 29 | } 30 | } 31 | 32 | pub fn avg(&self) -> f32 { 33 | self.samples.iter().sum::() / self.samples.len() as f32 34 | } 35 | 36 | pub fn min(&self) -> f32 { 37 | *self 38 | .samples 39 | .iter() 40 | .min_by(|a, b| a.partial_cmp(b).unwrap()) 41 | .unwrap() 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Defernus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/plugins/objects/components/items/log.rs: -------------------------------------------------------------------------------- 1 | use crate::plugins::{ 2 | loading::resources::{GameAssets, PhysicsObject}, 3 | objects::components::{GameWorldObjectTrait, ObjectDeserializationError}, 4 | }; 5 | 6 | #[derive(Debug, Default, Clone)] 7 | pub struct LogItem; 8 | 9 | impl LogItem { 10 | pub const ID: &str = "log"; 11 | } 12 | 13 | impl GameWorldObjectTrait for LogItem { 14 | fn id(&self) -> &'static str { 15 | Self::ID 16 | } 17 | 18 | fn take(&mut self) -> Box { 19 | Box::new(std::mem::take(self)) 20 | } 21 | 22 | fn get_clone(&self) -> Box { 23 | Box::new(self.clone()) 24 | } 25 | 26 | fn deserialize( 27 | &self, 28 | _data: &[u8], 29 | ) -> Result, ObjectDeserializationError> { 30 | #[allow(clippy::box_default)] 31 | Ok(Box::new(Self::default())) 32 | } 33 | 34 | fn get_model<'a>(&self, assets: &'a GameAssets) -> &'a PhysicsObject { 35 | &assets.log_object 36 | } 37 | 38 | fn is_item(&self) -> bool { 39 | true 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/plugins/objects/components/items/rock.rs: -------------------------------------------------------------------------------- 1 | use crate::plugins::{ 2 | loading::resources::{GameAssets, PhysicsObject}, 3 | objects::components::{GameWorldObjectTrait, ObjectDeserializationError}, 4 | }; 5 | 6 | #[derive(Debug, Default, Clone)] 7 | pub struct RockItem; 8 | 9 | impl RockItem { 10 | pub const ID: &str = "rock"; 11 | } 12 | 13 | impl GameWorldObjectTrait for RockItem { 14 | fn id(&self) -> &'static str { 15 | Self::ID 16 | } 17 | 18 | fn take(&mut self) -> Box { 19 | Box::new(std::mem::take(self)) 20 | } 21 | 22 | fn get_clone(&self) -> Box { 23 | Box::new(self.clone()) 24 | } 25 | 26 | fn deserialize( 27 | &self, 28 | _data: &[u8], 29 | ) -> Result, ObjectDeserializationError> { 30 | #[allow(clippy::box_default)] 31 | Ok(Box::new(Self::default())) 32 | } 33 | 34 | fn get_model<'a>(&self, assets: &'a GameAssets) -> &'a PhysicsObject { 35 | &assets.rock_object 36 | } 37 | 38 | fn is_item(&self) -> bool { 39 | true 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/plugins/objects/components/objects/fire.rs: -------------------------------------------------------------------------------- 1 | use crate::plugins::{ 2 | loading::resources::{GameAssets, PhysicsObject}, 3 | objects::components::{GameWorldObjectTrait, ObjectDeserializationError}, 4 | }; 5 | use bevy_reflect::{FromReflect, Reflect}; 6 | 7 | #[derive(Debug, Clone, Default, Reflect, FromReflect)] 8 | pub struct FireObject; 9 | 10 | impl FireObject { 11 | const ID: &str = "fire"; 12 | } 13 | 14 | impl GameWorldObjectTrait for FireObject { 15 | fn id(&self) -> &'static str { 16 | Self::ID 17 | } 18 | 19 | fn take(&mut self) -> Box { 20 | Box::new(std::mem::take(self)) 21 | } 22 | 23 | fn get_clone(&self) -> Box { 24 | Box::new(self.clone()) 25 | } 26 | 27 | fn deserialize( 28 | &self, 29 | _data: &[u8], 30 | ) -> Result, ObjectDeserializationError> { 31 | #[allow(clippy::box_default)] 32 | Ok(Box::new(Self::default())) 33 | } 34 | 35 | fn get_model<'a>(&self, assets: &'a GameAssets) -> &'a PhysicsObject { 36 | &assets.fire_object 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/plugins/objects/components/objects/stump.rs: -------------------------------------------------------------------------------- 1 | use crate::plugins::{ 2 | loading::resources::{GameAssets, PhysicsObject}, 3 | objects::components::{GameWorldObjectTrait, ObjectDeserializationError}, 4 | }; 5 | use bevy_reflect::{FromReflect, Reflect}; 6 | 7 | #[derive(Debug, Clone, Default, Reflect, FromReflect)] 8 | pub struct StumpObject; 9 | 10 | impl StumpObject { 11 | const ID: &str = "stump"; 12 | } 13 | 14 | impl GameWorldObjectTrait for StumpObject { 15 | fn id(&self) -> &'static str { 16 | Self::ID 17 | } 18 | 19 | fn take(&mut self) -> Box { 20 | Box::new(std::mem::take(self)) 21 | } 22 | 23 | fn get_clone(&self) -> Box { 24 | Box::new(self.clone()) 25 | } 26 | 27 | fn deserialize( 28 | &self, 29 | _data: &[u8], 30 | ) -> Result, ObjectDeserializationError> { 31 | #[allow(clippy::box_default)] 32 | Ok(Box::new(Self::default())) 33 | } 34 | 35 | fn get_model<'a>(&self, assets: &'a GameAssets) -> &'a PhysicsObject { 36 | &assets.stump_object 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/plugins/objects/components/items/branch.rs: -------------------------------------------------------------------------------- 1 | use crate::plugins::{ 2 | loading::resources::{GameAssets, PhysicsObject}, 3 | objects::components::{GameWorldObjectTrait, ObjectDeserializationError}, 4 | }; 5 | 6 | #[derive(Debug, Default, Clone)] 7 | pub struct BranchItem; 8 | 9 | impl BranchItem { 10 | pub const ID: &str = "branch"; 11 | } 12 | 13 | impl GameWorldObjectTrait for BranchItem { 14 | fn id(&self) -> &'static str { 15 | Self::ID 16 | } 17 | 18 | fn take(&mut self) -> Box { 19 | Box::new(std::mem::take(self)) 20 | } 21 | 22 | fn get_clone(&self) -> Box { 23 | Box::new(self.clone()) 24 | } 25 | 26 | fn deserialize( 27 | &self, 28 | _data: &[u8], 29 | ) -> Result, ObjectDeserializationError> { 30 | #[allow(clippy::box_default)] 31 | Ok(Box::new(Self::default())) 32 | } 33 | 34 | fn get_model<'a>(&self, assets: &'a GameAssets) -> &'a PhysicsObject { 35 | &assets.branch_object 36 | } 37 | 38 | fn is_item(&self) -> bool { 39 | true 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/plugins/objects/components/objects/cactus.rs: -------------------------------------------------------------------------------- 1 | use crate::plugins::{ 2 | loading::resources::{GameAssets, PhysicsObject}, 3 | objects::components::{GameWorldObjectTrait, ObjectDeserializationError}, 4 | }; 5 | use bevy_reflect::{FromReflect, Reflect}; 6 | 7 | #[derive(Debug, Clone, Default, Reflect, FromReflect)] 8 | pub struct CactusObject; 9 | 10 | impl CactusObject { 11 | const ID: &str = "cactus"; 12 | } 13 | 14 | impl GameWorldObjectTrait for CactusObject { 15 | fn id(&self) -> &'static str { 16 | Self::ID 17 | } 18 | 19 | fn take(&mut self) -> Box { 20 | Box::new(std::mem::take(self)) 21 | } 22 | 23 | fn get_clone(&self) -> Box { 24 | Box::new(self.clone()) 25 | } 26 | 27 | fn deserialize( 28 | &self, 29 | _data: &[u8], 30 | ) -> Result, ObjectDeserializationError> { 31 | #[allow(clippy::box_default)] 32 | Ok(Box::new(Self::default())) 33 | } 34 | 35 | fn get_model<'a>(&self, assets: &'a GameAssets) -> &'a PhysicsObject { 36 | &assets.cactus_object 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/plugins/objects/components/items/flax_item.rs: -------------------------------------------------------------------------------- 1 | use crate::plugins::{ 2 | loading::resources::{GameAssets, PhysicsObject}, 3 | objects::components::{GameWorldObjectTrait, ObjectDeserializationError}, 4 | }; 5 | 6 | #[derive(Debug, Default, Clone)] 7 | pub struct FlaxItem; 8 | 9 | impl FlaxItem { 10 | pub const ID: &str = "flax-item"; 11 | } 12 | 13 | impl GameWorldObjectTrait for FlaxItem { 14 | fn id(&self) -> &'static str { 15 | Self::ID 16 | } 17 | 18 | fn take(&mut self) -> Box { 19 | Box::new(std::mem::take(self)) 20 | } 21 | 22 | fn get_clone(&self) -> Box { 23 | Box::new(self.clone()) 24 | } 25 | 26 | fn deserialize( 27 | &self, 28 | _data: &[u8], 29 | ) -> Result, ObjectDeserializationError> { 30 | #[allow(clippy::box_default)] 31 | Ok(Box::new(Self::default())) 32 | } 33 | 34 | fn get_model<'a>(&self, assets: &'a GameAssets) -> &'a PhysicsObject { 35 | &assets.flax_item_object 36 | } 37 | 38 | fn is_item(&self) -> bool { 39 | true 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/plugins/objects/components/items/stone_axe.rs: -------------------------------------------------------------------------------- 1 | use crate::plugins::{ 2 | loading::resources::{GameAssets, PhysicsObject}, 3 | objects::components::{GameWorldObjectTrait, ObjectDeserializationError}, 4 | }; 5 | 6 | #[derive(Debug, Default, Clone)] 7 | pub struct StoneAxeItem; 8 | 9 | impl StoneAxeItem { 10 | pub const ID: &str = "stone-axe"; 11 | } 12 | 13 | impl GameWorldObjectTrait for StoneAxeItem { 14 | fn id(&self) -> &'static str { 15 | Self::ID 16 | } 17 | 18 | fn take(&mut self) -> Box { 19 | Box::new(std::mem::take(self)) 20 | } 21 | 22 | fn get_clone(&self) -> Box { 23 | Box::new(self.clone()) 24 | } 25 | 26 | fn deserialize( 27 | &self, 28 | _data: &[u8], 29 | ) -> Result, ObjectDeserializationError> { 30 | #[allow(clippy::box_default)] 31 | Ok(Box::new(Self::default())) 32 | } 33 | 34 | fn get_model<'a>(&self, assets: &'a GameAssets) -> &'a PhysicsObject { 35 | &assets.stone_axe_object 36 | } 37 | 38 | fn is_item(&self) -> bool { 39 | true 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/plugins/game_world/systems/setup_world.rs: -------------------------------------------------------------------------------- 1 | use crate::plugins::game_world::components::WorldSun; 2 | use bevy::prelude::*; 3 | 4 | pub fn setup_world(mut commands: Commands) { 5 | commands.insert_resource(AmbientLight { 6 | color: Color::rgb_u8(227, 255, 255), 7 | brightness: 0.7, 8 | }); 9 | 10 | let size = 64.0; 11 | 12 | commands 13 | .spawn(DirectionalLightBundle { 14 | directional_light: DirectionalLight { 15 | illuminance: 32000.0, 16 | shadows_enabled: true, 17 | shadow_projection: OrthographicProjection { 18 | left: -size, 19 | right: size, 20 | bottom: -size, 21 | top: size, 22 | near: -size * 128.0, 23 | far: size * 128.0, 24 | ..Default::default() 25 | }, 26 | ..Default::default() 27 | }, 28 | transform: Transform::default().looking_at(Vec3::new(0.3, -1.0, 0.1), Vec3::Y), 29 | ..Default::default() 30 | }) 31 | .insert(WorldSun) 32 | .insert(Name::new("sun")); 33 | } 34 | -------------------------------------------------------------------------------- /src/plugins/inspector/mod.rs: -------------------------------------------------------------------------------- 1 | use bevy::{diagnostic, prelude::*}; 2 | use bevy_inspector_egui::DefaultInspectorConfigPlugin; 3 | 4 | use crate::states::game_state::GameState; 5 | 6 | use self::{ 7 | resources::InspectorOpen, 8 | systems::{inspector::inspector_ui_system, toggle::toggle_inspector_system}, 9 | }; 10 | 11 | pub mod components; 12 | pub mod resources; 13 | mod systems; 14 | 15 | pub struct InspectorPlugin; 16 | impl Plugin for InspectorPlugin { 17 | fn build(&self, app: &mut App) { 18 | app.add_plugin(bevy_egui::EguiPlugin) 19 | .add_plugin(DefaultInspectorConfigPlugin) 20 | .add_plugin(diagnostic::FrameTimeDiagnosticsPlugin) 21 | .add_plugin(diagnostic::EntityCountDiagnosticsPlugin) 22 | .register_type::() 23 | .register_type::>>() 24 | .register_type::() 25 | .insert_resource(InspectorOpen(true)) 26 | .add_system_set( 27 | SystemSet::on_update(GameState::InGame) 28 | .with_system(toggle_inspector_system) 29 | .with_system(inspector_ui_system), 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/plugins/objects/components/items/coarse_string.rs: -------------------------------------------------------------------------------- 1 | use crate::plugins::{ 2 | loading::resources::{GameAssets, PhysicsObject}, 3 | objects::components::{GameWorldObjectTrait, ObjectDeserializationError}, 4 | }; 5 | 6 | #[derive(Debug, Default, Clone)] 7 | pub struct CoarseStringItem; 8 | 9 | impl CoarseStringItem { 10 | pub const ID: &str = "coarse_string"; 11 | } 12 | 13 | impl GameWorldObjectTrait for CoarseStringItem { 14 | fn id(&self) -> &'static str { 15 | Self::ID 16 | } 17 | 18 | fn take(&mut self) -> Box { 19 | Box::new(std::mem::take(self)) 20 | } 21 | 22 | fn get_clone(&self) -> Box { 23 | Box::new(self.clone()) 24 | } 25 | 26 | fn deserialize( 27 | &self, 28 | _data: &[u8], 29 | ) -> Result, ObjectDeserializationError> { 30 | #[allow(clippy::box_default)] 31 | Ok(Box::new(Self::default())) 32 | } 33 | 34 | fn get_model<'a>(&self, assets: &'a GameAssets) -> &'a PhysicsObject { 35 | &assets.coarse_string_object 36 | } 37 | 38 | fn is_item(&self) -> bool { 39 | true 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/plugins/objects/components/items/wooden_shovel.rs: -------------------------------------------------------------------------------- 1 | use crate::plugins::{ 2 | loading::resources::{GameAssets, PhysicsObject}, 3 | objects::components::{GameWorldObjectTrait, ObjectDeserializationError}, 4 | }; 5 | 6 | #[derive(Debug, Default, Clone)] 7 | pub struct WoodenShovelItem; 8 | 9 | impl WoodenShovelItem { 10 | pub const ID: &str = "wooden-shovel"; 11 | } 12 | 13 | impl GameWorldObjectTrait for WoodenShovelItem { 14 | fn id(&self) -> &'static str { 15 | Self::ID 16 | } 17 | 18 | fn take(&mut self) -> Box { 19 | Box::new(std::mem::take(self)) 20 | } 21 | 22 | fn get_clone(&self) -> Box { 23 | Box::new(self.clone()) 24 | } 25 | 26 | fn deserialize( 27 | &self, 28 | _data: &[u8], 29 | ) -> Result, ObjectDeserializationError> { 30 | #[allow(clippy::box_default)] 31 | Ok(Box::new(Self::default())) 32 | } 33 | 34 | fn get_model<'a>(&self, assets: &'a GameAssets) -> &'a PhysicsObject { 35 | &assets.wooden_shovel_object 36 | } 37 | 38 | fn is_item(&self) -> bool { 39 | true 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | use primitive_engineering::{ 3 | plugins::{ 4 | chunks::ChunksPlugin, craft::CraftPlugin, game_world::GameWorldPlugin, 5 | inspector::InspectorPlugin, loading::LoadingPlugin, main_menu::MainMenuPlugin, 6 | objects::ObjectsPlugin, physics::PhysicsPlugin, player::PlayerPlugin, 7 | static_mesh::StaticMeshPlugin, tooltip::TooltipPlugin, ui::UiPlugin, 8 | world_generator::WorldGeneratorPlugin, 9 | }, 10 | states::game_state::GameState, 11 | }; 12 | 13 | fn main() { 14 | App::new() 15 | .add_state(GameState::default()) 16 | .register_type::() 17 | .add_plugin(WorldGeneratorPlugin) 18 | .add_plugins(DefaultPlugins) 19 | .add_plugin(TooltipPlugin) 20 | .add_plugin(LoadingPlugin) 21 | .add_plugin(MainMenuPlugin) 22 | .add_plugin(GameWorldPlugin) 23 | .add_plugin(ChunksPlugin) 24 | .add_plugin(InspectorPlugin) 25 | .add_plugin(PlayerPlugin) 26 | .add_plugin(StaticMeshPlugin) 27 | .add_plugin(ObjectsPlugin) 28 | .add_plugin(PhysicsPlugin) 29 | .add_plugin(CraftPlugin) 30 | .add_plugin(UiPlugin) 31 | .run(); 32 | } 33 | -------------------------------------------------------------------------------- /src/plugins/loading/systems/process_assets.rs: -------------------------------------------------------------------------------- 1 | use super::assets_processors::physics_object::process_physic_objects; 2 | use crate::{plugins::loading::resources::GameAssets, states::game_state::GameState}; 3 | use bevy::prelude::*; 4 | 5 | pub fn process_assets( 6 | mut game_assets: ResMut, 7 | mut game_state: ResMut>, 8 | mut scenes: ResMut>, 9 | mut meshes: ResMut>, 10 | ) { 11 | let fields: Vec<_> = game_assets 12 | .iter_fields() 13 | .enumerate() 14 | .map(|(i, _)| i) 15 | .collect(); 16 | 17 | let all_loaded = fields.iter().all(|&index| { 18 | let field_name = { 19 | let name = game_assets.name_at(index).unwrap(); 20 | name.to_string() 21 | }; 22 | let field = game_assets.field_at_mut(index).unwrap(); 23 | 24 | // Try to process field as specific asset type. 25 | // If field is not loaded yet, return true and skip frame 26 | if !process_physic_objects(&field_name, field, &mut scenes, &mut meshes) { 27 | return false; 28 | } 29 | 30 | true 31 | }); 32 | 33 | if !all_loaded { 34 | return; 35 | } 36 | 37 | game_state.set(GameState::MenuMain).unwrap(); 38 | } 39 | -------------------------------------------------------------------------------- /src/plugins/inspector/systems/inspector/assets.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | use bevy_egui::egui; 3 | use bevy_inspector_egui::bevy_inspector::ui_for_assets; 4 | 5 | #[derive(Default, Clone, Copy, PartialEq, Eq, Hash, Debug)] 6 | pub enum AssetsInspectorTab { 7 | #[default] 8 | Materials, 9 | Images, 10 | } 11 | 12 | #[derive(Resource, Default)] 13 | pub struct AssetsInspectorState { 14 | pub tab_open: AssetsInspectorTab, 15 | } 16 | 17 | pub fn assets_inspector(world: &mut World, ui: &mut egui::Ui) { 18 | let mut state = world 19 | .remove_resource::() 20 | .unwrap_or_default(); 21 | 22 | ui.horizontal(|ui| { 23 | ui.selectable_value( 24 | &mut state.tab_open, 25 | AssetsInspectorTab::Materials, 26 | "materials", 27 | ); 28 | ui.selectable_value(&mut state.tab_open, AssetsInspectorTab::Images, "images"); 29 | }); 30 | 31 | egui::ScrollArea::vertical() 32 | .auto_shrink([false, false]) 33 | .show(ui, |ui| match state.tab_open { 34 | AssetsInspectorTab::Materials => ui_for_assets::(world, ui), 35 | AssetsInspectorTab::Images => ui_for_assets::(world, ui), 36 | }); 37 | 38 | world.insert_resource(state); 39 | } 40 | -------------------------------------------------------------------------------- /src/plugins/chunks/mod.rs: -------------------------------------------------------------------------------- 1 | use self::{ 2 | components::ChunkComponent, 3 | resources::ChunkLoadingEnabled, 4 | systems::{ 5 | details::*, 6 | loading::{handle_region_loaded_system, region_loading_system}, 7 | mine::*, 8 | unload::*, 9 | }, 10 | }; 11 | use crate::states::game_state::GameState; 12 | use bevy::prelude::*; 13 | 14 | pub mod components; 15 | pub mod helpers; 16 | pub mod resources; 17 | mod systems; 18 | 19 | pub struct ChunksPlugin; 20 | 21 | impl Plugin for ChunksPlugin { 22 | fn build(&self, app: &mut App) { 23 | app.register_type::() 24 | .register_type::() 25 | .insert_resource(ChunkLoadingEnabled(true)) 26 | .add_system_set( 27 | SystemSet::on_update(GameState::InGame) 28 | .with_system(chunk_details_system) 29 | .with_system(handle_region_loaded_system) 30 | .with_system(spawn_detailed_chunk_system) 31 | .with_system(handle_unload_task_system) 32 | .with_system(region_loading_system) 33 | .with_system(mine_system) 34 | .with_system(handle_mining_system) 35 | .with_system(unload_system), 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/plugins/loading/resources/mod.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | use bevy_rapier3d::prelude::Collider; 3 | 4 | #[derive(Default, Debug, Clone, Reflect, FromReflect)] 5 | pub struct PhysicsObject { 6 | pub scene: Handle, 7 | pub processed: bool, 8 | #[reflect(ignore)] 9 | pub colliders: Vec<(Collider, Transform)>, 10 | } 11 | 12 | #[derive(Resource, Default, Reflect, FromReflect)] 13 | #[reflect(Resource)] 14 | pub struct GameAssets { 15 | pub main_font: Handle, 16 | pub default_material: Handle, 17 | pub craft_zone_material: Handle, 18 | pub pointer_mesh: Handle, 19 | pub craft_zone_mesh: Handle, 20 | 21 | pub flax_object: PhysicsObject, 22 | pub tree_object: PhysicsObject, 23 | pub branch_object: PhysicsObject, 24 | pub rock_object: PhysicsObject, 25 | pub coarse_string_object: PhysicsObject, 26 | pub flax_item_object: PhysicsObject, 27 | pub fire_object: PhysicsObject, 28 | pub wooden_shovel_object: PhysicsObject, 29 | pub log_object: PhysicsObject, 30 | pub stump_object: PhysicsObject, 31 | pub cactus_object: PhysicsObject, 32 | pub spruce_object: PhysicsObject, 33 | pub spruce_snow_object: PhysicsObject, 34 | pub stone_axe_object: PhysicsObject, 35 | 36 | pub crosshair_image: Handle, 37 | } 38 | -------------------------------------------------------------------------------- /src/plugins/player/resources/mod.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | pub mod input_settings; 5 | pub mod look_at; 6 | 7 | pub const PLAYER_ACCESS_RADIUS: f32 = 6.0; 8 | 9 | #[derive( 10 | Resource, Reflect, FromReflect, PartialEq, Eq, Debug, Clone, Copy, Serialize, Deserialize, 11 | )] 12 | pub enum PlayerMovementMode { 13 | Fly, 14 | Walk, 15 | } 16 | 17 | #[derive(Resource, Clone, Debug, Reflect, FromReflect, Serialize, Deserialize)] 18 | #[reflect(Resource)] 19 | pub struct PlayerStats { 20 | pub sensitivity: f32, 21 | pub fly_speed: f32, 22 | pub on_ground_speed: f32, 23 | pub in_air_speed: f32, 24 | pub jump_speed: f32, 25 | pub friction_factor: f32, 26 | pub mode: PlayerMovementMode, 27 | pub mining_range: f32, 28 | pub mining_radius: f32, 29 | pub mining_strength: f32, 30 | } 31 | 32 | impl Default for PlayerStats { 33 | fn default() -> Self { 34 | Self { 35 | sensitivity: 0.00012, 36 | fly_speed: 50., 37 | jump_speed: 5.0, 38 | in_air_speed: 2.0, 39 | on_ground_speed: 40.0, 40 | friction_factor: 15.0, 41 | mode: PlayerMovementMode::Fly, 42 | mining_radius: 1.0, 43 | mining_range: 16.0, 44 | mining_strength: 1.0, 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/internal/voxel/voxel_types.rs: -------------------------------------------------------------------------------- 1 | use bevy_reflect::{FromReflect, Reflect}; 2 | use lerp::Lerp; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | use crate::internal::color::Color; 6 | 7 | #[derive( 8 | Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Reflect, FromReflect, 9 | )] 10 | pub struct VoxelId(u32); 11 | 12 | impl Lerp for VoxelId { 13 | fn lerp(self, other: Self, pos: f32) -> Self { 14 | if rand::random::() > pos { 15 | self 16 | } else { 17 | other 18 | } 19 | } 20 | } 21 | 22 | impl VoxelId { 23 | pub const GRASS: Self = Self(0); 24 | pub const DIRT: Self = Self(1); 25 | pub const STONE: Self = Self(2); 26 | pub const SAND: Self = Self(3); 27 | pub const SAND_STONE: Self = Self(4); 28 | pub const SNOW: Self = Self(5); 29 | 30 | pub const fn new(id: u32) -> Self { 31 | Self(id) 32 | } 33 | 34 | pub fn is_empty(&self) -> bool { 35 | self.0 == 0 36 | } 37 | 38 | pub fn get_color(&self) -> Color { 39 | match self.0 { 40 | 0 => Color::rgb_u8(40, 133, 7), 41 | 1 => Color::rgb_u8(65, 40, 22), 42 | 2 => Color::rgb_u8(100, 100, 100), 43 | 3 => Color::rgb_u8(218, 185, 113), 44 | 4 => Color::rgb_u8(200, 158, 100), 45 | 5 => Color::rgb_u8(255, 255, 255), 46 | // Unknown voxel id. 47 | _ => Color::rgb_u8(255, 0, 255), 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/plugins/objects/components/objects/spruce.rs: -------------------------------------------------------------------------------- 1 | use crate::plugins::{ 2 | loading::resources::{GameAssets, PhysicsObject}, 3 | objects::components::{GameWorldObjectTrait, ObjectDeserializationError}, 4 | }; 5 | use bevy_reflect::{FromReflect, Reflect}; 6 | 7 | #[derive(Debug, Clone, Default, Reflect, FromReflect)] 8 | pub struct SpruceObject { 9 | pub snow: bool, 10 | } 11 | 12 | impl SpruceObject { 13 | const ID: &str = "spruce"; 14 | pub const WITH_SNOW: Self = Self { snow: true }; 15 | pub const WITHOUT_SNOW: Self = Self { snow: false }; 16 | } 17 | 18 | impl GameWorldObjectTrait for SpruceObject { 19 | fn id(&self) -> &'static str { 20 | Self::ID 21 | } 22 | 23 | fn take(&mut self) -> Box { 24 | Box::new(std::mem::take(self)) 25 | } 26 | 27 | fn get_clone(&self) -> Box { 28 | Box::new(self.clone()) 29 | } 30 | 31 | fn deserialize( 32 | &self, 33 | data: &[u8], 34 | ) -> Result, ObjectDeserializationError> { 35 | let r = Self { 36 | snow: !data.is_empty() && data[0] != 0, 37 | }; 38 | Ok(Box::new(r)) 39 | } 40 | 41 | fn serialize(&self) -> Vec { 42 | vec![self.snow as u8] 43 | } 44 | 45 | fn get_model<'a>(&self, assets: &'a GameAssets) -> &'a PhysicsObject { 46 | if self.snow { 47 | &assets.spruce_snow_object 48 | } else { 49 | &assets.spruce_object 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/plugins/player/systems/cursor.rs: -------------------------------------------------------------------------------- 1 | use bevy::{ 2 | prelude::*, 3 | window::{CursorGrabMode, Window}, 4 | }; 5 | 6 | fn set_cursor_grabbed(window: &mut Window) { 7 | window.set_cursor_grab_mode(CursorGrabMode::Confined); 8 | window.set_cursor_visibility(false); 9 | } 10 | 11 | fn set_cursor_released(window: &mut Window) { 12 | window.set_cursor_grab_mode(CursorGrabMode::None); 13 | window.set_cursor_visibility(true); 14 | } 15 | 16 | fn toggle_grab_cursor(window: &mut Window) { 17 | match window.cursor_grab_mode() { 18 | CursorGrabMode::None => { 19 | set_cursor_grabbed(window); 20 | } 21 | _ => { 22 | set_cursor_released(window); 23 | } 24 | } 25 | } 26 | 27 | pub fn cursor_toggle(keys: Res>, mut windows: ResMut) { 28 | if let Some(window) = windows.get_primary_mut() { 29 | if keys.just_pressed(KeyCode::Escape) { 30 | toggle_grab_cursor(window); 31 | } 32 | } else { 33 | warn!("Primary window not found for `cursor_toggle`!"); 34 | } 35 | } 36 | 37 | pub fn cursor_grab(mut windows: ResMut) { 38 | if let Some(window) = windows.get_primary_mut() { 39 | set_cursor_grabbed(window); 40 | } else { 41 | warn!("Primary window not found for `cursor_grab`!"); 42 | } 43 | } 44 | 45 | pub fn cursor_release(mut windows: ResMut) { 46 | if let Some(window) = windows.get_primary_mut() { 47 | set_cursor_released(window); 48 | } else { 49 | warn!("Primary window not found for `cursor_toggle`!"); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/internal/chunks/pointer.rs: -------------------------------------------------------------------------------- 1 | use super::Chunk; 2 | use crate::{internal::pos::ChunkPos, plugins::game_world::resources::GameWorld}; 3 | use bevy::prelude::*; 4 | use std::{ 5 | fmt::{Debug, Formatter}, 6 | sync::{Arc, Mutex, MutexGuard}, 7 | }; 8 | 9 | #[derive(Clone, Default, Reflect, FromReflect)] 10 | pub struct ChunkPointer { 11 | #[reflect(ignore)] 12 | chunk: Arc>, 13 | pos: ChunkPos, 14 | level: usize, 15 | } 16 | 17 | impl ChunkPointer { 18 | pub fn new(chunk: Chunk, pos: ChunkPos, detail_level: usize) -> Self { 19 | Self { 20 | chunk: Arc::new(Mutex::new(chunk)), 21 | pos, 22 | level: detail_level, 23 | } 24 | } 25 | 26 | pub fn is_real(&self) -> bool { 27 | self.level == GameWorld::MAX_DETAIL_LEVEL 28 | } 29 | 30 | pub fn lock(&self) -> MutexGuard { 31 | self.chunk.lock().unwrap() 32 | } 33 | 34 | pub fn get_level(&self) -> usize { 35 | self.level 36 | } 37 | 38 | pub fn get_pos(&self) -> ChunkPos { 39 | self.pos 40 | } 41 | 42 | pub fn get_translation(&self) -> Vec3 { 43 | (self.pos * GameWorld::level_to_scale(self.level) as i64).to_vec3() * Chunk::REAL_SIZE 44 | } 45 | 46 | pub fn get_size(&self) -> f32 { 47 | GameWorld::level_to_scale(self.level) as f32 * Chunk::REAL_SIZE 48 | } 49 | 50 | pub fn is_need_save(&self) -> bool { 51 | self.lock().is_need_save() 52 | } 53 | } 54 | 55 | impl Debug for ChunkPointer { 56 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 57 | f.debug_struct("ChunkPointer").finish() 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/plugins/game_world/mod.rs: -------------------------------------------------------------------------------- 1 | use self::{ 2 | components::WorldSun, 3 | resources::{meta::GameWorldMeta, GameWorld}, 4 | systems::{ 5 | create_world::{start_world_creating, world_creating_progress}, 6 | load_world::world_loading_system, 7 | save::save_system, 8 | setup_world::setup_world, 9 | sun_to_player::move_sun_to_player, 10 | }, 11 | }; 12 | use crate::states::game_state::GameState; 13 | use bevy::{pbr::DirectionalLightShadowMap, prelude::*}; 14 | 15 | pub mod components; 16 | pub mod resources; 17 | mod systems; 18 | 19 | pub struct GameWorldPlugin; 20 | 21 | impl Plugin for GameWorldPlugin { 22 | fn build(&self, app: &mut App) { 23 | app.add_system_set( 24 | SystemSet::on_enter(GameState::WorldCreating).with_system(start_world_creating), 25 | ) 26 | .add_system_set( 27 | SystemSet::on_update(GameState::InGame) 28 | .with_system(move_sun_to_player) 29 | .with_system(save_system), 30 | ) 31 | .add_system_set( 32 | SystemSet::on_update(GameState::WorldCreating).with_system(world_creating_progress), 33 | ) 34 | .add_system_set( 35 | SystemSet::on_update(GameState::WorldLoading).with_system(world_loading_system), 36 | ) 37 | .insert_resource(ClearColor(Color::rgb(0.7, 0.9, 1.0))) 38 | .insert_resource(DirectionalLightShadowMap { size: 16384 }) 39 | .register_type::() 40 | .register_type::() 41 | .register_type::() 42 | .add_startup_system(setup_world) 43 | .insert_resource(GameWorldMeta::default()); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/plugins/game_world/systems/load_world.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | plugins::{ 3 | game_world::resources::{meta::GameWorldMeta, GameWorld}, 4 | loading::resources::GameAssets, 5 | objects::resources::objects_registry::ObjectsRegistry, 6 | player::{ 7 | components::{PlayerComponent, PlayerHand, PlayerHeadComponent}, 8 | resources::PlayerStats, 9 | }, 10 | }, 11 | states::game_state::GameState, 12 | }; 13 | use bevy::prelude::*; 14 | 15 | pub fn world_loading_system( 16 | objects_registry: Res, 17 | assets: Res, 18 | mut commands: Commands, 19 | mut game_state: ResMut>, 20 | 21 | meta: Res, 22 | mut player_q: Query<(&mut Transform, &mut PlayerComponent)>, 23 | player_hand_q: Query>, 24 | mut head_q: Query<&mut Transform, (With, Without)>, 25 | mut player_stats: ResMut, 26 | ) { 27 | let world = GameWorld::new(); 28 | commands.insert_resource(world); 29 | 30 | if let Some(player_save) = meta.load_player() { 31 | let player = player_q.single_mut(); 32 | let mut head = head_q.single_mut(); 33 | let hand = player_hand_q.single(); 34 | 35 | player_save.apply_to_player( 36 | &objects_registry, 37 | &assets, 38 | &mut commands, 39 | hand, 40 | player, 41 | &mut head, 42 | &mut player_stats, 43 | ); 44 | } else { 45 | warn!("No player save found, creating new player"); 46 | } 47 | 48 | game_state.set(GameState::InGame).unwrap(); 49 | } 50 | -------------------------------------------------------------------------------- /src/plugins/craft/resources/crafts_registry/craft.rs: -------------------------------------------------------------------------------- 1 | use crate::plugins::{loading::resources::GameAssets, objects::components::GameWorldObject}; 2 | use bevy::prelude::*; 3 | use std::fmt::Debug; 4 | 5 | #[derive(Debug, Default, Reflect, FromReflect)] 6 | pub struct CraftEntry { 7 | #[reflect(ignore)] 8 | pub craft: Option>, 9 | } 10 | 11 | impl CraftEntry { 12 | pub fn id(&self) -> &'static str { 13 | self.craft.as_ref().unwrap().id() 14 | } 15 | 16 | pub fn craft( 17 | &self, 18 | commands: &mut Commands, 19 | assets: &GameAssets, 20 | craft_center: Vec3, 21 | hand_item: &mut Option<(Entity, Mut)>, 22 | items: &mut [(Entity, Mut)], 23 | ) -> bool { 24 | self.craft 25 | .as_ref() 26 | .unwrap() 27 | .craft(commands, assets, craft_center, hand_item, items) 28 | } 29 | } 30 | 31 | impl From for CraftEntry 32 | where 33 | T: CraftTrait + 'static, 34 | { 35 | fn from(value: T) -> Self { 36 | Self { 37 | craft: Some(Box::new(value)), 38 | } 39 | } 40 | } 41 | 42 | pub trait CraftTrait: Send + Sync + Debug { 43 | fn id(&self) -> &'static str; 44 | 45 | fn craft( 46 | &self, 47 | commands: &mut Commands, 48 | assets: &GameAssets, 49 | craft_center: Vec3, 50 | hand_item: &mut Option<(Entity, Mut)>, 51 | items: &mut [(Entity, Mut)], 52 | ) -> bool; 53 | 54 | fn check( 55 | &self, 56 | hand_item: &Option<(Entity, Mut)>, 57 | items: &[(Entity, Mut)], 58 | ) -> bool; 59 | } 60 | -------------------------------------------------------------------------------- /src/plugins/player/events/mod.rs: -------------------------------------------------------------------------------- 1 | use bevy_reflect::{FromReflect, Reflect}; 2 | 3 | #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Reflect, FromReflect)] 4 | pub struct GoForwardEvent; 5 | 6 | #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Reflect, FromReflect)] 7 | pub struct GoBackwardEvent; 8 | 9 | #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Reflect, FromReflect)] 10 | pub struct GoLeftEvent; 11 | 12 | #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Reflect, FromReflect)] 13 | pub struct GoRightEvent; 14 | 15 | #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Reflect, FromReflect)] 16 | pub struct GoUpEvent; 17 | 18 | #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Reflect, FromReflect)] 19 | pub struct GoDownEvent; 20 | 21 | #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Reflect, FromReflect)] 22 | pub struct JumpEvent; 23 | 24 | #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Reflect, FromReflect)] 25 | pub struct SprintEvent; 26 | 27 | #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Reflect, FromReflect)] 28 | pub struct ToggleFlyEvent; 29 | 30 | #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Reflect, FromReflect)] 31 | pub struct SpawnItemEvent; 32 | 33 | #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Reflect, FromReflect)] 34 | pub struct MineEvent; 35 | 36 | #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Reflect, FromReflect)] 37 | pub struct CraftEvent; 38 | 39 | #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Reflect, FromReflect)] 40 | pub struct UseGrabPlaceEvent; 41 | 42 | #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Reflect, FromReflect)] 43 | pub struct InteractEvent; 44 | -------------------------------------------------------------------------------- /src/plugins/inspector/systems/inspector/profiling/mod.rs: -------------------------------------------------------------------------------- 1 | use bevy::{ 2 | diagnostic::{Diagnostics, FrameTimeDiagnosticsPlugin}, 3 | prelude::*, 4 | }; 5 | use bevy_egui::egui; 6 | 7 | use self::avg_samples::AvgSamples; 8 | 9 | mod avg_samples; 10 | 11 | #[derive(Resource)] 12 | pub struct ProfilerState { 13 | low_1p_fps: AvgSamples, 14 | low_01p_fps: AvgSamples, 15 | } 16 | 17 | impl Default for ProfilerState { 18 | fn default() -> Self { 19 | Self { 20 | low_1p_fps: AvgSamples::new(100), 21 | low_01p_fps: AvgSamples::new(1000), 22 | } 23 | } 24 | } 25 | 26 | pub fn profiling_inspector(world: &mut World, ui: &mut egui::Ui) { 27 | let mut state = world.remove_resource::().unwrap_or_default(); 28 | let diagnostics = world.get_resource::().unwrap(); 29 | 30 | let fps = diagnostics 31 | .get_measurement(FrameTimeDiagnosticsPlugin::FPS) 32 | .unwrap() 33 | .value; 34 | 35 | state.low_1p_fps.update(fps as f32); 36 | state.low_01p_fps.update(fps as f32); 37 | 38 | egui::ScrollArea::vertical() 39 | .auto_shrink([false, false]) 40 | .show(ui, |ui| { 41 | ui.horizontal(|ui| { 42 | ui.label("fps:"); 43 | ui.label(format!("{}", state.low_1p_fps.avg().ceil())) 44 | }); 45 | ui.horizontal(|ui| { 46 | ui.label("low 1% fps:"); 47 | ui.label(format!("{}", state.low_1p_fps.min().ceil())); 48 | }); 49 | ui.horizontal(|ui| { 50 | ui.label("low 0.1% fps:"); 51 | ui.label(format!("{}", state.low_01p_fps.min().ceil())) 52 | }); 53 | }); 54 | 55 | world.insert_resource(state); 56 | } 57 | -------------------------------------------------------------------------------- /src/plugins/objects/utils/object_save.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | use crate::plugins::objects::{ 5 | components::{object_spawner::ObjectSpawner, GameWorldObject}, 6 | resources::objects_registry::ObjectsRegistry, 7 | }; 8 | 9 | #[derive(Debug, Serialize, Deserialize)] 10 | pub struct GameWorldObjectSave { 11 | object_id: String, 12 | object_data: Vec, 13 | translation: (f32, f32, f32), 14 | rotation: (f32, f32, f32, f32), 15 | scale: (f32, f32, f32), 16 | } 17 | 18 | impl GameWorldObjectSave { 19 | pub fn new(obj: &GameWorldObject, transform: Transform) -> Self { 20 | Self { 21 | object_id: obj.0.id().to_string(), 22 | object_data: obj.0.serialize(), 23 | translation: transform.translation.into(), 24 | rotation: transform.rotation.into(), 25 | scale: transform.scale.into(), 26 | } 27 | } 28 | 29 | pub fn to_spawner(self, registry: &ObjectsRegistry, offset: Vec3) -> ObjectSpawner { 30 | let translation = Vec3::from(self.translation) + offset; 31 | 32 | let transform = Transform::from_translation(translation) 33 | .with_rotation(Quat::from_xyzw( 34 | self.rotation.0, 35 | self.rotation.1, 36 | self.rotation.2, 37 | self.rotation.3, 38 | )) 39 | .with_scale(self.scale.into()); 40 | 41 | let object = registry 42 | .deserialize_object(&self.object_id, &self.object_data) 43 | .unwrap_or_else(|| panic!("Failed to deserialize object with id {}", self.object_id)); 44 | 45 | ObjectSpawner { 46 | id: self.object_id, 47 | object: Some(object), 48 | transform, 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/plugins/tooltip/systems/redraw.rs: -------------------------------------------------------------------------------- 1 | use crate::plugins::{ 2 | player::components::PlayerCameraComponent, 3 | tooltip::{ 4 | components::UiTooltip, 5 | resources::{TooltipEntry, Tooltips}, 6 | }, 7 | }; 8 | use bevy::prelude::*; 9 | 10 | fn redraw_tooltip( 11 | tooltip: &TooltipEntry, 12 | camera_transform: &GlobalTransform, 13 | camera: &Camera, 14 | in_world_q: &Query<&GlobalTransform, Without>, 15 | on_screen_q: &mut Query<(&mut Style, &mut Visibility)>, 16 | ) -> Option<()> { 17 | let in_world_transform = in_world_q.get(tooltip.in_world_entity.unwrap()).ok()?; 18 | let world_position = in_world_transform.translation(); 19 | 20 | let (mut style, mut visibility) = on_screen_q.get_mut(tooltip.ui_entity.unwrap()).unwrap(); 21 | 22 | let pos = camera.world_to_viewport(camera_transform, world_position); 23 | 24 | if let Some(pos) = pos { 25 | style.position.left = Val::Px(pos.x - UiTooltip::WIDTH_PX / 2.0); 26 | style.position.bottom = Val::Px(pos.y); 27 | 28 | visibility.is_visible = true; 29 | } else { 30 | visibility.is_visible = false; 31 | } 32 | 33 | Some(()) 34 | } 35 | 36 | pub fn tooltip_redraw_system( 37 | tooltips: Res, 38 | camera_q: Query<(&GlobalTransform, &Camera), With>, 39 | in_world_q: Query<&GlobalTransform, Without>, 40 | mut on_screen_q: Query<(&mut Style, &mut Visibility)>, 41 | ) { 42 | let (camera_transform, camera) = camera_q.get_single().unwrap(); 43 | 44 | for (_, tooltip) in tooltips.iter() { 45 | redraw_tooltip( 46 | tooltip, 47 | &camera_transform, 48 | &camera, 49 | &in_world_q, 50 | &mut on_screen_q, 51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/plugins/chunks/helpers/spawn_chunk.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | internal::chunks::pointer::ChunkPointer, 3 | plugins::{ 4 | chunks::components::{ChunkComponent, ChunkMeshComponent, RealChunkComponent}, 5 | game_world::resources::GameWorld, 6 | inspector::components::InspectorGroupChunks, 7 | loading::resources::GameAssets, 8 | static_mesh::components::{StaticMeshComponent, Vertex}, 9 | }, 10 | }; 11 | use bevy::prelude::*; 12 | 13 | pub fn spawn_chunk( 14 | commands: &mut Commands, 15 | meshes: &mut Assets, 16 | assets: &GameAssets, 17 | world: &mut GameWorld, 18 | chunk: ChunkPointer, 19 | vertices: Vec, 20 | ) -> Entity { 21 | let mesh = StaticMeshComponent::spawn(commands, meshes, assets, vertices); 22 | commands 23 | .entity(mesh) 24 | .insert(ChunkMeshComponent) 25 | .insert(Name::new("chunk:mesh")); 26 | 27 | let chunk_pos_vec = chunk.get_translation(); 28 | 29 | let mut chunk_entity = commands.spawn(( 30 | InspectorGroupChunks, 31 | Name::new(format!( 32 | "chunk[{:?}-{}]", 33 | chunk.get_pos(), 34 | chunk.get_level() 35 | )), 36 | ChunkComponent { 37 | chunk: chunk.clone(), 38 | }, 39 | GlobalTransform::default(), 40 | Transform::from_translation(chunk_pos_vec), 41 | VisibilityBundle::default(), 42 | )); 43 | 44 | chunk_entity.add_child(mesh); 45 | 46 | let pos = chunk.get_pos(); 47 | let level = chunk.get_level(); 48 | 49 | let entity = chunk_entity.id(); 50 | 51 | world 52 | .update_chunk(chunk.clone(), entity) 53 | .unwrap_or_else(|_| panic!("Failed to update chunk {:?}-{}", pos, level)); 54 | 55 | if chunk.is_real() { 56 | chunk_entity.insert(RealChunkComponent); 57 | } 58 | 59 | entity 60 | } 61 | -------------------------------------------------------------------------------- /src/plugins/objects/systems/spawn_object.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | internal::{chunks::Chunk, pos::ChunkPos}, 3 | plugins::{ 4 | game_world::resources::GameWorld, loading::resources::GameAssets, 5 | objects::components::object_spawner::ObjectSpawner, 6 | }, 7 | }; 8 | use bevy::prelude::*; 9 | 10 | #[derive(Debug)] 11 | enum ObjectSpawnError { 12 | ChunkNotExist(ChunkPos), 13 | ObjectAlreadySpawned, 14 | } 15 | 16 | fn spawn( 17 | commands: &mut Commands, 18 | object_spawn: &mut ObjectSpawner, 19 | world: &GameWorld, 20 | assets: &GameAssets, 21 | ) -> Result<(), ObjectSpawnError> { 22 | let chunk_pos = Chunk::vec_to_chunk_pos(object_spawn.transform.translation); 23 | 24 | let (chunk, chunk_entity) = world 25 | .get_detailest_chunk(chunk_pos) 26 | .ok_or(ObjectSpawnError::ChunkNotExist(chunk_pos))?; 27 | 28 | object_spawn 29 | .spawn(commands, assets, chunk, chunk_entity) 30 | .ok_or(ObjectSpawnError::ObjectAlreadySpawned)?; 31 | 32 | Ok(()) 33 | } 34 | 35 | pub fn spawn_object_system( 36 | mut commands: Commands, 37 | assets: Res, 38 | mut object_spawn_q: Query<(Entity, &mut ObjectSpawner)>, 39 | world: Res, 40 | ) { 41 | for (spawn_entity, mut object_spawn) in object_spawn_q.iter_mut() { 42 | if let Err(err) = spawn(&mut commands, &mut object_spawn, &world, &assets) { 43 | if let ObjectSpawnError::ObjectAlreadySpawned = err { 44 | warn!( 45 | "Error spawning object {} at {:?}: object already spawned", 46 | object_spawn.id, object_spawn.transform.translation 47 | ); 48 | commands.entity(spawn_entity).despawn_recursive(); 49 | } 50 | } else { 51 | commands.entity(spawn_entity).despawn_recursive(); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/plugins/objects/components/items/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::{internal::chunks::Chunk, plugins::game_world::resources::GameWorld}; 2 | use bevy::{ecs::system::EntityCommands, prelude::*}; 3 | use bevy_rapier3d::prelude::*; 4 | 5 | pub mod branch; 6 | pub mod coarse_string; 7 | pub mod flax_item; 8 | pub mod log; 9 | pub mod rock; 10 | pub mod stone_axe; 11 | pub mod wooden_shovel; 12 | 13 | #[derive(Component, Debug, Clone, Copy)] 14 | pub struct ItemComponent; 15 | 16 | #[derive(Component, Debug, Default, Clone, Copy, Reflect, FromReflect)] 17 | #[reflect(Component)] 18 | pub struct ItemGrabbed; 19 | 20 | pub fn set_item_physics_enabled(item: &mut EntityCommands, state: bool) { 21 | if state { 22 | item.remove::(); 23 | item.remove::(); 24 | } else { 25 | item.insert(RigidBodyDisabled); 26 | item.insert(ColliderDisabled); 27 | } 28 | } 29 | 30 | pub fn grab_item(mut item: EntityCommands, hand: Entity) { 31 | item.set_parent(hand); 32 | 33 | item.remove::(); 34 | item.insert(Transform::default()); 35 | item.insert(ItemGrabbed); 36 | 37 | set_item_physics_enabled(&mut item, false); 38 | } 39 | 40 | pub fn drop_item(mut item: EntityCommands, mut transform: Transform, world: &mut GameWorld) { 41 | item.remove::(); 42 | item.remove_parent(); 43 | 44 | set_item_physics_enabled(&mut item, true); 45 | 46 | let chunk_pos = Chunk::vec_to_chunk_pos(transform.translation); 47 | if let Some((chunk, entity)) = world.get_detailest_chunk(chunk_pos) { 48 | let chunk_offset = chunk.get_translation(); 49 | transform.translation -= chunk_offset; 50 | item.set_parent(entity); 51 | } else { 52 | warn!("Failed to find chunk for item {:?}", transform.translation); 53 | } 54 | 55 | item.remove::(); 56 | item.insert(transform); 57 | } 58 | -------------------------------------------------------------------------------- /src/plugins/player/systems/mod.rs: -------------------------------------------------------------------------------- 1 | use super::components::{PlayerCameraComponent, PlayerComponent, PlayerHand, PlayerHeadComponent}; 2 | use bevy::prelude::*; 3 | use bevy_rapier3d::prelude::*; 4 | 5 | pub mod cursor; 6 | pub mod input; 7 | pub mod look; 8 | pub mod look_at; 9 | pub mod movements; 10 | pub mod spawn_item; 11 | 12 | pub const HEAD_LEVEL: f32 = 0.75; 13 | 14 | pub fn setup_player_system(mut commands: Commands) { 15 | commands 16 | .spawn(( 17 | Name::new("player"), 18 | PlayerComponent::default(), 19 | VisibilityBundle::default(), 20 | Collider::capsule_y(0.75, 0.25), 21 | RigidBodyDisabled, 22 | ColliderDisabled, 23 | KinematicCharacterControllerOutput::default(), 24 | KinematicCharacterController { 25 | up: Vec3::Y, 26 | filter_flags: QueryFilterFlags::default() ^ QueryFilterFlags::EXCLUDE_SENSORS, 27 | 28 | ..Default::default() 29 | }, 30 | TransformBundle::from_transform(Transform::from_xyz(0.0, 2.0, 0.0)), 31 | )) 32 | .with_children(|parent| { 33 | parent.spawn(( 34 | Name::new("player:camera"), 35 | PlayerCameraComponent, 36 | PlayerHeadComponent, 37 | VisibilityBundle::default(), 38 | Camera3dBundle { 39 | transform: Transform::from_xyz(0.0, HEAD_LEVEL, 0.0), 40 | ..Default::default() 41 | }, 42 | )); 43 | }) 44 | .with_children(|parent| { 45 | parent.spawn(( 46 | Name::new("player:hand"), 47 | PlayerHand, 48 | VisibilityBundle::default(), 49 | TransformBundle::from_transform(Transform::from_xyz(0.4, 0.3, -0.7)), 50 | )); 51 | }); 52 | } 53 | -------------------------------------------------------------------------------- /src/plugins/main_menu/systems/load_game.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | plugins::{ 3 | game_world::resources::meta::GameWorldMeta, world_generator::resources::WorldGenerator, 4 | }, 5 | states::game_state::GameState, 6 | }; 7 | use bevy::prelude::*; 8 | use bevy_egui::{egui, EguiContext}; 9 | 10 | pub struct SavedWorlds { 11 | worlds: Vec, 12 | } 13 | 14 | impl Default for SavedWorlds { 15 | fn default() -> Self { 16 | Self { 17 | worlds: GameWorldMeta::get_saves(), 18 | } 19 | } 20 | } 21 | 22 | // expected seed 0b4380c4-b685-448c-ba55-847554c36e8e 23 | // 0b4380c4-b685-448c-ba55-847554c36e8e 24 | 25 | // !TODO:ui create loading progress 26 | pub fn load_game_system( 27 | mut game_state: ResMut>, 28 | mut egui_context: ResMut, 29 | mut saved_worlds: Local, 30 | mut game_world_meta: ResMut, 31 | mut generator: ResMut, 32 | ) { 33 | egui::Window::new("Load world").show(egui_context.ctx_mut(), |ui| { 34 | ui.vertical(|ui| { 35 | for world in saved_worlds.worlds.iter() { 36 | ui.horizontal(|ui| { 37 | if ui.button("Load").clicked() { 38 | *game_world_meta = world.clone(); 39 | generator.set_seed(game_world_meta.seed); 40 | game_state.set(GameState::WorldLoading).unwrap(); 41 | } 42 | 43 | ui.label(format!("{} ({})", world.name, world.id)); 44 | }); 45 | } 46 | }); 47 | 48 | ui.horizontal(|ui| { 49 | if ui.button("Update").clicked() { 50 | saved_worlds.worlds = GameWorldMeta::get_saves(); 51 | } 52 | 53 | if ui.button("Back").clicked() { 54 | game_state.set(GameState::MenuMain).unwrap(); 55 | } 56 | }); 57 | }); 58 | } 59 | -------------------------------------------------------------------------------- /src/plugins/objects/components/objects/flax.rs: -------------------------------------------------------------------------------- 1 | use crate::plugins::{ 2 | inspector::components::InspectorDisabled, 3 | loading::resources::{GameAssets, PhysicsObject}, 4 | objects::components::{ 5 | items::flax_item::FlaxItem, GameWorldObject, GameWorldObjectTrait, 6 | ObjectDeserializationError, 7 | }, 8 | }; 9 | use bevy::prelude::*; 10 | use bevy_reflect::{FromReflect, Reflect}; 11 | 12 | #[derive(Debug, Clone, Default, Reflect, FromReflect)] 13 | pub struct FlaxObject; 14 | 15 | impl FlaxObject { 16 | const ID: &str = "flax"; 17 | } 18 | 19 | impl GameWorldObjectTrait for FlaxObject { 20 | fn id(&self) -> &'static str { 21 | Self::ID 22 | } 23 | 24 | fn take(&mut self) -> Box { 25 | Box::new(std::mem::take(self)) 26 | } 27 | 28 | fn get_clone(&self) -> Box { 29 | Box::new(self.clone()) 30 | } 31 | 32 | fn deserialize( 33 | &self, 34 | _data: &[u8], 35 | ) -> Result, ObjectDeserializationError> { 36 | #[allow(clippy::box_default)] 37 | Ok(Box::new(Self::default())) 38 | } 39 | 40 | fn on_use( 41 | &mut self, 42 | commands: &mut Commands, 43 | _assets: &GameAssets, 44 | self_entity: Entity, 45 | transform: Transform, 46 | _hand_item: &mut Option<(Entity, Mut)>, 47 | ) -> bool { 48 | commands.spawn(( 49 | FlaxItem.to_spawner(transform.with_translation(transform.translation + Vec3::Y * 0.1)), 50 | Name::new("flax_harvest_result"), 51 | InspectorDisabled, 52 | )); 53 | 54 | commands.entity(self_entity).despawn_recursive(); 55 | 56 | true 57 | } 58 | 59 | fn get_tooltip(&self, _hand_item: Option<&GameWorldObject>) -> String { 60 | "Flax (press E to harvest)".into() 61 | } 62 | 63 | fn is_solid(&self) -> bool { 64 | false 65 | } 66 | 67 | fn get_model<'a>(&self, assets: &'a GameAssets) -> &'a PhysicsObject { 68 | &assets.flax_object 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/plugins/player/systems/look.rs: -------------------------------------------------------------------------------- 1 | use crate::plugins::player::{ 2 | components::{PlayerCameraComponent, PlayerComponent}, 3 | resources::PlayerStats, 4 | }; 5 | use bevy::{ 6 | ecs::event::ManualEventReader, input::mouse::MouseMotion, prelude::*, window::CursorGrabMode, 7 | }; 8 | 9 | #[derive(Default)] 10 | pub struct InputState { 11 | reader_motion: ManualEventReader, 12 | pitch: f32, 13 | yaw: f32, 14 | } 15 | 16 | pub fn player_look( 17 | settings: Res, 18 | windows: Res, 19 | mut delta_state: Local, 20 | motion: Res>, 21 | mut player_q: Query<&mut Transform, With>, 22 | mut camera_q: Query<&mut Transform, (With, Without)>, 23 | ) { 24 | if let Some(window) = windows.get_primary() { 25 | let mut player_transform = player_q.single_mut(); 26 | let mut camera_transform = camera_q.single_mut(); 27 | 28 | let mut pitch = delta_state.pitch; 29 | let mut yaw = delta_state.yaw; 30 | 31 | for ev in delta_state.reader_motion.iter(&motion) { 32 | match window.cursor_grab_mode() { 33 | CursorGrabMode::None => (), 34 | _ => { 35 | // Using smallest of height or width ensures equal vertical and horizontal sensitivity 36 | let window_scale = window.height().min(window.width()); 37 | pitch -= (settings.sensitivity * ev.delta.y * window_scale).to_radians(); 38 | yaw -= (settings.sensitivity * ev.delta.x * window_scale).to_radians(); 39 | } 40 | } 41 | 42 | pitch = pitch.clamp(-1.54, 1.54); 43 | 44 | // Order is important to prevent unintended roll 45 | player_transform.rotation = Quat::from_axis_angle(Vec3::Y, yaw); 46 | camera_transform.rotation = Quat::from_axis_angle(Vec3::X, pitch); 47 | } 48 | 49 | delta_state.pitch = pitch; 50 | delta_state.yaw = yaw; 51 | } else { 52 | warn!("Primary window not found for `player_look`!"); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/plugins/main_menu/systems/new_game.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | plugins::{ 3 | game_world::resources::meta::GameWorldMeta, 4 | world_generator::resources::{WorldGenerator, WorldSeed}, 5 | }, 6 | states::game_state::GameState, 7 | }; 8 | use bevy::prelude::*; 9 | use bevy_egui::{egui, EguiContext}; 10 | use std::hash::{Hash, Hasher}; 11 | 12 | pub fn init_new_game(mut game_world_meta: ResMut) { 13 | game_world_meta.reset(); 14 | } 15 | 16 | pub struct NewGameLocalState { 17 | pub seed: String, 18 | } 19 | 20 | impl Default for NewGameLocalState { 21 | fn default() -> Self { 22 | Self { 23 | seed: rand::random::().to_string(), 24 | } 25 | } 26 | } 27 | 28 | pub fn new_game_system( 29 | mut game_state: ResMut>, 30 | mut generator: ResMut, 31 | mut game_world_meta: ResMut, 32 | mut egui_context: ResMut, 33 | mut local_state: Local, 34 | ) { 35 | egui::Window::new("New world") 36 | .collapsible(false) 37 | .show(egui_context.ctx_mut(), |ui| { 38 | ui.horizontal(|ui| { 39 | ui.label("Name: "); 40 | ui.text_edit_singleline(&mut game_world_meta.name); 41 | }); 42 | ui.horizontal(|ui| { 43 | ui.label("Seed: "); 44 | ui.text_edit_singleline(&mut local_state.seed); 45 | 46 | if ui.button("random").clicked() { 47 | local_state.seed = rand::random::().to_string(); 48 | } 49 | 50 | let seed = local_state.seed.parse().unwrap_or_else(|_| { 51 | let mut hasher = std::collections::hash_map::DefaultHasher::new(); 52 | local_state.seed.hash(&mut hasher); 53 | hasher.finish() 54 | }); 55 | 56 | game_world_meta.seed = seed as WorldSeed; 57 | generator.set_seed(game_world_meta.seed); 58 | }); 59 | 60 | if ui.button("Generate world").clicked() { 61 | game_state.set(GameState::WorldCreating).unwrap(); 62 | } 63 | }); 64 | } 65 | -------------------------------------------------------------------------------- /src/plugins/chunks/helpers/update_objects_parent.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | internal::chunks::pointer::ChunkPointer, plugins::objects::components::GameWorldObject, 3 | }; 4 | use bevy::prelude::*; 5 | use bevy_rapier3d::prelude::RigidBodyDisabled; 6 | 7 | #[derive(Debug, Clone, Copy)] 8 | pub struct FailedToSpawnError { 9 | pub object_translation: Vec3, 10 | } 11 | 12 | pub fn update_objects_parent( 13 | prev_chunk_children: &Children, 14 | commands: &mut Commands, 15 | chunks: Vec<(ChunkPointer, Entity)>, 16 | objects_q: &mut Query<(Entity, &mut Transform, &GlobalTransform), With>, 17 | ) -> Result<(), FailedToSpawnError> { 18 | for child in prev_chunk_children.iter() { 19 | if let Ok((entity, mut transform, global)) = objects_q.get_mut(*child) { 20 | let global = global.translation(); 21 | let mut spawned = false; 22 | for (chunk, chunk_entity) in chunks.iter() { 23 | let chunk_translation = chunk.get_translation(); 24 | let relative_pos = global - chunk_translation; 25 | let chunk_size = chunk.get_size(); 26 | 27 | if relative_pos.x < 0.0 28 | || relative_pos.y < 0.0 29 | || relative_pos.z < 0.0 30 | || relative_pos.x > chunk_size 31 | || relative_pos.y > chunk_size 32 | || relative_pos.z > chunk_size 33 | { 34 | continue; 35 | } 36 | 37 | transform.translation = relative_pos; 38 | 39 | let mut obj_commands = commands.entity(entity); 40 | obj_commands.set_parent(*chunk_entity); 41 | 42 | if chunk.is_real() { 43 | obj_commands.remove::(); 44 | } else { 45 | obj_commands.insert(RigidBodyDisabled); 46 | } 47 | spawned = true; 48 | break; 49 | } 50 | 51 | if !spawned { 52 | return Err(FailedToSpawnError { 53 | object_translation: global, 54 | }); 55 | } 56 | } 57 | } 58 | 59 | Ok(()) 60 | } 61 | -------------------------------------------------------------------------------- /src/plugins/inspector/systems/inspector/mod.rs: -------------------------------------------------------------------------------- 1 | use self::{ 2 | assets::assets_inspector, entities::entities_inspector, profiling::profiling_inspector, 3 | resources::resources_inspector, 4 | }; 5 | use crate::plugins::inspector::resources::InspectorOpen; 6 | use bevy::prelude::*; 7 | use bevy_egui::{egui, EguiContext}; 8 | 9 | mod assets; 10 | mod entities; 11 | mod profiling; 12 | mod resources; 13 | 14 | #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] 15 | pub enum InspectorTabOpen { 16 | #[default] 17 | Entities, 18 | Assets, 19 | Resources, 20 | Profiling, 21 | } 22 | 23 | #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Resource)] 24 | pub struct InspectorState { 25 | pub tab_open: InspectorTabOpen, 26 | } 27 | 28 | pub fn inspector_ui_system(world: &mut World) { 29 | let is_open = world.resource::(); 30 | 31 | if !is_open.0 { 32 | return; 33 | } 34 | 35 | let mut state = world 36 | .remove_resource::() 37 | .unwrap_or_default(); 38 | 39 | let egui_context = world.resource_mut::().ctx_mut().clone(); 40 | 41 | egui::Window::new("Inspector (press tab to close)") 42 | .resizable(true) 43 | .show(&egui_context, |ui| { 44 | ui.horizontal(|ui| { 45 | ui.selectable_value(&mut state.tab_open, InspectorTabOpen::Entities, "entities"); 46 | ui.selectable_value(&mut state.tab_open, InspectorTabOpen::Assets, "assets"); 47 | ui.selectable_value( 48 | &mut state.tab_open, 49 | InspectorTabOpen::Resources, 50 | "resources", 51 | ); 52 | ui.selectable_value( 53 | &mut state.tab_open, 54 | InspectorTabOpen::Profiling, 55 | "profiling", 56 | ); 57 | }); 58 | 59 | match state.tab_open { 60 | InspectorTabOpen::Entities => entities_inspector(world, ui), 61 | InspectorTabOpen::Assets => assets_inspector(world, ui), 62 | InspectorTabOpen::Resources => resources_inspector(world, ui), 63 | InspectorTabOpen::Profiling => profiling_inspector(world, ui), 64 | } 65 | }); 66 | 67 | world.insert_resource(state); 68 | } 69 | -------------------------------------------------------------------------------- /src/plugins/objects/components/object_spawner.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | use bevy_rapier3d::prelude::RigidBodyDisabled; 3 | 4 | use crate::{internal::chunks::pointer::ChunkPointer, plugins::loading::resources::GameAssets}; 5 | 6 | use super::{items::grab_item, GameWorldObjectTrait}; 7 | 8 | #[derive(Component, Debug)] 9 | pub struct GameWorldObjectSpawn(pub Box); 10 | 11 | /// The component that is used to spawn objects in the world. 12 | /// 13 | /// Spawn system will try to find the chunk that the object is in and spawn it there. 14 | #[derive(Component, Debug)] 15 | pub struct ObjectSpawner { 16 | pub id: String, 17 | pub object: Option>, 18 | pub transform: Transform, 19 | } 20 | 21 | impl ObjectSpawner { 22 | pub fn id(&self) -> String { 23 | self.id.clone() 24 | } 25 | 26 | /// Check if object is already spawned 27 | pub fn is_spawned(&self) -> bool { 28 | self.object.is_none() 29 | } 30 | 31 | /// Spawn object to players hand 32 | pub fn spawn_to_hand(&mut self, commands: &mut Commands, assets: &GameAssets, hand: Entity) { 33 | if let Some(mut object) = std::mem::replace(&mut self.object, None) { 34 | let object = object.spawn(commands, assets, self.transform); 35 | 36 | grab_item(object, hand); 37 | } else { 38 | panic!("Object is already spawned"); 39 | } 40 | } 41 | 42 | /// Try to spawn object in the world 43 | /// 44 | /// If object is already spawned, return None 45 | pub fn spawn( 46 | &mut self, 47 | commands: &mut Commands, 48 | assets: &GameAssets, 49 | chunk: &ChunkPointer, 50 | chunk_entity: Entity, 51 | ) -> Option { 52 | if let Some(mut object) = std::mem::replace(&mut self.object, None) { 53 | let chunk_offset = chunk.get_translation(); 54 | 55 | let mut transform = self.transform; 56 | transform.translation -= chunk_offset; 57 | 58 | let mut object = object.spawn(commands, assets, transform); 59 | object.set_parent(chunk_entity); 60 | 61 | if !chunk.is_real() { 62 | object.insert(RigidBodyDisabled); 63 | } 64 | 65 | Some(object.id()) 66 | } else { 67 | None 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/plugins/craft/resources/crafts_registry/mod.rs: -------------------------------------------------------------------------------- 1 | use self::{craft::CraftEntry, crafts::simple::SimpleCraft}; 2 | use crate::{ 3 | plugins::{ 4 | loading::resources::GameAssets, 5 | objects::components::{ 6 | items::{ 7 | branch::BranchItem, coarse_string::CoarseStringItem, flax_item::FlaxItem, 8 | rock::RockItem, stone_axe::StoneAxeItem, 9 | }, 10 | GameWorldObject, 11 | }, 12 | }, 13 | simple_craft, 14 | }; 15 | use bevy::{prelude::*, utils::HashMap}; 16 | 17 | pub mod craft; 18 | pub mod crafts; 19 | 20 | #[derive(Debug, Default, Resource, Reflect, FromReflect)] 21 | #[reflect(Resource)] 22 | pub struct CraftsRegistry { 23 | crafts: HashMap, 24 | } 25 | 26 | pub const COARSE_STRING_CRAFT_ID: &str = "coarse_string"; 27 | pub const FIRE_CRAFT_ID: &str = "fire"; 28 | pub const STONE_AXE_CRAFT_ID: &str = "stone_axe"; 29 | 30 | impl CraftsRegistry { 31 | pub fn new() -> Self { 32 | let mut result = Self { 33 | crafts: HashMap::new(), 34 | }; 35 | 36 | result.register(simple_craft!( 37 | COARSE_STRING_CRAFT_ID, 38 | CoarseStringItem, 39 | (FlaxItem, 2) 40 | )); 41 | 42 | result.register(simple_craft!( 43 | FIRE_CRAFT_ID, 44 | CoarseStringItem, 45 | (BranchItem, 5) 46 | )); 47 | 48 | result.register(simple_craft!( 49 | STONE_AXE_CRAFT_ID, 50 | StoneAxeItem, 51 | (BranchItem, 1), 52 | (RockItem, 1), 53 | (CoarseStringItem, 2) 54 | )); 55 | 56 | result 57 | } 58 | 59 | pub fn register(&mut self, craft: CraftEntry) { 60 | let id = craft.id().to_string(); 61 | self.crafts.insert(id, craft); 62 | } 63 | 64 | pub fn try_craft( 65 | &self, 66 | commands: &mut Commands, 67 | assets: &GameAssets, 68 | craft_center: Vec3, 69 | hand_item: &mut Option<(Entity, Mut)>, 70 | items: &mut [(Entity, Mut)], 71 | ) -> bool { 72 | for (_, craft) in self.crafts.iter() { 73 | if craft.craft(commands, assets, craft_center, hand_item, items) { 74 | return true; 75 | } 76 | } 77 | 78 | false 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/plugins/objects/resources/objects_registry.rs: -------------------------------------------------------------------------------- 1 | use crate::plugins::objects::components::{ 2 | items::{ 3 | branch::BranchItem, coarse_string::CoarseStringItem, flax_item::FlaxItem, log::LogItem, 4 | rock::RockItem, stone_axe::StoneAxeItem, wooden_shovel::WoodenShovelItem, 5 | }, 6 | objects::{ 7 | cactus::CactusObject, fire::FireObject, flax::FlaxObject, spruce::SpruceObject, 8 | stump::StumpObject, tree::TreeObject, 9 | }, 10 | GameWorldObjectTrait, 11 | }; 12 | use bevy::{prelude::*, utils::HashMap}; 13 | use std::sync::Arc; 14 | 15 | #[derive(Debug, Default, Clone, Reflect, FromReflect)] 16 | pub struct ObjectRegistryEntry { 17 | #[reflect(ignore)] 18 | object: Option>, 19 | } 20 | 21 | #[derive(Resource, Default, Debug, Clone, Reflect, FromReflect)] 22 | #[reflect(Resource)] 23 | pub struct ObjectsRegistry { 24 | objects: HashMap, 25 | } 26 | 27 | impl ObjectsRegistry { 28 | pub fn new() -> Self { 29 | let mut result = Self { 30 | objects: HashMap::new(), 31 | }; 32 | 33 | result.register(TreeObject::default()); 34 | result.register(FireObject::default()); 35 | result.register(SpruceObject::default()); 36 | result.register(CactusObject::default()); 37 | result.register(FlaxObject::default()); 38 | result.register(StumpObject::default()); 39 | 40 | result.register(FlaxItem::default()); 41 | result.register(RockItem::default()); 42 | result.register(BranchItem::default()); 43 | result.register(StoneAxeItem::default()); 44 | result.register(CoarseStringItem::default()); 45 | result.register(LogItem::default()); 46 | result.register(WoodenShovelItem::default()); 47 | 48 | result 49 | } 50 | 51 | pub fn register(&mut self, object: impl GameWorldObjectTrait) { 52 | self.objects.insert( 53 | object.id().to_string(), 54 | ObjectRegistryEntry { 55 | object: Some(Arc::new(object)), 56 | }, 57 | ); 58 | } 59 | 60 | pub fn deserialize_object( 61 | &self, 62 | id: &str, 63 | data: &[u8], 64 | ) -> Option> { 65 | let object = self.objects.get(id)?.object.clone()?; 66 | 67 | Some(object.deserialize(data).unwrap()) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/plugins/tooltip/resources/mod.rs: -------------------------------------------------------------------------------- 1 | use super::{events::UpsertTooltipEvent, utils::TooltipId}; 2 | use bevy::{prelude::*, utils::HashMap}; 3 | use std::time::Duration; 4 | 5 | #[derive(Debug, Default, Resource, Reflect, FromReflect)] 6 | #[reflect(Resource)] 7 | pub struct Tooltips { 8 | tooltips: HashMap, 9 | } 10 | 11 | pub enum UpsertTooltipResult<'a> { 12 | Added(&'a mut TooltipEntry), 13 | Updated(&'a mut TooltipEntry), 14 | NoChange, 15 | } 16 | 17 | impl Tooltips { 18 | pub fn upsert<'a>(&'a mut self, tooltip: UpsertTooltipEvent) -> UpsertTooltipResult<'a> { 19 | let UpsertTooltipEvent { 20 | id, 21 | tooltip_type, 22 | text, 23 | parent, 24 | position, 25 | } = tooltip; 26 | 27 | let mut existed = true; 28 | 29 | let entry = self.tooltips.entry(id).or_insert_with(|| { 30 | existed = false; 31 | TooltipEntry { 32 | text: text.clone(), 33 | tooltip_type: tooltip_type.clone(), 34 | parent, 35 | position, 36 | in_world_entity: None, 37 | ui_entity: None, 38 | } 39 | }); 40 | 41 | if !existed { 42 | return UpsertTooltipResult::Added(entry); 43 | } 44 | 45 | if entry.text != text 46 | || entry.tooltip_type != tooltip_type 47 | || parent != entry.parent 48 | || position != entry.position 49 | { 50 | entry.tooltip_type = tooltip_type; 51 | entry.text = text; 52 | entry.parent = parent; 53 | entry.position = position; 54 | 55 | return UpsertTooltipResult::Updated(entry); 56 | } 57 | 58 | UpsertTooltipResult::NoChange 59 | } 60 | 61 | pub fn iter(&self) -> impl Iterator { 62 | self.tooltips.iter() 63 | } 64 | } 65 | 66 | #[derive(Debug, Clone, Default, Reflect, FromReflect)] 67 | pub struct TooltipEntry { 68 | pub text: String, 69 | pub tooltip_type: TooltipType, 70 | pub parent: Option, 71 | pub position: Vec3, 72 | pub in_world_entity: Option, 73 | pub ui_entity: Option, 74 | } 75 | 76 | #[derive(Debug, Clone, Reflect, PartialEq, Eq, FromReflect, Default)] 77 | pub enum TooltipType { 78 | #[default] 79 | Constant, 80 | Fadeout(Duration), 81 | Disabled, 82 | } 83 | -------------------------------------------------------------------------------- /src/internal/direction/mod.rs: -------------------------------------------------------------------------------- 1 | use super::pos::Pos; 2 | use bevy_reflect::Reflect; 3 | use strum_macros::EnumIter; 4 | 5 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EnumIter)] 6 | #[repr(usize)] 7 | pub enum Direction { 8 | /// +y 9 | UP, 10 | /// -y 11 | DOWN, 12 | /// -x 13 | WEST, 14 | /// +x 15 | EAST, 16 | /// -z 17 | NORTH, 18 | /// +z 19 | SOUTH, 20 | } 21 | 22 | impl Direction { 23 | pub const COUNT: usize = 6; 24 | pub const X: Self = Direction::EAST; 25 | pub const Y: Self = Direction::UP; 26 | pub const Z: Self = Direction::SOUTH; 27 | pub const X_NEG: Self = Direction::WEST; 28 | pub const Y_NEG: Self = Direction::DOWN; 29 | pub const Z_NEG: Self = Direction::NORTH; 30 | 31 | pub fn opposite(&self) -> Self { 32 | match self { 33 | Direction::UP => Direction::DOWN, 34 | Direction::DOWN => Direction::UP, 35 | Direction::WEST => Direction::EAST, 36 | Direction::EAST => Direction::WEST, 37 | Direction::NORTH => Direction::SOUTH, 38 | Direction::SOUTH => Direction::NORTH, 39 | } 40 | } 41 | 42 | pub fn iter_map T>(mut f: F) -> [T; Self::COUNT] { 43 | [ 44 | f(Direction::UP), 45 | f(Direction::DOWN), 46 | f(Direction::WEST), 47 | f(Direction::EAST), 48 | f(Direction::NORTH), 49 | f(Direction::SOUTH), 50 | ] 51 | } 52 | } 53 | 54 | impl + Reflect + Copy + Clone> From for Pos { 55 | fn from(val: Direction) -> Self { 56 | match val { 57 | Direction::UP => (0, 1, 0).into(), 58 | Direction::DOWN => (0, -1, 0).into(), 59 | Direction::WEST => (-1, 0, 0).into(), 60 | Direction::EAST => (1, 0, 0).into(), 61 | Direction::NORTH => (0, 0, -1).into(), 62 | Direction::SOUTH => (0, 0, 1).into(), 63 | } 64 | } 65 | } 66 | 67 | impl TryFrom for Direction { 68 | type Error = (); 69 | 70 | fn try_from(value: usize) -> Result { 71 | match value { 72 | 0 => Ok(Direction::UP), 73 | 1 => Ok(Direction::DOWN), 74 | 2 => Ok(Direction::WEST), 75 | 3 => Ok(Direction::EAST), 76 | 4 => Ok(Direction::NORTH), 77 | 5 => Ok(Direction::SOUTH), 78 | _ => Err(()), 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/plugins/world_generator/internal/biomes/desert.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::{ 3 | internal::{pos::ChunkPos, voxel::voxel_types::VoxelId}, 4 | plugins::{ 5 | objects::components::{ 6 | items::rock::RockItem, objects::cactus::CactusObject, GameWorldObjectTrait, 7 | }, 8 | world_generator::resources::{GenCaveInp, GenVoxelInp, LandscapeHeightInp, WorldGenerator}, 9 | }, 10 | }; 11 | use std::sync::Arc; 12 | 13 | #[derive(Debug, Clone)] 14 | pub struct DesertBiome; 15 | 16 | impl DesertBiome { 17 | pub const ID: BiomeID = "desert"; 18 | 19 | pub fn new() -> Arc { 20 | Arc::new(Self) 21 | } 22 | } 23 | 24 | impl Biome for DesertBiome { 25 | fn get_id(&self) -> BiomeID { 26 | Self::ID 27 | } 28 | 29 | fn get_generate_voxel_inp(&self, _gen: &WorldGenerator, _pos: ChunkPos) -> GenVoxelInp { 30 | GenVoxelInp { 31 | cave_inp: GenCaveInp { 32 | cave_factor: 1.3, 33 | cave_offset: 0.3, 34 | cave_strength: 0.0, 35 | }, 36 | bumps_factor: 0.1, 37 | first_layer_id: VoxelId::SAND, 38 | second_layer_id: VoxelId::SAND_STONE, 39 | rest_layers_id: VoxelId::STONE, 40 | } 41 | } 42 | 43 | fn get_landscape_height_inp( 44 | &self, 45 | _gen: &WorldGenerator, 46 | _pos: ChunkPos, 47 | ) -> LandscapeHeightInp { 48 | LandscapeHeightInp { height: 10.0 } 49 | } 50 | 51 | fn check_pos(&self, _gen: &WorldGenerator, _pos: ChunkPos, inp: BiomeCheckInput) -> bool { 52 | inp.temperature > 30.0 53 | } 54 | 55 | fn spawn_objects( 56 | &self, 57 | biomes: &ChunkBiomes, 58 | chunk_pos: ChunkPos, 59 | commands: &mut Commands, 60 | gen: &WorldGenerator, 61 | ) -> usize { 62 | spawn_objects( 63 | biomes, 64 | chunk_pos, 65 | commands, 66 | gen, 67 | vec![ 68 | SpawnObjectInp { 69 | allow_air: false, 70 | amount: 1, 71 | chance: 0.0125, 72 | get_spawner: Box::new(|t| CactusObject.to_spawner(t)), 73 | offset: Vec3::ZERO, 74 | }, 75 | SpawnObjectInp { 76 | allow_air: false, 77 | amount: 1, 78 | chance: 0.25, 79 | get_spawner: Box::new(|t| RockItem.to_spawner(t)), 80 | offset: Vec3::Y * 0.1, 81 | }, 82 | ], 83 | ) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/plugins/objects/systems/user_grab.rs: -------------------------------------------------------------------------------- 1 | use crate::plugins::{ 2 | game_world::resources::GameWorld, 3 | loading::resources::GameAssets, 4 | objects::components::{ 5 | items::{drop_item, grab_item, ItemComponent, ItemGrabbed}, 6 | GameWorldObject, 7 | }, 8 | player::{components::PlayerHand, events::UseGrabPlaceEvent, resources::look_at::PlayerLookAt}, 9 | }; 10 | use bevy::prelude::*; 11 | use bevy_rapier3d::prelude::*; 12 | 13 | #[allow(clippy::too_many_arguments)] 14 | pub fn use_grab_system( 15 | mut use_place_grab_e: EventReader, 16 | player_hand_q: Query<(Entity, &GlobalTransform), With>, 17 | mut commands: Commands, 18 | mut item_grabbed_q: Query<(Entity, &mut GameWorldObject), With>, 19 | item_q: Query, Without)>, 20 | mut object_q: Query<(&mut GameWorldObject, &GlobalTransform), Without>, 21 | colliders_q: Query<&Parent, With>, 22 | mut world: ResMut, 23 | look_at: Res, 24 | assets: Res, 25 | ) { 26 | for _ in use_place_grab_e.iter() { 27 | let look_at = look_at 28 | .target 29 | .and_then(|entity| match colliders_q.get(entity) { 30 | Ok(parent) => Some(parent.get()), 31 | Err(_) => None, 32 | }); 33 | 34 | let mut hand_item = item_grabbed_q.iter_mut().next(); 35 | 36 | // try to use object 37 | if let Some((obj, obj_entity)) = 38 | look_at.and_then(|entity| Some((object_q.get_mut(entity).ok()?, entity))) 39 | { 40 | let (mut obj, transform) = obj; 41 | 42 | if obj.0.on_use( 43 | &mut commands, 44 | &assets, 45 | obj_entity, 46 | transform.compute_transform(), 47 | &mut hand_item, 48 | ) { 49 | continue; 50 | } 51 | } 52 | 53 | // try to drop item from hand 54 | if let Some((item_entity, _)) = hand_item { 55 | let (_, transform) = player_hand_q.single(); 56 | 57 | drop_item( 58 | commands.entity(item_entity), 59 | transform.compute_transform(), 60 | &mut world, 61 | ); 62 | } 63 | 64 | // try to grab item 65 | if let Some(entity) = look_at { 66 | if let Ok(item) = item_q.get(entity) { 67 | let (hand, _) = player_hand_q.single(); 68 | grab_item(commands.entity(item), hand); 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/plugins/world_generator/internal/biomes/tundra.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::{ 3 | internal::{pos::ChunkPos, voxel::voxel_types::VoxelId}, 4 | plugins::{ 5 | objects::components::{ 6 | items::branch::BranchItem, objects::spruce::SpruceObject, GameWorldObjectTrait, 7 | }, 8 | world_generator::resources::{GenCaveInp, GenVoxelInp, LandscapeHeightInp, WorldGenerator}, 9 | }, 10 | }; 11 | use std::sync::Arc; 12 | 13 | #[derive(Debug, Clone)] 14 | pub struct TundraBiome; 15 | 16 | impl TundraBiome { 17 | pub const ID: BiomeID = "tundra"; 18 | 19 | pub fn new() -> Arc { 20 | Arc::new(Self) 21 | } 22 | } 23 | 24 | impl Biome for TundraBiome { 25 | fn get_id(&self) -> BiomeID { 26 | Self::ID 27 | } 28 | 29 | fn get_generate_voxel_inp(&self, _gen: &WorldGenerator, _pos: ChunkPos) -> GenVoxelInp { 30 | GenVoxelInp { 31 | cave_inp: GenCaveInp { 32 | cave_factor: 1.3, 33 | cave_offset: 0.3, 34 | cave_strength: 100.0, 35 | }, 36 | bumps_factor: 0.1, 37 | first_layer_id: VoxelId::SNOW, 38 | second_layer_id: VoxelId::DIRT, 39 | rest_layers_id: VoxelId::STONE, 40 | } 41 | } 42 | 43 | fn get_landscape_height_inp( 44 | &self, 45 | _gen: &WorldGenerator, 46 | _pos: ChunkPos, 47 | ) -> LandscapeHeightInp { 48 | LandscapeHeightInp { height: 10.0 } 49 | } 50 | 51 | fn check_pos(&self, _gen: &WorldGenerator, _pos: ChunkPos, inp: BiomeCheckInput) -> bool { 52 | inp.temperature < 0.0 53 | } 54 | 55 | fn spawn_objects( 56 | &self, 57 | biomes: &ChunkBiomes, 58 | chunk_pos: ChunkPos, 59 | commands: &mut Commands, 60 | gen: &WorldGenerator, 61 | ) -> usize { 62 | spawn_objects( 63 | biomes, 64 | chunk_pos, 65 | commands, 66 | gen, 67 | vec![ 68 | SpawnObjectInp { 69 | allow_air: false, 70 | amount: 1, 71 | chance: 0.05, 72 | get_spawner: Box::new(|t| SpruceObject::WITH_SNOW.clone().to_spawner(t)), 73 | offset: Vec3::ZERO, 74 | }, 75 | SpawnObjectInp { 76 | allow_air: false, 77 | amount: 1, 78 | chance: 0.075, 79 | get_spawner: Box::new(|t| BranchItem.to_spawner(t)), 80 | offset: Vec3::Y * 0.1, 81 | }, 82 | ], 83 | ) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/plugins/player/mod.rs: -------------------------------------------------------------------------------- 1 | use self::components::{PlayerComponent, PlayerHand}; 2 | use self::events::*; 3 | use self::resources::look_at::PlayerLookAt; 4 | use self::resources::{input_settings::PlayerInputSettings, PlayerStats}; 5 | use self::systems::look_at::look_at_system; 6 | use self::systems::{cursor::*, input::*, look::*, movements::*, spawn_item::*, *}; 7 | use crate::states::game_state::GameState; 8 | use bevy::prelude::*; 9 | 10 | use super::tooltip::systems::upsert::handle_upsert_tooltip_system; 11 | 12 | pub mod components; 13 | pub mod events; 14 | pub mod resources; 15 | mod systems; 16 | 17 | /// modified version of [bevy_flycam](https://github.com/sburris0/bevy_flycam 18 | pub struct PlayerPlugin; 19 | impl Plugin for PlayerPlugin { 20 | fn build(&self, app: &mut App) { 21 | app.add_event::() 22 | .add_event::() 23 | .add_event::() 24 | .add_event::() 25 | .add_event::() 26 | .add_event::() 27 | .add_event::() 28 | .add_event::() 29 | .add_event::() 30 | .add_event::() 31 | .add_event::() 32 | .add_event::() 33 | .add_event::() 34 | .add_event::() 35 | .register_type::() 36 | .register_type::() 37 | .register_type::() 38 | .register_type::() 39 | .insert_resource(PlayerStats::default()) 40 | .insert_resource(PlayerLookAt::default()) 41 | .insert_resource(PlayerInputSettings::default()) 42 | .add_startup_system(setup_player_system) 43 | .add_system_set( 44 | SystemSet::on_update(GameState::InGame) 45 | .with_system(player_fly_movement) 46 | .with_system(player_walk_movement) 47 | .with_system(toggle_movement_mode) 48 | .with_system(player_look) 49 | .with_system(look_at_system.before(handle_upsert_tooltip_system)) 50 | .with_system(process_input_1) 51 | .with_system(process_input_2) 52 | .with_system(spawn_item) 53 | .with_system(cursor_toggle), 54 | ) 55 | .add_system_set(SystemSet::on_enter(GameState::InGame).with_system(cursor_grab)) 56 | .add_system_set(SystemSet::on_exit(GameState::InGame).with_system(cursor_release)); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/plugins/player/systems/look_at.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | use bevy_rapier3d::prelude::*; 3 | 4 | use crate::plugins::{ 5 | objects::components::{items::ItemGrabbed, GameWorldObject, GameWorldObjectTrait}, 6 | player::{ 7 | components::{PlayerCameraComponent, PlayerComponent}, 8 | resources::{look_at::PlayerLookAt, PLAYER_ACCESS_RADIUS}, 9 | }, 10 | tooltip::{events::UpsertTooltipEvent, resources::TooltipType}, 11 | }; 12 | 13 | fn draw_tooltip( 14 | mut tooltip_ew: EventWriter, 15 | position: Vec3, 16 | object: &Box, 17 | hand_item: Option<&GameWorldObject>, 18 | ) { 19 | tooltip_ew.send(UpsertTooltipEvent { 20 | id: "loot_at".into(), 21 | text: object.get_tooltip(hand_item), 22 | position, 23 | ..default() 24 | }); 25 | } 26 | 27 | fn disable_tooltip(mut tooltip_ew: EventWriter) { 28 | tooltip_ew.send(UpsertTooltipEvent { 29 | id: "loot_at".into(), 30 | tooltip_type: TooltipType::Disabled, 31 | ..default() 32 | }); 33 | } 34 | 35 | pub fn look_at_system( 36 | mut look_at: ResMut, 37 | rapier_context: Res, 38 | player_q: Query>, 39 | player_camera_q: Query<&GlobalTransform, With>, 40 | tooltip_ew: EventWriter, 41 | parent_q: Query<&Parent>, 42 | object_q: Query<&GameWorldObject>, 43 | hand_item_q: Query<&GameWorldObject, With>, 44 | ) { 45 | let player = player_q.single(); 46 | let cam = player_camera_q.single(); 47 | 48 | let ray_origin = cam.translation(); 49 | let dir = cam.forward(); 50 | 51 | if let Some((entity, far)) = rapier_context.cast_ray( 52 | ray_origin, 53 | dir, 54 | PLAYER_ACCESS_RADIUS, 55 | false, 56 | QueryFilter::default().exclude_collider(player), 57 | ) { 58 | look_at.target = Some(entity); 59 | look_at.distance = far; 60 | look_at.origin = ray_origin; 61 | look_at.dir = dir; 62 | look_at.position = ray_origin + dir * far; 63 | 64 | if let Some(object) = parent_q 65 | .get(entity) 66 | .ok() 67 | .and_then(|parent| object_q.get(parent.get()).ok()) 68 | { 69 | let hand_item = hand_item_q.iter().next(); 70 | draw_tooltip(tooltip_ew, look_at.position, &object.0, hand_item); 71 | } else { 72 | disable_tooltip(tooltip_ew); 73 | } 74 | } else { 75 | disable_tooltip(tooltip_ew); 76 | 77 | look_at.target = None; 78 | look_at.distance = 0.0; 79 | look_at.origin = ray_origin; 80 | look_at.dir = dir; 81 | look_at.position = ray_origin; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/plugins/player/components/save.rs: -------------------------------------------------------------------------------- 1 | use crate::plugins::{ 2 | loading::resources::GameAssets, 3 | objects::{ 4 | components::GameWorldObject, resources::objects_registry::ObjectsRegistry, 5 | utils::object_save::GameWorldObjectSave, 6 | }, 7 | player::{components::PlayerComponent, resources::PlayerStats, systems::HEAD_LEVEL}, 8 | }; 9 | use bevy::prelude::*; 10 | use serde::{Deserialize, Serialize}; 11 | 12 | #[derive(Component, Default, Debug, Serialize, Deserialize)] 13 | pub struct PlayerSave { 14 | stats: PlayerStats, 15 | /// (x, y, z) 16 | pos: (f32, f32, f32), 17 | 18 | /// (pitch, yaw) 19 | rot: (f32, f32), 20 | 21 | /// (x, y, z) 22 | vel: (f32, f32, f32), 23 | 24 | hand_item: Option, 25 | } 26 | 27 | impl PlayerSave { 28 | pub fn new( 29 | stats: &PlayerStats, 30 | player: &PlayerComponent, 31 | head_transform: &GlobalTransform, 32 | hand_item: Option<(&GameWorldObject, &Transform)>, 33 | ) -> Self { 34 | let vel = player.velocity; 35 | 36 | let head_transform = head_transform.compute_transform(); 37 | let player_pos = head_transform.translation + Vec3::Y * HEAD_LEVEL; 38 | 39 | let player_rot: Quat = head_transform.rotation; 40 | 41 | let player_rot = player_rot.to_euler(EulerRot::YXZ); 42 | let rot = (player_rot.0, player_rot.1); 43 | 44 | let hand_item = 45 | hand_item.map(|(item, transform)| GameWorldObjectSave::new(item, *transform)); 46 | 47 | Self { 48 | stats: stats.clone(), 49 | vel: vel.into(), 50 | pos: player_pos.into(), 51 | hand_item, 52 | rot, 53 | } 54 | } 55 | 56 | pub fn apply_to_player( 57 | self, 58 | registry: &ObjectsRegistry, 59 | assets: &GameAssets, 60 | commands: &mut Commands, 61 | hand: Entity, 62 | mut player: (Mut, Mut), 63 | head: &mut Transform, 64 | player_stats: &mut PlayerStats, 65 | ) { 66 | let Self { 67 | hand_item, 68 | pos, 69 | rot, 70 | stats, 71 | vel, 72 | } = self; 73 | 74 | *player_stats = stats; 75 | 76 | player.1.velocity = vel.into(); 77 | 78 | let body_rotation = Quat::from_rotation_y(rot.0); 79 | let head_rotation = Quat::from_rotation_x(rot.1); 80 | 81 | *player.0 = Transform::from_translation(pos.into()).with_rotation(body_rotation); 82 | 83 | *head = Transform::from_translation(Vec3::Y * HEAD_LEVEL).with_rotation(head_rotation); 84 | 85 | if let Some(hand_item) = hand_item { 86 | hand_item 87 | .to_spawner(registry, Vec3::ZERO) 88 | .spawn_to_hand(commands, assets, hand) 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/plugins/player/systems/input.rs: -------------------------------------------------------------------------------- 1 | use crate::plugins::player::{ 2 | events::*, 3 | resources::input_settings::{ 4 | InputCondition, KeyboardInputCondition, MouseInputCondition, PlayerInputSettings, 5 | }, 6 | }; 7 | use bevy::{ecs::event::Event, prelude::*}; 8 | 9 | fn process( 10 | keys: &Input, 11 | mouse: &Input, 12 | condition: InputCondition, 13 | event_writer: &mut EventWriter, 14 | ) { 15 | match condition { 16 | InputCondition::Keyboard(KeyboardInputCondition { key, allow_repeat }) => { 17 | if allow_repeat && keys.pressed(key) || !allow_repeat && keys.just_pressed(key) { 18 | event_writer.send(T::default()); 19 | } 20 | } 21 | InputCondition::Mouse(MouseInputCondition { 22 | button, 23 | allow_repeat, 24 | }) => { 25 | if allow_repeat && mouse.pressed(button) || !allow_repeat && mouse.just_pressed(button) 26 | { 27 | event_writer.send(T::default()); 28 | } 29 | } 30 | } 31 | } 32 | 33 | // TODO refactor input system 34 | #[allow(clippy::too_many_arguments)] 35 | pub fn process_input_1( 36 | k: Res>, 37 | m: Res>, 38 | s: Res, 39 | mut go_forward_ew: EventWriter, 40 | mut go_backward_ew: EventWriter, 41 | mut go_left_ew: EventWriter, 42 | mut go_right_ew: EventWriter, 43 | mut go_up_ew: EventWriter, 44 | mut go_down_ew: EventWriter, 45 | mut jump_ew: EventWriter, 46 | ) { 47 | process(&k, &m, s.go_forward, &mut go_forward_ew); 48 | process(&k, &m, s.go_backward, &mut go_backward_ew); 49 | process(&k, &m, s.go_left, &mut go_left_ew); 50 | process(&k, &m, s.go_right, &mut go_right_ew); 51 | process(&k, &m, s.go_up, &mut go_up_ew); 52 | process(&k, &m, s.go_down, &mut go_down_ew); 53 | process(&k, &m, s.jump, &mut jump_ew); 54 | } 55 | 56 | #[allow(clippy::too_many_arguments)] 57 | pub fn process_input_2( 58 | k: Res>, 59 | m: Res>, 60 | s: Res, 61 | mut toggle_fly_ew: EventWriter, 62 | mut sprint_ew: EventWriter, 63 | mut spawn_item_ew: EventWriter, 64 | mut mine_ew: EventWriter, 65 | mut use_place_grab_ew: EventWriter, 66 | mut craft_ew: EventWriter, 67 | mut interact_ew: EventWriter, 68 | ) { 69 | process(&k, &m, s.toggle_fly, &mut toggle_fly_ew); 70 | process(&k, &m, s.sprint, &mut sprint_ew); 71 | process(&k, &m, s.spawn_item, &mut spawn_item_ew); 72 | process(&k, &m, s.mine, &mut mine_ew); 73 | process(&k, &m, s.use_place_grab, &mut use_place_grab_ew); 74 | process(&k, &m, s.craft, &mut craft_ew); 75 | process(&k, &m, s.interact, &mut interact_ew); 76 | } 77 | -------------------------------------------------------------------------------- /src/plugins/chunks/components/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::internal::chunks::pointer::ChunkPointer; 2 | use crate::internal::chunks::Chunk; 3 | use crate::internal::pos::ChunkPos; 4 | use crate::plugins::static_mesh::components::Vertex; 5 | use crate::plugins::world_generator::internal::biomes::ChunkBiomes; 6 | use bevy::prelude::*; 7 | use crossbeam_channel::Receiver; 8 | use std::collections::LinkedList; 9 | use std::time::Duration; 10 | 11 | #[derive(Debug, Default, Clone, Component, Reflect, FromReflect)] 12 | #[reflect(Component)] 13 | pub struct ChunkComponent { 14 | pub chunk: ChunkPointer, 15 | } 16 | 17 | pub struct ComputeChunkCreateData { 18 | pub pos: ChunkPos, 19 | pub chunk: Chunk, 20 | pub vertices: Vec, 21 | pub biomes: ChunkBiomes, 22 | } 23 | 24 | pub struct ComputeChunkUnloadData { 25 | pub unloaded_chunks: LinkedList, 26 | pub pos: ChunkPos, 27 | pub level: usize, 28 | pub chunk: Chunk, 29 | pub vertices: Vec, 30 | } 31 | 32 | pub struct ComputeChunkDetailedData { 33 | pub prev_chunk_entity: Entity, 34 | pub pos: ChunkPos, 35 | pub level: usize, 36 | pub chunks: Vec<(Chunk, Vec)>, 37 | } 38 | 39 | #[derive(Component)] 40 | pub struct ComputeTask(pub Receiver>); 41 | 42 | #[derive(Debug, Clone, Copy, Component, Default, Reflect, FromReflect)] 43 | #[reflect(Component)] 44 | pub struct ChunkMeshComponent; 45 | 46 | #[derive(Debug, Clone, Copy, Component, Default, Reflect, FromReflect)] 47 | #[reflect(Component)] 48 | pub struct RealChunkComponent; 49 | 50 | #[derive(Debug, Clone, Copy, Component, Default, Reflect, FromReflect)] 51 | #[reflect(Component)] 52 | pub struct DetailingChunkComponent; 53 | 54 | #[derive(Debug, Clone, Copy, Component, Default, Reflect, FromReflect)] 55 | #[reflect(Component)] 56 | pub struct UnloadingChunkComponent; 57 | 58 | #[derive(Debug, Clone, Component, Default, Reflect, FromReflect)] 59 | #[reflect(Component)] 60 | pub struct ChunkSmoothMining { 61 | duration: Duration, 62 | started_at: Duration, 63 | strength: f32, 64 | rest: f32, 65 | radius: f32, 66 | } 67 | 68 | impl ChunkSmoothMining { 69 | pub fn new(time: &Time, duration: Duration, strength: f32, radius: f32) -> Self { 70 | Self { 71 | duration, 72 | started_at: time.elapsed(), 73 | strength, 74 | rest: strength, 75 | radius, 76 | } 77 | } 78 | 79 | pub fn update(&mut self, time: &Time) -> f32 { 80 | if (time.elapsed() - self.started_at) >= self.duration { 81 | let delta_strength = self.rest; 82 | self.rest = 0.0; 83 | return delta_strength; 84 | } 85 | 86 | let delta_strength = self.strength * time.delta_seconds() / self.duration.as_secs_f32(); 87 | 88 | self.rest -= delta_strength; 89 | 90 | delta_strength 91 | } 92 | 93 | pub fn get_radius(&self) -> f32 { 94 | self.radius 95 | } 96 | 97 | pub fn is_done(&self) -> bool { 98 | self.rest == 0.0 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/plugins/loading/systems/load_assets.rs: -------------------------------------------------------------------------------- 1 | use crate::plugins::loading::resources::{GameAssets, PhysicsObject}; 2 | use bevy::{asset::AssetPath, prelude::*}; 3 | 4 | fn load_scene_with_physics<'a>( 5 | path: impl Into>, 6 | asset_server: &AssetServer, 7 | ) -> PhysicsObject { 8 | let scene_h: Handle = asset_server.load(path); 9 | 10 | PhysicsObject { 11 | scene: scene_h, 12 | ..default() 13 | } 14 | } 15 | 16 | pub fn load_assets( 17 | asset_server: Res, 18 | mut meshes: ResMut>, 19 | mut materials: ResMut>, 20 | mut commands: Commands, 21 | ) { 22 | let game_assets = GameAssets { 23 | main_font: asset_server.load("fonts/roboto.ttf"), 24 | default_material: materials.add(StandardMaterial { 25 | base_color: Color::rgb(1.0, 1.0, 1.0), 26 | perceptual_roughness: 1., 27 | metallic: 0., 28 | reflectance: 0., 29 | ..default() 30 | }), 31 | craft_zone_material: materials.add(StandardMaterial { 32 | base_color: Color::rgba(1.0, 1.0, 1.0, 0.1), 33 | perceptual_roughness: 1., 34 | metallic: 0., 35 | alpha_mode: AlphaMode::Blend, 36 | reflectance: 0., 37 | ..default() 38 | }), 39 | pointer_mesh: meshes.add(Mesh::from(shape::Cube { size: 0.2 })), 40 | craft_zone_mesh: meshes.add(Mesh::from(shape::Icosphere { 41 | radius: 0.05, 42 | subdivisions: 9, 43 | })), 44 | 45 | tree_object: load_scene_with_physics("models/tree.glb#Scene0", &asset_server), 46 | branch_object: load_scene_with_physics("models/branch.glb#Scene0", &asset_server), 47 | rock_object: load_scene_with_physics("models/rock.glb#Scene0", &asset_server), 48 | fire_object: load_scene_with_physics("models/fire.glb#Scene0", &asset_server), 49 | log_object: load_scene_with_physics("models/log.glb#Scene0", &asset_server), 50 | stump_object: load_scene_with_physics("models/stump.glb#Scene0", &asset_server), 51 | cactus_object: load_scene_with_physics("models/cactus.glb#Scene0", &asset_server), 52 | wooden_shovel_object: load_scene_with_physics( 53 | "models/wooden_shovel.glb#Scene0", 54 | &asset_server, 55 | ), 56 | spruce_object: load_scene_with_physics("models/spruce.glb#Scene0", &asset_server), 57 | spruce_snow_object: load_scene_with_physics("models/spruce-snow.glb#Scene0", &asset_server), 58 | flax_object: load_scene_with_physics("models/flax.glb#Scene0", &asset_server), 59 | flax_item_object: load_scene_with_physics("models/flax-item.glb#Scene0", &asset_server), 60 | stone_axe_object: load_scene_with_physics("models/stone-axe.glb#Scene0", &asset_server), 61 | coarse_string_object: load_scene_with_physics( 62 | "models/coarse-string.glb#Scene0", 63 | &asset_server, 64 | ), 65 | 66 | crosshair_image: asset_server.load("textures/crosshair.png"), 67 | }; 68 | 69 | commands.insert_resource(game_assets); 70 | } 71 | -------------------------------------------------------------------------------- /src/plugins/craft/systems/mod.rs: -------------------------------------------------------------------------------- 1 | use super::{components::CraftZoneComponent, resources::crafts_registry::CraftsRegistry}; 2 | use crate::plugins::{ 3 | craft::resources::CRAFT_ZONE_RADIUS, 4 | loading::resources::GameAssets, 5 | objects::components::{ 6 | items::{ItemComponent, ItemGrabbed}, 7 | GameWorldObject, 8 | }, 9 | player::{ 10 | components::PlayerCameraComponent, events::CraftEvent, resources::look_at::PlayerLookAt, 11 | }, 12 | }; 13 | use bevy::prelude::*; 14 | 15 | pub fn setup_craft_zone( 16 | mut commands: Commands, 17 | assets: Res, 18 | camera_q: Query>, 19 | ) { 20 | let camera = camera_q.single(); 21 | commands.entity(camera).with_children(|parent| { 22 | parent.spawn(( 23 | Name::new("player:craft-zone"), 24 | CraftZoneComponent, 25 | PbrBundle { 26 | visibility: Visibility::INVISIBLE, 27 | transform: Transform::from_xyz(0.0, 0.0, -1.0), 28 | material: assets.craft_zone_material.clone(), 29 | mesh: assets.craft_zone_mesh.clone(), 30 | ..Default::default() 31 | }, 32 | )); 33 | }); 34 | } 35 | 36 | #[allow(clippy::too_many_arguments)] 37 | pub fn craft_zone( 38 | mut commands: Commands, 39 | mut zone_q: Query<(&mut Visibility, &mut Transform), With>, 40 | mut craft_e: EventReader, 41 | mut item_grabbed_q: Query<(Entity, &mut GameWorldObject), With>, 42 | mut items_q: Query< 43 | (&GlobalTransform, &mut GameWorldObject, Entity), 44 | (With, Without), 45 | >, 46 | look_at: Res, 47 | registry: Res, 48 | assets: Res, 49 | ) { 50 | if look_at.target.is_some() { 51 | let (mut visibility, mut transform) = zone_q.single_mut(); 52 | *transform = Transform::from_xyz(0.0, 0.0, -look_at.distance); 53 | visibility.is_visible = true; 54 | 55 | let craft_center = look_at.position; 56 | 57 | for _ in craft_e.iter() { 58 | // Prepare items in craft zone 59 | let mut items = items_q 60 | .iter_mut() 61 | .filter_map(|(transform, item, e)| { 62 | let pos = transform.compute_transform().translation; 63 | let dist = (pos - craft_center).length_squared(); 64 | if dist < CRAFT_ZONE_RADIUS * CRAFT_ZONE_RADIUS { 65 | Some((e, item)) 66 | } else { 67 | None 68 | } 69 | }) 70 | .collect::>(); 71 | 72 | // Get hand item 73 | let mut hand_item = item_grabbed_q.iter_mut().next(); 74 | 75 | // Try to craft 76 | registry.try_craft( 77 | &mut commands, 78 | &assets, 79 | craft_center, 80 | &mut hand_item, 81 | &mut items, 82 | ); 83 | } 84 | } else { 85 | let (mut visibility, _) = zone_q.single_mut(); 86 | visibility.is_visible = false; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/plugins/world_generator/internal/biomes/plains.rs: -------------------------------------------------------------------------------- 1 | use super::{spawn_objects, Biome, BiomeCheckInput, BiomeID, ChunkBiomes, SpawnObjectInp}; 2 | use crate::{ 3 | internal::{pos::ChunkPos, voxel::voxel_types::VoxelId}, 4 | plugins::{ 5 | objects::components::{ 6 | items::{branch::BranchItem, rock::RockItem}, 7 | objects::{flax::FlaxObject, tree::TreeObject}, 8 | GameWorldObjectTrait, 9 | }, 10 | world_generator::resources::{GenCaveInp, GenVoxelInp, LandscapeHeightInp, WorldGenerator}, 11 | }, 12 | }; 13 | use bevy::prelude::*; 14 | use std::sync::Arc; 15 | 16 | #[derive(Debug, Clone)] 17 | pub struct PlainsBiome; 18 | 19 | impl PlainsBiome { 20 | pub const ID: BiomeID = "plains"; 21 | 22 | pub fn new() -> Arc { 23 | Arc::new(Self) 24 | } 25 | } 26 | 27 | impl Biome for PlainsBiome { 28 | fn get_id(&self) -> BiomeID { 29 | Self::ID 30 | } 31 | 32 | fn get_generate_voxel_inp(&self, _gen: &WorldGenerator, _pos: ChunkPos) -> GenVoxelInp { 33 | GenVoxelInp { 34 | cave_inp: GenCaveInp { 35 | cave_factor: 1.3, 36 | cave_offset: 0.3, 37 | cave_strength: 100.0, 38 | }, 39 | bumps_factor: 0.05, 40 | first_layer_id: VoxelId::GRASS, 41 | second_layer_id: VoxelId::DIRT, 42 | rest_layers_id: VoxelId::STONE, 43 | } 44 | } 45 | 46 | fn get_landscape_height_inp( 47 | &self, 48 | _gen: &WorldGenerator, 49 | _pos: ChunkPos, 50 | ) -> LandscapeHeightInp { 51 | LandscapeHeightInp { height: 10.0 } 52 | } 53 | 54 | fn check_pos(&self, _gen: &WorldGenerator, _pos: ChunkPos, _inp: BiomeCheckInput) -> bool { 55 | // always return true as this is the default biome 56 | true 57 | } 58 | 59 | fn spawn_objects( 60 | &self, 61 | biomes: &ChunkBiomes, 62 | chunk_pos: ChunkPos, 63 | commands: &mut Commands, 64 | gen: &WorldGenerator, 65 | ) -> usize { 66 | spawn_objects( 67 | biomes, 68 | chunk_pos, 69 | commands, 70 | gen, 71 | vec![ 72 | SpawnObjectInp { 73 | allow_air: false, 74 | amount: 2, 75 | chance: 0.2, 76 | get_spawner: Box::new(|t| FlaxObject.to_spawner(t)), 77 | offset: Vec3::ZERO, 78 | }, 79 | SpawnObjectInp { 80 | allow_air: false, 81 | amount: 1, 82 | chance: 0.05, 83 | get_spawner: Box::new(|t| TreeObject.to_spawner(t)), 84 | offset: Vec3::ZERO, 85 | }, 86 | SpawnObjectInp { 87 | allow_air: false, 88 | amount: 1, 89 | chance: 0.15, 90 | get_spawner: Box::new(|t| BranchItem.to_spawner(t)), 91 | offset: Vec3::Y * 0.1, 92 | }, 93 | SpawnObjectInp { 94 | allow_air: false, 95 | amount: 1, 96 | chance: 0.125, 97 | get_spawner: Box::new(|t| RockItem.to_spawner(t)), 98 | offset: Vec3::Y * 0.1, 99 | }, 100 | ], 101 | ) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/internal/voxel/add_edge.rs: -------------------------------------------------------------------------------- 1 | use super::append_triangle::append_triangle_with_normal; 2 | use crate::{ 3 | internal::{chunks::Chunk, color::Color, pos::VoxelPos}, 4 | plugins::static_mesh::components::Vertex, 5 | }; 6 | use bevy::prelude::Vec3; 7 | 8 | const FRAME_SIZE: f32 = 0.25; 9 | 10 | /// Additional mesh at the chunks border to hide seams between chunks with different LODs 11 | pub fn append_edge( 12 | vertex: &mut Vec, 13 | color: Color, 14 | scale: f32, 15 | pos: VoxelPos, 16 | normal: Vec3, 17 | points: (Vec3, Vec3, Vec3), 18 | ) { 19 | let chunk_size = Chunk::SIZE as f32; 20 | let (a, b, c) = points; 21 | 22 | let mut f = |a: Vec3, b: Vec3, mask: Vec3, color: Color| { 23 | let dir = -normal * mask; 24 | let dir = dir.normalize() * FRAME_SIZE; 25 | 26 | let c = b + dir; 27 | let d = a + dir; 28 | 29 | append_triangle_with_normal(vertex, scale, color, c, b, a, normal); 30 | append_triangle_with_normal(vertex, scale, color, a, d, c, normal); 31 | }; 32 | 33 | // We have 3 points, if two of them are on the same edge, wee need to create 34 | // additional face to fill potential gap between chunks with different 35 | // detail level 36 | 37 | if pos.x == 0 { 38 | if a.x == 0. && b.x == 0. { 39 | f(a, b, Vec3::new(0.0, 1.0, 1.0), color); 40 | } else if a.x == 0. && c.x == 0. { 41 | f(c, a, Vec3::new(0.0, 1.0, 1.0), color); 42 | } else if b.x == 0. && c.x == 0. { 43 | f(b, c, Vec3::new(0.0, 1.0, 1.0), color); 44 | } 45 | } else if pos.x == Chunk::SIZE - 1 { 46 | if a.x == chunk_size && b.x == chunk_size { 47 | f(a, b, Vec3::new(0.0, 1.0, 1.0), color); 48 | } else if a.x == chunk_size && c.x == chunk_size { 49 | f(c, a, Vec3::new(0.0, 1.0, 1.0), color); 50 | } else if b.x == chunk_size && c.x == chunk_size { 51 | f(b, c, Vec3::new(0.0, 1.0, 1.0), color); 52 | } 53 | } 54 | 55 | if pos.y == 0 { 56 | if a.y == 0. && b.y == 0. { 57 | f(a, b, Vec3::new(1.0, 0.0, 1.0), color); 58 | } else if a.y == 0. && c.y == 0. { 59 | f(c, a, Vec3::new(1.0, 0.0, 1.0), color); 60 | } else if b.y == 0. && c.y == 0. { 61 | f(b, c, Vec3::new(1.0, 0.0, 1.0), color); 62 | } 63 | } else if pos.y == Chunk::SIZE - 1 { 64 | if a.y == chunk_size && b.y == chunk_size { 65 | f(a, b, Vec3::new(1.0, 0.0, 1.0), color); 66 | } else if a.y == chunk_size && c.y == chunk_size { 67 | f(c, a, Vec3::new(1.0, 0.0, 1.0), color); 68 | } else if b.y == chunk_size && c.y == chunk_size { 69 | f(b, c, Vec3::new(1.0, 0.0, 1.0), color); 70 | } 71 | } 72 | 73 | if pos.z == 0 { 74 | if a.z == 0. && b.z == 0. { 75 | f(a, b, Vec3::new(1.0, 1.0, 0.0), color); 76 | } else if a.z == 0. && c.z == 0. { 77 | f(c, a, Vec3::new(1.0, 1.0, 0.0), color); 78 | } else if b.z == 0. && c.z == 0. { 79 | f(b, c, Vec3::new(1.0, 1.0, 0.0), color); 80 | } 81 | } else if pos.z == Chunk::SIZE - 1 { 82 | if a.z == chunk_size && b.z == chunk_size { 83 | f(a, b, Vec3::new(1.0, 1.0, 0.0), color); 84 | } else if a.z == chunk_size && c.z == chunk_size { 85 | f(c, a, Vec3::new(1.0, 1.0, 0.0), color); 86 | } else if b.z == chunk_size && c.z == chunk_size { 87 | f(b, c, Vec3::new(1.0, 1.0, 0.0), color); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/plugins/game_world/systems/save.rs: -------------------------------------------------------------------------------- 1 | use bevy::{ 2 | prelude::*, 3 | time::{Timer, TimerMode}, 4 | utils::HashMap, 5 | }; 6 | use std::time::Duration; 7 | 8 | use crate::{ 9 | internal::pos::ChunkPos, 10 | plugins::{ 11 | game_world::resources::{meta::GameWorldMeta, GameWorld}, 12 | objects::{ 13 | components::{items::ItemGrabbed, GameWorldObject}, 14 | utils::object_save::GameWorldObjectSave, 15 | }, 16 | player::{ 17 | components::{save::PlayerSave, PlayerComponent, PlayerHeadComponent}, 18 | resources::PlayerStats, 19 | }, 20 | }, 21 | }; 22 | 23 | const SAVE_INTERVAL_SECS: u64 = 5; 24 | 25 | pub struct SaveTimer(pub Timer); 26 | 27 | impl Default for SaveTimer { 28 | fn default() -> Self { 29 | Self(Timer::new( 30 | Duration::from_secs(SAVE_INTERVAL_SECS), 31 | TimerMode::Repeating, 32 | )) 33 | } 34 | } 35 | 36 | pub fn save_system( 37 | mut timer: Local, 38 | mut world: ResMut, 39 | meta: Res, 40 | items: Query<(&GlobalTransform, &GameWorldObject), Without>, 41 | time: Res