From 586e2e6c4d4363c160cc0b2c24b5fd79ffc8c228 Mon Sep 17 00:00:00 2001 From: Kyani <65205627+EmeraldCandy@users.noreply.github.com> Date: Fri, 13 Jun 2025 15:40:29 -0400 Subject: [PATCH] Job Perks - True Professionals (#29478) * ddsadsa ddsadsa * moderate again * revert * medical first steps * more work * lots more work done * more progress * finished up * attack chain linter * attack chain update * attack chain work * fixes + chemist update * gives comments --- code/__HELPERS/trait_helpers.dm | 59 +++++++++++++++++++ code/_onclick/item_attack.dm | 11 +++- code/datums/diseases/_MobProcs.dm | 7 +++ code/game/jobs/job/engineering_jobs.dm | 11 ++++ code/game/jobs/job/job.dm | 20 +++++++ code/game/jobs/job/medical_jobs.dm | 30 ++++++++++ code/game/jobs/job/science_jobs.dm | 12 ++++ code/game/jobs/job/security_jobs.dm | 20 +++++++ code/game/jobs/job/supervisor.dm | 41 +++++++++++++ code/game/jobs/job/support.dm | 28 ++++++++- code/game/objects/items/granters/_granters.dm | 22 ++++--- .../objects/items/weapons/extinguisher.dm | 3 +- code/game/objects/items/weapons/manuals.dm | 22 +++++++ code/game/objects/items/weapons/mop.dm | 5 +- .../structures/crates_lockers/closets.dm | 16 ++--- code/modules/crafting/craft.dm | 4 +- code/modules/hydroponics/hydroponics_tray.dm | 4 ++ code/modules/library/book.dm | 42 ++++++++----- .../mob/living/carbon/human/human_life.dm | 15 +++-- .../mob/living/carbon/human/human_mob.dm | 42 +++++++++++++ code/modules/mob/living/living.dm | 8 ++- code/modules/mob/living/living_defines.dm | 3 + code/modules/mob/living/living_say.dm | 5 +- .../mob/living/silicon/robot/robot_mob.dm | 33 +++++++++-- code/modules/power/apc/apc.dm | 31 +++++++++- code/modules/recycling/sortingmachinery.dm | 9 ++- code/modules/smithing/smith_item.dm | 5 ++ .../modules/smithing/smithed_items/inserts.dm | 17 ++++++ code/modules/smithing/smithed_items/lens.dm | 18 ++++++ .../smithing/smithed_items/tool_bits.dm | 18 ++++++ code/modules/vehicle/ambulance.dm | 48 +++++++++++++++ 31 files changed, 553 insertions(+), 56 deletions(-) diff --git a/code/__HELPERS/trait_helpers.dm b/code/__HELPERS/trait_helpers.dm index 0f2fd485598..771d639c3fc 100644 --- a/code/__HELPERS/trait_helpers.dm +++ b/code/__HELPERS/trait_helpers.dm @@ -345,6 +345,64 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai /// Does this person produce loud runechat #define TRAIT_LOUD "loud" +//***** JOB TRAITS *****// + +// med traits + +/// allows user to do_after to give bone and IB locations +#define TRAIT_MED_EXAMINE "med_examine" +/// Grants a 85% chance to NOT catch a disease when contract() is called. You'll probably still catch it though with exposure +#define TRAIT_GERMOPHOBE "germophobe" +/// user wont vomit around corpses +#define TRAIT_CORPSE_RESIST "corpse_resist" +/// user will take 25% less tox damage +#define TRAIT_QUICK_HEATER "quick_heater" +/// user can drive the ambulance cart to push people out of the way +#define TRAIT_SPEED_DEMON "speed_demon" + +// sci traits + +/// user can craft things from the crafting menu in 1/4 time +#define TRAIT_CRAFTY "crafty" +/// User is alloted a higher genetics budget +#define TRAIT_GENETIC_BUDGET "genetic_budget" +/// allows the user to quick-charge borgs by using a battery on them +#define TRAIT_CYBORG_SPECIALIST "cyborg_specialist" + +// service traits + +/// Allows user to pick weeds with their bare hands +#define TRAIT_GREEN_THUMB "green_thumb" +/// Allows user to use mops faster, and have galoshes slow less +#define TRAIT_JANITOR "janitor" + +// Engi traits + +/// Allows user to to quick-repair unlocked APCs with a frame and board, and quick-swap cells.alist +#define TRAIT_ELECTRICAL_SPECIALIST "electrical_specialist" +/// Allows user to spray extinguishers further, and gives precisions = 1 during spray +#define TRAIT_FIRE_FIGHTER "fire_fighter" + +// cargo traits + +/// allows the user to stuff more items in lockers/crates and wrap them faster +#define TRAIT_PACK_RAT "pack_rat" +/// Allows the user to ???? +#define TRAIT_SMITH "tait_smith" +/// Allows faster butchering times +#define TRAIT_BUTCHER "butcher" + +// sec traits + +/// Gives the user a new tab for being able to open up space law/sop book pages +#define TRAIT_JUDICIAL "judicial" + +// command traits + +/// Allows user to drink coffee to point rapidly to get others to do what you want +#define TRAIT_COFFEE_SNOB "coffee_snob" + + //****** OBJ TRAITS *****// ///An /obj that should not increase the "depth" of the search for adjacency, @@ -384,6 +442,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define GHOSTED "isghost" #define GHOST_ROLE "ghost_role" #define ORGAN_TRAIT "organ" +#define JOB_TRAIT "job_trait" // unique trait sources #define STATUE_MUTE "statue" diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index 7e934b6e022..a9046b91000 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -69,9 +69,14 @@ if(temperature && A.reagents && !ismob(A) && !istype(A, /obj/item/clothing/mask/cigarette)) var/reagent_temp = A.reagents.chem_temp var/time = (reagent_temp / 10) / (temperature / 1000) - if(do_after_once(user, time, TRUE, user, TRUE, attempt_cancel_message = "You stop heating up [A].")) - to_chat(user, "You heat [A] with [src].") - A.reagents.temperature_reagents(temperature) + if(user.mind && HAS_TRAIT(user.mind, TRAIT_QUICK_HEATER)) + while(do_after_once(user, time, TRUE, user, TRUE, attempt_cancel_message = "You stop heating up [A].")) + to_chat(user, "You heat [A] with [src].") + A.reagents.temperature_reagents(temperature) + else + if(do_after_once(user, time, TRUE, user, TRUE, attempt_cancel_message = "You stop heating up [A].")) + to_chat(user, "You heat [A] with [src].") + A.reagents.temperature_reagents(temperature) /** * Called when mob `user` is hitting us with an item `attacking`. diff --git a/code/datums/diseases/_MobProcs.dm b/code/datums/diseases/_MobProcs.dm index 5e299ada323..0a74ed74024 100644 --- a/code/datums/diseases/_MobProcs.dm +++ b/code/datums/diseases/_MobProcs.dm @@ -60,6 +60,13 @@ if(!CanContractDisease(D)) return 0 + if(src.mind) + if(HAS_TRAIT(src.mind, TRAIT_GERMOPHOBE) && prob(85)) + log_debug("failed to contract disease") + return 0 + else + log_debug("contracted a disease!") + var/obj/item/clothing/Cl = null var/passed = 1 diff --git a/code/game/jobs/job/engineering_jobs.dm b/code/game/jobs/job/engineering_jobs.dm index d5db8c18b78..d0e8583c936 100644 --- a/code/game/jobs/job/engineering_jobs.dm +++ b/code/game/jobs/job/engineering_jobs.dm @@ -59,6 +59,10 @@ dufflebag = /obj/item/storage/backpack/duffel/engineering box = /obj/item/storage/box/engineer +/datum/outfit/job/chief_engineer/on_mind_initialize(mob/living/carbon/human/H) + . = ..() + ADD_TRAIT(H.mind, TRAIT_ELECTRICAL_SPECIALIST, JOB_TRAIT) + /datum/job/engineer title = "Station Engineer" flag = JOB_ENGINEER @@ -102,6 +106,10 @@ dufflebag = /obj/item/storage/backpack/duffel/engineering box = /obj/item/storage/box/engineer +/datum/outfit/job/engineer/on_mind_initialize(mob/living/carbon/human/H) + . = ..() + ADD_TRAIT(H.mind, TRAIT_ELECTRICAL_SPECIALIST, JOB_TRAIT) + /datum/job/atmos title = "Life Support Specialist" flag = JOB_ATMOSTECH @@ -142,3 +150,6 @@ dufflebag = /obj/item/storage/backpack/duffel/atmos box = /obj/item/storage/box/engineer +/datum/outfit/job/atmos/on_mind_initialize(mob/living/carbon/human/H) + . = ..() + ADD_TRAIT(H.mind, TRAIT_FIRE_FIGHTER, JOB_TRAIT) diff --git a/code/game/jobs/job/job.dm b/code/game/jobs/job/job.dm index 68f6a822cf9..c40fee49cf5 100644 --- a/code/game/jobs/job/job.dm +++ b/code/game/jobs/job/job.dm @@ -304,3 +304,23 @@ /// Used to give the gaze ability to NTReps and IAAs /datum/outfit/job/proc/give_gaze(mob/living/carbon/human/user) user.AddSpell(new /datum/spell/inspectors_gaze(null)) + +/// Gives the imaginary space law booklet verb +/mob/living/carbon/human/proc/space_law() + set name = "Open Space Law" + set desc = "Open a memorized version of the space law booklet." + set category = "Space Law" + + var/obj/item/book/manual/wiki/security_space_law/imaginary/book = new() + if(!put_in_any_hand_if_possible(book)) + QDEL_NULL(book) + +/// Gives the imaginary legal sop booklet verb +/mob/living/carbon/human/proc/sop_legal() + set name = "Open Legal SOP" + set desc = "Open a memorized version of the legal SOP booklet." + set category = "Space Law" + + var/obj/item/book/manual/wiki/sop_legal/imaginary/book = new() + if(!put_in_any_hand_if_possible(book)) + QDEL_NULL(book) diff --git a/code/game/jobs/job/medical_jobs.dm b/code/game/jobs/job/medical_jobs.dm index eed32e7dfe5..8160d45f5c5 100644 --- a/code/game/jobs/job/medical_jobs.dm +++ b/code/game/jobs/job/medical_jobs.dm @@ -56,6 +56,11 @@ satchel = /obj/item/storage/backpack/satchel_med dufflebag = /obj/item/storage/backpack/duffel/medical + +/datum/outfit/job/cmo/on_mind_initialize(mob/living/carbon/human/H) + . = ..() + ADD_TRAIT(H.mind, TRAIT_MED_EXAMINE, JOB_TRAIT) + /datum/job/doctor title = "Medical Doctor" flag = JOB_DOCTOR @@ -94,6 +99,10 @@ satchel = /obj/item/storage/backpack/satchel_med dufflebag = /obj/item/storage/backpack/duffel/medical +/datum/outfit/job/doctor/on_mind_initialize(mob/living/carbon/human/H) + . = ..() + ADD_TRAIT(H.mind, TRAIT_MED_EXAMINE, JOB_TRAIT) + /datum/job/coroner title = "Coroner" flag = JOB_CORONER @@ -137,6 +146,10 @@ /obj/item/healthanalyzer = 1, /obj/item/storage/box/bodybags = 1) +/datum/outfit/job/coroner/on_mind_initialize(mob/living/carbon/human/H) + . = ..() + ADD_TRAIT(H.mind, TRAIT_CORPSE_RESIST, JOB_TRAIT) + /datum/outfit/job/doctor/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE) . = ..() if(H.mind && H.mind.role_alt_title) @@ -194,6 +207,10 @@ satchel = /obj/item/storage/backpack/satchel_chem dufflebag = /obj/item/storage/backpack/duffel/chemistry +/datum/outfit/job/chemist/on_mind_initialize(mob/living/carbon/human/H) + . = ..() + ADD_TRAIT(H.mind, TRAIT_QUICK_HEATER, JOB_TRAIT) + /datum/job/geneticist title = "Geneticist" flag = JOB_GENETICIST @@ -231,6 +248,10 @@ satchel = /obj/item/storage/backpack/satchel_gen dufflebag = /obj/item/storage/backpack/duffel/genetics +/datum/outfit/job/geneticist/on_mind_initialize(mob/living/carbon/human/H) + . = ..() + ADD_TRAIT(H.mind, TRAIT_GENETIC_BUDGET, JOB_TRAIT) + /datum/job/virologist title = "Virologist" flag = JOB_VIROLOGIST @@ -255,6 +276,7 @@ ) outfit = /datum/outfit/job/virologist + /datum/outfit/job/virologist name = "Virologist" jobtype = /datum/job/virologist @@ -272,6 +294,10 @@ satchel = /obj/item/storage/backpack/satchel_vir dufflebag = /obj/item/storage/backpack/duffel/virology +/datum/outfit/job/virologist/on_mind_initialize(mob/living/carbon/human/H) + . = ..() + ADD_TRAIT(H.mind, TRAIT_GERMOPHOBE, JOB_TRAIT) + /datum/job/psychiatrist title = "Psychiatrist" flag = JOB_PSYCHIATRIST @@ -362,3 +388,7 @@ satchel = /obj/item/storage/backpack/satchel_med dufflebag = /obj/item/storage/backpack/duffel/medical box = /obj/item/storage/box/engineer + +/datum/outfit/job/paramedic/on_mind_initialize(mob/living/carbon/human/H) + . = ..() + ADD_TRAIT(H.mind, TRAIT_SPEED_DEMON, JOB_TRAIT) diff --git a/code/game/jobs/job/science_jobs.dm b/code/game/jobs/job/science_jobs.dm index 87e76f75072..0b119fd74fe 100644 --- a/code/game/jobs/job/science_jobs.dm +++ b/code/game/jobs/job/science_jobs.dm @@ -65,6 +65,10 @@ satchel = /obj/item/storage/backpack/satchel_tox dufflebag = /obj/item/storage/backpack/duffel/science +/datum/outfit/job/rd/on_mind_initialize(mob/living/carbon/human/H) + . = ..() + ADD_TRAIT(H.mind, TRAIT_CRAFTY, JOB_TRAIT) + /datum/job/scientist title = "Scientist" flag = JOB_SCIENTIST @@ -107,6 +111,10 @@ satchel = /obj/item/storage/backpack/satchel_tox dufflebag = /obj/item/storage/backpack/duffel/science +/datum/outfit/job/scientist/on_mind_initialize(mob/living/carbon/human/H) + . = ..() + ADD_TRAIT(H.mind, TRAIT_CRAFTY, JOB_TRAIT) + /datum/job/xenobiologist title = "Xenobiologist" flag = JOB_XENOBIOLOGIST @@ -205,3 +213,7 @@ backpack = /obj/item/storage/backpack/robotics satchel = /obj/item/storage/backpack/satchel_robo dufflebag = /obj/item/storage/backpack/duffel/robotics + +/datum/outfit/job/roboticist/on_mind_initialize(mob/living/carbon/human/H) + . = ..() + ADD_TRAIT(H.mind, TRAIT_CYBORG_SPECIALIST, JOB_TRAIT) diff --git a/code/game/jobs/job/security_jobs.dm b/code/game/jobs/job/security_jobs.dm index 383841ec667..f560857512b 100644 --- a/code/game/jobs/job/security_jobs.dm +++ b/code/game/jobs/job/security_jobs.dm @@ -70,6 +70,11 @@ satchel = /obj/item/storage/backpack/satchel_sec dufflebag = /obj/item/storage/backpack/duffel/security +/datum/outfit/job/hos/on_mind_initialize(mob/living/carbon/human/H) + . = ..() + add_verb(H, /mob/living/carbon/human/proc/sop_legal) + add_verb(H, /mob/living/carbon/human/proc/space_law) + /datum/job/warden title = "Warden" flag = JOB_WARDEN @@ -121,6 +126,11 @@ satchel = /obj/item/storage/backpack/satchel_sec dufflebag = /obj/item/storage/backpack/duffel/security +/datum/outfit/job/warden/on_mind_initialize(mob/living/carbon/human/H) + . = ..() + add_verb(H, /mob/living/carbon/human/proc/sop_legal) + add_verb(H, /mob/living/carbon/human/proc/space_law) + /datum/job/detective title = "Detective" flag = JOB_DETECTIVE @@ -173,6 +183,12 @@ bio_chips = list(/obj/item/bio_chip/mindshield) +/datum/outfit/job/detective/on_mind_initialize(mob/living/carbon/human/H) + . = ..() + ADD_TRAIT(H.mind, TRAIT_CORPSE_RESIST, JOB_TRAIT) // too cool to vomit + add_verb(H, /mob/living/carbon/human/proc/sop_legal) + add_verb(H, /mob/living/carbon/human/proc/space_law) + /datum/outfit/job/detective/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE) . = ..() if(H.mind && H.mind.role_alt_title) @@ -236,3 +252,7 @@ satchel = /obj/item/storage/backpack/satchel_sec dufflebag = /obj/item/storage/backpack/duffel/security +/datum/outfit/job/officer/on_mind_initialize(mob/living/carbon/human/H) + . = ..() + add_verb(H, /mob/living/carbon/human/proc/sop_legal) + add_verb(H, /mob/living/carbon/human/proc/space_law) diff --git a/code/game/jobs/job/supervisor.dm b/code/game/jobs/job/supervisor.dm index 5810625a5fe..29b5a91f705 100644 --- a/code/game/jobs/job/supervisor.dm +++ b/code/game/jobs/job/supervisor.dm @@ -53,6 +53,10 @@ U.accessories += M M.on_attached(U) +/datum/outfit/job/captain/on_mind_initialize(mob/living/carbon/human/H) + . = ..() + H.AddSpell(new /datum/spell/big_voice) + /datum/job/hop title = "Head of Personnel" flag = JOB_HOP @@ -200,6 +204,7 @@ /datum/outfit/job/nanotrasenrep/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) . = ..() + ADD_TRAIT(H.mind, TRAIT_COFFEE_SNOB, JOB_TRAIT) if(visualsOnly) return @@ -314,6 +319,12 @@ satchel = /obj/item/storage/backpack/satchel_sec dufflebag = /obj/item/storage/backpack/duffel/security +/datum/outfit/job/judge/on_mind_initialize(mob/living/carbon/human/H) + . = ..() + add_verb(H, /mob/living/carbon/human/proc/sop_legal) + add_verb(H, /mob/living/carbon/human/proc/space_law) + ADD_TRAIT(H.mind, TRAIT_COFFEE_SNOB, JOB_TRAIT) + /datum/job/iaa title = "Internal Affairs Agent" flag = JOB_INTERNAL_AFFAIRS @@ -364,6 +375,8 @@ /datum/outfit/job/iaa/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) . = ..() + add_verb(H, /mob/living/carbon/human/proc/sop_legal) + add_verb(H, /mob/living/carbon/human/proc/space_law) if(visualsOnly) return @@ -440,3 +453,31 @@ /datum/outfit/job/nct/on_mind_initialize(mob/living/carbon/human/H) . = ..() H.mind.offstation_role = TRUE + +/datum/spell/big_voice + name = "Speak with Authority" + desc = "Speak with a COMMANDING AUTHORITY against those you govorn." + base_cooldown = 1 MINUTES + action_background_icon_state = "bg_default" + action_icon = 'icons/obj/clothing/accessories.dmi' + action_icon_state = "gold" + sound = null + invocation_type = "none" + invocation = null + clothes_req = FALSE + +/datum/spell/big_voice/create_new_targeting() + return new /datum/spell_targeting/self + +/datum/spell/big_voice/cast(list/targets, mob/living/user) + var/say_message = tgui_input_text(user, "Message:", "Speak With Authority") + if(isnull(say_message)) + revert_cast() + else + if(user.big_voice != 2) + user.big_voice = 1 + user.say(say_message) + user.big_voice = 0 + else + user.say(say_message) + cooldown_handler.start_recharge() diff --git a/code/game/jobs/job/support.dm b/code/game/jobs/job/support.dm index 31226f2a598..198462f810d 100644 --- a/code/game/jobs/job/support.dm +++ b/code/game/jobs/job/support.dm @@ -54,7 +54,9 @@ /obj/item/melee/classic_baton/telescopic = 1 ) - +/datum/outfit/job/qm/on_mind_initialize(mob/living/carbon/human/H) + . = ..() + ADD_TRAIT(H.mind, TRAIT_PACK_RAT, JOB_TRAIT) /datum/job/cargo_tech title = "Cargo Technician" @@ -88,6 +90,10 @@ id = /obj/item/card/id/supply pda = /obj/item/pda/cargo +/datum/outfit/job/cargo_tech/on_mind_initialize(mob/living/carbon/human/H) + . = ..() + ADD_TRAIT(H.mind, TRAIT_PACK_RAT, JOB_TRAIT) + /datum/job/smith title = "Smith" flag = JOB_SMITH @@ -122,6 +128,10 @@ id = /obj/item/card/id/smith pda = /obj/item/pda/cargo +/datum/outfit/job/smith/on_mind_initialize(mob/living/carbon/human/H) + . = ..() + ADD_TRAIT(H.mind, TRAIT_SMITH, JOB_TRAIT) + /datum/job/mining title = "Shaft Miner" flag = JOB_MINER @@ -164,6 +174,10 @@ satchel = /obj/item/storage/backpack/satchel/explorer box = /obj/item/storage/box/survival_mining +/datum/outfit/job/mining/on_mind_initialize(mob/living/carbon/human/H) + . = ..() + ADD_TRAIT(H.mind, TRAIT_BUTCHER, JOB_TRAIT) + /datum/outfit/job/mining/equipped name = "Shaft Miner" @@ -234,6 +248,10 @@ satchel = /obj/item/storage/backpack/satchel/explorer box = /obj/item/storage/box/survival_mining +/datum/outfit/job/explorer/on_mind_initialize(mob/living/carbon/human/H) + . = ..() + ADD_TRAIT(H.mind, TRAIT_BUTCHER, JOB_TRAIT) + //Food /datum/job/bartender title = "Bartender" @@ -353,7 +371,9 @@ satchel = /obj/item/storage/backpack/satchel_hyd dufflebag = /obj/item/storage/backpack/duffel/hydro - +/datum/outfit/job/hydro/on_mind_initialize(mob/living/carbon/human/H) + . = ..() + ADD_TRAIT(H.mind, TRAIT_GREEN_THUMB, JOB_TRAIT) //Griff //BS12 EDIT @@ -545,6 +565,10 @@ return ADD_TRAIT(H, TRAIT_NEVER_MISSES_DISPOSALS, ROUNDSTART_TRAIT) +/datum/outfit/job/janitor/on_mind_initialize(mob/living/carbon/human/H) + . = ..() + ADD_TRAIT(H.mind, TRAIT_JANITOR, JOB_TRAIT) + //More or less assistants /datum/job/librarian title = "Librarian" diff --git a/code/game/objects/items/granters/_granters.dm b/code/game/objects/items/granters/_granters.dm index a3954dcf40d..e5ba5715e04 100644 --- a/code/game/objects/items/granters/_granters.dm +++ b/code/game/objects/items/granters/_granters.dm @@ -21,21 +21,27 @@ 'sound/effects/pageturn3.ogg' ) -/obj/item/book/granter/attack_self__legacy__attackchain(mob/living/user) +/obj/item/book/granter/Initialize(mapload, datum/cachedbook/CB, _copyright, _protected) + . = ..() + RegisterSignal(src, COMSIG_ACTIVATE_SELF, TYPE_PROC_REF(/datum, signal_cancel_activate_self)) + +/obj/item/book/granter/activate_self(mob/user) + if(..()) + return if(reading) to_chat(user, "You're already reading this!") - return FALSE + return FINISH_ATTACK if(!user.has_vision()) to_chat(user, "You are blind and can't read anything!") - return FALSE + return FINISH_ATTACK if(!isliving(user)) - return FALSE + return FINISH_ATTACK if(!can_learn(user)) - return FALSE + return FINISH_ATTACK if(uses <= 0) recoil(user) - return FALSE + return FINISH_ATTACK on_reading_start(user) reading = TRUE @@ -43,14 +49,12 @@ if(!turn_page(user)) on_reading_stopped(user) reading = FALSE - return + return CONTINUE_ATTACK if(do_after(user, reading_time, src)) uses-- on_reading_finished(user) reading = FALSE - return TRUE - /// Called when the user starts to read the granter. /obj/item/book/granter/proc/on_reading_start(mob/living/user) to_chat(user, "You start reading [name]...") diff --git a/code/game/objects/items/weapons/extinguisher.dm b/code/game/objects/items/weapons/extinguisher.dm index 4a02bed7e86..f796266f1e4 100644 --- a/code/game/objects/items/weapons/extinguisher.dm +++ b/code/game/objects/items/weapons/extinguisher.dm @@ -143,7 +143,8 @@ INVOKE_ASYNC(src, PROC_REF(buckled_speed_move), user.buckled, direction) else user.newtonian_move(turn(direction, 180)) - + if(user.mind && HAS_TRAIT(user.mind, TRAIT_FIRE_FIGHTER)) + precision = TRUE var/turf/T = get_turf(A) var/turf/T1 = get_step(T, turn(direction, 90)) var/turf/T2 = get_step(T, turn(direction, -90)) diff --git a/code/game/objects/items/weapons/manuals.dm b/code/game/objects/items/weapons/manuals.dm index 840dcf8af8f..a7af5c55252 100644 --- a/code/game/objects/items/weapons/manuals.dm +++ b/code/game/objects/items/weapons/manuals.dm @@ -928,6 +928,17 @@ title = "Space Law" wiki_article_title = "Space_law" +/obj/item/book/manual/wiki/security_space_law/imaginary + name = "Imaginary Space Law Manual" + desc = "A set of memorized Nanotrasen guidelines for keeping law and order on their space stations." + flags = DROPDEL | ABSTRACT | NOBLUDGEON + imaginary = TRUE + +/obj/item/book/manual/wiki/security_space_law/imaginary/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_SKIP_EXAMINE, INNATE_TRAIT) + ADD_TRAIT(src, TRAIT_NO_STRIP, INNATE_TRAIT) + /obj/item/book/manual/wiki/security_space_law/black name = "Space Law - Limited Edition" desc = "A leather-bound, immaculately-written copy of JUSTICE." @@ -958,6 +969,17 @@ title = "Legal Standard Operating Procedures" wiki_article_title = "Legal_Standard_Operating_Procedure" +/obj/item/book/manual/wiki/sop_legal/imaginary + name = "Imaginary Legal SOP Manual" + desc = "A set of memorized Nanotrasen guidelines aiming at the safe conduct of all legal activities." + flags = DROPDEL | ABSTRACT | NOBLUDGEON + imaginary = TRUE + +/obj/item/book/manual/wiki/sop_legal/imaginary/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_SKIP_EXAMINE, INNATE_TRAIT) + ADD_TRAIT(src, TRAIT_NO_STRIP, INNATE_TRAIT) + /obj/item/book/manual/wiki/sop_supply name = "Supply Standard Operating Procedures" desc = "A set of guidelines aiming at the safe conduct of all supply activities." diff --git a/code/game/objects/items/weapons/mop.dm b/code/game/objects/items/weapons/mop.dm index 7d6a684df85..05b92c8b2ac 100644 --- a/code/game/objects/items/weapons/mop.dm +++ b/code/game/objects/items/weapons/mop.dm @@ -71,7 +71,10 @@ if(world.time > mop_sound_cooldown) playsound(loc, pick('sound/weapons/mopping1.ogg', 'sound/weapons/mopping2.ogg'), 30, TRUE, -1) mop_sound_cooldown = world.time + MOP_SOUND_CD - target.cleaning_act(user, src, mopspeed, text_verb = "mop", text_description = ".") + if(user.mind && HAS_TRAIT(user, TRAIT_JANITOR)) + target.cleaning_act(user, src, mopspeed / 2, text_verb = "mop", text_description = ".") + else + target.cleaning_act(user, src, mopspeed, text_verb = "mop", text_description = ".") return ITEM_INTERACT_COMPLETE /obj/item/mop/can_clean() diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index 6f11c416704..dac03e147c6 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -218,30 +218,32 @@ update_appearance() return TRUE -/obj/structure/closet/proc/close() +/obj/structure/closet/proc/close(mob/user) if(!opened) return FALSE if(!can_close()) return FALSE var/itemcount = 0 - + var/temp_capacity = storage_capacity + if(user && user.mind && HAS_TRAIT(user.mind, TRAIT_PACK_RAT)) + temp_capacity *= 1.5 //Cham Projector Exception for(var/obj/effect/dummy/chameleon/AD in loc) - if(itemcount >= storage_capacity) + if(itemcount >= temp_capacity) break AD.forceMove(src) itemcount++ for(var/obj/item/I in loc) - if(itemcount >= storage_capacity) + if(itemcount >= temp_capacity) break if(!I.anchored) I.forceMove(src) itemcount++ for(var/mob/M in loc) - if(itemcount >= storage_capacity) + if(itemcount >= temp_capacity) break if(isobserver(M)) continue @@ -264,7 +266,7 @@ return TRUE /obj/structure/closet/proc/toggle(mob/user) - if(!(opened ? close() : open())) + if(!(opened ? close(user) : open())) to_chat(user, "It won't budge!") /obj/structure/closet/proc/bust_open() @@ -498,7 +500,7 @@ if(opened && can_close()) target.forceMove(src) visible_message("[attacker] shoves [target] inside [src]!", "You hear a thud, and something clangs shut.") - close() + close(attacker) add_attack_logs(attacker, target, "shoved into [src]") return TRUE diff --git a/code/modules/crafting/craft.dm b/code/modules/crafting/craft.dm index d56cd287322..b27dafe2209 100644 --- a/code/modules/crafting/craft.dm +++ b/code/modules/crafting/craft.dm @@ -166,7 +166,9 @@ return ", missing tool." if(!check_pathtools(user, recipe, contents)) return ", missing tool." - + var/craft_time = recipe.time + if(HAS_TRAIT(user.mind, TRAIT_CRAFTY)) + craft_time /= 3 if(!do_after(user, recipe.time, target = user)) return "." contents = get_surroundings(user) diff --git a/code/modules/hydroponics/hydroponics_tray.dm b/code/modules/hydroponics/hydroponics_tray.dm index ba7c6a6bc81..7b2cb64a435 100644 --- a/code/modules/hydroponics/hydroponics_tray.dm +++ b/code/modules/hydroponics/hydroponics_tray.dm @@ -936,6 +936,10 @@ update_state() plant_hud_set_status() plant_hud_set_health() + else if(user.mind && HAS_TRAIT(user.mind, TRAIT_GREEN_THUMB) && weedlevel > 0) + user.visible_message("[user] uproots the weeds.", "You pluck the weeds from [src] with your hands.") + adjustWeeds(-10) + update_state() else examine(user) diff --git a/code/modules/library/book.dm b/code/modules/library/book.dm index 016a5fb8136..a2c1e104fc2 100644 --- a/code/modules/library/book.dm +++ b/code/modules/library/book.dm @@ -23,6 +23,7 @@ resistance_flags = FLAMMABLE drop_sound = 'sound/items/handling/book_drop.ogg' pickup_sound = 'sound/items/handling/book_pickup.ogg' + new_attack_chain = TRUE ///Title & Real name of the book var/title @@ -40,6 +41,8 @@ var/pages = list() ///What page is the book currently opened to? Page 0 - Intro Page | Page 1-5 - Content Pages var/current_page = 0 + ///Is this item imaginary and not able to interact with anything else? + var/imaginary = FALSE ///Book UI Popup Height var/book_height = 400 @@ -74,37 +77,48 @@ icon_state = "book[rand(1,8)]" -/obj/item/book/attack__legacy__attackchain(mob/M, mob/living/user) +/obj/item/book/attack(mob/M, mob/living/user) + if(imaginary) + return FINISH_ATTACK if(user.a_intent == INTENT_HELP) force = 0 attack_verb = list("educated") else force = initial(force) attack_verb = list("bashed", "whacked") - ..() + return ..() -/obj/item/book/attack_self__legacy__attackchain(mob/user) +/obj/item/book/activate_self(mob/user) + if(..()) + return + if(imaginary) + read_book(user) + return FINISH_ATTACK if(carved) //Attempt to remove inserted object, if none found, remind user that someone vandalized their book (Bastards)! if(!remove_stored_item(user, TRUE)) to_chat(user, "The pages of [title] have been cut out!") - return + return FINISH_ATTACK user.visible_message("[user] opens a book titled \"[title]\" and begins reading intently.") read_book(user) + return ..() -/obj/item/book/attackby__legacy__attackchain(obj/item/I, mob/user, params) - if(is_pen(I)) +/obj/item/book/item_interaction(mob/living/user, obj/item/used, list/modifiers) + if(imaginary) + return ITEM_INTERACT_COMPLETE + if(is_pen(used)) edit_book(user) - else if(istype(I, /obj/item/barcodescanner)) - var/obj/item/barcodescanner/scanner = I + else if(istype(used, /obj/item/barcodescanner)) + var/obj/item/barcodescanner/scanner = used scanner.scanBook(src, user) //abstraction and proper scoping ftw | did you know barcode scanner code used to be here? - return - else if(I.sharp && !carved) //don't use sharp objects on your books if you don't want to carve out all of its pages kids! - carve_book(user, I) - else if(store_item(I, user)) - return + return ITEM_INTERACT_COMPLETE + else if(used.sharp && !carved) //don't use sharp objects on your books if you don't want to carve out all of its pages kids! + carve_book(user, used) + return ITEM_INTERACT_COMPLETE + else if(store_item(used, user)) + return ITEM_INTERACT_COMPLETE else - ..() + return ..() /obj/item/book/examine(mob/user) . = ..() diff --git a/code/modules/mob/living/carbon/human/human_life.dm b/code/modules/mob/living/carbon/human/human_life.dm index 7c524d33edf..b450e7e007f 100644 --- a/code/modules/mob/living/carbon/human/human_life.dm +++ b/code/modules/mob/living/carbon/human/human_life.dm @@ -109,20 +109,22 @@ for(var/mutation_type in active_mutations) var/datum/mutation/mutation = GLOB.dna_mutations[mutation_type] mutation.on_life(src) - - if(!ignore_gene_stability && gene_stability < GENETIC_DAMAGE_STAGE_1) + var/gene_bonus + if(mind && HAS_TRAIT(mind, TRAIT_GENETIC_BUDGET)) + gene_bonus = 5 + if(!ignore_gene_stability && gene_stability < GENETIC_DAMAGE_STAGE_1 - gene_bonus) var/instability = DEFAULT_GENE_STABILITY - gene_stability if(prob(instability * 0.1)) adjustFireLoss(min(10, instability * 0.67)) to_chat(src, "You feel like your skin is burning and bubbling off!") - if(gene_stability < GENETIC_DAMAGE_STAGE_2) + if(gene_stability < GENETIC_DAMAGE_STAGE_2 - gene_bonus) if(prob(instability * 0.83)) adjustCloneLoss(min(8, instability * 0.05)) to_chat(src, "You feel as if your body is warping.") if(prob(instability * 0.1)) adjustToxLoss(min(10, instability * 0.67)) to_chat(src, "You feel weak and nauseous.") - if(gene_stability < GENETIC_DAMAGE_STAGE_3 && prob(1)) + if(gene_stability < (GENETIC_DAMAGE_STAGE_3 - gene_bonus) && prob(1)) to_chat(src, "You feel incredibly sick... Something isn't right!") spawn(300) if(gene_stability < GENETIC_DAMAGE_STAGE_3) @@ -895,8 +897,9 @@ if(HAS_TRAIT(H, TRAIT_NOBREATH)) continue //no puking if you can't smell! // Humans can lack a mind datum, y'know - if(H.mind && (H.mind.assigned_role == "Detective" || H.mind.assigned_role == "Coroner")) - continue //too cool for puke + if(H.mind && HAS_TRAIT(H.mind, TRAIT_CORPSE_RESIST)) + to_chat(H, "You smell something rotting nearby.") + continue to_chat(H, "You smell something foul...") H.fakevomit() diff --git a/code/modules/mob/living/carbon/human/human_mob.dm b/code/modules/mob/living/carbon/human/human_mob.dm index 7ddc06cc77f..39bcd00c6f7 100644 --- a/code/modules/mob/living/carbon/human/human_mob.dm +++ b/code/modules/mob/living/carbon/human/human_mob.dm @@ -2174,3 +2174,45 @@ Eyes need to have significantly high darksight to shine unless the mob has the X return FALSE return TRUE + +/mob/living/carbon/human/AltClick(mob/user, modifiers) + . = ..() + if(user.stat || user.restrained() || (!in_range(src, user))) + return + + if(user.mind && !HAS_TRAIT(user.mind, TRAIT_MED_EXAMINE)) + return + + user.visible_message("[user] begins to thoroughly examine [src].") + if(do_after_once(user, 12 SECONDS, target = src, allow_moving = FALSE, attempt_cancel_message = "You couldn't get a good look at [src]!")) + var/list/missing = list("head", "chest", "groin", "l_arm", "r_arm", "l_hand", "r_hand", "l_leg", "r_leg", "l_foot", "r_foot") + var/list/analysis = list() + for(var/obj/item/organ/external/E in src.bodyparts) + missing -= E.limb_name + if(E.status & ORGAN_DEAD) + analysis += "You conclude [src]'s [E.name] is dead." + if(E.status & ORGAN_INT_BLEEDING) + analysis += "You conclude [src]'s [E.name] has internal bleeding." + if(E.status & ORGAN_BURNT) + analysis += "You conclude [src]'s [E.name] has been critically burned." + if(E.status & ORGAN_BROKEN) + if(!E.broken_description) + analysis += "You conclude [src]'s [E.name] is broken." + else + analysis += "You conclude [src]'s [E.name] has a [E.broken_description]." + to_chat(user, chat_box_healthscan(analysis.Join("
"))) + +/mob/living/carbon/human/pointed(atom/A as mob|obj|turf in view()) + set name = "Point To" + set category = null + if(next_move >= world.time) + return + + if(istype(A, /obj/effect/temp_visual/point) || istype(A, /atom/movable/emissive_blocker)) + return FALSE + if(mind && HAS_TRAIT(mind, TRAIT_COFFEE_SNOB) && reagents.has_reagent("coffee")) + changeNext_move(CLICK_CD_POINT / 3) + else + changeNext_move(CLICK_CD_POINT) + + DEFAULT_QUEUE_OR_CALL_VERB(VERB_CALLBACK(src, PROC_REF(run_pointed), A)) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 7367e882886..2e1283bad08 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -941,8 +941,12 @@ if(user.a_intent == INTENT_HARM && stat == DEAD && butcher_results && I.sharp) //can we butcher it? to_chat(user, "You begin to butcher [src]...") playsound(loc, 'sound/weapons/slice.ogg', 50, TRUE, -1) - if(do_mob(user, src, 8 SECONDS) && Adjacent(I)) - harvest(user) + if(user.mind && HAS_TRAIT(user.mind, TRAIT_BUTCHER)) + if(do_mob(user, src, 3 SECONDS) && Adjacent(I)) + harvest(user) + else + if(do_mob(user, src, 8 SECONDS) && Adjacent(I)) + harvest(user) return TRUE /mob/living/proc/harvest(mob/living/user) diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index cf18665b4b5..f9357358420 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -104,6 +104,9 @@ /// Famous last words -- if succumbing, what the user's last words were var/last_words + /// Does this mob talk with a BIG VOICE. 0 off, 1 single use, 2 multi-use + var/big_voice = 0 + ///This variable is the chance for a mob to automatically dodge a bullet. Useful for admins, and applied to some mobs by default, such as the malfunctioning drone mobs. var/advanced_bullet_dodge_chance = 0 diff --git a/code/modules/mob/living/living_say.dm b/code/modules/mob/living/living_say.dm index c2974a2a9b5..8db87c4fc1b 100644 --- a/code/modules/mob/living/living_say.dm +++ b/code/modules/mob/living/living_say.dm @@ -105,7 +105,7 @@ GLOBAL_LIST_EMPTY(channel_to_radio_key) return returns -/mob/living/say(message, verb = null, sanitize = TRUE, ignore_speech_problems = FALSE, ignore_atmospherics = FALSE, ignore_languages = FALSE, automatic = FALSE) +/mob/living/say(message, verb = null, sanitize = TRUE, ignore_speech_problems = FALSE, ignore_atmospherics = FALSE, ignore_languages = FALSE, automatic = FALSE, bigvoice = FALSE) if(client) if(check_mute(client.ckey, MUTE_IC)) to_chat(src, "You cannot speak in IC (Muted).") @@ -139,6 +139,9 @@ GLOBAL_LIST_EMPTY(channel_to_radio_key) message = trim_left(message) + if(big_voice) + message = "[message]" + //parse the language code and consume it var/list/message_pieces = list() if(ignore_languages) diff --git a/code/modules/mob/living/silicon/robot/robot_mob.dm b/code/modules/mob/living/silicon/robot/robot_mob.dm index 61b30cc067c..b27f15db510 100644 --- a/code/modules/mob/living/silicon/robot/robot_mob.dm +++ b/code/modules/mob/living/silicon/robot/robot_mob.dm @@ -130,6 +130,8 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( var/base_icon = "" /// If set to TRUE, the robot's 3 module slots will progressively become unusable as they take damage. var/modules_break = TRUE + /// Is the robot already being charged by a roboticist? + var/being_charged /// Maximum brightness of a robot's headlamp. Set as a var for easy adjusting. var/lamp_max = 10 @@ -923,6 +925,33 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( /mob/living/silicon/robot/item_interaction(mob/living/user, obj/item/W, list/modifiers) // Check if the user is trying to insert another component like a radio, actuator, armor etc. + if(istype(W, /obj/item/stock_parts/cell) && user.mind && HAS_TRAIT(user.mind, TRAIT_CYBORG_SPECIALIST) && !opened && user.a_intent != INTENT_HARM) + var/obj/item/stock_parts/cell/donor = W + if(being_charged) + to_chat(user, "You are already charging [src]!") + return ITEM_INTERACT_COMPLETE + if(donor.charge == 0) + to_chat(user, "[donor] has no charge to donate!") + return ITEM_INTERACT_COMPLETE + playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE) + being_charged = TRUE + while(do_after(user, 0.5 SECONDS, target = src)) + var/cell_difference = cell.maxcharge - cell.charge + if(donor.charge >= 500 && cell_difference >= 500) + cell.charge += 500 + donor.charge -= 500 + else if(donor.charge <= cell_difference) + cell.charge += donor.charge + donor.charge = 0 + else if(donor.charge > cell_difference) + cell.charge = cell.maxcharge + donor.charge -= cell_difference + if(donor.charge == 0 || cell.charge >= cell.maxcharge) + donor.update_icon(UPDATE_OVERLAYS) + break + cell.update_icon(UPDATE_OVERLAYS) + being_charged = FALSE + return ITEM_INTERACT_COMPLETE if(istype(W, /obj/item/robot_parts/robot_component) && opened) for(var/V in components) var/datum/robot_component/C = components[V] @@ -1129,10 +1158,6 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( C.uninstall() thing.forceMove(loc) - - - - /mob/living/silicon/robot/attacked_by(obj/item/I, mob/living/user, def_zone) if(I.force && I.damtype != STAMINA && stat != DEAD) //only sparks if real damage is dealt. spark_system.start() diff --git a/code/modules/power/apc/apc.dm b/code/modules/power/apc/apc.dm index 83762150494..e825830a176 100644 --- a/code/modules/power/apc/apc.dm +++ b/code/modules/power/apc/apc.dm @@ -331,8 +331,33 @@ // Adding APC electronics. if(istype(used, /obj/item/apc_electronics) && opened) if(has_electronics()) - to_chat(user, "[src] already contains APC electronics!") - return ITEM_INTERACT_COMPLETE + if(user.mind && HAS_TRAIT(user.mind, TRAIT_ELECTRICAL_SPECIALIST)) + if(stat & BROKEN) + to_chat(user, "[src] is damaged! You must repair the frame before you can install [used]!") + return ITEM_INTERACT_COMPLETE + if(malfhack) + malfai = null + malfhack = FALSE + user.visible_message(\ + "[name] has discarded the strangely programmed APC electronics from [src]!", + "You discarded the strangely programmed board.", + "You hear metallic levering." + ) + else + user.visible_message( + "[user] exchanges out broken the APC electronics inside [src]!", + "You carefully remove the charred electronics, replacing it with a functional board.", + "You hear metallic levering and a crack, followed by a gentle click.") + playsound(loc, 'sound/items/deconstruct.ogg', 50, TRUE) + qdel(used) + electronics_state = APC_ELECTRONICS_INSTALLED + locked = FALSE + stat &= ~MAINT + update_icon() + return ITEM_INTERACT_COMPLETE + else + to_chat(user, "[src] already contains APC electronics!") + return ITEM_INTERACT_COMPLETE if(stat & BROKEN) to_chat(user, "[src] is damaged! You must repair the frame before you can install [used]!") @@ -368,7 +393,7 @@ update_icon() return ITEM_INTERACT_COMPLETE - if(has_electronics()) + if(has_electronics() && user.mind && !HAS_TRAIT(user.mind, TRAIT_ELECTRICAL_SPECIALIST)) to_chat(user, "You cannot repair [src] until you remove the electronics!") return ITEM_INTERACT_COMPLETE diff --git a/code/modules/recycling/sortingmachinery.dm b/code/modules/recycling/sortingmachinery.dm index 1a391158a0c..00a8ed58fa6 100644 --- a/code/modules/recycling/sortingmachinery.dm +++ b/code/modules/recycling/sortingmachinery.dm @@ -231,8 +231,13 @@ if(amount < 3) to_chat(user, "You need more paper.") return - if(!do_after_once(user, 1.5 SECONDS, target = C) || C.opened || !use(3)) // Checking these again since it's after a delay - return + // Checking these again since it's after a delay + if(user.mind && HAS_TRAIT(user.mind, TRAIT_PACK_RAT)) + if(!do_after_once(user, 0.75 SECONDS, target = C) || C.opened || !use(3)) + return + else + if(!do_after_once(user, 2 SECONDS, target = C) || C.opened || !use(3)) + return var/obj/structure/big_delivery/P = new(get_turf(C)) P.wrapped = C C.loc = P diff --git a/code/modules/smithing/smith_item.dm b/code/modules/smithing/smith_item.dm index 636e07b0ccf..84b21e62d59 100644 --- a/code/modules/smithing/smith_item.dm +++ b/code/modules/smithing/smith_item.dm @@ -41,3 +41,8 @@ name = "[quality.name] " + name return name = "[quality.name] [material.name] [initial(name)]" + +/obj/item/smithed_item/examine(mob/user) + . = ..() + if(HAS_TRAIT(user.mind, TRAIT_SMITH)) + . += "You can use ALT + Click while in-hand to determine specific statistics of this item." diff --git a/code/modules/smithing/smithed_items/inserts.dm b/code/modules/smithing/smithed_items/inserts.dm index 64df3631286..6047bd154c7 100644 --- a/code/modules/smithing/smithed_items/inserts.dm +++ b/code/modules/smithing/smithed_items/inserts.dm @@ -168,3 +168,20 @@ siemens_coeff = 1 quality = /datum/smith_quality/masterwork material = /datum/smith_material/platinum + +/obj/item/smithed_item/insert/AltClick(mob/user, modifiers) + if(!HAS_TRAIT(user.mind, TRAIT_SMITH)) + return + if(do_after_once(user, 3 SECONDS, target = src, allow_moving = TRUE, must_be_held = TRUE)) + var/compiled_message = "\ + You determine the following properties on [src]:
\ + Brute Armor mod: [brute_armor]
\ + Burn Armor mod: [burn_armor]
\ + Laser Armor mod: [laser_armor]
\ + Explosive Resistance mod: [explosive_armor]
\ + Movement Speed mod: [movement_speed_mod]
\ + Heat Insulation mod: [heat_insulation]
\ + Siemens Coefficient mod: [siemens_coeff]
\ + Radiation Resistance mod: [radiation_armor]
\ +
" + to_chat(user, compiled_message) diff --git a/code/modules/smithing/smithed_items/lens.dm b/code/modules/smithing/smithed_items/lens.dm index eb325a3f595..52aded1eb4b 100644 --- a/code/modules/smithing/smithed_items/lens.dm +++ b/code/modules/smithing/smithed_items/lens.dm @@ -148,3 +148,21 @@ durability = 3000 quality = /datum/smith_quality/masterwork material = /datum/smith_material/platinum + +/obj/item/smithed_item/lens/AltClick(mob/user, modifiers) + if(!HAS_TRAIT(user.mind, TRAIT_SMITH)) + return + if(do_after_once(user, 3 SECONDS, target = src, allow_moving = TRUE, must_be_held = TRUE)) + var/compiled_message = "\ + You determine the following properties on [src]:
\ + Base Laser Speed mod: [base_laser_speed_mult]
\ + Base Power Draw mod: [base_power_mult]
\ + Base Damage mod: [base_damage_mult]
\ + Base Fire Rate mod: [base_fire_rate_mult]
\ + Laser Speed Multiplier: [laser_speed_mult]
\ + Power Draw Multiplier: [power_mult]
\ + Damage multiplier: [damage_mult]
\ + Fire Rate Multiplier: [fire_rate_mult]
\ + Durability: [durability]
\ +
" + to_chat(user, compiled_message) diff --git a/code/modules/smithing/smithed_items/tool_bits.dm b/code/modules/smithing/smithed_items/tool_bits.dm index b8866e35fc1..e3d428ca619 100644 --- a/code/modules/smithing/smithed_items/tool_bits.dm +++ b/code/modules/smithing/smithed_items/tool_bits.dm @@ -150,3 +150,21 @@ durability = 300 quality = /datum/smith_quality/masterwork material = /datum/smith_material/platinum + +/obj/item/smithed_item/tool_bit/AltClick(mob/user, modifiers) + if(!HAS_TRAIT(user.mind, TRAIT_SMITH)) + return + if(do_after_once(user, 3 SECONDS, target = src, allow_moving = TRUE, must_be_held = TRUE)) + var/compiled_message = "\ + You determine the following properties on [src]:
\ + Base Speed mod: [base_speed_mod]
\ + Base Efficiency mod: [base_efficiency_mod]
\ + Base Productivity mod: [base_productivity_mod]
\ + Speed Multiplier: [speed_mod]
\ + Efficiency Multiplier: [efficiency_mod]
\ + Productivity Multiplier: [productivity_mod]
\ + Failure Rate: [failure_rate]
\ + Size Mod: [size_mod]
\ + Durability: [durability]
\ +
" + to_chat(user, compiled_message) diff --git a/code/modules/vehicle/ambulance.dm b/code/modules/vehicle/ambulance.dm index 12f543cb644..1238a0de208 100644 --- a/code/modules/vehicle/ambulance.dm +++ b/code/modules/vehicle/ambulance.dm @@ -3,6 +3,7 @@ desc = "This is what the paramedic uses to run over people they need to take to medbay." icon_state = "docwagon2" key_type = /obj/item/key/ambulance + var/siren_on = FALSE var/obj/structure/bed/amb_trolley/bed = null var/datum/action/ambulance_alarm/AA var/datum/looping_sound/ambulance_alarm/soundloop @@ -41,9 +42,11 @@ cooldown = world.time if(A.soundloop.muted) + A.siren_on = TRUE A.soundloop.start() A.set_light(4,3,"#F70027") else + A.siren_on = FALSE A.soundloop.stop() A.set_light(0) @@ -104,6 +107,51 @@ var/mob/living/buckled_mob = m buckled_mob.setDir(Dir) +/obj/vehicle/ambulance/Bump(atom/movable/M) + if(has_buckled_mobs()) + for(var/mob/living/L in buckled_mobs) + if(L.mind && HAS_TRAIT(L.mind, TRAIT_SPEED_DEMON)) + if(isobj(M)) + var/obj/O = M + if(!O.anchored) + step(M, dir) + else if(ismob(M) && siren_on) + run_over(M) + break + +/obj/vehicle/ambulance/proc/run_over(mob/living/M) + var/directional_blocked = FALSE + var/turf/T = get_step(M.loc, turn(dir, 90)) + if(check_density(T)) + T = get_step(M.loc, turn(dir, -90)) + if(check_density(T)) + directional_blocked = TRUE + else + step(M, turn(dir, -90)) + else + step(M, turn(dir, 90)) + playsound(src, 'sound/weapons/punch4.ogg', 50, TRUE) + if(directional_blocked || (emagged == TRUE && installed_vtec == TRUE)) // GET OUT OF THE WAY, ASSHOLE! + M.KnockDown(2 SECONDS) + +/obj/vehicle/ambulance/proc/check_density(turf/T) + if(T.density) + return TRUE + for(var/atom/movable/thing in T.contents) + if(thing.density) + return TRUE + return FALSE + +/obj/vehicle/ambulance/emag_act(mob/user) + emagged = TRUE + visible_message("[src] sputters and fizzles as the safeties are shorted out!") + do_sparks(3, 0, src) + +/obj/vehicle/ambulance/examine(mob/user) + . = ..() + if(emagged && in_range(src, user)) + . += "The safeties seem to have been shorted out!" + /obj/structure/bed/amb_trolley name = "ambulance train trolley" icon = 'icons/vehicles/CargoTrain.dmi'