Merge remote-tracking branch 'citadel/master' into respawn_system_2
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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 ..()
|
||||
|
||||
@@ -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)
|
||||
. |= 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//
|
||||
/////////////
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user