@@ -359,6 +359,8 @@ namespace Content.Client.Entry
|
|||||||
"Thirst",
|
"Thirst",
|
||||||
"CanEscapeInventory",
|
"CanEscapeInventory",
|
||||||
"PowerSink",
|
"PowerSink",
|
||||||
|
"Mousetrap",
|
||||||
|
"DamageOnTrigger",
|
||||||
"Wires",
|
"Wires",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
12
Content.Server/Damage/Components/DamageOnTriggerComponent.cs
Normal file
12
Content.Server/Damage/Components/DamageOnTriggerComponent.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using Content.Shared.Damage;
|
||||||
|
|
||||||
|
namespace Content.Server.Damage.Components;
|
||||||
|
|
||||||
|
[RegisterComponent]
|
||||||
|
public sealed class DamageOnTriggerComponent : Component
|
||||||
|
{
|
||||||
|
[DataField("ignoreResistances")] public bool IgnoreResistances;
|
||||||
|
|
||||||
|
[DataField("damage", required: true)]
|
||||||
|
public DamageSpecifier Damage = default!;
|
||||||
|
}
|
||||||
49
Content.Server/Damage/Systems/DamageOnTriggerSystem.cs
Normal file
49
Content.Server/Damage/Systems/DamageOnTriggerSystem.cs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
using Content.Server.Damage.Components;
|
||||||
|
using Content.Shared.Damage;
|
||||||
|
using Content.Shared.StepTrigger;
|
||||||
|
|
||||||
|
namespace Content.Server.Damage.Systems;
|
||||||
|
|
||||||
|
// System for damage that occurs on specific triggers.
|
||||||
|
// This is originally meant for mousetraps, but could
|
||||||
|
// probably be extended to fit other triggers as well.
|
||||||
|
public sealed class DamageOnTriggerSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
SubscribeLocalEvent<DamageOnTriggerComponent, StepTriggeredEvent>(OnStepTrigger);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStepTrigger(EntityUid uid, DamageOnTriggerComponent component, ref StepTriggeredEvent args)
|
||||||
|
{
|
||||||
|
OnDamageTrigger(uid, args.Tripper, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDamageTrigger(EntityUid source, EntityUid target, DamageOnTriggerComponent? component = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(source, ref component))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var damage = new DamageSpecifier(component.Damage);
|
||||||
|
var ev = new BeforeDamageOnTriggerEvent(damage, target);
|
||||||
|
RaiseLocalEvent(source, ev);
|
||||||
|
|
||||||
|
_damageableSystem.TryChangeDamage(target, ev.Damage, component.IgnoreResistances);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class BeforeDamageOnTriggerEvent : EntityEventArgs
|
||||||
|
{
|
||||||
|
public DamageSpecifier Damage { get; set; }
|
||||||
|
public EntityUid Tripper { get; }
|
||||||
|
|
||||||
|
public BeforeDamageOnTriggerEvent(DamageSpecifier damage, EntityUid target)
|
||||||
|
{
|
||||||
|
Damage = damage;
|
||||||
|
Tripper = target;
|
||||||
|
}
|
||||||
|
}
|
||||||
21
Content.Server/Mousetrap/MousetrapComponent.cs
Normal file
21
Content.Server/Mousetrap/MousetrapComponent.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
namespace Content.Server.Mousetrap;
|
||||||
|
|
||||||
|
[RegisterComponent]
|
||||||
|
public sealed class MousetrapComponent : Component
|
||||||
|
{
|
||||||
|
[ViewVariables]
|
||||||
|
public bool IsActive;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set this to change where the
|
||||||
|
/// inflection point in the scaling
|
||||||
|
/// equation will occur.
|
||||||
|
/// The default is 10.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
[DataField("massBalance")]
|
||||||
|
public int MassBalance = 10;
|
||||||
|
|
||||||
|
[DataField("ignoreDamageIfInventorySlotsFilled")]
|
||||||
|
public List<string> IgnoreDamageIfSlotFilled = new();
|
||||||
|
}
|
||||||
82
Content.Server/Mousetrap/MousetrapSystem.cs
Normal file
82
Content.Server/Mousetrap/MousetrapSystem.cs
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
using Content.Server.Damage.Systems;
|
||||||
|
using Content.Server.Explosion.EntitySystems;
|
||||||
|
using Content.Shared.Interaction.Events;
|
||||||
|
using Content.Shared.Inventory;
|
||||||
|
using Content.Shared.Mousetrap;
|
||||||
|
using Content.Shared.StepTrigger;
|
||||||
|
|
||||||
|
namespace Content.Server.Mousetrap;
|
||||||
|
|
||||||
|
public sealed class MousetrapSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly InventorySystem _inventorySystem = default!;
|
||||||
|
[Dependency] private readonly TriggerSystem _triggerSystem = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
SubscribeLocalEvent<MousetrapComponent, UseInHandEvent>(OnUseInHand);
|
||||||
|
SubscribeLocalEvent<MousetrapComponent, BeforeDamageOnTriggerEvent>(BeforeDamageOnTrigger);
|
||||||
|
SubscribeLocalEvent<MousetrapComponent, StepTriggerAttemptEvent>(OnStepTriggerAttempt);
|
||||||
|
SubscribeLocalEvent<MousetrapComponent, StepTriggeredEvent>(OnStepTrigger);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUseInHand(EntityUid uid, MousetrapComponent component, UseInHandEvent args)
|
||||||
|
{
|
||||||
|
component.IsActive = !component.IsActive;
|
||||||
|
|
||||||
|
UpdateVisuals(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStepTriggerAttempt(EntityUid uid, MousetrapComponent component, ref StepTriggerAttemptEvent args)
|
||||||
|
{
|
||||||
|
args.Continue = component.IsActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BeforeDamageOnTrigger(EntityUid uid, MousetrapComponent component, BeforeDamageOnTriggerEvent args)
|
||||||
|
{
|
||||||
|
foreach (var slot in component.IgnoreDamageIfSlotFilled)
|
||||||
|
{
|
||||||
|
if (!_inventorySystem.TryGetSlotContainer(args.Tripper, slot, out var container, out _))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This also means that wearing slippers won't
|
||||||
|
// hurt the entity.
|
||||||
|
if (container.ContainedEntity != null)
|
||||||
|
{
|
||||||
|
args.Damage *= 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TryComp(args.Tripper, out PhysicsComponent? physics))
|
||||||
|
{
|
||||||
|
// The idea here is inverse,
|
||||||
|
// Small - big damage,
|
||||||
|
// Large - small damage
|
||||||
|
// yes i punched numbers into a calculator until the graph looked right
|
||||||
|
var scaledDamage = -50 * Math.Atan(physics.Mass - component.MassBalance) + (25 * Math.PI);
|
||||||
|
args.Damage *= scaledDamage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStepTrigger(EntityUid uid, MousetrapComponent component, ref StepTriggeredEvent args)
|
||||||
|
{
|
||||||
|
component.IsActive = false;
|
||||||
|
_triggerSystem.Trigger(uid);
|
||||||
|
|
||||||
|
UpdateVisuals(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateVisuals(EntityUid uid, MousetrapComponent? mousetrap = null, AppearanceComponent? appearance = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref mousetrap, ref appearance, false))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
appearance.SetData(MousetrapVisuals.Visual,
|
||||||
|
mousetrap.IsActive ? MousetrapVisuals.Armed : MousetrapVisuals.Unarmed);
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Content.Shared/Mousetrap/MousetrapVisuals.cs
Normal file
11
Content.Shared/Mousetrap/MousetrapVisuals.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Mousetrap;
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum MousetrapVisuals : byte
|
||||||
|
{
|
||||||
|
Visual,
|
||||||
|
Armed,
|
||||||
|
Unarmed
|
||||||
|
}
|
||||||
@@ -22,6 +22,18 @@
|
|||||||
category: Service
|
category: Service
|
||||||
group: market
|
group: market
|
||||||
|
|
||||||
|
- type: cargoProduct
|
||||||
|
name: "mousetraps crate"
|
||||||
|
id: MousetrapBoxes
|
||||||
|
description: "Mousetraps, for when all of service is being haunted by an entire horde of rats. Use sparingly... or not."
|
||||||
|
icon:
|
||||||
|
sprite: Objects/Devices/mousetrap.rsi
|
||||||
|
state: normal
|
||||||
|
product: CrateMousetrapBoxes
|
||||||
|
cost: 1000
|
||||||
|
category: Service
|
||||||
|
group: market
|
||||||
|
|
||||||
- type: cargoProduct
|
- type: cargoProduct
|
||||||
name: "lung cancer crate"
|
name: "lung cancer crate"
|
||||||
id: ServiceSmokeables
|
id: ServiceSmokeables
|
||||||
@@ -57,7 +69,7 @@
|
|||||||
cost: 1000
|
cost: 1000
|
||||||
category: Service
|
category: Service
|
||||||
group: market
|
group: market
|
||||||
|
|
||||||
- type: cargoProduct
|
- type: cargoProduct
|
||||||
name: "personnel crate"
|
name: "personnel crate"
|
||||||
id: ServicePersonnel
|
id: ServicePersonnel
|
||||||
|
|||||||
@@ -7,6 +7,21 @@
|
|||||||
- type: Sprite
|
- type: Sprite
|
||||||
state: box
|
state: box
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
name: mousetrap box
|
||||||
|
parent: BoxCardboard
|
||||||
|
id: BoxMousetrap
|
||||||
|
description: This box is filled with mousetraps. Try not to get your hand stuck in one.
|
||||||
|
components:
|
||||||
|
- type: StorageFill
|
||||||
|
contents:
|
||||||
|
- id: Mousetrap
|
||||||
|
amount: 6
|
||||||
|
- type: Sprite
|
||||||
|
layers:
|
||||||
|
- state: box
|
||||||
|
- state: mousetraps
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
name: lightbulb box
|
name: lightbulb box
|
||||||
parent: BoxCardboard
|
parent: BoxCardboard
|
||||||
|
|||||||
@@ -34,6 +34,17 @@
|
|||||||
- id: BoxLightbulb
|
- id: BoxLightbulb
|
||||||
amount: 1
|
amount: 1
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: CrateMousetrapBoxes
|
||||||
|
name: mousetraps crate
|
||||||
|
description: Mousetraps, for when all of service is being haunted by an entire horde of rats. Use sparingly... or not.
|
||||||
|
parent: CrateGenericSteel
|
||||||
|
components:
|
||||||
|
- type: StorageFill
|
||||||
|
contents:
|
||||||
|
- id: BoxMousetrap
|
||||||
|
amount: 1
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: CrateServiceSmokeables
|
id: CrateServiceSmokeables
|
||||||
name: smokeables crate
|
name: smokeables crate
|
||||||
|
|||||||
@@ -30,6 +30,8 @@
|
|||||||
contents:
|
contents:
|
||||||
- id: CrowbarRed
|
- id: CrowbarRed
|
||||||
- id: MonkeyCubeBox
|
- id: MonkeyCubeBox
|
||||||
|
- id: BoxMousetrap
|
||||||
|
amount: 2
|
||||||
- id: SprayBottleWater
|
- id: SprayBottleWater
|
||||||
- id: ReagentContainerFlour
|
- id: ReagentContainerFlour
|
||||||
amount: 2
|
amount: 2
|
||||||
@@ -49,6 +51,8 @@
|
|||||||
- type: StorageFill
|
- type: StorageFill
|
||||||
contents:
|
contents:
|
||||||
- id: MopItem
|
- id: MopItem
|
||||||
|
- id: BoxMousetrap
|
||||||
|
amount: 2
|
||||||
- id: WetFloorSign
|
- id: WetFloorSign
|
||||||
amount: 3
|
amount: 3
|
||||||
- id: TrashBag
|
- id: TrashBag
|
||||||
|
|||||||
@@ -507,7 +507,7 @@
|
|||||||
- shape:
|
- shape:
|
||||||
!type:PhysShapeCircle
|
!type:PhysShapeCircle
|
||||||
radius: 0.48
|
radius: 0.48
|
||||||
mass: 20
|
mass: 60
|
||||||
mask:
|
mask:
|
||||||
- MobMask
|
- MobMask
|
||||||
layer:
|
layer:
|
||||||
@@ -560,7 +560,7 @@
|
|||||||
crit: kangaroo-dead
|
crit: kangaroo-dead
|
||||||
dead: kangaroo-dead
|
dead: kangaroo-dead
|
||||||
- type: Puller
|
- type: Puller
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
name: boxing kangaroo
|
name: boxing kangaroo
|
||||||
parent: MobKangaroo
|
parent: MobKangaroo
|
||||||
@@ -997,7 +997,7 @@
|
|||||||
- shape:
|
- shape:
|
||||||
!type:PhysShapeCircle
|
!type:PhysShapeCircle
|
||||||
radius: 0.25
|
radius: 0.25
|
||||||
mass: 10
|
mass: 20
|
||||||
mask:
|
mask:
|
||||||
- MobMask
|
- MobMask
|
||||||
layer:
|
layer:
|
||||||
@@ -1213,7 +1213,7 @@
|
|||||||
- shape:
|
- shape:
|
||||||
!type:PhysShapeCircle
|
!type:PhysShapeCircle
|
||||||
radius: 0.35
|
radius: 0.35
|
||||||
mass: 10
|
mass: 20
|
||||||
mask:
|
mask:
|
||||||
- MobMask
|
- MobMask
|
||||||
layer:
|
layer:
|
||||||
@@ -1269,7 +1269,7 @@
|
|||||||
- shape:
|
- shape:
|
||||||
!type:PhysShapeCircle
|
!type:PhysShapeCircle
|
||||||
radius: 0.35
|
radius: 0.35
|
||||||
mass: 10
|
mass: 20
|
||||||
mask:
|
mask:
|
||||||
- MobMask
|
- MobMask
|
||||||
layer:
|
layer:
|
||||||
@@ -1325,7 +1325,7 @@
|
|||||||
- shape:
|
- shape:
|
||||||
!type:PhysShapeCircle
|
!type:PhysShapeCircle
|
||||||
radius: 0.35
|
radius: 0.35
|
||||||
mass: 10
|
mass: 20
|
||||||
mask:
|
mask:
|
||||||
- MobMask
|
- MobMask
|
||||||
layer:
|
layer:
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
- shape:
|
- shape:
|
||||||
!type:PhysShapeCircle
|
!type:PhysShapeCircle
|
||||||
radius: 0.35
|
radius: 0.35
|
||||||
mass: 10
|
mass: 20
|
||||||
mask:
|
mask:
|
||||||
- MobMask
|
- MobMask
|
||||||
layer:
|
layer:
|
||||||
@@ -138,7 +138,7 @@
|
|||||||
spawned:
|
spawned:
|
||||||
- id: FoodMeatCorgi
|
- id: FoodMeatCorgi
|
||||||
amount: 2
|
amount: 2
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
name: Old Ian
|
name: Old Ian
|
||||||
parent: MobCorgi
|
parent: MobCorgi
|
||||||
@@ -225,7 +225,7 @@
|
|||||||
- shape:
|
- shape:
|
||||||
!type:PhysShapeCircle
|
!type:PhysShapeCircle
|
||||||
radius: 0.35
|
radius: 0.35
|
||||||
mass: 10
|
mass: 20
|
||||||
mask:
|
mask:
|
||||||
- MobMask
|
- MobMask
|
||||||
layer:
|
layer:
|
||||||
@@ -375,7 +375,7 @@
|
|||||||
- shape:
|
- shape:
|
||||||
!type:PhysShapeCircle
|
!type:PhysShapeCircle
|
||||||
radius: 0.35
|
radius: 0.35
|
||||||
mass: 10
|
mass: 20
|
||||||
mask:
|
mask:
|
||||||
- MobMask
|
- MobMask
|
||||||
layer:
|
layer:
|
||||||
@@ -418,7 +418,7 @@
|
|||||||
- shape:
|
- shape:
|
||||||
!type:PhysShapeCircle
|
!type:PhysShapeCircle
|
||||||
radius: 0.35
|
radius: 0.35
|
||||||
mass: 10
|
mass: 20
|
||||||
mask:
|
mask:
|
||||||
- MobMask
|
- MobMask
|
||||||
layer:
|
layer:
|
||||||
@@ -472,7 +472,7 @@
|
|||||||
- shape:
|
- shape:
|
||||||
!type:PhysShapeCircle
|
!type:PhysShapeCircle
|
||||||
radius: 0.35
|
radius: 0.35
|
||||||
mass: 10
|
mass: 20
|
||||||
mask:
|
mask:
|
||||||
- MobMask
|
- MobMask
|
||||||
layer:
|
layer:
|
||||||
@@ -515,7 +515,7 @@
|
|||||||
- shape:
|
- shape:
|
||||||
!type:PhysShapeCircle
|
!type:PhysShapeCircle
|
||||||
radius: 0.35
|
radius: 0.35
|
||||||
mass: 10
|
mass: 20
|
||||||
mask:
|
mask:
|
||||||
- MobMask
|
- MobMask
|
||||||
layer:
|
layer:
|
||||||
@@ -592,7 +592,7 @@
|
|||||||
- shape:
|
- shape:
|
||||||
!type:PhysShapeCircle
|
!type:PhysShapeCircle
|
||||||
radius: 0.35
|
radius: 0.35
|
||||||
mass: 10
|
mass: 20
|
||||||
mask:
|
mask:
|
||||||
- MobMask
|
- MobMask
|
||||||
layer:
|
layer:
|
||||||
|
|||||||
58
Resources/Prototypes/Entities/Objects/Devices/mousetrap.yml
Normal file
58
Resources/Prototypes/Entities/Objects/Devices/mousetrap.yml
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
- type: entity
|
||||||
|
name: mousetrap
|
||||||
|
parent: BaseItem
|
||||||
|
id: Mousetrap
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Objects/Devices/mousetrap.rsi
|
||||||
|
netsync: false
|
||||||
|
drawdepth: SmallMobs # if mice can hide under tables, so can mousetraps
|
||||||
|
layers:
|
||||||
|
- state: mousetrap
|
||||||
|
- type: StepTrigger
|
||||||
|
intersectRatio: 0.2
|
||||||
|
requiredTriggeredSpeed: 0
|
||||||
|
- type: Mousetrap
|
||||||
|
ignoreDamageIfInventorySlotsFilled:
|
||||||
|
- shoes # shoes
|
||||||
|
- type: DamageOnTrigger
|
||||||
|
damage:
|
||||||
|
types:
|
||||||
|
Blunt: 2 # base damage, scales based on mass
|
||||||
|
- type: EmitSoundOnUse
|
||||||
|
sound: "/Audio/Items/Handcuffs/cuff_end.ogg"
|
||||||
|
- type: EmitSoundOnTrigger
|
||||||
|
sound: "/Audio/Items/snap.ogg"
|
||||||
|
- type: Item
|
||||||
|
sprite: Objects/Devices/mousetrap.rsi
|
||||||
|
- type: Appearance
|
||||||
|
visuals:
|
||||||
|
- type: GenericEnumVisualizer
|
||||||
|
key: enum.MousetrapVisuals.Visual
|
||||||
|
layer: 0
|
||||||
|
states:
|
||||||
|
enum.MousetrapVisuals.Armed: mousetraparmed
|
||||||
|
enum.MousetrapVisuals.Unarmed: mousetrap
|
||||||
|
- type: Physics
|
||||||
|
bodyType: Dynamic
|
||||||
|
- type: CollisionWake
|
||||||
|
enabled: false
|
||||||
|
- type: Fixtures
|
||||||
|
fixtures:
|
||||||
|
- shape:
|
||||||
|
!type:PhysShapeAabb
|
||||||
|
bounds: "-0.2,-0.2,0.2,0.2"
|
||||||
|
id: "slips"
|
||||||
|
hard: false
|
||||||
|
layer:
|
||||||
|
- LowImpassable
|
||||||
|
- shape:
|
||||||
|
!type:PhysShapeAabb
|
||||||
|
bounds: "-0.2,-0.2,0.2,0.2"
|
||||||
|
mass: 5
|
||||||
|
mask:
|
||||||
|
- ItemMask
|
||||||
|
- type: Rotatable
|
||||||
|
- type: Tag
|
||||||
|
tags:
|
||||||
|
- DroneUsable
|
||||||
23
Resources/Textures/Objects/Devices/mousetrap.rsi/meta.json
Normal file
23
Resources/Textures/Objects/Devices/mousetrap.rsi/meta.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
|
||||||
|
"license": "CC-BY-SA-3.0",
|
||||||
|
"copyright": "Taken from https://github.com/tgstation/tgstation/commit/4323540b6bec3ae93bbf13d685c2dbe0cb40a36e",
|
||||||
|
|
||||||
|
"size": {
|
||||||
|
"x": 32,
|
||||||
|
"y": 32
|
||||||
|
},
|
||||||
|
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "mousetrap",
|
||||||
|
"directions": 4
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "mousetraparmed",
|
||||||
|
"directions": 4
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
Resources/Textures/Objects/Devices/mousetrap.rsi/mousetrap.png
Normal file
BIN
Resources/Textures/Objects/Devices/mousetrap.rsi/mousetrap.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
Reference in New Issue
Block a user