diff --git a/src/states/linear/components_cannon.rs b/src/states/linear/components_cannon.rs new file mode 100644 index 0000000..bc631a4 --- /dev/null +++ b/src/states/linear/components_cannon.rs @@ -0,0 +1,46 @@ +use crate::states::linear::RoundBallType; +use bevy::prelude::*; +use std::mem; + +#[derive(Component)] +pub struct LinearCannon; + +#[derive(Resource, Debug, Clone)] +pub struct LinearCannonState { + pub shot: RoundBallType, + pub swap: RoundBallType, +} + +impl Default for LinearCannonState { + fn default() -> Self { + Self { + shot: RoundBallType::random_from_4(), + swap: RoundBallType::random_from_4(), + } + } +} + +impl LinearCannonState { + pub fn fire(&mut self) { + self.shot = self.swap; + self.swap = RoundBallType::random_from_4(); + } + + pub fn cycle(&mut self) { + mem::swap(&mut self.shot, &mut self.swap); + } +} + +#[derive(Component)] +pub struct RoundBallProjectile { + pub velocity: Vec2, + pub previous_position: Vec2, + pub ball_type: RoundBallType, +} + + +#[derive(Component)] +pub struct ShotMarker; + +#[derive(Component)] +pub struct SwapMarker; \ No newline at end of file diff --git a/src/states/linear/constants.rs b/src/states/linear/constants.rs index c1536a5..ae03297 100644 --- a/src/states/linear/constants.rs +++ b/src/states/linear/constants.rs @@ -11,6 +11,9 @@ pub const SCALE: f32 = 2.0; // Z-INDEXES pub const ROUND_BALL_Z: f32 = 10.0; +pub const ROUND_SHOT_Z: f32 = 11.0; +pub const LINEAR_CANNON_Z: f32 = 20.0; +pub const LINEAR_CANNON_BALL_Z: f32 = 15.0; // COLORS pub const PINK: Color = Color::srgb_u8(250, 0, 155); diff --git a/src/states/linear/mod.rs b/src/states/linear/mod.rs index c2c08ac..1bb90fd 100644 --- a/src/states/linear/mod.rs +++ b/src/states/linear/mod.rs @@ -21,5 +21,10 @@ pub use constants::*; mod ui; pub use ui::*; +mod components_cannon; +pub use components_cannon::*; +mod systems_cannon; +pub use systems_cannon::*; + diff --git a/src/states/linear/plugin.rs b/src/states/linear/plugin.rs index 68ccab7..3b82147 100644 --- a/src/states/linear/plugin.rs +++ b/src/states/linear/plugin.rs @@ -14,7 +14,7 @@ pub enum LinearUpdateSet { impl Plugin for LinearPlayPlugin { fn build(&self, app: &mut App) { app - .add_systems(OnEnter(LinearPlayState), setup) + .add_systems(OnEnter(LinearPlayState), (setup, spawn_linear_cannon).chain()) .add_systems(OnEnter(LinearGameRestart), linear_restart) .add_plugins(LinearUIPlugin) .configure_sets( @@ -26,8 +26,14 @@ impl Plugin for LinearPlayPlugin { ( draw_track_gizmos, draw_grid, + //несколько систем с соблюдением порядка + linear_move_projectiles, + cycle_cannon_balls, + update_linear_cannon_preview, + spawn_projectile_from_cannon, spawn_round_ball, move_round_balls, + rotate_linear_cannon, ) .in_set(LinearUpdateSet::Track), ) @@ -41,6 +47,7 @@ fn setup(mut commands: Commands) { commands.insert_resource(build_track); commands.insert_resource(precalculated_track); + commands.insert_resource(build_linear_cannon_state()); } fn cleanup(mut commands: Commands, query: Query>) { @@ -50,8 +57,9 @@ fn cleanup(mut commands: Commands, query: Query> } // зачищаем ресурсы по типу - commands.remove_resource::(); + commands.remove_resource::(); //зачистить сразу после калькуляции commands.remove_resource::(); + commands.remove_resource::(); } fn linear_restart(mut next_state: ResMut>) { diff --git a/src/states/linear/systems_cannon.rs b/src/states/linear/systems_cannon.rs new file mode 100644 index 0000000..023aa28 --- /dev/null +++ b/src/states/linear/systems_cannon.rs @@ -0,0 +1,189 @@ +use crate::states::linear::*; +use bevy::prelude::*; +use bevy::window::PrimaryWindow; +use std::f32::consts::FRAC_PI_2; +use bevy::asset::ErasedAssetLoader; +use crate::{FACTOR, HEIGHT, WIDTH}; +use crate::states::level::{BallProjectile, BallType, Cannon, CannonState, CurrentPreviewMarker, NextPreviewMarker, CURRENT_SHOT_Z_INDEX, NEXT_SHOT_Z_INDEX, SLOT_SIZE}; + +pub fn spawn_linear_cannon(mut commands: Commands, asset_server: Res) { + let image: Handle = asset_server.load("sprites/cannon/cannon.png"); + + commands.spawn(( + Sprite { + image, + custom_size: Some(Vec2::new(STEP * 2.5, STEP * 4.0)), + ..default() + }, + Transform::from_xyz(6.0 * STEP, 3.0 * STEP, LINEAR_CANNON_Z), + LinearStateMarker, + LinearCannon, + )); +} + +pub fn rotate_linear_cannon( + mut query: Query<&mut Transform, With>, + window_query: Query<&Window, With>, + camera_query: Query<(&Camera, &GlobalTransform)>, +) { + let Ok(mut cannon_tf) = query.single_mut() else { + return; + }; + + let Ok(window) = window_query.single() else { + return; + }; + + let Some(cursor_pos) = window.cursor_position() else { + return; + }; + + let Ok((camera, camera_tf)) = camera_query.single() else { + return; + }; + + let Ok(world_cursor_pos) = camera.viewport_to_world_2d(camera_tf, cursor_pos) else { + return; + }; + + let cannon_pos = Vec2::new(cannon_tf.translation.x, cannon_tf.translation.y); + let direction = world_cursor_pos - cannon_pos; + + let angle = direction.to_angle() - FRAC_PI_2; + cannon_tf.rotation = Quat::from_rotation_z(angle); +} + +pub fn spawn_projectile_from_cannon( + mut commands: Commands, + cannon_query: Query<&Transform, With>, + mouse_input: Res>, + asset_server: Res, + mut cannon_state: ResMut +) { + if !mouse_input.just_pressed(MouseButton::Left) { + return; + } + + let Ok(cannon_tf) = cannon_query.single() else { + return; + }; + + let ball_type = cannon_state.shot; + let image: Handle = asset_server.load(ball_type.asset_path()); + + let offset = 100.0; // сдвиг от центра к дулу пушки + let direction = cannon_tf.rotation.mul_vec3(Vec3::Y).truncate().normalize(); + let spawn_pos_2d = cannon_tf.translation.truncate() + direction * offset; + let spawn_pos = spawn_pos_2d.extend(ROUND_SHOT_Z); + + commands.spawn(( + Sprite { + image, + custom_size: Some(Vec2::splat(STEP)), + ..default() + }, + Transform::from_translation(spawn_pos), + RoundBallProjectile { + velocity: direction * 800.0, + previous_position: spawn_pos_2d, + ball_type, + }, + LinearStateMarker, + )); + cannon_state.fire(); +} + +pub fn build_linear_cannon_state() -> LinearCannonState { + LinearCannonState { + shot: RoundBallType::random_from_4(), + swap: RoundBallType::random_from_4(), + } +} + +pub fn update_linear_cannon_preview( + mut commands: Commands, + cannon_state: Res, + asset_server: Res, + cannon_query: Query>, + shot_query: Query>, + swap_query: Query>, +) { + let Ok(cannon_entity) = cannon_query.single() else { return }; + + let shot_image: Handle = asset_server.load(cannon_state.shot.asset_path()); + + if let Ok(shot_entity) = shot_query.single() { + // усли шарик у дула есть, то обновляем спрайт + commands.entity(shot_entity).insert( + Sprite{ + image: shot_image, + custom_size: Some(Vec2::splat(STEP)), + ..default() + } + ); + } else { + //иначе создаем + let preview_entity = commands.spawn(( + Sprite{ + image: shot_image, + custom_size: Some(Vec2::splat(STEP)), + ..default() + }, + Transform::from_xyz(0.0, STEP * 2.2, LINEAR_CANNON_BALL_Z), + ShotMarker + )).id(); + commands.entity(cannon_entity).add_child(preview_entity); + } + + // следующий шарик в хвосте + let swap_image = asset_server.load(cannon_state.swap.asset_path()); + + if let Ok(swap_entity) = swap_query.single() { + commands.entity(swap_entity).insert(Sprite{ + image: swap_image, + custom_size: Some(Vec2::splat(STEP)), + ..default() + }); + } else { + let preview_entity = commands.spawn(( + Sprite{ + image: swap_image, + custom_size: Some(Vec2::splat(STEP)), + ..default() + }, + Transform::from_xyz(0.0, -STEP * 1.5, LINEAR_CANNON_BALL_Z), + SwapMarker, + )).id(); + commands.entity(cannon_entity).add_child(preview_entity); + } +} + +pub fn cycle_cannon_balls( + mut cannon_state: ResMut, + mouse_input: Res>, +) { + if mouse_input.just_pressed(MouseButton::Right) { + cannon_state.cycle(); + } +} + +pub fn linear_move_projectiles( + mut commands: Commands, + mut projectiles: Query<(Entity, &mut RoundBallProjectile, &mut Transform)>, + time: Res