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

This commit is contained in:
silicons
2020-12-13 22:54:16 -07:00
54 changed files with 899 additions and 479 deletions
+1 -1
View File
@@ -72,7 +72,7 @@
/turf/open/indestructible/hotelwood, /turf/open/indestructible/hotelwood,
/area/hilbertshotel) /area/hilbertshotel)
"n" = ( "n" = (
/obj/effect/landmark/xmastree, /obj/effect/landmark/xmastree/hilbert,
/turf/open/indestructible/hotelwood, /turf/open/indestructible/hotelwood,
/area/hilbertshotel) /area/hilbertshotel)
"o" = ( "o" = (
+2
View File
@@ -81,6 +81,8 @@
//stamina stuff //stamina stuff
/// crit for stamina damage. forces a rest, and stops movement until stamina goes back to stamina softcrit /// crit for stamina damage. forces a rest, and stops movement until stamina goes back to stamina softcrit
#define STAMINA_CRIT 140 #define STAMINA_CRIT 140
/// Threshold for leaving stamina critical
#define STAMINA_CRIT_REMOVAL_THRESHOLD 100
/// Threshold under for which you are unable to draw from stamina health to replace stamina buffer /// Threshold under for which you are unable to draw from stamina health to replace stamina buffer
#define STAMINA_NO_OVERDRAW_THRESHOLD 100 #define STAMINA_NO_OVERDRAW_THRESHOLD 100
+1 -1
View File
@@ -10,7 +10,7 @@
/// Usage for eyestabbing with a screwdriver /// Usage for eyestabbing with a screwdriver
#define STAMINA_COST_ITEM_EYESTAB 7.5 #define STAMINA_COST_ITEM_EYESTAB 7.5
/// Usage for shoving yourself off the ground instantly /// Usage for shoving yourself off the ground instantly
#define STAMINA_COST_SHOVE_UP 10 #define STAMINA_COST_SHOVE_UP 15
//items total mass, used to calculate their attacks' stamina costs. If not defined, the cost will be (w_class * 1.25) //items total mass, used to calculate their attacks' stamina costs. If not defined, the cost will be (w_class * 1.25)
#define TOTAL_MASS_TINY_ITEM 1.25 #define TOTAL_MASS_TINY_ITEM 1.25
+11
View File
@@ -490,6 +490,17 @@
#define COMPONENT_PROGRAM_INSTALLED 1 //Installation successful #define COMPONENT_PROGRAM_INSTALLED 1 //Installation successful
#define COMPONENT_PROGRAM_NOT_INSTALLED 2 //Installation failed, but there are still nanites #define COMPONENT_PROGRAM_NOT_INSTALLED 2 //Installation failed, but there are still nanites
#define COMSIG_NANITE_SYNC "nanite_sync" //(datum/component/nanites, full_overwrite, copy_activation) Called to sync the target's nanites to a given nanite component #define COMSIG_NANITE_SYNC "nanite_sync" //(datum/component/nanites, full_overwrite, copy_activation) Called to sync the target's nanites to a given nanite component
/// Checks if a nanite component is able to be controlled by console
#define COMSIG_NANITE_CHECK_CONSOLE_LOCK "is_console_locked"
/// Checks if a nanite component is able to be interfaced with by a host with innate nanite control
#define COMSIG_NANITE_CHECK_HOST_LOCK "is_host_locked"
/// Checks if a nanite component is able to be overwritten by viral replica
#define COMSIG_NANITE_CHECK_VIRAL_PREVENTION "is_virus_locked"
#define NANITE_CHANGES_LOCKED 1
// Internal signals that programs register to and respond with to not require for loops
#define COMSIG_NANITE_INTERNAL_CONSOLE_LOCK_CHECK "naniteiconsolelocked"
#define COMSIG_NANITE_INTERNAL_HOST_LOCK_CHECK "naniteihostlocked"
#define COMSIG_NANITE_INTERNAL_VIRAL_PREVENTION_CHECK "naniteiviruslocked"
// /datum/component/storage signals // /datum/component/storage signals
#define COMSIG_CONTAINS_STORAGE "is_storage" //() - returns bool. #define COMSIG_CONTAINS_STORAGE "is_storage" //() - returns bool.
+3
View File
@@ -48,6 +48,8 @@
#define MOB_EPIC (1 << 7) // Megafauna #define MOB_EPIC (1 << 7) // Megafauna
#define MOB_REPTILE (1 << 8) #define MOB_REPTILE (1 << 8)
#define MOB_SPIRIT (1 << 9) #define MOB_SPIRIT (1 << 9)
/// Mobs that otherwise support nanites
#define MOB_NANITES (1 << 10)
// Organ defines for carbon mobs // Organ defines for carbon mobs
#define ORGAN_ORGANIC 1 #define ORGAN_ORGANIC 1
@@ -56,6 +58,7 @@
#define BODYPART_ORGANIC 1 #define BODYPART_ORGANIC 1
#define BODYPART_ROBOTIC 2 #define BODYPART_ROBOTIC 2
#define BODYPART_HYBRID 3 #define BODYPART_HYBRID 3
#define BODYPART_NANITES 4
#define HYBRID_BODYPART_DAMAGE_THRESHHOLD 25 //How much damage has to be suffered until the damage threshhold counts as passed #define HYBRID_BODYPART_DAMAGE_THRESHHOLD 25 //How much damage has to be suffered until the damage threshhold counts as passed
#define HYBRID_BODYPART_THESHHOLD_MINDAMAGE 15 //Which damage value this limb cannot be healed out of via easy nonsurgical means if the threshhold has been passed, state resets if damage value goes below mindamage. #define HYBRID_BODYPART_THESHHOLD_MINDAMAGE 15 //Which damage value this limb cannot be healed out of via easy nonsurgical means if the threshhold has been passed, state resets if damage value goes below mindamage.
+29
View File
@@ -0,0 +1,29 @@
// helpers
// sources
/// Species
#define ABILITY_SOURCE_SPECIES "species"
/// Changeling
#define ABILITY_SOURCE_CHANGELING "changeling"
// abilities
/// Full customization and transformation of mutantparts/hair/sprite accessories/etc - excludes name by default
#define INNATE_ABILITY_HUMANOID_CUSTOMIZATION "humanoid_customization"
/// Slime blobform
#define INNATE_ABILITY_SLIME_BLOBFORM "slime_blobform"
/// limb regrowth
#define INNATE_ABILITY_LIMB_REGROWTH "limb_regrowth"
/// ability properties
// customization/body change
/// is this silent?
#define PROPERTY_CUSTOMIZATION_SILENT "silent"
// blobform
/// Blobform color
#define PROPERTY_BLOBFORM_COLOR "color"
// limb regrwoth
/// limb regrowth usage type
#define PROPERTY_LIMB_REGROWTH_USAGE_TYPE "cost"
/// blood
#define REGROWTH_USES_BLOOD "blood"
+4
View File
@@ -213,6 +213,10 @@
#define TRAIT_NO_STAMINA_BUFFER_REGENERATION "block_stamina_buffer_regen" /// Prevents stamina buffer regeneration #define TRAIT_NO_STAMINA_BUFFER_REGENERATION "block_stamina_buffer_regen" /// Prevents stamina buffer regeneration
#define TRAIT_NO_STAMINA_REGENERATION "block_stamina_regen" /// Prevents stamina regeneration #define TRAIT_NO_STAMINA_REGENERATION "block_stamina_regen" /// Prevents stamina regeneration
#define TRAIT_ARMOR_BROKEN "armor_broken" //acts as if you are wearing no clothing when taking damage, does not affect non-clothing sources of protection #define TRAIT_ARMOR_BROKEN "armor_broken" //acts as if you are wearing no clothing when taking damage, does not affect non-clothing sources of protection
/// forces update_density to make us not dense
#define TRAIT_LIVING_NO_DENSITY "living_no_density"
/// forces us to not render our overlays
#define TRAIT_HUMAN_NO_RENDER "human_no_render"
// mobility flag traits // mobility flag traits
// IN THE FUTURE, IT WOULD BE NICE TO DO SOMETHING SIMILAR TO https://github.com/tgstation/tgstation/pull/48923/files (ofcourse not nearly the same because I have my.. thoughts on it) // IN THE FUTURE, IT WOULD BE NICE TO DO SOMETHING SIMILAR TO https://github.com/tgstation/tgstation/pull/48923/files (ofcourse not nearly the same because I have my.. thoughts on it)
+115 -14
View File
@@ -17,6 +17,44 @@
var/stealth = FALSE //if TRUE, does not appear on HUDs and health scans var/stealth = FALSE //if TRUE, does not appear on HUDs and health scans
var/diagnostics = TRUE //if TRUE, displays program list when scanned by nanite scanners var/diagnostics = TRUE //if TRUE, displays program list when scanned by nanite scanners
/// Delete ourselves when we're depleted.
var/qdel_self_on_depletion = TRUE
/// Allow deletion
var/can_be_deleted = TRUE
/// Whether or not we can survive no cloud syncing without errors
var/requires_cloud_sync = TRUE
/// Permanent programs - can never be deleted. does not count towards max_programs.
var/list/datum/nanite_program/permanent_programs = list()
// Vulnerabilities
/// EMP flat deletion upper
var/emp_flat_deletion_upper = 35
/// EMP flat deletion lower
var/emp_flat_deletion_lower = 20
/// EMP percent deletion upper
var/emp_percent_deletion_upper = 0.35
/// EMP percent deletion lower
var/emp_percent_deletion_lower = 0.30
/// EMP severity multiplier, capping to 0 to 100
var/emp_severity_mod = 1
/// EMP severity div for cloudsync reset chance
var/emp_desync_mod = 0.25
/// Shock flat deletion upper
var/shock_flat_deletion_upper = 45
/// Shock flat deletion lower
var/shock_flat_deletion_lower = 25
/// Shock percent deletion upper
var/shock_percent_deletion_upper = 0.25
/// Shock percent deletion lower
var/shock_percent_deletion_lower = 0.20
/// minor shock deletion lower
var/minor_shock_deletion_lower = 5
/// minor shock deletion upper
var/minor_shock_deletion_upper = 15
/datum/component/nanites/Initialize(amount = 100, cloud = 0) /datum/component/nanites/Initialize(amount = 100, cloud = 0)
if(!isliving(parent) && !istype(parent, /datum/nanite_cloud_backup)) if(!isliving(parent) && !istype(parent, /datum/nanite_cloud_backup))
return COMPONENT_INCOMPATIBLE return COMPONENT_INCOMPATIBLE
@@ -55,6 +93,9 @@
RegisterSignal(parent, COMSIG_NANITE_ADD_PROGRAM, .proc/add_program) RegisterSignal(parent, COMSIG_NANITE_ADD_PROGRAM, .proc/add_program)
RegisterSignal(parent, COMSIG_NANITE_SCAN, .proc/nanite_scan) RegisterSignal(parent, COMSIG_NANITE_SCAN, .proc/nanite_scan)
RegisterSignal(parent, COMSIG_NANITE_SYNC, .proc/sync) RegisterSignal(parent, COMSIG_NANITE_SYNC, .proc/sync)
RegisterSignal(parent, COMSIG_NANITE_CHECK_CONSOLE_LOCK, .proc/check_console_locking)
RegisterSignal(parent, COMSIG_NANITE_CHECK_HOST_LOCK, .proc/check_host_lockout)
RegisterSignal(parent, COMSIG_NANITE_CHECK_VIRAL_PREVENTION, .proc/check_viral_prevention)
if(isliving(parent)) if(isliving(parent))
RegisterSignal(parent, COMSIG_ATOM_EMP_ACT, .proc/on_emp) RegisterSignal(parent, COMSIG_ATOM_EMP_ACT, .proc/on_emp)
@@ -118,13 +159,63 @@
next_sync = world.time + NANITE_SYNC_DELAY next_sync = world.time + NANITE_SYNC_DELAY
set_nanite_bar() set_nanite_bar()
/**
* Called when nanites are depleted.
* Deletes ourselves by default.
*/
/datum/component/nanites/proc/nanites_depleted()
if(qdel_self_on_depletion)
delete_nanites()
/**
* Used to rid ourselves
*/
/datum/component/nanites/proc/delete_nanites() /datum/component/nanites/proc/delete_nanites()
qdel(src) if(can_be_deleted)
qdel(src)
/**
* Adds permanent programs
*
* WARNING: Has no sanity checks. Make sure you know what you are doing! (make sure programs do not conflict)
*/
/datum/component/nanites/proc/add_permanent_program(list/program, immutable = FALSE)
if(!islist(program))
program = list(program)
for(var/i in program)
if(i in permanent_programs)
continue
var/datum/nanite_program/P = i
permanent_programs += P
if(immutable)
P.immutable = TRUE
for(var/e in programs)
var/datum/nanite_program/E = e
if(E.unique && (E.type == P.type))
qdel(e)
programs += P
/**
* Checks if we can block out console modification
*/
/datum/component/nanites/proc/check_console_locking()
return SEND_SIGNAL(src, COMSIG_NANITE_INTERNAL_CONSOLE_LOCK_CHECK)
/**
* Checks if we can lock out host internal conscious modification
*/
/datum/component/nanites/proc/check_host_lockout()
return SEND_SIGNAL(src, COMSIG_NANITE_INTERNAL_HOST_LOCK_CHECK)
/**
* Checks if we can block out viral replica
*/
/datum/component/nanites/proc/check_viral_prevention()
return SEND_SIGNAL(src, COMSIG_NANITE_INTERNAL_VIRAL_PREVENTION_CHECK)
//Syncs the nanite component to another, making it so programs are the same with the same programming (except activation status) //Syncs the nanite component to another, making it so programs are the same with the same programming (except activation status)
/datum/component/nanites/proc/sync(datum/signal_source, datum/component/nanites/source, full_overwrite = TRUE, copy_activation = FALSE) /datum/component/nanites/proc/sync(datum/signal_source, datum/component/nanites/source, full_overwrite = TRUE, copy_activation = FALSE)
var/list/programs_to_remove = programs.Copy() var/list/programs_to_remove = programs.Copy() - permanent_programs
var/list/programs_to_add = source.programs.Copy() var/list/programs_to_add = source.programs.Copy()
for(var/X in programs) for(var/X in programs)
var/datum/nanite_program/NP = X var/datum/nanite_program/NP = X
@@ -151,7 +242,7 @@
sync(null, cloud_copy) sync(null, cloud_copy)
return return
//Without cloud syncing nanites can accumulate errors and/or defects //Without cloud syncing nanites can accumulate errors and/or defects
if(prob(8) && programs.len) if(prob(8) && programs.len && requires_cloud_sync)
var/datum/nanite_program/NP = pick(programs) var/datum/nanite_program/NP = pick(programs)
NP.software_error() NP.software_error()
@@ -159,8 +250,11 @@
for(var/X in programs) for(var/X in programs)
var/datum/nanite_program/NP = X var/datum/nanite_program/NP = X
if(NP.unique && NP.type == new_program.type) if(NP.unique && NP.type == new_program.type)
qdel(NP) if(NP in permanent_programs)
if(programs.len >= max_programs) return COMPONENT_PROGRAM_NOT_INSTALLED
else
qdel(NP)
if((programs.len - length(permanent_programs)) >= max_programs)
return COMPONENT_PROGRAM_NOT_INSTALLED return COMPONENT_PROGRAM_NOT_INSTALLED
if(source_program) if(source_program)
source_program.copy_programming(new_program) source_program.copy_programming(new_program)
@@ -177,7 +271,7 @@
/datum/component/nanites/proc/adjust_nanites(datum/source, amount) /datum/component/nanites/proc/adjust_nanites(datum/source, amount)
nanite_volume = clamp(nanite_volume + amount, 0, max_nanites) nanite_volume = clamp(nanite_volume + amount, 0, max_nanites)
if(nanite_volume <= 0) //oops we ran out if(nanite_volume <= 0) //oops we ran out
qdel(src) nanites_depleted()
/datum/component/nanites/proc/set_nanite_bar(remove = FALSE) /datum/component/nanites/proc/set_nanite_bar(remove = FALSE)
var/image/holder = host_mob.hud_list[DIAG_NANITE_FULL_HUD] var/image/holder = host_mob.hud_list[DIAG_NANITE_FULL_HUD]
@@ -191,28 +285,28 @@
holder.icon_state = "nanites[nanite_percent]" holder.icon_state = "nanites[nanite_percent]"
/datum/component/nanites/proc/on_emp(datum/source, severity) /datum/component/nanites/proc/on_emp(datum/source, severity)
nanite_volume *= (rand(60, 90) * 0.01) //Lose 10-40% of nanites severity *= emp_severity_mod
adjust_nanites(null, -(rand(5, 50))) //Lose 5-50 flat nanite volume var/loss = (severity / 100) * (rand(emp_percent_deletion_lower, emp_percent_deletion_upper) * nanite_volume) + rand(emp_flat_deletion_lower, emp_flat_deletion_upper)
if(prob(40/severity)) adjust_nanites(null, -loss)
if(prob(severity * emp_desync_mod))
cloud_id = 0 cloud_id = 0
for(var/X in programs) for(var/X in programs)
var/datum/nanite_program/NP = X var/datum/nanite_program/NP = X
NP.on_emp(severity) NP.on_emp(severity)
/datum/component/nanites/proc/on_shock(datum/source, shock_damage, siemens_coeff = 1, flags = NONE) /datum/component/nanites/proc/on_shock(datum/source, shock_damage, siemens_coeff = 1, flags = NONE)
if(shock_damage < 1) if(shock_damage < 1)
return return
if(!HAS_TRAIT_NOT_FROM(host_mob, TRAIT_SHOCKIMMUNE, "nanites"))//Another shock protection must protect nanites too, but nanites protect only host if(!HAS_TRAIT_NOT_FROM(host_mob, TRAIT_SHOCKIMMUNE, "nanites"))//Another shock protection must protect nanites too, but nanites protect only host
nanite_volume *= (rand(45, 80) * 0.01) //Lose 20-55% of nanites var/loss = (rand(shock_percent_deletion_lower, shock_percent_deletion_upper) * nanite_volume) + rand(shock_flat_deletion_lower, shock_flat_deletion_upper)
adjust_nanites(null, -(rand(5, 50))) //Lose 5-50 flat nanite volume adjust_nanites(null, -loss)
for(var/X in programs) for(var/X in programs)
var/datum/nanite_program/NP = X var/datum/nanite_program/NP = X
NP.on_shock(shock_damage) NP.on_shock(shock_damage)
/datum/component/nanites/proc/on_minor_shock(datum/source) /datum/component/nanites/proc/on_minor_shock(datum/source)
adjust_nanites(null, -(rand(5, 15))) //Lose 5-15 flat nanite volume adjust_nanites(null, -(rand(minor_shock_deletion_lower, minor_shock_deletion_upper))) //Lose 5-15 flat nanite volume
for(var/X in programs) for(var/X in programs)
var/datum/nanite_program/NP = X var/datum/nanite_program/NP = X
NP.on_minor_shock() NP.on_minor_shock()
@@ -237,7 +331,7 @@
NP.receive_comm_signal(comm_code, comm_message, comm_source) NP.receive_comm_signal(comm_code, comm_message, comm_source)
/datum/component/nanites/proc/check_viable_biotype() /datum/component/nanites/proc/check_viable_biotype()
if(!(host_mob.mob_biotypes & (MOB_ORGANIC|MOB_UNDEAD))) if(!(host_mob.mob_biotypes & (MOB_ORGANIC|MOB_UNDEAD|MOB_NANITES)))
qdel(src) //bodytype no longer sustains nanites qdel(src) //bodytype no longer sustains nanites
/datum/component/nanites/proc/check_access(datum/source, obj/O) /datum/component/nanites/proc/check_access(datum/source, obj/O)
@@ -378,3 +472,10 @@
id++ id++
mob_programs += list(mob_program) mob_programs += list(mob_program)
data["mob_programs"] = mob_programs data["mob_programs"] = mob_programs
/**
* Subtype that doesn't erase itself from running out
*/
/datum/component/nanites/permanent
qdel_self_on_depletion = FALSE
can_be_deleted = FALSE
+12 -3
View File
@@ -660,27 +660,34 @@
return return
if(dna.stability > 0) if(dna.stability > 0)
return return
var/instability = -dna.stability var/instability = - dna.stability
dna.remove_all_mutations() dna.remove_all_mutations()
dna.stability = 100 dna.stability = 100
if(prob(max(70-instability,0))) if(prob(max(70 - instability,0)))
switch(rand(0,3)) //not complete and utter death switch(rand(0,3)) //not complete and utter death
if(0) if(0)
monkeyize() monkeyize()
if(1) if(1)
gain_trauma(/datum/brain_trauma/severe/paralysis) gain_trauma(/datum/brain_trauma/severe/paralysis)
if(2) if(2)
unequip_everything()
drop_all_held_items()
corgize() corgize()
if(3) if(3)
to_chat(src, "<span class='notice'>Oh, we actually feel quite alright!</span>") to_chat(src, "<span class='notice'>Oh, we actually feel quite alright!</span>")
else else
switch(rand(0,3)) switch(rand(0,3))
if(0) if(0)
unequip_everything()
drop_all_held_items()
gib() gib()
if(1) if(1)
unequip_everything()
drop_all_held_items()
dust() dust()
if(2) if(2)
unequip_everything()
drop_all_held_items()
death() death()
petrify(INFINITY) petrify(INFINITY)
if(3) if(3)
@@ -689,6 +696,8 @@
if(BP) if(BP)
BP.dismember() BP.dismember()
else else
unequip_everything()
drop_all_held_items()
gib() gib()
else else
set_species(/datum/species/dullahan) set_species(/datum/species/dullahan)
+37 -6
View File
@@ -17,9 +17,6 @@
/obj/item/storage/fancy /obj/item/storage/fancy
icon = 'icons/obj/food/containers.dmi' icon = 'icons/obj/food/containers.dmi'
icon_state = "donutbox6"
name = "donut box"
desc = "Mmm. Donuts."
resistance_flags = FLAMMABLE resistance_flags = FLAMMABLE
var/icon_type = "donut" var/icon_type = "donut"
var/spawn_type = null var/spawn_type = null
@@ -59,18 +56,22 @@
fancy_open = TRUE fancy_open = TRUE
update_icon() update_icon()
#define DONUT_INBOX_SPRITE_WIDTH 3
/* /*
* Donut Box * Donut Box
*/ */
/obj/item/storage/fancy/donut_box /obj/item/storage/fancy/donut_box
icon = 'icons/obj/food/containers.dmi'
icon_state = "donutbox6"
icon_type = "donut"
name = "donut box" name = "donut box"
desc = "Mmm. Donuts."
icon = 'icons/obj/food/donut.dmi'
icon_state = "donutbox_inner"
icon_type = "donut"
spawn_type = /obj/item/reagent_containers/food/snacks/donut spawn_type = /obj/item/reagent_containers/food/snacks/donut
fancy_open = TRUE fancy_open = TRUE
custom_price = PRICE_NORMAL custom_price = PRICE_NORMAL
appearance_flags = KEEP_TOGETHER
/obj/item/storage/fancy/donut_box/ComponentInitialize() /obj/item/storage/fancy/donut_box/ComponentInitialize()
. = ..() . = ..()
@@ -78,6 +79,36 @@
STR.max_items = 6 STR.max_items = 6
STR.can_hold = typecacheof(list(/obj/item/reagent_containers/food/snacks/donut)) STR.can_hold = typecacheof(list(/obj/item/reagent_containers/food/snacks/donut))
/obj/item/storage/fancy/donut_box/PopulateContents()
. = ..()
update_icon()
/obj/item/storage/fancy/donut_box/update_icon_state()
if(fancy_open)
icon_state = "donutbox_inner"
else
icon_state = "donutbox"
/obj/item/storage/fancy/donut_box/update_overlays()
. = ..()
if (!fancy_open)
return
var/donuts = 0
for (var/_donut in contents)
var/obj/item/reagent_containers/food/snacks/donut/donut = _donut
if (!istype(donut))
continue
. += image(icon = initial(icon), icon_state = donut.in_box_sprite(), pixel_x = donuts * DONUT_INBOX_SPRITE_WIDTH)
donuts += 1
. += image(icon = initial(icon), icon_state = "donutbox_top")
#undef DONUT_INBOX_SPRITE_WIDTH
/* /*
* Egg Box * Egg Box
*/ */
+5
View File
@@ -271,6 +271,11 @@
icon = "the_lightbulb" icon = "the_lightbulb"
desc = "A cafe popular among moths and moffs. Once shut down for a week after the bartender used mothballs to protect her spare uniforms." desc = "A cafe popular among moths and moffs. Once shut down for a week after the bartender used mothballs to protect her spare uniforms."
/datum/barsign/goose
name = "The Loose Goose"
icon = "goose"
desc = "Drink till you puke and/or break the laws of reality!"
/datum/barsign/cybersylph /datum/barsign/cybersylph
name = "Cyber Sylph's" name = "Cyber Sylph's"
icon = "cybersylph" icon = "cybersylph"
+7 -9
View File
@@ -96,18 +96,16 @@ GLOBAL_LIST_INIT(freqtospan, list(
return "[say_mod(input, message_mode)][spanned ? ", \"[spanned]\"" : ""]" return "[say_mod(input, message_mode)][spanned ? ", \"[spanned]\"" : ""]"
// Citadel edit [spanned ? ", \"[spanned]\"" : ""]" // Citadel edit [spanned ? ", \"[spanned]\"" : ""]"
#define ENCODE_HTML_EPHASIS(input, char, html, varname) \ /// Converts specific characters, like +, |, and _ to formatted output.
var/static/regex/##varname = regex("[char]{2}(.+?)[char]{2}", "g");\
input = varname.Replace_char(input, "<[html]>$1</[html]>")
/atom/movable/proc/say_emphasis(input) /atom/movable/proc/say_emphasis(input)
ENCODE_HTML_EPHASIS(input, "\\|", "i", italics) var/static/regex/italics = regex(@"\|(\S[\w\W]*?\S)\|", "g")
ENCODE_HTML_EPHASIS(input, "\\+", "b", bold) input = italics.Replace_char(input, "<i>$1</i>")
ENCODE_HTML_EPHASIS(input, "_", "u", underline) var/static/regex/bold = regex(@"\+(\S[\w\W]*?\S)\+", "g")
input = bold.Replace_char(input, "<b>$1</b>")
var/static/regex/underline = regex(@"_(\S[\w\W]*?\S)_", "g")
input = underline.Replace_char(input, "<u>$1</u>")
return input return input
#undef ENCODE_HTML_EPHASIS
/// Quirky citadel proc for our custom sayverbs to strip the verb out. Snowflakey as hell, say rewrite 3.0 when? /// Quirky citadel proc for our custom sayverbs to strip the verb out. Snowflakey as hell, say rewrite 3.0 when?
/atom/movable/proc/quoteless_say_quote(input, list/spans = list(speech_span), message_mode) /atom/movable/proc/quoteless_say_quote(input, list/spans = list(speech_span), message_mode)
if((input[1] == "!") && (length_char(input) > 1)) if((input[1] == "!") && (length_char(input) > 1))
+3
View File
@@ -60,6 +60,9 @@ Credit dupes that require a lot of manual work shouldn't be removed, unless they
if(!dry_run && (sold || delete_unsold)) if(!dry_run && (sold || delete_unsold))
if(ismob(thing)) if(ismob(thing))
thing.investigate_log("deleted through cargo export",INVESTIGATE_CARGO) thing.investigate_log("deleted through cargo export",INVESTIGATE_CARGO)
if(ismecha(thing))
var/obj/mecha/mech = thing
mech.wreckage = null // why a mech left a wreck when sold i will never know
qdel(thing) qdel(thing)
return report return report
+3
View File
@@ -53,6 +53,9 @@
new festive_tree(get_turf(src)) new festive_tree(get_turf(src))
return INITIALIZE_HINT_QDEL return INITIALIZE_HINT_QDEL
/obj/effect/landmark/xmastree/hilbert
christmas_tree = /obj/structure/flora/tree/pine/xmas
/obj/effect/landmark/xmastree/rdrod /obj/effect/landmark/xmastree/rdrod
name = "festivus pole spawner" name = "festivus pole spawner"
festive_tree = /obj/structure/festivus festive_tree = /obj/structure/festivus
@@ -34,6 +34,10 @@
filling_color = "#FF69B4" filling_color = "#FF69B4"
return TRUE return TRUE
/// Returns the sprite of the donut while in a donut box
/obj/item/reagent_containers/food/snacks/donut/proc/in_box_sprite()
return "[icon_state]_inbox"
/obj/item/reagent_containers/food/snacks/donut/checkLiked(fraction, mob/M) //Sec officers always love donuts /obj/item/reagent_containers/food/snacks/donut/checkLiked(fraction, mob/M) //Sec officers always love donuts
if(last_check_time + 50 < world.time) if(last_check_time + 50 < world.time)
if(ishuman(M)) if(ishuman(M))
@@ -165,6 +169,10 @@
tastes = list("jelly" = 1, "donut" = 3) tastes = list("jelly" = 1, "donut" = 3)
foodtype = JUNKFOOD | GRAIN | FRIED | FRUIT | SUGAR | BREAKFAST foodtype = JUNKFOOD | GRAIN | FRIED | FRUIT | SUGAR | BREAKFAST
// Jelly donuts don't have holes, but look the same on the outside
/obj/item/reagent_containers/food/snacks/donut/jelly/in_box_sprite()
return "[replacetext(icon_state, "jelly", "donut")]_inbox"
/obj/item/reagent_containers/food/snacks/donut/jelly/Initialize() /obj/item/reagent_containers/food/snacks/donut/jelly/Initialize()
. = ..() . = ..()
if(extra_reagent) if(extra_reagent)
+57
View File
@@ -0,0 +1,57 @@
/**
* Sets an ability property
*/
/mob/proc/set_ability_property(ability, property, value)
LAZYINITLIST(ability_properties)
LAZYINITLIST(ability_properties[ability])
ability_properties[ability][property] = value
/**
* Gets an ability property
*/
/mob/proc/get_ability_property(ability, property)
return ability_properties && ability_properties[ability] && ability_properties[ability][property]
GLOBAL_LIST_INIT(innate_ability_typepaths, all_innate_ability_typepaths())
/proc/all_innate_ability_typepaths()
return list(
INNATE_ABILITY_HUMANOID_CUSTOMIZATION = /datum/action/innate/ability/humanoid_customization,
INNATE_ABILITY_SLIME_BLOBFORM = /datum/action/innate/ability/slime_blobform,
INNATE_ABILITY_LIMB_REGROWTH = /datum/action/innate/ability/limb_regrowth
)
/**
* Grants an ability from a source
*/
/mob/proc/grant_ability_from_source(list/abilities, source)
if(!islist(abilities))
abilities = list(abilities)
LAZYINITLIST(ability_actions)
LAZYINITLIST(innate_abilities)
for(var/ability in abilities)
LAZYINITLIST(innate_abilities[ability])
innate_abilities[ability] |= source
if(ability_actions[ability])
continue
var/path = GLOB.innate_ability_typepaths[ability]
var/datum/action/innate/ability/A = new path
ability_actions[ability] = A
A.Grant(src)
/**
* Removes an ability from a source
*/
/mob/proc/remove_ability_from_source(list/abilities, source)
if(!islist(abilities))
abilities = list(abilities)
if(!length(innate_abilities))
return
for(var/ability in abilities)
if(!length(innate_abilities[ability]))
continue
innate_abilities[ability] -= source
if(!length(innate_abilities[ability]))
innate_abilities -= ability
qdel(ability_actions[ability])
ability_actions -= ability
+1 -1
View File
@@ -604,7 +604,7 @@
ENABLE_BITFIELD(combat_flags, COMBAT_FLAG_HARD_STAMCRIT) ENABLE_BITFIELD(combat_flags, COMBAT_FLAG_HARD_STAMCRIT)
filters += CIT_FILTER_STAMINACRIT filters += CIT_FILTER_STAMINACRIT
update_mobility() update_mobility()
if((combat_flags & COMBAT_FLAG_HARD_STAMCRIT) && total_health <= STAMINA_CRIT) if((combat_flags & COMBAT_FLAG_HARD_STAMCRIT) && total_health <= STAMINA_CRIT_REMOVAL_THRESHOLD)
to_chat(src, "<span class='notice'>You don't feel nearly as exhausted anymore.</span>") to_chat(src, "<span class='notice'>You don't feel nearly as exhausted anymore.</span>")
DISABLE_BITFIELD(combat_flags, COMBAT_FLAG_HARD_STAMCRIT) DISABLE_BITFIELD(combat_flags, COMBAT_FLAG_HARD_STAMCRIT)
filters -= CIT_FILTER_STAMINACRIT filters -= CIT_FILTER_STAMINACRIT
@@ -100,8 +100,3 @@
if(dna.species.space_move(src)) if(dna.species.space_move(src))
return TRUE return TRUE
return ..() return ..()
/mob/living/carbon/human/CanPass(atom/movable/mover, turf/target)
if(dna.species.species_pass_check())
return TRUE
return ..()
@@ -0,0 +1,112 @@
/datum/action/innate/ability/slime_blobform
name = "Puddle Transformation"
check_flags = AB_CHECK_CONSCIOUS
button_icon_state = "slimepuddle"
icon_icon = 'icons/mob/actions/actions_slime.dmi'
background_icon_state = "bg_alien"
required_mobility_flags = MOBILITY_STAND
var/is_puddle = FALSE
var/in_transformation_duration = 12
var/out_transformation_duration = 7
var/puddle_into_effect = /obj/effect/temp_visual/slime_puddle
var/puddle_from_effect = /obj/effect/temp_visual/slime_puddle/reverse
var/puddle_icon = 'icons/mob/mob.dmi'
var/puddle_state = "puddle"
var/mutable_appearance/tracked_overlay
var/datum/component/squeak/squeak
var/transforming = FALSE
var/last_use
/datum/action/innate/ability/slime_blobform/IsAvailable()
if(!transforming)
return ..()
else
return FALSE
/datum/action/innate/ability/slime_blobform/Remove(mob/M)
if(is_puddle)
detransform()
return ..()
/datum/action/innate/ability/slime_blobform/Activate()
var/mob/living/carbon/human/H = owner
//if they have anything stuck to their hands, we immediately say 'no' and return
for(var/obj/item/I in H.held_items)
if(HAS_TRAIT(I, TRAIT_NODROP))
to_chat(owner, "There's something stuck to your hand, stopping you from transforming!")
return
if(IsAvailable())
transforming = TRUE
UpdateButtonIcon()
var/mutcolor = owner.get_ability_property(INNATE_ABILITY_SLIME_BLOBFORM, PROPERTY_BLOBFORM_COLOR) || ("#" + H.dna.features["mcolor"])
if(!is_puddle)
if(CHECK_MOBILITY(H, MOBILITY_USE)) //if we can use items, we can turn into a puddle
is_puddle = TRUE //so we know which transformation to use when its used
ADD_TRAIT(H, TRAIT_HUMAN_NO_RENDER, SLIMEPUDDLE_TRAIT)
owner.cut_overlays() //we dont show our normal sprite, we show a puddle sprite
var/obj/effect/puddle_effect = new puddle_into_effect(get_turf(owner), owner.dir)
puddle_effect.color = mutcolor
H.Stun(in_transformation_duration, ignore_canstun = TRUE) //cant move while transforming
//series of traits that make up the puddle behaviour
ADD_TRAIT(H, TRAIT_PARALYSIS_L_ARM, SLIMEPUDDLE_TRAIT)
ADD_TRAIT(H, TRAIT_PARALYSIS_R_ARM, SLIMEPUDDLE_TRAIT)
ADD_TRAIT(H, TRAIT_MOBILITY_NOPICKUP, SLIMEPUDDLE_TRAIT)
ADD_TRAIT(H, TRAIT_MOBILITY_NOUSE, SLIMEPUDDLE_TRAIT)
ADD_TRAIT(H, TRAIT_SPRINT_LOCKED, SLIMEPUDDLE_TRAIT)
ADD_TRAIT(H, TRAIT_COMBAT_MODE_LOCKED, SLIMEPUDDLE_TRAIT)
ADD_TRAIT(H, TRAIT_MOBILITY_NOREST, SLIMEPUDDLE_TRAIT)
ADD_TRAIT(H, TRAIT_ARMOR_BROKEN, SLIMEPUDDLE_TRAIT)
H.update_disabled_bodyparts(silent = TRUE) //silently update arms to be paralysed
H.add_movespeed_modifier(/datum/movespeed_modifier/slime_puddle)
H.layer -= 1 //go one layer down so people go over you
ENABLE_BITFIELD(H.pass_flags, PASSMOB) //this actually lets people pass over you
squeak = H.AddComponent(/datum/component/squeak, custom_sounds = list('sound/effects/blobattack.ogg')) //blorble noise when people step on you
//if the user is a changeling, retract their sting
H.unset_sting()
sleep(in_transformation_duration) //wait for animation to end
//set the puddle overlay up
var/mutable_appearance/puddle_overlay = mutable_appearance(icon = puddle_icon, icon_state = puddle_state)
puddle_overlay.color = mutcolor
tracked_overlay = puddle_overlay
owner.add_overlay(puddle_overlay)
transforming = FALSE
UpdateButtonIcon()
else
detransform()
else
to_chat(owner, "<span class='warning'>You need to be standing up to do this!") //just assume they're a slime because it's such a weird edgecase to have it and not be one (it shouldn't even be possible)
/datum/action/innate/ability/slime_blobform/proc/detransform()
var/mob/living/carbon/human/H = owner
//like the above, but reverse everything done!
H.cut_overlay(tracked_overlay)
var/obj/effect/puddle_effect = new puddle_from_effect(get_turf(owner), owner.dir)
puddle_effect.color = tracked_overlay.color
H.Stun(out_transformation_duration, ignore_canstun = TRUE)
sleep(out_transformation_duration)
REMOVE_TRAIT(H, TRAIT_PARALYSIS_L_ARM, SLIMEPUDDLE_TRAIT)
REMOVE_TRAIT(H, TRAIT_PARALYSIS_R_ARM, SLIMEPUDDLE_TRAIT)
REMOVE_TRAIT(H, TRAIT_MOBILITY_NOPICKUP, SLIMEPUDDLE_TRAIT)
REMOVE_TRAIT(H, TRAIT_MOBILITY_NOUSE, SLIMEPUDDLE_TRAIT)
REMOVE_TRAIT(H, TRAIT_SPRINT_LOCKED, SLIMEPUDDLE_TRAIT)
REMOVE_TRAIT(H, TRAIT_COMBAT_MODE_LOCKED, SLIMEPUDDLE_TRAIT)
REMOVE_TRAIT(H, TRAIT_MOBILITY_NOREST, SLIMEPUDDLE_TRAIT)
REMOVE_TRAIT(H, TRAIT_ARMOR_BROKEN, SLIMEPUDDLE_TRAIT)
REMOVE_TRAIT(H, TRAIT_HUMAN_NO_RENDER, SLIMEPUDDLE_TRAIT)
H.update_disabled_bodyparts(silent = TRUE)
H.remove_movespeed_modifier(/datum/movespeed_modifier/slime_puddle)
H.layer += 1 //go one layer back above!
DISABLE_BITFIELD(H.pass_flags, PASSMOB)
is_puddle = FALSE
if(squeak)
squeak.RemoveComponent()
transforming = FALSE
UpdateButtonIcon()
@@ -0,0 +1,208 @@
/datum/action/innate/ability/humanoid_customization
name = "Alter Form"
check_flags = AB_CHECK_CONSCIOUS
button_icon_state = "alter_form" //placeholder
icon_icon = 'modular_citadel/icons/mob/actions/actions_slime.dmi'
background_icon_state = "bg_alien"
/datum/action/innate/ability/humanoid_customization/Activate()
if(owner.get_ability_property(INNATE_ABILITY_HUMANOID_CUSTOMIZATION, PROPERTY_CUSTOMIZATION_SILENT))
owner.visible_message("<span class='notice'>[owner] gains a look of \
concentration while standing perfectly still.\
Their body seems to shift and starts getting more goo-like.</span>",
"<span class='notice'>You focus intently on altering your body while \
standing perfectly still...</span>")
change_form()
///////
/////// NOTICE: This currently doens't support skin tone - if anyone wants to add this to non slimes, it's up to YOU to do this.
////// (someone should also add genital color switching, more mutant color selection)
///// maybe just make this entire thing tgui based. maybe.
///////
/datum/action/innate/ability/humanoid_customization/proc/change_form()
var/mob/living/carbon/human/H = owner
var/select_alteration = input(owner, "Select what part of your form to alter", "Form Alteration", "cancel") in list("Body Color","Hair Style", "Genitals", "Tail", "Snout", "Markings", "Ears", "Taur body", "Penis", "Vagina", "Penis Length", "Breast Size", "Breast Shape", "Cancel")
if(select_alteration == "Body Color")
var/new_color = input(owner, "Choose your skin color:", "Race change","#"+H.dna.features["mcolor"]) as color|null
if(new_color)
var/temp_hsv = RGBtoHSV(new_color)
if(ReadHSV(temp_hsv)[3] >= ReadHSV(MINIMUM_MUTANT_COLOR)[3]) // mutantcolors must be bright
H.dna.features["mcolor"] = sanitize_hexcolor(new_color, 6)
H.update_body()
H.update_hair()
else
to_chat(H, "<span class='notice'>Invalid color. Your color is not bright enough.</span>")
else if(select_alteration == "Hair Style")
if(H.gender == MALE)
var/new_style = input(owner, "Select a facial hair style", "Hair Alterations") as null|anything in GLOB.facial_hair_styles_list
if(new_style)
H.facial_hair_style = new_style
else
H.facial_hair_style = "Shaved"
//handle normal hair
var/new_style = input(owner, "Select a hair style", "Hair Alterations") as null|anything in GLOB.hair_styles_list
if(new_style)
H.hair_style = new_style
H.update_hair()
else if (select_alteration == "Genitals")
var/operation = input("Select organ operation.", "Organ Manipulation", "cancel") in list("add sexual organ", "remove sexual organ", "cancel")
switch(operation)
if("add sexual organ")
var/new_organ = input("Select sexual organ:", "Organ Manipulation") as null|anything in GLOB.genitals_list
if(!new_organ)
return
H.give_genital(GLOB.genitals_list[new_organ])
if("remove sexual organ")
var/list/organs = list()
for(var/obj/item/organ/genital/X in H.internal_organs)
var/obj/item/organ/I = X
organs["[I.name] ([I.type])"] = I
var/obj/item/O = input("Select sexual organ:", "Organ Manipulation", null) as null|anything in organs
var/obj/item/organ/genital/G = organs[O]
if(!G)
return
G.forceMove(get_turf(H))
qdel(G)
H.update_genitals()
else if (select_alteration == "Ears")
var/list/snowflake_ears_list = list("Normal" = null)
for(var/path in GLOB.mam_ears_list)
var/datum/sprite_accessory/ears/mam_ears/instance = GLOB.mam_ears_list[path]
if(istype(instance, /datum/sprite_accessory))
var/datum/sprite_accessory/S = instance
if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(H.client.ckey)))
snowflake_ears_list[S.name] = path
var/new_ears
new_ears = input(owner, "Choose your character's ears:", "Ear Alteration") as null|anything in snowflake_ears_list
if(new_ears)
H.dna.features["mam_ears"] = new_ears
H.update_body()
else if (select_alteration == "Snout")
var/list/snowflake_snouts_list = list("Normal" = null)
for(var/path in GLOB.mam_snouts_list)
var/datum/sprite_accessory/snouts/mam_snouts/instance = GLOB.mam_snouts_list[path]
if(istype(instance, /datum/sprite_accessory))
var/datum/sprite_accessory/S = instance
if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(H.client.ckey)))
snowflake_snouts_list[S.name] = path
var/new_snout
new_snout = input(owner, "Choose your character's face:", "Face Alteration") as null|anything in snowflake_snouts_list
if(new_snout)
H.dna.features["mam_snouts"] = new_snout
H.update_body()
else if (select_alteration == "Markings")
var/list/snowflake_markings_list = list("None")
for(var/path in GLOB.mam_body_markings_list)
var/datum/sprite_accessory/mam_body_markings/instance = GLOB.mam_body_markings_list[path]
if(istype(instance, /datum/sprite_accessory))
var/datum/sprite_accessory/S = instance
if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(H.client.ckey)))
snowflake_markings_list[S.name] = path
var/new_mam_body_markings
new_mam_body_markings = input(H, "Choose your character's body markings:", "Marking Alteration") as null|anything in snowflake_markings_list
if(new_mam_body_markings)
H.dna.features["mam_body_markings"] = new_mam_body_markings
for(var/X in H.bodyparts) //propagates the markings changes
var/obj/item/bodypart/BP = X
BP.update_limb(FALSE, H)
H.update_body()
else if (select_alteration == "Tail")
var/list/snowflake_tails_list = list("Normal" = null)
for(var/path in GLOB.mam_tails_list)
var/datum/sprite_accessory/tails/mam_tails/instance = GLOB.mam_tails_list[path]
if(istype(instance, /datum/sprite_accessory))
var/datum/sprite_accessory/S = instance
if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(H.client.ckey)))
snowflake_tails_list[S.name] = path
var/new_tail
new_tail = input(owner, "Choose your character's Tail(s):", "Tail Alteration") as null|anything in snowflake_tails_list
if(new_tail)
H.dna.features["mam_tail"] = new_tail
if(new_tail != "None")
H.dna.features["taur"] = "None"
H.update_body()
else if (select_alteration == "Taur body")
var/list/snowflake_taur_list = list("Normal" = null)
for(var/path in GLOB.taur_list)
var/datum/sprite_accessory/taur/instance = GLOB.taur_list[path]
if(istype(instance, /datum/sprite_accessory))
var/datum/sprite_accessory/S = instance
if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(H.client.ckey)))
snowflake_taur_list[S.name] = path
var/new_taur
new_taur = input(owner, "Choose your character's tauric body:", "Tauric Alteration") as null|anything in snowflake_taur_list
if(new_taur)
H.dna.features["taur"] = new_taur
if(new_taur != "None")
H.dna.features["mam_tail"] = "None"
H.update_body()
else if (select_alteration == "Penis")
for(var/obj/item/organ/genital/penis/X in H.internal_organs)
qdel(X)
var/new_shape
new_shape = input(owner, "Choose your character's dong", "Genital Alteration") as null|anything in GLOB.cock_shapes_list
if(new_shape)
H.dna.features["cock_shape"] = new_shape
H.update_genitals()
H.give_genital(/obj/item/organ/genital/testicles)
H.give_genital(/obj/item/organ/genital/penis)
H.apply_overlay()
else if (select_alteration == "Vagina")
for(var/obj/item/organ/genital/vagina/X in H.internal_organs)
qdel(X)
var/new_shape
new_shape = input(owner, "Choose your character's pussy", "Genital Alteration") as null|anything in GLOB.vagina_shapes_list
if(new_shape)
H.dna.features["vag_shape"] = new_shape
H.update_genitals()
H.give_genital(/obj/item/organ/genital/womb)
H.give_genital(/obj/item/organ/genital/vagina)
H.apply_overlay()
else if (select_alteration == "Penis Length")
for(var/obj/item/organ/genital/penis/X in H.internal_organs)
qdel(X)
var/min_D = CONFIG_GET(number/penis_min_inches_prefs)
var/max_D = CONFIG_GET(number/penis_max_inches_prefs)
var/new_length = input(owner, "Penis length in inches:\n([min_D]-[max_D])", "Genital Alteration") as num|null
if(new_length)
H.dna.features["cock_length"] = clamp(round(new_length), min_D, max_D)
H.update_genitals()
H.apply_overlay()
H.give_genital(/obj/item/organ/genital/testicles)
H.give_genital(/obj/item/organ/genital/penis)
else if (select_alteration == "Breast Size")
for(var/obj/item/organ/genital/breasts/X in H.internal_organs)
qdel(X)
var/new_size = input(owner, "Breast Size", "Genital Alteration") as null|anything in CONFIG_GET(keyed_list/breasts_cups_prefs)
if(new_size)
H.dna.features["breasts_size"] = new_size
H.update_genitals()
H.apply_overlay()
H.give_genital(/obj/item/organ/genital/breasts)
else if (select_alteration == "Breast Shape")
for(var/obj/item/organ/genital/breasts/X in H.internal_organs)
qdel(X)
var/new_shape
new_shape = input(owner, "Breast Shape", "Genital Alteration") as null|anything in GLOB.breasts_shapes_list
if(new_shape)
H.dna.features["breasts_shape"] = new_shape
H.update_genitals()
H.apply_overlay()
H.give_genital(/obj/item/organ/genital/breasts)
else
return
@@ -0,0 +1,49 @@
/datum/action/innate/ability/limb_regrowth
name = "Regenerate Limbs"
check_flags = AB_CHECK_CONSCIOUS
button_icon_state = "slimeheal"
icon_icon = 'icons/mob/actions/actions_slime.dmi'
background_icon_state = "bg_alien"
required_mobility_flags = NONE
/datum/action/innate/ability/limb_regrowth/IsAvailable(silent = FALSE)
if(..())
var/mob/living/carbon/human/H = owner
var/list/limbs_to_heal = H.get_missing_limbs()
if(limbs_to_heal.len < 1)
return 0
var/mode = H.get_ability_property(INNATE_ABILITY_LIMB_REGROWTH, PROPERTY_LIMB_REGROWTH_USAGE_TYPE)
switch(mode)
if(REGROWTH_USES_BLOOD)
if(H.blood_volume >= (BLOOD_VOLUME_OKAY*H.blood_ratio)+40)
return TRUE
else
return FALSE
return 0
/datum/action/innate/ability/limb_regrowth/Activate()
var/mob/living/carbon/human/H = owner
var/list/limbs_to_heal = H.get_missing_limbs()
if(limbs_to_heal.len < 1)
to_chat(H, "<span class='notice'>You feel intact enough as it is.</span>")
return
to_chat(H, "<span class='notice'>You focus intently on your missing [limbs_to_heal.len >= 2 ? "limbs" : "limb"]...</span>")
var/mode = H.get_ability_property(INNATE_ABILITY_LIMB_REGROWTH, PROPERTY_LIMB_REGROWTH_USAGE_TYPE)
switch(mode)
if(REGROWTH_USES_BLOOD)
if(H.blood_volume >= 40*limbs_to_heal.len+(BLOOD_VOLUME_OKAY*H.blood_ratio))
H.regenerate_limbs()
H.blood_volume -= 40*limbs_to_heal.len
to_chat(H, "<span class='notice'>...and after a moment you finish reforming!</span>")
return
else if(H.blood_volume >= 40)//We can partially heal some limbs
while(H.blood_volume >= (BLOOD_VOLUME_OKAY*H.blood_ratio)+40)
var/healed_limb = pick(limbs_to_heal)
H.regenerate_limb(healed_limb)
limbs_to_heal -= healed_limb
H.blood_volume -= 40
to_chat(H, "<span class='warning'>...but there is not enough of you to fix everything! You must attain more mass to heal completely!</span>")
return
to_chat(H, "<span class='warning'>...but there is not enough of you to go around! You must attain more mass to heal!</span>")
@@ -2002,14 +2002,6 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(HAS_BONE in species_traits) if(HAS_BONE in species_traits)
. |= BIO_JUST_BONE . |= BIO_JUST_BONE
//a check for if you should render any overlays or not
/datum/species/proc/should_render(mob/living/carbon/human/H)
return TRUE
//a check for if you want to forcibly make CanPass return TRUE for the mob with this species
/datum/species/proc/species_pass_check()
return FALSE
///////////// /////////////
//BREATHING// //BREATHING//
///////////// /////////////
@@ -15,9 +15,6 @@
exotic_bloodtype = "GEL" exotic_bloodtype = "GEL"
exotic_blood_color = "BLOOD_COLOR_SLIME" exotic_blood_color = "BLOOD_COLOR_SLIME"
damage_overlay_type = "" damage_overlay_type = ""
var/datum/action/innate/regenerate_limbs/regenerate_limbs
var/datum/action/innate/slime_change/slime_change
var/datum/action/innate/slime_puddle/slime_puddle
liked_food = TOXIC | MEAT liked_food = TOXIC | MEAT
disliked_food = null disliked_food = null
toxic_food = ANTITOXIC toxic_food = ANTITOXIC
@@ -37,27 +34,19 @@
icon_state = "brain-slime" icon_state = "brain-slime"
/datum/species/jelly/on_species_loss(mob/living/carbon/C) /datum/species/jelly/on_species_loss(mob/living/carbon/C)
if(slime_puddle && slime_puddle.is_puddle)
slime_puddle.Activate()
if(regenerate_limbs)
regenerate_limbs.Remove(C)
if(slime_change)
slime_change.Remove(C)
if(slime_puddle)
slime_puddle.Remove(C)
C.faction -= "slime" C.faction -= "slime"
if(ishuman(C))
var/mob/living/carbon/human/H = C
H.remove_ability_from_source(list(INNATE_ABILITY_SLIME_BLOBFORM, INNATE_ABILITY_LIMB_REGROWTH, INNATE_ABILITY_HUMANOID_CUSTOMIZATION), ABILITY_SOURCE_SPECIES)
..() ..()
C.faction -= "slime" C.faction -= "slime"
/datum/species/jelly/on_species_gain(mob/living/carbon/C, datum/species/old_species) /datum/species/jelly/on_species_gain(mob/living/carbon/C, datum/species/old_species)
..() ..()
if(ishuman(C)) if(ishuman(C))
regenerate_limbs = new var/mob/living/carbon/human/H = C
regenerate_limbs.Grant(C) H.grant_ability_from_source(list(INNATE_ABILITY_SLIME_BLOBFORM, INNATE_ABILITY_LIMB_REGROWTH, INNATE_ABILITY_HUMANOID_CUSTOMIZATION), ABILITY_SOURCE_SPECIES)
slime_change = new H.set_ability_property(INNATE_ABILITY_LIMB_REGROWTH, PROPERTY_LIMB_REGROWTH_USAGE_TYPE, REGROWTH_USES_BLOOD)
slime_change.Grant(C)
slime_puddle = new
slime_puddle.Grant(C)
C.faction |= "slime" C.faction |= "slime"
/datum/species/jelly/handle_body(mob/living/carbon/human/H) /datum/species/jelly/handle_body(mob/living/carbon/human/H)
@@ -65,18 +54,6 @@
//update blood color to body color //update blood color to body color
exotic_blood_color = "#" + H.dna.features["mcolor"] exotic_blood_color = "#" + H.dna.features["mcolor"]
/datum/species/jelly/should_render()
if(slime_puddle && slime_puddle.is_puddle)
return FALSE
else
return ..()
/datum/species/jelly/species_pass_check()
if(slime_puddle && slime_puddle.is_puddle)
return TRUE
else
return ..()
/datum/species/jelly/spec_life(mob/living/carbon/human/H) /datum/species/jelly/spec_life(mob/living/carbon/human/H)
if(H.stat == DEAD || HAS_TRAIT(H, TRAIT_NOMARROW)) //can't farm slime jelly from a dead slime/jelly person indefinitely, and no regeneration for blooduskers if(H.stat == DEAD || HAS_TRAIT(H, TRAIT_NOMARROW)) //can't farm slime jelly from a dead slime/jelly person indefinitely, and no regeneration for blooduskers
return return
@@ -94,8 +71,6 @@
to_chat(H, "<span class='danger'>You feel drained!</span>") to_chat(H, "<span class='danger'>You feel drained!</span>")
if(H.blood_volume < (BLOOD_VOLUME_BAD*H.blood_ratio)) if(H.blood_volume < (BLOOD_VOLUME_BAD*H.blood_ratio))
Cannibalize_Body(H) Cannibalize_Body(H)
if(regenerate_limbs)
regenerate_limbs.UpdateButtonIcon()
/datum/species/jelly/proc/Cannibalize_Body(mob/living/carbon/human/H) /datum/species/jelly/proc/Cannibalize_Body(mob/living/carbon/human/H)
var/list/limbs_to_consume = list(BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG) - H.get_missing_limbs() var/list/limbs_to_consume = list(BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG) - H.get_missing_limbs()
@@ -111,47 +86,6 @@
qdel(consumed_limb) qdel(consumed_limb)
H.blood_volume += 20 H.blood_volume += 20
/datum/action/innate/regenerate_limbs
name = "Regenerate Limbs"
check_flags = AB_CHECK_CONSCIOUS
button_icon_state = "slimeheal"
icon_icon = 'icons/mob/actions/actions_slime.dmi'
background_icon_state = "bg_alien"
required_mobility_flags = NONE
/datum/action/innate/regenerate_limbs/IsAvailable(silent = FALSE)
if(..())
var/mob/living/carbon/human/H = owner
var/list/limbs_to_heal = H.get_missing_limbs()
if(limbs_to_heal.len < 1)
return 0
if(H.blood_volume >= (BLOOD_VOLUME_OKAY*H.blood_ratio)+40)
return 1
return 0
/datum/action/innate/regenerate_limbs/Activate()
var/mob/living/carbon/human/H = owner
var/list/limbs_to_heal = H.get_missing_limbs()
if(limbs_to_heal.len < 1)
to_chat(H, "<span class='notice'>You feel intact enough as it is.</span>")
return
to_chat(H, "<span class='notice'>You focus intently on your missing [limbs_to_heal.len >= 2 ? "limbs" : "limb"]...</span>")
if(H.blood_volume >= 40*limbs_to_heal.len+(BLOOD_VOLUME_OKAY*H.blood_ratio))
H.regenerate_limbs()
H.blood_volume -= 40*limbs_to_heal.len
to_chat(H, "<span class='notice'>...and after a moment you finish reforming!</span>")
return
else if(H.blood_volume >= 40)//We can partially heal some limbs
while(H.blood_volume >= (BLOOD_VOLUME_OKAY*H.blood_ratio)+40)
var/healed_limb = pick(limbs_to_heal)
H.regenerate_limb(healed_limb)
limbs_to_heal -= healed_limb
H.blood_volume -= 40
to_chat(H, "<span class='warning'>...but there is not enough of you to fix everything! You must attain more mass to heal completely!</span>")
return
to_chat(H, "<span class='warning'>...but there is not enough of you to go around! You must attain more mass to heal!</span>")
////////////////////////////////////////////////////////SLIMEPEOPLE/////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////SLIMEPEOPLE///////////////////////////////////////////////////////////////////
//Slime people are able to split like slimes, retaining a single mind that can swap between bodies at will, even after death. //Slime people are able to split like slimes, retaining a single mind that can swap between bodies at will, even after death.
@@ -482,314 +416,6 @@
allowed_limb_ids = list(SPECIES_SLIME,SPECIES_STARGAZER,SPECIES_SLIME_LUMI) allowed_limb_ids = list(SPECIES_SLIME,SPECIES_STARGAZER,SPECIES_SLIME_LUMI)
/datum/action/innate/slime_change
name = "Alter Form"
check_flags = AB_CHECK_CONSCIOUS
button_icon_state = "alter_form" //placeholder
icon_icon = 'modular_citadel/icons/mob/actions/actions_slime.dmi'
background_icon_state = "bg_alien"
/datum/action/innate/slime_change/Activate()
var/mob/living/carbon/human/H = owner
if(!isjellyperson(H))
return
else
H.visible_message("<span class='notice'>[owner] gains a look of \
concentration while standing perfectly still.\
Their body seems to shift and starts getting more goo-like.</span>",
"<span class='notice'>You focus intently on altering your body while \
standing perfectly still...</span>")
change_form()
/datum/action/innate/slime_change/proc/change_form()
var/mob/living/carbon/human/H = owner
var/select_alteration = input(owner, "Select what part of your form to alter", "Form Alteration", "cancel") in list("Body Color","Hair Style", "Genitals", "Tail", "Snout", "Markings", "Ears", "Taur body", "Penis", "Vagina", "Penis Length", "Breast Size", "Breast Shape", "Cancel")
if(select_alteration == "Body Color")
var/new_color = input(owner, "Choose your skin color:", "Race change","#"+H.dna.features["mcolor"]) as color|null
if(new_color)
var/temp_hsv = RGBtoHSV(new_color)
if(ReadHSV(temp_hsv)[3] >= ReadHSV(MINIMUM_MUTANT_COLOR)[3]) // mutantcolors must be bright
H.dna.features["mcolor"] = sanitize_hexcolor(new_color, 6)
H.update_body()
H.update_hair()
else
to_chat(H, "<span class='notice'>Invalid color. Your color is not bright enough.</span>")
else if(select_alteration == "Hair Style")
if(H.gender == MALE)
var/new_style = input(owner, "Select a facial hair style", "Hair Alterations") as null|anything in GLOB.facial_hair_styles_list
if(new_style)
H.facial_hair_style = new_style
else
H.facial_hair_style = "Shaved"
//handle normal hair
var/new_style = input(owner, "Select a hair style", "Hair Alterations") as null|anything in GLOB.hair_styles_list
if(new_style)
H.hair_style = new_style
H.update_hair()
else if (select_alteration == "Genitals")
var/operation = input("Select organ operation.", "Organ Manipulation", "cancel") in list("add sexual organ", "remove sexual organ", "cancel")
switch(operation)
if("add sexual organ")
var/new_organ = input("Select sexual organ:", "Organ Manipulation") as null|anything in GLOB.genitals_list
if(!new_organ)
return
H.give_genital(GLOB.genitals_list[new_organ])
if("remove sexual organ")
var/list/organs = list()
for(var/obj/item/organ/genital/X in H.internal_organs)
var/obj/item/organ/I = X
organs["[I.name] ([I.type])"] = I
var/obj/item/O = input("Select sexual organ:", "Organ Manipulation", null) as null|anything in organs
var/obj/item/organ/genital/G = organs[O]
if(!G)
return
G.forceMove(get_turf(H))
qdel(G)
H.update_genitals()
else if (select_alteration == "Ears")
var/list/snowflake_ears_list = list("Normal" = null)
for(var/path in GLOB.mam_ears_list)
var/datum/sprite_accessory/ears/mam_ears/instance = GLOB.mam_ears_list[path]
if(istype(instance, /datum/sprite_accessory))
var/datum/sprite_accessory/S = instance
if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(H.client.ckey)))
snowflake_ears_list[S.name] = path
var/new_ears
new_ears = input(owner, "Choose your character's ears:", "Ear Alteration") as null|anything in snowflake_ears_list
if(new_ears)
H.dna.features["mam_ears"] = new_ears
H.update_body()
else if (select_alteration == "Snout")
var/list/snowflake_snouts_list = list("Normal" = null)
for(var/path in GLOB.mam_snouts_list)
var/datum/sprite_accessory/snouts/mam_snouts/instance = GLOB.mam_snouts_list[path]
if(istype(instance, /datum/sprite_accessory))
var/datum/sprite_accessory/S = instance
if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(H.client.ckey)))
snowflake_snouts_list[S.name] = path
var/new_snout
new_snout = input(owner, "Choose your character's face:", "Face Alteration") as null|anything in snowflake_snouts_list
if(new_snout)
H.dna.features["mam_snouts"] = new_snout
H.update_body()
else if (select_alteration == "Markings")
var/list/snowflake_markings_list = list("None")
for(var/path in GLOB.mam_body_markings_list)
var/datum/sprite_accessory/mam_body_markings/instance = GLOB.mam_body_markings_list[path]
if(istype(instance, /datum/sprite_accessory))
var/datum/sprite_accessory/S = instance
if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(H.client.ckey)))
snowflake_markings_list[S.name] = path
var/new_mam_body_markings
new_mam_body_markings = input(H, "Choose your character's body markings:", "Marking Alteration") as null|anything in snowflake_markings_list
if(new_mam_body_markings)
H.dna.features["mam_body_markings"] = new_mam_body_markings
for(var/X in H.bodyparts) //propagates the markings changes
var/obj/item/bodypart/BP = X
BP.update_limb(FALSE, H)
H.update_body()
else if (select_alteration == "Tail")
var/list/snowflake_tails_list = list("Normal" = null)
for(var/path in GLOB.mam_tails_list)
var/datum/sprite_accessory/tails/mam_tails/instance = GLOB.mam_tails_list[path]
if(istype(instance, /datum/sprite_accessory))
var/datum/sprite_accessory/S = instance
if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(H.client.ckey)))
snowflake_tails_list[S.name] = path
var/new_tail
new_tail = input(owner, "Choose your character's Tail(s):", "Tail Alteration") as null|anything in snowflake_tails_list
if(new_tail)
H.dna.features["mam_tail"] = new_tail
if(new_tail != "None")
H.dna.features["taur"] = "None"
H.update_body()
else if (select_alteration == "Taur body")
var/list/snowflake_taur_list = list("Normal" = null)
for(var/path in GLOB.taur_list)
var/datum/sprite_accessory/taur/instance = GLOB.taur_list[path]
if(istype(instance, /datum/sprite_accessory))
var/datum/sprite_accessory/S = instance
if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(H.client.ckey)))
snowflake_taur_list[S.name] = path
var/new_taur
new_taur = input(owner, "Choose your character's tauric body:", "Tauric Alteration") as null|anything in snowflake_taur_list
if(new_taur)
H.dna.features["taur"] = new_taur
if(new_taur != "None")
H.dna.features["mam_tail"] = "None"
H.update_body()
else if (select_alteration == "Penis")
for(var/obj/item/organ/genital/penis/X in H.internal_organs)
qdel(X)
var/new_shape
new_shape = input(owner, "Choose your character's dong", "Genital Alteration") as null|anything in GLOB.cock_shapes_list
if(new_shape)
H.dna.features["cock_shape"] = new_shape
H.update_genitals()
H.give_genital(/obj/item/organ/genital/testicles)
H.give_genital(/obj/item/organ/genital/penis)
H.apply_overlay()
else if (select_alteration == "Vagina")
for(var/obj/item/organ/genital/vagina/X in H.internal_organs)
qdel(X)
var/new_shape
new_shape = input(owner, "Choose your character's pussy", "Genital Alteration") as null|anything in GLOB.vagina_shapes_list
if(new_shape)
H.dna.features["vag_shape"] = new_shape
H.update_genitals()
H.give_genital(/obj/item/organ/genital/womb)
H.give_genital(/obj/item/organ/genital/vagina)
H.apply_overlay()
else if (select_alteration == "Penis Length")
for(var/obj/item/organ/genital/penis/X in H.internal_organs)
qdel(X)
var/min_D = CONFIG_GET(number/penis_min_inches_prefs)
var/max_D = CONFIG_GET(number/penis_max_inches_prefs)
var/new_length = input(owner, "Penis length in inches:\n([min_D]-[max_D])", "Genital Alteration") as num|null
if(new_length)
H.dna.features["cock_length"] = clamp(round(new_length), min_D, max_D)
H.update_genitals()
H.apply_overlay()
H.give_genital(/obj/item/organ/genital/testicles)
H.give_genital(/obj/item/organ/genital/penis)
else if (select_alteration == "Breast Size")
for(var/obj/item/organ/genital/breasts/X in H.internal_organs)
qdel(X)
var/new_size = input(owner, "Breast Size", "Genital Alteration") as null|anything in CONFIG_GET(keyed_list/breasts_cups_prefs)
if(new_size)
H.dna.features["breasts_size"] = new_size
H.update_genitals()
H.apply_overlay()
H.give_genital(/obj/item/organ/genital/breasts)
else if (select_alteration == "Breast Shape")
for(var/obj/item/organ/genital/breasts/X in H.internal_organs)
qdel(X)
var/new_shape
new_shape = input(owner, "Breast Shape", "Genital Alteration") as null|anything in GLOB.breasts_shapes_list
if(new_shape)
H.dna.features["breasts_shape"] = new_shape
H.update_genitals()
H.apply_overlay()
H.give_genital(/obj/item/organ/genital/breasts)
else
return
/datum/action/innate/slime_puddle
name = "Puddle Transformation"
check_flags = AB_CHECK_CONSCIOUS
button_icon_state = "slimepuddle"
icon_icon = 'icons/mob/actions/actions_slime.dmi'
background_icon_state = "bg_alien"
required_mobility_flags = MOBILITY_STAND
var/is_puddle = FALSE
var/in_transformation_duration = 12
var/out_transformation_duration = 7
var/puddle_into_effect = /obj/effect/temp_visual/slime_puddle
var/puddle_from_effect = /obj/effect/temp_visual/slime_puddle/reverse
var/puddle_icon = 'icons/mob/mob.dmi'
var/puddle_state = "puddle"
var/tracked_overlay
var/datum/component/squeak/squeak
var/transforming = FALSE
var/last_use
/datum/action/innate/slime_puddle/IsAvailable()
if(!transforming)
return ..()
else
return FALSE
/datum/action/innate/slime_puddle/Activate()
var/mob/living/carbon/human/H = owner
//if they have anything stuck to their hands, we immediately say 'no' and return
for(var/obj/item/I in H.held_items)
if(HAS_TRAIT(I, TRAIT_NODROP))
to_chat(owner, "There's something stuck to your hand, stopping you from transforming!")
return
if(isjellyperson(owner) && IsAvailable())
transforming = TRUE
UpdateButtonIcon()
var/mutcolor = "#" + H.dna.features["mcolor"]
if(!is_puddle)
if(CHECK_MOBILITY(H, MOBILITY_USE)) //if we can use items, we can turn into a puddle
is_puddle = TRUE //so we know which transformation to use when its used
owner.cut_overlays() //we dont show our normal sprite, we show a puddle sprite
var/obj/effect/puddle_effect = new puddle_into_effect(get_turf(owner), owner.dir)
puddle_effect.color = mutcolor
H.Stun(in_transformation_duration, ignore_canstun = TRUE) //cant move while transforming
//series of traits that make up the puddle behaviour
ADD_TRAIT(H, TRAIT_PARALYSIS_L_ARM, SLIMEPUDDLE_TRAIT)
ADD_TRAIT(H, TRAIT_PARALYSIS_R_ARM, SLIMEPUDDLE_TRAIT)
ADD_TRAIT(H, TRAIT_MOBILITY_NOPICKUP, SLIMEPUDDLE_TRAIT)
ADD_TRAIT(H, TRAIT_MOBILITY_NOUSE, SLIMEPUDDLE_TRAIT)
ADD_TRAIT(H, TRAIT_SPRINT_LOCKED, SLIMEPUDDLE_TRAIT)
ADD_TRAIT(H, TRAIT_COMBAT_MODE_LOCKED, SLIMEPUDDLE_TRAIT)
ADD_TRAIT(H, TRAIT_MOBILITY_NOREST, SLIMEPUDDLE_TRAIT)
ADD_TRAIT(H, TRAIT_ARMOR_BROKEN, SLIMEPUDDLE_TRAIT)
H.update_disabled_bodyparts(silent = TRUE) //silently update arms to be paralysed
H.add_movespeed_modifier(/datum/movespeed_modifier/slime_puddle)
H.layer -= 1 //go one layer down so people go over you
ENABLE_BITFIELD(H.pass_flags, PASSMOB) //this actually lets people pass over you
squeak = H.AddComponent(/datum/component/squeak, custom_sounds = list('sound/effects/blobattack.ogg')) //blorble noise when people step on you
//if the user is a changeling, retract their sting
H.unset_sting()
sleep(in_transformation_duration) //wait for animation to end
//set the puddle overlay up
var/mutable_appearance/puddle_overlay = mutable_appearance(icon = puddle_icon, icon_state = puddle_state)
puddle_overlay.color = mutcolor
tracked_overlay = puddle_overlay
owner.add_overlay(puddle_overlay)
transforming = FALSE
UpdateButtonIcon()
else
//like the above, but reverse everything done!
owner.cut_overlay(tracked_overlay)
var/obj/effect/puddle_effect = new puddle_from_effect(get_turf(owner), owner.dir)
puddle_effect.color = mutcolor
H.Stun(out_transformation_duration, ignore_canstun = TRUE)
sleep(out_transformation_duration)
REMOVE_TRAIT(H, TRAIT_PARALYSIS_L_ARM, SLIMEPUDDLE_TRAIT)
REMOVE_TRAIT(H, TRAIT_PARALYSIS_R_ARM, SLIMEPUDDLE_TRAIT)
REMOVE_TRAIT(H, TRAIT_MOBILITY_NOPICKUP, SLIMEPUDDLE_TRAIT)
REMOVE_TRAIT(H, TRAIT_MOBILITY_NOUSE, SLIMEPUDDLE_TRAIT)
REMOVE_TRAIT(H, TRAIT_SPRINT_LOCKED, SLIMEPUDDLE_TRAIT)
REMOVE_TRAIT(H, TRAIT_COMBAT_MODE_LOCKED, SLIMEPUDDLE_TRAIT)
REMOVE_TRAIT(H, TRAIT_MOBILITY_NOREST, SLIMEPUDDLE_TRAIT)
REMOVE_TRAIT(H, TRAIT_ARMOR_BROKEN, SLIMEPUDDLE_TRAIT)
H.update_disabled_bodyparts(silent = TRUE)
H.remove_movespeed_modifier(/datum/movespeed_modifier/slime_puddle)
H.layer += 1 //go one layer back above!
DISABLE_BITFIELD(H.pass_flags, PASSMOB)
is_puddle = FALSE
if(squeak)
squeak.RemoveComponent()
owner.regenerate_icons()
transforming = FALSE
UpdateButtonIcon()
else
to_chat(owner, "<span class='warning'>You need to be standing up to do this!") //just assume they're a slime because it's such a weird edgecase to have it and not be one (it shouldn't even be possible)
///////////////////////////////////LUMINESCENTS////////////////////////////////////////// ///////////////////////////////////LUMINESCENTS//////////////////////////////////////////
//Luminescents are able to consume and use slime extracts, without them decaying. //Luminescents are able to consume and use slime extracts, without them decaying.
@@ -48,18 +48,22 @@ There are several things that need to be remembered:
*/ */
/mob/living/carbon/human/ComponentInitialize()
. = ..()
RegisterSignal(src, SIGNAL_TRAIT(TRAIT_HUMAN_NO_RENDER), /mob.proc/regenerate_icons)
//HAIR OVERLAY //HAIR OVERLAY
/mob/living/carbon/human/update_hair() /mob/living/carbon/human/update_hair()
if(dna.species.should_render()) if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER))
dna.species.handle_hair(src) dna.species.handle_hair(src)
//used when putting/removing clothes that hide certain mutant body parts to just update those and not update the whole body. //used when putting/removing clothes that hide certain mutant body parts to just update those and not update the whole body.
/mob/living/carbon/human/proc/update_mutant_bodyparts() /mob/living/carbon/human/proc/update_mutant_bodyparts()
if(dna.species.should_render()) if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER))
dna.species.handle_mutant_bodyparts(src) dna.species.handle_mutant_bodyparts(src)
/mob/living/carbon/human/update_body(update_genitals = FALSE) /mob/living/carbon/human/update_body(update_genitals = FALSE)
if(dna.species.should_render()) if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER))
remove_overlay(BODY_LAYER) remove_overlay(BODY_LAYER)
dna.species.handle_body(src) dna.species.handle_body(src)
..() ..()
@@ -69,11 +73,10 @@ There are several things that need to be remembered:
/mob/living/carbon/human/update_fire() /mob/living/carbon/human/update_fire()
..((fire_stacks > 3) ? "Standing" : "Generic_mob_burning") ..((fire_stacks > 3) ? "Standing" : "Generic_mob_burning")
/* --------------------------------------- */ /* --------------------------------------- */
//For legacy support. //For legacy support.
/mob/living/carbon/human/regenerate_icons() /mob/living/carbon/human/regenerate_icons()
if(dna.species.should_render()) if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER))
if(!..()) if(!..())
icon_render_key = null //invalidate bodyparts cache icon_render_key = null //invalidate bodyparts cache
update_body(TRUE) update_body(TRUE)
@@ -102,7 +105,7 @@ There are several things that need to be remembered:
//vvvvvv UPDATE_INV PROCS vvvvvv //vvvvvv UPDATE_INV PROCS vvvvvv
/mob/living/carbon/human/update_inv_w_uniform() /mob/living/carbon/human/update_inv_w_uniform()
if(dna.species.should_render()) if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER))
remove_overlay(UNIFORM_LAYER) remove_overlay(UNIFORM_LAYER)
if(client && hud_used) if(client && hud_used)
@@ -154,7 +157,7 @@ There are several things that need to be remembered:
update_mutant_bodyparts() update_mutant_bodyparts()
/mob/living/carbon/human/update_inv_wear_id() /mob/living/carbon/human/update_inv_wear_id()
if(dna.species.should_render()) if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER))
remove_overlay(ID_LAYER) remove_overlay(ID_LAYER)
if(client && hud_used) if(client && hud_used)
@@ -179,7 +182,7 @@ There are several things that need to be remembered:
/mob/living/carbon/human/update_inv_gloves() /mob/living/carbon/human/update_inv_gloves()
if(dna.species.should_render()) if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER))
remove_overlay(GLOVES_LAYER) remove_overlay(GLOVES_LAYER)
if(client && hud_used && hud_used.inv_slots[SLOT_GLOVES]) if(client && hud_used && hud_used.inv_slots[SLOT_GLOVES])
@@ -213,7 +216,7 @@ There are several things that need to be remembered:
/mob/living/carbon/human/update_inv_glasses() /mob/living/carbon/human/update_inv_glasses()
if(dna.species.should_render()) if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER))
remove_overlay(GLASSES_LAYER) remove_overlay(GLASSES_LAYER)
if(!get_bodypart(BODY_ZONE_HEAD)) //decapitated if(!get_bodypart(BODY_ZONE_HEAD)) //decapitated
@@ -240,7 +243,7 @@ There are several things that need to be remembered:
apply_overlay(GLASSES_LAYER) apply_overlay(GLASSES_LAYER)
/mob/living/carbon/human/update_inv_ears() /mob/living/carbon/human/update_inv_ears()
if(dna.species.should_render()) if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER))
remove_overlay(EARS_LAYER) remove_overlay(EARS_LAYER)
if(!get_bodypart(BODY_ZONE_HEAD)) //decapitated if(!get_bodypart(BODY_ZONE_HEAD)) //decapitated
@@ -266,7 +269,7 @@ There are several things that need to be remembered:
apply_overlay(EARS_LAYER) apply_overlay(EARS_LAYER)
/mob/living/carbon/human/update_inv_shoes() /mob/living/carbon/human/update_inv_shoes()
if(dna.species.should_render()) if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER))
remove_overlay(SHOES_LAYER) remove_overlay(SHOES_LAYER)
if(get_num_legs(FALSE) <2) if(get_num_legs(FALSE) <2)
@@ -304,7 +307,7 @@ There are several things that need to be remembered:
apply_overlay(SHOES_LAYER) apply_overlay(SHOES_LAYER)
/mob/living/carbon/human/update_inv_s_store() /mob/living/carbon/human/update_inv_s_store()
if(dna.species.should_render()) if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER))
remove_overlay(SUIT_STORE_LAYER) remove_overlay(SUIT_STORE_LAYER)
if(client && hud_used) if(client && hud_used)
@@ -328,7 +331,7 @@ There are several things that need to be remembered:
apply_overlay(SUIT_STORE_LAYER) apply_overlay(SUIT_STORE_LAYER)
/mob/living/carbon/human/update_inv_head() /mob/living/carbon/human/update_inv_head()
if(dna.species.should_render()) if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER))
remove_overlay(HEAD_LAYER) remove_overlay(HEAD_LAYER)
if(!get_bodypart(BODY_ZONE_HEAD)) //Decapitated if(!get_bodypart(BODY_ZONE_HEAD)) //Decapitated
@@ -368,7 +371,7 @@ There are several things that need to be remembered:
update_mutant_bodyparts() update_mutant_bodyparts()
/mob/living/carbon/human/update_inv_belt() /mob/living/carbon/human/update_inv_belt()
if(dna.species.should_render()) if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER))
remove_overlay(BELT_LAYER) remove_overlay(BELT_LAYER)
if(client && hud_used) if(client && hud_used)
@@ -390,7 +393,7 @@ There are several things that need to be remembered:
apply_overlay(BELT_LAYER) apply_overlay(BELT_LAYER)
/mob/living/carbon/human/update_inv_wear_suit() /mob/living/carbon/human/update_inv_wear_suit()
if(dna.species.should_render()) if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER))
remove_overlay(SUIT_LAYER) remove_overlay(SUIT_LAYER)
if(client && hud_used) if(client && hud_used)
@@ -477,7 +480,7 @@ There are several things that need to be remembered:
/mob/living/carbon/human/update_inv_wear_mask() /mob/living/carbon/human/update_inv_wear_mask()
if(dna.species.should_render()) if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER))
remove_overlay(FACEMASK_LAYER) remove_overlay(FACEMASK_LAYER)
if(!get_bodypart(BODY_ZONE_HEAD)) //Decapitated if(!get_bodypart(BODY_ZONE_HEAD)) //Decapitated
@@ -518,7 +521,7 @@ There are several things that need to be remembered:
update_mutant_bodyparts() //e.g. upgate needed because mask now hides lizard snout update_mutant_bodyparts() //e.g. upgate needed because mask now hides lizard snout
/mob/living/carbon/human/update_inv_back() /mob/living/carbon/human/update_inv_back()
if(dna.species.should_render()) if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER))
..() ..()
var/mutable_appearance/back_overlay = overlays_standing[BACK_LAYER] var/mutable_appearance/back_overlay = overlays_standing[BACK_LAYER]
if(back_overlay) if(back_overlay)
@@ -747,7 +750,7 @@ use_mob_overlay_icon: if FALSE, it will always use the default_icon_file even if
if(!dna.species) if(!dna.species)
return return
if(dna.species.should_render()) if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER))
return return
var/obj/item/bodypart/HD = get_bodypart("head") var/obj/item/bodypart/HD = get_bodypart("head")
+1
View File
@@ -34,6 +34,7 @@
ranged_ability.remove_ranged_ability(src) ranged_ability.remove_ranged_ability(src)
if(buckled) if(buckled)
buckled.unbuckle_mob(src,force=1) buckled.unbuckle_mob(src,force=1)
QDEL_LIST_ASSOC_VAL(ability_actions)
remove_from_all_data_huds() remove_from_all_data_huds()
GLOB.mob_living_list -= src GLOB.mob_living_list -= src
+2 -1
View File
@@ -6,6 +6,7 @@
RegisterSignal(src, SIGNAL_TRAIT(TRAIT_MOBILITY_NOPICKUP), .proc/update_mobility) RegisterSignal(src, SIGNAL_TRAIT(TRAIT_MOBILITY_NOPICKUP), .proc/update_mobility)
RegisterSignal(src, SIGNAL_TRAIT(TRAIT_MOBILITY_NOUSE), .proc/update_mobility) RegisterSignal(src, SIGNAL_TRAIT(TRAIT_MOBILITY_NOUSE), .proc/update_mobility)
RegisterSignal(src, SIGNAL_TRAIT(TRAIT_MOBILITY_NOREST), .proc/update_mobility) RegisterSignal(src, SIGNAL_TRAIT(TRAIT_MOBILITY_NOREST), .proc/update_mobility)
RegisterSignal(src, SIGNAL_TRAIT(TRAIT_LIVING_NO_DENSITY), .proc/update_density)
//Stuff like mobility flag updates, resting updates, etc. //Stuff like mobility flag updates, resting updates, etc.
@@ -148,7 +149,7 @@
L.update_pull_movespeed() L.update_pull_movespeed()
//Handle lying down, voluntary or involuntary //Handle lying down, voluntary or involuntary
density = !lying update_density()
if(lying) if(lying)
set_resting(TRUE, TRUE, FALSE) set_resting(TRUE, TRUE, FALSE)
if(layer == initial(layer)) //to avoid special cases like hiding larvas. if(layer == initial(layer)) //to avoid special cases like hiding larvas.
@@ -17,6 +17,9 @@
pixel_x = get_standard_pixel_x_offset(lying) pixel_x = get_standard_pixel_x_offset(lying)
pixel_y = get_standard_pixel_y_offset(lying) pixel_y = get_standard_pixel_y_offset(lying)
/mob/living/proc/update_density()
density = !lying && !HAS_TRAIT(src, TRAIT_LIVING_NO_DENSITY)
/mob/living/CanPass(atom/movable/mover, turf/target) /mob/living/CanPass(atom/movable/mover, turf/target)
if((mover.pass_flags & PASSMOB)) if((mover.pass_flags & PASSMOB))
return TRUE return TRUE
+9 -1
View File
@@ -11,7 +11,7 @@
blocks_emissive = EMISSIVE_BLOCK_GENERIC blocks_emissive = EMISSIVE_BLOCK_GENERIC
vis_flags = VIS_INHERIT_PLANE //when this be added to vis_contents of something it inherit something.plane, important for visualisation of mob in openspace. vis_flags = VIS_INHERIT_PLANE //when this be added to vis_contents of something it inherit something.plane, important for visualisation of mob in openspace.
attack_hand_is_action = TRUE attack_hand_is_action = TRUE
attack_hand_unwieldlyness = CLICK_CD_MELEE attack_hand_unwieldlyness = CLICK_CD_MELEE
attack_hand_speed = 0 attack_hand_speed = 0
@@ -169,3 +169,11 @@
var/typing_indicator_timerid var/typing_indicator_timerid
/// Current state of our typing indicator. Used for cut overlay, DO NOT RUNTIME ASSIGN OTHER THAN FROM SHOW/CLEAR. Used to absolutely ensure we do not get stuck overlays. /// Current state of our typing indicator. Used for cut overlay, DO NOT RUNTIME ASSIGN OTHER THAN FROM SHOW/CLEAR. Used to absolutely ensure we do not get stuck overlays.
var/mutable_appearance/typing_indicator_current var/mutable_appearance/typing_indicator_current
/// Ability system based on action buttons. Can be ported to base /mob or /mob/living later if needed, easily - the procs are currently on living/carbon/human/innate_abilities.dm
/// datum traits-style lazylist of abilities
var/list/innate_abilities
/// ability = action button instance.
var/list/ability_actions
/// ability = list(data). see __DEFINES/mobs/innate_abilities.dm
var/list/ability_properties
@@ -113,7 +113,7 @@
/datum/design/nanites/spreading /datum/design/nanites/spreading
name = "Infective Exo-Locomotion" name = "Infective Exo-Locomotion"
desc = "The nanites gain the ability to survive for brief periods outside of the human body, as well as the ability to start new colonies without an integration process; \ desc = "The nanites gain the ability to survive for brief periods outside of the human body, as well as the ability to start new colonies without an integration process; \
resulting in an extremely infective strain of nanites." resulting in an extremely infective strain of nanites. Bypasses antiviral defense"
id = "spreading_nanites" id = "spreading_nanites"
program_type = /datum/nanite_program/spreading program_type = /datum/nanite_program/spreading
category = list("Utility Nanites") category = list("Utility Nanites")
@@ -133,6 +133,21 @@
program_type = /datum/nanite_program/mitosis program_type = /datum/nanite_program/mitosis
category = list("Utility Nanites") category = list("Utility Nanites")
/datum/design/nanites/antiviral
name = "Enhanced Error Correction"
desc = "The nanites self-propagate and replicate their program storage memory, preventing viral takeovers."
id = "antiviral_nanites"
program_type = /datum/nanite_program/lockout/antiviral
category = list("Utility Nanites")
/datum/design/nanites/hostile_lockdown
name = "Hostile Lockdown"
desc = "The nanites constantly encrypt and scramble their own control sectors, preventing consoles from controlling them. Furthermore, \
if the host happens to be a synthetic organism with innate control over nanite strains, this will prevent them from acting on the nanites as well."
id = "hostile_lockdown"
program_type = /datum/nanite_program/lockout/hostile_lockdown
category = list("Utility Nanites")
////////////////////MEDICAL NANITES////////////////////////////////////// ////////////////////MEDICAL NANITES//////////////////////////////////////
/datum/design/nanites/regenerative /datum/design/nanites/regenerative
name = "Accelerated Regeneration" name = "Accelerated Regeneration"
@@ -41,12 +41,12 @@
update_icon() update_icon()
/obj/machinery/nanite_chamber/proc/set_safety(threshold) /obj/machinery/nanite_chamber/proc/set_safety(threshold)
if(!occupant) if(!occupant || SEND_SIGNAL(occupant, COMSIG_NANITE_CHECK_CONSOLE_LOCK))
return return
SEND_SIGNAL(occupant, COMSIG_NANITE_SET_SAFETY, threshold) SEND_SIGNAL(occupant, COMSIG_NANITE_SET_SAFETY, threshold)
/obj/machinery/nanite_chamber/proc/set_cloud(cloud_id) /obj/machinery/nanite_chamber/proc/set_cloud(cloud_id)
if(!occupant) if(!occupant || SEND_SIGNAL(occupant, COMSIG_NANITE_CHECK_CONSOLE_LOCK))
return return
SEND_SIGNAL(occupant, COMSIG_NANITE_SET_CLOUD, cloud_id) SEND_SIGNAL(occupant, COMSIG_NANITE_SET_CLOUD, cloud_id)
@@ -82,7 +82,7 @@
return return
if((stat & MAINT) || panel_open) if((stat & MAINT) || panel_open)
return return
if(!occupant || busy) if(!occupant || busy || SEND_SIGNAL(occupant, COMSIG_NANITE_CHECK_CONSOLE_LOCK))
return return
var/locked_state = locked var/locked_state = locked
@@ -173,7 +173,6 @@
return FALSE return FALSE
..() ..()
return TRUE return TRUE
/obj/machinery/nanite_chamber/relaymove(mob/user as mob) /obj/machinery/nanite_chamber/relaymove(mob/user as mob)
@@ -50,6 +50,10 @@
data["status_msg"] = chamber.busy_message data["status_msg"] = chamber.busy_message
return data return data
if(SEND_SIGNAL(L, COMSIG_NANITE_CHECK_CONSOLE_LOCK))
data["status_msg"] = "Error: Nanite keycodes scrambled. Unable to operate."
return data
data["status_msg"] = null data["status_msg"] = null
data["scan_level"] = chamber.scan_level data["scan_level"] = chamber.scan_level
data["locked"] = chamber.locked data["locked"] = chamber.locked
@@ -54,6 +54,13 @@
//Rules that automatically manage if the program's active without requiring separate sensor programs //Rules that automatically manage if the program's active without requiring separate sensor programs
var/list/datum/nanite_rule/rules = list() var/list/datum/nanite_rule/rules = list()
/// Corruptable - able to have code/configuration changed
var/corruptable = TRUE
/// error flicking - able to be randomly toggled by errors
var/error_flicking = TRUE
/// immutable - cannot be overwritten by other programs
var/immutable = FALSE
/datum/nanite_program/New() /datum/nanite_program/New()
. = ..() . = ..()
register_extra_settings() register_extra_settings()
@@ -68,8 +75,15 @@
on_mob_remove() on_mob_remove()
if(nanites) if(nanites)
nanites.programs -= src nanites.programs -= src
nanites.permanent_programs -= src
return ..() return ..()
/**
* Checks if we're a permanent program
*/
/datum/nanite_program/proc/is_permanent()
return nanites && (src in nanites.permanent_programs)
/datum/nanite_program/proc/copy() /datum/nanite_program/proc/copy()
var/datum/nanite_program/new_program = new type() var/datum/nanite_program/new_program = new type()
copy_programming(new_program, TRUE) copy_programming(new_program, TRUE)
@@ -77,6 +91,8 @@
return new_program return new_program
/datum/nanite_program/proc/copy_programming(datum/nanite_program/target, copy_activated = TRUE) /datum/nanite_program/proc/copy_programming(datum/nanite_program/target, copy_activated = TRUE)
if(target.immutable)
return
if(copy_activated) if(copy_activated)
target.activated = activated target.activated = activated
target.timer_restart = timer_restart target.timer_restart = timer_restart
@@ -234,7 +250,7 @@
/datum/nanite_program/proc/on_emp(severity) /datum/nanite_program/proc/on_emp(severity)
if(program_flags & NANITE_EMP_IMMUNE) if(program_flags & NANITE_EMP_IMMUNE)
return return
if(prob(80 / severity)) if(prob(severity / 2))
software_error() software_error()
/datum/nanite_program/proc/on_shock(shock_damage) /datum/nanite_program/proc/on_shock(shock_damage)
@@ -242,7 +258,7 @@
if(prob(10)) if(prob(10))
software_error() software_error()
else if(prob(33)) else if(prob(33))
qdel(src) self_destruct()
/datum/nanite_program/proc/on_minor_shock() /datum/nanite_program/proc/on_minor_shock()
if(!program_flags & NANITE_SHOCK_IMMUNE) if(!program_flags & NANITE_SHOCK_IMMUNE)
@@ -254,26 +270,29 @@
/datum/nanite_program/proc/software_error(type) /datum/nanite_program/proc/software_error(type)
if(!type) if(!type)
type = rand(1,5) type = rand(1,is_permanent()? 4 : 5)
switch(type) switch(type)
if(1) if(1)
qdel(src) //kill switch self_destruct() //kill switch
return return
if(2) //deprogram codes if(2) //deprogram codes
activation_code = 0 if(corruptable)
deactivation_code = 0 activation_code = 0
kill_code = 0 deactivation_code = 0
trigger_code = 0 kill_code = 0
trigger_code = 0
if(3) if(3)
toggle() //enable/disable if(error_flicking)
toggle() //enable/disable
if(4) if(4)
if(can_trigger) if(error_flicking && can_trigger)
trigger() trigger()
if(5) //Program is scrambled and does something different if(5) //Program is scrambled and does something different
var/rogue_type = pick(rogue_types) if(corruptable)
var/datum/nanite_program/rogue = new rogue_type var/rogue_type = pick(rogue_types)
nanites.add_program(null, rogue, src) var/datum/nanite_program/rogue = new rogue_type
qdel(src) nanites.add_program(null, rogue, src)
self_destruct()
/datum/nanite_program/proc/receive_signal(code, source) /datum/nanite_program/proc/receive_signal(code, source)
if(activation_code && code == activation_code && !activated) if(activation_code && code == activation_code && !activated)
@@ -285,10 +304,18 @@
if(can_trigger && trigger_code && code == trigger_code) if(can_trigger && trigger_code && code == trigger_code)
trigger() trigger()
host_mob.investigate_log("'s [name] nanite program was triggered by [source] with code [code].", INVESTIGATE_NANITES) host_mob.investigate_log("'s [name] nanite program was triggered by [source] with code [code].", INVESTIGATE_NANITES)
if(kill_code && code == kill_code) if((kill_code && code == kill_code) && !is_permanent())
host_mob.investigate_log("'s [name] nanite program was deleted by [source] with code [code].", INVESTIGATE_NANITES) host_mob.investigate_log("'s [name] nanite program was deleted by [source] with code [code].", INVESTIGATE_NANITES)
qdel(src) qdel(src)
/**
* Attempts to destroy ourselves
*/
/datum/nanite_program/proc/self_destruct()
if(is_permanent())
return
qdel(src)
///A nanite program containing a behaviour protocol. Only one protocol of each class can be active at once. ///A nanite program containing a behaviour protocol. Only one protocol of each class can be active at once.
//Moved to being 'normally' researched due to lack of B.E.P.I.S. //Moved to being 'normally' researched due to lack of B.E.P.I.S.
/datum/nanite_program/protocol /datum/nanite_program/protocol
@@ -11,7 +11,7 @@
return FALSE return FALSE
if(iscarbon(host_mob)) if(iscarbon(host_mob))
var/mob/living/carbon/C = host_mob var/mob/living/carbon/C = host_mob
var/list/parts = C.get_damaged_bodyparts(TRUE,TRUE, status = list(BODYPART_ORGANIC)) var/list/parts = C.get_damaged_bodyparts(TRUE,TRUE, status = list(BODYPART_ORGANIC, BODYPART_NANITES))
if(!parts.len) if(!parts.len)
return FALSE return FALSE
return ..() return ..()
@@ -19,7 +19,7 @@
/datum/nanite_program/regenerative/active_effect() /datum/nanite_program/regenerative/active_effect()
if(iscarbon(host_mob)) if(iscarbon(host_mob))
var/mob/living/carbon/C = host_mob var/mob/living/carbon/C = host_mob
var/list/parts = C.get_damaged_bodyparts(TRUE,TRUE, status = list(BODYPART_ORGANIC)) var/list/parts = C.get_damaged_bodyparts(TRUE,TRUE, status = list(BODYPART_ORGANIC, BODYPART_NANITES))
if(!parts.len) if(!parts.len)
return return
for(var/obj/item/bodypart/L in parts) for(var/obj/item/bodypart/L in parts)
@@ -121,7 +121,7 @@
if(iscarbon(host_mob)) if(iscarbon(host_mob))
var/mob/living/carbon/C = host_mob var/mob/living/carbon/C = host_mob
var/list/parts = C.get_damaged_bodyparts(TRUE, TRUE, status = list(BODYPART_ROBOTIC, BODYPART_HYBRID)) var/list/parts = C.get_damaged_bodyparts(TRUE, TRUE, status = list(BODYPART_ROBOTIC, BODYPART_HYBRID, BODYPART_NANITES))
if(!parts.len) if(!parts.len)
return FALSE return FALSE
else else
@@ -132,7 +132,7 @@
/datum/nanite_program/repairing/active_effect(mob/living/M) /datum/nanite_program/repairing/active_effect(mob/living/M)
if(iscarbon(host_mob)) if(iscarbon(host_mob))
var/mob/living/carbon/C = host_mob var/mob/living/carbon/C = host_mob
var/list/parts = C.get_damaged_bodyparts(TRUE, TRUE, status = list(BODYPART_ROBOTIC, BODYPART_HYBRID)) var/list/parts = C.get_damaged_bodyparts(TRUE, TRUE, status = list(BODYPART_ROBOTIC, BODYPART_HYBRID, BODYPART_NANITES))
if(!parts.len) if(!parts.len)
return return
var/update = FALSE var/update = FALSE
@@ -176,12 +176,12 @@
/datum/nanite_program/regenerative_advanced/active_effect() /datum/nanite_program/regenerative_advanced/active_effect()
if(iscarbon(host_mob)) if(iscarbon(host_mob))
var/mob/living/carbon/C = host_mob var/mob/living/carbon/C = host_mob
var/list/parts = C.get_damaged_bodyparts(TRUE,TRUE, status = list(BODYPART_ORGANIC)) var/list/parts = C.get_damaged_bodyparts(TRUE,TRUE, status = list(BODYPART_ORGANIC, BODYPART_NANITES))
if(!parts.len) if(!parts.len)
return return
var/update = FALSE var/update = FALSE
for(var/obj/item/bodypart/L in parts) for(var/obj/item/bodypart/L in parts)
if(L.heal_damage(3/parts.len, 3/parts.len, FALSE)) if(L.heal_damage(3/parts.len, 3/parts.len, 0))
update = TRUE update = TRUE
if(update) if(update)
host_mob.update_damage_overlays() host_mob.update_damage_overlays()
@@ -16,7 +16,7 @@
var/datum/nanite_extra_setting/program = extra_settings[NES_PROGRAM_OVERWRITE] var/datum/nanite_extra_setting/program = extra_settings[NES_PROGRAM_OVERWRITE]
var/datum/nanite_extra_setting/cloud = extra_settings[NES_CLOUD_OVERWRITE] var/datum/nanite_extra_setting/cloud = extra_settings[NES_CLOUD_OVERWRITE]
for(var/mob/M in orange(host_mob, 5)) for(var/mob/M in orange(host_mob, 5))
if(SEND_SIGNAL(M, COMSIG_NANITE_IS_STEALTHY)) if(SEND_SIGNAL(M, COMSIG_NANITE_CHECK_VIRAL_PREVENTION))
continue continue
switch(program.get_value()) switch(program.get_value())
if("Overwrite") if("Overwrite")
@@ -350,3 +350,66 @@
/datum/action/innate/nanite_button/Activate() /datum/action/innate/nanite_button/Activate()
program.press() program.press()
/datum/nanite_program/lockout
unique = TRUE
var/emp_disable_time = 0
var/shock_disable_time = 0
var/minor_shock_disable_time = 0
var/disable_time = 0
var/lock_console = FALSE
var/lock_virus = FALSE
var/lock_host = FALSE
/datum/nanite_program/lockout/enable_passive_effect()
. = ..()
if(lock_console)
RegisterSignal(src, COMSIG_NANITE_INTERNAL_CONSOLE_LOCK_CHECK, .proc/check_antivirus)
if(lock_host)
RegisterSignal(src, COMSIG_NANITE_INTERNAL_HOST_LOCK_CHECK, .proc/check_antivirus)
if(lock_virus)
RegisterSignal(src, COMSIG_NANITE_INTERNAL_VIRAL_PREVENTION_CHECK, .proc/check_antivirus)
/datum/nanite_program/lockout/disable_passive_effect()
. = ..()
UnregisterSignal(src, list(
COMSIG_NANITE_INTERNAL_HOST_LOCK_CHECK,
COMSIG_NANITE_INTERNAL_CONSOLE_LOCK_CHECK,
COMSIG_NANITE_INTERNAL_VIRAL_PREVENTION_CHECK
))
/datum/nanite_program/lockout/on_emp(severity)
// no parent call on purpose
disable_time = max(disable_time, world.time + emp_disable_time)
/datum/nanite_program/lockout/on_shock(shock_damage)
// no parent call on purpose
disable_time = max(disable_time, world.time + shock_disable_time)
/datum/nanite_program/lockout/on_minor_shock()
// no parent call on purpose
disable_time = max(disable_time, world.time + minor_shock_disable_time)
/datum/nanite_program/lockout/proc/check_antivirus()
return (world.time <= disable_time)? NANITE_CHANGES_LOCKED : NONE
/datum/nanite_program/lockout/antiviral
name = "Enhanced Error Correction"
desc = "Through expensive CRC checking and replication, prevents viral takeover of the nanite strain's control sectors. \
Temporarily disabled by EMPs and shocks."
use_rate = 0.5
emp_disable_time = 3 MINUTES
shock_disable_time = 45 SECONDS
minor_shock_disable_time = 10 SECONDS
lock_virus = TRUE
/datum/nanite_program/lockout/hostile_lockdown
name = "Hostile Lockdown"
desc = "Constantly encrypts and scrambles the nanites' control memory, preventing consoles from modifying them and also locking out conscious host control of the nanite strain, if the host happens to be capable of that. \
Temporarily disabled by EMPs and shocks."
use_rate = 0.5
emp_disable_time = 3 MINUTES
shock_disable_time = 1 MINUTES
minor_shock_disable_time = 15 SECONDS
lock_host = TRUE
lock_console = TRUE
@@ -91,7 +91,7 @@
var/heavy_range = FLOOR(nanite_amount/100, 1) - 1 var/heavy_range = FLOOR(nanite_amount/100, 1) - 1
var/light_range = FLOOR(nanite_amount/50, 1) - 1 var/light_range = FLOOR(nanite_amount/50, 1) - 1
explosion(host_mob, 0, heavy_range, light_range) explosion(host_mob, 0, heavy_range, light_range)
qdel(nanites) nanites.delete_nanites()
//TODO make it defuse if triggered again //TODO make it defuse if triggered again
@@ -23,7 +23,7 @@
display_name = "Mesh Nanite Programming" display_name = "Mesh Nanite Programming"
description = "Nanite programs that require static structures and membranes." description = "Nanite programs that require static structures and membranes."
prereq_ids = list("nanite_base","engineering") prereq_ids = list("nanite_base","engineering")
design_ids = list("hardening_nanites", "dermal_button_nanites", "refractive_nanites", "cryo_nanites", "conductive_nanites", "shock_nanites", "emp_nanites", "temperature_nanites") design_ids = list("hardening_nanites", "dermal_button_nanites", "refractive_nanites", "cryo_nanites", "conductive_nanites", "shock_nanites", "emp_nanites", "temperature_nanites", "antiviral_nanites", "hostile_lockdown")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
/datum/techweb_node/nanite_bio /datum/techweb_node/nanite_bio
@@ -0,0 +1,5 @@
author: "silicons"
delete-after: True
changes:
- tweak: "nanite resistances tweaked"
- rscadd: "new nanite programs added for locking the user out from being modified by consoles or antivirals."
@@ -0,0 +1,4 @@
author: "silicons"
delete-after: True
changes:
- tweak: "text formatting now uses one character instead of two around the text to emphasize."
@@ -0,0 +1,4 @@
author: "silicons"
delete-after: True
changes:
- balance: "stamina crit is only removed when at or under 100 stamina, rather than 140. stamina crit threshold is still at 140."
@@ -0,0 +1,4 @@
author: "silicons"
delete-after: True
changes:
- balance: "shoving yourself up now costs 50% more"
@@ -0,0 +1,4 @@
author: "silicons"
delete-after: True
changes:
- balance: "dna melt drops all items being destroying you"
@@ -0,0 +1,4 @@
author: "Hatterhat"
delete-after: True
changes:
- tweak: "Exosuits sold on the Supply shuttle no longer leave wreckages."
@@ -0,0 +1,4 @@
author: "MrJWhit"
delete-after: True
changes:
- tweak: "Updated jukebox sprite."
@@ -0,0 +1,4 @@
author: "MrJWhit"
delete-after: True
changes:
- tweak: "Updates our dark gygax sprites to /tg/'s"
@@ -0,0 +1,4 @@
author: "MrJWhit"
delete-after: True
changes:
- rscadd: "Adds a goose bar sign"
@@ -0,0 +1,4 @@
author: "MrJWhit"
delete-after: True
changes:
- tweak: "Donut boxes show what's inside of them now"
@@ -0,0 +1,4 @@
author: "The Grinch"
delete-after: True
changes:
- rscdel: "infinite presents from hilbert hotel"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 159 KiB

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 686 KiB

After

Width:  |  Height:  |  Size: 686 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 79 KiB

@@ -212,7 +212,7 @@
return ..() return ..()
if(HAS_TRAIT(C, TRAIT_ROBOTIC_ORGANISM)) if(HAS_TRAIT(C, TRAIT_ROBOTIC_ORGANISM))
C.adjustToxLoss(1, toxins_type = TOX_SYSCORRUPT) //Interferes with robots. Rare chem, so, pretty good at that too. C.adjustToxLoss(1, toxins_type = TOX_SYSCORRUPT) //Interferes with robots. Rare chem, so, pretty good at that too.
N.nanite_volume += -cached_purity*5//0.5 seems to be the default to me, so it'll neuter them. N.adjust_nanites(-cached_purity*5) //0.5 seems to be the default to me, so it'll neuter them.
..() ..()
/datum/reagent/fermi/nanite_b_gone/overdose_process(mob/living/carbon/C) /datum/reagent/fermi/nanite_b_gone/overdose_process(mob/living/carbon/C)
@@ -227,7 +227,7 @@
to_chat(C, "<span class='warning'>You feel a strange tingling sensation come from your core.</b></span>") to_chat(C, "<span class='warning'>You feel a strange tingling sensation come from your core.</b></span>")
if(isnull(N)) if(isnull(N))
return ..() return ..()
N.nanite_volume += -10*cached_purity N.adjust_nanites(-10*cached_purity)
..() ..()
datum/reagent/fermi/nanite_b_gone/reaction_obj(obj/O, reac_volume) datum/reagent/fermi/nanite_b_gone/reaction_obj(obj/O, reac_volume)
+5
View File
@@ -145,6 +145,7 @@
#include "code\__DEFINES\dcs\signals.dm" #include "code\__DEFINES\dcs\signals.dm"
#include "code\__DEFINES\mapping\maploader.dm" #include "code\__DEFINES\mapping\maploader.dm"
#include "code\__DEFINES\material\worth.dm" #include "code\__DEFINES\material\worth.dm"
#include "code\__DEFINES\mobs\innate_abilities.dm"
#include "code\__DEFINES\mobs\slowdowns.dm" #include "code\__DEFINES\mobs\slowdowns.dm"
#include "code\__DEFINES\research\anomalies.dm" #include "code\__DEFINES\research\anomalies.dm"
#include "code\__DEFINES\research\stock_parts.dm" #include "code\__DEFINES\research\stock_parts.dm"
@@ -2404,6 +2405,7 @@
#include "code\modules\mob\clickdelay.dm" #include "code\modules\mob\clickdelay.dm"
#include "code\modules\mob\death.dm" #include "code\modules\mob\death.dm"
#include "code\modules\mob\emote.dm" #include "code\modules\mob\emote.dm"
#include "code\modules\mob\innate_abilities.dm"
#include "code\modules\mob\inventory.dm" #include "code\modules\mob\inventory.dm"
#include "code\modules\mob\login.dm" #include "code\modules\mob\login.dm"
#include "code\modules\mob\logout.dm" #include "code\modules\mob\logout.dm"
@@ -2558,6 +2560,9 @@
#include "code\modules\mob\living\carbon\human\status_procs.dm" #include "code\modules\mob\living\carbon\human\status_procs.dm"
#include "code\modules\mob\living\carbon\human\typing_indicator.dm" #include "code\modules\mob\living\carbon\human\typing_indicator.dm"
#include "code\modules\mob\living\carbon\human\update_icons.dm" #include "code\modules\mob\living\carbon\human\update_icons.dm"
#include "code\modules\mob\living\carbon\human\innate_abilities\blobform.dm"
#include "code\modules\mob\living\carbon\human\innate_abilities\customization.dm"
#include "code\modules\mob\living\carbon\human\innate_abilities\limb_regeneration.dm"
#include "code\modules\mob\living\carbon\human\species_types\abductor.dm" #include "code\modules\mob\living\carbon\human\species_types\abductor.dm"
#include "code\modules\mob\living\carbon\human\species_types\android.dm" #include "code\modules\mob\living\carbon\human\species_types\android.dm"
#include "code\modules\mob\living\carbon\human\species_types\angel.dm" #include "code\modules\mob\living\carbon\human\species_types\angel.dm"