other things I missed. oops
This commit is contained in:
@@ -97,7 +97,7 @@
|
||||
|
||||
wizmind.AddSpell(new /obj/effect/proc_holder/spell/targeted/ethereal_jaunt)
|
||||
wizmind.AddSpell(new /obj/effect/proc_holder/spell/targeted/projectile/magic_missile)
|
||||
wizmind.AddSpell(new /obj/effect/proc_holder/spell/fireball)
|
||||
wizmind.AddSpell(new /obj/effect/proc_holder/spell/aimed/fireball)
|
||||
|
||||
current_wizard = wizbody
|
||||
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
..()
|
||||
|
||||
/obj/item/clothing/suit/hooded/Destroy()
|
||||
. = ..()
|
||||
qdel(hood)
|
||||
hood = null
|
||||
return ..()
|
||||
|
||||
/obj/item/clothing/suit/hooded/proc/MakeHood()
|
||||
if(!hood)
|
||||
|
||||
@@ -75,35 +75,29 @@
|
||||
item_state = "y_suit"
|
||||
item_color = "yellow"
|
||||
|
||||
/obj/item/clothing/under/color/lightblue
|
||||
name = "lightblue jumpsuit"
|
||||
icon_state = "lightblue"
|
||||
/obj/item/clothing/under/color/darkblue
|
||||
name = "darkblue jumpsuit"
|
||||
icon_state = "darkblue"
|
||||
item_state = "b_suit"
|
||||
item_color = "lightblue"
|
||||
item_color = "darkblue"
|
||||
|
||||
/obj/item/clothing/under/color/aqua
|
||||
name = "aqua jumpsuit"
|
||||
icon_state = "aqua"
|
||||
/obj/item/clothing/under/color/teal
|
||||
name = "teal jumpsuit"
|
||||
icon_state = "teal"
|
||||
item_state = "b_suit"
|
||||
item_color = "aqua"
|
||||
|
||||
/obj/item/clothing/under/color/purple
|
||||
name = "purple jumpsuit"
|
||||
icon_state = "purple"
|
||||
item_state = "p_suit"
|
||||
item_color = "purple"
|
||||
item_color = "teal"
|
||||
|
||||
/obj/item/clothing/under/color/lightpurple
|
||||
name = "lightpurple jumpsuit"
|
||||
name = "purple jumpsuit"
|
||||
icon_state = "lightpurple"
|
||||
item_state = "p_suit"
|
||||
item_color = "lightpurple"
|
||||
|
||||
/obj/item/clothing/under/color/lightgreen
|
||||
name = "lightgreen jumpsuit"
|
||||
icon_state = "lightgreen"
|
||||
/obj/item/clothing/under/color/darkgreen
|
||||
name = "darkgreen jumpsuit"
|
||||
icon_state = "darkgreen"
|
||||
item_state = "g_suit"
|
||||
item_color = "lightgreen"
|
||||
item_color = "darkgreen"
|
||||
|
||||
/obj/item/clothing/under/color/lightbrown
|
||||
name = "lightbrown jumpsuit"
|
||||
@@ -117,30 +111,6 @@
|
||||
item_state = "lb_suit"
|
||||
item_color = "brown"
|
||||
|
||||
/obj/item/clothing/under/color/yellowgreen
|
||||
name = "yellowgreen jumpsuit"
|
||||
icon_state = "yellowgreen"
|
||||
item_state = "y_suit"
|
||||
item_color = "yellowgreen"
|
||||
|
||||
/obj/item/clothing/under/color/darkblue
|
||||
name = "darkblue jumpsuit"
|
||||
icon_state = "darkblue"
|
||||
item_state = "b_suit"
|
||||
item_color = "darkblue"
|
||||
|
||||
/obj/item/clothing/under/color/lightred
|
||||
name = "lightred jumpsuit"
|
||||
icon_state = "lightred"
|
||||
item_state = "r_suit"
|
||||
item_color = "lightred"
|
||||
|
||||
/obj/item/clothing/under/color/darkred
|
||||
name = "darkred jumpsuit"
|
||||
icon_state = "darkred"
|
||||
item_state = "r_suit"
|
||||
item_color = "darkred"
|
||||
|
||||
/obj/item/clothing/under/color/maroon
|
||||
name = "maroon jumpsuit"
|
||||
icon_state = "maroon"
|
||||
|
||||
@@ -36,6 +36,9 @@
|
||||
|
||||
forge_valentines_objective(date, L)
|
||||
|
||||
if(valentines.len && prob(4))
|
||||
var/mob/living/notgoodenough = pick_n_take(valentines)
|
||||
forge_valentines_objective(notgoodenough, date)
|
||||
|
||||
|
||||
else
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
/datum/round_event_control/ion_storm
|
||||
name = "Ion Storm"
|
||||
typepath = /datum/round_event/ion_storm
|
||||
weight = 3
|
||||
weight = 15
|
||||
min_players = 2
|
||||
|
||||
/datum/round_event/ion_storm
|
||||
@@ -545,7 +545,7 @@
|
||||
message = "ALL [ionthreats] ARE NOW NAMED [ionspecies]."
|
||||
if(4)
|
||||
message = "ALL [ionthreats] ARE NOW NAMED [ionobjects]."
|
||||
|
||||
|
||||
return message
|
||||
|
||||
#undef ION_RANDOM
|
||||
|
||||
@@ -37,9 +37,12 @@
|
||||
projectilesound = 'sound/weapons/emitter.ogg'
|
||||
maxHealth = 50
|
||||
health = 50
|
||||
|
||||
var/allowed_projectile_types = list(/obj/item/projectile/magic/change, /obj/item/projectile/magic/animate, /obj/item/projectile/magic/resurrection,
|
||||
/obj/item/projectile/magic/death, /obj/item/projectile/magic/teleport, /obj/item/projectile/magic/door, /obj/item/projectile/magic/aoe/fireball,
|
||||
/obj/item/projectile/magic/spellblade, /obj/item/projectile/magic/arcane_barrage)
|
||||
|
||||
/mob/living/simple_animal/hostile/carp/ranged/New()
|
||||
projectiletype = pick(typesof(initial(projectiletype)))
|
||||
projectiletype = pick(allowed_projectile_types)
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/carp/ranged/chaos
|
||||
@@ -50,5 +53,5 @@
|
||||
health = 75
|
||||
|
||||
/mob/living/simple_animal/hostile/carp/ranged/chaos/Shoot()
|
||||
projectiletype = pick(typesof(initial(projectiletype)))
|
||||
..()
|
||||
projectiletype = pick(allowed_projectile_types)
|
||||
..()
|
||||
|
||||
@@ -58,7 +58,7 @@ Medical Doctor
|
||||
|
||||
outfit = /datum/outfit/job/doctor
|
||||
|
||||
access = list(access_medical, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics, access_cloning, access_mineral_storeroom)
|
||||
access = list(access_medical, access_morgue, access_surgery, access_chemistry, access_genetics, access_cloning, access_mineral_storeroom)
|
||||
minimal_access = list(access_medical, access_morgue, access_surgery, access_cloning)
|
||||
|
||||
/datum/outfit/job/doctor
|
||||
@@ -93,7 +93,7 @@ Chemist
|
||||
|
||||
outfit = /datum/outfit/job/chemist
|
||||
|
||||
access = list(access_medical, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics, access_cloning, access_mineral_storeroom)
|
||||
access = list(access_medical, access_morgue, access_surgery, access_chemistry, access_genetics, access_cloning, access_mineral_storeroom)
|
||||
minimal_access = list(access_medical, access_chemistry, access_mineral_storeroom)
|
||||
|
||||
/datum/outfit/job/chemist
|
||||
@@ -127,7 +127,7 @@ Geneticist
|
||||
|
||||
outfit = /datum/outfit/job/geneticist
|
||||
|
||||
access = list(access_medical, access_morgue, access_chemistry, access_virology, access_genetics, access_cloning, access_research, access_xenobiology, access_robotics, access_mineral_storeroom, access_tech_storage)
|
||||
access = list(access_medical, access_morgue, access_chemistry, access_genetics, access_cloning, access_research, access_xenobiology, access_robotics, access_mineral_storeroom, access_tech_storage)
|
||||
minimal_access = list(access_medical, access_morgue, access_genetics, access_cloning, access_research)
|
||||
|
||||
/datum/outfit/job/geneticist
|
||||
|
||||
@@ -684,6 +684,41 @@
|
||||
animate(whole_screen, transform = matrix(), time = 5, easing = QUAD_EASING)
|
||||
..()
|
||||
|
||||
/datum/reagent/toxin/skewium
|
||||
name = "Skewium"
|
||||
id = "skewium"
|
||||
description = "A strange, dull coloured liquid that appears to warp back and forth inside its container. Causes any consumer to experience a visual phenomena similar to said warping."
|
||||
reagent_state = LIQUID
|
||||
color = "#ADBDCD"
|
||||
metabolization_rate = 0.8 * REAGENTS_METABOLISM
|
||||
toxpwr = 0.25
|
||||
|
||||
/datum/reagent/toxin/skewium/on_mob_life(mob/living/M)
|
||||
if(M.hud_used)
|
||||
if(current_cycle >= 5 && current_cycle % 3 == 0)
|
||||
var/list/screens = list(M.hud_used.plane_masters["[GAME_PLANE]"], M.hud_used.plane_masters["[LIGHTING_PLANE]"])
|
||||
var/matrix/skew = matrix()
|
||||
var/intensity = 8
|
||||
skew.set_skew(rand(-intensity,intensity), rand(-intensity,intensity))
|
||||
var/matrix/newmatrix = skew
|
||||
|
||||
if(prob(33)) // 1/3rd of the time, let's make it stack with the previous matrix! Mwhahahaha!
|
||||
var/obj/screen/plane_master/PM = M.hud_used.plane_masters["[GAME_PLANE]"]
|
||||
newmatrix = skew * PM.transform
|
||||
|
||||
for(var/whole_screen in screens)
|
||||
animate(whole_screen, transform = newmatrix, time = 5, easing = QUAD_EASING, loop = -1)
|
||||
animate(transform = -newmatrix, time = 5, easing = QUAD_EASING)
|
||||
return ..()
|
||||
|
||||
/datum/reagent/toxin/skewium/on_mob_delete(mob/living/M)
|
||||
if(M && M.hud_used)
|
||||
var/list/screens = list(M.hud_used.plane_masters["[GAME_PLANE]"], M.hud_used.plane_masters["[LIGHTING_PLANE]"])
|
||||
for(var/whole_screen in screens)
|
||||
animate(whole_screen, transform = matrix(), time = 5, easing = QUAD_EASING)
|
||||
..()
|
||||
|
||||
|
||||
/datum/reagent/toxin/anacea
|
||||
name = "Anacea"
|
||||
id = "anacea"
|
||||
|
||||
@@ -94,7 +94,14 @@
|
||||
results = list("rotatium" = 3)
|
||||
required_reagents = list("mindbreaker" = 1, "teslium" = 1, "neurotoxin2" = 1)
|
||||
mix_message = "<span class='danger'>After sparks, fire, and the smell of mindbreaker, the mix is constantly spinning with no stop in sight.</span>"
|
||||
|
||||
|
||||
/datum/chemical_reaction/skewium
|
||||
name = "Skewium"
|
||||
id = "Skewium"
|
||||
results = list("skewium" = 5)
|
||||
required_reagents = list("rotatium" = 2, "plasma" = 2, "sacid" = 1)
|
||||
mix_message = "<span class='danger'>Wow! it turns out if you mix rotatium with some plasma and sulphuric acid, it gets even worse!</span>"
|
||||
|
||||
/datum/chemical_reaction/anacea
|
||||
name = "Anacea"
|
||||
id = "anacea"
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
|
||||
/obj/effect/proc_holder/spell/aimed
|
||||
name = "aimed projectile spell"
|
||||
var/projectile_type = /obj/item/projectile/magic/teleport
|
||||
var/deactive_msg = "You discharge your projectile..."
|
||||
var/active_msg = "You charge your projectile!"
|
||||
var/base_icon_state = "projectile"
|
||||
var/active_icon_state = "projectile"
|
||||
|
||||
/obj/effect/proc_holder/spell/aimed/Click()
|
||||
var/mob/living/user = usr
|
||||
if(!istype(user))
|
||||
return
|
||||
var/msg
|
||||
if(!can_cast(user))
|
||||
msg = "<span class='warning'>You can no longer cast [name]!</span>"
|
||||
remove_ranged_ability(msg)
|
||||
return
|
||||
if(active)
|
||||
msg = "<span class='notice'>[deactive_msg]</span>"
|
||||
remove_ranged_ability(msg)
|
||||
else
|
||||
msg = "<span class='notice'>[active_msg]<B>Left-click to shoot it at a target!</B></span>"
|
||||
add_ranged_ability(user, msg, TRUE)
|
||||
|
||||
/obj/effect/proc_holder/spell/aimed/update_icon()
|
||||
if(!action)
|
||||
return
|
||||
action.button_icon_state = "[base_icon_state][active]"
|
||||
action.UpdateButtonIcon()
|
||||
|
||||
/obj/effect/proc_holder/spell/aimed/InterceptClickOn(mob/living/caller, params, atom/target)
|
||||
if(..())
|
||||
return FALSE
|
||||
if(!cast_check(0, ranged_ability_user))
|
||||
remove_ranged_ability()
|
||||
return FALSE
|
||||
var/list/targets = list(target)
|
||||
perform(targets,user = ranged_ability_user)
|
||||
return TRUE
|
||||
|
||||
/obj/effect/proc_holder/spell/aimed/cast(list/targets, mob/living/user)
|
||||
var/target = targets[1]
|
||||
var/turf/T = user.loc
|
||||
var/turf/U = get_step(user, user.dir) // Get the tile infront of the move, based on their direction
|
||||
if(!isturf(U) || !isturf(T))
|
||||
return FALSE
|
||||
fire_projectile(user, target)
|
||||
user.newtonian_move(get_dir(U, T))
|
||||
remove_ranged_ability() //Auto-disable the ability once successfully performed
|
||||
return TRUE
|
||||
|
||||
/obj/effect/proc_holder/spell/aimed/proc/fire_projectile(mob/living/user, atom/target)
|
||||
var/obj/item/projectile/P = new projectile_type(user.loc)
|
||||
P.current = get_turf(user)
|
||||
P.preparePixelProjectile(target, get_turf(target), user)
|
||||
P.fire()
|
||||
return TRUE
|
||||
|
||||
/obj/effect/proc_holder/spell/aimed/lightningbolt
|
||||
name = "Lightning Bolt"
|
||||
desc = "Fire a high powered lightning bolt at your foes!"
|
||||
school = "evocation"
|
||||
charge_max = 200
|
||||
clothes_req = 1
|
||||
invocation = "UN'LTD P'WAH"
|
||||
invocation_type = "shout"
|
||||
cooldown_min = 30
|
||||
active_icon_state = "lightning"
|
||||
base_icon_state = "lightning"
|
||||
sound = 'sound/magic/lightningbolt.ogg'
|
||||
active = FALSE
|
||||
var/tesla_range = 15
|
||||
var/tesla_power = 20000
|
||||
var/tesla_boom = FALSE
|
||||
active_msg = "You energize your hand with arcane lightning!"
|
||||
deactive_msg = "You let the energy flow out of your hands back into yourself..."
|
||||
|
||||
/obj/effect/proc_holder/spell/aimed/lightningbolt/fire_projectile(mob/living/user, atom/target)
|
||||
var/obj/item/projectile/magic/aoe/lightning/P = new /obj/item/projectile/magic/aoe/lightning(user.loc)
|
||||
P.current = get_turf(user)
|
||||
P.preparePixelProjectile(target, get_turf(target), user)
|
||||
P.tesla_power = tesla_power
|
||||
P.tesla_range = tesla_range
|
||||
P.tesla_boom = tesla_boom
|
||||
P.fire()
|
||||
return TRUE
|
||||
|
||||
/obj/effect/proc_holder/spell/aimed/fireball
|
||||
name = "Fireball"
|
||||
desc = "This spell fires a fireball at a target and does not require wizard garb."
|
||||
school = "evocation"
|
||||
charge_max = 60
|
||||
clothes_req = 0
|
||||
invocation = "ONI SOMA"
|
||||
invocation_type = "shout"
|
||||
range = 20
|
||||
cooldown_min = 20 //10 deciseconds reduction per rank
|
||||
projectile_type = /obj/item/projectile/magic/aoe/fireball
|
||||
base_icon_state = "fireball"
|
||||
action_icon_state = "fireball0"
|
||||
sound = "sound/magic/Fireball.ogg"
|
||||
active_msg = "You prepare to cast your fireball spell!"
|
||||
deactive_msg = "You extinguish your fireball... for now."
|
||||
active = FALSE
|
||||
@@ -69,7 +69,7 @@
|
||||
user << "<span class='notice'>[C] seems to not be sentient. You cannot summon a contract for [C.p_them()].</span>"
|
||||
|
||||
|
||||
/obj/effect/proc_holder/spell/fireball/hellish
|
||||
/obj/effect/proc_holder/spell/aimed/fireball/hellish
|
||||
name = "Hellfire"
|
||||
desc = "This spell launches hellfire at the target."
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
invocation_type = "shout"
|
||||
range = 2
|
||||
|
||||
fireball_type = /obj/item/projectile/magic/fireball/infernal
|
||||
projectile_type = /obj/item/projectile/magic/aoe/fireball/infernal
|
||||
|
||||
action_background_icon_state = "bg_demon"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/obj/effect/proc_holder/spell/targeted/lightning
|
||||
name = "Lightning Bolt"
|
||||
desc = "Charges up and throws a lightning bolt at nearby enemies. Classic."
|
||||
/obj/effect/proc_holder/spell/targeted/tesla
|
||||
name = "Tesla Blast"
|
||||
desc = "Blast lightning at your foes!"
|
||||
charge_type = "recharge"
|
||||
charge_max = 300
|
||||
clothes_req = 1
|
||||
@@ -16,12 +16,12 @@
|
||||
|
||||
action_icon_state = "lightning"
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/lightning/Click()
|
||||
/obj/effect/proc_holder/spell/targeted/tesla/Click()
|
||||
if(!ready && cast_check())
|
||||
StartChargeup()
|
||||
return 1
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/lightning/proc/StartChargeup(mob/user = usr)
|
||||
/obj/effect/proc_holder/spell/targeted/tesla/proc/StartChargeup(mob/user = usr)
|
||||
ready = 1
|
||||
user << "<span class='notice'>You start gathering the power.</span>"
|
||||
Snd = new/sound('sound/magic/lightning_chargeup.ogg',channel = 7)
|
||||
@@ -36,18 +36,18 @@
|
||||
else
|
||||
revert_cast(user, 0)
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/lightning/proc/Reset(mob/user = usr)
|
||||
/obj/effect/proc_holder/spell/targeted/tesla/proc/Reset(mob/user = usr)
|
||||
ready = 0
|
||||
if(halo)
|
||||
user.overlays.Remove(halo)
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/lightning/revert_cast(mob/user = usr, message = 1)
|
||||
/obj/effect/proc_holder/spell/targeted/tesla/revert_cast(mob/user = usr, message = 1)
|
||||
if(message)
|
||||
user << "<span class='notice'>No target found in range.</span>"
|
||||
Reset(user)
|
||||
..()
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/lightning/cast(list/targets, mob/user = usr)
|
||||
/obj/effect/proc_holder/spell/targeted/tesla/cast(list/targets, mob/user = usr)
|
||||
ready = 0
|
||||
var/mob/living/carbon/target = targets[1]
|
||||
Snd=sound(null, repeat = 0, wait = 1, channel = Snd.channel) //byond, why you suck?
|
||||
@@ -63,7 +63,7 @@
|
||||
Bolt(user,target,30,5,user)
|
||||
Reset(user)
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/lightning/proc/Bolt(mob/origin,mob/target,bolt_energy,bounces,mob/user = usr)
|
||||
/obj/effect/proc_holder/spell/targeted/tesla/proc/Bolt(mob/origin,mob/target,bolt_energy,bounces,mob/user = usr)
|
||||
origin.Beam(target,icon_state="lightning[rand(1,12)]",time=5)
|
||||
var/mob/living/carbon/current = target
|
||||
if(bounces < 1)
|
||||
@@ -81,4 +81,4 @@
|
||||
return
|
||||
var/mob/living/next = pick(possible_targets)
|
||||
if(next)
|
||||
Bolt(current,next,max((bolt_energy-5),5),bounces-1,user)
|
||||
Bolt(current,next,max((bolt_energy-5),5),bounces-1,user)
|
||||
|
||||
@@ -227,78 +227,6 @@
|
||||
mutations = list(BLINDMUT)
|
||||
duration = 300
|
||||
sound="sound/magic/Blind.ogg"
|
||||
|
||||
/obj/effect/proc_holder/spell/fireball
|
||||
name = "Fireball"
|
||||
desc = "This spell fires a fireball at a target and does not require wizard garb."
|
||||
|
||||
school = "evocation"
|
||||
charge_max = 60
|
||||
clothes_req = 0
|
||||
invocation = "ONI SOMA"
|
||||
invocation_type = "shout"
|
||||
range = 20
|
||||
cooldown_min = 20 //10 deciseconds reduction per rank
|
||||
var/fireball_type = /obj/item/projectile/magic/fireball
|
||||
action_icon_state = "fireball0"
|
||||
sound = "sound/magic/Fireball.ogg"
|
||||
|
||||
active = FALSE
|
||||
|
||||
|
||||
/obj/effect/proc_holder/spell/fireball/Click()
|
||||
var/mob/living/user = usr
|
||||
if(!istype(user))
|
||||
return
|
||||
|
||||
var/msg
|
||||
|
||||
if(!can_cast(user))
|
||||
msg = "<span class='warning'>You can no longer cast Fireball.</span>"
|
||||
remove_ranged_ability(msg)
|
||||
return
|
||||
|
||||
if(active)
|
||||
msg = "<span class='notice'>You extinguish your fireball...for now.</span>"
|
||||
remove_ranged_ability(msg)
|
||||
else
|
||||
msg = "<span class='notice'>Your prepare to cast your fireball spell! <B>Left-click to cast at a target!</B></span>"
|
||||
add_ranged_ability(user, msg, TRUE)
|
||||
|
||||
/obj/effect/proc_holder/spell/fireball/update_icon()
|
||||
if(!action)
|
||||
return
|
||||
action.button_icon_state = "fireball[active]"
|
||||
action.UpdateButtonIcon()
|
||||
|
||||
/obj/effect/proc_holder/spell/fireball/InterceptClickOn(mob/living/caller, params, atom/target)
|
||||
if(..())
|
||||
return FALSE
|
||||
|
||||
if(!cast_check(0, ranged_ability_user))
|
||||
remove_ranged_ability()
|
||||
return FALSE
|
||||
|
||||
var/list/targets = list(target)
|
||||
perform(targets,user = ranged_ability_user)
|
||||
|
||||
return TRUE
|
||||
|
||||
/obj/effect/proc_holder/spell/fireball/cast(list/targets, mob/living/user)
|
||||
var/target = targets[1] //There is only ever one target for fireball
|
||||
var/turf/T = user.loc
|
||||
var/turf/U = get_step(user, user.dir) // Get the tile infront of the move, based on their direction
|
||||
if(!isturf(U) || !isturf(T))
|
||||
return 0
|
||||
|
||||
var/obj/item/projectile/magic/fireball/FB = new fireball_type(user.loc)
|
||||
FB.current = get_turf(user)
|
||||
FB.preparePixelProjectile(target, get_turf(target), user)
|
||||
FB.fire()
|
||||
user.newtonian_move(get_dir(U, T))
|
||||
remove_ranged_ability() //Auto-disable the ability once successfully performed
|
||||
return 1
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/repulse
|
||||
name = "Repulse"
|
||||
desc = "This spell throws everything around the user away."
|
||||
|
||||
Reference in New Issue
Block a user