restore after broken git
This commit is contained in:
commit
138c62ac34
51 changed files with 7559 additions and 0 deletions
156
src/states/level/system_cannon.rs
Normal file
156
src/states/level/system_cannon.rs
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
use bevy::prelude::*;
|
||||
use bevy::window::PrimaryWindow;
|
||||
use crate::states::level::components::{Ball, BallProjectile, BallType, Cannon, LevelMarker, TrackPath};
|
||||
use crate::states::level::system::SLOT_SIZE;
|
||||
|
||||
pub fn setup_cannon(
|
||||
mut commands: Commands,
|
||||
asset_server: Res<AssetServer>,
|
||||
) {
|
||||
let texture: Handle<Image> = asset_server.load("cannon/cannon.png");
|
||||
|
||||
commands.spawn((
|
||||
LevelMarker,
|
||||
Cannon,
|
||||
Transform::from_xyz(579.0-(1280.0/2.0), (768.0/2.0)-150.0, 1.0),
|
||||
Sprite::from_image(texture),
|
||||
Visibility::Visible,
|
||||
));
|
||||
}
|
||||
|
||||
pub fn rotate_cannon(
|
||||
mut query: Query<&mut Transform, With<Cannon>>,
|
||||
window_query: Query<&Window, With<PrimaryWindow>>,
|
||||
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;
|
||||
|
||||
// Применяем вращение по оси Z
|
||||
let angle = direction.to_angle() - std::f32::consts::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<Cannon>>,
|
||||
mouse_input: Res<ButtonInput<MouseButton>>,
|
||||
asset_server: Res<AssetServer>,
|
||||
) {
|
||||
if !mouse_input.just_pressed(MouseButton::Left) { return; }
|
||||
|
||||
let Ok(cannon_tf) = cannon_query.single() else { return; };
|
||||
|
||||
let ball_type = BallType::First; // TODO: рандом/цикл
|
||||
let image = 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(10.0); // z=10, чтобы было поверх трека
|
||||
|
||||
commands.spawn((
|
||||
LevelMarker,
|
||||
Transform::from_translation(spawn_pos),
|
||||
Sprite {
|
||||
image,
|
||||
custom_size: Some(Vec2::splat(SLOT_SIZE)),
|
||||
..default()
|
||||
},
|
||||
Visibility::Visible,
|
||||
BallProjectile {
|
||||
velocity: direction * 800.0,
|
||||
previous_position: spawn_pos_2d,
|
||||
ball_type,
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
pub fn move_projectiles(
|
||||
mut commands: Commands,
|
||||
mut projectiles: Query<(Entity, &mut BallProjectile, &mut Transform)>,
|
||||
time: Res<Time>,
|
||||
) {
|
||||
for (entity, mut proj, mut transform) in projectiles.iter_mut() {
|
||||
// Обновляем позицию
|
||||
let new_pos = proj.previous_position + proj.velocity * time.delta_secs();
|
||||
transform.translation = new_pos.extend(transform.translation.z);
|
||||
proj.previous_position = new_pos;
|
||||
|
||||
// Опционально: удаляем снаряды, улетевшие за экран
|
||||
if new_pos.x.abs() > 2000.0 || new_pos.y.abs() > 2000.0 {
|
||||
commands.entity(entity).despawn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn detect_projectile_hit(
|
||||
mut commands: Commands,
|
||||
track: Res<TrackPath>,
|
||||
mut projectiles: Query<(Entity, &mut BallProjectile)>,
|
||||
mut balls: Query<&mut Ball>, // <-- нужен мутабельный доступ для сдвига
|
||||
) {
|
||||
for (proj_entity, proj) in projectiles.iter_mut() {
|
||||
let mut nearest_idx = 0;
|
||||
let mut min_dist = f32::MAX;
|
||||
|
||||
for (idx, &slot_pos) in track.points.iter().enumerate() {
|
||||
let dist = proj.previous_position.distance(slot_pos);
|
||||
if dist < min_dist {
|
||||
min_dist = dist;
|
||||
nearest_idx = idx;
|
||||
}
|
||||
}
|
||||
|
||||
const HIT_THRESHOLD: f32 = SLOT_SIZE;
|
||||
if min_dist < HIT_THRESHOLD {
|
||||
|
||||
let occupied_slots: Vec<usize> = balls.iter()
|
||||
.map(|ball| ball.slot_index)
|
||||
.collect();
|
||||
|
||||
if occupied_slots.contains(&nearest_idx) {
|
||||
|
||||
let left_progress = if nearest_idx > 0 {
|
||||
balls.iter()
|
||||
.find(|b| b.slot_index == nearest_idx - 1)
|
||||
.map(|b| b.slot_progress)
|
||||
.unwrap_or(0.0)
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
// 2. Сдвигаем правую часть очереди вправо
|
||||
for mut ball in balls.iter_mut() {
|
||||
if ball.slot_index >= nearest_idx {
|
||||
ball.slot_index += 1;
|
||||
ball.slot_progress = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
commands.entity(proj_entity)
|
||||
.insert(Ball {
|
||||
ball_type: proj.ball_type,
|
||||
slot_index: nearest_idx,
|
||||
slot_progress: left_progress,
|
||||
})
|
||||
.remove::<BallProjectile>();
|
||||
|
||||
println!("Hit! Inserted at slot {}", nearest_idx);
|
||||
|
||||
} else {
|
||||
commands.entity(proj_entity).despawn();
|
||||
println!("Miss: slot {} is empty", nearest_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue