diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index 52fbb0c7b85..b099f50c431 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -994,6 +994,10 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_SKIP_BASIC_REACH_CHECK "skip_basic_reach_check" /// Increases chance of this brain getting special traumas, makes them harder to cure #define TRAIT_SPECIAL_TRAUMA_BOOST "special_trauma_boost" +/// If this item is offered when something is pulled. +#define TRAIT_OFFERED_WHEN_PULLED "offered_when_pulled" +/// If the item has a special mob/living/give() interaction. +#define TRAIT_BORG_GIVE "give_borg_item" //---- Heretic Traits /// Hides the heretic overlay that outs them as the heretic diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index 6dde06107ba..55ad2b7728b 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -714,6 +714,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_NODROP" = TRAIT_NODROP, "TRAIT_NO_WORN_ICON" = TRAIT_NO_WORN_ICON, "TRAIT_NULLROD_ITEM" = TRAIT_NULLROD_ITEM, + "TRAIT_OFFERED_WHEN_PULLED" = TRAIT_OFFERED_WHEN_PULLED, "TRAIT_OMNI_BAIT" = TRAIT_OMNI_BAIT, "TRAIT_PLANT_WILDMUTATE" = TRAIT_PLANT_WILDMUTATE, "TRAIT_POISONOUS_BAIT" = TRAIT_POISONOUS_BAIT, @@ -738,6 +739,9 @@ GLOBAL_LIST_INIT(traits_by_type, list( /obj/item/bodypart/head = list( "TRAIT_DISFIGURED" = TRAIT_DISFIGURED, ), + /obj/item/borg = list( + "TRAIT_BORG_GIVE" = TRAIT_BORG_GIVE, + ), /obj/item/card/id = list( "TRAIT_JOB_FIRST_ID_CARD" = TRAIT_JOB_FIRST_ID_CARD, "TRAIT_MAGNETIC_ID_CARD" = TRAIT_MAGNETIC_ID_CARD, diff --git a/code/_onclick/hud/screen_objects/alert.dm b/code/_onclick/hud/screen_objects/alert.dm index af7de7ee085..13272c9c407 100644 --- a/code/_onclick/hud/screen_objects/alert.dm +++ b/code/_onclick/hud/screen_objects/alert.dm @@ -410,6 +410,8 @@ var/screentip_override_text /// Whether the offered item can be examined by shift-clicking the alert var/examinable = TRUE + /// Whether this item should bypass active hand checks. + var/bypass_active_hand = FALSE /atom/movable/screen/alert/give/Initialize(mapload, datum/hud/hud_owner) . = ..() @@ -488,9 +490,13 @@ var/mob/living/taker = owner var/mob/living/offerer = offer.owner var/obj/item/receiving = offer.offered_item - taker.take(offerer, receiving) + taker.take(offerer, receiving, bypass_active_hand) SEND_SIGNAL(offerer, COMSIG_LIVING_ITEM_GIVEN, taker, receiving) +/// Mostly for borgs to offer items. +/atom/movable/screen/alert/give/borg + bypass_active_hand = TRUE + /atom/movable/screen/alert/give/highfive additional_desc_text = "Click this alert to slap it." screentip_override_text = "High Five" diff --git a/code/datums/beam.dm b/code/datums/beam.dm index b3debf2de38..e690536a15c 100644 --- a/code/datums/beam.dm +++ b/code/datums/beam.dm @@ -512,6 +512,68 @@ SHOULD_CALL_PARENT(TRUE) SEND_SIGNAL(owner, COMSIG_BEAM_TURFS_CHANGED, post_change_callbacks) +/// For beams that might be from a held item. +/datum/beam/held + // Is the fishing rod held in left side hand + var/lefthand = FALSE + + // Make these inline with final sprites + var/righthand_s_px = 13 + var/righthand_s_py = 16 + + var/righthand_e_px = 18 + var/righthand_e_py = 16 + + var/righthand_w_px = -20 + var/righthand_w_py = 18 + + var/righthand_n_px = -14 + var/righthand_n_py = 16 + + var/lefthand_s_px = -13 + var/lefthand_s_py = 15 + + var/lefthand_e_px = 24 + var/lefthand_e_py = 18 + + var/lefthand_w_px = -17 + var/lefthand_w_py = 16 + + var/lefthand_n_px = 13 + var/lefthand_n_py = 15 + +/datum/beam/held/Start() + update_offsets(origin.dir) + . = ..() + RegisterSignal(origin, COMSIG_ATOM_DIR_CHANGE, PROC_REF(handle_dir_change)) + +/datum/beam/held/Destroy() + UnregisterSignal(origin, COMSIG_ATOM_DIR_CHANGE) + . = ..() + +/datum/beam/held/proc/handle_dir_change(atom/movable/source, olddir, newdir) + SIGNAL_HANDLER + update_offsets(newdir) + INVOKE_ASYNC(src, TYPE_PROC_REF(/datum/beam, redrawing)) + +/datum/beam/held/proc/update_offsets(user_dir) + switch(user_dir) + if(SOUTH) + override_origin_pixel_x = lefthand ? lefthand_s_px : righthand_s_px + override_origin_pixel_y = lefthand ? lefthand_s_py : righthand_s_py + if(EAST) + override_origin_pixel_x = lefthand ? lefthand_e_px : righthand_e_px + override_origin_pixel_y = lefthand ? lefthand_e_py : righthand_e_py + if(WEST) + override_origin_pixel_x = lefthand ? lefthand_w_px : righthand_w_px + override_origin_pixel_y = lefthand ? lefthand_w_py : righthand_w_py + if(NORTH) + override_origin_pixel_x = lefthand ? lefthand_n_px : righthand_n_px + override_origin_pixel_y = lefthand ? lefthand_n_py : righthand_n_py + + override_origin_pixel_x += origin.pixel_x + override_origin_pixel_y += origin.pixel_y + /** * This is what you use to start a beam. Example: origin.Beam(target, args). **Store the return of this proc if you don't set maxdist or time, you need it to delete the beam.** * diff --git a/code/datums/components/offered_when_pulled.dm b/code/datums/components/offered_when_pulled.dm new file mode 100644 index 00000000000..78ee7e97584 --- /dev/null +++ b/code/datums/components/offered_when_pulled.dm @@ -0,0 +1,24 @@ +/**For items automatically offered to a player when they pull a borg.*/ + +/datum/component/borg_item_offered_when_pulled + var/mob/living/silicon/robot + +/datum/component/borg_item_offered_when_pulled/Initialize(mob/living/silicon/robot/borg) + . = ..() + if(!isitem(parent)) + return COMPONENT_INCOMPATIBLE + if(!istype(borg)) + stack_trace("Component assigned to [parent], but was not assigned a ref for type /mob/living/silicon/robot to attach COMSIG_LIVING_GET_PULLED") + return COMPONENT_INCOMPATIBLE + RegisterSignal(borg, COMSIG_LIVING_GET_PULLED, PROC_REF(on_pulled)) + ADD_TRAIT(parent, TRAIT_OFFERED_WHEN_PULLED, TRAIT_OFFERED_WHEN_PULLED) + robot = borg + +/datum/component/borg_item_offered_when_pulled/Destroy(datum/source, ...) + UnregisterSignal(robot, COMSIG_LIVING_GET_PULLED) + robot = null + return ..() + +/datum/component/borg_item_offered_when_pulled/proc/on_pulled(mob/living/holder, mob/living/puller) + SIGNAL_HANDLER + holder.give(puller, parent) diff --git a/code/datums/keybinding/living.dm b/code/datums/keybinding/living.dm index 33d24177b54..34b74388aff 100644 --- a/code/datums/keybinding/living.dm +++ b/code/datums/keybinding/living.dm @@ -215,6 +215,11 @@ return FALSE if(!user.mob) return FALSE + var/obj/item/object = user.mob.get_active_held_item() + if(isnull(object)) + return FALSE + if(HAS_TRAIT(object, TRAIT_BORG_GIVE)) + return TRUE if(!HAS_TRAIT(user.mob, TRAIT_CAN_HOLD_ITEMS)) return FALSE return TRUE @@ -224,7 +229,10 @@ if(.) return var/mob/living/living_user = user.mob - if(!HAS_TRAIT(living_user, TRAIT_CAN_HOLD_ITEMS)) + var/obj/item/held_item = living_user.get_active_held_item() + if(isnull(held_item)) + return + if(!HAS_TRAIT(living_user, TRAIT_CAN_HOLD_ITEMS) && !HAS_TRAIT(held_item, TRAIT_BORG_GIVE)) return living_user.give() diff --git a/code/datums/status_effects/debuffs/hooked.dm b/code/datums/status_effects/debuffs/hooked.dm index 4747941ca00..d6dfa473c64 100644 --- a/code/datums/status_effects/debuffs/hooked.dm +++ b/code/datums/status_effects/debuffs/hooked.dm @@ -12,7 +12,7 @@ /datum/status_effect/grouped/hooked/proc/still_exists() return !QDELETED(src) -/datum/status_effect/grouped/hooked/source_added(datum/beam/fishing_line/source) +/datum/status_effect/grouped/hooked/source_added(datum/beam/held/source) RegisterSignal(source, COMSIG_QDELETING, PROC_REF(on_fishing_line_deleted)) /datum/status_effect/grouped/hooked/proc/on_fishing_line_deleted(datum/source) @@ -37,7 +37,7 @@ if(!effect.try_unhook()) return owner.balloon_alert(owner, "hook removed") - var/datum/beam/fishing_line/rand_source = pick(effect.sources) + var/datum/beam/held/rand_source = pick(effect.sources) qdel(rand_source) ///Version used by the jawed fishing hook, which also applies slowdown diff --git a/code/datums/status_effects/neutral.dm b/code/datums/status_effects/neutral.dm index 9ccc6e3d333..4b787d51b82 100644 --- a/code/datums/status_effects/neutral.dm +++ b/code/datums/status_effects/neutral.dm @@ -214,6 +214,8 @@ var/list/possible_takers /// The actual item being offered var/obj/item/offered_item + ///If we should bypass active_hand checks. + var/bypass_active_hand = FALSE /// The type of alert given to people when offered, in case you need to override some behavior (like for high-fives) var/give_alert_type = /atom/movable/screen/alert/give diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index c8438dc81dc..296dc4e237b 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -1497,7 +1497,7 @@ GAME_VERB_SRC(/obj/item, verb_pickup, oview(1), "Pick up", null) * * taker - the living mob trying to accept the offer */ /obj/item/proc/on_offer_taken(mob/living/offerer, mob/living/taker) - if(!(HAS_TRAIT(offerer, TRAIT_CAN_HOLD_ITEMS) && HAS_TRAIT(taker, TRAIT_CAN_HOLD_ITEMS))) + if(!HAS_TRAIT(offerer, TRAIT_CAN_HOLD_ITEMS) && !HAS_TRAIT(src, TRAIT_BORG_GIVE) && HAS_TRAIT(taker, TRAIT_CAN_HOLD_ITEMS)) return TRUE // both must be able to hold items for this to make sense if(SEND_SIGNAL(src, COMSIG_ITEM_OFFER_TAKEN, offerer, taker) & COMPONENT_OFFER_INTERRUPT) return TRUE diff --git a/code/game/objects/items/robot/items/janitor.dm b/code/game/objects/items/robot/items/janitor.dm new file mode 100644 index 00000000000..75f47f70f27 --- /dev/null +++ b/code/game/objects/items/robot/items/janitor.dm @@ -0,0 +1,243 @@ +/obj/item/borg/cleaner_box + name = "janitorial vacuum suite" + desc = "A module designed to compensate for your lack of hands by offloading your job onto your more squishy overlords." + icon = 'icons/obj/items_cyborg.dmi' + icon_state = "cleanerbox" + var/obj/item/vacuum_item/hose + var/deployed = FALSE + var/locked = FALSE + var/datum/weakref/module_list + +/obj/item/borg/cleaner_box/Initialize(mapload) + . = ..() + var/mob/living/silicon/robot = loc + if(!istype(robot)) + return INITIALIZE_HINT_QDEL + var/obj/item/robot_model/janitor/model = locate() in robot.get_contents() + module_list = WEAKREF(model) + AddComponent(/datum/component/borg_item_offered_when_pulled, robot) + ADD_TRAIT(src, TRAIT_BORG_GIVE, TRAIT_BORG_GIVE) + hose = new(src) + hose.cleaner_box = WEAKREF(src) + hose.AddComponent( \ + /datum/component/transforming, \ + force_on = hose.force, \ + hitsound_on = hose.hitsound, \ + w_class_on = hose.w_class, \ + clumsy_check = FALSE, \ + attack_verb_continuous_on = list("washed", "mopped", "scrubbed", "whacked", "bapped", "decontaminated"), \ + attack_verb_simple_on = list("wash", "mop", "scrub", "whack", "bap", "decontaminate"), \ + ) + hose.RegisterSignal(hose, COMSIG_TRANSFORMING_ON_TRANSFORM, TYPE_PROC_REF(/obj/item/vacuum_item, on_transform)) + update_icon(UPDATE_OVERLAYS) + +/obj/item/borg/cleaner_box/Destroy(force) + if(hose?.borg_hose) + QDEL_NULL(hose.borg_hose) + if(deployed) + hose.retract_hose() + QDEL_NULL(hose) + return ..() + +/obj/item/borg/cleaner_box/attack_self(mob/user, modifiers) + . = ..() + if(deployed) + hose.retract_hose() + return COMPONENT_CANCEL_ATTACK_CHAIN + +/obj/item/borg/cleaner_box/click_alt(mob/user) + if(deployed) + hose.retract_hose() + locked = !locked + update_icon(UPDATE_OVERLAYS) + return CLICK_ACTION_SUCCESS + +/obj/item/borg/cleaner_box/on_offered(mob/living/offerer, mob/living/carbon/offered) + . = TRUE + if(SEND_SIGNAL(src, COMSIG_ITEM_OFFERING, offerer) & COMPONENT_OFFER_INTERRUPT) + return + if(hose.loc != src && !istype(hose.loc, /mob/living)) // Error handling + stack_trace("[src] has been offered with [hose] not present inside its contents or inside a mob's loc variable. Location: [isnull(loc) ? "NULLSPACE" : "[hose.loc], X: [hose.x], Y: [hose.y], Z:[hose.z]"]") + deployed = FALSE + hose.forceMove(src) + if(locked || deployed) + return + if(!offered) + offered = locate(/mob/living/carbon) in orange(1, offerer) + if(offered && istype(offered)) + offerer.visible_message( + span_notice("[offerer] extends the handle towards [offered] for their cleaning suite."), + span_notice("The handle to your [src] extends towards [offered]'s hand."), null, 2) + + offerer.apply_status_effect(/datum/status_effect/offering, src, /atom/movable/screen/alert/give/borg, offered) + return + +/obj/item/borg/cleaner_box/on_offer_taken(mob/living/offerer, mob/living/taker) + . = ..() + if(.) + return TRUE + hose.bag = istype(loc, /mob/living/silicon) ? pick(loc.get_all_contents_type(/obj/item/storage/bag/trash)) : locate(/obj/item/storage/bag/trash) in module_list.resolve() + if(!hose.bag) + stack_trace("[src] failed to connect to a trash bag on [module_list.resolve()].") + return TRUE + taker.visible_message( + span_notice("[taker] takes the [hose] from [offerer]."), + span_notice("You take the [hose] from [offerer]")) + hose.do_pickup_animation(taker, offerer) + taker.put_in_hands(hose) + hose.borg_hose = hose.generate_hose(offerer, taker) + hose.RegisterSignal(hose, COMSIG_ITEM_DROPPED, TYPE_PROC_REF(/obj/item/vacuum_item, on_drop)) + playsound(hose, 'sound/items/vacuum/vacuum_hose.ogg', 100, TRUE) + deployed = TRUE + update_icon(UPDATE_OVERLAYS) + offerer.remove_status_effect(/datum/status_effect/offering) + return TRUE + +/obj/item/borg/cleaner_box/update_overlays() + . = ..() + if(deployed) + . += "cleanerbox_on" + else + . += "cleanerbox_wand" + if(locked) + . += "cleanerbox_locked" + +/obj/item/borg/cleaner_box/examine(mob/user) + . = ..() + . += span_notice("Alt-Click to [locked ? "unlock" : "lock"] the [src].") + +/obj/item/vacuum_item + name = "janitorial floor cleaner" + desc = "This is the working end of an industrial cleaner that someone unfortinately gave sapience." + icon = 'icons/obj/items_cyborg.dmi' + icon_state = "vacuum-wand" + inhand_icon_state = "vacuum-wand" + righthand_file = 'icons/mob/inhands/items/vacuum_wand_righthand.dmi' + lefthand_file = 'icons/mob/inhands/items/vacuum_wand_lefthand.dmi' + w_class = WEIGHT_CLASS_BULKY + obj_flags = INDESTRUCTIBLE // To prevent fuckery and a broken borg module. + attack_verb_continuous = list("sucked", "vacuumed", "smacked", "forcefully dusted off", "beaten") + attack_verb_simple = list("suck", "vacuum", "smack", "dust off", "beat") + force = 12 + + var/datum/beam/held/vacuum/borg_hose + var/datum/weakref/cleaner_box + var/obj/item/storage/bag/trash/bag + var/cleaning = FALSE + +/obj/item/vacuum_item/Destroy(force) + bag = null + return ..() + +/obj/item/vacuum_item/interact_with_atom(obj/item/thing, mob/living/user, list/modifiers) + . = ..() + if(!istype(thing)) + return NONE + if(!bag && cleaning) + return NONE + if(thing.anchored || thing.w_class >= WEIGHT_CLASS_BULKY) + return NONE + playsound(bag, 'sound/items/vacuum/vacuum_use.ogg', 20, TRUE) + for(var/obj/item/I in get_turf(thing)) + if(!istype(I, thing.type)) + continue + if(!do_after(user, 0.1 SECONDS, user, progress = FALSE)) + break + if(bag.atom_storage.attempt_insert(I, user, FALSE)) + continue + break + +/obj/item/vacuum_item/proc/on_transform(obj/item/source, mob/living/user, active) + SIGNAL_HANDLER + + cleaning = !cleaning + if(!user) + return COMPONENT_NO_DEFAULT_MESSAGE + playsound(src, 'sound/items/vacuum/vacuum_clack.ogg', 30, TRUE) + if(cleaning) //CLEAN_SCRUB because if you get a borg to help you clean up a crime, you deserve to win. + balloon_alert(user, "cleaning") + AddComponent( \ + /datum/component/cleaner, \ + base_cleaning_duration = 1 SECONDS, \ + pre_clean_callback = CALLBACK(src, PROC_REF(clean_sound)), \ + ) + else + balloon_alert(user, "vacuuming") + qdel(GetComponent(/datum/component/cleaner)) + return COMPONENT_NO_DEFAULT_MESSAGE + +/obj/item/vacuum_item/proc/clean_sound() + playsound(src, 'sound/items/vacuum/vacuum_steam.ogg', 10, TRUE) + return CLEAN_ALLOWED + +/obj/item/vacuum_item/proc/retract_hose() + var/obj/item/borg/cleaner_box/cleaner_resolved = cleaner_box?.resolve() + if(!cleaner_resolved) + CRASH("Somehow [src] doesn't have a source to return to!") + if(loc == cleaner_resolved) + return + do_pickup_animation(cleaner_resolved, get_turf(src)) + forceMove(cleaner_resolved) + playsound(cleaner_resolved, 'sound/items/vacuum/vacuum_ploop.ogg', 100) + if(!isnull(borg_hose) && !QDELING(borg_hose)) + balloon_alert_to_hearers("snap") + QDEL_NULL(borg_hose) + bag = null + cleaner_resolved.deployed = FALSE + UnregisterSignal(src, COMSIG_ITEM_DROPPED) + cleaner_resolved.update_icon(UPDATE_OVERLAYS) + +/obj/item/vacuum_item/proc/generate_hose(mob/living/offerer, mob/living/taker) + var/datum/beam/held/vacuum/generated_borg_hose = new(taker, offerer, icon_state = "hosebeam", max_distance = 7, emissive = FALSE, beam_layer = BELOW_MOB_LAYER) + var/index = taker.get_held_index_of_item(src) + generated_borg_hose.lefthand = IS_LEFT_INDEX(index) + INVOKE_ASYNC(generated_borg_hose, TYPE_PROC_REF(/datum/beam, Start)) + RegisterSignal(generated_borg_hose, COMSIG_QDELETING, PROC_REF(retract_hose)) + RegisterSignal(generated_borg_hose, COMSIG_BEAM_BEFORE_DRAW, PROC_REF(on_update)) + return generated_borg_hose + +/obj/item/vacuum_item/examine(mob/user) + . = ..() + . += span_notice("Interact to switch to [cleaning ? "vacuum" : "cleaning"] mode.") +/obj/item/vacuum_item/proc/on_update() + SIGNAL_HANDLER + var/mob/living/mob = borg_hose.origin + if(istype(mob)) + var/index = mob.is_holding(src) + borg_hose.lefthand = IS_LEFT_INDEX(index) + if(prob(10)) + playsound(src, 'sound/items/vacuum/vacuum_hose.ogg', 50, TRUE) + +/obj/item/vacuum_item/proc/on_drop(obj/item/vacuum, mob/living/user) + SIGNAL_HANDLER + if(user == loc) + return + retract_hose() + +/datum/beam/held/vacuum + righthand_s_px = -7 + righthand_s_py = -3 + + righthand_e_px = 0 + righthand_e_py = -6 + + righthand_w_px = -3 + righthand_w_py = -6 + + righthand_n_px = 8 + righthand_n_py = -6 + + lefthand_s_px = 7 + lefthand_s_py = -3 + + lefthand_e_px = 3 + lefthand_e_py = -6 + + lefthand_w_px = 0 + lefthand_w_py = -6 + + lefthand_n_px = -8 + lefthand_n_py = -6 + + + diff --git a/code/modules/fishing/fishing_rod.dm b/code/modules/fishing/fishing_rod.dm index 5d7df05303d..34c0db258ea 100644 --- a/code/modules/fishing/fishing_rod.dm +++ b/code/modules/fishing/fishing_rod.dm @@ -32,7 +32,7 @@ var/atom/movable/currently_hooked /// Fishing line visual for the hooked item - var/datum/beam/fishing_line/fishing_line + var/datum/beam/held/fishing_line /// Are we currently casting var/casting = FALSE @@ -329,7 +329,7 @@ fishing_line.lefthand = IS_LEFT_INDEX(firer.get_held_index_of_item(src)) RegisterSignal(fishing_line, COMSIG_BEAM_BEFORE_DRAW, PROC_REF(check_los)) RegisterSignal(fishing_line, COMSIG_QDELETING, PROC_REF(clear_line)) - INVOKE_ASYNC(fishing_line, TYPE_PROC_REF(/datum/beam/, Start)) + INVOKE_ASYNC(fishing_line, TYPE_PROC_REF(/datum/beam, Start)) if(QDELETED(fishing_line)) return null firer.update_held_items() @@ -919,64 +919,3 @@ QDEL_NULL(owner.fishing_line) owner = null return ..() - -/datum/beam/fishing_line - // Is the fishing rod held in left side hand - var/lefthand = FALSE - - // Make these inline with final sprites - var/righthand_s_px = 13 - var/righthand_s_py = 16 - - var/righthand_e_px = 18 - var/righthand_e_py = 16 - - var/righthand_w_px = -20 - var/righthand_w_py = 18 - - var/righthand_n_px = -14 - var/righthand_n_py = 16 - - var/lefthand_s_px = -13 - var/lefthand_s_py = 15 - - var/lefthand_e_px = 24 - var/lefthand_e_py = 18 - - var/lefthand_w_px = -17 - var/lefthand_w_py = 16 - - var/lefthand_n_px = 13 - var/lefthand_n_py = 15 - -/datum/beam/fishing_line/Start() - update_offsets(origin.dir) - . = ..() - RegisterSignal(origin, COMSIG_ATOM_DIR_CHANGE, PROC_REF(handle_dir_change)) - -/datum/beam/fishing_line/Destroy() - UnregisterSignal(origin, COMSIG_ATOM_DIR_CHANGE) - . = ..() - -/datum/beam/fishing_line/proc/handle_dir_change(atom/movable/source, olddir, newdir) - SIGNAL_HANDLER - update_offsets(newdir) - INVOKE_ASYNC(src, TYPE_PROC_REF(/datum/beam, redrawing)) - -/datum/beam/fishing_line/proc/update_offsets(user_dir) - switch(user_dir) - if(SOUTH) - override_origin_pixel_x = lefthand ? lefthand_s_px : righthand_s_px - override_origin_pixel_y = lefthand ? lefthand_s_py : righthand_s_py - if(EAST) - override_origin_pixel_x = lefthand ? lefthand_e_px : righthand_e_px - override_origin_pixel_y = lefthand ? lefthand_e_py : righthand_e_py - if(WEST) - override_origin_pixel_x = lefthand ? lefthand_w_px : righthand_w_px - override_origin_pixel_y = lefthand ? lefthand_w_py : righthand_w_py - if(NORTH) - override_origin_pixel_x = lefthand ? lefthand_n_px : righthand_n_px - override_origin_pixel_y = lefthand ? lefthand_n_py : righthand_n_py - - override_origin_pixel_x += origin.pixel_x - override_origin_pixel_y += origin.pixel_y diff --git a/code/modules/mob/living/living_item_handling.dm b/code/modules/mob/living/living_item_handling.dm index 24b3e2692d1..7036f6bd65c 100644 --- a/code/modules/mob/living/living_item_handling.dm +++ b/code/modules/mob/living/living_item_handling.dm @@ -127,7 +127,7 @@ * Arguments: * * offered - The player being offered the item (optional, if null the offer is to everyone around) */ -/mob/living/proc/give(mob/living/offered) +/mob/living/proc/give(mob/living/offered, obj/item/item_bypass) if(has_status_effect(/datum/status_effect/offering)) to_chat(src, span_warning("You're already offering something!")) return @@ -136,9 +136,9 @@ to_chat(src, span_warning("You're unable to offer anything in your current state!")) return - var/obj/item/offered_item = get_active_held_item() + var/obj/item/offered_item = item_bypass ? item_bypass : get_active_held_item() // if it's an abstract item, should consider it to be non-existent (unless it's a HAND_ITEM, which means it's an obj/item that is just a representation of our hand) - if(!offered_item || ((offered_item.item_flags & ABSTRACT) && !(offered_item.item_flags & HAND_ITEM))) + if(!offered_item || ((offered_item.item_flags & ABSTRACT && !HAS_TRAIT(offered_item, TRAIT_BORG_GIVE)) && !HAS_TRAIT(offered_item, TRAIT_OFFERED_WHEN_PULLED) && !(offered_item.item_flags & HAND_ITEM))) to_chat(src, span_warning("You're not holding anything to offer!")) return @@ -187,7 +187,7 @@ * * offerer - The living mob giving the original item * * offered_item - The item being given by the offerer */ -/mob/living/proc/take(mob/living/offerer, obj/item/offered_item) +/mob/living/proc/take(mob/living/offerer, obj/item/offered_item, bypass) clear_alert("[offerer]") if(IS_DEAD_OR_INCAP(src)) to_chat(src, span_warning("You're unable to take anything in your current state!")) @@ -195,7 +195,7 @@ if(get_dist(src, offerer) > 1) to_chat(src, span_warning("[offerer] is out of range!")) return - if(!offered_item || offerer.get_active_held_item() != offered_item) + if(!offered_item || offerer.get_active_held_item() != offered_item && !bypass) to_chat(src, span_warning("[offerer] is no longer holding the item they were offering!")) return if(!get_empty_held_indexes()) diff --git a/code/modules/mob/living/silicon/robot/robot_model.dm b/code/modules/mob/living/silicon/robot/robot_model.dm index d60e9d91600..4b2a7174fb4 100644 --- a/code/modules/mob/living/silicon/robot/robot_model.dm +++ b/code/modules/mob/living/silicon/robot/robot_model.dm @@ -447,6 +447,7 @@ name = "Janitor" basic_modules = list( /obj/item/assembly/flash/cyborg, + /obj/item/borg/cleaner_box, /obj/item/screwdriver/cyborg, /obj/item/crowbar/cyborg, /obj/item/stack/tile/iron/base/cyborg, // haha jani will have old tiles >:D diff --git a/icons/effects/beam.dmi b/icons/effects/beam.dmi index c9723f51e2b..12ccaf2bf20 100644 Binary files a/icons/effects/beam.dmi and b/icons/effects/beam.dmi differ diff --git a/icons/mob/inhands/items/vacuum_wand_lefthand.dmi b/icons/mob/inhands/items/vacuum_wand_lefthand.dmi new file mode 100644 index 00000000000..8bd509597bc Binary files /dev/null and b/icons/mob/inhands/items/vacuum_wand_lefthand.dmi differ diff --git a/icons/mob/inhands/items/vacuum_wand_righthand.dmi b/icons/mob/inhands/items/vacuum_wand_righthand.dmi new file mode 100644 index 00000000000..5bfc27763cd Binary files /dev/null and b/icons/mob/inhands/items/vacuum_wand_righthand.dmi differ diff --git a/icons/obj/items_cyborg.dmi b/icons/obj/items_cyborg.dmi index 17a65770979..4f483a4fbd8 100644 Binary files a/icons/obj/items_cyborg.dmi and b/icons/obj/items_cyborg.dmi differ diff --git a/sound/items/vacuum/credits.txt b/sound/items/vacuum/credits.txt new file mode 100644 index 00000000000..d068f55cdab --- /dev/null +++ b/sound/items/vacuum/credits.txt @@ -0,0 +1,5 @@ +Sounds in this folder are CC-BY-NC + +These sounds are sourced from soundsnap under their free tier. + +https://soundsnap.zendesk.com/hc/en-us/articles/115003916431-Legal diff --git a/sound/items/vacuum/vacuum_clack.ogg b/sound/items/vacuum/vacuum_clack.ogg new file mode 100644 index 00000000000..de53eaebc91 Binary files /dev/null and b/sound/items/vacuum/vacuum_clack.ogg differ diff --git a/sound/items/vacuum/vacuum_hose.ogg b/sound/items/vacuum/vacuum_hose.ogg new file mode 100644 index 00000000000..fdb8b741500 Binary files /dev/null and b/sound/items/vacuum/vacuum_hose.ogg differ diff --git a/sound/items/vacuum/vacuum_ploop.ogg b/sound/items/vacuum/vacuum_ploop.ogg new file mode 100644 index 00000000000..39aed4c121f Binary files /dev/null and b/sound/items/vacuum/vacuum_ploop.ogg differ diff --git a/sound/items/vacuum/vacuum_steam.ogg b/sound/items/vacuum/vacuum_steam.ogg new file mode 100644 index 00000000000..b56f42d95b1 Binary files /dev/null and b/sound/items/vacuum/vacuum_steam.ogg differ diff --git a/sound/items/vacuum/vacuum_use.ogg b/sound/items/vacuum/vacuum_use.ogg new file mode 100644 index 00000000000..17b0cb06724 Binary files /dev/null and b/sound/items/vacuum/vacuum_use.ogg differ diff --git a/tgstation.dme b/tgstation.dme index 69551eb4a23..45c88b7d8b1 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1287,6 +1287,7 @@ #include "code\datums\components\mutant_hands.dm" #include "code\datums\components\nuclear_bomb_operator.dm" #include "code\datums\components\object_possession.dm" +#include "code\datums\components\offered_when_pulled.dm" #include "code\datums\components\omen.dm" #include "code\datums\components\onwear_mood.dm" #include "code\datums\components\orbiter.dm" @@ -2843,6 +2844,7 @@ #include "code\game\objects\items\robot\items\generic.dm" #include "code\game\objects\items\robot\items\hud.dm" #include "code\game\objects\items\robot\items\hypo.dm" +#include "code\game\objects\items\robot\items\janitor.dm" #include "code\game\objects\items\robot\items\storage.dm" #include "code\game\objects\items\robot\items\tools.dm" #include "code\game\objects\items\stacks\ammonia_crystals.dm"