Radiation Rework (#28320)

* initial changes

* Rad wave is working. now to implement the rest

* Makes the waves square

* Makes wave square

* multiplies the strength in all instances of radiation_pulse by 4 because each pulse makes one wave instead of 4 now

* Undef thing and apply suggestion

* Make radiation_pulse() not radiate the radiation source and implements contamination

* Adds contamination to uranium walls and meteors

* Fixes stuff

* Handle some contamination on attack still need to take care of meteors

* Fixed the meteor stuff

* Introduce emission types

* moves contaminate_touch to an atom proc and renames it to contaminate_atom

* deduplicates some contamination code

* Move inherent radioactivity to a component and signals from the atom vars and behaviour

* fix some things with the new component

* Update inherent_radioactivity.dm

* implement contaminating things that hit things that are inherently radioactive

* window things

* adds emission type to the rad_act call

* Changes radiation insulation values on a bunch of stuff

* fixes radioactive component radiating the wrong type

* more adjustments

* refactros rad_act

* adjustments to collector power production

* Adds plastitaniumglass windows and makes further adjustments

* Adds sprites to the shards and plastitanium directional window

* Update misc_cleanables.dm

* removes alpha rad insulation from floor turfs

* Fixes a bug with the starting tile of radiation waves

* More adjustments

* Adjusting singularity rad production

* reduces window half life a bit to make power smoother and buffs full window rad conversion

* Strengthens gamma and beta radiation effect on mobs.

* Makes radsuit block radiation completely

* Fixes Geiger Counters

* Fixes contamination not irradiating the contaminated thing

* Fixes inherent radioactivity not processing. Also makes it stop processing when a nuke core is contained

* Fixes ghost contamination

* Adds info to the collector

* Handles alpha radiation better on humans and changes some instances of rad_act to base_rad_act

* oops

* adjustments and fixes to alpha rad handling on mobs

* Make collector info more compact

* Core no longer radiates and contaminates from within the nuke until the plates are removed

* Contamination no longer counts as being inside a mob(it is supposed to be surface level)

* Adds inherent radioactivity to a bunch of uranium things. makes it all process.

* Nerf full windows

* Adjustments to collector and fulltile window radiation absorption

* Reduces passive contamination, especially while on the floor

* Adds different rad types to the geiger counter and fixes a runtime

* Makes full tile windows strong again and disallows building them on top of collectors

* adds emissive blockers to the rad blacklist and gives windows and collectors priority when irradiating for increased consistency

* Gives each contamination type it's own color.

* Gives each contamination type it's own color.  And makes the rad hud display them separately

* Changes how much the radiation wave affects the source tile, adds decay for performance reasons and adjusts collector parameters as well as SM and singulo rad production

* improves performance at very high rad amounts

* Fixes supermatter sliver box not containing radiation

* Restores supermatter sliver to old behaviour(not inherently radioactive)

* Slight nerf to fulltile windows and removes an unnecessary multiplication from rad wave processing

* Removes redundant line from window rad act

* Fixes radiation waves ignoring walls

* fixes it better

* more adjustments to collector stats

* Adjustment to collector gamma absorption

* increases grille beta blocking

* Review changes
This commit is contained in:
Migratingcocofruit
2025-03-02 22:34:45 +02:00
committed by GitHub
parent e29a599118
commit ebf7fdbfd2
66 changed files with 945 additions and 384 deletions
@@ -0,0 +1,72 @@
/datum/component/inherent_radioactivity
dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
var/radioactivity_alpha
var/radioactivity_beta
var/radioactivity_gamma
/// Contamination cooldown in seconds
var/contaminate_cd
var/contained = FALSE
COOLDOWN_DECLARE(contaminate_cooldown)
/datum/component/inherent_radioactivity/Initialize(_radioactivity_alpha = 0, _radioactivity_beta = 0, _radioactivity_gamma = 0, _contaminate_cd = 1.5)
radioactivity_alpha = _radioactivity_alpha
radioactivity_beta = _radioactivity_beta
radioactivity_gamma = _radioactivity_gamma
contaminate_cd = _contaminate_cd
RegisterSignal(parent, list(COMSIG_MOVABLE_IMPACT, COMSIG_ATOM_HITBY), PROC_REF(impact_contaminate))
RegisterSignal(parent, COMSIG_ATOM_ATTACK_HAND, PROC_REF(try_contaminate_hand))
if(isitem(parent))
RegisterSignal(parent, list(COMSIG_ATTACK, COMSIG_ATTACK_OBJ), PROC_REF(impact_contaminate))
START_PROCESSING(SSradiation, src)
/datum/component/inherent_radioactivity/proc/impact_contaminate(atom/source, atom/target)
SIGNAL_HANDLER
if(contaminate_cd <= 0 || COOLDOWN_FINISHED(src, contaminate_cooldown))
if(radioactivity_alpha)
target.contaminate_atom(source, radioactivity_alpha, ALPHA_RAD)
if(radioactivity_beta)
target.contaminate_atom(source, radioactivity_beta, BETA_RAD)
if(radioactivity_gamma)
target.contaminate_atom(source, radioactivity_gamma, GAMMA_RAD)
if(contaminate_cd > 0)
COOLDOWN_START(src, contaminate_cooldown, contaminate_cd SECONDS)
/datum/component/inherent_radioactivity/proc/try_contaminate_hand(atom/source, atom/target)
SIGNAL_HANDLER
if(contaminate_cd <= 0 || COOLDOWN_FINISHED(src, contaminate_cooldown))
if(radioactivity_alpha)
target.contaminate_atom(source, radioactivity_alpha, ALPHA_RAD, HANDS)
if(radioactivity_beta)
target.contaminate_atom(source, radioactivity_beta, BETA_RAD, HANDS)
if(radioactivity_gamma)
target.contaminate_atom(source, radioactivity_gamma, GAMMA_RAD, HANDS)
if(contaminate_cd > 0)
COOLDOWN_START(src, contaminate_cooldown, contaminate_cd SECONDS)
/datum/component/inherent_radioactivity/InheritComponent(datum/component/C, i_am_original, _radioactivity_alpha = 0, _radioactivity_beta = 0, _radioactivity_gamma = 0, _contaminate_cd = 1.5)
if(!i_am_original)
return
if(C)
var/datum/component/inherent_radioactivity/other = C
radioactivity_alpha = other.radioactivity_alpha
radioactivity_beta = other.radioactivity_beta
radioactivity_gamma = other.radioactivity_gamma
contaminate_cd = other.contaminate_cd
else
radioactivity_alpha = _radioactivity_alpha
radioactivity_beta = _radioactivity_beta
radioactivity_gamma = _radioactivity_gamma
contaminate_cd = _contaminate_cd
/datum/component/inherent_radioactivity/process()
if(radioactivity_alpha > 0)
contaminate_adjacent(parent, radioactivity_alpha, ALPHA_RAD)
radiation_pulse(parent, 2 * radioactivity_alpha, ALPHA_RAD)
if(radioactivity_beta > 0)
contaminate_adjacent(parent, radioactivity_beta, BETA_RAD)
radiation_pulse(parent, 2 * radioactivity_beta, BETA_RAD)
if(radioactivity_gamma > 0)
contaminate_adjacent(parent, radioactivity_gamma, GAMMA_RAD)
radiation_pulse(parent, 2 * radioactivity_gamma, GAMMA_RAD)
SSradiation.update_rad_cache_inherent(src)
+79 -22
View File
@@ -2,6 +2,9 @@
#define RAD_AMOUNT_MEDIUM 200
#define RAD_AMOUNT_HIGH 500
#define RAD_AMOUNT_EXTREME 1000
#define GLOW_ALPHA "#225dff5d"
#define GLOW_BETA "#39ff1430"
#define GLOW_GAMMA "#c125ff6b"
/datum/component/radioactive
dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
@@ -9,27 +12,31 @@
var/source
///the half-life measured in ticks
var/hl3_release_date
var/strength
var/can_contaminate
var/alpha_strength
var/beta_strength
var/gamma_strength
/datum/component/radioactive/Initialize(_strength = 0, _source, _half_life = RAD_HALF_LIFE, _can_contaminate = TRUE)
/datum/component/radioactive/Initialize(_strength, _source, emission_type, _half_life = RAD_HALF_LIFE)
if(!istype(parent, /atom))
return COMPONENT_INCOMPATIBLE
strength = _strength
switch(emission_type)
if(ALPHA_RAD)
alpha_strength = _strength
if(BETA_RAD)
beta_strength = _strength
if(GAMMA_RAD)
gamma_strength = _strength
source = _source
hl3_release_date = _half_life
can_contaminate = _can_contaminate
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, PROC_REF(rad_examine))
RegisterSignal(parent, COMSIG_ADMIN_DECONTAMINATE, PROC_REF(admin_decontaminate))
if(isitem(parent))
RegisterSignal(parent, COMSIG_ATTACK, PROC_REF(rad_attack))
RegisterSignal(parent, COMSIG_ATTACK_OBJ, PROC_REF(rad_attack))
if(strength > RAD_MINIMUM_CONTAMINATION)
SSradiation.warn(src)
//Let's make er glow
//This relies on parent not being a turf or something. IF YOU CHANGE THAT, CHANGE THIS
var/atom/movable/master = parent
master.add_filter("rad_glow", 2, list("type" = "outline", "color" = "#39ff1430", "size" = 2))
master.add_filter("rad_glow", 2, list("type" = "outline", "color" = get_glow_color(), "size" = 2))
addtimer(CALLBACK(src, PROC_REF(glow_loop), master), rand(1, 19)) //Things should look uneven
LAZYADD(SSradiation.all_radiations, src)
START_PROCESSING(SSradiation, src)
@@ -41,15 +48,40 @@
master.remove_filter("rad_glow")
return ..()
/datum/component/radioactive/proc/get_glow_color()
var/list/glow_alpha = rgb2num(GLOW_ALPHA)
var/list/glow_beta = rgb2num(GLOW_BETA)
var/list/glow_gamma = rgb2num(GLOW_GAMMA)
var/list/rad_color = list()
var/alpha_part = alpha_strength / (alpha_strength + beta_strength + gamma_strength)
var/beta_part = beta_strength / (alpha_strength + beta_strength + gamma_strength)
var/gamma_part = 1 - (alpha_part + beta_part)
var/max_ratio = 0
for(var/i in 1 to 4)
rad_color += glow_alpha[i] * alpha_part + glow_beta[i] * beta_part + glow_gamma[i] * gamma_part
// Find the ratio between the color value closest to 256 and 256.
if(i < 4 && max_ratio < (rad_color[i] / 256))
max_ratio = rad_color[i] / 256
return rgb(rad_color[1] / max_ratio, rad_color[2] / max_ratio, rad_color[3] / max_ratio, rad_color[4])
/datum/component/radioactive/process()
if(!prob(50))
return
radiation_pulse(parent, strength, RAD_DISTANCE_COEFFICIENT * 2, FALSE, can_contaminate)
if(alpha_strength > RAD_BACKGROUND_RADIATION)
radiation_pulse(parent, alpha_strength, ALPHA_RAD)
if(beta_strength > RAD_BACKGROUND_RADIATION)
radiation_pulse(parent, beta_strength, BETA_RAD)
if(gamma_strength > RAD_BACKGROUND_RADIATION)
radiation_pulse(parent, gamma_strength, GAMMA_RAD)
if(!hl3_release_date)
return
strength -= strength / hl3_release_date
SSradiation.update_rad_cache(src)
if(strength <= RAD_BACKGROUND_RADIATION)
alpha_strength -= alpha_strength / hl3_release_date
beta_strength -= beta_strength / hl3_release_date
gamma_strength -= gamma_strength / hl3_release_date
SSradiation.update_rad_cache_contaminated(src)
if(alpha_strength + beta_strength + gamma_strength <= RAD_BACKGROUND_RADIATION)
qdel(src)
return PROCESS_KILL
@@ -59,16 +91,30 @@
animate(filter, alpha = 110, time = 15, loop = -1)
animate(alpha = 40, time = 25)
/datum/component/radioactive/InheritComponent(datum/component/C, i_am_original, _strength, _source, _half_life, _can_contaminate)
/datum/component/radioactive/InheritComponent(datum/component/C, i_am_original, _strength, _source, emission_type, _half_life = RAD_HALF_LIFE)
if(!i_am_original)
return
if(!hl3_release_date) // Permanently radioactive things don't get to grow stronger
return
if(C)
var/datum/component/radioactive/other = C
strength = max(strength, other.strength)
alpha_strength = max(alpha_strength, other.alpha_strength)
beta_strength = max(beta_strength, other.beta_strength)
gamma_strength = max(gamma_strength, other.gamma_strength)
hl3_release_date = other.hl3_release_date
else
strength = max(strength, _strength)
switch(emission_type)
if(ALPHA_RAD)
alpha_strength = max(alpha_strength, _strength)
if(BETA_RAD)
beta_strength = max(beta_strength, _strength)
if(GAMMA_RAD)
gamma_strength = max(gamma_strength, _strength)
hl3_release_date = _half_life
var/atom/movable/master = parent
var/filter = master.get_filter("rad_glow")
animate(filter, color = get_glow_color())
/datum/component/radioactive/proc/rad_examine(datum/source, mob/user, list/out)
SIGNAL_HANDLER
@@ -78,7 +124,7 @@
var/list/fragments = list()
if(get_dist(master, user) <= 1)
fragments += "The air around [master] feels warm"
switch(strength)
switch(alpha_strength + beta_strength + gamma_strength)
if(0 to RAD_AMOUNT_LOW)
if(length(fragments))
fragments += "."
@@ -94,12 +140,20 @@
/datum/component/radioactive/proc/rad_attack(datum/source, atom/movable/target, mob/living/user)
SIGNAL_HANDLER
radiation_pulse(parent, strength / 20)
target.rad_act(strength / 2)
if(alpha_strength > RAD_BACKGROUND_RADIATION)
radiation_pulse(parent, alpha_strength / 20, ALPHA_RAD)
target.base_rad_act(parent ,alpha_strength / 2, ALPHA_RAD)
if(beta_strength > RAD_BACKGROUND_RADIATION)
radiation_pulse(parent, beta_strength / 20, BETA_RAD)
target.base_rad_act(parent, beta_strength / 2, BETA_RAD)
if(gamma_strength > RAD_BACKGROUND_RADIATION)
radiation_pulse(parent, gamma_strength / 20, GAMMA_RAD)
target.base_rad_act(parent, gamma_strength / 2, GAMMA_RAD)
if(!hl3_release_date)
return
strength -= strength / hl3_release_date
alpha_strength -= alpha_strength / hl3_release_date
beta_strength -= beta_strength / hl3_release_date
gamma_strength -= gamma_strength / hl3_release_date
/datum/component/radioactive/proc/admin_decontaminate()
SIGNAL_HANDLER
@@ -116,3 +170,6 @@
#undef RAD_AMOUNT_MEDIUM
#undef RAD_AMOUNT_HIGH
#undef RAD_AMOUNT_EXTREME
#undef GLOW_ALPHA
#undef GLOW_BETA
#undef GLOW_GAMMA
+1 -9
View File
@@ -12,8 +12,6 @@
RegisterSignal(target, COMSIG_ATOM_RAD_PROBE, PROC_REF(rad_probe_react))
if(contamination_proof) // Can this object be contaminated?
RegisterSignal(target, COMSIG_ATOM_RAD_CONTAMINATING, PROC_REF(rad_contaminating))
if(_amount != 1) // If it's 1 it won't have any impact on radiation passing through anyway
RegisterSignal(target, COMSIG_ATOM_RAD_WAVE_PASSING, PROC_REF(rad_pass))
amount = _amount
@@ -22,13 +20,7 @@
return COMPONENT_BLOCK_RADIATION
/datum/element/rad_insulation/proc/rad_contaminating(datum/source, strength)
/datum/element/rad_insulation/proc/rad_contaminating(datum/source)
SIGNAL_HANDLER
return COMPONENT_BLOCK_CONTAMINATION
/datum/element/rad_insulation/proc/rad_pass(datum/source, datum/radiation_wave/wave, width)
SIGNAL_HANDLER
wave.intensity = wave.intensity * (1 - ((1 - amount) / width)) // The further out the rad wave goes the less it's affected by insulation (larger width)
return COMPONENT_RAD_WAVE_HANDLED
+69 -108
View File
@@ -1,41 +1,31 @@
#define WRAP_INDEX(index, length)((index - 1) % length + 1)
/datum/radiation_wave
/// The thing that spawned this radiation wave
var/source
/// The center of the wave
/// The top left corner of the wave, from which we begin iteration on a step
var/turf/master_turf
/// How far we've moved
var/steps = 0
/// How strong it was originaly
/// The strength at the origin. Multiplied by the weight of a tile to determine the strength of radiation there.
var/intensity
/// How much contaminated material it still has
var/remaining_contam
/// Higher than 1 makes it drop off faster, 0.5 makes it drop off half etc
var/range_modifier
/// The distance from the source point the wave can cover without losing any strength.
var/source_radius
/// The direction of movement
var/move_dir
/// The directions to the side of the wave, stored for easy looping
var/list/__dirs
/// Whether or not this radiation wave can create contaminated objects
var/can_contaminate
/// Weights of the current tiles in the step going clockwise from the top left corner. Starts as one tile with a weight of 1
var/list/weights = list(1)
/// Sum of all weights
var/weight_sum = 1
/// The type of particle emitted
var/emission_type = ALPHA_RAD
/datum/radiation_wave/New(atom/_source, dir, _intensity = 0, _range_modifier = RAD_DISTANCE_COEFFICIENT, _can_contaminate = TRUE, _source_radius = 0)
source = "[_source] \[[_source.UID()]\]"
/datum/radiation_wave/New(atom/_source, _intensity = 0, _emission_type = ALPHA_RAD)
source = _source
master_turf = get_turf(_source)
move_dir = dir
__dirs = list()
__dirs += turn(dir, 90)
__dirs += turn(dir, -90)
intensity = _intensity
remaining_contam = intensity
range_modifier = _range_modifier
can_contaminate = _can_contaminate
source_radius = _source_radius
emission_type = _emission_type
START_PROCESSING(SSradiation, src)
/datum/radiation_wave/Destroy()
@@ -43,99 +33,70 @@
STOP_PROCESSING(SSradiation, src)
..()
/// Deals with wave propagation. Radiation waves always expand in a 90 degree cone
/datum/radiation_wave/process()
master_turf = get_step(master_turf, move_dir)
// If the wave is too weak to do anything
if(weight_sum * intensity < RAD_BACKGROUND_RADIATION)
qdel(src)
return
/// We start iteration from the top left corner of the current wave step
master_turf = get_step(master_turf, NORTHWEST)
if(!master_turf)
qdel(src)
return
steps++
var/list/atoms = get_rad_atoms()
var/list/new_weights = list()
var/turf/current_turf = master_turf
weight_sum = 0
var/weight_left
var/weight_center
var/weight_right
var/index
var/offset
var/walk_dir = EAST
var/ratio = steps > 1 ? (steps - 1) / steps : (1 / 8)
var/weight_length = length(weights)
// Iterate around the periphery of a square for each step
for(var/i in 0 to (8 * steps - 1))
// our index along the edge we are on, each corner starts a new edge.
index = (i % (2 * steps)) + 1
// Get weights for rear, right rear and left rear tiles if they were part of the previous step, where rear means towards the center and left and right are along the edge we are on
weight_left = index > 2 ? weights[WRAP_INDEX((index + offset - 2), weight_length)] : 0
weight_center = index > 1 ? weights[WRAP_INDEX((index + offset - 1), weight_length)] : 0
weight_right = index < (2 * steps) ? weights[WRAP_INDEX((index + offset), weight_length)] : 0
// The weight of the current tile the average of the weights of the tiles we checked for earlier
// And is reduced by irradiating things and getting blocked
if(weight_left + weight_center + weight_right)
new_weights += radiate(source, current_turf, (ratio) * (weight_left + weight_center + weight_right) / ((1 + (index > 1 && index < (2 * steps + 1) && steps > 1) + (index > 2 && index < (2 * steps)))), emission_type)
else
new_weights += 0
weight_sum += new_weights[i + 1]
// Advance to next turf
current_turf = get_step(current_turf, walk_dir)
// If we reached a corner turn to the right
if(index == (2 * steps))
walk_dir = turn(walk_dir, -90)
offset += 2 * (steps - 1)
var/strength
if(steps > source_radius + 1)
strength = INVERSE_SQUARE(intensity, max(range_modifier * (steps - source_radius), 1), 1)
weights = new_weights
// With the spread each step being linear waves can spread very far. This limits the distance for performace reasons
if(steps > RAD_DECAY_POINT)
intensity *= RAD_DECAY_RATE
/// Calls rad act on each relevant atom in the turf and returns the resulting weight for that tile after reduction by insulation
/datum/radiation_wave/proc/radiate(atom/source, turf/current_turf, weight, emission_type)
var/list/turf_atoms = list()
if(length(current_turf.contents))
turf_atoms = get_rad_contents(current_turf, emission_type)
else
strength = intensity
if(strength < RAD_BACKGROUND_RADIATION)
qdel(src)
return
radiate(atoms, strength)
check_obstructions(atoms) // reduce our overall strength if there are radiation insulators
/datum/radiation_wave/proc/get_rad_atoms()
var/list/atoms = list()
var/distance = steps
var/cmove_dir = move_dir
var/cmaster_turf = master_turf
if(cmove_dir == NORTH || cmove_dir == SOUTH)
distance-- //otherwise corners overlap
atoms += get_rad_contents(cmaster_turf)
var/turf/place
for(var/dir in __dirs) //There should be just 2 dirs in here, left and right of the direction of movement
place = cmaster_turf
for(var/i in 1 to distance)
place = get_step(place, dir)
if(!place)
break
atoms += get_rad_contents(place)
return atoms
/datum/radiation_wave/proc/check_obstructions(list/atoms)
var/width = steps
var/cmove_dir = move_dir
if(cmove_dir == NORTH || cmove_dir == SOUTH)
width--
width = 1 + (2 * width)
for(var/k in 1 to length(atoms))
var/atom/thing = atoms[k]
if(!thing)
continue
if(SEND_SIGNAL(thing, COMSIG_ATOM_RAD_WAVE_PASSING, src, width) & COMPONENT_RAD_WAVE_HANDLED)
continue
if(thing.rad_insulation != RAD_NO_INSULATION)
intensity *= (1 - ((1 - thing.rad_insulation) / width))
/datum/radiation_wave/proc/radiate(list/atoms, strength)
var/can_contam = strength >= RAD_MINIMUM_CONTAMINATION
var/contamination_strength = (strength - RAD_MINIMUM_CONTAMINATION) * RAD_CONTAMINATION_STR_COEFFICIENT
contamination_strength = max(contamination_strength, RAD_BACKGROUND_RADIATION)
// It'll never reach 100% chance but the further out it gets the more likely it'll contaminate
var/contamination_chance = 100 - (90 / (1 + steps * 0.1))
for(var/k in atoms)
turf_atoms = list(current_turf)
for(var/k in turf_atoms)
var/atom/thing = k
if(QDELETED(thing))
continue
thing.rad_act(strength)
weight = weight * thing.base_rad_act(source ,weight * intensity, emission_type)
// return the resulting weight if the radiation on the tile would end up greater than background
return (((weight * intensity) > RAD_BACKGROUND_RADIATION) ? weight : 0)
// This list should only be for types which don't get contaminated but you want to look in their contents
// If you don't want to look in their contents and you don't want to rad_act them:
// modify the ignored_things list in __HELPERS/radiation.dm instead
var/static/list/blacklisted = typecacheof(list(
/turf,
/obj/structure/cable,
/obj/machinery/atmospherics,
/obj/item/ammo_casing,
/obj/item/bio_chip,
/obj/singularity,
))
if(!can_contaminate || !can_contam || blacklisted[thing.type])
continue
if(thing.flags_2 & RAD_NO_CONTAMINATE_2 || SEND_SIGNAL(thing, COMSIG_ATOM_RAD_CONTAMINATING, strength) & COMPONENT_BLOCK_CONTAMINATION)
continue
if(contamination_strength > remaining_contam)
contamination_strength = remaining_contam
if(!prob(contamination_chance))
continue
if(SEND_SIGNAL(thing, COMSIG_ATOM_RAD_CONTAMINATING, strength) & COMPONENT_BLOCK_CONTAMINATION)
continue
remaining_contam -= contamination_strength
if(remaining_contam < RAD_BACKGROUND_RADIATION)
can_contaminate = FALSE
thing.AddComponent(/datum/component/radioactive, contamination_strength, source)
#undef WRAP_INDEX
@@ -55,7 +55,7 @@
return
if(prob(max(0, 100 - ARMOUR_VALUE_TO_PERCENTAGE(resist))))
L.rad_act(400)
L.base_rad_act(L ,400 , BETA_RAD)
if(HAS_TRAIT(H, TRAIT_GENELESS))
return
randmuti(H) // Applies bad mutation