Files
Aurora.3/code/game/objects/structures/janicart.dm
Fluffy c24b4c7097 Projectile refactoring madness (#19878)
Refactored the projectile code, mostly in line with TG's now.
Refactored various procs that are used or depends on it.
Projectiles can now ricochet if enabled to.
Damage falloffs with distance.
Homing projectiles can now have accuracy falloff with distance.
Projectiles have a maximum range.
Muzzle flash is configurable per projectile.
Impact effect of the projectile is configurable per projectile.
Accuracy decreases with distance.
Projectiles work with signals and emits them, for easy hooking up from
other parts of the code.
Meatshielding is now less effective .
Impact sound is now configurable per projectile.

High risk.
2024-09-23 10:12:57 +00:00

439 lines
13 KiB
Plaintext

/obj/structure/janitorialcart
name = "custodial cart"
desc = "The ultimate in custodial carts. Has space for water, mops, signs, trash bags, and more."
desc_info = "Click and drag a mop bucket onto the cart to mount it\
</br>Alt+Click with a mop to put it away, a normal click will wet it in the bucket.\
</br>Alt+Click with a container, such as a bucket, to pour its contents into the mounted bucket. A normal click will toss it into the trash\
</br>You can also use a lightreplacer, spraybottle (of spacecleaner) and four wet-floor signs on the cart to store them"
icon = 'icons/obj/janitor.dmi'
icon_state = "cart"
anchored = FALSE
density = TRUE
climbable = TRUE
atom_flags = ATOM_FLAG_OPEN_CONTAINER
build_amt = 15
slowdown = 0
var/amount_per_transfer_from_this = 5 //shit I dunno, adding this so syringes stop runtime erroring. --NeoFite
var/obj/item/storage/bag/trash/mybag = null
var/obj/item/mop/mymop = null
var/obj/item/reagent_containers/spray/myspray = null
var/obj/item/device/lightreplacer/myreplacer = null
var/obj/structure/mopbucket/mybucket = null
var/signs = 0 //maximum capacity hardcoded below
var/has_items = FALSE //This is set true whenever the cart has anything loaded/mounted on it
var/driving
var/mob/living/pulling
// Regular Variant
// No trashbag and no light replacer, this is inside the custodian's locker.
/obj/structure/janitorialcart/Initialize()
. = ..()
mymop = new /obj/item/mop(src)
myspray = new /obj/item/reagent_containers/spray/cleaner(src)
mybucket = new /obj/structure/mopbucket(src)
for(signs, signs < 4, signs++)
new /obj/item/clothing/suit/caution(src)
update_icon()
// Full Variant
// Has everything.
/obj/structure/janitorialcart/full/Initialize()
. = ..()
mybag = new /obj/item/storage/bag/trash(src)
mymop = new /obj/item/mop(src)
myspray = new /obj/item/reagent_containers/spray/cleaner(src)
myreplacer = new /obj/item/device/lightreplacer(src)
mybucket = new /obj/structure/mopbucket(src)
for(signs, signs < 4, signs++)
new /obj/item/clothing/suit/caution(src)
update_icon()
// Full with Water Variant
// Has everything as well as water in the mop bucket.
/obj/structure/janitorialcart/full/water/Initialize()
. = ..()
mybag = new /obj/item/storage/bag/trash(src)
mymop = new /obj/item/mop(src)
myspray = new /obj/item/reagent_containers/spray/cleaner(src)
myreplacer = new /obj/item/device/lightreplacer(src)
mybucket = new /obj/structure/mopbucket(src)
mybucket.reagents.add_reagent(/singleton/reagent/water, mybucket.bucketsize)
for(signs, signs < 4, signs++)
new /obj/item/clothing/suit/caution(src)
update_icon()
/obj/structure/janitorialcart/New()
..()
GLOB.janitorial_supplies |= src
/obj/structure/janitorialcart/Destroy()
GLOB.janitorial_supplies -= src
QDEL_NULL(mybag)
QDEL_NULL(mymop)
QDEL_NULL(myspray)
QDEL_NULL(myreplacer)
QDEL_NULL(mybucket)
return ..()
/obj/structure/janitorialcart/proc/get_short_status()
return "Contents: [english_list(contents)]"
/obj/structure/janitorialcart/get_examine_text(mob/user, distance, is_adjacent, infix, suffix)
. = ..()
if(distance <= 1)
if (mybucket)
var/contains = mybucket.reagents.total_volume
. += "[icon2html(src, user)] The bucket contains [contains] unit\s of liquid!"
else
. += "[icon2html(src, user)] There is no bucket mounted on it!"
//everything else is visible, so doesn't need to be mentioned
/obj/structure/janitorialcart/MouseDrop_T(atom/dropping, mob/user)
var/atom/movable/O = dropping
if (istype(O, /obj/structure/mopbucket) && !mybucket)
O.forceMove(src)
mybucket = O
to_chat(user, "You mount the [O] on the janicart.")
update_icon()
else
..()
//New Altclick functionality!
//Altclick the cart with a mop to stow the mop away
//Altclick the cart with a reagent container to pour things into the bucket without putting the bottle in trash
/obj/structure/janitorialcart/AltClick()
if(!usr || usr.stat || usr.lying || usr.restrained() || !Adjacent(usr)) return
var/obj/I = usr.get_active_hand()
if(istype(I, /obj/item/mop))
if(!mymop)
usr.drop_from_inventory(I,src)
mymop = I
update_icon()
updateUsrDialog()
to_chat(usr, SPAN_NOTICE("You put [I] into [src]."))
else
to_chat(usr, SPAN_NOTICE("The cart already has a mop attached"))
return
else if(istype(I, /obj/item/reagent_containers) && mybucket)
var/obj/item/reagent_containers/C = I
C.afterattack(mybucket, usr, 1)
else if(istype (I, /obj/item/device/lightreplacer))
var/obj/item/device/lightreplacer/LR = I
if (LR.store_broken)
return mybag.attackby(I, usr)
/obj/structure/janitorialcart/attackby(obj/item/attacking_item, mob/user)
if(istype(attacking_item, /obj/item/mop) || istype(attacking_item, /obj/item/reagent_containers/glass/rag) || istype(attacking_item, /obj/item/soap))
if (mybucket)
if(attacking_item.reagents.total_volume < attacking_item.reagents.maximum_volume)
if(mybucket.reagents.total_volume < 1)
to_chat(user, SPAN_NOTICE("[mybucket] is empty!"))
update_icon()
else
mybucket.reagents.trans_to_obj(attacking_item, 5) //
to_chat(user, SPAN_NOTICE("You wet [attacking_item] in [mybucket]."))
playsound(loc, 'sound/effects/slosh.ogg', 25, 1)
update_icon()
else
to_chat(user, SPAN_NOTICE("[attacking_item] can't absorb anymore liquid!"))
else
to_chat(user, SPAN_NOTICE("There is no bucket mounted here to dip [attacking_item] into!"))
return 1
else if(istype(attacking_item, /obj/item/reagent_containers/spray) && !myspray)
user.drop_from_inventory(attacking_item, src)
myspray = attacking_item
update_icon()
updateUsrDialog()
to_chat(user, SPAN_NOTICE("You put [attacking_item] into [src]."))
return 1
else if(istype(attacking_item, /obj/item/device/lightreplacer) && !myreplacer)
user.drop_from_inventory(attacking_item, src)
myreplacer = attacking_item
update_icon()
updateUsrDialog()
to_chat(user, SPAN_NOTICE("You put [attacking_item] into [src]."))
return 1
else if(istype(attacking_item, /obj/item/storage/bag/trash) && !mybag)
user.drop_from_inventory(attacking_item, src)
mybag = attacking_item
attacking_item.forceMove(src)
update_icon()
updateUsrDialog()
to_chat(user, SPAN_NOTICE("You put [attacking_item] into [src]."))
return 1
else if(istype(attacking_item, /obj/item/clothing/suit/caution))
if(signs < 4)
user.drop_from_inventory(attacking_item, src)
signs++
update_icon()
updateUsrDialog()
to_chat(user, SPAN_NOTICE("You put [attacking_item] into [src]."))
else
to_chat(user, SPAN_NOTICE("[src] can't hold any more signs."))
return 1
else if(mybag)
return mybag.attackby(attacking_item, user)
//This return will prevent afterattack from executing if the object goes into the trashbag,
//This prevents dumb stuff like splashing the cart with the contents of a container, after putting said container into trash
else if (!has_items && (attacking_item.iswrench() || attacking_item.iswelder() || istype(attacking_item, /obj/item/gun/energy/plasmacutter)))
take_apart(user, attacking_item)
return
..()
/obj/structure/janitorialcart/proc/take_apart(var/mob/user = null, var/obj/I)
if(has_items)
spill()
if(user)
if(iswelder(I))
var/obj/item/welder = I
welder.play_tool_sound(get_turf(src), 50)
user.visible_message("<b>[user]</b> starts taking apart the [src]...", SPAN_NOTICE("You start disassembling the [src]..."))
if (!do_after(user, 30, do_flags = DO_DEFAULT & ~DO_USER_SAME_HAND))
return
dismantle()
/obj/structure/janitorialcart/ex_act(severity)
spill(100 / severity)
..()
//This is called if the cart is caught in an explosion, or destroyed by weapon fire
/obj/structure/janitorialcart/proc/spill(var/chance = 100)
var/turf/dropspot = get_turf(src)
if (mymop && prob(chance))
mymop.forceMove(dropspot)
mymop.tumble(2)
mymop = null
if (myspray && prob(chance))
myspray.forceMove(dropspot)
myspray.tumble(3)
myspray = null
if (myreplacer && prob(chance))
myreplacer.forceMove(dropspot)
myreplacer.tumble(3)
myreplacer = null
if (mybucket && prob(chance*0.5))//bucket is heavier, harder to knock off
mybucket.forceMove(dropspot)
mybucket.tumble(1)
mybucket = null
if (signs)
for (var/obj/item/clothing/suit/caution/Sign in src)
if (prob(min((chance*2),100)))
signs--
Sign.forceMove(dropspot)
Sign.tumble(3)
if (signs < 0)//safety for something that shouldn't happen
signs = 0
update_icon()
return
if (mybag && prob(min((chance*2),100)))//Bag is flimsy
mybag.forceMove(dropspot)
mybag.tumble(1)
mybag.spill()//trashbag spills its contents too
mybag = null
update_icon()
/obj/structure/janitorialcart/attack_hand(mob/user)
ui_interact(user)
return
/obj/structure/janitorialcart/ui_interact(var/mob/user, var/ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
var/data[0]
data["name"] = capitalize(name)
data["bag"] = mybag ? capitalize(mybag.name) : null
data["bucket"] = mybucket ? capitalize(mybucket.name) : null
data["mop"] = mymop ? capitalize(mymop.name) : null
data["spray"] = myspray ? capitalize(myspray.name) : null
data["replacer"] = myreplacer ? capitalize(myreplacer.name) : null
data["signs"] = signs ? "[signs] sign\s" : null
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if(!ui)
ui = new(user, src, ui_key, "janitorcart.tmpl", "Janitorial cart", 240, 160)
ui.set_initial_data(data)
ui.open()
/obj/structure/janitorialcart/Topic(href, href_list)
if(!in_range(src, usr))
return
if(!isliving(usr))
return
var/mob/living/user = usr
if(href_list["take"])
switch(href_list["take"])
if("garbage")
if(mybag)
user.put_in_hands(mybag)
to_chat(user, SPAN_NOTICE("You take [mybag] from [src]."))
mybag = null
if("mop")
if(mymop)
user.put_in_hands(mymop)
to_chat(user, SPAN_NOTICE("You take [mymop] from [src]."))
mymop = null
if("spray")
if(myspray)
user.put_in_hands(myspray)
to_chat(user, SPAN_NOTICE("You take [myspray] from [src]."))
myspray = null
if("replacer")
if(myreplacer)
user.put_in_hands(myreplacer)
to_chat(user, SPAN_NOTICE("You take [myreplacer] from [src]."))
myreplacer = null
if("sign")
if(signs)
var/obj/item/clothing/suit/caution/Sign = locate() in src
if(Sign)
user.put_in_hands(Sign)
to_chat(user, SPAN_NOTICE("You take \a [Sign] from [src]."))
signs--
else
warning("[src] signs ([signs]) didn't match contents")
signs = 0
if("bucket")
if(mybucket)
mybucket.forceMove(get_turf(user))
to_chat(user, SPAN_NOTICE("You unmount [mybucket] from [src]."))
mybucket.update_icon()
mybucket = null
update_icon()
updateUsrDialog()
/obj/structure/janitorialcart/update_icon()
ClearOverlays()
has_items = 0
if(mybucket)
AddOverlays("cart_bucket")
has_items = 1
if(mybucket.reagents.total_volume > 0)
AddOverlays("cart_water")
if(mybag)
AddOverlays("cart_garbage")
has_items = 1
if(mymop)
AddOverlays("cart_mop")
has_items = 1
if(myspray)
AddOverlays("cart_spray")
has_items = 1
if(myreplacer)
if (istype(myreplacer, /obj/item/device/lightreplacer/advanced))
AddOverlays("cart_adv_lightreplacer")
else
AddOverlays("cart_replacer")
has_items = 1
if(signs)
AddOverlays("cart_sign[signs]")
has_items = 1
//Shamelessly copied from wheelchair code
/obj/structure/janitorialcart/relaymove(mob/living/user, direction)
. = ..()
if(user.stat || user.stunned || user.weakened || user.paralysis || user.lying || user.restrained())
if(user==pulling)
pulling = null
user.pulledby = null
to_chat(user, SPAN_WARNING("You lost your grip!"))
return
if(user.pulling && (user == pulling))
pulling = null
user.pulledby = null
return
if(pulling && (get_dist(src, pulling) > 1))
pulling = null
user.pulledby = null
if(user==pulling)
return
if(pulling && (get_dir(src.loc, pulling.loc) == direction))
to_chat(user, SPAN_WARNING("You cannot go there."))
return
driving = 1
var/turf/T = null
if(pulling)
T = pulling.loc
if(get_dist(src, pulling) >= 1)
step(pulling, get_dir(pulling.loc, src.loc))
step(src, direction)
set_dir(direction)
if(pulling)
if(pulling.loc == src.loc)
pulling.forceMove(T)
else
spawn(0)
if(get_dist(src, pulling) > 1)
pulling = null
user.pulledby = null
pulling.set_dir(get_dir(pulling, src))
driving = 0
/obj/structure/janitorialcart/Move()
. = ..()
if (pulling && (get_dist(src, pulling) > 1))
pulling.pulledby = null
to_chat(pulling, SPAN_WARNING("You lost your grip!"))
pulling = null
/obj/structure/janitorialcart/CtrlClick(var/mob/user)
if(in_range(src, user))
if(!ishuman(user)) return
if(!pulling)
pulling = user
user.pulledby = src
if(user.pulling)
user.stop_pulling()
user.set_dir(get_dir(user, src))
to_chat(user, "You grip \the [name]'s handles.")
else
to_chat(usr, "You let go of \the [name]'s handles.")
pulling.pulledby = null
pulling = null
return
/obj/structure/janitorialcart/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
if(air_group || (height==0))
return TRUE
if(mover?.movement_type & PHASING)
return TRUE
if(istype(mover) && mover.pass_flags & PASSTABLE)
return TRUE
if(istype(mover, /mob/living) && mover == pulling)
return TRUE
else
if(istype(mover, /obj/projectile))
return prob(30)
else
return !density