mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-08-21 03:51:49 +01:00
Depends on #21458. Ports https://github.com/cmss13-devs/cmss13/pull/4229, with the original authors as: - https://github.com/tgstation/TerraGov-Marine-Corps/pull/1964 for the lighting controller (A-lexa) - https://github.com/tgstation/TerraGov-Marine-Corps/pull/4747 and https://github.com/tgstation/TerraGov-Marine-Corps/pull/7263 for the lighting (TiviPlus) - https://github.com/tgstation/tgstation/pull/54520 for the dir lighting component - https://github.com/tgstation/tgstation/pull/75018 for the out of bounds fix in lighting - https://github.com/tgstation/TerraGov-Marine-Corps/pull/6678 for the emissives (TiviPlus) The main driving reason behind this is that current lighting consumes way too much processing power, especially for things like odysseys/away sites where a billion light sources are processing/moving at once and the game slows down to a crawl. Hopefully this improves the situation by a good margin, but we will need some testmerging to confirm that. <img width="1349" height="1349" alt="image" src="https://github.com/user-attachments/assets/1059ba2b-c0c5-495a-9c76-2d75d0c42bf2" /> <img width="1349" height="1349" alt="image" src="https://github.com/user-attachments/assets/9704b0f6-4cf6-4dfd-a6cb-5702ad07d677" /> - [x] Resolve todos - [x] Look into open space fuckery (border objects) --------- Co-authored-by: Matt Atlas <liermattia@gmail.com> Co-authored-by: JohnWildkins <john.wildkins@gmail.com>
716 lines
27 KiB
Plaintext
716 lines
27 KiB
Plaintext
//This function is for code that is shared by diona nymphs and gestalt
|
|
|
|
/// Regen rate scales down linearly from normal to this temperature, stops completely below this value.
|
|
#define TEMP_REGEN_STOP 223
|
|
/// This represents a normal body temperature.
|
|
#define TEMP_REGEN_NORMAL 288
|
|
// Health regen is increased by 100% (additive) for every increment of this value we are above normal.
|
|
#define TEMP_INCREASE_REGEN_DOUBLE 700
|
|
#define LIFETICK_INTERVAL_LESS 5
|
|
|
|
#define NYMPH_ABSORB_NUTRITION 650
|
|
#define NYMPH_ABSORB_DEAD_FACTOR 0.3
|
|
|
|
#define REGROW_FOOD_REQ 150
|
|
#define REGROW_ENERGY_REQ 60
|
|
#define LIMB_REGROW_REQUIREMENT 2500
|
|
|
|
#define LANGUAGE_POINTS_TO_LEARN 1 //The number of samples of a language required to learn it
|
|
GLOBAL_LIST_INIT(diona_banned_languages, list(
|
|
/datum/language/cult,
|
|
/datum/language/cultcommon,
|
|
/datum/language/corticalborer,
|
|
/datum/language/binary,
|
|
/datum/language/binary/drone,
|
|
/datum/language/bug,
|
|
/datum/language/ling,
|
|
/datum/language/revenant,
|
|
/datum/language/machine,
|
|
))
|
|
|
|
#define DIONA_LIGHT_COEFICIENT 0.25
|
|
/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
|
|
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.last_lightlevel = light_amount
|
|
|
|
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
|
|
if(light_amount)
|
|
adjustNutritionLoss(-light_amount) // regenerate our nutrition from light.
|
|
|
|
/mob/living/carbon/alien/diona/death()
|
|
if(client && gestalt && switch_to_gestalt())
|
|
to_chat(gestalt, SPAN_WARNING("You feel tremendous pain as you lose connection to your detached nymph, there is no way to bring it back anymore."))
|
|
..()
|
|
|
|
/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(DS.regening_organ)
|
|
return
|
|
|
|
if (total_radiation && DS.stored_energy < (DS.max_energy * 0.8)) //Radiation can provide energy in place of light
|
|
apply_radiation(-2)
|
|
DS.stored_energy += 2
|
|
|
|
|
|
apply_radiation(-0.5) //Radiation is gradually wasted if its not used for something
|
|
if(total_radiation < 0)
|
|
total_radiation = null
|
|
|
|
#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 0
|
|
|
|
if (consume_nutrition_from_air && (nutrition / max_nutrition > 0.25))
|
|
to_chat(src, SPAN_NOTICE("You feel like you have replanished enough of nutrition to stay alive. Consuming more makes you feel gross."))
|
|
consume_nutrition_from_air = !consume_nutrition_from_air
|
|
return
|
|
|
|
var/plus= (min(pressure,diona_max_pressure) / diona_max_pressure)* diona_nutrition_factor
|
|
plus = min(plus, max_nutrition - nutrition)
|
|
|
|
adjustNutritionLoss(-plus)
|
|
|
|
return plus*7 //The return value is the number of moles to remove from the local environment
|
|
|
|
/// 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 * species.pain_mod) * DS.pain_factor) // pain mod here is to give a bit of padding to the amount of pain diona get, to make it less overbearing
|
|
DS.stored_energy = 0 //We reset the energy back to zero after calculating the damage. dont want it to go negative
|
|
|
|
/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 && !total_radiation)) //we need energy or total_radiation to heal
|
|
return FALSE
|
|
|
|
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 (total_radiation > 0)
|
|
value = min(CL, total_radiation, 2*HF)
|
|
adjustHalLoss(value*-3,1) //Halloss heals more quickly
|
|
apply_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 < (species ? species.total_health : 200))
|
|
CL = getBruteLoss()
|
|
|
|
if (CL > 0)
|
|
if (total_radiation > 0)
|
|
value = min(CL, total_radiation, 2*HF)
|
|
adjustBruteLoss(value*-1)
|
|
apply_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 (total_radiation > 0)
|
|
value = min(CL, total_radiation, 2*HF)
|
|
adjustFireLoss(value*-1)
|
|
apply_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 (total_radiation > 0)
|
|
value = min(CL, total_radiation, 2*HF)
|
|
stunned -= value
|
|
apply_radiation(-value)
|
|
CL = stunned
|
|
|
|
value = min(CL, DS.stored_energy, 1*HF)
|
|
stunned -= value
|
|
DS.stored_energy -= value
|
|
|
|
|
|
CL = weakened
|
|
if (CL > 0)
|
|
if (total_radiation > 0)
|
|
value = min(CL, total_radiation, 2*HF)
|
|
weakened -= value
|
|
apply_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 (total_radiation > 0)
|
|
value = min(CL, total_radiation, 2*HF*LIFETICK_INTERVAL_LESS)
|
|
adjustToxLoss(value*-1)
|
|
apply_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 (total_radiation > 0)
|
|
value = min(CL, total_radiation, 2*HF*LIFETICK_INTERVAL_LESS)
|
|
adjustCloneLoss(value/-2.5) //Genetic damage, should diona ever suffer it, heals much more slowly.
|
|
apply_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
|
|
|
|
if (src.is_diona() != DIONA_WORKER)
|
|
updatehealth()
|
|
return FALSE
|
|
|
|
/// Continuation of the Diona regen proc, but for human specific actions.
|
|
/mob/living/carbon/human/diona_handle_regeneration(var/datum/dionastats/DS, var/bypass = FALSE)
|
|
..()
|
|
|
|
// We cancel with regening organ, as it's meant to stop all other regenerative
|
|
// processes. Just pray to shit the timers don't implode.
|
|
if(DS.pause_regen)
|
|
return
|
|
|
|
if(DS.regening_organ)
|
|
diona_regen_progress(DS)
|
|
return
|
|
|
|
if (((!total_radiation && !DS.healing_factor && !DS.stored_energy) || DS.regening_organ) && !bypass)
|
|
return FALSE
|
|
|
|
//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 (bad_internal_organs.len)
|
|
for (var/obj/item/organ/O in bad_internal_organs)
|
|
var/CL = O.damage
|
|
var/value
|
|
if(total_radiation > 0)
|
|
value = min(CL, total_radiation, 2 * DS.healing_factor * LIFETICK_INTERVAL_LESS)
|
|
O.damage += value/-1.5
|
|
total_radiation -= value
|
|
CL = getCloneLoss()
|
|
|
|
value = min(CL, DS.stored_energy, 1 * DS.healing_factor * LIFETICK_INTERVAL_LESS)
|
|
O.damage += value/-3
|
|
DS.stored_energy -= value
|
|
|
|
//Last up, growing brand new limbs and organs to replace those lost or removed.
|
|
if ((life_tick % (LIFETICK_INTERVAL_LESS*8) == 0) || bypass )
|
|
//We will only replace ONE organ or limb each time this procs
|
|
|
|
var/path
|
|
for (var/i in species.has_limbs)
|
|
if(organs_by_name[i]) //Allow arm transplants + cyborg limbs
|
|
continue
|
|
path = species.has_limbs[i]["path"]
|
|
var/limb_exists = 0
|
|
for (var/obj/item/organ/external/B in 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 <= 0)
|
|
to_chat(src, SPAN_DANGER("You try to regrow a lost limb, but you lack the energy. Find more light!"))
|
|
return
|
|
if (nutrition <= 0)
|
|
to_chat(src, SPAN_DANGER("You try to regrow a lost limb, but you lack the biomass. Find some food!"))
|
|
return
|
|
DS.regen_limb_progress = 0
|
|
diona_regen_progress(DS)
|
|
|
|
visible_message(SPAN_WARNING("[src] begins to shift and quiver."),
|
|
SPAN_WARNING("You begin to shift and quiver, feeling a stirring within your trunk."))
|
|
|
|
DS.regening_organ = TRUE
|
|
to_chat(src, SPAN_NOTICE("You are trying to regrow a lost limb, this is a long and complicated process!"))
|
|
|
|
var/list/special_case = list(
|
|
/obj/item/organ/external/arm/diona = /obj/item/organ/external/hand/diona,
|
|
/obj/item/organ/external/arm/right/diona = /obj/item/organ/external/hand/right/diona,
|
|
/obj/item/organ/external/leg/diona = /obj/item/organ/external/foot/diona,
|
|
/obj/item/organ/external/leg/right/diona = /obj/item/organ/external/foot/right/diona
|
|
)
|
|
if(path in special_case)
|
|
DS.regen_extra = CALLBACK(src, PROC_REF(diona_regen_callback), special_case[path], DS)
|
|
if(!bypass)
|
|
DS.regen_limb = CALLBACK(src, PROC_REF(diona_regen_callback), path, DS)
|
|
else
|
|
diona_regen_callback(path, DS)
|
|
diona_regen_callback(special_case[path], DS)
|
|
DS.regen_extra = null
|
|
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/internal/B in 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)
|
|
to_chat(src, SPAN_DANGER("You try to regrow a lost organ, but you lack the energy. Find more light!"))
|
|
return
|
|
|
|
if (nutrition < REGROW_FOOD_REQ)
|
|
to_chat(src, SPAN_DANGER("You try to regrow a lost organ, but you lack the biomass. Find some food!"))
|
|
return
|
|
|
|
DS.stored_energy -= REGROW_ENERGY_REQ
|
|
adjustNutritionLoss(REGROW_FOOD_REQ)
|
|
var/obj/item/organ/O = new path(src)
|
|
internal_organs_by_name[O.organ_tag] = O
|
|
internal_organs.Add(O)
|
|
to_chat(src, SPAN_DANGER("You feel a shifting sensation inside you as your nymphs move apart to make space, forming a new [O.name]."))
|
|
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 || nutrition < REGROW_FOOD_REQ)
|
|
return
|
|
|
|
//If we have less than six nymphs, we add one each proc
|
|
if (topup_nymphs())
|
|
if (DS.stored_energy < REGROW_ENERGY_REQ)
|
|
return
|
|
|
|
DS.stored_energy -= REGROW_ENERGY_REQ
|
|
adjustNutritionLoss(REGROW_FOOD_REQ)
|
|
to_chat(src, SPAN_DANGER("You feel a stirring inside you as a new nymph is born within your trunk!"))
|
|
|
|
updatehealth()
|
|
|
|
/mob/living/carbon/human/proc/diona_regen_progress(var/datum/dionastats/DS)
|
|
if(!DS)
|
|
return
|
|
if(DS.regen_limb_progress > LIMB_REGROW_REQUIREMENT)
|
|
DS.regen_limb.Invoke()
|
|
DS.regen_limb = null
|
|
if(DS.regen_extra)
|
|
DS.regen_extra.Invoke()
|
|
DS.regen_extra = null
|
|
var/progress = nutrition * 0.45
|
|
adjustNutritionLoss(nutrition * 0.15)
|
|
progress += DS.stored_energy * 0.3
|
|
DS.stored_energy -= DS.stored_energy * 0.5
|
|
|
|
if(total_radiation)
|
|
progress += total_radiation * 50
|
|
apply_radiation(-total_radiation)
|
|
|
|
DS.regen_limb_progress += progress
|
|
|
|
/mob/living/carbon/human/proc/diona_regen_callback(organ_path, var/datum/dionastats/DS)
|
|
if (!organ_path || !DS)
|
|
return
|
|
|
|
var/obj/item/organ/external/our_organ = new organ_path(src)
|
|
|
|
// Check if the parent organ is still part of us. If it's not - it means it was cut off or made into a stump
|
|
// when we detached a nymph, so we must remake it.
|
|
if(our_organ.parent_organ != BP_CHEST)
|
|
// Possibility 1 - the stump is still there from when we broke off the nymph.
|
|
// Remake it into a normal limb and delete the stump.
|
|
|
|
// Possibility 2 - the stump is somehow gone. Don't do any of that.
|
|
var/obj/item/organ/external/likely_stump = organs_by_name[our_organ.parent_organ]
|
|
if(likely_stump)
|
|
if(likely_stump.is_stump())
|
|
likely_stump.removed(src, TRUE)
|
|
likely_stump.post_droplimb(src)
|
|
qdel(likely_stump)
|
|
|
|
// Possibility 3 - the parent organ is somehow there, now. We'll try not to replace it.
|
|
if(!organs_by_name[our_organ.parent_organ])
|
|
// Now that the stump has been deleted AND we know that there is no unexpected parent organ, replace it.
|
|
var/parent_organ_type = species.has_limbs[our_organ.parent_organ]["path"]
|
|
var/obj/item/organ/external/our_parent = new parent_organ_type(src)
|
|
our_parent.replaced(src)
|
|
|
|
our_organ.replaced(src)
|
|
visible_message(SPAN_WARNING("With a shower of sticky sap, a new mass of tendrils bursts forth from [src]'s trunk, forming a new [our_organ]."),
|
|
SPAN_WARNING("With a shower of sticky sap, a new mass of tendrils bursts forth from your trunk, forming a new [our_organ]."))
|
|
blood_splatter(get_turf(src), src, TRUE)
|
|
regenerate_icons()
|
|
|
|
DS.LMS = min(2, DS.LMS) //Prevents a message about darkness in light areas
|
|
DS.regening_organ = FALSE
|
|
|
|
updatehealth()
|
|
DS.regen_limb_progress = 0
|
|
playsound(src, 'sound/species/diona/gestalt_grow.ogg', 30, 1)
|
|
|
|
/**
|
|
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.
|
|
*/
|
|
/mob/living/carbon/proc/diona_handle_lightmessages(var/datum/dionastats/DS)
|
|
DS.EP = DS.stored_energy / DS.max_energy
|
|
|
|
if (DS.LMS == 1) //If we're full
|
|
if (DS.EP <= 0.8 && DS.last_lightlevel <= 0) //But at <=80% energy
|
|
DS.LMS = 2
|
|
to_chat(src, SPAN_WARNING("The darkness makes you uncomfortable..."))
|
|
|
|
else if (DS.LMS == 2)
|
|
if (DS.EP >= 0.99)
|
|
DS.LMS = 1
|
|
to_chat(src, SPAN_NOTICE("You bask in the light."))
|
|
else if (DS.EP <= 0.4 && DS.last_lightlevel <= 0)
|
|
DS.LMS = 3
|
|
to_chat(src, SPAN_WARNING("You feel lethargic as your energy drains away. Find some light!"))
|
|
|
|
else if (DS.LMS == 3)
|
|
if (DS.EP >= 0.5)
|
|
DS.LMS = 2
|
|
to_chat(src, "You feel a little more energised as you return to the light. Stay here for a while.")
|
|
else if (DS.EP <= 0.0 && DS.last_lightlevel <= 0)
|
|
DS.LMS = 4
|
|
to_chat(src, SPAN_DANGER("You feel sensory distress as your tendrils start to wither in the darkness. You will die soon without light."))
|
|
//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
|
|
to_chat(src, "At long last, light! Treasure it, savour it, hold onto it.")
|
|
DS.LMS = 3
|
|
else if(DS.last_lightlevel <= 0)
|
|
var/HP = 1 //HP = health-percentage
|
|
if (DS.LMS == 4)
|
|
if (HP < 0.6)
|
|
to_chat(src, SPAN_DANGER("The darkness burns. Your nymphs decay and wilt. You are in mortal danger!"))
|
|
DS.LMS = 5
|
|
|
|
else if (DS.LMS == 5)
|
|
if (paralysis > 0)
|
|
to_chat(src, SPAN_DANGER("Your body has reached critical integrity, it can no longer move. The end comes soon."))
|
|
DS.LMS = 6
|
|
else if (DS.LMS == 6)
|
|
return
|
|
|
|
/// Getter for the current lightlevel.
|
|
/mob/living/carbon/proc/get_lightlevel_diona(var/datum/dionastats/DS)
|
|
var/light_factor = 1.15
|
|
var/turf/T = get_turf(src)
|
|
if (is_ventcrawling)
|
|
return -1.5 //no light inside pipes
|
|
|
|
if (DS.dionatype == 2)
|
|
light_factor = 1
|
|
|
|
if (T)
|
|
var/raw = min(T.get_lumcount(0, 1) * light_factor * 5.5, 5.5)
|
|
return raw - 1.5
|
|
|
|
/// Getter for health.
|
|
/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()
|
|
return
|
|
|
|
/mob/living/carbon/alien/diona/get_dionastats()
|
|
return DS
|
|
|
|
/mob/living/carbon/human/get_dionastats()
|
|
return DS
|
|
|
|
/** 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)
|
|
to_chat(host, SPAN_NOTICE(FONT_LARGE("[src] has passed on its knowledge of the [L.name] language to you!")))
|
|
|
|
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
|
|
accent = host.accent //Get the accent of the main gestalt
|
|
|
|
for (var/datum/language/L in host.languages)
|
|
var/chance = 40
|
|
|
|
if (istype(L, /datum/language/diona))
|
|
continue
|
|
|
|
if (istype(L, /datum/language/common)) //more likely to keep common
|
|
chance = 85
|
|
|
|
|
|
if (prob(chance))
|
|
add_language(L.name)
|
|
else
|
|
to_chat(src, SPAN_DANGER("You have forgotten the [L.name] language!"))
|
|
|
|
/mob/living/carbon/alien/diona/proc/switch_to_gestalt()
|
|
set name = "Switch to Gestalt"
|
|
set desc = "Allows you to switch control back to your parent Gestalt."
|
|
set category = "Abilities"
|
|
|
|
if(!gestalt)
|
|
to_chat(src, SPAN_WARNING("You have no Gestalt!"))
|
|
else if(gestalt.stat == DEAD)
|
|
to_chat(src, SPAN_DANGER("Your Gestalt is not responding! Something might have happened to it!"))
|
|
else
|
|
gestalt.key = key
|
|
remove_verb(gestalt, /mob/living/carbon/alien/diona/proc/switch_to_gestalt)
|
|
add_verb(gestalt, /mob/living/carbon/human/proc/switch_to_nymph)
|
|
gestalt.client.init_verbs()
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/mob/living/carbon/alien/diona/proc/merge_back_to_gestalt()
|
|
set name = "Merge To Gestalt"
|
|
set desc = "Allows you to merge back to your parent Gestalt."
|
|
set category = "Abilities"
|
|
|
|
for(var/mob/living/carbon/human/H in range(1))
|
|
if(!H.is_diona())
|
|
continue
|
|
if(!Adjacent(H))
|
|
continue
|
|
var/mob/living/carbon/human/diona/C = H
|
|
if(C == gestalt)
|
|
C.nutrition += REGROW_FOOD_REQ * 0.75
|
|
C.DS.stored_energy += REGROW_ENERGY_REQ * 0.75
|
|
C.key = src.key
|
|
if(C.DS.regen_limb)
|
|
C.DS.regen_limb.Invoke()
|
|
if(C.DS.regen_extra)
|
|
C.DS.regen_extra.Invoke()
|
|
C.DS.regen_limb_progress = 0
|
|
else
|
|
C.diona_handle_regeneration(C.DS, TRUE)
|
|
to_chat(C, SPAN_NOTICE("Your lost nymph merged back."))
|
|
C.add_nymph()
|
|
remove_verb(src, /mob/living/carbon/alien/diona/proc/switch_to_gestalt)
|
|
remove_verb(C, /mob/living/carbon/human/proc/switch_to_nymph)
|
|
C.DS.nym = null
|
|
detached = FALSE
|
|
src.forceMove(C)
|
|
C.client.init_verbs()
|
|
break
|
|
|
|
//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
|
|
|
|
///Boolean, tracking whether or not an organ is currently being regenreated.
|
|
var/regening_organ = FALSE
|
|
|
|
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/list/sampled_DNA = list()
|
|
var/list/language_progress = list()
|
|
|
|
var/LMS = 1 //Lightmessage state. Switching between states gives the user a message
|
|
var/dionatype //1 = nymph, 2 = worker gestalt
|
|
var/datum/weakref/nym
|
|
var/datum/callback/regen_limb
|
|
var/datum/callback/regen_extra
|
|
var/regen_limb_progress
|
|
var/pause_regen = FALSE
|
|
|
|
/datum/dionastats/Destroy(force)
|
|
sampled_DNA = null
|
|
language_progress = null
|
|
|
|
last_location = null
|
|
regen_limb = null
|
|
regen_extra = null
|
|
. = ..()
|
|
|
|
/datum/dionastats/proc/do_blood_suck(var/mob/living/carbon/user, var/mob/living/carbon/human/H)
|
|
user.visible_message(SPAN_DANGER("[user] is trying to bite [H.name]."), SPAN_DANGER("You start biting \the [H], you both must stay still!"))
|
|
user.face_atom(get_turf(H))
|
|
if(do_mob(user, H, 40, needhand = FALSE))
|
|
var/remove_amount = H.species.blood_volume * 0.05
|
|
if(remove_amount > 0)
|
|
H.vessel.remove_reagent(/singleton/reagent/blood, remove_amount, TRUE)
|
|
user.adjustNutritionLoss(-remove_amount * 0.5)
|
|
var/list/data = REAGENT_DATA(H.vessel, /singleton/reagent/blood)
|
|
var/newDNA = data["blood_DNA"]
|
|
|
|
if(!newDNA) //Fallback. Adminspawned mobs, and possibly some others, have null dna.
|
|
newDNA = md5("[REF(H)]")
|
|
|
|
H.adjustBruteLoss(4)
|
|
user.visible_message(SPAN_NOTICE("[user] sucks some blood from \the [H].") , SPAN_NOTICE("You extract a delicious mouthful of blood from \the [H]!"))
|
|
to_chat(H, SPAN_NOTICE("You feel some liquid being injected at the bite site."))
|
|
H.reagents.add_reagent(/singleton/reagent/mortaphenyl/aphrodite, 5)
|
|
if(H.client)
|
|
INVOKE_ASYNC(src, PROC_REF(memory_transfer), user, H)
|
|
if(newDNA in sampled_DNA)
|
|
to_chat(user, SPAN_DANGER("You have already sampled the DNA of this creature before, you can learn nothing new. Move onto something else."))
|
|
return
|
|
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 H.languages)
|
|
learned = max(learned, 1)
|
|
if (!(L in user.languages) && !(L in GLOB.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
|
|
to_chat(user, SPAN_NOTICE(FONT_LARGE("You come a little closer to learning [L.name]!")))
|
|
learned = 2
|
|
|
|
if(!learned)
|
|
to_chat(user, SPAN_DANGER("This creature doesn't know any languages at all!"))
|
|
else if (learned == 1)
|
|
to_chat(user, SPAN_DANGER("We have nothing more to learn from this creature. Perhaps try a different sample?"))
|
|
|
|
update_languages(user)
|
|
else
|
|
to_chat(user, SPAN_WARNING("Something went wrong while trying to sample [H], both you and the target must remain still."))
|
|
|
|
/datum/dionastats/proc/memory_transfer(var/mob/user, var/mob/donor)
|
|
var/memory_drain = tgui_input_text(donor, "[user] just drained some of your blood, including some of your memory. Tell us something about yourself...", "Diona Memory Transfer")
|
|
if(!memory_drain || memory_drain == "")
|
|
to_chat(user, SPAN_WARNING("\The [donor] had nothing swimming around in their brain."))
|
|
else
|
|
memory_drain = capitalize(sanitize(memory_drain))
|
|
to_chat(user, SPAN_NOTICE("You gather the following knowledge from \the [donor]: [memory_drain]"))
|
|
|
|
/datum/dionastats/proc/update_languages(var/mob/user)
|
|
for(var/i in language_progress)
|
|
if(language_progress[i] >= LANGUAGE_POINTS_TO_LEARN)
|
|
user.add_language(i)
|
|
to_chat(user, SPAN_NOTICE(FONT_LARGE("You have learned a little bit more about the [i] language!")))
|
|
language_progress.Remove(i)
|
|
|
|
#undef TEMP_REGEN_STOP
|
|
#undef TEMP_REGEN_NORMAL
|
|
#undef TEMP_INCREASE_REGEN_DOUBLE
|
|
#undef LIFETICK_INTERVAL_LESS
|
|
#undef diona_max_pressure
|
|
#undef diona_nutrition_factor
|
|
#undef DIONA_LIGHT_COEFICIENT
|