diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm index 22c43fa5747..38311fa289c 100644 --- a/code/__defines/misc.dm +++ b/code/__defines/misc.dm @@ -8,13 +8,15 @@ #define INVISIBILITY_LIGHTING 20 #define INVISIBILITY_LEVEL_ONE 35 #define INVISIBILITY_LEVEL_TWO 45 +#define INVISIBILITY_SHADEKIN 55 #define INVISIBILITY_OBSERVER 60 #define INVISIBILITY_EYE 61 #define SEE_INVISIBLE_LIVING 25 -#define SEE_INVISIBLE_NOLIGHTING 15 +#define SEE_INVISIBLE_NOLIGHTING 15 #define SEE_INVISIBLE_LEVEL_ONE 35 #define SEE_INVISIBLE_LEVEL_TWO 45 +#define SEE_INVISIBILITY_SHADEKIN 55 #define SEE_INVISIBLE_CULT 60 #define SEE_INVISIBLE_OBSERVER 61 diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm index b8457de50bd..3bb8ebe6b0d 100644 --- a/code/__defines/mobs.dm +++ b/code/__defines/mobs.dm @@ -27,6 +27,7 @@ #define BORGTHERM 0x2 #define BORGXRAY 0x4 #define BORGMATERIAL 0x8 +#define BORGANOMALOUS 0x10 #define STANCE_ATTACK 11 // Backwards compatability #define STANCE_ATTACKING 12 // Ditto diff --git a/code/__defines/span_vr.dm b/code/__defines/span_vr.dm index eaf45491cd3..49e4e0ef69d 100644 --- a/code/__defines/span_vr.dm +++ b/code/__defines/span_vr.dm @@ -52,6 +52,12 @@ #define span_reflex_shoot(str) ("" + str + "") +/* Vore messages */ + +#define span_vdanger(str) ("" + str + "") +#define span_vwarning(str) ("" + str + "") +#define span_vnotice(str) ("" + str + "") + /* Languages */ #define span_alien(str) ("" + str + "") diff --git a/code/_helpers/global_lists_vr.dm b/code/_helpers/global_lists_vr.dm index 76b1703fa90..b025f45980d 100644 --- a/code/_helpers/global_lists_vr.dm +++ b/code/_helpers/global_lists_vr.dm @@ -240,6 +240,7 @@ var/global/list/edible_trash = list(/obj/item/broken_device, /obj/item/weapon/storage/wallet, /obj/item/weapon/storage/vore_egg, /obj/item/weapon/bikehorn/tinytether, + /obj/item/weapon/entrepreneur, /obj/item/capture_crystal, /obj/item/roulette_ball, /obj/item/pizzabox diff --git a/code/datums/outfits/jobs/civilian_vr.dm b/code/datums/outfits/jobs/civilian_vr.dm index 55a66114848..74e441e5eef 100644 --- a/code/datums/outfits/jobs/civilian_vr.dm +++ b/code/datums/outfits/jobs/civilian_vr.dm @@ -23,4 +23,70 @@ /decl/hierarchy/outfit/job/assistant/entrepreneur id_type = /obj/item/weapon/card/id/civilian/entrepreneur l_hand = /obj/item/device/ticket_printer/train + uniform = /obj/item/clothing/under/tropical/pink +/decl/hierarchy/outfit/job/assistant/entrepreneur/lawyer + uniform = /obj/item/clothing/under/lawyer/red + r_hand = /obj/item/weapon/clipboard + l_pocket = /obj/item/weapon/pen/fountain3 + +/decl/hierarchy/outfit/job/assistant/entrepreneur/private_eye + l_hand = /obj/item/weapon/storage/box/private_investigator + suit = /obj/item/clothing/suit/storage/trench + head = /obj/item/clothing/head/fedora/brown + +/decl/hierarchy/outfit/job/assistant/entrepreneur/bodyguard + glasses = /obj/item/clothing/glasses/sunglasses + l_pocket = /obj/item/weapon/reagent_containers/spray/pepper + suit = /obj/item/clothing/accessory/sweater/blackneck + +/decl/hierarchy/outfit/job/assistant/entrepreneur/personal_physician + suit = /obj/item/clothing/suit/storage/toggle/labcoat + l_pocket = /obj/item/clothing/accessory/stethoscope + r_pocket = /obj/item/device/healthanalyzer + +/decl/hierarchy/outfit/job/assistant/entrepreneur/dentist + l_hand = /obj/item/weapon/storage/box/dentist + suit = /obj/item/clothing/suit/storage/toggle/labcoat + +/decl/hierarchy/outfit/job/assistant/entrepreneur/fitness_instructor + l_hand = /obj/item/weapon/storage/box/fitness_trainer + +/decl/hierarchy/outfit/job/assistant/entrepreneur/yoga_teacher + uniform = /obj/item/clothing/under/pants/yogapants + l_hand = /obj/item/weapon/storage/box/yoga_teacher + +/decl/hierarchy/outfit/job/assistant/entrepreneur/masseuse + r_hand = /obj/item/roller/massage + +/decl/hierarchy/outfit/job/assistant/entrepreneur/tradesperson + r_hand = /obj/item/weapon/storage/toolbox/brass + +/decl/hierarchy/outfit/job/assistant/entrepreneur/streamer + l_pocket = /obj/item/device/tvcamera/streamer + +/decl/hierarchy/outfit/job/assistant/entrepreneur/influencer + l_pocket = /obj/item/device/camera/selfie + +/decl/hierarchy/outfit/job/assistant/entrepreneur/paranormal_investigator + l_hand = /obj/item/weapon/storage/box/paranormal_investigator + +/decl/hierarchy/outfit/job/assistant/entrepreneur/stylist + l_hand = /obj/item/weapon/storage/box/stylist + +/decl/hierarchy/outfit/job/assistant/entrepreneur/fisher + r_hand = /obj/item/weapon/material/fishing_rod/modern + l_pocket = /obj/item/weapon/material/fishing_net + head = /obj/item/clothing/head/fishing + r_pocket = /obj/item/weapon/storage/box/wormcan + +/decl/hierarchy/outfit/job/assistant/entrepreneur/personal_secretary + uniform = /obj/item/clothing/under/lawyer/blue + r_hand = /obj/item/weapon/clipboard + l_pocket = /obj/item/weapon/pen/fountain3 + +/decl/hierarchy/outfit/job/assistant/entrepreneur/fortune_teller + l_hand = /obj/item/weapon/storage/box/fortune_teller + +/decl/hierarchy/outfit/job/assistant/entrepreneur/spirit_healer + l_hand = /obj/item/weapon/storage/box/spirit_healer diff --git a/code/game/jobs/job/civilian_vr.dm b/code/game/jobs/job/civilian_vr.dm index e9f105637ed..31d69c1a893 100644 --- a/code/game/jobs/job/civilian_vr.dm +++ b/code/game/jobs/job/civilian_vr.dm @@ -337,70 +337,84 @@ /datum/alt_title/lawyer title = "Lawyer" title_blurb = "A Lawyer is knowledgable in various legal systems, including station's operations. They can try to offer their legal counsel, although nobody is really obliged to listen." + title_outfit = /decl/hierarchy/outfit/job/assistant/entrepreneur/personal_secretary /datum/alt_title/private_eye title = "Private Eye" title_blurb = "A Private Eye is a detective that has no credentials or equipment. But if someone wants something found without security's knowledge, they are the one to go to." + title_outfit = /decl/hierarchy/outfit/job/assistant/entrepreneur/private_eye /datum/alt_title/bodyguard title = "Bodyguard" title_blurb = "A Bodyguard offers service of personal protection. They may not be allowed any weapons, but their own body is weapon enough." + title_outfit = /decl/hierarchy/outfit/job/assistant/entrepreneur/bodyguard /datum/alt_title/personal_physician title = "Personal Physician" title_blurb = "A Personal Physicial is a doctor dedicated less to Hippocratic Oath and more to the moneymaking grind. Their license may be expired, but the grindset never will be." + title_outfit = /decl/hierarchy/outfit/job/assistant/entrepreneur/personal_physician /datum/alt_title/dentist title = "Dentist" title_blurb = "A Dentist is a doctor that specializes in oral care. Company may not recognize them as a proper doctor, but surely their customers will." + title_outfit = /decl/hierarchy/outfit/job/assistant/entrepreneur/dentist /datum/alt_title/fitness_instructor title = "Fitness Instructor" title_blurb = "A Fitness Instructor dedicates themselves to improving the health of the crew through physical activity, and boy, do they need the help." + title_outfit = /decl/hierarchy/outfit/job/assistant/entrepreneur/fitness_instructor /datum/alt_title/yoga_teacher title = "Yoga Teacher" title_blurb = "A Yoga Teacher is similar to fitness instructor, but rather than turning the round bodies into firm ones, they focus on helping people find balance and harmony." + title_outfit = /decl/hierarchy/outfit/job/assistant/entrepreneur/yoga_teacher /datum/alt_title/masseuse title = "Masseuse" title_blurb = "A Masseuse is master of physical therapy and working others' bodies with their hands." + title_outfit = /decl/hierarchy/outfit/job/assistant/entrepreneur/masseuse /datum/alt_title/tradesperson title = "Tradesperson" title_blurb = "A Tradesperson is someone attempting to make money via the most obvious act of all - buying and selling. Now if only customs allowed you to bring your goods along..." + title_outfit = /decl/hierarchy/outfit/job/assistant/entrepreneur/tradesperson /datum/alt_title/streamer title = "Streamer" title_blurb = "A Streamer is here to entertain. Not the crew! Their audience across exonet!" + title_outfit = /decl/hierarchy/outfit/job/assistant/entrepreneur/streamer /datum/alt_title/influencer title = "Influencer" title_blurb = "An Influencer has lucked out with some exonet following, and was given permission to go onstation to provide free exposure. Don't let it go to your head." + title_outfit = /decl/hierarchy/outfit/job/assistant/entrepreneur/influencer /datum/alt_title/paranormal_investigator title = "Paranormal Investigator" title_blurb = "A Paranormal Investigator looks beyond what is accepted by modern science, and searches for the true unknown. Aliens, alternate dimensions, ghosts... The truth is out there!" + title_outfit = /decl/hierarchy/outfit/job/assistant/entrepreneur/paranormal_investigator /datum/alt_title/personal_secretary title = "Personal Secretary" title_blurb = "A Personal Secretary offers services of general assistance. Although it's doubtful anyone will ever actually need those." + title_outfit = /decl/hierarchy/outfit/job/assistant/entrepreneur/personal_secretary /datum/alt_title/stylist title = "Stylist" title_blurb = "A Stylist offers fashion advice, as well as helps with adjusting appearance of the crew to better suit their beauty standards." + title_outfit = /decl/hierarchy/outfit/job/assistant/entrepreneur/stylist /datum/alt_title/fisher title = "Fisher" title_blurb = "A Fisher is a capable angler, who is good at obtaining large amounts of marine goods. Whether you generously give them to station or attempt to make a quick thaler by selling, it's up to you!" + title_outfit = /decl/hierarchy/outfit/job/assistant/entrepreneur/fisher /datum/alt_title/fortune_teller title = "Fortune Teller" title_blurb = "A Fortune Teller peers into the future, and offers these visions to others. Occasionally those visions may even come true!" + title_outfit = /decl/hierarchy/outfit/job/assistant/entrepreneur/fortune_teller /datum/alt_title/spirit_healer title = "Spirit Healer" title_blurb = "A Spirit Healer offers alternative forms of medicine. Rituals, magic rocks, seances... It totally works. What's that about placebo?" - - - + title_outfit = /decl/hierarchy/outfit/job/assistant/entrepreneur/spirit_healer diff --git a/code/game/objects/items/robot/robot_items.dm b/code/game/objects/items/robot/robot_items.dm index c7563b4412f..30ff96b052a 100644 --- a/code/game/objects/items/robot/robot_items.dm +++ b/code/game/objects/items/robot/robot_items.dm @@ -36,6 +36,8 @@ /obj/item/borg/sight/xray name = "\proper x-ray vision" sight_mode = BORGXRAY + icon_state = "night" + icon = 'icons/inventory/eyes/item.dmi' /obj/item/borg/sight/thermal @@ -57,6 +59,12 @@ icon_state = "material" icon = 'icons/inventory/eyes/item.dmi' +/obj/item/borg/sight/anomalous + name = "\proper anomaly vision" + sight_mode = BORGANOMALOUS + icon_state = "denight" + icon = 'icons/inventory/eyes/item.dmi' + /obj/item/borg/sight/hud name = "hud" var/obj/item/clothing/glasses/hud/hud = null diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm index aba84bf0005..fe41bc658ff 100644 --- a/code/game/objects/items/robot/robot_upgrades.dm +++ b/code/game/objects/items/robot/robot_upgrades.dm @@ -18,6 +18,16 @@ return 1 return 0 +/obj/item/borg/upgrade/proc/generic_error(var/mob/living/silicon/robot/R, var/obj/item/borg/type) + type = lowertext(initial(type.name)) + to_chat(R, "Upgrade mounting error! No suitable hardpoint for \the \"[type]\" detected!") + to_chat(usr, "There's no mounting point for \the \"[type]\" module!") + +/obj/item/borg/upgrade/proc/software_error(var/mob/living/silicon/robot/R, var/obj/item/borg/type) + type = lowertext(initial(type.name)) + to_chat(R, "Upgrade installation error! Incompatibility with \the \"[type]\" detected!") + to_chat(usr, "\The \"[type]\" upgrade is not compatibile!") + /* ###################################################################################################### # Utility section. All reusable upgrades without lasting effects, like renaming, reset, etc. go here.# ######################################################################################################*/ @@ -252,8 +262,7 @@ if(..()) return 0 if(R.has_advanced_upgrade(type)) - to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") - to_chat(usr, "There's no mounting point for the module!") + generic_error(R, type) return 0 R.module.modules += new/obj/item/weapon/tank/jetpack/carbondioxide(R.module) @@ -272,8 +281,7 @@ if(..()) return 0 if(R.has_advanced_upgrade(type)) - to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") - to_chat(usr, "There's no mounting point for the module!") + generic_error(R, type) return 0 R.module.modules += new/obj/item/device/healthanalyzer/advanced(R.module) @@ -291,8 +299,7 @@ if(..()) return 0 if(R.has_advanced_upgrade(type)) - to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") - to_chat(usr, "There's no mounting point for the module!") + generic_error(R, type) return 0 R.module.modules += new/obj/item/weapon/gun/energy/sizegun/mounted(R.module) @@ -315,8 +322,7 @@ if(..()) return 0 if(!R.supports_upgrade(type)) - to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") - to_chat(usr, "There's no mounting point for the module!") + generic_error(R, type) return 0 var/obj/T = R.has_upgrade_module(/obj/item/device/dogborg/sleeper) @@ -350,8 +356,7 @@ if(..()) return 0 if(!R.supports_upgrade(type)) - to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") - to_chat(usr, "There's no mounting point for the module!") + generic_error(R, type) return 0 var/obj/T = R.has_upgrade_module(/obj/item/weapon/gun/energy/taser/mounted/cyborg) @@ -381,13 +386,11 @@ if(..()) return 0 if(!R.supports_upgrade(type)) - to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") - to_chat(usr, "There's no mounting point for the module!") + generic_error(R, type) return 0 if(R.has_restricted_upgrade(type)) - to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") - to_chat(usr, "There's no mounting point for the module!") + generic_error(R, type) return 0 R.module.modules += new/obj/item/weapon/storage/part_replacer/adv(R.module) @@ -406,13 +409,11 @@ if(..()) return 0 if(!R.supports_upgrade(type)) - to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") - to_chat(usr, "There's no mounting point for the module!") + generic_error(R, type) return 0 if(R.has_restricted_upgrade(type)) - to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") - to_chat(usr, "There's no mounting point for the module!") + generic_error(R, type) return 0 R.module.modules += new/obj/item/weapon/pickaxe/diamonddrill(R.module) @@ -431,13 +432,11 @@ if(..()) return 0 if(!R.supports_upgrade(type)) - to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") - to_chat(usr, "There's no mounting point for the module!") + generic_error(R, type) return 0 if(R.has_restricted_upgrade(type)) - to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") - to_chat(usr, "There's no mounting point for the module!") + generic_error(R, type) return 0 R.module.modules += new/obj/item/weapon/gun/energy/kinetic_accelerator/cyborg(R.module) @@ -462,9 +461,98 @@ if(..()) return 0 if(R.has_no_prod_upgrade(type)) - to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") - to_chat(usr, "There's no mounting point for the module!") + generic_error(R, type) return 0 R.module.modules += new/obj/item/weapon/gun/projectile/cyborgtoy(R.module) return 1 + +/obj/item/borg/upgrade/no_prod/vision_xray + name = "Robot x-ray vision module" + desc = "Vision alterantion software to add x-ray sight capabilities." + icon_state = "cyborg_upgrade5" + item_state = "cyborg_upgrade" + require_module = 1 + hidden_from_scan = 1 + +/obj/item/borg/upgrade/no_prod/vision_xray/action(var/mob/living/silicon/robot/R) + if(..()) return 0 + + if(R.has_no_prod_upgrade(type)) + software_error(R, type) + return 0 + + R.module.modules += new/obj/item/borg/sight/xray(R.module) + return 1 + +/obj/item/borg/upgrade/no_prod/vision_thermal + name = "Robot thermal vision module" + desc = "Vision alterantion software to add thermal sight capabilities." + icon_state = "cyborg_upgrade5" + item_state = "cyborg_upgrade" + require_module = 1 + hidden_from_scan = 1 + +/obj/item/borg/upgrade/no_prod/vision_thermal/action(var/mob/living/silicon/robot/R) + if(..()) return 0 + + if(R.has_no_prod_upgrade(type)) + software_error(R, type) + return 0 + + R.module.modules += new/obj/item/borg/sight/thermal(R.module) + return 1 + +/obj/item/borg/upgrade/no_prod/vision_meson + name = "Robot meson vision module" + desc = "Vision alterantion software to add meson sight capabilities." + icon_state = "cyborg_upgrade5" + item_state = "cyborg_upgrade" + require_module = 1 + hidden_from_scan = 1 + +/obj/item/borg/upgrade/no_prod/vision_meson/action(var/mob/living/silicon/robot/R) + if(..()) return 0 + + if(R.has_no_prod_upgrade(type)) + software_error(R, type) + return 0 + + R.module.modules += new/obj/item/borg/sight/meson(R.module) + return 1 + +/obj/item/borg/upgrade/no_prod/vision_material + name = "Robot material vision module" + desc = "Vision alterantion software to add material sight capabilities." + icon_state = "cyborg_upgrade5" + item_state = "cyborg_upgrade" + require_module = 1 + hidden_from_scan = 1 + +/obj/item/borg/upgrade/no_prod/vision_material/action(var/mob/living/silicon/robot/R) + if(..()) return 0 + + if(R.has_no_prod_upgrade(type)) + software_error(R, type) + return 0 + + R.module.modules += new/obj/item/borg/sight/material(R.module) + return 1 + +/obj/item/borg/upgrade/no_prod/vision_anomalous + name = "Robot anomalous vision module" + desc = "Vision alterantion software to add anomalous sight capabilities." + icon_state = "cyborg_upgrade5" + item_state = "cyborg_upgrade" + require_module = 1 + hidden_from_scan = 1 + +/obj/item/borg/upgrade/no_prod/vision_anomalous/action(var/mob/living/silicon/robot/R) + if(..()) return 0 + + if(R.has_no_prod_upgrade(type)) + software_error(R, type) + return 0 + + R.module.modules += new/obj/item/borg/sight/anomalous(R.module) + return 1 diff --git a/code/modules/clothing/masks/tesh_synth_facemask.dm b/code/modules/clothing/masks/tesh_synth_facemask.dm new file mode 100644 index 00000000000..a321eb9dca6 --- /dev/null +++ b/code/modules/clothing/masks/tesh_synth_facemask.dm @@ -0,0 +1,73 @@ +//TESHARI FACE MASK //Defning all the procs in one go +/obj/item/clothing/mask/synthfacemask + name = "Synth Face" + desc = "A round dark muzzle made of LEDs." + flags = PHORONGUARD //Since it cant easily be removed... + item_flags = AIRTIGHT | FLEXIBLEMATERIAL | BLOCK_GAS_SMOKE_EFFECT //This should make it properly work as a mask... and allow you to eat stuff through it! + body_parts_covered = FACE|EYES + icon = 'icons/mob/species/teshari/synth_facemask.dmi' + icon_override = 'icons/mob/species/teshari/synth_facemask.dmi' + icon_state = "synth_facemask" + origin_tech = list(TECH_ILLEGAL = 1) + var/lstat + var/visor_state = "Neutral" //Separating this from lstat so that it could potentially be used for an override system or something + var/mob/living/carbon/maskmaster + +/obj/item/clothing/mask/synthfacemask/equipped() + ..() + var/mob/living/carbon/human/H = loc + if(istype(H) && H.wear_mask == src) + canremove = 0 + maskmaster = H + START_PROCESSING(SSprocessing, src) + +/obj/item/clothing/mask/synthfacemask/dropped() + canremove = 1 + maskmaster = null + STOP_PROCESSING(SSprocessing, src) + return ..() + +/obj/item/clothing/mask/synthfacemask/Destroy() + . = ..() + STOP_PROCESSING(SSprocessing, src) + +/obj/item/clothing/mask/synthfacemask/mob_can_equip(var/mob/living/carbon/human/user, var/slot, var/disable_warning = FALSE) + if (!..()) + return 0 + if(istype(user)) + var/obj/item/organ/external/E = user.organs_by_name[BP_HEAD] + if(istype(E) && (E.robotic >= ORGAN_ROBOT)) + return 1 + to_chat(user, "You must have a compatible robotic head to install this upgrade.") + return 0 + +/obj/item/clothing/mask/synthfacemask/update_icon() + var/mob/living/carbon/human/H = loc + switch(visor_state) + if (DEAD) + icon_state = "synth_facemask_dead" + else + icon_state = "synth_facemask" + if(istype(H)) H.update_inv_wear_mask() + +/obj/item/clothing/mask/synthfacemask/process() + if(maskmaster && lstat != maskmaster.stat) + lstat = maskmaster.stat + visor_state = "Neutral" //This does nothing at the moment, but it's there incase anyone wants to add more states. + //Maybe a verb that sets an emote override here + if(lstat == DEAD) + visor_state = DEAD + update_icon() + + +//LOADOUT ITEM +/datum/gear/mask/synthface/ + display_name = "Synth Facemask (Teshari)" + path = /obj/item/clothing/mask/synthfacemask + sort_category = "Xenowear" + whitelisted = SPECIES_TESHARI + cost = 1 + +/datum/gear/mask/synthface/New() + ..() + gear_tweaks += list(gear_tweak_free_color_choice) diff --git a/code/modules/clothing/spacesuits/rig/rig.dm b/code/modules/clothing/spacesuits/rig/rig.dm index 742a10ffabc..5f3b63d9542 100644 --- a/code/modules/clothing/spacesuits/rig/rig.dm +++ b/code/modules/clothing/spacesuits/rig/rig.dm @@ -610,6 +610,7 @@ species_icon = sprite_sheets[wearer.species.get_bodytype(wearer)] mob_icon = icon(icon = species_icon, icon_state = "[icon_state]") + chest.cut_overlays() if(installed_modules.len) for(var/obj/item/rig_module/module in installed_modules) if(module.suit_overlay) diff --git a/code/modules/entrepreneur/entrepreneur_items.dm b/code/modules/entrepreneur/entrepreneur_items.dm new file mode 100644 index 00000000000..b88a66a500f --- /dev/null +++ b/code/modules/entrepreneur/entrepreneur_items.dm @@ -0,0 +1,529 @@ +//Items for the entrepreneurs + +/obj/item/weapon/entrepreneur + name = "crystal ball" + icon = 'icons/obj/entrepreneur.dmi' + icon_state = "crystal_ball" + desc = "A perfect sphere that is partially translucent, allowing one to see into it's mysterious depths." + +/obj/item/weapon/entrepreneur/crystal_ball + +/obj/item/weapon/entrepreneur/horoscope + name = "horoscope book" + icon = 'icons/obj/entrepreneur.dmi' + icon_state = "horoscope" + desc = "A book with a years worth of horoscope readings in it, each one perfectly tailored to the phase of the stars and planets for every sign." + var/list/stars_list = list("Due to Mercuary being in retrograde, you are recieving a powerful energy.", + "The sun, Sol, of the Sol system, the birthplace of humanity, is your sign right now.", + "Jupiter is in antegrade, affecting your energy directly.", + "La luna has swung into virgo, be prepared!", + "The moon of Earth is lingering in your lazy, hazy twelfth house.", + "The moon starts the day in your intrepid ninth house!", + "Menhir hovers about your secluded third house.", + "Due to how Caledonia is in prograde, you are benefitting from it's energy.", + "Mercuary hovers in your second house, unfortunately.", + "Mars lingers in Scorpio now, this has a direct effect on your energy.", + "Menhir has swung into Leo!", + "The sun, Virgo-Erigone, lines up just right with Sol today.", + "Don't let the fact that Mars and Menhir are collaborating affect your day!", + "The moon is swinging between Cancer and Aries for you today.", + "Venus holds the key for you, resting right in your fourth house.", + "The wolftaur home of Elea lingers in Capricorn today.", + "The Salvoran star, Holloway, glows extra bright for you.", + "Mohges is caught in retrograde, Unathi are feeling it everywhere.", + "Qerr'balak is in the prograde of your first house.", + "Tal hovers above your ninth house.", + "Creatures from another plane watch you from afar.", + "Abundance in All Things Serene is in antegrade right now, you aren't benefiting from it any longer.", + "Paraiso is confiding with you today, you'll do well to bear it.", + "Sars Mara is no longer in Pisces, this will change your energy from the past few days.", + "The planet Salthan is bordering on Taurus now.", + "Procyon glistens for you betwixt the other stars.", + "Proxima Centauri is edging for you, take heed of it.", + "Sirius is cusping beyond your tenth house, you're getting a dose of its energy.", + "Sanctum and Infernum line up for you, chaos will no doubt ensue.", + "Infernum swings into Cancer for you, expect something firey!", + "Elysian stars turn their attention away from you now.", + "The stars of Elysium have caught you in their sights.", + "The burning planet of Zehtir in the virgo-erigone system twists against Gemini.", + "The purple soul of Geret Baht hungers for you.") + var/list/prediction_list = list("This leads you to listen to your own wants above those of others.", + "Prepare for this to lead you towards dark temptations.", + "Your soul is being filled with a glorious love for life.", + "Expect this to boost your confidence for a short while!", + "This is going to drain your energy and you will need to recover it somehow.", + "The effect will be strongest in the morning, but will settle down by the evening.", + "Don't let this get in the way of being your best self.", + "Consumption is on the menu for today, don't try to deny it.", + "An undeniable need for enhancement lingers within you.", + "A sense of adventure may blossom, but you are in danger of over-committing!", + "The sense of spontaneous spontaneity beats in your heart, don't let it go to waste!", + "Love is your world, combine with those around you, don't push them away.", + "Sullen moons make for a quiet day, but it is not as though you can't take advantage of the silence.", + "Some people are likely to be hidden away, people close to you.", + "Desire beats with in you, do not deny the call.", + "Beware of gluttony, it surrounds you today.", + "Power is unbalanced in your life, seize it for yourself lest others take it from you.", + "Joy, so much joy, all for yourself and to share with those you love.", + "You will make a new friend, it is someone you may not expect to encounter.", + "The universe pleads for you to plunder its riches, take what you deserve.", + "Punishment will be dished out today, and you are at the centre of it.", + "Allow yourself to make a real connection with another person today, it can only have good outcomes.", + "You may find yourself frustrated, but other people can take it from you.", + "Don't let yourself be consumed, the stars are tempting it.", + "Hope is on the horizon, chase it down and don't let it get away from you.", + "You might find yourself feeling drained due to those alignments or other factors in your life.", + "Pride will be the undoing of you today, but you can still harness it.", + "Perhaps you are feeling cute? Or perhaps something cute has taken your eye?", + "You will feel a growing warmth in your very core.", + "Expect a feeling of intense fulfilment.", + "There's a high likelyhood of you getting caught up in a sticky situation, one way or another.", + "Liberty is strong today, but beware it is fleeting for some.") + var/list/advice_list = list("You will have a lot of compassionate energy today. Use it on a needy friend.", + "It's wisest if you just admit to any anxieties or insecurities you have up front.", + "New beginnings are important, but don't lose your appreciation for old connections.", + "Your creativity could use a nice workout -- the kitchen is the perfect place for it.", + "Leaving too many questions unanswered will cause someone's imagination to spin in a weird direction.", + "Where you are going with someone isn't clear, but it's clear you are going together.", + "It's time to lavish yourself with the attention you deserve. Treat yourself right.", + "Ride your emotions like a roller coaster with your hands held straight up!", + "If you want to put one of your innovative ideas into action right now, you have to be open to the possibility of asking for help from someone in a higher position than you.", + "In any and all confrontations you have today, be sure to take the high road.", + "Indulge in nourishing meals that fuel your energy while allowing yourself a guilt-free treat every now and then.", + "Explore new cuisines and flavors to stimulate your taste buds and keep your meals exciting this month.", + "Whether dining alone or with loved ones, take a moment to appreciate the food on your plate and the hands that prepared it.", + "Tune into your body's needs and cravings without judgment, allowing yourself the freedom to enjoy food in a way that feels nourishing and satisfying.", + "Boldly pursue your goals and assert your authority in all endeavors today!", + "Try releasing control over outcomes and trusting in the universe to guide you towards your highest good.", + "Surrender to the natural rhythms of life and have faith that everything will unfold according to plan when the time is right.", + "Nurture your relationships and cultivate deep bonds with those who bring warmth and affection into your life and body.", + "Take the time to sit back and digest the things that have happened to you lately.", + "It never hurts to really absorb what you have taken in.", + "Focus on nurturing trust and intimacy in your relationships rather than letting insecurity devour you.", + "Keep your communication clear and your words sweet, lest you find yourself eaten alive by misunderstandings.", + "Remember to take time to savor life's simple pleasures along the way.", + "Be wary of being consumed by your own ego. Remember to share the spotlight and let others take a bite of the limelight too.", + "Listen to others reminisce about the past. You will gain some incredible insight.", + "Recognise your ability to communicate with others, and how this draws others to you.", + "Going out could be more trouble than it's worth. Protest with your vote or your wallet instead.", + "Walk side by side with others, eventually you will become one.", + "Take a break from your usual routine, focus on yourself and treat yourself a little.", + "This is a good day to get out and make connections with people. Maybe treat them a bit too!") + var/aries = "" + var/taurus = "" + var/gemini = "" + var/cancer = "" + var/leo = "" + var/virgo = "" + var/libra = "" + var/scorpio = "" + var/sagittarius = "" + var/capricorn = "" + var/aquarius = "" + var/pisces = "" + var/list/zodiacs = list("aries","taurus","gemini","cancer","leo","virgo","libra","scorpio","sagittarius","capricorn","aquarius","pisces") + +/obj/item/weapon/entrepreneur/horoscope/Initialize() + . = ..() + var/stars = pick(stars_list) + var/prediction = pick(prediction_list) + var/advice = pick(advice_list) + aries = "[stars] [prediction] [advice]" + + stars = pick(stars_list) + prediction = pick(prediction_list) + advice = pick(advice_list) + taurus = "[stars] [prediction] [advice]" + + stars = pick(stars_list) + prediction = pick(prediction_list) + advice = pick(advice_list) + gemini = "[stars] [prediction] [advice]" + + stars = pick(stars_list) + prediction = pick(prediction_list) + advice = pick(advice_list) + cancer = "[stars] [prediction] [advice]" + + stars = pick(stars_list) + prediction = pick(prediction_list) + advice = pick(advice_list) + leo = "[stars] [prediction] [advice]" + + stars = pick(stars_list) + prediction = pick(prediction_list) + advice = pick(advice_list) + virgo = "[stars] [prediction] [advice]" + + stars = pick(stars_list) + prediction = pick(prediction_list) + advice = pick(advice_list) + libra = "[stars] [prediction] [advice]" + + stars = pick(stars_list) + prediction = pick(prediction_list) + advice = pick(advice_list) + scorpio = "[stars] [prediction] [advice]" + + stars = pick(stars_list) + prediction = pick(prediction_list) + advice = pick(advice_list) + sagittarius = "[stars] [prediction] [advice]" + + stars = pick(stars_list) + prediction = pick(prediction_list) + advice = pick(advice_list) + capricorn = "[stars] [prediction] [advice]" + + stars = pick(stars_list) + prediction = pick(prediction_list) + advice = pick(advice_list) + aquarius = "[stars] [prediction] [advice]" + + stars = pick(stars_list) + prediction = pick(prediction_list) + advice = pick(advice_list) + pisces = "[stars] [prediction] [advice]" + +/obj/item/weapon/entrepreneur/horoscope/attack_self(var/mob/user) + var/zodiac = tgui_input_list(user, "Which of todays zodiacs do you want to read?", "Zodiac", zodiacs) + if(zodiac) + switch(zodiac) + if("aries") + to_chat(user, "Today's reading for Aries: [aries]") + if("taurus") + to_chat(user, "Today's reading for Taurus: [taurus]") + if("gemini") + to_chat(user, "Today's reading for Gemini: [gemini]") + if("cancer") + to_chat(user, "Today's reading for Cancer: [cancer]") + if("leo") + to_chat(user, "Today's reading for Leo: [leo]") + if("virgo") + to_chat(user, "Today's reading for Virgo: [virgo]") + if("libra") + to_chat(user, "Today's reading for Libra: [libra]") + if("scorpio") + to_chat(user, "Today's reading for Scorpio: [scorpio]") + if("sagittarius") + to_chat(user, "Today's reading for Sagittarius: [sagittarius]") + if("capricorn") + to_chat(user, "Today's reading for Capricorn: [capricorn]") + if("aquarius") + to_chat(user, "Today's reading for Aquarius: [aquarius]") + if("pisces") + to_chat(user, "Today's reading for Pisces: [pisces]") + +///////Dentist tools, basically just fluff for RP + +/obj/item/weapon/entrepreneur/dentist_mirror + name = "dental mirror" + icon = 'icons/obj/entrepreneur.dmi' + icon_state = "mirror" + desc = "A small mirror at the end of a short, stainless steel rod." + w_class = ITEMSIZE_TINY + +/obj/item/weapon/entrepreneur/dentist_mirror/attack(mob/M, mob/user) + if(user.a_intent == I_HELP) //A tad messy, but this should stop people from smacking their patients in surgery + to_chat(user, "You use the mirror to get a good look inside of [M]'s mouth.") + to_chat(M, "[user] uses a small mirror to look inside of your mouth.") + return 0 + ..() + +/obj/item/weapon/entrepreneur/dentist_probe + name = "dental probe" + icon = 'icons/obj/entrepreneur.dmi' + icon_state = "probe" + desc = "A short stainless steel rod that ends with a narrow pointy bit for poking." + w_class = ITEMSIZE_TINY + +/obj/item/weapon/entrepreneur/dentist_probe/attack(mob/M, mob/user) + if(user.a_intent == I_HELP) //A tad messy, but this should stop people from smacking their patients in surgery + to_chat(user, "You use the probe to poke about inside of [M]'s mouth.") + to_chat(M, "[user] examines the inside of your mouth with a sharp probe, it hurts a little being prodded.") + return 0 + ..() + +/obj/item/weapon/entrepreneur/dentist_sickle + name = "dental sickle" + icon = 'icons/obj/entrepreneur.dmi' + icon_state = "sickle" + desc = "A narrow, sharp hook at the end of a short, stainless steel rod." + w_class = ITEMSIZE_TINY + +/obj/item/weapon/entrepreneur/dentist_sickle/attack(mob/M, mob/user) + if(user.a_intent == I_HELP) //A tad messy, but this should stop people from smacking their patients in surgery + to_chat(user, "You loosen some stuck debris from [M]'s mouth with the hook.") + to_chat(M, "[user] uses a hook to scrape out something stuck in your mouth, it's pretty uncomfortable.") + return 0 + ..() + +/obj/item/weapon/entrepreneur/dentist_scaler + name = "dental scaler" + icon = 'icons/obj/entrepreneur.dmi' + icon_state = "scaler" + desc = "A flat and thin scraper at the end of a short, stainless steel rod." + w_class = ITEMSIZE_TINY + +/obj/item/weapon/entrepreneur/dentist_scaler/attack(mob/M, mob/user) + if(user.a_intent == I_HELP) //A tad messy, but this should stop people from smacking their patients in surgery + to_chat(user, "You scrape debris out from [M]'s mouth.") + to_chat(M, "[user] scrapes debris from out of your mouth.") + return 0 + ..() + +////// Exercise mat, yoga and trainer stuff + +/obj/item/weapon/bedsheet/pillow/exercise + name = "exercise mat" + desc = "A thick, flexible but tough mat designed for people to exercise on." + icon = 'icons/obj/entrepreneur.dmi' + icon_state = "exercise_mat" + +/obj/item/weapon/bedsheet/pillow/exercise/attackby(var/obj/item/component, mob/user as mob) + return + +/obj/item/weapon/entrepreneur/dumbbell + name = "dumbbell" + desc = "A small but heavy pair of weights connected by a bar, desgined to be held in one hand." + icon = 'icons/obj/entrepreneur.dmi' + icon_state = "dumbbell" + +/obj/item/weapon/entrepreneur/dumbbell/attack_self(var/mob/user) + var/mob/living/M = user + if(M.nutrition <= 100) + to_chat(user, "You are too hungry to exercise right now.") + return 0 + if(!do_after(user, 3 SECONDS, src, exclusive = TASK_USER_EXCLUSIVE)) + return 0 + M.adjust_nutrition(-10) + to_chat(user, "You successfully perform a [src] exercise!") + if(M.weight > 50) + M.weight -= 0.5 + +//////Paranormal Investigator stuff + +/obj/item/weapon/entrepreneur/emf + name = "EMF scanner" + desc = "A handheld device used for detecting disturbances to electromagnetic fields." + icon = 'icons/obj/entrepreneur.dmi' + icon_state = "emf" + var/emf = 5 + var/turf/last_used = 0 + var/emf_change = 0 + +/obj/item/weapon/entrepreneur/emf/attack_self(var/mob/user) + if(!last_used) + emf = rand(1,100) + last_used = get_turf(user) + var/current_used = get_turf(user) + var/mob/observer/spooky = locate() in current_used + if(last_used != current_used) + if(emf >= 100) + emf = 100 + if(emf <= 20) + emf = 20 + if(spooky) + emf_change = rand(-15,20) //Trend upwards but not by enough to prove ghosts actually exist + else + emf_change = rand(-20,15) //Trend downwards + last_used = get_turf(user) + emf = (emf + emf_change) + update_icon() + to_chat(user, "You update the EMF scanner and check the reading. It reads [emf]mG!") + +/obj/item/weapon/entrepreneur/emf/update_icon() + switch(emf) + if(-1000 to 20) + icon_state = "emf-0" + if(20 to 40) + icon_state = "emf-20" + if(40 to 60) + icon_state = "emf-40" + if(60 to 80) + icon_state = "emf-60" + if(80 to 1000) + icon_state = "emf-80" + return + +/obj/item/weapon/entrepreneur/spirit_board + name = "spirit board" + desc = "A wooden board with an alphabet at numbers on it, used to contact the dead. You need to use a glass to contact the spirit world. (It can be alt-clicked to decide the next letter in the sequence. This item does not canonise ghosts/souls in this setting, it's just a bit of fun!)" + icon = 'icons/obj/entrepreneur.dmi' + icon_state = "spirit_board" + var/list/possible_results = list("A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","Yes","No","1","2","3","4","5","6","7","8","9","0","Nothing") + var/next_result = 0 + var/ghost_enabled = 1 + +/obj/item/weapon/entrepreneur/spirit_board/attackby(obj/item/weapon/reagent_containers/food/drinks/W as obj, mob/living/user as mob) + if(!istype(user)) + return 0 + if(!istype(W)) + to_chat(user, "You need some sort of glass, bottle or cup to contact the spirit world.") + return 0 + var/result = 0 + if(!do_after(user, 3 SECONDS, src, exclusive = TASK_USER_EXCLUSIVE)) + return 0 + if(next_result) + result = next_result + else + result = pick(possible_results) + src.visible_message("[user] slides the [W] over to [result]!") + next_result = 0 + +/obj/item/weapon/entrepreneur/spirit_board/AltClick(mob/living/carbon/user) + if(!istype(user)) //admins can be cheeky + return 0 + next_result = tgui_input_list(user, "What should it land on next?", "Next result", possible_results) + +/obj/item/weapon/entrepreneur/spirit_board/attack_ghost(var/mob/observer/dead/user) + if(!ghost_enabled) + return + if(jobban_isbanned(user, "GhostRoles")) + to_chat(user, "You cannot interact with this board because you are banned from playing ghost roles.") + return + next_result = tgui_input_list(user, "What should it land on next?", "Next result", possible_results) + if(!is_admin(user)) //admins can bypass this for event stuff + if(prob(25)) + next_result = 0 //25% chance for the ghost to fail to manipulate the board + +// Spirit Healer stuff + +/obj/item/weapon/entrepreneur/crystal + name = "healing crystal" + desc = "A crystal with a powerful energy, apparantly, and is capable of healing the soul, apparantly." + icon = 'icons/obj/entrepreneur.dmi' + icon_state = "crystal_pink" + w_class = ITEMSIZE_TINY + +/obj/item/weapon/entrepreneur/crystal/Initialize() + . = ..() + var/list/colour_choice = list("crystal_pink","crystal_blue","crystal_green","crystal_orange","crystal_dblue","crystal_purple") + icon_state = pick(colour_choice) + update_icon() + +/obj/item/weapon/reagent_containers/glass/bottle/essential_oil + name = "essential oils" + desc = "A small bottle of various plant extracts said to improve upon a person's health as an alternative form of medicine." + icon = 'icons/obj/entrepreneur.dmi' + icon_state = "oil" + prefill = list("essential_oil" = 60) + +// Masseuse + +/obj/item/roller/massage + name = "massage bed" + desc = "A collapsed roller massage bed that can be carried around. When deployed, it can be locked in place (with alt-click)." + icon = 'icons/obj/entrepreneur.dmi' + icon_state = "folded_rollerbed" + rollertype = /obj/item/roller/massage + bedtype = /obj/structure/bed/roller/massage + +/obj/structure/bed/roller/massage + name = "massage bed" + desc = "A portable bed-on-wheels wish extra padding for getting people comfortable for massages. It can be locked in place (with alt-click)." + icon = 'icons/obj/entrepreneur.dmi' + icon_state = "rollerbed" + rollertype = /obj/item/roller/massage + bedtype = /obj/structure/bed/roller/massage + +/obj/structure/bed/roller/massage/AltClick(mob/living/carbon/user) + if(anchored) + anchored = 0 + src.visible_message("[user] turns the breaks off on the [src]!") + else if(!anchored) + anchored = 1 + src.visible_message("[user] turns the breaks on for the [src]!") + +/obj/structure/bed/roller/massage/buckle_mob(mob/living/M) + ..() + M.set_dir(1) + +//Magnifying glass + +/obj/item/weapon/entrepreneur/magnifying_glass + name = "magnifying glass" + desc = "A curved lense for looking at things a little closer." + icon = 'icons/obj/entrepreneur.dmi' + icon_state = "magnifying_glass" + w_class = ITEMSIZE_SMALL + +/obj/item/weapon/entrepreneur/magnifying_glass/afterattack(atom/T, mob/living/user as mob) + if(!T.desc) + return + user.visible_message("\The [user] examines the \the [T] with \the [src]!") + to_chat(user, "[T.desc]") + +// Streamer and influencer + +/obj/item/device/tvcamera/streamer + name = "streamer camera drone" + channel = "Virgo Live Stream" + +/obj/item/device/camera/selfie + name = "selfie stick" + desc = "A long stick with a camera on the end, designed for taking pictures of one's self, but could awkwardly be turned to take pictures of other things too!" + icon = 'icons/obj/entrepreneur.dmi' + icon_state = "selfie" + item_state = "selfie" + icon_on = "selfie" + icon_off = "selfie_off" + +// Containers + +/obj/item/weapon/storage/box/fortune_teller + name = "fortune teller kit" + desc = "A kit containing everything that a fortune teller needs." + icon = 'icons/obj/entrepreneur.dmi' + icon_state = "fortune_teller" + starts_with = list(/obj/item/weapon/entrepreneur/horoscope, /obj/item/weapon/deck/tarot, /obj/item/weapon/entrepreneur/crystal_ball, /obj/item/device/ticket_printer/train) + +/obj/item/weapon/storage/box/dentist + name = "dentist kit" + desc = "A kit containing everything that a dentist needs." + icon = 'icons/obj/entrepreneur.dmi' + icon_state = "dentist" + starts_with = list(/obj/item/weapon/entrepreneur/dentist_mirror, /obj/item/weapon/entrepreneur/dentist_probe, /obj/item/weapon/entrepreneur/dentist_sickle, /obj/item/weapon/entrepreneur/dentist_scaler, /obj/item/device/flashlight/pen, /obj/item/device/ticket_printer/train) + +/obj/item/weapon/storage/box/fitness_trainer + name = "exercise kit" + desc = "A kit containing everything that a fitness trainer needs." + icon = 'icons/obj/entrepreneur.dmi' + icon_state = "fitness_trainer" + starts_with = list(/obj/item/weapon/bedsheet/pillow/exercise, /obj/item/weapon/entrepreneur/dumbbell, /obj/item/weapon/entrepreneur/dumbbell, /obj/item/weapon/reagent_containers/food/snacks/candy/proteinbar, /obj/item/device/ticket_printer/train) + +/obj/item/weapon/storage/box/yoga_teacher + name = "yoga kit" + desc = "A kit containing everything that a yoga teacher needs." + icon = 'icons/obj/entrepreneur.dmi' + icon_state = "yoga_teacher" + starts_with = list(/obj/item/weapon/bedsheet/pillow/exercise, /obj/item/weapon/bedsheet/pillow/exercise, /obj/item/weapon/reagent_containers/food/snacks/fruitbar, /obj/item/device/ticket_printer/train) + +/obj/item/weapon/storage/box/paranormal_investigator + name = "ghost hunting kit" + desc = "A kit containing everything that a paranormal investigator needs." + icon = 'icons/obj/entrepreneur.dmi' + icon_state = "paranormal_investigator" + starts_with = list(/obj/item/weapon/entrepreneur/emf, /obj/item/weapon/entrepreneur/spirit_board, /obj/item/weapon/reagent_containers/food/drinks/glass2/shot, /obj/item/device/ticket_printer/train) + +/obj/item/weapon/storage/box/spirit_healer + name = "exercise kit" + desc = "A kit containing everything that a spirit healer needs." + icon = 'icons/obj/entrepreneur.dmi' + icon_state = "spirit_healer" + starts_with = list(/obj/item/weapon/entrepreneur/crystal, /obj/item/weapon/entrepreneur/crystal, /obj/item/weapon/entrepreneur/crystal, /obj/item/weapon/entrepreneur/crystal, /obj/item/weapon/entrepreneur/crystal, /obj/item/weapon/reagent_containers/glass/bottle/essential_oil, /obj/item/device/ticket_printer/train) + +/obj/item/weapon/storage/box/private_investigator + name = "investigator kit" + desc = "A kit containing everything that a private eye needs." + icon = 'icons/obj/entrepreneur.dmi' + icon_state = "private_investigator" + starts_with = list(/obj/item/device/taperecorder, /obj/item/device/tape, /obj/item/device/tape, /obj/item/device/camera, /obj/item/sticky_pad, /obj/item/weapon/entrepreneur/magnifying_glass, /obj/item/device/ticket_printer/train) + +/obj/item/weapon/storage/box/stylist + name = "stylist kit" + desc = "A kit containing everything that a stylist needs." + icon = 'icons/obj/entrepreneur.dmi' + icon_state = "stylist" + starts_with = list(/obj/item/weapon/makeover, /obj/item/weapon/lipstick/random, /obj/item/weapon/nailpolish, /obj/item/weapon/nailpolish_remover, /obj/item/weapon/haircomb, /obj/item/clothing/head/hairnet, /obj/item/device/ticket_printer/train) + diff --git a/code/modules/food/drinkingglass/metaglass_vr.dm b/code/modules/food/drinkingglass/metaglass_vr.dm index fe86ff962a9..ebaf4670e63 100644 --- a/code/modules/food/drinkingglass/metaglass_vr.dm +++ b/code/modules/food/drinkingglass/metaglass_vr.dm @@ -239,3 +239,7 @@ glass_icon_state = "burnout" glass_center_of_mass = list("x"=16, "y"=8) glass_icon_file = 'icons/obj/drinks_vr.dmi' + +/datum/reagent/ethanol/manager_summoner + glass_icon_state = "manager_summoner" + glass_icon_file = 'icons/obj/drinks_vr.dmi' diff --git a/code/modules/mob/living/carbon/human/species/station/traits_vr/negative.dm b/code/modules/mob/living/carbon/human/species/station/traits_vr/negative.dm index adc3c051f4d..84fba1d1cba 100644 --- a/code/modules/mob/living/carbon/human/species/station/traits_vr/negative.dm +++ b/code/modules/mob/living/carbon/human/species/station/traits_vr/negative.dm @@ -6,30 +6,40 @@ desc = "Allows you to move slower on average than baseline." cost = -2 var_changes = list("slowdown" = 0.5) + banned_species = list(SPECIES_ALRAUNE, SPECIES_SHADEKIN_CREW, SPECIES_DIONA, SPECIES_UNATHI) //These are already this slow. + custom_only = FALSE /datum/trait/negative/speed_slow_plus name = "Slowdown, Major" desc = "Allows you to move MUCH slower on average than baseline." cost = -3 var_changes = list("slowdown" = 1.0) + custom_only = FALSE + banned_species = list(SPECIES_DIONA) //Diona are even slower than this /datum/trait/negative/weakling name = "Weakling" desc = "Causes heavy equipment to slow you down more when carried." cost = -1 var_changes = list("item_slowdown_mod" = 1.5) + custom_only = FALSE + banned_species = list(SPECIES_SHADEKIN_CREW, SPECIES_TESHARI) //These are already this weak. /datum/trait/negative/weakling_plus name = "Weakling, Major" desc = "Allows you to carry heavy equipment with much more slowdown." cost = -2 var_changes = list("item_slowdown_mod" = 2.0) + custom_only = FALSE + banned_species = list(SPECIES_TESHARI) //These are already this weak. /datum/trait/negative/endurance_low name = "Low Endurance" desc = "Reduces your maximum total hitpoints to 75." cost = -2 var_changes = list("total_health" = 75) + custom_only = FALSE + banned_species = list(SPECIES_TESHARI, SPECIES_SHADEKIN_CREW) //These are already this weak. /datum/trait/negative/endurance_low/apply(var/datum/species/S,var/mob/living/carbon/human/H) ..() @@ -40,6 +50,8 @@ desc = "Reduces your maximum total hitpoints to 50." cost = -3 //Teshari HP. This makes the person a lot more suseptable to getting stunned, killed, etc. var_changes = list("total_health" = 50) + custom_only = FALSE + banned_species = list(SPECIES_TESHARI) //These are already this weak. /datum/trait/negative/endurance_very_low/apply(var/datum/species/S,var/mob/living/carbon/human/H) ..() @@ -49,18 +61,23 @@ name = "Brute Weakness, Minor" desc = "Increases damage from brute damage sources by 15%" cost = -1 + custom_only = FALSE var_changes = list("brute_mod" = 1.15) + banned_species = list(SPECIES_TESHARI, SPECIES_TAJ, SPECIES_ZADDAT, SPECIES_SHADEKIN_CREW) //These are already this weak. /datum/trait/negative/brute_weak name = "Brute Weakness" desc = "Increases damage from brute damage sources by 25%" cost = -2 + custom_only = FALSE var_changes = list("brute_mod" = 1.25) + banned_species = list(SPECIES_TESHARI, SPECIES_SHADEKIN_CREW) //These are already this weak. /datum/trait/negative/brute_weak_plus name = "Brute Weakness, Major" desc = "Increases damage from brute damage sources by 50%" cost = -3 + custom_only = FALSE var_changes = list("brute_mod" = 1.5) /datum/trait/negative/minor_burn_weak @@ -128,6 +145,7 @@ cost = -1 var_changes = list("trauma_mod" = 2) can_take = ORGANICS + custom_only = FALSE /datum/trait/negative/breathes cost = -2 diff --git a/code/modules/mob/living/carbon/human/species/station/traits_vr/neutral.dm b/code/modules/mob/living/carbon/human/species/station/traits_vr/neutral.dm index 9cc1c53f2ea..53e15ba08ee 100644 --- a/code/modules/mob/living/carbon/human/species/station/traits_vr/neutral.dm +++ b/code/modules/mob/living/carbon/human/species/station/traits_vr/neutral.dm @@ -52,6 +52,8 @@ autohiss_exempt = list(LANGUAGE_UNATHI)) excludes = list(/datum/trait/neutral/autohiss_tajaran, /datum/trait/neutral/autohiss_zaddat) + custom_only = FALSE + banned_species = list(SPECIES_TAJARAN, SPECIES_UNATHI, SPECIES_ZADDAT) /datum/trait/neutral/autohiss_tajaran name = "Autohiss (Tajaran)" @@ -63,6 +65,8 @@ ), autohiss_exempt = list(LANGUAGE_SIIK,LANGUAGE_AKHANI,LANGUAGE_ALAI)) excludes = list(/datum/trait/neutral/autohiss_unathi, /datum/trait/neutral/autohiss_zaddat) + custom_only = FALSE + banned_species = list(SPECIES_TAJARAN, SPECIES_UNATHI, SPECIES_ZADDAT) /datum/trait/neutral/autohiss_zaddat name = "Autohiss (Zaddat)" @@ -81,6 +85,8 @@ ), autohiss_exempt = list(LANGUAGE_ZADDAT,LANGUAGE_VESPINAE)) excludes = list(/datum/trait/neutral/autohiss_tajaran, /datum/trait/neutral/autohiss_unathi) + custom_only = FALSE + banned_species = list(SPECIES_TAJARAN, SPECIES_UNATHI, SPECIES_ZADDAT) /datum/trait/neutral/bloodsucker name = "Bloodsucker, Obligate" diff --git a/code/modules/mob/living/carbon/human/species/station/traits_vr/positive.dm b/code/modules/mob/living/carbon/human/species/station/traits_vr/positive.dm index 4dc7fe262b8..a9b6c0a495f 100644 --- a/code/modules/mob/living/carbon/human/species/station/traits_vr/positive.dm +++ b/code/modules/mob/living/carbon/human/species/station/traits_vr/positive.dm @@ -6,24 +6,32 @@ desc = "Allows you to move faster on average than baseline." cost = 4 var_changes = list("slowdown" = -0.5) +// banned_species = list(SPECIES_ALRAUNE, SPECIES_SHADEKIN_CREW, SPECIES_TESHARI, SPECIES_TAJ, SPECIES_DIONA, SPECIES_UNATHI) //Either not applicable or buffs ruin species flavour/balance +// custom_only = FALSE //Keeping these in comments in case we decide to open them up in future, so the species are already organised. /datum/trait/positive/hardy name = "Hardy" desc = "Allows you to carry heavy equipment with less slowdown." cost = 1 var_changes = list("item_slowdown_mod" = 0.5) + custom_only = FALSE + banned_species = list(SPECIES_ALRAUNE, SPECIES_TESHARI, SPECIES_UNATHI, SPECIES_DIONA, SPECIES_PROMETHEAN, SPECIES_PROTEAN) //Either not applicable or buffs are too strong /datum/trait/positive/hardy_plus name = "Hardy, Major" desc = "Allows you to carry heavy equipment with almost no slowdown." cost = 2 var_changes = list("item_slowdown_mod" = 0.25) + banned_species = list(SPECIES_ALRAUNE, SPECIES_TESHARI, SPECIES_UNATHI, SPECIES_DIONA, SPECIES_PROMETHEAN, SPECIES_PROTEAN) //Either not applicable or buffs are too strong + custom_only = FALSE /datum/trait/positive/endurance_high name = "High Endurance" desc = "Increases your maximum total hitpoints to 125" cost = 4 var_changes = list("total_health" = 125) + custom_only = FALSE + banned_species = list(SPECIES_TESHARI, SPECIES_UNATHI, SPECIES_SHADEKIN_CREW) //Either not applicable or buffs are too strong /datum/trait/positive/endurance_high/apply(var/datum/species/S,var/mob/living/carbon/human/H) ..() @@ -46,12 +54,16 @@ desc = "Allows you to see a short distance in the dark." cost = 1 var_changes = list("darksight" = 5, "flash_mod" = 1.1) + custom_only = FALSE + banned_species = list(SPECIES_TAJARAN, SPECIES_SHADEKIN_CREW, SPECIES_SHADEKIN, SPECIES_XENOHYBRID, SPECIES_VULPKANIN, SPECIES_XENO, SPECIES_XENOCHIMERA, SPECIES_VASILISSAN, SPECIES_WEREBEAST) //These species already have strong darksight by default. /datum/trait/positive/darksight_plus name = "Darksight, Major" desc = "Allows you to see in the dark for the whole screen." cost = 2 var_changes = list("darksight" = 8, "flash_mod" = 1.2) + custom_only = FALSE + banned_species = list(SPECIES_TAJARAN, SPECIES_SHADEKIN_CREW, SPECIES_SHADEKIN, SPECIES_XENOHYBRID, SPECIES_VULPKANIN, SPECIES_XENO, SPECIES_XENOCHIMERA, SPECIES_VASILISSAN, SPECIES_WEREBEAST) //These species already have strong darksight by default. /datum/trait/positive/melee_attack name = "Special Attack: Sharp Melee" // Trait Organization for easier browsing. TODO: Proper categorization of 'health/ability/resist/etc' @@ -76,6 +88,8 @@ desc = "Adds 15% resistance to brute damage sources." cost = 2 var_changes = list("brute_mod" = 0.85) + custom_only = FALSE + banned_species = list(SPECIES_TESHARI, SPECIES_UNATHI, SPECIES_XENOCHIMERA, SPECIES_VASILISSAN, SPECIES_WEREBEAST) //Most of these are already this resistant or stronger, or it'd be way too much of a boost for tesh. /datum/trait/positive/brute_resist name = "Brute Resist" @@ -211,6 +225,7 @@ var_changes = list("trauma_mod" = 0.85) excludes = list(/datum/trait/negative/neural_hypersensitivity) can_take = ORGANICS + custom_only = FALSE /datum/trait/positive/throw_resistance name = "Firm Body" diff --git a/code/modules/mob/living/silicon/robot/dogborg/dog_modules_vr.dm b/code/modules/mob/living/silicon/robot/dogborg/dog_modules_vr.dm index c2d00548c16..c1439e71fec 100644 --- a/code/modules/mob/living/silicon/robot/dogborg/dog_modules_vr.dm +++ b/code/modules/mob/living/silicon/robot/dogborg/dog_modules_vr.dm @@ -297,7 +297,8 @@ if(src.emagged) var/mob/living/silicon/robot/R = user var/mob/living/L = target - if(R.cell.charge <= 666) + if(!R.use_direct_power(666, 100)) + to_chat(user, span_warning("Warning, low power detected. Aborting action.")) return L.Stun(1) L.Weaken(1) @@ -305,7 +306,6 @@ L.visible_message("[user] has shocked [L] with its tongue!", \ "[user] has shocked you with its tongue! You can feel the betrayal.") playsound(src, 'sound/weapons/Egloves.ogg', 50, 1, -1) - R.cell.charge -= 666 else user.visible_message("\The [user] affectionately licks all over \the [target]'s face!", "You affectionately lick all over \the [target]'s face!") playsound(src, 'sound/effects/attackblob.ogg', 50, 1) @@ -406,6 +406,7 @@ desc = "Leap at your target to momentarily stun them." force = 0 throwforce = 0 + var/bluespace = FALSE /obj/item/weapon/dogborg/pounce/New() ..() @@ -413,14 +414,16 @@ /obj/item/weapon/dogborg/pounce/attack_self(mob/user) var/mob/living/silicon/robot/R = user - R.leap() + R.leap(bluespace) -/mob/living/silicon/robot/proc/leap() +/mob/living/silicon/robot/proc/leap(var/bluespace = FALSE) if(last_special > world.time) to_chat(src, "Your leap actuators are still recharging.") return - if(cell.charge < 1000) + var/power_cost = bluespace ? 1000 : 750 + var/minimum_power = bluespace ? 2500 : 1000 + if(cell.charge < minimum_power) to_chat(src, "Cell charge too low to continue.") return @@ -429,7 +432,8 @@ return var/list/choices = list() - for(var/mob/living/M in view(3,src)) + var/leap_distance = bluespace ? 5 : 3 + for(var/mob/living/M in view(leap_distance,src)) if(!istype(M,/mob/living/silicon)) choices += M choices -= src @@ -438,7 +442,16 @@ if(!T || !src || src.stat) return - if(get_dist(get_turf(T), get_turf(src)) > 3) return + if(get_dist(get_turf(T), get_turf(src)) > leap_distance) return + + if(ishuman(T)) + var/mob/living/carbon/human/H = T + if(H.get_species() == SPECIES_SHADEKIN && (H.ability_flags & AB_PHASE_SHIFTED)) + power_cost *= 2 + + if(!use_direct_power(power_cost, minimum_power - power_cost)) + to_chat(src, span_warning("Warning, low power detected. Aborting action.")) + return if(last_special > world.time) return @@ -452,12 +465,16 @@ pixel_y = pixel_y + 10 src.visible_message("\The [src] leaps at [T]!") - src.throw_at(get_step(get_turf(T),get_turf(src)), 4, 1, src) + if(bluespace) + src.forceMove(get_turf(T)) + T.hitby(src) + else + src.throw_at(get_step(get_turf(T),get_turf(src)), 4, 1, src) playsound(src, 'sound/mecha/mechstep2.ogg', 50, 1) pixel_y = default_pixel_y - cell.charge -= 750 - sleep(5) + if(!bluespace) + sleep(5) if(status_flags & LEAPING) status_flags &= ~LEAPING @@ -470,6 +487,7 @@ if(H.species.lightweight == 1) H.Weaken(3) return + var/armor_block = run_armor_check(T, "melee") var/armor_soak = get_armor_soak(T, "melee") T.apply_damage(20, HALLOSS,, armor_block, armor_soak) diff --git a/code/modules/mob/living/silicon/robot/inventory.dm b/code/modules/mob/living/silicon/robot/inventory.dm index de42bb8d171..98e91cdcdd9 100644 --- a/code/modules/mob/living/silicon/robot/inventory.dm +++ b/code/modules/mob/living/silicon/robot/inventory.dm @@ -51,6 +51,7 @@ module_state_3:loc = module module_state_3 = null inv3.icon_state = "inv3" + after_equip() update_icon() hud_used.update_robot_modules_display() @@ -84,6 +85,7 @@ module_state_3:loc = module module_state_3 = null inv3.icon_state = "inv3" + after_equip() update_icon() /mob/living/silicon/robot/proc/activated(obj/item/O) @@ -252,6 +254,23 @@ sight_mode |= module_state_3:sight_mode else to_chat(src, "You need to disable a module first!") + return + after_equip() + +/mob/living/silicon/robot/proc/after_equip() + if(sight_mode & BORGANOMALOUS) + var/obj/item/weapon/dogborg/pounce/pounce = has_upgrade_module(/obj/item/weapon/dogborg/pounce) + if(pounce) + pounce.name = "bluespace pounce" + pounce.icon_state = "bluespace_pounce" + pounce.bluespace = TRUE + else + var/obj/item/weapon/dogborg/pounce/pounce = has_upgrade_module(/obj/item/weapon/dogborg/pounce) + if(pounce) + pounce.name = initial(pounce.name) + pounce.icon_state = initial(pounce.icon_state) + pounce.desc = initial(pounce.desc) + pounce.bluespace = initial(pounce.bluespace) /mob/living/silicon/robot/put_in_hands(var/obj/item/W) // No hands. W.loc = get_turf(src) @@ -271,4 +290,4 @@ if(module_state_2) . += module_state_2 if(module_state_3) - . += module_state_3 \ No newline at end of file + . += module_state_3 diff --git a/code/modules/mob/living/silicon/robot/life.dm b/code/modules/mob/living/silicon/robot/life.dm index e69de8b3fdf..7bc8c4f7a2d 100644 --- a/code/modules/mob/living/silicon/robot/life.dm +++ b/code/modules/mob/living/silicon/robot/life.dm @@ -188,6 +188,10 @@ src.see_in_dark = 8 src.see_invisible = SEE_INVISIBLE_LEVEL_TWO fullbright = TRUE + else if (src.sight_mode & BORGANOMALOUS) + src.see_in_dark = 8 + src.see_invisible = INVISIBILITY_SHADEKIN + fullbright = TRUE else if (!seedarkness) src.sight &= ~SEE_MOBS src.sight &= ~SEE_TURFS diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 284331ce8cb..16615288388 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -1304,6 +1304,20 @@ return 1 return 0 +// Function to directly drain power from the robot's cell, allows to set a minimum level beneath which +// abilities can no longer be used +/mob/living/silicon/robot/proc/use_direct_power(var/amount = 0, var/lower_limit = 0) + // No cell inserted + if(!cell) + return FALSE + + // Power cell does not have sufficient charge to remain above the power limit. + if(cell.charge - (amount + lower_limit) <= 0) + return FALSE + + cell.charge -= amount + return TRUE + /mob/living/silicon/robot/binarycheck() if(get_restraining_bolt()) return FALSE @@ -1444,7 +1458,7 @@ G.drop_item_nm() /mob/living/silicon/robot/disable_spoiler_vision() - if(sight_mode & (BORGMESON|BORGMATERIAL|BORGXRAY)) // Whyyyyyyyy have seperate defines. + if(sight_mode & (BORGMESON|BORGMATERIAL|BORGXRAY|BORGANOMALOUS)) // Whyyyyyyyy have seperate defines. var/i = 0 // Borg inventory code is very . . interesting and as such, unequiping a specific item requires jumping through some (for) loops. var/current_selection_index = get_selected_module() // Will be 0 if nothing is selected. @@ -1452,7 +1466,7 @@ i++ if(istype(thing, /obj/item/borg/sight)) var/obj/item/borg/sight/S = thing - if(S.sight_mode & (BORGMESON|BORGMATERIAL|BORGXRAY)) + if(S.sight_mode & (BORGMESON|BORGMATERIAL|BORGXRAY|BORGANOMALOUS)) select_module(i) uneq_active() @@ -1558,6 +1572,16 @@ /mob/living/silicon/robot/proc/has_no_prod_upgrade(var/given_type) if(given_type == /obj/item/borg/upgrade/no_prod/toygun) return has_upgrade_module(/obj/item/weapon/gun/projectile/cyborgtoy) + if(given_type == /obj/item/borg/upgrade/no_prod/vision_xray) + return has_upgrade_module(/obj/item/borg/sight/xray) + if(given_type == /obj/item/borg/upgrade/no_prod/vision_thermal) + return has_upgrade_module(/obj/item/borg/sight/thermal) + if(given_type == /obj/item/borg/upgrade/no_prod/vision_meson) + return has_upgrade_module(/obj/item/borg/sight/meson) + if(given_type == /obj/item/borg/upgrade/no_prod/vision_material) + return has_upgrade_module(/obj/item/borg/sight/material) + if(given_type == /obj/item/borg/upgrade/no_prod/vision_anomalous) + return has_upgrade_module(/obj/item/borg/sight/anomalous) return null /mob/living/silicon/robot/proc/has_upgrade(var/given_type) diff --git a/code/modules/mob/living/silicon/robot/robot_damage.dm b/code/modules/mob/living/silicon/robot/robot_damage.dm index 749a5d2c5d0..89d4a69c708 100644 --- a/code/modules/mob/living/silicon/robot/robot_damage.dm +++ b/code/modules/mob/living/silicon/robot/robot_damage.dm @@ -76,9 +76,7 @@ var/absorb_burn = burn*shield.shield_level var/cost = (absorb_brute+absorb_burn) * 25 - cell.charge -= cost - if(cell.charge <= 0) - cell.charge = 0 + if(!use_direct_power(cost, 200)) to_chat(src, "[span_red("Your shield has overloaded!")]") else brute -= absorb_brute @@ -123,9 +121,7 @@ var/absorb_burn = burn*shield.shield_level var/cost = (absorb_brute+absorb_burn) * 25 - cell.charge -= cost - if(cell.charge <= 0) - cell.charge = 0 + if(!use_direct_power(cost, 200)) to_chat(src, "[span_red("Your shield has overloaded!")]") else brute -= absorb_brute diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index a27d6b396e0..f8990712473 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1175,7 +1175,7 @@ if(client) if(a_intent == I_HELP || client.prefs.throwmode_loud) src.visible_message("[src] relaxes from their ready stance.","You relax from your ready stance.") - if(src.throw_icon) //in case we don't have the HUD and we use the hotkey + if(src.throw_icon && !issilicon(src)) //in case we don't have the HUD and we use the hotkey. Silicon use this for something else. Do not overwrite their HUD icon src.throw_icon.icon_state = "act_throw_off" /mob/proc/throw_mode_on() @@ -1186,7 +1186,7 @@ src.visible_message("[src] winds up to throw [get_active_hand()]!","You wind up to throw [get_active_hand()].") else src.visible_message("[src] looks ready to catch anything thrown at them!","You get ready to catch anything thrown at you.") - if(src.throw_icon) + if(src.throw_icon && !issilicon(src)) // Silicon use this for something else. Do not overwrite their HUD icon src.throw_icon.icon_state = "act_throw_on" /mob/proc/isSynthetic() diff --git a/code/modules/projectiles/guns/launcher/confetti.dm b/code/modules/projectiles/guns/launcher/confetti.dm index a6924b34347..f6cffa45cb0 100644 --- a/code/modules/projectiles/guns/launcher/confetti.dm +++ b/code/modules/projectiles/guns/launcher/confetti.dm @@ -76,26 +76,22 @@ var/choice = tgui_alert(usr, "Load the Party Canon with?", "Change What?", list("Confetti","Banana Peel","Cream Pie")) if(!choice) return + if(istype(usr,/mob/living/silicon/robot)) + var/mob/living/silicon/robot/R = usr + if(!R.use_direct_power(200, 400)) + to_chat(R, span_warning("Warning, low power detected. Aborting action.")) + return playsound(src, 'sound/effects/pop.ogg', 50, 0) switch(choice) if("Confetti") chambered = new /obj/item/weapon/grenade/confetti/party_ball to_chat(usr, span_blue("Confetti loaded.")) - if(istype(usr,/mob/living/silicon/robot)) - var/mob/living/silicon/robot/R = usr - R.cell.charge -= 200 if("Banana Peel") chambered = new /obj/item/weapon/bananapeel to_chat(usr, span_blue("Banana peel loaded.")) - if(istype(usr,/mob/living/silicon/robot)) - var/mob/living/silicon/robot/R = usr - R.cell.charge -= 200 if("Cream Pie") chambered = new /obj/item/weapon/reagent_containers/food/snacks/pie to_chat(usr, span_blue("Banana cream pie loaded.")) - if(istype(usr,/mob/living/silicon/robot)) - var/mob/living/silicon/robot/R = usr - R.cell.charge -= 200 else to_chat(usr, span_red("The [src] is already loaded!")) diff --git a/code/modules/reagents/reactions/instant/drinks_vr.dm b/code/modules/reagents/reactions/instant/drinks_vr.dm index 319a6011b96..2fcedd4e532 100644 --- a/code/modules/reagents/reactions/instant/drinks_vr.dm +++ b/code/modules/reagents/reactions/instant/drinks_vr.dm @@ -231,4 +231,11 @@ id = "strawberry_protein_shake" result = "strawberry_protein_shake" required_reagents = list("water" = 5, "strawberry_protein_powder" = 1) - result_amount = 5 \ No newline at end of file + result_amount = 5 + +/decl/chemical_reaction/instant/drinks/manager_summoner + name = "Manager Summoner" + id = "manager_summoner" + result = "manager_summoner" + required_reagents = list("margarita" = 1, "redwine" = 1, "essential_oil" = 1) + result_amount = 3 diff --git a/code/modules/reagents/reagents/food_drinks_vr.dm b/code/modules/reagents/reagents/food_drinks_vr.dm index 8b83c192d85..01514b4ee4c 100644 --- a/code/modules/reagents/reagents/food_drinks_vr.dm +++ b/code/modules/reagents/reagents/food_drinks_vr.dm @@ -396,6 +396,17 @@ glass_name = "Giant Beer" glass_desc = "The Neo Detroit beer and ale cocktail, perfect for your average drunk." +/datum/reagent/ethanol/manager_summoner + name = "Manager Summoner" + id = "manager_summoner" + description = "A horrifying cocktail for those who desperately want feel above their peers." + taste_description = "bitter and sweet, with a hint of superiority" + strength = 30 + color = "#c9716b" + + glass_name = "Manager Summoner" + glass_desc = "The dreaded red juice of those who insist on taking advantage of minor positions of power to make the lives of bar staff unbearable." + /datum/reagent/drink/sweettea name = "Sweet Tea" id = "sweettea" diff --git a/code/modules/reagents/reagents/other.dm b/code/modules/reagents/reagents/other.dm index bc12d163fe1..32b9827ba58 100644 --- a/code/modules/reagents/reagents/other.dm +++ b/code/modules/reagents/reagents/other.dm @@ -661,7 +661,7 @@ description = "Liquified carpet fibers, ready for dyeing." reagent_state = LIQUID color = "#b51d05" - taste_description = "carpet" + taste_description = "carpet" /datum/reagent/carpet/black name = "Liquid Black Carpet" @@ -669,7 +669,7 @@ description = "Black Carpet Fibers, ready for reinforcement." reagent_state = LIQUID color = "#000000" - taste_description = "rare and ashy carpet" + taste_description = "rare and ashy carpet" /datum/reagent/carpet/blue name = "Liquid Blue Carpet" @@ -677,7 +677,7 @@ description = "Blue Carpet Fibers, ready for reinforcement." reagent_state = LIQUID color = "#3f4aee" - taste_description = "commanding carpet" + taste_description = "commanding carpet" /datum/reagent/carpet/turquoise name = "Liquid Turquoise Carpet" @@ -685,7 +685,7 @@ description = "Turquoise Carpet Fibers, ready for reinforcement." reagent_state = LIQUID color = "#0592b5" - taste_description = "water-logged carpet" + taste_description = "water-logged carpet" /datum/reagent/carpet/sblue name = "Liquid Silver Blue Carpet" @@ -693,7 +693,7 @@ description = "Silver Blue Carpet Fibers, ready for reinforcement." reagent_state = LIQUID color = "#0011ff" - taste_description = "sterile and medicinal carpet" + taste_description = "sterile and medicinal carpet" /datum/reagent/carpet/clown name = "Liquid Clown Carpet" @@ -701,7 +701,7 @@ description = "Clown Carpet Fibers.... No clowns were harmed in the making of this." reagent_state = LIQUID color = "#e925be" - taste_description = "clown shoes and banana peels" + taste_description = "clown shoes and banana peels" /datum/reagent/carpet/purple name = "Liquid Purple Carpet" @@ -709,7 +709,7 @@ description = "Purple Carpet Fibers, ready for reinforcement." reagent_state = LIQUID color = "#a614d3" - taste_description = "bleeding edge carpet research" + taste_description = "bleeding edge carpet research" /datum/reagent/carpet/orange name = "Liquid Orange Carpet" @@ -717,4 +717,12 @@ description = "Orange Carpet Fibers, ready for reinforcement." reagent_state = LIQUID color = "#f16e16" - taste_description = "extremely overengineered carpet" \ No newline at end of file + taste_description = "extremely overengineered carpet" + +/datum/reagent/essential_oil + name = "Essential Oils" + id = "essential_oil" + description = "A slurry of compounds that contains the basic requirements for life." + taste_description = "a mixture of thick, sweet, salty, salty and spicy flavours that all blend together to not be very nice at all" + reagent_state = LIQUID + color = "#e8e2b0" diff --git a/code/modules/research/prosfab_designs.dm b/code/modules/research/prosfab_designs.dm index e168af2b23c..0a725814d8d 100644 --- a/code/modules/research/prosfab_designs.dm +++ b/code/modules/research/prosfab_designs.dm @@ -520,6 +520,31 @@ id = "borg_hound_cyborg_blaster" build_path = /obj/item/borg/upgrade/no_prod/toygun +/datum/design/item/prosfab/robot_upgrade/no_prod/vision_xray + name = "X-ray vision" + id = "borg_hound_vision_xray" + build_path = /obj/item/borg/upgrade/no_prod/vision_xray + +/datum/design/item/prosfab/robot_upgrade/no_prod/vision_thermal + name = "Thermal vision" + id = "borg_hound_vision_thermal" + build_path = /obj/item/borg/upgrade/no_prod/vision_thermal + +/datum/design/item/prosfab/robot_upgrade/no_prod/vision_meson + name = "Meson vision" + id = "borg_hound_vision_meson" + build_path = /obj/item/borg/upgrade/no_prod/vision_meson + +/datum/design/item/prosfab/robot_upgrade/no_prod/vision_material + name = "Material vision" + id = "borg_hound_vision_material" + build_path = /obj/item/borg/upgrade/no_prod/vision_material + +/datum/design/item/prosfab/robot_upgrade/no_prod/vision_anomalous + name = "Anomalous vision" + id = "borg_hound_vision_anomalous" + build_path = /obj/item/borg/upgrade/no_prod/vision_anomalous + // Synthmorph Bags. /datum/design/item/prosfab/synthmorphbag diff --git a/code/modules/vore/eating/belly_obj_vr.dm b/code/modules/vore/eating/belly_obj_vr.dm index 690f03c08a0..8436586bc11 100644 --- a/code/modules/vore/eating/belly_obj_vr.dm +++ b/code/modules/vore/eating/belly_obj_vr.dm @@ -363,18 +363,31 @@ STOP_PROCESSING(SSbellies, src) owner?.vore_organs?.Remove(src) owner = null + for(var/mob/observer/G in src) + G.forceMove(get_turf(src)) //ported from CHOMPStation PR#7132 return ..() // Called whenever an atom enters this belly /obj/belly/Entered(atom/movable/thing, atom/OldLoc) + + if(istype(thing, /mob/observer)) //Ports CHOMPStation PR#3072 + if(desc) //Ports CHOMPStation PR#4772 + //Allow ghosts see where they are if they're still getting squished along inside. + var/formatted_desc + formatted_desc = replacetext(desc, "%belly", lowertext(name)) //replace with this belly's name + formatted_desc = replacetext(formatted_desc, "%pred", owner) //replace with this belly's owner + formatted_desc = replacetext(formatted_desc, "%prey", thing) //replace with whatever mob entered into this belly + to_chat(thing, "[formatted_desc]") + if(OldLoc in contents) return //Someone dropping something (or being stripdigested) //Generic entered message - to_chat(owner,"[thing] slides into your [lowertext(name)].") + if(!istype(thing, /mob/observer)) //Don't have ghosts announce they're reentering the belly on death + to_chat(owner,"[thing] slides into your [lowertext(name)].") //Sound w/ antispam flag setting - if(vore_sound && !recent_sound) + if(vore_sound && !recent_sound && !istype(thing, /mob/observer)) var/soundfile if(!fancy_vore) soundfile = classic_vore_sounds[vore_sound] @@ -639,7 +652,7 @@ privacy_volume = 25 //Print notifications/sound if necessary - if(!silent) + if(!silent && !isobserver(M)) owner.visible_message("[span_green("[owner] [release_verb] [M] from their [lowertext(name)]!")]",range = privacy_range) var/soundfile if(!fancy_vore) @@ -699,6 +712,15 @@ for(var/mob/living/L in contents) living_count++ + var/count_total = contents.len + for(var/mob/observer/C in contents) + count_total-- //Exclude any ghosts from %count + + var/list/vore_contents = list() + for(var/G in contents) + if(!isobserver(G)) + vore_contents += G //Exclude any ghosts from %prey + for(var/mob/living/P in contents) if(!P.absorbed) //This is required first, in case there's a person absorbed and not absorbed in a stomach. total_bulge += P.size_multiplier @@ -708,9 +730,9 @@ formatted_message = replacetext(raw_message, "%belly", lowertext(name)) formatted_message = replacetext(formatted_message, "%pred", owner) - formatted_message = replacetext(formatted_message, "%prey", english_list(contents)) + formatted_message = replacetext(formatted_message, "%prey", english_list(vore_contents)) formatted_message = replacetext(formatted_message, "%countprey", living_count) - formatted_message = replacetext(formatted_message, "%count", contents.len) + formatted_message = replacetext(formatted_message, "%count", count_total) return(span_red("[formatted_message]")) @@ -1020,7 +1042,9 @@ //Incase they have the loop going, let's double check to stop it. M.stop_sound_channel(CHANNEL_PREYLOOP) // Delete the digested mob - M.ghostize() // Make sure they're out, so we can copy attack logs and such. + var/mob/observer/G = M.ghostize() //Ports CHOMPStation PR#3074 Make sure they're out, so we can copy attack logs and such. + if(G) + G.forceMove(src) qdel(M) // Handle a mob being absorbed @@ -1636,7 +1660,7 @@ if(!(content in src) || !istype(target)) return content.forceMove(target) - if(ismob(content)) + if(ismob(content) && !isobserver(content)) var/mob/ourmob = content ourmob.reset_view(owner) if(isitem(content)) diff --git a/code/modules/vore/eating/living_vr.dm b/code/modules/vore/eating/living_vr.dm index 34b87cbb58f..13039e94f3a 100644 --- a/code/modules/vore/eating/living_vr.dm +++ b/code/modules/vore/eating/living_vr.dm @@ -968,6 +968,8 @@ I = stack nom = refined_taste[O.default_type] M = name_to_material[O.default_type] + else if(istype(I, /obj/item/weapon/entrepreneur/crystal)) + nom = list("nutrition" = 100, "remark" = "The crytal was particularly brittle and not difficult to break apart, but the inside was incredibly flavoursome. Though devoid of any actual healing power, it seems to be very nutritious!", "WTF" = FALSE) if(nom) //Ravenous 1-4, snackage confirmed. Clear for chowdown, over. playsound(src, 'sound/items/eatfood.ogg', rand(10,50), 1) diff --git a/config/alienwhitelist.txt b/config/alienwhitelist.txt index ec43837c48f..209dd878863 100644 --- a/config/alienwhitelist.txt +++ b/config/alienwhitelist.txt @@ -50,6 +50,7 @@ exgame - Vox falloutdog3 - Black-Eyed Shadekin flaktual - Vox flurriee - Protean +foxfire53 - Xenochimera foxglovepurpur - Diona funnyman2003 - Xenochimera fuzlet - Protean @@ -150,6 +151,7 @@ satinisle - Protean scoutisafolflol - Xenochimera seagha - Vox seiga - Vox +selter - Protean sepulchre - Vox shirosenshi - Xenochimera silvertalismen - Diona diff --git a/config/jobwhitelist.txt b/config/jobwhitelist.txt index 99900a4c559..9c4290b7c60 100644 --- a/config/jobwhitelist.txt +++ b/config/jobwhitelist.txt @@ -32,3 +32,4 @@ verkister - clown whiskyrose - clown yecrowbarman - clown yecrowbarman - mime +nullspaceindustries - mime diff --git a/icons/mob/dogborg_vr.dmi b/icons/mob/dogborg_vr.dmi index f6c27374d43..d19ff1c7c8f 100644 Binary files a/icons/mob/dogborg_vr.dmi and b/icons/mob/dogborg_vr.dmi differ diff --git a/icons/mob/items/lefthand.dmi b/icons/mob/items/lefthand.dmi index 8fdaf23cd80..b86728d0309 100644 Binary files a/icons/mob/items/lefthand.dmi and b/icons/mob/items/lefthand.dmi differ diff --git a/icons/mob/items/righthand.dmi b/icons/mob/items/righthand.dmi index 381f959c029..95fb36fa4cb 100644 Binary files a/icons/mob/items/righthand.dmi and b/icons/mob/items/righthand.dmi differ diff --git a/icons/mob/species/teshari/synth_facemask.dmi b/icons/mob/species/teshari/synth_facemask.dmi new file mode 100644 index 00000000000..d85b0ff129e Binary files /dev/null and b/icons/mob/species/teshari/synth_facemask.dmi differ diff --git a/icons/obj/drinks_vr.dmi b/icons/obj/drinks_vr.dmi index 0565f00583d..3a1d37b2315 100644 Binary files a/icons/obj/drinks_vr.dmi and b/icons/obj/drinks_vr.dmi differ diff --git a/icons/obj/entrepreneur.dmi b/icons/obj/entrepreneur.dmi new file mode 100644 index 00000000000..1d846dbef15 Binary files /dev/null and b/icons/obj/entrepreneur.dmi differ diff --git a/icons/obj/gun.dmi b/icons/obj/gun.dmi index e4bfbfd55dc..0809c085859 100644 Binary files a/icons/obj/gun.dmi and b/icons/obj/gun.dmi differ diff --git a/maps/offmap_vr/common_offmaps.dm b/maps/offmap_vr/common_offmaps.dm index 5ad2bc1d07a..ccef386bf73 100644 --- a/maps/offmap_vr/common_offmaps.dm +++ b/maps/offmap_vr/common_offmaps.dm @@ -236,6 +236,7 @@ name = "Redgate Destination" z = Z_LEVEL_REDGATE flags = MAP_LEVEL_PLAYER|MAP_LEVEL_SEALED + base_turf = /turf/simulated/floor/outdoors/rocks/caves /datum/map_template/common_lateload/redgate/on_map_loaded(z) . = ..() diff --git a/tgui/packages/tgui-panel/chat/renderer.jsx b/tgui/packages/tgui-panel/chat/renderer.jsx index 1dc17bc8971..1461491b4f5 100644 --- a/tgui/packages/tgui-panel/chat/renderer.jsx +++ b/tgui/packages/tgui-panel/chat/renderer.jsx @@ -216,7 +216,7 @@ class ChatRenderer { this.scrollToBottom(); }); // Flush the queue - this.tryFlushQueue(true); + this.tryFlushQueue(); } onStateLoaded() { diff --git a/tgui/packages/tgui/components/Autofocus.tsx b/tgui/packages/tgui/components/Autofocus.tsx index a0b3f6f7659..403dbe2e965 100644 --- a/tgui/packages/tgui/components/Autofocus.tsx +++ b/tgui/packages/tgui/components/Autofocus.tsx @@ -1,17 +1,23 @@ -import { createRef, PropsWithChildren, useEffect } from 'react'; +import { PropsWithChildren, useEffect, useRef } from 'react'; -export const Autofocus = (props: PropsWithChildren) => { - const ref = createRef(); +/** Used to force the window to steal focus on load. Children optional */ +export function Autofocus(props: PropsWithChildren) { + const { children } = props; + const ref = useRef(null); useEffect(() => { - setTimeout(() => { + const timer = setTimeout(() => { ref.current?.focus(); }, 1); + + return () => { + clearTimeout(timer); + }; }, []); return (
- {props.children} + {children}
); -}; +} diff --git a/tgui/packages/tgui/components/Section.tsx b/tgui/packages/tgui/components/Section.tsx index e18cb7e76a8..f85e96a05ea 100644 --- a/tgui/packages/tgui/components/Section.tsx +++ b/tgui/packages/tgui/components/Section.tsx @@ -5,7 +5,7 @@ */ import { canRender, classes } from 'common/react'; -import { forwardRef, ReactNode, RefObject, useEffect } from 'react'; +import { ReactNode, useEffect, useRef } from 'react'; import { addScrollableNode, removeScrollableNode } from '../events'; import { BoxProps, computeBoxClassName, computeBoxProps } from './Box'; @@ -23,6 +23,8 @@ type Props = Partial<{ scrollableHorizontal: boolean; /** Title of the section. */ title: ReactNode; + /** id to assosiate with the parent div element used by this section, for uses with procs like getElementByID */ + container_id: string; /** @member Callback function for the `scroll` event */ onScroll: ((this: GlobalEventHandlers, ev: Event) => any) | null; @@ -59,75 +61,77 @@ type Props = Partial<{ * * ``` */ -export const Section = forwardRef( - (props: Props, forwardedRef: RefObject) => { - const { - buttons, - children, - className, - fill, - fitted, - onScroll, - scrollable, - scrollableHorizontal, - title, - flexGrow, // VOREStation Addition - noTopPadding, // VOREStation Addition - stretchContents, // VOREStation Addition - ...rest - } = props; +export const Section = (props: Props) => { + const { + buttons, + children, + className, + fill, + fitted, + onScroll, + scrollable, + scrollableHorizontal, + title, + container_id, + flexGrow, // VOREStation Addition + noTopPadding, // VOREStation Addition + stretchContents, // VOREStation Addition + ...rest + } = props; + const node = useRef(null); - const hasTitle = canRender(title) || canRender(buttons); + const hasTitle = canRender(title) || canRender(buttons); - /** We want to be able to scroll on hover, but using focus will steal it from inputs */ - useEffect(() => { - if (!forwardedRef?.current) return; - if (!scrollable && !scrollableHorizontal) return; + /** We want to be able to scroll on hover, but using focus will steal it from inputs */ + useEffect(() => { + if (!node?.current) return; + if (!scrollable && !scrollableHorizontal) return; + const self = node.current; - addScrollableNode(forwardedRef.current); + addScrollableNode(self); - return () => { - if (!forwardedRef?.current) return; - removeScrollableNode(forwardedRef.current!); - }; - }, []); + return () => { + if (!self) return; + removeScrollableNode(self!); + }; + }, []); - return ( -
- {hasTitle && ( -
- {title} -
{buttons}
-
- )} -
-
- {children} -
+ return ( +
+ {hasTitle && ( +
+ {title} +
{buttons}
+
+ )} +
+
+ {children}
- ); - }, -); +
+ ); +}; diff --git a/tgui/packages/tgui/components/Tabs.tsx b/tgui/packages/tgui/components/Tabs.tsx index 84779bcf748..ba2b4e93c31 100644 --- a/tgui/packages/tgui/components/Tabs.tsx +++ b/tgui/packages/tgui/components/Tabs.tsx @@ -60,9 +60,17 @@ const Tab = (props: TabProps) => { leftSlot, rightSlot, children, + onClick, ...rest } = props; + const handleClick = (e) => { + if (onClick) { + onClick(e); + e.target.blur(); + } + }; + return (
{ className, computeBoxClassName(rest), ])} + onClick={handleClick} {...computeBoxProps(rest)} > {(canRender(leftSlot) &&
{leftSlot}
) || diff --git a/tgui/packages/tgui/events.ts b/tgui/packages/tgui/events.ts index cc53d31bfab..f7e14c13e13 100644 --- a/tgui/packages/tgui/events.ts +++ b/tgui/packages/tgui/events.ts @@ -106,6 +106,14 @@ const focusNearestTrackedParent = (node: HTMLElement | null) => { }; window.addEventListener('mousemove', (e) => { + const node = e.target as HTMLElement; + if (node !== lastVisitedNode && trackedNodes.length < 2) { + lastVisitedNode = node; + focusNearestTrackedParent(node); + } +}); + +window.addEventListener('click', (e) => { const node = e.target as HTMLElement; if (node !== lastVisitedNode) { lastVisitedNode = node; diff --git a/tgui/packages/tgui/interfaces/BodyDesigner.jsx b/tgui/packages/tgui/interfaces/BodyDesigner.jsx index 059cf61700b..e22a8c14c90 100644 --- a/tgui/packages/tgui/interfaces/BodyDesigner.jsx +++ b/tgui/packages/tgui/interfaces/BodyDesigner.jsx @@ -81,14 +81,16 @@ const BodyDesignerBodyRecords = (props) => { /> } > - {bodyrecords.map((record) => ( -