diff --git a/Cargo.lock b/Cargo.lock index d2eb367..67b85ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -283,7 +283,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -321,7 +321,7 @@ checksum = "f548ad2c4031f2902e3edc1f29c29e835829437de49562d8eb5dc5584d3a1043" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -538,7 +538,7 @@ checksum = "2656316165dbe2af6b3acaa763332f5dbdd12f809d59f5bf4304e0642a8005c9" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -661,7 +661,7 @@ dependencies = [ "bevy_macro_utils", "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -736,7 +736,7 @@ checksum = "1b837bf6c51806b10ebfa9edf1844ad80a3a0760d6c5fac4e90761df91a8901a" dependencies = [ "bevy_macro_utils", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -794,7 +794,7 @@ dependencies = [ "bevy_macro_utils", "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -893,7 +893,7 @@ dependencies = [ "bevy_macro_utils", "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -1062,7 +1062,7 @@ dependencies = [ "parking_lot", "proc-macro2", "quote", - "syn", + "syn 2.0.101", "toml_edit", ] @@ -1239,7 +1239,7 @@ dependencies = [ "bevy_macro_utils", "proc-macro2", "quote", - "syn", + "syn 2.0.101", "uuid", ] @@ -1327,7 +1327,7 @@ dependencies = [ "bevy_macro_utils", "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -1424,7 +1424,7 @@ dependencies = [ "bevy_macro_utils", "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -1556,6 +1556,23 @@ dependencies = [ "tracing", ] +[[package]] +name = "bevy_ui_text_input" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d020c0140e054a114f3bacb31c1a57bc9ae6a544025cbf02cc492b4a426510" +dependencies = [ + "arboard", + "bevy", + "cosmic_undo_2", + "once_cell", + "regex", + "sys-locale", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "bevy_utils" version = "0.16.1" @@ -1638,7 +1655,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn", + "syn 2.0.101", ] [[package]] @@ -1768,7 +1785,7 @@ checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -1874,6 +1891,46 @@ dependencies = [ "libloading", ] +[[package]] +name = "clap" +version = "4.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "clap_lex" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" + [[package]] name = "clipboard-win" version = "5.4.0" @@ -2061,6 +2118,17 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "cosmic_undo_2" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afd123aba915d4643617882bd494db7aaa4be4f4ed84e7b8cee2fd45efe41afe" +dependencies = [ + "derivative", + "rustc_version", + "serde", +] + [[package]] name = "cpal" version = "0.15.3" @@ -2176,6 +2244,17 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "derive_more" version = "1.0.0" @@ -2193,7 +2272,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", "unicode-xid", ] @@ -2323,7 +2402,7 @@ checksum = "f97b51c5cc57ef7c5f7a0c57c250251c49ee4c28f819f87ac32f4aceabc36792" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -2563,7 +2642,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -2789,7 +2868,7 @@ dependencies = [ "inflections", "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -3108,7 +3187,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -3181,7 +3260,7 @@ checksum = "f3c30758ddd7188629c6713fc45d1188af4f44c90582311d0c8d8c9907f60c48" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -3472,8 +3551,11 @@ dependencies = [ "bevy-inspector-egui", "bevy_skein", "bevy_third_person_camera", + "bevy_ui_text_input", + "clap", "env_logger", "log", + "shlex", ] [[package]] @@ -3733,7 +3815,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -3784,7 +3866,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -4226,7 +4308,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -4332,7 +4414,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.101", ] [[package]] @@ -4369,7 +4451,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" dependencies = [ "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -4675,6 +4757,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.38.44" @@ -4779,6 +4870,21 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749" +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + [[package]] name = "send_wrapper" version = "0.6.0" @@ -4802,7 +4908,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -4979,6 +5085,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "strum" version = "0.26.3" @@ -4998,7 +5110,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn", + "syn 2.0.101", ] [[package]] @@ -5018,6 +5130,17 @@ dependencies = [ "zeno", ] +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.101" @@ -5117,7 +5240,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -5128,7 +5251,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -5255,7 +5378,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -5456,7 +5579,7 @@ checksum = "41b6d82be61465f97d42bd1d15bf20f3b0a3a0905018f38f9d6f6962055b0b5c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -5524,7 +5647,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn", + "syn 2.0.101", "wasm-bindgen-shared", ] @@ -5559,7 +5682,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5967,7 +6090,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -5978,7 +6101,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -5989,7 +6112,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -6000,7 +6123,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -6011,7 +6134,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -6022,7 +6145,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -6518,7 +6641,7 @@ checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index dbd110b..f5d43e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,9 @@ bevy_third_person_camera = "0.3.0" env_logger = "0.11.8" log = "0.4.27" bevy_skein = { git = "https://github.com/rust-adventure/skein.git" } +clap = { version = "4", features = ["derive"] } +shlex = "1.3.0" +bevy_ui_text_input = "0.5.2" # Enable a small amount of optimization in the dev profile. [profile.dev] diff --git a/assets/fonts/DepartureMono-1.500/DepartureMono-Regular.otf b/assets/fonts/DepartureMono-1.500/DepartureMono-Regular.otf new file mode 100644 index 0000000..e0b2502 Binary files /dev/null and b/assets/fonts/DepartureMono-1.500/DepartureMono-Regular.otf differ diff --git a/assets/fonts/DepartureMono-1.500/LICENSE b/assets/fonts/DepartureMono-1.500/LICENSE new file mode 100644 index 0000000..de52476 --- /dev/null +++ b/assets/fonts/DepartureMono-1.500/LICENSE @@ -0,0 +1,93 @@ +Copyright 2022–2024 Helena Zhang (helenazhang.com). + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/src/debugging.rs b/src/debugging.rs index 3147859..02560fa 100644 --- a/src/debugging.rs +++ b/src/debugging.rs @@ -1,19 +1,14 @@ //! Seperate out debugging uis/plugins in it's own module for cleanliness. use bevy::prelude::*; -use bevy_inspector_egui::{ - bevy_egui::EguiPlugin, - quick::{StateInspectorPlugin, WorldInspectorPlugin}, -}; - -use crate::{AppState, menus}; +use bevy_inspector_egui::bevy_egui::EguiPlugin; pub fn plugin(app: &mut App) { app.add_plugins(EguiPlugin { enable_multipass_for_primary_context: true, }) .add_plugins(( - WorldInspectorPlugin::new(), - StateInspectorPlugin::::new(), - StateInspectorPlugin::::new(), + // WorldInspectorPlugin::new(), + // StateInspectorPlugin::::new(), + // StateInspectorPlugin::::new(), )); } diff --git a/src/game.rs b/src/game.rs index 8120eb6..731d28e 100644 --- a/src/game.rs +++ b/src/game.rs @@ -6,6 +6,7 @@ use crate::{ }; mod camera; +mod debug; mod scene; mod tram { use bevy::prelude::*; @@ -28,6 +29,6 @@ pub fn plugin(app: &mut App) { OnExit(AppState::Ingame), despawn::.in_set(GameplaySet), ) - .add_plugins(camera::plugin); + .add_plugins((debug::plugin, camera::plugin)); app.configure_sets(Update, GameplaySet.run_if(in_state(AppState::Ingame))); } diff --git a/src/game/camera.rs b/src/game/camera.rs index a1c1fa0..fddf235 100644 --- a/src/game/camera.rs +++ b/src/game/camera.rs @@ -3,7 +3,7 @@ use bevy::prelude::*; use bevy_third_person_camera::*; -use crate::{AppState, TPCTarget}; +use crate::AppState; use super::GameplaySet; diff --git a/src/game/debug.rs b/src/game/debug.rs new file mode 100644 index 0000000..6eeee43 --- /dev/null +++ b/src/game/debug.rs @@ -0,0 +1,13 @@ +use bevy::prelude::*; + +mod console; + +#[derive(Event)] +enum DebugEvent { + CloseDebugConsole, + PrintToConsole(String), +} + +pub(super) fn plugin(app: &mut App) { + app.add_plugins(console::plugin).add_event::(); +} diff --git a/src/game/debug/console.rs b/src/game/debug/console.rs new file mode 100644 index 0000000..d00d656 --- /dev/null +++ b/src/game/debug/console.rs @@ -0,0 +1,105 @@ +use bevy::prelude::*; +use ui::{ + DebugConsole, autofocus, autoscroll, handle_open_console, process_prompt, update_content, + update_scroll_position, +}; + +use crate::{AppState, cleanup::despawn, game::GameplaySet}; + +use super::DebugEvent; + +mod cli; +mod ui; + +pub(super) fn plugin(app: &mut App) { + app.init_state::() + .add_systems( + OnEnter(ConsoleState::Open), + (open_console, (autofocus, autoscroll).after(open_console)), + ) + .add_systems( + Update, + ( + handle_open_console + .in_set(GameplaySet) + .run_if(in_state(ConsoleState::Closed).and(in_state(AppState::Ingame))), + update_content.run_if(resource_changed::), + ( + update_scroll_position, + process_prompt, + execute_console_events, + ) + .run_if(in_state(ConsoleState::Open)), + ), + ) + .add_systems(OnExit(ConsoleState::Open), despawn::) + .insert_resource::(ConsoleLog::new()); +} + +/// the usize is a read index +#[derive(Resource, Clone)] +struct ConsoleLog(Vec, usize); + +impl ConsoleLog { + fn new() -> Self { + Self( + vec![ConsoleEvent::Output(String::from( + "Welcome to the dev console :3\n (i should probably put some proper info into this some time)", + ))], + 1, + ) + } + + fn unread(&mut self) -> &[ConsoleEvent] { + let res = &self.0[self.1..]; + self.1 = self.0.len(); + res + } + + pub fn input(&mut self, content: &str) { + self.0.push(ConsoleEvent::Input(content.to_string())) + } + + pub fn output(&mut self, content: &str) { + self.0.push(ConsoleEvent::Output(content.to_string())) + } + + pub fn error(&mut self, content: &str) { + self.0.push(ConsoleEvent::Error(content.to_string())) + } +} + +#[derive(Clone, Debug)] +enum ConsoleEvent { + Input(String), + Output(String), + Error(String), +} + +const OPEN_CONSOLE_DEFAULT: KeyCode = KeyCode::KeyC; + +#[derive(States, Default, Debug, Clone, PartialEq, Eq, Hash, Reflect)] +enum ConsoleState { + #[default] + Closed, + Open, +} + +fn open_console(mut c: Commands, console_data: Res, asset_server: Res) { + log::info!("opening debug console..."); + c.spawn(ui::console(console_data.clone(), asset_server.clone())); +} + +fn execute_console_events( + mut ev_reader: EventReader, + mut log: ResMut, + mut next_state: ResMut>, +) { + for ev in ev_reader.read() { + match ev { + DebugEvent::CloseDebugConsole => next_state.set(ConsoleState::Closed), + DebugEvent::PrintToConsole(s) => log.output(s), + _ => todo!(), + }; + } +} diff --git a/src/game/debug/console/cli.rs b/src/game/debug/console/cli.rs new file mode 100644 index 0000000..4fe321d --- /dev/null +++ b/src/game/debug/console/cli.rs @@ -0,0 +1,32 @@ +use clap::{Parser, Subcommand}; + +use crate::game::debug::DebugEvent; + +pub(super) fn respond(line: &str) -> Result { + let might_be_help_call = line.contains("help") || line.contains("-h"); + let args = shlex::split(line).ok_or("Invalid Quoting")?; + let cli = Cli::try_parse_from(args).map_err(|e| e.to_string()); + + if might_be_help_call && cli.as_ref().is_err_and(|item| item.starts_with("Usage: ")) { + let Err(s) = cli else { unreachable!() }; + return Ok(DebugEvent::PrintToConsole(s)); + } + + Ok(match cli?.cmd { + Commands::Close => DebugEvent::CloseDebugConsole, + Commands::Echo { text } => DebugEvent::PrintToConsole(text), + }) +} + +#[derive(Debug, Parser)] +#[command(multicall = true)] +struct Cli { + #[command(subcommand)] + cmd: Commands, +} + +#[derive(Debug, Subcommand)] +enum Commands { + Close, + Echo { text: String }, +} diff --git a/src/game/debug/console/ui.rs b/src/game/debug/console/ui.rs new file mode 100644 index 0000000..de265e3 --- /dev/null +++ b/src/game/debug/console/ui.rs @@ -0,0 +1,112 @@ +use bevy::{ + input::mouse::{MouseScrollUnit, MouseWheel}, + input_focus::InputFocus, + picking::hover::HoverMap, + prelude::*, +}; + +mod components; + +#[derive(Component)] +pub(super) struct DebugConsole; +#[derive(Component)] +struct TextInput; + +use bevy_ui_text_input::TextSubmitEvent; +pub(super) use components::console; +use components::{Content, Prompt}; + +use crate::game::debug::DebugEvent; + +use super::{ConsoleEvent, ConsoleLog, ConsoleState, OPEN_CONSOLE_DEFAULT, cli::respond}; + +// stolen from scrolling example +pub fn update_scroll_position( + mut mouse_wheel_events: EventReader, + hover_map: Res, + mut scrolled_node_query: Query<&mut ScrollPosition>, +) { + for mouse_wheel_event in mouse_wheel_events.read() { + let (dx, dy) = match mouse_wheel_event.unit { + MouseScrollUnit::Line => (mouse_wheel_event.x * 18., mouse_wheel_event.y * 18.), + MouseScrollUnit::Pixel => (mouse_wheel_event.x, mouse_wheel_event.y), + }; + + for (_pointer, pointer_map) in hover_map.iter() { + for (entity, _hit) in pointer_map.iter() { + if let Ok(mut scroll_position) = scrolled_node_query.get_mut(*entity) { + scroll_position.offset_x -= dx; + scroll_position.offset_y -= dy; + } + } + } + } +} + +pub fn autofocus(mut input_focus: ResMut, prompt: Single>) { + input_focus.set(*prompt); +} + +pub fn autoscroll(mut scrollables: Single<&mut ScrollPosition, With>) { + scrollables.offset_y = f32::MAX; +} + +pub fn handle_open_console( + kb_input: Res>, + mut console_open_state: ResMut>, +) { + if kb_input.pressed(OPEN_CONSOLE_DEFAULT) { + console_open_state.set(ConsoleState::Open); + } +} + +pub fn process_prompt( + mut log: ResMut, + mut submit_reader: EventReader, + mut debug_event_writer: EventWriter, + prompt: Single>, +) { + for submit in submit_reader.read() { + if submit.entity == *prompt && !submit.text.trim().is_empty() { + if submit.text.trim() != "close" { + log.input(&submit.text); + } + match respond(&submit.text) { + Ok(debug_ev) => { + debug_event_writer.write(debug_ev); + } + Err(e) => log.error(&e), + } + } + } +} + +pub fn update_content( + mut c: Commands, + content: Single>, + mut log: ResMut, + asset_server: Res, + mut scrollables: Single<&mut ScrollPosition, With>, +) { + c.entity(*content).with_children(|parent| { + for item in log.unread() { + match dbg!(item) { + ConsoleEvent::Input(s) => { + parent.spawn(components::input(s.to_string(), &asset_server)); + } + ConsoleEvent::Output(s) => { + parent.spawn(components::output(s.to_string(), &asset_server)); + } + ConsoleEvent::Error(s) => { + parent.spawn(components::error( + // dirty hack so they're not called subcommands + s.replace("subcommand", "command").to_string(), + &asset_server, + )); + } + _ => {} + }; + } + }); + scrollables.offset_y = f32::MAX; +} diff --git a/src/game/debug/console/ui/components.rs b/src/game/debug/console/ui/components.rs new file mode 100644 index 0000000..c9e6d0e --- /dev/null +++ b/src/game/debug/console/ui/components.rs @@ -0,0 +1,180 @@ +use bevy::{ecs::spawn::SpawnWith, prelude::*}; +use bevy_ui_text_input::{TextInputMode, TextInputNode, TextInputPrompt}; + +use crate::game::debug::console::{ConsoleEvent, ConsoleLog}; + +use super::DebugConsole; + +#[derive(Component)] +pub struct Prompt; +#[derive(Component)] +pub struct Content; + +pub fn console(data: ConsoleLog, asset_server: AssetServer) -> impl Bundle { + ( + DebugConsole, + Node { + justify_self: JustifySelf::Center, + width: Val::Percent(85.), + max_height: Val::Percent(100. / 3.), + flex_direction: FlexDirection::Column, + border: UiRect { + left: Val::Px(2.), + right: Val::Px(2.), + top: Val::Px(0.), + bottom: Val::Px(2.), + }, + ..default() + }, + BorderColor(Color::BLACK), + ZIndex(i32::MAX), + BackgroundColor(Color::Srgba(Srgba::BLACK.with_alpha(0.2))), + children![ + ( + Content, + Node { + padding: UiRect::all(Val::Px(10.)), + overflow: Overflow::scroll_y(), + flex_direction: FlexDirection::Column, + ..default() + }, + Children::spawn(SpawnWith({ + let asset_server = asset_server.clone(); + move |parent: &mut ChildSpawner| { + for item in data.0 { + match item { + ConsoleEvent::Input(s) => { + parent.spawn(input(s.to_string(), &asset_server)) + } + ConsoleEvent::Output(s) => { + parent.spawn(output(s.to_string(), &asset_server)) + } + ConsoleEvent::Error(s) => { + parent.spawn(error(s.to_string(), &asset_server)) + } + }; + } + } + })) + ), + ( + Node { + width: Val::Percent(100.), + padding: UiRect { + left: Val::Px(10.), + right: Val::Px(10.), + top: Val::Px(10.), + bottom: Val::Px(10.) + }, + ..default() + }, + BackgroundColor(Color::Srgba(Srgba::gray(0.2).with_alpha(0.3))), + children![ + ( + Text::new("> "), + TextFont { + font: asset_server + .load("fonts/DepartureMono-1.500/DepartureMono-Regular.otf"), + font_size: 14., + ..default() + } + ), + ( + Prompt, + Node { + width: Val::Auto, + flex_grow: 1., + ..default() + }, + TextInputNode { + clear_on_submit: true, + mode: TextInputMode::SingleLine, + is_enabled: true, + focus_on_pointer_down: true, + ..default() + }, + TextInputPrompt { + text: String::new(), + ..default() + }, + TextFont { + font: asset_server + .load("fonts/DepartureMono-1.500/DepartureMono-Regular.otf"), + font_size: 14., + ..default() + }, + ) + ] + ), + ], + ) +} + +pub fn input(content: String, asset_server: &AssetServer) -> impl Bundle { + console_output( + Srgba::GREEN.with_alpha(0.05), + UiRect::all(Val::Px(1.)), + Srgba::GREEN, + format!("> {content}"), + asset_server, + ) +} +pub fn output(content: String, asset_server: &AssetServer) -> impl Bundle { + console_output( + Srgba::WHITE.with_alpha(0.05), + UiRect::all(Val::ZERO), + Srgba::NONE, + content, + asset_server, + ) +} +pub fn error(content: String, asset_server: &AssetServer) -> impl Bundle { + console_output( + Srgba::RED.with_alpha(0.025), + UiRect::all(Val::Px(2.)), + Srgba::RED.with_alpha(0.75), + content, + asset_server, + ) +} + +fn console_output( + background_color: Srgba, + border_width: UiRect, + border_color: Srgba, + content: String, + asset_server: &AssetServer, +) -> impl Bundle { + ( + Node { + margin: UiRect { + left: Val::Px(0.), + right: Val::Px(0.), + top: Val::Px(0.), + bottom: Val::Px(10.), + }, + padding: UiRect::all(Val::Px(7.5)), + border: border_width, + ..default() + }, + BorderRadius::all(Val::Px(7.5)), + BorderColor(Color::Srgba(border_color)), + BackgroundColor(Color::Srgba(background_color)), + Pickable { + should_block_lower: false, + ..default() + }, + children![( + Text(content), + TextFont { + font: asset_server.load("fonts/DepartureMono-1.500/DepartureMono-Regular.otf"), + font_size: 14., + ..default() + }, + Pickable { + should_block_lower: false, + ..default() + }, + )], + ) +} diff --git a/src/main.rs b/src/main.rs index e5035a4..6868b28 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ #![feature(iter_collect_into)] use bevy::prelude::*; use bevy_skein::SkeinPlugin; +use bevy_ui_text_input::TextInputPlugin; mod camera; mod cleanup; @@ -21,7 +22,7 @@ fn main() { App::new() .register_type::() .add_systems(Startup, camera::setup) - .add_plugins(DefaultPlugins) + .add_plugins((DefaultPlugins, TextInputPlugin)) .add_plugins((game::plugin, menus::plugin, debugging::plugin)) .add_plugins(SkeinPlugin::default()) .init_state::()