From 63b94cd307e66b3a0796b288b6b6045f2b18bc46 Mon Sep 17 00:00:00 2001 From: carlarctg <53100513+carlarctg@users.noreply.github.com> Date: Wed, 10 Jul 2024 23:05:00 -0300 Subject: [PATCH] Adds six new strange object powers! (#84775) ## About The Pull Request DESIGN CONCEPT: Strange Devices are at their best when they're odd tools which can help a department out, with some strange drawbacks that make them interesting and unique. Foam for the Janitor, Flashbang for Security, Teleporter for tiders, Corgimaker for the chef. They're also ambiguous enough that they can really be used by anyone for silly hijinks. Drink Dispenser: Creates a glass, then fills it up with a random drink. Bioscrambler: After a noticeable warning, bioscrambles people nearby. 33% chance to be used up on use. Recharger: Shocks you, but recharges one random item to max battery. Hugger: Hugs everyone in range as if you had the friendly trait. DImensional: Converts the surroundings to a random material-theme. Each use increases cooldown by two seconds. Disguiser: Drops your clothing to the ground and replaces it with a delete-on-drop costume and a randomly assigned cardboard ID. Removed an unused and unusable shock flag. Removed an unused dumb ghost item. Added a proc that returns a hex color with the # attached. ## Why It's Good For The Game > DESIGN CONCEPT: Strange Devices are at their best when they're odd tools which can help a department out, with some strange drawbacks that make them interesting and unique. Foam for the Janitor, Flashbang for Security, Teleporter for tiders, Corgimaker for the chef. They're also ambiguous enough that they can really be used by anyone for silly hijinks. Request by ArcaneMusic! He wanted new abilities to go along with the new sprites, and approved these. > Removed an unused dumb ghost item. please stop using costume subtypes for OP items. whats the damn point of the subtype if youre just going to screw with it > Added a proc that returns a hex color with the # attached. idk why it didnt have this tbh ## Changelog :cl: add: Adds six new strange object powers! Drink dispenser, bioscrambler, recharger, hugger, dimensional, disguiser. code: Removed some unused code and items, and added a proc that returns a random #colorstring. /:cl: --- code/__HELPERS/reagents.dm | 10 ++ code/__HELPERS/text.dm | 3 + .../anomalies/anomalies_bioscrambler.dm | 6 +- code/modules/clothing/suits/ghostsheet.dm | 12 +- .../mob/living/carbon/carbon_defense.dm | 4 +- .../mob/living/carbon/human/human_defense.dm | 2 +- code/modules/research/experimentor.dm | 164 ++++++++++++++++++ 7 files changed, 196 insertions(+), 5 deletions(-) diff --git a/code/__HELPERS/reagents.dm b/code/__HELPERS/reagents.dm index 012a1fd5cc0..cb87e21cefa 100644 --- a/code/__HELPERS/reagents.dm +++ b/code/__HELPERS/reagents.dm @@ -190,6 +190,16 @@ var/picked_reagent = pick(random_reagents) return picked_reagent +///Returns a random reagent consumable ethanol object minus blacklisted reagents +/proc/get_random_drink_id() + var/static/list/random_drinks = list() + if(!random_drinks.len) + for(var/datum/reagent/drink_path as anything in subtypesof(/datum/reagent/consumable/ethanol)) + if(initial(drink_path.chemical_flags) & REAGENT_CAN_BE_SYNTHESIZED) + random_drinks += drink_path + var/picked_drink = pick(random_drinks) + return picked_drink + ///Returns reagent datum from reagent name string /proc/get_chem_id(chem_name) for(var/X in GLOB.chemical_reagents_list) diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index 53b8b95997c..3d9bab22285 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -463,6 +463,9 @@ GLOBAL_LIST_INIT(binary, list("0","1")) /proc/random_color() return random_string(6, GLOB.hex_characters) +/proc/ready_random_color() + return "#" + random_string(6, GLOB.hex_characters) + //merges non-null characters (3rd argument) from "from" into "into". Returns result //e.g. into = "Hello World" // from = "Seeya______" diff --git a/code/game/objects/effects/anomalies/anomalies_bioscrambler.dm b/code/game/objects/effects/anomalies/anomalies_bioscrambler.dm index c57a629d85c..2e2039dfaec 100644 --- a/code/game/objects/effects/anomalies/anomalies_bioscrambler.dm +++ b/code/game/objects/effects/anomalies/anomalies_bioscrambler.dm @@ -88,11 +88,15 @@ pixel_y = -16 duration = 0.5 SECONDS color = COLOR_LIME + var/max_alpha = 255 /obj/effect/temp_visual/bioscrambler_wave/Initialize(mapload) transform = matrix().Scale(0.1) animate(src, transform = matrix().Scale(2), time = duration, flags = ANIMATION_PARALLEL) - animate(src, alpha = 255, time = duration * 0.6, flags = ANIMATION_PARALLEL) + animate(src, alpha = max_alpha, time = duration * 0.6, flags = ANIMATION_PARALLEL) animate(alpha = 0, time = duration * 0.4) apply_wibbly_filters(src) return ..() + +/obj/effect/temp_visual/bioscrambler_wave/light + max_alpha = 128 diff --git a/code/modules/clothing/suits/ghostsheet.dm b/code/modules/clothing/suits/ghostsheet.dm index 9bd8753f50b..65213fd176d 100644 --- a/code/modules/clothing/suits/ghostsheet.dm +++ b/code/modules/clothing/suits/ghostsheet.dm @@ -22,8 +22,18 @@ if(!isinhands && check_holidays(HALLOWEEN)) . += emissive_appearance('icons/mob/simple/mob.dmi', "ghost", offset_spokesman = src, alpha = src.alpha) -/obj/item/clothing/suit/costume/ghost_sheet/spooky +/obj/item/clothing/suit/spooky_ghost_sheet name = "spooky ghost" desc = "This is obviously just a bedsheet, but maybe try it on?" + icon = 'icons/obj/clothing/suits/costume.dmi' + worn_icon = 'icons/mob/clothing/suits/costume.dmi' user_vars_to_edit = list("name" = "Spooky Ghost", "real_name" = "Spooky Ghost" , "incorporeal_move" = INCORPOREAL_MOVE_BASIC, "appearance_flags" = KEEP_TOGETHER|TILE_BOUND, "alpha" = 150) + inhand_icon_state = null + throwforce = 0 + throw_speed = 1 + throw_range = 2 + w_class = WEIGHT_CLASS_TINY + flags_inv = HIDEGLOVES|HIDEEARS|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR|HIDESNOUT + species_exception = list(/datum/species/golem) + supports_variations_flags = CLOTHING_DIGITIGRADE_VARIATION_NO_NEW_ICON alternate_worn_layer = ABOVE_BODY_FRONT_LAYER //so the bedsheet goes over everything but fire diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 8ef981b189d..12f390602d8 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -283,7 +283,7 @@ else Knockdown(stun_duration) -/mob/living/carbon/proc/help_shake_act(mob/living/carbon/helper) +/mob/living/carbon/proc/help_shake_act(mob/living/carbon/helper, force_friendly) if(on_fire) to_chat(helper, span_warning("You can't put [p_them()] out with just your bare hands!")) return @@ -374,7 +374,7 @@ else if(bodytemperature < BODYTEMP_COLD_DAMAGE_LIMIT) to_chat(helper, span_warning("It feels like [src] is freezing as you hug [p_them()].")) - if(HAS_TRAIT(helper, TRAIT_FRIENDLY)) + if(HAS_TRAIT(helper, TRAIT_FRIENDLY) || force_friendly) if (helper.mob_mood.sanity >= SANITY_GREAT) new /obj/effect/temp_visual/heart(loc) add_mood_event("friendly_hug", /datum/mood_event/besthug, helper) diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 976d7475c81..bea66f71112 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -551,7 +551,7 @@ . = rand(-1000, 1000) ..() //Called afterwards because getting the mind after getting gibbed is sketchy -/mob/living/carbon/human/help_shake_act(mob/living/carbon/helper) +/mob/living/carbon/human/help_shake_act(mob/living/carbon/helper, force_friendly) if(!istype(helper)) return diff --git a/code/modules/research/experimentor.dm b/code/modules/research/experimentor.dm index 2d0ba7017d2..730c57b07cc 100644 --- a/code/modules/research/experimentor.dm +++ b/code/modules/research/experimentor.dm @@ -617,6 +617,12 @@ 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) @@ -722,6 +728,164 @@ throw_smoke(get_turf(user)) 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, 'sound/effects/phasein.ogg', rand(25,50), TRUE, SHORT_RANGE_SOUND_EXTRARANGE) + addtimer(CALLBACK(src, PROC_REF(dispense_drink), freebie), 0.5 SECONDS) + +/obj/item/relic/proc/dispense_drink(obj/item/reagent_containers/cup/glass/glasser) + glasser.reagents.add_reagent(get_random_drink_id(), rand(glasser.volume * 0.3, glasser.volume)) + playsound(glasser, SFX_SPARKS, rand(25,50), TRUE, SHORT_RANGE_SOUND_EXTRARANGE) + 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/bioscrambler_wave/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/bioscrambler_wave(get_turf(src)) + playsound(src, 'sound/magic/cosmic_energy.ogg', vol = 50, vary = TRUE) + for(var/mob/living/carbon/nearby in hearers(2, src)) + 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_batteries = list() + for(var/obj/item/stock_parts/power_store/C in user.get_all_contents()) + if(C.charge < (C.maxcharge * 0.95)) // otherwise the PDA always gets recharged + chargeable_batteries |= C + + lightning_fx(user, stunner) + var/recharges = rand(1, 2) + if(!length(chargeable_batteries)) + to_chat(user, span_notice("You have a strange feeling for a moment, but then it passes.")) + return + for(var/obj/item/stock_parts/power_store/to_charge as anything in recharges) + to_charge = pick(chargeable_batteries) + to_charge.charge = to_charge.maxcharge + // The device powered by the cell is assumed to be its location. + var/obj/device = to_charge.loc + // If it's not an object, or the loc's assigned power_store isn't the cell, undo. + if(!istype(device) || (device.get_cell() != to_charge)) + device = to_charge + device.update_appearance(UPDATE_ICON|UPDATE_OVERLAYS) + to_chat(user, span_notice("[device] feels energized!")) + lightning_fx(device, 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) + qdel(shifter) + // 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(/obj/item/clothing/head/costume, 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(/obj/item/clothing/under/costume) + 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(/obj/item/clothing/suit/costume) + 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.update_name() + 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_overlays() + REMOVE_TRAIT(user, TRAIT_NO_JUMPSUIT, REF(src)) + +/obj/item/relic/proc/roll_costume(type, flagcheck) + var/list/candidates = list() + for(var/obj/item/thingy as anything in subtypesof(type)) + if(flagcheck && !(initial(thingy.flags_inv) & flagcheck)) + continue + if(isnull(initial(thingy.icon_state))) + continue + candidates |= thingy + 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)