mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-21 20:20:33 +01:00
## About The Pull Request This PR refactors ``effect_system``s to be a bit easier to use by getting rid of ``set_up``, allowing ``attach()`` to be chained into ``start()`` and refactoring most direct system usages in our code to use helper procs. ``set_up`` was unnecessary and only existed to allow ``New``'s behavior to be fully overriden, which is not required if we split sparks/lightning/steam into a new ``/datum/effect_system/basic`` subtype which houses the effect spreading behavior. This allows us to roll all logic from ``set_up`` into ``New`` and cut down on code complexity. Chaining setup as ``system.attach(src).start()`` also helps a bit in case no helper method exists I've added ``do_chem_smoke`` and ``do_foam`` helpers, which respectively allow chemical smoke or foam to be spawned easily without having to manually create effect datums and reagent holders. Also turns out we've had some nonfunctional effect systems which either never set themselves up, or never started, so I fixed those while I was at it (mostly by moving them to aforementioned helper procs) ## Why It's Good For The Game Cleaner code, makes it significantly easier for users to work with. Also most of our effect system usage was copypasta which was passing booleans as numbers, while perfectly fine helper procs existed in our code. ## Changelog 🆑 refactor: Refactored sparks, foam, smoke, and other miscellaneous effect systems. refactor: Vapes now have consistent rigging with cigs using the new system. fix: Fixed some effects never working. /🆑
866 lines
37 KiB
Plaintext
866 lines
37 KiB
Plaintext
//this is designed to replace the destructive analyzer
|
|
|
|
//NEEDS MAJOR CODE CLEANUP
|
|
|
|
#define SCANTYPE_POKE 1
|
|
#define SCANTYPE_IRRADIATE 2
|
|
#define SCANTYPE_GAS 3
|
|
#define SCANTYPE_HEAT 4
|
|
#define SCANTYPE_COLD 5
|
|
#define SCANTYPE_OBLITERATE 6
|
|
#define SCANTYPE_DISCOVER 7
|
|
|
|
#define EFFECT_PROB_VERYLOW 20
|
|
#define EFFECT_PROB_LOW 35
|
|
#define EFFECT_PROB_MEDIUM 50
|
|
#define EFFECT_PROB_HIGH 75
|
|
#define EFFECT_PROB_VERYHIGH 95
|
|
|
|
#define FAIL 8
|
|
/obj/machinery/rnd/experimentor
|
|
name = "\improper E.X.P.E.R.I-MENTOR"
|
|
desc = "A \"replacement\" for the destructive analyzer with a slight tendency to catastrophically fail."
|
|
icon = 'icons/obj/machines/experimentator.dmi'
|
|
icon_state = "h_lathe"
|
|
base_icon_state = "h_lathe"
|
|
density = TRUE
|
|
use_power = IDLE_POWER_USE
|
|
circuit = /obj/item/circuitboard/machine/experimentor
|
|
interaction_flags_machine = INTERACT_MACHINE_WIRES_IF_OPEN|INTERACT_MACHINE_ALLOW_SILICON|INTERACT_MACHINE_OPEN_SILICON
|
|
var/recentlyExperimented = 0
|
|
/// Weakref to the first ian we can find at init
|
|
var/datum/weakref/tracked_ian_ref
|
|
/// Weakref to the first runtime we can find at init
|
|
var/datum/weakref/tracked_runtime_ref
|
|
///Determines the probability of a malfunction.
|
|
var/malfunction_probability_coeff = 0
|
|
///Keeps track of how many times we've had a critical reaction
|
|
var/malfunction_probability_coeff_modifier = 0
|
|
var/resetTime = 15
|
|
var/cloneMode = FALSE
|
|
var/list/item_reactions
|
|
var/static/list/valid_items //valid items for special reactions like transforming
|
|
var/list/critical_items_typecache //items that can cause critical reactions
|
|
|
|
/obj/machinery/rnd/experimentor/proc/valid_items()
|
|
RETURN_TYPE(/list)
|
|
|
|
if (isnull(valid_items))
|
|
generate_valid_items_and_item_reactions()
|
|
|
|
return valid_items
|
|
|
|
/obj/machinery/rnd/experimentor/proc/item_reactions()
|
|
RETURN_TYPE(/list)
|
|
|
|
if (isnull(item_reactions))
|
|
generate_valid_items_and_item_reactions()
|
|
|
|
return item_reactions
|
|
|
|
/obj/machinery/rnd/experimentor/proc/generate_valid_items_and_item_reactions()
|
|
var/static/list/banned_typecache = typecacheof(list(
|
|
/obj/item/stock_parts/power_store/cell/infinite,
|
|
/obj/item/grenade/chem_grenade/tuberculosis
|
|
))
|
|
|
|
item_reactions = list()
|
|
valid_items = list()
|
|
|
|
for(var/obj/item/item_path as anything in valid_subtypesof(/obj/item))
|
|
if(ispath(item_path, /obj/item/relic))
|
|
item_reactions["[item_path]"] = SCANTYPE_DISCOVER
|
|
else
|
|
item_reactions["[item_path]"] = pick(SCANTYPE_POKE,SCANTYPE_IRRADIATE,SCANTYPE_GAS,SCANTYPE_HEAT,SCANTYPE_COLD,SCANTYPE_OBLITERATE)
|
|
|
|
if(is_type_in_typecache(item_path, banned_typecache))
|
|
continue
|
|
|
|
if(ispath(item_path, /obj/item/stock_parts) || ispath(item_path, /obj/item/grenade/chem_grenade) || ispath(item_path, /obj/item/knife))
|
|
valid_items["[item_path]"] += 15
|
|
|
|
if(ispath(item_path, /obj/item/food))
|
|
valid_items["[item_path]"] += rand(1,4)
|
|
|
|
/obj/machinery/rnd/experimentor/Initialize(mapload)
|
|
. = ..()
|
|
|
|
tracked_ian_ref = WEAKREF(locate(/mob/living/basic/pet/dog/corgi/ian) in GLOB.mob_living_list)
|
|
tracked_runtime_ref = WEAKREF(locate(/mob/living/basic/pet/cat/runtime) in GLOB.mob_living_list)
|
|
|
|
critical_items_typecache = typecacheof(list(
|
|
/obj/item/construction/rcd,
|
|
/obj/item/grenade,
|
|
/obj/item/aicard,
|
|
/obj/item/slime_extract,
|
|
/obj/item/transfer_valve))
|
|
|
|
/obj/machinery/rnd/experimentor/RefreshParts()
|
|
. = ..()
|
|
malfunction_probability_coeff = malfunction_probability_coeff_modifier
|
|
resetTime = initial(resetTime)
|
|
for(var/datum/stock_part/servo/servo in component_parts)
|
|
resetTime = max(1, resetTime - servo.tier)
|
|
for(var/datum/stock_part/scanning_module/scanning_module in component_parts)
|
|
malfunction_probability_coeff += scanning_module.tier * 2
|
|
for(var/datum/stock_part/micro_laser/micro_laser in component_parts)
|
|
malfunction_probability_coeff += micro_laser.tier
|
|
|
|
/obj/machinery/rnd/experimentor/examine(mob/user)
|
|
. = ..()
|
|
if(in_range(user, src) || isobserver(user))
|
|
. += span_notice("The status display reads: Malfunction probability reduced by <b>[malfunction_probability_coeff]%</b>.<br>Cooldown interval between experiments at <b>[resetTime*0.1]</b> seconds.")
|
|
|
|
/obj/machinery/rnd/experimentor/attackby(obj/item/weapon, mob/living/user, list/modifiers, list/attack_modifiers)
|
|
if(user.combat_mode)
|
|
return ..()
|
|
if(!is_insertion_ready(user))
|
|
return ..()
|
|
if(!user.transferItemToLoc(weapon, src))
|
|
to_chat(user, span_warning("\The [weapon] is stuck to your hand, you cannot put it in \the [src]!"))
|
|
return TRUE
|
|
loaded_item = weapon
|
|
to_chat(user, span_notice("You add [weapon] to the machine."))
|
|
flick("h_lathe_load", src)
|
|
return TRUE
|
|
|
|
/obj/machinery/rnd/experimentor/default_deconstruction_crowbar(obj/item/O)
|
|
ejectItem()
|
|
return ..(O)
|
|
|
|
/obj/machinery/rnd/experimentor/ui_interact(mob/user, datum/tgui/ui)
|
|
ui = SStgui.try_update_ui(user, src, ui)
|
|
if(!ui)
|
|
ui = new (user, src, "Experimentator")
|
|
ui.open()
|
|
|
|
/obj/machinery/rnd/experimentor/ui_data(mob/user)
|
|
var/list/data = list()
|
|
|
|
data["hasItem"] = !!loaded_item
|
|
data["isOnCooldown"] = recentlyExperimented
|
|
data["isServerConnected"] = !!stored_research
|
|
|
|
if(!isnull(loaded_item))
|
|
var/list/item_data = list()
|
|
|
|
item_data["name"] = loaded_item.name
|
|
item_data["icon"] = icon2base64(getFlatIcon(loaded_item, no_anim = TRUE))
|
|
item_data["isRelic"] = istype(loaded_item, /obj/item/relic)
|
|
|
|
item_data["associatedNodes"] = list()
|
|
var/list/unlockable_nodes = techweb_item_unlock_check(loaded_item)
|
|
for(var/node_id in unlockable_nodes)
|
|
var/datum/techweb_node/node = SSresearch.techweb_node_by_id(node_id)
|
|
|
|
item_data["associatedNodes"] += list(list(
|
|
"name" = node.display_name,
|
|
"isUnlocked" = !(node_id in stored_research.hidden_nodes),
|
|
))
|
|
|
|
data["loadedItem"] = item_data
|
|
|
|
return data
|
|
|
|
/obj/machinery/rnd/experimentor/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
|
. = ..()
|
|
if(.)
|
|
return
|
|
|
|
switch(action)
|
|
if("eject")
|
|
ejectItem()
|
|
return TRUE
|
|
|
|
if("experiment")
|
|
var/reaction = text2num(params["id"])
|
|
if(isnull(reaction))
|
|
return
|
|
|
|
try_perform_experiment(reaction)
|
|
return TRUE
|
|
|
|
/obj/machinery/rnd/experimentor/proc/ejectItem(delete = FALSE)
|
|
if(isnull(loaded_item))
|
|
return
|
|
|
|
if(delete)
|
|
QDEL_NULL(loaded_item)
|
|
return
|
|
|
|
var/atom/drop_atom = get_step(src, EAST) || drop_location()
|
|
if(cloneMode)
|
|
visible_message(span_notice("A duplicate of \the [loaded_item] pops out!"))
|
|
new loaded_item.type(drop_atom)
|
|
cloneMode = FALSE
|
|
return
|
|
|
|
loaded_item.forceMove(drop_atom)
|
|
loaded_item = null
|
|
|
|
/obj/machinery/rnd/experimentor/proc/match_reaction(obj/item/matching, target_reaction)
|
|
PRIVATE_PROC(TRUE)
|
|
if(isnull(matching) || isnull(target_reaction))
|
|
return FAIL
|
|
|
|
var/list/item_reactions = item_reactions()
|
|
if("[matching.type]" in item_reactions)
|
|
var/associated_reaction = item_reactions["[matching.type]"]
|
|
if(associated_reaction == target_reaction)
|
|
return associated_reaction
|
|
|
|
return FAIL
|
|
|
|
/obj/machinery/rnd/experimentor/proc/try_perform_experiment(reaction)
|
|
PRIVATE_PROC(TRUE)
|
|
if(isnull(stored_research))
|
|
return
|
|
|
|
if(recentlyExperimented)
|
|
return
|
|
|
|
if(isnull(loaded_item))
|
|
return
|
|
|
|
if(reaction != SCANTYPE_DISCOVER)
|
|
reaction = match_reaction(loaded_item, reaction)
|
|
|
|
if(reaction != FAIL)
|
|
var/picked_node_id = pick(techweb_item_unlock_check(loaded_item))
|
|
stored_research.unhide_node(SSresearch.techweb_node_by_id(picked_node_id))
|
|
|
|
experiment(reaction, loaded_item)
|
|
use_energy(750 JOULES)
|
|
|
|
/obj/machinery/rnd/experimentor/proc/throwSmoke(turf/where)
|
|
do_smoke(0, src, where)
|
|
|
|
/obj/machinery/rnd/experimentor/proc/experiment(exp,obj/item/exp_on)
|
|
recentlyExperimented = 1
|
|
icon_state = "[base_icon_state]_wloop"
|
|
var/chosenchem
|
|
var/criticalReaction = is_type_in_typecache(exp_on, critical_items_typecache)
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
if(exp == SCANTYPE_POKE)
|
|
visible_message(span_notice("[src] prods at [exp_on] with mechanical arms."))
|
|
if(prob(EFFECT_PROB_LOW) && criticalReaction)
|
|
visible_message(span_notice("[exp_on] is gripped in just the right way, enhancing its focus."))
|
|
malfunction_probability_coeff_modifier++
|
|
RefreshParts() //recalculate malfunction_probability_coeff
|
|
else if(prob(EFFECT_PROB_VERYLOW * (100 - malfunction_probability_coeff) * 0.01))
|
|
visible_message(span_danger("[src] malfunctions and destroys [exp_on], lashing its arms out at nearby people!"))
|
|
for(var/mob/living/m in oview(1, src))
|
|
m.apply_damage(15, BRUTE, pick(BODY_ZONE_HEAD,BODY_ZONE_CHEST,BODY_ZONE_CHEST))
|
|
investigate_log("Experimentor dealt minor brute to [m].", INVESTIGATE_EXPERIMENTOR)
|
|
ejectItem(TRUE)
|
|
else if(prob(EFFECT_PROB_LOW * (100 - malfunction_probability_coeff) * 0.01))
|
|
visible_message(span_warning("[src] malfunctions!"))
|
|
exp = SCANTYPE_OBLITERATE
|
|
else if(prob(EFFECT_PROB_MEDIUM * (100 - malfunction_probability_coeff) * 0.01))
|
|
visible_message(span_danger("[src] malfunctions, throwing the [exp_on]!"))
|
|
var/mob/living/target = locate(/mob/living) in oview(7,src)
|
|
if(target)
|
|
var/obj/item/throwing = loaded_item
|
|
investigate_log("Experimentor has thrown [loaded_item] at [key_name(target)]", INVESTIGATE_EXPERIMENTOR)
|
|
ejectItem()
|
|
if(throwing)
|
|
throwing.throw_at(target, 10, 1)
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
if(exp == SCANTYPE_IRRADIATE)
|
|
visible_message(span_danger("[src] reflects radioactive rays at [exp_on]!"))
|
|
if(prob(EFFECT_PROB_VERYLOW) && criticalReaction)
|
|
visible_message(span_notice("[exp_on] has activated an unknown subroutine!"))
|
|
cloneMode = TRUE
|
|
investigate_log("Experimentor has made a clone of [exp_on]", INVESTIGATE_EXPERIMENTOR)
|
|
ejectItem()
|
|
else if(prob(EFFECT_PROB_VERYLOW * (100 - malfunction_probability_coeff) * 0.01))
|
|
visible_message(span_danger("[src] malfunctions, melting [exp_on] and leaking radiation!"))
|
|
radiation_pulse(src, max_range = 6, threshold = 0.3)
|
|
ejectItem(TRUE)
|
|
else if(prob(EFFECT_PROB_LOW * (100 - malfunction_probability_coeff) * 0.01))
|
|
visible_message(span_warning("[src] malfunctions, spewing toxic waste!"))
|
|
for(var/turf/T in oview(1, src))
|
|
if(!T.density)
|
|
if(prob(EFFECT_PROB_VERYHIGH) && !(locate(/obj/effect/decal/cleanable/greenglow) in T))
|
|
var/obj/effect/decal/cleanable/reagentdecal = new/obj/effect/decal/cleanable/greenglow(T)
|
|
reagentdecal.reagents.add_reagent(/datum/reagent/uranium/radium, 7)
|
|
ejectItem(TRUE)
|
|
else if(prob(EFFECT_PROB_MEDIUM * (100 - malfunction_probability_coeff) * 0.01))
|
|
var/savedName = "[exp_on]"
|
|
ejectItem(TRUE)
|
|
var/newPath = text2path(pick_weight(valid_items()))
|
|
loaded_item = new newPath(src)
|
|
visible_message(span_warning("[src] malfunctions, transforming [savedName] into [loaded_item]!"))
|
|
investigate_log("Experimentor has transformed [savedName] into [loaded_item]", INVESTIGATE_EXPERIMENTOR)
|
|
if(istype(loaded_item, /obj/item/grenade/chem_grenade))
|
|
var/obj/item/grenade/chem_grenade/CG = loaded_item
|
|
CG.detonate()
|
|
ejectItem()
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
if(exp == SCANTYPE_GAS)
|
|
visible_message(span_warning("[src] fills its chamber with gas, [exp_on] included."))
|
|
if(prob(EFFECT_PROB_LOW) && criticalReaction)
|
|
visible_message(span_notice("[exp_on] achieves the perfect mix!"))
|
|
new /obj/item/stack/sheet/mineral/plasma(get_turf(pick(oview(1,src))))
|
|
else if(prob(EFFECT_PROB_VERYLOW * (100 - malfunction_probability_coeff) * 0.01))
|
|
visible_message(span_danger("[src] destroys [exp_on], leaking dangerous gas!"))
|
|
chosenchem = pick(/datum/reagent/carbon,/datum/reagent/uranium/radium,/datum/reagent/toxin,/datum/reagent/consumable/condensedcapsaicin,/datum/reagent/drug/mushroomhallucinogen,/datum/reagent/drug/space_drugs,/datum/reagent/consumable/ethanol,/datum/reagent/consumable/ethanol/beepsky_smash)
|
|
do_chem_smoke(0, src, loc, chosenchem, 50)
|
|
investigate_log("Experimentor has released [chosenchem] smoke.", INVESTIGATE_EXPERIMENTOR)
|
|
playsound(src, 'sound/effects/smoke.ogg', 50, TRUE, -3)
|
|
ejectItem(TRUE)
|
|
else if(prob(EFFECT_PROB_VERYLOW * (100 - malfunction_probability_coeff) * 0.01))
|
|
visible_message(span_danger("[src]'s chemical chamber has sprung a leak!"))
|
|
chosenchem = pick(/datum/reagent/mutationtoxin/classic,/datum/reagent/cyborg_mutation_nanomachines,/datum/reagent/toxin/acid)
|
|
do_chem_smoke(0, src, loc, chosenchem, 50)
|
|
playsound(src, 'sound/effects/smoke.ogg', 50, TRUE, -3)
|
|
ejectItem(TRUE)
|
|
warn_admins(usr, "[chosenchem] smoke")
|
|
investigate_log("Experimentor has released <font color='red'>[chosenchem]</font> smoke!", INVESTIGATE_EXPERIMENTOR)
|
|
else if(prob(EFFECT_PROB_LOW * (100 - malfunction_probability_coeff) * 0.01))
|
|
visible_message(span_warning("[src] malfunctions, spewing harmless gas."))
|
|
throwSmoke(loc)
|
|
else if(prob(EFFECT_PROB_MEDIUM * (100 - malfunction_probability_coeff) * 0.01))
|
|
visible_message(span_warning("[src] melts [exp_on], ionizing the air around it!"))
|
|
empulse(loc, 4, 6, emp_source = src)
|
|
investigate_log("Experimentor has generated an Electromagnetic Pulse.", INVESTIGATE_EXPERIMENTOR)
|
|
ejectItem(TRUE)
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
if(exp == SCANTYPE_HEAT)
|
|
visible_message(span_notice("[src] raises [exp_on]'s temperature."))
|
|
if(prob(EFFECT_PROB_LOW) && criticalReaction)
|
|
visible_message(span_warning("[src]'s emergency coolant system gives off a small ding!"))
|
|
playsound(src, 'sound/machines/ding.ogg', 50, TRUE)
|
|
var/obj/item/reagent_containers/cup/glass/coffee/C = new /obj/item/reagent_containers/cup/glass/coffee(get_turf(pick(oview(1,src))))
|
|
chosenchem = pick(/datum/reagent/toxin/plasma,/datum/reagent/consumable/capsaicin,/datum/reagent/consumable/ethanol)
|
|
C.reagents.remove_all(25)
|
|
C.reagents.add_reagent(chosenchem , 50)
|
|
C.name = "Cup of Suspicious Liquid"
|
|
C.desc = "It has a large hazard symbol printed on the side in fading ink."
|
|
investigate_log("Experimentor has made a cup of [chosenchem] coffee.", INVESTIGATE_EXPERIMENTOR)
|
|
else if(prob(EFFECT_PROB_VERYLOW * (100 - malfunction_probability_coeff) * 0.01))
|
|
var/turf/start = get_turf(src)
|
|
var/mob/M = locate(/mob/living) in view(src, 3)
|
|
var/turf/MT = get_turf(M)
|
|
if(MT)
|
|
visible_message(span_danger("[src] dangerously overheats, launching a flaming fuel orb!"))
|
|
investigate_log("Experimentor has launched a <font color='red'>fireball</font> at [M]!", INVESTIGATE_EXPERIMENTOR)
|
|
var/obj/projectile/magic/fireball/FB = new /obj/projectile/magic/fireball(start)
|
|
FB.aim_projectile(MT, start)
|
|
FB.fire()
|
|
else if(prob(EFFECT_PROB_LOW * (100 - malfunction_probability_coeff) * 0.01))
|
|
visible_message(span_danger("[src] malfunctions, melting [exp_on] and releasing a burst of flame!"))
|
|
explosion(src, devastation_range = -1, flame_range = 2, adminlog = FALSE)
|
|
investigate_log("Experimentor started a fire.", INVESTIGATE_EXPERIMENTOR)
|
|
ejectItem(TRUE)
|
|
else if(prob(EFFECT_PROB_MEDIUM * (100 - malfunction_probability_coeff) * 0.01))
|
|
visible_message(span_warning("[src] malfunctions, melting [exp_on] and leaking hot air!"))
|
|
var/datum/gas_mixture/env = loc.return_air()
|
|
if(env)
|
|
var/heat_capacity = max(env.heat_capacity(), 1)
|
|
env.temperature = min((env.temperature * heat_capacity + 100000) / heat_capacity, 1000)
|
|
air_update_turf(FALSE, FALSE)
|
|
investigate_log("Experimentor has released hot air.", INVESTIGATE_EXPERIMENTOR)
|
|
ejectItem(TRUE)
|
|
else if(prob(EFFECT_PROB_MEDIUM * (100 - malfunction_probability_coeff) * 0.01))
|
|
visible_message(span_warning("[src] malfunctions, activating its emergency coolant systems!"))
|
|
throwSmoke(loc)
|
|
for(var/mob/living/m in oview(1, src))
|
|
m.apply_damage(5, BURN, pick(BODY_ZONE_HEAD,BODY_ZONE_CHEST,BODY_ZONE_CHEST))
|
|
investigate_log("Experimentor has dealt minor burn damage to [key_name(m)]", INVESTIGATE_EXPERIMENTOR)
|
|
ejectItem()
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
if(exp == SCANTYPE_COLD)
|
|
visible_message(span_notice("[src] lowers [exp_on]'s temperature."))
|
|
if(prob(EFFECT_PROB_LOW) && criticalReaction)
|
|
visible_message(span_warning("[src]'s emergency coolant system gives off a small ding!"))
|
|
var/obj/item/reagent_containers/cup/glass/coffee/C = new /obj/item/reagent_containers/cup/glass/coffee(get_turf(pick(oview(1,src))))
|
|
playsound(src, 'sound/machines/ding.ogg', 50, TRUE) //Ding! Your death coffee is ready!
|
|
chosenchem = pick(/datum/reagent/uranium,/datum/reagent/consumable/frostoil,/datum/reagent/medicine/ephedrine)
|
|
C.reagents.remove_all(25)
|
|
C.reagents.add_reagent(chosenchem , 50)
|
|
C.name = "Cup of Suspicious Liquid"
|
|
C.desc = "It has a large hazard symbol printed on the side in fading ink."
|
|
investigate_log("Experimentor has made a cup of [chosenchem] coffee.", INVESTIGATE_EXPERIMENTOR)
|
|
else if(prob(EFFECT_PROB_VERYLOW * (100 - malfunction_probability_coeff) * 0.01))
|
|
visible_message(span_danger("[src] malfunctions, shattering [exp_on] and releasing a dangerous cloud of coolant!"))
|
|
do_chem_smoke(0, src, loc, /datum/reagent/consumable/frostoil, 50)
|
|
investigate_log("Experimentor has released frostoil gas.", INVESTIGATE_EXPERIMENTOR)
|
|
playsound(src, 'sound/effects/smoke.ogg', 50, TRUE, -3)
|
|
ejectItem(TRUE)
|
|
else if(prob(EFFECT_PROB_LOW * (100 - malfunction_probability_coeff) * 0.01))
|
|
visible_message(span_warning("[src] malfunctions, shattering [exp_on] and leaking cold air!"))
|
|
var/datum/gas_mixture/env = loc.return_air()
|
|
if(env)
|
|
var/heat_capacity = max(env.heat_capacity(), 1)
|
|
env.temperature = max((env.temperature * heat_capacity - 75000) / heat_capacity, TCMB)
|
|
air_update_turf(FALSE, FALSE)
|
|
investigate_log("Experimentor has released cold air.", INVESTIGATE_EXPERIMENTOR)
|
|
ejectItem(TRUE)
|
|
else if(prob(EFFECT_PROB_MEDIUM * (100 - malfunction_probability_coeff) * 0.01))
|
|
visible_message(span_warning("[src] malfunctions, releasing a flurry of chilly air as [exp_on] pops out!"))
|
|
do_smoke(0, src, loc)
|
|
ejectItem()
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
if(exp == SCANTYPE_OBLITERATE)
|
|
visible_message(span_warning("[exp_on] activates the crushing mechanism, [exp_on] is destroyed!"))
|
|
if(prob(EFFECT_PROB_LOW) && criticalReaction)
|
|
visible_message(span_warning("[src]'s crushing mechanism slowly and smoothly descends, flattening the [exp_on]!"))
|
|
new /obj/item/stack/sheet/plasteel(get_turf(pick(oview(1,src))))
|
|
else if(prob(EFFECT_PROB_VERYLOW * (100 - malfunction_probability_coeff) * 0.01))
|
|
visible_message(span_danger("[src]'s crusher goes way too many levels too high, crushing right through space-time!"))
|
|
playsound(src, 'sound/effects/supermatter.ogg', 50, TRUE, -3)
|
|
investigate_log("Experimentor has triggered the 'throw things' reaction.", INVESTIGATE_EXPERIMENTOR)
|
|
for(var/atom/movable/AM in oview(7,src))
|
|
if(!AM.anchored)
|
|
AM.throw_at(src,10,1)
|
|
else if(prob(EFFECT_PROB_LOW * (100 - malfunction_probability_coeff) * 0.01))
|
|
visible_message(span_danger("[src]'s crusher goes one level too high, crushing right into space-time!"))
|
|
playsound(src, 'sound/effects/supermatter.ogg', 50, TRUE, -3)
|
|
investigate_log("Experimentor has triggered the 'minor throw things' reaction.", INVESTIGATE_EXPERIMENTOR)
|
|
var/list/throwAt = list()
|
|
for(var/atom/movable/AM in oview(7,src))
|
|
if(!AM.anchored)
|
|
throwAt.Add(AM)
|
|
for(var/counter in 1 to throwAt.len)
|
|
var/atom/movable/cast = throwAt[counter]
|
|
cast.throw_at(pick(throwAt),10,1)
|
|
ejectItem(TRUE)
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
if(exp == FAIL)
|
|
var/a = pick("rumbles","shakes","vibrates","shudders","honks")
|
|
var/b = pick("crushes","spins","viscerates","smashes","insults")
|
|
visible_message(span_warning("[exp_on] [a], and [b], the experiment was a failure."))
|
|
|
|
if(exp == SCANTYPE_DISCOVER)
|
|
visible_message(span_notice("[src] scans the [exp_on], revealing its true nature!"))
|
|
playsound(src, 'sound/effects/supermatter.ogg', 50, 3, -1)
|
|
var/obj/item/relic/loaded_artifact = loaded_item
|
|
loaded_artifact.reveal()
|
|
investigate_log("Experimentor has revealed a relic with [span_danger("[loaded_artifact.hidden_power]")] effect.", INVESTIGATE_EXPERIMENTOR)
|
|
ejectItem()
|
|
|
|
//Global reactions
|
|
if(prob(EFFECT_PROB_VERYLOW * (100 - malfunction_probability_coeff) * 0.01) && loaded_item)
|
|
var/globalMalf = rand(1,100)
|
|
if(globalMalf < 15)
|
|
visible_message(span_warning("[src]'s onboard detection system has malfunctioned!"))
|
|
item_reactions()["[exp_on.type]"] = pick(SCANTYPE_POKE,SCANTYPE_IRRADIATE,SCANTYPE_GAS,SCANTYPE_HEAT,SCANTYPE_COLD,SCANTYPE_OBLITERATE)
|
|
ejectItem()
|
|
if(globalMalf > 16 && globalMalf < 35)
|
|
visible_message(span_warning("[src] melts [exp_on], ian-izing the air around it!"))
|
|
throwSmoke(loc)
|
|
var/mob/living/tracked_ian = tracked_ian_ref?.resolve()
|
|
if(tracked_ian)
|
|
throwSmoke(tracked_ian.loc)
|
|
tracked_ian.forceMove(loc)
|
|
investigate_log("Experimentor has stolen Ian!", INVESTIGATE_EXPERIMENTOR) //...if anyone ever fixes it...
|
|
else
|
|
new /mob/living/basic/pet/dog/corgi(loc)
|
|
investigate_log("Experimentor has spawned a new corgi.", INVESTIGATE_EXPERIMENTOR)
|
|
ejectItem(TRUE)
|
|
if(globalMalf > 36 && globalMalf < 50)
|
|
visible_message(span_warning("Experimentor draws the life essence of those nearby!"))
|
|
for(var/mob/living/m in view(4,src))
|
|
to_chat(m, span_danger("You feel your flesh being torn from you, mists of blood drifting to [src]!"))
|
|
m.apply_damage(50, BRUTE, BODY_ZONE_CHEST)
|
|
investigate_log("Experimentor has taken 50 brute a blood sacrifice from [m]", INVESTIGATE_EXPERIMENTOR)
|
|
if(globalMalf > 51 && globalMalf < 75)
|
|
visible_message(span_warning("[src] encounters a run-time error!"))
|
|
throwSmoke(loc)
|
|
var/mob/living/tracked_runtime = tracked_runtime_ref?.resolve()
|
|
if(tracked_runtime)
|
|
throwSmoke(tracked_runtime.loc)
|
|
tracked_runtime.forceMove(drop_location())
|
|
investigate_log("Experimentor has stolen Runtime!", INVESTIGATE_EXPERIMENTOR)
|
|
else
|
|
new /mob/living/basic/pet/cat(loc)
|
|
investigate_log("Experimentor failed to steal runtime, and instead spawned a new cat.", INVESTIGATE_EXPERIMENTOR)
|
|
ejectItem(TRUE)
|
|
if(globalMalf > 76 && globalMalf < 98)
|
|
visible_message(span_warning("[src] begins to smoke and hiss, shaking violently!"))
|
|
use_energy(500 KILO JOULES)
|
|
investigate_log("Experimentor has drained power from its APC", INVESTIGATE_EXPERIMENTOR)
|
|
if(globalMalf == 99)
|
|
visible_message(span_warning("[src] begins to glow and vibrate. It's going to blow!"))
|
|
addtimer(CALLBACK(src, PROC_REF(boom)), 5 SECONDS)
|
|
if(globalMalf == 100)
|
|
visible_message(span_warning("[src] begins to glow and vibrate. It's going to blow!"))
|
|
addtimer(CALLBACK(src, PROC_REF(honk)), 5 SECONDS)
|
|
|
|
addtimer(CALLBACK(src, PROC_REF(reset_exp)), resetTime)
|
|
|
|
/obj/machinery/rnd/experimentor/proc/boom()
|
|
explosion(src, devastation_range = 1, heavy_impact_range = 5, light_impact_range = 10, flash_range = 5, adminlog = TRUE)
|
|
|
|
/obj/machinery/rnd/experimentor/proc/honk()
|
|
playsound(src, 'sound/items/bikehorn.ogg', 500)
|
|
new /obj/item/grown/bananapeel(loc)
|
|
|
|
/obj/machinery/rnd/experimentor/proc/reset_exp()
|
|
update_appearance()
|
|
recentlyExperimented = FALSE
|
|
|
|
/obj/machinery/rnd/experimentor/update_icon_state()
|
|
icon_state = base_icon_state
|
|
return ..()
|
|
|
|
/obj/machinery/rnd/experimentor/proc/warn_admins(user, ReactionName)
|
|
var/turf/T = get_turf(user)
|
|
message_admins("Experimentor reaction: [ReactionName] generated by [ADMIN_LOOKUPFLW(user)] at [ADMIN_VERBOSEJMP(T)]")
|
|
log_game("Experimentor reaction: [ReactionName] generated by [key_name(user)] in [AREACOORD(T)]")
|
|
|
|
#undef SCANTYPE_POKE
|
|
#undef SCANTYPE_IRRADIATE
|
|
#undef SCANTYPE_GAS
|
|
#undef SCANTYPE_HEAT
|
|
#undef SCANTYPE_COLD
|
|
#undef SCANTYPE_OBLITERATE
|
|
#undef SCANTYPE_DISCOVER
|
|
|
|
#undef EFFECT_PROB_VERYLOW
|
|
#undef EFFECT_PROB_LOW
|
|
#undef EFFECT_PROB_MEDIUM
|
|
#undef EFFECT_PROB_HIGH
|
|
#undef EFFECT_PROB_VERYHIGH
|
|
|
|
#undef FAIL
|
|
|
|
|
|
// Relic \\
|
|
|
|
/obj/item/relic
|
|
name = "strange object"
|
|
desc = "What mysteries could this hold? Maybe Research & Development could find out."
|
|
icon = 'icons/obj/devices/artefacts.dmi'
|
|
icon_state = "debug_artefact"
|
|
//The name this artefact will have when it's activated.
|
|
var/real_name = "artefact"
|
|
//Has this artefact been activated?
|
|
var/activated = FALSE
|
|
//What effect this artefact has when used. Randomly determined when activated.
|
|
var/hidden_power
|
|
//Minimum possible cooldown.
|
|
var/min_cooldown = 6 SECONDS
|
|
//Max possible cooldown.
|
|
var/max_cooldown = 30 SECONDS
|
|
//Cooldown length. Randomly determined at activation if it isn't determined here.
|
|
var/cooldown_timer
|
|
COOLDOWN_DECLARE(cooldown)
|
|
//What visual theme this artefact has. Current possible choices: "prototype", "necrotech"
|
|
var/artifact_theme = "prototype"
|
|
|
|
/obj/item/relic/Initialize(mapload)
|
|
. = ..()
|
|
random_themed_appearance()
|
|
|
|
/obj/item/relic/proc/random_themed_appearance()
|
|
var/themed_name_prefix
|
|
var/themed_name_suffix
|
|
if(artifact_theme == "prototype")
|
|
icon_state = pick("prototype1", "prototype2", "prototype3", "prototype4", "prototype5", "prototype6", "prototype7", "prototype8","prototype9")
|
|
themed_name_prefix = pick("experimental","prototype","artificial","handcrafted","ramshackle","odd")
|
|
themed_name_suffix = pick("device","assembly","gadget","gizmo","contraption","machine","widget","object")
|
|
real_name = "[pick(themed_name_prefix)] [pick(themed_name_suffix)]"
|
|
name = "strange [pick(themed_name_suffix)]"
|
|
if(artifact_theme == "necrotech")
|
|
icon_state = pick("necrotech1", "necrotech2", "necrotech3", "necrotech4", "necrotech5", "necrotech6")
|
|
themed_name_prefix = pick("dark","bloodied","unholy","archeotechnological","dismal","ruined","thrumming")
|
|
themed_name_suffix = pick("instrument","shard","fetish","bibelot","trinket","offering","relic")
|
|
real_name = "[pick(themed_name_prefix)] [pick(themed_name_suffix)]"
|
|
name = "strange relic"
|
|
update_appearance()
|
|
|
|
/obj/item/relic/lavaland
|
|
name = "strange relic"
|
|
artifact_theme = "necrotech"
|
|
|
|
/obj/item/relic/proc/reveal()
|
|
if(activated) //no rerolling
|
|
return
|
|
activated = TRUE
|
|
name = real_name
|
|
if(!cooldown_timer)
|
|
cooldown_timer = rand(min_cooldown, max_cooldown)
|
|
if(!hidden_power)
|
|
hidden_power = pick(
|
|
PROC_REF(corgi_cannon),
|
|
PROC_REF(cleaning_foam),
|
|
PROC_REF(flashbanger),
|
|
PROC_REF(summon_animals),
|
|
PROC_REF(uncontrolled_teleport),
|
|
PROC_REF(heat_and_explode),
|
|
PROC_REF(rapid_self_dupe),
|
|
PROC_REF(drink_dispenser),
|
|
PROC_REF(tummy_ache),
|
|
PROC_REF(charger),
|
|
PROC_REF(hugger),
|
|
PROC_REF(dimensional_shift),
|
|
PROC_REF(disguiser),
|
|
)
|
|
|
|
/obj/item/relic/attack_self(mob/user)
|
|
if(!activated)
|
|
to_chat(user, span_notice("You aren't quite sure what this is. Maybe R&D knows what to do with it?"))
|
|
return
|
|
if(!COOLDOWN_FINISHED(src, cooldown))
|
|
to_chat(user, span_warning("[src] does not react!"))
|
|
return
|
|
if(loc != user)
|
|
return
|
|
COOLDOWN_START(src, cooldown, cooldown_timer)
|
|
call(src, hidden_power)(user)
|
|
|
|
/obj/item/relic/proc/throw_smoke(turf/where)
|
|
do_smoke(0, src, get_turf(where))
|
|
|
|
// Artefact Powers \\
|
|
|
|
/obj/item/relic/proc/corgi_cannon(mob/user)
|
|
playsound(src, SFX_SPARKS, rand(25,50), TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
|
|
var/mob/living/basic/pet/dog/corgi/sad_corgi = new(get_turf(user))
|
|
sad_corgi.throw_at(pick(oview(10,user)), 10, rand(3,8), callback = CALLBACK(src, PROC_REF(throw_smoke), sad_corgi))
|
|
warn_admins(user, "Corgi Cannon", 0)
|
|
|
|
/obj/item/relic/proc/cleaning_foam(mob/user)
|
|
playsound(src, SFX_SPARKS, rand(25,50), TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
|
|
var/obj/item/grenade/chem_grenade/cleaner/spawned_foamer = new/obj/item/grenade/chem_grenade/cleaner(get_turf(user))
|
|
spawned_foamer.detonate()
|
|
qdel(spawned_foamer)
|
|
warn_admins(user, "Foam", 0)
|
|
|
|
/obj/item/relic/proc/flashbanger(mob/user)
|
|
playsound(src, SFX_SPARKS, rand(25,50), TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
|
|
var/obj/item/grenade/flashbang/spawned_flashbang = new/obj/item/grenade/flashbang(user.loc)
|
|
spawned_flashbang.detonate()
|
|
warn_admins(user, "Flash")
|
|
|
|
/obj/item/relic/proc/summon_animals(mob/user)
|
|
var/message = span_danger("[src] begins to shake, and in the distance the sound of rampaging animals arises!")
|
|
visible_message(message)
|
|
to_chat(user, message)
|
|
var/static/list/valid_animals = list(
|
|
/mob/living/basic/bear,
|
|
/mob/living/basic/bee,
|
|
/mob/living/basic/butterfly,
|
|
/mob/living/basic/carp,
|
|
/mob/living/basic/crab,
|
|
/mob/living/basic/lizard,
|
|
/mob/living/basic/mouse,
|
|
/mob/living/basic/parrot,
|
|
/mob/living/basic/pet/cat,
|
|
/mob/living/basic/pet/dog/corgi,
|
|
/mob/living/basic/pet/dog/pug,
|
|
/mob/living/basic/pet/fox,
|
|
)
|
|
for(var/counter in 1 to rand(1, 25))
|
|
var/animal_spawn = pick(valid_animals)
|
|
var/mob/living/animal = new animal_spawn(get_turf(src))
|
|
ADD_TRAIT(animal, TRAIT_SPAWNED_MOB, INNATE_TRAIT)
|
|
warn_admins(user, "Mass Mob Spawn")
|
|
if(prob(60))
|
|
to_chat(user, span_warning("[src] falls apart!"))
|
|
qdel(src)
|
|
|
|
/obj/item/relic/proc/rapid_self_dupe(mob/user)
|
|
audible_message("[src] emits a loud pop!")
|
|
var/list/dummy_artifacts = list()
|
|
for(var/counter in 1 to rand(5,10))
|
|
var/obj/item/relic/duped = new type(get_turf(src))
|
|
duped.name = name
|
|
duped.desc = desc
|
|
duped.real_name = real_name
|
|
duped.hidden_power = hidden_power
|
|
duped.activated = TRUE
|
|
dummy_artifacts += duped
|
|
duped.throw_at(pick(oview(7,get_turf(src))),10,1)
|
|
QDEL_LIST_IN(dummy_artifacts, rand(1 SECONDS, 10 SECONDS))
|
|
warn_admins(user, "Rapid duplicator", 0)
|
|
|
|
/obj/item/relic/proc/heat_and_explode(mob/user)
|
|
to_chat(user, span_danger("[src] begins to heat up!"))
|
|
addtimer(CALLBACK(src, PROC_REF(blow_up), user), rand(3.5 SECONDS, 10 SECONDS))
|
|
|
|
/obj/item/relic/proc/blow_up(mob/user)
|
|
if(loc == user)
|
|
visible_message(span_notice("\The [src]'s top opens, releasing a powerful blast!"))
|
|
explosion(src, heavy_impact_range = rand(1,5), light_impact_range = rand(1,5), flame_range = 2, flash_range = rand(1,5), adminlog = TRUE)
|
|
warn_admins(user, "Explosion")
|
|
qdel(src) //Comment this line to produce a light grenade (the bomb that keeps on exploding when used)!!
|
|
|
|
/obj/item/relic/proc/uncontrolled_teleport(mob/user)
|
|
to_chat(user, span_notice("[src] begins to vibrate!"))
|
|
addtimer(CALLBACK(src, PROC_REF(do_the_teleport), user), rand(1 SECONDS, 3 SECONDS))
|
|
|
|
/obj/item/relic/proc/do_the_teleport(mob/user)
|
|
var/turf/userturf = get_turf(user)
|
|
//Because Nuke Ops bringing this back on their shuttle, then looting the ERT area is 2fun4you!
|
|
if(is_centcom_level(userturf.z))
|
|
return
|
|
var/to_teleport = ismovable(loc) ? loc : src
|
|
visible_message(span_notice("[to_teleport] twists and bends, relocating itself!"))
|
|
throw_smoke(get_turf(to_teleport))
|
|
do_teleport(to_teleport, userturf, 8, asoundin = 'sound/effects/phasein.ogg', channel = TELEPORT_CHANNEL_BLUESPACE)
|
|
throw_smoke(get_turf(to_teleport))
|
|
warn_admins(user, "Teleport", 0)
|
|
|
|
// Creates a glass and fills it up with a drink.
|
|
/obj/item/relic/proc/drink_dispenser(mob/user)
|
|
var/obj/item/reagent_containers/cup/glass/drinkingglass/freebie = new(get_step_rand(user))
|
|
playsound(freebie, SFX_SPARKS, rand(25,50), TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
|
|
do_sparks(5, TRUE, src, src)
|
|
addtimer(CALLBACK(src, PROC_REF(dispense_drink), freebie), 0.5 SECONDS)
|
|
|
|
/obj/item/relic/proc/dispense_drink(obj/item/reagent_containers/cup/glass/glasser)
|
|
playsound(glasser, 'sound/effects/phasein.ogg', rand(25,50), TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
|
|
glasser.reagents.add_reagent(get_random_drink_id(), rand(glasser.volume * 0.3, glasser.volume))
|
|
throw_smoke(get_turf(glasser))
|
|
|
|
// Scrambles your organs. 33% chance to delete after use.
|
|
/obj/item/relic/proc/tummy_ache(mob/user)
|
|
new /obj/effect/temp_visual/circle_wave/bioscrambler/light(get_turf(src))
|
|
to_chat(user, span_notice("Your stomach starts growling..."))
|
|
addtimer(CALLBACK(src, PROC_REF(scrambliticus), user), rand(1 SECONDS, 3 SECONDS)) // throw it away!
|
|
|
|
/obj/item/relic/proc/scrambliticus(mob/user)
|
|
new /obj/effect/temp_visual/circle_wave/bioscrambler/light(get_turf(src))
|
|
playsound(src, 'sound/effects/magic/cosmic_energy.ogg', vol = 50, vary = TRUE)
|
|
for(var/mob/living/carbon/nearby in range(2, get_turf(src))) //needs get_turf() to work
|
|
nearby.bioscramble(name)
|
|
playsound(nearby, SFX_SPARKS, rand(25,50), TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
|
|
throw_smoke(get_turf(nearby))
|
|
to_chat(nearby, span_notice("You feel weird."))
|
|
if(prob(33))
|
|
qdel(src)
|
|
|
|
// Charges an item or two in your inventory. Also yourself.
|
|
/obj/item/relic/proc/charger(mob/living/user)
|
|
to_chat(user, span_danger("You're recharged!"))
|
|
var/stunner = 1.25 SECONDS
|
|
if(iscarbon(user))
|
|
var/mob/living/carbon/carboner = user
|
|
carboner.electrocute_act(15, src, flags = SHOCK_NOGLOVES, stun_duration = stunner)
|
|
else
|
|
user.electrocute_act(15, src, flags = SHOCK_NOGLOVES)
|
|
playsound(user, SFX_SPARKS, rand(25,50), TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
|
|
|
|
var/list/chargeable_items = user.get_all_cells(max_percent = 0.95) // otherwise the PDA always gets recharged
|
|
|
|
lightning_fx(user, stunner)
|
|
var/recharges = rand(1, 2)
|
|
if(!length(chargeable_items))
|
|
to_chat(user, span_notice("You have a strange feeling for a moment, but then it passes."))
|
|
return
|
|
while(length(chargeable_items) && recharges)
|
|
recharges--
|
|
var/obj/item/to_charge_base = pick_n_take(chargeable_items)
|
|
var/obj/item/stock_parts/power_store/to_charge = chargeable_items[to_charge_base]
|
|
to_charge.charge = to_charge.maxcharge
|
|
to_charge_base.update_appearance(UPDATE_ICON|UPDATE_OVERLAYS)
|
|
to_chat(user, span_notice("[to_charge_base] feels energized!"))
|
|
lightning_fx(to_charge_base, 0.8 SECONDS)
|
|
|
|
/obj/item/relic/proc/lightning_fx(atom/shocker, time)
|
|
var/lightning = mutable_appearance('icons/effects/effects.dmi', "electricity3", layer = ABOVE_MOB_LAYER)
|
|
shocker.add_overlay(lightning)
|
|
addtimer(CALLBACK(src, PROC_REF(cut_the_overlay), shocker, lightning), time)
|
|
|
|
/obj/item/relic/proc/cut_the_overlay(atom/shocker, lightning)
|
|
shocker.cut_overlay(lightning)
|
|
|
|
// Hugs/shakes everyone in range!
|
|
/obj/item/relic/proc/hugger(mob/user)
|
|
var/list/mob/living/carbon/huggeds = oviewers(3, user)
|
|
for(var/mob/living/carbon/victim in huggeds)
|
|
victim.help_shake_act(user, force_friendly = TRUE)
|
|
new /obj/effect/temp_visual/heart(victim.loc)
|
|
if(length(huggeds))
|
|
to_chat(user, span_nicegreen("You feel friendly!"))
|
|
else
|
|
to_chat(user, pick(span_notice("You hug yourself, for some reason."), span_notice("You have a strange feeling for a moment, but then it passes.")))
|
|
|
|
// Converts a 3x3 area into a random dimensional theme.
|
|
/obj/item/relic/proc/dimensional_shift(mob/user)
|
|
var/new_theme_path = pick(subtypesof(/datum/dimension_theme))
|
|
var/datum/dimension_theme/shifter = SSmaterials.dimensional_themes[new_theme_path]
|
|
for(var/turf/shiftee in range(1, user))
|
|
shifter.apply_theme(shiftee, show_effect = TRUE)
|
|
// prevent *total* spam conversion
|
|
min_cooldown += 2 SECONDS
|
|
max_cooldown += 2 SECONDS
|
|
|
|
// Replaces your clothing with a random costume, and your ID with a cardboard one.
|
|
// TODO: make them part of the same kit (lobster hat, lobster suit)
|
|
/obj/item/relic/proc/disguiser(mob/user)
|
|
if(!iscarbon(user))
|
|
to_chat(user, span_notice("You have a strange feeling for a moment, but then it passes."))
|
|
return
|
|
|
|
if(prob(80)) // >:)
|
|
ADD_TRAIT(user, TRAIT_NO_JUMPSUIT, REF(src)) // prevent dropping pockets & belt
|
|
|
|
// magic trick!
|
|
playsound(user, SFX_SPARKS, rand(25,50), TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
|
|
throw_smoke(user)
|
|
|
|
// carbons always get a hat at least
|
|
var/mob/living/carbon/carbonius = user
|
|
//hat
|
|
var/obj/item/clothing/head/costume/disguise_hat = roll_costume(ITEM_SLOT_HEAD, HIDEMASK)
|
|
carbonius.dropItemToGround(carbonius.head)
|
|
carbonius.equip_to_slot_or_del(disguise_hat, ITEM_SLOT_HEAD)
|
|
if(!ishuman(carbonius))
|
|
to_chat(user, span_notice("You have a peculiar feeling for a moment, but then it passes."))
|
|
return
|
|
|
|
var/mob/living/carbon/human/humerus = carbonius
|
|
// uniform
|
|
var/obj/item/clothing/under/costume/disguise_uniform = roll_costume(ITEM_SLOT_ICLOTHING)
|
|
humerus.dropItemToGround(humerus.w_uniform)
|
|
humerus.equip_to_slot_or_del(disguise_uniform, ITEM_SLOT_ICLOTHING)
|
|
// suit
|
|
var/obj/item/clothing/suit/costume/disguise_suit = roll_costume(ITEM_SLOT_OCLOTHING)
|
|
humerus.dropItemToGround(humerus.wear_suit)
|
|
humerus.equip_to_slot_or_del(disguise_suit, ITEM_SLOT_OCLOTHING)
|
|
// id
|
|
var/obj/item/card/cardboard/card_id = new()
|
|
humerus.dropItemToGround(humerus.wear_id)
|
|
humerus.equip_to_slot_or_del(card_id, ITEM_SLOT_ID)
|
|
|
|
// edit the card to a random job & name
|
|
if(!card_id)
|
|
return
|
|
card_id.scribbled_name = "[pick(GLOB.first_names)] [pick(GLOB.last_names)]"
|
|
card_id.details_colors = list(ready_random_color(), ready_random_color(), ready_random_color())
|
|
card_id.item_flags |= DROPDEL
|
|
|
|
var/datum/id_trim/random_trim = pick(subtypesof(/datum/id_trim)) // this can pick silly things
|
|
random_trim = new random_trim()
|
|
if(random_trim.trim_state && random_trim.assignment)
|
|
card_id.scribbled_trim = replacetext(random_trim.trim_state, "trim_", "cardboard_")
|
|
card_id.scribbled_assignment = random_trim.assignment
|
|
card_id.update_appearance()
|
|
REMOVE_TRAIT(user, TRAIT_NO_JUMPSUIT, REF(src))
|
|
|
|
/obj/item/relic/proc/roll_costume(slot, flagcheck)
|
|
var/list/candidates = list()
|
|
for(var/obj/item/costume as anything in GLOB.all_autodrobe_items)
|
|
if(flagcheck && !(initial(costume.flags_inv) & flagcheck))
|
|
continue
|
|
if(slot && !(initial(costume.slot_flags) & slot))
|
|
continue
|
|
candidates |= costume
|
|
var/obj/item/new_costume = pick(candidates)
|
|
new_costume = new new_costume()
|
|
new_costume.item_flags |= DROPDEL
|
|
return new_costume
|
|
|
|
//Admin Warning proc for relics
|
|
/obj/item/relic/proc/warn_admins(mob/user, relic_type, priority = 1)
|
|
var/turf/location = get_turf(src)
|
|
var/log_msg = "[relic_type] relic used by [key_name(user)] in [AREACOORD(location)]"
|
|
if(priority) //For truly dangerous relics that may need an admin's attention. BWOINK!
|
|
message_admins("[relic_type] relic activated by [ADMIN_LOOKUPFLW(user)] in [ADMIN_VERBOSEJMP(location)]")
|
|
log_game(log_msg)
|
|
investigate_log(log_msg, "experimentor")
|