#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/87333478?s=200&v=4")]
#![cfg_attr(doc, allow(unknown_lints))]
#![deny(rustdoc::all)]
#![allow(clippy::too_many_arguments)]
#![allow(dead_code)]
#![allow(ambiguous_glob_reexports)]
#![doc = include_str!("./README.md")]
use bones_bevy_renderer::BonesBevyRenderer;
use bones_framework::prelude::*;
pub mod audio;
pub mod core;
pub mod debug;
pub mod fullscreen;
pub mod input;
pub mod profiler;
pub mod sessions;
pub mod settings;
pub mod ui;
mod prelude {
pub use crate::{
audio::*, core::prelude::*, impl_system_param, input::*, sessions::*, settings::*, GameMeta,
};
pub use bones_framework::prelude::*;
pub use once_cell::sync::Lazy;
pub use serde::{Deserialize, Serialize};
pub use std::{sync::Arc, time::Duration};
#[allow(unused)]
pub use tracing::{debug, error, info, trace, warn};
}
use crate::prelude::*;
#[cfg(all(debug_assertions, not(target_arch = "wasm32")))]
#[allow(unused_imports)]
#[allow(clippy::single_component_path_imports)]
use bevy_dylib;
#[derive(HasSchema, Clone, Debug, Default)]
#[type_data(metadata_asset("game"))]
#[repr(C)]
pub struct GameMeta {
pub plugins: SVec<Handle<LuaPlugin>>,
pub core: CoreMeta,
pub default_settings: settings::Settings,
pub localization: Handle<LocalizationAsset>,
pub theme: ui::UiTheme,
pub main_menu: ui::main_menu::MainMenuMeta,
pub music: GameMusic,
pub network: NetworkMeta,
}
#[derive(HasSchema, Copy, Clone, Debug)]
#[repr(C)]
pub struct NetworkMeta {
pub max_prediction_window: usize,
pub local_input_delay: usize,
}
#[allow(clippy::derivable_impls)]
impl Default for NetworkMeta {
fn default() -> Self {
#[cfg(target_arch = "wasm32")]
{
Self {
local_input_delay: 0,
max_prediction_window: 0,
}
}
#[cfg(not(target_arch = "wasm32"))]
{
Self {
local_input_delay: bones_framework::networking::NETWORK_LOCAL_INPUT_DELAY_DEFAULT,
max_prediction_window:
bones_framework::networking::NETWORK_MAX_PREDICTION_WINDOW_DEFAULT,
}
}
}
}
#[derive(HasSchema, Clone, Debug, Default)]
#[type_data(metadata_asset("assets"))]
#[repr(C)]
pub struct PackMeta {
pub plugins: SVec<Handle<LuaPlugin>>,
pub map_tilesets: SVec<Handle<Atlas>>,
pub players: SVec<Handle<PlayerMeta>>,
pub player_hats: SVec<Handle<HatMeta>>,
pub maps: SVec<Handle<MapMeta>>,
pub map_elements: SVec<Handle<ElementMeta>>,
}
impl GameMeta {
pub fn get_plugins(&self, asset_server: &AssetServer) -> Arc<Vec<Handle<LuaPlugin>>> {
let mut plugins = Vec::new();
plugins.extend(self.plugins.iter().copied());
plugins.extend(
self.core
.map_elements
.iter()
.map(|eh| asset_server.get(*eh).plugin)
.filter(|plugin_handle| plugin_handle != &Handle::default()),
);
for pack in asset_server.packs() {
let pack_meta = asset_server.get(pack.root.typed::<PackMeta>());
plugins.extend(pack_meta.plugins.iter().copied());
plugins.extend(
pack_meta
.map_elements
.iter()
.map(|eh| asset_server.get(*eh).plugin)
.filter(|plugin_handle| plugin_handle != &Handle::default()),
);
}
Arc::new(plugins)
}
}
#[derive(HasSchema, Clone, Debug, Default)]
#[repr(C)]
pub struct GameMusic {
pub title_screen: Handle<AudioSource>,
pub fight: SVec<Handle<AudioSource>>,
pub character_screen: Handle<AudioSource>,
pub results_screen: Handle<AudioSource>,
pub credits: Handle<AudioSource>,
}
fn main() {
setup_logs!("org", "fishfolk", "jumpy");
bevy_tasks::IoTaskPool::init(bevy_tasks::TaskPool::new);
settings::Settings::register_schema();
let mut game = Game::new();
GameMeta::register_schema();
PackMeta::register_schema();
game
.install_plugin(DefaultGamePlugin)
.install_plugin(audio::game_plugin)
.install_plugin(settings::game_plugin)
.install_plugin(fullscreen::game_plugin)
.install_plugin(input::game_plugin)
.install_plugin(core::game_plugin)
.install_plugin(debug::game_plugin)
.install_plugin(profiler::game_plugin)
.install_plugin(ui::scoring::game_plugin)
.init_shared_resource::<AssetServer>()
.register_default_assets();
game.sessions.start_menu();
game.sessions
.create(SessionNames::PAUSE_MENU)
.install_plugin(ui::pause_menu::session_plugin);
game.sessions
.get_mut(SessionNames::PAUSE_MENU)
.unwrap()
.priority = 1;
game.sessions
.create(SessionNames::SCORING)
.install_plugin(ui::scoring::session_plugin);
game.sessions
.create(SessionNames::NOTIFICATION)
.install_plugin(ui::notification::session_plugin);
BonesBevyRenderer {
game,
pixel_art: true,
game_version: Version::new(
env!("CARGO_PKG_VERSION_MAJOR").parse().unwrap(),
env!("CARGO_PKG_VERSION_MINOR").parse().unwrap(),
env!("CARGO_PKG_VERSION_PATCH").parse().unwrap(),
),
app_namespace: ("org".into(), "fishfolk".into(), "jumpy".into()),
asset_dir: std::env::var("JUMPY_ASSETS")
.unwrap_or_else(|_| "assets".into())
.into(),
packs_dir: std::env::var("JUMPY_ASSET_PACKS")
.unwrap_or_else(|_| "packs".into())
.into(),
custom_load_progress: Some(Box::new(load_progress)),
preload: true,
}
.app()
.run();
}
fn load_progress(assets: &AssetServer, ctx: &egui::Context) {
let errored = assets.load_progress.errored();
egui::CentralPanel::default()
.frame(egui::Frame::default().fill(egui::Color32::from_rgb(0x26, 0x2b, 0x44)))
.show(ctx, |ui| {
let height = ui.available_height();
let ctx = ui.ctx().clone();
let space_size = 0.03;
let spinner_size = 0.10;
let text_size = 0.034;
ui.vertical_centered(|ui| {
ui.add_space(height * 0.3);
if errored > 0 {
let err_color = egui::Color32::RED;
ui.label(
egui::RichText::new("⚠")
.color(err_color)
.size(height * spinner_size),
);
ui.add_space(height * space_size);
ui.label(
egui::RichText::new(format!(
"Error loading {errored} asset{}.",
if errored > 1 { "s" } else { "" }
))
.color(err_color)
.size(height * text_size * 0.75),
);
} else {
let rect = ui
.label(
egui::RichText::new("⚓")
.color(egui::Color32::WHITE)
.size(height * spinner_size),
)
.rect;
egui::Spinner::new().paint_at(ui, rect.expand(spinner_size * height * 0.2));
ui.add_space(height * space_size);
ui.label(
egui::RichText::new("Loading")
.color(egui::Color32::WHITE)
.size(height * text_size),
);
}
});
ctx.data_mut(|d| {
d.insert_temp(ui.id(), (spinner_size, space_size, text_size));
})
});
}