Files
BatrachophrenoandGitHub 5277251959 Tool behaviors port (#21663)
For a robust crafting system, I need a new materials framework.
For a new materials framework, I need to clean up reagents.
To clean up reagents, I need to pare down foods from reagent holders.
To pare down foods from reagent holders, I need to port edibility
components.
To port edibility components, I need to port processing components.
To port processing components, I need to port tool behaviors.

This is all back-end code, no new features or functionality from this.
2026-01-27 08:44:49 +00:00

294 lines
10 KiB
Plaintext

/obj/item/storage/box/bloodpacks
name = "\improper IV bags"
desc = "This box contains IV bags."
illustration = "blood"
/obj/item/storage/box/bloodpacks/fill()
..()
new /obj/item/reagent_containers/blood/empty(src)
new /obj/item/reagent_containers/blood/empty(src)
new /obj/item/reagent_containers/blood/empty(src)
new /obj/item/reagent_containers/blood/empty(src)
new /obj/item/reagent_containers/blood/empty(src)
new /obj/item/reagent_containers/blood/empty(src)
new /obj/item/reagent_containers/blood/empty(src)
/obj/item/reagent_containers/blood
name = "\improper IV bag"
desc = "Contains fluids used for transfusions."
icon = 'icons/obj/bloodpack.dmi'
icon_state = "bloodpack"
filling_states = "-10;10;25;50;75;80;100"
w_class = WEIGHT_CLASS_SMALL
volume = 200
amount_per_transfer_from_this = 0.2
possible_transfer_amounts = list(0.2, 1, 2, 3, 4)
atom_flags = ATOM_FLAG_OPEN_CONTAINER
var/datum/weakref/attached_mob
var/blood_type = null
var/vampire_marks = null
var/being_feed = FALSE
drop_sound = 'sound/items/drop/food.ogg'
pickup_sound = 'sound/items/pickup/food.ogg'
/obj/item/reagent_containers/blood/feedback_hints(mob/user, distance, is_adjacent)
. += ..()
if (distance <= 2 && vampire_marks)
. += SPAN_WARNING("There are sharp, canine-like teeth marks on it.")
/obj/item/reagent_containers/blood/Initialize()
. = ..()
if(blood_type != null)
name = "\improper IV bag - [blood_type] blood"
reagents.add_reagent(/singleton/reagent/blood, volume, list("donor"=null,"blood_DNA"=null,"blood_type"=blood_type,"trace_chem"=null,"dose_chem"=null))
w_class = WEIGHT_CLASS_NORMAL
update_icon()
/obj/item/reagent_containers/blood/Destroy()
STOP_PROCESSING(SSprocessing, src)
attached_mob = null
return ..()
/obj/item/reagent_containers/blood/on_reagent_change()
update_icon()
if(reagents.total_volume > volume / 2)
w_class = WEIGHT_CLASS_NORMAL
else
w_class = WEIGHT_CLASS_SMALL
/obj/item/reagent_containers/blood/update_icon()
ClearOverlays()
if(blood_type)
AddOverlays(image('icons/obj/bloodpack.dmi', "[blood_type]"))
if(attached_mob)
AddOverlays(image('icons/obj/bloodpack.dmi', "dongle"))
if(reagents && reagents.total_volume)
AddOverlays(overlay_image('icons/obj/bloodpack.dmi', "[icon_state][get_filling_state()]", color = reagents.get_color()))
/obj/item/reagent_containers/blood/attack(mob/living/target_mob, mob/living/user, target_zone)
if(user == target_mob && (MODE_VAMPIRE in user.mind?.antag_datums))
var/datum/vampire/vampire = user.mind.antag_datums[MODE_VAMPIRE]
if (being_feed)
to_chat(user, SPAN_NOTICE("You are already feeding on \the [src]."))
return
if (REAGENT_VOLUME(reagents, /singleton/reagent/blood))
user.visible_message(SPAN_WARNING("[user] raises \the [src] up to their mouth and bites into it."), SPAN_NOTICE("You raise \the [src] up to your mouth and bite into it, starting to drain its contents.<br>You need to stand still."))
being_feed = TRUE
vampire_marks = TRUE
if (!LAZYLEN(src.other_DNA))
LAZYADD(src.other_DNA, target_mob.dna.unique_enzymes)
src.other_DNA_type = "saliva"
while (do_after(user, 25, 5))
var/blood_taken = 0
blood_taken = min(5, REAGENT_VOLUME(reagents, /singleton/reagent/blood)/4)
reagents.remove_reagent(/singleton/reagent/blood, blood_taken*4)
vampire.blood_usable += blood_taken
if (blood_taken)
to_chat(user, SPAN_NOTICE("You have accumulated [vampire.blood_usable] unit\s of usable blood. It tastes quite stale."))
if (REAGENT_VOLUME(reagents, /singleton/reagent/blood) < 1)
break
user.visible_message(SPAN_WARNING("[user] licks [user.get_pronoun("his")] fangs dry, lowering \the [src]."), SPAN_NOTICE("You lick your fangs clean of the tasteless blood."))
being_feed = FALSE
else
..()
/obj/item/reagent_containers/blood/mouse_drop_dragged(atom/over, mob/user, src_location, over_location, params)
if(!ismob(loc))
return
var/turf/our_turf = get_turf(src)
var/turf/target_turf = get_turf(over)
if(!our_turf.Adjacent(target_turf))
return
if(attached_mob)
remove_iv_mob()
else if(ishuman(over))
visible_message(SPAN_WARNING("\The [user] starts hooking \the [over] up to \the [src]."))
if(do_after(user, 30))
to_chat(user, SPAN_NOTICE("You hook \the [over] up to \the [src]."))
attached_mob = WEAKREF(over)
START_PROCESSING(SSprocessing, src)
update_icon()
/obj/item/reagent_containers/blood/process()
var/mob/living/carbon/human/attached
if(attached_mob)
attached = attached_mob.resolve()
if(!attached)
attached_mob = null
return PROCESS_KILL
var/is_adjacent = loc.Adjacent(attached)
if(!is_adjacent || !ismob(loc))
remove_iv_mob(is_adjacent)
update_icon()
return PROCESS_KILL
else
remove_iv_mob()
return
var/mob/M = loc
if(M.l_hand != src && M.r_hand != src)
remove_iv_mob()
return
if(!reagents.total_volume)
remove_iv_mob()
return
reagents.trans_to_mob(attached, amount_per_transfer_from_this, CHEM_BLOOD)
update_icon()
/obj/item/reagent_containers/blood/proc/remove_iv_mob(var/safe = TRUE)
if(attached_mob)
var/mob/living/carbon/human/attached = attached_mob.resolve()
if(attached)
if(safe)
visible_message(SPAN_NOTICE("\The [attached] is taken off \the [src]."))
else
var/obj/item/organ/external/affecting = attached.get_organ(pick(BP_R_ARM, BP_L_ARM))
attached.visible_message(SPAN_WARNING("The needle is ripped out of [attached]'s [affecting.limb_name == BP_R_ARM ? "right arm" : "left arm"]."), SPAN_DANGER("The needle <b>painfully</b> rips out of your [affecting.limb_name == BP_R_ARM ? "right arm" : "left arm"]."))
affecting.take_damage(brute = 5, damage_flags = DAMAGE_FLAG_SHARP)
attached_mob = null
STOP_PROCESSING(SSprocessing, src)
/obj/item/reagent_containers/blood/attackby(obj/item/attacking_item, mob/user)
..()
if (attacking_item.tool_behaviour == TOOL_PEN)
if (REAGENT_VOLUME(reagents, /singleton/reagent/blood) && name != "empty IV bag") //Stops people mucking with bloodpacks that are filled
to_chat(user, SPAN_NOTICE("You can't relabel [name] until it is empty!"))
return
var/choices = list("A+ blood", "A- blood", "B+ blood", "B- blood", "O+ blood", "O- blood", "AB+ blood", "AB- blood", "SBS blood", "Saline Plus", "Cryonics mixture", "Other mixture", "Clear", "Cancel")
var/blood_name = tgui_input_list(user, "What would you like to label the IV bag?", "Label Selection", choices)
if(blood_name == "Cancel")
return
var/obj/item/i = user.get_active_hand()
if(!i.tool_behaviour == TOOL_PEN || !in_range(user, src)) //Checks to see if pen is still held or bloodpack is in range
return
if(blood_name == "Clear")
blood_type = null
name = initial(name)
desc = initial(desc)
to_chat(user, SPAN_NOTICE("You clear the IV bag label."))
update_icon()
return
blood_type = blood_name
name = "\improper IV bag - [blood_type]"
desc = "Contains fluids used for transfusions."
to_chat(user, SPAN_NOTICE("You label the IV bag as [blood_type]."))
update_icon()
return
if (istype(attacking_item, /obj/item/) && attacking_item.sharp == 1)
var/mob/living/carbon/human/H = usr
if(LAZYLEN(attacking_item.attack_verb))
user.visible_message(SPAN_DANGER("[src] has been [pick(attacking_item.attack_verb)] with \the [attacking_item] by [user]!"))
var/atkmsg_filled = null
if (REAGENT_VOLUME(reagents, /singleton/reagent/blood))
atkmsg_filled = " and the contents spray everywhere"
if (src.loc != usr)
var/strength
var/percent = round((REAGENT_VOLUME(reagents, /singleton/reagent/blood) / volume) * 100) //the amount of blood changes the strength of spray
switch(percent)
if(1 to 9) strength = 2
if(10 to 50) strength = 3
if(51 to INFINITY) strength = 4
for (var/j = 0, j < strength - 1, j++) //The number of separate splatters
spray_loop:
var/direction = pick(GLOB.alldirs)
var/target
for (var/i = 1, i < strength, i++) //The distance the splatters will travel from random direction
switch (direction)
if (NORTH)
target = locate(src.x, src.y+i, src.z)
if (SOUTH)
target = locate(src.x, src.y-i, src.z)
if (EAST)
target = locate(src.x+i, src.y, src.z)
if (WEST)
target = locate(src.x-i, src.y, src.z)
if (NORTHEAST)
target = locate(src.x+i, src.y+i, src.z)
if (NORTHWEST)
target = locate(src.x-i, src.y+i, src.z)
if (SOUTHEAST)
target = locate(src.x+i, src.y-i, src.z)
if (SOUTHWEST)
target = locate(src.x-i, src.y-i, src.z)
if (istype(get_turf(target), /turf/simulated/wall))
blood_splatter(target, null, 1)
break spray_loop
var/turf/base = get_turf(target)
for (var/atom/A in base) // Stops your blood spray if it meets a person, wall, door or window
if (istype(A, /mob/living/carbon/human/))
var/mob/living/carbon/human/K = A
K.bloody_body()
break spray_loop
if ((!istype(A, /obj/) && A.density) || istype(A, /obj/structure/window/) || istype(A, /obj/machinery/door/airlock/))
break spray_loop
blood_splatter(target, null, 1)
blood_splatter(src.loc, null, 1)
playsound(src.loc, 'sound/effects/splat.ogg', 50, 1, -6)
if(istype(H))
H.bloody_hands()
if (loc == usr)
H.bloody_body()
// Line below will do a check where the target bloodbag is located and create a new one accordingly
var/obj/item/reagent_containers/I = src.loc != usr ? new/obj/item/reagent_containers/blood/ripped(src.loc) : new/obj/item/reagent_containers/blood/ripped(usr.loc)
if (REAGENT_VOLUME(reagents, /singleton/reagent/blood))
I.add_blood()
var/atkmsg = SPAN_WARNING("\The [src] rips apart[atkmsg_filled]!")
user.visible_message(atkmsg)
qdel(src)
return
return
/obj/item/reagent_containers/blood/APlus
blood_type = "A+"
/obj/item/reagent_containers/blood/AMinus
blood_type = "A-"
/obj/item/reagent_containers/blood/BPlus
blood_type = "B+"
/obj/item/reagent_containers/blood/BMinus
blood_type = "B-"
/obj/item/reagent_containers/blood/OPlus
blood_type = "O+"
/obj/item/reagent_containers/blood/OMinus
blood_type = "O-"
/obj/item/reagent_containers/blood/sbs
blood_type = "SBS"
/obj/item/reagent_containers/blood/empty
name = "empty IV bag"
desc = "Seems pretty useless... Maybe if there were a way to fill it?"
icon_state = "bloodpack"
/obj/item/reagent_containers/blood/ripped
name = "ripped IV bag"
desc = "It's torn up and useless."
icon = 'icons/obj/bloodpack.dmi'
icon_state = "ripped"
volume = 0
/obj/item/reagent_containers/blood/ripped/attackby(obj/item/attacking_item, mob/user)
to_chat(user, SPAN_WARNING("You can't do anything further with this."))
return