Merge remote-tracking branch 'upstream/master' into robotic-limbs-PT2
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -351,7 +351,7 @@
|
||||
|
||||
/datum/holiday/halloween
|
||||
name = HALLOWEEN
|
||||
begin_day = 28
|
||||
begin_day = 1
|
||||
begin_month = OCTOBER
|
||||
end_day = 2
|
||||
end_month = NOVEMBER
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
|
||||
+3
-3
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 +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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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()
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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/////////////////////
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(" ")]")
|
||||
|
||||
@@ -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]")
|
||||
@@ -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]")
|
||||
@@ -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")
|
||||
@@ -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
|
||||
@@ -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 ..()
|
||||
@@ -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")
|
||||
@@ -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")
|
||||
@@ -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]")
|
||||
@@ -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)
|
||||
|
||||
@@ -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.")
|
||||
|
||||
@@ -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")
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user