mirror of
https://github.com/kristoferssolo/maze-ascension.git
synced 2025-10-21 19:20:34 +00:00
Merge pull request #6 from kristoferssolo/feature/update-bevy
chore: update to bevy0.15
This commit is contained in:
commit
80bc027477
1347
Cargo.lock
generated
1347
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
12
Cargo.toml
12
Cargo.toml
@ -1,11 +1,11 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "maze-ascension"
|
name = "maze-ascension"
|
||||||
authors = ["Kristofers Solo <dev@kristofers.xyz>"]
|
authors = ["Kristofers Solo <dev@kristofers.xyz>"]
|
||||||
version = "0.0.6"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bevy = { version = "0.14", features = ["wayland"] }
|
bevy = { version = "0.15", features = ["wayland"] }
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
# Compile low-severity logs out of native builds for performance.
|
# Compile low-severity logs out of native builds for performance.
|
||||||
log = { version = "0.4", features = [
|
log = { version = "0.4", features = [
|
||||||
@ -17,10 +17,10 @@ tracing = { version = "0.1", features = [
|
|||||||
"max_level_debug",
|
"max_level_debug",
|
||||||
"release_max_level_warn",
|
"release_max_level_warn",
|
||||||
] }
|
] }
|
||||||
hexx = { version = "0.18", features = ["bevy_reflect", "grid"] }
|
hexx = { version = "0.19", features = ["bevy_reflect", "grid"] }
|
||||||
hexlab = { version = "0.1", features = ["bevy"] }
|
hexlab = { version = "0.2", features = ["bevy"] }
|
||||||
bevy-inspector-egui = { version = "0.27", optional = true }
|
bevy-inspector-egui = { version = "0.28", optional = true }
|
||||||
bevy_egui = { version = "0.30", optional = true }
|
bevy_egui = { version = "0.31", optional = true }
|
||||||
thiserror = "2.0"
|
thiserror = "2.0"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -84,10 +84,8 @@ enum AppSet {
|
|||||||
fn spawn_camera(mut commands: Commands) {
|
fn spawn_camera(mut commands: Commands) {
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
Name::new("Camera"),
|
Name::new("Camera"),
|
||||||
Camera3dBundle {
|
Camera3d::default(),
|
||||||
transform: Transform::from_xyz(200., 200., 0.).looking_at(Vec3::ZERO, Vec3::Y),
|
Transform::from_xyz(200., 200., 0.).looking_at(Vec3::ZERO, Vec3::Y),
|
||||||
..default()
|
|
||||||
},
|
|
||||||
// Render all UI to this camera.
|
// Render all UI to this camera.
|
||||||
// Not strictly necessary since we only use one camera,
|
// Not strictly necessary since we only use one camera,
|
||||||
// but if we don't use this component, our UI will disappear as soon
|
// but if we don't use this component, our UI will disappear as soon
|
||||||
|
|||||||
@ -3,7 +3,7 @@ use bevy::prelude::*;
|
|||||||
use std::f32::consts::FRAC_PI_2;
|
use std::f32::consts::FRAC_PI_2;
|
||||||
|
|
||||||
const WALL_OVERLAP_MODIFIER: f32 = 1.25;
|
const WALL_OVERLAP_MODIFIER: f32 = 1.25;
|
||||||
const HEX_SIDES: usize = 6;
|
const HEX_SIDES: u32 = 6;
|
||||||
const WHITE_EMISSION_INTENSITY: f32 = 10.;
|
const WHITE_EMISSION_INTENSITY: f32 = 10.;
|
||||||
|
|
||||||
pub(crate) struct MazeAssets {
|
pub(crate) struct MazeAssets {
|
||||||
|
|||||||
@ -23,6 +23,6 @@ impl Plugin for MazePlugin {
|
|||||||
impl Command for MazePlugin {
|
impl Command for MazePlugin {
|
||||||
fn apply(self, world: &mut World) {
|
fn apply(self, world: &mut World) {
|
||||||
world.insert_resource(MazePluginLoaded);
|
world.insert_resource(MazePluginLoaded);
|
||||||
world.run_system_once(systems::setup::setup);
|
let _ = world.run_system_once(systems::setup::setup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,10 +32,8 @@ pub(super) fn setup_maze(
|
|||||||
.spawn((
|
.spawn((
|
||||||
Name::new("Floor"),
|
Name::new("Floor"),
|
||||||
MazeFloor(1),
|
MazeFloor(1),
|
||||||
SpatialBundle {
|
Transform::from_translation(Vec3::ZERO),
|
||||||
transform: Transform::from_translation(Vec3::ZERO),
|
Visibility::Visible,
|
||||||
..default()
|
|
||||||
},
|
|
||||||
))
|
))
|
||||||
.with_children(|parent| {
|
.with_children(|parent| {
|
||||||
for tile in maze.values() {
|
for tile in maze.values() {
|
||||||
|
|||||||
@ -26,12 +26,9 @@ pub(super) fn spawn_single_hex_tile(
|
|||||||
.spawn((
|
.spawn((
|
||||||
Name::new(format!("Hex {}", tile)),
|
Name::new(format!("Hex {}", tile)),
|
||||||
MazeTile,
|
MazeTile,
|
||||||
PbrBundle {
|
Mesh3d(assets.hex_mesh.clone()),
|
||||||
mesh: assets.hex_mesh.clone(),
|
MeshMaterial3d(assets.hex_material.clone()),
|
||||||
material: assets.hex_material.clone(),
|
Transform::from_translation(world_pos).with_rotation(rotation),
|
||||||
transform: Transform::from_translation(world_pos).with_rotation(rotation),
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
))
|
))
|
||||||
.with_children(|parent| spawn_walls(parent, assets, config, tile.walls()));
|
.with_children(|parent| spawn_walls(parent, assets, config, tile.walls()));
|
||||||
}
|
}
|
||||||
@ -58,20 +55,12 @@ fn spawn_walls(parent: &mut ChildBuilder, assets: &MazeAssets, config: &MazeConf
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawn_single_wall(
|
fn spawn_single_wall(parent: &mut ChildBuilder, assets: &MazeAssets, rotation: Quat, offset: Vec3) {
|
||||||
parent: &mut ChildBuilder,
|
|
||||||
asstets: &MazeAssets,
|
|
||||||
rotation: Quat,
|
|
||||||
offset: Vec3,
|
|
||||||
) {
|
|
||||||
parent.spawn((
|
parent.spawn((
|
||||||
Name::new("Wall"),
|
Name::new("Wall"),
|
||||||
MazeWall,
|
MazeWall,
|
||||||
PbrBundle {
|
Mesh3d(assets.wall_mesh.clone()),
|
||||||
mesh: asstets.wall_mesh.clone(),
|
MeshMaterial3d(assets.wall_material.clone()),
|
||||||
material: asstets.wall_material.clone(),
|
Transform::from_translation(offset).with_rotation(rotation),
|
||||||
transform: Transform::from_translation(offset).with_rotation(rotation),
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -56,10 +56,8 @@ fn play_credits_music(mut commands: Commands, mut music: ResMut<CreditsMusic>) {
|
|||||||
music.entity = Some(
|
music.entity = Some(
|
||||||
commands
|
commands
|
||||||
.spawn((
|
.spawn((
|
||||||
AudioBundle {
|
AudioPlayer::<AudioSource>(music.music.clone()),
|
||||||
source: music.music.clone(),
|
PlaybackSettings::LOOP,
|
||||||
settings: PlaybackSettings::LOOP,
|
|
||||||
},
|
|
||||||
Music,
|
Music,
|
||||||
))
|
))
|
||||||
.id(),
|
.id(),
|
||||||
|
|||||||
@ -15,12 +15,12 @@ pub(super) fn plugin(app: &mut App) {
|
|||||||
app.add_systems(
|
app.add_systems(
|
||||||
Update,
|
Update,
|
||||||
return_to_title_screen
|
return_to_title_screen
|
||||||
.run_if(in_state(Screen::Gameplay).and_then(input_just_pressed(KeyCode::Escape))),
|
.run_if(in_state(Screen::Gameplay).and(input_just_pressed(KeyCode::Escape))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawn_level(mut commands: Commands) {
|
fn spawn_level(mut commands: Commands) {
|
||||||
commands.add(spawn_level_command);
|
commands.queue(spawn_level_command);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Resource, Asset, Reflect, Clone)]
|
#[derive(Resource, Asset, Reflect, Clone)]
|
||||||
@ -44,10 +44,8 @@ fn play_gameplay_music(mut commands: Commands, mut music: ResMut<GameplayMusic>)
|
|||||||
music.entity = Some(
|
music.entity = Some(
|
||||||
commands
|
commands
|
||||||
.spawn((
|
.spawn((
|
||||||
AudioBundle {
|
AudioPlayer::<AudioSource>(music.handle.clone()),
|
||||||
source: music.handle.clone(),
|
PlaybackSettings::LOOP,
|
||||||
settings: PlaybackSettings::LOOP,
|
|
||||||
},
|
|
||||||
Music,
|
Music,
|
||||||
))
|
))
|
||||||
.id(),
|
.id(),
|
||||||
|
|||||||
@ -13,7 +13,7 @@ pub(super) fn plugin(app: &mut App) {
|
|||||||
|
|
||||||
app.add_systems(
|
app.add_systems(
|
||||||
Update,
|
Update,
|
||||||
continue_to_title_screen.run_if(in_state(Screen::Loading).and_then(all_assets_loaded)),
|
continue_to_title_screen.run_if(in_state(Screen::Loading).and(all_assets_loaded)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ fn spawn_loading_screen(mut commands: Commands) {
|
|||||||
.ui_root()
|
.ui_root()
|
||||||
.insert(StateScoped(Screen::Loading))
|
.insert(StateScoped(Screen::Loading))
|
||||||
.with_children(|children| {
|
.with_children(|children| {
|
||||||
children.label("Loading...").insert(Style {
|
children.label("Loading...").insert(Node {
|
||||||
justify_content: JustifyContent::Center,
|
justify_content: JustifyContent::Center,
|
||||||
..default()
|
..default()
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
//! A splash screen that plays briefly at startup.
|
//! A splash screen that plays bdelta_secsriefly at startup.
|
||||||
|
|
||||||
use bevy::{
|
use bevy::{
|
||||||
|
image::{ImageLoaderSettings, ImageSampler},
|
||||||
input::common_conditions::input_just_pressed,
|
input::common_conditions::input_just_pressed,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
render::texture::{ImageLoaderSettings, ImageSampler},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{screens::Screen, theme::prelude::*, AppSet};
|
use crate::{screens::Screen, theme::prelude::*, AppSet};
|
||||||
@ -40,7 +40,7 @@ pub(super) fn plugin(app: &mut App) {
|
|||||||
app.add_systems(
|
app.add_systems(
|
||||||
Update,
|
Update,
|
||||||
continue_to_loading_screen
|
continue_to_loading_screen
|
||||||
.run_if(input_just_pressed(KeyCode::Escape).and_then(in_state(Screen::Splash))),
|
.run_if(input_just_pressed(KeyCode::Escape).and(in_state(Screen::Splash))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,24 +59,21 @@ fn spawn_splash_screen(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|||||||
.with_children(|children| {
|
.with_children(|children| {
|
||||||
children.spawn((
|
children.spawn((
|
||||||
Name::new("Splash image"),
|
Name::new("Splash image"),
|
||||||
ImageBundle {
|
Node {
|
||||||
style: Style {
|
margin: UiRect::all(Val::Auto),
|
||||||
margin: UiRect::all(Val::Auto),
|
width: Val::Percent(70.0),
|
||||||
width: Val::Percent(70.0),
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
image: UiImage::new(asset_server.load_with_settings(
|
|
||||||
// This should be an embedded asset for instant loading, but that is
|
|
||||||
// currently [broken on Windows Wasm builds](https://github.com/bevyengine/bevy/issues/14246).
|
|
||||||
"images/splash.png",
|
|
||||||
|settings: &mut ImageLoaderSettings| {
|
|
||||||
// Make an exception for the splash image in case
|
|
||||||
// `ImagePlugin::default_nearest()` is used for pixel art.
|
|
||||||
settings.sampler = ImageSampler::linear();
|
|
||||||
},
|
|
||||||
)),
|
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
|
ImageNode::new(asset_server.load_with_settings(
|
||||||
|
// This should be an embedded asset for instant loading, but that is
|
||||||
|
// currently [broken on Windows Wasm builds](https://github.com/bevyengine/bevy/issues/14246).
|
||||||
|
"images/splash.png",
|
||||||
|
|settings: &mut ImageLoaderSettings| {
|
||||||
|
// Make an exception for the splash image in case
|
||||||
|
// `ImagePlugin::default_nearest()` is used for pixel art.
|
||||||
|
settings.sampler = ImageSampler::linear();
|
||||||
|
},
|
||||||
|
)),
|
||||||
UiImageFadeInOut {
|
UiImageFadeInOut {
|
||||||
total_duration: SPLASH_DURATION_SECS,
|
total_duration: SPLASH_DURATION_SECS,
|
||||||
fade_duration: SPLASH_FADE_DURATION_SECS,
|
fade_duration: SPLASH_FADE_DURATION_SECS,
|
||||||
@ -110,11 +107,11 @@ impl UiImageFadeInOut {
|
|||||||
|
|
||||||
fn tick_fade_in_out(time: Res<Time>, mut animation_query: Query<&mut UiImageFadeInOut>) {
|
fn tick_fade_in_out(time: Res<Time>, mut animation_query: Query<&mut UiImageFadeInOut>) {
|
||||||
for mut anim in &mut animation_query {
|
for mut anim in &mut animation_query {
|
||||||
anim.t += time.delta_seconds();
|
anim.t += time.delta_secs();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_fade_in_out(mut animation_query: Query<(&UiImageFadeInOut, &mut UiImage)>) {
|
fn apply_fade_in_out(mut animation_query: Query<(&UiImageFadeInOut, &mut ImageNode)>) {
|
||||||
for (anim, mut image) in &mut animation_query {
|
for (anim, mut image) in &mut animation_query {
|
||||||
image.color.set_alpha(anim.alpha())
|
image.color.set_alpha(anim.alpha())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -94,10 +94,8 @@ fn trigger_interaction_sound_effect(
|
|||||||
_ => continue,
|
_ => continue,
|
||||||
};
|
};
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
AudioBundle {
|
AudioPlayer::<AudioSource>(source),
|
||||||
source,
|
PlaybackSettings::DESPAWN,
|
||||||
settings: PlaybackSettings::DESPAWN,
|
|
||||||
},
|
|
||||||
SoundEffect,
|
SoundEffect,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,19 +16,20 @@ pub trait Widgets {
|
|||||||
fn label(&mut self, text: impl Into<String>) -> EntityCommands;
|
fn label(&mut self, text: impl Into<String>) -> EntityCommands;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Spawn> Widgets for T {
|
impl<T: SpawnUi> Widgets for T {
|
||||||
fn button(&mut self, text: impl Into<String>) -> EntityCommands {
|
fn button(&mut self, text: impl Into<String>) -> EntityCommands {
|
||||||
let mut entity = self.spawn((
|
let mut entity = self.spawn_ui((
|
||||||
Name::new("Button"),
|
Name::new("Button"),
|
||||||
ButtonBundle {
|
Button,
|
||||||
style: Style {
|
ImageNode {
|
||||||
width: Px(200.0),
|
color: NODE_BACKGROUND,
|
||||||
height: Px(65.0),
|
..default()
|
||||||
justify_content: JustifyContent::Center,
|
},
|
||||||
align_items: AlignItems::Center,
|
Node {
|
||||||
..default()
|
width: Px(200.0),
|
||||||
},
|
height: Px(65.0),
|
||||||
background_color: BackgroundColor(NODE_BACKGROUND),
|
justify_content: JustifyContent::Center,
|
||||||
|
align_items: AlignItems::Center,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
InteractionPalette {
|
InteractionPalette {
|
||||||
@ -38,16 +39,14 @@ impl<T: Spawn> Widgets for T {
|
|||||||
},
|
},
|
||||||
));
|
));
|
||||||
entity.with_children(|children| {
|
entity.with_children(|children| {
|
||||||
children.spawn((
|
children.spawn_ui((
|
||||||
Name::new("Button Text"),
|
Name::new("Button Text"),
|
||||||
TextBundle::from_section(
|
Text(text.into()),
|
||||||
text,
|
TextFont {
|
||||||
TextStyle {
|
font_size: 40.0,
|
||||||
font_size: 40.0,
|
..default()
|
||||||
color: BUTTON_TEXT,
|
},
|
||||||
..default()
|
TextColor(BUTTON_TEXT),
|
||||||
},
|
|
||||||
),
|
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -55,51 +54,47 @@ impl<T: Spawn> Widgets for T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn header(&mut self, text: impl Into<String>) -> EntityCommands {
|
fn header(&mut self, text: impl Into<String>) -> EntityCommands {
|
||||||
let mut entity = self.spawn((
|
let mut entity = self.spawn_ui((
|
||||||
Name::new("Header"),
|
Name::new("Header"),
|
||||||
NodeBundle {
|
Node {
|
||||||
style: Style {
|
width: Px(500.0),
|
||||||
width: Px(500.0),
|
height: Px(65.0),
|
||||||
height: Px(65.0),
|
justify_content: JustifyContent::Center,
|
||||||
justify_content: JustifyContent::Center,
|
align_items: AlignItems::Center,
|
||||||
align_items: AlignItems::Center,
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
background_color: BackgroundColor(NODE_BACKGROUND),
|
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
|
BackgroundColor(NODE_BACKGROUND),
|
||||||
));
|
));
|
||||||
entity.with_children(|children| {
|
entity.with_children(|children| {
|
||||||
children.spawn((
|
children.spawn_ui((
|
||||||
Name::new("Header Text"),
|
Name::new("Header Text"),
|
||||||
TextBundle::from_section(
|
Text(text.into()),
|
||||||
text,
|
TextFont {
|
||||||
TextStyle {
|
font_size: 40.0,
|
||||||
font_size: 40.0,
|
..default()
|
||||||
color: HEADER_TEXT,
|
},
|
||||||
..default()
|
TextColor(HEADER_TEXT),
|
||||||
},
|
|
||||||
),
|
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
entity
|
entity
|
||||||
}
|
}
|
||||||
|
|
||||||
fn label(&mut self, text: impl Into<String>) -> EntityCommands {
|
fn label(&mut self, _text: impl Into<String>) -> EntityCommands {
|
||||||
let entity = self.spawn((
|
let entity = self.spawn_ui((
|
||||||
Name::new("Label"),
|
Name::new("Label"),
|
||||||
TextBundle::from_section(
|
Text::default(),
|
||||||
text,
|
// TextBundle::from_section(
|
||||||
TextStyle {
|
// text,
|
||||||
font_size: 24.0,
|
// TextStyle {
|
||||||
color: LABEL_TEXT,
|
// font_size: 24.0,
|
||||||
..default()
|
// color: LABEL_TEXT,
|
||||||
},
|
// ..default()
|
||||||
)
|
// },
|
||||||
.with_style(Style {
|
// )
|
||||||
width: Px(500.0),
|
// .with_style(Style {
|
||||||
..default()
|
// width: Px(500.0),
|
||||||
}),
|
// ..default()
|
||||||
|
// }),
|
||||||
));
|
));
|
||||||
entity
|
entity
|
||||||
}
|
}
|
||||||
@ -116,17 +111,14 @@ impl Containers for Commands<'_, '_> {
|
|||||||
fn ui_root(&mut self) -> EntityCommands {
|
fn ui_root(&mut self) -> EntityCommands {
|
||||||
self.spawn((
|
self.spawn((
|
||||||
Name::new("UI Root"),
|
Name::new("UI Root"),
|
||||||
NodeBundle {
|
Node {
|
||||||
style: Style {
|
width: Percent(100.0),
|
||||||
width: Percent(100.0),
|
height: Percent(100.0),
|
||||||
height: Percent(100.0),
|
justify_content: JustifyContent::Center,
|
||||||
justify_content: JustifyContent::Center,
|
align_items: AlignItems::Center,
|
||||||
align_items: AlignItems::Center,
|
flex_direction: FlexDirection::Column,
|
||||||
flex_direction: FlexDirection::Column,
|
row_gap: Px(10.0),
|
||||||
row_gap: Px(10.0),
|
position_type: PositionType::Absolute,
|
||||||
position_type: PositionType::Absolute,
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
@ -137,18 +129,18 @@ impl Containers for Commands<'_, '_> {
|
|||||||
/// This is here so that [`Widgets`] can be implemented on all types that
|
/// This is here so that [`Widgets`] can be implemented on all types that
|
||||||
/// are able to spawn entities.
|
/// are able to spawn entities.
|
||||||
/// Ideally, this trait should be [part of Bevy itself](https://github.com/bevyengine/bevy/issues/14231).
|
/// Ideally, this trait should be [part of Bevy itself](https://github.com/bevyengine/bevy/issues/14231).
|
||||||
trait Spawn {
|
trait SpawnUi {
|
||||||
fn spawn<B: Bundle>(&mut self, bundle: B) -> EntityCommands;
|
fn spawn_ui<B: Bundle>(&mut self, bundle: B) -> EntityCommands;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Spawn for Commands<'_, '_> {
|
impl SpawnUi for Commands<'_, '_> {
|
||||||
fn spawn<B: Bundle>(&mut self, bundle: B) -> EntityCommands {
|
fn spawn_ui<B: Bundle>(&mut self, bundle: B) -> EntityCommands {
|
||||||
self.spawn(bundle)
|
self.spawn(bundle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Spawn for ChildBuilder<'_> {
|
impl SpawnUi for ChildBuilder<'_> {
|
||||||
fn spawn<B: Bundle>(&mut self, bundle: B) -> EntityCommands {
|
fn spawn_ui<B: Bundle>(&mut self, bundle: B) -> EntityCommands {
|
||||||
self.spawn(bundle)
|
self.spawn(bundle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user