pub mod abilities; pub mod ai; pub mod aim; pub mod animation; pub mod backpack; pub mod camera; pub mod cash; pub mod cash_heal; pub mod character; #[cfg(feature = "client")] pub mod client; pub mod config; pub mod control; pub mod cutscene; pub mod gates; pub mod head; pub mod head_drop; pub mod heads; pub mod heads_database; pub mod hitpoints; pub mod keys; pub mod loading_assets; pub mod loading_map; pub mod movables; pub mod npc; pub mod physics_layers; pub mod platforms; pub mod player; pub mod protocol; pub mod server; pub mod tb_entities; pub mod tick; pub mod utils; pub mod water; use crate::{ config::NetConfig, heads_database::{HeadDatabaseAsset, HeadsDatabase}, protocol::{PlayerIdCounter, messages::AssignClientPlayer}, tb_entities::SpawnPoint, }; use avian3d::{PhysicsPlugins, prelude::TransformInterpolation}; #[cfg(not(feature = "client"))] use bevy::app::ScheduleRunnerPlugin; use bevy::{core_pipeline::tonemapping::Tonemapping, prelude::*}; use bevy_common_assets::ron::RonAssetPlugin; use bevy_replicon::{RepliconPlugins, prelude::ClientId}; use bevy_replicon_renet::RepliconRenetPlugins; use bevy_sprite3d::Sprite3dPlugin; use bevy_steamworks::SteamworksEvent; use bevy_trenchbroom::{ TrenchBroomPlugins, config::TrenchBroomConfig, prelude::TrenchBroomPhysicsPlugin, }; use bevy_trenchbroom_avian::AvianPhysicsBackend; use utils::{billboards, squish_animation}; pub const HEDZ_GREEN: Srgba = Srgba::rgb(0.0, 1.0, 0.0); pub const HEDZ_PURPLE: Srgba = Srgba::rgb(91. / 256., 4. / 256., 138. / 256.); pub fn launch() { let mut app = App::new(); app.register_type::() .register_type::(); app.insert_resource(DebugVisuals { unlit: false, tonemapping: Tonemapping::None, exposure: 1., shadows: true, cam_follow: true, }); let default_plugins = DefaultPlugins; #[cfg(feature = "client")] let default_plugins = default_plugins.set(WindowPlugin { primary_window: Some(Window { title: "HEDZ Reloaded".into(), ..default() }), ..default() }); app.add_plugins(default_plugins); #[cfg(not(feature = "client"))] app.add_plugins(ScheduleRunnerPlugin::default()); #[cfg(feature = "client")] app.add_plugins( bevy_debug_log::LogViewerPlugin::default() .auto_open_threshold(bevy::log::tracing::level_filters::LevelFilter::OFF), ); app.add_plugins(PhysicsPlugins::default()); app.add_plugins((RepliconPlugins, RepliconRenetPlugins)); app.add_plugins(Sprite3dPlugin); app.add_plugins(TrenchBroomPlugins( TrenchBroomConfig::new("hedz").icon(None), )); app.add_plugins(TrenchBroomPhysicsPlugin::new(AvianPhysicsBackend)); app.add_plugins(RonAssetPlugin::::new(&["headsdb.ron"])); app.add_plugins(plugin); app.init_state::(); app.run(); } pub fn plugin(app: &mut App) { app.add_plugins(abilities::plugin); app.add_plugins(ai::plugin); app.add_plugins(animation::plugin); app.add_plugins(character::plugin); app.add_plugins(cash::plugin); app.add_plugins(cash_heal::plugin); app.add_plugins(config::plugin); app.add_plugins(player::plugin); app.add_plugins(gates::plugin); app.add_plugins(platforms::plugin); app.add_plugins(movables::plugin); app.add_plugins(utils::billboards::plugin); app.add_plugins(aim::plugin); app.add_plugins(npc::plugin); app.add_plugins(keys::plugin); app.add_plugins(utils::squish_animation::plugin); app.add_plugins(camera::plugin); #[cfg(feature = "client")] app.add_plugins(client::plugin); app.add_plugins(control::plugin); app.add_plugins(cutscene::plugin); app.add_plugins(backpack::plugin); app.add_plugins(loading_assets::LoadingPlugin); app.add_plugins(loading_map::plugin); app.add_plugins(heads::plugin); app.add_plugins(hitpoints::plugin); app.add_plugins(head_drop::plugin); app.add_plugins(protocol::plugin); app.add_plugins(server::plugin); app.add_plugins(tb_entities::plugin); app.add_plugins(tick::plugin); app.add_plugins(utils::plugin); app.add_plugins(utils::auto_rotate::plugin); app.add_plugins(utils::explosions::plugin); app.add_plugins(utils::sprite_3d_animation::plugin); app.add_plugins(utils::trail::plugin); app.add_plugins(water::plugin); if cfg!(feature = "client") { app.add_systems( OnEnter(GameState::Waiting), start_solo_client.run_if(|config: Res| config.is_singleplayer()), ); app.add_systems( OnEnter(GameState::Waiting), start_listen_server.run_if(|config: Res| config.is_host()), ); app.add_systems( OnEnter(GameState::Waiting), start_client.run_if(|config: Res| config.is_client()), ); } else { app.add_systems(OnEnter(GameState::Waiting), start_dedicated_server); } app.add_systems(Update, log_steam_events); } #[derive(Resource, Reflect, Debug)] #[reflect(Resource)] pub struct DebugVisuals { pub unlit: bool, pub tonemapping: Tonemapping, pub exposure: f32, pub shadows: bool, pub cam_follow: bool, } #[derive(States, Default, Clone, Copy, Eq, PartialEq, Debug, Hash)] pub enum GameState { /// Loading assets from disk #[default] AssetLoading, /// Loading + constructing map MapLoading, /// Waiting to host/connect/play Waiting, /// Connecting to server Connecting, /// Opening server Hosting, /// Running the game Playing, } fn log_steam_events(events: Option>) { let Some(mut events) = events else { return; }; for event in events.read() { let SteamworksEvent::CallbackResult(result) = event; info!("steam: {:?}", result); } } fn start_solo_client( commands: Commands, mut next: ResMut>, query: Query<&Transform, With>, heads_db: Res, mut assign_player_id: MessageWriter, mut ids: ResMut, ) { next.set(GameState::Playing); ids.reset(); let id = ids.alloc(); player::spawn(commands, ClientId::Server, id, query, heads_db); assign_player_id.write(AssignClientPlayer(id)); } fn start_listen_server( commands: Commands, mut next: ResMut>, query: Query<&Transform, With>, heads_db: Res, mut assign_player_id: MessageWriter, mut ids: ResMut, ) { next.set(GameState::Hosting); ids.reset(); let id = ids.alloc(); player::spawn(commands, ClientId::Server, id, query, heads_db); assign_player_id.write(AssignClientPlayer(id)); } fn start_client(mut next: ResMut>) { next.set(GameState::Connecting); } fn start_dedicated_server(mut next: ResMut>) { next.set(GameState::Hosting); }