mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
turnip and window blocks
This commit is contained in:
parent
c5efa43fa3
commit
98704a4753
BIN
assets/voxygen/voxel/sprite/window/window-0.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/window/window-0.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/window/window-1.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/window/window-1.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/window/window-2.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/window/window-2.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/window/window-3.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/window/window-3.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -1,3 +1,4 @@
|
||||
use crate::comp::Alignment;
|
||||
use vek::*;
|
||||
use crate::{
|
||||
comp::{self, Alignment, Body, Item, humanoid},
|
||||
@ -8,6 +9,7 @@ pub enum EntityTemplate {
|
||||
Traveller,
|
||||
}
|
||||
|
||||
|
||||
pub struct EntityInfo {
|
||||
pub pos: Vec3<f32>,
|
||||
pub is_waypoint: bool, // Edge case, overrides everything else
|
||||
|
@ -59,7 +59,12 @@ impl Route {
|
||||
|
||||
pub fn is_finished(&self) -> bool { self.next().is_none() }
|
||||
|
||||
pub fn traverse<V>(&mut self, vol: &V, pos: Vec3<f32>, traversal_tolerance: f32) -> Option<Vec3<f32>>
|
||||
pub fn traverse<V>(
|
||||
&mut self,
|
||||
vol: &V,
|
||||
pos: Vec3<f32>,
|
||||
traversal_tolerance: f32,
|
||||
) -> Option<Vec3<f32>>
|
||||
where
|
||||
V: BaseVol<Vox = Block> + ReadVol,
|
||||
{
|
||||
@ -68,7 +73,8 @@ impl Route {
|
||||
None
|
||||
} else {
|
||||
let next_tgt = next.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0);
|
||||
if ((pos - next_tgt) * Vec3::new(1.0, 1.0, 0.3)).magnitude_squared() < traversal_tolerance.powf(2.0)
|
||||
if ((pos - next_tgt) * Vec3::new(1.0, 1.0, 0.3)).magnitude_squared()
|
||||
< traversal_tolerance.powf(2.0)
|
||||
{
|
||||
self.next_idx += 1;
|
||||
}
|
||||
|
@ -1,9 +1,8 @@
|
||||
use std::{
|
||||
fmt,
|
||||
hash,
|
||||
ops::{Index, IndexMut},
|
||||
cmp::{PartialEq, Eq},
|
||||
cmp::{Eq, PartialEq},
|
||||
fmt, hash,
|
||||
marker::PhantomData,
|
||||
ops::{Index, IndexMut},
|
||||
};
|
||||
|
||||
pub struct Id<T>(usize, PhantomData<T>);
|
||||
@ -66,6 +65,7 @@ impl<T> Store<T> {
|
||||
|
||||
impl<T> Index<Id<T>> for Store<T> {
|
||||
type Output = T;
|
||||
|
||||
fn index(&self, id: Id<T>) -> &Self::Output { self.get(id) }
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,13 @@
|
||||
use crate::{
|
||||
comp::{self, agent::Activity, Agent, Alignment, Controller, ControlAction, MountState, Ori, Scale, Pos, Stats, Loadout, item::{ItemKind, tool::ToolKind}, CharacterState},
|
||||
comp::{
|
||||
self,
|
||||
agent::Activity,
|
||||
item::{tool::ToolKind, ItemKind},
|
||||
Agent, Alignment, CharacterState, ControlAction, Controller, Loadout, MountState, Ori, Pos,
|
||||
Scale, Stats,
|
||||
},
|
||||
path::Chaser,
|
||||
state::{Time, DeltaTime},
|
||||
state::{DeltaTime, Time},
|
||||
sync::UidAllocator,
|
||||
terrain::TerrainGrid,
|
||||
util::Dir,
|
||||
@ -55,7 +61,17 @@ impl<'a> System<'a> for Sys {
|
||||
mount_states,
|
||||
): Self::SystemData,
|
||||
) {
|
||||
for (entity, pos, ori, alignment, loadout, character_state, agent, controller, mount_state) in (
|
||||
for (
|
||||
entity,
|
||||
pos,
|
||||
ori,
|
||||
alignment,
|
||||
loadout,
|
||||
character_state,
|
||||
agent,
|
||||
controller,
|
||||
mount_state,
|
||||
) in (
|
||||
&entities,
|
||||
&positions,
|
||||
&orientations,
|
||||
@ -144,17 +160,17 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
|
||||
// Put away weapon
|
||||
if thread_rng().gen::<f32>() < 0.005 {
|
||||
if thread_rng().gen::<f32>() < 0.005 {
|
||||
controller.actions.push(ControlAction::Unwield);
|
||||
}
|
||||
|
||||
// Sit
|
||||
if thread_rng().gen::<f32>() < 0.0035 {
|
||||
if thread_rng().gen::<f32>() < 0.0035 {
|
||||
controller.actions.push(ControlAction::Sit);
|
||||
}
|
||||
|
||||
// Sometimes try searching for new targets
|
||||
if thread_rng().gen::<f32>() < 0.1 {
|
||||
if thread_rng().gen::<f32>() < 0.1 {
|
||||
choose_target = true;
|
||||
}
|
||||
},
|
||||
@ -165,9 +181,13 @@ impl<'a> System<'a> for Sys {
|
||||
let dist_sqrd = pos.0.distance_squared(tgt_pos.0);
|
||||
// Follow, or return to idle
|
||||
if dist_sqrd > AVG_FOLLOW_DIST.powf(2.0) {
|
||||
if let Some(bearing) =
|
||||
chaser.chase(&*terrain, pos.0, tgt_pos.0, AVG_FOLLOW_DIST, traversal_tolerance)
|
||||
{
|
||||
if let Some(bearing) = chaser.chase(
|
||||
&*terrain,
|
||||
pos.0,
|
||||
tgt_pos.0,
|
||||
AVG_FOLLOW_DIST,
|
||||
traversal_tolerance,
|
||||
) {
|
||||
inputs.move_dir = Vec2::from(bearing)
|
||||
.try_normalized()
|
||||
.unwrap_or(Vec2::zero());
|
||||
@ -193,10 +213,13 @@ impl<'a> System<'a> for Sys {
|
||||
Staff,
|
||||
}
|
||||
|
||||
let tactic = match loadout.active_item
|
||||
.as_ref()
|
||||
.and_then(|ic| if let ItemKind::Tool(tool) = &ic.item.kind { Some(&tool.kind) } else { None})
|
||||
{
|
||||
let tactic = match loadout.active_item.as_ref().and_then(|ic| {
|
||||
if let ItemKind::Tool(tool) = &ic.item.kind {
|
||||
Some(&tool.kind)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
Some(ToolKind::Bow(_)) => Tactic::RangedPowerup,
|
||||
Some(ToolKind::Staff(_)) => Tactic::Staff,
|
||||
_ => Tactic::Melee,
|
||||
@ -240,13 +263,16 @@ impl<'a> System<'a> for Sys {
|
||||
inputs.roll.set_state(true);
|
||||
}
|
||||
} else if dist_sqrd < MAX_CHASE_DIST.powf(2.0)
|
||||
|| (dist_sqrd < SIGHT_DIST.powf(2.0) && (!*been_close || !matches!(tactic, Tactic::Melee)))
|
||||
|| (dist_sqrd < SIGHT_DIST.powf(2.0)
|
||||
&& (!*been_close || !matches!(tactic, Tactic::Melee)))
|
||||
{
|
||||
let can_see_tgt = terrain
|
||||
.ray(pos.0 + Vec3::unit_z(), tgt_pos.0 + Vec3::unit_z())
|
||||
.until(|block| !block.is_air())
|
||||
.cast()
|
||||
.0.powf(2.0) >= dist_sqrd;
|
||||
.0
|
||||
.powf(2.0)
|
||||
>= dist_sqrd;
|
||||
|
||||
if can_see_tgt {
|
||||
if let Tactic::RangedPowerup = tactic {
|
||||
@ -271,9 +297,13 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
|
||||
// Long-range chase
|
||||
if let Some(bearing) =
|
||||
chaser.chase(&*terrain, pos.0, tgt_pos.0, 1.25, traversal_tolerance)
|
||||
{
|
||||
if let Some(bearing) = chaser.chase(
|
||||
&*terrain,
|
||||
pos.0,
|
||||
tgt_pos.0,
|
||||
1.25,
|
||||
traversal_tolerance,
|
||||
) {
|
||||
inputs.move_dir = Vec2::from(bearing)
|
||||
.try_normalized()
|
||||
.unwrap_or(Vec2::zero());
|
||||
|
@ -10,6 +10,10 @@ pub enum BlockKind {
|
||||
Normal,
|
||||
Dense,
|
||||
Water,
|
||||
Window1,
|
||||
Window2,
|
||||
Window3,
|
||||
Window4,
|
||||
LargeCactus,
|
||||
BarrelCactus,
|
||||
RoundCactus,
|
||||
@ -49,6 +53,7 @@ pub enum BlockKind {
|
||||
Carrot,
|
||||
Tomato,
|
||||
Radish,
|
||||
Turnip,
|
||||
Coconut,
|
||||
}
|
||||
|
||||
@ -101,6 +106,7 @@ impl BlockKind {
|
||||
BlockKind::Carrot => true,
|
||||
BlockKind::Tomato => false,
|
||||
BlockKind::Radish => true,
|
||||
BlockKind::Turnip => true,
|
||||
BlockKind::Coconut => true,
|
||||
_ => false,
|
||||
}
|
||||
@ -155,7 +161,12 @@ impl BlockKind {
|
||||
BlockKind::Carrot => false,
|
||||
BlockKind::Tomato => false,
|
||||
BlockKind::Radish => false,
|
||||
BlockKind::Turnip => false,
|
||||
BlockKind::Coconut => false,
|
||||
BlockKind::Window1 => false,
|
||||
BlockKind::Window2 => false,
|
||||
BlockKind::Window3 => false,
|
||||
BlockKind::Window4 => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
@ -200,6 +211,7 @@ impl BlockKind {
|
||||
BlockKind::Carrot => false,
|
||||
BlockKind::Tomato => true,
|
||||
BlockKind::Radish => false,
|
||||
BlockKind::Turnip => false,
|
||||
BlockKind::Coconut => false,
|
||||
_ => true,
|
||||
}
|
||||
|
@ -116,78 +116,76 @@ impl<'a> System<'a> for Sys {
|
||||
&body_data.species[&species].generic
|
||||
}
|
||||
|
||||
// const SPAWN_NPCS: &'static [fn() -> (
|
||||
// String,
|
||||
// comp::Body,
|
||||
// Option<comp::Item>,
|
||||
// comp::Alignment,
|
||||
// )] = &[
|
||||
// (|| {
|
||||
// let body = comp::humanoid::Body::random();
|
||||
// (
|
||||
// format!(
|
||||
// "{} Traveler",
|
||||
// get_npc_name(&NPC_NAMES.humanoid, body.race)
|
||||
// ),
|
||||
// comp::Body::Humanoid(body),
|
||||
// Some(assets::load_expect_cloned(
|
||||
// "common.items.weapons.starter_axe",
|
||||
// )),
|
||||
// comp::Alignment::Npc,
|
||||
// )
|
||||
// }) as _,
|
||||
// (|| {
|
||||
// let body = comp::humanoid::Body::random();
|
||||
// (
|
||||
// format!("{} Bandit", get_npc_name(&NPC_NAMES.humanoid, body.race)),
|
||||
// comp::Body::Humanoid(body),
|
||||
// Some(assets::load_expect_cloned(
|
||||
// "common.items.weapons.short_sword_0",
|
||||
// )),
|
||||
// comp::Alignment::Enemy,
|
||||
// )
|
||||
// }) as _,
|
||||
// (|| {
|
||||
// let body = comp::quadruped_medium::Body::random();
|
||||
// (
|
||||
// get_npc_name(&NPC_NAMES.quadruped_medium, body.species).into(),
|
||||
// comp::Body::QuadrupedMedium(body),
|
||||
// None,
|
||||
// comp::Alignment::Enemy,
|
||||
// )
|
||||
// }) as _,
|
||||
// (|| {
|
||||
// let body = comp::bird_medium::Body::random();
|
||||
// (
|
||||
// get_npc_name(&NPC_NAMES.bird_medium, body.species).into(),
|
||||
// comp::Body::BirdMedium(body),
|
||||
// None,
|
||||
// comp::Alignment::Wild,
|
||||
// )
|
||||
// }) as _,
|
||||
// (|| {
|
||||
// let body = comp::critter::Body::random();
|
||||
// (
|
||||
// get_npc_name(&NPC_NAMES.critter, body.species).into(),
|
||||
// comp::Body::Critter(body),
|
||||
// None,
|
||||
// comp::Alignment::Wild,
|
||||
// )
|
||||
// }) as _,
|
||||
// (|| {
|
||||
// let body = comp::quadruped_small::Body::random();
|
||||
// (
|
||||
// get_npc_name(&NPC_NAMES.quadruped_small, body.species).into(),
|
||||
// comp::Body::QuadrupedSmall(body),
|
||||
// None,
|
||||
// comp::Alignment::Wild,
|
||||
// )
|
||||
// }),
|
||||
// ];
|
||||
// let (name, mut body, main, mut alignment) = SPAWN_NPCS
|
||||
// .choose(&mut rand::thread_rng())
|
||||
// .expect("SPAWN_NPCS is nonempty")(
|
||||
// );
|
||||
const SPAWN_NPCS: &'static [fn() -> (
|
||||
String,
|
||||
comp::Body,
|
||||
Option<comp::Item>,
|
||||
comp::Alignment,
|
||||
)] = &[
|
||||
(|| {
|
||||
let body = comp::humanoid::Body::random();
|
||||
(
|
||||
format!("{} Traveler", get_npc_name(&NPC_NAMES.humanoid, body.race)),
|
||||
comp::Body::Humanoid(body),
|
||||
Some(assets::load_expect_cloned(
|
||||
"common.items.weapons.starter_axe",
|
||||
)),
|
||||
comp::Alignment::Npc,
|
||||
)
|
||||
}) as _,
|
||||
(|| {
|
||||
let body = comp::humanoid::Body::random();
|
||||
(
|
||||
format!("{} Bandit", get_npc_name(&NPC_NAMES.humanoid, body.race)),
|
||||
comp::Body::Humanoid(body),
|
||||
Some(assets::load_expect_cloned(
|
||||
"common.items.weapons.short_sword_0",
|
||||
)),
|
||||
comp::Alignment::Enemy,
|
||||
)
|
||||
}) as _,
|
||||
(|| {
|
||||
let body = comp::quadruped_medium::Body::random();
|
||||
(
|
||||
get_npc_name(&NPC_NAMES.quadruped_medium, body.species).into(),
|
||||
comp::Body::QuadrupedMedium(body),
|
||||
None,
|
||||
comp::Alignment::Enemy,
|
||||
)
|
||||
}) as _,
|
||||
(|| {
|
||||
let body = comp::bird_medium::Body::random();
|
||||
(
|
||||
get_npc_name(&NPC_NAMES.bird_medium, body.species).into(),
|
||||
comp::Body::BirdMedium(body),
|
||||
None,
|
||||
comp::Alignment::Wild,
|
||||
)
|
||||
}) as _,
|
||||
(|| {
|
||||
let body = comp::critter::Body::random();
|
||||
(
|
||||
get_npc_name(&NPC_NAMES.critter, body.species).into(),
|
||||
comp::Body::Critter(body),
|
||||
None,
|
||||
comp::Alignment::Wild,
|
||||
)
|
||||
}) as _,
|
||||
(|| {
|
||||
let body = comp::quadruped_small::Body::random();
|
||||
(
|
||||
get_npc_name(&NPC_NAMES.quadruped_small, body.species).into(),
|
||||
comp::Body::QuadrupedSmall(body),
|
||||
None,
|
||||
comp::Alignment::Wild,
|
||||
)
|
||||
}),
|
||||
];
|
||||
let (name, mut body, main, mut alignment) = SPAWN_NPCS
|
||||
.choose(&mut rand::thread_rng())
|
||||
.expect("SPAWN_NPCS is nonempty")(
|
||||
);
|
||||
let mut stats = comp::Stats::new(name, body);
|
||||
|
||||
let mut body = entity.body;
|
||||
let mut name = entity.name.unwrap_or("Unnamed".to_string());
|
||||
|
@ -59,6 +59,22 @@ struct SpriteConfig {
|
||||
|
||||
fn sprite_config_for(kind: BlockKind) -> Option<SpriteConfig> {
|
||||
match kind {
|
||||
BlockKind::Window1 => Some(SpriteConfig {
|
||||
variations: 1,
|
||||
wind_sway: 0.0,
|
||||
}),
|
||||
BlockKind::Window2 => Some(SpriteConfig {
|
||||
variations: 1,
|
||||
wind_sway: 0.0,
|
||||
}),
|
||||
BlockKind::Window3 => Some(SpriteConfig {
|
||||
variations: 1,
|
||||
wind_sway: 0.0,
|
||||
}),
|
||||
BlockKind::Window4 => Some(SpriteConfig {
|
||||
variations: 1,
|
||||
wind_sway: 0.0,
|
||||
}),
|
||||
BlockKind::LargeCactus => Some(SpriteConfig {
|
||||
variations: 2,
|
||||
wind_sway: 0.0,
|
||||
@ -206,6 +222,10 @@ fn sprite_config_for(kind: BlockKind) -> Option<SpriteConfig> {
|
||||
variations: 5,
|
||||
wind_sway: 0.1,
|
||||
}),
|
||||
BlockKind::Turnip => Some(SpriteConfig {
|
||||
variations: 6,
|
||||
wind_sway: 0.1,
|
||||
}),
|
||||
BlockKind::Coconut => Some(SpriteConfig {
|
||||
variations: 1,
|
||||
wind_sway: 0.0,
|
||||
@ -311,6 +331,35 @@ impl<V: RectRasterableVol> Terrain<V> {
|
||||
mesh_recv: recv,
|
||||
mesh_todo: HashMap::default(),
|
||||
sprite_models: vec![
|
||||
// Windows
|
||||
(
|
||||
(BlockKind::Window1, 0),
|
||||
make_model(
|
||||
"voxygen.voxel.sprite.window.window-0",
|
||||
Vec3::new(-6.0, -6.0, 0.0),
|
||||
),
|
||||
),
|
||||
(
|
||||
(BlockKind::Window2, 0),
|
||||
make_model(
|
||||
"voxygen.voxel.sprite.window.window-1",
|
||||
Vec3::new(-6.0, -6.0, 0.0),
|
||||
),
|
||||
),
|
||||
(
|
||||
(BlockKind::Window3, 0),
|
||||
make_model(
|
||||
"voxygen.voxel.sprite.window.window-2",
|
||||
Vec3::new(-6.0, -6.0, 0.0),
|
||||
),
|
||||
),
|
||||
(
|
||||
(BlockKind::Window4, 0),
|
||||
make_model(
|
||||
"voxygen.voxel.sprite.window.window-3",
|
||||
Vec3::new(-6.0, -6.0, 0.0),
|
||||
),
|
||||
),
|
||||
// Cacti
|
||||
(
|
||||
(BlockKind::LargeCactus, 0),
|
||||
@ -1423,6 +1472,7 @@ impl<V: RectRasterableVol> Terrain<V> {
|
||||
(BlockKind::Tomato, 4),
|
||||
make_model("voxygen.voxel.sprite.tomato.4", Vec3::new(-5.5, -5.5, 0.0)),
|
||||
),
|
||||
// Radish
|
||||
(
|
||||
(BlockKind::Radish, 0),
|
||||
make_model(
|
||||
@ -1458,6 +1508,49 @@ impl<V: RectRasterableVol> Terrain<V> {
|
||||
Vec3::new(-5.5, -5.5, -0.25),
|
||||
),
|
||||
),
|
||||
// Turnip
|
||||
(
|
||||
(BlockKind::Turnip, 0),
|
||||
make_model(
|
||||
"voxygen.voxel.sprite.turnip.turnip-0",
|
||||
Vec3::new(-5.5, -5.5, -0.25),
|
||||
),
|
||||
),
|
||||
(
|
||||
(BlockKind::Turnip, 1),
|
||||
make_model(
|
||||
"voxygen.voxel.sprite.turnip.turnip-1",
|
||||
Vec3::new(-5.5, -5.5, -0.25),
|
||||
),
|
||||
),
|
||||
(
|
||||
(BlockKind::Turnip, 2),
|
||||
make_model(
|
||||
"voxygen.voxel.sprite.turnip.turnip-2",
|
||||
Vec3::new(-5.5, -5.5, -0.25),
|
||||
),
|
||||
),
|
||||
(
|
||||
(BlockKind::Turnip, 3),
|
||||
make_model(
|
||||
"voxygen.voxel.sprite.turnip.turnip-3",
|
||||
Vec3::new(-5.5, -5.5, -0.25),
|
||||
),
|
||||
),
|
||||
(
|
||||
(BlockKind::Turnip, 4),
|
||||
make_model(
|
||||
"voxygen.voxel.sprite.turnip.turnip-4",
|
||||
Vec3::new(-5.5, -5.5, -0.25),
|
||||
),
|
||||
),
|
||||
(
|
||||
(BlockKind::Turnip, 5),
|
||||
make_model(
|
||||
"voxygen.voxel.sprite.turnip.turnip-5",
|
||||
Vec3::new(-5.5, -5.5, -0.25),
|
||||
),
|
||||
),
|
||||
// Coconut
|
||||
(
|
||||
(BlockKind::Coconut, 0),
|
||||
|
@ -20,8 +20,10 @@ use crate::{
|
||||
util::{Grid, Sampler},
|
||||
};
|
||||
use common::{
|
||||
comp::Alignment,
|
||||
generation::{ChunkSupplement, EntityInfo},
|
||||
comp::{self, humanoid, quadruped_medium, bird_medium, critter, quadruped_small},
|
||||
|
||||
terrain::{Block, BlockKind, TerrainChunk, TerrainChunkMeta, TerrainChunkSize},
|
||||
vol::{ReadVol, RectVolSize, Vox, WriteVol},
|
||||
};
|
||||
@ -206,8 +208,7 @@ impl World {
|
||||
};
|
||||
|
||||
if sim_chunk.contains_waypoint {
|
||||
supplement.add_entity(EntityInfo::at(gen_entity_pos())
|
||||
.into_waypoint());
|
||||
supplement.add_entity(EntityInfo::at(gen_entity_pos()).into_waypoint());
|
||||
}
|
||||
|
||||
// Apply site supplementary information
|
||||
|
@ -604,15 +604,10 @@ impl Settlement {
|
||||
Some(BlockKind::Pumpkin)
|
||||
},
|
||||
Crop::Flax if roll(4, 2) == 80 => Some(BlockKind::Flax),
|
||||
Crop::Carrot if roll(5, 2) == 80 => {
|
||||
Some(BlockKind::Carrot)
|
||||
},
|
||||
Crop::Tomato if roll(6, 2) == 0 => {
|
||||
Some(BlockKind::Tomato)
|
||||
},
|
||||
Crop::Radish if roll(7, 2) == 0 => {
|
||||
Some(BlockKind::Radish)
|
||||
},
|
||||
Crop::Carrot if roll(5, 2) == 80 => Some(BlockKind::Carrot),
|
||||
Crop::Tomato if roll(6, 2) == 0 => Some(BlockKind::Tomato),
|
||||
Crop::Radish if roll(7, 2) == 0 => Some(BlockKind::Radish),
|
||||
Crop::Turnip if roll(7, 2) == 0 => Some(BlockKind::Turnip),
|
||||
Crop::Sunflower => Some(BlockKind::Sunflower),
|
||||
_ => None,
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user