diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm index 385cbcb8d446..eb1b0540fb54 100644 --- a/code/controllers/configuration/entries/general.dm +++ b/code/controllers/configuration/entries/general.dm @@ -491,8 +491,6 @@ This maintains a list of ip addresses that are able to bypass topic filtering. /datum/config_entry/flag/respawn -/datum/config_entry/flag/ToRban - /datum/config_entry/flag/ooc_country_flags /datum/config_entry/flag/record_rounds diff --git a/code/datums/tutorial/_tutorial.dm b/code/datums/tutorial/_tutorial.dm index 5423453bbdb9..7dd7ac85c04d 100644 --- a/code/datums/tutorial/_tutorial.dm +++ b/code/datums/tutorial/_tutorial.dm @@ -52,6 +52,7 @@ GLOBAL_LIST_EMPTY_TYPED(ongoing_tutorials, /datum/tutorial) reservation = SSmapping.RequestBlockReservation(initial(tutorial_template.width), initial(tutorial_template.height)) if(!reservation) + abort_tutorial() return FALSE var/turf/bottom_left_corner_reservation = locate(reservation.bottom_left_coords[1], reservation.bottom_left_coords[2], reservation.bottom_left_coords[3]) diff --git a/code/datums/tutorial/_tutorial_menu.dm b/code/datums/tutorial/_tutorial_menu.dm index 42eb3f6aabfa..951b9654ef0e 100644 --- a/code/datums/tutorial/_tutorial_menu.dm +++ b/code/datums/tutorial/_tutorial_menu.dm @@ -79,5 +79,6 @@ return path = new path - path.start_tutorial(usr) - return TRUE + if(path.start_tutorial(usr)) + ui.close() + return TRUE diff --git a/code/datums/tutorial/xenomorph/_xenomorph.dm b/code/datums/tutorial/xenomorph/_xenomorph.dm index bd85cdb35f44..caa33d8eed43 100644 --- a/code/datums/tutorial/xenomorph/_xenomorph.dm +++ b/code/datums/tutorial/xenomorph/_xenomorph.dm @@ -22,6 +22,10 @@ // We don't want people talking to other xenomorphs across tutorials new_character.can_hivemind_speak = FALSE + // No age prefix or HUD element + new_character.age = XENO_NO_AGE + new_character.show_age_prefix = FALSE + new_character.generate_name() tutorial_mob = new_character xeno = new_character diff --git a/code/datums/tutorial/xenomorph/xenomorph_basic.dm b/code/datums/tutorial/xenomorph/xenomorph_basic.dm index 0415977835aa..e91c85e1e1e4 100644 --- a/code/datums/tutorial/xenomorph/xenomorph_basic.dm +++ b/code/datums/tutorial/xenomorph/xenomorph_basic.dm @@ -4,6 +4,7 @@ name = "Xenomorph - Basic" desc = "A tutorial to get you acquainted with the very basics of how to play a xenomorph." icon_state = "xeno" + tutorial_id = "xeno_basic_1" tutorial_template = /datum/map_template/tutorial/s12x12 starting_xenomorph_type = /mob/living/carbon/xenomorph/drone @@ -105,7 +106,7 @@ UnregisterSignal(human_dummy, COMSIG_MOB_DEATH) message_to_player("Well done. Killing humans is one of many ways to help the hive.") - message_to_player("Another way is to capture them. This will grow a new xenomorph inside them which will eventually burst into a new playable xenomorph!") + message_to_player("Another way is to capture them. This will grow a new xenomorph inside them which will eventually burst into a new playable xenomorph!") addtimer(CALLBACK(human_dummy, TYPE_PROC_REF(/mob/living, rejuvenate)), 8 SECONDS) addtimer(CALLBACK(src, PROC_REF(proceed_to_tackle_phase)), 10 SECONDS) @@ -227,3 +228,5 @@ /datum/tutorial/xenomorph/basic/init_map() loc_from_corner(9,0).ChangeTurf(/turf/closed/wall/resin/tutorial) + +#undef WAITING_HEALTH_THRESHOLD diff --git a/code/game/objects/items/toys/toys.dm b/code/game/objects/items/toys/toys.dm index 6fa420df35d5..91d8164dcf38 100644 --- a/code/game/objects/items/toys/toys.dm +++ b/code/game/objects/items/toys/toys.dm @@ -396,6 +396,14 @@ src.add_fingerprint(user) addtimer(VARSET_CALLBACK(src, spam_flag, FALSE), 2 SECONDS) +// rubber duck +/obj/item/toy/bikehorn/rubberducky + name = "rubber ducky" + desc = "Rubber ducky you're so fine, you make bathtime lots of fuuun. Rubber ducky I'm awfully fooooond of yooooouuuu~" //thanks doohl + icon = 'icons/obj/structures/props/watercloset.dmi' + icon_state = "rubberducky" + item_state = "rubberducky" + /obj/item/computer3_part name = "computer part" desc = "Holy jesus you donnit now" diff --git a/code/game/objects/structures/shower.dm b/code/game/objects/structures/shower.dm new file mode 100644 index 000000000000..b731a2c0e242 --- /dev/null +++ b/code/game/objects/structures/shower.dm @@ -0,0 +1,222 @@ +/obj/structure/machinery/shower + name = "shower" + desc = "The HS-451. Installed in the 2050s by the Weyland Hygiene Division." + icon = 'icons/obj/structures/props/watercloset.dmi' + icon_state = "shower" + density = FALSE + anchored = TRUE + use_power = USE_POWER_NONE + var/on = 0 + var/obj/effect/mist/mymist = null + /// needs a var so we can make it linger~ + var/ismist = 0 + /// freezing, normal, or boiling + var/watertemp = "normal" + /// true if there is a mob on the shower's loc, this is to ease process() + var/mobpresent = 0 + var/is_washing = 0 + +/obj/structure/machinery/shower/Initialize() + . = ..() + create_reagents(2) + +//add heat controls? when emagged, you can freeze to death in it? + +/obj/effect/mist + name = "mist" + icon = 'icons/obj/structures/props/watercloset.dmi' + icon_state = "mist" + layer = FLY_LAYER + anchored = TRUE + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + + +/obj/structure/machinery/shower/attack_hand(mob/M as mob) + on = !on + update_icon() + if(on) + start_processing() + if (M.loc == loc) + wash(M) + check_heat(M) + for (var/atom/movable/G in src.loc) + G.clean_blood() + else + stop_processing() + + +/obj/structure/machinery/shower/attackby(obj/item/I as obj, mob/user as mob) + if(I.type == /obj/item/device/analyzer) + to_chat(user, SPAN_NOTICE("The water temperature seems to be [watertemp].")) + if(HAS_TRAIT(I, TRAIT_TOOL_WRENCH)) + to_chat(user, SPAN_NOTICE("You begin to adjust the temperature valve with \the [I].")) + if(do_after(user, 50, INTERRUPT_ALL|BEHAVIOR_IMMOBILE, BUSY_ICON_BUILD)) + switch(watertemp) + if("normal") + watertemp = "freezing" + if("freezing") + watertemp = "boiling" + if("boiling") + watertemp = "normal" + user.visible_message(SPAN_NOTICE("[user] adjusts the shower with \the [I]."), SPAN_NOTICE("You adjust the shower with \the [I].")) + add_fingerprint(user) + + +/obj/structure/machinery/shower/update_icon() //this is terribly unreadable, but basically it makes the shower mist up + overlays.Cut() //once it's been on for a while, in addition to handling the water overlay. + QDEL_NULL(mymist) + + if(on) + overlays += image('icons/obj/structures/props/watercloset.dmi', src, "water", MOB_LAYER + 1, dir) + if(watertemp == "freezing") + return + if(!ismist) + spawn(50) + if(src && on) + ismist = 1 + mymist = new /obj/effect/mist(loc) + else + ismist = 1 + mymist = new /obj/effect/mist(loc) + else if(ismist) + ismist = 1 + mymist = new /obj/effect/mist(loc) + spawn(250) + if(src && !on) + QDEL_NULL(mymist) + ismist = 0 + + +/obj/structure/machinery/shower/Crossed(atom/movable/O) + ..() + wash(O) + if(ismob(O)) + mobpresent++ + check_heat(O) + + +/obj/structure/machinery/shower/Uncrossed(atom/movable/O) + if(ismob(O)) + mobpresent-- + ..() + +//Yes, showers are super powerful as far as washing goes. +/obj/structure/machinery/shower/proc/wash(atom/movable/O as obj|mob) + if(!on) return + + + if(isliving(O)) + var/mob/living/L = O + L.ExtinguishMob() + L.fire_stacks = -20 //Douse ourselves with water to avoid fire more easily + to_chat(L, SPAN_WARNING("You've been drenched in water!")) + if(iscarbon(O)) + var/mob/living/carbon/M = O + if(M.r_hand) + M.r_hand.clean_blood() + if(M.l_hand) + M.l_hand.clean_blood() + if(M.back) + if(M.back.clean_blood()) + M.update_inv_back(0) + if(ishuman(M)) + var/mob/living/carbon/human/H = M + var/washgloves = 1 + var/washshoes = 1 + var/washmask = 1 + var/washears = 1 + var/washglasses = 1 + + if(H.wear_suit) + washgloves = !(H.wear_suit.flags_inv_hide & HIDEGLOVES) + washshoes = !(H.wear_suit.flags_inv_hide & HIDESHOES) + + if(H.head) + washmask = !(H.head.flags_inv_hide & HIDEMASK) + washglasses = !(H.head.flags_inv_hide & HIDEEYES) + washears = !(H.head.flags_inv_hide & HIDEEARS) + + if(H.wear_mask) + if (washears) + washears = !(H.wear_mask.flags_inv_hide & HIDEEARS) + if (washglasses) + washglasses = !(H.wear_mask.flags_inv_hide & HIDEEYES) + + if(H.head) + if(H.head.clean_blood()) + H.update_inv_head() + if(H.wear_suit) + if(H.wear_suit.clean_blood()) + H.update_inv_wear_suit() + else if(H.w_uniform) + if(H.w_uniform.clean_blood()) + H.update_inv_w_uniform() + if(H.gloves && washgloves) + if(H.gloves.clean_blood()) + H.update_inv_gloves() + if(H.shoes && washshoes) + if(H.shoes.clean_blood()) + H.update_inv_shoes() + if(H.wear_mask && washmask) + if(H.wear_mask.clean_blood()) + H.update_inv_wear_mask() + if(H.glasses && washglasses) + if(H.glasses.clean_blood()) + H.update_inv_glasses() + if((H.wear_l_ear || H.wear_r_ear) && washears) + if((H.wear_l_ear && H.wear_l_ear.clean_blood()) ||(H.wear_r_ear && H.wear_r_ear.clean_blood())) + H.update_inv_ears() + if(H.belt) + if(H.belt.clean_blood()) + H.update_inv_belt() + H.clean_blood(washshoes) + else + if(M.wear_mask) //if the mob is not human, it cleans the mask without asking for bitflags + if(M.wear_mask.clean_blood()) + M.update_inv_wear_mask() + M.clean_blood() + else + O.clean_blood() + + if(isturf(loc)) + var/turf/tile = loc + tile.clean_blood() + for(var/obj/effect/E in tile) + if(istype(E,/obj/effect/decal/cleanable) || istype(E,/obj/effect/overlay)) + qdel(E) + + +/obj/structure/machinery/shower/process() + if(!on) return + wash_floor() + if(!mobpresent) return + for(var/mob/living/carbon/C in loc) + check_heat(C) + + +/obj/structure/machinery/shower/proc/wash_floor() + if(!ismist && is_washing) + return + is_washing = 1 + var/turf/T = get_turf(src) +// reagents.add_reagent("water", 2) + T.clean(src) + addtimer(VARSET_CALLBACK(src, is_washing, FALSE), 10 SECONDS) + + +/obj/structure/machinery/shower/proc/check_heat(mob/M as mob) + if(!on || watertemp == "normal") return + if(iscarbon(M)) + var/mob/living/carbon/C = M + + if(watertemp == "freezing") + C.bodytemperature = max(80, C.bodytemperature - 80) + C.recalculate_move_delay = TRUE + to_chat(C, SPAN_WARNING("The water is freezing!")) + return + if(watertemp == "boiling") + C.bodytemperature = min(500, C.bodytemperature + 35) + C.recalculate_move_delay = TRUE + C.apply_damage(5, BURN) + to_chat(C, SPAN_DANGER("The water is searing!")) + return diff --git a/code/game/objects/structures/sink.dm b/code/game/objects/structures/sink.dm new file mode 100644 index 000000000000..6bac40ea7da4 --- /dev/null +++ b/code/game/objects/structures/sink.dm @@ -0,0 +1,122 @@ +/obj/structure/sink + name = "sink" + icon = 'icons/obj/structures/props/watercloset.dmi' + icon_state = "sink_emptied_animation" + desc = "A sink used for washing one's hands and face." + anchored = TRUE + /// if something's being washed at the moment + var/busy = FALSE + + +/obj/structure/sink/Initialize() + . = ..() + if(prob(50)) + icon_state = "sink_emptied" + + +/obj/structure/sink/proc/stop_flow() //sets sink animation to normal sink (without running water) + + if(prob(50)) + icon_state = "sink_emptied_animation" + else + icon_state = "sink_emptied" + flick("sink_animation_empty", src) + + +/obj/structure/sink/attack_hand(mob/user) + if(isRemoteControlling(user)) + return + + if(!Adjacent(user)) + return + + if(busy) + to_chat(user, SPAN_DANGER("Someone's already washing here.")) + return + + to_chat(usr, SPAN_NOTICE(" You start washing your hands.")) + flick("sink_animation_fill", src) //<- play the filling animation then automatically switch back to the loop + icon_state = "sink_animation_fill_loop" //<- set it to the loop + addtimer(CALLBACK(src, PROC_REF(stop_flow)), 6 SECONDS) + playsound(loc, 'sound/effects/sinkrunning.ogg', 25, TRUE) + + busy = TRUE + sleep(40) + busy = FALSE + + if(!Adjacent(user)) return //Person has moved away from the sink + + user.clean_blood() + if(ishuman(user)) + user:update_inv_gloves() + for(var/mob/V in viewers(src, null)) + V.show_message(SPAN_NOTICE("[user] washes their hands using \the [src]."), SHOW_MESSAGE_VISIBLE) + + +/obj/structure/sink/attackby(obj/item/O as obj, mob/user as mob) + if(busy) + to_chat(user, SPAN_DANGER("Someone's already washing here.")) + return + + var/obj/item/reagent_container/RG = O + if (istype(RG) && RG.is_open_container()) + RG.reagents.add_reagent("water", min(RG.volume - RG.reagents.total_volume, RG.amount_per_transfer_from_this)) + user.visible_message(SPAN_NOTICE("[user] fills \the [RG] using \the [src]."),SPAN_NOTICE("You fill \the [RG] using \the [src].")) + return + + else if (istype(O, /obj/item/weapon/baton)) + var/obj/item/weapon/baton/B = O + if(B.bcell) + if(B.bcell.charge > 0 && B.status == 1) + flick("baton_active", src) + user.apply_effect(10, STUN) + user.stuttering = 10 + user.apply_effect(10, WEAKEN) + B.deductcharge(B.hitcost) + user.visible_message( \ + SPAN_DANGER("[user] was stunned by \his wet [O]!"), \ + SPAN_DANGER("You were stunned by your wet [O]!")) + return + + var/turf/location = user.loc + if(!isturf(location)) return + + var/obj/item/I = O + if(!I || !istype(I,/obj/item)) return + + to_chat(usr, SPAN_NOTICE(" You start washing \the [I].")) + + busy = TRUE + sleep(40) + busy = FALSE + + if(user.loc != location) return //User has moved + if(!I) return //Item's been destroyed while washing + if(user.get_active_hand() != I) return //Person has switched hands or the item in their hands + + O.clean_blood() + user.visible_message( \ + SPAN_NOTICE("[user] washes \a [I] using \the [src]."), \ + SPAN_NOTICE("You wash \a [I] using \the [src].")) + + +/obj/structure/sink/kitchen + name = "kitchen sink" + icon_state = "sink_alt" + + +/obj/structure/sink/puddle //splishy splashy ^_^ + name = "puddle" + icon_state = "puddle" + + +/obj/structure/sink/puddle/attack_hand(mob/M as mob) + icon_state = "puddle-splash" + ..() + icon_state = "puddle" + + +/obj/structure/sink/puddle/attackby(obj/item/O as obj, mob/user as mob) + icon_state = "puddle-splash" + ..() + icon_state = "puddle" diff --git a/code/game/objects/structures/urinal.dm b/code/game/objects/structures/urinal.dm new file mode 100644 index 000000000000..c6d14f46540a --- /dev/null +++ b/code/game/objects/structures/urinal.dm @@ -0,0 +1,22 @@ +/obj/structure/urinal + name = "urinal" + desc = "The HU-452, an experimental urinal." + icon = 'icons/obj/structures/props/watercloset.dmi' + icon_state = "urinal" + density = FALSE + anchored = TRUE + +/obj/structure/urinal/attackby(obj/item/I, mob/living/user) + if(istype(I, /obj/item/grab)) + if(isxeno(user)) return + var/obj/item/grab/G = I + if(isliving(G.grabbed_thing)) + var/mob/living/GM = G.grabbed_thing + if(user.grab_level > GRAB_PASSIVE) + if(!GM.loc == get_turf(src)) + to_chat(user, SPAN_NOTICE("[GM.name] needs to be on the urinal.")) + return + user.visible_message(SPAN_DANGER("[user] slams [GM.name] into [src]!"), SPAN_NOTICE("You slam [GM.name] into [src]!")) + GM.apply_damage(8, BRUTE) + else + to_chat(user, SPAN_NOTICE("You need a tighter grip.")) diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm index ea93d868c5a1..1014e8ab7a96 100644 --- a/code/game/objects/structures/watercloset.dm +++ b/code/game/objects/structures/watercloset.dm @@ -1,5 +1,3 @@ -//todo: toothbrushes, and some sort of "toilet-filthinator" for the hos - /obj/structure/toilet name = "toilet" desc = "The HT-451, a torque rotation-based, waste disposal unit for small matter. This one seems remarkably clean." @@ -9,16 +7,19 @@ anchored = TRUE can_buckle = TRUE buckle_lying = 0 - var/open = 0 //if the lid is up - var/cistern = 0 //if the cistern bit is open - var/w_items = 0 //the combined w_class of all the items in the cistern - var/mob/living/swirlie = null //the mob being given a swirlie + /// if the lid is up + var/open = 0 + /// if the cistern bit is open + var/cistern = 0 + /// the combined w_class of all the items in the cistern + var/w_items = 0 + /// the mob being given a swirlie + var/mob/living/swirlie = null var/list/buckling_y = list("north" = 1, "south" = 4, "east" = 0, "west" = 0) var/list/buckling_x = list("north" = 0, "south" = 0, "east" = -5, "west" = 4) var/atom/movable/overlay/cistern_overlay - /obj/structure/toilet/Initialize() . = ..() open = round(rand(0, 1)) @@ -29,6 +30,7 @@ vis_contents += cistern_overlay update_icon() + /obj/structure/toilet/attack_hand(mob/living/user as mob) if(buckled_mob) manual_unbuckle(user) @@ -69,7 +71,6 @@ flick("cistern[cistern]_flush", cistern_overlay) - /obj/structure/toilet/send_buckling_message(mob/M, mob/user) if (M == user) to_chat(M, SPAN_NOTICE("You seat yourself onto the toilet")) @@ -77,10 +78,10 @@ to_chat(user, SPAN_NOTICE("[M] has been seated onto the toilet by [user].")) to_chat(M, SPAN_NOTICE("You have been seated onto the toilet by [user].")) + /obj/structure/toilet/afterbuckle(mob/M) . = ..() - if(. && buckled_mob == M) var/direction = dir2text(dir) M.pixel_y = buckling_y[direction] + pixel_y @@ -106,7 +107,6 @@ M.overlays -= image("toilet00") - /obj/structure/toilet/verb/flip_lid() set name = "Flip lid" set category = "Object" @@ -116,11 +116,11 @@ update_icon() - /obj/structure/toilet/update_icon() icon_state = "toilet[open][cistern]" cistern_overlay.icon_state = "cistern[cistern]" + /obj/structure/toilet/attackby(obj/item/I, mob/living/user) if(HAS_TRAIT(I, TRAIT_TOOL_CROWBAR)) to_chat(user, SPAN_NOTICE("You start to [cistern ? "replace the lid on the cistern" : "lift the lid off the cistern"].")) @@ -168,374 +168,3 @@ w_items += I.w_class to_chat(user, "You carefully place \the [I] into the cistern.") return - - - -/obj/structure/urinal - name = "urinal" - desc = "The HU-452, an experimental urinal." - icon = 'icons/obj/structures/props/watercloset.dmi' - icon_state = "urinal" - density = FALSE - anchored = TRUE - -/obj/structure/urinal/attackby(obj/item/I, mob/living/user) - if(istype(I, /obj/item/grab)) - if(isxeno(user)) return - var/obj/item/grab/G = I - if(isliving(G.grabbed_thing)) - var/mob/living/GM = G.grabbed_thing - if(user.grab_level > GRAB_PASSIVE) - if(!GM.loc == get_turf(src)) - to_chat(user, SPAN_NOTICE("[GM.name] needs to be on the urinal.")) - return - user.visible_message(SPAN_DANGER("[user] slams [GM.name] into [src]!"), SPAN_NOTICE("You slam [GM.name] into [src]!")) - GM.apply_damage(8, BRUTE) - else - to_chat(user, SPAN_NOTICE("You need a tighter grip.")) - - - -/obj/structure/machinery/shower - name = "shower" - desc = "The HS-451. Installed in the 2050s by the Weyland Hygiene Division." - icon = 'icons/obj/structures/props/watercloset.dmi' - icon_state = "shower" - density = FALSE - anchored = TRUE - use_power = USE_POWER_NONE - var/on = 0 - var/obj/effect/mist/mymist = null - var/ismist = 0 //needs a var so we can make it linger~ - var/watertemp = "normal" //freezing, normal, or boiling - var/mobpresent = 0 //true if there is a mob on the shower's loc, this is to ease process() - var/is_washing = 0 - -/obj/structure/machinery/shower/Initialize() - . = ..() - create_reagents(2) - -//add heat controls? when emagged, you can freeze to death in it? - -/obj/effect/mist - name = "mist" - icon = 'icons/obj/structures/props/watercloset.dmi' - icon_state = "mist" - layer = FLY_LAYER - anchored = TRUE - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - -/obj/structure/machinery/shower/attack_hand(mob/M as mob) - on = !on - update_icon() - if(on) - start_processing() - if (M.loc == loc) - wash(M) - check_heat(M) - for (var/atom/movable/G in src.loc) - G.clean_blood() - else - stop_processing() - -/obj/structure/machinery/shower/attackby(obj/item/I as obj, mob/user as mob) - if(I.type == /obj/item/device/analyzer) - to_chat(user, SPAN_NOTICE("The water temperature seems to be [watertemp].")) - if(HAS_TRAIT(I, TRAIT_TOOL_WRENCH)) - to_chat(user, SPAN_NOTICE("You begin to adjust the temperature valve with \the [I].")) - if(do_after(user, 50, INTERRUPT_ALL|BEHAVIOR_IMMOBILE, BUSY_ICON_BUILD)) - switch(watertemp) - if("normal") - watertemp = "freezing" - if("freezing") - watertemp = "boiling" - if("boiling") - watertemp = "normal" - user.visible_message(SPAN_NOTICE("[user] adjusts the shower with \the [I]."), SPAN_NOTICE("You adjust the shower with \the [I].")) - add_fingerprint(user) - -/obj/structure/machinery/shower/update_icon() //this is terribly unreadable, but basically it makes the shower mist up - overlays.Cut() //once it's been on for a while, in addition to handling the water overlay. - QDEL_NULL(mymist) - - if(on) - overlays += image('icons/obj/structures/props/watercloset.dmi', src, "water", MOB_LAYER + 1, dir) - if(watertemp == "freezing") - return - if(!ismist) - spawn(50) - if(src && on) - ismist = 1 - mymist = new /obj/effect/mist(loc) - else - ismist = 1 - mymist = new /obj/effect/mist(loc) - else if(ismist) - ismist = 1 - mymist = new /obj/effect/mist(loc) - spawn(250) - if(src && !on) - QDEL_NULL(mymist) - ismist = 0 - -/obj/structure/machinery/shower/Crossed(atom/movable/O) - ..() - wash(O) - if(ismob(O)) - mobpresent++ - check_heat(O) - -/obj/structure/machinery/shower/Uncrossed(atom/movable/O) - if(ismob(O)) - mobpresent-- - ..() - -//Yes, showers are super powerful as far as washing goes. -/obj/structure/machinery/shower/proc/wash(atom/movable/O as obj|mob) - if(!on) return - - - if(isliving(O)) - var/mob/living/L = O - L.ExtinguishMob() - L.fire_stacks = -20 //Douse ourselves with water to avoid fire more easily - to_chat(L, SPAN_WARNING("You've been drenched in water!")) - if(iscarbon(O)) - var/mob/living/carbon/M = O - if(M.r_hand) - M.r_hand.clean_blood() - if(M.l_hand) - M.l_hand.clean_blood() - if(M.back) - if(M.back.clean_blood()) - M.update_inv_back(0) - if(ishuman(M)) - var/mob/living/carbon/human/H = M - var/washgloves = 1 - var/washshoes = 1 - var/washmask = 1 - var/washears = 1 - var/washglasses = 1 - - if(H.wear_suit) - washgloves = !(H.wear_suit.flags_inv_hide & HIDEGLOVES) - washshoes = !(H.wear_suit.flags_inv_hide & HIDESHOES) - - if(H.head) - washmask = !(H.head.flags_inv_hide & HIDEMASK) - washglasses = !(H.head.flags_inv_hide & HIDEEYES) - washears = !(H.head.flags_inv_hide & HIDEEARS) - - if(H.wear_mask) - if (washears) - washears = !(H.wear_mask.flags_inv_hide & HIDEEARS) - if (washglasses) - washglasses = !(H.wear_mask.flags_inv_hide & HIDEEYES) - - if(H.head) - if(H.head.clean_blood()) - H.update_inv_head() - if(H.wear_suit) - if(H.wear_suit.clean_blood()) - H.update_inv_wear_suit() - else if(H.w_uniform) - if(H.w_uniform.clean_blood()) - H.update_inv_w_uniform() - if(H.gloves && washgloves) - if(H.gloves.clean_blood()) - H.update_inv_gloves() - if(H.shoes && washshoes) - if(H.shoes.clean_blood()) - H.update_inv_shoes() - if(H.wear_mask && washmask) - if(H.wear_mask.clean_blood()) - H.update_inv_wear_mask() - if(H.glasses && washglasses) - if(H.glasses.clean_blood()) - H.update_inv_glasses() - if((H.wear_l_ear || H.wear_r_ear) && washears) - if((H.wear_l_ear && H.wear_l_ear.clean_blood()) ||(H.wear_r_ear && H.wear_r_ear.clean_blood())) - H.update_inv_ears() - if(H.belt) - if(H.belt.clean_blood()) - H.update_inv_belt() - H.clean_blood(washshoes) - else - if(M.wear_mask) //if the mob is not human, it cleans the mask without asking for bitflags - if(M.wear_mask.clean_blood()) - M.update_inv_wear_mask() - M.clean_blood() - else - O.clean_blood() - - if(isturf(loc)) - var/turf/tile = loc - tile.clean_blood() - for(var/obj/effect/E in tile) - if(istype(E,/obj/effect/decal/cleanable) || istype(E,/obj/effect/overlay)) - qdel(E) - -/obj/structure/machinery/shower/process() - if(!on) return - wash_floor() - if(!mobpresent) return - for(var/mob/living/carbon/C in loc) - check_heat(C) - -/obj/structure/machinery/shower/proc/wash_floor() - if(!ismist && is_washing) - return - is_washing = 1 - var/turf/T = get_turf(src) -// reagents.add_reagent("water", 2) - T.clean(src) - addtimer(VARSET_CALLBACK(src, is_washing, FALSE), 10 SECONDS) - -/obj/structure/machinery/shower/proc/check_heat(mob/M as mob) - if(!on || watertemp == "normal") return - if(iscarbon(M)) - var/mob/living/carbon/C = M - - if(watertemp == "freezing") - C.bodytemperature = max(80, C.bodytemperature - 80) - C.recalculate_move_delay = TRUE - to_chat(C, SPAN_WARNING("The water is freezing!")) - return - if(watertemp == "boiling") - C.bodytemperature = min(500, C.bodytemperature + 35) - C.recalculate_move_delay = TRUE - C.apply_damage(5, BURN) - to_chat(C, SPAN_DANGER("The water is searing!")) - return - - - -/obj/item/toy/bikehorn/rubberducky - name = "rubber ducky" - desc = "Rubber ducky you're so fine, you make bathtime lots of fuuun. Rubber ducky I'm awfully fooooond of yooooouuuu~" //thanks doohl - icon = 'icons/obj/structures/props/watercloset.dmi' - icon_state = "rubberducky" - item_state = "rubberducky" - - - -/obj/structure/sink - name = "sink" - icon = 'icons/obj/structures/props/watercloset.dmi' - icon_state = "sink_emptied_animation" - desc = "A sink used for washing one's hands and face." - anchored = TRUE - var/busy = FALSE //Something's being washed at the moment - -/obj/structure/sink/Initialize() - . = ..() - if(prob(50)) - icon_state = "sink_emptied" - - - -/obj/structure/sink/proc/stop_flow() //sets sink animation to normal sink (without running water) - - if(prob(50)) - icon_state = "sink_emptied_animation" - else - icon_state = "sink_emptied" - flick("sink_animation_empty", src) - - - -/obj/structure/sink/attack_hand(mob/user) - if(isRemoteControlling(user)) - return - - if(!Adjacent(user)) - return - - if(busy) - to_chat(user, SPAN_DANGER("Someone's already washing here.")) - return - - to_chat(usr, SPAN_NOTICE(" You start washing your hands.")) - flick("sink_animation_fill", src) //<- play the filling animation then automatically switch back to the loop - icon_state = "sink_animation_fill_loop" //<- set it to the loop - addtimer(CALLBACK(src, PROC_REF(stop_flow)), 6 SECONDS) - playsound(loc, 'sound/effects/sinkrunning.ogg', 25, TRUE) - - busy = TRUE - sleep(40) - busy = FALSE - - if(!Adjacent(user)) return //Person has moved away from the sink - - user.clean_blood() - if(ishuman(user)) - user:update_inv_gloves() - for(var/mob/V in viewers(src, null)) - V.show_message(SPAN_NOTICE("[user] washes their hands using \the [src]."), SHOW_MESSAGE_VISIBLE) - - -/obj/structure/sink/attackby(obj/item/O as obj, mob/user as mob) - if(busy) - to_chat(user, SPAN_DANGER("Someone's already washing here.")) - return - - var/obj/item/reagent_container/RG = O - if (istype(RG) && RG.is_open_container()) - RG.reagents.add_reagent("water", min(RG.volume - RG.reagents.total_volume, RG.amount_per_transfer_from_this)) - user.visible_message(SPAN_NOTICE("[user] fills \the [RG] using \the [src]."),SPAN_NOTICE("You fill \the [RG] using \the [src].")) - return - - else if (istype(O, /obj/item/weapon/baton)) - var/obj/item/weapon/baton/B = O - if(B.bcell) - if(B.bcell.charge > 0 && B.status == 1) - flick("baton_active", src) - user.apply_effect(10, STUN) - user.stuttering = 10 - user.apply_effect(10, WEAKEN) - B.deductcharge(B.hitcost) - user.visible_message( \ - SPAN_DANGER("[user] was stunned by \his wet [O]!"), \ - SPAN_DANGER("You were stunned by your wet [O]!")) - return - - var/turf/location = user.loc - if(!isturf(location)) return - - var/obj/item/I = O - if(!I || !istype(I,/obj/item)) return - - to_chat(usr, SPAN_NOTICE(" You start washing \the [I].")) - - busy = TRUE - sleep(40) - busy = FALSE - - if(user.loc != location) return //User has moved - if(!I) return //Item's been destroyed while washing - if(user.get_active_hand() != I) return //Person has switched hands or the item in their hands - - O.clean_blood() - user.visible_message( \ - SPAN_NOTICE("[user] washes \a [I] using \the [src]."), \ - SPAN_NOTICE("You wash \a [I] using \the [src].")) - - -/obj/structure/sink/kitchen - name = "kitchen sink" - icon_state = "sink_alt" - - -/obj/structure/sink/puddle //splishy splashy ^_^ - name = "puddle" - icon_state = "puddle" - -/obj/structure/sink/puddle/attack_hand(mob/M as mob) - icon_state = "puddle-splash" - ..() - icon_state = "puddle" - -/obj/structure/sink/puddle/attackby(obj/item/O as obj, mob/user as mob) - icon_state = "puddle-splash" - ..() - icon_state = "puddle" diff --git a/code/game/world.dm b/code/game/world.dm index f68263412715..627e245bc4c1 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -87,10 +87,6 @@ GLOBAL_LIST_INIT(reboot_sfx, file2list("config/reboot_sfx.txt")) GLOB.obfs_x = rand(-500, 500) //A number between -100 and 100 GLOB.obfs_y = rand(-500, 500) //A number between -100 and 100 - spawn(3000) //so we aren't adding to the round-start lag - if(CONFIG_GET(flag/ToRban)) - ToRban_autoupdate() - // If the server's configured for local testing, get everything set up ASAP. // Shamelessly stolen from the test manager's host_tests() proc if(testing_locally) diff --git a/code/modules/admin/IsBanned.dm b/code/modules/admin/IsBanned.dm index 94f40629fc6a..4a7307b247a5 100644 --- a/code/modules/admin/IsBanned.dm +++ b/code/modules/admin/IsBanned.dm @@ -17,6 +17,7 @@ message_admins("Failed Login: [key] - Guests not allowed") return list("reason"="guest", "desc"="\nReason: Guests not allowed. Please sign in with a byond account.") + // wait for database to be ready WAIT_DB_READY if(GLOB.admin_datums[ckey] && (GLOB.admin_datums[ckey].rights & R_MOD)) return ..() @@ -26,13 +27,6 @@ var/datum/entity/player/P = get_player_from_key(ckey) - //check if the IP address is a known TOR node - if(CONFIG_GET(flag/ToRban) && ToRban_isbanned(address)) - log_access("Failed Login: [src] - Banned: ToR") - message_admins("Failed Login: [src] - Banned: ToR") - return list("reason"="Using ToR", "desc"="\nReason: The network you are using to connect has been banned.\nIf you believe this is a mistake, please request help at [CONFIG_GET(string/banappeals)]") - - // wait for database to be ready . = P.check_ban(computer_id, address) if(.) diff --git a/code/modules/admin/ToRban.dm b/code/modules/admin/ToRban.dm deleted file mode 100644 index 549353facfb8..000000000000 --- a/code/modules/admin/ToRban.dm +++ /dev/null @@ -1,88 +0,0 @@ -//By Carnwennan -//fetches an external list and processes it into a list of ip addresses. -//It then stores the processed list into a savefile for later use -#define TORFILE "data/ToR_ban.bdb" -#define TOR_UPDATE_INTERVAL 216000 //~6 hours - -/proc/ToRban_isbanned(ip_address) - var/savefile/F = new(TORFILE) - if(F) - if( ip_address in F.dir ) - return 1 - return 0 - -/proc/ToRban_autoupdate() - var/savefile/F = new(TORFILE) - if(F) - var/last_update - F["last_update"] >> last_update - if((last_update + TOR_UPDATE_INTERVAL) < world.realtime) //we haven't updated for a while - ToRban_update() - return - -/proc/ToRban_update() - spawn(0) - log_misc("Downloading updated ToR data...") - var/http[] = world.Export("https://check.torproject.org/exit-addresses") - - var/list/rawlist = file2list(http["CONTENT"]) - if(rawlist.len) - fdel(TORFILE) - var/savefile/F = new(TORFILE) - for( var/line in rawlist ) - if(!line) continue - if( copytext(line,1,12) == "ExitAddress" ) - var/cleaned = copytext(line,13,length(line)-19) - if(!cleaned) continue - F[cleaned] << 1 - F["last_update"] << world.realtime - log_misc("ToR data updated!") - if(usr) to_chat(usr, "ToRban updated.") - return - log_misc("ToR data update aborted: no data.") - return - -/client/proc/ToRban(task in list("update","toggle","show","remove","remove all","find")) - set name = "ToR Ban Settings" - set category = "Server" - if(!admin_holder) return - switch(task) - if("update") - ToRban_update() - if("toggle") - if(config) - if(CONFIG_GET(flag/ToRban)) - CONFIG_SET(flag/ToRban, FALSE) - message_admins("ToR banning disabled.") - else - CONFIG_SET(flag/ToRban, TRUE) - message_admins("ToR banning enabled.") - if("show") - var/savefile/F = new(TORFILE) - var/dat - if( length(F.dir) ) - for( var/i=1, i<=length(F.dir), i++ ) - dat += "