#![allow(clippy::too_many_arguments)]
use bones_bevy_renderer::{bevy::diagnostic::LogDiagnosticsPlugin, BonesBevyRenderer};
use bones_framework::prelude::*;
#[derive(HasSchema, Default, Clone)]
#[repr(C)]
#[type_data(metadata_asset("game"))]
struct GameMeta {
menu_script: Handle<LuaScript>,
menu_image: Handle<Image>,
sprite_demo: Handle<Image>,
atlas_demo: Handle<AtlasDemoMeta>,
tilemap_demo: Handle<TilemapDemoMeta>,
audio_demo: Handle<AudioSource>,
path2d_color: Color,
localization: Handle<LocalizationAsset>,
title_font: FontMeta,
fonts: SVec<Handle<Font>>,
menu_border: BorderImageMeta,
button_style: ButtonThemeMeta,
}
#[derive(HasSchema, Default, Clone)]
#[repr(C)]
#[type_data(metadata_asset("atlas-demo"))]
struct AtlasDemoMeta {
camera_size: CameraSize,
pub atlas: Handle<Atlas>,
pub fps: f32,
pub animation: SVec<u32>,
}
#[derive(HasSchema, Default, Clone)]
#[repr(C)]
#[type_data(metadata_asset("tilemap"))]
struct TilemapDemoMeta {
pub atlas: Handle<Atlas>,
pub map_size: UVec2,
pub tiles: SVec<TileMeta>,
}
#[derive(HasSchema, Default, Clone)]
#[repr(C)]
struct TileMeta {
pos: UVec2,
idx: u32,
}
#[derive(HasSchema, Default, Clone)]
#[repr(C)]
struct PersistedTextData(String);
fn main() {
setup_logs!();
PersistedTextData::register_schema();
let mut renderer = BonesBevyRenderer::new(create_game());
renderer.app_namespace = (
"org".into(),
"fishfolk".into(),
"bones.demo_features".into(),
);
renderer
.app()
.add_plugins(LogDiagnosticsPlugin::default())
.run()
}
pub fn create_game() -> Game {
let mut game = Game::new();
game.install_plugin(DefaultGamePlugin)
.init_shared_resource::<AssetServer>()
.register_default_assets();
GameMeta::register_schema();
AtlasDemoMeta::register_schema();
TilemapDemoMeta::register_schema();
game.sessions.create("menu").install_plugin(menu_plugin);
game
}
#[derive(HasSchema, Default, Clone)]
#[repr(C)]
struct MenuData {
pub frame: u32,
}
pub fn menu_plugin(session: &mut Session) {
session
.install_plugin(DefaultSessionPlugin)
.world
.init_resource::<MenuData>();
session
.add_system_to_stage(Update, menu_system)
.add_startup_system(menu_startup);
}
fn menu_startup(
mut egui_settings: ResMutInit<EguiSettings>,
mut clear_color: ResMutInit<ClearColor>,
) {
**clear_color = Color::BLACK;
egui_settings.scale = 2.0;
}
fn menu_system(
meta: Root<GameMeta>,
ctx: Res<EguiCtx>,
mut sessions: ResMut<Sessions>,
mut session_options: ResMut<SessionOptions>,
mut exit_bones: Option<ResMut<ExitBones>>,
localization: Localization<GameMeta>,
world: &World,
lua_engine: Res<LuaEngine>,
) {
lua_engine.run_script_system(world, meta.menu_script);
egui::CentralPanel::default()
.frame(egui::Frame::none())
.show(&ctx, |ui| {
BorderedFrame::new(&meta.menu_border).show(ui, |ui| {
ui.vertical_centered(|ui| {
ui.add_space(20.0);
ui.label(meta.title_font.rich(localization.get("title")));
ui.add_space(20.0);
if BorderedButton::themed(&meta.button_style, localization.get("sprite-demo"))
.show(ui)
.clicked()
{
session_options.delete = true;
sessions
.create("sprite_demo")
.install_plugin(sprite_demo_plugin);
}
if BorderedButton::themed(&meta.button_style, localization.get("atlas-demo"))
.show(ui)
.clicked()
{
session_options.delete = true;
sessions
.create("atlas_demo")
.install_plugin(atlas_demo_plugin);
}
if BorderedButton::themed(&meta.button_style, localization.get("tilemap-demo"))
.show(ui)
.clicked()
{
session_options.delete = true;
sessions
.create("tilemap_demo")
.install_plugin(tilemap_demo_plugin);
}
if BorderedButton::themed(&meta.button_style, localization.get("audio-demo"))
.show(ui)
.clicked()
{
session_options.delete = true;
sessions
.create("audio_demo")
.install_plugin(audio_demo_plugin);
}
if BorderedButton::themed(&meta.button_style, localization.get("storage-demo"))
.show(ui)
.clicked()
{
session_options.delete = true;
sessions
.create("storage_demo")
.install_plugin(storage_demo_plugin);
}
if BorderedButton::themed(&meta.button_style, localization.get("path2d-demo"))
.show(ui)
.clicked()
{
session_options.delete = true;
sessions
.create("path2d_demo")
.install_plugin(path2d_demo_plugin);
}
if let Some(exit_bones) = &mut exit_bones {
if BorderedButton::themed(&meta.button_style, localization.get("quit"))
.show(ui)
.clicked()
{
***exit_bones = true;
}
}
ui.add_space(10.0);
world.run_system(demo_widget, ui);
ui.add_space(30.0);
});
})
});
}
fn sprite_demo_plugin(session: &mut Session) {
session
.install_plugin(DefaultSessionPlugin)
.add_startup_system(sprite_demo_startup)
.add_system_to_stage(Update, back_to_menu_ui)
.add_system_to_stage(Update, move_sprite);
}
fn sprite_demo_startup(
mut entities: ResMut<Entities>,
mut sprites: CompMut<Sprite>,
mut transforms: CompMut<Transform>,
mut cameras: CompMut<Camera>,
meta: Root<GameMeta>,
) {
spawn_default_camera(&mut entities, &mut transforms, &mut cameras);
let sprite_ent = entities.create();
transforms.insert(sprite_ent, default());
sprites.insert(
sprite_ent,
Sprite {
image: meta.sprite_demo,
..default()
},
);
}
fn move_sprite(
entities: Res<Entities>,
sprite: Comp<Sprite>,
mut transforms: CompMut<Transform>,
input: Res<KeyboardInputs>,
ctx: Res<EguiCtx>,
) {
egui::CentralPanel::default()
.frame(egui::Frame::none())
.show(&ctx, |ui| {
ui.label("Press left and right arrow keys to move sprite");
});
let mut left = false;
let mut right = false;
for input in &input.key_events {
match input.key_code {
Set(KeyCode::Right) => right = true,
Set(KeyCode::Left) => left = true,
_ => (),
}
}
for (_ent, (_sprite, transform)) in entities.iter_with((&sprite, &mut transforms)) {
if left {
transform.translation.x -= 2.0;
}
if right {
transform.translation.x += 2.0;
}
}
}
fn tilemap_demo_plugin(session: &mut Session) {
session
.install_plugin(DefaultSessionPlugin)
.add_startup_system(tilemap_startup_system)
.add_system_to_stage(Update, back_to_menu_ui);
}
fn tilemap_startup_system(
mut entities: ResMut<Entities>,
mut transforms: CompMut<Transform>,
mut tile_layers: CompMut<TileLayer>,
mut cameras: CompMut<Camera>,
mut tiles: CompMut<Tile>,
meta: Root<GameMeta>,
assets: Res<AssetServer>,
) {
spawn_default_camera(&mut entities, &mut transforms, &mut cameras);
let map_info = assets.get(meta.tilemap_demo);
let atlas = assets.get(map_info.atlas);
let mut layer = TileLayer::new(map_info.map_size, atlas.tile_size, map_info.atlas);
for tile in &map_info.tiles {
let tile_ent = entities.create();
tiles.insert(
tile_ent,
Tile {
idx: tile.idx,
..default()
},
);
layer.set(tile.pos, Some(tile_ent))
}
let layer_ent = entities.create();
tile_layers.insert(layer_ent, layer);
transforms.insert(layer_ent, default());
}
fn atlas_demo_plugin(session: &mut Session) {
session
.install_plugin(DefaultSessionPlugin)
.add_startup_system(atlas_demo_startup)
.add_system_to_stage(Update, back_to_menu_ui);
}
fn atlas_demo_startup(
mut entities: ResMut<Entities>,
mut transforms: CompMut<Transform>,
mut cameras: CompMut<Camera>,
mut atlas_sprites: CompMut<AtlasSprite>,
mut animated_sprites: CompMut<AnimatedSprite>,
mut clear_color: ResMutInit<ClearColor>,
meta: Root<GameMeta>,
assets: Res<AssetServer>,
) {
**clear_color = Color::GRAY;
let demo = assets.get(meta.atlas_demo);
let camera_ent = spawn_default_camera(&mut entities, &mut transforms, &mut cameras);
cameras.get_mut(camera_ent).unwrap().size = demo.camera_size;
let sprite_ent = entities.create();
transforms.insert(sprite_ent, default());
atlas_sprites.insert(
sprite_ent,
AtlasSprite {
atlas: demo.atlas,
..default()
},
);
animated_sprites.insert(
sprite_ent,
AnimatedSprite {
frames: demo.animation.iter().copied().collect(),
fps: demo.fps,
..default()
},
);
}
fn audio_demo_plugin(session: &mut Session) {
session
.install_plugin(DefaultSessionPlugin)
.add_system_to_stage(Update, back_to_menu_ui)
.add_system_to_stage(Update, audio_demo_ui);
}
fn audio_demo_ui(
ctx: Res<EguiCtx>,
localization: Localization<GameMeta>,
mut audio: ResMut<AudioManager>,
meta: Root<GameMeta>,
assets: Res<AssetServer>,
) {
egui::CentralPanel::default()
.frame(egui::Frame::none())
.show(&ctx, |ui| {
ui.vertical_centered(|ui| {
ui.add_space(50.0);
if ui.button(localization.get("play-sound")).clicked() {
audio.play(&*assets.get(meta.audio_demo)).unwrap();
}
})
});
}
fn storage_demo_plugin(session: &mut Session) {
session
.install_plugin(DefaultSessionPlugin)
.add_system_to_stage(Update, storage_demo_ui)
.add_system_to_stage(Update, back_to_menu_ui);
}
fn storage_demo_ui(
ctx: Res<EguiCtx>,
mut storage: ResMut<Storage>,
localization: Localization<GameMeta>,
) {
egui::CentralPanel::default().show(&ctx, |ui| {
ui.add_space(20.0);
ui.vertical_centered(|ui| {
ui.set_width(300.0);
{
let data = storage.get_or_insert_default_mut::<PersistedTextData>();
egui::TextEdit::singleline(&mut data.0)
.hint_text(localization.get("persisted-text-box-content"))
.show(ui);
}
if ui.button(localization.get("save")).clicked() {
storage.save()
}
});
});
}
fn path2d_demo_plugin(session: &mut Session) {
session
.install_plugin(DefaultSessionPlugin)
.add_startup_system(path2d_demo_startup)
.add_system_to_stage(Update, back_to_menu_ui);
}
fn path2d_demo_startup(
meta: Root<GameMeta>,
mut entities: ResMut<Entities>,
mut transforms: CompMut<Transform>,
mut cameras: CompMut<Camera>,
mut path2ds: CompMut<Path2d>,
) {
spawn_default_camera(&mut entities, &mut transforms, &mut cameras);
let ent = entities.create();
transforms.insert(ent, default());
const SIZE: f32 = 40.;
path2ds.insert(
ent,
Path2d {
color: meta.path2d_color,
points: vec![
vec2(-SIZE, 0.),
vec2(0., SIZE),
vec2(SIZE, 0.),
vec2(-SIZE, 0.),
],
thickness: 2.0,
..default()
},
);
}
fn back_to_menu_ui(
ctx: Res<EguiCtx>,
mut sessions: ResMut<Sessions>,
mut session_options: ResMut<SessionOptions>,
localization: Localization<GameMeta>,
) {
egui::TopBottomPanel::bottom("back-to-menu")
.frame(egui::Frame::none())
.show_separator_line(false)
.show(&ctx, |ui| {
ui.with_layout(egui::Layout::bottom_up(egui::Align::Center), |ui| {
ui.add_space(20.0);
if ui.button(localization.get("back-to-menu")).clicked() {
session_options.delete = true;
sessions.create("menu").install_plugin(menu_plugin);
}
});
});
}
fn demo_widget(
mut ui: In<&mut egui::Ui>,
meta: Root<GameMeta>,
egui_textures: Res<EguiTextures>,
) -> egui::Response {
ui.label("Demo Widget");
ui.image(egui::load::SizedTexture::new(
egui_textures.get(meta.menu_image),
[50., 50.],
))
}