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

View File

@@ -72,7 +72,7 @@
/turf/open/indestructible/hotelwood,
/area/hilbertshotel)
"n" = (
/obj/effect/landmark/xmastree,
/obj/effect/landmark/xmastree/hilbert,
/turf/open/indestructible/hotelwood,
/area/hilbertshotel)
"o" = (

View File

@@ -81,6 +81,8 @@
//stamina stuff
/// crit for stamina damage. forces a rest, and stops movement until stamina goes back to stamina softcrit
#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
#define STAMINA_NO_OVERDRAW_THRESHOLD 100

View File

@@ -10,7 +10,7 @@
/// Usage for eyestabbing with a screwdriver
#define STAMINA_COST_ITEM_EYESTAB 7.5
/// 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)
#define TOTAL_MASS_TINY_ITEM 1.25

View File

@@ -490,6 +490,17 @@
#define COMPONENT_PROGRAM_INSTALLED 1 //Installation successful
#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
/// 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
#define COMSIG_CONTAINS_STORAGE "is_storage" //() - returns bool.

View File

@@ -48,6 +48,8 @@
#define MOB_EPIC (1 << 7) // Megafauna
#define MOB_REPTILE (1 << 8)
#define MOB_SPIRIT (1 << 9)
/// Mobs that otherwise support nanites
#define MOB_NANITES (1 << 10)
// Organ defines for carbon mobs
#define ORGAN_ORGANIC 1
@@ -56,6 +58,7 @@
#define BODYPART_ORGANIC 1
#define BODYPART_ROBOTIC 2
#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_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.

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"

View File

@@ -213,6 +213,10 @@
#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_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
// 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)

View File

@@ -17,6 +17,44 @@
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
/// 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)
if(!isliving(parent) && !istype(parent, /datum/nanite_cloud_backup))
return COMPONENT_INCOMPATIBLE
@@ -55,6 +93,9 @@
RegisterSignal(parent, COMSIG_NANITE_ADD_PROGRAM, .proc/add_program)
RegisterSignal(parent, COMSIG_NANITE_SCAN, .proc/nanite_scan)
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))
RegisterSignal(parent, COMSIG_ATOM_EMP_ACT, .proc/on_emp)
@@ -118,13 +159,63 @@
next_sync = world.time + NANITE_SYNC_DELAY
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()
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)
/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()
for(var/X in programs)
var/datum/nanite_program/NP = X
@@ -151,7 +242,7 @@
sync(null, cloud_copy)
return
//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)
NP.software_error()
@@ -159,8 +250,11 @@
for(var/X in programs)
var/datum/nanite_program/NP = X
if(NP.unique && NP.type == new_program.type)
qdel(NP)
if(programs.len >= max_programs)
if(NP in permanent_programs)
return COMPONENT_PROGRAM_NOT_INSTALLED
else
qdel(NP)
if((programs.len - length(permanent_programs)) >= max_programs)
return COMPONENT_PROGRAM_NOT_INSTALLED
if(source_program)
source_program.copy_programming(new_program)
@@ -177,7 +271,7 @@
/datum/component/nanites/proc/adjust_nanites(datum/source, amount)
nanite_volume = clamp(nanite_volume + amount, 0, max_nanites)
if(nanite_volume <= 0) //oops we ran out
qdel(src)
nanites_depleted()
/datum/component/nanites/proc/set_nanite_bar(remove = FALSE)
var/image/holder = host_mob.hud_list[DIAG_NANITE_FULL_HUD]
@@ -191,28 +285,28 @@
holder.icon_state = "nanites[nanite_percent]"
/datum/component/nanites/proc/on_emp(datum/source, severity)
nanite_volume *= (rand(60, 90) * 0.01) //Lose 10-40% of nanites
adjust_nanites(null, -(rand(5, 50))) //Lose 5-50 flat nanite volume
if(prob(40/severity))
severity *= emp_severity_mod
var/loss = (severity / 100) * (rand(emp_percent_deletion_lower, emp_percent_deletion_upper) * nanite_volume) + rand(emp_flat_deletion_lower, emp_flat_deletion_upper)
adjust_nanites(null, -loss)
if(prob(severity * emp_desync_mod))
cloud_id = 0
for(var/X in programs)
var/datum/nanite_program/NP = X
NP.on_emp(severity)
/datum/component/nanites/proc/on_shock(datum/source, shock_damage, siemens_coeff = 1, flags = NONE)
if(shock_damage < 1)
return
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
adjust_nanites(null, -(rand(5, 50))) //Lose 5-50 flat nanite volume
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, -loss)
for(var/X in programs)
var/datum/nanite_program/NP = X
NP.on_shock(shock_damage)
/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)
var/datum/nanite_program/NP = X
NP.on_minor_shock()
@@ -237,7 +331,7 @@
NP.receive_comm_signal(comm_code, comm_message, comm_source)
/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
/datum/component/nanites/proc/check_access(datum/source, obj/O)
@@ -378,3 +472,10 @@
id++
mob_programs += list(mob_program)
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

View File

@@ -660,27 +660,34 @@
return
if(dna.stability > 0)
return
var/instability = -dna.stability
var/instability = - dna.stability
dna.remove_all_mutations()
dna.stability = 100
if(prob(max(70-instability,0)))
if(prob(max(70 - instability,0)))
switch(rand(0,3)) //not complete and utter death
if(0)
monkeyize()
if(1)
gain_trauma(/datum/brain_trauma/severe/paralysis)
if(2)
unequip_everything()
drop_all_held_items()
corgize()
if(3)
to_chat(src, "<span class='notice'>Oh, we actually feel quite alright!</span>")
else
switch(rand(0,3))
if(0)
unequip_everything()
drop_all_held_items()
gib()
if(1)
unequip_everything()
drop_all_held_items()
dust()
if(2)
unequip_everything()
drop_all_held_items()
death()
petrify(INFINITY)
if(3)
@@ -689,6 +696,8 @@
if(BP)
BP.dismember()
else
unequip_everything()
drop_all_held_items()
gib()
else
set_species(/datum/species/dullahan)

View File

@@ -17,9 +17,6 @@
/obj/item/storage/fancy
icon = 'icons/obj/food/containers.dmi'
icon_state = "donutbox6"
name = "donut box"
desc = "Mmm. Donuts."
resistance_flags = FLAMMABLE
var/icon_type = "donut"
var/spawn_type = null
@@ -59,18 +56,22 @@
fancy_open = TRUE
update_icon()
#define DONUT_INBOX_SPRITE_WIDTH 3
/*
* Donut Box
*/
/obj/item/storage/fancy/donut_box
icon = 'icons/obj/food/containers.dmi'
icon_state = "donutbox6"
icon_type = "donut"
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
fancy_open = TRUE
custom_price = PRICE_NORMAL
appearance_flags = KEEP_TOGETHER
/obj/item/storage/fancy/donut_box/ComponentInitialize()
. = ..()
@@ -78,6 +79,36 @@
STR.max_items = 6
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
*/

View File

@@ -271,6 +271,11 @@
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."
/datum/barsign/goose
name = "The Loose Goose"
icon = "goose"
desc = "Drink till you puke and/or break the laws of reality!"
/datum/barsign/cybersylph
name = "Cyber Sylph's"
icon = "cybersylph"

View File

@@ -96,18 +96,16 @@ GLOBAL_LIST_INIT(freqtospan, list(
return "[say_mod(input, message_mode)][spanned ? ", \"[spanned]\"" : ""]"
// Citadel edit [spanned ? ", \"[spanned]\"" : ""]"
#define ENCODE_HTML_EPHASIS(input, char, html, varname) \
var/static/regex/##varname = regex("[char]{2}(.+?)[char]{2}", "g");\
input = varname.Replace_char(input, "<[html]>$1</[html]>")
/// Converts specific characters, like +, |, and _ to formatted output.
/atom/movable/proc/say_emphasis(input)
ENCODE_HTML_EPHASIS(input, "\\|", "i", italics)
ENCODE_HTML_EPHASIS(input, "\\+", "b", bold)
ENCODE_HTML_EPHASIS(input, "_", "u", underline)
var/static/regex/italics = regex(@"\|(\S[\w\W]*?\S)\|", "g")
input = italics.Replace_char(input, "<i>$1</i>")
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
#undef ENCODE_HTML_EPHASIS
/// 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)
if((input[1] == "!") && (length_char(input) > 1))

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(ismob(thing))
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)
return report

View File

@@ -53,6 +53,9 @@
new festive_tree(get_turf(src))
return INITIALIZE_HINT_QDEL
/obj/effect/landmark/xmastree/hilbert
christmas_tree = /obj/structure/flora/tree/pine/xmas
/obj/effect/landmark/xmastree/rdrod
name = "festivus pole spawner"
festive_tree = /obj/structure/festivus

View File

@@ -34,6 +34,10 @@
filling_color = "#FF69B4"
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
if(last_check_time + 50 < world.time)
if(ishuman(M))
@@ -165,6 +169,10 @@
tastes = list("jelly" = 1, "donut" = 3)
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()
. = ..()
if(extra_reagent)

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

View File

@@ -604,7 +604,7 @@
ENABLE_BITFIELD(combat_flags, COMBAT_FLAG_HARD_STAMCRIT)
filters += CIT_FILTER_STAMINACRIT
update_mobility()
if((combat_flags & COMBAT_FLAG_HARD_STAMCRIT) && total_health <= STAMINA_CRIT)
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>")
DISABLE_BITFIELD(combat_flags, COMBAT_FLAG_HARD_STAMCRIT)
filters -= CIT_FILTER_STAMINACRIT

View File

@@ -100,8 +100,3 @@
if(dna.species.space_move(src))
return TRUE
return ..()
/mob/living/carbon/human/CanPass(atom/movable/mover, turf/target)
if(dna.species.species_pass_check())
return TRUE
return ..()

View File

@@ -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()

View File

@@ -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

View File

@@ -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>")

View File

@@ -2002,14 +2002,6 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(HAS_BONE in species_traits)
. |= 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//
/////////////

View File

@@ -15,9 +15,6 @@
exotic_bloodtype = "GEL"
exotic_blood_color = "BLOOD_COLOR_SLIME"
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
disliked_food = null
toxic_food = ANTITOXIC
@@ -37,27 +34,19 @@
icon_state = "brain-slime"
/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"
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"
/datum/species/jelly/on_species_gain(mob/living/carbon/C, datum/species/old_species)
..()
if(ishuman(C))
regenerate_limbs = new
regenerate_limbs.Grant(C)
slime_change = new
slime_change.Grant(C)
slime_puddle = new
slime_puddle.Grant(C)
var/mob/living/carbon/human/H = C
H.grant_ability_from_source(list(INNATE_ABILITY_SLIME_BLOBFORM, INNATE_ABILITY_LIMB_REGROWTH, INNATE_ABILITY_HUMANOID_CUSTOMIZATION), ABILITY_SOURCE_SPECIES)
H.set_ability_property(INNATE_ABILITY_LIMB_REGROWTH, PROPERTY_LIMB_REGROWTH_USAGE_TYPE, REGROWTH_USES_BLOOD)
C.faction |= "slime"
/datum/species/jelly/handle_body(mob/living/carbon/human/H)
@@ -65,18 +54,6 @@
//update blood color to body color
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)
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
@@ -94,8 +71,6 @@
to_chat(H, "<span class='danger'>You feel drained!</span>")
if(H.blood_volume < (BLOOD_VOLUME_BAD*H.blood_ratio))
Cannibalize_Body(H)
if(regenerate_limbs)
regenerate_limbs.UpdateButtonIcon()
/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()
@@ -111,47 +86,6 @@
qdel(consumed_limb)
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///////////////////////////////////////////////////////////////////
//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)
/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 are able to consume and use slime extracts, without them decaying.

View File

@@ -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
/mob/living/carbon/human/update_hair()
if(dna.species.should_render())
if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER))
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.
/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)
/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)
dna.species.handle_body(src)
..()
@@ -69,11 +73,10 @@ There are several things that need to be remembered:
/mob/living/carbon/human/update_fire()
..((fire_stacks > 3) ? "Standing" : "Generic_mob_burning")
/* --------------------------------------- */
//For legacy support.
/mob/living/carbon/human/regenerate_icons()
if(dna.species.should_render())
if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER))
if(!..())
icon_render_key = null //invalidate bodyparts cache
update_body(TRUE)
@@ -102,7 +105,7 @@ There are several things that need to be remembered:
//vvvvvv UPDATE_INV PROCS vvvvvv
/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)
if(client && hud_used)
@@ -154,7 +157,7 @@ There are several things that need to be remembered:
update_mutant_bodyparts()
/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)
if(client && hud_used)
@@ -179,7 +182,7 @@ There are several things that need to be remembered:
/mob/living/carbon/human/update_inv_gloves()
if(dna.species.should_render())
if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER))
remove_overlay(GLOVES_LAYER)
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()
if(dna.species.should_render())
if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER))
remove_overlay(GLASSES_LAYER)
if(!get_bodypart(BODY_ZONE_HEAD)) //decapitated
@@ -240,7 +243,7 @@ There are several things that need to be remembered:
apply_overlay(GLASSES_LAYER)
/mob/living/carbon/human/update_inv_ears()
if(dna.species.should_render())
if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER))
remove_overlay(EARS_LAYER)
if(!get_bodypart(BODY_ZONE_HEAD)) //decapitated
@@ -266,7 +269,7 @@ There are several things that need to be remembered:
apply_overlay(EARS_LAYER)
/mob/living/carbon/human/update_inv_shoes()
if(dna.species.should_render())
if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER))
remove_overlay(SHOES_LAYER)
if(get_num_legs(FALSE) <2)
@@ -304,7 +307,7 @@ There are several things that need to be remembered:
apply_overlay(SHOES_LAYER)
/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)
if(client && hud_used)
@@ -328,7 +331,7 @@ There are several things that need to be remembered:
apply_overlay(SUIT_STORE_LAYER)
/mob/living/carbon/human/update_inv_head()
if(dna.species.should_render())
if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER))
remove_overlay(HEAD_LAYER)
if(!get_bodypart(BODY_ZONE_HEAD)) //Decapitated
@@ -368,7 +371,7 @@ There are several things that need to be remembered:
update_mutant_bodyparts()
/mob/living/carbon/human/update_inv_belt()
if(dna.species.should_render())
if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER))
remove_overlay(BELT_LAYER)
if(client && hud_used)
@@ -390,7 +393,7 @@ There are several things that need to be remembered:
apply_overlay(BELT_LAYER)
/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)
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()
if(dna.species.should_render())
if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER))
remove_overlay(FACEMASK_LAYER)
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
/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]
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)
return
if(dna.species.should_render())
if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER))
return
var/obj/item/bodypart/HD = get_bodypart("head")

View File

@@ -34,6 +34,7 @@
ranged_ability.remove_ranged_ability(src)
if(buckled)
buckled.unbuckle_mob(src,force=1)
QDEL_LIST_ASSOC_VAL(ability_actions)
remove_from_all_data_huds()
GLOB.mob_living_list -= src

View File

@@ -6,6 +6,7 @@
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_NOREST), .proc/update_mobility)
RegisterSignal(src, SIGNAL_TRAIT(TRAIT_LIVING_NO_DENSITY), .proc/update_density)
//Stuff like mobility flag updates, resting updates, etc.
@@ -148,7 +149,7 @@
L.update_pull_movespeed()
//Handle lying down, voluntary or involuntary
density = !lying
update_density()
if(lying)
set_resting(TRUE, TRUE, FALSE)
if(layer == initial(layer)) //to avoid special cases like hiding larvas.

View File

@@ -17,6 +17,9 @@
pixel_x = get_standard_pixel_x_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)
if((mover.pass_flags & PASSMOB))
return TRUE

View File

@@ -11,7 +11,7 @@
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.
attack_hand_is_action = TRUE
attack_hand_unwieldlyness = CLICK_CD_MELEE
attack_hand_speed = 0
@@ -169,3 +169,11 @@
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.
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

View File

@@ -113,7 +113,7 @@
/datum/design/nanites/spreading
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; \
resulting in an extremely infective strain of nanites."
resulting in an extremely infective strain of nanites. Bypasses antiviral defense"
id = "spreading_nanites"
program_type = /datum/nanite_program/spreading
category = list("Utility Nanites")
@@ -133,6 +133,21 @@
program_type = /datum/nanite_program/mitosis
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//////////////////////////////////////
/datum/design/nanites/regenerative
name = "Accelerated Regeneration"

View File

@@ -41,12 +41,12 @@
update_icon()
/obj/machinery/nanite_chamber/proc/set_safety(threshold)
if(!occupant)
if(!occupant || SEND_SIGNAL(occupant, COMSIG_NANITE_CHECK_CONSOLE_LOCK))
return
SEND_SIGNAL(occupant, COMSIG_NANITE_SET_SAFETY, threshold)
/obj/machinery/nanite_chamber/proc/set_cloud(cloud_id)
if(!occupant)
if(!occupant || SEND_SIGNAL(occupant, COMSIG_NANITE_CHECK_CONSOLE_LOCK))
return
SEND_SIGNAL(occupant, COMSIG_NANITE_SET_CLOUD, cloud_id)
@@ -82,7 +82,7 @@
return
if((stat & MAINT) || panel_open)
return
if(!occupant || busy)
if(!occupant || busy || SEND_SIGNAL(occupant, COMSIG_NANITE_CHECK_CONSOLE_LOCK))
return
var/locked_state = locked
@@ -173,7 +173,6 @@
return FALSE
..()
return TRUE
/obj/machinery/nanite_chamber/relaymove(mob/user as mob)

View File

@@ -50,6 +50,10 @@
data["status_msg"] = chamber.busy_message
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["scan_level"] = chamber.scan_level
data["locked"] = chamber.locked

View File

@@ -54,6 +54,13 @@
//Rules that automatically manage if the program's active without requiring separate sensor programs
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()
. = ..()
register_extra_settings()
@@ -68,8 +75,15 @@
on_mob_remove()
if(nanites)
nanites.programs -= src
nanites.permanent_programs -= src
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()
var/datum/nanite_program/new_program = new type()
copy_programming(new_program, TRUE)
@@ -77,6 +91,8 @@
return new_program
/datum/nanite_program/proc/copy_programming(datum/nanite_program/target, copy_activated = TRUE)
if(target.immutable)
return
if(copy_activated)
target.activated = activated
target.timer_restart = timer_restart
@@ -234,7 +250,7 @@
/datum/nanite_program/proc/on_emp(severity)
if(program_flags & NANITE_EMP_IMMUNE)
return
if(prob(80 / severity))
if(prob(severity / 2))
software_error()
/datum/nanite_program/proc/on_shock(shock_damage)
@@ -242,7 +258,7 @@
if(prob(10))
software_error()
else if(prob(33))
qdel(src)
self_destruct()
/datum/nanite_program/proc/on_minor_shock()
if(!program_flags & NANITE_SHOCK_IMMUNE)
@@ -254,26 +270,29 @@
/datum/nanite_program/proc/software_error(type)
if(!type)
type = rand(1,5)
type = rand(1,is_permanent()? 4 : 5)
switch(type)
if(1)
qdel(src) //kill switch
self_destruct() //kill switch
return
if(2) //deprogram codes
activation_code = 0
deactivation_code = 0
kill_code = 0
trigger_code = 0
if(corruptable)
activation_code = 0
deactivation_code = 0
kill_code = 0
trigger_code = 0
if(3)
toggle() //enable/disable
if(error_flicking)
toggle() //enable/disable
if(4)
if(can_trigger)
if(error_flicking && can_trigger)
trigger()
if(5) //Program is scrambled and does something different
var/rogue_type = pick(rogue_types)
var/datum/nanite_program/rogue = new rogue_type
nanites.add_program(null, rogue, src)
qdel(src)
if(corruptable)
var/rogue_type = pick(rogue_types)
var/datum/nanite_program/rogue = new rogue_type
nanites.add_program(null, rogue, src)
self_destruct()
/datum/nanite_program/proc/receive_signal(code, source)
if(activation_code && code == activation_code && !activated)
@@ -285,10 +304,18 @@
if(can_trigger && trigger_code && code == trigger_code)
trigger()
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)
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.
//Moved to being 'normally' researched due to lack of B.E.P.I.S.
/datum/nanite_program/protocol

View File

@@ -11,7 +11,7 @@
return FALSE
if(iscarbon(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)
return FALSE
return ..()
@@ -19,7 +19,7 @@
/datum/nanite_program/regenerative/active_effect()
if(iscarbon(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)
return
for(var/obj/item/bodypart/L in parts)
@@ -121,7 +121,7 @@
if(iscarbon(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)
return FALSE
else
@@ -132,7 +132,7 @@
/datum/nanite_program/repairing/active_effect(mob/living/M)
if(iscarbon(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)
return
var/update = FALSE
@@ -176,12 +176,12 @@
/datum/nanite_program/regenerative_advanced/active_effect()
if(iscarbon(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)
return
var/update = FALSE
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
if(update)
host_mob.update_damage_overlays()

View File

@@ -16,7 +16,7 @@
var/datum/nanite_extra_setting/program = extra_settings[NES_PROGRAM_OVERWRITE]
var/datum/nanite_extra_setting/cloud = extra_settings[NES_CLOUD_OVERWRITE]
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
switch(program.get_value())
if("Overwrite")
@@ -350,3 +350,66 @@
/datum/action/innate/nanite_button/Activate()
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

View File

@@ -91,7 +91,7 @@
var/heavy_range = FLOOR(nanite_amount/100, 1) - 1
var/light_range = FLOOR(nanite_amount/50, 1) - 1
explosion(host_mob, 0, heavy_range, light_range)
qdel(nanites)
nanites.delete_nanites()
//TODO make it defuse if triggered again

View File

@@ -23,7 +23,7 @@
display_name = "Mesh Nanite Programming"
description = "Nanite programs that require static structures and membranes."
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)
/datum/techweb_node/nanite_bio

View File

@@ -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."

View File

@@ -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."

View File

@@ -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."

View File

@@ -0,0 +1,4 @@
author: "silicons"
delete-after: True
changes:
- balance: "shoving yourself up now costs 50% more"

View File

@@ -0,0 +1,4 @@
author: "silicons"
delete-after: True
changes:
- balance: "dna melt drops all items being destroying you"

View File

@@ -0,0 +1,4 @@
author: "Hatterhat"
delete-after: True
changes:
- tweak: "Exosuits sold on the Supply shuttle no longer leave wreckages."

View File

@@ -0,0 +1,4 @@
author: "MrJWhit"
delete-after: True
changes:
- tweak: "Updated jukebox sprite."

View File

@@ -0,0 +1,4 @@
author: "MrJWhit"
delete-after: True
changes:
- tweak: "Updates our dark gygax sprites to /tg/'s"

View File

@@ -0,0 +1,4 @@
author: "MrJWhit"
delete-after: True
changes:
- rscadd: "Adds a goose bar sign"

View File

@@ -0,0 +1,4 @@
author: "MrJWhit"
delete-after: True
changes:
- tweak: "Donut boxes show what's inside of them now"

View File

@@ -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

View File

@@ -212,7 +212,7 @@
return ..()
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.
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)
@@ -227,7 +227,7 @@
to_chat(C, "<span class='warning'>You feel a strange tingling sensation come from your core.</b></span>")
if(isnull(N))
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)

View File

@@ -145,6 +145,7 @@
#include "code\__DEFINES\dcs\signals.dm"
#include "code\__DEFINES\mapping\maploader.dm"
#include "code\__DEFINES\material\worth.dm"
#include "code\__DEFINES\mobs\innate_abilities.dm"
#include "code\__DEFINES\mobs\slowdowns.dm"
#include "code\__DEFINES\research\anomalies.dm"
#include "code\__DEFINES\research\stock_parts.dm"
@@ -2404,6 +2405,7 @@
#include "code\modules\mob\clickdelay.dm"
#include "code\modules\mob\death.dm"
#include "code\modules\mob\emote.dm"
#include "code\modules\mob\innate_abilities.dm"
#include "code\modules\mob\inventory.dm"
#include "code\modules\mob\login.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\typing_indicator.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\android.dm"
#include "code\modules\mob\living\carbon\human\species_types\angel.dm"