Diona Overhaul - SEP2016 (#442)

This commit is contained in:
NanakoAC
2016-09-09 23:55:48 +03:00
committed by skull132
parent a65c4290d6
commit 804851f6f6
83 changed files with 2597 additions and 442 deletions
+2
View File
@@ -254,11 +254,13 @@ BLIND // can't see anything
body_parts_covered = HEAD
slot_flags = SLOT_HEAD
w_class = 2.0
diona_restricted_light = 1//Light emitted by this object or creature has limited interaction with diona
var/light_overlay = "helmet_light"
var/light_applied
var/brightness_on
var/on = 0
offset_light = 1
/obj/item/clothing/head/New()
..()
+1 -1
View File
@@ -7,7 +7,7 @@
item_state = "electronic"
matter = list(DEFAULT_WALL_MATERIAL = 150)
origin_tech = list(TECH_MAGNET = 1, TECH_ENGINEERING = 1)
offset_light = 1
var/list/scanned = list()
var/list/stored_alpha = list()
var/list/reset_objects = list()
+25 -7
View File
@@ -129,6 +129,17 @@
)
/obj/machinery/portable_atmospherics/hydroponics/AltClick()
if (istype(usr, /mob/living/carbon/alien/diona))//A diona alt+clicking feeds the plant
if (closed_system)
usr << "The lid is closed, you don't have hands to open it and reach the plants inside!"
return
var/mob/living/carbon/alien/diona/nymph = usr
if(nymph.nutrition > 100 && nutrilevel < 10)
nymph.nutrition -= ((10-nutrilevel)*5)
nutrilevel = 10
nymph.visible_message("<font color='blue'><b>[nymph]</b> secretes a trickle of green liquid, refilling [src].</font>","<font color='blue'>You secrete a trickle of green liquid, refilling [src].</font>")
return//Nymphs cant open and close lids
if(mechanical && !usr.stat && !usr.lying && Adjacent(usr))
close_lid(usr)
return
@@ -138,17 +149,24 @@
if(istype(user,/mob/living/carbon/alien/diona))
var/mob/living/carbon/alien/diona/nymph = user
if (closed_system)
user << "The lid is closed, you don't have hands to open it and reach the plants inside!"
return
if(nymph.stat == DEAD || nymph.paralysis || nymph.weakened || nymph.stunned || nymph.restrained())
return
if(weedlevel > 0)
nymph.reagents.add_reagent("nutriment", weedlevel)
nymph.ingested.add_reagent("nutriment", weedlevel/6)
weedlevel = 0
nymph.visible_message("<font color='blue'><b>[nymph]</b> begins rooting through [src], ripping out weeds and eating them noisily.</font>","<font color='blue'>You begin rooting through [src], ripping out weeds and eating them noisily.</font>")
else if(nymph.nutrition > 100 && nutrilevel < 10)
nymph.nutrition -= ((10-nutrilevel)*5)
nutrilevel = 10
nymph.visible_message("<font color='blue'><b>[nymph]</b> secretes a trickle of green liquid, refilling [src].</font>","<font color='blue'>You secrete a trickle of green liquid, refilling [src].</font>")
nymph.visible_message("<font color='blue'><b>[nymph]</b> roots through [src], ripping out weeds and eating them noisily.</font>","<font color='blue'>You root through [src], ripping out weeds and eating them noisily.</font>")
return
if (dead)//Let nymphs eat dead plants
nymph.ingested.add_reagent("nutriment", 1)
nymph.visible_message("<font color='blue'><b>[nymph]</b> rips out the dead plants from [src], and loudly munches them.</font>","<font color='blue'>You root out the dead plants in [src], eating them with loud chewing sounds.</font>")
remove_dead(user)
return
if (harvest)
harvest(user)
return
else
nymph.visible_message("<font color='blue'><b>[nymph]</b> rolls around in [src] for a bit.</font>","<font color='blue'>You roll around in [src] for a bit.</font>")
return
+150 -3
View File
@@ -51,6 +51,29 @@
return ..()
//This proc is called manually on a light if you want it to be more responsive.
//It forces an update right now instead of waiting for the controller to get around to it, which can be up to 2.1 seconds
//Update is forced on this light source, and all tiles it effects.
//This can be very expensive and inefficient, use sparingly
/datum/light_source/proc/instant_update()
remove_lum()
if(!destroyed)
apply_lum()
else if(vis_update) //We smartly update only tiles that became (in) visible to use.
smart_vis_update()
vis_update = 0
force_update = 0
needs_update = 0
for (var/turf/T in effect_turf)
if (T.lighting_overlay)
T.lighting_overlay.update_overlay()
T.lighting_overlay.needs_update = 0
/datum/light_source/proc/destroy()
destroyed = 1
force_update()
@@ -148,6 +171,13 @@
/datum/light_source/proc/apply_lum()
applied = 1
if (istype(source_atom.loc, /mob))//If the light is carried by a mob
var/mob/M = source_atom.loc
if (source_atom.offset_light)//And its an offset light
apply_lum_offset(M)//Then we call the special offset variant and terminate there.
return//This is split off to minimise overhead added to the majority of non-offset lights
//Keep track of the last applied lum values so that the lighting can be reversed
applied_lum_r = lum_r
applied_lum_g = lum_g
@@ -183,6 +213,64 @@
effect_turf += T
END_FOR_DVIEW
//Duplicated code for speed. This is a variant of apply_lum for directional/offset lights carried by a mob
/datum/light_source/proc/apply_lum_offset(var/mob/M)//M is passed in for speed since we already fetched it
var/turf/lightfrom = get_step(M, M.dir)//Light source is offset infront of the user, simulates a directional light
var/list/dview = list()
//We run a special DVIEW call to fetch the list of tiles viewable from the MOB's position
//This is cross referenced with the below DVIEW loop which runs through tiles viewable from the lightfrom position
//This is used to prevent offset lights shining through walls
DVIEW(dview, light_range, source_turf, INVISIBILITY_LIGHTING)
applied = 1
//Keep track of the last applied lum values so that the lighting can be reversed
applied_lum_r = lum_r
applied_lum_g = lum_g
applied_lum_b = lum_b
if(istype(lightfrom))
FOR_DVIEW(var/turf/T, light_range, lightfrom, INVISIBILITY_LIGHTING)//List of turfs visible from the light centre
if(T.lighting_overlay)
if (!(T in dview))//If the turf is not also visible from the mob, then it's obscured and invalid
continue//Don't light this tile. This prevents offset lights from shining through walls
var/strength
LUM_FALLOFF(strength, T, lightfrom)
if (M && T == get_turf(M))//The light applied to the tile the holder is on is reduced, simulates directional light
strength *= light_power * (source_atom.owner_light_mult)
else
strength *= light_power
if(!strength) //Don't add turfs that aren't affected to the affected turfs.
continue
strength = round(strength, LIGHTING_ROUND_VALUE) //Screw sinking points.
effect_str += strength
T.lighting_overlay.update_lumcount(
applied_lum_r * strength,
applied_lum_g * strength,
applied_lum_b * strength
)
else
effect_str += 0
if(!T.affecting_lights)
T.affecting_lights = list()
T.affecting_lights += src
effect_turf += T
END_FOR_DVIEW
/datum/light_source/proc/remove_lum()
applied = 0
var/i = 1
@@ -193,8 +281,8 @@
if(T.lighting_overlay)
var/str = effect_str[i]
T.lighting_overlay.update_lumcount(
-str * applied_lum_r,
-str * applied_lum_g,
-str * applied_lum_r,
-str * applied_lum_g,
-str * applied_lum_b
)
@@ -255,7 +343,7 @@
effect_str.Cut(idx, idx + 1)
//Whoop yet not another copy pasta because speed ~~~~BYOND.
//Calculates and applies lighting for a single turf. This is intended for when a turf switches to
//Calculates and applies lighting for a single turf. This is intended for when a turf switches to
//using dynamic lighting when it was not doing so previously (when constructing a floor on space, for example).
//Assumes the turf is visible and such.
//For the love of god don't call this proc when it's not needed! Lighting artifacts WILL happen!
@@ -295,6 +383,65 @@
applied_lum_b * .
)
//This function returns the illumination it would/did apply to the specified turf.
//It is useful for gathering information on a particular source's contribution to a turf's light
/datum/light_source/proc/get_lum(var/turf/T)
var/turf/lightfrom = source_turf
var/mob/M = null
var/list/dview = list()
var/list/castview = list()
if (istype(source_atom.loc, /mob))
M = source_atom.loc
if (source_atom.offset_light)
DVIEW(dview, light_range, source_turf, INVISIBILITY_LIGHTING)
lightfrom = get_step(M, M.dir)//Light source is offset infront of the user, simulates a directional light
applied = 1
//Keep track of the last applied lum values so that the lighting can be reversed
applied_lum_r = lum_r
applied_lum_g = lum_g
applied_lum_b = lum_b
if(istype(lightfrom))
//Castview is a list of tiles seen from the light centre.
//If the desired turf isn't in it, then we couldn't contribute anything to that tile, return a zero list
DVIEW(castview, light_range, lightfrom, INVISIBILITY_LIGHTING)
if (!(T in castview))
return list(0,0,0)
if(T.lighting_overlay)
//Check for offset lights shining through walls
if (source_atom.offset_light)
if (!(T in dview))
return list(0,0,0)
var/strength
LUM_FALLOFF(strength, T, lightfrom)
if (M && T == get_turf(M))//The light applied to the tile the holder is on is reduced, simulates directional light
strength *= light_power * (source_atom.owner_light_mult)
else
strength *= light_power
if(!strength) //If no strength, then we contributed nothing.
return list(0,0,0)
strength = round(strength, LIGHTING_ROUND_VALUE)
//If we're here, then we've confirmed this light does affect the passed tile, and how much.
//Return the values we've applied to it.
return list(
applied_lum_r * strength,
applied_lum_g * strength,
applied_lum_b * strength)
#undef LUM_FALLOFF
#undef LUM_DISTANCE
#undef LUM_ATTENUATION
+16
View File
@@ -6,6 +6,22 @@
var/datum/light_source/light
var/list/light_sources
//If this var is set, and this object casts light, and the object is worn/held on a mob
//Then the light source will be offset this many tiles in the mob's facing direction
//To make this work, must make sure this object is set as the source atom of any lightsource it creates
//For now, this will only offset one tile, regardless of the value set, but this can be expanded in future
var/offset_light = 0
//If this object emits light and is worn/held on a mob
//The light applied to the owner's tile is multiplied by this value
//This is a means to simulate directional light and is only used with offset_light
var/owner_light_mult = 0.5
//If 1, this light has reduced effect on diona
//It won't stack with other restricted light sources
var/diona_restricted_light = 0
/atom/proc/set_light(l_range, l_power, l_color)
if(l_power != null) light_power = l_power
if(l_range != null) light_range = l_range
+9 -3
View File
@@ -1,5 +1,5 @@
/datum/language/diona
name = "Rootspeak"
name = "Rootsong"
desc = "A creaking, subvocal language spoken instinctively by the Dionaea. Due to the unique makeup of the average Diona, a phrase of Rootspeak can be a combination of anywhere from one to twelve individual voices and notes."
speech_verb = "creaks and rustles"
ask_verb = "creaks"
@@ -10,8 +10,14 @@
syllables = list("hs","zt","kr","st","sh")
/datum/language/diona/get_random_name()
var/new_name = "[pick(list("To Sleep Beneath","Wind Over","Embrace of","Dreams of","Witnessing","To Walk Beneath","Approaching the"))]"
new_name += " [pick(list("the Void","the Sky","Encroaching Night","Planetsong","Starsong","the Wandering Star","the Empty Day","Daybreak","Nightfall","the Rain"))]"
var/new_name = "[pick(list("To Sleep Beneath","Changing of", "Soaring Above", "Wind Over","Embrace of","Dreams of","Witnessing", "Lost in", "To Walk Beneath","Approaching the", "Distant Memories of", "Forgotten Glimpse of", "Roots of", "Tendrils of", "Leaves Rustling in", "Last Hope of", "Speaking to"))]"
new_name += " [pick(list("the Void","the Stillness of Death", "the Sky","Encroaching Night","Planetsong","Starsong","the Wandering Star","the Empty Day","Daybreak","Nightfall","the Rain", "a Distant Galaxy", "a Starless Night", "the Fruits of Dreams", "the Rising Dawn", "the Song of Life", "a Lonely Shadow", "Forlorn Hope", "a Bleak Wasteland"))]"
while(findtextEx(new_name,"the the",1,null))
new_name = replacetext(new_name, "the the", "the")
while(findtextEx(new_name,"the a",1,null))
new_name = replacetext(new_name, "the a", "a")
return new_name
/datum/language/unathi
@@ -1,35 +0,0 @@
/mob/living/carbon/alien/diona
name = "diona nymph"
voice_name = "diona nymph"
adult_form = /mob/living/carbon/human
speak_emote = list("chirrups")
icon_state = "nymph"
language = "Rootspeak"
death_msg = "expires with a pitiful chirrup..."
universal_understand = 1
universal_speak = 0 // Dionaea do not need to speak to people other than other dionaea.
holder_type = /obj/item/weapon/holder/diona
density = 0
/mob/living/carbon/alien/diona/New()
..()
species = all_species["Diona"]
verbs += /mob/living/carbon/alien/diona/proc/merge
/mob/living/carbon/alien/diona/start_pulling(var/atom/movable/AM)
//TODO: Collapse these checks into one proc (see pai and drone)
if(istype(AM,/obj/item))
var/obj/item/O = AM
if(O.w_class > 2)
src << "<span class='warning'>You are too small to pull that.</span>"
return
else
..()
else
src << "<span class='warning'>You are too small to pull that.</span>"
return
/mob/living/carbon/alien/diona/put_in_hands(var/obj/item/W) // No hands.
W.loc = get_turf(src)
return 1
@@ -0,0 +1,286 @@
#define evolve_nutrition 4000//when a nymph gathers this much nutrition, it can evolve into a gestalt
//Diona time variables, these differ slightly between a gestalt and a nymph. All values are times in seconds
/mob/living/carbon/alien/diona
var/datum/reagents/vessel
var/list/internal_organs_by_name = list() // so internal organs have less ickiness too
var/max_nutrition = 6000
language = null
var/energy_duration = 144//The time in seconds that this diona can exist in total darkness before its energy runs out
var/dark_consciousness = 144//How long this diona can stay on its feet and keep moving in darkness after energy is gone.
var/dark_survival = 216//How long this diona can survive in darkness after energy is gone, before it dies
var/datum/dionastats/DS
mob_size = 4
density = 0
mouth_size = 2//how large of a creature it can swallow at once, and how big of a bite it can take out of larger things
eat_types = 0//This is a bitfield which must be initialised in New(). The valid values for it are in devour.dm
composition_reagent = "nutriment"//Dionae are plants, so eating them doesn't give animal protein
var/mob/living/carbon/gestalt = null//If set, then this nymph is inside a gestalt
name = "diona nymph"
voice_name = "diona nymph"
adult_form = /mob/living/carbon/human
speak_emote = list("chirrups")
icon_state = "nymph"
language = "Rootsong"
death_msg = "expires with a pitiful chirrup..."
universal_understand = 0
universal_speak = 0
holder_type = /obj/item/weapon/holder/diona
var/list/sampled_DNA
var/list/language_progress
/mob/living/carbon/alien/diona/ex_act(severity)
if (life_tick < 4)
//If a nymph was just born, then it already took damage from the ex_act on its gestalt
//So we ignore any farther damage for a couple ticks after its born, to prevent it getting hit twice by the same blast
return
else
..()
/mob/living/carbon/alien/diona/New()
..()
//species = all_species[]
set_species("Diona")
setup_dionastats()
eat_types |= TYPE_ORGANIC
nutrition = 0//We dont start with biomass
update_verbs()
sampled_DNA = list()
language_progress = list()
/mob/living/carbon/alien/diona/verb/check_light()
set category = "Abilities"
set name = "Check light level"
usr << "The light level here is [get_lightlevel_diona(DS)]"
/mob/living/carbon/alien/diona/start_pulling(var/atom/movable/AM)
//TODO: Collapse these checks into one proc (see pai and drone)
if(istype(AM,/obj/item))
var/obj/item/O = AM
if(O.w_class > 2)
src << "<span class='warning'>You are too small to pull that.</span>"
return
else
..()
else
src << "<span class='warning'>You are too small to pull that.</span>"
return
/mob/living/carbon/alien/diona/put_in_hands(var/obj/item/W) // No hands.
W.loc = get_turf(src)
return 1
//Functions duplicated from humans, albeit slightly modified
/mob/living/carbon/alien/diona/proc/set_species(var/new_species)
if(!dna)
if(!new_species)
new_species = "Human"
else
if(!new_species)
new_species = dna.species
else
dna.species = new_species
// No more invisible screaming wheelchairs because of set_species() typos.
if(!all_species[new_species])
new_species = "Human"
if(species)
if(species.name && species.name == new_species)
return
if(species.language)
remove_language(species.language)
if(species.default_language)
remove_language(species.default_language)
// Clear out their species abilities.
species.remove_inherent_verbs(src)
holder_type = null
species = all_species[new_species]
if(species.language)
add_language(species.default_language)
if(species.holder_type)
holder_type = species.holder_type
icon_state = lowertext(species.name)
species.handle_post_spawn(src)
maxHealth = species.total_health
spawn(0)
regenerate_icons()
make_blood()
// Rebuild the HUD. If they aren't logged in then login() should reinstantiate it for them.
if(client && client.screen)
client.screen.len = null
if(hud_used)
qdel(hud_used)
hud_used = new /datum/hud(src)
if(species)
return 1
else
return 0
/mob/living/carbon/alien/diona/proc/make_blood()
if(vessel)
return
vessel = new/datum/reagents(600)
vessel.my_atom = src
vessel.add_reagent("blood",560)
spawn(1)
fixblood()
/mob/living/carbon/alien/diona/proc/fixblood()
for(var/datum/reagent/blood/B in vessel.reagent_list)
if(B.id == "blood")
B.data = list( "donor"=src,"viruses"=null,"species"=species.name,"blood_DNA"=name,"blood_colour"= species.blood_color,"blood_type"=null, \
"resistances"=null,"trace_chem"=null, "virus2" = null, "antibodies" = list())
var/color = B.data["blood_colour"]
B.color = color
/mob/living/carbon/alien/diona/proc/setup_dionastats()
var/MLS = (1.5 / 2.1)//Maximum energy lost per second, in total darkness
DS = new/datum/dionastats()
DS.max_energy = energy_duration * MLS
DS.stored_energy = (DS.max_energy / 2)
DS.max_health = maxHealth
DS.pain_factor = (50 / dark_consciousness) / MLS
DS.trauma_factor = (DS.max_health / dark_survival) / MLS
DS.dionatype = 1//Nymph
//This is called periodically to register or remove this nymph's status as a bad organ of the gestalt
//This is used to notify the gestalt when it needs repaired
/mob/living/carbon/alien/diona/proc/check_status_as_organ()
if (istype(gestalt, /mob/living/carbon/human))
var/mob/living/carbon/human/H = gestalt
if (health < maxHealth)
if (!(src in H.bad_internal_organs))
H.bad_internal_organs.Add(src)
else
H.bad_internal_organs.Remove(src)
//This function makes sure the nymph has the correct split/merge verbs, depending on whether or not its part of a gestalt
/mob/living/carbon/alien/diona/proc/update_verbs()
if (gestalt)
if (!(/mob/living/carbon/alien/diona/proc/split in verbs))
verbs.Add(/mob/living/carbon/alien/diona/proc/split)
verbs.Remove(/mob/living/proc/ventcrawl)
verbs.Remove(/mob/living/proc/hide)
verbs.Remove(/mob/living/carbon/alien/diona/proc/grow)
verbs.Remove(/mob/living/carbon/alien/diona/proc/merge)
verbs.Remove(/mob/living/carbon/proc/absorb_nymph)
verbs.Remove(/mob/living/proc/devour)
verbs.Remove(/mob/living/carbon/alien/diona/proc/sample)
else
if (!(/mob/living/carbon/alien/diona/proc/merge in verbs))
verbs.Add(/mob/living/carbon/alien/diona/proc/merge)
if (!(/mob/living/carbon/proc/absorb_nymph in verbs))
verbs.Add(/mob/living/carbon/proc/absorb_nymph)
if (!(/mob/living/carbon/alien/diona/proc/grow in verbs))
verbs.Add(/mob/living/carbon/alien/diona/proc/grow)
if (!(/mob/living/proc/devour in verbs))
verbs.Add(/mob/living/proc/devour)
if (!(/mob/living/proc/ventcrawl in verbs))
verbs.Add(/mob/living/proc/ventcrawl)
if (!(/mob/living/proc/hide in verbs))
verbs.Add(/mob/living/proc/hide)
if (!(/mob/living/carbon/alien/diona/proc/sample in verbs))
verbs.Add(/mob/living/carbon/alien/diona/proc/sample)
verbs.Remove(/mob/living/carbon/alien/diona/proc/split)
verbs.Remove(/mob/living/carbon/alien/verb/evolve)//We don't want the old alien evolve verb
/mob/living/carbon/alien/diona/Stat()
..()
if (statpanel("Status"))
stat(null, text("Biomass: [nutrition]/[evolve_nutrition]"))
if (nutrition > evolve_nutrition)
stat(null, text("You have enough biomass to grow!"))
//Overriding this function from /mob/living/carbon/alien/life.dm
/mob/living/carbon/alien/diona/handle_regular_status_updates()
if(status_flags & GODMODE) return 0
if(stat == DEAD)
blinded = 1
silent = 0
else
updatehealth()
handle_stunned()
handle_weakened()
if(health <= 0)
death()
blinded = 1
silent = 0
return 1
if (halloss > 50)
paralysis = 8
if(paralysis && paralysis > 0)
handle_paralysed()
blinded = 1
stat = UNCONSCIOUS
if(sleeping)
if (mind)
if(mind.active && client != null)
sleeping = max(sleeping-1, 0)
blinded = 1
stat = UNCONSCIOUS
else if(resting)
else
stat = CONSCIOUS
// Eyes and blindness.
if(!has_eyes())
eye_blind = 1
blinded = 1
eye_blurry = 1
else if(eye_blind)
eye_blind = max(eye_blind-1,0)
blinded = 1
else if(eye_blurry)
eye_blurry = max(eye_blurry-1, 0)
//Ears
if(sdisabilities & DEAF) //disabled-deaf, doesn't get better on its own
ear_deaf = max(ear_deaf, 1)
else if(ear_deaf) //deafness, heals slowly over time
ear_deaf = max(ear_deaf-1, 0)
ear_damage = max(ear_damage-0.05, 0)
update_icons()
return 1
@@ -1,16 +1,19 @@
//Verbs after this point.
//Merge:
//Joins yourself into an existing gestalt, becoming just a small part of it
//The nymph player moves inside the gestalt and no longer has control of movement or actions
/mob/living/carbon/alien/diona/proc/merge()
set category = "Abilities"
set name = "Merge with gestalt"
set desc = "Merge with another diona."
set desc = "Merge yourself into a larger gestalt, you will no longer retain control."
if(stat == DEAD || paralysis || weakened || stunned || restrained())
return
if(istype(src.loc,/mob/living/carbon))
src.verbs -= /mob/living/carbon/alien/diona/proc/merge
return
var/list/choices = list()
for(var/mob/living/carbon/C in view(1,src))
@@ -18,32 +21,135 @@
if(!(src.Adjacent(C)) || !(C.client)) continue
if(istype(C,/mob/living/carbon/human))
var/mob/living/carbon/human/D = C
if(D.species && D.species.name == "Diona")
choices += C
var/mob/living/carbon/human/H = C
if(H.species && H.species.name == "Diona" && H.client)
choices += H
var/mob/living/M = input(src,"Who do you wish to merge with?") in null|choices
if(!M)
src << "There is nothing nearby to merge with."
src << span("warning", "There are no active gestalts nearby to merge with.")
else if(!do_merge(M))
src << "You fail to merge with \the [M]..."
src << span("warning", "You fail to merge with \the [M]...")
/mob/living/carbon/alien/diona/proc/do_merge(var/mob/living/carbon/human/H)
if(!istype(H) || !src || !(src.Adjacent(H)))
return 0
H << "You feel your being twine with that of \the [src] as it merges with your biomass."
H.status_flags |= PASSEMOTES
src << "You feel your being twine with that of \the [H] as you merge with its biomass."
loc = H
verbs += /mob/living/carbon/alien/diona/proc/split
verbs -= /mob/living/carbon/alien/diona/proc/merge
src << span("warning", "Requesting consent from [H]")
var/r = alert(H,"[src] wishes to join your collective, and become a part of your gestalt. If you accept they will become an equal part of you, though you will remain in control?", "[src] wishes to join you", "Welcome!", "No, leave us..")
if (r != "Welcome!")
src << span("warning", "[H.name] has rejected your wish to merge!")
return 0
H.visible_message(span("warning", "[H] starts absorbing [src] into its body"), span("warning", "You start absorbing [src]. This will take 15 seconds and both of you must remain still"), span("warning", "You hear a strange, alien. sucking sound"))
src << "<span class='notice'>You feel yourself slowly becoming part of something greater, remain still to finish.</span>"
face_atom(H)
H.face_atom(get_turf(src))
if(do_mob(src, H, 150, needhand = 0))
if (!(src.Adjacent(H)) || !(istype(src.loc, /turf)))//The loc check prevents us from absorbing the same nymph multiple times at once
src << span("warning", "Something went wrong while trying to merge into [H], cancelling.")
return 0
gestalt = H
sync_languages(gestalt)
update_verbs()
sleep(2)//Altering the verbs list takes some time and it wont complete after the nymph is moved in. This sleep is necessary
H << "<span class='notice'>You feel your being twine with that of \the [src] as it merges with your biomass.</span>"
H.status_flags |= PASSEMOTES
src << "<span class='notice'>You feel your being twine with that of \the [H] as you merge with its biomass.</span>"
loc = H
else
src << span("warning", "Something went wrong while trying to merge into [H], cancelling.")
return 0
return 1
/mob/living/carbon/alien/diona/proc/split()
//This verb allows a diona to absorb nymphs - both gestalts and nymphs can use this
//If the target nymph is dead, they are simply recycled as biomass, adding some nutrition
//If the target is alive, they go inside the user, granting a larger amount of biomass and continuing to exist within them
//If the target is controlled by a player, they will be asked permission first.
//Absorbing them forcibly is not possible while alive, but they can be killed and recycled if the nymph is ruthless
//The absorbing player will act as the host, and will remain in control, as well as controlling the eventual gestalt
/mob/living/carbon/proc/absorb_nymph()
set category = "Abilities"
set name = "Split from gestalt"
set name = "Absorb Nymph"
set desc = "Absorb a diona nymph into yourself, you will remain in control and gain any biomass it has absorbed."
var/list/choices = list()
for(var/mob/living/carbon/alien/diona/C in view(1,src))
if((!(src.Adjacent(C)) || C.gestalt || C == src)) continue//cant steal nymphs right out of other gestalts
choices.Add(C)
var/mob/living/carbon/alien/diona/M = input(src,"Which nymph do you wish to absorb?") in null|choices
if(!M)
src << span("warning", "There is nothing nearby to absorb")
else if(!do_absorb(M))
src << span("warning", "You fail to merge with \the [M]...")
/mob/living/carbon/proc/do_absorb(var/mob/living/carbon/alien/diona/D)
if (D.key)
//Code for requesting permission goes here. We will return if its denied or ignored
src << span("warning", "Requesting consent from [D]")
var/r = alert(D,"[src] wishes to absorb your being, and make you a part of their gestalt. If you accept you will join with them, and give up control to be a part of their collective. \nYou will be part of their larger gestalt if they grow later, too, and all of your stored biomass will be transferred to them. You can split away at anytime, but you cannot reclaim the biomass. Do you wish to be absorbed?", "[src] wishes to absorb you", "Yes, we will join!", "No, i wish to remain alone")
if (r != "Yes, we will join!")
src << span("warning", "[D] has refused to join you!")
return
else
if (!(src.Adjacent(D)) || !(istype(D.loc, /turf)))
src << span("warning", "Something went wrong while trying to absorb [D], cancelling.")
return
src.visible_message(span("warning", "[src] starts absorbing [D] into its body"), span("warning", "You start absorbing [D]. This will take 15 seconds and both of you must remain still"), span("warning", "You hear a strange, alien. sucking sound"))
D << "<span class='notice'>You feel yourself slowly becoming part of something greater, remain still to finish.</span>"
face_atom(D)
D.face_atom(get_turf(src))
if(do_mob(src, D, 150, needhand = 0))
if (!(src.Adjacent(D)) || !(istype(D.loc, /turf)))//The loc check prevents us from absorbing the same nymph multiple times at once
src << span("warning", "Something went wrong while trying to absorb [D], cancelling.")
return
if (D.stat == DEAD)
src.nutrition += NYMPH_ABSORB_NUTRITION * NYMPH_ABSORB_DEAD_FACTOR //Consuming dead nymphs gives far less nutrition
qdel(D)
return 1
else
D.gestalt = src
D.sync_languages(D.gestalt)
D.update_verbs()
sleep(2)
if (is_diona() == DIONA_NYMPH)//We only care about biomass if we're a nymph
src.nutrition += NYMPH_ABSORB_NUTRITION
src.nutrition += D.nutrition //Any biomass in the absorbed nymph is transferred to the host
D.nutrition = 0
D << "<span class='notice'>You feel your being twine with that of \the [src] as you merge with its biomass.</span>"
src << "<span class='notice'>You feel your being twine with that of \the [D] as it merges with your biomass.</span>"
D.loc = src
D.stat = CONSCIOUS
status_flags |= PASSEMOTES
return 1
else
return 0
//Split allows a nymph to peel away from a gestalt and be a lone agent
/mob/living/carbon/alien/diona/proc/split()
set category = "Abilities"
set name = "Break from gestalt"
set desc = "Split away from your gestalt as a lone nymph."
if(stat == DEAD || paralysis || weakened || stunned || restrained())
@@ -53,17 +159,132 @@
src.verbs -= /mob/living/carbon/alien/diona/proc/split
return
src.loc << "You feel a pang of loss as [src] splits away from your biomass."
src << "You wiggle out of the depths of [src.loc]'s biomass and plop to the ground."
var/r = alert(src,"Splitting will remove you from your gestalt and deposit you on the ground, allowing you to go it alone. If you had any stored biomass before you joined the gestalt, you will not get it back. Are you sure you wish to split?", "Confirm Split", "Time to leaf", "I'll stick around")
if (r != "Time to leaf")
return
var/mob/living/M = src.loc
src.loc << span("warning", "You feel a pang of loss as [src] splits away from your biomass.")
src << "<span class='notice'>You wiggle out of the depths of [src.loc]'s biomass and plop to the ground.</span>"
if (gestalt.is_diona() == DIONA_NYMPH)
gestalt.nutrition -= NYMPH_ABSORB_NUTRITION//Preventing an exploit with repeatedly absorbing and splitting
split_languages(gestalt)
src.loc = get_turf(src)
src.verbs -= /mob/living/carbon/alien/diona/proc/split
src.verbs += /mob/living/carbon/alien/diona/proc/merge
stat = CONSCIOUS
gestalt = null
update_verbs()
if(istype(M))
for(var/atom/A in M.contents)
if(istype(A,/mob/living/simple_animal/borer) || istype(A,/obj/item/weapon/holder))
//Draws a sizeable blood sample from a victim to read their DNA and learn languages
/mob/living/carbon/alien/diona/proc/sample()
set category = "Abilities"
set name = "Sample DNA"
set desc = "Learn languages by draining some blood from a nearby lifeform. You will partially learn any language it knows."
//For fun factor, we'll allow the nymph to choose nonvalid targets
var/list/choices = list()
for(var/mob/living/L in view(1,src))
if((!(src.Adjacent(L)) || L == src)) continue
choices.Add(L)
if (!choices.len)
src << span("warning", "There are no life forms nearby to sample!")
return
if (choices.len == 1)
choices.Add("Cancel")
var/mob/living/donor = input(src,"Who do you wish to drain?") in null|choices
if (!donor || donor == "Cancel")//they cancelled
return
var/types = donor.find_type()
if (types & TYPE_SYNTHETIC)
src.visible_message("<span class='danger'>[src] attempts to bite into [donor.name] but leaps back in surprise as its fangs hit metal.</span>", "<span class='danger'>You attempt to sink your fangs into [donor.name] and get a faceful of unyielding steel as the force breaks several fine protrusions.in your mouth</span>")
donor.adjustBruteLoss(2)
src.adjustBruteLoss(15)//biting metal hurts!
return
else if (isanimal(donor) && (types & TYPE_ORGANIC) && donor.stat != DEAD)
src.visible_message("<span class='danger'>[src] bites into [donor.name] and drains some of their blood</span>", "<span class='danger'>You bite into [donor.name] and drain some blood.</span>")
src << "<span class='danger'>This simple creature has insufficient intelligence for you to learn anything!</span>"
donor.adjustBruteLoss(4)
nutrition += 20
return
else if (types & TYPE_WIERD)
src.visible_message("<span class='danger'>[src] attempts to bite into [donor.name] but passes right through it!.</span>", "<span class='danger'>You attempt to sink your fangs into [donor.name] but pass right through it!</span>")
return
else if (donor.is_diona())
src << span("warning", "You can't sample the DNA of other diona!")
return
else if (istype(donor, /mob/living/carbon))
//If we get here, it's -probably- valid
src.visible_message("<span class='danger'>[src] is trying to bite [donor.name]</span>", "\red You start biting [donor.name], you and them must stay still!")
face_atom(get_turf(donor))
if (do_mob(src, donor, 30, needhand = 0))
//Attempt to find the blood vessel, but don't create a fake one if its not there.
//If the target doesn't have a vessel its probably due to someone not implementing it properly, like xenos
//We'll still allow it
var/datum/reagents/vessel = donor.get_vessel(1)
var/newDNA
vessel.remove_reagent("blood", 85, 1)//85 units of blood is enough to affect a human and make them woozy
var/list/data = vessel.get_data("blood")
newDNA = data["blood_DNA"]
if (!newDNA)//Fallback. Adminspawned mobs, and possibly some others, have null dna.
newDNA = md5("\ref[donor]")
donor.adjustBruteLoss(4)
src.visible_message("<span class='notice'>[src] sucks some blood from [donor.name]</span>", "<span class='notice'>You extract a delicious mouthful of blood from [donor.name]!</span>")
nutrition += 40
if (newDNA in sampled_DNA)
src << "<span class='danger'>You have already sampled the DNA of this creature before, you can learn nothing new. Move onto something else.</span>"
return
M.status_flags &= ~PASSEMOTES
else
sampled_DNA.Add(newDNA)
var/learned = 0
//Learned var:
//0 = The target has no languages
//1 = We already everything they know or can't learn
//2 = We learned something!
//Now we sample their languages!
for (var/datum/language/L in donor.languages)
learned = max(learned, 1)
if (!(L in languages) && !(L in diona_banned_languages))
//We don't know this language, and we can learn it!
var/current_progress = language_progress[L.name]
current_progress += 1
language_progress[L.name] = current_progress
src << "<span class='notice'><font size=3>You come a little closer to learning [L.name]!</span>"
learned = 2
if (!learned)
src << "<span class='danger'>This creature doesn't know any languages at all!</span>"
else if (learned == 1)
src << "<span class='danger'>We have nothing more to learn from this creature. Perhaps try a different species?</span>"
update_languages()
else
src << span("warning", "Something went wrong while trying to sample [donor], both you and the target must remain still.")
//Checks progress on learned languages
/mob/living/carbon/alien/diona/proc/update_languages()
for (var/i in language_progress)
if (language_progress[i] >= LANGUAGE_POINTS_TO_LEARN)
add_language(i)
src << "<span class='notice'><font size=3>You have mastered the [i] language!!</span>"
language_progress.Remove(i)
@@ -1,22 +1,41 @@
//Dionaea regenerate health and nutrition in light.
/mob/living/carbon/alien/diona/handle_environment(datum/gas_mixture/environment)
if (stat != DEAD)
diona_handle_light(DS)
var/light_amount = 0 //how much light there is in the place, affects receiving nutrition and healing
if(isturf(loc)) //else, there's considered to be no light
var/turf/T = loc
var/atom/movable/lighting_overlay/L = locate(/atom/movable/lighting_overlay) in T
if(L)
light_amount = min(10,L.lum_r + L.lum_g + L.lum_b) - 5 //hardcapped so it's not abused by having a ton of flashlights
else
light_amount = 5
/mob/living/carbon/alien/diona/handle_chemicals_in_body()
chem_effects.Cut()
analgesic = 0
if(touching) touching.metabolize()
if(ingested) ingested.metabolize()
if(bloodstr) bloodstr.metabolize()
// nutrition decrease
if (nutrition > 0 && stat != 2)
nutrition = max (0, nutrition - HUNGER_FACTOR)
if (nutrition > max_nutrition)
nutrition = max_nutrition
//handle_trace_chems() implement this later maybe
handle_stomach()
updatehealth()
return
/mob/living/carbon/alien/diona/handle_mutations_and_radiation()
diona_handle_radiation(DS)
/mob/living/carbon/alien/diona/Life()
if (gestalt && (gestalt.life_tick % 5 == 0))//Minimal processing while in stasis
updatehealth()
check_status_as_organ()
if (!gestalt)
..()
nutrition += light_amount
if(nutrition > 500)
nutrition = 500
if(light_amount > 2) //if there's enough light, heal
adjustBruteLoss(-1)
adjustFireLoss(-1)
adjustToxLoss(-1)
adjustOxyLoss(-1)
@@ -1,19 +1,80 @@
/mob/living/carbon/alien/diona/confirm_evolution()
//Whitelist requirement for evolution experimentally removed
/*
if(!is_alien_whitelisted(src, "Diona") && config.usealienwhitelist)
src << alert("You are currently not whitelisted to play as a full diona.")
return null
*/
if(amount_grown < max_grown)
src << "You are not yet ready for your growth..."
return null
src.split()
var/response = alert(src, "A worker gestalt is a large, slow, and durable humanoid form. You will lose the ability to ventcrawl and devour animals, but you will gain hand-like tendrils and the ability to wear things.You have enough biomass, are you certain you're ready to form a new gestalt?","Confirm Gestalt","Growth!","Patience...")
if(response != "Growth!") return //Hit the wrong key...again.
if(istype(loc,/obj/item/weapon/holder/diona))
var/obj/item/weapon/holder/diona/L = loc
src.loc = L.loc
qdel(L)
src.visible_message("\red [src] begins to shift and quiver, and erupts in a shower of shed bark as it splits into a tangle of nearly a dozen new dionaea.","\red You begin to shift and quiver, feeling your awareness splinter. All at once, we consume our stored nutrients to surge with growth, splitting into a tangle of at least a dozen new dionaea. We have attained our gestalt form.")
return "Diona"
return "Diona"
/mob/living/carbon/alien/diona/proc/grow()
set name = "Exponential Growth"
set desc = "Evolve into your worker gestalt form, if you have enough biomass."
set category = "Abilities"
if(stat != CONSCIOUS)
return
if(nutrition < evolve_nutrition)
src << "\red You do not have enough biomass to grow yet. Currently [nutrition]/[evolve_nutrition]."
return
if(gestalt)
src << "\red You are already part of a collective, if you wish to form your own, you must split off first"
return
// confirm_evolution() handles choices and other specific requirements.
var/new_species = confirm_evolution()
if(!new_species || !adult_form )
return
stunned = 10//No more moving or talking for now
//muted = 10
playsound(src.loc, 'sound/species/diona/gestalt_grow.ogg', 100, 1)
src.visible_message("\red [src] begins to shift and quiver.",
"\red You begin to shift and quiver, feeling your awareness splinter. ")
sleep(52)
src.visible_message("\red [src] erupts in a shower of shed bark as it splits into a tangle of half a dozen new dionaea.",
"\red All at once, we consume our stored nutrients to surge with growth, splitting into a tangle of half a dozen new dionaea. We have attained our gestalt form.")
var/mob/living/carbon/human/adult = new adult_form(get_turf(src))
adult.set_species(new_species)
show_evolution_blurb()
if(mind)
mind.original = src//This tracks which nymph 'is' the gestalt
mind.transfer_to(adult)
else
adult.key = src.key
for (var/obj/item/W in src.contents)
src.drop_from_inventory(W)
for(var/datum/language/L in languages)
adult.add_language(L.name)
//If there are any nymphs inside us, then they become equal parts of the gestalt at the same level
//Although we are still host, these nymphs become neighbors, not contents
for(var/mob/living/carbon/alien/diona/D in contents)
D.forceMove(adult)
D.loc = adult
D.gestalt = adult
D.stat = CONSCIOUS
//Finally we put ourselves into the gestalt, NOT delete ourself
//Our mind is already in the gestalt, this is really just transferring our empty body
src.nutrition = 0
src.forceMove(adult)
src.loc = adult
src.stat = CONSCIOUS
src.gestalt = adult
@@ -1,6 +0,0 @@
/mob/living/carbon/alien/diona/say_understands(var/mob/other,var/datum/language/speaking = null)
if (istype(other, /mob/living/carbon/human) && !speaking)
if(languages.len >= 2) // They have sucked down some blood.
return 1
return ..()
+1 -1
View File
@@ -131,7 +131,7 @@
if (healths)
if (stat != 2)
switch(health)
switch(health - halloss)//Halloss should be factored in here for displaying
if(100 to INFINITY)
healths.icon_state = "health0"
if(80 to 100)
@@ -1,28 +0,0 @@
/mob/living/carbon/alien/say(var/message)
var/verb = "says"
var/message_range = world.view
if(client)
if(client.prefs.muted & MUTE_IC)
src << "\red You cannot speak in IC (Muted)."
return
message = sanitize(message)
if(stat == 2)
return say_dead(message)
if(copytext(message,1,2) == "*")
return emote(copytext(message,2))
var/datum/language/speaking = parse_language(message)
if(speaking)
message = copytext(message, 2+length(speaking.key))
message = trim(message)
if(!message || stat)
return
..(message, speaking, verb, null, null, message_range, null)
@@ -1,7 +1,7 @@
/mob/living/carbon/
gender = MALE
var/datum/species/species //Contains icon generation and language information, set during New().
var/list/stomach_contents = list()
//stomach contents redefined at mob/living level, removed from here
var/list/datum/disease2/disease/virus2 = list()
var/list/antibodies = list()
var/last_eating = 0 //Not sure what this does... I found it hidden in food.dm
@@ -20,7 +20,7 @@
var/list/chem_effects = list()
var/intoxication = 0//Units of alcohol in their system
var/datum/reagents/metabolism/bloodstr = null
var/datum/reagents/metabolism/ingested = null
var/datum/reagents/metabolism/touching = null
var/pulse = PULSE_NORM //current pulse level
var/light_energy //Used by diona. Stored light energy
@@ -0,0 +1,586 @@
//This function is for code that is shared by diona nymphs and gestalt
#define DIONA_MAX_LIGHT 5.5//Light from any tile is capped to prevent refilling too fast
#define TEMP_REGEN_STOP 223//Regen rate scales down linearly from normal to this temperature, stops completely below this value
#define TEMP_REGEN_NORMAL 288//normal body temperature
#define TEMP_INCREASE_REGEN_DOUBLE 700//Health regen is increased by 100% (additive) for every increment of this value we are above normal
#define LIFETICK_INTERVAL_LESS 5
#define NYMPH_ABSORB_NUTRITION 650
#define NYMPH_ABSORB_DEAD_FACTOR 0.3
#define REGROW_FOOD_REQ 100
#define REGROW_ENERGY_REQ 40
#define LANGUAGE_POINTS_TO_LEARN 3//The number of samples of a language required to learn it
var/list/diona_banned_languages = list(
/datum/language/cult,
/datum/language/cultcommon,
/datum/language/corticalborer,
/datum/language/binary,
/datum/language/binary/drone)
/mob/living/carbon/proc/diona_handle_light(var/datum/dionastats/DS)//Carbon is the highest common denominator between gestalts and nymphs. They will share light code
//if light_organ is non null, then we're working with a gestalt. otherwise nymph
var/light_amount = DS.last_lightlevel//If we're not re-fetching the light level then we'll use a recent cached version
if (life_tick % 2 == 0)//Only fetch the lightlevel every other proc to save performance
if (DS.last_location != loc || life_tick % 4 == 0)//Fetch it even less often if we haven't moved since last check
light_amount = get_lightlevel_diona(DS)
DS.stored_energy += light_amount
if(DS.stored_energy > DS.max_energy)
DS.stored_energy = DS.max_energy
if(DS.stored_energy > 0) //if there's enough energy stored then diona heal
diona_handle_regeneration(DS)
else //If light is <=0 then it hurts instead
//var/severity = DS.stored_energy - (DS.stored_energy*2)
var/severity = light_amount*-1//Get a positive value which is the severity of the damage
diona_darkness_damage(severity, DS)
diona_handle_lightmessages(DS)
DS.last_location = loc
/mob/living/carbon/proc/diona_handle_radiation(var/datum/dionastats/DS)
//Converts radiation to stored energy if its needed, and gives messages related to radiation
//Rads can be used to heal in place of light energy, that is handled in the regular regeneration proc
if (radiation && DS.stored_energy < (DS.max_energy * 0.8))//Radiation can provide energy in place of light
radiation -= 2
DS.stored_energy += 2
radiation -= 0.5//Radiation is gradually wasted if its not used for something
//This proc handles when diona take damage from being in darkness
/mob/living/carbon/proc/diona_darkness_damage(var/severity, var/datum/dionastats/DS)
adjustBruteLoss(severity*DS.trauma_factor)
adjustHalLoss(severity*DS.pain_factor, 1)
DS.stored_energy = 0//We reset the energy back to zero after calculating the damage. dont want it to go negative
//If the diona in question is a gestalt, then all the nymphs inside it will suffer damage too
if (DS.dionatype == DIONA_WORKER)
for(var/mob/living/carbon/alien/diona/D in src)
D.adjustBruteLoss(severity*DS.trauma_factor*0.5)
#define diona_max_pressure 100//kpa, Highest pressure that has an effect
#define diona_nutrition_factor 0.5//nutrients we gain per proc at max pressure
/mob/living/carbon/proc/diona_handle_air(var/datum/dionastats/DS, var/pressure)
//Diona don't need to breathe, and can survive happily in a vacuum
//But diona gestalts gain nutrition by extracting matter from gases in the air. If a gestalt spends a long time in space or on the asteroid, it may need to actually eat food
//For simplicity, we'll assume any gas is fine, so they'll just absorb nutrition based on pressure
if (!pressure)
return
if (DS.nutrient_organ)
if (DS.nutrient_organ.is_broken())
return
var/plus= (min(pressure,diona_max_pressure) / diona_max_pressure)* diona_nutrition_factor
if (DS.nutrient_organ)
if(DS.nutrient_organ.is_bruised())
plus *= 0.5
nutrition += plus
if (nutrition > 400)
nutrition = 400
/mob/living/carbon/proc/diona_handle_temperature(var/datum/dionastats/DS)
if (bodytemperature < TEMP_REGEN_STOP)
DS.healing_factor = 0
else if (bodytemperature <= TEMP_REGEN_NORMAL)
DS.healing_factor = (bodytemperature - TEMP_REGEN_STOP) / (TEMP_REGEN_NORMAL - TEMP_REGEN_STOP)
else
DS.healing_factor = 1 + (bodytemperature - TEMP_REGEN_NORMAL) / TEMP_INCREASE_REGEN_DOUBLE
//This is a loooong function.
//Broken up into a few segments:
//Things that run every process: Healing trauma, burns and halloss. TODO: Reducing stun/weaken durations
//Things that run less often: Healing toxins, genetic damage, and (TODO) damage to internal organs
//Things that run even less often: Regrowing removed/destroyed limbs and internal organs.
//As long as a gestalt survives, and has either energy or radiation, it can regrow any part of itself,
//and will eventually become whole again without medical intervention, although medical can help.
//Most medicines don't work on diona, but physical treatment for external wounds helps a little,
//and some alternative things that are toxic to other life, such as radium and mutagen, will benefit diona
/mob/living/carbon/proc/diona_handle_regeneration(var/datum/dionastats/DS)
if ((DS.stored_energy < 1 && !radiation))//we need energy or radiation to heal
return
var/value //A little variable we'll reuse to optimise
var/CL//Cached loss, to save on repeatedly recalculating it
var/HF = DS.healing_factor//I don't know if fetching a variable from an object repeatedly is slow, but this seems safe
//Diona only get halloss from running out of energy. If they have any, and yet we get this far,
//it means that they've just reached some light or radiation after almost dying.
//Their body prioritises spending resources here first to keep them on their feet
if (getHalLoss() > 0)
CL = getHalLoss()
if (CL > 0)
if (radiation)
value = min(CL, radiation, 2*HF)
adjustHalLoss(value*-3,1)//Halloss heals more quickly
radiation -= value
CL = getHalLoss()
value = min(CL, DS.stored_energy, 1*HF)
adjustHalLoss(value*-3,1)
DS.stored_energy -= value
//Next up, damage healing. Diona are only vulnerable to four of the six damage types
//Oxyloss doesn't apply because they don't breathe, and are thus immune to aquiring it in any way
//Brain damage is irrelevant because they have no brain.
if (health < 100)
CL = getBruteLoss()
if (CL > 0)
if (radiation)
value = min(CL, radiation, 2*HF)
adjustBruteLoss(value*-1)
radiation -= value
CL = getBruteLoss()//After adjusting it, recalculate for the lighthealing
value = min(CL, DS.stored_energy, 1*HF)
adjustBruteLoss(value*-1)
DS.stored_energy -= value
CL = getFireLoss()
if (CL > 0)
if (radiation)
value = min(CL, radiation, 2*HF)
adjustFireLoss(value*-1)
radiation -= value
CL = getFireLoss()
value = min(CL, DS.stored_energy, 1*HF)
adjustFireLoss(value*-1)
DS.stored_energy -= value
CL = stunned
if (CL > 0)
if (radiation)
value = min(CL, radiation, 2*HF)
stunned -= value
radiation -= value
CL = stunned
value = min(CL, DS.stored_energy, 1*HF)
stunned -= value
DS.stored_energy -= value
CL = weakened
if (CL > 0)
if (radiation)
value = min(CL, radiation, 2*HF)
weakened -= value
radiation -= value
CL = weakened
value = min(CL, DS.stored_energy, 1*HF)
weakened -= value
DS.stored_energy -= value
//Genetic damage and toxins are relatively rare. We'll process them less often to reduce on computations
if (life_tick % LIFETICK_INTERVAL_LESS == 0)
CL = getToxLoss()
if (CL > 0)
if (radiation)
value = min(CL, radiation, 2*HF*LIFETICK_INTERVAL_LESS)
adjustToxLoss(value*-1)
radiation -= value
CL = getToxLoss()
value = min(CL, DS.stored_energy, 1*HF*LIFETICK_INTERVAL_LESS)
adjustToxLoss(value*-1)
DS.stored_energy -= value
CL = getCloneLoss()
if (CL > 0)
if (radiation)
value = min(CL, radiation, 2*HF*LIFETICK_INTERVAL_LESS)
adjustCloneLoss(value/-2.5)//Genetic damage, should diona ever suffer it, heals much more slowly.
radiation -= value//Most likely the only time they'll cloneloss is escaping from being partially devoured
CL = getCloneLoss()
value = min(CL, DS.stored_energy, 1*HF*LIFETICK_INTERVAL_LESS)
adjustCloneLoss(value/-5)
DS.stored_energy -= value
var/mob/living/carbon/human/H
if (src.is_diona() == DIONA_WORKER)
H = src
else
updatehealth()
return//If its a nymph then it doesnt go farther than this
//Next up, healing any damage to internal organs.
//Diona really only have one critical organ, the light receptor node in the head.
//If badly damaged, the light receptor reduces the effectiveness of incoming light
//Nevertheless, we should still heal them all
if (life_tick % LIFETICK_INTERVAL_LESS == 0)
if (H.bad_internal_organs.len)
for (var/obj/item/organ/O in H.bad_internal_organs)
CL = O.damage
if (radiation)
value = min(CL, radiation, 2*HF*LIFETICK_INTERVAL_LESS)
O.damage += value/-1.5
radiation -= value
CL = getCloneLoss()
value = min(CL, DS.stored_energy, 1*HF*LIFETICK_INTERVAL_LESS)
O.damage += value/-3
DS.stored_energy -= value
//We only regenerate nymphs if the gestalt has plenty of energy to spare.
//Survival of the collective is prioritised over individual members
//And healing nymphs can suck up a lot of energy, which the gestalt may need
if (DS.stored_energy > (0.75 * DS.max_energy))
for (var/mob/living/carbon/alien/diona/D in H.bad_internal_organs)
if (!D.stat != DEAD)
D.diona_handle_regeneration(DS)
//IF a nymph inside the gestalt is damaged, we trigger its own regeneration function
//but we pass in the gestalt's Dionastats, so its energy/rads will be used to heal them
//Last up, growing brand new limbs and organs to replace those lost or removed.
if (life_tick % (LIFETICK_INTERVAL_LESS*8) == 0 && (DS.stored_energy > (0.5 * DS.max_energy)))
//We will only replace ONE organ or limb each time this procs
var/path
for (var/i in species.has_limbs)
path = species.has_limbs[i]["path"]
var/limb_exists = 0
for (var/obj/item/organ/external/diona/B in H.organs)
if (B.type == path)
limb_exists = 1
break
if (!limb_exists)//We've found a limb which is missing!
break
else
path = null
if (path)
if (DS.stored_energy < REGROW_ENERGY_REQ)
src << "<span class='danger'>You try to regrow a lost limb, but you lack the energy. Find more light!</span>"
return
if (H.nutrition < REGROW_FOOD_REQ)
src << "<span class='danger'>You try to regrow a lost limb, but you lack the biomass. Find some food!</span>"
return
DS.stored_energy -= REGROW_ENERGY_REQ
H.nutrition -= REGROW_FOOD_REQ
playsound(src, 'sound/species/diona/gestalt_grow.ogg', 30, 1)
src.visible_message("\red [src] begins to shift and quiver.",
"\red You begin to shift and quiver, feeling a stirring within your trunk ")
sleep(52)
var/obj/item/organ/O = new path(H)
src.visible_message("<span class='danger'>With a shower of sticky sap, a new mass of tendrils bursts forth from [H.name]'s trunk, forming a new [O.name]</span>","<span class='danger'>With a shower of sticky sap, a new mass of tendrils bursts forth from your trunk, forming a new [O.name]</span>")
var/datum/reagents/vessel = get_vessel(0)
var/datum/reagent/B = vessel.get_master_reagent()
B.touch_turf(get_turf(src))
H.regenerate_icons()
DS.LMS = min(2, DS.LMS)//Prevents a message about darkness in light areas
updatehealth()
return
//Now regrowing internal organs
for (var/i in species.has_organ)
path = species.has_organ[i]
var/organ_exists = 0
for (var/obj/item/organ/diona/B in H.internal_organs)
if (B.type == path)
organ_exists = 1
break
if (!organ_exists)//We've found an organ which is missing!
break
else
path = null
if (path)
if (DS.stored_energy < REGROW_ENERGY_REQ)
src << "<span class='danger'>You try to regrow a lost organ, but you lack the energy. Find more light!</span>"
if (H.nutrition < REGROW_FOOD_REQ)
src << "<span class='danger'>You try to regrow a lost organ, but you lack the biomass. Find some food!</span>"
DS.stored_energy -= REGROW_ENERGY_REQ
H.nutrition -= REGROW_FOOD_REQ
var/obj/item/organ/O = new path(H)
H.internal_organs_by_name[O.organ_tag] = O
H.internal_organs.Add(O)
src << "<span class='danger'>You feel a shifting sensation inside you as your nymphs move apart to make space, forming a new [O.name]</span>"
H.regenerate_icons()
DS.LMS = max(2, DS.LMS)//Prevents a message about darkness in light areas
updatehealth()
return
if (DS.stored_energy < REGROW_ENERGY_REQ || H.nutrition < REGROW_FOOD_REQ)
return
for (var/mob/living/carbon/alien/diona/D in H.bad_internal_organs)
if (D.stat == DEAD || D.health <= 0)
D.health = 1
D.stat = CONSCIOUS
src << "<span class='danger'>You feel a stirring within you as [D.name] returns to life!</span>"
updatehealth()
return
//Only one per proc
//If we have less than six nymphs, we add one each proc
if (H.topup_nymphs(1))
DS.stored_energy -= REGROW_ENERGY_REQ
H.nutrition -= REGROW_FOOD_REQ
src << "<span class='danger'>You feel a stirring inside you as a new nymph is born within your trunk!</span>"
updatehealth()
//MESSAGE FUNCTIONS
/mob/living/carbon/proc/diona_handle_lightmessages(var/datum/dionastats/DS)
//This function handles the RP messages that inform the diona player about their light/withering state
//Lightstates:
//1: Full. Go down from this state below 80%
//2. average: Go up a state at 100%, go down a state at 50%
//3. Subsisting: Go down from this state at 0.% light, go up from it at 40%
//4: Pain: Go up to this state when light is negative and damage < 40. Go down from when damage >60
//5: Critical:Go up to this state when damage < 100 and not paralysed. Go down from it when halloss hits 100 and you're paralysed
//6: Dying: You've collapsed from pain and are dying. theres nothing below this but death
DS.EP = DS.stored_energy / DS.max_energy
if (DS.LMS == 1)//If we're full
if (DS.EP <= 0.8)//But at <=80% energy
DS.LMS = 2
src << "<span class='warning'>The darkness makes you uncomfortable</span>"
else if (DS.LMS == 2)
if (DS.EP >= 0.99)
DS.LMS = 1
src << "You bask in the light"
else if (DS.EP <= 0.4)
DS.LMS = 3
src << "<span class='warning'>You feel lethargic as your energy drains away. Find some light soon!</span>"
else if (DS.LMS == 3)
if (DS.EP >= 0.5)
DS.LMS = 2
src << "You feel a little more energised as you return to the light. Stay awhile"
else if (DS.EP <= 0.0)
DS.LMS = 4
src << "<span class='danger'> You feel sensory distress as your tendrils start to wither in the darkness. You will die soon without light</span>"
//From here down, we immediately return to state 3 if we get any light
else
if (DS.EP > 0.0)//If there's any light at all, we can be saved
src << "At long last, light! Treasure it, savour it, hold onto it"
DS.LMS = 3
else
var/HP = diona_get_health(DS) / DS.max_health//HP = health-percentage
if (DS.LMS == 4)
if (HP < 0.6)
src << "<span class='danger'> The darkness burns. Your nymphs decay and wilt You are in mortal danger</span>"
DS.LMS = 5
else if (DS.LMS == 5)
if (paralysis > 0)
src << "<span class='danger'> Your body has reached critical integrity, it can no longer move. The end comes soon</span>"
DS.LMS = 6
else if (DS.LMS == 6)
return
/*
if (flashlight_active)
light_amount -= DS.flashlight_reduction * FLASHLIGHT_STRENGTH
if (pdalight_active)
light_amount -= DS.pdalight_reduction * PDALIGHT_STRENGTH
*/
//GETTER FUNCTIONS
/mob/living/carbon/proc/get_lightlevel_diona(var/datum/dionastats/DS)
var/light_amount = 1.5 //how much light there is in the place, affects receiving nutrition and healing
var/light_factor = 1//used for if a gestalt's response node is damaged. it will feed more slowly
if (DS.light_organ)
if (DS.light_organ.is_broken())
light_factor = 0.55
else if (DS.light_organ.is_bruised())
light_factor = 0.8
var/turf/T = get_turf(src)
var/atom/movable/lighting_overlay/L = locate(/atom/movable/lighting_overlay) in T
if(L)
//First we check if the tile has any flashlights or PDA lights
var/gathertype = 0//Simple checking of the turf
for (var/datum/light_source/LS in T.affecting_lights)
if (LS.source_atom.diona_restricted_light)
gathertype = 1//if restricted lights involved in lighting, then we need a more complex calculation.
break
if (gathertype == 0)//Simple, fast gather amount
light_amount = L.lum_r + L.lum_g + L.lum_b
else//If flashlights are involved, then we get a little more complex
var/best_restrictedlight = 0//We track any restricted lights, and only the single strongest of them to the diona
light_amount = 0
var/turf/ourturf = get_turf(src)
for (var/datum/light_source/LS in T.affecting_lights)//Cycle through the lights affecting the tile
var/n = Sum(LS.get_lum(ourturf))//Manually calculate each one's contribution. Get lum function is kind of expensive
if (LS.source_atom.diona_restricted_light)
n *= DS.restrictedlight_factor
if (n > best_restrictedlight)
best_restrictedlight = n
else
light_amount += n
light_amount += best_restrictedlight//apply only the single best of the restricted lightsources
light_amount = min(DIONA_MAX_LIGHT,light_amount) //hardcapped to DIONA_MAX_LIGHT so it's not abused by being in massively bright areas
light_amount = max(light_amount*light_factor,0)//Make sure light amount is >=0 and apply light factor
light_amount -= 1.5//Light values > 1.5 will increase energy, <1.5 will decrease it
return light_amount
/mob/living/carbon/proc/diona_get_health(var/datum/dionastats/DS)
if (DS.dionatype == 0)
return health
else
return health+(maxHealth*0.5)
/mob/living/carbon/proc/get_dionastats()
if (istype(src, /mob/living/carbon/alien/diona))
var/mob/living/carbon/alien/diona/T = src
return T.DS
if (istype(src, /mob/living/carbon/human))
var/mob/living/carbon/human/T = src
if (istype(T.species, /datum/species/diona))
return T.DS
return null
//Called on a nymph when it merges with a gestalt
//The nymph and gestalt get the combined total of both of their languages
//Note that the nymphs only have all languages while they're inside the gestalt.
/mob/living/carbon/proc/sync_languages(var/mob/living/carbon/host)
for (var/datum/language/L in languages)
if (!(L in host.languages))
host.add_language(L.name)
host << "<span class='notice'><font size=3>[src] has passed on its knowledge of the [L.name] language to you!</span>"
languages = host.languages.Copy()
//Called on a nymph when it splits off from a gestalt.
//Or on all of them if the gestalt splits into a swarm of nymphs
//The nymph has a chance to inherit each language
/mob/living/carbon/alien/diona/proc/split_languages(var/mob/living/carbon/host)
languages.Cut()
add_language(species.default_language)//They always have rootsong
for (var/datum/language/L in host.languages)
var/chance = 40
if (istype(L, /datum/language/common))//more likely to keep common
chance = 85
if (prob(chance))
add_language(L.name)
else
src << "<span class=;danger;>You have forgotten the [L.name] language!</span>"
//DIONASTATS DEFINES
//Dionastats is an instanced object that diona will each create and hold a reference to.
//It's used to store information which are relevant to both types of diona, to save on adding variables to carbon
//Most of these values are calculated from information configured at authortime in either diona_nymph.dm or diona_gestalt.dm
/datum/dionastats
var/max_energy//how much energy the diona can store. will determine how long its energy lasts in darkness
var/stored_energy//how much is currently stored
var/EP//Energy percentage.
var/trauma_factor//Multiplied with severity to determine how much damage the diona takes in darkness
var/pain_factor//Multiplied with severity to determine how much pain the diona takes in darkness
var/max_health = 100
var/healing_factor = 1.0//A multiplier that changes with body temperature
var/atom/last_location = null
var/last_lightlevel = 0
var/restrictedlight_factor = 0.8//A value between 0 and 1 that determines how much we nerf the strength of certain worn lights
//1 means flashlights work normally., 0 means they do nothing
var/obj/item/organ/diona/node/light_organ = null//The organ this gestalt uses to recieve light. This is left null for nymphs
var/obj/item/organ/diona/nutrients/nutrient_organ = null//Organ
var/LMS = 1//Lightmessage state. Switching between states gives the user a message
var/dionatype//1 = nymph, 2 = worker gestalt
/datum/dionastats/Destroy()
light_organ = null//Nulling out these references to prevent GC errors
nutrient_organ = null
..()
#undef FLASHLIGHT_STRENGTH
#undef PDALIGHT_STRENGTH
#undef DIONA_MAX_LIGHT
#undef TEMP_REGEN_STOP
#undef TEMP_REGEN_NORMAL
#undef TEMP_INCREASE_REGEN_DOUBLE
#undef LIFETICK_INTERVAL_LESS
#undef REGROW_FOOD_REQ
#undef REGROW_ENERGY_REQ
#undef diona_max_pressure
#undef diona_nutrition_factor
@@ -0,0 +1,242 @@
//This file defines variables and functions specific to diona worker gestalts, not used by nymphs
//Initialisation Section
//===========================
#define COLD_DAMAGE_LEVEL_1 0.5 //Copied from life.dm
#define COLD_DAMAGE_LEVEL_2 1.5
#define COLD_DAMAGE_LEVEL_3 3
#define NUM_NYMPHS 6
/mob/living/carbon/human
var/datum/dionastats/DS
/mob/living/carbon/human/proc/setup_gestalt()
composition_reagent = "nutriment"//Dionae are plants, so eating them doesn't give animal protein
setup_dionastats()
verbs += /mob/living/carbon/human/proc/check_light
verbs += /mob/living/carbon/human/proc/diona_split_nymph
spawn(10)
//This is delayed after a gestalt is spawned, to allow nymphs to be added to it before extras are created
//These initial nymphs are the nymph which grows into a gestalt, and any others it had inside it
//There are no initial nymphs for a newly spawned diona player
if (mind && mind.name && name && mind.name != name)
verbs += /mob/living/carbon/human/proc/gestalt_set_name
var/datum/language/L = locate(/datum/language/diona) in languages
var/newname
if (L)
newname = L.get_random_name()
else
newname = "Diona Gestalt ([rand(100,999)])"
real_name = newname
name = newname
src << "<span class=notice>We are named [real_name] for now, but we can choose a new name for our gestalt. (Check the Abilities Tab)</span>"
//This allows a gestalt to rename itself -once- upon reforming
verbs.Remove(/mob/living/proc/devour)//Gestalts cant devour
verbs.Add(/mob/living/carbon/proc/absorb_nymph)
topup_nymphs()
/mob/living/carbon/human/proc/topup_nymphs(var/max = 6)
var/i = 0
var/added = 0
for(var/mob/living/carbon/alien/diona/D in src)
i++
D.stat = CONSCIOUS
D.sync_languages(src)
if (i < NUM_NYMPHS)
for (i;i < NUM_NYMPHS;i++)
add_nymph()
added++
if (added >= max)
return added
return added
/mob/living/carbon/human/proc/add_nymph()
var/turf/T = get_turf(src)
var/mob/living/carbon/alien/diona/M = new /mob/living/carbon/alien/diona(T)
M.gestalt = src
M.stat = CONSCIOUS
M.update_verbs()
spawn(1)
M.forceMove(src)
//Environmental Functions
//================================
//This function is called when a gestalt is cold enough to take damage from icy temperatures
//It will also deal damage to contained nymphs, making them much less likely to survive and split if the diona dies of cold
//Damage is slightly randomised for each nymph, some will live longer than others, but in the long run all will die eventually
//Nymphs are slightly insulated from the cold within a gestalt. The temperature to hurt a nymph is 40k lower than what hurts the gestalt
/mob/living/carbon/human/proc/diona_contained_cold_damage()
if (bodytemperature < (species.cold_level_1-40))
var/damage
if(bodytemperature > (species.cold_level_2-40))
damage = COLD_DAMAGE_LEVEL_1
else if(bodytemperature > (species.cold_level_3-40))
damage = COLD_DAMAGE_LEVEL_2
else
damage = COLD_DAMAGE_LEVEL_3
for (var/mob/living/carbon/alien/diona/D in src)
D.adjustFireLoss(damage*(rand(30,150)/100))
D.updatehealth()
//This is called when a gestalt is hit by an explosion. Nymphs will take damage too
//Damage to nymphs depends on the severity of the blast, and on explosive-resistant armour worn by the gestalt
//A severity 1 explosion without armour will usually kill all nymphs in the gestalt
//Damage is randomised for each nymph, often some will survive and others wont
//Nymphs have 100 health, so without armour there is a small possibility for each nymph to survive a severity 1 blast
/mob/living/carbon/human/proc/diona_contained_explosion_damage(var/severity)
var/damage = 0
var/damage_factor = 0.1 //Safety value
if (severity)
damage_factor = (1 / severity)
var/armorval = getarmor(null, "bomb")
if (armorval)
damage_factor *= (1 - (armorval * 0.01))
if (damage_factor > 0)
for(var/mob/living/carbon/alien/diona/D in src)
damage = (rand(95,200)*damage_factor)
D.adjustBruteLoss(damage)
D.updatehealth()
/mob/living/carbon/human/proc/check_light()
set category = "Abilities"
set name = "Check light level"
usr << "The light level here is [get_lightlevel_diona(DS)]"
//1.5 is the maximum energy that can be lost per proc
//2.1 is the approximate delay between procs
/mob/living/carbon/human/proc/setup_dionastats()
//Diona time variables, these differ slightly between a gestalt and a nymph. All values are times in seconds
var/energy_duration = 120//How long this diona can exist in total darkness before its energy runs out
var/dark_consciousness = 120//How long this diona can stay on its feet and keep moving in darkness after energy is gone.
var/dark_survival = 180//How long this diona can survive in darkness after energy is gone, before it dies
var/MLS = (1.5 / 2.1)//Maximum (energy) lost per second, in total darkness
DS = new/datum/dionastats()
DS.max_energy = energy_duration * MLS
DS.max_health = maxHealth*2
DS.stored_energy = DS.max_energy
DS.pain_factor = (100 / dark_consciousness) / MLS
DS.trauma_factor = (DS.max_health / dark_survival) / MLS
DS.dionatype = 2//Gestalt
for (var/organ in internal_organs)
if (istype(organ, /obj/item/organ/diona/node))
DS.light_organ = organ
if (istype(organ, /obj/item/organ/diona/nutrients))
DS.nutrient_organ = organ
//Splitting functions
//====================
/mob/living/carbon/human/proc/diona_split_nymph()
set name = "Split"
set desc = "Split your humanoid form into its constituent nymphs."
set category = "Abilities"
var/response = alert(src, "Are you sure you want to split? This will break your gestalt into many smaller nymphs, but you will only control one.","Confirm Split","Split","Not now")
if(response != "Split") return
diona_split_into_nymphs(DS)
//This function allows a reformed gestalt to set its name, once only
/mob/living/carbon/human/proc/gestalt_set_name()
set name = "Set Gestalt Name"
set desc = "Choose a name for your new collective."
set category = "Abilities"
var/newname
var/suggestion = ""
var/textbox = ""
var/datum/language/L = locate(/datum/language/diona) in languages
if (L)
suggestion = L.get_random_name()
textbox = "What shall we name our new collective? Type in a name, or leave blank to cancel. We recall that we were once part of a collective named [mind.name] but it is not necessary to return to that"
newname = input(src,textbox,"Choosing a name.",suggestion)
if (newname)
real_name = newname
name = newname
mind.name = newname
src << "<span class=notice>Our collective shall now be known as [real_name] !</span>"
verbs.Remove(/mob/living/carbon/human/proc/gestalt_set_name)
/mob/living/carbon/human/proc/diona_split_into_nymphs(var/datum/dionastats/DS)
var/turf/T = get_turf(src)
var/mob/living/carbon/alien/diona/bestNymph = null
var/bestHealth = 0
//We iterate through all the nymphs and find which one is healthiest and not controlled
//The gestalt's player will control that nymph
//Start the splitting sound
playsound(src.loc, 'sound/species/diona/gestalt_split.ogg', 100, 1)
sleep(20)
for(var/mob/living/carbon/alien/diona/D in src)
if ((!D.key) && bestNymph == null)
//As a safety, we choose the first unkeyed one to begin with, even if its dead.
//We'll replace this choice when/if we find a better one
bestNymph = D
D.forceMove(T)
D.split_languages(src)
D.set_dir(pick(NORTH, SOUTH, EAST, WEST))
D.gestalt = null
if (D.stat != DEAD && D.health > (D.maxHealth*0.1))//If a nymph is alive and has enough health, it will emerge from the gestalt
D.stat = CONSCIOUS
D.stunned = 0
D.update_verbs()
if ((!D.key) && D.health > bestHealth)
bestHealth = D.health
bestNymph = D
else //If a nymph is too heavily damaged, it cannot survive and will be born dead
D.visible_message("[D] is too damaged to survive outside a gestalt, and expires with a pitiful chirrup", "You are too damaged to survive outside of your gestalt!", "You hear a pitiful chirrup!")
D.stat = DEAD
//D.tumble(2)//So they're not all dumped on one tile
for(var/obj/item/W in src)
drop_from_inventory(W)
if (bestNymph)
bestNymph.set_dir(dir)
transfer_languages(src, bestNymph)
if(mind)
mind.transfer_to(bestNymph)
bestNymph.stunned = 0//Switching mind seems to temporarily stun mobs
message_admins("\The [src] has split into nymphs; player now controls [key_name_admin(bestNymph)]")
log_admin("\The [src] has split into nymphs; player now controls [key_name(bestNymph)]")
//If bestNymph is still null at this point, it could only mean every nymph in the gestalt was a player
//In this unfathomably rare case, the gestalt player simply dies as its mob is qdel'd.
//We will generally prevent this from happening by ensuring any nymph-joining functions leave one free for the host
visible_message("<span class='warning'>\The [src] quivers slightly, then splits apart with a wet slithering noise.</span>")
qdel(src)
#undef COLD_DAMAGE_LEVEL_1
#undef COLD_DAMAGE_LEVEL_2
#undef COLD_DAMAGE_LEVEL_3
@@ -7,8 +7,11 @@
var/list/hud_list[10]
var/embedded_flag //To check if we've need to roll for damage on movement while an item is imbedded in us.
mob_size = 9//Based on average weight of a human
/mob/living/carbon/human/New(var/new_loc, var/new_species = null)
eat_types |= TYPE_ORGANIC//Any mobs that are given the devour verb, can eat nonhumanoid organics. Only applies to unathi for now
if(!dna)
dna = new /datum/dna(null)
@@ -48,6 +51,8 @@
human_mob_list -= src
for(var/organ in organs)
qdel(organ)
if (DS)
qdel(DS)//prevents the dionastats holding onto references and blocking GC
return ..()
/mob/living/carbon/human/Stat()
@@ -93,6 +98,10 @@
var/shielded = 0
var/b_loss = null
var/f_loss = null
if (is_diona() == DIONA_WORKER)//Thi
diona_contained_explosion_damage(severity)
switch (severity)
if (1.0)
b_loss += 500
@@ -1161,6 +1170,9 @@
qdel(hud_used)
hud_used = new /datum/hud(src)
if (src.is_diona())
setup_gestalt(1)
if(species)
return 1
else
@@ -76,6 +76,9 @@
var/last_dam = -1 //Used for determining if we need to process all organs or just some or even none.
var/list/bad_external_organs = list()// organs we check until they are good.
var/list/bad_internal_organs = list()//A list of internal organs which are damaged.
//This isnt used for regular processing, since all internal organs are regularly processed anyway, but it can be used as a shortlist for calls that only care about damaged organs
var/xylophone = 0 //For the spoooooooky xylophone cooldown
var/mob/remoteview_target = null
@@ -220,43 +220,9 @@
src << "\green You said: \"[msg]\" to [M]"
return
/mob/living/carbon/human/proc/diona_split_nymph()
set name = "Split"
set desc = "Split your humanoid form into its constituent nymphs."
set category = "Abilities"
diona_split_into_nymphs(5) // Separate proc to void argments being supplied when used as a verb
/mob/living/carbon/human/proc/diona_split_into_nymphs(var/number_of_resulting_nymphs)
var/turf/T = get_turf(src)
var/mob/living/carbon/alien/diona/S = new(T)
S.set_dir(dir)
transfer_languages(src, S)
if(mind)
mind.transfer_to(S)
message_admins("\The [src] has split into nymphs; player now controls [key_name_admin(S)]")
log_admin("\The [src] has split into nymphs; player now controls [key_name(S)]")
var/nymphs = 1
for(var/mob/living/carbon/alien/diona/D in src)
nymphs++
D.forceMove(T)
transfer_languages(src, D, WHITELISTED|RESTRICTED)
D.set_dir(pick(NORTH, SOUTH, EAST, WEST))
if(nymphs < number_of_resulting_nymphs)
for(var/i in nymphs to (number_of_resulting_nymphs - 1))
var/mob/M = new /mob/living/carbon/alien/diona(T)
transfer_languages(src, M, WHITELISTED|RESTRICTED)
M.set_dir(pick(NORTH, SOUTH, EAST, WEST))
for(var/obj/item/W in src)
drop_from_inventory(W)
visible_message("<span class='warning'>\The [src] quivers slightly, then splits apart with a wet slithering noise.</span>")
qdel(src)
/mob/living/carbon/human/proc/bugbite()
set category = "Abilities"
@@ -25,15 +25,27 @@
h_style = "blue IPC screen"
..(new_loc, "Machine")
/mob/living/carbon/human/monkey/
mob_size = 2.6//Based on howler monkey, rough real world equivilant to on-mob sprite size
/mob/living/carbon/human/monkey/New(var/new_loc)
..(new_loc, "Monkey")
/mob/living/carbon/human/farwa
mob_size = 2.6//Roughly the same size as monkey
/mob/living/carbon/human/farwa/New(var/new_loc)
..(new_loc, "Farwa")
/mob/living/carbon/human/neaera
mob_size = 2.6//Roughly the same size as monkey
/mob/living/carbon/human/neaera/New(var/new_loc)
..(new_loc, "Neaera")
/mob/living/carbon/human/stok
mob_size = 2.6//Roughly the same size as monkey
/mob/living/carbon/human/stok/New(var/new_loc)
..(new_loc, "Stok")
+60 -109
View File
@@ -92,7 +92,7 @@
handle_random_events()
//stuff in the stomach
handle_stomach()
handle_stomach()//This function is in devour.dm
handle_shock()
@@ -102,6 +102,9 @@
handle_heartbeat()
if (is_diona())
diona_handle_light(DS)
if(!client)
species.handle_npc(src)
@@ -280,55 +283,47 @@
radiation = Clamp(radiation,0,100)
if (radiation)
var/obj/item/organ/diona/nutrients/rad_organ = locate() in internal_organs
if(rad_organ && !rad_organ.is_broken())
var/rads = radiation/25
radiation -= rads
nutrition += rads
adjustBruteLoss(-(rads))
adjustFireLoss(-(rads))
adjustOxyLoss(-(rads))
adjustToxLoss(-(rads))
updatehealth()
return
var/damage = 0
radiation -= 1 * RADIATION_SPEED_COEFFICIENT
if(prob(25))
damage = 1
if (radiation > 50)
damage = 1
//var/obj/item/organ/diona/nutrients/rad_organ = locate() in internal_organs
if(src.is_diona())
diona_handle_regeneration(get_dionastats())
else
var/damage = 0
radiation -= 1 * RADIATION_SPEED_COEFFICIENT
if(prob(5) && prob(100 * RADIATION_SPEED_COEFFICIENT))
radiation -= 5 * RADIATION_SPEED_COEFFICIENT
src << "<span class='warning'>You feel weak.</span>"
Weaken(3)
if(!lying)
emote("collapse")
if(prob(5) && prob(100 * RADIATION_SPEED_COEFFICIENT) && species.name == "Human") //apes go bald
if((h_style != "Bald" || f_style != "Shaved" ))
src << "<span class='warning'>Your hair falls out.</span>"
h_style = "Bald"
f_style = "Shaved"
update_hair()
if(prob(25))
damage = 1
if (radiation > 75)
radiation -= 1 * RADIATION_SPEED_COEFFICIENT
damage = 3
if(prob(5))
take_overall_damage(0, 5 * RADIATION_SPEED_COEFFICIENT, used_weapon = "Radiation Burns")
if(prob(1))
src << "<span class='warning'>You feel strange!</span>"
adjustCloneLoss(5 * RADIATION_SPEED_COEFFICIENT)
emote("gasp")
if (radiation > 50)
damage = 1
radiation -= 1 * RADIATION_SPEED_COEFFICIENT
if(prob(5) && prob(100 * RADIATION_SPEED_COEFFICIENT))
radiation -= 5 * RADIATION_SPEED_COEFFICIENT
src << "<span class='warning'>You feel weak.</span>"
Weaken(3)
if(!lying)
emote("collapse")
if(prob(5) && prob(100 * RADIATION_SPEED_COEFFICIENT) && species.name == "Human") //apes go bald
if((h_style != "Bald" || f_style != "Shaved" ))
src << "<span class='warning'>Your hair falls out.</span>"
h_style = "Bald"
f_style = "Shaved"
update_hair()
if(damage)
adjustToxLoss(damage * RADIATION_SPEED_COEFFICIENT)
updatehealth()
if(organs.len)
var/obj/item/organ/external/O = pick(organs)
if(istype(O)) O.add_autopsy_data("Radiation Poisoning", damage)
if (radiation > 75)
radiation -= 1 * RADIATION_SPEED_COEFFICIENT
damage = 3
if(prob(5))
take_overall_damage(0, 5 * RADIATION_SPEED_COEFFICIENT, used_weapon = "Radiation Burns")
if(prob(1))
src << "<span class='warning'>You feel strange!</span>"
adjustCloneLoss(5 * RADIATION_SPEED_COEFFICIENT)
emote("gasp")
if(damage)
adjustToxLoss(damage * RADIATION_SPEED_COEFFICIENT)
updatehealth()
if(organs.len)
var/obj/item/organ/external/O = pick(organs)
if(istype(O)) O.add_autopsy_data("Radiation Poisoning", damage)
/** breathing **/
@@ -635,6 +630,9 @@
var/pressure = environment.return_pressure()
var/adjusted_pressure = calculate_affecting_pressure(pressure)
if (is_diona())
diona_handle_air(get_dionastats(), pressure)
//Check for contaminants before anything else because we don't want to skip it.
for(var/g in environment.gas)
if(gas_data.flags[g] & XGM_GAS_CONTAMINANT && environment.gas[g] > gas_data.overlay_limit[g] + 1)
@@ -690,17 +688,22 @@
fire_alert = max(fire_alert, 1)
if(status_flags & GODMODE) return 1 //godmode
if(!istype(loc, /obj/machinery/atmospherics/unary/cryo_cell))
if (is_diona() == DIONA_WORKER)
diona_contained_cold_damage()
if(!istype(loc, /obj/machinery/atmospherics/unary/cryo_cell) || is_diona())
if(bodytemperature > species.cold_level_2)
take_overall_damage(burn=COLD_DAMAGE_LEVEL_1, used_weapon = "High Body Temperature")
take_overall_damage(burn=COLD_DAMAGE_LEVEL_1, used_weapon = "Low Body Temperature")
fire_alert = max(fire_alert, 1)
else if(bodytemperature > species.cold_level_3)
take_overall_damage(burn=COLD_DAMAGE_LEVEL_2, used_weapon = "High Body Temperature")
take_overall_damage(burn=COLD_DAMAGE_LEVEL_2, used_weapon = "Low Body Temperature")
fire_alert = max(fire_alert, 1)
else
take_overall_damage(burn=COLD_DAMAGE_LEVEL_3, used_weapon = "High Body Temperature")
take_overall_damage(burn=COLD_DAMAGE_LEVEL_3, used_weapon = "Low Body Temperature")
fire_alert = max(fire_alert, 1)
// Account for massive pressure differences. Done by Polymorph
// Made it possible to actually have something that can protect against high pressure... Done by Errorage. Polymorph now has an axe sticking from his head for his previous hardcoded nonsense!
if(status_flags & GODMODE) return 1 //godmode
@@ -724,6 +727,9 @@
else
pressure_alert = -1
if (is_diona())
diona_handle_temperature(DS)
return
proc/adjust_body_temperature(current, loc_temp, boost)
@@ -918,43 +924,6 @@
if(status_flags & GODMODE) return 0 //godmode
var/obj/item/organ/diona/node/light_organ = locate() in internal_organs
if(light_organ && !light_organ.is_broken())
var/light_amount = 0 //how much light there is in the place, affects receiving nutrition and healing
if(isturf(loc)) //else, there's considered to be no light
var/turf/T = loc
var/atom/movable/lighting_overlay/L = locate(/atom/movable/lighting_overlay) in T
if(L)
light_amount = min(10,L.lum_r + L.lum_g + L.lum_b) - 2 //hardcapped so it's not abused by having a ton of flashlights
else
light_amount = 1
nutrition += light_amount
traumatic_shock -= light_amount
if(species.flags & IS_PLANT)
if(nutrition > 450)
nutrition = 450
if(light_amount >= 3) //if there's enough light, heal
adjustBruteLoss(-(round(light_amount/2)))
adjustFireLoss(-(round(light_amount/2)))
adjustToxLoss(-(light_amount))
adjustOxyLoss(-(light_amount))
//TODO: heal wounds, heal broken limbs.
if(species.light_dam)
var/light_amount = 0
if(isturf(loc))
var/turf/T = loc
var/atom/movable/lighting_overlay/L = locate(/atom/movable/lighting_overlay) in T
if(L)
light_amount = L.lum_r + L.lum_g + L.lum_b //hardcapped so it's not abused by having a ton of flashlights
else
light_amount = 10
if(light_amount > species.light_dam) //if there's enough light, start dying
take_overall_damage(1,1)
else //heal in the dark
heal_overall_damage(1,1)
// nutrition decrease
if (nutrition > 0 && stat != 2)
nutrition = max (0, nutrition - HUNGER_FACTOR)
@@ -966,10 +935,7 @@
if(overeatduration > 1)
overeatduration -= 2 //doubled the unfat rate
if(species.flags & IS_PLANT && (!light_organ || light_organ.is_broken()))
if(nutrition < 200)
take_overall_damage(2,0)
traumatic_shock++
if(!(species.flags & IS_SYNTHETIC)) handle_trace_chems()
@@ -1319,7 +1285,7 @@
if(2) healths.icon_state = "health7"
else
//switch(health - halloss)
switch(100 - ((species && species.flags & NO_PAIN & !IS_SYNTHETIC) ? 0 : traumatic_shock))
switch(health - traumatic_shock)
if(100 to INFINITY) healths.icon_state = "health0"
if(80 to 100) healths.icon_state = "health1"
if(60 to 80) healths.icon_state = "health2"
@@ -1484,22 +1450,7 @@
if(L && L.lum_r + L.lum_g + L.lum_b == 0)
playsound_local(src,pick(scarySounds),50, 1, -1)
proc/handle_stomach()
spawn(0)
for(var/mob/living/M in stomach_contents)
if(M.loc != src)
stomach_contents.Remove(M)
continue
if(istype(M, /mob/living/carbon) && stat != 2)
if(M.stat == 2)
M.death(1)
stomach_contents.Remove(M)
qdel(M)
continue
if(air_master.current_cycle%3==1)
if(!(M.status_flags & GODMODE))
M.adjustBruteLoss(5)
nutrition += 10
proc/handle_changeling()
if(mind && mind.changeling)
@@ -42,6 +42,11 @@
heat_level_2 = 480 //Default 400
heat_level_3 = 1100 //Default 1000
inherent_verbs = list(
/mob/living/proc/devour
)
flags = CAN_JOIN | IS_WHITELISTED | HAS_LIPS | HAS_UNDERWEAR | HAS_SKIN_COLOR | HAS_EYE_COLOR
flesh_color = "#34AF10"
@@ -146,7 +151,8 @@
name_plural = "Dionaea"
icobase = 'icons/mob/human_races/r_diona.dmi'
deform = 'icons/mob/human_races/r_def_plant.dmi'
language = "Rootspeak"
language = "Galactic Common"
default_language = "Rootsong"
unarmed_types = list(/datum/unarmed_attack/stomp, /datum/unarmed_attack/kick, /datum/unarmed_attack/diona)
//primitive_form = "Nymph"
slowdown = 7
@@ -189,26 +195,24 @@
"r_foot" = list("path" = /obj/item/organ/external/diona/foot/right)
)
inherent_verbs = list(
/mob/living/carbon/human/proc/diona_split_nymph
)
//inherent_verbs = list()
warning_low_pressure = 50
hazard_low_pressure = -1
cold_level_1 = 50
cold_level_2 = -1
cold_level_3 = -1
cold_level_1 = 273
cold_level_2 = 223
cold_level_3 = 173
heat_level_1 = 2000
heat_level_2 = 3000
heat_level_3 = 4000
heat_level_1 = 420 //Default 360 - Higher is better
heat_level_2 = 480 //Default 400
heat_level_3 = 1100 //Default 1000
body_temperature = T0C + 15 //make the plant people have a bit lower body temperature, why not
flags = CAN_JOIN | IS_WHITELISTED | NO_BREATHE | NO_SCAN | IS_PLANT | NO_BLOOD | NO_PAIN | NO_SLIP | REGENERATES_LIMBS
flags = CAN_JOIN | IS_WHITELISTED | NO_BREATHE | NO_SCAN | IS_PLANT | NO_BLOOD | NO_PAIN | NO_SLIP
blood_color = "#004400"
blood_color = "#97dd7c"
flesh_color = "#907E4A"
reagent_tag = IS_DIONA
@@ -219,6 +223,10 @@
return 1
return 0
/datum/species/diona/get_random_name(var/gender)
var/datum/language/species_language = all_languages[default_language]
return species_language.get_random_name()
/datum/species/diona/equip_survival_gear(var/mob/living/carbon/human/H)
if(H.backbag == 1)
H.equip_to_slot_or_del(new /obj/item/device/flashlight/flare(H), slot_r_hand)
@@ -5,7 +5,8 @@
pass_flags = PASSTABLE
var/is_adult = 0
speak_emote = list("chirps")
mob_size = 4
composition_reagent = "slimejelly"
layer = 5
maxHealth = 150
health = 150
@@ -66,6 +67,7 @@
src.colour = colour
number = rand(1, 1000)
name = "[colour] [is_adult ? "adult" : "baby"] slime ([number])"
if (is_adult)mob_size = 6
real_name = name
slime_mutation = mutation_table(colour)
mutation_chance = rand(25, 35)
@@ -117,6 +117,7 @@
if(!is_adult)
if(amount_grown >= 10)
is_adult = 1
mob_size = 6//Adult slimes are bigger
maxHealth = 200
amount_grown = 0
regenerate_icons()
+2 -2
View File
@@ -4,8 +4,8 @@
// proc to find out in how much pain the mob is at the moment
/mob/living/carbon/proc/updateshock()
if (species && (species.flags & NO_PAIN))
src.traumatic_shock = 0
return 0
src.traumatic_shock = src.halloss //Nopain species only recieve halloss from specific mechanics
return src.traumatic_shock
src.traumatic_shock = \
1 * src.getOxyLoss() + \
+1 -1
View File
@@ -50,7 +50,7 @@
if(PARALYZE)
Paralyse(effect/(blocked+1))
if(AGONY)
halloss += effect // Useful for objects that cause "subdual" damage. PAIN!
adjustHalLoss(effect) //Changed this to use the wrapper function, it shouldn't directly alter the value
if(IRRADIATE)
var/rad_protection = getarmor(null, "rad")/100
radiation += max((1-rad_protection)*effect/(blocked+1),0)//Rads auto check armor
+326
View File
@@ -0,0 +1,326 @@
//This file contains variables and helper functions for mobs that can eat other mobs
//There are two ways to eat a mob:
//Swallowing whole can only be done if the mob is sufficiently small
//It will place the mob inside you, and slowly digest it,
//Digesting deals genetic damage to the victim,
//drains blood from it,
//and adds protein to your stomach, based on the quantitys.
//Mob will be deleted from your contents when fully digested.
//Mob is fully digested when it has taken genetic damage equal to its max health. This continues past death if necessary
//Devouring eats the mob piece by piece. Taking a bite periodically
//Each bite deals genetic damage, and drains blood.
//Adds protein to your stomach based on quantities.
//Mob is fully digested when it has taken genetic damage equal to its max health. This continues past death if necessary
//Devouring is interrupted if you or the mob move away from each other, or if the eater gets disabled.
#define PPM 9//Protein per meat, used for calculating the quantity of protein in an animal
/mob/living/var/swallowed_mob = 0
//This is set true if there are any mobs in this mob's contents. they will be slowly digested.
//just used as a boolean to minimise extra processing
/mob/living/proc/attempt_devour(var/mob/living/victim, var/eat_types, var/mouth_size = null)
//This function will attempt to eat the victim,
//either by swallowing them if they're small enough, or starting to devour them otherwise
//If a mouth_size is passed in, it will be used instead of this mob's size, for determining whether the victim is small enough to swallow
//This function is the main gateway to devouring, and will have all the safety checks
if (victim == src)
src << "\red You can't eat yourself!"
return 0
//This check is exploit prevention.
//Nymphs have seperate mechanics for gaining biomass from other diona
//This check prevents the exploit of almost-devouring a nymph, and then absorbing it to gain double biomass
if (victim.is_diona() && src.is_diona())
src << "\red You can't eat other diona!"
return 0
if (!src.Adjacent(victim))
src << "\red That creature is too far away, move closer!"
return 0
if (!is_valid_for_devour(victim, eat_types))
src << "\red You can't eat that type of creature!"
return 0
if (!victim.mob_size || !src.mob_size)
src << "<span class='danger> Error, no mob size defined for [victim.type]! You have encountered a bug, report it on github </span>"
return 0
if (!mouth_size)
mouth_size = src.mob_size
if (victim.mob_size <= mouth_size)
swallow(victim, mouth_size)
else
devour_gradual(victim,mouth_size)
//This function checks against a list to see if the mob is in it.
//Any specified types are checked against exactly, using ==, not istype
//Any types ending in * will be tested with isType
/proc/mob_listed(var/mob/living/test, var/list/toCheck, var/specific = 0)
for (var/i in toCheck)
if (specific)
if (test.type == i)
return 1
else
if (istype(test, i))
return 1
return 0
/mob/living/proc/swallow(var/mob/living/victim, var/mouth_size)
//This function will move the victim inside the eater's contents.. There they will be digested over time
var/swallow_time = max(3 + (victim.mob_size * victim.mob_size) - mouth_size, 3)
src.visible_message("[src] starts swallowing [victim]","You start swallowing [victim], this will take approximately [swallow_time] seconds")
var/turf/ourloc = src.loc
var/turf/victimloc = victim.loc
if (do_mob(src, victim, swallow_time*10))
victim.loc = src
stomach_contents.Add(victim)
else if (victimloc != victim.loc)
src << "[victim] moved away, you need to keep it still. Try grabbing, stunning or killing it first."
else if (ourloc != src.loc)
src << "You moved! Can't eat if you move away from the victim"
else
src << "Swallowing failed!"//reason unknown, maybe the eater got stunned?
/mob/living/proc/devour_gradual(var/mob/living/victim, var/mouth_size)
//This function will start consuming the victim by taking bites out of them.
//Victim or attacker moving will interrupt it
//A bite will be taken every 4 seconds
var/bite_delay = 4
var/bite_size = mouth_size * 0.5
var/num_bites_needed = (victim.mob_size*victim.mob_size)/bite_size//total bites needed to eat it from full health
var/PEPB = 1/num_bites_needed//Percentage eaten per bite
var/turf/ourloc = src.loc
var/turf/victimloc = victim.loc
var/messes = 0//number of bloodstains we've placed
var/datum/reagents/vessel = victim.get_vessel(1)
if(!victim.composition_reagent_quantity)
victim.calculate_composition()
var/victim_maxhealth = victim.maxHealth//We cache this here incase we need to edit it, for example, for humans and anything else that doesn't die until negative health
//Now, incase we're resuming an earlier feeding session on the same creature
//We calculate the actual bites needed to fully eat it based on how eaten it already is
if (victim.cloneloss)
var/percentageDamaged = victim.cloneloss / victim_maxhealth
var/percentageRemaining = 1 - percentageDamaged
num_bites_needed = percentageRemaining / PEPB
var/time_needed_seconds = num_bites_needed*bite_delay//in seconds for now
var/time_needed_minutes
var/time_needed_string
if (time_needed_seconds > 60)
time_needed_minutes = round((time_needed_seconds/60))
time_needed_seconds = time_needed_seconds % 60
time_needed_string = "[time_needed_minutes] minutes and [time_needed_seconds] seconds"
else
time_needed_string = "[time_needed_seconds] seconds"
src.visible_message("<span class='danger'>[src] starts devouring [victim]</span>","<span class='danger'>You start devouring [victim], this will take approximately [time_needed_string]. You and the victim must remain still to continue, but you can interrupt feeding anytime and leave with what you've already eaten.</span>")
var/i = 0
for (i=0;i < num_bites_needed;i++)
if(do_mob(src, victim, bite_delay*10))
face_atom(victim)
victim.adjustCloneLoss(victim_maxhealth*PEPB)
victim.adjustHalLoss(victim_maxhealth*PEPB*5)//Being eaten hurts!
src.ingested.add_reagent(victim.composition_reagent, victim.composition_reagent_quantity*PEPB)
src.visible_message("<span class='danger'>[src] bites a chunk out of [victim]</span>","<span class='danger'>[bitemessage(victim)]</span>")
if (messes < victim.mob_size - 1 && prob(50))
handle_devour_mess(src, victim, vessel)
if (victim.cloneloss >= victim_maxhealth)
src.visible_message("[src] finishes devouring [victim]","You finish devouring [victim]")
handle_devour_mess(src, victim, vessel, 1)
qdel(victim)
break
else
if (victimloc != victim.loc)
src << "<span class='danger'>[victim] moved away, you need to keep it still. Try grabbing, stunning or killing it first.</span>"
else if (ourloc != src.loc)
src << "<span class='danger'>You moved! Devouring cancelled</span>"
else
src << "Devouring Cancelled"//reason unknown, maybe the eater got stunned?
break
//this function gradually digests things inside the mob's contents.
//It is called from life.dm. Any creatures that don't want to digest their contents simply don't call it
/mob/living/proc/handle_stomach()
for(var/mob/living/M in stomach_contents)
if(M.loc != src)//if something somehow escaped the stomach, then we remove it
stomach_contents.Remove(M)
continue
if(!M.composition_reagent_quantity)
M.calculate_composition()
var/digestion_power = (((mob_size * mob_size)/10) / (M.mob_size * M.mob_size))
var/digestion_time = digestion_power * 60//Number of seconds it will take to digest in total
var/DPPP = 1 / (digestion_time / 2.1)//Digestion percentage per proc
M.adjustCloneLoss(M.maxHealth*DPPP)
//Digestion power is how much of the creature we can digest per minute. Calculated as a tenth of our mob size squared, divided by the victim's mob size squared
//If the resulting value is >1, digestion will take under a minute.
src.ingested.add_reagent(M.composition_reagent, M.composition_reagent_quantity*DPPP)
if ((M.stat != DEAD) && (M.cloneloss > (M.maxHealth*0.5)))//If we've consumed half of the victim, then it dies
M.death()
M.stat = DEAD //Just in case the death function doesn't set it
src << "Your stomach feels a little more relaxed as [M] finally stops fighting"
if (M.cloneloss >= M.maxHealth)//If we've consumed all of it, then digestion is finished.
stomach_contents.Remove(M)
src << "Your stomach feels a little more empty as you finish digesting [M]"
qdel(M)
//Helpers
/proc/bitemessage(var/mob/living/victim)
return pick("You take a bite out of [victim]",
"You rip a chunk off of [victim]",
"You consume a piece of [victim]",
"You feast upon your prey",
"You chow down on [victim]",
"You gobble [victim]'s flesh")
/proc/handle_devour_mess(var/mob/user, var/mob/living/victim, var/datum/reagents/vessel, var/finish = 0)
//The maximum number of blood placements is equal to the mob size of the victim
//We will use one blood placement on each of the following, in this order
//Bloodying the victim's tile
//Bloodying the attacker, if possible
//Bloodying the attacker's tile
//After that, we will allocate the remaining blood placements to random tiles around the victim and attacker, until either all are used or victim is dead
var/datum/reagent/blood/B = vessel.reagent_list[/datum/reagent/blood]
if (!turf_hasblood(get_turf(victim)))
devour_add_blood(victim, get_turf(victim), vessel)
return 1
else if (istype(user, /mob/living/carbon/human) && !user.blood_DNA)
//if this blood isn't already in the list, add it
user.blood_DNA = list(B.data["blood_DNA"])
user.blood_color = B.data["blood_color"]
user.update_inv_gloves() //handles bloody hands overlays and updating
user.verbs += /mob/living/carbon/human/proc/bloody_doodle
return 1
else if (!turf_hasblood(get_turf(user)))
devour_add_blood(victim, get_turf(user), vessel)
return 1
if (finish)
//A bigger victim makes more gibs
if (victim.mob_size >= 3)
new /obj/effect/decal/cleanable/blood/gibs(get_turf(victim))
if (victim.mob_size >= 5)
new /obj/effect/decal/cleanable/blood/gibs(get_turf(victim))
if (victim.mob_size >= 7)
new /obj/effect/decal/cleanable/blood/gibs(get_turf(victim))
if (victim.mob_size >= 9)
new /obj/effect/decal/cleanable/blood/gibs(get_turf(victim))
return 1
return 0
/proc/devour_add_blood(var/mob/living/M, var/turf/location, var/datum/reagents/vessel)
for(var/datum/reagent/blood/source in vessel.reagent_list)
var/obj/effect/decal/cleanable/blood/B = new /obj/effect/decal/cleanable/blood(location)
// Update appearance.
if(source.data["blood_colour"])
B.basecolor = source.data["blood_colour"]
B.update_icon()
// Update blood information.
if(source.data["blood_DNA"])
B.blood_DNA = list()
if(source.data["blood_type"])
B.blood_DNA[source.data["blood_DNA"]] = source.data["blood_type"]
else
B.blood_DNA[source.data["blood_DNA"]] = "O+"
// Update virus information.
if(source.data["virus2"])
B.virus2 = virus_copylist(source.data["virus2"])
B.fluorescent = 0
B.invisibility = 0
/proc/turf_hasblood(var/turf/test)
for (var/obj/effect/decal/cleanable/blood/b in test)
return 1
return 0
/proc/is_valid_for_devour(var/mob/living/test, var/eat_types)
var/mobtypes = test.find_type()//We find a bitfield of types for the victim
//Then for each type the victim has, we test if we're allowed to eat that type.
//eat_types must contain all types that the mob has. For example we need both humanoid and synthetic to eat an IPC
if (mobtypes & TYPE_SYNTHETIC)
if (!(eat_types & TYPE_SYNTHETIC))
return 0
if (mobtypes & TYPE_HUMANOID)
if (!(eat_types & TYPE_HUMANOID))
return 0
if (mobtypes & TYPE_WIERD)
if (!(eat_types & TYPE_WIERD))
return 0
if (mobtypes & TYPE_ORGANIC)
if (!(eat_types & TYPE_ORGANIC))
return 0
//If we get here, none of the checks have failed, the mob must be valid!
return 1
/mob/living/proc/calculate_composition()
if (!composition_reagent)//if no reagent has been set, then we'll set one
var/type = find_type(src)
if (type & TYPE_SYNTHETIC)
src.composition_reagent = "iron"
else
src.composition_reagent = "protein"
//if the mob is a simple animal with a defined meat quantity
if (istype(src, /mob/living/simple_animal))
var/mob/living/simple_animal/SA = src
if (SA.meat_amount)
src.composition_reagent_quantity = SA.meat_amount*2*PPM
//The quantity of protein is based on the meat_amount, but multiplied by 2
var/size_reagent = (src.mob_size * src.mob_size) * 3//The quantity of protein is set to 3x mob size squared
if (size_reagent > src.composition_reagent_quantity)//We take the larger of the two
src.composition_reagent_quantity = size_reagent
#undef PPM
+12 -1
View File
@@ -153,7 +153,9 @@ default behaviour is:
health = 100
stat = CONSCIOUS
else
health = maxHealth - getOxyLoss() - getToxLoss() - getFireLoss() - getBruteLoss() - getCloneLoss() - halloss
health = maxHealth - getOxyLoss() - getToxLoss() - getFireLoss() - getBruteLoss() - getCloneLoss()
//Removed Halloss from here. Halloss isn't supposed to count towards death
//This proc is used for mobs which are affected by pressure to calculate the amount of pressure that actually
@@ -270,6 +272,15 @@ default behaviour is:
/mob/living/proc/adjustHalLoss(var/amount)
if(status_flags & GODMODE) return 0 //godmode
halloss = min(max(halloss + amount, 0),(maxHealth*2))
/mob/living/carbon/adjustHalLoss(var/amount, var/ignoreImmunity = 0)//An inherited version so this doesnt affect cyborgs
if(status_flags & GODMODE) return 0 //godmode
if(!ignoreImmunity)//Adjusting how hallloss works. Species with the NO_PAIN flag will suffer most of the effects of halloss, but will be immune to most conventional sources of accumulating it
if (species && species.flags & NO_PAIN)//Species with this flag will only gather halloss through species-specific mechanics, which apply it with the ignoreImmunity flag
return 0
halloss = min(max(halloss + amount, 0),(maxHealth*2))
/mob/living/proc/setHalLoss(var/amount)
+8 -2
View File
@@ -10,6 +10,9 @@
1 - halfblock
2 - fullblock
*/
#define MOB_FIRE_LIGHT_RANGE 3 //These control the intensity and range of light given off by a mob which is on fire
#define MOB_FIRE_LIGHT_POWER 2
/mob/living/proc/run_armor_check(var/def_zone = null, var/attack_flag = "melee", var/absorb_text = null, var/soften_text = null)
var/armor = getarmor(def_zone, attack_flag)
var/absorb = 0
@@ -201,14 +204,14 @@
/mob/living/proc/IgniteMob()
if(fire_stacks > 0 && !on_fire)
on_fire = 1
set_light(light_range + 3)
set_light(light_range + MOB_FIRE_LIGHT_RANGE, light_power + MOB_FIRE_LIGHT_POWER)
update_fire()
/mob/living/proc/ExtinguishMob()
if(on_fire)
on_fire = 0
fire_stacks = 0
set_light(max(0, light_range - 3))
set_light(max(0, light_range - MOB_FIRE_LIGHT_RANGE), max(0, light_power - MOB_FIRE_LIGHT_POWER))
update_fire()
/mob/living/proc/update_fire()
@@ -256,3 +259,6 @@
/mob/living/proc/reagent_permeability()
return 1
#undef MOB_FIRE_LIGHT_RANGE //These control the intensity and range of light given off by a mob which is on fire
#undef MOB_FIRE_LIGHT_POWER
@@ -40,3 +40,9 @@
var/on_fire = 0 //The "Are we on fire?" var
var/fire_stacks
var/footstep = 0
var/list/stomach_contents = list()//This is moved here from carbon defines
var/composition_reagent
var/composition_reagent_quantity
var/mouth_size = 2//how large of a creature it can swallow at once, and how big of a bite it can take out of larger things
var/eat_types = 0//This is a bitfield which must be initialised in New(). The valid values for it are in devour.dm
var/datum/reagents/metabolism/ingested = null
+28 -1
View File
@@ -21,4 +21,31 @@
src << text("\blue You are now hiding.")
else
layer = MOB_LAYER
src << text("\blue You have stopped hiding.")
src << text("\blue You have stopped hiding.")
/mob/living/proc/devour()
set category = "Abilities"
set name = "Devour Creature"
set desc = "Attempt to eat a nearby creature, swallowing it whole if small enough, or eating it piece by piece otherwise"
var/list/choices = list()
for(var/mob/living/C in view(1,src))
if((!(src.Adjacent(C)) || C == src)) continue//cant steal nymphs right out of other gestalts
if (C.is_diona() == DIONA_NYMPH)
var/mob/living/carbon/alien/diona/D = C
if (D.gestalt)
continue
choices.Add(C)
var/mob/living/L = input(src,"Which creature do you wish to consume?") in null|choices
attempt_devour(L, eat_types, mouth_size)
/*
/mob/living/verb/devourverb(var/mob/living/victim)//For situations where species inherent verbs isnt suitable
set category = "Abilities"
set name = "Devour Creature"
set desc = "Attempt to eat a nearby creature, swallowing it whole if small enough, or eating it piece by piece otherwise"
attempt_devour(victim, eat_types, mouth_size)
*/
+2 -1
View File
@@ -128,13 +128,14 @@ proc/get_radio_key_from_channel(var/channel)
return verb
/mob/living/say(var/message, var/datum/language/speaking = null, var/verb="says", var/alt_name="")
if(client)
if(client.prefs.muted & MUTE_IC)
src << "\red You cannot speak in IC (Muted)."
return
if(stat)
if(stat == 2)
if(stat == DEAD)
return say_dead(message)
return
@@ -8,6 +8,10 @@
pass_flags = 1
density = 0
mob_size = 1
mob_size = 1
var/network = "SS13"
var/obj/machinery/camera/current = null
@@ -16,7 +16,7 @@
req_access = list(access_engine, access_robotics)
integrated_light_power = 3
local_transmit = 1
mob_size = 2
mob_size = 4
small = 1
mob_bump_flag = SIMPLE_ANIMAL
@@ -7,6 +7,7 @@
icon_state = "robot"
maxHealth = 200
health = 200
mob_size = 16//robots are heavy
mob_bump_flag = ROBOT
mob_swap_flags = ROBOT|MONKEY|SLIME|SIMPLE_ANIMAL
@@ -1,6 +1,7 @@
/mob/living/silicon
gender = NEUTER
voice_name = "synthesized voice"
diona_restricted_light = 1//Light emitted by this object or creature has limited interaction with diona
var/syndicate = 0
var/const/MAIN_CHANNEL = "Main Frequency"
var/lawchannel = MAIN_CHANNEL // Default channel on which to state laws
@@ -20,6 +20,7 @@
pass_flags = PASSTABLE
universal_understand = 1
holder_type = /obj/item/weapon/holder/borer
mob_size = 1
var/used_dominate
var/chemicals = 10 // Chemicals used for reproduction and spitting neurotoxin.
@@ -23,7 +23,7 @@
minbodytemp = 223 //Below -50 Degrees Celcius
maxbodytemp = 323 //Above 50 Degrees Celcius
holder_type = /obj/item/weapon/holder/cat
mob_size = 5
mob_size = 2.5
density = 0
/mob/living/simple_animal/cat/Life()
@@ -17,7 +17,7 @@
response_harm = "stomps"
stop_automated_movement = 1
friendly = "pinches"
mob_size = 5
mob_size = 3
var/obj/item/inventory_head
var/obj/item/inventory_mask
@@ -13,13 +13,14 @@
turns_per_move = 5
see_in_dark = 6
meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat
meat_amount = 4
meat_amount = 6
mob_size = 4.5//weight based on Chanthangi goats
response_help = "pets"
response_disarm = "gently pushes aside"
response_harm = "kicks"
faction = "goat"
attacktext = "kicked"
health = 40
maxHealth = 40
melee_damage_lower = 1
melee_damage_upper = 5
var/datum/reagents/udder = null
@@ -98,13 +99,14 @@
turns_per_move = 5
see_in_dark = 6
meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat
meat_amount = 6
meat_amount = 30//Cows are huge, should be worth a lot of meat
response_help = "pets"
response_disarm = "gently pushes aside"
response_harm = "kicks"
attacktext = "kicked"
health = 50
maxHealth = 250//more robust because large
var/datum/reagents/udder = null
mob_size = 20//based on mass of holstein fresian dairy cattle, what the sprite is based on
/mob/living/simple_animal/cow/New()
udder = new(50)
@@ -164,12 +166,13 @@
response_disarm = "gently pushes aside"
response_harm = "kicks"
attacktext = "kicked"
health = 1
maxHealth = 1
var/amount_grown = 0
pass_flags = PASSTABLE | PASSGRILLE
small = 1
holder_type = /obj/item/weapon/holder/chick
density = 0
mob_size = 0.75//just a rough estimate, the real value should be way lower
/mob/living/simple_animal/chick/New()
..()
@@ -211,13 +214,14 @@ var/global/chicken_count = 0
response_disarm = "gently pushes aside"
response_harm = "kicks"
attacktext = "kicked"
health = 10
maxHealth = 10
var/eggsleft = 0
var/body_color
pass_flags = PASSTABLE
small = 1
holder_type = /obj/item/weapon/holder/chicken
density = 0
mob_size = 2
/mob/living/simple_animal/chicken/New()
..()
@@ -19,7 +19,8 @@
var/min_explode_time = 1200
density = 0
holder_type = /obj/item/weapon/holder/mushroom
mob_size = 2
/mob/living/simple_animal/mushroom/New()
..()
harvest_time = world.time
@@ -13,6 +13,8 @@
response_harm = "stomps on"
emote_see = list("jiggles", "bounces in place")
var/colour = "grey"
mob_size = 3
composition_reagent = "slimejelly"
/mob/living/simple_animal/adultslime
name = "pet slime"
@@ -28,6 +30,8 @@
response_harm = "stomps on"
emote_see = list("jiggles", "bounces in place")
var/colour = "grey"
mob_size = 6
composition_reagent = "slimejelly"
/mob/living/simple_animal/adultslime/New()
..()
@@ -15,4 +15,5 @@
harm_intent_damage = 5
melee_damage_upper = 15
melee_damage_lower = 10
attacktext = "mauled"
attacktext = "mauled"
mob_size = 2
@@ -33,6 +33,7 @@
status_flags = CANPUSH
minbodytemp = 0
heat_damage_per_tick = 20
mob_size = 10
/mob/living/simple_animal/hostile/alien/drone
@@ -72,6 +73,7 @@
projectilesound = 'sound/weapons/pierce.ogg'
rapid = 1
status_flags = 0
mob_size = 12
/mob/living/simple_animal/hostile/alien/queen/large
name = "alien empress"
@@ -82,6 +84,7 @@
move_to_delay = 4
maxHealth = 400
health = 400
mob_size = 14
/obj/item/projectile/neurotox
damage = 30
@@ -113,6 +116,7 @@
speak = list("Hruuugh!","Hrunnph")
emote_see = list("paws the ground","shakes its mane","stomps")
emote_hear = list("snuffles")
mob_size = 10
/mob/living/simple_animal/hostile/diyaab
name = "diyaab"
@@ -134,6 +138,7 @@
speak = list("Awrr?","Aowrl!","Worrl")
emote_see = list("sniffs the air cautiously","looks around")
emote_hear = list("snuffles")
mob_size = 3
/mob/living/simple_animal/hostile/shantak
name = "shantak"
@@ -154,6 +159,7 @@
speak_chance = 5
speak = list("Shuhn","Shrunnph?","Shunpf")
emote_see = list("scratches the ground","shakes out it's mane","tinkles gently")
mob_size = 5
/mob/living/simple_animal/yithian
name = "yithian"
@@ -162,6 +168,7 @@
icon_living = "yithian"
icon_dead = "yithian_dead"
icon = 'icons/jungle.dmi'
mob_size = 2
/mob/living/simple_animal/tindalos
name = "tindalos"
@@ -170,3 +177,4 @@
icon_living = "tindalos"
icon_dead = "tindalos_dead"
icon = 'icons/jungle.dmi'
mob_size = 1.5
@@ -15,6 +15,7 @@
speed = 4
maxHealth = 20
health = 20
mob_size = 2.5
harm_intent_damage = 8
melee_damage_lower = 10
@@ -22,6 +22,7 @@
health = 60
melee_damage_lower = 20
melee_damage_upper = 30
mob_size = 17
//Space bears aren't affected by atmos.
min_oxy = 0
@@ -16,6 +16,7 @@
speed = 4
maxHealth = 25
health = 25
mob_size = 10
harm_intent_damage = 8
melee_damage_lower = 15
@@ -14,6 +14,7 @@
attack_sound = 'sound/weapons/bite.ogg'
faction = "creature"
speed = 4
mob_size = 14
/mob/living/simple_animal/hostile/creature/cult
faction = "cult"
@@ -34,6 +34,7 @@
pass_flags = PASSTABLE
move_to_delay = 6
speed = 3
mob_size = 6
//nursemaids - these create webs and eggs
/mob/living/simple_animal/hostile/giant_spider/nurse
@@ -78,6 +78,7 @@
/mob/living/simple_animal/New()
..()
verbs -= /mob/verb/observe
health = maxHealth
if (mob_size)
nutrition_step = mob_size * 0.05
bite_factor = mob_size * 0.3
@@ -117,14 +118,6 @@
..()
//Health
if(stat == DEAD)
if(health > 0)
icon_state = icon_living
dead_mob_list -= src
living_mob_list += src
stat = CONSCIOUS
density = 1
return 0
if(health <= 0)
+3 -2
View File
@@ -769,12 +769,13 @@
/mob/proc/facedir(var/ndir)
if(!canface() || client.moving || world.time < client.move_delay)
if(!canface() || (client && client.moving) || (client && world.time < client.move_delay))
return 0
set_dir(ndir)
if(buckled && buckled.buckle_movable)
buckled.set_dir(ndir)
client.move_delay += movement_delay()
if (client)//Fixing a ton of runtime errors that came from checking client vars on an NPC
client.move_delay += movement_delay()
return 1
+16 -31
View File
@@ -1,8 +1,8 @@
/obj/item/weapon/grab/proc/inspect_organ(mob/living/carbon/human/H, mob/user, var/target_zone)
var/obj/item/organ/external/E = H.get_organ(target_zone)
if(!E || E.is_stump())
user << "<span class='notice'>[H] is missing that bodypart.</span>"
return
@@ -14,7 +14,7 @@
user << "<span class='warning'>You find [E.get_wounds_desc()]</span>"
else
user << "<span class='notice'>You find no visible wounds.</span>"
user << "<span class='notice'>Checking bones now...</span>"
if(!do_mob(user, H, 20))
user << "<span class='notice'>You must stand still to feel [E] for fractures.</span>"
@@ -23,7 +23,7 @@
H.custom_pain("Your [E.name] hurts where it's poked.")
else
user << "<span class='notice'>The [E.encased ? E.encased : "bones in the [E.name]"] seem to be fine.</span>"
user << "<span class='notice'>Checking skin now...</span>"
if(!do_mob(user, H, 10))
user << "<span class='notice'>You must stand still to check [H]'s skin for abnormalities.</span>"
@@ -45,7 +45,7 @@
if(state < GRAB_AGGRESSIVE)
attacker << "<span class='warning'>You require a better grab to do this.</span>"
return
var/obj/item/organ/external/organ = target.get_organ(check_zone(target_zone))
if(!organ || organ.dislocated == -1)
return
@@ -61,7 +61,7 @@
return
var/datum/unarmed_attack/attack = attacker.get_unarmed_attack(target, "eyes")
if(!attack)
return
if(state < GRAB_NECK)
@@ -78,7 +78,7 @@
attacker.attack_log += text("\[[time_stamp()]\] <font color='red'>Attacked [target.name]'s eyes using grab ([target.ckey])</font>")
target.attack_log += text("\[[time_stamp()]\] <font color='orange'>Had eyes attacked by [attacker.name]'s grab ([attacker.ckey])</font>")
msg_admin_attack("[key_name(attacker)] attacked [key_name(target)]'s eyes using a grab action.")
attack.handle_eye_attack(attacker, target)
/obj/item/weapon/grab/proc/headbut(mob/living/carbon/human/target, mob/living/carbon/human/attacker)
@@ -87,25 +87,25 @@
if(target.lying)
return
attacker.visible_message("<span class='danger'>[attacker] thrusts \his head into [target]'s skull!</span>")
var/damage = 20
var/obj/item/clothing/hat = attacker.head
if(istype(hat))
damage += hat.force * 10
var/armor = target.run_armor_check("head", "melee")
target.apply_damage(damage*rand(90, 110)/100, BRUTE, "head", armor)
attacker.apply_damage(10*rand(90, 110)/100, BRUTE, "head", attacker.run_armor_check("head", "melee"))
if(!armor && prob(damage))
target.apply_effect(20, PARALYZE)
target.visible_message("<span class='danger'>[target] has been knocked unconscious!</span>")
playsound(attacker.loc, "swing_hit", 25, 1, -1)
attacker.attack_log += text("\[[time_stamp()]\] <font color='red'>Headbutted [target.name] ([target.ckey])</font>")
target.attack_log += text("\[[time_stamp()]\] <font color='orange'>Headbutted by [attacker.name] ([attacker.ckey])</font>")
msg_admin_attack("[key_name(attacker)] has headbutted [key_name(target)]")
attacker.drop_from_inventory(src)
src.loc = null
qdel(src)
@@ -125,7 +125,7 @@
return
if(force_down)
attacker << "<span class='warning'>You are already pinning [target] to the ground.</span>"
attacker.visible_message("<span class='danger'>[attacker] starts forcing [target] to the ground!</span>")
if(do_after(attacker, 20) && target)
last_action = world.time
@@ -141,25 +141,10 @@
target.set_dir(SOUTH) //face up
/obj/item/weapon/grab/proc/devour(mob/target, mob/user)
var/mob/living/carbon/human/H = user
var/can_eat
if((FAT in user.mutations) && issmall(target))
if(istype(H) && H.species.gluttonous)
can_eat = 1
else
var/mob/living/carbon/human/H = user
if(istype(H) && H.species.gluttonous)
if(H.species.gluttonous == 2)
can_eat = 2
else if(!ishuman(target) && !issmall(target) && (target.small || iscarbon(target)))
can_eat = 1
if(can_eat)
var/mob/living/carbon/attacker = user
user.visible_message("<span class='danger'>[user] is attempting to devour [target]!</span>")
if(can_eat == 2)
if(!do_mob(user, target)||!do_after(user, 30)) return
else
if(!do_mob(user, target)||!do_after(user, 100)) return
user.visible_message("<span class='danger'>[user] devours [target]!</span>")
target.loc = user
attacker.stomach_contents.Add(target)
qdel(src)
H.attempt_devour(target, H.eat_types, H.mouth_size)
+124
View File
@@ -147,6 +147,17 @@
/mob/living/carbon/human/isMonkey()
return istype(species, /datum/species/monkey)
/mob/proc/is_diona()
//returns which type of diona we are, or zero
if (istype(src, /mob/living/carbon/human))
var/mob/living/carbon/human/T = src
if (istype(T.species, /datum/species/diona))
return DIONA_WORKER
if (istype(src, /mob/living/carbon/alien/diona))
return DIONA_NYMPH
return 0
/proc/ispAI(A)
if(istype(A, /mob/living/silicon/pai))
return 1
@@ -1003,4 +1014,117 @@ proc/is_blind(A)
P.time_of_death[which] = value
return 1
//Below here is stuff related to devouring, but which is generally helpful and thus placed here
//See Devour.dm for more info in how these are used
//Flags for the eat_types variable, a bitfield of what can or can't be eaten
//Note that any given mob can be more than one type
#define TYPE_ORGANIC 1//Almost any creature under /mob/living/carbon and most simple animals
#define TYPE_SYNTHETIC 2//Everything under /mob/living/silicon, plus IPCs, viscerators
#define TYPE_HUMANOID 4//Humans, skrell, unathi, tajara, vaurca, diona, IPC, vox
#define TYPE_WIERD 8//Slimes, constructs, demons, and other creatures of a magical or bluespace nature.
//Blacklists of mobs that can be excluded from eating by flags in the bitfield
var/list/humanoid_mobs_specific = list( /mob/living/carbon/human,
/mob/living/carbon/human/bst,
/mob/living/carbon/human/skrell,
/mob/living/carbon/human/unathi,
/mob/living/carbon/human/diona,
/mob/living/carbon/human/tajaran,
/mob/living/carbon/human/vox,
/mob/living/carbon/human/machine,
/mob/living/carbon/human/bug
)
var/list/humanoid_mobs_inclusive = list(
/mob/living/simple_animal/hostile/pirate,
/mob/living/simple_animal/hostile/russian,
/mob/living/simple_animal/hostile/syndicate
)
var/list/synthetic_mobs_specific = list(
/mob/living/carbon/human/machine,
/mob/living/simple_animal/hostile/retaliate/malf_drone,
/mob/living/simple_animal/hostile/viscerator,
/mob/living/simple_animal/spiderbot
)
var/list/synthetic_mobs_inclusive = list( /mob/living/silicon,
/mob/living/simple_animal/hostile/hivebot,
/mob/living/bot
)
var/list/wierd_mobs_specific = list(/mob/living/simple_animal/adultslime)
var/list/wierd_mobs_inclusive = list( /mob/living/simple_animal/construct,
/mob/living/simple_animal/shade,
/mob/living/simple_animal/slime,
/mob/living/simple_animal/hostile/faithless,
/mob/living/carbon/slime
)
/mob/living/proc/find_type()
//This function returns a bitfield indicating what type(s) the passed mob is.
//Synthetic and wierd are exclusive from organic. We assume it's organic if it's not either of those
//var/mob/living/test = src
var/mobtypes = 0
if (mob_listed(src, synthetic_mobs_specific,1))
mobtypes |= TYPE_SYNTHETIC
else if (mob_listed(src, synthetic_mobs_inclusive,0))
mobtypes |= TYPE_SYNTHETIC
else if (ishuman(src))
mobtypes |= TYPE_SYNTHETIC
if (mob_listed(src, wierd_mobs_specific,1))
mobtypes |= TYPE_WIERD
else if (mob_listed(src, wierd_mobs_inclusive,0))
mobtypes |= TYPE_WIERD
if (!(mobtypes & TYPE_WIERD) && !(mobtypes & TYPE_SYNTHETIC))
mobtypes |= TYPE_ORGANIC
if (mob_listed(src, humanoid_mobs_specific,1))
mobtypes |= TYPE_HUMANOID
else if (mob_listed(src, humanoid_mobs_inclusive,0))
mobtypes |= TYPE_HUMANOID
return mobtypes
//This function attempts to find the mob's blood vessel, if it has one.
//If it doesn't, and the create var is true, then it will create a new temporary one filled with blood that has fake DNA, and return that
//If no vessel and no create var, then null is returned, getting blood isnt possible.
//The fake DNA is generally useful for animals, it contains enough information to tell what kind of creature it came from
/mob/living/proc/get_vessel(var/create = 0)
//Add any other creatures which have blood, here
if(istype(src, /mob/living/carbon/human))
var/mob/living/carbon/human/H = src
return H.vessel
else if (istype(src, /mob/living/carbon/alien/diona))
var/mob/living/carbon/alien/diona/D = src
return D.vessel
else if (create)
//we make a new vessel for whatever creature we're devouring. this allows blood to come from creatures that can't normally bleed
//We create an MD5 hash of the mob's reference to use as its DNA string.
//This creates unique DNA for each creature in a consistently repeatable process
var/datum/reagents/vessel = new/datum/reagents(600)
vessel.add_reagent("blood",560)
for(var/datum/reagent/blood/B in vessel.reagent_list)
if(B.id == "blood")
B.data = list( "donor"=src,"viruses"=null,"species"=src.name,"blood_DNA"=md5("\ref[src]"),"blood_colour"= "#a10808","blood_type"=null, \
"resistances"=null,"trace_chem"=null, "virus2" = null, "antibodies" = list())
B.color = B.data["blood_colour"]
return vessel
else return null
#undef SAFE_PERP
+1 -1
View File
@@ -275,11 +275,11 @@ proc/blood_splatter(var/target,var/datum/reagent/blood/source,var/large)
var/obj/effect/decal/cleanable/blood/B
var/decal_type = /obj/effect/decal/cleanable/blood/splatter
var/turf/T = get_turf(target)
if(istype(source,/mob/living/carbon/human))
var/mob/living/carbon/human/M = source
source = M.get_blood(M.vessel)
// Are we dripping or splattering?
var/list/drips = list()
// Only a certain number of drips (or one large splatter) can be on a given turf.
+14
View File
@@ -82,6 +82,11 @@ var/list/organ_cache = list()
if(istype(loc,/obj/item/device/mmi) || istype(loc,/obj/item/bodybag/cryobag) || istype(loc,/obj/structure/closet/crate/freezer))
return
//Set/remove bad organ status, only every 10 ticks to reduce processing
//Unfortunately internal organs aren't a special subclass so this runs on every organ
if (owner && owner.life_tick % 10 == 0)
internal_set_bad()
//Process infections
if (robotic >= 2 || (owner && owner.species && (owner.species.flags & IS_PLANT)))
germ_level = 0
@@ -99,12 +104,21 @@ var/list/organ_cache = list()
if(damage >= max_damage)
die()
else if(owner.bodytemperature >= 170) //cryo stops germs from moving and doing their bad stuffs
//** Handle antibiotics and curing infections
handle_antibiotics()
handle_rejection()
handle_germ_effects()
/obj/item/organ/proc/internal_set_bad()
if(!istype(src, /obj/item/organ/external))//if the organ is an internal one
if (damage)
if (!(src in owner.bad_internal_organs))
owner.bad_internal_organs.Add(src)
else
owner.bad_internal_organs.Remove(src)
/obj/item/organ/proc/handle_germ_effects()
//** Handle the effects of infections
var/antibiotics = owner.reagents.get_reagent_amount("spaceacillin")
+17 -11
View File
@@ -22,6 +22,7 @@
amputation_point = "branch"
joint = "structural ligament"
dislocated = -1
status = ORGAN_PLANT
/obj/item/organ/external/diona/chest
name = "core trunk"
@@ -136,27 +137,39 @@
..()
if(!istype(H) || !H.organs || !H.organs.len)
H.death()
if(prob(50) && spawn_diona_nymph_from_organ(src))
qdel(src)
/obj/item/organ/diona/process()
return
if(loc != owner)
owner = null
//Set/remove bad organ status, only every 10 ticks to reduce processing
//Unfortunately internal organs aren't a special subclass so this runs on every organ
if (owner && owner.life_tick % 10 == 0)
internal_set_bad()
germ_level = 0//Diona don't get infections or organ decay
/obj/item/organ/diona/strata
name = "neural strata"
parent_organ = "chest"
organ_tag = "neural strata"
/obj/item/organ/diona/bladder
name = "gas bladder"
parent_organ = "head"
organ_tag = "gas bladder"
/obj/item/organ/diona/polyp
name = "polyp segment"
parent_organ = "groin"
organ_tag = "polyp segment"
/obj/item/organ/diona/ligament
name = "anchoring ligament"
parent_organ = "groin"
organ_tag = "anchoring ligament"
/obj/item/organ/diona
name = "diona nymph"
@@ -169,8 +182,6 @@
..()
if(!istype(H) || !H.organs || !H.organs.len)
H.death()
if(prob(50) && spawn_diona_nymph_from_organ(src))
qdel(src)
// These are different to the standard diona organs as they have a purpose in other
// species (absorbing radiation and light respectively)
@@ -181,18 +192,13 @@
icon = 'icons/mob/alien.dmi'
icon_state = "claw"
/obj/item/organ/diona/nutrients/removed()
return
/obj/item/organ/diona/node
name = "response node"
parent_organ = "head"
organ_tag = "receptor node"
organ_tag = "response node"
icon = 'icons/mob/alien.dmi'
icon_state = "claw"
/obj/item/organ/diona/node/removed()
return
//CORTICAL BORER ORGANS.
/obj/item/organ/borer
+6 -3
View File
@@ -167,6 +167,9 @@
spawn(1)
get_icon()
if ((status & ORGAN_PLANT))
cannot_break = 1
/obj/item/organ/external/replaced(var/mob/living/carbon/human/target)
owner = target
if(istype(owner))
@@ -413,7 +416,7 @@ This function completely restores a damaged organ to perfect condition.
//Determines if we even need to process this organ.
/obj/item/organ/external/proc/need_process()
if(status && status != ORGAN_ROBOT) // If it's robotic, that's fine it will have a status.
if(status && status != ORGAN_ROBOT && status != ORGAN_PLANT) // If it's robotic, that's fine it will have a status.
return 1
if(brute_dam || burn_dam)
return 1
@@ -446,7 +449,7 @@ This function completely restores a damaged organ to perfect condition.
trace_chemicals.Remove(chemID)
//Bone fractures
if(config.bones_can_break && brute_dam > min_broken_damage * config.organ_health_multiplier && !(status & ORGAN_ROBOT))
if(config.bones_can_break && !(status & ORGAN_ROBOT) && !(status & ORGAN_PLANT) && brute_dam > min_broken_damage * config.organ_health_multiplier)
src.fracture()
if(!(status & ORGAN_BROKEN))
@@ -557,7 +560,7 @@ Note that amputating the affected organ does in fact remove the infection from t
//Updating wounds. Handles wound natural I had some free spachealing, internal bleedings and infections
/obj/item/organ/external/proc/update_wounds()
if((status & ORGAN_ROBOT) || (status & ORGAN_ADV_ROBOT)) //Robotic limbs don't heal or get worse.
if((status & ORGAN_ROBOT) || (status & ORGAN_ADV_ROBOT) || (status & ORGAN_PLANT)) //Robotic limbs don't heal or get worse. Diona limbs heal using their own mechanic
return
var/updatehud
for(var/datum/wound/W in wounds)
@@ -4,18 +4,20 @@
/datum/reagents/metabolism/New(var/max = 100, mob/living/carbon/parent_mob, var/met_class)
..(max, parent_mob)
metabolism_class = met_class
if(istype(parent_mob))
parent = parent_mob
/datum/reagents/metabolism/proc/metabolize()
var/metabolism_type = 0 //non-human mobs
if(ishuman(parent))
var/mob/living/carbon/human/H = parent
metabolism_type = H.species.reagent_tag
else if (istype(parent, /mob/living/carbon/alien/diona))
metabolism_type = IS_DIONA
for(var/datum/reagent/current in reagent_list)
current.on_mob_life(parent, metabolism_type, metabolism_class)
update_total()
+2 -1
View File
@@ -49,7 +49,8 @@
removed = min(removed, volume)
max_dose = max(volume, max_dose)
dose = min(dose + removed, max_dose)
if(removed >= (metabolism * 0.1) || removed >= 0.1) // If there's too little chemical, don't affect the mob, just remove it
//Relaxed this small amount restriction a bit. it gets in the way of gradually digesting creatures
if(removed >= (metabolism * 0.01) || removed >= 0.01) // If there's too little chemical, don't affect the mob, just remove it
switch(location)
if(CHEM_BLOOD)
affect_blood(M, alien, removed)
@@ -51,7 +51,6 @@
M.nutrition += nutriment_factor * removed // For hunger and fatness
M.add_chemical_effect(CE_BLOODRESTORE, blood_factor * removed)
/datum/reagent/nutriment/protein // Bad for Skrell!
name = "animal protein"
id = "protein"
@@ -126,7 +126,7 @@
scannable = 1
/datum/reagent/cryoxadone/affect_blood(var/mob/living/carbon/M, var/alien, var/removed)
if(M.bodytemperature < 170)
if(M.bodytemperature < 170 && alien != IS_DIONA)
M.adjustCloneLoss(-10 * removed)
M.adjustOxyLoss(-10 * removed)
M.heal_organ_damage(10 * removed, 10 * removed)
@@ -142,7 +142,7 @@
scannable = 1
/datum/reagent/clonexadone/affect_blood(var/mob/living/carbon/M, var/alien, var/removed)
if(M.bodytemperature < 170)
if(M.bodytemperature < 170 && alien != IS_DIONA)
M.adjustCloneLoss(-30 * removed)
M.adjustOxyLoss(-30 * removed)
M.heal_organ_damage(30 * removed, 30 * removed)
@@ -167,6 +167,7 @@
reagent_state = LIQUID
color = "#49002E"
strength = 4
metabolism = REM * 0.35
/datum/reagent/toxin/plantbgone/touch_turf(var/turf/T)
if(istype(T, /turf/simulated/wall))
+1 -1
View File
@@ -238,7 +238,7 @@
env.merge(removed)
for(var/mob/living/carbon/human/l in view(src, min(7, round(sqrt(power/6))))) // If they can see it without mesons on. Bad on them.
if(!istype(l.glasses, /obj/item/clothing/glasses/meson))
if(!istype(l.glasses, /obj/item/clothing/glasses/meson) && !l.is_diona())
l.hallucination = max(0, min(200, l.hallucination + power * config_hallucination_power * sqrt( 1 / max(1,get_dist(l, src)) ) ) )
//adjusted range so that a power of 170 (pretty high) results in 9 tiles, roughly the distance from the core to the engine monitoring room.