Add basic game sounds
This commit is contained in:
@@ -18,9 +18,3 @@ rustflags = ["-C", "link-arg=-fuse-ld=/opt/homebrew/bin/zld", "-Zshare-generics=
|
||||
[target.x86_64-pc-windows-msvc]
|
||||
linker = "rust-lld.exe"
|
||||
rustflags = ["-Zshare-generics=n"]
|
||||
|
||||
# Optional: Uncommenting the following improves compile times, but reduces the amount of debug info to 'line number tables only'
|
||||
# In most cases the gains are negligible, but if you are on macos and have slow compile times you should see significant gains.
|
||||
#[profile.dev]
|
||||
#debug = 1
|
||||
|
||||
|
||||
115
Cargo.lock
generated
115
Cargo.lock
generated
@@ -180,9 +180,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.69"
|
||||
version = "1.0.70"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800"
|
||||
checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4"
|
||||
|
||||
[[package]]
|
||||
name = "approx"
|
||||
@@ -346,7 +346,7 @@ checksum = "fc47dfcdcb52182af97741c1582cc9b3bb4e82f0adacf4c3e78909d438cbfc8b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -354,6 +354,7 @@ name = "bevy-snake"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bevy",
|
||||
"bevy_asset_loader",
|
||||
"bevy_editor_pls",
|
||||
"bevy_tweening",
|
||||
"itertools",
|
||||
@@ -434,6 +435,28 @@ dependencies = [
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bevy_asset_loader"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "118490c65031cecd6586e6b2cbd16f05bc161438dd0d30c42e307638eab7daba"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bevy",
|
||||
"bevy_asset_loader_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bevy_asset_loader_derive"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac452d5861a4f9e69625b9a6d63a846dc9ee0e3c5ee32fe2d9c5b8cd59b916ba"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bevy_audio"
|
||||
version = "0.10.0"
|
||||
@@ -497,7 +520,7 @@ checksum = "cdf11701c01bf4dc7a3fac9f4547f3643d3db4cc1682af40c8c86e2f8734b617"
|
||||
dependencies = [
|
||||
"bevy_macro_utils",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -544,7 +567,7 @@ dependencies = [
|
||||
"bevy_macro_utils",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -742,7 +765,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f24ca3363292f1435641fbafd5c24ce362137dd7d69bee56dcaaa2bc1d512ffe"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
"toml_edit",
|
||||
]
|
||||
|
||||
@@ -838,7 +861,7 @@ dependencies = [
|
||||
"bit-set",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
@@ -897,7 +920,7 @@ dependencies = [
|
||||
"bevy_macro_utils",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1079,7 +1102,7 @@ checksum = "62a42e465c446800c57a5bf65b64f4fa1c1f3a74efc2a64a2a001e4a4f548a2e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1140,7 +1163,7 @@ dependencies = [
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1191,9 +1214,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "1.3.0"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ffdb39cb703212f3c11973452c2861b972f757b021158f3516ba10f2fa8b2c1"
|
||||
checksum = "c3d4260bcc2e8fc9df1eac4919a720effeb63a3f0952f5bf4944adfa18897f09"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"once_cell",
|
||||
@@ -1224,7 +1247,7 @@ checksum = "1aca418a974d83d40a0c1f0c5cba6ff4bc28d8df099109ca459a2118d40b6322"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1512,7 +1535,7 @@ checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1629,7 +1652,7 @@ checksum = "ae489d58959f3c4cdd1250866a05acfb341469affe4fced71aff3ba228be1693"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1905,7 +1928,7 @@ dependencies = [
|
||||
"inflections",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2034,6 +2057,12 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
|
||||
|
||||
[[package]]
|
||||
name = "hound"
|
||||
version = "3.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d13cdbd5dbb29f9c88095bbdc2590c9cba0d0a1269b983fef6b2cdd7e9f4db1"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.3.0"
|
||||
@@ -2290,7 +2319,7 @@ dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2587,7 +2616,7 @@ checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2638,7 +2667,7 @@ dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3057,6 +3086,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bdf1d4dea18dff2e9eb6dca123724f8b60ef44ad74a9ad283cdfe025df7e73fa"
|
||||
dependencies = [
|
||||
"cpal",
|
||||
"hound",
|
||||
"lewton",
|
||||
]
|
||||
|
||||
@@ -3128,22 +3158,22 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.156"
|
||||
version = "1.0.157"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "314b5b092c0ade17c00142951e50ced110ec27cea304b1037c6969246c2469a4"
|
||||
checksum = "707de5fcf5df2b5788fca98dd7eab490bc2fd9b7ef1404defc462833b83f25ca"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.156"
|
||||
version = "1.0.157"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7e29c4601e36bcec74a223228dce795f4cd3616341a4af93520ca1a837c087d"
|
||||
checksum = "78997f4555c22a7971214540c4a661291970619afd56de19f77e0de86296e1e5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3258,10 +3288,21 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sysinfo"
|
||||
version = "0.28.2"
|
||||
name = "syn"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3e847e2de7a137c8c2cede5095872dbb00f4f9bf34d061347e36b43322acd56"
|
||||
checksum = "4cff13bb1732bccfe3b246f3fdb09edfd51c01d6f5299b7ccd9457c2e4e37774"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sysinfo"
|
||||
version = "0.28.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f69e0d827cce279e61c2f3399eb789271a8f136d8245edef70f06e3c9601a670"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"core-foundation-sys 0.8.3",
|
||||
@@ -3293,22 +3334,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.39"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c"
|
||||
checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.39"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e"
|
||||
checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3384,7 +3425,7 @@ checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3574,7 +3615,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -3608,7 +3649,7 @@ checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@@ -3904,7 +3945,7 @@ checksum = "6ce87ca8e3417b02dc2a8a22769306658670ec92d78f1bd420d6310a67c245c6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3915,7 +3956,7 @@ checksum = "853f69a591ecd4f810d29f17e902d40e349fb05b0b11fff63b08b826bfe39c7f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -9,10 +9,11 @@ rand = "0.8.5"
|
||||
bevy_editor_pls = "0.3.0"
|
||||
bevy_tweening = "0.7.0"
|
||||
leafwing-input-manager = "0.9.0"
|
||||
bevy_asset_loader = "0.15.0"
|
||||
|
||||
[dependencies.bevy]
|
||||
version = "0.10"
|
||||
features = ["wayland"]
|
||||
features = ["wayland", "wav"]
|
||||
|
||||
# [profile.dev.package."*"]
|
||||
# opt-level = 3
|
||||
|
||||
BIN
assets/sounds/blip.wav
Normal file
BIN
assets/sounds/blip.wav
Normal file
Binary file not shown.
BIN
assets/sounds/collision.wav
Normal file
BIN
assets/sounds/collision.wav
Normal file
Binary file not shown.
BIN
assets/sounds/tick.wav
Normal file
BIN
assets/sounds/tick.wav
Normal file
Binary file not shown.
12
src/audio.rs
Normal file
12
src/audio.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
use bevy::prelude::*;
|
||||
use bevy_asset_loader::prelude::*;
|
||||
|
||||
#[derive(AssetCollection, Resource)]
|
||||
pub struct Assets {
|
||||
#[asset(path = "sounds/collision.wav")]
|
||||
pub collision: Handle<AudioSource>,
|
||||
#[asset(path = "sounds/tick.wav")]
|
||||
pub tick: Handle<AudioSource>,
|
||||
#[asset(path = "sounds/blip.wav")]
|
||||
pub blip: Handle<AudioSource>,
|
||||
}
|
||||
10
src/main.rs
10
src/main.rs
@@ -6,12 +6,14 @@ use crate::{
|
||||
ui::UiPlugin,
|
||||
};
|
||||
use bevy::{prelude::*, render::camera::ScalingMode, window::WindowResolution};
|
||||
use bevy_asset_loader::prelude::*;
|
||||
use bevy_tweening::TweeningPlugin;
|
||||
use std::time::Duration;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
use bevy_editor_pls::prelude::*;
|
||||
|
||||
mod audio;
|
||||
mod canvas;
|
||||
mod fruit;
|
||||
mod grid;
|
||||
@@ -22,7 +24,6 @@ const ASPECT_RATIO: f32 = 16. / 9.;
|
||||
const WINDOW_WIDTH: f32 = WINDOW_HEIGHT * ASPECT_RATIO;
|
||||
const WINDOW_HEIGHT: f32 = 720.;
|
||||
const TICK_PERIOD: Duration = Duration::from_millis(125);
|
||||
const CLEAR_COLOR: Color = Color::rgba(0.15, 0.15, 0.15, 1.0);
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)]
|
||||
enum GameState {
|
||||
@@ -38,7 +39,7 @@ struct Score(u32);
|
||||
fn main() {
|
||||
let mut app = App::new();
|
||||
|
||||
app.insert_resource(ClearColor(CLEAR_COLOR))
|
||||
app.insert_resource(ClearColor(Color::BLACK))
|
||||
.insert_resource(FixedTime::new(TICK_PERIOD))
|
||||
.insert_resource(Score(0))
|
||||
.add_plugins(DefaultPlugins.set(WindowPlugin {
|
||||
@@ -50,6 +51,7 @@ fn main() {
|
||||
}),
|
||||
..Default::default()
|
||||
}))
|
||||
.init_collection::<audio::Assets>()
|
||||
.add_state::<GameState>()
|
||||
.add_plugin(TweeningPlugin)
|
||||
.add_plugin(SnakePlugin)
|
||||
@@ -81,10 +83,6 @@ fn setup_system(mut commands: Commands) {
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
camera: Camera {
|
||||
hdr: true,
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.insert(Name::new("Orthographic Camera"));
|
||||
|
||||
71
src/snake.rs
71
src/snake.rs
@@ -3,7 +3,7 @@ mod bulge;
|
||||
mod direction;
|
||||
mod movement;
|
||||
|
||||
use crate::{fruit, grid, GameState};
|
||||
use crate::{audio, fruit, grid, GameState};
|
||||
use bevy::prelude::*;
|
||||
use direction::Direction;
|
||||
use itertools::Itertools;
|
||||
@@ -57,7 +57,24 @@ impl Plugin for SnakePlugin {
|
||||
.in_schedule(CoreSchedule::FixedUpdate)
|
||||
.in_set(SystemSet::CollisionDetection)
|
||||
.in_set(OnUpdate(GameState::InGame))
|
||||
.after(collision_system),
|
||||
.after(collision_system)
|
||||
.run_if(about_to_collide),
|
||||
)
|
||||
.add_system(
|
||||
collision_sound_system
|
||||
.run_if(in_state(GameState::InGame))
|
||||
.run_if(about_to_collide),
|
||||
)
|
||||
.add_system(
|
||||
tick_sound_system
|
||||
.in_schedule(CoreSchedule::FixedUpdate)
|
||||
.run_if(in_state(GameState::InGame))
|
||||
.run_if(not(about_to_collide)),
|
||||
)
|
||||
.add_system(
|
||||
blip_sound_system
|
||||
.in_schedule(CoreSchedule::FixedUpdate)
|
||||
.run_if(in_state(GameState::InGame)),
|
||||
)
|
||||
.add_system(direction::start_game_system.in_set(OnUpdate(GameState::Begin)))
|
||||
.add_system(direction::change_direction_system)
|
||||
@@ -104,11 +121,18 @@ struct Collision {
|
||||
|
||||
pub struct AddTailEvent;
|
||||
|
||||
fn create_snake_segment(commands: &mut Commands, grid_position: grid::Coordinate) -> Entity {
|
||||
fn create_snake_segment(
|
||||
commands: &mut Commands,
|
||||
grid_position: grid::Coordinate,
|
||||
segment_number: u32,
|
||||
) -> Entity {
|
||||
let mut color = Color::RED;
|
||||
color *= 0.99f32.powi(segment_number as _);
|
||||
|
||||
commands
|
||||
.spawn(SpriteBundle {
|
||||
sprite: Sprite {
|
||||
color: Color::RED,
|
||||
color,
|
||||
custom_size: Some(Vec2::splat(grid::SEGMENT_SIZE) * 0.9),
|
||||
..Default::default()
|
||||
},
|
||||
@@ -122,7 +146,8 @@ fn create_snake_segment(commands: &mut Commands, grid_position: grid::Coordinate
|
||||
}
|
||||
|
||||
fn setup_snake_system(mut commands: Commands) {
|
||||
let snake_head = create_snake_segment(&mut commands, grid::Coordinate::splat(grid::SIZE / 2));
|
||||
let snake_head =
|
||||
create_snake_segment(&mut commands, grid::Coordinate::splat(grid::SIZE / 2), 0);
|
||||
|
||||
commands
|
||||
.entity(snake_head)
|
||||
@@ -164,11 +189,14 @@ fn add_tail_system(
|
||||
mut tail_event_reader: EventReader<AddTailEvent>,
|
||||
) {
|
||||
for _ in tail_event_reader.iter() {
|
||||
let segment =
|
||||
create_snake_segment(&mut commands, grid::Coordinate::splat(grid::Index::MIN / 2));
|
||||
|
||||
let (snake_entity, mut segments) = snake_query.single_mut();
|
||||
|
||||
let segment = create_snake_segment(
|
||||
&mut commands,
|
||||
grid::Coordinate::splat(grid::Index::MIN / 2),
|
||||
segments.0.len() as _,
|
||||
);
|
||||
|
||||
segments.0.push(segment);
|
||||
|
||||
commands
|
||||
@@ -232,12 +260,29 @@ fn collision_system(
|
||||
}
|
||||
}
|
||||
|
||||
fn game_over_system(
|
||||
query: Query<&Collision, With<Snake>>,
|
||||
mut next_state: ResMut<NextState<GameState>>,
|
||||
fn about_to_collide(query: Query<&Collision, With<Snake>>) -> bool {
|
||||
query.get_single().unwrap().about_to_collide
|
||||
}
|
||||
|
||||
fn game_over_system(mut next_state: ResMut<NextState<GameState>>) {
|
||||
next_state.set(GameState::End);
|
||||
}
|
||||
|
||||
fn collision_sound_system(audio: Res<Audio>, audio_assets: Res<audio::Assets>) {
|
||||
audio.play(audio_assets.collision.clone());
|
||||
}
|
||||
|
||||
fn tick_sound_system(audio: Res<Audio>, audio_assets: Res<audio::Assets>) {
|
||||
audio.play(audio_assets.tick.clone());
|
||||
}
|
||||
|
||||
fn blip_sound_system(
|
||||
audio: Res<Audio>,
|
||||
audio_assets: Res<audio::Assets>,
|
||||
mut eaten_event_reader: EventReader<fruit::EatenEvent>,
|
||||
) {
|
||||
if query.get_single().unwrap().about_to_collide {
|
||||
next_state.set(GameState::End);
|
||||
for _ in eaten_event_reader.iter() {
|
||||
audio.play(audio_assets.blip.clone());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user