@@ -24,7 +24,7 @@
|
||||
/datum/notificationpanel/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.observer_state)
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "notificationpanel", "Notification Preferences", 270, 360, master_ui, state)
|
||||
ui = new(user, src, ui_key, "NotificationPreferences", "Notification Preferences", 270, 360, master_ui, state)
|
||||
ui.open()
|
||||
|
||||
/datum/notificationpanel/ui_data(mob/user)
|
||||
|
||||
@@ -54,6 +54,7 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
|
||||
// Used for displaying in ghost chat, without changing the actual name
|
||||
// of the mob
|
||||
var/deadchat_name
|
||||
var/datum/orbit_menu/orbit_menu
|
||||
var/datum/spawners_menu/spawners_menu
|
||||
|
||||
/mob/dead/observer/Initialize(mapload, mob/body)
|
||||
@@ -161,6 +162,7 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
|
||||
|
||||
updateallghostimages()
|
||||
|
||||
QDEL_NULL(orbit_menu)
|
||||
QDEL_NULL(spawners_menu)
|
||||
return ..()
|
||||
|
||||
@@ -490,10 +492,10 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
|
||||
set name = "Orbit" // "Haunt"
|
||||
set desc = "Follow and orbit a mob."
|
||||
|
||||
var/list/mobs = getpois(skip_mindless=1)
|
||||
var/input = input("Please, select a mob!", "Haunt", null, null) as null|anything in mobs
|
||||
var/mob/target = mobs[input]
|
||||
ManualFollow(target)
|
||||
if(!orbit_menu)
|
||||
orbit_menu = new(src)
|
||||
|
||||
orbit_menu.ui_interact(src)
|
||||
|
||||
// This is the ghost's follow verb with an argument
|
||||
/mob/dead/observer/proc/ManualFollow(atom/movable/target)
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/datum/orbit_menu
|
||||
var/mob/dead/observer/owner
|
||||
|
||||
/datum/orbit_menu/New(mob/dead/observer/new_owner)
|
||||
if(!istype(new_owner))
|
||||
qdel(src)
|
||||
owner = new_owner
|
||||
|
||||
/datum/orbit_menu/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.observer_state)
|
||||
if (!ui)
|
||||
ui = new(user, src, ui_key, "Orbit", "Orbit", 350, 700, master_ui, state)
|
||||
ui.open()
|
||||
|
||||
/datum/orbit_menu/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
||||
if (..())
|
||||
return
|
||||
|
||||
if (action == "orbit")
|
||||
var/list/pois = getpois(skip_mindless = 1)
|
||||
var/atom/movable/poi = pois[params["name"]]
|
||||
if (poi != null)
|
||||
owner.ManualFollow(poi)
|
||||
ui.close()
|
||||
|
||||
/datum/orbit_menu/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
|
||||
var/list/alive = list()
|
||||
var/list/antagonists = list()
|
||||
var/list/dead = list()
|
||||
var/list/ghosts = list()
|
||||
var/list/misc = list()
|
||||
var/list/npcs = list()
|
||||
|
||||
var/list/pois = getpois(skip_mindless = 1)
|
||||
for (var/name in pois)
|
||||
var/list/serialized = list()
|
||||
serialized["name"] = name
|
||||
|
||||
var/poi = pois[name]
|
||||
|
||||
var/mob/M = poi
|
||||
if (istype(M))
|
||||
if (isobserver(M))
|
||||
ghosts += list(serialized)
|
||||
else if (M.stat == DEAD)
|
||||
dead += list(serialized)
|
||||
else if (M.mind == null)
|
||||
npcs += list(serialized)
|
||||
else
|
||||
var/number_of_orbiters = M.orbiters?.orbiters?.len
|
||||
if (number_of_orbiters)
|
||||
serialized["orbiters"] = number_of_orbiters
|
||||
|
||||
var/datum/mind/mind = M.mind
|
||||
var/was_antagonist = FALSE
|
||||
|
||||
for (var/_A in mind.antag_datums)
|
||||
var/datum/antagonist/A = _A
|
||||
if (A.show_to_ghosts)
|
||||
was_antagonist = TRUE
|
||||
serialized["antag"] = A.name
|
||||
antagonists += list(serialized)
|
||||
break
|
||||
|
||||
if (!was_antagonist)
|
||||
alive += list(serialized)
|
||||
else
|
||||
misc += list(serialized)
|
||||
|
||||
data["alive"] = alive
|
||||
data["antagonists"] = antagonists
|
||||
data["dead"] = dead
|
||||
data["ghosts"] = ghosts
|
||||
data["misc"] = misc
|
||||
data["npcs"] = npcs
|
||||
|
||||
return data
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
status_flags = CANUNCONSCIOUS|CANPUSH
|
||||
|
||||
var/heat_protection = 0.5
|
||||
heat_protection = 0.5
|
||||
var/leaping = 0
|
||||
gib_type = /obj/effect/decal/cleanable/blood/gibs/xeno
|
||||
unique_name = 1
|
||||
|
||||
@@ -64,3 +64,11 @@
|
||||
|
||||
var/drunkenness = 0 //Overall drunkenness - check handle_alcohol() in life.dm for effects
|
||||
var/tackling = FALSE //Whether or not we are tackling, this will prevent the knock into effects for carbons
|
||||
|
||||
/// Protection (insulation) from the heat, Value 0-1 corresponding to the percentage of protection
|
||||
var/heat_protection = 0 // No heat protection
|
||||
/// Protection (insulation) from the cold, Value 0-1 corresponding to the percentage of protection
|
||||
var/cold_protection = 0 // No cold protection
|
||||
|
||||
/// Timer id of any transformation
|
||||
var/transformation_timer
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
enable_intentional_sprint_mode()
|
||||
|
||||
RegisterSignal(src, COMSIG_COMPONENT_CLEAN_ACT, /atom.proc/clean_blood)
|
||||
GLOB.human_list += src
|
||||
|
||||
|
||||
/mob/living/carbon/human/ComponentInitialize()
|
||||
@@ -47,6 +48,7 @@
|
||||
/mob/living/carbon/human/Destroy()
|
||||
QDEL_NULL(physiology)
|
||||
QDEL_NULL_LIST(vore_organs) // CITADEL EDIT belly stuff
|
||||
GLOB.human_list -= src
|
||||
return ..()
|
||||
|
||||
/mob/living/carbon/human/prepare_data_huds()
|
||||
|
||||
@@ -382,7 +382,9 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
//keep it at the right spot, so we can't have people taking shortcuts
|
||||
var/location = C.dna.mutation_index.Find(inert_mutation)
|
||||
C.dna.mutation_index[location] = new_species.inert_mutation
|
||||
C.dna.default_mutation_genes[location] = C.dna.mutation_index[location]
|
||||
C.dna.mutation_index[new_species.inert_mutation] = create_sequence(new_species.inert_mutation)
|
||||
C.dna.default_mutation_genes[new_species.inert_mutation] = C.dna.mutation_index[new_species.inert_mutation]
|
||||
|
||||
if(!new_species.has_field_of_vision && has_field_of_vision && ishuman(C) && CONFIG_GET(flag/use_field_of_vision))
|
||||
var/datum/component/field_of_vision/F = C.GetComponent(/datum/component/field_of_vision)
|
||||
|
||||
@@ -314,7 +314,7 @@
|
||||
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "slime_swap_body", name, 400, 400, master_ui, state)
|
||||
ui = new(user, src, ui_key, "SlimeBodySwapper", name, 400, 400, master_ui, state)
|
||||
ui.open()
|
||||
|
||||
/datum/action/innate/swap_body/ui_data(mob/user)
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/datum/robot_control
|
||||
var/mob/living/silicon/ai/owner
|
||||
|
||||
/datum/robot_control/New(mob/living/silicon/ai/new_owner)
|
||||
if(!istype(new_owner))
|
||||
qdel(src)
|
||||
owner = new_owner
|
||||
|
||||
/datum/robot_control/proc/is_interactable(mob/user)
|
||||
if(user != owner || owner.incapacitated())
|
||||
return FALSE
|
||||
if(owner.control_disabled)
|
||||
to_chat(user, "<span class='warning'>Wireless control is disabled.</span>")
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/robot_control/ui_status(mob/user)
|
||||
if(is_interactable(user))
|
||||
return ..()
|
||||
return UI_CLOSE
|
||||
|
||||
/datum/robot_control/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
|
||||
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.always_state)
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "RemoteRobotControl", "Remote Robot Control", 500, 500, master_ui, state)
|
||||
ui.open()
|
||||
|
||||
/datum/robot_control/ui_data(mob/user)
|
||||
if(!owner || user != owner)
|
||||
return
|
||||
var/list/data = list()
|
||||
var/turf/ai_current_turf = get_turf(owner)
|
||||
var/ai_zlevel = ai_current_turf.z
|
||||
|
||||
data["robots"] = list()
|
||||
for(var/mob/living/simple_animal/bot/B in GLOB.bots_list)
|
||||
if(B.z != ai_zlevel || B.remote_disabled) //Only non-emagged bots on the same Z-level are detected!
|
||||
continue
|
||||
var/list/robot_data = list(
|
||||
name = B.name,
|
||||
model = B.model,
|
||||
mode = B.get_mode(),
|
||||
hacked = B.hacked,
|
||||
location = get_area_name(B, TRUE),
|
||||
ref = REF(B)
|
||||
)
|
||||
data["robots"] += list(robot_data)
|
||||
|
||||
return data
|
||||
|
||||
/datum/robot_control/ui_act(action, params)
|
||||
if(..())
|
||||
return
|
||||
if(!is_interactable(usr))
|
||||
return
|
||||
|
||||
switch(action)
|
||||
if("callbot") //Command a bot to move to a selected location.
|
||||
if(owner.call_bot_cooldown > world.time)
|
||||
to_chat(usr, "<span class='danger'>Error: Your last call bot command is still processing, please wait for the bot to finish calculating a route.</span>")
|
||||
return
|
||||
owner.Bot = locate(params["ref"]) in GLOB.bots_list
|
||||
if(!owner.Bot || owner.Bot.remote_disabled || owner.control_disabled)
|
||||
return
|
||||
owner.waypoint_mode = TRUE
|
||||
to_chat(usr, "<span class='notice'>Set your waypoint by clicking on a valid location free of obstructions.</span>")
|
||||
. = TRUE
|
||||
if("interface") //Remotely connect to a bot!
|
||||
owner.Bot = locate(params["ref"]) in GLOB.bots_list
|
||||
if(!owner.Bot || owner.Bot.remote_disabled || owner.control_disabled)
|
||||
return
|
||||
owner.Bot.attack_ai(usr)
|
||||
. = TRUE
|
||||
@@ -115,6 +115,19 @@
|
||||
else
|
||||
return "<span class='average'>[mode_name[mode]]</span>"
|
||||
|
||||
/**
|
||||
* Returns a status string about the bot's current status, if it's moving, manually controlled, or idle.
|
||||
*/
|
||||
/mob/living/simple_animal/bot/proc/get_mode_ui()
|
||||
if(client) //Player bots do not have modes, thus the override. Also an easy way for PDA users/AI to know when a bot is a player.
|
||||
return paicard ? "pAI Controlled" : "Autonomous"
|
||||
else if(!on)
|
||||
return "Inactive"
|
||||
else if(!mode)
|
||||
return "Idle"
|
||||
else
|
||||
return "[mode_name[mode]]"
|
||||
|
||||
/mob/living/simple_animal/bot/proc/turn_on()
|
||||
if(stat)
|
||||
return FALSE
|
||||
|
||||
@@ -174,7 +174,7 @@
|
||||
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "mulebot", name, ui_x, ui_y, master_ui, state)
|
||||
ui = new(user, src, ui_key, "Mule", name, ui_x, ui_y, master_ui, state)
|
||||
ui.open()
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/ui_data(mob/user)
|
||||
|
||||
@@ -558,3 +558,17 @@ It's fairly easy to fix if dealing with single letters but not so much with comp
|
||||
//Can the mob see reagents inside of containers?
|
||||
/mob/proc/can_see_reagents()
|
||||
return stat == DEAD || silicon_privileges //Dead guys and silicons can always see reagents
|
||||
|
||||
/mob/proc/is_blind()
|
||||
SHOULD_BE_PURE(TRUE)
|
||||
return eye_blind ? TRUE : HAS_TRAIT(src, TRAIT_BLIND)
|
||||
|
||||
/mob/proc/can_read(obj/O)
|
||||
if(is_blind())
|
||||
to_chat(src, "<span class='warning'>As you are trying to read [O], you suddenly feel very stupid!</span>")
|
||||
return
|
||||
if(!is_literate())
|
||||
to_chat(src, "<span class='notice'>You try to read [O], but can't comprehend any of it.</span>")
|
||||
return
|
||||
return TRUE
|
||||
|
||||
|
||||
@@ -1,26 +1,8 @@
|
||||
/mob/living/carbon/proc/monkeyize(tr_flags = (TR_KEEPITEMS | TR_KEEPVIRUS | TR_DEFAULTMSG))
|
||||
if (mob_transforming)
|
||||
#define TRANSFORMATION_DURATION 22
|
||||
|
||||
/mob/living/carbon/proc/monkeyize(tr_flags = (TR_KEEPITEMS | TR_KEEPVIRUS | TR_KEEPSTUNS | TR_KEEPREAGENTS | TR_DEFAULTMSG))
|
||||
if(mob_transforming || transformation_timer)
|
||||
return
|
||||
//Handle items on mob
|
||||
|
||||
//first implants & organs
|
||||
var/list/stored_implants = list()
|
||||
var/list/int_organs = list()
|
||||
|
||||
if (tr_flags & TR_KEEPIMPLANTS)
|
||||
for(var/X in implants)
|
||||
var/obj/item/implant/IMP = X
|
||||
stored_implants += IMP
|
||||
IMP.removed(src, 1, 1)
|
||||
|
||||
var/list/missing_bodyparts_zones = get_missing_limbs()
|
||||
|
||||
var/obj/item/cavity_object
|
||||
|
||||
var/obj/item/bodypart/chest/CH = get_bodypart(BODY_ZONE_CHEST)
|
||||
if(CH.cavity_item)
|
||||
cavity_object = CH.cavity_item
|
||||
CH.cavity_item = null
|
||||
|
||||
if(tr_flags & TR_KEEPITEMS)
|
||||
var/Itemlist = get_equipped_items(TRUE)
|
||||
@@ -30,13 +12,36 @@
|
||||
|
||||
//Make mob invisible and spawn animation
|
||||
mob_transforming = TRUE
|
||||
Stun(INFINITY, ignore_canstun = TRUE)
|
||||
Paralyze(TRANSFORMATION_DURATION, ignore_canstun = TRUE)
|
||||
icon = null
|
||||
cut_overlays()
|
||||
invisibility = INVISIBILITY_MAXIMUM
|
||||
|
||||
new /obj/effect/temp_visual/monkeyify(loc)
|
||||
sleep(22)
|
||||
|
||||
transformation_timer = addtimer(CALLBACK(src, .proc/finish_monkeyize, tr_flags), TRANSFORMATION_DURATION, TIMER_UNIQUE)
|
||||
|
||||
/mob/living/carbon/proc/finish_monkeyize(tr_flags)
|
||||
transformation_timer = null
|
||||
|
||||
var/list/missing_bodyparts_zones = get_missing_limbs()
|
||||
|
||||
var/list/stored_implants = list()
|
||||
|
||||
if (tr_flags & TR_KEEPIMPLANTS)
|
||||
for(var/X in implants)
|
||||
var/obj/item/implant/IMP = X
|
||||
stored_implants += IMP
|
||||
IMP.removed(src, 1, 1)
|
||||
|
||||
var/list/int_organs = list()
|
||||
var/obj/item/cavity_object
|
||||
|
||||
var/obj/item/bodypart/chest/CH = get_bodypart(BODY_ZONE_CHEST)
|
||||
if(CH.cavity_item)
|
||||
cavity_object = CH.cavity_item
|
||||
CH.cavity_item = null
|
||||
|
||||
var/mob/living/carbon/monkey/O = new /mob/living/carbon/monkey( loc )
|
||||
|
||||
// hash the original name?
|
||||
@@ -50,6 +55,7 @@
|
||||
|
||||
if(tr_flags & TR_KEEPSE)
|
||||
O.dna.mutation_index = dna.mutation_index
|
||||
O.dna.default_mutation_genes = dna.default_mutation_genes
|
||||
O.dna.set_se(1, GET_INITIALIZED_MUTATION(RACEMUT))
|
||||
|
||||
if(suiciding)
|
||||
@@ -149,12 +155,33 @@
|
||||
////////////////////////// Humanize //////////////////////////////
|
||||
//Could probably be merged with monkeyize but other transformations got their own procs, too
|
||||
|
||||
/mob/living/carbon/proc/humanize(tr_flags = (TR_KEEPITEMS | TR_KEEPVIRUS | TR_DEFAULTMSG))
|
||||
if (mob_transforming)
|
||||
/mob/living/carbon/proc/humanize(tr_flags = (TR_KEEPITEMS | TR_KEEPVIRUS | TR_KEEPSTUNS | TR_KEEPREAGENTS | TR_DEFAULTMSG))
|
||||
if (mob_transforming || transformation_timer)
|
||||
return
|
||||
//Handle items on mob
|
||||
|
||||
//first implants & organs
|
||||
//now the rest
|
||||
if (tr_flags & TR_KEEPITEMS)
|
||||
var/Itemlist = get_equipped_items(TRUE)
|
||||
Itemlist += held_items
|
||||
for(var/obj/item/W in Itemlist)
|
||||
dropItemToGround(W, TRUE)
|
||||
if (client)
|
||||
client.screen -= W
|
||||
|
||||
//Make mob invisible and spawn animation
|
||||
mob_transforming = TRUE
|
||||
Paralyze(TRANSFORMATION_DURATION, ignore_canstun = TRUE)
|
||||
|
||||
icon = null
|
||||
cut_overlays()
|
||||
invisibility = INVISIBILITY_MAXIMUM
|
||||
new /obj/effect/temp_visual/monkeyify/humanify(loc)
|
||||
|
||||
transformation_timer = addtimer(CALLBACK(src, .proc/finish_humanize, tr_flags), TRANSFORMATION_DURATION, TIMER_UNIQUE)
|
||||
|
||||
/mob/living/carbon/proc/finish_humanize(tr_flags)
|
||||
transformation_timer = null
|
||||
|
||||
var/list/stored_implants = list()
|
||||
var/list/int_organs = list()
|
||||
|
||||
@@ -173,25 +200,6 @@
|
||||
cavity_object = CH.cavity_item
|
||||
CH.cavity_item = null
|
||||
|
||||
//now the rest
|
||||
if (tr_flags & TR_KEEPITEMS)
|
||||
var/Itemlist = get_equipped_items(TRUE)
|
||||
Itemlist += held_items
|
||||
for(var/obj/item/W in Itemlist)
|
||||
dropItemToGround(W, TRUE)
|
||||
if (client)
|
||||
client.screen -= W
|
||||
|
||||
|
||||
|
||||
//Make mob invisible and spawn animation
|
||||
mob_transforming = TRUE
|
||||
Stun(22, ignore_canstun = TRUE)
|
||||
icon = null
|
||||
cut_overlays()
|
||||
invisibility = INVISIBILITY_MAXIMUM
|
||||
new /obj/effect/temp_visual/monkeyify/humanify(loc)
|
||||
sleep(22)
|
||||
var/mob/living/carbon/human/O = new( loc )
|
||||
for(var/obj/item/C in O.loc)
|
||||
O.equip_to_appropriate_slot(C)
|
||||
@@ -208,6 +216,7 @@
|
||||
|
||||
if(tr_flags & TR_KEEPSE)
|
||||
O.dna.mutation_index = dna.mutation_index
|
||||
O.dna.default_mutation_genes = dna.default_mutation_genes
|
||||
O.dna.set_se(0, GET_INITIALIZED_MUTATION(RACEMUT))
|
||||
O.domutcheck()
|
||||
|
||||
@@ -353,7 +362,7 @@
|
||||
qdel(src)
|
||||
|
||||
/mob/living/carbon/human/proc/Robotize(delete_items = 0, transfer_after = TRUE)
|
||||
if (mob_transforming)
|
||||
if(mob_transforming)
|
||||
return
|
||||
for(var/obj/item/W in src)
|
||||
if(delete_items)
|
||||
@@ -582,6 +591,48 @@
|
||||
. = new_mob
|
||||
qdel(src)
|
||||
|
||||
|
||||
/* Certain mob types have problems and should not be allowed to be controlled by players.
|
||||
*
|
||||
* This proc is here to force coders to manually place their mob in this list, hopefully tested.
|
||||
* This also gives a place to explain -why- players shouldnt be turn into certain mobs and hopefully someone can fix them.
|
||||
*/
|
||||
/mob/proc/safe_animal(MP)
|
||||
|
||||
//Bad mobs! - Remember to add a comment explaining what's wrong with the mob
|
||||
if(!MP)
|
||||
return 0 //Sanity, this should never happen.
|
||||
|
||||
if(ispath(MP, /mob/living/simple_animal/hostile/construct))
|
||||
return 0 //Verbs do not appear for players.
|
||||
|
||||
//Good mobs!
|
||||
if(ispath(MP, /mob/living/simple_animal/pet/cat))
|
||||
return 1
|
||||
if(ispath(MP, /mob/living/simple_animal/pet/dog/corgi))
|
||||
return 1
|
||||
if(ispath(MP, /mob/living/simple_animal/crab))
|
||||
return 1
|
||||
if(ispath(MP, /mob/living/simple_animal/hostile/carp))
|
||||
return 1
|
||||
if(ispath(MP, /mob/living/simple_animal/hostile/mushroom))
|
||||
return 1
|
||||
if(ispath(MP, /mob/living/simple_animal/shade))
|
||||
return 1
|
||||
if(ispath(MP, /mob/living/simple_animal/hostile/killertomato))
|
||||
return 1
|
||||
if(ispath(MP, /mob/living/simple_animal/mouse))
|
||||
return 1 //It is impossible to pull up the player panel for mice (Fixed! - Nodrak)
|
||||
if(ispath(MP, /mob/living/simple_animal/hostile/bear))
|
||||
return 1 //Bears will auto-attack mobs, even if they're player controlled (Fixed! - Nodrak)
|
||||
if(ispath(MP, /mob/living/simple_animal/parrot))
|
||||
return 1 //Parrots are no longer unfinished! -Nodrak
|
||||
|
||||
//Not in here? Must be untested!
|
||||
return 0
|
||||
|
||||
#undef TRANSFORMATION_DURATION
|
||||
|
||||
/mob/living/proc/turn_into_pickle()
|
||||
//if they're already a pickle, turn them back instead
|
||||
if(istype(src, /mob/living/simple_animal/pickle))
|
||||
|
||||
Reference in New Issue
Block a user