Merge remote-tracking branch 'upstream/master' into robotic-limbs-PT2

This commit is contained in:
DeltaFire
2020-10-11 15:39:41 +02:00
187 changed files with 2386 additions and 950 deletions
@@ -6,6 +6,10 @@
Shield
Armor
Tentacles
Hatterhat Additions:
claws! (glove slot)
jagged (thieves') claws
bone gauntlets for punching dudes
*/
@@ -89,7 +93,7 @@
return 1
var/mob/living/carbon/human/H = user
if(istype(H.wear_suit, suit_type) || istype(H.head, helmet_type))
H.visible_message("<span class='warning'>[H] casts off [H.p_their()] [suit_name_simple]!</span>", "<span class='warning'>We cast off our [suit_name_simple].</span>", "<span class='italics'>You hear the organic matter ripping and tearing!</span>")
H.visible_message("<span class='warning'>[H] casts off [H.p_their()] [suit_name_simple]!</span>", "<span class='warning'>We cast off our [suit_name_simple].</span>", "<span class='italics'>You hear organic matter ripping and tearing!</span>")
H.temporarilyRemoveItemFromInventory(H.head, TRUE) //The qdel on dropped() takes care of it
H.temporarilyRemoveItemFromInventory(H.wear_suit, TRUE)
H.update_inv_wear_suit()
@@ -461,7 +465,7 @@
if(--remaining_uses < 1)
if(ishuman(loc))
var/mob/living/carbon/human/H = loc
H.visible_message("<span class='warning'>With a sickening crunch, [H] reforms [H.p_their()] shield into an arm!</span>", "<span class='notice'>We assimilate our shield into our body</span>", "<span class='italics>You hear organic matter ripping and tearing!</span>")
H.visible_message("<span class='warning'>With a sickening crunch, [H] reforms [H.p_their()] shield into an arm!</span>", "<span class='notice'>We assimilate our shield into our body.</span>", "<span class='italics>You hear organic matter ripping and tearing!</span>")
qdel(src)
/***************************************\
@@ -570,3 +574,165 @@
/obj/item/clothing/head/helmet/changeling/Initialize()
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, CHANGELING_TRAIT)
/***************************************\
|*****************CLAWS*****************|
\***************************************/
/obj/effect/proc_holder/changeling/gloves
name = "Mangled Claws"
desc = "Go tell a coder if you see this"
helptext = "Yell at Hatterhat, for fucking up Miauw and/or Perakp's work"
chemical_cost = 1000
dna_cost = -1
var/glove_type = /obj/item
var/glove_name_simple = " " // keep these plural bro
var/recharge_slowdown = 0
var/blood_on_castoff = 0
/obj/effect/proc_holder/changeling/gloves/try_to_sting(mob/user, mob/target)
if(check_gloves(user))
return
var/mob/living/carbon/human/H = user
..(H, target)
//checks if we already have claws and casts it off
/obj/effect/proc_holder/changeling/gloves/proc/check_gloves(mob/user)
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
if(!ishuman(user) || !changeling)
return 1
var/mob/living/carbon/human/H = user
if(istype(H.gloves, glove_type))
H.visible_message("<span class='warning'>With a sickening crunch, [H] reforms [H.p_their()] [glove_name_simple] into hands!</span>", "<span class='warning'>We assimilate our [glove_name_simple].</span>", "<span class='italics'>You hear organic matter ripping and tearing!</span>")
H.temporarilyRemoveItemFromInventory(H.gloves, TRUE) //The qdel on dropped() takes care of it
H.update_inv_gloves()
playsound(H.loc, 'sound/effects/blobattack.ogg', 30, 1)
if(blood_on_castoff)
H.add_splatter_floor()
playsound(H.loc, 'sound/effects/splat.ogg', 50, 1) //So real sounds
changeling.chem_recharge_slowdown -= recharge_slowdown
return 1
/obj/effect/proc_holder/changeling/gloves/on_refund(mob/user)
if(!ishuman(user))
return
action.Remove(user)
var/mob/living/carbon/human/H = user
check_gloves(H)
/obj/effect/proc_holder/changeling/gloves/sting_action(mob/living/carbon/human/user)
if(!user.canUnEquip(user.gloves))
to_chat(user, "\the [user.gloves] is stuck to your body, you cannot grow [glove_name_simple] over it!")
return
user.dropItemToGround(user.gloves)
user.equip_to_slot_if_possible(new glove_type(user), SLOT_GLOVES, 1, 1, 1)
playsound(user, 'sound/effects/blobattack.ogg', 30, 1)
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
changeling.chem_recharge_slowdown += recharge_slowdown
return TRUE
/obj/item/clothing/gloves/claws
name = "claws of doing nothing"
desc = "These shouldn't be here."
icon_state = "bracers"
item_state = "bracers"
transfer_prints = TRUE
body_parts_covered = HANDS
cold_protection = HANDS
min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT
max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT
armor = list("melee" = 20, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 35, "bio" = 35, "rad" = 35, "fire" = 0, "acid" = 0)
/obj/item/clothing/gloves/claws/Initialize()
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, CHANGELING_TRAIT)
/obj/item/clothing/gloves/claws/vulture // prying shit off dead/soon to be dead dudes!
name = "jagged claws"
desc = "Good for prying things off of people and looking incredibly creepy."
strip_mod = 2
/obj/effect/proc_holder/changeling/gloves/gauntlets
name = "Bone Gauntlets"
desc = "We turn our hands into solid bone and chitin, sacrificing dexterity for raw strength."
helptext = "These grotesque, bone-and-chitin gauntlets are remarkably good at beating victims senseless, and cannot be used in lesser form. This ability is loud, and might cause our blood to react violently to heat."
chemical_cost = 10 // same cost as armblade because its a sidegrade (sacrifice utility for punching people violently)
dna_cost = 2
loudness = 2
req_human = 1
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_gauntlets"
action_background_icon_state = "bg_ling"
glove_type = /obj/item/clothing/gloves/fingerless/pugilist/cling // just punch his head off dude
glove_name_simple = "bone gauntlets"
/obj/item/clothing/gloves/fingerless/pugilist/cling // switches between lesser GotNS and Big Punchy Rib Breaky Hands
name = "hewn bone gauntlets"
icon_state = "ling_gauntlets"
item_state = "ling_gauntlets"
desc = "Rough bone and chitin, pulsing with an abomination barely called \"life\". Good for punching people, not so much for firearms."
transfer_prints = TRUE
body_parts_covered = ARMS|HANDS
cold_protection = ARMS|HANDS
min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT
max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT
armor = list("melee" = 20, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 35, "bio" = 35, "rad" = 35, "fire" = 0, "acid" = 0)
enhancement = 9 // first, do harm. all of it. all of the harm. just fuck em up.
wound_enhancement = 9
var/fast_enhancement = 9
var/fast_wound_enhancement = 9
var/slow_enhancement = 20
var/slow_wound_enhancement = 20
silent = TRUE
inherited_trait = TRAIT_CHUNKYFINGERS // dummy thicc bone hands
secondary_trait = TRAIT_MAULER // its only violence from here, bucko
var/fasthands = TRUE
/obj/item/clothing/gloves/fingerless/pugilist/cling/examine(mob/user)
. = ..()
. += "[src] are formed to allow for [fasthands ? "fast, precise strikes" : "crippling, damaging blows"]."
. += "Alt-click them to change between rapid strikes and strong blows."
/obj/item/clothing/gloves/fingerless/pugilist/cling/AltClick(mob/user)
. = ..()
use_buffs(user, FALSE) // reset
fasthands = !fasthands
if(fasthands)
enhancement = fast_enhancement
wound_enhancement = fast_wound_enhancement
else
enhancement = slow_enhancement // fuck em up kiddo
wound_enhancement = slow_wound_enhancement // really. fuck em up.
to_chat(user, "<span class='notice'>[src] are now formed to allow for [fasthands ? "fast, precise strikes" : "crippling, damaging blows"].</span>")
addtimer(CALLBACK(src, .proc/use_buffs, user, TRUE), 0.1) // go fuckin get em
/obj/item/clothing/gloves/fingerless/pugilist/cling/Initialize()
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, CHANGELING_TRAIT)
/obj/item/clothing/gloves/fingerless/pugilist/cling/equipped(mob/user, slot)
. = ..()
if(current_equipped_slot == SLOT_GLOVES)
to_chat(user, "<span class='notice'>With [src] formed around our arms, we are ready to fight.</span>")
/obj/item/clothing/gloves/fingerless/pugilist/cling/dropped(mob/user)
. = ..()
if(wornonce)
to_chat(user, "<span class='warning'>With [src] assimilated, we feel less ready to punch things.</span>")
/obj/item/clothing/gloves/fingerless/pugilist/cling/Touch(atom/target, proximity = TRUE)
if(!isliving(target))
return
var/mob/living/M = loc
if(fasthands)
M.SetNextAction(CLICK_CD_RANGE) // fast punches
else
M.SetNextAction(CLICK_CD_GRABBING) // strong punches
return NO_AUTO_CLICKDELAY_HANDLING | ATTACK_IGNORE_ACTION
+1 -3
View File
@@ -45,13 +45,12 @@
var/dynamic_fhair_suffix = ""//mask > head for facial hair
//basically a restriction list.
var/list/species_restricted = null
var/list/species_restricted
//Basically syntax is species_restricted = list("Species Name","Species Name")
//Add a "exclude" string to do the opposite, making it only only species listed that can't wear it.
//You append this to clothing objects
// How much clothing damage has been dealt to each of the limbs of the clothing, assuming it covers more than one limb
var/list/damage_by_parts
// How much integrity is in a specific limb before that limb is disabled (for use in [/obj/item/clothing/proc/take_damage_zone], and only if we cover multiple zones.) Set to 0 to disable shredding.
@@ -477,7 +476,6 @@ BLIND // can't see anything
return TRUE
/// If we're a clothing with at least 1 shredded/disabled zone, give the wearer a periodic heads up letting them know their clothes are damaged
/obj/item/clothing/proc/bristle(mob/living/L)
if(!istype(L))
+33 -12
View File
@@ -20,8 +20,14 @@
body_parts_covered = ARMS
cold_protection = ARMS
strip_delay = 300 //you can't just yank them off
/// did you ever get around to wearing these or no
var/wornonce = FALSE
///Extra damage through the punch.
var/enhancement = 0 //it's a +0 to your punches because it isn't magical
///extra wound bonus through the punch (MAYBE DON'T BE GENEROUS WITH THIS)
var/wound_enhancement = 0
/// do we give the flavortext for wearing them
var/silent = FALSE
///Main trait added by the gloves to the user on wear.
var/inherited_trait = TRAIT_NOGUNS //what are you, dishonoroable?
///Secondary trait added by the gloves to the user on wear.
@@ -29,7 +35,18 @@
/obj/item/clothing/gloves/fingerless/pugilist/equipped(mob/user, slot)
. = ..()
if(slot == SLOT_GLOVES)
if(current_equipped_slot == SLOT_GLOVES)
use_buffs(user, TRUE)
wornonce = TRUE
/obj/item/clothing/gloves/fingerless/pugilist/dropped(mob/user)
. = ..()
if(wornonce)
use_buffs(user, FALSE)
wornonce = FALSE
/obj/item/clothing/gloves/fingerless/pugilist/proc/use_buffs(mob/user, buff)
if(buff) // tarukaja
if(ishuman(user))
var/mob/living/carbon/human/H = user
ADD_TRAIT(H, TRAIT_PUGILIST, GLOVE_TRAIT)
@@ -37,17 +54,20 @@
ADD_TRAIT(H, secondary_trait, GLOVE_TRAIT)
H.dna.species.punchdamagehigh += enhancement
H.dna.species.punchdamagelow += enhancement
/obj/item/clothing/gloves/fingerless/pugilist/dropped(mob/user)
REMOVE_TRAIT(user, TRAIT_PUGILIST, GLOVE_TRAIT)
REMOVE_TRAIT(user, inherited_trait, GLOVE_TRAIT)
REMOVE_TRAIT(user, secondary_trait, GLOVE_TRAIT)
if(ishuman(user))
var/mob/living/carbon/human/H = user
H.dna.species.punchdamagehigh -= enhancement
H.dna.species.punchdamagelow -= enhancement
return ..()
H.dna.species.punchwoundbonus += wound_enhancement
if(!silent)
to_chat(H, "<span class='notice'>With [src] on your arms, you feel ready to punch things.</span>")
else // dekaja
REMOVE_TRAIT(user, TRAIT_PUGILIST, GLOVE_TRAIT)
REMOVE_TRAIT(user, inherited_trait, GLOVE_TRAIT)
REMOVE_TRAIT(user, secondary_trait, GLOVE_TRAIT)
if(ishuman(user))
var/mob/living/carbon/human/H = user
H.dna.species.punchdamagehigh -= enhancement
H.dna.species.punchdamagelow -= enhancement
H.dna.species.punchwoundbonus -= wound_enhancement
if(!silent)
to_chat(user, "<span class='warning'>With [src] off of your arms, you feel less ready to punch things.</span>")
/obj/item/clothing/gloves/fingerless/pugilist/chaplain
name = "armwraps of unyielding resolve"
@@ -93,6 +113,7 @@
icon_state = "rapid"
item_state = "rapid"
enhancement = 10 //omae wa mou shindeiru
wound_enhancement = 10
var/warcry = "AT"
secondary_trait = TRAIT_NOSOFTCRIT //basically extra health
+2
View File
@@ -65,12 +65,14 @@
heat_protection = HANDS
max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT
resistance_flags = NONE
strip_mod = 1.2 // because apparently black gloves had this
/obj/item/clothing/gloves/tackler/combat/insulated
name = "guerrilla gloves"
desc = "Superior quality combative gloves, good for performing tackle takedowns as well as absorbing electrical shocks."
siemens_coefficient = 0
permeability_coefficient = 0.05
strip_mod = 1.5 // and combat gloves had this??
/obj/item/clothing/gloves/tackler/combat/insulated/infiltrator
name = "insidious guerrilla gloves"
+2 -2
View File
@@ -355,7 +355,7 @@
/obj/item/clothing/mask/paper/Initialize(mapload)
.=..()
. = ..()
papermask_designs = list(
"Blank" = image(icon = src.icon, icon_state = "plainmask"),
"Neutral" = image(icon = src.icon, icon_state = "neutralmask"),
@@ -384,7 +384,7 @@
"Vertical" = "verticalmask", "Horizontal" = "horizontalmask", "X" ="xmask",
"Bugeyes" = "bugmask", "Double" = "doublemask", "Mark" = "markmask")
var/choice = show_radial_menu(user,src, papermask_designs, custom_check = FALSE, radius = 36, require_near = TRUE)
var/choice = show_radial_menu(user, src, papermask_designs, custom_check = FALSE, radius = 36, require_near = TRUE)
if(src && choice && !user.incapacitated() && in_range(user,src))
icon_state = options[choice]
+1 -1
View File
@@ -29,7 +29,7 @@
for(var/i = 1, i <= number_of_wormholes, i++)
var/turf/T = pick(pick_turfs)
wormholes += new /obj/effect/portal/wormhole(T, null, 0, null, FALSE)
wormholes += new /obj/effect/portal/wormhole(T, 0, null, FALSE)
/datum/round_event/wormholes/announce(fake)
priority_announce("Space-time anomalies detected on the station. There is no additional data.", "Anomaly Alert", "spanomalies")
@@ -291,3 +291,12 @@
tastes = list("creamy peas"= 2, "parsnip" = 1)
filling_color = "#9dc530"
foodtype = VEGETABLES
/obj/item/reagent_containers/food/snacks/soup/facehuggerpot
name = "pot of face hugger jambalaya"
desc = "An entire pot of an extremely spicy dish made using extremely exotic ingredients. Highly recommend by an interdimensional businessman."
icon_state = "facehuggerpot"
bonus_reagents = list(/datum/reagent/consumable/nutriment = 1, /datum/reagent/consumable/nutriment/vitamin = 2)
list_reagents = list(/datum/reagent/consumable/nutriment = 10, /datum/reagent/consumable/nutriment/vitamin = 4)
tastes = list("face huggers" = 1)
foodtype = MEAT
@@ -291,3 +291,18 @@
)
result = /obj/item/reagent_containers/food/snacks/soup/peasoup
subcategory = CAT_SOUP
/datum/crafting_recipe/food/facehuggerjambalayapot
name = "Facehugger jambalaya"
reqs = list(
/datum/reagent/bluespace = 10,
/datum/reagent/consumable/ethanol/booger =10,
/obj/item/organ/heart= 3,
/obj/item/clothing/mask/facehugger = 1,
/obj/item/reagent_containers/food/snacks/egg = 1,
/datum/reagent/consumable/blackpepper = 5,
/datum/reagent/toxin/acid = 5
)
result = /obj/item/reagent_containers/food/snacks/soup/facehuggerpot
subcategory = CAT_SOUP
+1 -1
View File
@@ -351,7 +351,7 @@
/datum/holiday/halloween
name = HALLOWEEN
begin_day = 28
begin_day = 1
begin_month = OCTOBER
end_day = 2
end_month = NOVEMBER
+8
View File
@@ -42,6 +42,14 @@
icon = 'icons/obj/hydroponics/equipment.dmi'
icon_state = "hydrotray3"
/obj/machinery/hydroponics/constructable/ComponentInitialize()
. = ..()
AddComponent(/datum/component/simple_rotation, ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_COUNTERCLOCKWISE | ROTATION_VERBS, null, CALLBACK(src, .proc/can_be_rotated))
AddComponent(/datum/component/plumbing/simple_demand)
/obj/machinery/hydroponics/constructable/proc/can_be_rotated(mob/user, rotation_type)
return !anchored
/obj/machinery/hydroponics/constructable/RefreshParts()
var/tmp_capacity = 0
for (var/obj/item/stock_parts/matter_bin/M in component_parts)
@@ -44,18 +44,18 @@
/obj/item/integrated_circuit/output/screen/large/do_work()
..()
if(isliving(assembly.loc))//this whole block just returns if the assembly is neither in a mobs hands or on the ground
var/mob/living/H = assembly.loc
if(H.get_active_held_item() != assembly && H.get_inactive_held_item() != assembly)
return
else
if(!isturf(assembly.loc))
return
var/atom/host = assembly || src
var/list/mobs = list()
for(var/mob/M in range(0, get_turf(src)))
mobs += M
if(isliving(assembly.loc))
mobs += assembly.loc
var/mob/living/L = assembly.loc
if(L.is_holding(src))
for(var/mob/M in range(1, get_turf(src)))
mobs += M
else
for(var/mob/M in range(2, get_turf(src)))
mobs += M
var/atom/host = assembly || src
to_chat(mobs, "<span class='notice'>[icon2html(host.icon, world, host.icon_state)] flashes a message: [stuff_to_display]</span>")
host.investigate_log("displayed \"[html_encode(stuff_to_display)]\" as [type].", INVESTIGATE_CIRCUIT)
+25 -3
View File
@@ -73,6 +73,31 @@
/// Starting skill modifiers.
var/list/starting_modifiers
/**
* Checks if we should be created on a certain map
*/
/datum/job/proc/map_check(datum/map_config/C)
return (length(C.job_whitelist)? (type in C.job_whitelist) : !(type in C.job_blacklist))
/**
* Processes map specific overrides
*/
/datum/job/proc/process_map_overrides(datum/map_config/C)
if(type in C.job_override_spawn_positions)
spawn_positions = C.job_override_spawn_positions[type]
if(type in C.job_override_total_positions)
total_positions = C.job_override_total_positions[type]
if(type in C.job_access_override)
access = C.job_access_override[type]
minimal_access = access
else
if(type in C.job_access_add)
access += C.job_access_add[type]
minimal_access += C.job_access_add[type]
if(type in C.job_access_remove)
access -= C.job_access_add[type]
minimal_access -= C.job_access_remove[type]
//Only override this proc
//H is usually a human unless an /equip override transformed it
/datum/job/proc/after_spawn(mob/living/H, mob/M, latejoin = FALSE)
@@ -175,9 +200,6 @@
/datum/job/proc/config_check()
return TRUE
/datum/job/proc/map_check()
return TRUE
/datum/job/proc/radio_help_message(mob/M)
to_chat(M, "<b>Prefix your message with :h to speak on your department's radio. To see other prefixes, look closely at your headset.</b>")
@@ -1,4 +0,0 @@
//this needs to come after the job_types subfolder to keep the correct ordering
#include "..\..\..\..\_maps\map_files\PubbyStation\job_changes.dm"
#undef JOB_MODIFICATION_MAP_NAME
@@ -1,6 +1,7 @@
// Clients aren't datums so we have to define these procs indpendently.
// These verbs are called for all key press and release events
/client/verb/keyDown(_key as text)
SHOULD_NOT_SLEEP(TRUE)
set instant = TRUE
set hidden = TRUE
@@ -83,6 +84,7 @@
keyUp("[key]")
/client/verb/keyUp(_key as text)
SHOULD_NOT_SLEEP(TRUE)
set instant = TRUE
set hidden = TRUE
+3 -4
View File
@@ -1,12 +1,11 @@
/datum/proc/key_down(key, client/user) // Called when a key is pressed down initially
return
SHOULD_NOT_SLEEP(TRUE)
/datum/proc/key_up(key, client/user) // Called when a key is released
return
SHOULD_NOT_SLEEP(TRUE)
/datum/proc/keyLoop(client/user) // Called once every frame
set waitfor = FALSE
return
SHOULD_NOT_SLEEP(TRUE)
// removes all the existing macros
/client/proc/erase_all_macros()
+85
View File
@@ -43,6 +43,21 @@
/// Orientation to load in by default.
var/orientation = SOUTH //byond defaults to placing everyting SOUTH.
/// Jobs whitelist - if this is not empty, ONLY these jobs are allowed. Overrides blacklist.
var/list/job_whitelist
/// Jobs blacklist - if this is not empty, jobs in this aren't allowed.
var/list/job_blacklist
/// Job spawn position mod - type = number
var/list/job_override_spawn_positions
/// Job total position mod - type = number
var/list/job_override_total_positions
/// Add these accesses to jobs - type = list()
var/list/job_access_add
/// Remove these accesses from jobs - type = list()
var/list/job_access_remove
/// Override job accesses - type = list() - overrides everything else
var/list/job_access_override
/proc/load_map_config(filename = "data/next_map.json", default_to_box, delete_after, error_if_missing = TRUE)
var/datum/map_config/config = new
if (default_to_box)
@@ -161,6 +176,69 @@
allow_custom_shuttles = json["allow_custom_shuttles"] != FALSE
if("job_whitelist" in json)
job_whitelist = list()
for(var/path in json["job_whitelist"])
var/type = text2path(path)
if(!path)
log_config("map datum [config_filename] failed to validate path [path] in job overrides.")
continue
job_whitelist += type
if("job_blacklist" in json)
job_blacklist = list()
for(var/path in json["job_blacklist"])
var/type = text2path(path)
if(!path)
log_config("map datum [config_filename] failed to validate path [path] in job overrides.")
continue
job_blacklist += type
if("job_override_spawn_positions" in json)
job_override_spawn_positions = list()
for(var/path in json["job_override_spawn_positions"])
var/type = text2path(path)
if(!path)
log_config("map datum [config_filename] failed to validate path [path] in job overrides.")
continue
job_override_spawn_positions += type
if("job_override_total_positions" in json)
job_override_total_positions = list()
for(var/path in json["job_override_total_positions"])
var/type = text2path(path)
if(!path)
log_config("map datum [config_filename] failed to validate path [path] in job overrides.")
continue
job_override_total_positions += type
if("job_access_add" in json)
job_access_add = list()
for(var/path in json["job_acces_add"])
var/type = text2path(path)
if(!path)
log_config("map datum [config_filename] failed to validate path [path] in job overrides.")
continue
job_access_add[type] = json["job_access_add"]
if("job_access_remove" in json)
job_access_remove = list()
for(var/path in json["job_acces_add"])
var/type = text2path(path)
if(!path)
log_config("map datum [config_filename] failed to validate path [path] in job overrides.")
continue
job_access_remove[type] = json["job_access_remove"]
if("job_access_override" in json)
job_access_override = list()
for(var/path in json["job_acces_add"])
var/type = text2path(path)
if(!path)
log_config("map datum [config_filename] failed to validate path [path] in job overrides.")
continue
job_access_override[type] = json["job_access_override"]
defaulted = FALSE
return TRUE
#undef CHECK_EXISTS
@@ -190,6 +268,13 @@
jsonlist["announcertype"] = announcertype
jsonlist["orientation"] = orientation
jsonlist["allow_custom_shuttles"] = allow_custom_shuttles
jsonlist["job_whitelist"] = job_whitelist
jsonlist["job_blacklist"] = job_blacklist
jsonlist["job_override_spawn_positions"] = job_override_spawn_positions
jsonlist["job_override_total_positions"] = job_override_total_positions
jsonlist["job_access_add"] = job_access_add
jsonlist["job_access_remove"] = job_access_remove
jsonlist["job_access_override"] = job_access_override
if(fexists("data/next_map.json"))
fdel("data/next_map.json")
var/F = file("data/next_map.json")
+5
View File
@@ -135,7 +135,12 @@
//That's done remove from priority even if it failed
if(forced)
//TODO : handle forced ruins with multiple variants
// this might work?
forced_ruins -= current_pick
if(!current_pick.allow_duplicates)
for(var/datum/map_template/ruin/R in forced_ruins)
if(R.id == current_pick.id)
forced_ruins -= R
forced = FALSE
if(failed_to_place)
+21 -12
View File
@@ -191,8 +191,8 @@
if(HAS_TRAIT(src, TRAIT_PACIFISM))
to_chat(src, "<span class='notice'>You gently let go of [throwable_mob].</span>")
return
adjustStaminaLossBuffered(STAM_COST_THROW_MOB * ((throwable_mob.mob_size+1)**2))// throwing an entire person shall be very tiring
if(!UseStaminaBuffer(STAM_COST_THROW_MOB * ((throwable_mob.mob_size+1)**2), TRUE))
return
var/turf/start_T = get_turf(loc) //Get the start and target tile for the descriptors
var/turf/end_T = get_turf(target)
if(start_T && end_T)
@@ -206,7 +206,8 @@
to_chat(src, "<span class='notice'>You set [I] down gently on the ground.</span>")
return
adjustStaminaLossBuffered(I.getweight(src, STAM_COST_THROW_MULT, SKILL_THROW_STAM_COST))
if(!UseStaminaBuffer(I.getweight(src, STAM_COST_THROW_MULT, SKILL_THROW_STAM_COST), warn = TRUE))
return
if(thrown_thing)
var/power_throw = 0
@@ -594,15 +595,23 @@
remove_movespeed_modifier(/datum/movespeed_modifier/carbon_softcrit)
/mob/living/carbon/update_stamina()
var/stam = getStaminaLoss()
if(stam > DAMAGE_PRECISION)
var/total_health = (maxHealth - stam)
if(total_health <= crit_threshold && !stat)
if(CHECK_MOBILITY(src, MOBILITY_STAND))
to_chat(src, "<span class='notice'>You're too exhausted to keep going...</span>")
KnockToFloor(TRUE)
update_health_hud()
var/total_health = getStaminaLoss()
if(total_health)
if(!(combat_flags & COMBAT_FLAG_HARD_STAMCRIT) && total_health >= STAMINA_CRIT && !stat)
to_chat(src, "<span class='notice'>You're too exhausted to keep going...</span>")
set_resting(TRUE, FALSE, FALSE)
SEND_SIGNAL(src, COMSIG_DISABLE_COMBAT_MODE)
ENABLE_BITFIELD(combat_flags, COMBAT_FLAG_HARD_STAMCRIT)
filters += CIT_FILTER_STAMINACRIT
update_mobility()
if((combat_flags & COMBAT_FLAG_HARD_STAMCRIT) && total_health <= STAMINA_CRIT)
to_chat(src, "<span class='notice'>You don't feel nearly as exhausted anymore.</span>")
DISABLE_BITFIELD(combat_flags, COMBAT_FLAG_HARD_STAMCRIT)
filters -= CIT_FILTER_STAMINACRIT
update_mobility()
UpdateStaminaBuffer()
update_health_hud()
/mob/living/carbon/update_sight()
if(!client)
return
@@ -254,10 +254,10 @@
if(href_list["pockets"])
var/strip_mod = 1
var/strip_silence = FALSE
var/obj/item/clothing/gloves/g = gloves
if (istype(g))
strip_mod = g.strip_mod
strip_silence = g.strip_silence
var/obj/item/clothing/gloves/G = gloves
if(istype(G))
strip_mod = G.strip_mod
strip_silence = G.strip_silence
var/pocket_side = href_list["pockets"]
var/pocket_id = (pocket_side == "right" ? SLOT_R_STORE : SLOT_L_STORE)
var/obj/item/pocket_item = (pocket_id == SLOT_R_STORE ? r_store : l_store)
@@ -805,7 +805,7 @@
hud_used.healthdoll.icon_state = "healthdoll_DEAD"
hud_used.staminas?.update_icon_state()
hud_used.staminabuffer?.update_icon_state()
hud_used.staminabuffer?.mark_dirty()
/mob/living/carbon/human/fully_heal(admin_revive = FALSE)
if(admin_revive)
@@ -1044,10 +1044,9 @@
remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown)
remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown_flying)
return
var/stambufferinfluence = (bufferedstam*(100/stambuffer))*0.2 //CIT CHANGE - makes stamina buffer influence movedelay
if(!HAS_TRAIT(src, TRAIT_IGNOREDAMAGESLOWDOWN)) //if we want to ignore slowdown from damage, but not from equipment
var/scaling = maxHealth / 100
var/health_deficiency = (((maxHealth / scaling) + stambufferinfluence) - (health / scaling) + (getStaminaLoss()*0.75))//CIT CHANGE - reduces the impact of staminaloss and makes stamina buffer influence it
var/health_deficiency = ((maxHealth / scaling) - (health / scaling) + (getStaminaLoss()*0.75))//CIT CHANGE - reduces the impact of staminaloss and makes stamina buffer influence it
if(health_deficiency >= 40)
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown, TRUE, (health_deficiency-39) / 75)
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown_flying, TRUE, (health_deficiency-39) / 25)
@@ -7,7 +7,7 @@
buckle_lying = FALSE
mob_biotypes = MOB_ORGANIC|MOB_HUMANOID
/// Enable stamina combat
combat_flags = COMBAT_FLAGS_DEFAULT | COMBAT_FLAG_UNARMED_PARRY
combat_flags = COMBAT_FLAGS_STAMINA_COMBAT | COMBAT_FLAG_UNARMED_PARRY
status_flags = CANSTUN|CANKNOCKDOWN|CANUNCONSCIOUS|CANPUSH|CANSTAGGER
has_field_of_vision = FALSE //Handled by species.
@@ -11,11 +11,6 @@
/mob/living/carbon/human/movement_delay()
. = ..()
if(CHECK_MOBILITY(src, MOBILITY_STAND) && m_intent == MOVE_INTENT_RUN && (combat_flags & COMBAT_FLAG_SPRINT_ACTIVE))
var/static/datum/config_entry/number/movedelay/sprint_speed_increase/SSI
if(!SSI)
SSI = CONFIG_GET_ENTRY(number/movedelay/sprint_speed_increase)
. -= SSI.config_entry_value
if (m_intent == MOVE_INTENT_WALK && HAS_TRAIT(src, TRAIT_SPEEDY_STEP))
. -= 1.5
@@ -61,7 +56,11 @@
HM.on_move(NewLoc)
if(. && (combat_flags & COMBAT_FLAG_SPRINT_ACTIVE) && !(movement_type & FLYING) && CHECK_ALL_MOBILITY(src, MOBILITY_MOVE|MOBILITY_STAND) && m_intent == MOVE_INTENT_RUN && has_gravity(loc) && (!pulledby || (pulledby.pulledby == src)))
if(!HAS_TRAIT(src, TRAIT_FREESPRINT))
doSprintLossTiles(1)
var/datum/movespeed_modifier/equipment_speedmod/MM = get_movespeed_modifier_datum(/datum/movespeed_modifier/equipment_speedmod)
var/amount = 1
if(MM?.multiplicative_slowdown >= 1)
amount *= (1 + (6 - (3 / MM.multiplicative_slowdown)))
doSprintLossTiles(amount)
if((oldpseudoheight - pseudo_z_axis) >= 8)
to_chat(src, "<span class='warning'>You trip off of the elevated surface!</span>")
for(var/obj/item/I in held_items)
+36 -20
View File
@@ -64,7 +64,8 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
var/stunmod = 1 // multiplier for stun duration
var/punchdamagelow = 1 //lowest possible punch damage. if this is set to 0, punches will always miss
var/punchdamagehigh = 10 //highest possible punch damage
var/punchstunthreshold = 10//damage at which punches from this race will stun //yes it should be to the attacked race but it's not useful that way even if it's logical
var/punchstunthreshold = 10 //damage at which punches from this race will stun //yes it should be to the attacked race but it's not useful that way even if it's logical
var/punchwoundbonus = 0 // additional wound bonus. generally zero.
var/siemens_coeff = 1 //base electrocution coefficient
var/damage_overlay_type = "human" //what kind of damage overlays (if any) appear on our species when wounded?
var/fixed_mut_color = "" //to use MUTCOLOR with a fixed color that's independent of dna.feature["mcolor"]
@@ -106,7 +107,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
var/whitelisted = 0 //Is this species restricted to certain players?
var/whitelist = list() //List the ckeys that can use this species, if it's whitelisted.: list("John Doe", "poopface666", "SeeALiggerPullTheTrigger") Spaces & capitalization can be included or ignored entirely for each key as it checks for both.
var/icon_limbs //Overrides the icon used for the limbs of this species. Mainly for downstream, and also because hardcoded icons disgust me. Implemented and maintained as a favor in return for a downstream's implementation of synths.
var/species_type
var/species_category
var/tail_type //type of tail i.e. mam_tail
var/wagging_type //type of wagging i.e. waggingtail_lizard
@@ -1361,9 +1362,10 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(!(attackchain_flags & ATTACK_IS_PARRY_COUNTERATTACK))
if(HAS_TRAIT(user, TRAIT_PUGILIST))//CITADEL CHANGE - makes punching cause staminaloss but funny martial artist types get a discount
user.adjustStaminaLossBuffered(1.5)
else
user.adjustStaminaLossBuffered(3.5)
if(!user.UseStaminaBuffer(1.5, warn = TRUE))
return
else if(!user.UseStaminaBuffer(3.5, warn = TRUE))
return
if(attacker_style && attacker_style.harm_act(user,target))
return TRUE
@@ -1384,6 +1386,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
user.do_attack_animation(target, ATTACK_EFFECT_PUNCH)
var/damage = rand(user.dna.species.punchdamagelow, user.dna.species.punchdamagehigh)
var/punchwoundbonus = user.dna.species.punchwoundbonus
var/puncherstam = user.getStaminaLoss()
var/puncherbrute = user.getBruteLoss()
var/punchedstam = target.getStaminaLoss()
@@ -1399,6 +1402,8 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
//END OF CITADEL CHANGES
var/obj/item/bodypart/affecting = target.get_bodypart(ran_zone(user.zone_selected))
if(HAS_TRAIT(user, TRAIT_PUGILIST))
affecting = target.get_bodypart(check_zone(user.zone_selected)) // if you're going the based unarmed route you won't miss
if(!affecting) //Maybe the bodypart is missing? Or things just went wrong..
affecting = target.get_bodypart(BODY_ZONE_CHEST) //target chest instead, as failsafe. Or hugbox? You decide.
@@ -1410,8 +1415,8 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(user.dna.species.punchdamagelow)
if(atk_verb == ATTACK_EFFECT_KICK) //kicks never miss (provided your species deals more than 0 damage)
miss_chance = 0
else if(HAS_TRAIT(user, TRAIT_PUGILIST)) //pugilists have a flat 10% miss chance
miss_chance = 10
else if(HAS_TRAIT(user, TRAIT_PUGILIST)) //pugilists, being good at Punching People, also never miss
miss_chance = 0
else
miss_chance = min(10 + max(puncherstam * 0.5, puncherbrute * 0.5), 100) //probability of miss has a base of 10, and modified based on half brute total. Capped at max 100 to prevent weirdness in prob()
@@ -1425,12 +1430,13 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
var/armor_block = target.run_armor_check(affecting, "melee")
if(HAS_TRAIT(user, TRAIT_MAULER)) // maulers get 15 armorpierce because if you're going to punch someone you might as well do a good job of it
armor_block = target.run_armor_check(affecting, "melee", armour_penetration = 15) // lot of good that sec jumpsuit did you
playsound(target.loc, user.dna.species.attack_sound, 25, 1, -1)
target.visible_message("<span class='danger'>[user] [atk_verb]s [target]!</span>", \
"<span class='userdanger'>[user] [atk_verb]s you!</span>", null, COMBAT_MESSAGE_RANGE, null, \
user, "<span class='danger'>You [atk_verb] [target]!</span>")
target.visible_message("<span class='danger'>[user] [atk_verb]ed [target]!</span>", \
"<span class='userdanger'>[user] [atk_verb]ed you!</span>", null, COMBAT_MESSAGE_RANGE, null, \
user, "<span class='danger'>You [atk_verb]ed [target]!</span>")
target.lastattacker = user.real_name
target.lastattackerckey = user.ckey
@@ -1440,11 +1446,15 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
target.dismembering_strike(user, affecting.body_zone)
if(atk_verb == ATTACK_EFFECT_KICK)//kicks deal 1.5x raw damage + 0.5x stamina damage
target.apply_damage(damage*1.5, attack_type, affecting, armor_block)
target.apply_damage(damage*1.5, attack_type, affecting, armor_block, wound_bonus = punchwoundbonus)
target.apply_damage(damage*0.5, STAMINA, affecting, armor_block)
log_combat(user, target, "kicked")
else//other attacks deal full raw damage + 2x in stamina damage
target.apply_damage(damage, attack_type, affecting, armor_block)
else if(HAS_TRAIT(user, TRAIT_MAULER)) // mauler punches deal 1.3x raw damage + 1x stam damage, and have some armor pierce
target.apply_damage(damage*1.3, attack_type, affecting, armor_block, wound_bonus = punchwoundbonus)
target.apply_damage(damage, STAMINA, affecting, armor_block)
log_combat(user, target, "punched (mauler)")
else //other attacks deal full raw damage + 2x in stamina damage
target.apply_damage(damage, attack_type, affecting, armor_block, wound_bonus = punchwoundbonus)
target.apply_damage(damage*2, STAMINA, affecting, armor_block)
log_combat(user, target, "punched")
@@ -1494,6 +1504,8 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
return FALSE
else if(aim_for_mouth && ( target_on_help || target_restrained || target_aiming_for_mouth))
if(!user.UseStaminaBuffer(3, warn = TRUE))
return
playsound(target.loc, 'sound/weapons/slap.ogg', 50, 1, -1)
target.visible_message(\
@@ -1501,7 +1513,6 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
"<span class='notice'>[user] slaps you in the face! </span>",\
"You hear a slap.", target = user, target_message = "<span class='notice'>You slap [user == target ? "yourself" : "\the [target]"] in the face! </span>")
user.do_attack_animation(target, ATTACK_EFFECT_FACE_SLAP)
user.adjustStaminaLossBuffered(3)
if (!HAS_TRAIT(target, TRAIT_PERMABONER))
stop_wagging_tail(target)
return FALSE
@@ -1509,8 +1520,9 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(target.client?.prefs.cit_toggles & NO_ASS_SLAP)
to_chat(user,"A force stays your hand, preventing you from slapping \the [target]'s ass!")
return FALSE
if(!user.UseStaminaBuffer(3, warn = TRUE))
return FALSE
user.do_attack_animation(target, ATTACK_EFFECT_ASS_SLAP)
user.adjustStaminaLossBuffered(3)
target.adjust_arousal(20,maso = TRUE)
if (ishuman(target) && HAS_TRAIT(target, TRAIT_MASO) && target.has_dna() && prob(10))
target.mob_climax(forced_climax=TRUE)
@@ -1528,9 +1540,11 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
user.do_attack_animation(target, ATTACK_EFFECT_DISARM)
if(HAS_TRAIT(user, TRAIT_PUGILIST))//CITADEL CHANGE - makes disarmspam cause staminaloss, pugilists can do it almost effortlessly
user.adjustStaminaLossBuffered(1)
if(!user.UseStaminaBuffer(1, warn = TRUE))
return
else
user.adjustStaminaLossBuffered(3)
if(!user.UseStaminaBuffer(1, warn = TRUE))
return
if(attacker_style && attacker_style.disarm_act(user,target))
return TRUE
@@ -1766,9 +1780,10 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(CHECK_MOBILITY(user, MOBILITY_STAND))
to_chat(user, "<span class='notice'>You can only force yourself up if you're on the ground.</span>")
return
if(!user.UseStaminaBuffer(STAMINA_COST_SHOVE_UP, TRUE))
return
user.visible_message("<span class='notice'>[user] forces [p_them()]self up to [p_their()] feet!</span>", "<span class='notice'>You force yourself up to your feet!</span>")
user.set_resting(FALSE, TRUE)
user.adjustStaminaLossBuffered(user.stambuffer) //Rewards good stamina management by making it easier to instantly get up from resting
playsound(user, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
/datum/species/proc/altdisarm(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style)
@@ -1787,8 +1802,9 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
else
if(user == target)
return
if(!user.UseStaminaBuffer(4, warn = TRUE))
return
user.do_attack_animation(target, ATTACK_EFFECT_DISARM)
user.adjustStaminaLossBuffered(4)
playsound(target, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
if(target.w_uniform)
@@ -1,12 +1,12 @@
/datum/species/abductor
name = "Abductor"
id = "abductor"
id = SPECIES_ABDUCTOR
say_mod = "gibbers"
sexes = FALSE
species_traits = list(NOBLOOD,NOEYES,NOGENITALS,NOAROUSAL,HAS_FLESH,HAS_BONE)
inherent_traits = list(TRAIT_VIRUSIMMUNE,TRAIT_CHUNKYFINGERS,TRAIT_NOHUNGER,TRAIT_NOBREATH)
mutanttongue = /obj/item/organ/tongue/abductor
species_type = "alien"
species_category = SPECIES_CATEGORY_ALIEN
/datum/species/abductor/on_species_gain(mob/living/carbon/C, datum/species/old_species)
. = ..()
@@ -1,6 +1,6 @@
/datum/species/android
name = "Android"
id = "android"
id = SPECIES_ANDROID
say_mod = "states"
species_traits = list(NOBLOOD,NOGENITALS,NOAROUSAL,ROBOTIC_LIMBS)
inherent_traits = list(TRAIT_RESISTHEAT,TRAIT_NOBREATH,TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_RADIMMUNE,TRAIT_NOFIRE,TRAIT_PIERCEIMMUNE,TRAIT_NOHUNGER,TRAIT_LIMBATTACHMENT)
@@ -11,4 +11,4 @@
mutanttongue = /obj/item/organ/tongue/robot
species_language_holder = /datum/language_holder/synthetic
limbs_id = "synth"
species_type = "robotic"
species_category = SPECIES_CATEGORY_ROBOT
@@ -1,6 +1,6 @@
/datum/species/angel
name = "Angel"
id = "angel"
id = SPECIES_ANGEL
default_color = "FFFFFF"
species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,HAS_FLESH,HAS_BONE)
mutant_bodyparts = list("tail_human" = "None", "ears" = "None", "wings" = "Angel")
@@ -9,7 +9,7 @@
blacklisted = 1
limbs_id = "human"
skinned_type = /obj/item/stack/sheet/animalhide/human
species_type = "human" //they're a kind of human
species_category = SPECIES_CATEGORY_BASIC //they're a kind of human
var/datum/action/innate/flight/fly
@@ -1,6 +1,6 @@
/datum/species/mammal
name = "Anthromorph"
id = "mammal"
id = SPECIES_MAMMAL
default_color = "4B4B4B"
species_traits = list(MUTCOLORS,EYECOLOR,LIPS,HAIR,HORNCOLOR,WINGCOLOR,HAS_FLESH,HAS_BONE)
inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID|MOB_BEAST
@@ -15,6 +15,6 @@
tail_type = "mam_tail"
wagging_type = "mam_waggingtail"
species_type = "furry"
species_category = SPECIES_CATEGORY_FURRY
allowed_limb_ids = list("mammal","aquatic","avian")
allowed_limb_ids = list("mammal","aquatic","avian")
@@ -1,6 +1,6 @@
/datum/species/insect
name = "Anthromorphic Insect"
id = "insect"
id = SPECIES_INSECT
say_mod = "chitters"
default_color = "00FF00"
species_traits = list(LIPS,EYECOLOR,HAIR,FACEHAIR,MUTCOLORS,HORNCOLOR,WINGCOLOR,HAS_FLESH,HAS_BONE)
@@ -18,7 +18,7 @@
tail_type = "mam_tail"
wagging_type = "mam_waggingtail"
species_type = "insect"
species_category = SPECIES_CATEGORY_INSECT
allowed_limb_ids = list("insect","apid","moth","moth_not_greyscale")
@@ -18,4 +18,4 @@
inherent_traits = list(TRAIT_RADIMMUNE,TRAIT_VIRUSIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NODISMEMBER,TRAIT_NOLIMBDISABLE,TRAIT_NOHUNGER)
sexes = 0
gib_types = /obj/effect/gibspawner/robot
species_type = "robotic"
species_category = SPECIES_CATEGORY_ROBOT
@@ -1,6 +1,6 @@
/datum/species/dullahan
name = "Dullahan"
id = "dullahan"
id = SPECIES_DULLAHAN
default_color = "FFFFFF"
species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,HAS_FLESH,HAS_BONE)
inherent_traits = list(TRAIT_NOHUNGER,TRAIT_NOBREATH)
@@ -14,7 +14,7 @@
limbs_id = "human"
skinned_type = /obj/item/stack/sheet/animalhide/human
has_field_of_vision = FALSE //Too much of a trouble, their vision is already bound to their severed head.
species_type = "undead"
species_category = SPECIES_CATEGORY_UNDEAD
var/pumpkin = FALSE
var/obj/item/dullahan_relay/myhead
@@ -4,7 +4,7 @@ GLOBAL_LIST_INIT(dwarf_last, world.file2list("strings/names/dwarf_last.txt")) //
/datum/species/dwarf //not to be confused with the genetic manlets
name = "Dwarf"
id = "dwarf" //Also called Homo sapiens pumilionis
id = SPECIES_DWARF //Also called Homo sapiens pumilionis
default_color = "FFFFFF"
species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,HAS_FLESH,HAS_BONE)
inherent_traits = list(TRAIT_DWARF,TRAIT_SNOB)
@@ -18,7 +18,7 @@ GLOBAL_LIST_INIT(dwarf_last, world.file2list("strings/names/dwarf_last.txt")) //
mutant_organs = list(/obj/item/organ/dwarfgland) //Dwarven alcohol gland, literal gland warrior
mutantliver = /obj/item/organ/liver/dwarf //Dwarven super liver (Otherwise they r doomed)
species_language_holder = /datum/language_holder/dwarf
species_type = "human" //a kind of human
species_category = SPECIES_CATEGORY_BASIC //a kind of human
/mob/living/carbon/human/species/dwarf //species admin spawn path
race = /datum/species/dwarf //and the race the path is set to.
@@ -2,7 +2,7 @@
/datum/species/ethereal
name = "Ethereal"
id = "ethereal"
id = SPECIES_ETHEREAL
attack_verb = "burn"
attack_sound = 'sound/weapons/etherealhit.ogg'
miss_sound = 'sound/weapons/etherealmiss.ogg'
@@ -1,7 +1,7 @@
//Subtype of human
/datum/species/human/felinid
name = "Felinid"
id = "felinid"
id = SPECIES_FELINID
limbs_id = "human"
mutant_bodyparts = list("mam_tail" = "Cat", "mam_ears" = "Cat", "deco_wings" = "None")
@@ -11,7 +11,7 @@
tail_type = "mam_tail"
wagging_type = "mam_waggingtail"
species_type = "furry"
species_category = SPECIES_CATEGORY_FURRY
/datum/species/human/felinid/on_species_gain(mob/living/carbon/C, datum/species/old_species, pref_load)
if(ishuman(C))
@@ -1,6 +1,6 @@
/datum/species/fly
name = "Anthromorphic Fly"
id = "fly"
id = SPECIES_FLY
say_mod = "buzzes"
species_traits = list(NOEYES,HAS_FLESH,HAS_BONE)
inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID|MOB_BUG
@@ -12,7 +12,7 @@
liked_food = GROSS
exotic_bloodtype = "BUG"
exotic_blood_color = BLOOD_COLOR_BUG
species_type = "insect"
species_category = SPECIES_CATEGORY_INSECT
/datum/species/fly/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/H)
if(istype(chem, /datum/reagent/toxin/pestkiller))
@@ -1,7 +1,7 @@
/datum/species/golem
// Animated beings of stone. They have increased defenses, and do not need to breathe. They're also slow as fuuuck.
name = "Golem"
id = "iron golem"
id = SPECIES_GOLEM
species_traits = list(NOBLOOD,MUTCOLORS,NO_UNDERWEAR,NOGENITALS,NOAROUSAL)
inherent_traits = list(TRAIT_RESISTHEAT,TRAIT_NOBREATH,TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_NOFIRE,TRAIT_CHUNKYFINGERS,TRAIT_RADIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NODISMEMBER)
inherent_biotypes = MOB_HUMANOID|MOB_MINERAL
@@ -32,7 +32,7 @@
var/special_name_chance = 5
var/owner //dobby is a free golem
species_type = "golem"
species_category = SPECIES_CATEGORY_GOLEM
/datum/species/golem/random_name(gender,unique,lastname)
var/golem_surname = pick(GLOB.golem_names)
@@ -1,6 +1,6 @@
/datum/species/human
name = "Human"
id = "human"
id = SPECIES_HUMAN
default_color = "FFFFFF"
species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,MUTCOLORS_PARTSONLY,WINGCOLOR,HAS_FLESH,HAS_BONE)
@@ -12,7 +12,7 @@
tail_type = "tail_human"
wagging_type = "waggingtail_human"
species_type = "human"
species_category = SPECIES_CATEGORY_BASIC
/datum/species/human/spec_death(gibbed, mob/living/carbon/human/H)
if(H)
@@ -1,6 +1,6 @@
/datum/species/ipc
name = "I.P.C."
id = "ipc"
id = SPECIES_IPC
say_mod = "beeps"
default_color = "00FF00"
blacklisted = 0
@@ -28,7 +28,7 @@
exotic_bloodtype = "HF"
exotic_blood_color = BLOOD_COLOR_OIL
species_type = "robotic"
species_category = SPECIES_CATEGORY_ROBOT
var/datum/action/innate/monitor_change/screen
@@ -1,7 +1,7 @@
/datum/species/jelly
// Entirely alien beings that seem to be made entirely out of gel. They have three eyes and a skeleton visible within them.
name = "Xenobiological Jelly Entity"
id = "jelly"
id = SPECIES_JELLY
default_color = "00FF90"
say_mod = "chirps"
species_traits = list(MUTCOLORS,EYECOLOR,HAIR,FACEHAIR,WINGCOLOR,HAS_FLESH)
@@ -29,7 +29,7 @@
tail_type = "mam_tail"
wagging_type = "mam_waggingtail"
species_type = "jelly"
species_category = SPECIES_CATEGORY_JELLY
/obj/item/organ/brain/jelly
name = "slime nucleus"
@@ -158,7 +158,7 @@
/datum/species/jelly/slime
name = "Xenobiological Slime Entity"
id = "slime"
id = SPECIES_SLIME
default_color = "00FFFF"
species_traits = list(MUTCOLORS,EYECOLOR,HAIR,FACEHAIR)
say_mod = "says"
@@ -467,7 +467,7 @@
/datum/species/jelly/roundstartslime
name = "Xenobiological Slime Hybrid"
id = "slimeperson"
id = SPECIES_SLIME_HYBRID
limbs_id = "slime"
default_color = "00FFFF"
species_traits = list(MUTCOLORS,EYECOLOR,HAIR,FACEHAIR)
@@ -777,7 +777,7 @@
/datum/species/jelly/luminescent
name = "Luminescent Slime Entity"
id = "lum"
id = SPECIES_SLIME_LUMI
say_mod = "says"
var/glow_intensity = LUMINESCENT_DEFAULT_GLOW
var/obj/effect/dummy/luminescent_glow/glow
@@ -944,7 +944,7 @@
/datum/species/jelly/stargazer
name = "Stargazer Slime Entity"
id = "stargazer"
id = SPECIES_STARGAZER
var/datum/action/innate/project_thought/project_thought
var/datum/action/innate/link_minds/link_minds
var/list/mob/living/linked_mobs = list()
@@ -1,7 +1,7 @@
/datum/species/lizard
// Reptilian humanoids with scaled skin and tails.
name = "Anthromorphic Lizard"
id = "lizard"
id = SPECIES_LIZARD
say_mod = "hisses"
default_color = "00FF00"
species_traits = list(MUTCOLORS,EYECOLOR,HAIR,FACEHAIR,LIPS,HORNCOLOR,WINGCOLOR,HAS_FLESH,HAS_BONE)
@@ -28,7 +28,7 @@
tail_type = "tail_lizard"
wagging_type = "waggingtail_lizard"
species_type = "lizard"
species_category = "lizard"
/datum/species/lizard/random_name(gender,unique,lastname)
if(unique)
@@ -46,7 +46,7 @@
*/
/datum/species/lizard/ashwalker
name = "Ash Walker"
id = "ashlizard"
id = SPECIES_ASHWALKER
limbs_id = "lizard"
species_traits = list(MUTCOLORS,EYECOLOR,LIPS,DIGITIGRADE)
inherent_traits = list(TRAIT_CHUNKYFINGERS)
@@ -1,6 +1,6 @@
/datum/species/mush //mush mush codecuck
name = "Anthromorphic Mushroom"
id = "mush"
id = SPECIES_MUSHROOM
mutant_bodyparts = list("caps" = "Round")
fixed_mut_color = "DBBF92"
@@ -21,7 +21,7 @@
burnmod = 1.25
heatmod = 1.5
species_type = "plant"
species_category = SPECIES_CATEGORY_PLANT
mutanteyes = /obj/item/organ/eyes/night_vision/mushroom
var/datum/martial_art/mushpunch/mush
@@ -1,6 +1,6 @@
/datum/species/plasmaman
name = "Plasmaman"
id = "plasmaman"
id = SPECIES_PLASMAMAN
say_mod = "rattles"
sexes = 0
meat = /obj/item/stack/sheet/mineral/plasma
@@ -22,7 +22,7 @@
liked_food = VEGETABLES
outfit_important_for_life = /datum/outfit/plasmaman
species_type = "skeleton"
species_category = SPECIES_CATEGORY_SKELETON
/datum/species/plasmaman/spec_life(mob/living/carbon/human/H)
var/datum/gas_mixture/environment = H.loc.return_air()
@@ -1,7 +1,7 @@
/datum/species/pod
// A mutation caused by a human being ressurected in a revival pod. These regain health in light, and begin to wither in darkness.
name = "Anthromorphic Plant"
id = "pod"
id = SPECIES_POD
default_color = "59CE00"
species_traits = list(MUTCOLORS,EYECOLOR,CAN_SCAR,HAS_FLESH,HAS_BONE)
attack_verb = "slash"
@@ -19,7 +19,7 @@
var/light_burnheal = -1
var/light_bruteheal = -1
species_type = "plant"
species_category = SPECIES_CATEGORY_PLANT
allowed_limb_ids = list("pod","mush")
@@ -66,7 +66,7 @@
/datum/species/pod/pseudo_weak
name = "Anthromorphic Plant"
id = "podweak"
id = SPECIES_POD_WEAK
species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,MUTCOLORS)
mutant_bodyparts = list("mcolor" = "FFFFFF","mcolor2" = "FFFFFF","mcolor3" = "FFFFFF", "mam_snouts" = "Husky", "mam_tail" = "Husky", "mam_ears" = "Husky", "mam_body_markings" = "Husky", "taur" = "None", "legs" = "Normal Legs")
limbs_id = "pod"
@@ -4,7 +4,7 @@
/datum/species/shadow
// Humans cursed to stay in the darkness, lest their life forces drain. They regain health in shadow and die in light.
name = "???"
id = "shadow"
id = SPECIES_SHADOW
sexes = 0
blacklisted = 1
ignored_by = list(/mob/living/simple_animal/hostile/faithless)
@@ -15,7 +15,7 @@
dangerous_existence = 1
mutanteyes = /obj/item/organ/eyes/night_vision
species_type = "shadow"
species_category = SPECIES_CATEGORY_SHADOW
/datum/species/shadow/on_species_gain(mob/living/carbon/C, datum/species/old_species)
. = ..()
@@ -32,7 +32,7 @@
/datum/species/shadow/nightmare
name = "Nightmare"
id = "nightmare"
id = SPECIES_NIGHTMARE
limbs_id = "shadow"
burnmod = 1.5
blacklisted = TRUE
@@ -1,6 +1,6 @@
/datum/species/skeleton
name = "Skeleton"
id = "skeleton"
id = SPECIES_SKELETON
say_mod = "rattles"
blacklisted = 0
sexes = 0
@@ -15,7 +15,7 @@
brutemod = 1.25
burnmod = 1.25
species_type = "skeleton" //they have their own category that's disassociated from undead, paired with plasmapeople
species_category = SPECIES_CATEGORY_SKELETON //they have their own category that's disassociated from undead, paired with plasmapeople
/datum/species/skeleton/New()
if(SSevents.holidays && SSevents.holidays[HALLOWEEN]) //skeletons are stronger during the spooky season!
@@ -31,7 +31,7 @@
/datum/species/skeleton/space
name = "Spooky Spacey Skeleton"
id = "spaceskeleton"
id = SPECIES_SKELETON_SPACE
limbs_id = "skeleton"
blacklisted = 1
inherent_traits = list(TRAIT_RESISTHEAT,TRAIT_NOBREATH,TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_RADIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NOHUNGER,TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT, TRAIT_FAKEDEATH, TRAIT_CALCIUM_HEALER)
@@ -1,6 +1,6 @@
/datum/species/synthliz
name = "Synthetic Lizardperson"
id = "synthliz"
id = SPECIES_SYNTH_LIZARD
say_mod = "beeps"
default_color = "00FF00"
species_traits = list(MUTCOLORS,NOTRANSSTING,EYECOLOR,LIPS,HAIR,ROBOTIC_LIMBS,HAS_FLESH,HAS_BONE)
@@ -27,4 +27,4 @@
tail_type = "mam_tail"
wagging_type = "mam_waggingtail"
species_type = "robotic"
species_category = SPECIES_CATEGORY_ROBOT
@@ -1,6 +1,6 @@
/datum/species/synth
name = "Synthetic" //inherited from the real species, for health scanners and things
id = "synth"
id = SPECIES_SYNTH
say_mod = "beep boops" //inherited from a user's real species
sexes = 0
species_traits = list(NOTRANSSTING,NOGENITALS,NOAROUSAL) //all of these + whatever we inherit from the real species
@@ -17,11 +17,11 @@
var/disguise_fail_health = 75 //When their health gets to this level their synthflesh partially falls off
var/datum/species/fake_species = null //a species to do most of our work for us, unless we're damaged
species_language_holder = /datum/language_holder/synthetic
species_type = "robotic"
species_category = SPECIES_CATEGORY_ROBOT
/datum/species/synth/military
name = "Military Synth"
id = "military_synth"
id = SPECIES_SYNTH_MIL
armor = 25
punchdamagelow = 10
punchdamagehigh = 19
@@ -1,6 +1,6 @@
/datum/species/vampire
name = "Vampire"
id = "vampire"
id = SPECIES_VAMPIRE
default_color = "FFFFFF"
species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,DRINKSBLOOD,HAS_FLESH,HAS_BONE)
inherent_traits = list(TRAIT_NOHUNGER,TRAIT_NOBREATH)
@@ -14,7 +14,7 @@
limbs_id = "human"
skinned_type = /obj/item/stack/sheet/animalhide/human
var/info_text = "You are a <span class='danger'>Vampire</span>. You will slowly but constantly lose blood if outside of a coffin. If inside a coffin, you will slowly heal. You may gain more blood by grabbing a live victim and using your drain ability."
species_type = "undead"
species_category = SPECIES_CATEGORY_UNDEAD
/datum/species/vampire/check_roundstart_eligible()
if(SSevents.holidays && SSevents.holidays[HALLOWEEN])
@@ -1,7 +1,7 @@
/datum/species/xeno
// A cloning mistake, crossing human and xenomorph DNA
name = "Xenomorph Hybrid"
id = "xeno"
id = SPECIES_XENOHYBRID
say_mod = "hisses"
default_color = "00FF00"
species_traits = list(MUTCOLORS,EYECOLOR,LIPS,CAN_SCAR)
@@ -15,4 +15,4 @@
exotic_bloodtype = "X*"
damage_overlay_type = "xeno"
liked_food = MEAT
species_type = "alien"
species_category = SPECIES_CATEGORY_ALIEN
@@ -15,7 +15,7 @@
var/static/list/spooks = list('sound/hallucinations/growl1.ogg','sound/hallucinations/growl2.ogg','sound/hallucinations/growl3.ogg','sound/hallucinations/veryfar_noise.ogg','sound/hallucinations/wail.ogg')
disliked_food = NONE
liked_food = GROSS | MEAT | RAW
species_type = "undead"
species_category = SPECIES_CATEGORY_UNDEAD
/datum/species/zombie/notspaceproof
id = "notspaceproofzombie"
@@ -98,7 +98,7 @@
// Your skin falls off
/datum/species/krokodil_addict
name = "Human"
name = SPECIES_HUMAN
id = "goofzombies"
limbs_id = "zombie" //They look like zombies
sexes = 0
+3 -8
View File
@@ -502,19 +502,14 @@ GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put
//this updates all special effects: stun, sleeping, knockdown, druggy, stuttering, etc..
/mob/living/carbon/handle_status_effects()
..()
if(getStaminaLoss() && !SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE)) //CIT CHANGE - prevents stamina regen while combat mode is active
adjustStaminaLoss(!CHECK_MOBILITY(src, MOBILITY_STAND) ? ((combat_flags & COMBAT_FLAG_HARD_STAMCRIT) ? STAM_RECOVERY_STAM_CRIT : STAM_RECOVERY_RESTING) : STAM_RECOVERY_NORMAL)
var/combat_mode = SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE)
if(getStaminaLoss() && !HAS_TRAIT(src, TRAIT_NO_STAMINA_REGENERATION))
adjustStaminaLoss((!CHECK_MOBILITY(src, MOBILITY_STAND) ? ((combat_flags & COMBAT_FLAG_HARD_STAMCRIT) ? STAM_RECOVERY_STAM_CRIT : STAM_RECOVERY_RESTING) : STAM_RECOVERY_NORMAL) * (combat_mode? 0.25 : 1))
if(!(combat_flags & COMBAT_FLAG_HARD_STAMCRIT) && incomingstammult != 1)
incomingstammult = max(0.01, incomingstammult)
incomingstammult = min(1, incomingstammult*2)
//CIT CHANGES START HERE. STAMINA BUFFER STUFF
if(bufferedstam && world.time > stambufferregentime)
var/drainrate = max((bufferedstam*(bufferedstam/(5)))*0.1,1)
bufferedstam = max(bufferedstam - drainrate, 0)
//END OF CIT CHANGES
var/restingpwr = 1 + 4 * !CHECK_MOBILITY(src, MOBILITY_STAND)
//Dizziness
+1 -1
View File
@@ -562,7 +562,7 @@
if(. && iscarbon(user))
var/mob/living/carbon/C = user
if(isjellyperson(C))
pick(playsound(C, 'sound/effects/attackblob.ogg', 50, 1),playsound(C, 'sound/effects/blobattack.ogg', 50, 1))
playsound(C, 'sound/effects/attackblob.ogg', 50, 1)
/datum/emote/living/audio_emote/blurp
key = "blurp"
+8 -6
View File
@@ -8,6 +8,8 @@
for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds)
diag_hud.add_to_hud(src)
faction += "[REF(src)]"
stamina_buffer = INFINITY
UpdateStaminaBuffer()
GLOB.mob_living_list += src
/mob/living/prepare_huds()
@@ -842,12 +844,12 @@
return
var/strip_mod = 1
var/strip_silence = FALSE
if (ishuman(src)) //carbon doesn't actually wear gloves
if(ishuman(src)) //carbon doesn't actually wear gloves
var/mob/living/carbon/C = src
var/obj/item/clothing/gloves/g = C.gloves
if (istype(g))
strip_mod = g.strip_mod
strip_silence = g.strip_silence
var/obj/item/clothing/gloves/G = C.gloves
if(istype(G))
strip_mod = G.strip_mod
strip_silence = G.strip_silence
if (!strip_silence)
who.visible_message("<span class='danger'>[src] tries to remove [who]'s [what.name].</span>", \
"<span class='userdanger'>[src] tries to remove your [what.name].</span>", target = src,
@@ -1257,7 +1259,7 @@
SetUnconscious(clamp_unconscious_to)
HealAllImmobilityUpTo(clamp_immobility_to)
adjustStaminaLoss(min(0, -stamina_boost))
adjustStaminaLossBuffered(min(0, -stamina_buffer_boost))
RechargeStaminaBuffer(stamina_buffer_boost) // this MUST GO AFTER ADJUSTSTAMINALOSS.
if(scale_stamina_loss_recovery)
adjustStaminaLoss(min(-((getStaminaLoss() - stamina_loss_recovery_bypass) * scale_stamina_loss_recovery), 0))
if(put_on_feet)
@@ -8,6 +8,8 @@
active_block_item = null
REMOVE_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_BLOCK_TRAIT)
REMOVE_TRAIT(src, TRAIT_SPRINT_LOCKED, ACTIVE_BLOCK_TRAIT)
REMOVE_TRAIT(src, TRAIT_NO_STAMINA_BUFFER_REGENERATION, ACTIVE_BLOCK_TRAIT)
REMOVE_TRAIT(src, TRAIT_NO_STAMINA_REGENERATION, ACTIVE_BLOCK_TRAIT)
remove_movespeed_modifier(/datum/movespeed_modifier/active_block)
var/datum/block_parry_data/data = I.get_block_parry_data()
DelayNextAction(data.block_end_click_cd_add)
@@ -27,6 +29,10 @@
ADD_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_BLOCK_TRAIT) //probably should be something else at some point
if(data.block_lock_sprinting)
ADD_TRAIT(src, TRAIT_SPRINT_LOCKED, ACTIVE_BLOCK_TRAIT)
if(data.block_no_stamina_regeneration)
ADD_TRAIT(src, TRAIT_NO_STAMINA_REGENERATION, ACTIVE_BLOCK_TRAIT)
if(data.block_no_stambuffer_regeneration)
ADD_TRAIT(src, TRAIT_NO_STAMINA_BUFFER_REGENERATION, ACTIVE_BLOCK_TRAIT)
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/active_block, multiplicative_slowdown = data.block_slowdown)
active_block_effect_start()
return TRUE
@@ -192,7 +198,7 @@
var/held_index = C.get_held_index_of_item(src)
var/obj/item/bodypart/BP = C.hand_bodyparts[held_index]
if(!BP?.body_zone)
return C.adjustStaminaLossBuffered(stamina_amount) //nah
return C.adjustStaminaLoss(stamina_amount) //nah
var/zone = BP.body_zone
var/stamina_to_zone = data.block_stamina_limb_ratio * stamina_amount
var/stamina_to_chest = stamina_amount - stamina_to_zone
@@ -200,9 +206,9 @@
stamina_to_chest -= stamina_buffered
C.apply_damage(stamina_to_zone, STAMINA, zone)
C.apply_damage(stamina_to_chest, STAMINA, BODY_ZONE_CHEST)
C.adjustStaminaLossBuffered(stamina_buffered)
C.adjustStaminaLoss(stamina_buffered)
else
owner.adjustStaminaLossBuffered(stamina_amount)
owner.adjustStaminaLoss(stamina_amount)
/obj/item/proc/on_active_block(mob/living/owner, atom/object, damage, damage_blocked, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return, override_direction)
return
@@ -68,12 +68,13 @@
// can always implement it later, whatever.
if((data.parry_respect_clickdelay && !CheckActionCooldown()) || ((parry_end_time_last + data.parry_cooldown) > world.time))
to_chat(src, "<span class='warning'>You are not ready to parry (again)!</span>")
return
return FALSE
// Point of no return, make sure everything is set.
parrying = method
if(method == ITEM_PARRY)
active_parry_item = using_item
adjustStaminaLossBuffered(data.parry_stamina_cost)
if(!UseStaminaBuffer(data.parry_stamina_cost, TRUE))
return FALSE
parry_start_time = world.time
successful_parries = list()
addtimer(CALLBACK(src, .proc/end_parry_sequence), full_parry_duration)
@@ -74,8 +74,12 @@ GLOBAL_LIST_EMPTY(block_parry_data)
/// Ratio of stamina incurred by chest (so after [block_stamina_limb_ratio] runs) that is buffered.
var/block_stamina_buffer_ratio = 1
/// Stamina dealt directly via adjustStaminaLossBuffered() per SECOND of block.
/// Stamina dealt directly via UseStaminaBuffer() per SECOND of block.
var/block_stamina_cost_per_second = 1.5
/// Prevent stamina buffer regeneration while block?
var/block_no_stambuffer_regeneration = TRUE
/// Prevent stamina regeneration while block?
var/block_no_stamina_regeneration = FALSE
/// Bitfield for attack types that we can block while down. This will work in any direction.
var/block_resting_attack_types_anydir = ATTACK_TYPE_MELEE | ATTACK_TYPE_UNARMED | ATTACK_TYPE_TACKLE
@@ -307,7 +311,7 @@ GLOBAL_LIST_EMPTY(block_parry_data)
/mob/living/proc/handle_block_parry(seconds = 1)
if(combat_flags & COMBAT_FLAG_ACTIVE_BLOCKING)
var/datum/block_parry_data/data = return_block_parry_datum(active_block_item.block_parry_data)
adjustStaminaLossBuffered(data.block_stamina_cost_per_second * seconds)
UseStaminaBuffer(data.block_stamina_cost_per_second * seconds)
/mob/living/on_item_dropped(obj/item/I)
if(I == active_block_item)
+11 -3
View File
@@ -53,6 +53,7 @@
var/hallucination = 0 //Directly affects how long a mob will hallucinate for
var/last_special = 0 //Used by the resist verb, likely used to prevent players from bypassing next_move by logging in/out.
var/timeofdeath = 0
//Allows mobs to move through dense areas without restriction. For instance, in space or out of holder objects.
@@ -148,9 +149,6 @@
var/combatmessagecooldown = 0
var/incomingstammult = 1
var/bufferedstam = 0
var/stambuffer = 20
var/stambufferregentime
//Sprint buffer---
var/sprint_buffer = 42 //Tiles
@@ -159,3 +157,13 @@
var/sprint_buffer_regen_last = 0 //last world.time this was regen'd for math.
var/sprint_stamina_cost = 0.70 //stamina loss per tile while insufficient sprint buffer.
//---End
// Stamina Buffer---
/// Our stamina buffer
var/stamina_buffer
/// Stamina buffer regen modifier
var/stamina_buffer_regen_mod = 1
/// Last time stamina buffer regen was done
var/stamina_buffer_regen_last = 0
/// Last time we used stamina buffer
var/stamina_buffer_last_use = 0
@@ -176,4 +176,6 @@
else
remove_movespeed_modifier(/datum/movespeed_modifier/limbless)
update_movespeed()
return mobility_flags
+2
View File
@@ -29,6 +29,7 @@
if(combat_flags & COMBAT_FLAG_SPRINT_ACTIVE)
return
ENABLE_BITFIELD(combat_flags, COMBAT_FLAG_SPRINT_ACTIVE)
add_movespeed_modifier(/datum/movespeed_modifier/sprinting)
if(update_icon)
update_sprint_icon()
@@ -36,6 +37,7 @@
if(!(combat_flags & COMBAT_FLAG_SPRINT_ACTIVE) || (combat_flags & COMBAT_FLAG_SPRINT_FORCED))
return
DISABLE_BITFIELD(combat_flags, COMBAT_FLAG_SPRINT_ACTIVE)
remove_movespeed_modifier(/datum/movespeed_modifier/sprinting)
if(update_icon)
update_sprint_icon()
+1
View File
@@ -82,6 +82,7 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
return new_msg
/mob/living/say(message, bubble_type,var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
set waitfor = FALSE
var/static/list/crit_allowed_modes = list(MODE_WHISPER = TRUE, MODE_CHANGELING = TRUE, MODE_ALIEN = TRUE)
var/static/list/unconscious_allowed_modes = list(MODE_CHANGELING = TRUE, MODE_ALIEN = TRUE)
var/talk_key = get_key(message)
@@ -1132,10 +1132,6 @@
bellyup = 1
update_icons()
/mob/living/silicon/robot/adjustStaminaLossBuffered(amount, updating_health = 1)
if(istype(cell))
cell.charge -= amount * 5
/mob/living/silicon/robot/verb/viewmanifest()
set category = "Robot Commands"
set name = "View Crew Manifest"
@@ -236,7 +236,7 @@ Auto Patrol[]"},
if(targets.len>0)
var/mob/living/carbon/t = pick(targets)
if((t.stat!=2) && (t.lying != 1) && (!t.handcuffed)) //we don't shoot people who are dead, cuffed or lying down.
shootAt(t)
INVOKE_ASYNC(src, .proc/shootAt, t)
switch(mode)
if(BOT_IDLE) // idle
@@ -254,7 +254,7 @@ Auto Patrol[]"},
if(target) // make sure target exists
if(Adjacent(target) && isturf(target.loc)) // if right next to perp
stun_attack(target)
INVOKE_ASYNC(src, .proc/stun_attack, target)
mode = BOT_PREP_ARREST
anchored = TRUE
@@ -62,6 +62,9 @@
/mob/living/simple_animal/hostile/wizard/handle_automated_action()
. = ..()
INVOKE_ASYNC(src, .proc/AutomatedCast)
/mob/living/simple_animal/hostile/wizard/proc/AutomatedCast()
if(target && next_cast < world.time)
if((get_dir(src,target) in list(SOUTH,EAST,WEST,NORTH)) && fireball.cast_check(0,src)) //Lined up for fireball
src.setDir(get_dir(src,target))
@@ -436,12 +436,7 @@
newspeak.Add(possible_phrase)
speak = newspeak
//Search for item to steal
parrot_interest = search_for_item()
if(parrot_interest)
emote("me", EMOTE_VISIBLE, "looks in [parrot_interest]'s direction and takes flight.")
parrot_state = PARROT_SWOOP | PARROT_STEAL
icon_state = icon_living
INVOKE_ASYNC(src, .proc/attempt_item_theft)
return
//-----WANDERING - This is basically a 'I dont know what to do yet' state
@@ -620,6 +615,14 @@
parrot_lastmove = src.loc
return 0
/mob/living/simple_animal/parrot/proc/attempt_item_theft()
//Search for item to steal
search_for_item()
if(parrot_interest)
emote("me", EMOTE_VISIBLE, "looks in [parrot_interest]'s direction and takes flight.")
parrot_state = PARROT_SWOOP | PARROT_STEAL
icon_state = icon_living
/mob/living/simple_animal/parrot/proc/search_for_item()
var/item
for(var/atom/movable/AM in view(src))
+56
View File
@@ -0,0 +1,56 @@
/**
* Attempts to use an amount of stamina from our stamina buffer.
* Does not use anything if we don't have enough.
*
* Returns TRUE or FALSE based on if we have it.
*/
/mob/living/proc/UseStaminaBuffer(amount, warn = FALSE, considered_action = TRUE)
if(!(combat_flags & COMBAT_FLAG_STAMINA_BUFFER))
return TRUE
if(stamina_buffer < amount)
var/stamina_health = getStaminaLoss()
if(stamina_health > STAMINA_NO_OVERDRAW_THRESHOLD)
if(warn)
to_chat(src, "<span class='warning'>You do not have enough action stamina to do that!</span>")
return FALSE
adjustStaminaLoss(amount * CONFIG_GET(number/stamina_combat/overdraw_penalty_factor))
else
stamina_buffer -= amount
if(considered_action)
stamina_buffer_last_use = world.time
UpdateStaminaBuffer()
return TRUE
/**
* Updates our stamina buffer amount.
*/
/mob/living/proc/UpdateStaminaBuffer(updating_hud = TRUE)
var/time = world.time - stamina_buffer_regen_last
CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/buffer_max, buffer_max)
stamina_buffer_regen_last = world.time
if((stamina_buffer >= buffer_max) || !(combat_flags & COMBAT_FLAG_STAMINA_BUFFER))
stamina_buffer = buffer_max
return
else if(!time || HAS_TRAIT(src, TRAIT_NO_STAMINA_BUFFER_REGENERATION))
return
CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/out_of_combat_timer, out_of_combat_timer)
CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/base_regeneration, base_regeneration)
CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/combat_regeneration, combat_regeneration)
CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/percent_regeneration_out_of_combat, percent_regeneration_out_of_combat)
CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/post_action_penalty_delay, post_action_penalty_delay)
CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/post_action_penalty_factor, post_action_penalty_factor)
var/base_regen = (SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))? base_regeneration : combat_regeneration
var/time_since_last_action = world.time - stamina_buffer_last_use
var/action_penalty = ((time_since_last_action) < (post_action_penalty_delay * 10))? post_action_penalty_factor : 1
var/out_of_combat_bonus = (time_since_last_action < (out_of_combat_timer * 10))? 0 : ((buffer_max * percent_regeneration_out_of_combat * 0.01))
var/regen = ((base_regen * action_penalty) + out_of_combat_bonus) * time * 0.1 * stamina_buffer_regen_mod
stamina_buffer += min((buffer_max - stamina_buffer), regen)
if(updating_hud)
hud_used?.staminabuffer?.mark_dirty()
/**
* Boosts our stamina buffer by this much.
*/
/mob/living/proc/RechargeStaminaBuffer(amount)
stamina_buffer += abs(amount)
UpdateStaminaBuffer()
+29 -4
View File
@@ -38,6 +38,12 @@ Key procs
/// Multiplicative slowdown
var/multiplicative_slowdown = 0
/// Next two variables depend on this: Should we do advanced calculations?
var/complex_calculation = FALSE
/// Absolute max tiles we can boost to
var/absolute_max_tiles_per_second
/// Max tiles per second we can boost
var/max_tiles_per_second_boost
/// Movetypes this applies to
var/movetypes = ALL
@@ -53,6 +59,16 @@ Key procs
if(!id)
id = "[type]" //We turn the path into a string.
/**
* Returns new multiplicative movespeed after modification.
*/
/datum/movespeed_modifier/proc/apply_multiplicative(existing, mob/target)
if(!complex_calculation || (multiplicative_slowdown > 0)) // we aren't limiting how much things can slowdown.. yet.
return existing + multiplicative_slowdown
var/current_tiles = 10 / max(existing, world.tick_lag)
var/minimum_speed = 10 / min(current_tiles + max_tiles_per_second_boost, max(current_tiles, absolute_max_tiles_per_second))
return max(minimum_speed, existing + multiplicative_slowdown)
GLOBAL_LIST_EMPTY(movespeed_modification_cache)
/// Grabs a STATIC MODIFIER datum from cache. YOU MUST NEVER EDIT THESE DATUMS, OR IT WILL AFFECT ANYTHING ELSE USING IT TOO!
@@ -171,13 +187,15 @@ GLOBAL_LIST_EMPTY(movespeed_modification_cache)
/// Set or update the global movespeed config on a mob
/mob/proc/update_config_movespeed()
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/mob_config_speedmod, multiplicative_slowdown = get_config_multiplicative_speed())
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/mob_config_speedmod_floating, multiplicative_slowdown = get_config_multiplicative_speed(TRUE))
/// Get the global config movespeed of a mob by type
/mob/proc/get_config_multiplicative_speed()
if(!islist(GLOB.mob_config_movespeed_type_lookup) || !GLOB.mob_config_movespeed_type_lookup[type])
/mob/proc/get_config_multiplicative_speed(floating = FALSE)
var/list/read = floating? GLOB.mob_config_movespeed_type_lookup_floating : GLOB.mob_config_movespeed_type_lookup
if(!islist(read) || !read[type])
return 0
else
return GLOB.mob_config_movespeed_type_lookup[type]
return read[type]
/// Go through the list of movespeed modifiers and calculate a final movespeed. ANY ADD/REMOVE DONE IN UPDATE_MOVESPEED MUST HAVE THE UPDATE ARGUMENT SET AS FALSE!
/mob/proc/update_movespeed()
@@ -198,7 +216,7 @@ GLOBAL_LIST_EMPTY(movespeed_modification_cache)
conflict_tracker[conflict] = amt
else
continue
. += amt
. = M.apply_multiplicative(., src)
var/old = cached_multiplicative_slowdown // CITAEDL EDIT - To make things a bit less jarring, when in situations where
// your delay decreases, "give" the delay back to the client
cached_multiplicative_slowdown = .
@@ -220,6 +238,13 @@ GLOBAL_LIST_EMPTY(movespeed_modification_cache)
var/datum/movespeed_modifier/M = movespeed_modification[id]
. += M.multiplicative_slowdown
/**
* Gets the movespeed modifier datum of a modifier on a mob. Returns null if not found.
* DANGER: IT IS UP TO THE PERSON USING THIS TO MAKE SURE THE MODIFIER IS NOT MODIFIED IF IT HAPPENS TO BE GLOBAL/CACHED.
*/
/mob/proc/get_movespeed_modifier_datum(id)
return movespeed_modification[id]
/// Checks if a move speed modifier is valid and not missing any data
/proc/movespeed_data_null_check(datum/movespeed_modifier/M) //Determines if a data list is not meaningful and should be discarded.
. = TRUE
+3 -2
View File
@@ -1,12 +1,13 @@
/datum/movespeed_modifier/jetpack
conflicts_with = MOVE_CONFLICT_JETPACK
movetypes = FLOATING
multiplicative_slowdown = -1
/datum/movespeed_modifier/jetpack/cybernetic
multiplicative_slowdown = -0.5
multiplicative_slowdown = -1.25
/datum/movespeed_modifier/jetpack/fullspeed
multiplicative_slowdown = -2
multiplicative_slowdown = -1.5
/datum/movespeed_modifier/die_of_fate
multiplicative_slowdown = 1
+33
View File
@@ -111,6 +111,12 @@
/datum/movespeed_modifier/mob_config_speedmod
variable = TRUE
blacklisted_movetypes = FLOATING
flags = IGNORE_NOSLOW
/datum/movespeed_modifier/mob_config_speedmod_floating
variable = TRUE
movetypes = FLOATING
flags = IGNORE_NOSLOW
/datum/movespeed_modifier/liver_cirrhosis
@@ -120,3 +126,30 @@
/datum/movespeed_modifier/active_block
variable = TRUE
flags = IGNORE_NOSLOW
/datum/movespeed_modifier/sprinting
flags = IGNORE_NOSLOW
blacklisted_movetypes = FLOATING
priority = 100
/// for speed reasons this is sorta copypasty.
/datum/movespeed_modifier/sprinting/apply_multiplicative(existing, mob/target)
. = existing
if(target.m_intent != MOVE_INTENT_RUN)
return
if(isliving(target))
var/mob/living/L = target
if(!(L.mobility_flags & MOBILITY_STAND))
return
var/static/datum/config_entry/number/movedelay/sprint_max_tiles_increase/SMTI
if(!SMTI)
SMTI = CONFIG_GET_ENTRY(number/movedelay/sprint_max_tiles_increase)
var/static/datum/config_entry/number/movedelay/sprint_speed_increase/SSI
if(!SSI)
SSI = CONFIG_GET_ENTRY(number/movedelay/sprint_speed_increase)
var/static/datum/config_entry/number/movedelay/sprint_absolute_max_tiles/SAMT
if(!SAMT)
SAMT = CONFIG_GET_ENTRY(number/movedelay/sprint_absolute_max_tiles)
var/current_tiles = 10 / max(existing, world.tick_lag)
var/minimum_speed = 10 / min(max(SAMT.config_entry_value, current_tiles), current_tiles + SMTI.config_entry_value)
. = min(., max(minimum_speed, existing - SSI.config_entry_value))
@@ -30,20 +30,8 @@
. = ..()
START_PROCESSING(SSobj, src)
create_reagents(100 , AMOUNT_VISIBLE)
AddComponent(/datum/component/plumbing/simple_demand)
/obj/machinery/hydroponics/constructable/automagic/ComponentInitialize()
. = ..()
AddComponent(/datum/component/simple_rotation, ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_COUNTERCLOCKWISE | ROTATION_VERBS, null, CALLBACK(src, .proc/can_be_rotated))
/obj/machinery/hydroponics/constructable/proc/can_be_rotated(mob/user, rotation_type)
return !anchored
/obj/machinery/hydroponics/constructable/automagic/process()
if(reagents)
reagents.add_reagent(reagents)
reagents.clear_reagents()
if(dead)
dead = 0
qdel(myseed)
+27
View File
@@ -53,6 +53,33 @@
power_gen = 1250 // 2500 on T1, 10000 on T4.
circuit = /obj/item/circuitboard/machine/rtg/advanced
/obj/machinery/power/rtg/advanced/fullupgrade //fully ugpraded stock parts
desc = "An advanced RTG capable of moderating isotope decay, increasing power output but reducing lifetime. It uses plasma-fueled radiation collectors to increase output even further. This model is fully upgraded with the latest tech available in this quadrant."
/obj/machinery/power/rtg/advanced/fullupgrade/Initialize()
. = ..()
//This looks terrifying. And apparently instancing vars and modifying the amount variable causes runtime errors. Guess we're sticking to copy pasta, thanks, byond.
component_parts = list()
component_parts += new /obj/item/circuitboard/machine/rtg/advanced(null)
component_parts += new /obj/item/stack/sheet/mineral/uranium(null)
component_parts += new /obj/item/stack/sheet/mineral/uranium(null)
component_parts += new /obj/item/stack/sheet/mineral/uranium(null)
component_parts += new /obj/item/stack/sheet/mineral/uranium(null)
component_parts += new /obj/item/stack/sheet/mineral/uranium(null)
component_parts += new /obj/item/stack/sheet/mineral/uranium(null)
component_parts += new /obj/item/stack/sheet/mineral/uranium(null)
component_parts += new /obj/item/stack/sheet/mineral/uranium(null)
component_parts += new /obj/item/stack/sheet/mineral/uranium(null)
component_parts += new /obj/item/stack/sheet/mineral/uranium(null)
component_parts += new /obj/item/stack/sheet/mineral/plasma(null)
component_parts += new /obj/item/stack/sheet/mineral/plasma(null)
component_parts += new /obj/item/stack/sheet/mineral/plasma(null)
component_parts += new /obj/item/stack/sheet/mineral/plasma(null)
component_parts += new /obj/item/stack/sheet/mineral/plasma(null)
component_parts += new /obj/item/stock_parts/capacitor/quadratic(null)
component_parts += new /obj/item/stock_parts/micro_laser/quadultra(null)
RefreshParts()
// Void Core, power source for Abductor ships and bases.
// Provides a lot of power, but tends to explode when mistreated.
@@ -928,8 +928,15 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
var/mob/living/user = AM
if(user.status_flags & GODMODE)
return
message_admins("[src] has consumed [key_name_admin(user)] [ADMIN_JMP(src)].")
investigate_log("has consumed [key_name(user)].", INVESTIGATE_SUPERMATTER)
var/add
if(user.mind?.assigned_role == "Clown")
var/denergy = rand(-1000, 1000)
var/ddamage = rand(-150, clamp(150, 0, (explosion_point - damage) + 150))
power += denergy
damage += ddamage
add = ", adding [denergy] energy and [ddamage] damage to the crystal"
message_admins("[src] has consumed [key_name_admin(user)] [ADMIN_JMP(src)][add].")
investigate_log("has consumed [key_name(user)][add].", INVESTIGATE_SUPERMATTER)
user.dust(force = TRUE)
if(power_changes)
matter_power += 200
+6 -2
View File
@@ -154,8 +154,8 @@
shake_camera(user, recoil + 1, recoil)
if(stam_cost) //CIT CHANGE - makes gun recoil cause staminaloss
var/safe_cost = clamp(stam_cost, 0, STAMINA_NEAR_CRIT - user.getStaminaLoss())*(firing && burst_size >= 2 ? 1/burst_size : 1)
user.adjustStaminaLossBuffered(safe_cost) //CIT CHANGE - ditto
var/safe_cost = clamp(stam_cost, 0, user.stamina_buffer)*(firing && burst_size >= 2 ? 1/burst_size : 1)
user.UseStaminaBuffer(safe_cost)
if(suppressed)
playsound(user, fire_sound, 10, 1)
@@ -590,6 +590,9 @@
update_icon()
/obj/item/gun/proc/getinaccuracy(mob/living/user, bonus_spread, stamloss)
return 0 // Replacement TBD: Exponential curved aim instability system.
/*
if(inaccuracy_modifier == 0)
return bonus_spread
var/base_inaccuracy = weapon_weight * 25 * inaccuracy_modifier
@@ -612,6 +615,7 @@
if(mult < 0) //accurate weapons should provide a proper bonus with negative inaccuracy. the opposite is true too.
mult *= 1/inaccuracy_modifier
return max(bonus_spread + (base_inaccuracy * mult), 0) //no negative spread.
*/
/obj/item/gun/proc/getstamcost(mob/living/carbon/user)
. = recoil
@@ -44,10 +44,9 @@
if(HAS_TRAIT(user, TRAIT_FAST_PUMP))
recentpump = world.time + 2
else
if(!user.UseStaminaBuffer(2, warn = TRUE))
return
recentpump = world.time + 10
if(istype(user))//CIT CHANGE - makes pumping shotguns cost a lil bit of stamina.
user.adjustStaminaLossBuffered(2) //CIT CHANGE - DITTO. make this scale inversely to the strength stat when stats/skills are added
return
/obj/item/gun/ballistic/shotgun/blow_up(mob/user)
. = 0
@@ -233,7 +233,7 @@
p_blue.link_portal(p_orange)
/obj/item/gun/energy/wormhole_projector/proc/create_portal(obj/item/projectile/beam/wormhole/W, turf/target)
var/obj/effect/portal/P = new /obj/effect/portal(target, src, 300, null, FALSE, null, atmos_link)
var/obj/effect/portal/P = new /obj/effect/portal(target, 300, null, FALSE, null, atmos_link)
if(istype(W, /obj/item/projectile/beam/wormhole/orange))
qdel(p_orange)
p_orange = P
@@ -2283,7 +2283,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
boozepwr = 50
/datum/reagent/consumable/ethanol/species_drink/on_mob_life(mob/living/carbon/C)
if(C.dna.species && C.dna.species.species_type == species_required) //species have a species_type variable that refers to one of the drinks
if(C.dna.species && C.dna.species.species_category == species_required) //species have a species_category variable that refers to one of the drinks
quality = RACE_DRINK
else
C.adjust_disgust(disgust)
@@ -181,6 +181,14 @@
build_path = /obj/item/storage/belt/janitor
category = list("initial","Organic Materials")
/datum/design/plantbelt
name = "Botanical Belt"
id = "plantbelt"
build_type = BIOGENERATOR
materials = list(/datum/material/biomass= 300)
build_path = /obj/item/storage/belt/plant
category = list("initial","Organic Materials")
/datum/design/s_holster
name = "Shoulder Holster"
id = "s_holster"
@@ -32,7 +32,7 @@
materials = list(/datum/material/glass = 3000, /datum/material/plasma = 3000, /datum/material/diamond = 250, /datum/material/bluespace = 250)
build_path = /obj/item/reagent_containers/glass/beaker/bluespace
category = list("Medical Designs")
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_SERVICE
/datum/design/noreactbeaker
name = "Cryostasis Beaker"
@@ -973,6 +973,10 @@
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
/////////////////////////////////////////
//////////// Plumbing //////////
/////////////////////////////////////////
/datum/design/acclimator
name = "Plumbing Acclimator"
desc = "A heating and cooling device for pipes!"
@@ -1126,3 +1130,14 @@
build_path = /obj/item/construction/plumbing
category = list("Misc","Medical Designs")
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
/datum/design/rplunger
name = "Reinforced Plunger"
desc = "A plunger designed for heavy duty clogs."
id = "rplunger"
build_type = PROTOLATHE
materials = list(/datum/material/plasma = 1000, /datum/material/iron = 1000, /datum/material/glass = 1000)
construction_time = 15
build_path = /obj/item/plunger/reinforced
category = list ("Misc","Medical Designs")
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_CARGO
@@ -38,7 +38,7 @@
display_name = "Advanced Plumbing Technology"
description = "Plumbing RCD."
prereq_ids = list("plumbing", "adv_engi")
design_ids = list("plumb_rcd", "autohydrotray")
design_ids = list("plumb_rcd", "autohydrotray", "rplunger")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
//////////////////////Cybernetics/////////////////////
+28
View File
@@ -2,13 +2,41 @@
//Keep this sorted alphabetically
#ifdef UNIT_TESTS
/// Asserts that a condition is true
/// If the condition is not true, fails the test
#define TEST_ASSERT(assertion, reason) if (!(assertion)) { return Fail("Assertion failed: [reason || "No reason"]") }
/// Asserts that the two parameters passed are equal, fails otherwise
/// Optionally allows an additional message in the case of a failure
#define TEST_ASSERT_EQUAL(a, b, message) if ((a) != (b)) { return Fail("Expected [isnull(a) ? "null" : a] to be equal to [isnull(b) ? "null" : b].[message ? " [message]" : ""]") }
#include "anchored_mobs.dm"
#include "bespoke_id.dm"
// #include "binary_insert.dm"
// #include "card_mismatch.dm" shame we don't have this!
#include "chain_pull_through_space.dm"
#include "character_saving.dm"
#include "component_tests.dm"
// #include "confusion.dm"
// #include "keybinding_init.dm"
#include "machine_disassembly.dm"
#include "medical_wounds.dm"
// #include "metabolizing.dm"
// #include "outfit_sanity.dm"
// #include "plantgrowth_tests.dm"
// #include "quick_swap_sanity.dm" - we don't have quick swap yet
#include "reagent_id_typos.dm"
#include "reagent_recipe_collisions.dm"
#include "resist.dm"
// #include "say.dm" //no saymods, someone update saycode please.
// #include "siunit.dm"
#include "spawn_humans.dm"
// #include "species_whitelists.dm"
#include "subsystem_init.dm"
#include "surgeries.dm"
#include "timer_sanity.dm"
#include "unit_test.dm"
#undef TEST_ASSERT
#undef TEST_ASSERT_EQUAL
#endif
+1 -1
View File
@@ -6,4 +6,4 @@
L += "[i]"
if(!L.len)
return //passed!
Fail("The following mobs are defined as anchored. This is incompatible with the new move force/resist system and needs to be revised.: [L.Join(" ")]")
Fail("The following mobs are defined as anchored. This is incompatible with the new move force/resist system and needs to be revised.: [L.Join(" ")]")
+8
View File
@@ -0,0 +1,8 @@
/datum/unit_test/bespoke_id/Run()
var/datum/element/base = /datum/element
var/base_index = initial(base.id_arg_index)
for(var/i in subtypesof(/datum/element))
var/datum/element/faketype = i
if((initial(faketype.element_flags) & ELEMENT_BESPOKE) && initial(faketype.id_arg_index) == base_index)
Fail("A bespoke element was not configured with a proper id_arg_index: [faketype]")
+26
View File
@@ -0,0 +1,26 @@
/// A test to ensure the sanity of BINARY_INSERT
/datum/unit_test/binary_insert/Run()
var/list/datum/binary_insert_node/nodes = list()
var/datum/binary_insert_node/node_a = new /datum/binary_insert_node(10)
BINARY_INSERT(node_a, nodes, /datum/binary_insert_node, node_a, x, COMPARE_KEY)
TEST_ASSERT_EQUAL(nodes.len, 1, "List should have one node")
var/datum/binary_insert_node/node_b = new /datum/binary_insert_node(5)
BINARY_INSERT(node_b, nodes, /datum/binary_insert_node, node_b, x, COMPARE_KEY)
TEST_ASSERT_EQUAL(nodes.len, 2, "List should have two nodes")
TEST_ASSERT_EQUAL(nodes[1].x, 5, "The first node should be the one with 5")
TEST_ASSERT_EQUAL(nodes[2].x, 10, "The second node should be the one with 10")
var/datum/binary_insert_node/node_c = new /datum/binary_insert_node(15)
BINARY_INSERT(node_c, nodes, /datum/binary_insert_node, node_c, x, COMPARE_KEY)
TEST_ASSERT_EQUAL(nodes.len, 3, "List should have three nodes")
TEST_ASSERT_EQUAL(nodes[1].x, 5, "The first node should be the one with 5")
TEST_ASSERT_EQUAL(nodes[2].x, 10, "The second node should be the one with 10")
TEST_ASSERT_EQUAL(nodes[3].x, 15, "The third node should be the one with 15")
/datum/binary_insert_node
var/x
/datum/binary_insert_node/New(_x)
x = _x
@@ -0,0 +1,62 @@
/datum/unit_test/chain_pull_through_space
var/turf/open/space/space_tile
var/turf/claimed_tile
var/mob/living/carbon/human/alice
var/mob/living/carbon/human/bob
var/mob/living/carbon/human/charlie
/datum/unit_test/chain_pull_through_space/New()
..()
// Create a space tile that goes to another z-level
claimed_tile = run_loc_bottom_left
space_tile = new(locate(run_loc_bottom_left.x, run_loc_bottom_left.y, run_loc_bottom_left.z))
space_tile.destination_x = 100
space_tile.destination_y = 100
space_tile.destination_z = 5
// Create our list of humans, all adjacent to one another
alice = new(locate(run_loc_bottom_left.x + 2, run_loc_bottom_left.y, run_loc_bottom_left.z))
alice.name = "Alice"
bob = new(locate(run_loc_bottom_left.x + 3, run_loc_bottom_left.y, run_loc_bottom_left.z))
bob.name = "Bob"
charlie = new(locate(run_loc_bottom_left.x + 4, run_loc_bottom_left.y, run_loc_bottom_left.z))
charlie.name = "Charlie"
/datum/unit_test/chain_pull_through_space/Destroy()
space_tile.copyTurf(claimed_tile)
qdel(alice)
qdel(bob)
qdel(charlie)
return ..()
/datum/unit_test/chain_pull_through_space/Run()
// Alice pulls Bob, who pulls Charlie
// Normally, when Alice moves forward, the rest follow
alice.start_pulling(bob)
bob.start_pulling(charlie)
// Walk normally to the left, make sure we're still a chain
alice.Move(locate(run_loc_bottom_left.x + 1, run_loc_bottom_left.y, run_loc_bottom_left.z))
if (bob.x != run_loc_bottom_left.x + 2)
return Fail("During normal move, Bob was not at the correct x ([bob.x])")
if (charlie.x != run_loc_bottom_left.x + 3)
return Fail("During normal move, Charlie was not at the correct x ([charlie.x])")
// We're going through the space turf now that should teleport us
alice.Move(run_loc_bottom_left)
if (alice.z != space_tile.destination_z)
return Fail("Alice did not teleport to the destination z-level. Current location: ([alice.x], [alice.y], [alice.z])")
if (bob.z != space_tile.destination_z)
return Fail("Bob did not teleport to the destination z-level. Current location: ([bob.x], [bob.y], [bob.z])")
if (!bob.Adjacent(alice))
return Fail("Bob is not adjacent to Alice. Bob is at [bob.x], Alice is at [alice.x]")
if (charlie.z != space_tile.destination_z)
return Fail("Charlie did not teleport to the destination z-level. Current location: ([charlie.x], [charlie.y], [charlie.z])")
if (!charlie.Adjacent(bob))
return Fail("Charlie is not adjacent to Bob. Charlie is at [charlie.x], Bob is at [bob.x]")
+1 -1
View File
@@ -9,4 +9,4 @@
if(dupe_type && !ispath(dupe_type))
bad_dts += t
if(length(bad_dms) || length(bad_dts))
Fail("Components with invalid dupe modes: ([bad_dms.Join(",")]) ||| Components with invalid dupe types: ([bad_dts.Join(",")])")
Fail("Components with invalid dupe modes: ([bad_dms.Join(",")]) ||| Components with invalid dupe types: ([bad_dts.Join(",")])")
@@ -0,0 +1,13 @@
/// Ensures that when disassembling a machine, all the parts are given back
/datum/unit_test/machine_disassembly/Run()
var/obj/machinery/freezer = allocate(/obj/machinery/atmospherics/components/unary/thermomachine/freezer)
var/turf/freezer_location = freezer.loc
freezer_location.ChangeTurf(/turf/open/floor/plasteel)
freezer.deconstruct()
// Check that the components are created
TEST_ASSERT(locate(/obj/item/stock_parts/micro_laser) in freezer_location, "Couldn't find micro-laser when disassembling freezer")
// Check that the circuit board itself is created
TEST_ASSERT(locate(/obj/item/circuitboard/machine/thermomachine/freezer) in freezer_location, "Couldn't find the circuit board when disassembling freezer")
+87
View File
@@ -0,0 +1,87 @@
/// This test is used to make sure a flesh-and-bone base human can suffer all the types of wounds, and that suffering more severe wounds removes and replaces the lesser wound. Also tests that [/mob/living/carbon/proc/fully_heal] removes all wounds
/datum/unit_test/test_human_base/Run()
var/mob/living/carbon/human/victim = allocate(/mob/living/carbon/human)
/// the limbs have no wound resistance like the chest and head do, so let's go with the r_arm
var/obj/item/bodypart/tested_part = victim.get_bodypart(BODY_ZONE_R_ARM)
/// In order of the wound types we're trying to inflict, what sharpness do we need to deal them?
var/list/sharps = list(SHARP_NONE, SHARP_EDGED, SHARP_POINTY, SHARP_NONE)
/// Since burn wounds need burn damage, duh
var/list/dam_types = list(BRUTE, BRUTE, BRUTE, BURN)
var/i = 1
var/list/iter_test_wound_list
for(iter_test_wound_list in list(list(/datum/wound/blunt/moderate, /datum/wound/blunt/severe, /datum/wound/blunt/critical),\
list(/datum/wound/slash/moderate, /datum/wound/slash/severe, /datum/wound/slash/critical),\
list(/datum/wound/pierce/moderate, /datum/wound/pierce/severe, /datum/wound/pierce/critical),\
list(/datum/wound/burn/moderate, /datum/wound/burn/severe, /datum/wound/burn/critical)))
TEST_ASSERT_EQUAL(length(victim.all_wounds), 0, "Patient is somehow wounded before test")
var/datum/wound/iter_test_wound
var/threshold_penalty = 0
for(iter_test_wound in iter_test_wound_list)
var/threshold = initial(iter_test_wound.threshold_minimum) - threshold_penalty // just enough to guarantee the next tier of wound, given the existing wound threshold penalty
if(dam_types[i] == BRUTE)
tested_part.receive_damage(WOUND_MINIMUM_DAMAGE, 0, wound_bonus = threshold, sharpness=sharps[i])
else if(dam_types[i] == BURN)
tested_part.receive_damage(0, WOUND_MINIMUM_DAMAGE, wound_bonus = threshold, sharpness=sharps[i])
TEST_ASSERT(length(victim.all_wounds), "Patient has no wounds when one wound is expected. Severity: [initial(iter_test_wound.severity)]")
TEST_ASSERT_EQUAL(length(victim.all_wounds), 1, "Patient has more than one wound when only one is expected. Severity: [initial(iter_test_wound.severity)]")
var/datum/wound/actual_wound = victim.all_wounds[1]
TEST_ASSERT_EQUAL(actual_wound.type, iter_test_wound, "Patient has wound of incorrect severity. Expected: [initial(iter_test_wound.name)] Got: [actual_wound]")
threshold_penalty = actual_wound.threshold_penalty
i++
victim.fully_heal(TRUE) // should clear all wounds between types
/// This test is used for making sure species with bones but no flesh (skeletons, plasmamen) can only suffer BONE_WOUNDS, and nothing tagged with FLESH_WOUND (it's possible to require both)
/datum/unit_test/test_human_bone/Run()
var/mob/living/carbon/human/victim = allocate(/mob/living/carbon/human)
/// the limbs have no wound resistance like the chest and head do, so let's go with the r_arm
var/obj/item/bodypart/tested_part = victim.get_bodypart(BODY_ZONE_R_ARM)
/// In order of the wound types we're trying to inflict, what sharpness do we need to deal them?
var/list/sharps = list(SHARP_NONE, SHARP_EDGED, SHARP_POINTY, SHARP_NONE)
/// Since burn wounds need burn damage, duh
var/list/dam_types = list(BRUTE, BRUTE, BRUTE, BURN)
var/i = 1
var/list/iter_test_wound_list
victim.dna.species.species_traits &= HAS_FLESH // take away the base human's flesh (ouchie!) ((not actually ouchie, this just affects their wounds and dismemberment handling))
for(iter_test_wound_list in list(list(/datum/wound/blunt/moderate, /datum/wound/blunt/severe, /datum/wound/blunt/critical),\
list(/datum/wound/slash/moderate, /datum/wound/slash/severe, /datum/wound/slash/critical),\
list(/datum/wound/pierce/moderate, /datum/wound/pierce/severe, /datum/wound/pierce/critical),\
list(/datum/wound/burn/moderate, /datum/wound/burn/severe, /datum/wound/burn/critical)))
TEST_ASSERT_EQUAL(length(victim.all_wounds), 0, "Patient is somehow wounded before test")
var/datum/wound/iter_test_wound
var/threshold_penalty = 0
for(iter_test_wound in iter_test_wound_list)
var/threshold = initial(iter_test_wound.threshold_minimum) - threshold_penalty // just enough to guarantee the next tier of wound, given the existing wound threshold penalty
if(dam_types[i] == BRUTE)
tested_part.receive_damage(WOUND_MINIMUM_DAMAGE, 0, wound_bonus = threshold, sharpness=sharps[i])
else if(dam_types[i] == BURN)
tested_part.receive_damage(0, WOUND_MINIMUM_DAMAGE, wound_bonus = threshold, sharpness=sharps[i])
// so if we just tried to deal a flesh wound, make sure we didn't actually suffer it. We may have suffered a bone wound instead, but we just want to make sure we don't have a flesh wound
if(initial(iter_test_wound.wound_flags) & FLESH_WOUND)
if(!length(victim.all_wounds)) // not having a wound is good news
continue
else // we have to check that it's actually a bone wound and not the intended wound type
TEST_ASSERT_EQUAL(length(victim.all_wounds), 1, "Patient has more than one wound when only one is expected. Severity: [initial(iter_test_wound.severity)]")
var/datum/wound/actual_wound = victim.all_wounds[1]
TEST_ASSERT((actual_wound.wound_flags & ~FLESH_WOUND), "Patient has flesh wound despite no HAS_FLESH flag, expected either no wound or bone wound. Offending wound: [actual_wound]")
threshold_penalty = actual_wound.threshold_penalty
else // otherwise if it's a bone wound, check that we have it per usual
TEST_ASSERT(length(victim.all_wounds), "Patient has no wounds when one wound is expected. Severity: [initial(iter_test_wound.severity)]")
TEST_ASSERT_EQUAL(length(victim.all_wounds), 1, "Patient has more than one wound when only one is expected. Severity: [initial(iter_test_wound.severity)]")
var/datum/wound/actual_wound = victim.all_wounds[1]
TEST_ASSERT_EQUAL(actual_wound.type, iter_test_wound, "Patient has wound of incorrect severity. Expected: [initial(iter_test_wound.name)] Got: [actual_wound]")
threshold_penalty = actual_wound.threshold_penalty
i++
victim.fully_heal(TRUE) // should clear all wounds between types
+19
View File
@@ -0,0 +1,19 @@
/datum/unit_test/metabolization/Run()
// Pause natural mob life so it can be handled entirely by the test
SSmobs.pause()
var/mob/living/carbon/human/human = allocate(/mob/living/carbon/human)
var/mob/living/carbon/monkey/monkey = allocate(/mob/living/carbon/monkey)
for (var/reagent_type in subtypesof(/datum/reagent))
test_reagent(human, reagent_type)
test_reagent(monkey, reagent_type)
/datum/unit_test/metabolization/proc/test_reagent(mob/living/carbon/C, reagent_type)
C.reagents.add_reagent(reagent_type, 10)
C.reagents.metabolize(C, can_overdose = TRUE)
C.reagents.clear_reagents()
/datum/unit_test/metabolization/Destroy()
SSmobs.ignite()
return ..()
+50
View File
@@ -0,0 +1,50 @@
#define CHECK_OUTFIT_SLOT(outfit_key, slot_name) if (outfit.##outfit_key) { \
H.equip_to_slot_or_del(new outfit.##outfit_key(H), ##slot_name, TRUE); \
/* We don't check the result of equip_to_slot_or_del because it returns false for random jumpsuits, as they delete themselves on init */ \
if (!H.get_item_by_slot(##slot_name)) { \
Fail("[outfit.name]'s [#outfit_key] is invalid!"); \
} \
}
/datum/unit_test/outfit_sanity/Run()
var/mob/living/carbon/human/H = allocate(/mob/living/carbon/human)
for (var/outfit_type in subtypesof(/datum/outfit))
// Only make one human and keep undressing it because it's much faster
for (var/obj/item/I in H.get_equipped_items(include_pockets = TRUE))
qdel(I)
var/datum/outfit/outfit = new outfit_type
outfit.pre_equip(H, TRUE)
CHECK_OUTFIT_SLOT(uniform, ITEM_SLOT_ICLOTHING)
CHECK_OUTFIT_SLOT(suit, ITEM_SLOT_OCLOTHING)
CHECK_OUTFIT_SLOT(back, ITEM_SLOT_BACK)
CHECK_OUTFIT_SLOT(belt, ITEM_SLOT_BELT)
CHECK_OUTFIT_SLOT(gloves, ITEM_SLOT_GLOVES)
CHECK_OUTFIT_SLOT(shoes, ITEM_SLOT_FEET)
CHECK_OUTFIT_SLOT(head, ITEM_SLOT_HEAD)
CHECK_OUTFIT_SLOT(mask, ITEM_SLOT_MASK)
CHECK_OUTFIT_SLOT(neck, ITEM_SLOT_NECK)
CHECK_OUTFIT_SLOT(ears, ITEM_SLOT_EARS)
CHECK_OUTFIT_SLOT(glasses, ITEM_SLOT_EYES)
CHECK_OUTFIT_SLOT(id, ITEM_SLOT_ID)
CHECK_OUTFIT_SLOT(suit_store, ITEM_SLOT_SUITSTORE)
CHECK_OUTFIT_SLOT(l_pocket, ITEM_SLOT_POCKET)
CHECK_OUTFIT_SLOT(r_pocket, ITEM_SLOT_POCKET)
if (outfit.backpack_contents || outfit.box)
var/list/backpack_contents = outfit.backpack_contents?.Copy()
if (outfit.box)
if (!backpack_contents)
backpack_contents = list()
backpack_contents.Insert(1, outfit.box)
backpack_contents[outfit.box] = 1
for (var/path in backpack_contents)
var/number = backpack_contents[path] || 1
for (var/_ in 1 to number)
if (!H.equip_to_slot_or_del(new path(H), ITEM_SLOT_BACKPACK, TRUE))
Fail("[outfit.name]'s backpack_contents are invalid! Couldn't add [path] to backpack.")
#undef CHECK_OUTFIT_SLOT
@@ -0,0 +1,27 @@
// Checks plants for broken tray icons. Use Advanced Proc Call to activate.
// Maybe some day it would be used as unit test.
// -------- IT IS NOW!
/datum/unit_test/plantgrowth/Run()
var/list/states = icon_states('icons/obj/hydroponics/growing.dmi')
states |= icon_states('icons/obj/hydroponics/growing_fruits.dmi')
states |= icon_states('icons/obj/hydroponics/growing_flowers.dmi')
states |= icon_states('icons/obj/hydroponics/growing_mushrooms.dmi')
states |= icon_states('icons/obj/hydroponics/growing_vegetables.dmi')
states |= icon_states('goon/icons/obj/hydroponics.dmi')
var/list/paths = subtypesof(/obj/item/seeds) - /obj/item/seeds - typesof(/obj/item/seeds/sample) - /obj/item/seeds/lavaland
for(var/seedpath in paths)
var/obj/item/seeds/seed = new seedpath
for(var/i in 1 to seed.growthstages)
if("[seed.icon_grow][i]" in states)
continue
Fail("[seed.name] ([seed.type]) lacks the [seed.icon_grow][i] icon!")
if(!(seed.icon_dead in states))
Fail("[seed.name] ([seed.type]) lacks the [seed.icon_dead] icon!")
if(seed.icon_harvest) // mushrooms have no grown sprites, same for items with no product
if(!(seed.icon_harvest in states))
Fail("[seed.name] ([seed.type]) lacks the [seed.icon_harvest] icon!")
@@ -0,0 +1,31 @@
/// Test that quick swap correctly swaps items and invalidates suit storage
/datum/unit_test/quick_swap_sanity/Run()
// Create a human with a medical winter coat and a health analyzer in suit storage
var/mob/living/carbon/human/human = allocate(/mob/living/carbon/human)
var/obj/item/coat = allocate(/obj/item/clothing/suit/hooded/wintercoat/medical)
TEST_ASSERT(human.equip_to_slot_if_possible(coat, ITEM_SLOT_OCLOTHING), "Couldn't equip winter coat")
var/obj/item/analyzer = allocate(/obj/item/healthanalyzer)
TEST_ASSERT(human.equip_to_slot_if_possible(analyzer, ITEM_SLOT_SUITSTORE), "Couldn't equip health analyzer")
// Then, have them quick swap between the coat and a space suit
var/obj/item/hardsuit = allocate(/obj/item/clothing/suit/space/hardsuit)
TEST_ASSERT(human.equip_to_appropriate_slot(hardsuit, swap = TRUE), "Couldn't quick swap to hardsuit")
// Check if the human has the hardsuit on
TEST_ASSERT_EQUAL(human.wear_suit, hardsuit, "Human didn't equip the hardsuit")
// Make sure the health analyzer was dropped as part of the swap
// Since health analyzers are an invalid suit storage item
TEST_ASSERT_EQUAL(human.s_store, null, "Human didn't drop the health analyzer")
// Give the human an emergency oxygen tank
// This is valid suit storage for both the winter coat AND the hardsuit
var/obj/item/tank = allocate(/obj/item/tank/internals/emergency_oxygen)
TEST_ASSERT(human.equip_to_slot_if_possible(tank, ITEM_SLOT_SUITSTORE), "Couldn't equip emergency oxygen tank")
// Now, quick swap back to the coat
// Since the tank is a valid suit storage item, it should not be dropped
TEST_ASSERT(human.equip_to_appropriate_slot(coat, swap = TRUE), "Couldn't quick swap to coat")
TEST_ASSERT_EQUAL(human.s_store, tank, "Human dropped the oxygen tank, when it was a valid item to keep in suit storage")
+29
View File
@@ -0,0 +1,29 @@
/// Test that stop, drop, and roll lowers fire stacks
/datum/unit_test/stop_drop_and_roll/Run()
var/mob/living/carbon/human/human = allocate(/mob/living/carbon/human)
TEST_ASSERT_EQUAL(human.fire_stacks, 0, "Human does not have 0 fire stacks pre-ignition")
human.adjust_fire_stacks(5)
human.IgniteMob()
TEST_ASSERT_EQUAL(human.fire_stacks, 5, "Human does not have 5 fire stacks pre-resist")
// Stop, drop, and roll has a sleep call. This would delay the test, and is not necessary.
CallAsync(human, /mob/living/verb/resist)
TEST_ASSERT(human.fire_stacks < 5, "Human did not lower fire stacks after resisting")
/// Test that you can resist out of a container
/datum/unit_test/container_resist/Run()
var/mob/living/carbon/human/human = allocate(/mob/living/carbon/human)
var/obj/structure/closet/closet = allocate(/obj/structure/closet, get_turf(human))
closet.open(human)
TEST_ASSERT(!(human in closet.contents), "Human was in the contents of an open closet")
closet.close(human)
TEST_ASSERT(human in closet.contents, "Human was not in the contents of the closed closet")
human.resist()
TEST_ASSERT(!(human in closet.contents), "Human resisted out of a standard closet, but was still in it")
+24
View File
@@ -0,0 +1,24 @@
/// Test to verify message mods are parsed correctly
/datum/unit_test/get_message_mods
var/mob/host_mob
/datum/unit_test/get_message_mods/Run()
host_mob = allocate(/mob/living/carbon/human)
test("Hello", "Hello", list())
test(";HELP", "HELP", list(MODE_HEADSET = TRUE))
test(";%Never gonna give you up", "Never gonna give you up", list(MODE_HEADSET = TRUE, MODE_SING = TRUE))
test(".s Gun plz", "Gun plz", list(RADIO_KEY = RADIO_KEY_SECURITY, RADIO_EXTENSION = RADIO_CHANNEL_SECURITY))
test("...What", "...What", list())
//note to lettern: add the ++, ||, __, and the verb*text checks
/datum/unit_test/get_message_mods/proc/test(message, expected_message, list/expected_mods)
var/list/mods = list()
TEST_ASSERT_EQUAL(host_mob.get_message_mods(message, mods), expected_message, "Chopped message was not what we expected. Message: [message]")
for (var/mod_key in mods)
TEST_ASSERT_EQUAL(mods[mod_key], expected_mods[mod_key], "The value for [mod_key] was not what we expected. Message: [message]")
expected_mods -= mod_key
if (expected_mods.len)
Fail("Some message mods were expected, but were not returned by get_message_mods: [json_encode(expected_mods)]. Message: [message]")
+4 -4
View File
@@ -1,7 +1,7 @@
/datum/unit_test/spawn_humans/Run()
var/locs = block(run_loc_bottom_left, run_loc_top_right)
var/locs = block(run_loc_bottom_left, run_loc_top_right)
for(var/I in 1 to 5)
new /mob/living/carbon/human(pick(locs))
for(var/I in 1 to 5)
new /mob/living/carbon/human(pick(locs))
sleep(50)
sleep(50)
+1 -1
View File
@@ -4,4 +4,4 @@
if(ss.flags & SS_NO_INIT)
continue
if(!ss.initialized)
Fail("[ss]([ss.type]) is a subsystem meant to initialize but doesn't get set as initialized.")
Fail("[ss]([ss.type]) is a subsystem meant to initialize but doesn't get set as initialized.")
+79
View File
@@ -0,0 +1,79 @@
/datum/unit_test/amputation/Run()
var/mob/living/carbon/human/patient = allocate(/mob/living/carbon/human)
var/mob/living/carbon/human/user = allocate(/mob/living/carbon/human)
TEST_ASSERT_EQUAL(patient.get_missing_limbs().len, 0, "Patient is somehow missing limbs before surgery")
var/datum/surgery/amputation/surgery = new(patient, BODY_ZONE_R_ARM, patient.get_bodypart(BODY_ZONE_R_ARM))
var/datum/surgery_step/sever_limb/sever_limb = new
sever_limb.success(user, patient, BODY_ZONE_R_ARM, null, surgery)
TEST_ASSERT_EQUAL(patient.get_missing_limbs().len, 1, "Patient did not lose any limbs")
TEST_ASSERT_EQUAL(patient.get_missing_limbs()[1], BODY_ZONE_R_ARM, "Patient is missing a limb that isn't the one we operated on")
/datum/unit_test/brain_surgery/Run()
var/mob/living/carbon/human/patient = allocate(/mob/living/carbon/human)
patient.gain_trauma_type(BRAIN_TRAUMA_MILD, TRAUMA_RESILIENCE_SURGERY)
patient.setOrganLoss(ORGAN_SLOT_BRAIN, 20)
TEST_ASSERT(patient.has_trauma_type(), "Patient does not have any traumas, despite being given one")
var/mob/living/carbon/human/user = allocate(/mob/living/carbon/human)
var/datum/surgery_step/fix_brain/fix_brain = new
fix_brain.success(user, patient)
TEST_ASSERT(!patient.has_trauma_type(), "Patient kept their brain trauma after brain surgery")
TEST_ASSERT(patient.getOrganLoss(ORGAN_SLOT_BRAIN) < 20, "Patient did not heal their brain damage after brain surgery")
/datum/unit_test/multiple_surgeries/Run()
var/mob/living/carbon/human/user = allocate(/mob/living/carbon/human)
var/mob/living/carbon/human/patient_zero = allocate(/mob/living/carbon/human)
var/mob/living/carbon/human/patient_one = allocate(/mob/living/carbon/human)
var/obj/item/scalpel/scalpel = allocate(/obj/item/scalpel)
var/datum/surgery_step/incise/surgery_step = new
var/datum/surgery/organ_manipulation/surgery_for_zero = new
INVOKE_ASYNC(surgery_step, /datum/surgery_step/proc/initiate, user, patient_zero, BODY_ZONE_CHEST, scalpel, surgery_for_zero)
TEST_ASSERT(surgery_for_zero.step_in_progress, "Surgery on patient zero was not initiated")
var/datum/surgery/organ_manipulation/surgery_for_one = new
// Without waiting for the incision to complete, try to start a new surgery
TEST_ASSERT(!surgery_step.initiate(user, patient_one, BODY_ZONE_CHEST, scalpel, surgery_for_one), "Was allowed to start a second surgery without the rod of asclepius")
TEST_ASSERT(!surgery_for_one.step_in_progress, "Surgery for patient one is somehow in progress, despite not initiating")
user.apply_status_effect(STATUS_EFFECT_HIPPOCRATIC_OATH)
INVOKE_ASYNC(surgery_step, /datum/surgery_step/proc/initiate, user, patient_one, BODY_ZONE_CHEST, scalpel, surgery_for_one)
TEST_ASSERT(surgery_for_one.step_in_progress, "Surgery on patient one was not initiated, despite having rod of asclepius")
/datum/unit_test/tend_wounds/Run()
var/mob/living/carbon/human/patient = allocate(/mob/living/carbon/human)
patient.take_overall_damage(100, 100)
var/mob/living/carbon/human/user = allocate(/mob/living/carbon/human)
// Test that tending wounds actually lowers damage
var/datum/surgery_step/heal/brute/basic/basic_brute_heal = new
basic_brute_heal.success(user, patient, BODY_ZONE_CHEST)
TEST_ASSERT(patient.getBruteLoss() < 100, "Tending brute wounds didn't lower brute damage ([patient.getBruteLoss()])")
var/datum/surgery_step/heal/burn/basic/basic_burn_heal = new
basic_burn_heal.success(user, patient, BODY_ZONE_CHEST)
TEST_ASSERT(patient.getFireLoss() < 100, "Tending burn wounds didn't lower burn damage ([patient.getFireLoss()])")
// Test that wearing clothing lowers heal amount
var/mob/living/carbon/human/naked_patient = allocate(/mob/living/carbon/human)
naked_patient.take_overall_damage(100)
var/mob/living/carbon/human/clothed_patient = allocate(/mob/living/carbon/human)
clothed_patient.equipOutfit(/datum/outfit/job/doctor, TRUE)
clothed_patient.take_overall_damage(100)
basic_brute_heal.success(user, naked_patient, BODY_ZONE_CHEST)
basic_brute_heal.success(user, clothed_patient, BODY_ZONE_CHEST)
TEST_ASSERT(naked_patient.getBruteLoss() < clothed_patient.getBruteLoss(), "Naked patient did not heal more from wounds tending than a clothed patient")
+16 -6
View File
@@ -1,14 +1,9 @@
/*
Usage:
Override /Run() to run your test code
Call Fail() to fail the test (You should specify a reason)
You may use /New() and /Destroy() for setup/teardown respectively
You can use the run_loc_bottom_left and run_loc_top_right to get turfs for testing
*/
GLOBAL_DATUM(current_test, /datum/unit_test)
@@ -18,16 +13,18 @@ GLOBAL_VAR(test_log)
/datum/unit_test
//Bit of metadata for the future maybe
var/list/procs_tested
//usable vars
var/turf/run_loc_bottom_left
var/turf/run_loc_top_right
//internal shit
var/succeeded = TRUE
var/list/allocated
var/list/fail_reasons
/datum/unit_test/New()
allocated = new
run_loc_bottom_left = locate(1, 1, 1)
run_loc_top_right = locate(5, 5, 1)
@@ -35,6 +32,7 @@ GLOBAL_VAR(test_log)
//clear the test area
for(var/atom/movable/AM in block(run_loc_bottom_left, run_loc_top_right))
qdel(AM)
QDEL_LIST(allocated)
return ..()
/datum/unit_test/proc/Run()
@@ -48,6 +46,18 @@ GLOBAL_VAR(test_log)
LAZYADD(fail_reasons, reason)
/// Allocates an instance of the provided type, and places it somewhere in an available loc
/// Instances allocated through this proc will be destroyed when the test is over
/datum/unit_test/proc/allocate(type, ...)
var/list/arguments = args.Copy(2)
if (!arguments.len)
arguments = list(run_loc_bottom_left)
else if (arguments[1] == null)
arguments[1] = run_loc_bottom_left
var/instance = new type(arglist(arguments))
allocated += instance
return instance
/proc/RunUnitTests()
CHECK_TICK
@@ -92,8 +92,8 @@
cost = 6
exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
/datum/uplink_item/device_tools/guerillagloves
name = "Guerilla Gloves"
/datum/uplink_item/device_tools/guerrillagloves
name = "Guerrilla Gloves"
desc = "A pair of highly robust combat gripper gloves that excels at performing takedowns at close range, with an added lining of insulation. Careful not to hit a wall!"
item = /obj/item/clothing/gloves/tackler/combat/insulated
include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)