Merge branch 'master' into upstream-merge-38661
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
anchored = TRUE
|
||||
var/popped = FALSE
|
||||
|
||||
/obj/effect/fun_balloon/New()
|
||||
/obj/effect/fun_balloon/Initialize()
|
||||
. = ..()
|
||||
SSobj.processing |= src
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
var/mob/living/carbon/human/H = null
|
||||
|
||||
for(var/mob/living/carbon/human/applicant in GLOB.player_list)
|
||||
if(isReadytoRumble(applicant, ROLE_TRAITOR, FALSE))
|
||||
if(isReadytoRumble(applicant, ROLE_TRAITOR))
|
||||
if(temp.age_check(applicant.client))
|
||||
if(!(applicant.job in temp.restricted_jobs))
|
||||
candidates += applicant
|
||||
|
||||
@@ -496,10 +496,4 @@
|
||||
#undef STIMULUM_SECOND_RISE
|
||||
#undef STIMULUM_ABSOLUTE_DROP
|
||||
#undef REACTION_OPPRESSION_THRESHOLD
|
||||
#undef PLASMA_BINDING_ENERGY
|
||||
#undef MAX_CATALYST_EFFICENCY
|
||||
#undef PLASMA_FUSED_COEFFICENT
|
||||
#undef CATALYST_COEFFICENT
|
||||
#undef FUSION_PURITY_THRESHOLD
|
||||
#undef FUSION_HEAT_DROPOFF
|
||||
#undef NOBLIUM_FORMATION_ENERGY
|
||||
|
||||
@@ -1746,14 +1746,26 @@
|
||||
|
||||
/datum/supply_pack/costumes_toys/randomised/contraband
|
||||
name = "Contraband Crate"
|
||||
desc = "Psst.. bud... want some contraband? I can get you a poster, some nice cigs, bling, even some ambrosia deus...you know, the good stuff. Just keep it away from the cops, kay?"
|
||||
desc = "Psst.. bud... want some contraband? I can get you a poster, some nice cigs, dank, even some sponsored items...you know, the good stuff. Just keep it away from the cops, kay?"
|
||||
contraband = TRUE
|
||||
cost = 3000
|
||||
num_contained = 5
|
||||
num_contained = 7
|
||||
contains = list(/obj/item/poster/random_contraband,
|
||||
/obj/item/poster/random_contraband,
|
||||
/obj/item/reagent_containers/food/snacks/grown/cannabis,
|
||||
/obj/item/reagent_containers/food/snacks/grown/cannabis/rainbow,
|
||||
/obj/item/reagent_containers/food/snacks/grown/cannabis/white,
|
||||
/obj/item/storage/pill_bottle/zoom,
|
||||
/obj/item/storage/pill_bottle/happy,
|
||||
/obj/item/storage/pill_bottle/lsd,
|
||||
/obj/item/storage/pill_bottle/aranesp,
|
||||
/obj/item/storage/pill_bottle/stimulant,
|
||||
/obj/item/toy/cards/deck/syndicate,
|
||||
/obj/item/reagent_containers/food/drinks/bottle/absinthe,
|
||||
/obj/item/clothing/under/syndicate/tacticool,
|
||||
/obj/item/storage/fancy/cigarettes/cigpack_syndicate,
|
||||
/obj/item/storage/fancy/cigarettes/cigpack_shadyjims,
|
||||
/obj/item/storage/fancy/cigarettes/cigpack_midori,
|
||||
/obj/item/seeds/ambrosia/deus,
|
||||
/obj/item/clothing/mask/gas/syndicate,
|
||||
/obj/item/clothing/neck/necklace/dope)
|
||||
crate_name = "crate"
|
||||
|
||||
|
||||
@@ -100,6 +100,11 @@
|
||||
icon_state = "scarf"
|
||||
color = "#4A4A4B" //Grey but it looks black
|
||||
|
||||
/obj/item/clothing/neck/scarf/pink
|
||||
name = "pink scarf"
|
||||
icon_state = "scarf"
|
||||
color = "#F699CD" //Pink
|
||||
|
||||
/obj/item/clothing/neck/scarf/red
|
||||
name = "red scarf"
|
||||
icon_state = "scarf"
|
||||
@@ -118,7 +123,7 @@
|
||||
/obj/item/clothing/neck/scarf/purple
|
||||
name = "purple scarf"
|
||||
icon_state = "scarf"
|
||||
color = "#9557C5" //purple
|
||||
color = "#9557C5" //Purple
|
||||
|
||||
/obj/item/clothing/neck/scarf/yellow
|
||||
name = "yellow scarf"
|
||||
@@ -128,7 +133,7 @@
|
||||
/obj/item/clothing/neck/scarf/orange
|
||||
name = "orange scarf"
|
||||
icon_state = "scarf"
|
||||
color = "#C67A4B" //orange
|
||||
color = "#C67A4B" //Orange
|
||||
|
||||
/obj/item/clothing/neck/scarf/cyan
|
||||
name = "cyan scarf"
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
if(!proximity)
|
||||
return
|
||||
|
||||
if(target.is_refillable()) //Something like a glass. Player probably wants to transfer TO it.
|
||||
if(target.is_refillable() && is_drainable()) //Something like a glass. Player probably wants to transfer TO it.
|
||||
if(!reagents.total_volume)
|
||||
to_chat(user, "<span class='warning'>[src] is empty.</span>")
|
||||
return
|
||||
|
||||
@@ -427,7 +427,7 @@
|
||||
|
||||
/obj/item/disk/plantgene/proc/update_name()
|
||||
if(gene)
|
||||
name = "[gene.get_name()] (Plant Data Disk)"
|
||||
name = "[gene.get_name()] (plant data disk)"
|
||||
else
|
||||
name = "plant data disk"
|
||||
|
||||
@@ -437,4 +437,6 @@
|
||||
|
||||
/obj/item/disk/plantgene/examine(mob/user)
|
||||
..()
|
||||
if(gene && (istype(gene, /datum/plant_gene/core/potency)))
|
||||
to_chat(user,"<span class='notice'>Percent is relative to potency, not maximum volume of the plant.</span>")
|
||||
to_chat(user, "The write-protect tab is set to [src.read_only ? "protected" : "unprotected"].")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/obj/item/integrated_circuit/input/card_reader
|
||||
name = "card reader"
|
||||
name = "ID card reader" //To differentiate it from the data card reader
|
||||
desc = "A circuit that can read the registred name, assignment, and PassKey string from an ID card."
|
||||
icon_state = "card_reader"
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
"on read" = IC_PINTYPE_PULSE_OUT
|
||||
)
|
||||
|
||||
/obj/item/integrated_circuit/input/card_reader/old
|
||||
name = "card reader"
|
||||
spawn_flags = 0
|
||||
|
||||
/obj/item/integrated_circuit/input/card_reader/attackby_react(obj/item/I, mob/living/user, intent)
|
||||
var/obj/item/card/id/card = I.GetID()
|
||||
var/list/access = I.GetAccess()
|
||||
|
||||
@@ -1104,3 +1104,43 @@
|
||||
else
|
||||
activate_pin(3)
|
||||
|
||||
/obj/item/integrated_circuit/input/data_card_reader
|
||||
name = "data card reader"
|
||||
desc = "A circuit that can read from and write to data cards."
|
||||
extended_desc = "Setting the \"write mode\" boolean to true will cause any data cards that are used on the assembly to replace\
|
||||
their existing function and data strings with the given strings, if it is set to false then using a data card on the assembly will cause\
|
||||
the function and data strings stored on the card to be written to the output pins."
|
||||
icon_state = "card_reader"
|
||||
complexity = 4
|
||||
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
|
||||
inputs = list(
|
||||
"function" = IC_PINTYPE_STRING,
|
||||
"data to store" = IC_PINTYPE_STRING,
|
||||
"write mode" = IC_PINTYPE_BOOLEAN
|
||||
)
|
||||
outputs = list(
|
||||
"function" = IC_PINTYPE_STRING,
|
||||
"stored data" = IC_PINTYPE_STRING
|
||||
)
|
||||
activators = list(
|
||||
"on write" = IC_PINTYPE_PULSE_OUT,
|
||||
"on read" = IC_PINTYPE_PULSE_OUT
|
||||
)
|
||||
|
||||
/obj/item/integrated_circuit/input/data_card_reader/attackby_react(obj/item/I, mob/living/user, intent)
|
||||
var/obj/item/card/data/card = I.GetCard()
|
||||
var/write_mode = get_pin_data(IC_INPUT, 3)
|
||||
if(card)
|
||||
if(write_mode == TRUE)
|
||||
card.function = get_pin_data(IC_INPUT, 1)
|
||||
card.data = get_pin_data(IC_INPUT, 2)
|
||||
push_data()
|
||||
activate_pin(1)
|
||||
else
|
||||
set_pin_data(IC_OUTPUT, 1, card.function)
|
||||
set_pin_data(IC_OUTPUT, 2, card.data)
|
||||
push_data()
|
||||
activate_pin(2)
|
||||
else
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
@@ -56,10 +56,10 @@
|
||||
|
||||
if (top_atom)
|
||||
LAZYREMOVE(top_atom.light_sources, src)
|
||||
|
||||
|
||||
if (needs_update)
|
||||
GLOB.lighting_update_lights -= src
|
||||
|
||||
|
||||
. = ..()
|
||||
|
||||
// Yes this doesn't align correctly on anything other than 4 width tabs.
|
||||
@@ -116,7 +116,6 @@
|
||||
. = LUM_FALLOFF(C, pixel_turf); \
|
||||
. *= light_power; \
|
||||
var/OLD = effect_str[C]; \
|
||||
\
|
||||
effect_str[C] = .; \
|
||||
\
|
||||
C.update_lumcount \
|
||||
@@ -126,7 +125,6 @@
|
||||
(. * lum_b) - (OLD * applied_lum_b) \
|
||||
);
|
||||
|
||||
|
||||
#define REMOVE_CORNER(C) \
|
||||
. = -effect_str[C]; \
|
||||
C.update_lumcount \
|
||||
@@ -167,8 +165,9 @@
|
||||
|
||||
/datum/light_source/proc/update_corners()
|
||||
var/update = FALSE
|
||||
var/atom/source_atom = src.source_atom
|
||||
|
||||
if (!source_atom || QDELETED(source_atom))
|
||||
if (QDELETED(source_atom))
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
GLOBAL_LIST(labor_sheet_values)
|
||||
|
||||
/**********************Prisoners' Console**************************/
|
||||
|
||||
/obj/machinery/mineral/labor_claim_console
|
||||
@@ -20,6 +22,18 @@
|
||||
Radio.listening = FALSE
|
||||
locate_stacking_machine()
|
||||
|
||||
if(!GLOB.labor_sheet_values)
|
||||
var/sheet_list = list()
|
||||
for(var/sheet_type in subtypesof(/obj/item/stack/sheet))
|
||||
var/obj/item/stack/sheet/sheet = sheet_type
|
||||
if(!initial(sheet.point_value) || (initial(sheet.merge_type) && initial(sheet.merge_type) != sheet_type)) //ignore no-value sheets and x/fifty subtypes
|
||||
continue
|
||||
sheet_list += list(list("ore" = initial(sheet.name), "value" = initial(sheet.point_value)))
|
||||
GLOB.labor_sheet_values = sortList(sheet_list, /proc/cmp_sheet_list)
|
||||
|
||||
/proc/cmp_sheet_list(list/a, list/b)
|
||||
return a["value"] - b["value"]
|
||||
|
||||
/obj/machinery/mineral/labor_claim_console/attackby(obj/item/I, mob/user, params)
|
||||
if(istype(I, /obj/item/card/id/prisoner))
|
||||
if(!inserted_id)
|
||||
@@ -52,16 +66,10 @@
|
||||
if(check_auth())
|
||||
can_go_home = TRUE
|
||||
|
||||
var/list/ores = list()
|
||||
if(stacking_machine)
|
||||
data["unclaimed_points"] = stacking_machine.points
|
||||
for(var/ore in stacking_machine.ore_values)
|
||||
var/list/O = list()
|
||||
O["ore"] = ore
|
||||
O["value"] = stacking_machine.ore_values[ore]
|
||||
ores += list(O)
|
||||
|
||||
data["ores"] = ores
|
||||
data["ores"] = GLOB.labor_sheet_values
|
||||
data["can_go_home"] = can_go_home
|
||||
|
||||
return data
|
||||
@@ -127,15 +135,10 @@
|
||||
|
||||
|
||||
/obj/machinery/mineral/stacking_machine/laborstacker
|
||||
var/points = 0 //The unclaimed value of ore stacked. Value for each ore loosely relative to its rarity.
|
||||
var/list/ore_values = list("glass" = 1, "metal" = 2, "reinforced glass" = 4, "gold" = 20, "silver" = 20, "uranium" = 20, "titanium" = 20, "solid plasma" = 20, "plasteel" = 23, "plasma glass" = 23, "diamond" = 25, "bluespace polycrystal" = 30, "plastitanium" = 45, "bananium" = 50)
|
||||
var/points = 0 //The unclaimed value of ore stacked.
|
||||
|
||||
/obj/machinery/mineral/stacking_machine/laborstacker/process_sheet(obj/item/stack/sheet/inp)
|
||||
if(istype(inp))
|
||||
var/n = inp.name
|
||||
var/a = inp.amount
|
||||
if(n in ore_values)
|
||||
points += ore_values[n] * a
|
||||
points += inp.point_value * inp.amount
|
||||
..()
|
||||
|
||||
|
||||
|
||||
@@ -105,8 +105,8 @@
|
||||
stack_list[inp.type] = s
|
||||
var/obj/item/stack/sheet/storage = stack_list[inp.type]
|
||||
storage.amount += inp.amount //Stack the sheets
|
||||
qdel(inp) //Let the old sheet garbage collect
|
||||
while(storage.amount > stack_amt) //Get rid of excessive stackage
|
||||
var/obj/item/stack/sheet/out = new inp.type(null, stack_amt)
|
||||
unload_mineral(out)
|
||||
storage.amount -= stack_amt
|
||||
qdel(inp) //Let the old sheet garbage collect
|
||||
|
||||
@@ -171,8 +171,6 @@
|
||||
if("Survival Capsule and Explorer's Webbing")
|
||||
new /obj/item/storage/belt/mining/vendor(drop_location)
|
||||
if("Resonator Kit")
|
||||
new /obj/item/storage/belt/mining/alt(drop_location)
|
||||
new /obj/item/t_scanner/adv_mining_scanner(drop_location)
|
||||
new /obj/item/extinguisher/mini(drop_location)
|
||||
new /obj/item/resonator(drop_location)
|
||||
if("Minebot Kit")
|
||||
@@ -185,8 +183,6 @@
|
||||
new /obj/item/fulton_core(drop_location)
|
||||
new /obj/item/stack/marker_beacon/thirty(drop_location)
|
||||
if("Crusher Kit")
|
||||
new /obj/item/storage/belt/mining/alt(drop_location)
|
||||
new /obj/item/t_scanner/adv_mining_scanner(drop_location)
|
||||
new /obj/item/extinguisher/mini(drop_location)
|
||||
new /obj/item/twohanded/required/kinetic_crusher(drop_location)
|
||||
if("Mining Conscription Kit")
|
||||
|
||||
@@ -164,8 +164,8 @@
|
||||
|
||||
//If there's an MMI in the robot, have it ejected when the mob goes away. --NEO
|
||||
/mob/living/silicon/robot/Destroy()
|
||||
var/atom/T = drop_location()//To hopefully prevent run time errors.
|
||||
if(mmi && mind)//Safety for when a cyborg gets dust()ed. Or there is no MMI inside.
|
||||
var/turf/T = get_turf(loc)//To hopefully prevent run time errors.
|
||||
if(T)
|
||||
mmi.forceMove(T)
|
||||
if(mmi.brainmob)
|
||||
@@ -184,6 +184,10 @@
|
||||
connected_ai.connected_robots -= src
|
||||
if(shell)
|
||||
GLOB.available_ai_shells -= src
|
||||
else
|
||||
if(T && istype(radio) && istype(radio.keyslot))
|
||||
radio.keyslot.forceMove(T)
|
||||
radio.keyslot = null
|
||||
qdel(wires)
|
||||
qdel(module)
|
||||
qdel(eye_lights)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#define MEDAL_PREFIX "Bubblegum"
|
||||
|
||||
/*
|
||||
|
||||
BUBBLEGUM
|
||||
@@ -7,22 +9,15 @@ Bubblegum's footsteps are heralded by shaking booms, proving its tremendous size
|
||||
|
||||
It acts as a melee creature, chasing down and attacking its target while also using different attacks to augment its power that increase as it takes damage.
|
||||
|
||||
It tries to strike at its target through any bloodpools under them; if it fails to do that, it will spray blood and then attempt to warp to a bloodpool near the target.
|
||||
If it fails to warp to a target, it may summon up to 6 slaughterlings from the blood around it.
|
||||
If it does not summon all 6 slaughterlings, it will instead charge at its target, dealing massive damage to anything it hits and spraying a stream of blood.
|
||||
At half health, it will either charge three times or warp, then charge, instead of doing a single charge.
|
||||
It often charges, dealing massive damage to anything unfortunate enough to be standing where it's aiming.
|
||||
Whenever it isn't chasing something down, it will sink into nearby blood pools (if possible) and springs out of the closest one to its target.
|
||||
To make this possible, it sprays streams of blood at random.
|
||||
From these blood pools Bubblegum may summon slaughterlings - weak, low-damage minions designed to impede the target's progress.
|
||||
|
||||
<<<<<<< HEAD
|
||||
When Bubblegum dies, it leaves behind a H.E.C.K. mining suit as well as a chest that can contain three things:
|
||||
1. A bottle that, when activated, drives everyone nearby into a frenzy
|
||||
2. A contract that marks for death the chosen target
|
||||
3. A spellblade that can slice off limbs at range
|
||||
=======
|
||||
When Bubblegum dies, it leaves behind a H.E.C.K. suit+helmet as well as a chest that can contain three things:
|
||||
1. A spellblade that can slice off limbs at range
|
||||
2. A bottle that, when activated, drives everyone nearby into a frenzy
|
||||
3. A contract that marks for death the chosen target
|
||||
>>>>>>> c6ca01f... Merge pull request #38661 from 81Denton/fug
|
||||
|
||||
Difficulty: Hard
|
||||
|
||||
@@ -51,8 +46,7 @@ Difficulty: Hard
|
||||
del_on_death = 1
|
||||
crusher_loot = list(/obj/structure/closet/crate/necropolis/bubblegum/crusher)
|
||||
loot = list(/obj/structure/closet/crate/necropolis/bubblegum)
|
||||
blood_volume = BLOOD_VOLUME_MAXIMUM //BLEED FOR ME
|
||||
var/charging = FALSE
|
||||
var/charging = 0
|
||||
medal_type = BOSS_MEDAL_BUBBLEGUM
|
||||
score_type = BUBBLEGUM_SCORE
|
||||
deathmessage = "sinks into a pool of blood, fleeing the battle. You've won, for now... "
|
||||
@@ -64,54 +58,33 @@ Difficulty: Hard
|
||||
desc = "You're not quite sure how a signal can be bloody."
|
||||
invisibility = 100
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/adjustBruteLoss(amount, updating_health = TRUE, forced = FALSE)
|
||||
. = ..()
|
||||
if(. > 0 && prob(25))
|
||||
var/obj/effect/decal/cleanable/blood/gibs/bubblegum/B = new /obj/effect/decal/cleanable/blood/gibs/bubblegum(loc)
|
||||
if(prob(40))
|
||||
step(B, pick(GLOB.cardinals))
|
||||
else
|
||||
B.setDir(pick(GLOB.cardinals))
|
||||
|
||||
/obj/effect/decal/cleanable/blood/gibs/bubblegum
|
||||
name = "thick blood"
|
||||
desc = "Thick, splattered blood."
|
||||
random_icon_states = list("gib3", "gib5", "gib6")
|
||||
bloodiness = 20
|
||||
|
||||
/obj/effect/decal/cleanable/blood/gibs/bubblegum/can_bloodcrawl_in()
|
||||
return TRUE
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/Life()
|
||||
..()
|
||||
move_to_delay = CLAMP((health/maxHealth) * 10, 5, 10)
|
||||
move_to_delay = CLAMP(round((health/maxHealth) * 10), 5, 10)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/OpenFire()
|
||||
anger_modifier = CLAMP(((maxHealth - health)/60),0,20)
|
||||
anger_modifier = CLAMP(((maxHealth - health)/50),0,20)
|
||||
if(charging)
|
||||
return
|
||||
ranged_cooldown = world.time + ranged_cooldown_time
|
||||
|
||||
var/warped = FALSE
|
||||
if(!try_bloodattack())
|
||||
INVOKE_ASYNC(src, .proc/blood_spray)
|
||||
warped = blood_warp()
|
||||
if(warped && prob(100 - anger_modifier))
|
||||
return
|
||||
blood_warp()
|
||||
|
||||
if(prob(90 - anger_modifier) || slaughterlings())
|
||||
if(health > maxHealth * 0.5)
|
||||
if(prob(25))
|
||||
INVOKE_ASYNC(src, .proc/blood_spray)
|
||||
|
||||
else if(prob(5+anger_modifier/2))
|
||||
slaughterlings()
|
||||
else
|
||||
if(health > maxHealth/2 && !client)
|
||||
INVOKE_ASYNC(src, .proc/charge)
|
||||
else
|
||||
if(prob(70) || warped)
|
||||
INVOKE_ASYNC(src, .proc/charge, 2)
|
||||
else
|
||||
INVOKE_ASYNC(src, .proc/warp_charge)
|
||||
INVOKE_ASYNC(src, .proc/triple_charge)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/Initialize()
|
||||
. = ..()
|
||||
for(var/mob/living/simple_animal/hostile/megafauna/bubblegum/B in GLOB.mob_living_list)
|
||||
for(var/mob/living/simple_animal/hostile/megafauna/bubblegum/B in GLOB.mob_list)
|
||||
if(B != src)
|
||||
return INITIALIZE_HINT_QDEL //There can be only one
|
||||
var/obj/effect/proc_holder/spell/bloodcrawl/bloodspell = new
|
||||
@@ -125,56 +98,53 @@ Difficulty: Hard
|
||||
if(.)
|
||||
SSshuttle.shuttle_purchase_requirements_met |= "bubblegum"
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/do_attack_animation(atom/A, visual_effect_icon)
|
||||
if(!charging)
|
||||
..()
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/do_attack_animation(atom/A)
|
||||
if(charging)
|
||||
return
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/AttackingTarget()
|
||||
if(!charging)
|
||||
return ..()
|
||||
if(charging)
|
||||
return
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/Goto(target, delay, minimum_distance)
|
||||
if(!charging)
|
||||
..()
|
||||
if(charging)
|
||||
return
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/Move()
|
||||
if(!stat)
|
||||
playsound(src.loc, 'sound/effects/meteorimpact.ogg', 200, 1, 2, 1)
|
||||
if(charging)
|
||||
new /obj/effect/temp_visual/decoy/fading(loc,src)
|
||||
new/obj/effect/temp_visual/decoy/fading(loc,src)
|
||||
DestroySurroundings()
|
||||
. = ..()
|
||||
if(!stat && .)
|
||||
playsound(src, 'sound/effects/meteorimpact.ogg', 200, 1, 2, 1)
|
||||
if(charging)
|
||||
DestroySurroundings()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/warp_charge()
|
||||
blood_warp()
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/triple_charge()
|
||||
charge()
|
||||
sleep(10)
|
||||
charge()
|
||||
sleep(10)
|
||||
charge()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/charge(bonus_charges)
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/charge()
|
||||
var/turf/T = get_turf(target)
|
||||
if(!T || T == loc)
|
||||
return
|
||||
new /obj/effect/temp_visual/dragon_swoop/bubblegum(T)
|
||||
charging = TRUE
|
||||
new /obj/effect/temp_visual/dragon_swoop(T)
|
||||
charging = 1
|
||||
DestroySurroundings()
|
||||
walk(src, 0)
|
||||
setDir(get_dir(src, T))
|
||||
var/obj/effect/temp_visual/decoy/D = new /obj/effect/temp_visual/decoy(loc,src)
|
||||
animate(D, alpha = 0, color = "#FF0000", transform = matrix()*2, time = 3)
|
||||
sleep(3)
|
||||
throw_at(T, get_dist(src, T), 1, src, 0, callback = CALLBACK(src, .charge_end, bonus_charges))
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/charge_end(bonus_charges, list/effects_to_destroy)
|
||||
charging = FALSE
|
||||
try_bloodattack()
|
||||
if(target)
|
||||
if(bonus_charges)
|
||||
bonus_charges--
|
||||
charge(bonus_charges)
|
||||
else
|
||||
Goto(target, move_to_delay, minimum_distance)
|
||||
SetRecoveryTime(MEGAFAUNA_DEFAULT_RECOVERY_TIME)
|
||||
animate(D, alpha = 0, color = "#FF0000", transform = matrix()*2, time = 5)
|
||||
sleep(5)
|
||||
throw_at(T, get_dist(src, T), 1, src, 0)
|
||||
charging = 0
|
||||
Goto(target, move_to_delay, minimum_distance)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/Collide(atom/A)
|
||||
@@ -198,142 +168,20 @@ Difficulty: Hard
|
||||
var/throwtarget = get_edge_target_turf(src, get_dir(src, get_step_away(L, src)))
|
||||
L.throw_at(throwtarget, 3)
|
||||
|
||||
charging = FALSE
|
||||
charging = 0
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/get_mobs_on_blood()
|
||||
var/list/targets = ListTargets()
|
||||
. = list()
|
||||
for(var/mob/living/L in targets)
|
||||
var/list/bloodpool = get_pools(get_turf(L), 0)
|
||||
if(bloodpool.len && (!faction_check_mob(L) || L.stat == DEAD))
|
||||
. += L
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/try_bloodattack()
|
||||
var/list/targets = get_mobs_on_blood()
|
||||
if(targets.len)
|
||||
INVOKE_ASYNC(src, .proc/bloodattack, targets, prob(50))
|
||||
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/bloodattack(list/targets, handedness)
|
||||
var/mob/living/target_one = pick_n_take(targets)
|
||||
var/turf/target_one_turf = get_turf(target_one)
|
||||
var/mob/living/target_two
|
||||
if(targets.len)
|
||||
target_two = pick_n_take(targets)
|
||||
var/turf/target_two_turf = get_turf(target_two)
|
||||
if(target_two.stat != CONSCIOUS || prob(10))
|
||||
bloodgrab(target_two_turf, handedness)
|
||||
else
|
||||
bloodsmack(target_two_turf, handedness)
|
||||
|
||||
if(target_one)
|
||||
var/list/pools = get_pools(get_turf(target_one), 0)
|
||||
if(pools.len)
|
||||
target_one_turf = get_turf(target_one)
|
||||
if(target_one_turf)
|
||||
if(target_one.stat != CONSCIOUS || prob(10))
|
||||
bloodgrab(target_one_turf, !handedness)
|
||||
else
|
||||
bloodsmack(target_one_turf, !handedness)
|
||||
|
||||
if(!target_two && target_one)
|
||||
var/list/poolstwo = get_pools(get_turf(target_one), 0)
|
||||
if(poolstwo.len)
|
||||
target_one_turf = get_turf(target_one)
|
||||
if(target_one_turf)
|
||||
if(target_one.stat != CONSCIOUS || prob(10))
|
||||
bloodgrab(target_one_turf, handedness)
|
||||
else
|
||||
bloodsmack(target_one_turf, handedness)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/bloodsmack(turf/T, handedness)
|
||||
if(handedness)
|
||||
new /obj/effect/temp_visual/bubblegum_hands/rightsmack(T)
|
||||
else
|
||||
new /obj/effect/temp_visual/bubblegum_hands/leftsmack(T)
|
||||
sleep(2.5)
|
||||
for(var/mob/living/L in T)
|
||||
if(!faction_check_mob(L))
|
||||
to_chat(L, "<span class='userdanger'>[src] rends you!</span>")
|
||||
playsound(T, attack_sound, 100, 1, -1)
|
||||
var/limb_to_hit = L.get_bodypart(pick(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG))
|
||||
L.apply_damage(25, BRUTE, limb_to_hit, L.run_armor_check(limb_to_hit, "melee", null, null, armour_penetration))
|
||||
sleep(3)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/bloodgrab(turf/T, handedness)
|
||||
if(handedness)
|
||||
new /obj/effect/temp_visual/bubblegum_hands/rightpaw(T)
|
||||
new /obj/effect/temp_visual/bubblegum_hands/rightthumb(T)
|
||||
else
|
||||
new /obj/effect/temp_visual/bubblegum_hands/leftpaw(T)
|
||||
new /obj/effect/temp_visual/bubblegum_hands/leftthumb(T)
|
||||
sleep(6)
|
||||
for(var/mob/living/L in T)
|
||||
if(!faction_check_mob(L))
|
||||
to_chat(L, "<span class='userdanger'>[src] drags you through the blood!</span>")
|
||||
playsound(T, 'sound/magic/enter_blood.ogg', 100, 1, -1)
|
||||
var/turf/targetturf = get_step(src, dir)
|
||||
L.forceMove(targetturf)
|
||||
playsound(targetturf, 'sound/magic/exit_blood.ogg', 100, 1, -1)
|
||||
if(L.stat != CONSCIOUS)
|
||||
addtimer(CALLBACK(src, .proc/devour, L), 2)
|
||||
sleep(1)
|
||||
|
||||
/obj/effect/temp_visual/dragon_swoop/bubblegum
|
||||
duration = 10
|
||||
|
||||
/obj/effect/temp_visual/bubblegum_hands
|
||||
icon = 'icons/effects/bubblegum.dmi'
|
||||
duration = 9
|
||||
|
||||
/obj/effect/temp_visual/bubblegum_hands/rightthumb
|
||||
icon_state = "rightthumbgrab"
|
||||
|
||||
/obj/effect/temp_visual/bubblegum_hands/leftthumb
|
||||
icon_state = "leftthumbgrab"
|
||||
|
||||
/obj/effect/temp_visual/bubblegum_hands/rightpaw
|
||||
icon_state = "rightpawgrab"
|
||||
layer = BELOW_MOB_LAYER
|
||||
|
||||
/obj/effect/temp_visual/bubblegum_hands/leftpaw
|
||||
icon_state = "leftpawgrab"
|
||||
layer = BELOW_MOB_LAYER
|
||||
|
||||
/obj/effect/temp_visual/bubblegum_hands/rightsmack
|
||||
icon_state = "rightsmack"
|
||||
|
||||
/obj/effect/temp_visual/bubblegum_hands/leftsmack
|
||||
icon_state = "leftsmack"
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/blood_warp()
|
||||
if(Adjacent(target))
|
||||
return FALSE
|
||||
var/list/can_jaunt = get_pools(get_turf(src), 1)
|
||||
if(!can_jaunt.len)
|
||||
return FALSE
|
||||
|
||||
var/list/pools = get_pools(get_turf(target), 2)
|
||||
var/list/pools_to_remove = get_pools(get_turf(target), 1)
|
||||
pools -= pools_to_remove
|
||||
if(!pools.len)
|
||||
return FALSE
|
||||
|
||||
var/obj/effect/temp_visual/decoy/DA = new /obj/effect/temp_visual/decoy(loc,src)
|
||||
DA.color = "#FF0000"
|
||||
var/oldtransform = DA.transform
|
||||
DA.transform = matrix()*2
|
||||
animate(DA, alpha = 255, color = initial(DA.color), transform = oldtransform, time = 3)
|
||||
sleep(3)
|
||||
qdel(DA)
|
||||
|
||||
var/obj/effect/decal/cleanable/blood/found_bloodpool
|
||||
pools = get_pools(get_turf(target), 2)
|
||||
pools_to_remove = get_pools(get_turf(target), 1)
|
||||
pools -= pools_to_remove
|
||||
var/list/pools = list()
|
||||
var/can_jaunt = FALSE
|
||||
for(var/obj/effect/decal/cleanable/blood/nearby in view(src,2))
|
||||
can_jaunt = TRUE
|
||||
break
|
||||
if(!can_jaunt)
|
||||
return
|
||||
for(var/obj/effect/decal/cleanable/blood/nearby in view(get_turf(target),2))
|
||||
pools += nearby
|
||||
if(pools.len)
|
||||
shuffle_inplace(pools)
|
||||
found_bloodpool = pick(pools)
|
||||
@@ -343,51 +191,30 @@ Difficulty: Hard
|
||||
forceMove(get_turf(found_bloodpool))
|
||||
playsound(get_turf(src), 'sound/magic/exit_blood.ogg', 100, 1, -1)
|
||||
visible_message("<span class='danger'>And springs back out!</span>")
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/get_pools(turf/T, range)
|
||||
. = list()
|
||||
for(var/obj/effect/decal/cleanable/nearby in view(T, range))
|
||||
if(nearby.can_bloodcrawl_in())
|
||||
. += nearby
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/blood_spray()
|
||||
visible_message("<span class='danger'>[src] sprays a stream of gore!</span>")
|
||||
var/range = 6 + round(anger_modifier * 0.4)
|
||||
var/turf/E = get_edge_target_turf(src, src.dir)
|
||||
var/range = 10
|
||||
var/turf/previousturf = get_turf(src)
|
||||
var/turf/J = previousturf
|
||||
var/targetdir = get_dir(src, target)
|
||||
if(target.loc == loc)
|
||||
targetdir = dir
|
||||
face_atom(target)
|
||||
new /obj/effect/decal/cleanable/blood/bubblegum(J)
|
||||
for(var/i in 1 to range)
|
||||
J = get_step(previousturf, targetdir)
|
||||
new /obj/effect/temp_visual/dir_setting/bloodsplatter(previousturf, get_dir(previousturf, J))
|
||||
playsound(previousturf,'sound/effects/splat.ogg', 100, 1, -1)
|
||||
if(!J || !previousturf.atmos_adjacent_turfs || !previousturf.atmos_adjacent_turfs[J])
|
||||
for(var/turf/J in getline(src,E))
|
||||
if(!range)
|
||||
break
|
||||
new /obj/effect/decal/cleanable/blood/bubblegum(J)
|
||||
new /obj/effect/temp_visual/dir_setting/bloodsplatter(previousturf, get_dir(previousturf, J))
|
||||
if(!previousturf.CanAtmosPass(J))
|
||||
break
|
||||
playsound(J,'sound/effects/splat.ogg', 100, 1, -1)
|
||||
new /obj/effect/decal/cleanable/blood(J)
|
||||
range--
|
||||
previousturf = J
|
||||
sleep(1)
|
||||
|
||||
/obj/effect/decal/cleanable/blood/bubblegum
|
||||
bloodiness = 0
|
||||
|
||||
/obj/effect/decal/cleanable/blood/bubblegum/can_bloodcrawl_in()
|
||||
return TRUE
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/slaughterlings()
|
||||
visible_message("<span class='danger'>[src] summons a shoal of slaughterlings!</span>")
|
||||
var/max_amount = 6
|
||||
for(var/H in get_pools(get_turf(src), 1))
|
||||
if(!max_amount)
|
||||
break
|
||||
max_amount--
|
||||
var/obj/effect/decal/cleanable/blood/B = H
|
||||
new /mob/living/simple_animal/hostile/asteroid/hivelordbrood/slaughter(B.loc)
|
||||
return max_amount
|
||||
for(var/obj/effect/decal/cleanable/blood/H in range(src, 10))
|
||||
if(prob(25))
|
||||
new /mob/living/simple_animal/hostile/asteroid/hivelordbrood/slaughter(H.loc)
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/slaughter
|
||||
name = "slaughterling"
|
||||
@@ -405,3 +232,5 @@ Difficulty: Hard
|
||||
if(istype(mover, /mob/living/simple_animal/hostile/megafauna/bubblegum))
|
||||
return 1
|
||||
return 0
|
||||
|
||||
#undef MEDAL_PREFIX
|
||||
@@ -11,8 +11,21 @@
|
||||
clumsy_check = FALSE
|
||||
randomspread = FALSE
|
||||
|
||||
var/hugbox = TRUE
|
||||
var/max_power = INFINITY
|
||||
var/reaction_volume_mod = 0
|
||||
var/reaction_cycles = 3 //How many times gases react() before calculation. Very finnicky value, do not mess with without good reason.
|
||||
var/prereaction = TRUE
|
||||
|
||||
var/bombcheck = TRUE
|
||||
var/debug_power = 0
|
||||
|
||||
var/obj/item/transfer_valve/bomb
|
||||
|
||||
/obj/item/gun/blastcannon/debug
|
||||
debug_power = 80
|
||||
bombcheck = FALSE
|
||||
|
||||
/obj/item/gun/blastcannon/Initialize()
|
||||
. = ..()
|
||||
if(!pin)
|
||||
@@ -56,27 +69,33 @@
|
||||
return TRUE
|
||||
return ..()
|
||||
|
||||
//returns the third value of a bomb blast
|
||||
/obj/item/gun/blastcannon/proc/calculate_bomb()
|
||||
if(!istype(bomb) || !istype(bomb.tank_one) || !istype(bomb.tank_two))
|
||||
return 0
|
||||
var/datum/gas_mixture/temp = new(60) //directional buff.
|
||||
temp.merge(bomb.tank_one.air_contents.remove_ratio(1))
|
||||
temp.merge(bomb.tank_two.air_contents.remove_ratio(2))
|
||||
for(var/i in 1 to 6)
|
||||
var/datum/gas_mixture/temp = new(max(reaction_volume_mod, 0))
|
||||
bomb.merge_gases(temp)
|
||||
if(prereaction)
|
||||
temp.react(src)
|
||||
var/prereaction_pressure = temp.return_pressure()
|
||||
if(prereaction_pressure < TANK_FRAGMENT_PRESSURE)
|
||||
return 0
|
||||
for(var/i in 1 to reaction_cycles)
|
||||
temp.react(src)
|
||||
var/pressure = temp.return_pressure()
|
||||
qdel(temp)
|
||||
if(pressure < TANK_FRAGMENT_PRESSURE)
|
||||
return 0
|
||||
return (pressure / TANK_FRAGMENT_SCALE)
|
||||
return ((pressure - TANK_FRAGMENT_PRESSURE) / TANK_FRAGMENT_SCALE)
|
||||
|
||||
/obj/item/gun/blastcannon/afterattack(atom/target, mob/user, flag, params)
|
||||
if((!bomb) || (!target) || (get_dist(get_turf(target), get_turf(user)) <= 2))
|
||||
if((!bomb && bombcheck) || (!target) || (get_dist(get_turf(target), get_turf(user)) <= 2))
|
||||
return ..()
|
||||
var/power = calculate_bomb()
|
||||
var/power = bomb? calculate_bomb() : debug_power
|
||||
power = min(power, max_power)
|
||||
QDEL_NULL(bomb)
|
||||
update_icon()
|
||||
var/heavy = power * 0.2
|
||||
var/heavy = power * 0.25
|
||||
var/medium = power * 0.5
|
||||
var/light = power
|
||||
user.visible_message("<span class='danger'>[user] opens [bomb] on [user.p_their()] [name] and fires a blast wave at [target]!</span>","<span class='danger'>You open [bomb] on your [name] and fire a blast wave at [target]!</span>")
|
||||
@@ -87,7 +106,8 @@
|
||||
message_admins(log_str)
|
||||
log_game(log_str)
|
||||
var/obj/item/projectile/blastwave/BW = new(loc, heavy, medium, light)
|
||||
BW.preparePixelProjectile(target, get_turf(target), user, params, 0)
|
||||
BW.hugbox = hugbox
|
||||
BW.preparePixelProjectile(target, get_turf(src), params, 0)
|
||||
BW.fire()
|
||||
|
||||
/obj/item/projectile/blastwave
|
||||
@@ -99,6 +119,7 @@
|
||||
var/heavyr = 0
|
||||
var/mediumr = 0
|
||||
var/lightr = 0
|
||||
var/hugbox = TRUE
|
||||
range = 150
|
||||
|
||||
/obj/item/projectile/blastwave/Initialize(mapload, _h, _m, _l)
|
||||
@@ -110,14 +131,25 @@
|
||||
/obj/item/projectile/blastwave/Range()
|
||||
..()
|
||||
var/amount_destruction = EXPLODE_NONE
|
||||
var/wallbreak_chance = 0
|
||||
if(heavyr)
|
||||
amount_destruction = EXPLODE_DEVASTATE
|
||||
wallbreak_chance = 99
|
||||
else if(mediumr)
|
||||
amount_destruction = EXPLODE_HEAVY
|
||||
wallbreak_chance = 66
|
||||
else if(lightr)
|
||||
amount_destruction = EXPLODE_LIGHT
|
||||
wallbreak_chance = 33
|
||||
if(amount_destruction)
|
||||
loc.ex_act(amount_destruction)
|
||||
if(hugbox)
|
||||
loc.contents_explosion(EXPLODE_HEAVY, loc)
|
||||
if(istype(loc, /turf/closed/wall))
|
||||
var/turf/closed/wall/W = loc
|
||||
if(prob(wallbreak_chance))
|
||||
W.dismantle_wall(TRUE, TRUE)
|
||||
else
|
||||
loc.ex_act(amount_destruction)
|
||||
else
|
||||
qdel(src)
|
||||
|
||||
|
||||
@@ -151,10 +151,32 @@
|
||||
icon_state = "pill18"
|
||||
list_reagents = list("insulin" = 50)
|
||||
roundstart = 1
|
||||
|
||||
///////////////////////////////////////// this pill is used only in a legion mob drop
|
||||
/obj/item/reagent_containers/pill/shadowtoxin
|
||||
name = "black pill"
|
||||
desc = "I wouldn't eat this if I were you."
|
||||
icon_state = "pill9"
|
||||
color = "#454545"
|
||||
list_reagents = list("shadowmutationtoxin" = 1)
|
||||
list_reagents = list("shadowmutationtoxin" = 1)
|
||||
//////////////////////////////////////// drugs
|
||||
/obj/item/reagent_containers/pill/zoom
|
||||
name = "zoom pill"
|
||||
list_reagents = list("synaptizine" = 10, "nicotine" = 10, "methamphetamine" = 1)
|
||||
|
||||
|
||||
/obj/item/reagent_containers/pill/happy
|
||||
name = "happy pill"
|
||||
list_reagents = list("sugar" = 10, "space_drugs" = 10)
|
||||
|
||||
|
||||
/obj/item/reagent_containers/pill/lsd
|
||||
name = "hallucinogen pill"
|
||||
list_reagents = list("mushroomhallucinogen" = 15, "mindbreaker" = 15)
|
||||
|
||||
|
||||
/obj/item/reagent_containers/pill/aranesp
|
||||
name = "speedy pill"
|
||||
list_reagents = list("aranesp" = 10)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -199,7 +199,7 @@
|
||||
/obj/item/bodypart/r_leg/drop_limb(special)
|
||||
if(owner && !special)
|
||||
if(owner.legcuffed)
|
||||
owner.legcuffed.forceMove(drop_location())
|
||||
owner.legcuffed.forceMove(owner.drop_location()) //At this point bodypart is still in nullspace
|
||||
owner.legcuffed.dropped(owner)
|
||||
owner.legcuffed = null
|
||||
owner.update_inv_legcuffed()
|
||||
@@ -210,7 +210,7 @@
|
||||
/obj/item/bodypart/l_leg/drop_limb(special) //copypasta
|
||||
if(owner && !special)
|
||||
if(owner.legcuffed)
|
||||
owner.legcuffed.forceMove(drop_location())
|
||||
owner.legcuffed.forceMove(owner.drop_location())
|
||||
owner.legcuffed.dropped(owner)
|
||||
owner.legcuffed = null
|
||||
owner.update_inv_legcuffed()
|
||||
|
||||
@@ -1412,6 +1412,16 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
|
||||
limited_stock = 2 //you can't use more than two!
|
||||
restricted_roles = list("Shaft Miner")
|
||||
|
||||
/datum/uplink_item/role_restricted/blastcannon
|
||||
name = "Blast Cannon"
|
||||
desc = "A highly specialized weapon, the Blast Cannon is actually relatively simple. It contains an attachment for a tank transfer valve mounted to an angled pipe specially constructed \
|
||||
withstand extreme pressure and temperatures, and has a mechanical trigger for triggering the transfer valve. Essentially, it turns the explosive force of a bomb into a narrow-angle \
|
||||
blast wave \"projectile\". Aspiring scientists may find this highly useful, as forcing the pressure shockwave into a narrow angle seems to be able to bypass whatever quirk of physics \
|
||||
disallows explosive ranges above a certain distance, allowing for the device to use the theoretical yield of a transfer valve bomb, instead of the factual yield."
|
||||
item = /obj/item/gun/blastcannon
|
||||
cost = 14 //High cost because of the potential for extreme damage in the hands of a skilled scientist.
|
||||
restricted_roles = list("Research Director", "Scientist")
|
||||
|
||||
/datum/uplink_item/device_tools/clown_bomb
|
||||
name = "Clown Bomb"
|
||||
desc = "The Clown bomb is a hilarious device capable of massive pranks. It has an adjustable timer, \
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
/obj/item/clothing/neck/tie/red = 1,
|
||||
/obj/item/clothing/neck/tie/black = 1,
|
||||
/obj/item/clothing/neck/tie/horrible = 1,
|
||||
/obj/item/clothing/neck/scarf/pink = 1,
|
||||
/obj/item/clothing/neck/scarf/red = 1,
|
||||
/obj/item/clothing/neck/scarf/green = 1,
|
||||
/obj/item/clothing/neck/scarf/darkblue = 1,
|
||||
@@ -118,5 +119,5 @@
|
||||
/obj/item/vending_refill/clothing
|
||||
machine_name = "ClothesMate"
|
||||
icon_state = "refill_clothes"
|
||||
charges = list(37, 4, 4)// of 111 standard, 12 contraband, 10 premium(?)
|
||||
init_charges = list(37, 4, 4)
|
||||
charges = list(38, 4, 4)// of 112 standard, 12 contraband, 10 premium(?)
|
||||
init_charges = list(38, 4, 4)
|
||||
|
||||
Reference in New Issue
Block a user