Merge remote-tracking branch 'citadel/master' into combat_rework_experimental

This commit is contained in:
silicons
2020-09-19 15:56:13 -07:00
1781 changed files with 68073 additions and 31311 deletions
+106
View File
@@ -0,0 +1,106 @@
/datum/accent
/datum/accent/proc/modify_speech(list/speech_args, datum/source, mob/living/carbon/owner) //transforms the message in some way
return speech_args
/datum/accent/lizard/modify_speech(list/speech_args)
var/message = speech_args[SPEECH_MESSAGE]
var/static/regex/lizard_hiss = new("s+", "g")
var/static/regex/lizard_hiSS = new("S+", "g")
if(message[1] != "*")
message = lizard_hiss.Replace(message, "sss")
message = lizard_hiSS.Replace(message, "SSS")
speech_args[SPEECH_MESSAGE] = message
return speech_args
/datum/accent/fly/modify_speech(list/speech_args)
var/message = speech_args[SPEECH_MESSAGE]
var/static/regex/fly_buzz = new("z+", "g")
var/static/regex/fly_buZZ = new("Z+", "g")
if(message[1] != "*")
message = fly_buzz.Replace(message, "zzz")
message = fly_buZZ.Replace(message, "ZZZ")
speech_args[SPEECH_MESSAGE] = message
return speech_args
/datum/accent/abductor/modify_speech(list/speech_args, datum/source)
var/message = speech_args[SPEECH_MESSAGE]
var/mob/living/carbon/human/user = source
var/rendered = "<span class='abductor'><b>[user.name]:</b> [message]</span>"
user.log_talk(message, LOG_SAY, tag="abductor")
for(var/mob/living/carbon/human/H in GLOB.alive_mob_list)
var/obj/item/organ/tongue/T = H.getorganslot(ORGAN_SLOT_TONGUE)
if(!T || T.type != type)
continue
if(H.dna && H.dna.species.id == "abductor" && user.dna && user.dna.species.id == "abductor")
var/datum/antagonist/abductor/A = user.mind.has_antag_datum(/datum/antagonist/abductor)
if(!A || !(H.mind in A.team.members))
continue
to_chat(H, rendered)
for(var/mob/M in GLOB.dead_mob_list)
var/link = FOLLOW_LINK(M, user)
to_chat(M, "[link] [rendered]")
speech_args[SPEECH_MESSAGE] = ""
return speech_args
/datum/accent/zombie/modify_speech(list/speech_args)
var/message = speech_args[SPEECH_MESSAGE]
var/list/message_list = splittext(message, " ")
var/maxchanges = max(round(message_list.len / 1.5), 2)
for(var/i = rand(maxchanges / 2, maxchanges), i > 0, i--)
var/insertpos = rand(1, message_list.len - 1)
var/inserttext = message_list[insertpos]
if(!(copytext(inserttext, -3) == "..."))//3 == length("...")
message_list[insertpos] = inserttext + "..."
if(prob(20) && message_list.len > 3)
message_list.Insert(insertpos, "[pick("BRAINS", "Brains", "Braaaiinnnsss", "BRAAAIIINNSSS")]...")
speech_args[SPEECH_MESSAGE] = jointext(message_list, " ")
return speech_args
/datum/accent/alien/modify_speech(list/speech_args, datum/source)
playsound(source, "hiss", 25, 1, 1)
return speech_args
/datum/accent/fluffy/modify_speech(list/speech_args)
var/message = speech_args[SPEECH_MESSAGE]
if(message[1] != "*")
message = replacetext(message, "ne", "nye")
message = replacetext(message, "nu", "nyu")
message = replacetext(message, "na", "nya")
message = replacetext(message, "no", "nyo")
message = replacetext(message, "ove", "uv")
message = replacetext(message, "l", "w")
message = replacetext(message, "r", "w")
speech_args[SPEECH_MESSAGE] = lowertext(message)
return speech_args
/datum/accent/span
var/span_flag
/datum/accent/span/modify_speech(list/speech_args)
speech_args[SPEECH_SPANS] |= span_flag
return speech_args
//bone tongues either have the sans accent or the papyrus accent
/datum/accent/span/sans
span_flag = SPAN_SANS
/datum/accent/span/papyrus
span_flag = SPAN_PAPYRUS
/datum/accent/span/robot
span_flag = SPAN_ROBOT
/datum/accent/dullahan/modify_speech(list/speech_args, datum/source, mob/living/carbon/owner)
if(owner)
if(isdullahan(owner))
var/datum/species/dullahan/D = owner.dna.species
if(isobj(D.myhead.loc))
var/obj/O = D.myhead.loc
O.say(speech_args[SPEECH_MESSAGE])
speech_args[SPEECH_MESSAGE] = ""
return speech_args
+1 -1
View File
@@ -157,7 +157,7 @@
/datum/action/proc/ApplyIcon(obj/screen/movable/action_button/current_button, force = FALSE)
if(icon_icon && button_icon_state && ((current_button.button_icon_state != button_icon_state) || force))
current_button.cut_overlays(TRUE)
current_button.cut_overlays()
current_button.add_overlay(mutable_appearance(icon_icon, button_icon_state))
current_button.button_icon_state = button_icon_state
+17 -10
View File
@@ -431,25 +431,31 @@
if(!owner)
owner = M
/datum/ai_laws/proc/get_law_list(include_zeroth = 0, show_numbers = 1)
/**
* Generates a list of all laws on this datum, including rendered HTML tags if required
*
* Arguments:
* * include_zeroth - Operator that controls if law 0 or law 666 is returned in the set
* * show_numbers - Operator that controls if law numbers are prepended to the returned laws
* * render_html - Operator controlling if HTML tags are rendered on the returned laws
*/
/datum/ai_laws/proc/get_law_list(include_zeroth = FALSE, show_numbers = TRUE, render_html = TRUE)
var/list/data = list()
if (include_zeroth && devillaws && devillaws.len)
for(var/i in devillaws)
data += "[show_numbers ? "666:" : ""] <font color='#cc5500'>[i]</font>"
if (include_zeroth && devillaws)
for(var/law in devillaws)
data += "[show_numbers ? "666:" : ""] [render_html ? "<font color='#cc5500'>[law]</font>" : law]"
if (include_zeroth && zeroth)
data += "[show_numbers ? "0:" : ""] <font color='#ff0000'><b>[zeroth]</b></font>"
data += "[show_numbers ? "0:" : ""] [render_html ? "<font color='#ff0000'><b>[zeroth]</b></font>" : zeroth]"
for(var/law in hacked)
if (length(law) > 0)
var/num = ionnum()
data += "[show_numbers ? "[num]:" : ""] <font color='#660000'>[law]</font>"
data += "[show_numbers ? "[ionnum()]:" : ""] [render_html ? "<font color='#660000'>[law]</font>" : law]"
for(var/law in ion)
if (length(law) > 0)
var/num = ionnum()
data += "[show_numbers ? "[num]:" : ""] <font color='#547DFE'>[law]</font>"
data += "[show_numbers ? "[ionnum()]:" : ""] [render_html ? "<font color='#547DFE'>[law]</font>" : law]"
var/number = 1
for(var/law in inherent)
@@ -459,6 +465,7 @@
for(var/law in supplied)
if (length(law) > 0)
data += "[show_numbers ? "[number]:" : ""] <font color='#990099'>[law]</font>"
data += "[show_numbers ? "[number]:" : ""] [render_html ? "<font color='#990099'>[law]</font>" : law]"
number++
return data
+17 -20
View File
@@ -8,14 +8,14 @@
var/window_options = "can_close=1;can_minimize=1;can_maximize=0;can_resize=1;titlebar=1;" // window option is set using window_id
var/stylesheets[0]
var/scripts[0]
var/title_image
var/head_elements
var/body_elements
var/head_content = ""
var/content = ""
var/static/datum/asset/simple/namespaced/common/common_asset = get_asset_datum(/datum/asset/simple/namespaced/common)
/datum/browser/New(nuser, nwindow_id, ntitle = 0, nwidth = 0, nheight = 0, var/atom/nref = null)
/datum/browser/New(nuser, nwindow_id, ntitle = 0, nwidth = 0, nheight = 0, atom/nref = null)
user = nuser
window_id = nwindow_id
@@ -27,7 +27,6 @@
height = nheight
if (nref)
ref = nref
add_stylesheet("common", 'html/browser/common.css') // this CSS sheet is common to all UIs
/datum/browser/proc/add_head_content(nhead_content)
head_content = nhead_content
@@ -35,22 +34,21 @@
/datum/browser/proc/set_window_options(nwindow_options)
window_options = nwindow_options
/datum/browser/proc/set_title_image(ntitle_image)
//title_image = ntitle_image
/datum/browser/proc/add_stylesheet(name, file)
if(istype(name, /datum/asset/spritesheet))
var/datum/asset/spritesheet/sheet = name
stylesheets["spritesheet_[sheet.name].css"] = "data/spritesheets/[sheet.name]"
else
var/asset_name = "[name].css"
stylesheets[asset_name] = file
if(!SSassets.cache[asset_name])
register_asset(asset_name, file)
if (!SSassets.cache[asset_name])
SSassets.transport.register_asset(asset_name, file)
/datum/browser/proc/add_script(name, file)
scripts["[ckey(name)].js"] = file
register_asset("[ckey(name)].js", file)
SSassets.transport.register_asset("[ckey(name)].js", file)
/datum/browser/proc/set_content(ncontent)
content = ncontent
@@ -60,15 +58,13 @@
/datum/browser/proc/get_header()
var/file
head_content += "<link rel='stylesheet' type='text/css' href='[common_asset.get_url_mappings()["common.css"]]'>"
for (file in stylesheets)
head_content += "<link rel='stylesheet' type='text/css' href='[file]'>"
head_content += "<link rel='stylesheet' type='text/css' href='[SSassets.transport.get_asset_url(file)]'>"
for (file in scripts)
head_content += "<script type='text/javascript' src='[file]'></script>"
var/title_attributes = "class='uiTitle'"
if (title_image)
title_attributes = "class='uiTitle icon' style='background-image: url([title_image]);'"
head_content += "<script type='text/javascript' src='[SSassets.transport.get_asset_url(file)]'></script>"
return {"<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
@@ -79,7 +75,7 @@
</head>
<body scroll=auto>
<div class='uiWrapper'>
[title ? "<div class='uiTitleWrapper'><div [title_attributes]><tt>[title]</tt></div></div>" : ""]
[title ? "<div class='uiTitleWrapper'><div class='uiTitle'><tt>[title]</tt></div></div>" : ""]
<div class='uiContent'>
"}
//" This is here because else the rest of the file looks like a string in notepad++.
@@ -105,10 +101,11 @@
var/window_size = ""
if(width && height)
window_size = "size=[width]x[height];"
common_asset.send(user)
if(stylesheets.len)
send_asset_list(user, stylesheets)
SSassets.transport.send_assets(user, stylesheets)
if(scripts.len)
send_asset_list(user, scripts)
SSassets.transport.send_assets(user, scripts)
user << browse(get_content(), "window=[window_id];[window_size][window_options]")
if(use_onclose)
setup_onclose()
@@ -169,7 +166,7 @@
return Button3
//Same shit, but it returns the button number, could at some point support unlimited button amounts.
/proc/askuser(var/mob/User,Message, Title, Button1="Ok", Button2, Button3, StealFocus = 1, Timeout = 6000)
/proc/askuser(mob/User,Message, Title, Button1="Ok", Button2, Button3, StealFocus = 1, Timeout = 6000)
if (!istype(User))
if (istype(User, /client/))
var/client/C = User
@@ -188,7 +185,7 @@
var/selectedbutton = 0
var/stealfocus
/datum/browser/modal/New(nuser, nwindow_id, ntitle = 0, nwidth = 0, nheight = 0, var/atom/nref = null, StealFocus = 1, Timeout = 6000)
/datum/browser/modal/New(nuser, nwindow_id, ntitle = 0, nwidth = 0, nheight = 0, atom/nref = null, StealFocus = 1, Timeout = 6000)
..()
stealfocus = StealFocus
if (!StealFocus)
+1 -1
View File
@@ -67,7 +67,7 @@
H.apply_damage(source.force, BRUTE, BODY_ZONE_HEAD, wound_bonus=CANT_WOUND) // easy tiger, we'll get to that in a sec
var/obj/item/bodypart/slit_throat = H.get_bodypart(BODY_ZONE_HEAD)
if(slit_throat)
var/datum/wound/brute/cut/critical/screaming_through_a_slit_throat = new
var/datum/wound/slash/critical/screaming_through_a_slit_throat = new
screaming_through_a_slit_throat.apply_wound(slit_throat)
H.apply_status_effect(/datum/status_effect/neck_slice)
+1 -9
View File
@@ -76,19 +76,11 @@
return FALSE
if(M.is_flying())
return FALSE
if(ishuman(AM))
var/mob/living/carbon/human/H = AM
if(istype(H.belt, /obj/item/wormhole_jaunter))
var/obj/item/wormhole_jaunter/J = H.belt
//To freak out any bystanders
H.visible_message("<span class='boldwarning'>[H] falls into [parent]!</span>")
J.chasm_react(H)
return FALSE
return TRUE
/datum/component/chasm/proc/drop(atom/movable/AM)
//Make sure the item is still there after our sleep
if(!AM || QDELETED(AM))
if(!AM || QDELETED(AM) || SEND_SIGNAL(AM, COMSIG_MOVABLE_CHASM_DROP, src))
return
falling_atoms[AM] = (falling_atoms[AM] || 0) + 1
var/turf/T = target_turf
+11 -13
View File
@@ -203,9 +203,9 @@
Set var amt to the value current cycle req is pointing to, its amount of type we need to delete
Get var/surroundings list of things accessable to crafting by get_environment()
Check the type of the current cycle req
If its reagent then do a while loop, inside it try to locate() reagent containers, inside such containers try to locate needed reagent, if there isnt remove thing from surroundings
If its reagent then do a while loop, inside it try to locate() reagent containers, inside such containers try to locate needed reagent, if there isn't remove thing from surroundings
If there is enough reagent in the search result then delete the needed amount, create the same type of reagent with the same data var and put it into deletion list
If there isnt enough take all of that reagent from the container, put into deletion list, substract the amt var by the volume of reagent, remove the container from surroundings list and keep searching
If there isn't enough take all of that reagent from the container, put into deletion list, substract the amt var by the volume of reagent, remove the container from surroundings list and keep searching
While doing above stuff check deletion list if it already has such reagnet, if yes merge instead of adding second one
If its stack check if it has enough amount
If yes create new stack with the needed amount and put in into deletion list, substract taken amount from the stack
@@ -216,7 +216,7 @@
Then do a loop over parts var of the recipe
Do similar stuff to what we have done above, but now in deletion list, until the parts conditions are satisfied keep taking from the deletion list and putting it into parts list for return
After its done loop over deletion list and delete all the shit that wasnt taken by parts loop
After its done loop over deletion list and delete all the shit that wasn't taken by parts loop
del_reqs return the list of parts resulting object will receive as argument of CheckParts proc, on the atom level it will add them all to the contents, on all other levels it calls ..() and does whatever is needed afterwards but from contents list already
*/
@@ -323,9 +323,12 @@
if(user == parent)
ui_interact(user)
/datum/component/personal_crafting/ui_state(mob/user)
return GLOB.not_incapacitated_turf_state
//For the UI related things we're going to assume the user is a mob rather than typesetting it to an atom as the UI isn't generated if the parent is an atom
/datum/component/personal_crafting/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.not_incapacitated_turf_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/datum/component/personal_crafting/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
cur_category = categories[1]
if(islist(categories[cur_category]))
@@ -333,7 +336,7 @@
cur_subcategory = subcats[1]
else
cur_subcategory = CAT_NONE
ui = new(user, src, ui_key, "PersonalCrafting", "Crafting Menu", 700, 800, master_ui, state)
ui = new(user, src, "PersonalCrafting")
ui.open()
/datum/component/personal_crafting/ui_data(mob/user)
@@ -413,13 +416,8 @@
display_compact = !display_compact
. = TRUE
if("set_category")
if(!isnull(params["category"]))
cur_category = params["category"]
if(!isnull(params["subcategory"]))
if(params["subcategory"] == "0")
cur_subcategory = ""
else
cur_subcategory = params["subcategory"]
cur_category = params["category"]
cur_subcategory = params["subcategory"] || ""
. = TRUE
/datum/component/personal_crafting/proc/build_recipe_data(datum/crafting_recipe/R)
@@ -40,13 +40,23 @@
reqs = list(/obj/item/paper = 20)
category = CAT_CLOTHING
/datum/crafting_recipe/balaclavabreath
name = "Breathaclava"
result = /obj/item/clothing/mask/balaclava/breath
time = 10
reqs = list(/obj/item/clothing/mask/balaclava = 1,
/obj/item/clothing/mask/breath = 1)
category = CAT_CLOTHING
/datum/crafting_recipe/armwraps
name = "Armwraps"
result = /obj/item/clothing/gloves/fingerless/pugilist
time = 60
tools = list(TOOL_WIRECUTTER)
reqs = list(/obj/item/stack/sheet/cloth = 4,
/obj/item/stack/sheet/durathread = 2,
/obj/item/stack/sticky_tape = 2,
/obj/item/stack/sheet/leather = 2)
category = CAT_CLOTHING
@@ -263,6 +273,16 @@
time = 30
category = CAT_CLOTHING
/datum/crafting_recipe/twinsheath
name = "Twin Sword Sheath"
result = /obj/item/storage/belt/sabre/twin
reqs = list(/obj/item/stack/sheet/mineral/wood = 3,
/obj/item/stack/sheet/leather = 8)
tools = list(TOOL_WIRECUTTER)
time = 70
category = CAT_CLOTHING
/datum/crafting_recipe/durathread_reinforcement_kit
name = "Durathread Reinforcement Kit"
result = /obj/item/armorkit
@@ -2,6 +2,15 @@
//Large Objects//
/////////////////
/datum/crafting_recipe/plunger
name = "Plunger"
result = /obj/item/plunger
time = 1
reqs = list(/obj/item/stack/sheet/plastic = 1,
/obj/item/stack/sheet/mineral/wood = 1)
category = CAT_MISC
subcategory = CAT_TOOL
/datum/crafting_recipe/showercurtain
name = "Shower Curtains"
reqs = list(/obj/item/stack/sheet/cloth = 2,
@@ -111,6 +120,53 @@
category = CAT_MISC
always_availible = FALSE // Disabled til learned
/datum/crafting_recipe/furnace
name = "Sandstone Furnace"
result = /obj/structure/furnace
time = 300
reqs = list(/obj/item/stack/sheet/mineral/sandstone = 15,
/obj/item/stack/sheet/metal = 4,
/obj/item/stack/rods = 2)
tools = list(TOOL_CROWBAR)
subcategory = CAT_MISCELLANEOUS
category = CAT_MISC
/datum/crafting_recipe/tableanvil
name = "Table Anvil"
result = /obj/structure/anvil/obtainable/table
time = 300
reqs = list(/obj/item/stack/sheet/metal = 4,
/obj/item/stack/rods = 2)
tools = list(TOOL_SCREWDRIVER, TOOL_WRENCH, TOOL_WELDER)
subcategory = CAT_MISCELLANEOUS
category = CAT_MISC
/datum/crafting_recipe/sandvil
name = "Sandstone Anvil"
result = /obj/structure/anvil/obtainable/sandstone
time = 300
reqs = list(/obj/item/stack/sheet/mineral/sandstone = 24)
tools = list(TOOL_CROWBAR)
subcategory = CAT_MISCELLANEOUS
category = CAT_MISC
/datum/crafting_recipe/basaltblock
name = "Sintered Basalt Block"
result = /obj/item/basaltblock
time = 200
reqs = list(/obj/item/stack/ore/glass/basalt = 50)
tools = list(TOOL_WELDER)
subcategory = CAT_MISCELLANEOUS
category = CAT_MISC
/datum/crafting_recipe/basaltanvil
name = "Basalt Anvil"
result = /obj/structure/anvil/obtainable/basalt
time = 200
reqs = list(/obj/item/basaltblock = 5)
tools = list(TOOL_CROWBAR)
subcategory = CAT_MISCELLANEOUS
category = CAT_MISC
///////////////////
//Tools & Storage//
///////////////////
@@ -126,7 +182,7 @@
/datum/crafting_recipe/brute_pack
name = "Suture Pack"
result = /obj/item/stack/medical/suture/one
result = /obj/item/stack/medical/suture/five
time = 1
reqs = list(/obj/item/stack/medical/gauze = 1,
/datum/reagent/medicine/styptic_powder = 10)
@@ -135,7 +191,7 @@
/datum/crafting_recipe/burn_pack
name = "Regenerative Mesh"
result = /obj/item/stack/medical/mesh/one
result = /obj/item/stack/medical/mesh/five
time = 1
reqs = list(/obj/item/stack/medical/gauze = 1,
/datum/reagent/medicine/silver_sulfadiazine = 10)
@@ -166,6 +222,17 @@
subcategory = CAT_TOOL
category = CAT_MISC
/datum/crafting_recipe/toolboxhammer
name = "Toolbox Hammer"
result = /obj/item/melee/smith/hammer/toolbox
tools = list(TOOL_SCREWDRIVER, TOOL_WRENCH, TOOL_WELDER)
reqs = list(/obj/item/storage/toolbox = 1,
/obj/item/stack/sheet/metal = 4,
/obj/item/stack/rods = 2)
time = 40
subcategory = CAT_TOOL
category = CAT_MISC
/datum/crafting_recipe/papersack
name = "Paper Sack"
result = /obj/item/storage/box/papersack
@@ -188,7 +255,7 @@
result = /obj/item/screwdriver/bronze
reqs = list(/obj/item/screwdriver = 1,
/obj/item/stack/cable_coil = 10,
/obj/item/stack/tile/bronze = 1,
/obj/item/stack/sheet/bronze = 1,
/datum/reagent/water = 15)
time = 40
subcategory = CAT_TOOL
@@ -200,7 +267,7 @@
result = /obj/item/weldingtool/bronze
reqs = list(/obj/item/weldingtool = 1,
/obj/item/stack/cable_coil = 10,
/obj/item/stack/tile/bronze = 1,
/obj/item/stack/sheet/bronze = 1,
/datum/reagent/water = 15)
time = 40
subcategory = CAT_TOOL
@@ -212,7 +279,7 @@
result = /obj/item/wirecutters/bronze
reqs = list(/obj/item/wirecutters = 1,
/obj/item/stack/cable_coil = 10,
/obj/item/stack/tile/bronze = 1,
/obj/item/stack/sheet/bronze = 1,
/datum/reagent/water = 15)
time = 40
subcategory = CAT_TOOL
@@ -224,7 +291,7 @@
result = /obj/item/crowbar/bronze
reqs = list(/obj/item/crowbar = 1,
/obj/item/stack/cable_coil = 10,
/obj/item/stack/tile/bronze = 1,
/obj/item/stack/sheet/bronze = 1,
/datum/reagent/water = 15)
time = 40
subcategory = CAT_TOOL
@@ -236,7 +303,7 @@
result = /obj/item/wrench/bronze
reqs = list(/obj/item/wrench = 1,
/obj/item/stack/cable_coil = 10,
/obj/item/stack/tile/bronze = 1,
/obj/item/stack/sheet/bronze = 1,
/datum/reagent/water = 15)
time = 40
subcategory = CAT_TOOL
@@ -269,6 +336,19 @@
subcategory = CAT_TOOL
category = CAT_MISC
/datum/crafting_recipe/heretic/codex
name = "Codex Cicatrix"
result = /obj/item/forbidden_book
tools = list(/obj/item/pen)
reqs = list(/obj/item/paper = 5,
/obj/item/organ/eyes = 1,
/obj/item/organ/heart = 1,
/obj/item/stack/sheet/animalhide/human = 1)
time = 150
subcategory = CAT_MISCELLANEOUS
category = CAT_MISC
always_availible = FALSE
////////////
//Vehicles//
////////////
@@ -324,7 +404,7 @@
result = /obj/item/toy/sword/cx
subcategory = CAT_MISCELLANEOUS
category = CAT_MISC
/datum/crafting_recipe/catgirlplushie
name = "Catgirl Plushie"
reqs = list(/obj/item/toy/plush/hairball = 3)
@@ -336,6 +416,25 @@
//Unsorted//
////////////
/datum/crafting_recipe/stick
name = "Stick"
time = 30
reqs = list(/obj/item/stack/sheet/mineral/wood = 1)
result = /obj/item/stick
subcategory = CAT_MISCELLANEOUS
category = CAT_MISC
/datum/crafting_recipe/swordhilt
name = "Sword Hilt"
time = 30
reqs = list(/obj/item/stack/sheet/mineral/wood = 2)
result = /obj/item/swordhandle
subcategory = CAT_MISCELLANEOUS
category = CAT_MISC
/datum/crafting_recipe/blackcarpet
name = "Black Carpet"
reqs = list(/obj/item/stack/tile/carpet = 50, /obj/item/toy/crayon/black = 1)
+24 -14
View File
@@ -29,7 +29,6 @@
*/
/datum/component/embedded
dupe_mode = COMPONENT_DUPE_ALLOWED
var/obj/item/bodypart/limb
@@ -120,7 +119,7 @@
UnregisterSignal(weapon, list(COMSIG_MOVABLE_MOVED, COMSIG_PARENT_QDELETING))
if(overlay)
var/atom/A = parent
A.cut_overlay(overlay, TRUE)
UnregisterSignal(A,COMSIG_ATOM_UPDATE_OVERLAYS)
qdel(overlay)
return ..()
@@ -139,30 +138,36 @@
limb.embedded_objects |= weapon // on the inside... on the inside...
weapon.forceMove(victim)
RegisterSignal(weapon, list(COMSIG_MOVABLE_MOVED, COMSIG_PARENT_QDELETING), .proc/byeItemCarbon)
var/damage = 0
if(harmful)
victim.visible_message("<span class='danger'>[weapon] embeds itself in [victim]'s [limb.name]!</span>",ignored_mobs=victim)
to_chat(victim, "<span class='userdanger'>[weapon] embeds itself in your [limb.name]!</span>")
victim.throw_alert("embeddedobject", /obj/screen/alert/embeddedobject)
playsound(victim,'sound/weapons/bladeslice.ogg', 40)
weapon.add_mob_blood(victim)//it embedded itself in you, of course it's bloody!
var/damage = weapon.w_class * impact_pain_mult
limb.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage, wound_bonus=-30, sharpness = TRUE)
damage = weapon.w_class * impact_pain_mult
SEND_SIGNAL(victim, COMSIG_ADD_MOOD_EVENT, "embedded", /datum/mood_event/embedded)
else
victim.visible_message("<span class='danger'>[weapon] sticks itself to [victim]'s [limb.name]!</span>",ignored_mobs=victim)
to_chat(victim, "<span class='userdanger'>[weapon] sticks itself to your [limb.name]!</span>")
if(damage > 0)
var/armor = victim.run_armor_check(limb.body_zone, "melee", "Your armor has protected your [limb.name].", "Your armor has softened a hit to your [limb.name].",weapon.armour_penetration)
limb.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage, blocked=armor, sharpness = weapon.get_sharpness())
/// Called every time a carbon with a harmful embed moves, rolling a chance for the item to cause pain. The chance is halved if the carbon is crawling or walking.
/datum/component/embedded/proc/jostleCheck()
var/mob/living/carbon/victim = parent
var/chance = jostle_chance
var/damage = weapon.w_class * pain_mult
var/pain_chance_current = jostle_chance
if(victim.m_intent == MOVE_INTENT_WALK || !(victim.mobility_flags & MOBILITY_STAND))
chance *= 0.5
pain_chance_current *= 0.5
if(harmful && prob(chance))
var/damage = weapon.w_class * jostle_pain_mult
if(pain_stam_pct && IS_STAMCRIT(victim)) //if it's a less-lethal embed, give them a break if they're already stamcritted
pain_chance_current *= 0.2
damage *= 0.5
if(harmful && prob(pain_chance_current))
limb.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage, wound_bonus = CANT_WOUND)
to_chat(victim, "<span class='userdanger'>[weapon] embedded in your [limb.name] jostles and stings!</span>")
@@ -199,7 +204,7 @@
if(harmful)
var/damage = weapon.w_class * remove_pain_mult
limb.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage, sharpness=TRUE) //It hurts to rip it out, get surgery you dingus.
limb.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage, wound_bonus = CANT_WOUND) //It hurts to rip it out, get surgery you dingus.
victim.emote("scream")
victim.visible_message("<span class='notice'>[victim] successfully rips [weapon] out of [victim.p_their()] [limb.name]!</span>", "<span class='notice'>You successfully remove [weapon] from your [limb.name].</span>")
else
@@ -279,11 +284,13 @@
limb.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage, wound_bonus = CANT_WOUND)
to_chat(victim, "<span class='userdanger'>[weapon] embedded in your [limb.name] hurts!</span>")
if(prob(fall_chance))
var/fall_chance_current = fall_chance
if(victim.mobility_flags & ~MOBILITY_STAND)
fall_chance_current *= 0.2
if(prob(fall_chance_current))
fallOutCarbon()
////////////////////////////////////////
//////////////TURF PROCS////////////////
////////////////////////////////////////
@@ -319,7 +326,8 @@
var/matrix/M = matrix()
M.Translate(pixelX, pixelY)
overlay.transform = M
hit.add_overlay(overlay, TRUE)
RegisterSignal(hit,COMSIG_ATOM_UPDATE_OVERLAYS,.proc/apply_overlay)
hit.update_icon()
if(harmful)
hit.visible_message("<span class='danger'>[weapon] embeds itself in [hit]!</span>")
@@ -332,6 +340,8 @@
else
hit.visible_message("<span class='danger'>[weapon] sticks itself to [hit]!</span>")
/datum/component/embedded/proc/apply_overlay(atom/source, list/overlay_list)
overlay_list += overlay
/datum/component/embedded/proc/examineTurf(datum/source, mob/user, list/examine_list)
if(harmful)
+3 -2
View File
@@ -45,8 +45,9 @@
/datum/fantasy_affix/tactical/apply(datum/component/fantasy/comp, newName)
var/obj/item/master = comp.parent
master.AddElement(/datum/element/tactical)
comp.appliedElements += list(/datum/element/tactical)
var/list/dat = list(/datum/element/tactical)
master._AddElement(dat)
comp.appliedElements += list(dat)
return "tactical [newName]"
/datum/fantasy_affix/pyromantic
+4 -8
View File
@@ -80,19 +80,15 @@ GLOBAL_LIST_EMPTY(GPS_list)
to_chat(user, "<span class='notice'>[parent] is now tracking, and visible to other GPS devices.</span>")
tracking = TRUE
/datum/component/gps/item/ui_interact(mob/user, ui_key = "gps", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) // Remember to use the appropriate state.
/datum/component/gps/item/ui_interact(mob/user, datum/tgui/ui)
if(emped)
to_chat(user, "<span class='hear'>[parent] fizzles weakly.</span>")
return
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
// Variable window height, depending on how many GPS units there are
// to show, clamped to relatively safe range.
var/gps_window_height = clamp(325 + GLOB.GPS_list.len * 14, 325, 700)
ui = new(user, src, ui_key, "Gps", "Global Positioning System", 470, gps_window_height, master_ui, state) //width, height
ui = new(user, src, "Gps")
ui.open()
ui.set_autoupdate(state = updating)
ui.set_autoupdate(updating)
/datum/component/gps/item/ui_data(mob/user)
var/list/data = list()
+22
View File
@@ -0,0 +1,22 @@
// This used to be in paper.dm, it was some snowflake code that was
// used ONLY on april's fool. I moved it to a component so it could be
// used in other places
/datum/component/honkspam
dupe_mode = COMPONENT_DUPE_UNIQUE
var/spam_flag = FALSE
/datum/component/honkspam/Initialize()
if(!isitem(parent))
return COMPONENT_INCOMPATIBLE
RegisterSignal(parent, COMSIG_ITEM_ATTACK_SELF, .proc/interact)
/datum/component/honkspam/proc/reset_spamflag()
spam_flag = FALSE
/datum/component/honkspam/proc/interact(mob/user)
if(!spam_flag)
spam_flag = TRUE
var/obj/item/parent_item = parent
playsound(parent_item.loc, 'sound/items/bikehorn.ogg', 50, TRUE)
addtimer(CALLBACK(src, .proc/reset_spamflag), 2 SECONDS)
+91
View File
@@ -0,0 +1,91 @@
/**
* KILLER QUEEN
*
* Simple contact bomb component
* Blows up the first person to touch it.
*/
/datum/component/killerqueen
can_transfer = TRUE
/// strength of explosion on the touch-er. 0 to disable. usually only used if the normal explosion is disabled (this is the default).
var/ex_strength = EXPLODE_HEAVY
/// callback to invoke with (parent, victim) before standard detonation - useful for losing a reference to this component or implementing custom behavior. return FALSE to prevent explosion.
var/datum/callback/pre_explode
/// callback to invoke with (parent) when deleting without an explosion
var/datum/callback/failure
/// did we explode
var/exploded = FALSE
/// examine message
var/examine_message
/// light explosion radius
var/light = 0
/// heavy explosion radius
var/heavy = 0
/// dev explosion radius
var/dev = 0
/// flame explosion radius
var/flame = 0
/// only triggered by living mobs
var/living_only = TRUE
/datum/component/killerqueen/Initialize(ex_strength = EXPLODE_HEAVY, datum/callback/pre_explode, datum/callback/failure, examine_message, light = 0, heavy = 0, dev = 0, flame = 0, living_only = TRUE)
. = ..()
if(. & COMPONENT_INCOMPATIBLE)
return
if(!isatom(parent))
return COMPONENT_INCOMPATIBLE
src.ex_strength = ex_strength
src.pre_explode = pre_explode
src.failure = failure
src.examine_message = examine_message
src.light = light
src.heavy = heavy
src.dev = dev
src.flame = flame
src.living_only = living_only
/datum/component/killerqueen/Destroy()
if(!exploded)
failure?.Invoke(parent)
return ..()
/datum/component/killerqueen/RegisterWithParent()
. = ..()
RegisterSignal(parent, list(COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_ATTACK_PAW, COMSIG_ATOM_ATTACK_ANIMAL), .proc/touch_detonate)
RegisterSignal(parent, COMSIG_MOVABLE_BUMP, .proc/bump_detonate)
RegisterSignal(parent, COMSIG_PARENT_ATTACKBY, .proc/attackby_detonate)
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, .proc/on_examine)
/datum/component/killerqueen/UnregisterFromParent()
. = ..()
UnregisterSignal(parent, list(COMSIG_ATOM_ATTACK_ANIMAL, COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_ATTACK_PAW,
COMSIG_MOVABLE_BUMP, COMSIG_PARENT_ATTACKBY, COMSIG_PARENT_EXAMINE))
/datum/component/killerqueen/proc/attackby_detonate(datum/source, obj/item/I, mob/user)
detonate(user)
/datum/component/killerqueen/proc/bump_detonate(datum/source, atom/A)
detonate(A)
/datum/component/killerqueen/proc/touch_detonate(datum/source, mob/user)
detonate(user)
/datum/component/killerqueen/proc/on_examine(datum/source, mob/examiner, list/examine_return)
if(examine_message)
examine_return += examine_message
/datum/component/killerqueen/proc/detonate(atom/victim)
if(!isliving(victim) && living_only)
return
if(pre_explode && !pre_explode.Invoke(parent, victim))
return
if(ex_strength)
victim.ex_act(ex_strength)
if(light || heavy || dev || flame)
explosion(parent, dev, heavy, light, flame_range = flame)
else
var/turf/T = get_turf(parent)
playsound(T, 'sound/effects/explosion2.ogg', 200, 1)
new /obj/effect/temp_visual/explosion(T)
exploded = TRUE
qdel(src)
+87
View File
@@ -0,0 +1,87 @@
/**
The label component.
This component is used to manage labels applied by the hand labeler.
Atoms can only have one instance of this component, and therefore only one label at a time.
This is to avoid having names like "Backpack (label1) (label2) (label3)". This is annoying and abnoxious to read.
When a player clicks the atom with a hand labeler to apply a label, this component gets applied to it.
If the labeler is off, the component will be removed from it, and the label will be removed from its name.
*/
/datum/component/label
dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
/// The name of the label the player is applying to the parent.
var/label_name
/datum/component/label/Initialize(_label_name)
if(!isatom(parent))
return COMPONENT_INCOMPATIBLE
label_name = _label_name
apply_label()
/datum/component/label/RegisterWithParent()
RegisterSignal(parent, COMSIG_PARENT_ATTACKBY, .proc/OnAttackby)
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, .proc/Examine)
/datum/component/label/UnregisterFromParent()
UnregisterSignal(parent, list(COMSIG_PARENT_ATTACKBY, COMSIG_PARENT_EXAMINE))
/**
This proc will fire after the parent is hit by a hand labeler which is trying to apply another label.
Since the parent already has a label, it will remove the old one from the parent's name, and apply the new one.
*/
/datum/component/label/InheritComponent(datum/component/label/new_comp , i_am_original, _label_name)
remove_label()
if(new_comp)
label_name = new_comp.label_name
else
label_name = _label_name
apply_label()
/**
This proc will trigger when any object is used to attack the parent.
If the attacking object is not a hand labeler, it will return.
If the attacking object is a hand labeler it will restore the name of the parent to what it was before this component was added to it, and the component will be deleted.
Arguments:
* source: The parent.
* attacker: The object that is hitting the parent.
* user: The mob who is wielding the attacking object.
*/
/datum/component/label/proc/OnAttackby(datum/source, obj/item/attacker, mob/user)
// If the attacking object is not a hand labeler or its mode is 1 (has a label ready to apply), return.
// The hand labeler should be off (mode is 0), in order to remove a label.
var/obj/item/hand_labeler/labeler = attacker
if(!istype(labeler) || labeler.mode)
return
remove_label()
playsound(parent, 'sound/items/poster_ripped.ogg', 20, TRUE)
to_chat(user, "<span class='warning'>You remove the label from [parent].</span>")
qdel(src) // Remove the component from the object.
/**
This proc will trigger when someone examines the parent.
It will attach the text found in the body of the proc to the `examine_list` and display it to the player examining the parent.
Arguments:
* source: The parent.
* user: The mob exmaining the parent.
* examine_list: The current list of text getting passed from the parent's normal examine() proc.
*/
/datum/component/label/proc/Examine(datum/source, mob/user, list/examine_list)
examine_list += "<span class='notice'>It has a label with some words written on it. Use a hand labeler to remove it.</span>"
/// Applies a label to the name of the parent in the format of: "parent_name (label)"
/datum/component/label/proc/apply_label()
var/atom/owner = parent
owner.name += " ([label_name])"
/// Removes the label from the parent's name
/datum/component/label/proc/remove_label()
var/atom/owner = parent
owner.name = replacetext(owner.name, "([label_name])", "") // Remove the label text from the parent's name, wherever it's located.
owner.name = trim(owner.name) // Shave off any white space from the beginning or end of the parent's name.
+20
View File
@@ -307,6 +307,10 @@
/datum/component/mood/proc/HandleNutrition(mob/living/L)
if(isethereal(L))
HandleCharge(L)
if(HAS_TRAIT(L, TRAIT_NOHUNGER))
return FALSE //no mood events for nutrition
switch(L.nutrition)
if(NUTRITION_LEVEL_FULL to INFINITY)
add_event(null, "nutrition", /datum/mood_event/fat)
@@ -321,6 +325,22 @@
if(0 to NUTRITION_LEVEL_STARVING)
add_event(null, "nutrition", /datum/mood_event/starving)
/datum/component/mood/proc/HandleCharge(mob/living/carbon/human/H)
var/datum/species/ethereal/E = H.dna.species
switch(E.get_charge(H))
if(ETHEREAL_CHARGE_NONE to ETHEREAL_CHARGE_LOWPOWER)
add_event(null, "charge", /datum/mood_event/decharged)
if(ETHEREAL_CHARGE_LOWPOWER to ETHEREAL_CHARGE_NORMAL)
add_event(null, "charge", /datum/mood_event/lowpower)
if(ETHEREAL_CHARGE_NORMAL to ETHEREAL_CHARGE_ALMOSTFULL)
clear_event(null, "charge")
if(ETHEREAL_CHARGE_ALMOSTFULL to ETHEREAL_CHARGE_FULL)
add_event(null, "charge", /datum/mood_event/charged)
if(ETHEREAL_CHARGE_FULL to ETHEREAL_CHARGE_OVERLOAD)
add_event(null, "charge", /datum/mood_event/overcharged)
if(ETHEREAL_CHARGE_OVERLOAD to ETHEREAL_CHARGE_DANGEROUS)
add_event(null, "charge", /datum/mood_event/supercharged)
/datum/component/mood/proc/update_beauty(area/A)
if(A.outdoors) //if we're outside, we don't care.
clear_event(null, "area_beauty")
+1 -1
View File
@@ -1,4 +1,4 @@
//Thing meant for allowing datums and objects to access a NTnet network datum.
//Thing meant for allowing datums and objects to access an NTnet network datum.
/datum/proc/ntnet_receive(datum/netdata/data)
return
+47 -8
View File
@@ -1,3 +1,8 @@
// the following defines are used for [/datum/component/pellet_cloud/var/list/wound_info_by_part] to store the damage, wound_bonus, and bw_bonus for each bodypart hit
#define CLOUD_POSITION_DAMAGE 1
#define CLOUD_POSITION_W_BONUS 2
#define CLOUD_POSITION_BW_BONUS 3
/*
* This component is used when you want to create a bunch of shrapnel or projectiles (say, shrapnel from a fragmentation grenade, or buckshot from a shotgun) from a central point,
* without necessarily printing a separate message for every single impact. This component should be instantiated right when you need it (like the moment of firing), then activated
@@ -29,7 +34,10 @@
var/list/pellets = list()
/// An associated list with the atom hit as the key and how many pellets they've eaten for the value, for printing aggregate messages
var/list/targets_hit = list()
/// For grenades, any /mob/living's the grenade is moved onto, see [/datum/component/pellet_cloud/proc/handle_martyrs()]
/// Another associated list for hit bodyparts on carbons so we can track how much wounding potential we have for each bodypart
var/list/wound_info_by_part = list()
/// For grenades, any /mob/living's the grenade is moved onto, see [/datum/component/pellet_cloud/proc/handle_martyrs]
var/list/bodies
/// For grenades, tracking people who die covering a grenade for achievement purposes, see [/datum/component/pellet_cloud/proc/handle_martyrs()]
var/list/purple_hearts
@@ -64,6 +72,7 @@
/datum/component/pellet_cloud/Destroy(force, silent)
purple_hearts = null
pellets = null
wound_info_by_part = null
targets_hit = null
bodies = null
return ..()
@@ -187,10 +196,26 @@
break
///One of our pellets hit something, record what it was and check if we're done (terminated == num_pellets)
/datum/component/pellet_cloud/proc/pellet_hit(obj/item/projectile/P, atom/movable/firer, atom/target, Angle)
/datum/component/pellet_cloud/proc/pellet_hit(obj/item/projectile/P, atom/movable/firer, atom/target, Angle, hit_zone)
pellets -= P
terminated++
hits++
var/obj/item/bodypart/hit_part
if(iscarbon(target) && hit_zone)
var/mob/living/carbon/hit_carbon = target
hit_part = hit_carbon.get_bodypart(hit_zone)
if(hit_part)
target = hit_part
if(P.wound_bonus != CANT_WOUND) // handle wounding
// unfortunately, due to how pellet clouds handle finalizing only after every pellet is accounted for, that also means there might be a short delay in dealing wounds if one pellet goes wide
// while buckshot may reach a target or miss it all in one tick, we also have to account for possible ricochets that may take a bit longer to hit the target
if(isnull(wound_info_by_part[hit_part]))
wound_info_by_part[hit_part] = list(0, 0, 0)
wound_info_by_part[hit_part][CLOUD_POSITION_DAMAGE] += P.damage // these account for decay
wound_info_by_part[hit_part][CLOUD_POSITION_W_BONUS] += P.wound_bonus
wound_info_by_part[hit_part][CLOUD_POSITION_BW_BONUS] += P.bare_wound_bonus
P.wound_bonus = CANT_WOUND // actual wounding will be handled aggregate
targets_hit[target]++
if(targets_hit[target] == 1)
RegisterSignal(target, COMSIG_PARENT_QDELETING, .proc/on_target_qdel, override=TRUE)
@@ -231,13 +256,23 @@
for(var/atom/target in targets_hit)
var/num_hits = targets_hit[target]
UnregisterSignal(target, COMSIG_PARENT_QDELETING)
if(num_hits > 1)
target.visible_message("<span class='danger'>[target] is hit by [num_hits] [proj_name]s!</span>", null, null, COMBAT_MESSAGE_RANGE, target)
to_chat(target, "<span class='userdanger'>You're hit by [num_hits] [proj_name]s!</span>")
else
target.visible_message("<span class='danger'>[target] is hit by a [proj_name]!</span>", null, null, COMBAT_MESSAGE_RANGE, target)
to_chat(target, "<span class='userdanger'>You're hit by a [proj_name]!</span>")
var/obj/item/bodypart/hit_part
if(isbodypart(target))
hit_part = target
target = hit_part.owner
var/damage_dealt = wound_info_by_part[hit_part][CLOUD_POSITION_DAMAGE]
var/w_bonus = wound_info_by_part[hit_part][CLOUD_POSITION_W_BONUS]
var/bw_bonus = wound_info_by_part[hit_part][CLOUD_POSITION_BW_BONUS]
var/wound_type = (initial(P.damage_type) == BRUTE) ? WOUND_BLUNT : WOUND_BURN // sharpness is handled in the wound rolling
wound_info_by_part[hit_part] = null
hit_part.painless_wound_roll(wound_type, damage_dealt, w_bonus, bw_bonus, initial(P.sharpness))
if(num_hits > 1)
target.visible_message("<span class='danger'>[target] is hit by [num_hits] [proj_name]s[hit_part ? " in the [hit_part.name]" : ""]!</span>", null, null, COMBAT_MESSAGE_RANGE, target)
to_chat(target, "<span class='userdanger'>You're hit by [num_hits] [proj_name]s[hit_part ? " in the [hit_part.name]" : ""]!</span>")
else
target.visible_message("<span class='danger'>[target] is hit by a [proj_name][hit_part ? " in the [hit_part.name]" : ""]!</span>", null, null, COMBAT_MESSAGE_RANGE, target)
to_chat(target, "<span class='userdanger'>You're hit by a [proj_name][hit_part ? " in the [hit_part.name]" : ""]!</span>")
UnregisterSignal(parent, COMSIG_PARENT_PREQDELETED)
if(queued_delete)
qdel(parent)
@@ -281,3 +316,7 @@
targets_hit -= target
bodies -= target
purple_hearts -= target
#undef CLOUD_POSITION_DAMAGE
#undef CLOUD_POSITION_W_BONUS
#undef CLOUD_POSITION_BW_BONUS
@@ -0,0 +1,215 @@
/datum/component/plumbing
///Index with "1" = /datum/ductnet/theductpointingnorth etc. "1" being the num2text from NORTH define
var/list/datum/ductnet/ducts = list()
///shortcut to our parents' reagent holder
var/datum/reagents/reagents
///TRUE if we wanna add proper pipe outless under our parent object. this is pretty good if i may so so myself
var/use_overlays = TRUE
///We can't just cut all of the parents' overlays, so we'll track them here
var/list/image/ducterlays
///directions in wich we act as a supplier
var/supply_connects
///direction in wich we act as a demander
var/demand_connects
///FALSE to pretty much just not exist in the plumbing world so we can be moved, TRUE to go plumbo mode
var/active = FALSE
///if TRUE connects will spin with the parent object visually and codually, so you can have it work in any direction. FALSE if you want it to be static
var/turn_connects = TRUE
/datum/component/plumbing/Initialize(start=TRUE, _turn_connects=TRUE) //turn_connects for wheter or not we spin with the object to change our pipes
if(parent && !istype(parent, /atom/movable))
return COMPONENT_INCOMPATIBLE
var/atom/movable/AM = parent
if(!AM.reagents)
return COMPONENT_INCOMPATIBLE
reagents = AM.reagents
turn_connects = _turn_connects
RegisterSignal(parent, list(COMSIG_MOVABLE_MOVED,COMSIG_PARENT_PREQDELETED), .proc/disable)
RegisterSignal(parent, list(COMSIG_OBJ_DEFAULT_UNFASTEN_WRENCH), .proc/toggle_active)
if(start)
enable()
if(use_overlays)
create_overlays()
/datum/component/plumbing/process()
if(!demand_connects || !reagents)
STOP_PROCESSING(SSfluids, src)
return
if(reagents.total_volume < reagents.maximum_volume)
for(var/D in GLOB.cardinals)
if(D & demand_connects)
send_request(D)
///Can we be added to the ductnet?
/datum/component/plumbing/proc/can_add(datum/ductnet/D, dir)
if(!active)
return
if(!dir || !D)
return FALSE
if(num2text(dir) in ducts)
return FALSE
return TRUE
///called from in process(). only calls process_request(), but can be overwritten for children with special behaviour
/datum/component/plumbing/proc/send_request(dir)
process_request(amount = MACHINE_REAGENT_TRANSFER, reagent = null, dir = dir)
///check who can give us what we want, and how many each of them will give us
/datum/component/plumbing/proc/process_request(amount, reagent, dir)
var/list/valid_suppliers = list()
var/datum/ductnet/net
if(!ducts.Find(num2text(dir)))
return
net = ducts[num2text(dir)]
for(var/A in net.suppliers)
var/datum/component/plumbing/supplier = A
if(supplier.can_give(amount, reagent, net))
valid_suppliers += supplier
for(var/A in valid_suppliers)
var/datum/component/plumbing/give = A
give.transfer_to(src, amount / valid_suppliers.len, reagent, net)
///returns TRUE when they can give the specified amount and reagent. called by process request
/datum/component/plumbing/proc/can_give(amount, reagent, datum/ductnet/net)
if(amount <= 0)
return
if(reagent) //only asked for one type of reagent
for(var/A in reagents.reagent_list)
var/datum/reagent/R = A
if(R.type == reagent)
return TRUE
else if(reagents.total_volume > 0) //take whatever
return TRUE
///this is where the reagent is actually transferred and is thus the finish point of our process()
/datum/component/plumbing/proc/transfer_to(datum/component/plumbing/target, amount, reagent, datum/ductnet/net)
if(!reagents || !target || !target.reagents)
return FALSE
if(reagent)
reagents.trans_id_to(target.parent, reagent, amount)
else
reagents.trans_to(target.parent, amount)
///We create our luxurious piping overlays/underlays, to indicate where we do what. only called once if use_overlays = TRUE in Initialize()
/datum/component/plumbing/proc/create_overlays()
var/atom/movable/AM = parent
for(var/image/I in ducterlays)
AM.overlays.Remove(I)
qdel(I)
ducterlays = list()
for(var/D in GLOB.cardinals)
var/color
var/direction
if(D & demand_connects)
color = "red" //red because red is mean and it takes
else if(D & supply_connects)
color = "blue" //blue is nice and gives
else
continue
var/image/I
if(turn_connects)
switch(D)
if(NORTH)
direction = "north"
if(SOUTH)
direction = "south"
if(EAST)
direction = "east"
if(WEST)
direction = "west"
I = image('icons/obj/plumbing/plumbers.dmi', "[direction]-[color]", layer = AM.layer - 1)
else
I = image('icons/obj/plumbing/plumbers.dmi', color, layer = AM.layer - 1) //color is not color as in the var, it's just the name
I.dir = D
AM.add_overlay(I)
ducterlays += I
///we stop acting like a plumbing thing and disconnect if we are, so we can safely be moved and stuff
/datum/component/plumbing/proc/disable()
if(!active)
return
STOP_PROCESSING(SSfluids, src)
for(var/A in ducts)
var/datum/ductnet/D = ducts[A]
D.remove_plumber(src)
active = FALSE
for(var/D in GLOB.cardinals)
if(D & (demand_connects | supply_connects))
for(var/obj/machinery/duct/duct in get_step(parent, D))
duct.attempt_connect()
///settle wherever we are, and start behaving like a piece of plumbing
/datum/component/plumbing/proc/enable()
if(active)
return
update_dir()
active = TRUE
var/atom/movable/AM = parent
for(var/obj/machinery/duct/D in AM.loc) //Destroy any ducts under us. Ducts also self destruct if placed under a plumbing machine. machines disable when they get moved
if(D.anchored) //that should cover everything
D.disconnect_duct()
if(demand_connects)
START_PROCESSING(SSfluids, src)
for(var/D in GLOB.cardinals)
if(D & (demand_connects | supply_connects))
for(var/atom/movable/A in get_step(parent, D))
if(istype(A, /obj/machinery/duct))
var/obj/machinery/duct/duct = A
duct.attempt_connect()
else
var/datum/component/plumbing/P = A.GetComponent(/datum/component/plumbing)
if(P)
direct_connect(P, D)
/// Toggle our machinery on or off. This is called by a hook from default_unfasten_wrench with anchored as only param, so we dont have to copypaste this on every object that can move
/datum/component/plumbing/proc/toggle_active(obj/O, new_state)
if(new_state)
enable()
else
disable()
/** We update our connects only when we settle down by taking our current and original direction to find our new connects
* If someone wants it to fucking spin while connected to something go actually knock yourself out
*/
/datum/component/plumbing/proc/update_dir()
if(!turn_connects)
return
var/atom/movable/AM = parent
var/new_demand_connects
var/new_supply_connects
var/new_dir = AM.dir
var/angle = 180 - dir2angle(new_dir)
if(new_dir == SOUTH)
demand_connects = initial(demand_connects)
supply_connects = initial(supply_connects)
else
for(var/D in GLOB.cardinals)
if(D & initial(demand_connects))
new_demand_connects += turn(D, angle)
if(D & initial(supply_connects))
new_supply_connects += turn(D, angle)
demand_connects = new_demand_connects
supply_connects = new_supply_connects
///Give the direction of a pipe, and it'll return wich direction it originally was when it's object pointed SOUTH
/datum/component/plumbing/proc/get_original_direction(dir)
var/atom/movable/AM = parent
return turn(dir, dir2angle(AM.dir) - 180)
//special case in-case we want to connect directly with another machine without a duct
/datum/component/plumbing/proc/direct_connect(datum/component/plumbing/P, dir)
if(!P.active)
return
var/opposite_dir = turn(dir, 180)
if(P.demand_connects & opposite_dir && supply_connects & dir || P.supply_connects & opposite_dir && demand_connects & dir) //make sure we arent connecting two supplies or demands
var/datum/ductnet/net = new()
net.add_plumber(src, dir)
net.add_plumber(P, opposite_dir)
///has one pipe input that only takes, example is manual output pipe
/datum/component/plumbing/simple_demand
demand_connects = NORTH
///has one pipe output that only supplies. example is liquid pump and manual input pipe
/datum/component/plumbing/simple_supply
supply_connects = NORTH
///input and output, like a holding tank
/datum/component/plumbing/tank
demand_connects = WEST
supply_connects = EAST
@@ -0,0 +1,21 @@
/datum/component/plumbing/acclimator
demand_connects = WEST
supply_connects = EAST
var/obj/machinery/plumbing/acclimator/AC
/datum/component/plumbing/acclimator/Initialize(start=TRUE, _turn_connects=TRUE)
. = ..()
if(!istype(parent, /obj/machinery/plumbing/acclimator))
return COMPONENT_INCOMPATIBLE
AC = parent
/datum/component/plumbing/acclimator/can_give(amount, reagent)
. = ..()
if(. && AC.emptying)
return TRUE
return FALSE
///We're overriding process and not send_request, because all process does is do the requests, so we might aswell cut out the middle man and save some code from running
/datum/component/plumbing/acclimator/process()
if(AC.emptying)
return
. = ..()
+59
View File
@@ -0,0 +1,59 @@
///The magical plumbing component used by the chemical filters. The different supply connects behave differently depending on the filters set on the chemical filter
/datum/component/plumbing/filter
demand_connects = NORTH
supply_connects = SOUTH | EAST | WEST //SOUTH is straight, EAST is left and WEST is right. We look from the perspective of the insert
/datum/component/plumbing/filter/Initialize()
. = ..()
if(!istype(parent, /obj/machinery/plumbing/filter))
return COMPONENT_INCOMPATIBLE
/datum/component/plumbing/filter/can_give(amount, reagent, datum/ductnet/net)
. = ..()
if(.)
var/direction
for(var/A in ducts)
if(ducts[A] == net)
direction = get_original_direction(text2num(A)) //we need it relative to the direction, so filters don't change when we turn the filter
break
if(!direction)
return FALSE
if(reagent)
if(!can_give_in_direction(direction, reagent))
return FALSE
/datum/component/plumbing/filter/transfer_to(datum/component/plumbing/target, amount, reagent, datum/ductnet/net)
if(!reagents || !target || !target.reagents)
return FALSE
var/direction
for(var/A in ducts)
if(ducts[A] == net)
direction = get_original_direction(text2num(A))
break
if(reagent)
reagents.trans_id_to(target.parent, reagent, amount)
else
for(var/A in reagents.reagent_list)
var/datum/reagent/R = A
if(!can_give_in_direction(direction, R.type))
continue
var/new_amount
if(R.volume < amount)
new_amount = amount - R.volume
reagents.trans_id_to(target.parent, R.type, amount)
amount = new_amount
if(amount <= 0)
break
///We check if the direction and reagent are valid to give. Needed for filters since different outputs have different behaviours
/datum/component/plumbing/filter/proc/can_give_in_direction(dir, reagent)
var/obj/machinery/plumbing/filter/F = parent
switch(dir)
if(SOUTH) //straight
if(!F.left.Find(reagent) && !F.right.Find(reagent))
return TRUE
if(WEST) //right
if(F.right.Find(reagent))
return TRUE
if(EAST) //left
if(F.left.Find(reagent))
return TRUE
@@ -0,0 +1,38 @@
/datum/component/plumbing/reaction_chamber
demand_connects = WEST
supply_connects = EAST
/datum/component/plumbing/reaction_chamber/Initialize(start=TRUE, _turn_connects=TRUE)
. = ..()
if(!istype(parent, /obj/machinery/plumbing/reaction_chamber))
return COMPONENT_INCOMPATIBLE
/datum/component/plumbing/reaction_chamber/can_give(amount, reagent, datum/ductnet/net)
. = ..()
var/obj/machinery/plumbing/reaction_chamber/RC = parent
if(!. || !RC.emptying)
return FALSE
/datum/component/plumbing/reaction_chamber/send_request(dir)
var/obj/machinery/plumbing/reaction_chamber/RC = parent
if(RC.emptying || !LAZYLEN(RC.required_reagents))
return
for(var/RT in RC.required_reagents)
var/has_reagent = FALSE
for(var/A in reagents.reagent_list)
var/datum/reagent/RD = A
if(RT == RD.type)
has_reagent = TRUE
if(RD.volume < RC.required_reagents[RT])
process_request(min(RC.required_reagents[RT] - RD.volume, MACHINE_REAGENT_TRANSFER) , RT, dir)
return
if(!has_reagent)
process_request(min(RC.required_reagents[RT], MACHINE_REAGENT_TRANSFER), RT, dir)
return
RC.reagent_flags &= ~NO_REACT
reagents.handle_reactions()
RC.emptying = TRUE //If we move this up, it'll instantly get turned off since any reaction always sets the reagent_total to zero. Other option is make the reaction update
//everything for every chemical removed, wich isn't a good option either.
RC.on_reagent_change() //We need to check it now, because some reactions leave nothing left.
@@ -0,0 +1,45 @@
/datum/component/plumbing/splitter
demand_connects = NORTH
supply_connects = SOUTH | EAST
/datum/component/plumbing/splitter/Initialize()
. = ..()
if(. && !istype(parent, /obj/machinery/plumbing/splitter))
return FALSE
/datum/component/plumbing/splitter/can_give(amount, reagent, datum/ductnet/net)
. = ..()
if(!.)
return
. = FALSE
var/direction
for(var/A in ducts)
if(ducts[A] == net)
direction = get_original_direction(text2num(A))
break
var/obj/machinery/plumbing/splitter/S = parent
switch(direction)
if(SOUTH)
if(S.turn_straight && S.transfer_straight <= amount)
S.turn_straight = FALSE
return TRUE
if(EAST)
if(!S.turn_straight && S.transfer_side <= amount)
S.turn_straight = TRUE
return TRUE
/datum/component/plumbing/splitter/transfer_to(datum/component/plumbing/target, amount, reagent, datum/ductnet/net)
var/direction
for(var/A in ducts)
if(ducts[A] == net)
direction = get_original_direction(text2num(A))
break
var/obj/machinery/plumbing/splitter/S = parent
switch(direction)
if(SOUTH)
if(amount >= S.transfer_straight)
amount = S.transfer_straight
if(EAST)
if(amount >= S.transfer_side)
amount = S.transfer_side
. = ..()
+15
View File
@@ -29,10 +29,18 @@
if(strength > RAD_MINIMUM_CONTAMINATION)
SSradiation.warn(src)
//Let's make er glow
//This relies on parent not being a turf or something. IF YOU CHANGE THAT, CHANGE THIS
var/atom/movable/master = parent
master.add_filter("rad_glow", 2, list("type" = "outline", "color" = "#39ff1430", "size" = 2))
addtimer(CALLBACK(src, .proc/glow_loop, master), rand(1,19))//Things should look uneven
START_PROCESSING(SSradiation, src)
/datum/component/radioactive/Destroy()
STOP_PROCESSING(SSradiation, src)
var/atom/movable/master = parent
master.remove_filter("rad_glow")
return ..()
/datum/component/radioactive/process()
@@ -46,6 +54,13 @@
if(strength <= RAD_BACKGROUND_RADIATION)
return PROCESS_KILL
/datum/component/radioactive/proc/glow_loop(atom/movable/master)
var/filter = master.get_filter("rad_glow")
if(filter)
animate(filter, alpha = 110, time = 15, loop = -1)
animate(alpha = 40, time = 25)
/datum/component/radioactive/InheritComponent(datum/component/C, i_am_original, _strength, _source, _half_life, _can_contaminate)
if(!i_am_original)
return
+18 -20
View File
@@ -14,6 +14,7 @@
var/mob/living/holder //who is currently benefiting from the shield.
var/dissipating = FALSE //Is this shield meant to dissipate over time instead of recharging.
var/del_on_overload = FALSE //will delete itself once it has no charges left.
var/cached_vis_overlay //text identifier of the visual overlay.
/datum/component/shielded/Initialize(current, max = 3, delay = 20 SECONDS, rate = 1, slots, state = "shield-old", broken, \
sound = 'sound/magic/charge.ogg', end_sound = 'sound/machines/ding.ogg', diss = FALSE, del_overload = FALSE)
@@ -47,9 +48,8 @@
holder = L
var/to_add = charges >= 1 ? shield_state : broken_state
if(to_add)
var/mutable_appearance/M = mutable_appearance('icons/effects/effects.dmi', to_add)
M.layer = (L.layer > MOB_LAYER ? L.layer : MOB_LAYER) + 0.01
holder.add_overlay(M, TRUE)
var/layer = (L.layer > MOB_LAYER ? L.layer : MOB_LAYER) + 0.01
SSvis_overlays.add_vis_overlay(L, 'icons/effects/effects.dmi', to_add, layer, GAME_PLANE, L.dir)
/datum/component/shielded/UnregisterFromParent()
. = ..()
@@ -57,9 +57,9 @@
UnregisterSignal(parent, list(COMSIG_ITEM_RUN_BLOCK,COMSIG_ITEM_CHECK_BLOCK,COMSIG_ITEM_EQUIPPED,COMSIG_ITEM_DROPPED))
if(holder)
UnregisterSignal(holder, list(COMSIG_LIVING_RUN_BLOCK, COMSIG_LIVING_GET_BLOCKING_ITEMS))
var/to_remove = charges >= 1 ? shield_state : broken_state
if(to_remove)
holder.cut_overlay(mutable_appearance('icons/effects/effects.dmi', to_remove), TRUE)
if(cached_vis_overlay)
SSvis_overlays.remove_vis_overlay(holder, cached_vis_overlay)
cached_vis_overlay = null
holder = null
/datum/component/shielded/process()
@@ -80,7 +80,7 @@
holder.visible_message("[holder]'s shield overloads!")
qdel(src)
return
if(holder && (old_charges < 1 && charges >= 1) || (!del_on_overload && old_charges >= 1 && charges < 1))
if(holder && ((old_charges < 1 && charges >= 1) || (!del_on_overload && old_charges >= 1 && charges < 1)))
update_shield_overlay(charges < 1)
/datum/component/shielded/proc/adjust_charges(amount)
@@ -93,20 +93,19 @@
holder.visible_message("[holder]'s shield overloads!")
qdel(src)
return
if(holder && (old_charges < 1 && charges >= 1) || (!del_on_overload && old_charges >= 1 && charges < 1))
if(holder && ((old_charges < 1 && charges >= 1) || (!del_on_overload && old_charges >= 1 && charges < 1)))
update_shield_overlay(charges < 1)
/datum/component/shielded/proc/update_shield_overlay(broken)
if(!holder)
return
var/to_remove = broken ? shield_state : broken_state
var/to_add = broken ? broken_state : shield_state
if(to_remove)
holder.cut_overlay(mutable_appearance('icons/effects/effects.dmi', to_remove), TRUE)
if(cached_vis_overlay)
SSvis_overlays.remove_vis_overlay(holder, cached_vis_overlay)
cached_vis_overlay = null
if(to_add)
var/mutable_appearance/M = mutable_appearance('icons/effects/effects.dmi', to_add)
M.layer = (holder.layer > MOB_LAYER ? holder.layer : MOB_LAYER) + 0.01
holder.add_overlay(M, TRUE)
var/layer = (holder.layer > MOB_LAYER ? holder.layer : MOB_LAYER) + 0.01
SSvis_overlays.add_vis_overlay(holder, 'icons/effects/effects.dmi', to_add, layer, GAME_PLANE, holder.dir)
/datum/component/shielded/proc/on_equip(obj/item/source, mob/living/equipper, slot)
if(!(accepted_slots & slotdefine2slotbit(slot)))
@@ -117,17 +116,16 @@
RegisterSignal(equipper, COMSIG_LIVING_GET_BLOCKING_ITEMS, .proc/include_shield)
var/to_add = charges >= 1 ? shield_state : broken_state
if(to_add)
var/mutable_appearance/M = mutable_appearance('icons/effects/effects.dmi', to_add)
M.layer = (holder.layer > MOB_LAYER ? holder.layer : MOB_LAYER) + 0.01
equipper.add_overlay(M, TRUE)
var/layer = (holder.layer > MOB_LAYER ? holder.layer : MOB_LAYER) + 0.01
cached_vis_overlay = SSvis_overlays.add_vis_overlay(holder, 'icons/effects/effects.dmi', to_add, layer, GAME_PLANE, holder.dir)
/datum/component/shielded/proc/on_drop(obj/item/source, mob/dropper)
if(holder == dropper)
UnregisterSignal(holder, COMSIG_LIVING_GET_BLOCKING_ITEMS)
UnregisterSignal(parent, list(COMSIG_ITEM_RUN_BLOCK, COMSIG_ITEM_CHECK_BLOCK))
var/to_remove = charges >= 1 ? shield_state : broken_state
if(to_remove)
holder.cut_overlay(mutable_appearance('icons/effects/effects.dmi', to_remove), TRUE)
if(cached_vis_overlay)
SSvis_overlays.remove_vis_overlay(holder, cached_vis_overlay)
cached_vis_overlay = null
holder = null
/datum/component/shielded/proc/include_shield(mob/source, list/items)
+24 -3
View File
@@ -11,6 +11,13 @@
// This is to stop squeak spam from inhand usage
var/last_use = 0
var/use_delay = 20
// squeak cooldowns
var/last_squeak = 0
var/squeak_delay = 5
/// chance we'll be stopped from squeaking by cooldown when something crossing us squeaks
var/cross_squeak_delay_chance = 33 // about 3 things can squeak at a time
/datum/component/squeak/Initialize(custom_sounds, volume_override, chance_override, step_delay_override, use_delay_override)
if(!isatom(parent))
@@ -19,6 +26,7 @@
if(ismovable(parent))
RegisterSignal(parent, list(COMSIG_MOVABLE_BUMP, COMSIG_MOVABLE_IMPACT), .proc/play_squeak)
RegisterSignal(parent, list(COMSIG_MOVABLE_CROSSED, COMSIG_ITEM_WEARERCROSSED), .proc/play_squeak_crossed)
RegisterSignal(parent, COMSIG_CROSS_SQUEAKED, .proc/delay_squeak)
RegisterSignal(parent, COMSIG_MOVABLE_DISPOSING, .proc/disposing_react)
if(isitem(parent))
RegisterSignal(parent, list(COMSIG_ITEM_ATTACK, COMSIG_ITEM_ATTACK_OBJ, COMSIG_ITEM_HIT_REACT), .proc/play_squeak)
@@ -39,20 +47,28 @@
use_delay = use_delay_override
/datum/component/squeak/proc/play_squeak()
do_play_squeak()
/datum/component/squeak/proc/do_play_squeak(bypass_cooldown = FALSE)
if(!bypass_cooldown && ((last_squeak + squeak_delay) >= world.time))
return FALSE
if(prob(squeak_chance))
if(!override_squeak_sounds)
playsound(parent, pickweight(default_squeak_sounds), volume, 1, -1)
else
playsound(parent, pickweight(override_squeak_sounds), volume, 1, -1)
last_squeak = world.time
return TRUE
return FALSE
/datum/component/squeak/proc/step_squeak()
if(steps > step_delay)
play_squeak()
do_play_squeak(TRUE)
steps = 0
else
steps++
/datum/component/squeak/proc/play_squeak_crossed(atom/movable/AM)
/datum/component/squeak/proc/play_squeak_crossed(datum/source, atom/movable/AM)
if(isitem(AM))
var/obj/item/I = AM
if(I.item_flags & ABSTRACT)
@@ -63,13 +79,18 @@
return
var/atom/current_parent = parent
if(isturf(current_parent.loc))
play_squeak()
if(do_play_squeak())
SEND_SIGNAL(AM, COMSIG_CROSS_SQUEAKED)
/datum/component/squeak/proc/use_squeak()
if(last_use + use_delay < world.time)
last_use = world.time
play_squeak()
/datum/component/squeak/proc/delay_squeak()
if(prob(cross_squeak_delay_chance))
last_squeak = world.time
/datum/component/squeak/proc/on_equip(datum/source, mob/equipper, slot)
RegisterSignal(equipper, COMSIG_MOVABLE_DISPOSING, .proc/disposing_react, TRUE)
+2
View File
@@ -105,6 +105,8 @@
/// Triggered on attack self of the item containing the component
/datum/component/two_handed/proc/on_attack_self(datum/source, mob/user)
if(!user.is_holding(parent))
return //give no quarter to telekinesis powergaemrs (telekinetic wielding will desync the offhand and result in all sorts of bugs so no until someone codes it properly)
if(wielded)
unwield(user)
else
+20 -12
View File
@@ -29,8 +29,8 @@ GLOBAL_LIST_EMPTY(uplinks)
var/saved_player_population = 0
var/list/filters = list()
/datum/component/uplink/Initialize(_owner, _lockable = TRUE, _enabled = FALSE, datum/game_mode/_gamemode, starting_tc = 20, datum/ui_state/_checkstate, datum/traitor_class/traitor_class)
/datum/component/uplink/Initialize(_owner, _lockable = TRUE, _enabled = FALSE, datum/game_mode/_gamemode, starting_tc = 20, datum/traitor_class/traitor_class)
if(!isitem(parent))
return COMPONENT_INCOMPATIBLE
@@ -143,23 +143,21 @@ GLOBAL_LIST_EMPTY(uplinks)
// an unlocked uplink blocks also opening the PDA or headset menu
return COMPONENT_NO_INTERACT
/datum/component/uplink/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.inventory_state)
/datum/component/uplink/ui_state(mob/user)
if(istype(parent, /obj/item/implant/uplink))
return GLOB.not_incapacitated_state
return GLOB.inventory_state
/datum/component/uplink/ui_interact(mob/user, datum/tgui/ui)
active = TRUE
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "Uplink", name, 620, 580, master_ui, state)
ui = new(user, src, "Uplink", name)
// This UI is only ever opened by one person,
// and never is updated outside of user input.
ui.set_autoupdate(FALSE)
ui.open()
/datum/component/uplink/ui_host(mob/user)
if(istype(parent, /obj/item/implant)) //implants are like organs, not really located inside mobs codewise.
var/obj/item/implant/I = parent
return I.imp_in
return ..()
/datum/component/uplink/ui_data(mob/user)
if(!user.mind)
return
@@ -187,8 +185,18 @@ GLOBAL_LIST_EMPTY(uplinks)
is_inaccessible = FALSE
if(is_inaccessible)
continue
/*
if(I.restricted_species) //catpeople specfic gloves.
if(ishuman(user))
var/is_inaccessible = TRUE
var/mob/living/carbon/human/H = user
for(var/F in I.restricted_species)
if(F == H.dna.species.id || debug)
is_inaccessible = FALSE
break
if(is_inaccessible)
continue
*/
cat["items"] += list(list(
"name" = I.name,
"cost" = I.cost,
+42 -6
View File
@@ -1,13 +1,13 @@
//TODO: someone please get rid of this shit
/datum/datacore
var/medical[] = list()
var/list/medical = list()
var/medicalPrintCount = 0
var/general[] = list()
var/security[] = list()
var/list/general = list()
var/list/security = list()
var/securityPrintCount = 0
var/securityCrimeCounter = 0
//This list tracks characters spawned in the world and cannot be modified in-game. Currently referenced by respawn_character().
var/locked[] = list()
///This list tracks characters spawned in the world and cannot be modified in-game. Currently referenced by respawn_character().
var/list/locked = list()
/datum/data
var/name = "data"
@@ -91,6 +91,42 @@
if(foundrecord)
foundrecord.fields["rank"] = assignment
/datum/datacore/proc/get_manifest_tg() //copypasted from tg, renamed to avoid namespace conflicts
var/list/manifest_out = list()
var/list/departments = list(
"Command" = GLOB.command_positions,
"Security" = GLOB.security_positions,
"Engineering" = GLOB.engineering_positions,
"Medical" = GLOB.medical_positions,
"Science" = GLOB.science_positions,
"Supply" = GLOB.supply_positions,
"Service" = GLOB.civilian_positions,
"Silicon" = GLOB.nonhuman_positions
)
for(var/datum/data/record/t in GLOB.data_core.general)
var/name = t.fields["name"]
var/rank = t.fields["rank"]
var/has_department = FALSE
for(var/department in departments)
var/list/jobs = departments[department]
if(rank in jobs)
if(!manifest_out[department])
manifest_out[department] = list()
manifest_out[department] += list(list(
"name" = name,
"rank" = rank
))
has_department = TRUE
break
if(!has_department)
if(!manifest_out["Misc"])
manifest_out["Misc"] = list()
manifest_out["Misc"] += list(list(
"name" = name,
"rank" = rank
))
return manifest_out
/datum/datacore/proc/get_manifest(monochrome, OOC)
var/list/heads = list()
var/list/sec = list()
+3
View File
@@ -31,6 +31,9 @@
VV_DROPDOWN_OPTION(VV_HK_EXPOSE, "Show VV To Player")
VV_DROPDOWN_OPTION(VV_HK_ADDCOMPONENT, "Add Component/Element")
VV_DROPDOWN_OPTION(VV_HK_MODIFY_TRAITS, "Modify Traits")
#ifdef REFERENCE_TRACKING
VV_DROPDOWN_OPTION(VV_HK_VIEW_REFERENCES, "View References")
#endif
//This proc is only called if everything topic-wise is verified. The only verifications that should happen here is things like permission checks!
//href_list is a reference, modifying it in these procs WILL change the rest of the proc in topic.dm of admin/view_variables!
@@ -113,9 +113,9 @@ Bonus
symptom_delay_max = 90
var/chems = FALSE
var/explosion_power = 1
threshold_desc = "<b>Resistance 9:</b> Doubles the intensity of the effect, but reduces its frequency.<br>\
<b>Stage Speed 8:</b> Increases explosion radius when the host is wet.<br>\
<b>Transmission 8:</b> Additionally synthesizes chlorine trifluoride and napalm inside the host."
threshold_desc = list("Resistance 9" = "Doubles the intensity of the effect, but reduces its frequency.",
"Stage Speed 8" = "Increases explosion radius when the host is wet.",
"Transmission 8" = "Additionally synthesizes chlorine trifluoride and napalm inside the host.")
/datum/symptom/alkali/Start(datum/disease/advance/A)
if(!..())
@@ -31,4 +31,3 @@
/datum/symptom/inorganic_adaptation/OnRemove(datum/disease/advance/A)
A.infectable_biotypes &= ~MOB_MINERAL
+4 -2
View File
@@ -50,6 +50,7 @@
destination.dna.skin_tone_override = skin_tone_override
destination.dna.features = features.Copy()
destination.set_species(species.type, icon_update=0)
destination.dna.species.say_mod = species.say_mod
destination.dna.real_name = real_name
destination.dna.nameless = nameless
destination.dna.custom_species = custom_species
@@ -74,6 +75,7 @@
new_dna.skin_tone_override = skin_tone_override
new_dna.features = features.Copy()
new_dna.species = new species.type
new_dna.species.say_mod = species.say_mod
new_dna.real_name = real_name
new_dna.nameless = nameless
new_dna.custom_species = custom_species
@@ -135,10 +137,10 @@
L[DNA_COLOR_TWO_BLOCK] = sanitize_hexcolor(features["mcolor2"], 6)
L[DNA_COLOR_THREE_BLOCK] = sanitize_hexcolor(features["mcolor3"], 6)
if(!GLOB.mam_tails_list.len)
init_sprite_accessory_subtypes(/datum/sprite_accessory/mam_tails, GLOB.mam_tails_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/mam_tails, GLOB.mam_tails_list)
L[DNA_MUTANTTAIL_BLOCK] = construct_block(GLOB.mam_tails_list.Find(features["mam_tail"]), GLOB.mam_tails_list.len)
if(!GLOB.mam_ears_list.len)
init_sprite_accessory_subtypes(/datum/sprite_accessory/mam_ears, GLOB.mam_ears_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/ears/mam_ears, GLOB.mam_ears_list)
L[DNA_MUTANTEAR_BLOCK] = construct_block(GLOB.mam_ears_list.Find(features["mam_ears"]), GLOB.mam_ears_list.len)
if(!GLOB.mam_body_markings_list.len)
init_sprite_accessory_subtypes(/datum/sprite_accessory/mam_body_markings, GLOB.mam_body_markings_list)
+65
View File
@@ -0,0 +1,65 @@
///We handle the unity part of plumbing. We track who is connected to who.
/datum/ductnet
var/list/suppliers = list()
var/list/demanders = list()
var/list/obj/machinery/duct/ducts = list()
var/capacity
///Add a duct to our network
/datum/ductnet/proc/add_duct(obj/machinery/duct/D)
if(!D || (D in ducts))
return
ducts += D
D.duct = src
///Remove a duct from our network and commit suicide, because this is probably easier than to check who that duct was connected to and what part of us was lost
/datum/ductnet/proc/remove_duct(obj/machinery/duct/ducting)
destroy_network(FALSE)
for(var/obj/machinery/duct/D in ducting.neighbours)
addtimer(CALLBACK(D, /obj/machinery/duct/proc/reconnect), 0) //all needs to happen after the original duct that was destroyed finishes destroying itself
addtimer(CALLBACK(D, /obj/machinery/duct/proc/generate_connects), 0)
qdel(src)
///add a plumbing object to either demanders or suppliers
/datum/ductnet/proc/add_plumber(datum/component/plumbing/P, dir)
if(!P.can_add(src, dir))
return FALSE
P.ducts[num2text(dir)] = src
if(dir & P.supply_connects)
suppliers += P
else if(dir & P.demand_connects)
demanders += P
return TRUE
///remove a plumber. we dont delete ourselves because ductnets dont persist through plumbing objects
/datum/ductnet/proc/remove_plumber(datum/component/plumbing/P)
suppliers.Remove(P) //we're probably only in one of these, but Remove() is inherently sane so this is fine
demanders.Remove(P)
for(var/dir in P.ducts)
if(P.ducts[dir] == src)
P.ducts -= dir
if(!ducts.len) //there were no ducts, so it was a direct connection. we destroy ourselves since a ductnet with only one plumber and no ducts is worthless
destroy_network()
///we combine ductnets. this occurs when someone connects to seperate sets of fluid ducts
/datum/ductnet/proc/assimilate(datum/ductnet/D)
ducts.Add(D.ducts)
suppliers.Add(D.suppliers)
demanders.Add(D.demanders)
for(var/A in D.suppliers + D.demanders)
var/datum/component/plumbing/P = A
for(var/s in P.ducts)
if(P.ducts[s] != D)
continue
P.ducts[s] = src //all your ducts are belong to us
for(var/A in D.ducts)
var/obj/machinery/duct/M = A
M.duct = src //forget your old master
destroy_network()
///destroy the network and tell all our ducts and plumbers we are gone
/datum/ductnet/proc/destroy_network(delete=TRUE)
for(var/A in suppliers + demanders)
remove_plumber(A)
for(var/A in ducts)
var/obj/machinery/duct/D = A
D.duct = null
if(delete) //I don't want code to run with qdeleted objects because that can never be good, so keep this in-case the ductnet has some business left to attend to before commiting suicide
qdel(src)
+10
View File
@@ -0,0 +1,10 @@
//blocks bluespace artillery beams that try to fly through
//look not all elements need to be fancy
/datum/element/bsa_blocker/Attach(datum/target)
if(!isatom(target))
return ELEMENT_INCOMPATIBLE
RegisterSignal(target, COMSIG_ATOM_BSA_BEAM, .proc/block_bsa)
return ..()
/datum/element/bsa_blocker/proc/block_bsa()
return COMSIG_ATOM_BLOCKS_BSA_BEAM
+24 -21
View File
@@ -32,40 +32,43 @@
RegisterSignal(A, COMSIG_COMPONENT_CLEAN_ACT, .proc/clean_react)
if(description)
RegisterSignal(A, COMSIG_PARENT_EXAMINE, .proc/examine)
apply(A, TRUE)
RegisterSignal(A, COMSIG_ATOM_UPDATE_OVERLAYS, .proc/apply_overlay, TRUE)
num_decals_per_atom[A]++
apply(A)
/datum/element/decal/Detach(datum/target)
var/atom/A = target
remove(A, A.dir)
UnregisterSignal(A, list(COMSIG_ATOM_DIR_CHANGE, COMSIG_COMPONENT_CLEAN_ACT, COMSIG_PARENT_EXAMINE))
LAZYREMOVE(num_decals_per_atom, A)
num_decals_per_atom[A]--
if(!num_decals_per_atom[A])
UnregisterSignal(A, list(COMSIG_ATOM_DIR_CHANGE, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZE,
COMSIG_COMPONENT_CLEAN_ACT, COMSIG_PARENT_EXAMINE, COMSIG_ATOM_UPDATE_OVERLAYS))
LAZYREMOVE(num_decals_per_atom, A)
apply(A)
return ..()
/datum/element/decal/proc/remove(atom/target, old_dir)
pic.dir = first_dir == NORTH ? target.dir : turn(first_dir, dir2angle(old_dir))
for(var/i in 1 to num_decals_per_atom[target])
target.cut_overlay(pic, TRUE)
/datum/element/decal/proc/apply(atom/target)
if(target.flags_1 & INITIALIZED_1)
target.update_icon() //could use some queuing here now maybe.
else if(!QDELETED(target) && num_decals_per_atom[target] == 1)
RegisterSignal(target, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZE, .proc/late_update_icon)
if(isitem(target))
addtimer(CALLBACK(target, /obj/item/.proc/update_slot_icon), 0, TIMER_UNIQUE)
/datum/element/decal/proc/apply(atom/target, init = FALSE)
pic.dir = first_dir == NORTH ? target.dir : turn(first_dir, dir2angle(target.dir))
if(init)
target.add_overlay(pic, TRUE)
else
for(var/i in 1 to num_decals_per_atom[target])
target.add_overlay(pic, TRUE)
if(isitem(target))
addtimer(CALLBACK(target, /obj/item/.proc/update_slot_icon), 0, TIMER_UNIQUE)
/datum/element/decal/proc/late_update_icon(atom/source)
source.update_icon()
UnregisterSignal(source,COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZE)
/datum/element/decal/proc/rotate_react(datum/source, old_dir, new_dir)
/datum/element/decal/proc/apply_overlay(atom/source, list/overlay_list)
if(first_dir)
pic.dir = first_dir == SOUTH ? source.dir : turn(first_dir, dir2angle(source.dir)-180) //Never turn a dir by 0.
for(var/i in 1 to num_decals_per_atom[source])
overlay_list += pic
/datum/element/decal/proc/rotate_react(atom/source, old_dir, new_dir)
if(old_dir == new_dir)
return
remove(source, old_dir)
apply(source)
source.update_icon()
/datum/element/decal/proc/clean_react(datum/source, strength)
if(strength >= cleanable)
+20 -36
View File
@@ -2,7 +2,7 @@
The presence of this element allows an item (or a projectile carrying an item) to embed itself in a human or turf when it is thrown into a target (whether by hand, gun, or explosive wave) with either
at least 4 throwspeed (EMBED_THROWSPEED_THRESHOLD) or ignore_throwspeed_threshold set to TRUE. Items meant to be used as shrapnel for projectiles should have ignore_throwspeed_threshold set to true.
Whether we're dealing with a direct /obj/item (throwing a knife at someone) or an /obj/projectile with a shrapnel_type, how we handle things plays out the same, with one extra step separating them.
Whether we're dealing with a direct /obj/item (throwing a knife at someone) or an /obj/item/projectile with a shrapnel_type, how we handle things plays out the same, with one extra step separating them.
Items simply make their COMSIG_MOVABLE_IMPACT or COMSIG_MOVABLE_IMPACT_ZONE check (against a closed turf or a carbon, respectively), while projectiles check on COMSIG_PROJECTILE_SELF_ON_HIT.
Upon a projectile hitting a valid target, it spawns whatever type of payload it has defined, then has that try to embed itself in the target on its own.
@@ -37,10 +37,10 @@
if(!isitem(target) && !isprojectile(target))
return ELEMENT_INCOMPATIBLE
RegisterSignal(target, COMSIG_ELEMENT_ATTACH, .proc/severancePackage)
if(isitem(target))
RegisterSignal(target, COMSIG_MOVABLE_IMPACT_ZONE, .proc/checkEmbedMob)
RegisterSignal(target, COMSIG_MOVABLE_IMPACT, .proc/checkEmbedOther)
RegisterSignal(target, COMSIG_ELEMENT_ATTACH, .proc/severancePackage)
RegisterSignal(target, COMSIG_PARENT_EXAMINE, .proc/examined)
RegisterSignal(target, COMSIG_EMBED_TRY_FORCE, .proc/tryForceEmbed)
RegisterSignal(target, COMSIG_ITEM_DISABLE_EMBED, .proc/detachFromWeapon)
@@ -68,7 +68,7 @@
if(isitem(target))
UnregisterSignal(target, list(COMSIG_MOVABLE_IMPACT_ZONE, COMSIG_ELEMENT_ATTACH, COMSIG_MOVABLE_IMPACT, COMSIG_PARENT_EXAMINE, COMSIG_EMBED_TRY_FORCE, COMSIG_ITEM_DISABLE_EMBED))
else
UnregisterSignal(target, list(COMSIG_PROJECTILE_SELF_ON_HIT))
UnregisterSignal(target, list(COMSIG_PROJECTILE_SELF_ON_HIT, COMSIG_ELEMENT_ATTACH))
/// Checking to see if we're gonna embed into a human
@@ -79,13 +79,13 @@
var/actual_chance = embed_chance
if(!weapon.isEmbedHarmless()) // all the armor in the world won't save you from a kick me sign
var/armor = max(victim.run_armor_check(hit_zone, "bullet", silent=TRUE), victim.run_armor_check(hit_zone, "bomb", silent=TRUE)) // we'll be nice and take the better of bullet and bomb armor
var/armor = max(victim.run_armor_check(hit_zone, "bullet", silent=TRUE), victim.run_armor_check(hit_zone, "bomb", silent=TRUE)) * 0.5 // we'll be nice and take the better of bullet and bomb armor, halved
if(armor) // we only care about armor penetration if there's actually armor to penetrate
var/pen_mod = -armor + weapon.armour_penetration // even a little bit of armor can make a big difference for shrapnel with large negative armor pen
actual_chance += pen_mod // doing the armor pen as a separate calc just in case this ever gets expanded on
if(actual_chance <= 0)
victim.visible_message("<span class='danger'>[weapon] bounces off [victim]'s armor!</span>", "<span class='notice'>[weapon] bounces off your armor!</span>", vision_distance = COMBAT_MESSAGE_RANGE)
victim.visible_message("<span class='danger'>[weapon] bounces off [victim]'s armor, unable to embed!</span>", "<span class='notice'>[weapon] bounces off your armor, unable to embed!</span>", vision_distance = COMBAT_MESSAGE_RANGE)
return
var/roll_embed = prob(actual_chance)
@@ -147,7 +147,7 @@
return TRUE
///A different embed element has been attached, so we'll detach and let them handle things
/datum/element/embed/proc/severancePackage(obj/item/weapon, datum/element/E)
/datum/element/embed/proc/severancePackage(obj/weapon, datum/element/E)
if(istype(E, /datum/element/embed))
Detach(weapon)
@@ -169,46 +169,35 @@
* it to call tryForceEmbed() on its own embed element (it's out of our hands here, our projectile is done), where it will run through all the checks it needs to.
*/
/datum/element/embed/proc/checkEmbedProjectile(obj/item/projectile/P, atom/movable/firer, atom/hit, angle, hit_zone)
if(!iscarbon(hit) && !isclosedturf(hit))
if(!iscarbon(hit))
Detach(P)
return // we don't care
var/obj/item/payload = new payload_type(get_turf(hit))
var/did_embed
if(iscarbon(hit))
var/mob/living/carbon/C = hit
var/obj/item/bodypart/limb
limb = C.get_bodypart(hit_zone)
if(!limb)
limb = C.get_bodypart()
did_embed = payload.tryEmbed(limb)
else
did_embed = payload.tryEmbed(hit)
if(istype(payload, /obj/item/shrapnel/bullet))
payload.name = P.name
payload.embedding = P.embedding
payload.updateEmbedding()
var/mob/living/carbon/C = hit
var/obj/item/bodypart/limb = C.get_bodypart(hit_zone)
if(!limb)
limb = C.get_bodypart()
if(!did_embed)
payload.failedEmbed()
payload.tryEmbed(limb)
Detach(P)
/**
* tryForceEmbed() is called here when we fire COMSIG_EMBED_TRY_FORCE from [/obj/item/proc/tryEmbed]. Mostly, this means we're a piece of shrapnel from a projectile that just impacted something, and we're trying to embed in it.
*
* The reason for this extra mucking about is avoiding having to do an extra hitby(), and annoying the target by impacting them once with the projectile, then again with the shrapnel (which likely represents said bullet), and possibly
* AGAIN if we actually embed. This way, we save on at least one message. Runs the standard embed checks on the mob/turf.
*
* Arguments:
* * I- what we're trying to embed, obviously
* * target- what we're trying to shish-kabob, either a bodypart, a carbon, or a closed turf
* * I- the item we're trying to insert into the target
* * target- what we're trying to shish-kabob, either a bodypart or a carbon
* * hit_zone- if our target is a carbon, try to hit them in this zone, if we don't have one, pick a random one. If our target is a bodypart, we already know where we're hitting.
* * forced- if we want this to succeed 100%
*/
/datum/element/embed/proc/tryForceEmbed(obj/item/I, atom/target, hit_zone, forced=FALSE)
var/obj/item/bodypart/limb
var/mob/living/carbon/C
var/turf/closed/T
if(!forced && !prob(embed_chance))
return
if(iscarbon(target))
C = target
if(!hit_zone)
@@ -218,10 +207,5 @@
limb = target
hit_zone = limb.body_zone
C = limb.owner
else if(isclosedturf(target))
T = target
if(C)
return checkEmbedMob(I, C, hit_zone, forced=TRUE)
else if(T)
return checkEmbedOther(I, T, forced=TRUE)
checkEmbedMob(I, C, hit_zone, forced=TRUE)
return TRUE
+1 -1
View File
@@ -60,7 +60,7 @@
if(L.stat == DEAD)
continue
if(light_nutrition_gain)
L.adjust_nutrition(light_amount * light_nutrition_gain * attached_atoms[AM], NUTRITION_LEVEL_FULL)
L.adjust_nutrition(light_amount * light_nutrition_gain * attached_atoms[AM], NUTRITION_LEVEL_WELL_FED)
if(light_amount > bonus_lum || light_amount < malus_lum)
var/mult = ((light_amount > bonus_lum) ? 1 : -1) * attached_atoms[AM]
if(light_bruteheal)
+10 -1
View File
@@ -61,7 +61,7 @@
A.AddElement(/datum/element/update_icon_updates_onmob)
RegisterSignal(A, COMSIG_ITEM_WORN_OVERLAYS, .proc/apply_worn_overlays)
if(suits_with_helmet_typecache[A.type])
RegisterSignal(A, COMSIG_SUIT_MADE_HELMET, .proc/register_helmet)
RegisterSignal(A, COMSIG_SUIT_MADE_HELMET, .proc/register_helmet) //you better work now you slut
else if(_flags & POLYCHROMIC_ACTION && ismob(A)) //in the event mob update icon procs are ever standarized.
var/datum/action/polychromic/P = new(A)
RegisterSignal(P, COMSIG_ACTION_TRIGGER, .proc/activate_action)
@@ -166,6 +166,15 @@
examine_list += "<span class='notice'>Alt-click to recolor it.</span>"
/datum/element/polychromic/proc/register_helmet(atom/source, obj/item/clothing/head/H)
if(!isitem(H)) //backup in case if it messes up somehow
if(istype(source,/obj/item/clothing/suit/hooded)) //so how come it be like this, where toggleable headslots are named separately (helmet/hood) anyways?
var/obj/item/clothing/suit/hooded/sourcesuit = source
H = sourcesuit.hood
else if(istype(source,/obj/item/clothing/suit/space/hardsuit))
var/obj/item/clothing/suit/space/hardsuit/sourcesuit = source
H = sourcesuit.helmet
else
return
suit_by_helmet[H] = source
helmet_by_suit[source] = H
colors_by_atom[H] = colors_by_atom[source]
+3 -2
View File
@@ -102,8 +102,9 @@
/datum/emote/proc/can_run_emote(mob/user, status_check = TRUE, intentional = FALSE)
. = TRUE
if(!is_type_in_typecache(user, mob_type_allowed_typecache))
return FALSE
if(mob_type_allowed_typecache) //empty list = anyone can use it unless specifically blacklisted
if(!is_type_in_typecache(user, mob_type_allowed_typecache))
return FALSE
if(is_type_in_typecache(user, mob_type_blacklist_typecache))
return FALSE
if(status_check && !is_type_in_typecache(user, mob_type_ignore_stat_typecache))
+37 -13
View File
@@ -103,13 +103,21 @@ GLOBAL_LIST_EMPTY(explosions)
// 3/7/14 will calculate to 80 + 35
var/far_dist = 0
far_dist += heavy_impact_range * 5
far_dist += heavy_impact_range * 15 // Large explosions carry further
far_dist += devastation_range * 20
if(!silent)
var/frequency = get_rand_frequency()
var/sound/explosion_sound = sound(get_sfx("explosion"))
var/sound/far_explosion_sound = sound('sound/effects/explosionfar.ogg')
var/sound/creaking_explosion_sound = sound(get_sfx("explosion_creaking"))
var/sound/hull_creaking_sound = sound(get_sfx("hull_creaking"))
var/sound/explosion_echo_sound = sound('sound/effects/explosion_distant.ogg')
var/on_station = SSmapping.level_trait(epicenter.z, ZTRAIT_STATION)
var/creaking_explosion = FALSE
if(prob(devastation_range*30+heavy_impact_range*5) && on_station) // Huge explosions are near guaranteed to make the station creak and whine, smaller ones might.
creaking_explosion = TRUE // prob over 100 always returns true
for(var/mob/M in GLOB.player_list)
// Double check for client
@@ -126,11 +134,29 @@ GLOBAL_LIST_EMPTY(explosions)
shake_camera(M, 25, clamp(baseshakeamount, 0, 10))
// You hear a far explosion if you're outside the blast radius. Small bombs shouldn't be heard all over the station.
else if(dist <= far_dist)
var/far_volume = clamp(far_dist, 30, 50) // Volume is based on explosion size and dist
far_volume += (dist <= far_dist * 0.5 ? 50 : 0) // add 50 volume if the mob is pretty close to the explosion
M.playsound_local(epicenter, null, far_volume, 1, frequency, falloff = 5, S = far_explosion_sound)
if(baseshakeamount > 0)
var/far_volume = clamp(far_dist/2, 40, 60) // Volume is based on explosion size and dist
if(creaking_explosion)
M.playsound_local(epicenter, null, far_volume, 1, frequency, S = creaking_explosion_sound, distance_multiplier = 0)
else if(prob(75))
M.playsound_local(epicenter, null, far_volume, 1, frequency, S = far_explosion_sound, distance_multiplier = 0) // Far sound
else
M.playsound_local(epicenter, null, far_volume, 1, frequency, S = explosion_echo_sound, distance_multiplier = 0) // Echo sound
if(baseshakeamount > 0 || devastation_range)
if(!baseshakeamount) // Devastating explosions rock the station and ground
baseshakeamount = devastation_range*3
shake_camera(M, 10, clamp(baseshakeamount*0.25, 0, 2.5))
else if(M.can_hear() && !isspaceturf(get_turf(M)) && heavy_impact_range) // Big enough explosions echo throughout the hull
var/echo_volume = 40
if(devastation_range)
baseshakeamount = devastation_range
shake_camera(M, 10, clamp(baseshakeamount*0.25, 0, 2.5))
echo_volume = 60
M.playsound_local(epicenter, null, echo_volume, 1, frequency, S = explosion_echo_sound, distance_multiplier = 0)
if(creaking_explosion) // 5 seconds after the bang, the station begins to creak
addtimer(CALLBACK(M, /mob/proc/playsound_local, epicenter, null, rand(25, 40), 1, frequency, null, null, FALSE, hull_creaking_sound, null, null, null, null, 0), 5 SECONDS)
EX_PREPROCESS_CHECK_TICK
//postpone processing for a bit
@@ -196,14 +222,12 @@ GLOBAL_LIST_EMPTY(explosions)
//------- EX_ACT AND TURF FIRES -------
if(T == epicenter) // Ensures explosives detonating from bags trigger other explosives in that bag
var/list/items = list()
for(var/I in T)
var/atom/A = I
if (!(A.flags_1 & PREVENT_CONTENTS_EXPLOSION_1)) //The atom/contents_explosion() proc returns null if the contents ex_acting has been handled by the atom, and TRUE if it hasn't.
items += A.GetAllContents()
for(var/O in items)
var/atom/A = O
if((T == epicenter) && !QDELETED(explosion_source) && ismovable(explosion_source) && (get_turf(explosion_source) == T)) // Ensures explosives detonating from bags trigger other explosives in that bag
var/list/atoms = list()
for(var/atom/A in explosion_source.loc) // the ismovableatom check 2 lines above makes sure we don't nuke an /area
atoms += A
for(var/i in atoms)
var/atom/A = i
if(!QDELETED(A))
A.ex_act(dist)
+119 -5
View File
@@ -32,20 +32,29 @@
var/datum/action/innate/end_holocall/hangup //hangup action
var/call_start_time
var/head_call = FALSE //calls from a head of staff autoconnect, if the receiving pad is not secure.
//creates a holocall made by `caller` from `calling_pad` to `callees`
/datum/holocall/New(mob/living/caller, obj/machinery/holopad/calling_pad, list/callees)
/datum/holocall/New(mob/living/caller, obj/machinery/holopad/calling_pad, list/callees, elevated_access = FALSE)
call_start_time = world.time
user = caller
calling_pad.outgoing_call = src
calling_holopad = calling_pad
head_call = elevated_access
dialed_holopads = list()
for(var/I in callees)
var/obj/machinery/holopad/H = I
if(!QDELETED(H) && H.is_operational())
dialed_holopads += H
H.say("Incoming call.")
if(head_call)
if(H.secure)
calling_pad.say("Auto-connection refused, falling back to call mode.")
H.say("Incoming call.")
else
H.say("Incoming connection.")
else
H.say("Incoming call.")
LAZYADD(H.holo_calls, src)
if(!dialed_holopads.len)
@@ -79,6 +88,7 @@
dialed_holopads.Cut()
if(calling_holopad)
calling_holopad.calling = FALSE
calling_holopad.outgoing_call = null
calling_holopad.SetLightsAndPower()
calling_holopad = null
@@ -145,6 +155,7 @@
if(!Check())
return
calling_holopad.calling = FALSE
hologram = H.activate_holo(user)
hologram.HC = src
@@ -160,6 +171,8 @@
hangup = new(eye, src)
hangup.Grant(user)
playsound(H, 'sound/machines/ping.ogg', 100)
H.say("Connection established.")
//Checks the validity of a holocall and qdels itself if it's not. Returns TRUE if valid, FALSE otherwise
/datum/holocall/proc/Check()
@@ -178,7 +191,6 @@
. = world.time < (call_start_time + HOLOPAD_MAX_DIAL_TIME)
if(!.)
calling_holopad.say("No answer received.")
calling_holopad.temp = ""
if(!.)
testing("Holocall Check fail")
@@ -241,10 +253,10 @@
record.caller_image = holodiskOriginal.record.caller_image
record.entries = holodiskOriginal.record.entries.Copy()
record.language = holodiskOriginal.record.language
to_chat(user, "You copy the record from [holodiskOriginal] to [src] by connecting the ports!")
to_chat(user, "<span class='notice'>You copy the record from [holodiskOriginal] to [src] by connecting the ports!</span>")
name = holodiskOriginal.name
else
to_chat(user, "[holodiskOriginal] has no record on it!")
to_chat(user, "<span class='warning'>[holodiskOriginal] has no record on it!</span>")
..()
/obj/item/disk/holodisk/proc/build_record()
@@ -331,6 +343,21 @@
DELAY 20"}
/datum/preset_holoimage/engineer
outfit_type = /datum/outfit/job/engineer
/datum/preset_holoimage/engineer/rig
outfit_type = /datum/outfit/job/engineer/gloved/rig
/datum/preset_holoimage/engineer/ce
outfit_type = /datum/outfit/job/ce
/datum/preset_holoimage/engineer/ce/rig
outfit_type = /datum/outfit/job/engineer/gloved/rig
/datum/preset_holoimage/engineer/atmos
outfit_type = /datum/outfit/job/atmos
/datum/preset_holoimage/engineer/atmos/rig
outfit_type = /datum/outfit/job/engineer/gloved/rig
/datum/preset_holoimage/researcher
@@ -350,3 +377,90 @@
/datum/preset_holoimage/clown
outfit_type = /datum/outfit/job/clown
/obj/item/disk/holodisk/donutstation/whiteship
name = "Blackbox Print-out #DS024"
desc = "A holodisk containing the last viable recording of DS024's blackbox."
preset_image_type = /datum/preset_holoimage/engineer/ce
preset_record_text = {"
NAME Geysr Shorthalt
SAY Engine renovations complete and the ships been loaded. We all ready?
DELAY 25
PRESET /datum/preset_holoimage/engineer
NAME Jacob Ullman
SAY Lets blow this popsicle stand of a station.
DELAY 20
PRESET /datum/preset_holoimage/engineer/atmos
NAME Lindsey Cuffler
SAY Uh, sir? Shouldn't we call for a secondary shuttle? The bluespace drive on this thing made an awfully weird noise when we jumped here..
DELAY 30
PRESET /datum/preset_holoimage/engineer/ce
NAME Geysr Shorthalt
SAY Pah! Ship techie at the dock said to give it a good few kicks if it started acting up, let me just..
DELAY 25
SOUND punch
SOUND sparks
DELAY 10
SOUND punch
SOUND sparks
DELAY 10
SOUND punch
SOUND sparks
SOUND warpspeed
DELAY 15
PRESET /datum/preset_holoimage/engineer/atmos
NAME Lindsey Cuffler
SAY Uhh.. is it supposed to be doing that??
DELAY 15
PRESET /datum/preset_holoimage/engineer/ce
NAME Geysr Shorthalt
SAY See? Working as intended. Now, are we all ready?
DELAY 10
PRESET /datum/preset_holoimage/engineer
NAME Jacob Ullman
SAY Is it supposed to be glowing like that?
DELAY 20
SOUND explosion
"}
/obj/item/disk/holodisk/ruin/snowengieruin
name = "Blackbox Print-out #EB412"
desc = "A holodisk containing the last moments of EB412. There's a bloody fingerprint on it."
preset_image_type = /datum/preset_holoimage/engineer
preset_record_text = {"
NAME Dave Tundrale
SAY Maria, how's Build?
DELAY 10
NAME Maria Dell
PRESET /datum/preset_holoimage/engineer/atmos
SAY It's fine, don't worry. I've got Plastic on it. And frankly, i'm kinda busy with, the, uhhm, incinerator.
DELAY 30
NAME Dave Tundrale
PRESET /datum/preset_holoimage/engineer
SAY Aight, wonderful. The science mans been kinda shit though. No RCDs-
DELAY 20
NAME Maria Dell
PRESET /datum/preset_holoimage/engineer/atmos
SAY Enough about your RCDs. They're not even that important, just bui-
DELAY 15
SOUND explosion
DELAY 10
SAY Oh, shit!
DELAY 10
PRESET /datum/preset_holoimage/engineer/atmos/rig
LANGUAGE /datum/language/narsie
NAME Unknown
SAY RISE, MY LORD!!
DELAY 10
LANGUAGE /datum/language/common
NAME Plastic
PRESET /datum/preset_holoimage/engineer/rig
SAY Fuck, fuck, fuck!
DELAY 20
SAY It's loose! CALL THE FUCKING SHUTT-
DELAY 10
PRESET /datum/preset_holoimage/corgi
NAME Blackbox Automated Message
SAY Connection lost. Dumping audio logs to disk.
DELAY 50"}
+2 -1
View File
@@ -28,7 +28,8 @@ GLOBAL_LIST_INIT(huds, list(
ANTAG_HUD_CLOCKWORK = new/datum/atom_hud/antag(),
ANTAG_HUD_BROTHER = new/datum/atom_hud/antag/hidden(),
ANTAG_HUD_BLOODSUCKER = new/datum/atom_hud/antag/bloodsucker(),
ANTAG_HUD_FUGITIVE = new/datum/atom_hud/antag()
ANTAG_HUD_FUGITIVE = new/datum/atom_hud/antag(),
ANTAG_HUD_HERETIC = new/datum/atom_hud/antag/hidden()
))
/datum/atom_hud
+21 -6
View File
@@ -35,23 +35,24 @@ Unless you know what you're doing, only use the first three numbers. They're in
value_per_unit = 0.025
beauty_modifier = 0.075
///Slight force increase
///Slight force decrease. It's gold, it's soft as fuck.
/datum/material/gold
name = "gold"
desc = "Gold"
color = list(340/255, 240/255, 50/255,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0) //gold is shiny, but not as bright as bananium
strength_modifier = 1.2
strength_modifier = 0.8
categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE)
sheet_type = /obj/item/stack/sheet/mineral/gold
value_per_unit = 0.0625
beauty_modifier = 0.15
armor_modifiers = list("melee" = 1.1, "bullet" = 1.1, "laser" = 1.15, "energy" = 1.15, "bomb" = 1, "bio" = 1, "rad" = 1, "fire" = 0.7, "acid" = 1.1)
///Has no special properties
///Small force increase, for diamond swords
/datum/material/diamond
name = "diamond"
desc = "Highly pressurized carbon"
color = list(48/255, 272/255, 301/255,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0)
strength_modifier = 1.1
alpha = 132
categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE)
sheet_type = /obj/item/stack/sheet/mineral/diamond
@@ -106,6 +107,7 @@ Unless you know what you're doing, only use the first three numbers. They're in
name = "bluespace crystal"
desc = "Crystals with bluespace properties"
color = list(119/255, 217/255, 396/255,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0)
integrity_modifier = 0.2 //these things shatter when thrown.
alpha = 200
categories = list(MAT_CATEGORY_ORE = TRUE)
beauty_modifier = 0.5
@@ -139,7 +141,7 @@ Unless you know what you're doing, only use the first three numbers. They're in
name = "titanium"
desc = "Titanium"
color = "#b3c0c7"
strength_modifier = 1.3
strength_modifier = 1.1
categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE)
sheet_type = /obj/item/stack/sheet/mineral/titanium
value_per_unit = 0.0625
@@ -203,7 +205,7 @@ Unless you know what you're doing, only use the first three numbers. They're in
name = "adamantine"
desc = "A powerful material made out of magic, I mean science!"
color = "#6d7e8e"
strength_modifier = 1.5
strength_modifier = 1.3
categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE)
sheet_type = /obj/item/stack/sheet/mineral/adamantine
value_per_unit = 0.25
@@ -276,18 +278,31 @@ Unless you know what you're doing, only use the first three numbers. They're in
desc = "Mir'ntrath barhah Nar'sie."
color = "#3C3434"
categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE)
strength_modifier = 1.2
sheet_type = /obj/item/stack/sheet/runed_metal
value_per_unit = 0.75
armor_modifiers = list("melee" = 1.2, "bullet" = 1.2, "laser" = 1, "energy" = 1, "bomb" = 1.2, "bio" = 1.2, "rad" = 1.5, "fire" = 1.5, "acid" = 1.5)
beauty_modifier = -0.15
texture_layer_icon_state = "runed"
/datum/material/brass
name = "brass"
desc = "Tybel gb-Ratvar"
color = "#917010"
categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE)
strength_modifier = 1.3 // Replicant Alloy is very good for skull beatings..
sheet_type = /obj/item/stack/tile/brass
value_per_unit = 0.75
armor_modifiers = list("melee" = 1.4, "bullet" = 1.4, "laser" = 0, "energy" = 0, "bomb" = 1.4, "bio" = 1.2, "rad" = 1.5, "fire" = 1.5, "acid" = 1.5) //But it has.. a few problems that can't easily be compensated for.
beauty_modifier = 0.3 //It really beats the cold plain plating of the station, doesn't it?
/datum/material/bronze
name = "bronze"
desc = "Clock Cult? Never heard of it."
color = "#92661A"
strength_modifier = 1.1
categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE)
sheet_type = /obj/item/stack/tile/bronze
sheet_type = /obj/item/stack/sheet/bronze
value_per_unit = 0.025
armor_modifiers = list("melee" = 1, "bullet" = 1, "laser" = 1, "energy" = 1, "bomb" = 1, "bio" = 1, "rad" = 1.5, "fire" = 1.5, "acid" = 1.5)
beauty_modifier = 0.2
+6
View File
@@ -41,6 +41,8 @@
var/special_role
var/list/restricted_roles = list()
var/hide_ckey = FALSE //hide ckey from round-end report
var/list/spell_list = list() // Wizard mode & "Give Spell" badmin button.
var/linglink
@@ -69,6 +71,7 @@
///What character we spawned in as- either at roundstart or latejoin, so we know for persistent scars if we ended as the same person or not
var/mob/original_character
/datum/mind/New(var/key)
skill_holder = new(src)
src.key = key
@@ -137,6 +140,8 @@
if(L.client?.prefs && L.client.prefs.auto_ooc && L.client.prefs.chat_toggles & CHAT_OOC)
DISABLE_BITFIELD(L.client.prefs.chat_toggles,CHAT_OOC)
hide_ckey = current.client?.prefs?.hide_ckey
SEND_SIGNAL(src, COMSIG_MIND_TRANSFER, new_character, old_character)
SEND_SIGNAL(new_character, COMSIG_MOB_ON_NEW_MIND)
@@ -780,6 +785,7 @@
if(!mind.name)
mind.name = real_name
mind.current = src
mind.hide_ckey = client?.prefs?.hide_ckey
/mob/living/carbon/mind_initialize()
..()
@@ -162,6 +162,11 @@
mood_change = -8
timeout = 3 MINUTES
/datum/mood_event/gates_of_mansus
description = "<span class='boldwarning'>LIVING IN A PERFORMANCE IS WORSE THAN DEATH</span>\n"
mood_change = -25
timeout = 4 MINUTES
//These are unused so far but I want to remember them to use them later
/datum/mood_event/cloned_corpse
@@ -199,6 +204,11 @@
mood_change = -1
timeout = 2 MINUTES
/datum/mood_event/plush_bite
description = "<span class='warning'>IT BIT ME!! OW!</span>\n"
mood_change = -3
timeout = 2 MINUTES
//Cursed stuff below
/datum/mood_event/emptypred
@@ -70,6 +70,11 @@
mood_change = 40 //maybe being a cultist isnt that bad after all
hidden = TRUE
/datum/mood_event/heretics
description = "<span class='nicegreen'>THE HIGHER I RISE, THE MORE I SEE.</span>\n"
mood_change = 12 //maybe being a cultist isnt that bad after all
hidden = TRUE
/datum/mood_event/family_heirloom
description = "<span class='nicegreen'>My family heirloom is safe with me.</span>\n"
mood_change = 1
+21
View File
@@ -19,6 +19,27 @@
description = "<span class='boldwarning'>I'm starving!</span>\n"
mood_change = -15
//charge
/datum/mood_event/supercharged
description = "<span class='boldwarning'>I can't possibly keep all this power inside, I need to release some quick!</span>\n"
mood_change = -10
/datum/mood_event/overcharged
description = "<span class='warning'>I feel dangerously overcharged, perhaps I should release some power.</span>\n"
mood_change = -4
/datum/mood_event/charged
description = "<span class='nicegreen'>I feel the power in my veins!</span>\n"
mood_change = 6
/datum/mood_event/lowpower
description = "<span class='warning'>My power is running low, I should go charge up somewhere.</span>\n"
mood_change = -6
/datum/mood_event/decharged
description = "<span class='boldwarning'>I'm in desperate need of some electricity!</span>\n"
mood_change = -10
//Disgust
/datum/mood_event/gross
description = "<span class='warning'>I saw something gross.</span>\n"
+1 -1
View File
@@ -11,7 +11,7 @@
var/lowest_value = 256 * 8
var/text_gain_indication = ""
var/text_lose_indication = ""
var/list/mutable_appearance/visual_indicators = list()
var/list/visual_indicators = list()
var/obj/effect/proc_holder/spell/power
var/layer_used = MUTATIONS_LAYER //which mutation layer to use
var/list/species_allowed = list() //to restrict mutation to only certain species
+1 -1
View File
@@ -410,7 +410,7 @@
throw_speed = 4
embedding = list("embedded_pain_multiplier" = 4, "embed_chance" = 100, "embedded_fall_chance" = 0)
w_class = WEIGHT_CLASS_SMALL
sharpness = IS_SHARP
sharpness = SHARP_POINTY
var/mob/living/carbon/human/fired_by
/// if we missed our target
var/missed = TRUE
+2
View File
@@ -90,6 +90,8 @@
to_chat(user, "<span class='boldnotice'>You catch some drifting memories of their past conversations...</span>")
for(var/spoken_memory in recent_speech)
to_chat(user, "<span class='notice'>[recent_speech[spoken_memory]]</span>")
if(usr in GLOB.rockpaperscissors_players)
to_chat(user, "<span class='notice'>They're planning on playing [GLOB.rockpaperscissors_players[usr][1]]</span>")
if(iscarbon(M))
var/mob/living/carbon/human/H = M
to_chat(user, "<span class='boldnotice'>You find that their intent is to [H.a_intent]...</span>")
+18
View File
@@ -27,6 +27,24 @@
target.attack_hulk(owner)
return INTERRUPT_UNARMED_ATTACK | NO_AUTO_CLICKDELAY_HANDLING
/**
*Checks damage of a hulk's arm and applies bone wounds as necessary.
*
*Called by specific atoms being attacked, such as walls. If an atom
*does not call this proc, than punching that atom will not cause
*arm breaking (even if the atom deals recoil damage to hulks).
*Arguments:
*arg1 is the arm to evaluate damage of and possibly break.
*/
/datum/mutation/human/hulk/proc/break_an_arm(obj/item/bodypart/arm)
switch(arm.brute_dam)
if(45 to 50)
arm.force_wound_upwards(/datum/wound/blunt/critical)
if(41 to 45)
arm.force_wound_upwards(/datum/wound/blunt/severe)
if(35 to 41)
arm.force_wound_upwards(/datum/wound/blunt/moderate)
/datum/mutation/human/hulk/on_life()
if(owner.health < 0)
on_losing(owner)
-1
View File
@@ -105,7 +105,6 @@
// modify the ignored_things list in __HELPERS/radiation.dm instead
var/static/list/blacklisted = typecacheof(list(
/turf,
/mob,
/obj/structure/cable,
/obj/machinery/atmospherics,
/obj/item/ammo_casing,
+6
View File
@@ -325,6 +325,12 @@
name = "Abductor Replication Lab"
description = "Some scientists tried and almost succeeded to recreate abductor tools. Somewhat slower and a bit less modern than their originals, these tools are the best you can get if you aren't an alien."
/datum/map_template/ruin/space/spacediner
id = "spacediner"
suffix = "spacediner.dmm"
name = "Space Diner"
description = "Come, traveler of the bluespace planes. Sit, enjoy a drink and take one of the fair maidens for a night. The exit is the way you came in, via that teleporter thingy, but do remember to stay safe."
//Space ruins for the station z
/datum/map_template/ruin/spacenearstation
prefix = "_maps/RandomRuins/SpaceRuinsStation/"
+7 -4
View File
@@ -14,11 +14,14 @@
mind.skill_holder.ui_interact(src)
/datum/skill_holder/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.always_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/datum/skill_holder/ui_state(mob/user)
return GLOB.always_state
/datum/skill_holder/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "SkillPanel", "[owner.name]'s Skills", 620, 580, master_ui, state)
ui.set_autoupdate(FALSE) // This UI is only ever opened by one person, and never is updated outside of user input.
ui = new(user, src, "SkillPanel", "[owner.name]'s Skills")
ui.set_autoupdate(FALSE)
ui.open()
else if(need_static_data_update)
update_static_data(user)
+1 -1
View File
@@ -110,7 +110,7 @@
/**
* Automatic skill increase, multiplied by skill affinity if existing.
* Only works if skill is numerical.
* Only works if skill is numerical or levelled..
*/
/datum/mind/proc/auto_gain_experience(skill, value, maximum, silent = FALSE)
if(!ispath(skill, /datum/skill))
+6
View File
@@ -0,0 +1,6 @@
/datum/skill/level/dorfy/blacksmithing
name = "Blacksmithing"
desc = "Making metal into fancy shapes using heat and force. Higher levels increase both your working speed at an anvil as well as the quality of your works."
name_color = COLOR_FLOORTILE_GRAY
skill_traits = list(SKILL_SANITY, SKILL_INTELLIGENCE, SKILL_USE_TOOL, SKILL_TRAINING_TOOL)
ui_category = SKILL_UI_CAT_MISC
+15 -8
View File
@@ -6,10 +6,13 @@
qdel(src)
owner = new_owner
/datum/spawners_menu/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.observer_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/datum/spawners_menu/ui_state(mob/user)
return GLOB.observer_state
/datum/spawners_menu/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "SpawnersMenu", "Spawners Menu", 700, 600, master_ui, state)
ui = new(user, src, "SpawnersMenu")
ui.open()
/datum/spawners_menu/ui_data(mob/user)
@@ -42,11 +45,15 @@
if(..())
return
var/spawner_ref = pick(GLOB.mob_spawners[params["name"]])
var/obj/effect/mob_spawn/MS = locate(spawner_ref) in GLOB.poi_list
if(!MS)
var/group_name = params["name"]
if(!group_name || !(group_name in GLOB.mob_spawners))
return
var/list/spawnerlist = GLOB.mob_spawners[group_name]
if(!spawnerlist.len)
return
var/obj/effect/mob_spawn/MS = pick(spawnerlist)
if(!istype(MS) || !(MS in GLOB.poi_list))
return
switch(action)
if("jump")
if(MS)
@@ -55,4 +62,4 @@
if("spawn")
if(MS)
MS.attack_ghost(owner)
. = TRUE
. = TRUE
+60 -1
View File
@@ -580,4 +580,63 @@
/datum/status_effect/regenerative_core/on_remove()
. = ..()
REMOVE_TRAIT(owner, TRAIT_IGNOREDAMAGESLOWDOWN, "regenerative_core")
owner.updatehealth()
owner.updatehealth()
/datum/status_effect/panacea
id = "Anatomic Panacea"
duration = 100
tick_interval = 10
alert_type = /obj/screen/alert/status_effect/panacea
/obj/screen/alert/status_effect/panacea
name = "Panacea"
desc = "We purge the impurities from our body."
icon_state = "panacea"
// Changeling's anatomic panacea now in buff form. Directly fixes issues instead of injecting chems
/datum/status_effect/panacea/tick()
var/mob/living/carbon/M = owner
//Heal brain damage and toxyloss, alongside trauma
owner.adjustOrganLoss(ORGAN_SLOT_BRAIN, -8)
owner.adjustToxLoss(-6, forced = TRUE)
M.cure_trauma_type(resilience = TRAUMA_RESILIENCE_BASIC)
//Purges 50 rads per tick
if(owner.radiation > 0)
owner.radiation -= min(owner.radiation, 50)
//Mutadone effects
owner.jitteriness = 0
if(owner.has_dna())
M.dna.remove_all_mutations(mutadone = TRUE)
if(!QDELETED(owner)) //We were a monkey, now a human
..()
// Purges toxins
for(var/datum/reagent/toxin/R in owner.reagents.reagent_list)
owner.reagents.remove_reagent(R.type, 5)
//Antihol effects
M.reagents.remove_all_type(/datum/reagent/consumable/ethanol, 10, 0, 1)
M.drunkenness = max(M.drunkenness - 10, 0)
owner.dizziness = 0
owner.drowsyness = 0
owner.slurring = 0
owner.confused = 0
//Organ and disease cure moved from panacea.dm to buff proc
var/list/bad_organs = list(
owner.getorgan(/obj/item/organ/body_egg),
owner.getorgan(/obj/item/organ/zombie_infection))
for(var/o in bad_organs)
var/obj/item/organ/O = o
if(!istype(O))
continue
O.Remove()
if(iscarbon(owner))
var/mob/living/carbon/C = owner
C.vomit(0, toxic = TRUE)
O.forceMove(get_turf(owner))
if(isliving(owner))
var/mob/living/L = owner
for(var/thing in L.diseases)
var/datum/disease/D = thing
if(D.severity == DISEASE_SEVERITY_POSITIVE)
continue
D.cure()
+241 -3
View File
@@ -97,6 +97,21 @@
duration = set_duration
return ..()
/datum/status_effect/off_balance
id = "offbalance"
alert_type = null
/datum/status_effect/off_balance/on_creation(mob/living/new_owner, set_duration)
if(isnum(set_duration))
duration = set_duration
return ..()
/datum/status_effect/off_balance/on_remove()
var/active_item = owner.get_active_held_item()
if(is_type_in_typecache(active_item, GLOB.shove_disarming_types))
owner.visible_message("<span class='warning'>[owner.name] regains their grip on \the [active_item]!</span>", "<span class='warning'>You regain your grip on \the [active_item]</span>", null, COMBAT_MESSAGE_RANGE)
return ..()
/obj/screen/alert/status_effect/asleep
name = "Asleep"
desc = "You've fallen asleep. Wait a bit and you should wake up. Unless you don't, considering how helpless you are."
@@ -388,6 +403,229 @@
owner.underlays -= marked_underlay //if this is being called, we should have an owner at this point.
..()
/datum/status_effect/eldritch
duration = 15 SECONDS
status_type = STATUS_EFFECT_REPLACE
alert_type = null
on_remove_on_mob_delete = TRUE
///underlay used to indicate that someone is marked
var/mutable_appearance/marked_underlay
///path for the underlay
var/effect_sprite = ""
/datum/status_effect/eldritch/on_creation(mob/living/new_owner, ...)
marked_underlay = mutable_appearance('icons/effects/effects.dmi', effect_sprite,BELOW_MOB_LAYER)
return ..()
/datum/status_effect/eldritch/on_apply()
. = ..()
if(owner.mob_size >= MOB_SIZE_HUMAN)
RegisterSignal(owner,COMSIG_ATOM_UPDATE_OVERLAYS,.proc/update_owner_underlay)
owner.update_icon()
return TRUE
return FALSE
/datum/status_effect/eldritch/on_remove()
UnregisterSignal(owner,COMSIG_ATOM_UPDATE_OVERLAYS)
owner.update_icon()
return ..()
/datum/status_effect/eldritch/proc/update_owner_underlay(atom/source, list/overlays)
overlays += marked_underlay
/datum/status_effect/eldritch/Destroy()
QDEL_NULL(marked_underlay)
return ..()
/**
* What happens when this mark gets popped
*
* Adds actual functionality to each mark
*/
/datum/status_effect/eldritch/proc/on_effect()
playsound(owner, 'sound/magic/repulse.ogg', 75, TRUE)
qdel(src) //what happens when this is procced.
//Each mark has diffrent effects when it is destroyed that combine with the mansus grasp effect.
/datum/status_effect/eldritch/flesh
id = "flesh_mark"
effect_sprite = "emark1"
/datum/status_effect/eldritch/flesh/on_effect()
if(ishuman(owner))
var/mob/living/carbon/human/H = owner
var/obj/item/bodypart/bodypart = pick(H.bodyparts)
var/datum/wound/slash/severe/crit_wound = new
crit_wound.apply_wound(bodypart)
return ..()
/datum/status_effect/eldritch/ash
id = "ash_mark"
effect_sprite = "emark2"
///Dictates how much damage and stamina loss this mark will cause.
var/repetitions = 1
/datum/status_effect/eldritch/ash/on_creation(mob/living/new_owner, _repetition = 5)
. = ..()
repetitions = min(1,_repetition)
/datum/status_effect/eldritch/ash/on_effect()
if(iscarbon(owner))
var/mob/living/carbon/carbon_owner = owner
carbon_owner.adjustStaminaLoss(10 * repetitions)
carbon_owner.adjustFireLoss(5 * repetitions)
for(var/mob/living/carbon/victim in range(1,carbon_owner))
if(IS_HERETIC(victim) || victim == carbon_owner)
continue
victim.apply_status_effect(type,repetitions-1)
break
return ..()
/datum/status_effect/eldritch/rust
id = "rust_mark"
effect_sprite = "emark3"
/datum/status_effect/eldritch/rust/on_effect()
if(!iscarbon(owner))
return
var/mob/living/carbon/carbon_owner = owner
for(var/obj/item/I in carbon_owner.get_all_gear()) //Affects roughly 75% of items
if(!QDELETED(I) && prob(75)) //Just in case
I.take_damage(100)
return ..()
/datum/status_effect/corrosion_curse
id = "corrosion_curse"
status_type = STATUS_EFFECT_REPLACE
alert_type = null
tick_interval = 1 SECONDS
/datum/status_effect/corrosion_curse/on_creation(mob/living/new_owner, ...)
. = ..()
to_chat(owner, "<span class='danger'>Your feel your body starting to break apart...</span>")
/datum/status_effect/corrosion_curse/tick()
. = ..()
if(!ishuman(owner))
return
var/mob/living/carbon/human/H = owner
var/chance = rand(0,100)
switch(chance)
if(0 to 19)
H.vomit()
if(20 to 29)
H.Dizzy(10)
if(30 to 39)
H.adjustOrganLoss(ORGAN_SLOT_LIVER,5)
if(40 to 49)
H.adjustOrganLoss(ORGAN_SLOT_HEART,5)
if(50 to 59)
H.adjustOrganLoss(ORGAN_SLOT_STOMACH,5)
if(60 to 69)
H.adjustOrganLoss(ORGAN_SLOT_EYES,10)
if(70 to 79)
H.adjustOrganLoss(ORGAN_SLOT_EARS,10)
if(80 to 89)
H.adjustOrganLoss(ORGAN_SLOT_LUNGS,10)
if(90 to 99)
H.adjustOrganLoss(ORGAN_SLOT_TONGUE,10)
if(100)
H.adjustOrganLoss(ORGAN_SLOT_BRAIN,20)
/datum/status_effect/corrosion_curse/lesser
id = "corrosion_curse_lesser"
duration = 20 SECONDS
/datum/status_effect/corrosion_curse/lesser/tick()
. = ..()
if(!ishuman(owner))
return
var/mob/living/carbon/human/H = owner
var/chance = rand(0,100)
switch(chance)
if(0 to 19)
H.adjustBruteLoss(6)
if(20 to 29)
H.Dizzy(10)
if(30 to 39)
H.adjustOrganLoss(ORGAN_SLOT_LIVER,2)
if(40 to 49)
H.adjustOrganLoss(ORGAN_SLOT_HEART,2)
if(50 to 59)
H.adjustOrganLoss(ORGAN_SLOT_STOMACH,2)
if(60 to 69)
H.adjustOrganLoss(ORGAN_SLOT_EYES,5)
if(70 to 79)
H.adjustOrganLoss(ORGAN_SLOT_EARS,5)
if(80 to 89)
H.adjustOrganLoss(ORGAN_SLOT_LUNGS,5)
if(90 to 99)
H.adjustOrganLoss(ORGAN_SLOT_TONGUE,5)
if(100)
H.adjustOrganLoss(ORGAN_SLOT_BRAIN,10)
/datum/status_effect/amok
id = "amok"
status_type = STATUS_EFFECT_REPLACE
alert_type = null
duration = 10 SECONDS
tick_interval = 1 SECONDS
/datum/status_effect/amok/on_apply(mob/living/afflicted)
. = ..()
to_chat(owner, "<span class='boldwarning'>Your feel filled with a rage that is not your own!</span>")
/datum/status_effect/amok/tick()
. = ..()
var/prev_intent = owner.a_intent
owner.a_intent = INTENT_HARM
var/list/mob/living/targets = list()
for(var/mob/living/potential_target in oview(owner, 1))
if(IS_HERETIC(potential_target) || potential_target.mind?.has_antag_datum(/datum/antagonist/heretic_monster))
continue
targets += potential_target
if(LAZYLEN(targets))
owner.log_message(" attacked someone due to the amok debuff.", LOG_ATTACK) //the following attack will log itself
owner.ClickOn(pick(targets))
owner.a_intent = prev_intent
/datum/status_effect/cloudstruck
id = "cloudstruck"
status_type = STATUS_EFFECT_REPLACE
duration = 3 SECONDS
on_remove_on_mob_delete = TRUE
///This overlay is applied to the owner for the duration of the effect.
var/mutable_appearance/mob_overlay
/datum/status_effect/cloudstruck/on_creation(mob/living/new_owner, set_duration)
if(isnum(set_duration))
duration = set_duration
. = ..()
/datum/status_effect/cloudstruck/on_apply()
. = ..()
mob_overlay = mutable_appearance('icons/effects/eldritch.dmi', "cloud_swirl", ABOVE_MOB_LAYER)
owner.overlays += mob_overlay
owner.update_icon()
ADD_TRAIT(owner, TRAIT_BLIND, "cloudstruck")
return TRUE
/datum/status_effect/cloudstruck/on_remove()
. = ..()
if(QDELETED(owner))
return
REMOVE_TRAIT(owner, TRAIT_BLIND, "cloudstruck")
if(owner)
owner.overlays -= mob_overlay
owner.update_icon()
/datum/status_effect/cloudstruck/Destroy()
. = ..()
QDEL_NULL(mob_overlay)
/datum/status_effect/stacking/saw_bleed
id = "saw_bleed"
tick_interval = 6
@@ -433,7 +671,7 @@
var/still_bleeding = FALSE
for(var/thing in throat.wounds)
var/datum/wound/W = thing
if(W.wound_type == WOUND_LIST_CUT && W.severity > WOUND_SEVERITY_MODERATE)
if(W.wound_type == WOUND_SLASH && W.severity > WOUND_SEVERITY_MODERATE)
still_bleeding = TRUE
break
if(!still_bleeding)
@@ -547,8 +785,8 @@
owner.DefaultCombatKnockdown(15, TRUE, FALSE, 15)
if(iscarbon(owner))
var/mob/living/carbon/C = owner
C.silent = max(2, C.silent)
C.stuttering = max(5, C.stuttering)
C.silent = max(5, C.silent) //Increased, now lasts until five seconds after it ends, instead of 2
C.stuttering = max(10, C.stuttering) //Increased, now lasts for five seconds after the mute ends, instead of 3
if(!old_health)
old_health = owner.health
if(!old_oxyloss)
+20 -11
View File
@@ -117,8 +117,7 @@
/datum/status_effect/wound/on_creation(mob/living/new_owner, incoming_wound)
. = ..()
var/datum/wound/W = incoming_wound
linked_wound = W
linked_wound = incoming_wound
linked_limb = linked_wound.limb
/datum/status_effect/wound/on_remove()
@@ -140,9 +139,9 @@
// bones
/datum/status_effect/wound/bone
/datum/status_effect/wound/blunt
/datum/status_effect/wound/bone/interact_speed_modifier()
/datum/status_effect/wound/blunt/interact_speed_modifier()
var/mob/living/carbon/C = owner
if(C.get_active_hand() == linked_limb)
@@ -151,7 +150,7 @@
return 1
/datum/status_effect/wound/bone/action_cooldown_mod()
/datum/status_effect/wound/blunt/action_cooldown_mod()
var/mob/living/carbon/C = owner
if(C.get_active_hand() == linked_limb)
@@ -159,24 +158,34 @@
return 1
/datum/status_effect/wound/bone/moderate
/datum/status_effect/wound/blunt/moderate
id = "disjoint"
/datum/status_effect/wound/bone/severe
/datum/status_effect/wound/blunt/severe
id = "hairline"
/datum/status_effect/wound/bone/critical
/datum/status_effect/wound/blunt/critical
id = "compound"
// cuts
/datum/status_effect/wound/cut/moderate
/datum/status_effect/wound/slash/moderate
id = "abrasion"
/datum/status_effect/wound/cut/severe
/datum/status_effect/wound/slash/severe
id = "laceration"
/datum/status_effect/wound/cut/critical
/datum/status_effect/wound/slash/critical
id = "avulsion"
// pierce
/datum/status_effect/wound/pierce/moderate
id = "breakage"
/datum/status_effect/wound/pierce/severe
id = "puncture"
/datum/status_effect/wound/pierce/critical
id = "rupture"
// burns
/datum/status_effect/wound/burn/moderate
id = "seconddeg"
+19 -54
View File
@@ -14,7 +14,7 @@
if(NOBLOOD in H.dna.species.species_traits) //can't lose blood if your species doesn't have any
return
else
quirk_holder.blood_volume -= 0.275
quirk_holder.blood_volume -= 0.2
/datum/quirk/depression
name = "Depression"
@@ -54,7 +54,7 @@ GLOBAL_LIST_EMPTY(family_heirlooms)
if("Botanist")
heirloom_type = pick(/obj/item/cultivator, /obj/item/reagent_containers/glass/bucket, /obj/item/storage/bag/plants, /obj/item/toy/plush/beeplushie)
if("Medical Doctor")
heirloom_type = /obj/item/healthanalyzer/advanced
heirloom_type = /obj/item/healthanalyzer
if("Paramedic")
heirloom_type = /obj/item/lighter
if("Station Engineer")
@@ -211,24 +211,27 @@ GLOBAL_LIST_EMPTY(family_heirlooms)
H.gain_trauma(T, TRAUMA_RESILIENCE_ABSOLUTE)
/datum/quirk/paraplegic/on_spawn()
if(quirk_holder.buckled) // Handle late joins being buckled to arrival shuttle chairs.
quirk_holder.buckled.unbuckle_mob(quirk_holder)
if(quirk_holder.client)
var/modified_limbs = quirk_holder.client.prefs.modified_limbs
if(!(modified_limbs[BODY_ZONE_L_LEG] == LOADOUT_LIMB_AMPUTATED && modified_limbs[BODY_ZONE_R_LEG] == LOADOUT_LIMB_AMPUTATED && !isjellyperson(quirk_holder)))
if(quirk_holder.buckled) // Handle late joins being buckled to arrival shuttle chairs.
quirk_holder.buckled.unbuckle_mob(quirk_holder)
var/turf/T = get_turf(quirk_holder)
var/obj/structure/chair/spawn_chair = locate() in T
var/turf/T = get_turf(quirk_holder)
var/obj/structure/chair/spawn_chair = locate() in T
var/obj/vehicle/ridden/wheelchair/wheels = new(T)
if(spawn_chair) // Makes spawning on the arrivals shuttle more consistent looking
wheels.setDir(spawn_chair.dir)
var/obj/vehicle/ridden/wheelchair/wheels = new(T)
if(spawn_chair) // Makes spawning on the arrivals shuttle more consistent looking
wheels.setDir(spawn_chair.dir)
wheels.buckle_mob(quirk_holder)
wheels.buckle_mob(quirk_holder)
// During the spawning process, they may have dropped what they were holding, due to the paralysis
// So put the things back in their hands.
// During the spawning process, they may have dropped what they were holding, due to the paralysis
// So put the things back in their hands.
for(var/obj/item/I in T)
if(I.fingerprintslast == quirk_holder.ckey)
quirk_holder.put_in_hands(I)
for(var/obj/item/I in T)
if(I.fingerprintslast == quirk_holder.ckey)
quirk_holder.put_in_hands(I)
/datum/quirk/poor_aim
name = "Poor Aim"
@@ -244,42 +247,6 @@ GLOBAL_LIST_EMPTY(family_heirlooms)
mob_trait = TRAIT_PROSOPAGNOSIA
medical_record_text = "Patient suffers from prosopagnosia and cannot recognize faces."
/datum/quirk/prosthetic_limb
name = "Prosthetic Limb"
desc = "An accident caused you to lose one of your limbs. Because of this, you now have a random prosthetic!"
value = -1
var/slot_string = "limb"
/datum/quirk/prosthetic_limb/on_spawn()
var/mob/living/carbon/human/H = quirk_holder
var/limb_slot
if(HAS_TRAIT(H, TRAIT_PARA))//Prevent paraplegic legs being replaced
limb_slot = pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM)
else
limb_slot = pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
var/obj/item/bodypart/old_part = H.get_bodypart(limb_slot)
var/obj/item/bodypart/prosthetic
switch(limb_slot)
if(BODY_ZONE_L_ARM)
prosthetic = new/obj/item/bodypart/l_arm/robot/surplus(quirk_holder)
slot_string = "left arm"
if(BODY_ZONE_R_ARM)
prosthetic = new/obj/item/bodypart/r_arm/robot/surplus(quirk_holder)
slot_string = "right arm"
if(BODY_ZONE_L_LEG)
prosthetic = new/obj/item/bodypart/l_leg/robot/surplus(quirk_holder)
slot_string = "left leg"
if(BODY_ZONE_R_LEG)
prosthetic = new/obj/item/bodypart/r_leg/robot/surplus(quirk_holder)
slot_string = "right leg"
prosthetic.replace_limb(H)
qdel(old_part)
H.regenerate_icons()
/datum/quirk/prosthetic_limb/post_add()
to_chat(quirk_holder, "<span class='boldannounce'>Your [slot_string] has been replaced with a surplus prosthetic. It is fragile and will easily come apart under duress. Additionally, \
you need to use a welding tool and cables to repair it, instead of bruise packs and ointment.</span>")
/datum/quirk/insanity
name = "Reality Dissociation Syndrome"
desc = "You suffer from a severe disorder that causes very vivid hallucinations. Mindbreaker toxin can suppress its effects, and you are immune to mindbreaker's hallucinogenic properties. <b>This is not a license to grief.</b>"
@@ -337,10 +304,8 @@ GLOBAL_LIST_EMPTY(family_heirlooms)
dumb_thing = FALSE //only once per life
if(prob(1))
new/obj/item/reagent_containers/food/snacks/pastatomato(get_turf(H)) //now that's what I call spaghetti code
// small chance to make eye contact with inanimate objects/mindless mobs because of nerves
/datum/quirk/social_anxiety/proc/looks_at_floor(datum/source, atom/A)
var/mob/living/mind_check = A
if(prob(85) || (istype(mind_check) && mind_check.mind))
+14 -5
View File
@@ -97,6 +97,12 @@
/datum/wires/proc/get_wire(color)
return colors[color]
/datum/wires/proc/get_color_of_wire(wire_type)
for(var/color in colors)
var/other_type = colors[color]
if(wire_type == other_type)
return color
/datum/wires/proc/get_attached(color)
if(assemblies[color])
return assemblies[color]
@@ -117,7 +123,7 @@
return TRUE
/datum/wires/proc/is_dud(wire)
return findtext(wire, WIRE_DUD_PREFIX)
return findtext(wire, WIRE_DUD_PREFIX, 1, length(WIRE_DUD_PREFIX) + 1)
/datum/wires/proc/is_dud_color(color)
return is_dud(get_wire(color))
@@ -197,6 +203,7 @@
S.forceMove(holder.drop_location())
return S
/// Called from [/atom/proc/emp_act]
/datum/wires/proc/emp_pulse()
var/list/possible_wires = shuffle(wires)
var/remaining_pulses = MAXIMUM_EMP_WIRES
@@ -239,11 +246,13 @@
return ..()
return UI_CLOSE
/datum/wires/ui_interact(mob/user, ui_key = "wires", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/datum/wires/ui_state(mob/user)
return GLOB.physical_state
/datum/wires/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if (!ui)
ui = new(user, src, ui_key, "Wires", "[holder.name] Wires", 350, 150 + wires.len * 30, master_ui, state)
ui = new(user, src, "Wires", "[holder.name] Wires")
ui.open()
/datum/wires/ui_data(mob/user)
+13 -22
View File
@@ -53,11 +53,8 @@
/datum/wires/airlock/interactable(mob/user)
var/obj/machinery/door/airlock/A = holder
if(!A.panel_open)
return FALSE
if(!A.hasSiliconAccessInArea(user) && A.isElectrified() && A.shock(user, 100))
return FALSE
return TRUE
if(A.panel_open)
return TRUE
/datum/wires/airlock/get_status()
var/obj/machinery/door/airlock/A = holder
@@ -74,6 +71,8 @@
/datum/wires/airlock/on_pulse(wire)
set waitfor = FALSE
var/obj/machinery/door/airlock/A = holder
if(usr && !A.hasSiliconAccessInArea(usr) && A.isElectrified() && A.shock(usr, 100))
return FALSE
switch(wire)
if(WIRE_POWER1, WIRE_POWER2) // Pulse to loose power.
A.loseMainPower()
@@ -115,10 +114,7 @@
A.aiControlDisabled = -1
if(WIRE_SHOCK) // Pulse to shock the door for 10 ticks.
if(!A.secondsElectrified)
A.set_electrified(30)
if(usr)
LAZYADD(A.shockedby, text("\[[TIME_STAMP("hh:mm:ss", FALSE)]\] [key_name(usr)]"))
log_combat(usr, A, "electrified")
A.set_electrified(30, usr)
if(WIRE_SAFETY)
A.safe = !A.safe
if(!A.density)
@@ -131,25 +127,23 @@
/datum/wires/airlock/on_cut(wire, mend)
var/obj/machinery/door/airlock/A = holder
if(usr && !A.hasSiliconAccessInArea(usr) && A.isElectrified() && A.shock(usr, 100))
return FALSE
switch(wire)
if(WIRE_POWER1, WIRE_POWER2) // Cut to loose power, repair all to gain power.
if(mend && !is_cut(WIRE_POWER1) && !is_cut(WIRE_POWER2))
A.regainMainPower()
if(usr)
A.shock(usr, 50)
else
A.loseMainPower()
if(usr)
A.shock(usr, 50)
if(isliving(usr))
A.shock(usr, 50)
if(WIRE_BACKUP1, WIRE_BACKUP2) // Cut to loose backup power, repair all to gain backup power.
if(mend && !is_cut(WIRE_BACKUP1) && !is_cut(WIRE_BACKUP2))
A.regainBackupPower()
if(usr)
A.shock(usr, 50)
else
A.loseBackupPower()
if(usr)
A.shock(usr, 50)
if(isliving(usr))
A.shock(usr, 50)
if(WIRE_BOLTS) // Cut to drop bolts, mend does nothing.
if(!mend)
A.bolt()
@@ -170,10 +164,7 @@
A.set_electrified(0)
else
if(A.secondsElectrified != -1)
A.set_electrified(-1)
if(usr)
LAZYADD(A.shockedby, text("\[[TIME_STAMP("hh:mm:ss", FALSE)]\] [key_name(usr)]"))
log_combat(usr, A, "electrified")
A.set_electrified(-1, usr)
if(WIRE_SAFETY) // Cut to disable safeties, mend to re-enable.
A.safe = mend
if(WIRE_TIMING) // Cut to disable auto-close, mend to re-enable.
@@ -184,5 +175,5 @@
A.lights = mend
A.update_icon()
if(WIRE_ZAP1, WIRE_ZAP2) // Ouch.
if(usr)
if(isliving(usr))
A.shock(usr, 50)
+28 -10
View File
@@ -53,8 +53,12 @@
if(victim)
LAZYADD(victim.all_scars, src)
description = pick(W.scarring_descriptions)
precise_location = pick(limb.specific_locations)
if(victim && victim.get_biological_state() == BIO_JUST_BONE)
description = pick(strings(BONE_SCAR_FILE, W.scar_keyword)) || "general disfigurement"
else
description = pick(strings(FLESH_SCAR_FILE, W.scar_keyword)) || "general disfigurement"
precise_location = pick(strings(SCAR_LOC_FILE, limb.body_zone))
switch(W.severity)
if(WOUND_SEVERITY_MODERATE)
visibility = 2
@@ -62,6 +66,9 @@
visibility = 3
if(WOUND_SEVERITY_CRITICAL)
visibility = 5
if(WOUND_SEVERITY_LOSS)
visibility = 7
precise_location = "amputation"
/// Used when we finalize a scar from a healing cut
/datum/scar/proc/lazy_attach(obj/item/bodypart/BP, datum/wound/W)
@@ -71,10 +78,11 @@
LAZYADD(victim.all_scars, src)
/// Used to "load" a persistent scar
/datum/scar/proc/load(obj/item/bodypart/BP, description, specific_location, severity=WOUND_SEVERITY_SEVERE)
if(!(BP.body_zone in applicable_zones))
/datum/scar/proc/load(obj/item/bodypart/BP, version, description, specific_location, severity=WOUND_SEVERITY_SEVERE)
if(!(BP.body_zone in applicable_zones) || !BP.is_organic_limb())
qdel(src)
return
limb = BP
src.severity = severity
LAZYADD(limb.scars, src)
@@ -90,6 +98,8 @@
visibility = 3
if(WOUND_SEVERITY_CRITICAL)
visibility = 5
if(WOUND_SEVERITY_LOSS)
visibility = 7
return TRUE
/// What will show up in examine_more() if this scar is visible
@@ -102,9 +112,12 @@
if(WOUND_SEVERITY_MODERATE)
msg = "<span class='tinynotice'>[msg]</span>"
if(WOUND_SEVERITY_SEVERE)
msg = "<span class='smallnotice'>[msg]</span>"
msg = "<span class='smallnoticeital'>[msg]</span>"
if(WOUND_SEVERITY_CRITICAL)
msg = "<span class='smallnotice'><b>[msg]</b></span>"
msg = "<span class='smallnoticeital'><b>[msg]</b></span>"
if(WOUND_SEVERITY_LOSS)
msg = "[victim.p_their(TRUE)] [limb.name] [description]." // different format
msg = "<span class='notice'><i><b>[msg]</b></i></span>"
return "\t[msg]"
/// Whether a scar can currently be seen by the viewer
@@ -117,12 +130,12 @@
if(!ishuman(victim) || isobserver(viewer) || victim == viewer)
return TRUE
var/mob/living/carbon/human/H = victim
var/mob/living/carbon/human/human_victim = victim
if(istype(limb, /obj/item/bodypart/head))
if((H.wear_mask && (H.wear_mask.flags_inv & HIDEFACE)) || (H.head && (H.head.flags_inv & HIDEFACE)))
if((human_victim.wear_mask && (human_victim.wear_mask.flags_inv & HIDEFACE)) || (human_victim.head && (human_victim.head.flags_inv & HIDEFACE)))
return FALSE
else if(limb.scars_covered_by_clothes)
var/num_covers = LAZYLEN(H.clothingonpart(limb))
var/num_covers = LAZYLEN(human_victim.clothingonpart(limb))
if(num_covers + get_dist(viewer, victim) >= visibility)
return FALSE
@@ -131,4 +144,9 @@
/// Used to format a scar to safe in preferences for persistent scars
/datum/scar/proc/format()
if(!fake)
return "[limb.body_zone]|[description]|[precise_location]|[severity]"
return "[SCAR_CURRENT_VERSION]|[limb.body_zone]|[description]|[precise_location]|[severity]"
/// Used to format a scar to safe in preferences for persistent scars
/datum/scar/proc/format_amputated(body_zone)
description = pick(list("is several skintone shades paler than the rest of the body", "is a gruesome patchwork of artificial flesh", "has a large series of attachment scars at the articulation points"))
return "[SCAR_CURRENT_VERSION]|[body_zone]|[description]|amputated|[WOUND_SEVERITY_LOSS]"
+30 -21
View File
@@ -33,15 +33,13 @@
/// Either WOUND_SEVERITY_TRIVIAL (meme wounds like stubbed toe), WOUND_SEVERITY_MODERATE, WOUND_SEVERITY_SEVERE, or WOUND_SEVERITY_CRITICAL (or maybe WOUND_SEVERITY_LOSS)
var/severity = WOUND_SEVERITY_MODERATE
/// The list of wounds it belongs in, WOUND_LIST_BONE, WOUND_LIST_CUT, or WOUND_LIST_BURN
/// The list of wounds it belongs in, WOUND_LIST_BLUNT, WOUND_LIST_SLASH, or WOUND_LIST_BURN
var/wound_type
/// What body zones can we affect
var/list/viable_zones = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
/// Who owns the body part that we're wounding
var/mob/living/carbon/victim = null
/// If we only work on organics (everything right now)
var/organic_only = TRUE
/// The bodypart we're parented to
var/obj/item/bodypart/limb = null
@@ -51,8 +49,6 @@
var/list/treatable_by_grabbed
/// Tools with the specified tool flag will also be able to try directly treating this wound
var/treatable_tool
/// Set to TRUE if we don't give a shit about the patient's comfort and are allowed to just use any random sharp thing on this wound. Will require an aggressive grab or more to perform
var/treatable_sharp
/// How long it will take to treat this wound with a standard effective tool, assuming it doesn't need surgery
var/base_treat_time = 5 SECONDS
@@ -65,17 +61,13 @@
/// How much we're contributing to this limb's bleed_rate
var/blood_flow
/// The minimum we need to roll on [/obj/item/bodypart/proc/check_wounding()] to begin suffering this wound, see check_wounding_mods() for more
/// The minimum we need to roll on [/obj/item/bodypart/proc/check_wounding] to begin suffering this wound, see check_wounding_mods() for more
var/threshold_minimum
/// How much having this wound will add to all future check_wounding() rolls on this limb, to allow progression to worse injuries with repeated damage
var/threshold_penalty
/// If we need to process each life tick
var/processes = FALSE
/// If TRUE and an item that can treat multiple different types of coexisting wounds (gauze can be used to splint broken bones, staunch bleeding, and cover burns), we get first dibs if we come up first for it, then become nonpriority.
/// Otherwise, if no untreated wound claims the item, we cycle through the non priority wounds and pick a random one who can use that item.
var/treat_priority = FALSE
/// If having this wound makes currently makes the parent bodypart unusable
var/disabling
@@ -89,12 +81,15 @@
var/cryo_progress
/// What kind of scars this wound will create description wise once healed
var/list/scarring_descriptions = list("general disfigurement")
var/scar_keyword = "generic"
/// If we've already tried scarring while removing (since remove_wound calls qdel, and qdel calls remove wound, .....) TODO: make this cleaner
var/already_scarred = FALSE
/// If we forced this wound through badmin smite, we won't count it towards the round totals
var/from_smite
/// What flags apply to this wound
var/wound_flags = (FLESH_WOUND | BONE_WOUND | ACCEPTS_GAUZE)
/datum/wound/Destroy()
if(attached_surgery)
QDEL_NULL(attached_surgery)
@@ -115,13 +110,13 @@
* * smited- If this is a smite, we don't care about this wound for stat tracking purposes (not yet implemented)
*/
/datum/wound/proc/apply_wound(obj/item/bodypart/L, silent = FALSE, datum/wound/old_wound = null, smited = FALSE)
if(!istype(L) || !L.owner || !(L.body_zone in viable_zones) || isalien(L.owner))
if(!istype(L) || !L.owner || !(L.body_zone in viable_zones) || isalien(L.owner) || !L.is_organic_limb())
qdel(src)
return
if(ishuman(L.owner))
var/mob/living/carbon/human/H = L.owner
if(organic_only && ((NOBLOOD in H.dna.species.species_traits) || !L.is_organic_limb()))
if(((wound_flags & BONE_WOUND) && !(HAS_BONE in H.dna.species.species_traits)) || ((wound_flags & FLESH_WOUND) && !(HAS_FLESH in H.dna.species.species_traits)))
qdel(src)
return
@@ -161,7 +156,7 @@
victim.visible_message(msg, "<span class='userdanger'>Your [limb.name] [occur_text]!</span>", vision_distance = vis_dist)
if(sound_effect)
playsound(L.owner, sound_effect, 60 + 20 * severity, TRUE)
playsound(L.owner, sound_effect, 70 + 20 * severity, TRUE)
if(!demoted)
wound_injury(old_wound)
@@ -181,7 +176,7 @@
SEND_SIGNAL(victim, COMSIG_CARBON_LOSE_WOUND, src, limb)
if(limb && !ignore_limb)
LAZYREMOVE(limb.wounds, src)
limb.update_wounds()
limb.update_wounds(replaced)
/**
* replace_wound() is used when you want to replace the current wound with a new wound, presumably of the same category, just of a different severity (either up or down counts)
@@ -189,7 +184,7 @@
* This proc actually instantiates the new wound based off the specific type path passed, then returns the new instantiated wound datum.
*
* Arguments:
* * new_type- The TYPE PATH of the wound you want to replace this, like /datum/wound/brute/cut/severe
* * new_type- The TYPE PATH of the wound you want to replace this, like /datum/wound/slash/severe
* * smited- If this is a smite, we don't care about this wound for stat tracking purposes (not yet implemented)
*/
/datum/wound/proc/replace_wound(new_type, smited = FALSE)
@@ -206,7 +201,6 @@
/// Additional beneficial effects when the wound is gained, in case you want to give a temporary boost to allow the victim to try an escape or last stand
/datum/wound/proc/second_wind()
switch(severity)
if(WOUND_SEVERITY_MODERATE)
victim.reagents.add_reagent(/datum/reagent/determination, WOUND_DETERMINATION_MODERATE)
@@ -214,11 +208,13 @@
victim.reagents.add_reagent(/datum/reagent/determination, WOUND_DETERMINATION_SEVERE)
if(WOUND_SEVERITY_CRITICAL)
victim.reagents.add_reagent(/datum/reagent/determination, WOUND_DETERMINATION_CRITICAL)
if(WOUND_SEVERITY_LOSS)
victim.reagents.add_reagent(/datum/reagent/determination, WOUND_DETERMINATION_LOSS)
/**
* try_treating() is an intercept run from [/mob/living/carbon/attackby()] right after surgeries but before anything else. Return TRUE here if the item is something that is relevant to treatment to take over the interaction.
* try_treating() is an intercept run from [/mob/living/carbon/proc/attackby] right after surgeries but before anything else. Return TRUE here if the item is something that is relevant to treatment to take over the interaction.
*
* This proc leads into [/datum/wound/proc/treat()] and probably shouldn't be added onto in children types. You can specify what items or tools you want to be intercepted
* This proc leads into [/datum/wound/proc/treat] and probably shouldn't be added onto in children types. You can specify what items or tools you want to be intercepted
* with var/list/treatable_by and var/treatable_tool, then if an item fulfills one of those requirements and our wound claims it first, it goes over to treat() and treat_self().
*
* Arguments:
@@ -258,7 +254,7 @@
treat(I, user)
return TRUE
/// Return TRUE if we have an item that can only be used while aggro grabbed (unhanded aggro grab treatments go in [/datum/wound/proc/try_handling()]). Treatment is still is handled in [/datum/wound/proc/treat()]
/// Return TRUE if we have an item that can only be used while aggro grabbed (unhanded aggro grab treatments go in [/datum/wound/proc/try_handling]). Treatment is still is handled in [/datum/wound/proc/treat]
/datum/wound/proc/check_grab_treatments(obj/item/I, mob/user)
return FALSE
@@ -288,10 +284,22 @@
if(cryo_progress > 33 * severity)
qdel(src)
/// When synthflesh is applied to the victim, we call this. No sense in setting up an entire chem reaction system for wounds when we only care for a few chems. Probably will change in the future
/datum/wound/proc/on_synthflesh(power)
return
/// Called when the patient is undergoing stasis, so that having fully treated a wound doesn't make you sit there helplessly until you think to unbuckle them
/datum/wound/proc/on_stasis()
return
/// Called when we're crushed in an airlock or firedoor, for one of the improvised joint dislocation fixes
/datum/wound/proc/crush()
return
/// Used when we're being dragged while bleeding, the value we return is how much bloodloss this wound causes from being dragged. Since it's a proc, you can let bandages soak some of the blood
/datum/wound/proc/drag_bleed_amount()
return
/**
* get_examine_description() is used in carbon/examine and human/examine to show the status of this wound. Useful if you need to show some status like the wound being splinted or bandaged.
*
@@ -301,7 +309,8 @@
* * mob/user: The user examining the wound's owner, if that matters
*/
/datum/wound/proc/get_examine_description(mob/user)
return "<B>[victim.p_their(TRUE)] [limb.name] [examine_desc]!</B>"
. = "[victim.p_their(TRUE)] [limb.name] [examine_desc]"
. = severity <= WOUND_SEVERITY_MODERATE ? "[.]." : "<B>[.]!</B>"
/datum/wound/proc/get_scanner_description(mob/user)
return "Type: [name]\nSeverity: [severity_text()]\nDescription: [desc]\nRecommended Treatment: [treat_text]"
+82 -115
View File
@@ -1,4 +1,3 @@
/*
Bones
*/
@@ -7,12 +6,10 @@
/*
Base definition
*/
/datum/wound/brute/bone
sound_effect = 'sound/effects/crack1.ogg'
wound_type = WOUND_LIST_BONE
/// The item we're currently splinted with, if there is one
var/obj/item/stack/splinted
/datum/wound/blunt
sound_effect = 'sound/effects/wounds/crack1.ogg'
wound_type = WOUND_BLUNT
wound_flags = (BONE_WOUND | ACCEPTS_GAUZE)
/// Have we been taped?
var/taped
@@ -31,12 +28,12 @@
/// How long do we wait +/- 20% for the next trauma?
var/trauma_cycle_cooldown
/// If this is a chest wound and this is set, we have this chance to cough up blood when hit in the chest
var/chance_internal_bleeding = 0
var/internal_bleeding_chance = 0
/*
Overwriting of base procs
*/
/datum/wound/brute/bone/wound_injury(datum/wound/old_wound = null)
/datum/wound/blunt/wound_injury(datum/wound/old_wound = null)
if(limb.body_zone == BODY_ZONE_HEAD && brain_trauma_group)
processes = TRUE
active_trauma = victim.gain_trauma_type(brain_trauma_group, TRAUMA_RESILIENCE_WOUND)
@@ -53,14 +50,14 @@
update_inefficiencies()
/datum/wound/brute/bone/remove_wound(ignore_limb, replaced)
/datum/wound/blunt/remove_wound(ignore_limb, replaced)
limp_slowdown = 0
QDEL_NULL(active_trauma)
if(victim)
UnregisterSignal(victim, COMSIG_HUMAN_EARLY_UNARMED_ATTACK)
return ..()
/datum/wound/brute/bone/handle_process()
/datum/wound/blunt/handle_process()
. = ..()
if(limb.body_zone == BODY_ZONE_HEAD && brain_trauma_group && world.time > next_trauma_cycle)
if(active_trauma)
@@ -86,7 +83,7 @@
remove_wound()
/// If we're a human who's punching something with a broken arm, we might hurt ourselves doing so
/datum/wound/brute/bone/proc/attack_with_hurt_hand(mob/M, atom/target, proximity)
/datum/wound/blunt/proc/attack_with_hurt_hand(mob/M, atom/target, proximity)
if(victim.get_active_hand() != limb || victim.a_intent == INTENT_HELP || !ismob(target) || severity <= WOUND_SEVERITY_MODERATE)
return
@@ -104,61 +101,54 @@
limb.receive_damage(brute=rand(3,7))
return COMPONENT_NO_ATTACK_HAND
/datum/wound/brute/bone/receive_damage(wounding_type, wounding_dmg, wound_bonus)
if(!victim)
/datum/wound/blunt/receive_damage(wounding_type, wounding_dmg, wound_bonus)
if(!victim || wounding_dmg < WOUND_MINIMUM_DAMAGE)
return
if(ishuman(victim))
var/mob/living/carbon/human/human_victim = victim
if(NOBLOOD in human_victim.dna?.species.species_traits)
return
if(limb.body_zone == BODY_ZONE_CHEST && victim.blood_volume && prob(chance_internal_bleeding + wounding_dmg))
if(limb.body_zone == BODY_ZONE_CHEST && victim.blood_volume && prob(internal_bleeding_chance + wounding_dmg))
var/blood_bled = rand(1, wounding_dmg * (severity == WOUND_SEVERITY_CRITICAL ? 2 : 1.5)) // 12 brute toolbox can cause up to 18/24 bleeding with a severe/critical chest wound
switch(blood_bled)
if(1 to 6)
victim.bleed(blood_bled, TRUE)
if(7 to 13)
victim.visible_message("<span class='danger'>[victim] coughs up a bit of blood from the blow to [victim.p_their()] chest.</span>", "<span class='danger'>You cough up a bit of blood from the blow to your chest.</span>")
victim.visible_message("<span class='smalldanger'>[victim] coughs up a bit of blood from the blow to [victim.p_their()] chest.</span>", "<span class='danger'>You cough up a bit of blood from the blow to your chest.</span>", vision_distance=COMBAT_MESSAGE_RANGE)
victim.bleed(blood_bled, TRUE)
if(14 to 19)
victim.visible_message("<span class='danger'>[victim] spits out a string of blood from the blow to [victim.p_their()] chest!</span>", "<span class='danger'>You spit out a string of blood from the blow to your chest!</span>")
victim.visible_message("<span class='smalldanger'>[victim] spits out a string of blood from the blow to [victim.p_their()] chest!</span>", "<span class='danger'>You spit out a string of blood from the blow to your chest!</span>", vision_distance=COMBAT_MESSAGE_RANGE)
new /obj/effect/temp_visual/dir_setting/bloodsplatter(victim.loc, victim.dir)
victim.bleed(blood_bled)
if(20 to INFINITY)
victim.visible_message("<span class='danger'>[victim] chokes up a spray of blood from the blow to [victim.p_their()] chest!</span>", "<span class='danger'><b>You choke up on a spray of blood from the blow to your chest!</b></span>")
victim.visible_message("<span class='danger'>[victim] chokes up a spray of blood from the blow to [victim.p_their()] chest!</span>", "<span class='danger'><b>You choke up on a spray of blood from the blow to your chest!</b></span>", vision_distance=COMBAT_MESSAGE_RANGE)
victim.bleed(blood_bled)
new /obj/effect/temp_visual/dir_setting/bloodsplatter(victim.loc, victim.dir)
victim.add_splatter_floor(get_step(victim.loc, victim.dir))
if(!(wounding_type in list(WOUND_SHARP, WOUND_BURN)) || !splinted || wound_bonus == CANT_WOUND)
return
splinted.take_damage(wounding_dmg, damage_type = (wounding_type == WOUND_SHARP ? BRUTE : BURN), sound_effect = FALSE)
if(QDELETED(splinted))
var/destroyed_verb = (wounding_type == WOUND_SHARP ? "torn" : "burned")
victim.visible_message("<span class='danger'>The splint securing [victim]'s [limb.name] is [destroyed_verb] away!</span>", "<span class='danger'><b>The splint securing your [limb.name] is [destroyed_verb] away!</b></span>", vision_distance=COMBAT_MESSAGE_RANGE)
splinted = null
treat_priority = TRUE
update_inefficiencies()
/datum/wound/brute/bone/get_examine_description(mob/user)
if(!splinted && !gelled && !taped)
/datum/wound/blunt/get_examine_description(mob/user)
if(!limb.current_gauze && !gelled && !taped)
return ..()
var/msg = ""
if(!splinted)
msg = "<B>[victim.p_their(TRUE)] [limb.name] [examine_desc]"
var/list/msg = list()
if(!limb.current_gauze)
msg += "[victim.p_their(TRUE)] [limb.name] [examine_desc]"
else
var/splint_condition = ""
var/sling_condition = ""
// how much life we have left in these bandages
switch(splinted.obj_integrity / splinted.max_integrity * 100)
switch(limb.current_gauze.obj_integrity / limb.current_gauze.max_integrity * 100)
if(0 to 25)
splint_condition = "just barely "
sling_condition = "just barely "
if(25 to 50)
splint_condition = "loosely "
sling_condition = "loosely "
if(50 to 75)
splint_condition = "mostly "
sling_condition = "mostly "
if(75 to INFINITY)
splint_condition = "tightly "
sling_condition = "tightly "
msg = "<B>[victim.p_their(TRUE)] [limb.name] is [splint_condition] fastened in a splint of [splinted.name]</B>"
msg += "[victim.p_their(TRUE)] [limb.name] is [sling_condition] fastened in a sling of [limb.current_gauze.name]"
if(taped)
msg += ", <span class='notice'>and appears to be reforming itself under some surgical tape!</span>"
@@ -166,58 +156,35 @@
msg += ", <span class='notice'>with fizzing flecks of blue bone gel sparking off the bone!</span>"
else
msg += "!"
return "[msg]</B>"
return "<B>[msg.Join()]</B>"
/*
New common procs for /datum/wound/brute/bone/
New common procs for /datum/wound/blunt/
*/
/datum/wound/brute/bone/proc/update_inefficiencies()
/datum/wound/blunt/proc/update_inefficiencies()
if(limb.body_zone in list(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG))
if(splinted)
limp_slowdown = initial(limp_slowdown) * splinted.splint_factor
if(limb.current_gauze)
limp_slowdown = initial(limp_slowdown) * limb.current_gauze.splint_factor
else
limp_slowdown = initial(limp_slowdown)
victim.apply_status_effect(STATUS_EFFECT_LIMP)
else if(limb.body_zone in list(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
if(splinted)
interaction_efficiency_penalty = 1 + ((interaction_efficiency_penalty - 1) * splinted.splint_factor)
if(limb.current_gauze)
interaction_efficiency_penalty = 1 + ((interaction_efficiency_penalty - 1) * limb.current_gauze.splint_factor)
else
interaction_efficiency_penalty = interaction_efficiency_penalty
if(initial(disabling) && splinted)
disabling = FALSE
else if(initial(disabling))
disabling = TRUE
if(initial(disabling))
disabling = !limb.current_gauze
limb.update_wounds()
/*
BEWARE OF REDUNDANCY AHEAD THAT I MUST PARE DOWN
*/
/datum/wound/brute/bone/proc/splint(obj/item/stack/I, mob/user)
if(splinted && splinted.splint_factor >= I.splint_factor)
to_chat(user, "<span class='warning'>The splint already on [user == victim ? "your" : "[victim]'s"] [limb.name] is better than you can do with [I].</span>")
return
user.visible_message("<span class='danger'>[user] begins splinting [victim]'s [limb.name] with [I].</span>", "<span class='warning'>You begin splinting [user == victim ? "your" : "[victim]'s"] [limb.name] with [I]...</span>")
if(!do_after(user, base_treat_time * (user == victim ? 1.5 : 1), target = victim, extra_checks=CALLBACK(src, .proc/still_exists)))
return
user.visible_message("<span class='green'>[user] finishes splinting [victim]'s [limb.name]!</span>", "<span class='green'>You finish splinting [user == victim ? "your" : "[victim]'s"] [limb.name]!</span>")
treat_priority = FALSE
splinted = new I.type(limb)
splinted.amount = 1
I.use(1)
update_inefficiencies()
/*
Moderate (Joint Dislocation)
*/
/datum/wound/brute/bone/moderate
/datum/wound/blunt/moderate
name = "Joint Dislocation"
desc = "Patient's bone has been unset from socket, causing pain and reduced motor function."
treat_text = "Recommended application of bonesetter to affected limb, though manual relocation by applying an aggressive grab to the patient and helpfully interacting with afflicted limb may suffice."
@@ -226,19 +193,20 @@
severity = WOUND_SEVERITY_MODERATE
viable_zones = list(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
interaction_efficiency_penalty = 1.5
limp_slowdown = 3
threshold_minimum = 35
limp_slowdown = 1.5
threshold_minimum = 45
threshold_penalty = 15
treatable_tool = TOOL_BONESET
status_effect_type = /datum/status_effect/wound/bone/moderate
scarring_descriptions = list("light discoloring", "a slight blue tint")
wound_flags = (BONE_WOUND)
status_effect_type = /datum/status_effect/wound/blunt/moderate
scar_keyword = "bluntmoderate"
/datum/wound/brute/bone/moderate/crush()
/datum/wound/blunt/moderate/crush()
if(prob(33))
victim.visible_message("<span class='danger'>[victim]'s dislocated [limb.name] pops back into place!</span>", "<span class='userdanger'>Your dislocated [limb.name] pops back into place! Ow!</span>")
remove_wound()
/datum/wound/brute/bone/moderate/try_handling(mob/living/carbon/human/user)
/datum/wound/blunt/moderate/try_handling(mob/living/carbon/human/user)
if(user.pulling != victim || user.zone_selected != limb.body_zone || user.a_intent == INTENT_GRAB)
return FALSE
@@ -256,7 +224,7 @@
return TRUE
/// If someone is snapping our dislocated joint back into place by hand with an aggro grab and help intent
/datum/wound/brute/bone/moderate/proc/chiropractice(mob/living/carbon/human/user)
/datum/wound/blunt/moderate/proc/chiropractice(mob/living/carbon/human/user)
var/time = base_treat_time
if(!do_after(user, time, target=victim, extra_checks = CALLBACK(src, .proc/still_exists)))
@@ -275,7 +243,7 @@
chiropractice(user)
/// If someone is snapping our dislocated joint into a fracture by hand with an aggro grab and harm or disarm intent
/datum/wound/brute/bone/moderate/proc/malpractice(mob/living/carbon/human/user)
/datum/wound/blunt/moderate/proc/malpractice(mob/living/carbon/human/user)
var/time = base_treat_time
if(!do_after(user, time, target=victim, extra_checks = CALLBACK(src, .proc/still_exists)))
@@ -293,7 +261,7 @@
malpractice(user)
/datum/wound/brute/bone/moderate/treat(obj/item/I, mob/user)
/datum/wound/blunt/moderate/treat(obj/item/I, mob/user)
if(victim == user)
victim.visible_message("<span class='danger'>[user] begins resetting [victim.p_their()] [limb.name] with [I].</span>", "<span class='warning'>You begin resetting your [limb.name] with [I]...</span>")
else
@@ -317,56 +285,57 @@
Severe (Hairline Fracture)
*/
/datum/wound/brute/bone/severe
/datum/wound/blunt/severe
name = "Hairline Fracture"
desc = "Patient's bone has suffered a crack in the foundation, causing serious pain and reduced limb functionality."
treat_text = "Recommended light surgical application of bone gel, though splinting will prevent worsening situation."
examine_desc = "appears bruised and grotesquely swollen"
treat_text = "Recommended light surgical application of bone gel, though a sling of medical gauze will prevent worsening situation."
examine_desc = "appears grotesquely swollen, its attachment weakened"
occur_text = "sprays chips of bone and develops a nasty looking bruise"
severity = WOUND_SEVERITY_SEVERE
interaction_efficiency_penalty = 2
limp_slowdown = 6
threshold_minimum = 60
limp_slowdown = 4
threshold_minimum = 70
threshold_penalty = 30
treatable_by = list(/obj/item/stack/sticky_tape/surgical, /obj/item/stack/medical/gauze, /obj/item/stack/medical/bone_gel)
status_effect_type = /datum/status_effect/wound/bone/severe
treat_priority = TRUE
scarring_descriptions = list("a faded, fist-sized bruise", "a vaguely triangular peel scar")
treatable_by = list(/obj/item/stack/sticky_tape/surgical, /obj/item/stack/medical/bone_gel)
status_effect_type = /datum/status_effect/wound/blunt/severe
scar_keyword = "bluntsevere"
brain_trauma_group = BRAIN_TRAUMA_MILD
trauma_cycle_cooldown = 1.5 MINUTES
chance_internal_bleeding = 40
internal_bleeding_chance = 40
wound_flags = (BONE_WOUND | ACCEPTS_GAUZE | MANGLES_BONE)
/datum/wound/brute/bone/critical
/datum/wound/blunt/critical
name = "Compound Fracture"
desc = "Patient's bones have suffered multiple gruesome fractures, causing significant pain and near uselessness of limb."
treat_text = "Immediate binding of affected limb, followed by surgical intervention ASAP."
examine_desc = "has a cracked bone sticking out of it"
examine_desc = "is mangled and pulped, seemingly held together by tissue alone"
occur_text = "cracks apart, exposing broken bones to open air"
severity = WOUND_SEVERITY_CRITICAL
interaction_efficiency_penalty = 4
limp_slowdown = 9
sound_effect = 'sound/effects/crack2.ogg'
threshold_minimum = 115
limp_slowdown = 6
sound_effect = 'sound/effects/wounds/crack2.ogg'
threshold_minimum = 125
threshold_penalty = 50
disabling = TRUE
treatable_by = list(/obj/item/stack/sticky_tape/surgical, /obj/item/stack/medical/gauze, /obj/item/stack/medical/bone_gel)
status_effect_type = /datum/status_effect/wound/bone/critical
treat_priority = TRUE
scarring_descriptions = list("a section of janky skin lines and badly healed scars", "a large patch of uneven skin tone", "a cluster of calluses")
treatable_by = list(/obj/item/stack/sticky_tape/surgical, /obj/item/stack/medical/bone_gel)
status_effect_type = /datum/status_effect/wound/blunt/critical
scar_keyword = "bluntcritical"
brain_trauma_group = BRAIN_TRAUMA_SEVERE
trauma_cycle_cooldown = 2.5 MINUTES
chance_internal_bleeding = 60
internal_bleeding_chance = 60
wound_flags = (BONE_WOUND | ACCEPTS_GAUZE | MANGLES_BONE)
// doesn't make much sense for "a" bone to stick out of your head
/datum/wound/brute/bone/critical/apply_wound(obj/item/bodypart/L, silent, datum/wound/old_wound, smited)
/datum/wound/blunt/critical/apply_wound(obj/item/bodypart/L, silent, datum/wound/old_wound, smited)
if(L.body_zone == BODY_ZONE_HEAD)
occur_text = "splits open, exposing a bare, cracked skull through the flesh and blood"
examine_desc = "has an unsettling indent, with bits of skull poking out"
. = ..()
/// if someone is using bone gel on our wound
/datum/wound/brute/bone/proc/gel(obj/item/stack/medical/bone_gel/I, mob/user)
/datum/wound/blunt/proc/gel(obj/item/stack/medical/bone_gel/I, mob/user)
if(gelled)
to_chat(user, "<span class='warning'>[user == victim ? "Your" : "[victim]'s"] [limb.name] is already coated with bone gel!</span>")
return
@@ -385,12 +354,12 @@
var/painkiller_bonus = 0
if(victim.drunkenness)
painkiller_bonus += 5
if(victim.reagents && victim.reagents.has_reagent(/datum/reagent/medicine/morphine))
if(victim.reagents?.has_reagent(/datum/reagent/medicine/morphine))
painkiller_bonus += 10
if(victim.reagents && victim.reagents.has_reagent(/datum/reagent/determination))
if(victim.reagents?.has_reagent(/datum/reagent/determination))
painkiller_bonus += 5
if(prob(25 + (20 * (severity - 2)) - painkiller_bonus)) // 25%/45% chance to fail self-applying with severe and critical wounds, modded by painkillers
if(prob(25 + (20 * severity - 2) - painkiller_bonus)) // 25%/45% chance to fail self-applying with severe and critical wounds, modded by painkillers
victim.visible_message("<span class='danger'>[victim] fails to finish applying [I] to [victim.p_their()] [limb.name], passing out from the pain!</span>", "<span class='notice'>You black out from the pain of applying [I] to your [limb.name] before you can finish!</span>")
victim.AdjustUnconscious(5 SECONDS)
return
@@ -401,7 +370,7 @@
gelled = TRUE
/// if someone is using surgical tape on our wound
/datum/wound/brute/bone/proc/tape(obj/item/stack/sticky_tape/surgical/I, mob/user)
/datum/wound/blunt/proc/tape(obj/item/stack/sticky_tape/surgical/I, mob/user)
if(!gelled)
to_chat(user, "<span class='warning'>[user == victim ? "Your" : "[victim]'s"] [limb.name] must be coated with bone gel to perform this emergency operation!</span>")
return
@@ -426,15 +395,13 @@
taped = TRUE
processes = TRUE
/datum/wound/brute/bone/treat(obj/item/I, mob/user)
/datum/wound/blunt/treat(obj/item/I, mob/user)
if(istype(I, /obj/item/stack/medical/bone_gel))
gel(I, user)
else if(istype(I, /obj/item/stack/sticky_tape/surgical))
tape(I, user)
else if(istype(I, /obj/item/stack/medical/gauze))
splint(I, user)
/datum/wound/brute/bone/get_scanner_description(mob/user)
/datum/wound/blunt/get_scanner_description(mob/user)
. = ..()
. += "<div class='ml-3'>"
@@ -444,7 +411,7 @@
else if(!taped)
. += "<span class='notice'>Continue Alternative Treatment: Apply surgical tape directly to injured limb to begin bone regeneration. Note, this is both excruciatingly painful and slow.</span>\n"
else
. += "<span class='notice'>Note: Bone regeneration in effect. Bone is [round((regen_points_current*100)/regen_points_needed,0.1)]% regenerated.</span>\n"
. += "<span class='notice'>Note: Bone regeneration in effect. Bone is [round(regen_points_current*100/regen_points_needed)]% regenerated.</span>\n"
if(limb.body_zone == BODY_ZONE_HEAD)
. += "Cranial Trauma Detected: Patient will suffer random bouts of [severity == WOUND_SEVERITY_SEVERE ? "mild" : "severe"] brain traumas until bone is repaired."
+42 -69
View File
@@ -1,14 +1,14 @@
// TODO: well, a lot really, but specifically I want to add potential fusing of clothing/equipment on the affected area, and limb infections, though those may go in body part code
/datum/wound/burn
a_or_from = "from"
wound_type = WOUND_LIST_BURN
wound_type = WOUND_BURN
processes = TRUE
sound_effect = 'sound/effects/sizzle1.ogg'
sound_effect = 'sound/effects/wounds/sizzle1.ogg'
wound_flags = (FLESH_WOUND | ACCEPTS_GAUZE)
treatable_by = list(/obj/item/stack/medical/gauze, /obj/item/stack/medical/ointment, /obj/item/stack/medical/mesh) // sterilizer and alcohol will require reagent treatments, coming soon
treatable_by = list(/obj/item/stack/medical/ointment, /obj/item/stack/medical/mesh) // sterilizer and alcohol will require reagent treatments, coming soon
// Flesh damage vars
/// How much damage to our flesh we currently have. Once both this and infestation reach 0, the wound is considered healed
@@ -27,8 +27,6 @@
/// Once we reach infestation beyond WOUND_INFESTATION_SEPSIS, we get this many warnings before the limb is completely paralyzed (you'd have to ignore a really bad burn for a really long time for this to happen)
var/strikes_to_lose_limb = 3
/// The current bandage we have for this wound (maybe move bandages to the limb?)
var/obj/item/stack/current_bandage
/datum/wound/burn/handle_process()
. = ..()
@@ -47,15 +45,11 @@
sanitization += 0.3
flesh_healing += 0.5
if(current_bandage)
current_bandage.absorption_capacity -= WOUND_BURN_SANITIZATION_RATE
if(current_bandage.absorption_capacity <= 0)
victim.visible_message("<span class='danger'>Pus soaks through \the [current_bandage] on [victim]'s [limb.name].</span>", "<span class='warning'>Pus soaks through \the [current_bandage] on your [limb.name].</span>", vision_distance=COMBAT_MESSAGE_RANGE)
QDEL_NULL(current_bandage)
treat_priority = TRUE
if(limb.current_gauze)
limb.seep_gauze(WOUND_BURN_SANITIZATION_RATE)
if(flesh_healing > 0)
var/bandage_factor = (current_bandage ? current_bandage.splint_factor : 1)
var/bandage_factor = (limb.current_gauze ? limb.current_gauze.splint_factor : 1)
flesh_damage = max(0, flesh_damage - 1)
flesh_healing = max(0, flesh_healing - bandage_factor) // good bandages multiply the length of flesh healing
@@ -67,7 +61,7 @@
// sanitization is checked after the clearing check but before the rest, because we freeze the effects of infection while we have sanitization
if(sanitization > 0)
var/bandage_factor = (current_bandage ? current_bandage.splint_factor : 1)
var/bandage_factor = (limb.current_gauze ? limb.current_gauze.splint_factor : 1)
infestation = max(0, infestation - WOUND_BURN_SANITIZATION_RATE)
sanitization = max(0, sanitization - (WOUND_BURN_SANITIZATION_RATE * bandage_factor))
return
@@ -122,10 +116,10 @@
if(strikes_to_lose_limb <= 0)
return "<span class='deadsay'><B>[victim.p_their(TRUE)] [limb.name] is completely dead and unrecognizable as organic.</B></span>"
var/condition = ""
if(current_bandage)
var/list/condition = list("[victim.p_their(TRUE)] [limb.name] [examine_desc]")
if(limb.current_gauze)
var/bandage_condition
switch(current_bandage.absorption_capacity)
switch(limb.current_gauze.absorption_capacity)
if(0 to 1.25)
bandage_condition = "nearly ruined "
if(1.25 to 2.75)
@@ -135,7 +129,7 @@
if(4 to INFINITY)
bandage_condition = "clean "
condition += " underneath a dressing of [bandage_condition] [current_bandage.name]"
condition += " underneath a dressing of [bandage_condition] [limb.current_gauze.name]"
else
switch(infestation)
if(WOUND_INFECTION_MODERATE to WOUND_INFECTION_SEVERE)
@@ -149,7 +143,7 @@
else
condition += "!"
return "<B>[victim.p_their(TRUE)] [limb.name] [examine_desc][condition]</B>"
return "<B>[condition.Join()]</B>"
/datum/wound/burn/get_scanner_description(mob/user)
if(strikes_to_lose_limb == 0)
@@ -186,7 +180,7 @@
/// if someone is using ointment on our burns
/datum/wound/burn/proc/ointment(obj/item/stack/medical/ointment/I, mob/user)
user.visible_message("<span class='notice'>[user] begins applying [I] to [victim]'s [limb.name]...</span>", "<span class='notice'>You begin applying [I] to [user == victim ? "your" : "[victim]'s"] [limb.name]...</span>")
if(!do_after(user, (user == victim ? I.self_delay : I.other_delay), target = victim))
if(!do_after(user, (user == victim ? I.self_delay : I.other_delay), extra_checks = CALLBACK(src, .proc/still_exists)))
return
limb.heal_damage(I.heal_brute, I.heal_burn)
@@ -200,36 +194,6 @@
else
try_treating(I, user)
/// for use in the burn dressing surgery since we don't want to make them do another do_after obviously
/datum/wound/burn/proc/force_bandage(obj/item/stack/medical/gauze/I, mob/user)
QDEL_NULL(current_bandage)
current_bandage = new I.type(limb)
current_bandage.amount = 1
treat_priority = FALSE
sanitization += I.sanitization
I.use(1)
/// if someone is wrapping gauze on our burns
/datum/wound/burn/proc/bandage(obj/item/stack/medical/gauze/I, mob/user)
if(current_bandage)
if(current_bandage.absorption_capacity > I.absorption_capacity + 1)
to_chat(user, "<span class='warning'>The [current_bandage] on [victim]'s [limb.name] is still in better condition than your [I.name]!</span>")
return
user.visible_message("<span class='warning'>[user] begins to redress the burns on [victim]'s [limb.name] with [I]...</span>", "<span class='warning'>You begin redressing the burns on [user == victim ? "your" : "[victim]'s"] [limb.name] with [I]...</span>")
else
user.visible_message("<span class='notice'>[user] begins to dress the burns on [victim]'s [limb.name] with [I]...</span>", "<span class='notice'>You begin dressing the burns on [user == victim ? "your" : "[victim]'s"] [limb.name] with [I]...</span>")
if(!do_after(user, (user == victim ? I.self_delay : I.other_delay), target=victim, extra_checks = CALLBACK(src, .proc/still_exists)))
return
user.visible_message("<span class='green'>[user] applies [I] to [victim].</span>", "<span class='green'>You apply [I] to [user == victim ? "your" : "[victim]'s"] [limb.name].</span>")
QDEL_NULL(current_bandage)
current_bandage = new I.type(limb)
current_bandage.amount = 1
treat_priority = FALSE
sanitization += I.sanitization
I.use(1)
/// if someone is using mesh on our burns
/datum/wound/burn/proc/mesh(obj/item/stack/medical/mesh/I, mob/user)
user.visible_message("<span class='notice'>[user] begins wrapping [victim]'s [limb.name] with [I]...</span>", "<span class='notice'>You begin wrapping [user == victim ? "your" : "[victim]'s"] [limb.name] with [I]...</span>")
@@ -249,7 +213,7 @@
/// Paramedic UV penlights
/datum/wound/burn/proc/uv(obj/item/flashlight/pen/paramedic/I, mob/user)
if(I.uv_cooldown > world.time)
if(!COOLDOWN_FINISHED(I, uv_cooldown))
to_chat(user, "<span class='notice'>[I] is still recharging!</span>")
return
if(infestation <= 0 || infestation < sanitization)
@@ -258,20 +222,29 @@
user.visible_message("<span class='notice'>[user] flashes the burns on [victim]'s [limb] with [I].</span>", "<span class='notice'>You flash the burns on [user == victim ? "your" : "[victim]'s"] [limb.name] with [I].</span>", vision_distance=COMBAT_MESSAGE_RANGE)
sanitization += I.uv_power
I.uv_cooldown = world.time + I.uv_cooldown_length
COOLDOWN_START(I, uv_cooldown, I.uv_cooldown_length)
/datum/wound/burn/treat(obj/item/I, mob/user)
if(istype(I, /obj/item/stack/medical/gauze))
bandage(I, user)
else if(istype(I, /obj/item/stack/medical/ointment))
if(istype(I, /obj/item/stack/medical/ointment))
ointment(I, user)
else if(istype(I, /obj/item/stack/medical/mesh))
mesh(I, user)
else if(istype(I, /obj/item/flashlight/pen/paramedic))
uv(I, user)
/// basic support for instabitaluri/synthflesh healing flesh damage, more chem support in the future
/datum/wound/burn/proc/regenerate_flesh(amount)
// people complained about burns not healing on stasis beds, so in addition to checking if it's cured, they also get the special ability to very slowly heal on stasis beds if they have the healing effects stored
/datum/wound/burn/on_stasis()
. = ..()
if(flesh_healing > 0)
flesh_damage = max(0, flesh_damage - 0.2)
if((flesh_damage <= 0) && (infestation <= 1))
to_chat(victim, "<span class='green'>The burns on your [limb.name] have cleared up!</span>")
qdel(src)
return
if(sanitization > 0)
infestation = max(0, infestation - WOUND_BURN_SANITIZATION_RATE * 0.2)
/datum/wound/burn/on_synthflesh(amount)
flesh_healing += amount * 0.5 // 20u patch will heal 10 flesh standard
// we don't even care about first degree burns, straight to second
@@ -282,12 +255,12 @@
examine_desc = "is badly burned and breaking out in blisters"
occur_text = "breaks out with violent red burns"
severity = WOUND_SEVERITY_MODERATE
damage_mulitplier_penalty = 1.1
threshold_minimum = 40
damage_mulitplier_penalty = 1.05
threshold_minimum = 50
threshold_penalty = 30 // burns cause significant decrease in limb integrity compared to other wounds
status_effect_type = /datum/status_effect/wound/burn/moderate
flesh_damage = 5
scarring_descriptions = list("small amoeba-shaped skinmarks", "a faded streak of depressed skin")
scar_keyword = "burnmoderate"
/datum/wound/burn/severe
name = "Third Degree Burns"
@@ -296,14 +269,14 @@
examine_desc = "appears seriously charred, with aggressive red splotches"
occur_text = "chars rapidly, exposing ruined tissue and spreading angry red burns"
severity = WOUND_SEVERITY_SEVERE
damage_mulitplier_penalty = 1.2
threshold_minimum = 80
damage_mulitplier_penalty = 1.1
threshold_minimum = 90
threshold_penalty = 40
status_effect_type = /datum/status_effect/wound/burn/severe
treatable_by = list(/obj/item/flashlight/pen/paramedic, /obj/item/stack/medical/gauze, /obj/item/stack/medical/ointment, /obj/item/stack/medical/mesh)
treatable_by = list(/obj/item/flashlight/pen/paramedic, /obj/item/stack/medical/ointment, /obj/item/stack/medical/mesh)
infestation_rate = 0.05 // appx 13 minutes to reach sepsis without any treatment
flesh_damage = 12.5
scarring_descriptions = list("a large, jagged patch of faded skin", "random spots of shiny, smooth skin", "spots of taut, leathery skin")
scar_keyword = "burnsevere"
/datum/wound/burn/critical
name = "Catastrophic Burns"
@@ -312,12 +285,12 @@
examine_desc = "is a ruined mess of blanched bone, melted fat, and charred tissue"
occur_text = "vaporizes as flesh, bone, and fat melt together in a horrifying mess"
severity = WOUND_SEVERITY_CRITICAL
damage_mulitplier_penalty = 1.3
sound_effect = 'sound/effects/sizzle2.ogg'
threshold_minimum = 140
damage_mulitplier_penalty = 1.15
sound_effect = 'sound/effects/wounds/sizzle2.ogg'
threshold_minimum = 150
threshold_penalty = 80
status_effect_type = /datum/status_effect/wound/burn/critical
treatable_by = list(/obj/item/flashlight/pen/paramedic, /obj/item/stack/medical/gauze, /obj/item/stack/medical/ointment, /obj/item/stack/medical/mesh)
treatable_by = list(/obj/item/flashlight/pen/paramedic, /obj/item/stack/medical/ointment, /obj/item/stack/medical/mesh)
infestation_rate = 0.15 // appx 4.33 minutes to reach sepsis without any treatment
flesh_damage = 20
scarring_descriptions = list("massive, disfiguring keloid scars", "several long streaks of badly discolored and malformed skin", "unmistakeable splotches of dead tissue from serious burns")
scar_keyword = "burncritical"
+41
View File
@@ -0,0 +1,41 @@
/datum/wound/loss
name = "Dismembered"
desc = "oof ouch!!"
sound_effect = 'sound/effects/dismember.ogg'
severity = WOUND_SEVERITY_LOSS
threshold_minimum = 180
status_effect_type = null
scar_keyword = "dismember"
wound_flags = null
/// Our special proc for our special dismembering, the wounding type only matters for what text we have
/datum/wound/loss/proc/apply_dismember(obj/item/bodypart/dismembered_part, wounding_type=WOUND_SLASH)
if(!istype(dismembered_part) || !dismembered_part.owner || !(dismembered_part.body_zone in viable_zones) || isalien(dismembered_part.owner) || !dismembered_part.can_dismember())
qdel(src)
return
already_scarred = TRUE // so we don't scar a limb we don't have. If I add different levels of amputation desc, do it here
switch(wounding_type)
if(WOUND_BLUNT)
occur_text = "is shattered through the last bone holding it together, severing it completely!"
if(WOUND_SLASH)
occur_text = "is slashed through the last tissue holding it together, severing it completely!"
if(WOUND_PIERCE)
occur_text = "is pierced through the last tissue holding it together, severing it completely!"
if(WOUND_BURN)
occur_text = "is completely incinerated, falling to dust!"
victim = dismembered_part.owner
var/msg = "<span class='bolddanger'>[victim]'s [dismembered_part.name] [occur_text]!</span>"
victim.visible_message(msg, "<span class='userdanger'>Your [dismembered_part.name] [occur_text]!</span>")
limb = dismembered_part
severity = WOUND_SEVERITY_LOSS
second_wind()
log_wound(victim, src)
dismembered_part.dismember(wounding_type == WOUND_BURN ? BURN : BRUTE)
qdel(src)
+170
View File
@@ -0,0 +1,170 @@
/*
Pierce
*/
/datum/wound/pierce
sound_effect = 'sound/weapons/slice.ogg'
processes = TRUE
wound_type = WOUND_PIERCE
treatable_by = list(/obj/item/stack/medical/suture)
treatable_tool = TOOL_CAUTERY
base_treat_time = 3 SECONDS
wound_flags = (FLESH_WOUND | ACCEPTS_GAUZE)
/// How much blood we start losing when this wound is first applied
var/initial_flow
/// If gauzed, what percent of the internal bleeding actually clots of the total absorption rate
var/gauzed_clot_rate
/// When hit on this bodypart, we have this chance of losing some blood + the incoming damage
var/internal_bleeding_chance
/// If we let off blood when hit, the max blood lost is this * the incoming damage
var/internal_bleeding_coefficient
/datum/wound/pierce/wound_injury(datum/wound/old_wound)
blood_flow = initial_flow
/datum/wound/pierce/receive_damage(wounding_type, wounding_dmg, wound_bonus)
if(victim.stat == DEAD || wounding_dmg < 5)
return
if(victim.blood_volume && prob(internal_bleeding_chance + wounding_dmg))
if(limb.current_gauze && limb.current_gauze.splint_factor)
wounding_dmg *= (1 - limb.current_gauze.splint_factor)
var/blood_bled = rand(1, wounding_dmg * internal_bleeding_coefficient) // 12 brute toolbox can cause up to 15/18/21 bloodloss on mod/sev/crit
switch(blood_bled)
if(1 to 6)
victim.bleed(blood_bled, TRUE)
if(7 to 13)
victim.visible_message("<span class='smalldanger'>Blood droplets fly from the hole in [victim]'s [limb.name].</span>", "<span class='danger'>You cough up a bit of blood from the blow to your [limb.name].</span>", vision_distance=COMBAT_MESSAGE_RANGE)
victim.bleed(blood_bled, TRUE)
if(14 to 19)
victim.visible_message("<span class='smalldanger'>A small stream of blood spurts from the hole in [victim]'s [limb.name]!</span>", "<span class='danger'>You spit out a string of blood from the blow to your [limb.name]!</span>", vision_distance=COMBAT_MESSAGE_RANGE)
new /obj/effect/temp_visual/dir_setting/bloodsplatter(victim.loc, victim.dir)
victim.bleed(blood_bled)
if(20 to INFINITY)
victim.visible_message("<span class='danger'>A spray of blood streams from the gash in [victim]'s [limb.name]!</span>", "<span class='danger'><b>You choke up on a spray of blood from the blow to your [limb.name]!</b></span>", vision_distance=COMBAT_MESSAGE_RANGE)
victim.bleed(blood_bled)
new /obj/effect/temp_visual/dir_setting/bloodsplatter(victim.loc, victim.dir)
victim.add_splatter_floor(get_step(victim.loc, victim.dir))
/datum/wound/pierce/handle_process()
blood_flow = min(blood_flow, WOUND_SLASH_MAX_BLOODFLOW)
if(victim.bodytemperature < (BODYTEMP_NORMAL - 10))
blood_flow -= 0.2
if(prob(5))
to_chat(victim, "<span class='notice'>You feel the [lowertext(name)] in your [limb.name] firming up from the cold!</span>")
if(victim.reagents?.has_reagent(/datum/reagent/toxin/heparin))
blood_flow += 0.5 // old herapin used to just add +2 bleed stacks per tick, this adds 0.5 bleed flow to all open cuts which is probably even stronger as long as you can cut them first
if(limb.current_gauze)
blood_flow -= limb.current_gauze.absorption_rate * gauzed_clot_rate
limb.current_gauze.absorption_capacity -= limb.current_gauze.absorption_rate
if(blood_flow <= 0)
qdel(src)
/datum/wound/pierce/on_stasis()
. = ..()
if(blood_flow <= 0)
qdel(src)
/datum/wound/pierce/treat(obj/item/I, mob/user)
if(istype(I, /obj/item/stack/medical/suture))
suture(I, user)
else if(I.tool_behaviour == TOOL_CAUTERY || I.get_temperature() > 300)
tool_cauterize(I, user)
/datum/wound/pierce/on_xadone(power)
. = ..()
blood_flow -= 0.03 * power // i think it's like a minimum of 3 power, so .09 blood_flow reduction per tick is pretty good for 0 effort
/datum/wound/pierce/on_synthflesh(power)
. = ..()
blood_flow -= 0.05 * power // 20u * 0.05 = -1 blood flow, less than with slashes but still good considering smaller bleed rates
/// If someone is using a suture to close this cut
/datum/wound/pierce/proc/suture(obj/item/stack/medical/suture/I, mob/user)
var/self_penalty_mult = (user == victim ? 1.4 : 1)
user.visible_message("<span class='notice'>[user] begins stitching [victim]'s [limb.name] with [I]...</span>", "<span class='notice'>You begin stitching [user == victim ? "your" : "[victim]'s"] [limb.name] with [I]...</span>")
if(!do_after(user, base_treat_time * self_penalty_mult, target=victim, extra_checks = CALLBACK(src, .proc/still_exists)))
return
user.visible_message("<span class='green'>[user] stitches up some of the bleeding on [victim].</span>", "<span class='green'>You stitch up some of the bleeding on [user == victim ? "yourself" : "[victim]"].</span>")
var/blood_sutured = I.stop_bleeding / self_penalty_mult * 0.5
blood_flow -= blood_sutured
limb.heal_damage(I.heal_brute, I.heal_burn)
if(blood_flow > 0)
try_treating(I, user)
else
to_chat(user, "<span class='green'>You successfully close the hole in [user == victim ? "your" : "[victim]'s"] [limb.name].</span>")
/// If someone is using either a cautery tool or something with heat to cauterize this pierce
/datum/wound/pierce/proc/tool_cauterize(obj/item/I, mob/user)
var/self_penalty_mult = (user == victim ? 1.5 : 1)
user.visible_message("<span class='danger'>[user] begins cauterizing [victim]'s [limb.name] with [I]...</span>", "<span class='danger'>You begin cauterizing [user == victim ? "your" : "[victim]'s"] [limb.name] with [I]...</span>")
if(!do_after(user, base_treat_time * self_penalty_mult, target=victim, extra_checks = CALLBACK(src, .proc/still_exists)))
return
user.visible_message("<span class='green'>[user] cauterizes some of the bleeding on [victim].</span>", "<span class='green'>You cauterize some of the bleeding on [victim].</span>")
limb.receive_damage(burn = 2 + severity, wound_bonus = CANT_WOUND)
if(prob(30))
victim.emote("scream")
var/blood_cauterized = (0.6 / self_penalty_mult) * 0.5
blood_flow -= blood_cauterized
if(blood_flow > 0)
try_treating(I, user)
/datum/wound/pierce/moderate
name = "Minor Breakage"
desc = "Patient's skin has been broken open, causing severe bruising and minor internal bleeding in affected area."
treat_text = "Treat affected site with bandaging or exposure to extreme cold. In dire cases, brief exposure to vacuum may suffice." // space is cold in ss13, so it's like an ice pack!
examine_desc = "has a small, circular hole, gently bleeding"
occur_text = "spurts out a thin stream of blood"
sound_effect = 'sound/effects/wounds/pierce1.ogg'
severity = WOUND_SEVERITY_MODERATE
initial_flow = 1.5
gauzed_clot_rate = 0.8
internal_bleeding_chance = 30
internal_bleeding_coefficient = 1.25
threshold_minimum = 40
threshold_penalty = 15
status_effect_type = /datum/status_effect/wound/pierce/moderate
scar_keyword = "piercemoderate"
/datum/wound/pierce/severe
name = "Open Puncture"
desc = "Patient's internal tissue is penetrated, causing sizeable internal bleeding and reduced limb stability."
treat_text = "Repair punctures in skin by suture or cautery, extreme cold may also work."
examine_desc = "is pierced clear through, with bits of tissue obscuring the open hole"
occur_text = "looses a violent spray of blood, revealing a pierced wound"
sound_effect = 'sound/effects/wounds/pierce2.ogg'
severity = WOUND_SEVERITY_SEVERE
initial_flow = 2.25
gauzed_clot_rate = 0.6
internal_bleeding_chance = 60
internal_bleeding_coefficient = 1.5
threshold_minimum = 60
threshold_penalty = 25
status_effect_type = /datum/status_effect/wound/pierce/severe
scar_keyword = "piercesevere"
/datum/wound/pierce/critical
name = "Ruptured Cavity"
desc = "Patient's internal tissue and circulatory system is shredded, causing significant internal bleeding and damage to internal organs."
treat_text = "Surgical repair of puncture wound, followed by supervised resanguination."
examine_desc = "is ripped clear through, barely held together by exposed bone"
occur_text = "blasts apart, sending chunks of viscera flying in all directions"
sound_effect = 'sound/effects/wounds/pierce3.ogg'
severity = WOUND_SEVERITY_CRITICAL
initial_flow = 3
gauzed_clot_rate = 0.4
internal_bleeding_chance = 80
internal_bleeding_coefficient = 1.75
threshold_minimum = 110
threshold_penalty = 40
status_effect_type = /datum/status_effect/wound/pierce/critical
scar_keyword = "piercecritical"
wound_flags = (FLESH_WOUND | ACCEPTS_GAUZE | MANGLES_FLESH)
@@ -1,17 +1,16 @@
/*
Cuts
*/
/datum/wound/brute/cut
/datum/wound/slash
sound_effect = 'sound/weapons/slice.ogg'
processes = TRUE
wound_type = WOUND_LIST_CUT
treatable_by = list(/obj/item/stack/medical/suture, /obj/item/stack/medical/gauze)
wound_type = WOUND_SLASH
treatable_by = list(/obj/item/stack/medical/suture)
treatable_by_grabbed = list(/obj/item/gun/energy/laser)
treatable_tool = TOOL_CAUTERY
treat_priority = TRUE
base_treat_time = 3 SECONDS
wound_flags = (FLESH_WOUND | ACCEPTS_GAUZE)
/// How much blood we start losing when this wound is first applied
var/initial_flow
@@ -27,75 +26,82 @@
var/max_per_type
/// The maximum flow we've had so far
var/highest_flow
/// How much flow we've already cauterized
var/cauterized
/// How much flow we've already sutured
var/sutured
/// The current bandage we have for this wound (maybe move bandages to the limb?)
var/obj/item/stack/current_bandage
/// A bad system I'm using to track the worst scar we earned (since we can demote, we want the biggest our wound has been, not what it was when it was cured (probably moderate))
var/datum/scar/highest_scar
/datum/wound/brute/cut/wound_injury(datum/wound/brute/cut/old_wound = null)
/datum/wound/slash/wound_injury(datum/wound/slash/old_wound = null)
blood_flow = initial_flow
if(old_wound)
blood_flow = max(old_wound.blood_flow, initial_flow)
if(old_wound.severity > severity && old_wound.highest_scar)
highest_scar = old_wound.highest_scar
old_wound.highest_scar = null
if(old_wound.current_bandage)
current_bandage = old_wound.current_bandage
old_wound.current_bandage = null
if(!highest_scar)
highest_scar = new
highest_scar.generate(limb, src, add_to_scars=FALSE)
/datum/wound/brute/cut/remove_wound(ignore_limb, replaced)
/datum/wound/slash/remove_wound(ignore_limb, replaced)
if(!replaced && highest_scar)
already_scarred = TRUE
highest_scar.lazy_attach(limb)
return ..()
/datum/wound/brute/cut/get_examine_description(mob/user)
if(!current_bandage)
/datum/wound/slash/get_examine_description(mob/user)
if(!limb.current_gauze)
return ..()
var/bandage_condition = ""
var/list/msg = list("The cuts on [victim.p_their()] [limb.name] are wrapped with")
// how much life we have left in these bandages
switch(current_bandage.absorption_capacity)
switch(limb.current_gauze.absorption_capacity)
if(0 to 1.25)
bandage_condition = "nearly ruined "
msg += "nearly ruined "
if(1.25 to 2.75)
bandage_condition = "badly worn "
msg += "badly worn "
if(2.75 to 4)
bandage_condition = "slightly bloodied "
msg += "slightly bloodied "
if(4 to INFINITY)
bandage_condition = "clean "
return "<B>The cuts on [victim.p_their()] [limb.name] are wrapped with [bandage_condition] [current_bandage.name]!</B>"
msg += "clean "
msg += "[limb.current_gauze.name]!"
/datum/wound/brute/cut/receive_damage(wounding_type, wounding_dmg, wound_bonus)
if(victim.stat != DEAD && wounding_type == WOUND_SHARP) // can't stab dead bodies to make it bleed faster this way
return "<B>[msg.Join()]</B>"
/datum/wound/slash/receive_damage(wounding_type, wounding_dmg, wound_bonus)
if(victim.stat != DEAD && wounding_type == WOUND_SLASH) // can't stab dead bodies to make it bleed faster this way
blood_flow += 0.05 * wounding_dmg
/datum/wound/brute/cut/handle_process()
blood_flow = min(blood_flow, WOUND_CUT_MAX_BLOODFLOW)
/datum/wound/slash/drag_bleed_amount()
// say we have 3 severe cuts with 3 blood flow each, pretty reasonable
// compare with being at 100 brute damage before, where you bled (brute/100 * 2), = 2 blood per tile
var/bleed_amt = min(blood_flow * 0.1, 1) // 3 * 3 * 0.1 = 0.9 blood total, less than before! the share here is .3 blood of course.
if(victim.reagents && victim.reagents.has_reagent(/datum/reagent/toxin/heparin))
if(limb.current_gauze) // gauze stops all bleeding from dragging on this limb, but wears the gauze out quicker
limb.seep_gauze(bleed_amt * 0.33)
return
return bleed_amt
/datum/wound/slash/handle_process()
if(victim.stat == DEAD)
blood_flow -= max(clot_rate, WOUND_SLASH_DEAD_CLOT_MIN)
if(blood_flow < minimum_flow)
if(demotes_to)
replace_wound(demotes_to)
return
qdel(src)
return
blood_flow = min(blood_flow, WOUND_SLASH_MAX_BLOODFLOW)
if(victim.reagents?.has_reagent(/datum/reagent/toxin/heparin))
blood_flow += 0.5 // old herapin used to just add +2 bleed stacks per tick, this adds 0.5 bleed flow to all open cuts which is probably even stronger as long as you can cut them first
else if(victim.reagents && victim.reagents.has_reagent(/datum/reagent/medicine/coagulant))
blood_flow -= 0.25
if(current_bandage)
if(limb.current_gauze)
if(clot_rate > 0)
blood_flow -= clot_rate
blood_flow -= current_bandage.absorption_rate
current_bandage.absorption_capacity -= current_bandage.absorption_rate
if(current_bandage.absorption_capacity < 0)
victim.visible_message("<span class='danger'>Blood soaks through \the [current_bandage] on [victim]'s [limb.name].</span>", "<span class='warning'>Blood soaks through \the [current_bandage] on your [limb.name].</span>", vision_distance=COMBAT_MESSAGE_RANGE)
QDEL_NULL(current_bandage)
treat_priority = TRUE
blood_flow -= limb.current_gauze.absorption_rate
limb.seep_gauze(limb.current_gauze.absorption_rate)
else
blood_flow -= clot_rate
@@ -109,41 +115,57 @@
to_chat(victim, "<span class='green'>The cut on your [limb.name] has stopped bleeding!</span>")
qdel(src)
/datum/wound/slash/on_stasis()
if(blood_flow >= minimum_flow)
return
if(demotes_to)
replace_wound(demotes_to)
return
qdel(src)
/* BEWARE, THE BELOW NONSENSE IS MADNESS. bones.dm looks more like what I have in mind and is sufficiently clean, don't pay attention to this messiness */
/datum/wound/brute/cut/check_grab_treatments(obj/item/I, mob/user)
/datum/wound/slash/check_grab_treatments(obj/item/I, mob/user)
if(istype(I, /obj/item/gun/energy/laser))
return TRUE
/datum/wound/brute/cut/treat(obj/item/I, mob/user)
/datum/wound/slash/treat(obj/item/I, mob/user)
if(istype(I, /obj/item/gun/energy/laser))
las_cauterize(I, user)
else if(I.tool_behaviour == TOOL_CAUTERY || I.get_temperature() > 300)
tool_cauterize(I, user)
else if(istype(I, /obj/item/stack/medical/gauze))
bandage(I, user)
else if(istype(I, /obj/item/stack/medical/suture))
suture(I, user)
/datum/wound/brute/cut/try_handling(mob/living/carbon/human/user)
/datum/wound/slash/try_handling(mob/living/carbon/human/user)
if(user.pulling != victim || user.zone_selected != limb.body_zone || user.a_intent == INTENT_GRAB)
return FALSE
if(!iscatperson(user))
return FALSE
if(!(user.client?.prefs.vore_flags & LICKABLE))
return FALSE
lick_wounds(user)
return TRUE
/// if a felinid is licking this cut to reduce bleeding
/datum/wound/brute/cut/proc/lick_wounds(mob/living/carbon/human/user)
/datum/wound/slash/proc/lick_wounds(mob/living/carbon/human/user)
if(INTERACTING_WITH(user, victim))
to_chat(user, "<span class='warning'>You're already interacting with [victim]!</span>")
return
if(user.is_mouth_covered())
to_chat(user, "<span class='warning'>Your mouth is covered, you can't lick [victim]'s wounds!</span>")
return
if(!user.getorganslot(ORGAN_SLOT_TONGUE))
to_chat(user, "<span class='warning'>You can't lick wounds without a tongue!</span>") // f in chat
return
// transmission is one way patient -> felinid since google said cat saliva is antiseptic or whatever, and also because felinids are already risking getting beaten for this even without people suspecting they're spreading a deathvirus
for(var/datum/disease/D in victim.diseases)
user.ForceContractDisease(D)
user.visible_message("<span class='notice'>[user] begins licking the wounds on [victim]'s [limb.name].</span>", "<span class='notice'>You begin licking the wounds on [victim]'s [limb.name]...</span>", ignored_mobs=victim)
to_chat(victim, "<span class='notice'>[user] begins to lick the wounds on your [limb.name].</span")
if(!do_after(user, base_treat_time, target=victim, extra_checks = CALLBACK(src, .proc/still_exists)))
@@ -163,12 +185,16 @@
else if(demotes_to)
to_chat(user, "<span class='green'>You successfully lower the severity of [victim]'s cuts.</span>")
/datum/wound/brute/cut/on_xadone(power)
/datum/wound/slash/on_xadone(power)
. = ..()
blood_flow -= 0.03 * power // i think it's like a minimum of 3 power, so .09 blood_flow reduction per tick is pretty good for 0 effort
/datum/wound/slash/on_synthflesh(power)
. = ..()
blood_flow -= 0.075 * power // 20u * 0.075 = -1.5 blood flow, pretty good for how little effort it is
/// If someone's putting a laser gun up to our cut to cauterize it
/datum/wound/brute/cut/proc/las_cauterize(obj/item/gun/energy/laser/lasgun, mob/user)
/datum/wound/slash/proc/las_cauterize(obj/item/gun/energy/laser/lasgun, mob/user)
var/self_penalty_mult = (user == victim ? 1.25 : 1)
user.visible_message("<span class='warning'>[user] begins aiming [lasgun] directly at [victim]'s [limb.name]...</span>", "<span class='userdanger'>You begin aiming [lasgun] directly at [user == victim ? "your" : "[victim]'s"] [limb.name]...</span>")
if(!do_after(user, base_treat_time * self_penalty_mult, target=victim, extra_checks = CALLBACK(src, .proc/still_exists)))
@@ -180,11 +206,10 @@
return
victim.emote("scream")
blood_flow -= damage / (5 * self_penalty_mult) // 20 / 5 = 4 bloodflow removed, p good
cauterized += damage / (5 * self_penalty_mult)
victim.visible_message("<span class='warning'>The cuts on [victim]'s [limb.name] scar over!</span>")
/// If someone is using either a cautery tool or something with heat to cauterize this cut
/datum/wound/brute/cut/proc/tool_cauterize(obj/item/I, mob/user)
/datum/wound/slash/proc/tool_cauterize(obj/item/I, mob/user)
var/self_penalty_mult = (user == victim ? 1.5 : 1)
user.visible_message("<span class='danger'>[user] begins cauterizing [victim]'s [limb.name] with [I]...</span>", "<span class='danger'>You begin cauterizing [user == victim ? "your" : "[victim]'s"] [limb.name] with [I]...</span>")
if(!do_after(user, base_treat_time * self_penalty_mult, target=victim, extra_checks = CALLBACK(src, .proc/still_exists)))
@@ -196,7 +221,6 @@
victim.emote("scream")
var/blood_cauterized = (0.6 / self_penalty_mult)
blood_flow -= blood_cauterized
cauterized += blood_cauterized
if(blood_flow > minimum_flow)
try_treating(I, user)
@@ -204,15 +228,15 @@
to_chat(user, "<span class='green'>You successfully lower the severity of [user == victim ? "your" : "[victim]'s"] cuts.</span>")
/// If someone is using a suture to close this cut
/datum/wound/brute/cut/proc/suture(obj/item/stack/medical/suture/I, mob/user)
/datum/wound/slash/proc/suture(obj/item/stack/medical/suture/I, mob/user)
var/self_penalty_mult = (user == victim ? 1.4 : 1)
user.visible_message("<span class='notice'>[user] begins stitching [victim]'s [limb.name] with [I]...</span>", "<span class='notice'>You begin stitching [user == victim ? "your" : "[victim]'s"] [limb.name] with [I]...</span>")
if(!do_after(user, base_treat_time * self_penalty_mult, target=victim, extra_checks = CALLBACK(src, .proc/still_exists)))
return
user.visible_message("<span class='green'>[user] stitches up some of the bleeding on [victim].</span>", "<span class='green'>You stitch up some of the bleeding on [user == victim ? "yourself" : "[victim]"].</span>")
var/blood_sutured = I.stop_bleeding / self_penalty_mult
blood_flow -= blood_sutured
sutured += blood_sutured
limb.heal_damage(I.heal_brute, I.heal_burn)
if(blood_flow > minimum_flow)
@@ -220,95 +244,57 @@
else if(demotes_to)
to_chat(user, "<span class='green'>You successfully lower the severity of [user == victim ? "your" : "[victim]'s"] cuts.</span>")
/// If someone is using gauze on this cut
/datum/wound/brute/cut/proc/bandage(obj/item/stack/I, mob/user)
if(current_bandage)
if(current_bandage.absorption_capacity > I.absorption_capacity + 1)
to_chat(user, "<span class='warning'>The [current_bandage] on [victim]'s [limb.name] is still in better condition than your [I.name]!</span>")
return
else
user.visible_message("<span class='warning'>[user] begins rewrapping the cuts on [victim]'s [limb.name] with [I]...</span>", "<span class='warning'>You begin rewrapping the cuts on [user == victim ? "your" : "[victim]'s"] [limb.name] with [I]...</span>")
else
user.visible_message("<span class='warning'>[user] begins wrapping the cuts on [victim]'s [limb.name] with [I]...</span>", "<span class='warning'>You begin wrapping the cuts on [user == victim ? "your" : "[victim]'s"] [limb.name] with [I]...</span>")
if(!do_after(user, base_treat_time, target=victim, extra_checks = CALLBACK(src, .proc/still_exists)))
return
user.visible_message("<span class='green'>[user] applies [I] to [victim]'s [limb.name].</span>", "<span class='green'>You bandage some of the bleeding on [user == victim ? "yourself" : "[victim]"].</span>")
QDEL_NULL(current_bandage)
current_bandage = new I.type(limb)
current_bandage.amount = 1
treat_priority = FALSE
I.use(1)
/datum/wound/brute/cut/moderate
/datum/wound/slash/moderate
name = "Rough Abrasion"
desc = "Patient's skin has been badly scraped, generating moderate blood loss."
treat_text = "Application of clean bandages or first-aid grade sutures, followed by food and rest."
examine_desc = "has an open cut"
occur_text = "is cut open, slowly leaking blood"
sound_effect = 'sound/effects/blood1.ogg'
sound_effect = 'sound/effects/wounds/blood1.ogg'
severity = WOUND_SEVERITY_MODERATE
initial_flow = 2
minimum_flow = 0.5
initial_flow = 1.5
minimum_flow = 0.375
max_per_type = 3
clot_rate = 0.15
threshold_minimum = 20
clot_rate = 0.12
threshold_minimum = 30
threshold_penalty = 10
status_effect_type = /datum/status_effect/wound/cut/moderate
scarring_descriptions = list("light, faded lines", "minor cut marks", "a small faded slit", "a series of small scars")
status_effect_type = /datum/status_effect/wound/slash/moderate
scar_keyword = "slashmoderate"
/datum/wound/brute/cut/severe
/datum/wound/slash/severe
name = "Open Laceration"
desc = "Patient's skin is ripped clean open, allowing significant blood loss."
treat_text = "Speedy application of first-aid grade sutures and clean bandages, followed by vitals monitoring to ensure recovery."
examine_desc = "has a severe cut"
occur_text = "is ripped open, veins spurting blood"
sound_effect = 'sound/effects/blood2.ogg'
sound_effect = 'sound/effects/wounds/blood2.ogg'
severity = WOUND_SEVERITY_SEVERE
initial_flow = 3.25
minimum_flow = 2.75
initial_flow = 2.4375
minimum_flow = 2.0625
clot_rate = 0.07
max_per_type = 4
threshold_minimum = 50
threshold_minimum = 60
threshold_penalty = 25
demotes_to = /datum/wound/brute/cut/moderate
status_effect_type = /datum/status_effect/wound/cut/severe
scarring_descriptions = list("a twisted line of faded gashes", "a gnarled sickle-shaped slice scar", "a long-faded puncture wound")
demotes_to = /datum/wound/slash/moderate
status_effect_type = /datum/status_effect/wound/slash/severe
scar_keyword = "slashsevere"
/datum/wound/brute/cut/critical
/datum/wound/slash/critical
name = "Weeping Avulsion"
desc = "Patient's skin is completely torn open, along with significant loss of tissue. Extreme blood loss will lead to quick death without intervention."
treat_text = "Immediate bandaging and either suturing or cauterization, followed by supervised resanguination."
examine_desc = "is spurting blood at an alarming rate"
examine_desc = "is carved down to the bone, spraying blood wildly"
occur_text = "is torn open, spraying blood wildly"
sound_effect = 'sound/effects/blood3.ogg'
sound_effect = 'sound/effects/wounds/blood3.ogg'
severity = WOUND_SEVERITY_CRITICAL
initial_flow = 4.25
minimum_flow = 4
initial_flow = 3.1875
minimum_flow = 3
clot_rate = -0.05 // critical cuts actively get worse instead of better
max_per_type = 5
threshold_minimum = 80
threshold_minimum = 90
threshold_penalty = 40
demotes_to = /datum/wound/brute/cut/severe
status_effect_type = /datum/status_effect/wound/cut/critical
scarring_descriptions = list("a winding path of very badly healed scar tissue", "a series of peaks and valleys along a gruesome line of cut scar tissue", "a grotesque snake of indentations and stitching scars")
// TODO: see about moving dismemberment over to this, i'll have to add judging dismembering power/wound potential wrt item size i guess
/datum/wound/brute/cut/loss
name = "Dismembered"
desc = "oof ouch!!"
occur_text = "is violently dismembered!"
sound_effect = 'sound/effects/dismember.ogg'
viable_zones = list(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
severity = WOUND_SEVERITY_LOSS
threshold_minimum = 180
status_effect_type = null
/datum/wound/brute/cut/loss/apply_wound(obj/item/bodypart/L, silent, datum/wound/brute/cut/old_wound, smited = FALSE)
if(!L.dismemberable)
qdel(src)
return
L.dismember()
qdel(src)
demotes_to = /datum/wound/slash/severe
status_effect_type = /datum/status_effect/wound/slash/critical
scar_keyword = "slashcritical"
wound_flags = (FLESH_WOUND | ACCEPTS_GAUZE | MANGLES_FLESH)