diff --git a/README.md b/README.md index b646f25..4c0c224 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Textship # +# Text Sub # -Build a spaceship out of ASCII Symbols and fight in a 2 Player deathmatch. +Build a submarine out of ASCII Symbols and fight in a multiplayer deathmatch. ## Setup Bevy to run in the browser ## @@ -49,6 +49,11 @@ Build a spaceship out of ASCII Symbols and fight in a 2 Player deathmatch. ## Client: Run a development build ## +- Run the signalling server locally: + ``` + matchbox_server + ``` + - Start the wasm-server-runner: ``` cargo run --target wasm32-unknown-unknown @@ -56,11 +61,6 @@ Build a spaceship out of ASCII Symbols and fight in a 2 Player deathmatch. - Open a browser tab at 127.0.0.1:1334 (or whatever the port is). -- Run the signalling server locally: - ``` - matchbox_server - ``` - Multiple tabs can connect to the wasm-server-runner, which means you can run multiple clients on the same machine. ### Client: Run tests ### @@ -77,14 +77,18 @@ wasm-pack build --release --target bundler --out-dir target/bundle ## FAQ ## -### My ships don't move when I open two tabs! ### +### My subs don't move when I open two tabs! ### -This is a known bug with the ship detection algorithm. It will work with multi windows and real 2 player scenarios. +This is a known bug with the sub detection algorithm. It will work with multi windows and real 2 player scenarios. ## Libraries ## - rust syntax refresher: https://learnxinyminutes.com/docs/rust/ +- rust book: https://doc.rust-lang.org/stable/book/ + +- rust api: https://doc.rust-lang.org/std/ + - bevy api: https://docs.rs/bevy/latest/bevy/ - bevy unofficial book: https://bevy-cheatbook.github.io/ @@ -103,6 +107,12 @@ This is a known bug with the ship detection algorithm. It will work with multi w - rocket api: https://api.rocket.rs/v0.5-rc/rocket/ -- matchbox: https://github.com/johanhelsing/matchbox/ +- bevy_ggrs api: https://docs.rs/bevy_ggrs/0.12.0/bevy_ggrs/ + +- bevy_matchbox api: https://docs.rs/bevy_matchbox/0.7.0/bevy_matchbox/index.html - wasm server runner: https://github.com/jakobhellermann/wasm-server-runner + +- bevy_ui api: https://docs.rs/bevy_ui/latest/bevy_ui/ + +- bevy_pkg: https://docs.rs/bevy_pkv/0.8.0/bevy_pkv/ diff --git a/TODO.md b/TODO.md index 8b13789..a6909d4 100644 --- a/TODO.md +++ b/TODO.md @@ -1 +1,19 @@ +- ship builder feature + - fix not able to draw outside the box + + - fix being able to draw where parts are not connected + + - spawn placement grid + +- fixes feature + + - direction of torpedo should rotate with ship + + - change explosions into text, not sprites + + - change torpedos into text, not sprites + + - fix player should be in the center of the screen at all times + + - send sub builds to other players diff --git a/client/Cargo.toml b/client/Cargo.toml index 712ac8d..1da9518 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "textship" +name = "textsub" version = "0.1.0" edition = "2021" authors = ["doublecouponday ", "bobscorn " ] @@ -7,38 +7,43 @@ authors = ["doublecouponday ", "bobscorn ", "bobscorn " ] diff --git a/client/components.rs b/client/components.rs index d8ad9af..7e7d2c8 100644 --- a/client/components.rs +++ b/client/components.rs @@ -1,4 +1,24 @@ use bevy::prelude::*; +use crate::constants::*; +use crate::enums::*; +use crate::resources::*; +use crate::structs::*; + +#[derive(Component)] +pub struct SymbolButton {} + +#[derive(Component)] +pub struct HoverText {} + +#[derive(Component)] +pub struct MyButton { + pub identifier: ButtonType +} + +#[derive(Component)] +pub struct SubBuilderButton { + pub part: SubPart, +} #[derive(Component, Reflect, Default)] pub struct Velocity { @@ -105,7 +125,7 @@ pub struct HangarDoor { } #[derive(Component)] -pub struct Thruster { +pub struct Propeller { } @@ -114,11 +134,18 @@ pub struct FighterDrone { } -#[derive(Component, Default)] -pub struct Mothership { +#[derive(Component)] +pub struct SubsRoot { + pub parts: Vec, pub num_entities: u32 } +impl Default for SubsRoot { + fn default() -> Self { + SubsRoot { parts: Vec::new(), num_entities: 0 } + } +} + #[derive(Component)] pub struct Player { pub handle: usize @@ -128,3 +155,12 @@ pub struct Player { pub struct Lifetime { pub lifetime: f32 // Remaining Lifetime in seconds } + +#[derive(Component)] +pub struct MainCamera; + +#[derive(Component)] +pub struct MySubmarineTag; + +#[derive(Component)] +pub struct UnerasablePiece; diff --git a/client/constants.rs b/client/constants.rs index 39edf50..119fb30 100644 --- a/client/constants.rs +++ b/client/constants.rs @@ -1,37 +1,56 @@ - -pub const MOTHERSHIP_SPEED: f32 = 0.25; +pub const SUB_SPEED: f32 = 0.25; pub const ROTATION_SPEED: f32 = 0.025; -pub const MOTHERSHIP_STRUCTURE_SPACING: f32 = 15.0; +pub const SUB_STRUCTURE_SPACING: f32 = 3.5; +pub const SUB_SCALE: f32 = 0.2; +pub const SUB_MAX_WIDTH: usize = 100; +pub const SUB_MAX_HEIGHT: usize = 100; pub const TEXT_FONT_SIZE: f32 = 30.0; +pub const EMPTY_CHAR: char = '\0'; + +//armour +pub const ARMOUR_LABEL: &'static str = "Hull Panel"; +pub const ARMOUR_DESC: &'static str = "Submarine construction part. Suitable for pressurised environments."; pub const SQUARE_OPEN: char = '['; pub const SQUARE_CLOSE: char = ']'; -pub const ANGLE_OPEN: char = '<'; -pub const ANGLE_CLOSE: char = '>'; + pub const ROUND_OPEN: char = '('; pub const ROUND_CLOSE: char = ')'; + pub const CURLY_OPEN: char = '{'; -pub const CURL_CLOSE: char = '}'; +pub const CURLY_CLOSE: char = '}'; pub const BACKSLASH: char = '\\'; pub const FORWARD_SLASH: char = '/'; pub const PIPE: char = '|'; -pub const EQUALS: char = '='; -pub const PERCENT: char = '%'; -pub const TILDE: char = '~'; -pub const FULLSTOP: char = '.'; +//utilities +pub static PROPELLER_LABEL: &'static str = "Propeller "; +pub static PROPELLER_DESCRIPTION: &'static str = "Used for propulsion in the watery depths."; pub const PLUS: char = '+'; pub const MINUS: char = '-'; -pub const BACKTICK: char = '`'; -pub const STAR: char = '*'; + +pub static TORPEDO_LABEL: &'static str = "Torpedo "; +pub static TORPEDO_DESC: &'static str = "Explodey thing"; pub const EXCLAMATION: char = '!'; + + +pub static TORPEDO_LAUNCHER_LABEL: &'static str = "Torpedo Launcher"; +pub static TORPEDO_LAUNCHER_DESC: &'static str = "Throws explodey things."; +pub const EQUALS: char = '='; + +pub static REACTOR_LABEL: &'static str = "Reactor "; +pub static REACTOR_DESC: &'static str = "Generates power using unholy science."; pub const AT: char = '@'; -pub const DOLLAR: char = '$'; -pub const QUESTION: char = '?'; -pub const APOSTROPHE: char = '\''; -pub const COLON: char = ':'; +//debris +pub const TILDE: char = '~'; +pub const FULLSTOP: char = '.'; +pub const BUBBLE: char = 'o'; + +pub const TEAM_KEY: &'static str = "amogus"; +pub const GAME_NAME: &'static str = "Text Sub"; +pub const CACHED_KEY: &'static str = "saved_build"; diff --git a/client/enums.rs b/client/enums.rs new file mode 100644 index 0000000..8d82e3a --- /dev/null +++ b/client/enums.rs @@ -0,0 +1,75 @@ +use std::{default, f32::consts::PI}; + +use bevy::prelude::*; +use serde::{Serialize, Deserialize}; + +use crate::resources::Submarine; + +#[derive(PartialEq)] +pub enum ButtonType { + PlayButton, + SubBuilderButton, + BackToMenuButton, + SaveButton +} + +#[derive(States, Clone, Eq, PartialEq, Debug, Hash, Default)] +pub enum GameState { + #[default] + AssetLoading, + SubBuilding, + MainMenu, + MatchMaking, + SubSyncing, + InGame, +} + +#[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize, Reflect)] +pub enum PieceRotation { + #[default] + North, + East, + South, + West +} + +impl PieceRotation { + pub fn default() -> PieceRotation { + PieceRotation::North + } + + pub fn rotated_cw(&self) -> Self { + match self { + Self::North => Self::East, + Self::East => Self::South, + Self::South => Self::West, + Self::West => Self::North + } + } + + pub fn rotated_ccw(&self) -> Self { + match self { + Self::North => Self::West, + Self::West => Self::South, + Self::South => Self::East, + Self::East => Self::North + } + } + + pub fn rotation_radians(&self) -> f32 { + match self { + Self::North => 0.0, + Self::East => PI * 1.5, + Self::South => PI, + Self::West => PI * 0.5 + } + } +} + +#[derive(Serialize, Deserialize)] +pub enum SyncShipsMessageType { + SyncShip(Submarine), + SyncShipAck, + ReadyToStart, + ReadyToStartAck +} diff --git a/client/events.rs b/client/events.rs index e540e43..4ccb362 100644 --- a/client/events.rs +++ b/client/events.rs @@ -1,20 +1,14 @@ use bevy::prelude::*; -#[derive(Debug)] +#[derive(Debug, Event)] pub struct TorpedoCollisionEvent { pub position: Vec3, pub damage: u8, pub radius: f32 } -#[derive(Debug)] +#[derive(Debug, Event)] pub struct SpawnTorpedoEvent { pub position: Vec3, pub velocity: Vec3 } - -#[derive(Resource, Default)] -pub struct FontResource { - pub font: TextStyle, - pub p1_font: TextStyle, -} diff --git a/client/game_state.rs b/client/game_state.rs deleted file mode 100644 index 37bf554..0000000 --- a/client/game_state.rs +++ /dev/null @@ -1,9 +0,0 @@ -use bevy::prelude::*; - -#[derive(States, Clone, Eq, PartialEq, Debug, Hash, Default)] -pub enum GameState { - #[default] - AssetLoading, - MatchMaking, - InGame, -} \ No newline at end of file diff --git a/client/implementations.rs b/client/implementations.rs new file mode 100644 index 0000000..aa91c33 --- /dev/null +++ b/client/implementations.rs @@ -0,0 +1,12 @@ +use bevy_ggrs::ggrs; +use bevy_matchbox::prelude::*; + +pub struct GgrsConfig; + +impl ggrs::Config for GgrsConfig { + // A single byte should fit a bunch of inputs + type Input = u8; + type State = u8; + + type Address = PeerId; +} diff --git a/client/lib.rs b/client/lib.rs index 8529ee4..e68020d 100644 --- a/client/lib.rs +++ b/client/lib.rs @@ -1,14 +1,18 @@ mod start; mod utils; mod plugin; -mod systems; mod events; mod constants; mod components; mod macros; -mod input; mod resources; -mod game_state; +mod implementations; +mod systems; +mod enums; +mod input; +mod world; +mod structs; +mod parts; use start::start; use utils::set_panic_hook; diff --git a/client/main.rs b/client/main.rs index 0f36c4d..506ae42 100644 --- a/client/main.rs +++ b/client/main.rs @@ -1,14 +1,18 @@ +mod start; mod utils; -mod components; -mod systems; -mod constants; -mod events; mod plugin; -mod start; +mod events; +mod constants; +mod components; mod macros; -mod input; mod resources; -mod game_state; +mod implementations; +mod systems; +mod enums; +mod input; +mod world; +mod structs; +mod parts; use wasm_bindgen::prelude::*; use start::start; diff --git a/client/parts.rs b/client/parts.rs new file mode 100644 index 0000000..6f441bc --- /dev/null +++ b/client/parts.rs @@ -0,0 +1,87 @@ + +use crate::constants::*; +use crate::structs::*; + +pub static ARMOUR1: SubPart = SubPart { + symbol: SQUARE_OPEN, + label: ARMOUR_LABEL, + description: ARMOUR_DESC +}; + +pub static ARMOUR2: SubPart = SubPart { + symbol: SQUARE_CLOSE, + label: ARMOUR_LABEL, + description: ARMOUR_DESC +}; + +pub static ARMOUR3: SubPart = SubPart { + symbol: ROUND_OPEN, + label: ARMOUR_LABEL, + description: ARMOUR_DESC +}; + +pub static ARMOUR4: SubPart = SubPart { + symbol: ROUND_CLOSE, + label: ARMOUR_LABEL, + description: ARMOUR_DESC +}; + +pub static ARMOUR5: SubPart = SubPart { + symbol: CURLY_OPEN, + label: ARMOUR_LABEL, + description: ARMOUR_DESC +}; + +pub static ARMOUR6: SubPart = SubPart { + symbol: CURLY_CLOSE, + label: ARMOUR_LABEL, + description: ARMOUR_DESC +}; + +pub static ARMOUR7: SubPart = SubPart { + symbol: BACKSLASH, + label: ARMOUR_LABEL, + description: ARMOUR_DESC +}; + +pub static ARMOUR8: SubPart = SubPart { + symbol: FORWARD_SLASH, + label: ARMOUR_LABEL, + description: ARMOUR_DESC +}; + +pub static ARMOUR9: SubPart = SubPart { + symbol: PIPE, + label: ARMOUR_LABEL, + description: ARMOUR_DESC +}; + +pub static PROPELLER1: SubPart = SubPart { + symbol: PLUS, + label: PROPELLER_LABEL, + description: PROPELLER_DESCRIPTION +}; + +pub static PROPELLER2: SubPart = SubPart { + symbol: MINUS, + label: PROPELLER_LABEL, + description: PROPELLER_DESCRIPTION +}; + +pub static TORPEDO: SubPart = SubPart { + symbol: EXCLAMATION, + label: TORPEDO_LABEL, + description: TORPEDO_DESC +}; + +pub static TORPEDO_LAUNCHER: SubPart = SubPart { + symbol: EQUALS, + label: TORPEDO_LAUNCHER_LABEL, + description: TORPEDO_LAUNCHER_DESC +}; + +pub static REACTOR: SubPart = SubPart { + symbol: AT, + label: REACTOR_LABEL, + description: REACTOR_DESC +}; diff --git a/client/plugin.rs b/client/plugin.rs index cb47374..c738b3a 100644 --- a/client/plugin.rs +++ b/client/plugin.rs @@ -1,11 +1,20 @@ use bevy::prelude::*; -use bevy_ggrs::GGRSSchedule; +extern crate bevy_ggrs; + +use bevy_ggrs::GgrsSchedule; use bevy_asset_loader::prelude::*; -use crate::events::{TorpedoCollisionEvent, SpawnTorpedoEvent, FontResource}; -use crate::game_state::GameState; -use crate::systems::*; +use crate::events::*; +use crate::enums::*; use crate::resources::*; +use crate::systems::movement::*; +use crate::systems::menus::*; +use crate::systems::multiplayer::*; +use crate::systems::sync_ships::synchronise_peer_ships; +use crate::systems::torpedos::*; +use crate::systems::utils::*; +use crate::systems::ui::*; +use crate::world::*; pub struct GamePlugin; @@ -15,14 +24,29 @@ impl Plugin for GamePlugin { .add_state::() .add_loading_state( LoadingState::new(GameState::AssetLoading) - .continue_to_state(GameState::MatchMaking) + .continue_to_state(GameState::MainMenu) ) .add_collection_to_loading_state::<_, ImageAssets>(GameState::AssetLoading) .add_event::() .add_event::() .insert_resource(FontResource::default()) - .add_system(wait_for_players.run_if(in_state(GameState::MatchMaking))) - .add_systems(( + .insert_resource(UiHandling::default()) + // General Systems + .add_systems(Startup, setup_world) + .add_systems(Update, (check_ui_interaction,handle_interaction_buttons, handle_menu_buttons)) + // Main Menu + .add_systems(OnEnter(GameState::MainMenu), setup_mainmenu.after(setup_world)) + .add_systems(OnExit(GameState::MainMenu), exit_main_menu) + // sub Building + .add_systems(OnEnter(GameState::SubBuilding), setup_sub_builder) + .add_systems(Update, (sub_builder_part_buttons, do_sub_builder_parts, rotate_sub_preview).run_if(in_state(GameState::SubBuilding))) + .add_systems(OnExit(GameState::SubBuilding), exit_sub_builder) + // Match making + .add_systems(Update, wait_for_players.run_if(in_state(GameState::MatchMaking))) + // Sub synchronisation + .add_systems(Update, synchronise_peer_ships.run_if(in_state(GameState::SubSyncing))) + // In Game + .add_systems(GgrsSchedule, ( fire_torpedo.after(player_action), player_action.before(move_projectile).before(accelerate_projectile), reload_torpedo.before(fire_torpedo), @@ -31,12 +55,10 @@ impl Plugin for GamePlugin { process_torpedo_collision.after(move_projectile), do_torpedo_events.after(process_torpedo_collision), do_lifetime.after(do_torpedo_events) - ).in_schedule(GGRSSchedule)) - .add_systems(( - setup_world.before(spawn_mothership), + )) + .add_systems(OnEnter(GameState::MatchMaking), ( start_matchbox_socket, - spawn_mothership - ).in_schedule(OnEnter(GameState::MatchMaking))); + spawn_mothersub.after(setup_world) + )); } } - diff --git a/client/resources.rs b/client/resources.rs index e383e2f..81863f8 100644 --- a/client/resources.rs +++ b/client/resources.rs @@ -1,6 +1,31 @@ use bevy::prelude::*; use bevy_asset_loader::prelude::*; +use bevy_ggrs::ggrs::PlayerType; +use bevy_matchbox::matchbox_socket::WebRtcChannel; +use bevy_matchbox::prelude::PeerId; +use serde::*; +use crate::structs::*; +use crate::enums::*; + +#[derive(Resource, Default)] +pub struct FontResource { + pub font: Handle, + pub preview_font: TextStyle, + pub p1_font: TextStyle, +} + +#[derive(Resource)] +pub struct UIMenu { + pub ui: Entity +} + +#[derive(Resource)] +pub struct SubBuilderPreview { + pub entity: Entity, + pub part: SubPart, + pub rotation: PieceRotation +} #[derive(Resource)] pub struct LocalPlayerHandle(pub usize); @@ -12,3 +37,57 @@ pub struct ImageAssets { #[asset(path = "explosion.png")] pub explosion: Handle, } + +#[derive(Resource)] +pub struct Colors { + pub normal_text: Color, + pub submarine_text: Color, + pub menu_background: Color, + pub node_background: Color, + pub button_normal: Color, + pub button_pressed: Color +} + +#[derive(Resource, Serialize, Deserialize, Clone)] +pub struct Submarine { + pub parts: Vec>, + pub rotations: Vec> +} + +impl Default for Submarine { + fn default() -> Self { + Submarine{ parts: Vec::new(), rotations: Vec::new() } + } +} + +#[derive(Resource, Serialize, Deserialize)] +pub struct EnemySubmarine { + pub parts: Vec>, + pub rotations: Vec> +} + +impl Default for EnemySubmarine { + fn default() -> Self { + EnemySubmarine{ parts: Vec::new(), rotations: Vec::new() } + } +} + +#[derive(Resource)] +pub struct SubBuilder { + pub root: Entity, + pub parts: Vec>> +} + +impl Default for SubBuilder { + fn default() -> Self { + SubBuilder { root: Entity::PLACEHOLDER, parts: Vec::new() } + } +} + +#[derive(Resource)] +pub struct SyncSubsSocket { + pub players: Vec>, + pub channel: Option, + pub synced: bool, + pub ready: bool +} diff --git a/client/start.rs b/client/start.rs index bb942c0..6a065a4 100644 --- a/client/start.rs +++ b/client/start.rs @@ -1,15 +1,18 @@ use bevy::prelude::*; -use bevy_ggrs::GGRSPlugin; +use bevy_ggrs::*; +use bevy_pkv::*; +use bevy_inspector_egui::quick::*; use crate::components::*; use crate::plugin::GamePlugin; -use crate::input::player_input; -use crate::systems::GgrsConfig; +use crate::input::*; +use crate::implementations::*; +use crate::constants::*; pub fn start() { let mut app = App::new(); - GGRSPlugin::::new() + GgrsPlugin::::new() .with_input_system(player_input) .register_rollback_component::() .register_rollback_component::() @@ -27,5 +30,7 @@ pub fn start() { ..default() })) .add_plugin(GamePlugin) + .add_plugin(WorldInspectorPlugin::new()) + .insert_resource(PkvStore::new(TEAM_KEY, GAME_NAME)) .run(); } diff --git a/client/structs.rs b/client/structs.rs new file mode 100644 index 0000000..0e7911c --- /dev/null +++ b/client/structs.rs @@ -0,0 +1,9 @@ +use std::clone::*; +use std::marker::*; + +#[derive(Copy, Clone)] +pub struct SubPart { + pub symbol: char, + pub label: &'static str, + pub description: &'static str +} diff --git a/client/systems.rs b/client/systems.rs deleted file mode 100644 index 188158f..0000000 --- a/client/systems.rs +++ /dev/null @@ -1,360 +0,0 @@ -use std::f32::consts::PI; -use std::vec; - -use bevy::prelude::*; -use bevy::window::PrimaryWindow; -use bevy_matchbox::prelude::*; -use bevy_ggrs::*; -use bevy_ggrs::ggrs::*; - -use crate::constants::*; -use crate::components::*; -use crate::events::*; -use crate::game_state::GameState; -use crate::resources::*; -use crate::input::*; -pub struct GgrsConfig; - -impl ggrs::Config for GgrsConfig { - // A single byte should fit a bunch of inputs - type Input = u8; - type State = u8; - - type Address = PeerId; -} - -pub fn start_matchbox_socket(mut commands: Commands) { - let room_url = "ws://127.0.0.1:3536/extreme_bevy?next=2"; - info!("connecting to matchbox server: {:?}", room_url); - - let socket: MatchboxSocket = WebRtcSocketBuilder::new(room_url) - .add_channel(ChannelConfig::reliable()) - .into(); - - commands.insert_resource(socket); -} - -pub fn wait_for_players( - mut socket: ResMut>, - mut commands: Commands, - mut next_state: ResMut> -) { - if socket.get_channel(0).is_err() { - return; // We've already started - } - - socket.update_peers(); - let players = socket.players(); - - let num_players = 2; - if players.len() < num_players { - return; // Not enough players yet - } - - info!("All peers have joined, going in game!"); - crate::log!("All peers have joined, going in game!"); - - let mut session_builder = ggrs::SessionBuilder::::new() - .with_num_players(2) - .with_input_delay(2); - - for (i, player) in players.into_iter().enumerate() { - if player == PlayerType::Local { - commands.insert_resource(LocalPlayerHandle(i)); - } - session_builder = session_builder - .add_player(player, i) - .expect("Failed to add player"); - } - - // Move channel out of socket to give to ggrs - let channel = socket.take_channel(0).unwrap(); - - // Start the GGRS Session - let ggrs_session = session_builder - .start_p2p_session(channel) - .expect("Failed to start session"); - - commands.insert_resource(bevy_ggrs::Session::P2PSession(ggrs_session)); - - next_state.set(GameState::InGame); -} - -pub fn player_action( - inputs: Res>, - mut player_query: Query<(&mut Velocity, &Player, &mut Transform, &mut AngularVelocity), With> -) { - // Basic demonstrational movement for now - for (mut velocity, player, mut transform, mut angular) in player_query.iter_mut() { - let (input, _) = inputs[player.handle]; - - let direction = crate::input::direction(input); - - if direction != Vec2::ZERO { //player did not move - let move_delta = direction * MOTHERSHIP_SPEED; - velocity.value += move_delta; - } - - let angular_direction = crate::input::get_rotation(input); - - if angular_direction != 0.0_f32 { - angular.rotation += angular_direction; - } - transform.rotate_local_z(angular.rotation * PI / 180.0); - } -} - -pub fn reload_torpedo(inputs: Res>, mut query: Query<(&mut BulletReady, &Player)>) { - for (mut can_fire, player) in query.iter_mut() { - let (input, _) = inputs[player.handle]; - if !fire(input) { - can_fire.0 = true; - } - } -} - -pub fn fire_torpedo( - mut commands: Commands, - inputs: Res>, - images: Res, - mut player_query: Query<(&Transform, &Player, &mut BulletReady)>, - mut rip: ResMut -) { - for (transform, player, mut bullet_ready) in player_query.iter_mut() { - let (input, _) = inputs[player.handle]; - if fire(input) && bullet_ready.0 { - commands.spawn(( - SpriteBundle { - transform: Transform::from_translation(transform.translation), - texture: images.bullet.clone(), - sprite: Sprite { - custom_size: Some(Vec2::new(5., 5.)), - ..default() - }, - ..default() - }, - Torpedo::default(), - Velocity { - value: Vec2::new(20., 0.) - }, Acceleration( - Vec2::new(5., 0.) - ), - rip.next() - )); - - bullet_ready.0 = false; - } - } -} - -pub fn setup_world(mut commands: Commands, mut font_res: ResMut, asset_server: Res) { - commands.spawn(Camera2dBundle{ - projection: OrthographicProjection { scaling_mode: bevy::render::camera::ScalingMode::FixedVertical(250.), ..default() }, - ..default() - }); - commands.insert_resource(ClearColor {0: Color::BLACK}); - - let font: Handle = asset_server.load("fonts/FallingSkyBlack.otf"); - - let text_style = TextStyle { - font: font.clone(), - font_size: TEXT_FONT_SIZE, - color: Color::LIME_GREEN, - }; - - font_res.font = text_style.clone(); - font_res.p1_font = TextStyle{ font: font.clone(), font_size: TEXT_FONT_SIZE, color: Color::LIME_GREEN }; -} - -pub fn spawn_mothership( - mut commands: Commands, - fonts: Res, - mut rip: ResMut, - wind_q: Query<&Window, With> -) { - //let window = wind_q.single(); - //let win_width = window.width(); - - //let ship_width = 5.5 * MOTHERSHIP_STRUCTURE_SPACING; - //let min_gap = 0.1; // 0.1 = 10% of window width - //let pos = (0.25 * win_width).max(min_gap * view_width + ship_width * 0.5); - let pos = MOTHERSHIP_STRUCTURE_SPACING * 10.5; - - let bottom_left = Vec3::new(-(MOTHERSHIP_STRUCTURE_SPACING * 5.5), -(MOTHERSHIP_STRUCTURE_SPACING * 2.5), 0.); - - - let width = 11; - let height = 5; - - let chars = vec!["}", "{", "6", "=", "-", "/", ":", "]", "[", "!", "#", "%", "$"]; - - let base_poses = vec![Vec3::new(-pos, 0., 0.), Vec3::new(pos, 0., 0.)]; - - for i in 0..2 { - let ship_pos = base_poses[i]; - println!("Spawning ship at {:?}", ship_pos); - commands.spawn(( - SpriteBundle{ transform: Transform::from_scale(Vec3::ONE * 0.2f32) * Transform::from_translation(ship_pos), ..default() }, - Mothership::default(), - Velocity{ value: Vec2::ZERO }, - AngularVelocity::default(), - Player{ handle: i }, - BulletReady(true), - rip.next() - )) - .with_children(|parent| { - for x in 0..width { - for y in 0..height { - parent.spawn(( - Text2dBundle{ - text: Text { - sections: vec![TextSection::new(chars[(x + y) % 13], fonts.p1_font.clone())], - ..default() - }, - transform: Transform::from_translation(bottom_left + Vec3::new(x as f32 * MOTHERSHIP_STRUCTURE_SPACING, y as f32 * MOTHERSHIP_STRUCTURE_SPACING, 0.)), - ..default() - }, - Structure{ integrity: 5, max_integrity: 5 } - )); - } - } - } - ); - } -} - -pub fn print_position_system(query: Query<&Transform>) { - for transform in &query { - println!("position: {:?}", transform.translation); - } -} - -pub fn move_projectile(time: Res