Merge pull request #3642 from Citadel-Station-13/upstream-merge-32051

[MIRROR] More radiation balance changes and fixes
This commit is contained in:
LetterJay
2017-10-29 02:41:44 -04:00
committed by GitHub
14 changed files with 213 additions and 106 deletions
+20 -19
View File
@@ -6,29 +6,30 @@ Ask ninjanomnom if they're around
#define RAD_BACKGROUND_RADIATION 9 // How much radiation is harmless to a mob, this is also when radiation waves stop spreading
// WARNING: Lowering this value significantly increases SSradiation load
#define RAD_AMOUNT_LOW 50
#define RAD_AMOUNT_MEDIUM 200
#define RAD_AMOUNT_HIGH 500
#define RAD_AMOUNT_EXTREME 1000
// apply_effect(amount * RAD_MOB_COEFFICIENT, IRRADIATE, blocked)
#define RAD_MOB_COEFFICIENT 0.25 // Radiation applied is multiplied by this
// apply_effect((amount*RAD_MOB_COEFFICIENT)/max(1, (radiation**2)*RAD_OVERDOSE_REDUCTION), IRRADIATE, blocked)
#define RAD_MOB_COEFFICIENT 0.20 // Radiation applied is multiplied by this
#define RAD_MOB_SKIN_PROTECTION ((1/RAD_MOB_COEFFICIENT)+RAD_BACKGROUND_RADIATION)
#define RAD_LOSS_PER_TICK 1
#define RAD_LOSS_PER_TICK 0.5
#define RAD_TOX_COEFFICIENT 0.05 // Toxin damage per tick coefficient
#define RAD_OVERDOSE_REDUCTION 0.000001 // Coefficient to the reduction in applied rads once the thing, usualy mob, has too much radiation
// WARNING: This number is highly sensitive to change, graph is first for best results
#define RAD_BURN_THRESHOLD 1000 // Applied radiation must be over this to burn
#define RAD_MOB_SAFE 300 // How much stored radiation in a mob with no ill effects
#define RAD_MOB_SAFE 500 // How much stored radiation in a mob with no ill effects
#define RAD_MOB_HAIRLOSS 800 // How much stored radiation to check for hair loss
#define RAD_MOB_MUTATE 1250 // How much stored radiation to check for mutation
#define RAD_MOB_VOMIT 2000 // The amount of radiation to check for vomitting
#define RAD_MOB_VOMIT_PROB 1 // Chance per tick of vomitting
#define RAD_MOB_KNOCKDOWN 2000 // How much stored radiation to check for stunning
#define RAD_MOB_KNOCKDOWN_PROB 1 // Chance of knockdown per tick when over threshold
#define RAD_MOB_KNOCKDOWN_AMOUNT 3 // Amount of knockdown when it occurs
#define RAD_MOB_VOMIT 1500 // The amount of radiation to check for vomitting
#define RAD_MOB_VOMIT_PROB 1 // Chance per tick of vomitting
#define RAD_MOB_MUTATE 1000 // How much stored radiation to check for mutation
#define RAD_MOB_HAIRLOSS 500 // How much stored radiation to check for hair loss
#define RAD_NO_INSULATION 1.0 // For things that shouldn't become irradiated for whatever reason
#define RAD_VERY_LIGHT_INSULATION 0.9 // What girders have
#define RAD_LIGHT_INSULATION 0.8
@@ -39,10 +40,10 @@ Ask ninjanomnom if they're around
// WARNING: The deines below could have disastrous consequences if tweaked incorrectly. See: The great SM purge of Oct.6.2017
// contamination_chance = (strength-RAD_MINIMUM_CONTAMINATION) * RAD_CONTAMINATION_CHANCE_COEFFICIENT * min(1/(steps*RAD_DISTANCE_COEFFICIENT), 1))
// contamination_strength = (strength-RAD_MINIMUM_CONTAMINATION) * RAD_CONTAMINATION_STR_COEFFICIENT * min(1/(steps*RAD_DISTANCE_COEFFICIENT), 1)
#define RAD_MINIMUM_CONTAMINATION 300 // How strong does a radiation wave have to be to contaminate objects
#define RAD_CONTAMINATION_CHANCE_COEFFICIENT 0.0075 // Higher means higher strength scaling contamination chance
#define RAD_CONTAMINATION_STR_COEFFICIENT 0.5 // Higher means higher strength scaling contamination strength
// contamination_strength = (strength-RAD_MINIMUM_CONTAMINATION) * RAD_CONTAMINATION_STR_COEFFICIENT
#define RAD_MINIMUM_CONTAMINATION 350 // How strong does a radiation wave have to be to contaminate objects
#define RAD_CONTAMINATION_CHANCE_COEFFICIENT 0.005 // Higher means higher strength scaling contamination chance
#define RAD_CONTAMINATION_STR_COEFFICIENT 0.3 // Higher means higher strength scaling contamination strength
#define RAD_DISTANCE_COEFFICIENT 1 // Lower means further rad spread
#define RAD_HALF_LIFE 150 // The half-life of contaminated objects
#define RAD_HALF_LIFE 90 // The half-life of contaminated objects
+18 -18
View File
@@ -1,28 +1,28 @@
/proc/get_rad_contents(atom/location, list/output=list()) // A special GetAllContents that doesn't search past things with rad insulation
. = output
if(!location)
return
output += location
var/datum/component/rad_insulation/insulation = location.GetComponent(/datum/component/rad_insulation)
if(insulation && insulation.protects)
return
for(var/i in 1 to location.contents.len)
var/static/list/ignored_things = typecacheof(list( // These types will never have radiation applied to them or be looked inside of
// A special GetAllContents that doesn't search past things with rad insulation
// The protection var only protects the things inside from being affected.
// The protecting object itself will get returned still.
// The ignore list makes those objects never return at all
/proc/get_rad_contents(atom/location)
var/list/processing_list = list(location)
. = list()
while(processing_list.len)
var/static/list/ignored_things = typecacheof(list(
/mob/dead,
/mob/camera,
/obj/effect,
/obj/docking_port,
/atom/movable/lighting_object
/atom/movable/lighting_object,
/obj/item/projectile
))
var/atom/thing = location.contents[i]
var/atom/thing = processing_list[1]
processing_list -= thing
if(ignored_things[thing.type])
continue
get_rad_contents(thing, output)
. += thing
var/datum/component/rad_insulation/insulation = thing.GetComponent(/datum/component/rad_insulation)
if(insulation && insulation.protects)
continue
processing_list += thing.contents
/proc/radiation_pulse(atom/source, intensity, range_modifier, log=FALSE, can_contaminate=TRUE)
if(!SSradiation.can_fire)
+38 -1
View File
@@ -1,4 +1,41 @@
PROCESSING_SUBSYSTEM_DEF(radiation)
name = "Radiation"
flags = SS_NO_INIT | SS_BACKGROUND
priority = 25
priority = 25
var/list/warned_atoms = list()
var/list/next_warn = list()
var/last_warn = 0
/datum/controller/subsystem/processing/radiation/proc/warn(datum/component/radioactive)
if(!radioactive || QDELETED(radioactive))
return
if(warned_atoms["\ref[radioactive.parent]"])
return
var/atom/master = radioactive.parent
SSblackbox.add_details("contaminated", "[master.type]")
next_warn["\ref[master]"] = "\ref[radioactive]"
var/wait_time = max(0, 500-(world.time-last_warn))+20 // wait at least 20 ticks, longer if we just messaged
addtimer(CALLBACK(src, .proc/send_warn), wait_time, TIMER_UNIQUE | TIMER_OVERRIDE)
/datum/controller/subsystem/processing/radiation/proc/send_warn()
var/msg = "Atom(s) have become contaminated by radiation and are strong enough they could pass it on:"
var/still_alive = FALSE
var/list/next_warn = src.next_warn // It's free performance!
for(var/i in next_warn)
var/atom/parent = locate(i)
var/datum/component/radioactive/radioactive = locate(next_warn[i])
if(!parent || !istype(parent) || !radioactive || !istype(radioactive))
continue
if(!still_alive)
msg += "\n"
still_alive = TRUE
else
msg += ", "
msg += "[parent][ADMIN_VV(parent)]source:[radioactive.source]"
if(!still_alive)
return
warned_atoms += next_warn
src.next_warn = list()
last_warn = world.time
message_admins(msg)
+5 -3
View File
@@ -27,6 +27,9 @@
CRASH("Something that wasn't an atom was given /datum/component/radioactive")
return
if(strength > RAD_MINIMUM_CONTAMINATION)
SSradiation.warn(src)
START_PROCESSING(SSradiation, src)
/datum/component/radioactive/Destroy()
@@ -34,9 +37,8 @@
return ..()
/datum/component/radioactive/process()
radiation_pulse(parent,strength,1,FALSE,can_contaminate)
if(hl3_release_date && prob(50))
radiation_pulse(parent, strength, RAD_DISTANCE_COEFFICIENT*2, FALSE, can_contaminate)
strength -= strength / hl3_release_date
if(strength <= RAD_BACKGROUND_RADIATION)
qdel(src)
@@ -48,7 +50,6 @@
return
var/datum/component/radioactive/other = C
strength = max(strength, other.strength)
return
/datum/component/radioactive/proc/rad_examine(mob/user, atom/thing)
var/atom/master = parent
@@ -69,6 +70,7 @@
/datum/component/radioactive/proc/rad_attack(atom/movable/target, mob/living/user)
radiation_pulse(parent, strength/20)
target.rad_act(strength/2)
strength -= strength / hl3_release_date
#undef RAD_AMOUNT_LOW
#undef RAD_AMOUNT_MEDIUM
+37
View File
@@ -0,0 +1,37 @@
#define RAD_GEIGER_LOW 100 // Geiger counter sound thresholds
#define RAD_GEIGER_MEDIUM 500
#define RAD_GEIGER_HIGH 1000
/datum/looping_sound/geiger
mid_sounds = list(
list('sound/items/geiger/low1.ogg'=1, 'sound/items/geiger/low2.ogg'=1, 'sound/items/geiger/low3.ogg'=1, 'sound/items/geiger/low4.ogg'=1),
list('sound/items/geiger/med1.ogg'=1, 'sound/items/geiger/med2.ogg'=1, 'sound/items/geiger/med3.ogg'=1, 'sound/items/geiger/med4.ogg'=1),
list('sound/items/geiger/high1.ogg'=1, 'sound/items/geiger/high2.ogg'=1, 'sound/items/geiger/high3.ogg'=1, 'sound/items/geiger/high4.ogg'=1),
list('sound/items/geiger/ext1.ogg'=1, 'sound/items/geiger/ext2.ogg'=1, 'sound/items/geiger/ext3.ogg'=1, 'sound/items/geiger/ext4.ogg'=1)
)
mid_length = 2
volume = 25
var/last_radiation
/datum/looping_sound/geiger/get_sound(looped)
var/danger
switch(last_radiation)
if(RAD_BACKGROUND_RADIATION to RAD_GEIGER_LOW)
danger = 1
if(RAD_GEIGER_LOW to RAD_GEIGER_MEDIUM)
danger = 2
if(RAD_GEIGER_MEDIUM to RAD_GEIGER_HIGH)
danger = 3
if(RAD_GEIGER_HIGH to INFINITY)
danger = 4
else
return null
return ..(looped, mid_sounds[danger])
/datum/looping_sound/geiger/stop()
. = ..()
last_radiation = 0
#undef RAD_GEIGER_LOW
#undef RAD_GEIGER_MEDIUM
#undef RAD_GEIGER_HIGH
+21 -10
View File
@@ -1,5 +1,5 @@
/*
list/atom/output_atoms
output_atoms (list of atoms) The destination(s) for the sounds
mid_sounds (list or soundfile) Since this can be either a list or a single soundfile you can have random sounds. May contain further lists but must contain a soundfile at the end.
mid_length (num) The length to wait between playing mid_sounds
@@ -13,6 +13,7 @@
volume (num) Sound output volume
muted (bool) Private. Used to stop the sound loop.
max_loops (num) The max amount of loops to run for.
direct (bool) If true plays directly to provided atoms instead of from them
*/
/datum/looping_sound
var/list/atom/output_atoms
@@ -22,19 +23,18 @@
var/start_length
var/end_sound
var/chance
var/volume
var/volume = 100
var/muted = TRUE
var/max_loops
var/direct
/datum/looping_sound/New(list/_output_atoms, start_immediately=FALSE)
/datum/looping_sound/New(list/_output_atoms=list(), start_immediately=FALSE, _direct=FALSE)
if(!mid_sounds)
WARNING("A looping sound datum was created without sounds to play.")
return
if(_output_atoms)
output_atoms = _output_atoms
else
output_atoms = list()
output_atoms = _output_atoms
direct = _direct
if(start_immediately)
start()
@@ -44,13 +44,17 @@
output_atoms = null
return ..()
/datum/looping_sound/proc/start()
/datum/looping_sound/proc/start(atom/add_thing)
if(add_thing)
output_atoms |= add_thing
if(!muted)
return
muted = FALSE
on_start()
/datum/looping_sound/proc/stop()
/datum/looping_sound/proc/stop(atom/remove_thing)
if(remove_thing)
output_atoms -= remove_thing
if(muted)
return
muted = TRUE
@@ -65,9 +69,16 @@
/datum/looping_sound/proc/play(soundfile)
var/list/atoms_cache = output_atoms
var/sound/S = sound(soundfile)
if(direct)
S.channel = open_sound_channel()
S.volume = volume
for(var/i in 1 to atoms_cache.len)
var/atom/thing = atoms_cache[i]
playsound(thing, soundfile, volume)
if(direct)
SEND_SOUND(thing, S)
else
playsound(thing, S, volume)
/datum/looping_sound/proc/get_sound(looped, _mid_sounds)
if(!_mid_sounds)
+10 -4
View File
@@ -89,18 +89,24 @@
continue
thing.rad_act(strength)
var/static/list/blacklisted = typecacheof(list( //These types will never be contaminated
// 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,
/mob,
/obj/structure/cable,
/obj/machinery/atmospherics
/obj/machinery/atmospherics,
/obj/item/ammo_casing,
/obj/item/implant
))
if(!can_contaminate || blacklisted[thing.type])
continue
if(prob((strength-RAD_MINIMUM_CONTAMINATION) * RAD_CONTAMINATION_CHANCE_COEFFICIENT * min(1/(steps*range_modifier), 1))) // Only stronk rads get to have little baby rads
var/contamination_chance = (strength-RAD_MINIMUM_CONTAMINATION) * RAD_CONTAMINATION_CHANCE_COEFFICIENT * min(1, 1/(steps*range_modifier))
if(prob(contamination_chance)) // Only stronk rads get to have little baby rads
var/datum/component/rad_insulation/insulation = thing.GetComponent(/datum/component/rad_insulation)
if(insulation && insulation.contamination_proof)
continue
else
var/rad_strength = (strength-RAD_MINIMUM_CONTAMINATION) * RAD_CONTAMINATION_STR_COEFFICIENT * min(1/(steps*range_modifier), 1)
var/rad_strength = (strength-RAD_MINIMUM_CONTAMINATION) * RAD_CONTAMINATION_STR_COEFFICIENT
thing.AddComponent(/datum/component/radioactive, rad_strength, source)
+4 -1
View File
@@ -210,7 +210,7 @@
mob_occupant.adjustFireLoss(rand(20, 36))
else
mob_occupant.adjustFireLoss(rand(10, 16))
mob_occupant.emote("scream")
mob_occupant.emote("scream")
addtimer(CALLBACK(src, .proc/cook), 50)
else
uv_cycles = initial(uv_cycles)
@@ -238,6 +238,9 @@
for(var/obj/item/I in src) //Scorches away blood and forensic evidence, although the SSU itself is unaffected
I.clean_blood()
I.fingerprints = list()
var/datum/component/radioactive/contamination = I.GetComponent(/datum/component/radioactive)
if(contamination)
qdel(contamination)
open_machine(FALSE)
if(occupant)
dump_contents()
@@ -19,15 +19,8 @@
slot_flags = SLOT_BELT
materials = list(MAT_METAL = 150, MAT_GLASS = 150)
var/muted = TRUE
var/danger = 0
var/grace = RAD_GRACE_PERIOD
var/static/list/sounds = list( //hah, static. get it?
list('sound/items/geiger/low1.ogg'=1, 'sound/items/geiger/low2.ogg'=1, 'sound/items/geiger/low3.ogg'=1, 'sound/items/geiger/low4.ogg'=1),
list('sound/items/geiger/med1.ogg'=1, 'sound/items/geiger/med2.ogg'=1, 'sound/items/geiger/med3.ogg'=1, 'sound/items/geiger/med4.ogg'=1),
list('sound/items/geiger/high1.ogg'=1, 'sound/items/geiger/high2.ogg'=1, 'sound/items/geiger/high3.ogg'=1, 'sound/items/geiger/high4.ogg'=1),
list('sound/items/geiger/ext1.ogg'=1, 'sound/items/geiger/ext2.ogg'=1, 'sound/items/geiger/ext3.ogg'=1, 'sound/items/geiger/ext4.ogg'=1)
)
var/datum/looping_sound/geiger/soundloop
var/scanning = FALSE
var/radiation_count = 0
@@ -40,7 +33,7 @@
. = ..()
START_PROCESSING(SSobj, src)
soundLoop()
soundloop = new(list(src), FALSE)
/obj/item/device/geiger_counter/Destroy()
STOP_PROCESSING(SSobj, src)
@@ -48,6 +41,7 @@
/obj/item/device/geiger_counter/process()
update_icon()
update_sound()
if(!scanning)
current_tick_amount = 0
@@ -64,8 +58,6 @@
grace--
if(grace <= 0)
radiation_count = 0
update_sound()
current_tick_amount = 0
@@ -116,28 +108,15 @@
..()
/obj/item/device/geiger_counter/proc/update_sound()
switch(radiation_count)
if(RAD_BACKGROUND_RADIATION to RAD_LEVEL_MODERATE)
danger = 1
if(RAD_LEVEL_MODERATE to RAD_LEVEL_VERY_HIGH)
danger = 2
if(RAD_LEVEL_VERY_HIGH to RAD_LEVEL_CRITICAL)
danger = 3
if(RAD_LEVEL_CRITICAL to INFINITY)
danger = 4
else
danger = 0
if(!danger)
muted = TRUE
else if(muted)
muted = FALSE
soundLoop()
/obj/item/device/geiger_counter/proc/soundLoop()
if(muted || !danger)
var/datum/looping_sound/geiger/loop = soundloop
if(!scanning)
loop.stop()
return
playsound(src, pickweight(sounds[danger]), 25)
addtimer(CALLBACK(src, .proc/soundLoop), 2)
if(!radiation_count)
loop.stop()
return
loop.last_radiation = radiation_count
loop.start()
/obj/item/device/geiger_counter/rad_act(amount)
if(amount <= RAD_BACKGROUND_RADIATION || !scanning)
@@ -147,11 +126,6 @@
/obj/item/device/geiger_counter/attack_self(mob/user)
scanning = !scanning
if(!scanning)
muted = TRUE
else
muted = FALSE
soundLoop()
update_icon()
to_chat(user, "<span class='notice'>[icon2html(src, user)] You switch [scanning ? "on" : "off"] [src].</span>")
+34 -3
View File
@@ -13,6 +13,20 @@
item_color = "engineering" //Determines used sprites: hardsuit[on]-[color] and hardsuit[on]-[color]2 (lying down sprite)
actions_types = list(/datum/action/item_action/toggle_helmet_light)
var/rad_count = 0
var/rad_record = 0
var/grace_count = 0
var/datum/looping_sound/geiger/soundloop
/obj/item/clothing/head/helmet/space/hardsuit/Initialize()
. = ..()
soundloop = new(list(), FALSE, TRUE)
soundloop.volume = 5
START_PROCESSING(SSobj, src)
/obj/item/clothing/head/helmet/space/hardsuit/Destroy()
. = ..()
STOP_PROCESSING(SSobj, src)
/obj/item/clothing/head/helmet/space/hardsuit/attack_self(mob/user)
on = !on
@@ -31,6 +45,7 @@
..()
if(suit)
suit.RemoveHelmet()
soundloop.stop(user)
/obj/item/clothing/head/helmet/space/hardsuit/item_action_slot_check(slot)
if(slot == slot_head)
@@ -41,8 +56,11 @@
if(slot != slot_head)
if(suit)
suit.RemoveHelmet()
soundloop.stop(user)
else
qdel(src)
else
soundloop.start(user)
/obj/item/clothing/head/helmet/space/hardsuit/proc/display_visor_message(var/msg)
var/mob/wearer = loc
@@ -50,9 +68,22 @@
wearer.show_message("[icon2html(src, wearer)]<b><span class='robot'>[msg]</span></b>", 1)
/obj/item/clothing/head/helmet/space/hardsuit/rad_act(severity)
..()
if(severity > RAD_AMOUNT_EXTREME)
display_visor_message("Radiation pulse detected! Magnitude: <span class='green'>[severity]</span> RADs.")
. = ..()
rad_count += severity
/obj/item/clothing/head/helmet/space/hardsuit/process()
if(!rad_count)
grace_count++
if(grace_count == 2)
soundloop.last_radiation = 0
return
grace_count = 0
rad_record -= rad_record/5
rad_record += rad_count/5
rad_count = 0
soundloop.last_radiation = rad_record
/obj/item/clothing/head/helmet/space/hardsuit/emp_act(severity)
..()
+3 -2
View File
@@ -112,8 +112,9 @@
item_state = "bombsuit_white"
/*
* Radiation protection
*/
* Radiation protection
*/
/obj/item/clothing/head/radiation
name = "radiation hood"
icon_state = "rad"
+9 -6
View File
@@ -898,14 +898,17 @@
to_chat(G, "<span class='holoparasite'>Your summoner has changed form!</span>")
/mob/living/rad_act(amount)
amount = max(amount-RAD_BACKGROUND_RADIATION, 0)
if(!amount || amount < RAD_MOB_SKIN_PROTECTION)
return
if(amount)
var/blocked = getarmor(null, "rad")
amount -= RAD_BACKGROUND_RADIATION // This will always be at least 1 because of how skin protection is calculated
apply_effect(amount * RAD_MOB_COEFFICIENT, IRRADIATE, blocked)
if(amount > RAD_AMOUNT_EXTREME)
apply_damage((amount-RAD_AMOUNT_EXTREME)/RAD_AMOUNT_EXTREME, BURN, null, blocked)
var/blocked = getarmor(null, "rad")
if(amount > RAD_BURN_THRESHOLD)
apply_damage((amount-RAD_BURN_THRESHOLD)/RAD_BURN_THRESHOLD, BURN, null, blocked)
apply_effect((amount*RAD_MOB_COEFFICIENT)/max(1, (radiation**2)*RAD_OVERDOSE_REDUCTION), IRRADIATE, blocked)
/mob/living/proc/fakefireextinguish()
return
@@ -454,7 +454,7 @@
/datum/reagent/medicine/potass_iodide/on_mob_life(mob/living/M)
if(M.radiation > 0)
M.radiation -= min(M.radiation, 4)
M.radiation -= min(M.radiation, 8)
..()
/datum/reagent/medicine/pen_acid
@@ -466,7 +466,7 @@
metabolization_rate = 0.5 * REAGENTS_METABOLISM
/datum/reagent/medicine/pen_acid/on_mob_life(mob/living/M)
M.radiation -= max(M.radiation-RAD_MOB_SAFE, 0)/100
M.radiation -= max(M.radiation-RAD_MOB_SAFE, 0)/50
M.adjustToxLoss(-2*REM, 0)
for(var/datum/reagent/R in M.reagents.reagent_list)
if(R != src)
+1
View File
@@ -377,6 +377,7 @@
#include "code\datums\helper_datums\icon_snapshot.dm"
#include "code\datums\helper_datums\teleport.dm"
#include "code\datums\helper_datums\topic_input.dm"
#include "code\datums\looping_sounds\item_sounds.dm"
#include "code\datums\looping_sounds\looping_sound.dm"
#include "code\datums\looping_sounds\machinery_sounds.dm"
#include "code\datums\martial\boxing.dm"