Compare commits
No commits in common. "4da000f1c83c3eb7be45ef89d30cd1d14137c0b7" and "138c62ac3463510c8bf1a56dd244a16dfd3305fb" have entirely different histories.
4da000f1c8
...
138c62ac34
10 changed files with 145 additions and 370 deletions
|
|
@ -19,6 +19,39 @@ pub struct TrackPath {
|
||||||
pub buffer_size: usize,
|
pub buffer_size: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
|
||||||
|
pub enum BallType {
|
||||||
|
#[default]
|
||||||
|
First,
|
||||||
|
Second,
|
||||||
|
Third,
|
||||||
|
Forth,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BallType {
|
||||||
|
pub const fn asset_path(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
BallType::First => "balls/1.png",
|
||||||
|
BallType::Second => "balls/2.png",
|
||||||
|
BallType::Third => "balls/3.png",
|
||||||
|
BallType::Forth => "balls/4.png",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn random() -> Self {
|
||||||
|
[Self::First, Self::Second, Self::Third, Self::Forth]
|
||||||
|
.choose(&mut rand::rng())
|
||||||
|
.copied()
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component, Debug)]
|
||||||
|
pub struct Ball {
|
||||||
|
pub ball_type: BallType,
|
||||||
|
pub slot_index: usize,
|
||||||
|
pub slot_progress: f32,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
pub struct SpawnTimer {
|
pub struct SpawnTimer {
|
||||||
|
|
@ -38,7 +71,6 @@ pub struct WaveState {
|
||||||
pub spawning_allowed: bool,
|
pub spawning_allowed: bool,
|
||||||
pub ball_reached_end: bool,
|
pub ball_reached_end: bool,
|
||||||
pub is_warming_up: bool,
|
pub is_warming_up: bool,
|
||||||
pub is_queue_locked: bool, //отдельно для большей явности
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for WaveState {
|
impl Default for WaveState {
|
||||||
|
|
@ -47,7 +79,6 @@ impl Default for WaveState {
|
||||||
spawning_allowed: false,
|
spawning_allowed: false,
|
||||||
ball_reached_end: false,
|
ball_reached_end: false,
|
||||||
is_warming_up: false,
|
is_warming_up: false,
|
||||||
is_queue_locked: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -57,12 +88,12 @@ pub struct WarmupTarget {
|
||||||
pub target_index: usize,
|
pub target_index: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Component)]
|
||||||
pub enum Direction {
|
pub struct Cannon;
|
||||||
Left,
|
|
||||||
Right,
|
#[derive(Component)]
|
||||||
Top,
|
pub struct BallProjectile {
|
||||||
Bottom,
|
pub velocity: Vec2,
|
||||||
|
pub previous_position: Vec2,
|
||||||
|
pub ball_type: BallType,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
use bevy::prelude::*;
|
|
||||||
use rand::seq::IndexedRandom;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
|
|
||||||
pub enum BallType {
|
|
||||||
#[default]
|
|
||||||
First,
|
|
||||||
Second,
|
|
||||||
Third,
|
|
||||||
Forth,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BallType {
|
|
||||||
pub const fn asset_path(&self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
BallType::First => "balls/1.png",
|
|
||||||
BallType::Second => "balls/2.png",
|
|
||||||
BallType::Third => "balls/3.png",
|
|
||||||
BallType::Forth => "balls/4.png",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn random() -> Self {
|
|
||||||
[Self::First, Self::Second, Self::Third, Self::Forth]
|
|
||||||
.choose(&mut rand::rng())
|
|
||||||
.copied()
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Component, Debug)]
|
|
||||||
pub struct Ball {
|
|
||||||
pub ball_type: BallType,
|
|
||||||
pub slot_index: usize,
|
|
||||||
pub slot_progress: f32,
|
|
||||||
}
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
use bevy::math::Vec2;
|
|
||||||
use bevy::prelude::{Component, Resource};
|
|
||||||
use crate::states::level::*;
|
|
||||||
|
|
||||||
#[derive(Component)]
|
|
||||||
pub struct Cannon;
|
|
||||||
|
|
||||||
#[derive(Resource, Debug, Clone)]
|
|
||||||
pub struct CannonState {
|
|
||||||
pub current_type: BallType,
|
|
||||||
pub next_type: BallType,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for CannonState {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
current_type: BallType::random(),
|
|
||||||
next_type: BallType::random(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CannonState {
|
|
||||||
pub fn fire(&mut self) {
|
|
||||||
self.current_type = self.next_type;
|
|
||||||
self.next_type = BallType::random();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cycle_next(&mut self) {
|
|
||||||
// self.next_type = match self.next_type {
|
|
||||||
// BallType::First => BallType::Second,
|
|
||||||
// BallType::Second => BallType::Third,
|
|
||||||
// BallType::Third => BallType::Forth,
|
|
||||||
// BallType::Forth => BallType::First,
|
|
||||||
// };
|
|
||||||
let cur = self.current_type;
|
|
||||||
self.current_type = self.next_type;
|
|
||||||
self.next_type = cur;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Component)]
|
|
||||||
pub struct CurrentPreviewMarker;
|
|
||||||
|
|
||||||
#[derive(Component)]
|
|
||||||
pub struct NextPreviewMarker;
|
|
||||||
|
|
||||||
#[derive(Component)]
|
|
||||||
pub struct BallProjectile {
|
|
||||||
pub velocity: Vec2,
|
|
||||||
pub previous_position: Vec2,
|
|
||||||
pub ball_type: BallType,
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +1,8 @@
|
||||||
pub mod plugin;
|
pub mod plugin;
|
||||||
mod components;
|
pub mod components;
|
||||||
pub use components::*;
|
pub mod system;
|
||||||
mod system;
|
pub mod state;
|
||||||
pub use system::*;
|
pub mod system_ball;
|
||||||
mod state;
|
pub mod system_cannon;
|
||||||
pub use state::*;
|
pub mod system_track;
|
||||||
mod system_ball;
|
|
||||||
pub use system_ball::*;
|
|
||||||
mod system_cannon;
|
|
||||||
pub use system_cannon::*;
|
|
||||||
mod system_track;
|
|
||||||
pub use system_track::*;
|
|
||||||
pub mod system_grid;
|
pub mod system_grid;
|
||||||
pub use system_grid::*;
|
|
||||||
|
|
||||||
mod components_cannon;
|
|
||||||
pub use components_cannon::*;
|
|
||||||
mod components_ball;
|
|
||||||
pub use components_ball::*;
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
use crate::states::AppState::GameState;
|
use crate::states::AppState::GameState;
|
||||||
use crate::states::level::*;
|
use crate::states::level::system_ball::*;
|
||||||
|
use crate::states::level::system::*;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
use crate::states::level::system_cannon::*;
|
||||||
|
use crate::states::level::components::LevelMarker;
|
||||||
|
use crate::states::level::system_track::{debug_draw_track};
|
||||||
|
|
||||||
pub struct LevelPlugin;
|
pub struct LevelPlugin;
|
||||||
|
|
||||||
|
|
@ -20,12 +23,8 @@ impl Plugin for LevelPlugin {
|
||||||
spawn_new_ball,
|
spawn_new_ball,
|
||||||
move_queue_along_track,
|
move_queue_along_track,
|
||||||
check_wave_completion,
|
check_wave_completion,
|
||||||
//cannon systems
|
|
||||||
cycle_cannon_type,
|
|
||||||
update_cannon_preview,
|
|
||||||
spawn_projectile_from_cannon,
|
|
||||||
//сохранять порядок верхних трех
|
|
||||||
rotate_cannon,
|
rotate_cannon,
|
||||||
|
spawn_projectile_from_cannon,
|
||||||
move_projectiles,
|
move_projectiles,
|
||||||
detect_projectile_hit,
|
detect_projectile_hit,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
use crate::states::level::*;
|
|
||||||
|
use crate::states::level::components::*;
|
||||||
use crate::{FACTOR, HEIGHT, WIDTH};
|
use crate::{FACTOR, HEIGHT, WIDTH};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
use crate::states::level::system_track::setup_track_with_buffer;
|
||||||
|
|
||||||
pub const SHIFT: f32 = 1.0;
|
pub const SHIFT: f32 = 2.0;
|
||||||
pub const SLOT_SIZE: f32 = FACTOR as f32 / SHIFT;
|
pub const SLOT_SIZE: f32 = FACTOR as f32 / SHIFT;
|
||||||
|
|
||||||
pub fn setup_timer(mut commands: Commands){
|
pub fn setup_timer(mut commands: Commands){
|
||||||
|
|
@ -11,12 +13,6 @@ pub fn setup_timer(mut commands: Commands){
|
||||||
|
|
||||||
pub fn setup_level(mut commands: Commands){
|
pub fn setup_level(mut commands: Commands){
|
||||||
commands.insert_resource(setup_track_with_buffer());
|
commands.insert_resource(setup_track_with_buffer());
|
||||||
println!("Track is set up");
|
|
||||||
|
|
||||||
commands.insert_resource(WaveState::default());
|
commands.insert_resource(WaveState::default());
|
||||||
println!("WaveState is set up");
|
|
||||||
|
|
||||||
commands.insert_resource(build_cannon_state());
|
|
||||||
println!("CannonState is set up");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
|
use crate::states::level::components::{Ball, BallType, LevelMarker, TrackPath, WarmupTarget, WaveState};
|
||||||
|
use crate::states::level::system::SLOT_SIZE;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use crate::states::level::*;
|
|
||||||
|
|
||||||
pub fn spawn_new_ball(
|
pub fn spawn_new_ball(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
|
|
@ -10,7 +11,7 @@ pub fn spawn_new_ball(
|
||||||
balls: Query<&Ball>,
|
balls: Query<&Ball>,
|
||||||
wave: Res<WaveState>,
|
wave: Res<WaveState>,
|
||||||
) {
|
) {
|
||||||
if !wave.spawning_allowed || wave.is_queue_locked {
|
if !wave.spawning_allowed {
|
||||||
timer.reset();
|
timer.reset();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -208,4 +209,3 @@ pub fn check_wave_completion(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,21 @@
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use crate::states::level::*;
|
|
||||||
use bevy::window::PrimaryWindow;
|
use bevy::window::PrimaryWindow;
|
||||||
|
use crate::states::level::components::{Ball, BallProjectile, BallType, Cannon, LevelMarker, TrackPath};
|
||||||
|
use crate::states::level::system::SLOT_SIZE;
|
||||||
|
|
||||||
const CANNON_Z_INDEX: f32 = 10.0;
|
pub fn setup_cannon(
|
||||||
const PROJECTILE_Z_INDEX: f32 = 11.0;
|
mut commands: Commands,
|
||||||
const CURRENT_SHOT_Z_INDEX: f32 = 12.0;
|
asset_server: Res<AssetServer>,
|
||||||
const NEXT_SHOT_Z_INDEX: f32 = 12.0;
|
) {
|
||||||
|
|
||||||
pub fn setup_cannon(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|
||||||
let texture: Handle<Image> = asset_server.load("cannon/cannon.png");
|
let texture: Handle<Image> = asset_server.load("cannon/cannon.png");
|
||||||
|
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
LevelMarker,
|
LevelMarker,
|
||||||
Cannon,
|
Cannon,
|
||||||
Transform::from_xyz(579.0 - (1280.0 / 2.0), (768.0 / 2.0) - 150.0, CANNON_Z_INDEX),
|
Transform::from_xyz(579.0-(1280.0/2.0), (768.0/2.0)-150.0, 1.0),
|
||||||
Sprite::from_image(texture),
|
Sprite::from_image(texture),
|
||||||
Visibility::Visible,
|
Visibility::Visible,
|
||||||
));
|
));
|
||||||
println!("Cannon visuals is set up");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rotate_cannon(
|
pub fn rotate_cannon(
|
||||||
|
|
@ -25,23 +23,13 @@ pub fn rotate_cannon(
|
||||||
window_query: Query<&Window, With<PrimaryWindow>>,
|
window_query: Query<&Window, With<PrimaryWindow>>,
|
||||||
camera_query: Query<(&Camera, &GlobalTransform)>,
|
camera_query: Query<(&Camera, &GlobalTransform)>,
|
||||||
) {
|
) {
|
||||||
let Ok(mut cannon_tf) = query.single_mut() else {
|
let Ok(mut cannon_tf) = query.single_mut() else { return };
|
||||||
return;
|
let Ok(window) = window_query.single() else { return };
|
||||||
};
|
let Some(cursor_pos) = window.cursor_position() else { return };
|
||||||
let Ok(window) = window_query.single() else {
|
let Ok((camera, camera_tf)) = camera_query.single() else { return };
|
||||||
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 {
|
let Ok(world_cursor_pos) = camera.viewport_to_world_2d(camera_tf, cursor_pos) else { return };
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Вектор направления от пушки к курсору
|
// Вектор направления от пушки к курсору
|
||||||
let cannon_pos = Vec2::new(cannon_tf.translation.x, cannon_tf.translation.y);
|
let cannon_pos = Vec2::new(cannon_tf.translation.x, cannon_tf.translation.y);
|
||||||
|
|
@ -57,23 +45,18 @@ pub fn spawn_projectile_from_cannon(
|
||||||
cannon_query: Query<&Transform, With<Cannon>>,
|
cannon_query: Query<&Transform, With<Cannon>>,
|
||||||
mouse_input: Res<ButtonInput<MouseButton>>,
|
mouse_input: Res<ButtonInput<MouseButton>>,
|
||||||
asset_server: Res<AssetServer>,
|
asset_server: Res<AssetServer>,
|
||||||
mut cannon_state: ResMut<CannonState>
|
|
||||||
) {
|
) {
|
||||||
if !mouse_input.just_pressed(MouseButton::Left) {
|
if !mouse_input.just_pressed(MouseButton::Left) { return; }
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let Ok(cannon_tf) = cannon_query.single() else {
|
let Ok(cannon_tf) = cannon_query.single() else { return; };
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let ball_type = cannon_state.current_type;
|
let ball_type = BallType::First; // TODO: рандом/цикл
|
||||||
let image = asset_server.load(ball_type.asset_path());
|
let image = asset_server.load(ball_type.asset_path());
|
||||||
|
|
||||||
let offset = 100.0; // сдвиг от центра к дулу пушки
|
let offset = 100.0;
|
||||||
let direction = cannon_tf.rotation.mul_vec3(Vec3::Y).truncate().normalize();
|
let direction = cannon_tf.rotation.mul_vec3(Vec3::Y).truncate().normalize();
|
||||||
let spawn_pos_2d = cannon_tf.translation.truncate() + direction * offset;
|
let spawn_pos_2d = cannon_tf.translation.truncate() + direction * offset;
|
||||||
let spawn_pos = spawn_pos_2d.extend(PROJECTILE_Z_INDEX);
|
let spawn_pos = spawn_pos_2d.extend(10.0); // z=10, чтобы было поверх трека
|
||||||
|
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
LevelMarker,
|
LevelMarker,
|
||||||
|
|
@ -90,7 +73,6 @@ pub fn spawn_projectile_from_cannon(
|
||||||
ball_type,
|
ball_type,
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
cannon_state.fire();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn move_projectiles(
|
pub fn move_projectiles(
|
||||||
|
|
@ -100,9 +82,7 @@ pub fn move_projectiles(
|
||||||
) {
|
) {
|
||||||
for (entity, mut proj, mut transform) in projectiles.iter_mut() {
|
for (entity, mut proj, mut transform) in projectiles.iter_mut() {
|
||||||
// Обновляем позицию
|
// Обновляем позицию
|
||||||
let delta = proj.velocity * time.delta_secs();
|
let new_pos = proj.previous_position + proj.velocity * time.delta_secs();
|
||||||
let new_pos = proj.previous_position + delta;
|
|
||||||
|
|
||||||
transform.translation = new_pos.extend(transform.translation.z);
|
transform.translation = new_pos.extend(transform.translation.z);
|
||||||
proj.previous_position = new_pos;
|
proj.previous_position = new_pos;
|
||||||
|
|
||||||
|
|
@ -113,151 +93,64 @@ pub fn move_projectiles(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const HIT_THRESHOLD: f32 = SLOT_SIZE;
|
|
||||||
pub fn detect_projectile_hit(
|
pub fn detect_projectile_hit(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
track: Res<TrackPath>,
|
track: Res<TrackPath>,
|
||||||
mut wave: ResMut<WaveState>,
|
|
||||||
mut projectiles: Query<(Entity, &mut BallProjectile)>,
|
mut projectiles: Query<(Entity, &mut BallProjectile)>,
|
||||||
mut balls: Query<(Entity, &mut Ball)>,
|
mut balls: Query<&mut Ball>, // <-- нужен мутабельный доступ для сдвига
|
||||||
) {
|
) {
|
||||||
// пока идёт структурное изменение, спавн и другие мутации ждут
|
|
||||||
if wave.is_queue_locked {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (proj_entity, proj) in projectiles.iter_mut() {
|
for (proj_entity, proj) in projectiles.iter_mut() {
|
||||||
let mut min_dist_any = f32::MAX;
|
let mut nearest_idx = 0;
|
||||||
let mut nearest_idx_any = 0;
|
let mut min_dist = f32::MAX;
|
||||||
|
|
||||||
for (idx, &slot_pos) in track.points.iter().enumerate() {
|
for (idx, &slot_pos) in track.points.iter().enumerate() {
|
||||||
let dist = proj.previous_position.distance(slot_pos);
|
let dist = proj.previous_position.distance(slot_pos);
|
||||||
if dist < min_dist_any {
|
if dist < min_dist {
|
||||||
min_dist_any = dist;
|
min_dist = dist;
|
||||||
nearest_idx_any = idx;
|
nearest_idx = idx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// если снаряд далеко от трека - промах
|
const HIT_THRESHOLD: f32 = SLOT_SIZE;
|
||||||
if min_dist_any > HIT_THRESHOLD {
|
if min_dist < HIT_THRESHOLD {
|
||||||
// commands.entity(proj_entity).despawn();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let insert_idx;
|
let occupied_slots: Vec<usize> = balls.iter()
|
||||||
let target_progress;
|
.map(|ball| ball.slot_index)
|
||||||
|
.collect();
|
||||||
|
|
||||||
if let Some((_, target_ball)) = balls.iter().find(|(_, b)| b.slot_index == nearest_idx_any) {
|
if occupied_slots.contains(&nearest_idx) {
|
||||||
// Копируем значения в локальные переменные
|
|
||||||
target_progress = target_ball.slot_progress;
|
|
||||||
|
|
||||||
// Определяем индекс вставки
|
let left_progress = if nearest_idx > 0 {
|
||||||
insert_idx = if target_progress <= 0.5 { //TODO check correction
|
balls.iter()
|
||||||
nearest_idx_any - 1
|
.find(|b| b.slot_index == nearest_idx - 1)
|
||||||
|
.map(|b| b.slot_progress)
|
||||||
|
.unwrap_or(0.0)
|
||||||
} else {
|
} else {
|
||||||
nearest_idx_any
|
0.0
|
||||||
};
|
};
|
||||||
} else {
|
|
||||||
// Слот пуст — снаряд пролетает мимо
|
|
||||||
// commands.entity(proj_entity).despawn();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
wave.is_queue_locked = true;
|
// 2. Сдвигаем правую часть очереди вправо
|
||||||
|
for mut ball in balls.iter_mut() {
|
||||||
// сдвигаем очередь
|
if ball.slot_index >= nearest_idx {
|
||||||
let max_idx = track.points.len() - 1;
|
|
||||||
let mut to_despawn = Vec::new();
|
|
||||||
|
|
||||||
|
|
||||||
for (entity, mut ball) in balls.iter_mut() {
|
|
||||||
if ball.slot_index >= insert_idx {
|
|
||||||
if ball.slot_index < max_idx {
|
|
||||||
ball.slot_index += 1;
|
ball.slot_index += 1;
|
||||||
} else {
|
ball.slot_progress = 0.0;
|
||||||
to_despawn.push(entity);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for entity in to_despawn {
|
commands.entity(proj_entity)
|
||||||
commands.entity(entity).despawn();
|
.insert(Ball {
|
||||||
}
|
|
||||||
|
|
||||||
commands.entity(proj_entity).insert({
|
|
||||||
Ball {
|
|
||||||
ball_type: proj.ball_type,
|
ball_type: proj.ball_type,
|
||||||
slot_index: insert_idx,
|
slot_index: nearest_idx,
|
||||||
slot_progress: target_progress,
|
slot_progress: left_progress,
|
||||||
}
|
})
|
||||||
}).remove::<BallProjectile>();
|
.remove::<BallProjectile>();
|
||||||
}
|
|
||||||
|
|
||||||
// Разблокировка (структурные изменения завершены)
|
println!("Hit! Inserted at slot {}", nearest_idx);
|
||||||
wave.is_queue_locked = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cycle_cannon_type(
|
|
||||||
mut cannon_state: ResMut<CannonState>,
|
|
||||||
mouse_input: Res<ButtonInput<MouseButton>>,
|
|
||||||
) {
|
|
||||||
if mouse_input.just_pressed(MouseButton::Right) {
|
|
||||||
cannon_state.cycle_next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update_cannon_preview(
|
|
||||||
mut commands: Commands,
|
|
||||||
cannon_state: Res<CannonState>,
|
|
||||||
asset_server: Res<AssetServer>,
|
|
||||||
cannon_query: Query<Entity, With<Cannon>>,
|
|
||||||
muzzle_query: Query<Entity, With<CurrentPreviewMarker>>,
|
|
||||||
next_query: Query<Entity, With<NextPreviewMarker>>,
|
|
||||||
) {
|
|
||||||
let Ok(cannon_entity) = cannon_query.single() else { return };
|
|
||||||
|
|
||||||
let muzzle_image = asset_server.load(cannon_state.current_type.asset_path());
|
|
||||||
|
|
||||||
if let Ok(muzzle_entity) = muzzle_query.single() {
|
|
||||||
// усли шарик у дула есть, то обновляем спрайт
|
|
||||||
commands.entity(muzzle_entity).insert(Sprite::from_image(muzzle_image));
|
|
||||||
} else {
|
} else {
|
||||||
//иначе создаем
|
commands.entity(proj_entity).despawn();
|
||||||
let preview_entity = commands.spawn((
|
println!("Miss: slot {} is empty", nearest_idx);
|
||||||
CurrentPreviewMarker,
|
|
||||||
Sprite{
|
|
||||||
image: muzzle_image,
|
|
||||||
custom_size: Some(Vec2::splat(SLOT_SIZE)),
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
Transform::from_xyz(0.0, 100.0, CURRENT_SHOT_Z_INDEX),
|
|
||||||
Visibility::Visible,
|
|
||||||
)).id();
|
|
||||||
commands.entity(cannon_entity).add_child(preview_entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
let next_image = asset_server.load(cannon_state.next_type.asset_path());
|
|
||||||
|
|
||||||
if let Ok(next_entity) = next_query.single() {
|
|
||||||
commands.entity(next_entity).insert(Sprite::from_image(next_image));
|
|
||||||
} else {
|
|
||||||
let preview_entity = commands.spawn((
|
|
||||||
NextPreviewMarker,
|
|
||||||
Sprite{
|
|
||||||
image: next_image,
|
|
||||||
custom_size: Some(Vec2::splat(SLOT_SIZE)),
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
Transform::from_xyz(0.0, -50.0, NEXT_SHOT_Z_INDEX),
|
|
||||||
Visibility::Visible,
|
|
||||||
)).id();
|
|
||||||
commands.entity(cannon_entity).add_child(preview_entity);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_cannon_state() -> CannonState {
|
|
||||||
CannonState {
|
|
||||||
current_type: BallType::random(),
|
|
||||||
next_type: BallType::random(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
use bevy::color::Color;
|
use bevy::color::Color;
|
||||||
use bevy::math::{Vec2, Vec3};
|
use bevy::math::{Vec2, Vec3};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::{default, Commands, Gizmos, Text2d, TextColor, TextFont, Transform};
|
||||||
use crate::states::level::*;
|
use crate::states::level::components::DebugSlotNumber;
|
||||||
|
use crate::states::level::system::{SHIFT, SLOT_SIZE};
|
||||||
use crate::{HEIGHT, WIDTH};
|
use crate::{HEIGHT, WIDTH};
|
||||||
|
|
||||||
const GRID_COLS: f32 = WIDTH as f32 * SHIFT;
|
const GRID_COLS: f32 = WIDTH as f32 * SHIFT;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
use bevy::color::Color;
|
use bevy::color::Color;
|
||||||
use bevy::math::Vec2;
|
use bevy::math::Vec2;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::{Gizmos, Res};
|
||||||
use crate::states::level::*;
|
use crate::states::level::components::TrackPath;
|
||||||
|
use crate::states::level::system::SLOT_SIZE;
|
||||||
|
|
||||||
const BUFFER_SLOTS: usize = 20;
|
const BUFFER_SLOTS: usize = 20;
|
||||||
|
|
||||||
|
|
@ -34,38 +35,8 @@ pub fn setup_track_with_buffer() -> TrackPath {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// for debug
|
|
||||||
fn make_track() -> Vec<Vec2> {
|
fn make_track() -> Vec<Vec2> {
|
||||||
make_track_80()
|
vec![
|
||||||
}
|
|
||||||
|
|
||||||
fn make_track_80() -> Vec<Vec2> {
|
|
||||||
// let track = generate_track_line(Vec2::new(-600.0, -80.0), Vec2::new(600.0, -80.0), Direction::Right);
|
|
||||||
|
|
||||||
let track: Vec<Vec2> = vec![
|
|
||||||
Vec2::new(-600.0, -80.0),
|
|
||||||
Vec2::new(-520.0, -80.0),
|
|
||||||
Vec2::new(-440.0, -80.0),
|
|
||||||
Vec2::new(-360.0, -80.0),
|
|
||||||
Vec2::new(-280.0, -80.0),
|
|
||||||
Vec2::new(-200.0, -80.0),
|
|
||||||
Vec2::new(-120.0, -80.0),
|
|
||||||
Vec2::new(-40.0, -80.0),
|
|
||||||
Vec2::new(40.0, -80.0),
|
|
||||||
Vec2::new(120.0, -80.0),
|
|
||||||
Vec2::new(200.0, -80.0),
|
|
||||||
Vec2::new(280.0, -80.0),
|
|
||||||
Vec2::new(360.0, -80.0),
|
|
||||||
Vec2::new(440.0, -80.0),
|
|
||||||
Vec2::new(520.0, -80.0),
|
|
||||||
Vec2::new(600.0, -80.0),
|
|
||||||
];
|
|
||||||
|
|
||||||
track
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_track_40() -> Vec<Vec2> {
|
|
||||||
let mut track = vec![
|
|
||||||
//спавн точка за видимой областью
|
//спавн точка за видимой областью
|
||||||
// Vec2::new(-660.0, -140.0),
|
// Vec2::new(-660.0, -140.0),
|
||||||
Vec2::new(-620.0, -140.0),
|
Vec2::new(-620.0, -140.0),
|
||||||
|
|
@ -101,8 +72,7 @@ fn make_track_40() -> Vec<Vec2> {
|
||||||
Vec2::new(300.0, 140.0),
|
Vec2::new(300.0, 140.0),
|
||||||
Vec2::new(340.0, 140.0),
|
Vec2::new(340.0, 140.0),
|
||||||
Vec2::new(380.0, 140.0),
|
Vec2::new(380.0, 140.0),
|
||||||
];
|
]
|
||||||
track
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -116,30 +86,17 @@ pub fn debug_draw_track(mut gizmos: Gizmos, track: Res<TrackPath>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_track_line(start: Vec2, end: Vec2, direction: Direction) -> Vec<Vec2> {
|
// fn generate_track(mut commands: Commands) {
|
||||||
//генерируем только точки между началом и концом
|
// let half_w = crate::states::level::system::GRID_COLS as f32 * SLOT_SIZE / 2.0;
|
||||||
let mut result = vec![end];
|
// let half_h = crate::states::level::system::GRID_ROWS as f32 * SLOT_SIZE / 2.0;
|
||||||
|
//
|
||||||
let step = match direction {
|
// let mut points = Vec::with_capacity(crate::states::level::system::GRID_COLS as usize);
|
||||||
Direction::Left => Vec2::new(-SLOT_SIZE, 0.0),
|
//
|
||||||
Direction::Right => Vec2::new( SLOT_SIZE, 0.0),
|
// for col in (0..crate::states::level::system::GRID_COLS as u32).rev() {
|
||||||
Direction::Top => Vec2::new(0.0, SLOT_SIZE),
|
// let x = -half_w + col as f32 * SLOT_SIZE + SLOT_SIZE / 2.0;
|
||||||
Direction::Bottom => Vec2::new(0.0, -SLOT_SIZE),
|
// let y = 0.0;
|
||||||
};
|
// points.push(Vec2::new(x, y));
|
||||||
|
// }
|
||||||
let steps = match direction {
|
//
|
||||||
Direction::Left | Direction::Right => {
|
// commands.insert_resource(TrackPath { points, spawn_index: 0, buffer_size: 0 });
|
||||||
((end.x - start.x).abs() / SLOT_SIZE) as usize
|
// }
|
||||||
}
|
|
||||||
Direction::Top | Direction::Bottom => {
|
|
||||||
((end.y - start.y).abs() / SLOT_SIZE) as usize
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for i in 0..=steps {
|
|
||||||
let pos = start + step * i as f32;
|
|
||||||
result.push(pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue