mirror of
https://github.com/fulpstation/fulpstation.git
synced 2025-12-10 01:57:01 +00:00
Cable Layers (#46761)
* Cable Layers rudimentary function layer placement icons rebase fixes sprites refactor dme fix dme fix again potentially functional version power bridge sprites work correctly slightly better sprite examine message + minor organization * revert runtime changes
This commit is contained in:
@@ -24,6 +24,7 @@
|
|||||||
#define DISPOSAL_PIPE_LAYER 2.3
|
#define DISPOSAL_PIPE_LAYER 2.3
|
||||||
#define GAS_PIPE_HIDDEN_LAYER 2.35
|
#define GAS_PIPE_HIDDEN_LAYER 2.35
|
||||||
#define WIRE_LAYER 2.4
|
#define WIRE_LAYER 2.4
|
||||||
|
#define WIRE_BRIDGE_LAYER 2.44
|
||||||
#define WIRE_TERMINAL_LAYER 2.45
|
#define WIRE_TERMINAL_LAYER 2.45
|
||||||
#define GAS_SCRUBBER_LAYER 2.46
|
#define GAS_SCRUBBER_LAYER 2.46
|
||||||
#define GAS_PIPE_VISIBLE_LAYER 2.47
|
#define GAS_PIPE_VISIBLE_LAYER 2.47
|
||||||
|
|||||||
3
code/__DEFINES/power.dm
Normal file
3
code/__DEFINES/power.dm
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
#define CABLE_LAYER_1 "l1"
|
||||||
|
#define CABLE_LAYER_2 "l2"
|
||||||
|
#define CABLE_LAYER_3 "l3"
|
||||||
@@ -184,8 +184,6 @@
|
|||||||
return TRUE
|
return TRUE
|
||||||
if(can_lay_cable() && istype(C, /obj/item/stack/cable_coil))
|
if(can_lay_cable() && istype(C, /obj/item/stack/cable_coil))
|
||||||
var/obj/item/stack/cable_coil/coil = C
|
var/obj/item/stack/cable_coil/coil = C
|
||||||
for(var/obj/structure/cable/LC in src)
|
|
||||||
return
|
|
||||||
coil.place_turf(src, user)
|
coil.place_turf(src, user)
|
||||||
return TRUE
|
return TRUE
|
||||||
else if(can_have_cabling() && istype(C, /obj/item/stack/pipe_cleaner_coil))
|
else if(can_have_cabling() && istype(C, /obj/item/stack/pipe_cleaner_coil))
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//Use this only for things that aren't a subtype of obj/machinery/power
|
//Use this only for things that aren't a subtype of obj/machinery/power
|
||||||
//For things that are, override "should_have_node()" on them
|
//For things that are, override "should_have_node()" on them
|
||||||
GLOBAL_LIST_INIT(wire_node_generating_types, typecacheof(list(/obj/structure/grille)))
|
GLOBAL_LIST_INIT(wire_node_generating_types, typecacheof(list(/obj/structure/grille, /obj/structure/cable_bridge)))
|
||||||
|
|
||||||
#define UNDER_SMES -1
|
#define UNDER_SMES -1
|
||||||
#define UNDER_TERMINAL 1
|
#define UNDER_TERMINAL 1
|
||||||
@@ -14,17 +14,31 @@ GLOBAL_LIST_INIT(wire_node_generating_types, typecacheof(list(/obj/structure/gri
|
|||||||
/obj/structure/cable
|
/obj/structure/cable
|
||||||
name = "power cable"
|
name = "power cable"
|
||||||
desc = "A flexible, superconducting insulated cable for heavy-duty power transfer."
|
desc = "A flexible, superconducting insulated cable for heavy-duty power transfer."
|
||||||
icon = 'icons/obj/power_cond/cable.dmi'
|
icon = 'icons/obj/power_cond/layer_cable.dmi'
|
||||||
icon_state = "1-2-4-8-node"
|
icon_state = "l2-1-2-4-8-node"
|
||||||
|
color = "yellow"
|
||||||
level = 1 //is underfloor
|
level = 1 //is underfloor
|
||||||
layer = WIRE_LAYER //Above hidden pipes, GAS_PIPE_HIDDEN_LAYER
|
layer = WIRE_LAYER //Above hidden pipes, GAS_PIPE_HIDDEN_LAYER
|
||||||
anchored = TRUE
|
anchored = TRUE
|
||||||
obj_flags = CAN_BE_HIT | ON_BLUEPRINTS
|
obj_flags = CAN_BE_HIT | ON_BLUEPRINTS
|
||||||
var/linked_dirs = 0 //bitflag
|
var/linked_dirs = 0 //bitflag
|
||||||
var/node = FALSE //used for sprites display
|
var/node = FALSE //used for sprites display
|
||||||
|
var/cable_layer = CABLE_LAYER_2
|
||||||
var/datum/powernet/powernet
|
var/datum/powernet/powernet
|
||||||
|
|
||||||
/obj/structure/cable/Initialize(mapload, param_color)
|
/obj/structure/cable/layer1
|
||||||
|
color = "red"
|
||||||
|
cable_layer = CABLE_LAYER_1
|
||||||
|
layer = WIRE_LAYER - 0.01
|
||||||
|
icon_state = "l1-1-2-4-8-node"
|
||||||
|
|
||||||
|
/obj/structure/cable/layer3
|
||||||
|
color = "blue"
|
||||||
|
cable_layer = CABLE_LAYER_3
|
||||||
|
layer = WIRE_LAYER + 0.01
|
||||||
|
icon_state = "l3-1-2-4-8-node"
|
||||||
|
|
||||||
|
/obj/structure/cable/Initialize(mapload)
|
||||||
. = ..()
|
. = ..()
|
||||||
|
|
||||||
var/turf/T = get_turf(src) // hide if turf is not intact
|
var/turf/T = get_turf(src) // hide if turf is not intact
|
||||||
@@ -68,6 +82,7 @@ GLOBAL_LIST_INIT(wire_node_generating_types, typecacheof(list(/obj/structure/gri
|
|||||||
continue
|
continue
|
||||||
var/inverse = turn(check_dir, 180)
|
var/inverse = turn(check_dir, 180)
|
||||||
for(var/obj/structure/cable/C in TB)
|
for(var/obj/structure/cable/C in TB)
|
||||||
|
if(C.cable_layer == cable_layer)
|
||||||
linked_dirs |= check_dir
|
linked_dirs |= check_dir
|
||||||
C.linked_dirs |= inverse
|
C.linked_dirs |= inverse
|
||||||
C.update_icon()
|
C.update_icon()
|
||||||
@@ -80,8 +95,8 @@ GLOBAL_LIST_INIT(wire_node_generating_types, typecacheof(list(/obj/structure/gri
|
|||||||
var/inverse = turn(check_dir, 180)
|
var/inverse = turn(check_dir, 180)
|
||||||
if(linked_dirs & check_dir)
|
if(linked_dirs & check_dir)
|
||||||
var/TB = get_step(loc, check_dir)
|
var/TB = get_step(loc, check_dir)
|
||||||
var/obj/structure/cable/C = locate(/obj/structure/cable) in TB
|
for(var/obj/structure/cable/C in TB)
|
||||||
if(C)
|
if(cable_layer == C.cable_layer)
|
||||||
C.linked_dirs &= ~inverse
|
C.linked_dirs &= ~inverse
|
||||||
C.update_icon()
|
C.update_icon()
|
||||||
|
|
||||||
@@ -108,7 +123,7 @@ GLOBAL_LIST_INIT(wire_node_generating_types, typecacheof(list(/obj/structure/gri
|
|||||||
|
|
||||||
/obj/structure/cable/update_icon()
|
/obj/structure/cable/update_icon()
|
||||||
if(!linked_dirs)
|
if(!linked_dirs)
|
||||||
icon_state = "noconnection"
|
icon_state = "[cable_layer]-noconnection"
|
||||||
else
|
else
|
||||||
var/list/dir_icon_list = list()
|
var/list/dir_icon_list = list()
|
||||||
for(var/check_dir in GLOB.cardinals)
|
for(var/check_dir in GLOB.cardinals)
|
||||||
@@ -125,6 +140,7 @@ GLOBAL_LIST_INIT(wire_node_generating_types, typecacheof(list(/obj/structure/gri
|
|||||||
if(P.should_have_node())
|
if(P.should_have_node())
|
||||||
dir_string = "[dir_string]-node"
|
dir_string = "[dir_string]-node"
|
||||||
break
|
break
|
||||||
|
dir_string = "[cable_layer]-[dir_string]"
|
||||||
icon_state = dir_string
|
icon_state = dir_string
|
||||||
|
|
||||||
|
|
||||||
@@ -234,6 +250,9 @@ GLOBAL_LIST_INIT(wire_node_generating_types, typecacheof(list(/obj/structure/gri
|
|||||||
if(src == C)
|
if(src == C)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if(cable_layer != C.cable_layer)
|
||||||
|
continue
|
||||||
|
|
||||||
if(C.linked_dirs & inverse_dir) //we've got a matching cable in the neighbor turf
|
if(C.linked_dirs & inverse_dir) //we've got a matching cable in the neighbor turf
|
||||||
if(!C.powernet) //if the matching cable somehow got no powernet, make him one (should not happen for cables)
|
if(!C.powernet) //if the matching cable somehow got no powernet, make him one (should not happen for cables)
|
||||||
var/datum/powernet/newPN = new()
|
var/datum/powernet/newPN = new()
|
||||||
@@ -286,13 +305,16 @@ GLOBAL_LIST_INIT(wire_node_generating_types, typecacheof(list(/obj/structure/gri
|
|||||||
|
|
||||||
/obj/structure/cable/proc/get_cable_connections(powernetless_only)
|
/obj/structure/cable/proc/get_cable_connections(powernetless_only)
|
||||||
. = list()
|
. = list()
|
||||||
var/turf/T
|
var/turf/T = get_turf(src)
|
||||||
|
if(locate(/obj/structure/cable_bridge) in T)
|
||||||
|
for(var/obj/structure/cable/C in T)
|
||||||
|
if(C != src)
|
||||||
|
. += C
|
||||||
for(var/check_dir in GLOB.cardinals)
|
for(var/check_dir in GLOB.cardinals)
|
||||||
if(linked_dirs & check_dir)
|
if(linked_dirs & check_dir)
|
||||||
T = get_step(src, check_dir)
|
T = get_step(src, check_dir)
|
||||||
if(T)
|
for(var/obj/structure/cable/C in T)
|
||||||
var/obj/structure/cable/C = locate(/obj/structure/cable) in T
|
if(cable_layer == C.cable_layer)
|
||||||
if(C)
|
|
||||||
. += C
|
. += C
|
||||||
|
|
||||||
/obj/structure/cable/proc/get_machine_connections(powernetless_only)
|
/obj/structure/cable/proc/get_machine_connections(powernetless_only)
|
||||||
@@ -354,7 +376,8 @@ GLOBAL_LIST_INIT(wire_node_generating_types, typecacheof(list(/obj/structure/gri
|
|||||||
// Definitions
|
// Definitions
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
|
|
||||||
GLOBAL_LIST_INIT(cable_coil_recipes, list (new/datum/stack_recipe("cable restraints", /obj/item/restraints/handcuffs/cable, 15)))
|
GLOBAL_LIST_INIT(cable_coil_recipes, list(new/datum/stack_recipe("cable restraints", /obj/item/restraints/handcuffs/cable, 15),
|
||||||
|
new/datum/stack_recipe("cable bridge", /obj/structure/cable_bridge, 15)))
|
||||||
|
|
||||||
/obj/item/stack/cable_coil
|
/obj/item/stack/cable_coil
|
||||||
name = "cable coil"
|
name = "cable coil"
|
||||||
@@ -368,7 +391,8 @@ GLOBAL_LIST_INIT(cable_coil_recipes, list (new/datum/stack_recipe("cable restrai
|
|||||||
max_amount = MAXCOIL
|
max_amount = MAXCOIL
|
||||||
amount = MAXCOIL
|
amount = MAXCOIL
|
||||||
merge_type = /obj/item/stack/cable_coil // This is here to let its children merge between themselves
|
merge_type = /obj/item/stack/cable_coil // This is here to let its children merge between themselves
|
||||||
var/cable_color = "red"
|
color = "yellow"
|
||||||
|
var/cable_color = "yellow"
|
||||||
desc = "A coil of insulated power cable."
|
desc = "A coil of insulated power cable."
|
||||||
throwforce = 0
|
throwforce = 0
|
||||||
w_class = WEIGHT_CLASS_SMALL
|
w_class = WEIGHT_CLASS_SMALL
|
||||||
@@ -382,23 +406,7 @@ GLOBAL_LIST_INIT(cable_coil_recipes, list (new/datum/stack_recipe("cable restrai
|
|||||||
full_w_class = WEIGHT_CLASS_SMALL
|
full_w_class = WEIGHT_CLASS_SMALL
|
||||||
grind_results = list(/datum/reagent/copper = 2) //2 copper per cable in the coil
|
grind_results = list(/datum/reagent/copper = 2) //2 copper per cable in the coil
|
||||||
usesound = 'sound/items/deconstruct.ogg'
|
usesound = 'sound/items/deconstruct.ogg'
|
||||||
|
var/obj/structure/cable/target_type = /obj/structure/cable
|
||||||
/obj/item/stack/cable_coil/cyborg
|
|
||||||
is_cyborg = 1
|
|
||||||
materials = list()
|
|
||||||
cost = 1
|
|
||||||
|
|
||||||
/obj/item/stack/cable_coil/cyborg/attack_self(mob/user)
|
|
||||||
var/picked = input(user,"Pick a cable color.","Cable Color") in list("red","yellow","green","blue","pink","orange","cyan","white")
|
|
||||||
cable_color = picked
|
|
||||||
update_icon()
|
|
||||||
|
|
||||||
/obj/item/stack/cable_coil/suicide_act(mob/user)
|
|
||||||
if(locate(/obj/structure/chair/stool) in get_turf(user))
|
|
||||||
user.visible_message("<span class='suicide'>[user] is making a noose with [src]! It looks like [user.p_theyre()] trying to commit suicide!</span>")
|
|
||||||
else
|
|
||||||
user.visible_message("<span class='suicide'>[user] is strangling [user.p_them()]self with [src]! It looks like [user.p_theyre()] trying to commit suicide!</span>")
|
|
||||||
return(OXYLOSS)
|
|
||||||
|
|
||||||
/obj/item/stack/cable_coil/Initialize(mapload, new_amount = null)
|
/obj/item/stack/cable_coil/Initialize(mapload, new_amount = null)
|
||||||
. = ..()
|
. = ..()
|
||||||
@@ -407,11 +415,50 @@ GLOBAL_LIST_INIT(cable_coil_recipes, list (new/datum/stack_recipe("cable restrai
|
|||||||
update_icon()
|
update_icon()
|
||||||
recipes = GLOB.cable_coil_recipes
|
recipes = GLOB.cable_coil_recipes
|
||||||
|
|
||||||
|
/obj/item/stack/cable_coil/examine(mob/user)
|
||||||
|
. = ..()
|
||||||
|
. += "<b>Ctrl+Click</b> to change the layer you are placing on."
|
||||||
|
|
||||||
|
/obj/item/stack/cable_coil/suicide_act(mob/user)
|
||||||
|
if(locate(/obj/structure/chair/stool) in get_turf(user))
|
||||||
|
user.visible_message("<span class='suicide'>[user] is making a noose with [src]! It looks like [user.p_theyre()] trying to commit suicide!</span>")
|
||||||
|
else
|
||||||
|
user.visible_message("<span class='suicide'>[user] is strangling [user.p_them()]self with [src]! It looks like [user.p_theyre()] trying to commit suicide!</span>")
|
||||||
|
return(OXYLOSS)
|
||||||
|
|
||||||
|
/obj/item/stack/cable_coil/proc/check_menu(mob/living/user)
|
||||||
|
if(!istype(user))
|
||||||
|
return FALSE
|
||||||
|
if(user.incapacitated() || !user.Adjacent(src))
|
||||||
|
return FALSE
|
||||||
|
return TRUE
|
||||||
|
|
||||||
|
/obj/item/stack/cable_coil/CtrlClick(mob/living/user)
|
||||||
|
if(!user)
|
||||||
|
return
|
||||||
|
var/list/layer_list = list(
|
||||||
|
"Layer 1" = image(icon = 'icons/mob/radial.dmi', icon_state = "coil-red"),
|
||||||
|
"Layer 2" = image(icon = 'icons/mob/radial.dmi', icon_state = "coil-yellow"),
|
||||||
|
"Layer 3" = image(icon = 'icons/mob/radial.dmi', icon_state = "coil-blue")
|
||||||
|
)
|
||||||
|
var/layer_result = show_radial_menu(user, src, layer_list, custom_check = CALLBACK(src, .proc/check_menu, user), require_near = TRUE, tooltips = TRUE)
|
||||||
|
if(!check_menu(user))
|
||||||
|
return
|
||||||
|
switch(layer_result)
|
||||||
|
if("Layer 1")
|
||||||
|
color = "red"
|
||||||
|
target_type = /obj/structure/cable/layer1
|
||||||
|
if("Layer 2")
|
||||||
|
color = "yellow"
|
||||||
|
target_type = /obj/structure/cable
|
||||||
|
if("Layer 3")
|
||||||
|
color = "blue"
|
||||||
|
target_type = /obj/structure/cable/layer3
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////
|
///////////////////////////////////
|
||||||
// General procedures
|
// General procedures
|
||||||
///////////////////////////////////
|
///////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
//you can use wires to heal robotics
|
//you can use wires to heal robotics
|
||||||
/obj/item/stack/cable_coil/attack(mob/living/carbon/human/H, mob/user)
|
/obj/item/stack/cable_coil/attack(mob/living/carbon/human/H, mob/user)
|
||||||
if(!istype(H))
|
if(!istype(H))
|
||||||
@@ -443,15 +490,10 @@ GLOBAL_LIST_INIT(cable_coil_recipes, list (new/datum/stack_recipe("cable restrai
|
|||||||
update_icon()
|
update_icon()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////
|
///////////////////////////////////////////////
|
||||||
// Cable laying procedures
|
// Cable laying procedures
|
||||||
//////////////////////////////////////////////
|
//////////////////////////////////////////////
|
||||||
|
|
||||||
/obj/item/stack/cable_coil/proc/get_new_cable(location)
|
|
||||||
var/path = /obj/structure/cable
|
|
||||||
return new path(location, cable_color)
|
|
||||||
|
|
||||||
// called when cable_coil is clicked on a turf
|
// called when cable_coil is clicked on a turf
|
||||||
/obj/item/stack/cable_coil/proc/place_turf(turf/T, mob/user, dirnew)
|
/obj/item/stack/cable_coil/proc/place_turf(turf/T, mob/user, dirnew)
|
||||||
if(!isturf(user.loc))
|
if(!isturf(user.loc))
|
||||||
@@ -469,11 +511,12 @@ GLOBAL_LIST_INIT(cable_coil_recipes, list (new/datum/stack_recipe("cable restrai
|
|||||||
to_chat(user, "<span class='warning'>You can't lay cable at a place that far away!</span>")
|
to_chat(user, "<span class='warning'>You can't lay cable at a place that far away!</span>")
|
||||||
return
|
return
|
||||||
|
|
||||||
if(is_type_in_list(/obj/structure/cable, T.contents))
|
for(var/obj/structure/cable/C in T)
|
||||||
|
if(target_type == C.type)
|
||||||
to_chat(user, "<span class='warning'>There's already a cable at that position!</span>")
|
to_chat(user, "<span class='warning'>There's already a cable at that position!</span>")
|
||||||
return
|
return
|
||||||
|
|
||||||
var/obj/structure/cable/C = get_new_cable(T)
|
var/obj/structure/cable/C = new target_type(T)
|
||||||
|
|
||||||
//create a new powernet with the cable, if needed it will be merged later
|
//create a new powernet with the cable, if needed it will be merged later
|
||||||
var/datum/powernet/PN = new()
|
var/datum/powernet/PN = new()
|
||||||
@@ -507,5 +550,41 @@ GLOBAL_LIST_INIT(cable_coil_recipes, list (new/datum/stack_recipe("cable restrai
|
|||||||
pixel_y = rand(-2,2)
|
pixel_y = rand(-2,2)
|
||||||
update_icon()
|
update_icon()
|
||||||
|
|
||||||
|
/obj/item/stack/cable_coil/cyborg
|
||||||
|
is_cyborg = 1
|
||||||
|
materials = list()
|
||||||
|
cost = 1
|
||||||
|
|
||||||
|
/obj/item/stack/cable_coil/cyborg/attack_self(mob/user)
|
||||||
|
var/picked = input(user,"Pick a cable color.","Cable Color") in list("red","yellow","green","blue","pink","orange","cyan","white")
|
||||||
|
cable_color = picked
|
||||||
|
update_icon()
|
||||||
|
|
||||||
|
/obj/structure/cable_bridge
|
||||||
|
name = "cable bridge"
|
||||||
|
desc = "A bridge to connect different cable layers, or link terminals to incompatible cable layers"
|
||||||
|
icon = 'icons/obj/power.dmi'
|
||||||
|
icon_state = "cable_bridge"
|
||||||
|
level = 1 //is underfloor
|
||||||
|
layer = WIRE_LAYER + 0.02 //Above all the cables but below terminals
|
||||||
|
anchored = TRUE
|
||||||
|
obj_flags = CAN_BE_HIT | ON_BLUEPRINTS
|
||||||
|
|
||||||
|
/obj/structure/cable_bridge/Initialize()
|
||||||
|
. = ..()
|
||||||
|
var/first = TRUE
|
||||||
|
var/datum/powernet/PN
|
||||||
|
for(var/obj/structure/cable/C in get_turf(src))
|
||||||
|
C.update_icon()
|
||||||
|
if(first == TRUE)
|
||||||
|
first = FALSE
|
||||||
|
PN = C.powernet
|
||||||
|
continue
|
||||||
|
propagate_network(C, PN)
|
||||||
|
|
||||||
|
/obj/structure/cable_bridge/wirecutter_act(mob/living/user, obj/item/I)
|
||||||
|
. = ..()
|
||||||
|
qdel(src)
|
||||||
|
|
||||||
#undef UNDER_SMES
|
#undef UNDER_SMES
|
||||||
#undef UNDER_TERMINAL
|
#undef UNDER_TERMINAL
|
||||||
|
|||||||
@@ -356,7 +356,9 @@
|
|||||||
/turf/proc/get_cable_node()
|
/turf/proc/get_cable_node()
|
||||||
if(!can_have_cabling())
|
if(!can_have_cabling())
|
||||||
return null
|
return null
|
||||||
|
var/obj/structure/cable_bridge/B = locate() in src
|
||||||
for(var/obj/structure/cable/C in src)
|
for(var/obj/structure/cable/C in src)
|
||||||
|
if(C.cable_layer == CABLE_LAYER_2 || B)
|
||||||
C.update_icon()
|
C.update_icon()
|
||||||
return C
|
return C
|
||||||
return null
|
return null
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 16 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 33 KiB |
BIN
icons/obj/power_cond/layer_cable.dmi
Normal file
BIN
icons/obj/power_cond/layer_cable.dmi
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
@@ -78,6 +78,7 @@
|
|||||||
#include "code\__DEFINES\pinpointers.dm"
|
#include "code\__DEFINES\pinpointers.dm"
|
||||||
#include "code\__DEFINES\pipe_construction.dm"
|
#include "code\__DEFINES\pipe_construction.dm"
|
||||||
#include "code\__DEFINES\plumbing.dm"
|
#include "code\__DEFINES\plumbing.dm"
|
||||||
|
#include "code\__DEFINES\power.dm"
|
||||||
#include "code\__DEFINES\preferences.dm"
|
#include "code\__DEFINES\preferences.dm"
|
||||||
#include "code\__DEFINES\procpath.dm"
|
#include "code\__DEFINES\procpath.dm"
|
||||||
#include "code\__DEFINES\profile.dm"
|
#include "code\__DEFINES\profile.dm"
|
||||||
|
|||||||
Reference in New Issue
Block a user