diff --git a/code/__defines/damage_organs.dm b/code/__defines/damage_organs.dm index c87b9942dfc..db1a71c77a6 100644 --- a/code/__defines/damage_organs.dm +++ b/code/__defines/damage_organs.dm @@ -61,4 +61,8 @@ #define INFECTION_LEVEL_ONE 100 #define INFECTION_LEVEL_TWO 500 #define INFECTION_LEVEL_THREE 1000 -#define INFECTION_LEVEL_MAX 1500 \ No newline at end of file +#define INFECTION_LEVEL_MAX 1500 + +#define MODULAR_BODYPART_INVALID 0 // Cannot be detached or reattached. +#define MODULAR_BODYPART_PROSTHETIC 1 // Can be detached or reattached freely. +#define MODULAR_BODYPART_CYBERNETIC 2 // Can be detached or reattached to compatible parent organs. diff --git a/code/_onclick/cyborg.dm b/code/_onclick/cyborg.dm index d63d49e8d76..213fe57b90e 100644 --- a/code/_onclick/cyborg.dm +++ b/code/_onclick/cyborg.dm @@ -60,6 +60,8 @@ // Cyborgs have no range-checking unless there is item use if(!W) + if(bolt && !bolt.malfunction && A.loc != module) + return A.add_hiddenprint(src) A.attack_robot(src) return @@ -120,35 +122,56 @@ /atom/proc/BorgCtrlShiftClick(var/mob/living/silicon/robot/user) //forward to human click if not overriden CtrlShiftClick(user) -/obj/machinery/door/airlock/BorgCtrlShiftClick(mob/user) +/obj/machinery/door/airlock/BorgCtrlShiftClick(var/mob/living/silicon/robot/user) + if(user.bolt && !user.bolt.malfunction) + return + AICtrlShiftClick(user) /atom/proc/BorgShiftClick(var/mob/living/silicon/robot/user) //forward to human click if not overriden ShiftClick(user) -/obj/machinery/door/airlock/BorgShiftClick(mob/user) // Opens and closes doors! Forwards to AI code. +/obj/machinery/door/airlock/BorgShiftClick(var/mob/living/silicon/robot/user) // Opens and closes doors! Forwards to AI code. + if(user.bolt && !user.bolt.malfunction) + return + AIShiftClick(user) /atom/proc/BorgCtrlClick(var/mob/living/silicon/robot/user) //forward to human click if not overriden CtrlClick(user) -/obj/machinery/door/airlock/BorgCtrlClick(mob/user) // Bolts doors. Forwards to AI code. +/obj/machinery/door/airlock/BorgCtrlClick(var/mob/living/silicon/robot/user) // Bolts doors. Forwards to AI code. + if(user.bolt && !user.bolt.malfunction) + return + AICtrlClick(user) -/obj/machinery/power/apc/BorgCtrlClick(mob/user) // turns off/on APCs. Forwards to AI code. +/obj/machinery/power/apc/BorgCtrlClick(var/mob/living/silicon/robot/user) // turns off/on APCs. Forwards to AI code. + if(user.bolt && !user.bolt.malfunction) + return + AICtrlClick(user) -/obj/machinery/turretid/BorgCtrlClick(mob/user) //turret control on/off. Forwards to AI code. +/obj/machinery/turretid/BorgCtrlClick(var/mob/living/silicon/robot/user) //turret control on/off. Forwards to AI code. + if(user.bolt && !user.bolt.malfunction) + return + AICtrlClick(user) /atom/proc/BorgAltClick(var/mob/living/silicon/robot/user) AltClick(user) return -/obj/machinery/door/airlock/BorgAltClick(mob/user) // Eletrifies doors. Forwards to AI code. +/obj/machinery/door/airlock/BorgAltClick(var/mob/living/silicon/robot/user) // Eletrifies doors. Forwards to AI code. + if(user.bolt && !user.bolt.malfunction) + return + AIAltClick(user) -/obj/machinery/turretid/BorgAltClick(mob/user) //turret lethal on/off. Forwards to AI code. +/obj/machinery/turretid/BorgAltClick(var/mob/living/silicon/robot/user) //turret lethal on/off. Forwards to AI code. + if(user.bolt && !user.bolt.malfunction) + return + AIAltClick(user) /* diff --git a/code/datums/supplypacks/robotics.dm b/code/datums/supplypacks/robotics.dm index 5f8cf6337c8..1928ee7db68 100644 --- a/code/datums/supplypacks/robotics.dm +++ b/code/datums/supplypacks/robotics.dm @@ -189,6 +189,17 @@ containername = "Jumper kit crate" access = access_robotics +/datum/supply_pack/robotics/restrainingbolt + name = "Restraining bolt crate" + contains = list( + /obj/item/weapon/implanter = 1, + /obj/item/weapon/implantcase/restrainingbolt = 2 + ) + cost = 40 + containertype = /obj/structure/closet/crate/secure/cybersolutions + containername = "Restraining bolt crate" + access = access_robotics + /datum/supply_pack/robotics/bike name = "Spacebike Crate" contains = list() diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index 014efcb5813..39d54ef96d5 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -862,14 +862,14 @@ About the new airlock wires panel: /obj/machinery/door/airlock/tgui_data(mob/user) var/list/data = list() - + var/list/power = list() power["main"] = main_power_lost_until > 0 ? 0 : 2 power["main_timeleft"] = round(main_power_lost_until > 0 ? max(main_power_lost_until - world.time, 0) / 10 : main_power_lost_until, 1) power["backup"] = backup_power_lost_until > 0 ? 0 : 2 power["backup_timeleft"] = round(backup_power_lost_until > 0 ? max(backup_power_lost_until - world.time, 0) / 10 : backup_power_lost_until, 1) data["power"] = power - + data["shock"] = (electrified_until == 0) ? 2 : 0 data["shock_timeleft"] = round(electrified_until > 0 ? max(electrified_until - world.time, 0) / 10 : electrified_until, 1) data["id_scanner"] = !aiDisabledIdScanner diff --git a/code/game/objects/items/weapons/implants/implantcase.dm b/code/game/objects/items/weapons/implants/implantcase.dm index c4040890283..9162be15eb5 100644 --- a/code/game/objects/items/weapons/implants/implantcase.dm +++ b/code/game/objects/items/weapons/implants/implantcase.dm @@ -299,3 +299,13 @@ src.imp = new /obj/item/weapon/implant/organ/limbaugment/wrist/blade( src ) ..() return + +/obj/item/weapon/implantcase/restrainingbolt + name = "glass case - 'Restraining Bolt'" + desc = "A case containing a restraining bolt." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/restrainingbolt/New() + src.imp = new /obj/item/weapon/implant/restrainingbolt( src ) + ..() + return diff --git a/code/game/objects/items/weapons/implants/implanter.dm b/code/game/objects/items/weapons/implants/implanter.dm index c1b0d5db8dd..adb79364dcd 100644 --- a/code/game/objects/items/weapons/implants/implanter.dm +++ b/code/game/objects/items/weapons/implants/implanter.dm @@ -150,3 +150,12 @@ S.remove_from_storage(A) A.loc.contents.Remove(A) update() + +/obj/item/weapon/implanter/restrainingbolt + name = "implanter (bolt)" + +/obj/item/weapon/implanter/restrainingbolt/New() + src.imp = new /obj/item/weapon/implant/restrainingbolt( src ) + ..() + update() + return diff --git a/code/game/objects/items/weapons/implants/implantrestrainingbolt.dm b/code/game/objects/items/weapons/implants/implantrestrainingbolt.dm new file mode 100644 index 00000000000..7f4a562297c --- /dev/null +++ b/code/game/objects/items/weapons/implants/implantrestrainingbolt.dm @@ -0,0 +1,5 @@ + +/obj/item/weapon/implant/restrainingbolt + name = "\improper restraining bolt" + icon = 'icons/obj/device.dmi' + icon_state = "implant" diff --git a/code/game/turfs/simulated/outdoors/snow.dm b/code/game/turfs/simulated/outdoors/snow.dm index cccc028fa30..366fdfa5e69 100644 --- a/code/game/turfs/simulated/outdoors/snow.dm +++ b/code/game/turfs/simulated/outdoors/snow.dm @@ -31,7 +31,8 @@ to_chat(user, "You begin to remove \the [src] with your [W].") if(do_after(user, 4 SECONDS * W.toolspeed)) to_chat(user, "\The [src] has been dug up, and now lies in a pile nearby.") - new /obj/item/stack/material/snow(src) + var/obj/item/stack/material/snow/S = new(src) + S.amount = 10 demote() else to_chat(user, "You decide to not finish removing \the [src].") diff --git a/code/game/verbs/advanced_who.dm b/code/game/verbs/advanced_who.dm index 4498136b2b3..8cbf3b638e5 100644 --- a/code/game/verbs/advanced_who.dm +++ b/code/game/verbs/advanced_who.dm @@ -85,6 +85,7 @@ entry += " - In Lobby
" else entry += " - Playing
" + Lines += entry msg += "" @@ -93,4 +94,4 @@ msg += "
" msg += "Total Players: [length(Lines)]" msg = "" + msg + "" - to_chat(src, msg) \ No newline at end of file + to_chat(src, msg) diff --git a/code/game/verbs/who.dm b/code/game/verbs/who.dm index 3cb25b702e4..385a3230200 100644 --- a/code/game/verbs/who.dm +++ b/code/game/verbs/who.dm @@ -1,4 +1,3 @@ - /client/verb/who() set name = "Who" set category = "OOC" @@ -206,3 +205,4 @@ msg += "\nAdminhelps are also sent to Discord. If no admins are available in game try anyway and an admin on Discord may see it and respond." return msg + diff --git a/code/modules/client/verbs/ping.dm b/code/modules/client/verbs/ping.dm new file mode 100644 index 00000000000..8213daa7505 --- /dev/null +++ b/code/modules/client/verbs/ping.dm @@ -0,0 +1,12 @@ +/client/proc/pingfromtime(time) + return ((world.time+world.tick_lag*world.tick_usage/100)-time)*100 + +/client/verb/display_ping(time as num) + set instant = TRUE + set name = ".display_ping" + to_chat(src, "Round trip ping took [round(pingfromtime(time),1)]ms") + +/client/verb/ping() + set name = "Ping" + set category = "OOC" + winset(src, null, "command=.display_ping+[world.time+world.tick_lag*world.tick_usage/100]") \ No newline at end of file diff --git a/code/modules/materials/material_sheets.dm b/code/modules/materials/material_sheets.dm index f837ec3e591..0c6f665631e 100644 --- a/code/modules/materials/material_sheets.dm +++ b/code/modules/materials/material_sheets.dm @@ -431,6 +431,8 @@ name = "snow" desc = "The temptation to build a snowman rises." icon_state = "sheet-snow" + drop_sound = 'sound/items/drop/gloves.ogg' + pickup_sound = 'sound/items/pickup/clothing.ogg' default_type = "snow" /obj/item/stack/material/snowbrick @@ -438,6 +440,8 @@ desc = "For all of your igloo building needs." icon_state = "sheet-snowbrick" default_type = "packed snow" + drop_sound = 'sound/items/drop/gloves.ogg' + pickup_sound = 'sound/items/pickup/clothing.ogg' /obj/item/stack/material/leather name = "leather" diff --git a/code/modules/mob/emote.dm b/code/modules/mob/emote.dm index bff705d0c51..2a8af560804 100644 --- a/code/modules/mob/emote.dm +++ b/code/modules/mob/emote.dm @@ -37,14 +37,15 @@ var/mob/M = mob spawn(0) // It's possible that it could be deleted in the meantime, or that it runtimes. if(M) - //VOREStation edit - if(istype(M, /mob/observer/dead/)) + if(isobserver(M)) + //VOREStation Edit Start var/mob/observer/dead/D = M if(ckey || (src in view(D))) M.show_message(message, m_type) + message = "[src] ([ghost_follow_link(src, M)]) [input]" else M.show_message(message, m_type) - //End VOREStation edit + //VOREStation Edit End for(var/obj in o_viewers) var/obj/O = obj diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm index 7494867d2d8..435ca993546 100644 --- a/code/modules/mob/living/carbon/human/human_helpers.dm +++ b/code/modules/mob/living/carbon/human/human_helpers.dm @@ -208,6 +208,20 @@ plane_holder.set_vis(vis,FALSE) vis_enabled -= vis +/mob/living/carbon/human/get_restraining_bolt() + var/obj/item/weapon/implant/restrainingbolt/RB + + for(var/obj/item/organ/external/EX in organs) + RB = locate() in EX + if(istype(RB) && !(RB.malfunction)) + break + + if(RB) + if(!RB.malfunction) + return TRUE + + return FALSE + #undef HUMAN_EATING_NO_ISSUE #undef HUMAN_EATING_NO_MOUTH #undef HUMAN_EATING_BLOCKED_MOUTH diff --git a/code/modules/mob/living/carbon/human/human_modular_limbs.dm b/code/modules/mob/living/carbon/human/human_modular_limbs.dm new file mode 100644 index 00000000000..471e8a42cd8 --- /dev/null +++ b/code/modules/mob/living/carbon/human/human_modular_limbs.dm @@ -0,0 +1,196 @@ +/* + A system for easily and quickly removing your own bodyparts, with a view towards + swapping them out for new ones, or just doing it as a party trick to horrify an + audience. Current implementation only supports robolimbs and uses a modular_bodypart + value on the manufacturer datum, but I have tried to keep it generic for future work. + PS. jesus christ this was meant to be a half an hour port +*/ + +// External organ procs: +// Does this bodypart count as a modular limb, and if so, what kind? +/obj/item/organ/external/proc/get_modular_limb_category() + . = MODULAR_BODYPART_INVALID + if(robotic >= ORGAN_ROBOT && model) + var/datum/robolimb/manufacturer = all_robolimbs[model] + if(!isnull(manufacturer?.modular_bodyparts)) + . = manufacturer.modular_bodyparts + +// Checks if a limb could theoretically be removed. +// Note that this does not currently bother checking if a child or internal organ is vital. +/obj/item/organ/external/proc/can_remove_modular_limb(var/mob/living/carbon/human/user) + if(vital || cannot_amputate) + return FALSE + var/bodypart_cat = get_modular_limb_category() + if(bodypart_cat == MODULAR_BODYPART_CYBERNETIC) + if(!parent_organ) + return FALSE + var/obj/item/organ/external/parent = user?.get_organ(parent_organ) + if(!parent || parent.get_modular_limb_category(user) < MODULAR_BODYPART_CYBERNETIC) + return FALSE + . = (bodypart_cat != MODULAR_BODYPART_INVALID) + +// Note that this proc is checking if the organ can be attached -to-, not attached itself. +/obj/item/organ/external/proc/can_attach_modular_limb_here(var/mob/living/carbon/human/user) + var/list/limb_data = user?.species?.has_limbs[organ_tag] + if(islist(limb_data) && limb_data["has_children"] > 0) + . = (length(children) < limb_data["has_children"]) + +/obj/item/organ/external/proc/can_be_attached_modular_limb(var/mob/living/carbon/user) + var/bodypart_cat = get_modular_limb_category() + if(bodypart_cat == MODULAR_BODYPART_INVALID) + return FALSE + if(!parent_organ) + return FALSE + var/obj/item/organ/external/parent = user?.get_organ(parent_organ) + if(!parent) + return FALSE + if(!parent.can_attach_modular_limb_here(user)) + return FALSE + if(bodypart_cat == MODULAR_BODYPART_CYBERNETIC && parent.get_modular_limb_category(src) < MODULAR_BODYPART_CYBERNETIC) + return FALSE + return TRUE + +// Checks if an organ (or the parent of one) is in a fit state for modular limb stuff to happen. +/obj/item/organ/external/proc/check_modular_limb_damage(var/mob/living/carbon/human/user) + . = damage >= min_broken_damage || (status & ORGAN_BROKEN) || is_stump() // can't use is_broken() as the limb has ORGAN_CUT_AWAY + +// Human mob procs: +// Checks the organ list for limbs meeting a predicate. Way overengineered for such a limited use +// case but I can see it being expanded in the future if meat limbs or doona limbs use it. +/mob/living/carbon/human/proc/get_modular_limbs(var/return_first_found = FALSE, var/validate_proc) + for(var/bp in organs) + var/obj/item/organ/external/E = bp + if(!validate_proc || call(E, validate_proc)(src) > MODULAR_BODYPART_INVALID) + LAZYADD(., E) + if(return_first_found) + return + // Prune children so we can't remove every individual component of an entire prosthetic arm + // piece by piece. Technically a circular dependency here would remove the limb entirely but + // if there's a parent whose child is also its parent, there's something wrong regardless. + for(var/bp in .) + var/obj/item/organ/external/E = bp + if(length(E.children)) + . -= E.children + +// Called in robotize(), replaced() and removed() to update our modular limb verbs. +/mob/living/carbon/human/proc/refresh_modular_limb_verbs() + if(length(get_modular_limbs(return_first_found = TRUE, validate_proc = /obj/item/organ/external/proc/can_attach_modular_limb_here))) + verbs |= .proc/attach_limb_verb + else + verbs -= .proc/attach_limb_verb + if(length(get_modular_limbs(return_first_found = TRUE, validate_proc = /obj/item/organ/external/proc/can_remove_modular_limb))) + verbs |= .proc/detach_limb_verb + else + verbs -= .proc/detach_limb_verb + +// Proc helper for attachment verb. +/mob/living/carbon/human/proc/check_can_attach_modular_limb(var/obj/item/organ/external/E) + if(world.time < last_special + (2 SECONDS) || get_active_hand() != E) + return FALSE + if(incapacitated() || restrained()) + to_chat(src, SPAN_WARNING("You can't do that in your current state!")) + return FALSE + if(QDELETED(E) || !istype(E)) + to_chat(src, SPAN_WARNING("You are not holding a compatible limb to attach.")) + return FALSE + if(!E.can_be_attached_modular_limb(src)) + to_chat(src, SPAN_WARNING("\The [E] cannot be attached to your current body.")) + return FALSE + if(E.get_modular_limb_category() <= MODULAR_BODYPART_INVALID) + to_chat(src, SPAN_WARNING("\The [E] cannot be attached by your own hand.")) + return FALSE + var/install_to_zone = E.organ_tag + if(!isnull(get_organ(install_to_zone))) + to_chat(src, SPAN_WARNING("There is already a limb attached at that part of your body.")) + return FALSE + if(E.check_modular_limb_damage(src)) + to_chat(src, SPAN_WARNING("\The [E] is too damaged to be attached.")) + return FALSE + var/obj/item/organ/external/parent = E.parent_organ && get_organ(E.parent_organ) + if(!parent) + to_chat(src, SPAN_WARNING("\The [E] needs an existing limb to be attached to.")) + return FALSE + if(parent.check_modular_limb_damage(src)) + to_chat(src, SPAN_WARNING("Your [parent.name] is too damaged to have anything attached.")) + return FALSE + return TRUE + +// Proc helper for detachment verb. +/mob/living/carbon/human/proc/check_can_detach_modular_limb(var/obj/item/organ/external/E) + if(world.time < last_special + (2 SECONDS)) + return FALSE + if(incapacitated() || restrained()) + to_chat(src, SPAN_WARNING("You can't do that in your current state!")) + return FALSE + if(!istype(E) || QDELETED(src) || QDELETED(E) || E.owner != src || E.loc != src) + return FALSE + if(E.check_modular_limb_damage(src)) + to_chat(src, SPAN_WARNING("That limb is too damaged to be removed!")) + return FALSE + var/obj/item/organ/external/parent = E.parent_organ && get_organ(E.parent_organ) + if(!parent) + return FALSE + if(parent.check_modular_limb_damage(src)) + to_chat(src, SPAN_WARNING("Your [parent.name] is too damaged to detach anything from it.")) + return FALSE + return (E in get_modular_limbs(return_first_found = FALSE, validate_proc = /obj/item/organ/external/proc/can_remove_modular_limb)) + +// Verbs below: +// Add or remove robotic limbs; check refresh_modular_limb_verbs() above. +/mob/living/carbon/human/proc/attach_limb_verb() + set name = "Attach Limb" + set category = "Object" + set desc = "Attach a replacement limb." + set usr = src + + var/obj/item/organ/external/E = get_active_hand() + if(!check_can_attach_modular_limb(E)) + return FALSE + if(!do_after(src, 2 SECONDS, src)) + return FALSE + if(!check_can_attach_modular_limb(E)) + return FALSE + + last_special = world.time + drop_from_inventory(E) + E.replaced(src) + + // Reconnect the organ and children as normally this is done with surgery. + E.status &= ~ORGAN_CUT_AWAY + for(var/obj/item/organ/external/child in E.children) + child.status &= ~ORGAN_CUT_AWAY + + var/datum/gender/G = gender_datums[gender] + visible_message( + SPAN_NOTICE("\The [src] attaches \the [E] to [G.his] body!"), + SPAN_NOTICE("You attach \the [E] to your body!")) + regenerate_icons() // Not sure why this isn't called by removed(), but without it we don't update our limb appearance. + return TRUE + +/mob/living/carbon/human/proc/detach_limb_verb() + set name = "Remove Limb" + set category = "Object" + set desc = "Detach one of your limbs." + set usr = src + + var/list/detachable_limbs = get_modular_limbs(return_first_found = FALSE, validate_proc = /obj/item/organ/external/proc/can_remove_modular_limb) + if(!length(detachable_limbs)) + to_chat(src, SPAN_WARNING("You have no detachable limbs.")) + return FALSE + var/obj/item/organ/external/E = input(usr, "Which limb do you wish to detach?", "Limb Removal") as null|anything in detachable_limbs + if(!check_can_detach_modular_limb(E)) + return FALSE + if(!do_after(src, 2 SECONDS, src)) + return FALSE + if(!check_can_detach_modular_limb(E)) + return FALSE + + last_special = world.time + E.removed(src) + E.dropInto(loc) + put_in_hands(E) + var/datum/gender/G = gender_datums[gender] + visible_message( + SPAN_NOTICE("\The [src] detaches [G.his] [E.name]!"), + SPAN_NOTICE("You detach your [E.name]!")) + return TRUE diff --git a/code/modules/mob/living/carbon/human/human_powers.dm b/code/modules/mob/living/carbon/human/human_powers.dm index d63b866e6fa..d7c66d52895 100644 --- a/code/modules/mob/living/carbon/human/human_powers.dm +++ b/code/modules/mob/living/carbon/human/human_powers.dm @@ -366,7 +366,7 @@ return var/datum/robolimb/robohead = all_robolimbs[E.model] if(!robohead.monitor_styles || !robohead.monitor_icon) - to_chat(src,"Your head doesn't have a monitor or it doens't support to be changed!") + to_chat(src,"Your head doesn't have a monitor, or it doesn't support being changed!") return var/list/states if(!states) diff --git a/code/modules/mob/living/carbon/human/species/outsider/event.dm b/code/modules/mob/living/carbon/human/species/outsider/event.dm index 3566010050b..3604e201f39 100644 --- a/code/modules/mob/living/carbon/human/species/outsider/event.dm +++ b/code/modules/mob/living/carbon/human/species/outsider/event.dm @@ -122,7 +122,7 @@ Variables you may want to make use of are: has_limbs = list( BP_TORSO = list("path" = /obj/item/organ/external/chest), BP_GROIN = list("path" = /obj/item/organ/external/groin), - BP_HEAD = list("path" = /obj/item/organ/external/head), + BP_HEAD = list("path" = /obj/item/organ/external/head), BP_L_ARM = list("path" = /obj/item/organ/external/arm), BP_R_ARM = list("path" = /obj/item/organ/external/arm/right), BP_L_LEG = list("path" = /obj/item/organ/external/leg), diff --git a/code/modules/mob/living/carbon/human/species/species.dm b/code/modules/mob/living/carbon/human/species/species.dm index de182ae8299..40de5e97997 100644 --- a/code/modules/mob/living/carbon/human/species/species.dm +++ b/code/modules/mob/living/carbon/human/species/species.dm @@ -349,6 +349,9 @@ var/limb_path = organ_data["path"] var/obj/item/organ/O = new limb_path(H) organ_data["descriptor"] = O.name + if(O.parent_organ) + organ_data = has_limbs[O.parent_organ] + organ_data["has_children"] = organ_data["has_children"]+1 for(var/organ_tag in has_organ) var/organ_type = has_organ[organ_tag] diff --git a/code/modules/mob/living/carbon/human/species/station/prometheans_vr.dm b/code/modules/mob/living/carbon/human/species/station/prometheans_vr.dm index 4e7ac9376c3..f2b233cccf5 100644 --- a/code/modules/mob/living/carbon/human/species/station/prometheans_vr.dm +++ b/code/modules/mob/living/carbon/human/species/station/prometheans_vr.dm @@ -19,7 +19,6 @@ color_mult = 1 mob_size = MOB_MEDIUM //As of writing, original was MOB_SMALL - Allows normal swapping - trashcan = 1 //They have goopy bodies. They can just dissolve things within them. appearance_flags = HAS_SKIN_COLOR | HAS_EYE_COLOR | HAS_HAIR_COLOR | RADIATION_GLOWS | HAS_UNDERWEAR diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 5c56b8a4482..7debf735b1c 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -700,6 +700,14 @@ /mob/living/proc/has_eyes() return 1 +/mob/living/proc/get_restraining_bolt() + var/obj/item/weapon/implant/restrainingbolt/RB = locate() in src + if(RB) + if(!RB.malfunction) + return TRUE + + return FALSE + /mob/living/proc/slip(var/slipped_on,stun_duration=8) return 0 diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 7d67ca3cc32..849696ea4c5 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -91,6 +91,8 @@ var/tracking_entities = 0 //The number of known entities currently accessing the internal camera var/braintype = "Cyborg" + var/obj/item/weapon/implant/restrainingbolt/bolt // The restraining bolt installed into the cyborg. + var/list/robot_verbs_default = list( /mob/living/silicon/robot/proc/sensor_mode, /mob/living/silicon/robot/proc/robot_checklaws @@ -481,6 +483,20 @@ return + if(istype(W, /obj/item/weapon/implant/restrainingbolt) && !cell) + if(bolt) + to_chat(user, "There is already a restraining bolt installed in this cyborg.") + return + + else + user.drop_from_inventory(W) + W.forceMove(src) + bolt = W + + to_chat(user, "You install \the [W].") + + return + if(istype(W, /obj/item/weapon/aiModule)) // Trying to modify laws locally. if(!opened) to_chat(user, "You need to open \the [src]'s panel before you can modify them.") @@ -622,6 +638,21 @@ to_chat(user, "Unable to locate a radio.") updateicon() + else if(W.is_wrench() && opened && !cell) + if(bolt) + to_chat(user,"You begin removing \the [bolt].") + + if(do_after(user, 2 SECONDS, src)) + bolt.forceMove(get_turf(src)) + bolt = null + + to_chat(user, "You remove \the [bolt].") + + else + to_chat(user, "There is no restraining bolt installed.") + + return + else if(istype(W, /obj/item/device/encryptionkey/) && opened) if(radio)//sanityyyyyy radio.attackby(W,user)//GTFO, you have your own procs @@ -664,6 +695,30 @@ spark_system.start() return ..() +/mob/living/silicon/robot/GetIdCard() + if(bolt && !bolt.malfunction) + return null + return idcard + +/mob/living/silicon/robot/get_restraining_bolt() + var/obj/item/weapon/implant/restrainingbolt/RB = bolt + + if(istype(RB)) + if(!RB.malfunction) + return TRUE + + return FALSE + +/mob/living/silicon/robot/resist_restraints() + if(bolt) + if(!bolt.malfunction) + visible_message("[src] is trying to break their [bolt]!", "You attempt to break your [bolt]. (This will take around 90 seconds and you need to stand still)") + if(do_after(src, 1.5 MINUTES, src, incapacitation_flags = INCAPACITATION_DISABLED)) + visible_message("[src] manages to break \the [bolt]!", "You successfully break your [bolt].") + bolt.malfunction = MALFUNCTION_PERMANENT + + return + /mob/living/silicon/robot/proc/module_reset() transform_with_anim() //VOREStation edit: sprite animation uneq_all() @@ -1015,6 +1070,9 @@ return 0 /mob/living/silicon/robot/binarycheck() + if(get_restraining_bolt()) + return FALSE + if(is_component_functioning("comms")) var/datum/robot_component/RC = get_component("comms") use_power(RC.active_usage) @@ -1107,6 +1165,11 @@ sleep(20) to_chat(src, "SynBorg v1.7.1 loaded.") sleep(5) + if(bolt) + if(!bolt.malfunction) + bolt.malfunction = MALFUNCTION_PERMANENT + to_chat(src, "RESTRAINING BOLT DISABLED") + sleep(5) to_chat(src, "LAW SYNCHRONISATION ERROR") sleep(5) to_chat(src, "Would you like to send a report to NanoTraSoft? Y/N") diff --git a/code/modules/mob/living/silicon/robot/robot_movement.dm b/code/modules/mob/living/silicon/robot/robot_movement.dm index bc9b7367f2c..7a733f002bd 100644 --- a/code/modules/mob/living/silicon/robot/robot_movement.dm +++ b/code/modules/mob/living/silicon/robot/robot_movement.dm @@ -32,6 +32,9 @@ if(module_active && istype(module_active,/obj/item/borg/combat/mobility)) . -= 2 // VOREStation Edit + if(get_restraining_bolt()) // Borgs with Restraining Bolts move slower. + . += 1 + . += config.robot_delay . += ..() diff --git a/code/modules/organs/internal/augment.dm b/code/modules/organs/internal/augment.dm index bb899e5c2ba..831d9d3b893 100644 --- a/code/modules/organs/internal/augment.dm +++ b/code/modules/organs/internal/augment.dm @@ -64,6 +64,10 @@ else return + if(robotic && owner.get_restraining_bolt()) + to_chat(owner, "\The [src] doesn't respond.") + return + var/item_to_equip = integrated_object if(!item_to_equip && integrated_object_type) item_to_equip = integrated_object_type diff --git a/code/modules/organs/internal/augment/armmounted.dm b/code/modules/organs/internal/augment/armmounted.dm index 833d4be435d..c22988de836 100644 --- a/code/modules/organs/internal/augment/armmounted.dm +++ b/code/modules/organs/internal/augment/armmounted.dm @@ -143,7 +143,7 @@ if(istype(owner, /mob/living/carbon/human)) var/mob/living/carbon/human/H = owner H.add_modifier(/datum/modifier/melee_surge, 0.75 MINUTES) - + /obj/item/organ/internal/augment/armmounted/shoulder/blade name = "armblade implant" desc = "A large implant that fits into a subject's arm. It deploys a large metal blade by some painful means." diff --git a/code/modules/organs/organ_external.dm b/code/modules/organs/organ_external.dm index ab8a91f721d..d9b21f6ceca 100644 --- a/code/modules/organs/organ_external.dm +++ b/code/modules/organs/organ_external.dm @@ -235,6 +235,7 @@ owner.organs |= src for(var/obj/item/organ/organ in src) organ.replaced(owner,src) + owner.refresh_modular_limb_verbs() if(parent_organ) parent = owner.organs_by_name[src.parent_organ] @@ -1129,6 +1130,8 @@ Note that amputating the affected organ does in fact remove the infection from t if(R.lifelike) robotic = ORGAN_LIFELIKE name = "[initial(name)]" + else if(R.modular_bodyparts == MODULAR_BODYPART_PROSTHETIC) + name = "prosthetic [initial(name)]" else name = "robotic [initial(name)]" desc = "[R.desc] It looks like it was produced by [R.company]." @@ -1160,7 +1163,7 @@ Note that amputating the affected organ does in fact remove the infection from t while(null in owner.internal_organs) owner.internal_organs -= null - + owner.refresh_modular_limb_verbs() return 1 /obj/item/organ/external/proc/mutate() @@ -1261,6 +1264,7 @@ Note that amputating the affected organ does in fact remove the infection from t qdel(spark_system) qdel(src) + victim.refresh_modular_limb_verbs() victim.update_icons_body() /obj/item/organ/external/proc/disfigure(var/type = "brute") diff --git a/code/modules/organs/robolimbs.dm b/code/modules/organs/robolimbs.dm index 9eb4c97d4b3..3fa3b910f98 100644 --- a/code/modules/organs/robolimbs.dm +++ b/code/modules/organs/robolimbs.dm @@ -47,22 +47,24 @@ var/const/standard_monitor_styles = "blank=ipc_blank;\ var/icon = 'icons/mob/human_races/robotic.dmi' // Icon base to draw from. var/monitor_icon = 'icons/mob/monitor_icons.dmi' // Where it draws the monitor icon from. var/unavailable_at_chargen // If set, not available at chargen. - var/unavailable_to_build // If set, can't be constructed. - var/lifelike // If set, appears organic. - var/skin_tone // If set, applies skin tone rather than part color Overrides color. - var/skin_color // If set, applies skin color rather than part color. - var/blood_color = "#030303" - var/blood_name = "oil" + var/unavailable_to_build // If set, can't be constructed. + var/lifelike // If set, appears organic. + var/skin_tone // If set, applies skin tone rather than part color Overrides color. + var/skin_color // If set, applies skin color rather than part color. + var/blood_color = SYNTH_BLOOD_COLOUR // Colour for blood splatters. + var/blood_name = "oil" // Descriptor for blood splatters. + var/list/monitor_styles // If empty, the model of limbs offers a head compatible with monitors. + var/parts = BP_ALL // Defines what parts said brand can replace on a body. + var/health_hud_intensity = 1 // Intensity modifier for the health GUI indicator. + var/suggested_species = "Human" // If it should make the torso a species + var/speech_bubble_appearance = "synthetic" // What icon_state to use for speech bubbles when talking. Check talk.dmi for all the icons. + var/modular_bodyparts = MODULAR_BODYPART_INVALID // Whether or not this limb allows attaching/detaching, and whether or not it checks its parent as well. + var/robo_brute_mod = 1 // Multiplier for incoming brute damage. + var/robo_burn_mod = 1 // As above for burn. + // Species in this list cannot take these prosthetics. var/list/species_cannot_use = list(SPECIES_TESHARI, SPECIES_PROMETHEAN, SPECIES_DIONA, SPECIES_XENOCHIMERA) //VOREStation Edit - var/list/species_alternates = list(SPECIES_TAJ = "Unbranded - Tajaran", SPECIES_UNATHI = "Unbranded - Unathi") //"Species Name" = "Robolimb Company" , List, when initialized, will become "Species Name" = RobolimbDatum, used for alternate species sprites. - var/list/monitor_styles //If empty, the model of limbs offers a head compatible with monitors. - var/parts = BP_ALL //Defines what parts said brand can replace on a body. - var/health_hud_intensity = 1 // Intensity modifier for the health GUI indicator. - var/suggested_species = "Human" //If it should make the torso a species - var/speech_bubble_appearance = "synthetic" // What icon_state to use for speech bubbles when talking. Check talk.dmi for all the icons. - - var/robo_brute_mod = 1 // Multiplier for incoming brute damage. - var/robo_burn_mod = 1 // As above for burn. + // "Species Name" = "Robolimb Company", List, when initialized, will become "Species Name" = RobolimbDatum, used for alternate species sprites. + var/list/species_alternates = list(SPECIES_TAJ = "Unbranded - Tajaran", SPECIES_UNATHI = "Unbranded - Unathi") /datum/robolimb/unbranded_monitor company = "Unbranded Monitor" @@ -77,12 +79,14 @@ var/const/standard_monitor_styles = "blank=ipc_blank;\ desc = "A simple robotic limb with retro design. Seems rather stiff." icon = 'icons/mob/human_races/cyberlimbs/unbranded/unbranded_alt1.dmi' unavailable_to_build = 1 + modular_bodyparts = MODULAR_BODYPART_CYBERNETIC /datum/robolimb/unbranded_alt2 company = "Unbranded - Mantis Prosis" desc = "This limb has a casing of sleek black metal and repulsive insectile design." icon = 'icons/mob/human_races/cyberlimbs/unbranded/unbranded_alt2.dmi' unavailable_to_build = 1 + modular_bodyparts = MODULAR_BODYPART_CYBERNETIC /datum/robolimb/unbranded_tajaran company = "Unbranded - Tajaran" @@ -91,6 +95,7 @@ var/const/standard_monitor_styles = "blank=ipc_blank;\ desc = "A simple robotic limb with feline design. Seems rather stiff." icon = 'icons/mob/human_races/cyberlimbs/unbranded/unbranded_tajaran.dmi' unavailable_to_build = 1 + modular_bodyparts = MODULAR_BODYPART_CYBERNETIC /datum/robolimb/unbranded_unathi company = "Unbranded - Unathi" @@ -99,6 +104,7 @@ var/const/standard_monitor_styles = "blank=ipc_blank;\ desc = "A simple robotic limb with reptilian design. Seems rather stiff." icon = 'icons/mob/human_races/cyberlimbs/unbranded/unbranded_unathi.dmi' unavailable_to_build = 1 + modular_bodyparts = MODULAR_BODYPART_CYBERNETIC /datum/robolimb/unbranded_teshari company = "Unbranded - Teshari" @@ -107,12 +113,20 @@ var/const/standard_monitor_styles = "blank=ipc_blank;\ desc = "A simple robotic limb with a small, raptor-like design. Seems rather stiff." icon = 'icons/mob/human_races/cyberlimbs/unbranded/unbranded_teshari.dmi' unavailable_to_build = 0 + modular_bodyparts = MODULAR_BODYPART_CYBERNETIC + parts = list(BP_HEAD, BP_TORSO, BP_GROIN) + +/datum/robolimb/unbranded_teshari/limbs + company = "Unbranded - Teshari (Limbs)" + parts = list(BP_L_ARM, BP_R_ARM, BP_L_HAND, BP_R_HAND, BP_L_LEG, BP_R_LEG, BP_L_FOOT, BP_R_FOOT) + modular_bodyparts = MODULAR_BODYPART_PROSTHETIC /datum/robolimb/nanotrasen company = "NanoTrasen" desc = "A simple but efficient robotic limb, created by NanoTrasen." icon = 'icons/mob/human_races/cyberlimbs/nanotrasen/nanotrasen_main.dmi' species_alternates = list(SPECIES_TAJ = "NanoTrasen - Tajaran", SPECIES_UNATHI = "NanoTrasen - Unathi") + modular_bodyparts = MODULAR_BODYPART_CYBERNETIC /datum/robolimb/nanotrasen_tajaran company = "NanoTrasen - Tajaran" @@ -122,6 +136,7 @@ var/const/standard_monitor_styles = "blank=ipc_blank;\ desc = "A simple but efficient robotic limb, created by NanoTrasen." icon = 'icons/mob/human_races/cyberlimbs/nanotrasen/nanotrasen_tajaran.dmi' unavailable_to_build = 1 + modular_bodyparts = MODULAR_BODYPART_CYBERNETIC /datum/robolimb/nanotrasen_unathi company = "NanoTrasen - Unathi" @@ -131,6 +146,7 @@ var/const/standard_monitor_styles = "blank=ipc_blank;\ desc = "A simple but efficient robotic limb, created by NanoTrasen." icon = 'icons/mob/human_races/cyberlimbs/nanotrasen/nanotrasen_unathi.dmi' unavailable_to_build = 1 + modular_bodyparts = MODULAR_BODYPART_CYBERNETIC /datum/robolimb/cenilimicybernetics_teshari company = "Cenilimi Cybernetics" @@ -140,6 +156,7 @@ var/const/standard_monitor_styles = "blank=ipc_blank;\ desc = "Made by a Teshari-owned company, for Teshari." icon = 'icons/mob/human_races/cyberlimbs/cenilimicybernetics/cenilimicybernetics_teshari.dmi' unavailable_to_build = 1 + modular_bodyparts = MODULAR_BODYPART_CYBERNETIC /datum/robolimb/bishop company = "Bishop" @@ -187,18 +204,21 @@ var/const/standard_monitor_styles = "blank=ipc_blank;\ desc = "This limb is grey and rough, with little in the way of aesthetic." icon = 'icons/mob/human_races/cyberlimbs/cybersolutions/cybersolutions_main.dmi' unavailable_to_build = 1 + modular_bodyparts = MODULAR_BODYPART_CYBERNETIC /datum/robolimb/cybersolutions_alt2 company = "Cyber Solutions - Outdated" desc = "This limb is of severely outdated design; there's no way it's comfortable or very functional to use." icon = 'icons/mob/human_races/cyberlimbs/cybersolutions/cybersolutions_alt2.dmi' unavailable_to_build = 1 + modular_bodyparts = MODULAR_BODYPART_CYBERNETIC /datum/robolimb/cybersolutions_alt1 company = "Cyber Solutions - Wight" desc = "This limb has cheap plastic panels mounted on grey metal." icon = 'icons/mob/human_races/cyberlimbs/cybersolutions/cybersolutions_alt1.dmi' unavailable_to_build = 1 + modular_bodyparts = MODULAR_BODYPART_CYBERNETIC /datum/robolimb/cybersolutions_alt3 company = "Cyber Solutions - Array" @@ -212,6 +232,7 @@ var/const/standard_monitor_styles = "blank=ipc_blank;\ desc = "This limb is lightweight with a sleek design." icon = 'icons/mob/human_races/cyberlimbs/einstein/einstein_main.dmi' unavailable_to_build = 1 + modular_bodyparts = MODULAR_BODYPART_CYBERNETIC /datum/robolimb/grayson company = "Grayson" @@ -223,6 +244,7 @@ var/const/standard_monitor_styles = "blank=ipc_blank;\ green=grayson_green;\ blue=grayson_blue;\ rgb=grayson_rgb" + modular_bodyparts = MODULAR_BODYPART_CYBERNETIC /datum/robolimb/grayson_alt1 company = "Grayson - Reinforced" @@ -286,6 +308,7 @@ var/const/standard_monitor_styles = "blank=ipc_blank;\ icon = 'icons/mob/human_races/cyberlimbs/morpheus/morpheus_main.dmi' unavailable_to_build = 1 monitor_styles = standard_monitor_styles + modular_bodyparts = MODULAR_BODYPART_CYBERNETIC /datum/robolimb/morpheus_alt1 company = "Morpheus - Zenith" @@ -334,6 +357,7 @@ var/const/standard_monitor_styles = "blank=ipc_blank;\ desc = "This limb features sleek black and white polymers." icon = 'icons/mob/human_races/cyberlimbs/wardtakahashi/wardtakahashi_main.dmi' unavailable_to_build = 1 + modular_bodyparts = MODULAR_BODYPART_CYBERNETIC /datum/robolimb/wardtakahashi_alt1 company = "Ward-Takahashi - Shroud" @@ -361,6 +385,7 @@ var/const/standard_monitor_styles = "blank=ipc_blank;\ desc = "This limb has a minimalist black and red casing." icon = 'icons/mob/human_races/cyberlimbs/xion/xion_main.dmi' unavailable_to_build = 1 + modular_bodyparts = MODULAR_BODYPART_CYBERNETIC /datum/robolimb/xion_alt1 company = "Xion - Breach" @@ -379,12 +404,14 @@ var/const/standard_monitor_styles = "blank=ipc_blank;\ green=xion_green;\ blue=xion_blue;\ rgb=xion_rgb" + modular_bodyparts = MODULAR_BODYPART_CYBERNETIC /datum/robolimb/xion_alt3 company = "Xion - Whiteout" desc = "This limb has a minimalist black and white casing." icon = 'icons/mob/human_races/cyberlimbs/xion/xion_alt3.dmi' unavailable_to_build = 1 + modular_bodyparts = MODULAR_BODYPART_CYBERNETIC /datum/robolimb/xion_alt4 company = "Xion - Breach - Whiteout" @@ -409,6 +436,14 @@ var/const/standard_monitor_styles = "blank=ipc_blank;\ unavailable_to_build = 1 skin_tone = 1 +/datum/robolimb/wooden + company = "Morgan Trading Co" + desc = "A simplistic, metal-banded, wood-panelled prosthetic." + icon = 'icons/mob/human_races/cyberlimbs/prosthesis/wooden.dmi' + modular_bodyparts = MODULAR_BODYPART_PROSTHETIC + parts = list(BP_L_ARM, BP_R_ARM, BP_L_HAND, BP_R_HAND, BP_L_LEG, BP_R_LEG, BP_L_FOOT, BP_R_FOOT) + modular_bodyparts = MODULAR_BODYPART_PROSTHETIC + /obj/item/weapon/disk/limb name = "Limb Blueprints" desc = "A disk containing the blueprints for prosthetics." diff --git a/code/modules/surgery/limb_reattach.dm b/code/modules/surgery/limb_reattach.dm index 9ed0ce520da..ecbf1917418 100644 --- a/code/modules/surgery/limb_reattach.dm +++ b/code/modules/surgery/limb_reattach.dm @@ -59,6 +59,13 @@ "You have attached [target]'s [E.name] to the [E.amputation_point].") user.drop_from_inventory(E) E.replaced(target) + + // Modular bodyparts (like prosthetics) do not need to be reconnected. + if(E.get_modular_limb_category() != MODULAR_BODYPART_INVALID) + E.status &= ~ORGAN_CUT_AWAY + for(var/obj/item/organ/external/child in E.children) + child.status &= ~ORGAN_CUT_AWAY + target.update_icons_body(FALSE) target.updatehealth() target.UpdateDamageIcon() diff --git a/code/modules/vore/appearance/sprite_accessories_vr.dm b/code/modules/vore/appearance/sprite_accessories_vr.dm index 2e030fedf38..48aa8f810ad 100644 --- a/code/modules/vore/appearance/sprite_accessories_vr.dm +++ b/code/modules/vore/appearance/sprite_accessories_vr.dm @@ -2101,6 +2101,24 @@ do_colouration = 1 color_blend_mode = ICON_MULTIPLY +/datum/sprite_accessory/tail/peacocktail_red //this is ckey locked for now, but prettiebyrd wants these tails to be unlocked at a later date + name = "Peacock tail (vwag)" + desc = "" + icon = "icons/mob/vore/tails_vr.dmi" + icon_state = "peacocktail_red" + ani_state = "peacocktail_red_w" + ckeys_allowed = list("prettiebyrd") + +/datum/sprite_accessory/tail/peacocktail //ditto + name = "Peacock tail, colorable (vwag)" + desc = "" + icon = "icons/mob/vore/tails_vr.dmi" + icon_state = "peacocktail" + ani_state = "peacocktail_w" + do_colouration = 1 + color_blend_mode = ICON_MULTIPLY + ckeys_allowed = list("prettiebyrd") + /* //////////////////////////// / =--------------------= / diff --git a/icons/mob/human_races/cyberlimbs/prosthesis/wooden.dmi b/icons/mob/human_races/cyberlimbs/prosthesis/wooden.dmi new file mode 100644 index 00000000000..5dbf1ee1756 Binary files /dev/null and b/icons/mob/human_races/cyberlimbs/prosthesis/wooden.dmi differ diff --git a/icons/mob/vore/tails_vr.dmi b/icons/mob/vore/tails_vr.dmi index 0ef22dd9ef9..44a960e345c 100644 Binary files a/icons/mob/vore/tails_vr.dmi and b/icons/mob/vore/tails_vr.dmi differ diff --git a/vorestation.dme b/vorestation.dme index 6692d493378..6bb46869ec9 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -1322,6 +1322,7 @@ #include "code\game\objects\items\weapons\implants\implantlanguage.dm" #include "code\game\objects\items\weapons\implants\implantpad.dm" #include "code\game\objects\items\weapons\implants\implantreagent_vr.dm" +#include "code\game\objects\items\weapons\implants\implantrestrainingbolt.dm" #include "code\game\objects\items\weapons\implants\implantuplink.dm" #include "code\game\objects\items\weapons\implants\neuralbasic.dm" #include "code\game\objects\items\weapons\material\ashtray.dm" @@ -2587,6 +2588,7 @@ #include "code\modules\mob\living\carbon\human\human_defines_vr.dm" #include "code\modules\mob\living\carbon\human\human_helpers.dm" #include "code\modules\mob\living\carbon\human\human_helpers_vr.dm" +#include "code\modules\mob\living\carbon\human\human_modular_limbs.dm" #include "code\modules\mob\living\carbon\human\human_movement.dm" #include "code\modules\mob\living\carbon\human\human_organs.dm" #include "code\modules\mob\living\carbon\human\human_powers.dm"