Files
Alexis 21b4095dfd [MDB IGNORE] [IDB IGNORE] Upstream Sync - 04/17/2026 (#5453)
Upstream 04/17/2026

fixes https://github.com/Bubberstation/Bubberstation/issues/5549

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: tgstation-ci[bot] <179393467+tgstation-ci[bot]@users.noreply.github.com>
Co-authored-by: ArcaneMusic <41715314+ArcaneMusic@users.noreply.github.com>
Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com>
Co-authored-by: Rhials <28870487+Rhials@users.noreply.github.com>
Co-authored-by: rageguy505 <54517726+rageguy505@users.noreply.github.com>
Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com>
Co-authored-by: John Willard <53777086+JohnFulpWillard@users.noreply.github.com>
Co-authored-by: Aliceee2ch <160794176+Aliceee2ch@users.noreply.github.com>
Co-authored-by: Time-Green <7501474+Time-Green@users.noreply.github.com>
Co-authored-by: Tsar-Salat <62388554+Tsar-Salat@users.noreply.github.com>
Co-authored-by: SmArtKar <44720187+SmArtKar@users.noreply.github.com>
Co-authored-by: Maxipat <108554989+Maxipat112@users.noreply.github.com>
Co-authored-by: SyncIt21 <110812394+SyncIt21@users.noreply.github.com>
Co-authored-by: deltanedas <39013340+deltanedas@users.noreply.github.com>
Co-authored-by: SimplyLogan <47579821+loganuk@users.noreply.github.com>
Co-authored-by: loganuk <fakeemail123@aol.com>
Co-authored-by: Leland Kemble <70413276+lelandkemble@users.noreply.github.com>
Co-authored-by: FalloutFalcon <86381784+FalloutFalcon@users.noreply.github.com>
Co-authored-by: Roxy <75404941+TealSeer@users.noreply.github.com>
Co-authored-by: Lucy <lucy@absolucy.moe>
Co-authored-by: siliconOpossum <138069572+siliconOpossum@users.noreply.github.com>
Co-authored-by: Isratosh <Isratosh@hotmail.com>
Co-authored-by: TheRyeGuyWhoWillNowDie <70169560+TheRyeGuyWhoWillNowDie@users.noreply.github.com>
Co-authored-by: Neocloudy <88008002+Neocloudy@users.noreply.github.com>
Co-authored-by: Alexander V. <volas@ya.ru>
Co-authored-by: ElGitificador <168473461+ElGitificador@users.noreply.github.com>
Co-authored-by: Twaticus <46540570+Twaticus@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Bloop <13398309+vinylspiders@users.noreply.github.com>
Co-authored-by: Cameron Lennox <killer65311@gmail.com>
Co-authored-by: Tim <timothymtorres@gmail.com>
Co-authored-by: Iamgoofball <iamgoofball@gmail.com>
Co-authored-by: Layzu666 <121319428+Layzu666@users.noreply.github.com>
Co-authored-by: Arturlang <24881678+Arturlang@users.noreply.github.com>
Co-authored-by: _0Steven <42909981+00-Steven@users.noreply.github.com>
Co-authored-by: mrmanlikesbt <99309552+mrmanlikesbt@users.noreply.github.com>
Co-authored-by: Ben10Omintrix <138636438+Ben10Omintrix@users.noreply.github.com>
Co-authored-by: John F. Kennedy <54908920+MacaroniCritter@users.noreply.github.com>
Co-authored-by: Cursor <102828457+theselfish@users.noreply.github.com>
Co-authored-by: Josh <josh.adam.powell@gmail.com>
Co-authored-by: Josh Powell <josh.powell@softwire.com>
Co-authored-by: Yobrocharlie <Charliemiller5617@gmail.com>
Co-authored-by: Hardly3D <66234359+Hardly3D@users.noreply.github.com>
Co-authored-by: shayoki <96078776+shayoki@users.noreply.github.com>
Co-authored-by: LT3 <83487515+lessthnthree@users.noreply.github.com>
2026-05-16 00:56:00 +02:00

651 lines
26 KiB
Plaintext

#define RELIC_PROTOTYPE "prototype"
#define RELIC_NECROTECH "necrotech"
/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
/// What visual theme this artefact has. Current possible choices: "prototype", "necrotech"
var/artifact_theme = RELIC_PROTOTYPE
COOLDOWN_DECLARE(cooldown)
/obj/item/relic/Initialize(mapload)
. = ..()
random_themed_appearance()
RegisterSignal(src, COMSIG_ITEM_OPENED_FROM_GIFT, PROC_REF(auto_reveal))
/obj/item/relic/proc/auto_reveal(...)
SIGNAL_HANDLER
reveal()
/obj/item/relic/proc/random_themed_appearance()
var/themed_name_prefix
var/themed_name_suffix
if(artifact_theme == RELIC_PROTOTYPE)
icon_state = "[RELIC_PROTOTYPE][rand(1, 9)]"
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 == RELIC_NECROTECH)
icon_state = "[RELIC_NECROTECH][rand(1, 6)]"
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 = RELIC_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)
if(artifact_theme == RELIC_PROTOTYPE)
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),
)
if(artifact_theme == RELIC_NECROTECH)
hidden_power = pick(
PROC_REF(dimensional_shift),
PROC_REF(summon_animals_monsters),
PROC_REF(heat_and_explode),
PROC_REF(t1_shield_holder),
PROC_REF(t2_shield_holder),
PROC_REF(uncontrolled_teleport),
PROC_REF(uncontrolled_aoe_teleport),
PROC_REF(charger),
PROC_REF(place_rocks),
PROC_REF(yeet_blood),
PROC_REF(suck_blood),
PROC_REF(cleaning_foam),
PROC_REF(cleaning_foam_acid),
)
obj_flags |= UNIQUE_RENAME
/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)
/// Helper to spawn smoke somewhere
/obj/item/relic/proc/throw_smoke(turf/where)
do_smoke(0, src, get_turf(where))
/// Helper to show a message to people around the relic
/obj/item/relic/proc/relic_message(message)
var/atom/message_source = ismob(loc) ? loc : src
message_source.visible_message(message)
// Artefact Powers \\
/// Throws a corgi somewhere
/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", FALSE)
/// Spawns cleaning foam
/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(get_turf(user))
spawned_foamer.detonate()
qdel(spawned_foamer)
warn_admins(user, "Foam", FALSE)
/// Similar to cleaning foam but spawns the acid variant
/obj/item/relic/proc/cleaning_foam_acid(mob/user)
playsound(src, SFX_SPARKS, rand(25, 50), TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
var/obj/item/grenade/chem_grenade/ez_clean/spawned_foamer = new(get_turf(user))
spawned_foamer.detonate()
qdel(spawned_foamer)
warn_admins(user, "Acid Foam", TRUE)
if(prob(80))
to_chat(user, span_warning("[src] melts apart!"))
acid_melt()
/// Flashbangs anyone nearby
/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(get_turf(user))
spawned_flashbang.detonate()
warn_admins(user, "Flash")
/// Summon a bunch of random animals, some of which are dangerous
/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))
relic_message(span_warning("[src] falls apart!"))
deconstruct(FALSE)
/// Version of summon_animals that spawns mostly lavaland monsters
/obj/item/relic/proc/summon_animals_monsters(mob/user)
var/message = span_danger("[src] begins to shake, and in the distance the sound of roaring arises!")
visible_message(message)
to_chat(user, message)
var/static/list/valid_monsters = list(
/mob/living/basic/construct/artificer/hostile,
/mob/living/basic/construct/juggernaut/hostile,
/mob/living/basic/construct/proteon/hostile,
/mob/living/basic/construct/wraith/hostile,
/mob/living/basic/mining/brimdemon,
/mob/living/basic/mining/goldgrub,
/mob/living/basic/mining/goliath,
/mob/living/basic/mining/hivelord,
/mob/living/basic/mining/ice_demon,
/mob/living/basic/mining/legion,
/mob/living/basic/mining/lobstrosity,
/mob/living/basic/mining/watcher,
/mob/living/basic/raptor/blue,
/mob/living/basic/raptor/green,
/mob/living/basic/raptor/purple,
/mob/living/basic/raptor/red,
/mob/living/basic/raptor/white,
/mob/living/basic/raptor/yellow,
)
for(var/counter in 1 to rand(3, 9))
var/animal_spawn = pick(valid_monsters)
var/mob/living/animal = new animal_spawn(get_turf(src))
ADD_TRAIT(animal, TRAIT_SPAWNED_MOB, INNATE_TRAIT)
warn_admins(user, "Mass Mob Spawn (Monster)")
if(prob(80))
relic_message(span_warning("[src] falls apart!"))
deconstruct(FALSE)
/// Spawns a bunch of mimics of the relic which also can spawn relics, but despawn shortly
/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)
/// Explodes after a few seconds
/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")
deconstruct(FALSE) //Comment this line to produce a light grenade (the bomb that keeps on exploding when used)!!
/// Teleports the relic, and anyone holding it, to a random location nearby
/obj/item/relic/proc/uncontrolled_teleport(mob/user)
to_chat(user, span_notice("[src] begins to vibrate!"))
var/teleport_time = rand(1 SECONDS, 3 SECONDS)
addtimer(CALLBACK(src, PROC_REF(do_the_teleport), user), teleport_time)
Shake(1, 1, teleport_time, 0.05 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", FALSE)
/// Version of uncontrolled_teleport with cult theming, and that affects all nearby movables rather than just the relic
/obj/item/relic/proc/uncontrolled_aoe_teleport(mob/user)
to_chat(user, span_notice("[src] begins to vibrate intensely!"))
var/teleport_time = rand(1 SECONDS, 3 SECONDS)
addtimer(CALLBACK(src, PROC_REF(do_the_aoe_teleport), user), teleport_time)
Shake(2, 2, teleport_time, 0.03 SECONDS)
/obj/item/relic/proc/do_the_aoe_teleport(mob/user)
visible_message(span_notice("[src] twists and bends, relocating anything nearby!"))
var/turf/teleturf = get_turf(src)
for(var/atom/movable/nearby in view(2, teleturf))
if(nearby.anchored || nearby.invisibility || HAS_TRAIT(nearby, TRAIT_UNDERFLOOR))
continue
if(isliving(nearby))
var/mob/living/nearby_living = nearby
if(nearby_living.can_block_magic(MAGIC_RESISTANCE_HOLY, 1))
continue
if(isliving(nearby))
new /obj/effect/temp_visual/dir_setting/cult/phase/out(nearby.loc, nearby.dir)
else
new /obj/effect/temp_visual/cult/sparks(nearby.loc)
do_teleport(
teleatom = nearby,
destination = teleturf,
precision = 5,
asoundin = 'sound/effects/phasein.ogg',
channel = TELEPORT_CHANNEL_CULT,
)
if(isliving(nearby))
new /obj/effect/temp_visual/dir_setting/cult/phase(nearby.loc, nearby.dir)
else
new /obj/effect/temp_visual/cult/sparks(nearby.loc)
warn_admins(user, "AOE Teleport")
if(prob(30))
relic_message(span_warning("[src] teleports away, never to be seen again!"))
qdel(src)
// 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_reagent_id(whitelist = subtypesof(/datum/reagent/consumable/ethanol)), 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))
relic_message(span_warning("[src] falls apart!"))
deconstruct(FALSE)
/// 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))
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
/// Makes the relic holder have a shield that blocks 3 common attacks
/obj/item/relic/proc/t1_shield_holder(mob/user)
var/datum/component/shield = AddComponent( \
/datum/component/shielded, \
max_charges = 3, \
recharge_start_delay = 0, \
shield_inhand = TRUE, \
show_charge_as_alpha = TRUE, \
can_block_overwhelming = FALSE, \
shield_icon_file = 'icons/effects/effects.dmi', \
shield_icon = "at_shield1", \
run_hit_callback = CALLBACK(src, PROC_REF(shield_hit)), \
)
light_system = OVERLAY_LIGHT
var/datum/component/light = AddComponent( \
/datum/component/overlay_lighting, \
_range = 2.5, \
_power = 1.5, \
_color = COLOR_BIOLUMINESCENCE_YELLOW, \
starts_on = TRUE, \
)
addtimer(CALLBACK(src, PROC_REF(remove_shield), list(shield, light)), cooldown_timer * 0.5)
add_filter("block_shield", 1, outline_filter(0, COLOR_BIOLUMINESCENCE_YELLOW), shield)
transition_filter("block_shield", outline_filter(2, COLOR_BIOLUMINESCENCE_YELLOW), cooldown_timer * 0.5)
relic_message(span_notice("[src] starts to glow a bright yellow!"))
warn_admins(user, "Shield", FALSE)
/// Makes the relic holder have a shield that blocks 1 powerful attack
/obj/item/relic/proc/t2_shield_holder(mob/user)
var/datum/component/shield = AddComponent( \
/datum/component/shielded, \
max_charges = 1, \
recharge_start_delay = 0, \
shield_inhand = TRUE, \
show_charge_as_alpha = TRUE, \
can_block_overwhelming = TRUE, \
shield_icon_file = 'icons/effects/effects.dmi', \
shield_icon = "at_shield2", \
run_hit_callback = CALLBACK(src, PROC_REF(shield_hit)), \
)
light_system = OVERLAY_LIGHT
var/datum/component/light = AddComponent( \
/datum/component/overlay_lighting, \
_range = 3, \
_power = 1.5, \
_color = COLOR_BIOLUMINESCENCE_YELLOW, \
starts_on = TRUE, \
)
addtimer(CALLBACK(src, PROC_REF(remove_shield), list(shield, light)), cooldown_timer * 0.5)
add_filter("block_shield", 1, outline_filter(0, COLOR_BIOLUMINESCENCE_YELLOW), shield)
transition_filter("block_shield", outline_filter(2, COLOR_BIOLUMINESCENCE_YELLOW), cooldown_timer * 0.5)
relic_message(span_notice("[src] starts to glow a bright yellow!"))
warn_admins(user, "Shield", FALSE)
/obj/item/relic/proc/shield_hit(mob/living/owner, attack_text, current_charges)
playsound(src, 'sound/items/weapons/marauder.ogg', 20, TRUE, frequency = 1.25)
owner.visible_message(span_danger("[owner]'s holds [src] up, blocking [attack_text] with a projected shield!"))
if(current_charges <= 0)
set_light_on(FALSE)
relic_message(span_notice("[src] stops glowing."))
remove_filter("block_shield")
/obj/item/relic/proc/remove_shield(list/cleanup_components)
for(var/datum/component/comp as anything in cleanup_components)
qdel(comp)
light_system = initial(light_system)
if(light_on)
relic_message(span_notice("[src] stops glowing."))
remove_filter("block_shield")
/// Places rock turfs around the relic
/obj/item/relic/proc/place_rocks(mob/user)
relic_message(span_notice("A spire of rock erupts from the ground beneath [src]!"))
playsound(src, 'sound/effects/rock/rock_break.ogg', 50, TRUE)
var/turf/spawnloc = get_turf(src)
for(var/turf/open/open_turf in RANGE_TURFS(rand(1, 2), spawnloc))
var/turf/closed/mineral/asteroid/rock = open_turf.place_on_top(/turf/closed/mineral/asteroid)
if(istype(rock))
rock.name = "fragile rock"
rock.weak_turf = TRUE
rock.tool_mine_speed = 2 SECONDS
rock.hand_mine_speed = 10 SECONDS
for(var/mob/living/within_rock in rock)
within_rock.Paralyze(1 SECONDS)
within_rock.Knockdown(3 SECONDS)
within_rock.apply_damage(10, BRUTE, BODY_ZONE_CHEST, blocked = within_rock.getarmor(BODY_ZONE_CHEST, MELEE), wound_bonus = 10, exposed_wound_bonus = 10)
to_chat(within_rock, span_danger("You are smashed by [rock]!"))
warn_admins(user, "Rocks", FALSE)
if(prob(20))
relic_message(span_warning("[src] crumbles into dust!"))
deconstruct(FALSE)
/// User sprays out blood in all directions
/// Has a small chance of changing the power to suck_blood
/obj/item/relic/proc/yeet_blood(mob/living/user)
var/yeet_time = rand(1 SECONDS, 3 SECONDS)
add_filter("blood_outgoing", 1, outline_filter(0, COLOR_DARK))
transition_filter("blood_outgoing", outline_filter(2, BLOOD_COLOR_RED), yeet_time)
if(istype(user) && CAN_HAVE_BLOOD(user))
to_chat(user, span_danger("[src] starts glowing an ominous red!"))
else
to_chat(user, span_danger("[src] starts glowing an ominous red..."))
addtimer(CALLBACK(src, PROC_REF(actually_yeet_blood)), yeet_time)
/obj/item/relic/proc/actually_yeet_blood()
var/mob/living/user = loc
var/splatcount = 0
if(istype(user) && CAN_HAVE_BLOOD(user) && !user.can_block_magic(MAGIC_RESISTANCE_HOLY, 1))
for(var/splatdir in GLOB.alldirs)
if(prob(splatcount * 5))
continue
var/strength = rand(2, 3)
user.spray_blood(splatdir, strength)
user.bleed(strength ** 2)
splatcount += strength
if(splatcount > 0)
relic_message(span_warning("Blood sprays out from [src]!"))
warn_admins(user, "Blood Dispersal", FALSE)
playsound(src, 'sound/effects/wounds/blood3.ogg', 50, TRUE)
if(prob(20))
hidden_power = PROC_REF(suck_blood)
else
relic_message(span_warning("[src] pulses ominously, but nothing happens!"))
transition_filter("blood_outgoing", outline_filter(0, COLOR_DARK), 1 SECONDS)
addtimer(CALLBACK(src, TYPE_PROC_REF(/datum, remove_filter), "blood_outgoing"), 2 SECONDS)
/// Nearby mobs transfer blood to the user
/// Has a small chance of changing the power to yeet_blood
/obj/item/relic/proc/suck_blood(mob/living/user)
var/suck_time = rand(1 SECONDS, 3 SECONDS)
add_filter("blood_incoming", 1, outline_filter(0, COLOR_DARK))
transition_filter("blood_incoming", outline_filter(2, BLOOD_COLOR_RED), suck_time)
if(istype(user) && CAN_HAVE_BLOOD(user))
to_chat(user, span_danger("[src] starts glowing an ominous red!"))
else
to_chat(user, span_danger("[src] starts glowing an ominous red..."))
addtimer(CALLBACK(src, PROC_REF(actually_suck_blood)), suck_time)
/obj/item/relic/proc/actually_suck_blood()
var/mob/living/user = loc
var/any_affected = FALSE
if(istype(user) && CAN_HAVE_BLOOD(user) && !user.can_block_magic(MAGIC_RESISTANCE_HOLY, 1))
for(var/mob/living/nearby in view(2, user))
if(nearby == user || !CAN_HAVE_BLOOD(nearby) || nearby.can_block_magic(MAGIC_RESISTANCE_HOLY, 1))
continue
nearby.transfer_blood_to(user, rand(6, 10), ignore_low_blood = TRUE, ignore_incompatibility = TRUE, transfer_viruses = FALSE)
to_chat(nearby, span_danger("You feel a sudden weakness as blood is drawn out of you [nearby.is_blind() ? "" : " and into [user]"]!"))
any_affected = TRUE
nearby.Beam(user, icon_state = "blood", time = 1 SECONDS)
new /obj/effect/temp_visual/cult/sparks(nearby.loc)
if(any_affected)
playsound(src, 'sound/effects/magic/enter_blood.ogg', 50, TRUE)
relic_message(span_warning("Blood from nearby creatures is drawn towards [src], and into [user]!"))
warn_admins(user, "Blood Suck")
if(prob(10))
hidden_power = PROC_REF(yeet_blood)
else
relic_message(span_warning("[src] pulses ominously, but nothing happens!"))
transition_filter("blood_incoming", outline_filter(0, COLOR_DARK), 1 SECONDS)
addtimer(CALLBACK(src, TYPE_PROC_REF(/datum, remove_filter), "blood_incoming"), 2 SECONDS)
/// Alerts admins on usage of dagnerous relics
/obj/item/relic/proc/warn_admins(mob/user, relic_type, priority = TRUE)
var/turf/location = get_turf(src)
var/log_msg = "[relic_type] relic used by [key_name(user)] in [AREACOORD(location)]"
if(priority)
message_admins("[relic_type] relic activated by [ADMIN_LOOKUPFLW(user)] in [ADMIN_VERBOSEJMP(location)]")
log_game(log_msg)
investigate_log(log_msg, "experimentor")
// Subtypes that spawn revealed, primarily for debug/testing/badmin purposes
/obj/item/relic/revealed
/obj/item/relic/revealed/Initialize(mapload)
. = ..()
auto_reveal()
/obj/item/relic/lavaland
/obj/item/relic/lavaland/revealed/Initialize(mapload)
. = ..()
auto_reveal()
#undef RELIC_PROTOTYPE
#undef RELIC_NECROTECH