Enforcer rocket attack
This commit is contained in:
parent
2e354f5e84
commit
0a21a0432e
@ -0,0 +1,46 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.client.model.entity;
|
||||||
|
|
||||||
|
import net.minecraft.client.model.HierarchicalModel;
|
||||||
|
import net.minecraft.client.model.geom.ModelLayerLocation;
|
||||||
|
import net.minecraft.client.model.geom.ModelPart;
|
||||||
|
import net.minecraft.client.model.geom.PartPose;
|
||||||
|
import net.minecraft.client.model.geom.builders.*;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import ru.dbotthepony.mc.otm.entity.RocketProjectile;
|
||||||
|
import ru.dbotthepony.mc.otm.registry.MNames;
|
||||||
|
|
||||||
|
import static ru.dbotthepony.mc.otm.OverdriveThatMatters.loc;
|
||||||
|
|
||||||
|
public class RocketModel extends HierarchicalModel<RocketProjectile> {
|
||||||
|
public static final ModelLayerLocation LAYER_LOCATION = new ModelLayerLocation(loc(MNames.ROCKET), "main");
|
||||||
|
private final ModelPart mainPart;
|
||||||
|
|
||||||
|
public RocketModel(ModelPart root) {
|
||||||
|
this.mainPart = root.getChild("bb_main");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LayerDefinition createBodyLayer() {
|
||||||
|
MeshDefinition meshDefinition = new MeshDefinition();
|
||||||
|
PartDefinition partDefinition = meshDefinition.getRoot();
|
||||||
|
|
||||||
|
partDefinition.addOrReplaceChild(
|
||||||
|
"bb_main",
|
||||||
|
CubeListBuilder.create()
|
||||||
|
.texOffs(0, 0)
|
||||||
|
.addBox(-2.0F, -2.0F, -6.0F, 4.0F, 4.0F, 12.0F),
|
||||||
|
PartPose.offset(0.0F, 0.0F, 0.0F)
|
||||||
|
);
|
||||||
|
|
||||||
|
return LayerDefinition.create(meshDefinition, 32, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setupAnim(RocketProjectile entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ModelPart root() {
|
||||||
|
return mainPart;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.client.renderer.entity
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack
|
||||||
|
import net.minecraft.client.model.geom.ModelPart
|
||||||
|
import net.minecraft.client.renderer.MultiBufferSource
|
||||||
|
import net.minecraft.client.renderer.RenderType
|
||||||
|
import net.minecraft.client.renderer.entity.EntityRenderer
|
||||||
|
import net.minecraft.client.renderer.entity.EntityRendererProvider
|
||||||
|
import net.minecraft.client.renderer.texture.OverlayTexture
|
||||||
|
import net.minecraft.resources.ResourceLocation
|
||||||
|
import net.minecraft.world.phys.Vec3
|
||||||
|
import ru.dbotthepony.mc.otm.OverdriveThatMatters.loc
|
||||||
|
import ru.dbotthepony.mc.otm.client.model.entity.RocketModel
|
||||||
|
import ru.dbotthepony.mc.otm.entity.RocketProjectile
|
||||||
|
|
||||||
|
class RocketRenderer(context: EntityRendererProvider.Context) : EntityRenderer<RocketProjectile>(context) {
|
||||||
|
|
||||||
|
private val model: ModelPart = RocketModel.createBodyLayer().bakeRoot()
|
||||||
|
private val TEXTURE = loc("textures/entity/missile.png")
|
||||||
|
|
||||||
|
override fun getTextureLocation(entity: RocketProjectile): ResourceLocation = TEXTURE
|
||||||
|
|
||||||
|
override fun render(
|
||||||
|
entity: RocketProjectile,
|
||||||
|
yaw: Float,
|
||||||
|
partialTicks: Float,
|
||||||
|
poseStack: PoseStack,
|
||||||
|
buffer: MultiBufferSource,
|
||||||
|
packedLight: Int
|
||||||
|
) {
|
||||||
|
poseStack.pushPose()
|
||||||
|
|
||||||
|
val motion: Vec3 = entity.deltaMovement
|
||||||
|
val speed = motion.length()
|
||||||
|
|
||||||
|
if (speed > 0) {
|
||||||
|
val yawRotation = Math.toDegrees(Math.atan2(motion.x, motion.z)).toFloat()
|
||||||
|
|
||||||
|
val pitch = Math.toDegrees(-motion.y / speed).toFloat()
|
||||||
|
|
||||||
|
poseStack.mulPose(com.mojang.math.Axis.YP.rotationDegrees(yawRotation))
|
||||||
|
poseStack.mulPose(com.mojang.math.Axis.XP.rotationDegrees(pitch+ 180F))
|
||||||
|
}
|
||||||
|
|
||||||
|
val vertexConsumer = buffer.getBuffer(RenderType.entityCutout(TEXTURE))
|
||||||
|
model.render(poseStack, vertexConsumer, 15728880, OverlayTexture.NO_OVERLAY)
|
||||||
|
|
||||||
|
poseStack.popPose()
|
||||||
|
super.render(entity, yaw, partialTicks, poseStack, buffer, packedLight)
|
||||||
|
}
|
||||||
|
}
|
@ -10,6 +10,7 @@ import net.minecraft.server.level.ServerPlayer
|
|||||||
import net.minecraft.sounds.SoundEvent
|
import net.minecraft.sounds.SoundEvent
|
||||||
import net.minecraft.sounds.SoundEvents
|
import net.minecraft.sounds.SoundEvents
|
||||||
import net.minecraft.sounds.SoundSource
|
import net.minecraft.sounds.SoundSource
|
||||||
|
import net.minecraft.tags.ItemTags
|
||||||
import net.minecraft.util.Mth
|
import net.minecraft.util.Mth
|
||||||
import net.minecraft.world.BossEvent
|
import net.minecraft.world.BossEvent
|
||||||
import net.minecraft.world.damagesource.DamageSource
|
import net.minecraft.world.damagesource.DamageSource
|
||||||
@ -32,6 +33,8 @@ import net.minecraft.world.entity.monster.Zombie
|
|||||||
import net.minecraft.world.entity.npc.Villager
|
import net.minecraft.world.entity.npc.Villager
|
||||||
import net.minecraft.world.entity.player.Player
|
import net.minecraft.world.entity.player.Player
|
||||||
import net.minecraft.world.entity.projectile.SmallFireball
|
import net.minecraft.world.entity.projectile.SmallFireball
|
||||||
|
import net.minecraft.world.item.Items
|
||||||
|
import net.minecraft.world.item.ShieldItem
|
||||||
import net.minecraft.world.level.Level
|
import net.minecraft.world.level.Level
|
||||||
import net.minecraft.world.phys.Vec3
|
import net.minecraft.world.phys.Vec3
|
||||||
import ru.dbotthepony.mc.otm.registry.game.MSoundEvents
|
import ru.dbotthepony.mc.otm.registry.game.MSoundEvents
|
||||||
@ -89,7 +92,7 @@ class Enforcer(type: EntityType<Enforcer>, level: Level) : Monster(type,level) {
|
|||||||
|
|
||||||
goalSelector.addGoal(2, RammingGoal(this))
|
goalSelector.addGoal(2, RammingGoal(this))
|
||||||
goalSelector.addGoal(2, StayNearGoal(this))
|
goalSelector.addGoal(2, StayNearGoal(this))
|
||||||
goalSelector.addGoal(2, BlazeFireballGoal(this))
|
goalSelector.addGoal(2, EnforcerArsenalGoal(this))
|
||||||
|
|
||||||
goalSelector.addGoal(7, LookAtPlayerGoal(this, Player::class.java, 8f))
|
goalSelector.addGoal(7, LookAtPlayerGoal(this, Player::class.java, 8f))
|
||||||
}
|
}
|
||||||
@ -168,16 +171,13 @@ class Enforcer(type: EntityType<Enforcer>, level: Level) : Monster(type,level) {
|
|||||||
private fun shootMissile(math: Float) {
|
private fun shootMissile(math: Float) {
|
||||||
if (level().isClientSide) return
|
if (level().isClientSide) return
|
||||||
|
|
||||||
//idea: make it fire 3 rockets that fall onto the ground instead of just straight/homing missiles/rockets
|
val offset_dist = 1.2
|
||||||
//TODO
|
|
||||||
|
|
||||||
val offset_dist = 1.4
|
|
||||||
val viewVector = this.getViewVector(1.0F).normalize()
|
val viewVector = this.getViewVector(1.0F).normalize()
|
||||||
val leftVec = Vec3(-viewVector.z, 0.0, viewVector.x).normalize().scale(offset_dist)
|
val leftVec = Vec3(-viewVector.z, 0.0, viewVector.x).normalize().scale(offset_dist)
|
||||||
|
|
||||||
val gun_x = x + leftVec.x
|
val gun_x = x + leftVec.x
|
||||||
val gun_z = z + leftVec.z
|
val gun_z = z + leftVec.z
|
||||||
val gun_y = y + 1.2
|
val gun_y = y + 2.2
|
||||||
|
|
||||||
//stfu idea i like my underscores
|
//stfu idea i like my underscores
|
||||||
|
|
||||||
@ -185,12 +185,26 @@ class Enforcer(type: EntityType<Enforcer>, level: Level) : Monster(type,level) {
|
|||||||
val missileY = gun_y
|
val missileY = gun_y
|
||||||
val missileZ = gun_z
|
val missileZ = gun_z
|
||||||
|
|
||||||
val fireball = SmallFireball(level(), missileX, missileY, missileZ, viewVector)
|
val rocket1 = RocketProjectile(level())
|
||||||
fireball.shootFromRotation(this, this.xRot, this.yHeadRot, 0.0F, 1.5F, 0.2F)
|
val rocket2 = RocketProjectile(level())
|
||||||
fireball.setPos(missileX, missileY, missileZ)
|
val rocket3 = RocketProjectile(level())
|
||||||
fireball.owner = this
|
|
||||||
|
rocket1.setPos(missileX, missileY, missileZ)
|
||||||
|
rocket2.setPos(missileX, missileY, missileZ)
|
||||||
|
rocket3.setPos(missileX, missileY, missileZ)
|
||||||
|
|
||||||
|
rocket1.shootFromRotation(this, this.xRot, this.yHeadRot, 0.0F, 1.2F, 0.4F)
|
||||||
|
rocket2.shootFromRotation(this, this.xRot-15, this.yHeadRot, 0.0F, 1.2F, 0.4F)
|
||||||
|
rocket3.shootFromRotation(this, this.xRot-30, this.yHeadRot, 0.0F, 1.2F, 0.4F)
|
||||||
|
|
||||||
|
rocket1.owner = this
|
||||||
|
rocket2.owner = this
|
||||||
|
rocket3.owner = this
|
||||||
|
|
||||||
|
level().addFreshEntity(rocket1)
|
||||||
|
level().addFreshEntity(rocket2)
|
||||||
|
level().addFreshEntity(rocket3)
|
||||||
|
|
||||||
level().addFreshEntity(fireball)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//charge attack
|
//charge attack
|
||||||
@ -300,11 +314,20 @@ class Enforcer(type: EntityType<Enforcer>, level: Level) : Monster(type,level) {
|
|||||||
val knockbackStrength = 1.5
|
val knockbackStrength = 1.5
|
||||||
val knockback = dir.scale(knockbackStrength)
|
val knockback = dir.scale(knockbackStrength)
|
||||||
|
|
||||||
target.push(knockback.x, 0.5, knockback.z)
|
|
||||||
|
|
||||||
val damageAmount = 14.0f
|
val damageAmount = 14.0f
|
||||||
target.hurt(mob.damageSources().mobAttack(mob), damageAmount)
|
target.hurt(mob.damageSources().mobAttack(mob), damageAmount)
|
||||||
|
|
||||||
|
if (target is Player) {
|
||||||
|
val player = target as Player
|
||||||
|
|
||||||
|
if (player.isBlocking) {
|
||||||
|
target.disableShield()
|
||||||
|
if (!player.level().isClientSide) {
|
||||||
|
mob.playSound(SoundEvents.ZOMBIE_BREAK_WOODEN_DOOR, 1.0f, 1.0f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
target.push(knockback.x, 0.5, knockback.z)
|
||||||
stop()
|
stop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -319,7 +342,7 @@ class Enforcer(type: EntityType<Enforcer>, level: Level) : Monster(type,level) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val minCooldown = 35
|
private const val minCooldown = 50
|
||||||
private const val maxCooldown = 60
|
private const val maxCooldown = 60
|
||||||
private const val windupTime = 20
|
private const val windupTime = 20
|
||||||
private const val maxChargeTime = 40
|
private const val maxChargeTime = 40
|
||||||
@ -365,8 +388,10 @@ class Enforcer(type: EntityType<Enforcer>, level: Level) : Monster(type,level) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class BlazeFireballGoal(private val mob: Enforcer) : Goal() {
|
class EnforcerArsenalGoal(private val mob: Enforcer) : Goal() {
|
||||||
private var cooldown = 0
|
private var cooldown = 0
|
||||||
|
private var rocketcooldown = 0
|
||||||
|
|
||||||
private val tickList = TickList()
|
private val tickList = TickList()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@ -392,6 +417,15 @@ class Enforcer(type: EntityType<Enforcer>, level: Level) : Monster(type,level) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (--rocketcooldown <= 0) {
|
||||||
|
rocketcooldown = ROCKET_COOLDOWN + Mth.nextInt(mob.random, MIN_COOLDOWN, MAX_COOLDOWN)
|
||||||
|
|
||||||
|
|
||||||
|
mob.shootMissile(0.5f)
|
||||||
|
mob.playSound(SoundEvents.WITHER_SHOOT, 1.0f, Mth.nextFloat(mob.random, 0.9f, 1.1f))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
tickList.tick()
|
tickList.tick()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -405,6 +439,9 @@ class Enforcer(type: EntityType<Enforcer>, level: Level) : Monster(type,level) {
|
|||||||
private val SHOOT_AT = intArrayOf(5, 7, 9)
|
private val SHOOT_AT = intArrayOf(5, 7, 9)
|
||||||
private const val MIN_COOLDOWN = 5
|
private const val MIN_COOLDOWN = 5
|
||||||
private const val MAX_COOLDOWN = 15
|
private const val MAX_COOLDOWN = 15
|
||||||
|
|
||||||
|
private const val ROCKET_COOLDOWN = 20
|
||||||
|
|
||||||
private const val CYCLE_LENGTH = 20
|
private const val CYCLE_LENGTH = 20
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,88 +1,57 @@
|
|||||||
package ru.dbotthepony.mc.otm.entity
|
package ru.dbotthepony.mc.otm.entity
|
||||||
|
|
||||||
import net.minecraft.core.particles.ParticleTypes
|
import net.minecraft.core.particles.ParticleTypes
|
||||||
import net.minecraft.network.syncher.SynchedEntityData
|
import net.minecraft.sounds.SoundEvent
|
||||||
import net.minecraft.world.damagesource.DamageTypes
|
import net.minecraft.sounds.SoundEvents
|
||||||
import net.minecraft.world.entity.projectile.Projectile
|
import net.minecraft.world.entity.projectile.AbstractArrow
|
||||||
import net.minecraft.world.entity.projectile.ProjectileUtil
|
|
||||||
import net.minecraft.world.item.ItemStack
|
import net.minecraft.world.item.ItemStack
|
||||||
import net.minecraft.world.level.Level
|
import net.minecraft.world.level.Level
|
||||||
import net.minecraft.world.level.block.Blocks
|
|
||||||
import net.minecraft.world.phys.BlockHitResult
|
import net.minecraft.world.phys.BlockHitResult
|
||||||
import net.minecraft.world.phys.EntityHitResult
|
import net.minecraft.world.phys.EntityHitResult
|
||||||
import net.minecraft.world.phys.HitResult
|
|
||||||
import net.neoforged.neoforge.event.EventHooks
|
|
||||||
import ru.dbotthepony.mc.otm.registry.game.MEntityTypes
|
import ru.dbotthepony.mc.otm.registry.game.MEntityTypes
|
||||||
import ru.dbotthepony.mc.otm.registry.MatteryDamageSource
|
|
||||||
import ru.dbotthepony.mc.otm.util.damageType
|
|
||||||
|
|
||||||
class RocketProjectile(level: Level) : Projectile(MEntityTypes.PLASMA, level) {
|
class RocketProjectile(level: Level) : AbstractArrow(MEntityTypes.ROCKET, level) {
|
||||||
var inflictor: ItemStack? = null
|
|
||||||
var damage = 6.0f
|
|
||||||
var ttl = 200
|
|
||||||
|
|
||||||
override fun defineSynchedData(p_326003_: SynchedEntityData.Builder) {
|
init {
|
||||||
|
setBaseDamage(5.0)
|
||||||
}
|
setCritArrow(false)
|
||||||
|
pickup = Pickup.DISALLOWED
|
||||||
override fun onHit(p_37260_: HitResult) {
|
|
||||||
super.onHit(p_37260_)
|
|
||||||
|
|
||||||
if (!level().isClientSide) {
|
|
||||||
discard()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onHitEntity(p_37259_: EntityHitResult) {
|
|
||||||
super.onHitEntity(p_37259_)
|
|
||||||
|
|
||||||
if (!level().isClientSide) {
|
|
||||||
p_37259_.entity.hurt(MatteryDamageSource(level().registryAccess().damageType(DamageTypes.EXPLOSION), owner, inflictor), damage)
|
|
||||||
level().explode(this, x, y, z, 2.5f, Level.ExplosionInteraction.BLOCK)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onHitBlock(p_37258_: BlockHitResult) {
|
|
||||||
super.onHitBlock(p_37258_)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun tick() {
|
override fun tick() {
|
||||||
if (--ttl <= 0) {
|
if (!inGround) deltaMovement = deltaMovement.add(0.0, -0.05, 0.0)
|
||||||
discard()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
super.tick()
|
super.tick()
|
||||||
|
if (level().isClientSide) {
|
||||||
val trace = ProjectileUtil.getHitResultOnMoveVector(this, this::canHitEntity)
|
level().addParticle(ParticleTypes.CAMPFIRE_COSY_SMOKE, xOld, yOld, zOld, 0.0, 0.0, 0.0)
|
||||||
|
|
||||||
if (trace.type == HitResult.Type.BLOCK) {
|
|
||||||
val pos = (trace as BlockHitResult).blockPos
|
|
||||||
val state = level().getBlockState(pos)
|
|
||||||
level().explode(this, x, y, z, 2.5f, Level.ExplosionInteraction.BLOCK)
|
|
||||||
|
|
||||||
|
|
||||||
if (state.`is`(Blocks.NETHER_PORTAL) || state.`is`(Blocks.END_GATEWAY)) {
|
|
||||||
onHitBlock(trace)
|
|
||||||
|
|
||||||
// don't teleport plasma projectile
|
|
||||||
if (!level().isClientSide)
|
|
||||||
discard()
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (trace.type != HitResult.Type.MISS && !EventHooks.onProjectileImpact(this, trace)) {
|
override fun onHitEntity(result: EntityHitResult) {
|
||||||
onHit(trace)
|
if (!level().isClientSide) {
|
||||||
|
level().explode(null, x, y, z, 2.0f, Level.ExplosionInteraction.NONE)
|
||||||
|
discard()
|
||||||
}
|
}
|
||||||
|
super.onHitEntity(result)
|
||||||
|
}
|
||||||
|
|
||||||
checkInsideBlocks()
|
override fun onHitBlock(result: BlockHitResult) {
|
||||||
|
if (!level().isClientSide) {
|
||||||
|
level().explode(null, x, y, z, 2.0f, Level.ExplosionInteraction.NONE)
|
||||||
|
discard()
|
||||||
|
}
|
||||||
|
super.onHitBlock(result)
|
||||||
|
}
|
||||||
|
|
||||||
val x = x + deltaMovement.x
|
//asdasdadad
|
||||||
val y = y + deltaMovement.y
|
override fun playSound(sound: SoundEvent, volume: Float, pitch: Float) {
|
||||||
val z = z + deltaMovement.z
|
if (sound != SoundEvents.ARROW_HIT) {
|
||||||
|
super.playSound(sound, volume, pitch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setPos(x, y, z)
|
override fun getDefaultPickupItem(): ItemStack {
|
||||||
|
return ItemStack.EMPTY
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ import net.neoforged.bus.api.IEventBus
|
|||||||
import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent
|
import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent
|
||||||
import net.neoforged.neoforge.event.entity.EntityAttributeCreationEvent
|
import net.neoforged.neoforge.event.entity.EntityAttributeCreationEvent
|
||||||
import ru.dbotthepony.mc.otm.client.render.entity.*
|
import ru.dbotthepony.mc.otm.client.render.entity.*
|
||||||
|
import ru.dbotthepony.mc.otm.client.renderer.entity.RocketRenderer
|
||||||
import ru.dbotthepony.mc.otm.entity.*
|
import ru.dbotthepony.mc.otm.entity.*
|
||||||
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
|
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
|
||||||
import ru.dbotthepony.mc.otm.registry.MNames
|
import ru.dbotthepony.mc.otm.registry.MNames
|
||||||
@ -119,6 +120,7 @@ object MEntityTypes {
|
|||||||
private fun registerClient(event: FMLClientSetupEvent) {
|
private fun registerClient(event: FMLClientSetupEvent) {
|
||||||
event.enqueueWork {
|
event.enqueueWork {
|
||||||
EntityRenderers.register(PLASMA) { PlasmaProjectileRenderer(it) as EntityRenderer<Entity> }
|
EntityRenderers.register(PLASMA) { PlasmaProjectileRenderer(it) as EntityRenderer<Entity> }
|
||||||
|
EntityRenderers.register(ROCKET) { RocketRenderer(it) as EntityRenderer<Entity> }
|
||||||
|
|
||||||
for (type in CARGO_CRATE_MINECARTS.values) {
|
for (type in CARGO_CRATE_MINECARTS.values) {
|
||||||
EntityRenderers.register(type) { MinecartRenderer<MinecartCargoCrate>(it, ModelLayers.CHEST_MINECART) as EntityRenderer<Entity> }
|
EntityRenderers.register(type) { MinecartRenderer<MinecartCargoCrate>(it, ModelLayers.CHEST_MINECART) as EntityRenderer<Entity> }
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 241 B After Width: | Height: | Size: 519 B |
Loading…
Reference in New Issue
Block a user