mod abilities; mod ai; mod aim; mod backpack; mod camera; mod cash; mod cash_heal; mod character; mod control; mod cutscene; mod debug; mod gates; mod head; mod head_drop; mod heads; mod heads_database; mod heal_effect; mod hitpoints; mod keys; mod loading_assets; mod loading_map; mod movables; mod npc; mod physics_layers; mod platforms; mod player; mod sounds; mod tb_entities; mod utils; mod water; use avian3d::prelude::*; use bevy::{ audio::{PlaybackMode, Volume}, core_pipeline::tonemapping::Tonemapping, prelude::*, render::view::ColorGrading, }; use bevy_common_assets::ron::RonAssetPlugin; use bevy_sprite3d::Sprite3dPlugin; use bevy_steamworks::{Client, FriendFlags, SteamworksEvent, SteamworksPlugin}; use bevy_trenchbroom::prelude::*; use bevy_ui_gradients::UiGradientsPlugin; use camera::MainCamera; use heads_database::HeadDatabaseAsset; use loading_assets::AudioAssets; use std::io::{Read, Write}; use utils::{billboards, sprite_3d_animation, squish_animation, trail}; #[derive(Resource, Reflect, Debug)] #[reflect(Resource)] struct DebugVisuals { pub unlit: bool, pub tonemapping: Tonemapping, pub exposure: f32, pub shadows: bool, pub cam_follow: bool, } #[derive(States, Default, Clone, Eq, PartialEq, Debug, Hash)] enum GameState { #[default] AssetLoading, MapLoading, Playing, } fn main() { let mut app = App::new(); app.register_type::(); app.insert_resource(DebugVisuals { unlit: false, tonemapping: Tonemapping::None, exposure: 1., shadows: true, cam_follow: true, }); app.add_plugins( DefaultPlugins .set(WindowPlugin { primary_window: Some(Window { title: "HEDZ Reloaded".into(), // resolution: (1024., 768.).into(), ..default() }), ..default() }) .set(bevy::log::LogPlugin { filter: "info".into(), level: bevy::log::Level::INFO, // provide custom log layer to receive logging events custom_layer: bevy_debug_log::log_capture_layer, }), ); let app_id = 1603000; // should only be done in production builds #[cfg(not(debug_assertions))] if steamworks::restart_app_if_necessary(app_id.into()) { info!("Restarting app via steam"); return; } match SteamworksPlugin::init_app(app_id) { Ok(plugin) => { app.add_plugins(plugin); } Err(e) => { warn!("steam init error: {e:?}"); } }; 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(Sprite3dPlugin); app.add_plugins(TrenchBroomPlugins( TrenchBroomConfig::new("hedz").icon(None), )); app.add_plugins(UiGradientsPlugin); app.add_plugins(RonAssetPlugin::::new(&["headsdb.ron"])); #[cfg(feature = "dbg")] { app.add_plugins(bevy_inspector_egui::bevy_egui::EguiPlugin { enable_multipass_for_primary_context: true, }); app.add_plugins(bevy_inspector_egui::quick::WorldInspectorPlugin::new()); app.add_plugins(PhysicsDebugPlugin::default()); // app.add_plugins(bevy::pbr::wireframe::WireframePlugin) // .insert_resource(bevy::pbr::wireframe::WireframeConfig { // global: true, // default_color: bevy::color::palettes::css::WHITE.into(), // }); } app.add_plugins(ai::plugin); app.add_plugins(character::plugin); app.add_plugins(cash::plugin); app.add_plugins(player::plugin); app.add_plugins(gates::plugin); app.add_plugins(platforms::plugin); app.add_plugins(movables::plugin); app.add_plugins(billboards::plugin); app.add_plugins(aim::plugin); app.add_plugins(npc::plugin); app.add_plugins(keys::plugin); app.add_plugins(squish_animation::plugin); app.add_plugins(cutscene::plugin); app.add_plugins(control::plugin); app.add_plugins(sounds::plugin); app.add_plugins(camera::plugin); app.add_plugins(backpack::plugin); app.add_plugins(loading_assets::LoadingPlugin); app.add_plugins(loading_map::plugin); app.add_plugins(sprite_3d_animation::plugin); app.add_plugins(abilities::plugin); app.add_plugins(heads::plugin); app.add_plugins(hitpoints::plugin); app.add_plugins(cash_heal::plugin); app.add_plugins(debug::plugin); app.add_plugins(utils::observers::plugin); app.add_plugins(water::plugin); app.add_plugins(head_drop::plugin); app.add_plugins(trail::plugin); app.add_plugins(heal_effect::plugin); app.add_plugins(tb_entities::plugin); app.init_state::(); app.insert_resource(AmbientLight { color: Color::WHITE, brightness: 400., ..Default::default() }); app.insert_resource(ClearColor(Color::BLACK)); //TODO: let user control this app.insert_resource(GlobalVolume::new(Volume::Linear(0.4))); app.add_systems( Startup, ( write_trenchbroom_config, (steam_system, steam_events) .chain() .run_if(resource_exists::), ), ); app.add_systems(OnEnter(GameState::Playing), music); app.add_systems(Update, (set_materials_unlit, set_tonemapping, set_shadows)); app.run(); } fn steam_events(mut events: EventReader) { for e in events.read() { info!("steam ev: {:?}", e); } } fn steam_system(steam_client: Res) { steam_client.matchmaking().request_lobby_list(|list| { let Ok(list) = list else { return }; info!("lobby list: [{}]", list.len()); for (i, l) in list.iter().enumerate() { info!("lobby [{i}]: {:?}", l); } }); steam_client .matchmaking() .create_lobby( steamworks::LobbyType::FriendsOnly, 4, |result| match result { Ok(lobby_id) => { info!("Created lobby with ID: {:?}", lobby_id); } Err(e) => error!("Failed to create lobby: {}", e), }, ); for friend in steam_client.friends().get_friends(FriendFlags::IMMEDIATE) { info!( "Steam Friend: {:?} - {}({:?})", friend.id(), friend.name(), friend.state() ); } steam_client .remote_storage() .set_cloud_enabled_for_app(true); let f = steam_client.remote_storage().file("hedz_data.dat"); if f.exists() { let mut buf = String::new(); if let Err(e) = f.read().read_to_string(&mut buf) { error!("File read error: {}", e); } else { info!("File content: {}", buf); } } else { info!("File does not exist"); if let Err(e) = f.write().write_all(String::from("hello world").as_bytes()) { error!("steam cloud error: {}", e); } else { info!("steam cloud saved"); } } } fn music(assets: Res, mut commands: Commands) { commands.spawn(( Name::new("sfx-music"), AudioPlayer::new(assets.music.clone()), PlaybackSettings { mode: PlaybackMode::Loop, volume: Volume::Linear(0.6), ..default() }, )); commands.spawn(( Name::new("sfx-ambient"), AudioPlayer::new(assets.ambient.clone()), PlaybackSettings { mode: PlaybackMode::Loop, volume: Volume::Linear(0.8), ..default() }, )); } fn write_trenchbroom_config(server: Res, type_registry: Res) { if let Err(e) = server .config .write_game_config("trenchbroom/hedz", &type_registry.read()) { warn!("Failed to write trenchbroom config: {}", e); } } fn set_tonemapping( mut cams: Query<(&mut Tonemapping, &mut ColorGrading), With>, visuals: Res, ) { for (mut tm, mut color) in cams.iter_mut() { *tm = visuals.tonemapping; color.global.exposure = visuals.exposure; } } fn set_materials_unlit( mut materials: ResMut>, visuals: Res, ) { if !materials.is_changed() { return; } for (_, material) in materials.iter_mut() { material.unlit = visuals.unlit; } } fn set_shadows(mut lights: Query<&mut DirectionalLight>, visuals: Res) { for mut l in lights.iter_mut() { l.shadows_enabled = visuals.shadows; } }