mirror of
				https://github.com/tobspr/shapez.io.git
				synced 2025-06-13 13:04:03 +00:00 
			
		
		
		
	Refactor belt underlay feature into seperate component
This commit is contained in:
		
							parent
							
								
									f91e677f2e
								
							
						
					
					
						commit
						b3b8da04a1
					
				@ -1,14 +1,14 @@
 | 
			
		||||
import { globalConfig } from "../../core/config";
 | 
			
		||||
import { enumDirection, Vector } from "../../core/vector";
 | 
			
		||||
import { ItemAcceptorComponent } from "../components/item_acceptor";
 | 
			
		||||
import { ItemEjectorComponent } from "../components/item_ejector";
 | 
			
		||||
import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor";
 | 
			
		||||
import { Entity } from "../entity";
 | 
			
		||||
import { MetaBuilding, defaultBuildingVariant } from "../meta_building";
 | 
			
		||||
import { GameRoot, enumLayer } from "../root";
 | 
			
		||||
import { GameRoot } from "../root";
 | 
			
		||||
import { enumHubGoalRewards } from "../tutorial_goals";
 | 
			
		||||
import { T } from "../../translations";
 | 
			
		||||
import { formatItemsPerSecond } from "../../core/utils";
 | 
			
		||||
import { BeltUnderlaysComponent } from "../components/belt_underlays";
 | 
			
		||||
 | 
			
		||||
/** @enum {string} */
 | 
			
		||||
export const enumSplitterVariants = { compact: "compact", compactInverse: "compact-inverse" };
 | 
			
		||||
@ -88,6 +88,8 @@ export class MetaSplitterBuilding extends MetaBuilding {
 | 
			
		||||
                slots: [], // set later
 | 
			
		||||
            })
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        entity.addComponent(new BeltUnderlaysComponent({ underlays: [] }));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -115,9 +117,9 @@ export class MetaSplitterBuilding extends MetaBuilding {
 | 
			
		||||
                    { pos: new Vector(1, 0), direction: enumDirection.top },
 | 
			
		||||
                ]);
 | 
			
		||||
 | 
			
		||||
                entity.components.ItemAcceptor.beltUnderlays = [
 | 
			
		||||
                    { pos: new Vector(0, 0), direction: enumDirection.top, layer: enumLayer.regular },
 | 
			
		||||
                    { pos: new Vector(1, 0), direction: enumDirection.top, layer: enumLayer.regular },
 | 
			
		||||
                entity.components.BeltUnderlays.underlays = [
 | 
			
		||||
                    { pos: new Vector(0, 0), direction: enumDirection.top },
 | 
			
		||||
                    { pos: new Vector(1, 0), direction: enumDirection.top },
 | 
			
		||||
                ];
 | 
			
		||||
 | 
			
		||||
                break;
 | 
			
		||||
@ -143,8 +145,8 @@ export class MetaSplitterBuilding extends MetaBuilding {
 | 
			
		||||
                    { pos: new Vector(0, 0), direction: enumDirection.top },
 | 
			
		||||
                ]);
 | 
			
		||||
 | 
			
		||||
                entity.components.ItemAcceptor.beltUnderlays = [
 | 
			
		||||
                    { pos: new Vector(0, 0), direction: enumDirection.top, layer: enumLayer.regular },
 | 
			
		||||
                entity.components.BeltUnderlays.underlays = [
 | 
			
		||||
                    { pos: new Vector(0, 0), direction: enumDirection.top },
 | 
			
		||||
                ];
 | 
			
		||||
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
@ -13,6 +13,7 @@ import { StorageComponent } from "./components/storage";
 | 
			
		||||
import { EnergyGeneratorComponent } from "./components/energy_generator";
 | 
			
		||||
import { WiredPinsComponent } from "./components/wired_pins";
 | 
			
		||||
import { EnergyConsumerComponent } from "./components/energy_consumer";
 | 
			
		||||
import { BeltUnderlaysComponent } from "./components/belt_underlays";
 | 
			
		||||
 | 
			
		||||
export function initComponentRegistry() {
 | 
			
		||||
    gComponentRegistry.register(StaticMapEntityComponent);
 | 
			
		||||
@ -29,6 +30,7 @@ export function initComponentRegistry() {
 | 
			
		||||
    gComponentRegistry.register(EnergyGeneratorComponent);
 | 
			
		||||
    gComponentRegistry.register(WiredPinsComponent);
 | 
			
		||||
    gComponentRegistry.register(EnergyConsumerComponent);
 | 
			
		||||
    gComponentRegistry.register(BeltUnderlaysComponent);
 | 
			
		||||
 | 
			
		||||
    // IMPORTANT ^^^^^ UPDATE ENTITY COMPONENT STORAGE AFTERWARDS
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										44
									
								
								src/js/game/components/belt_underlays.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/js/game/components/belt_underlays.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,44 @@
 | 
			
		||||
import { Component } from "../component";
 | 
			
		||||
import { types } from "../../savegame/serialization";
 | 
			
		||||
import { enumDirection, Vector } from "../../core/vector";
 | 
			
		||||
 | 
			
		||||
export class BeltUnderlaysComponent extends Component {
 | 
			
		||||
    static getId() {
 | 
			
		||||
        return "BeltUnderlays";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static getSchema() {
 | 
			
		||||
        return {
 | 
			
		||||
            underlays: types.array(
 | 
			
		||||
                types.structured({
 | 
			
		||||
                    pos: types.vector,
 | 
			
		||||
                    direction: types.enum(enumDirection),
 | 
			
		||||
                })
 | 
			
		||||
            ),
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    duplicateWithoutContents() {
 | 
			
		||||
        const beltUnderlaysCopy = [];
 | 
			
		||||
        for (let i = 0; i < this.underlays.length; ++i) {
 | 
			
		||||
            const underlay = this.underlays[i];
 | 
			
		||||
            beltUnderlaysCopy.push({
 | 
			
		||||
                pos: underlay.pos.copy(),
 | 
			
		||||
                direction: underlay.direction,
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return new BeltUnderlaysComponent({
 | 
			
		||||
            underlays: beltUnderlaysCopy,
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {object} param0
 | 
			
		||||
     * @param {Array<{pos: Vector, direction: enumDirection}>=} param0.underlays Where to render belt underlays
 | 
			
		||||
     */
 | 
			
		||||
    constructor({ underlays }) {
 | 
			
		||||
        super();
 | 
			
		||||
        this.underlays = underlays;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -39,16 +39,6 @@ export class ItemAcceptorComponent extends Component {
 | 
			
		||||
                    directions: types.array(types.enum(enumDirection)),
 | 
			
		||||
                    filter: types.nullable(types.enum(enumItemType)),
 | 
			
		||||
 | 
			
		||||
                    // TODO: MIGRATE
 | 
			
		||||
                    layer: types.enum(enumLayer),
 | 
			
		||||
                })
 | 
			
		||||
            ),
 | 
			
		||||
            animated: types.bool,
 | 
			
		||||
            beltUnderlays: types.array(
 | 
			
		||||
                types.structured({
 | 
			
		||||
                    pos: types.vector,
 | 
			
		||||
                    direction: types.enum(enumDirection),
 | 
			
		||||
 | 
			
		||||
                    // TODO: MIGRATE
 | 
			
		||||
                    layer: types.enum(enumLayer),
 | 
			
		||||
                })
 | 
			
		||||
@ -68,20 +58,8 @@ export class ItemAcceptorComponent extends Component {
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const beltUnderlaysCopy = [];
 | 
			
		||||
        for (let i = 0; i < this.beltUnderlays.length; ++i) {
 | 
			
		||||
            const underlay = this.beltUnderlays[i];
 | 
			
		||||
            beltUnderlaysCopy.push({
 | 
			
		||||
                pos: underlay.pos.copy(),
 | 
			
		||||
                direction: underlay.direction,
 | 
			
		||||
                layer: underlay.layer,
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return new ItemAcceptorComponent({
 | 
			
		||||
            slots: slotsCopy,
 | 
			
		||||
            beltUnderlays: beltUnderlaysCopy,
 | 
			
		||||
            animated: this.animated,
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -89,23 +67,16 @@ export class ItemAcceptorComponent extends Component {
 | 
			
		||||
     *
 | 
			
		||||
     * @param {object} param0
 | 
			
		||||
     * @param {Array<ItemAcceptorSlotConfig>} param0.slots The slots from which we accept items
 | 
			
		||||
     * @param {boolean=} param0.animated Whether to animate item consumption
 | 
			
		||||
     * @param {Array<{pos: Vector, direction: enumDirection, layer: enumLayer}>=} param0.beltUnderlays Where to render belt underlays
 | 
			
		||||
     */
 | 
			
		||||
    constructor({ slots = [], beltUnderlays = [], animated = true }) {
 | 
			
		||||
    constructor({ slots = [] }) {
 | 
			
		||||
        super();
 | 
			
		||||
 | 
			
		||||
        this.animated = animated;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Fixes belt animations
 | 
			
		||||
         * @type {Array<{ item: BaseItem, slotIndex: number, animProgress: number, direction: enumDirection }>}
 | 
			
		||||
         */
 | 
			
		||||
        this.itemConsumptionAnimations = [];
 | 
			
		||||
 | 
			
		||||
        /* Which belt underlays to render */
 | 
			
		||||
        this.beltUnderlays = beltUnderlays;
 | 
			
		||||
 | 
			
		||||
        this.setSlots(slots);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -164,14 +135,12 @@ export class ItemAcceptorComponent extends Component {
 | 
			
		||||
     * @param {BaseItem} item
 | 
			
		||||
     */
 | 
			
		||||
    onItemAccepted(slotIndex, direction, item) {
 | 
			
		||||
        if (this.animated) {
 | 
			
		||||
            this.itemConsumptionAnimations.push({
 | 
			
		||||
                item,
 | 
			
		||||
                slotIndex,
 | 
			
		||||
                direction,
 | 
			
		||||
                animProgress: 0.0,
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        this.itemConsumptionAnimations.push({
 | 
			
		||||
            item,
 | 
			
		||||
            slotIndex,
 | 
			
		||||
            direction,
 | 
			
		||||
            animProgress: 0.0,
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
@ -398,7 +398,7 @@ export class GameCore {
 | 
			
		||||
 | 
			
		||||
        if (!this.root.camera.getIsMapOverlayActive()) {
 | 
			
		||||
            // Underlays for splitters / balancers
 | 
			
		||||
            systems.itemAcceptor.drawUnderlays(params, enumLayer.regular);
 | 
			
		||||
            systems.beltUnderlays.drawUnderlays(params, enumLayer.regular);
 | 
			
		||||
 | 
			
		||||
            // Belt items
 | 
			
		||||
            systems.belt.drawLayerBeltItems(params, enumLayer.regular);
 | 
			
		||||
 | 
			
		||||
@ -13,6 +13,7 @@ import { StorageComponent } from "./components/storage";
 | 
			
		||||
import { EnergyGeneratorComponent } from "./components/energy_generator";
 | 
			
		||||
import { WiredPinsComponent } from "./components/wired_pins";
 | 
			
		||||
import { EnergyConsumerComponent } from "./components/energy_consumer";
 | 
			
		||||
import { BeltUnderlaysComponent } from "./components/belt_underlays";
 | 
			
		||||
/* typehints:end */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@ -65,6 +66,9 @@ export class EntityComponentStorage {
 | 
			
		||||
        /** @type {EnergyConsumerComponent} */
 | 
			
		||||
        this.EnergyConsumer;
 | 
			
		||||
 | 
			
		||||
        /** @type {BeltUnderlaysComponent} */
 | 
			
		||||
        this.BeltUnderlays;
 | 
			
		||||
 | 
			
		||||
        /* typehints:end */
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -16,6 +16,7 @@ import { StorageSystem } from "./systems/storage";
 | 
			
		||||
import { EnergyGeneratorSystem } from "./systems/energy_generator";
 | 
			
		||||
import { WiredPinsSystem } from "./systems/wired_pins";
 | 
			
		||||
import { EnergyConsumerSystem } from "./systems/energy_consumer";
 | 
			
		||||
import { BeltUnderlaysSystem } from "./systems/belt_underlays";
 | 
			
		||||
 | 
			
		||||
const logger = createLogger("game_system_manager");
 | 
			
		||||
 | 
			
		||||
@ -68,6 +69,9 @@ export class GameSystemManager {
 | 
			
		||||
            /** @type {EnergyConsumerSystem} */
 | 
			
		||||
            energyConsumer: null,
 | 
			
		||||
 | 
			
		||||
            /** @type {BeltUnderlaysSystem} */
 | 
			
		||||
            beltUnderlays: null,
 | 
			
		||||
 | 
			
		||||
            /* typehints:end */
 | 
			
		||||
        };
 | 
			
		||||
        this.systemUpdateOrder = [];
 | 
			
		||||
@ -110,6 +114,8 @@ export class GameSystemManager {
 | 
			
		||||
 | 
			
		||||
        add("energyConsumer", EnergyConsumerSystem);
 | 
			
		||||
 | 
			
		||||
        add("beltUnderlays", BeltUnderlaysSystem);
 | 
			
		||||
 | 
			
		||||
        // IMPORTANT: Must be after belt system since belt system can change the
 | 
			
		||||
        // orientation of an entity after it is placed -> the item acceptor cache
 | 
			
		||||
        // then would be invalid
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										75
									
								
								src/js/game/systems/belt_underlays.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								src/js/game/systems/belt_underlays.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,75 @@
 | 
			
		||||
import { GameSystemWithFilter } from "../game_system_with_filter";
 | 
			
		||||
import { BeltUnderlaysComponent } from "../components/belt_underlays";
 | 
			
		||||
import { BELT_ANIM_COUNT } from "./belt";
 | 
			
		||||
import { Loader } from "../../core/loader";
 | 
			
		||||
import { enumLayer } from "../root";
 | 
			
		||||
import { Entity } from "../entity";
 | 
			
		||||
import { enumDirectionToAngle } from "../../core/vector";
 | 
			
		||||
import { globalConfig } from "../../core/config";
 | 
			
		||||
import { drawRotatedSprite } from "../../core/draw_utils";
 | 
			
		||||
 | 
			
		||||
export class BeltUnderlaysSystem extends GameSystemWithFilter {
 | 
			
		||||
    constructor(root) {
 | 
			
		||||
        super(root, [BeltUnderlaysComponent]);
 | 
			
		||||
 | 
			
		||||
        this.underlayBeltSprites = [];
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < BELT_ANIM_COUNT; ++i) {
 | 
			
		||||
            this.underlayBeltSprites.push(Loader.getSprite("sprites/belt/forward_" + i + ".png"));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Draws the acceptor underlays
 | 
			
		||||
     * @param {import("../../core/draw_utils").DrawParameters} parameters
 | 
			
		||||
     * @param {enumLayer} layer
 | 
			
		||||
     */
 | 
			
		||||
    drawUnderlays(parameters, layer) {
 | 
			
		||||
        this.forEachMatchingEntityOnScreen(parameters, this.drawEntityUnderlays.bind(this, layer));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {enumLayer} layer
 | 
			
		||||
     * @param {import("../../core/draw_utils").DrawParameters} parameters
 | 
			
		||||
     * @param {Entity} entity
 | 
			
		||||
     */
 | 
			
		||||
    drawEntityUnderlays(layer, parameters, entity) {
 | 
			
		||||
        const staticComp = entity.components.StaticMapEntity;
 | 
			
		||||
        const underlayComp = entity.components.BeltUnderlays;
 | 
			
		||||
 | 
			
		||||
        if (entity.layer !== layer) {
 | 
			
		||||
            // Not our layer
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!staticComp.shouldBeDrawn(parameters)) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Limit speed to avoid belts going backwards
 | 
			
		||||
        const speedMultiplier = Math.min(this.root.hubGoals.getBeltBaseSpeed(layer), 10);
 | 
			
		||||
 | 
			
		||||
        const underlays = underlayComp.underlays;
 | 
			
		||||
        for (let i = 0; i < underlays.length; ++i) {
 | 
			
		||||
            const { pos, direction } = underlays[i];
 | 
			
		||||
 | 
			
		||||
            const transformedPos = staticComp.localTileToWorld(pos);
 | 
			
		||||
            const angle = enumDirectionToAngle[staticComp.localDirectionToWorld(direction)];
 | 
			
		||||
 | 
			
		||||
            // SYNC with systems/belt.js:drawSingleEntity!
 | 
			
		||||
            const animationIndex = Math.floor(
 | 
			
		||||
                ((this.root.time.realtimeNow() * speedMultiplier * BELT_ANIM_COUNT * 126) / 42) *
 | 
			
		||||
                    globalConfig.beltItemSpacingByLayer[layer]
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            drawRotatedSprite({
 | 
			
		||||
                parameters,
 | 
			
		||||
                sprite: this.underlayBeltSprites[animationIndex % this.underlayBeltSprites.length],
 | 
			
		||||
                x: (transformedPos.x + 0.5) * globalConfig.tileSize,
 | 
			
		||||
                y: (transformedPos.y + 0.5) * globalConfig.tileSize,
 | 
			
		||||
                angle: Math.radians(angle),
 | 
			
		||||
                size: globalConfig.tileSize,
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -13,12 +13,6 @@ import { enumLayer } from "../root";
 | 
			
		||||
export class ItemAcceptorSystem extends GameSystemWithFilter {
 | 
			
		||||
    constructor(root) {
 | 
			
		||||
        super(root, [ItemAcceptorComponent]);
 | 
			
		||||
 | 
			
		||||
        this.underlayBeltSprites = [];
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < BELT_ANIM_COUNT; ++i) {
 | 
			
		||||
            this.underlayBeltSprites.push(Loader.getSprite("sprites/belt/forward_" + i + ".png"));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    update() {
 | 
			
		||||
@ -59,15 +53,6 @@ export class ItemAcceptorSystem extends GameSystemWithFilter {
 | 
			
		||||
        this.forEachMatchingEntityOnScreen(parameters, this.drawEntityRegularLayer.bind(this, layer));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Draws the acceptor underlays
 | 
			
		||||
     * @param {DrawParameters} parameters
 | 
			
		||||
     * @param {enumLayer} layer
 | 
			
		||||
     */
 | 
			
		||||
    drawUnderlays(parameters, layer) {
 | 
			
		||||
        this.forEachMatchingEntityOnScreen(parameters, this.drawEntityUnderlays.bind(this, layer));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {enumLayer} layer
 | 
			
		||||
     * @param {DrawParameters} parameters
 | 
			
		||||
@ -105,48 +90,4 @@ export class ItemAcceptorSystem extends GameSystemWithFilter {
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {enumLayer} layer
 | 
			
		||||
     * @param {DrawParameters} parameters
 | 
			
		||||
     * @param {Entity} entity
 | 
			
		||||
     */
 | 
			
		||||
    drawEntityUnderlays(layer, parameters, entity) {
 | 
			
		||||
        const staticComp = entity.components.StaticMapEntity;
 | 
			
		||||
        const acceptorComp = entity.components.ItemAcceptor;
 | 
			
		||||
 | 
			
		||||
        if (!staticComp.shouldBeDrawn(parameters)) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Limit speed to avoid belts going backwards
 | 
			
		||||
        const speedMultiplier = Math.min(this.root.hubGoals.getBeltBaseSpeed(layer), 10);
 | 
			
		||||
 | 
			
		||||
        const underlays = acceptorComp.beltUnderlays;
 | 
			
		||||
        for (let i = 0; i < underlays.length; ++i) {
 | 
			
		||||
            const { pos, direction, layer: underlayLayer } = underlays[i];
 | 
			
		||||
            if (underlayLayer !== layer) {
 | 
			
		||||
                // Not our layer
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const transformedPos = staticComp.localTileToWorld(pos);
 | 
			
		||||
            const angle = enumDirectionToAngle[staticComp.localDirectionToWorld(direction)];
 | 
			
		||||
 | 
			
		||||
            // SYNC with systems/belt.js:drawSingleEntity!
 | 
			
		||||
            const animationIndex = Math.floor(
 | 
			
		||||
                ((this.root.time.realtimeNow() * speedMultiplier * BELT_ANIM_COUNT * 126) / 42) *
 | 
			
		||||
                    globalConfig.beltItemSpacingByLayer[layer]
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            drawRotatedSprite({
 | 
			
		||||
                parameters,
 | 
			
		||||
                sprite: this.underlayBeltSprites[animationIndex % this.underlayBeltSprites.length],
 | 
			
		||||
                x: (transformedPos.x + 0.5) * globalConfig.tileSize,
 | 
			
		||||
                y: (transformedPos.y + 0.5) * globalConfig.tileSize,
 | 
			
		||||
                angle: Math.radians(angle),
 | 
			
		||||
                size: globalConfig.tileSize,
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user