mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-04 14:01:22 +00:00
## About The Pull Request Flux anomalies, when they detonate, do a minor explosion and emit a wide EMP pulse. This can cause a lot of damage in its own right, but it won't syndicate bomb a random section of the station anymore. Gravity anomalies A) No longer have the click catching giant image floating above it anymore. B) No longer chain hardstuns you if you get too close every time it ticks. Instead, it knocks you down. You can also avoid this effect by wearing magboots. C) Also doesn't throw literally every object in an area around it straight into you every time it ticks. Instead, it does so only 20% of the time. Long range gas analyzers can now scan anomalies. ## Why It's Good For The Game > Flux A really annoying anomaly and one that I think has caused several shuttle calls in its time. If you don't catch it, it blows up a segment of the station entirely at random. This thing can almost be as bad as a vortex in some contexts. A giant EMP can still be pretty destructive, but it isn't quite at the same level as a giant explosion and I suspect not shuttle call worthy. The minor explosion will at most trash a room. > Gravity I literally do not understand how anyone was supposed to disable these. I literally gave up years ago even trying to catch them. They're practically designed to kill you for trying. The click catching warp effect not only looked kind of lame, but it just straight up stopped you being able to disable them (mouse opacity doesn't work here and I tried, it redirects clicks to other tiles for some godawful reason, probably byond weirdness). You had to spam click a LOT to even try. On top of that, it just stuns you? It literally just stuns you if you approach it? And magboots can't help you to avoid that? What the fuck are you _supposed_ to do to avoid that? Also it can just instantly kill you if you approach it and it gathered enough stuff because it will just keep slapping you with every loose object within a radius around itself? At speed 5 so things that don't normally embed will embed? So it mulches you for approaching it, chain hardstuns you for approaching it, and it deliberately intercepts clicks in a completely unintuitive fashion even if you do manage to click it. You need to approach it to stop it. A recipe for disaster. Fuck that. > Analyzer It was annoying this wasn't able to do this already! ## Changelog 🆑 balance: Gravity anomalies can be properly clicked. Rather than chain stunning you, the anomaly will knock you down. You can avoid the negatives of gravity anomalies by wearing magboots. (Yes, it did not really protect you until now) balance: Flux anomalies detonate in a much smaller explosion, but release a wide EMP as well. balance: Long range gas analyzers can now scan anomalies. /🆑
191 lines
9.4 KiB
Plaintext
191 lines
9.4 KiB
Plaintext
// Any power past this number will be clamped down
|
|
#define MAX_ACCEPTED_POWER_OUTPUT 5000
|
|
|
|
// At the highest power output, assuming no integrity changes, the threshold will be 0.
|
|
#define THRESHOLD_EQUATION_SLOPE (-1 / MAX_ACCEPTED_POWER_OUTPUT)
|
|
|
|
// The higher this number, the faster low integrity will drop threshold
|
|
// I would've named this "power", but y'know. :P
|
|
#define INTEGRITY_EXPONENTIAL_DEGREE 2
|
|
|
|
// At INTEGRITY_MIN_NUDGABLE_AMOUNT, the power will be treated as, at most, INTEGRITY_MAX_POWER_NUDGE.
|
|
// Any lower integrity will result in INTEGRITY_MAX_POWER_NUDGE.
|
|
#define INTEGRITY_MAX_POWER_NUDGE 1500
|
|
#define INTEGRITY_MIN_NUDGABLE_AMOUNT 0.7
|
|
|
|
#define RADIATION_CHANCE_AT_FULL_INTEGRITY 0.03
|
|
#define RADIATION_CHANCE_AT_ZERO_INTEGRITY 0.4
|
|
#define CHANCE_EQUATION_SLOPE (RADIATION_CHANCE_AT_ZERO_INTEGRITY - RADIATION_CHANCE_AT_FULL_INTEGRITY)
|
|
|
|
/obj/machinery/power/supermatter_crystal/proc/emit_radiation()
|
|
// As power goes up, rads go up.
|
|
// A standard N2 SM seems to produce a value of around 1,500.
|
|
var/power_factor = min(internal_energy, MAX_ACCEPTED_POWER_OUTPUT)
|
|
|
|
var/integrity = 1 - CLAMP01(damage / explosion_point)
|
|
|
|
// When integrity goes down, the threshold (from an observable point of view, rads) go up.
|
|
// However, the power factor must go up as well, otherwise turning off the emitters
|
|
// on a delaminating SM would stop radiation from escaping.
|
|
// To fix this, lower integrities raise the power factor to a minimum.
|
|
var/integrity_power_nudge = LERP(INTEGRITY_MAX_POWER_NUDGE, 0, CLAMP01((integrity - INTEGRITY_MIN_NUDGABLE_AMOUNT) / (1 - INTEGRITY_MIN_NUDGABLE_AMOUNT)))
|
|
|
|
power_factor = max(power_factor, integrity_power_nudge)
|
|
|
|
// At the "normal" N2 power output (with max integrity), this is 0.7, which is enough to be stopped
|
|
// by the walls or the radation shutters.
|
|
// As integrity does down, rads go up.
|
|
var/threshold
|
|
switch(integrity)
|
|
if(0)
|
|
threshold = power_factor ? 0 : 1
|
|
if(1)
|
|
threshold = (THRESHOLD_EQUATION_SLOPE * power_factor + 1)
|
|
else
|
|
threshold = (THRESHOLD_EQUATION_SLOPE * power_factor + 1) ** ((1 / integrity) ** INTEGRITY_EXPONENTIAL_DEGREE)
|
|
|
|
// Calculating chance is done entirely on integrity, so that actively delaminating SMs feel more dangerous
|
|
var/chance = (CHANCE_EQUATION_SLOPE * (1 - integrity)) + RADIATION_CHANCE_AT_FULL_INTEGRITY
|
|
|
|
radiation_pulse(
|
|
src,
|
|
max_range = 8,
|
|
threshold = threshold,
|
|
chance = chance * 100,
|
|
)
|
|
|
|
/obj/machinery/power/supermatter_crystal/proc/processing_sound()
|
|
if(internal_energy)
|
|
soundloop.volume = clamp((50 + (internal_energy / 50)), 50, 100)
|
|
if(damage >= 300)
|
|
soundloop.mid_sounds = list('sound/machines/sm/loops/delamming.ogg' = 1)
|
|
else
|
|
soundloop.mid_sounds = list('sound/machines/sm/loops/calm.ogg' = 1)
|
|
|
|
//We play delam/neutral sounds at a rate determined by power and damage
|
|
if(last_accent_sound >= world.time || !prob(20))
|
|
return
|
|
var/aggression = min(((damage / 800) * (internal_energy / 2500)), 1.0) * 100
|
|
if(damage >= 300)
|
|
playsound(src, SFX_SM_DELAM, max(50, aggression), FALSE, 40, 30, falloff_distance = 10)
|
|
else
|
|
playsound(src, SFX_SM_CALM, max(50, aggression), FALSE, 25, 25, falloff_distance = 10)
|
|
var/next_sound = round((100 - aggression) * 5)
|
|
last_accent_sound = world.time + max(SUPERMATTER_ACCENT_SOUND_MIN_COOLDOWN, next_sound)
|
|
|
|
/obj/machinery/power/supermatter_crystal/proc/psychological_examination()
|
|
// Defaults to a value less than 1. Over time the psy_coeff goes to 0 if
|
|
// no supermatter soothers are nearby.
|
|
var/psy_coeff_diff = -0.05
|
|
for(var/mob/living/carbon/human/seen_by_sm in view(src, SM_HALLUCINATION_RANGE(internal_energy)))
|
|
// Someone (generally a Psychologist), when looking at the SM within hallucination range makes it easier to manage.
|
|
if(HAS_MIND_TRAIT(seen_by_sm, TRAIT_SUPERMATTER_SOOTHER))
|
|
psy_coeff_diff = 0.05
|
|
visible_hallucination_pulse(
|
|
center = src,
|
|
radius = SM_HALLUCINATION_RANGE(internal_energy),
|
|
hallucination_duration = internal_energy * hallucination_power,
|
|
hallucination_max_duration = 400 SECONDS,
|
|
)
|
|
psy_coeff = clamp(psy_coeff + psy_coeff_diff, 0, 1)
|
|
|
|
/obj/machinery/power/supermatter_crystal/proc/handle_high_power()
|
|
if(internal_energy <= POWER_PENALTY_THRESHOLD && damage <= danger_point) //If the power is above 5000 or if the damage is above 550
|
|
last_high_energy_accumulation_perspective_machines = SSmachines.times_fired //Prevent oddly high initial zap due to high energy zaps not getting triggered via too low energy.
|
|
return
|
|
var/range = 4
|
|
zap_cutoff = 1500
|
|
var/total_moles = absorbed_gasmix.total_moles()
|
|
var/pressure = absorbed_gasmix.return_pressure()
|
|
var/temp = absorbed_gasmix.temperature
|
|
if(pressure > 0 && temp > 0)
|
|
//You may be able to freeze the zapstate of the engine with good planning, we'll see
|
|
zap_cutoff = clamp(1.2e6 - (internal_energy * total_moles * 40) / temp, 1.4e5, 1.2e6)//If the core is cold, it's easier to jump, ditto if there are a lot of mols
|
|
//We should always be able to zap our way out of the default enclosure
|
|
//See supermatter_zap() for more details
|
|
range = clamp(internal_energy / pressure * 10, 2, 7)
|
|
var/flags = ZAP_SUPERMATTER_FLAGS
|
|
var/zap_count = 0
|
|
//Deal with power zaps
|
|
switch(internal_energy)
|
|
if(POWER_PENALTY_THRESHOLD to SEVERE_POWER_PENALTY_THRESHOLD)
|
|
zap_icon = DEFAULT_ZAP_ICON_STATE
|
|
zap_count = 2
|
|
if(SEVERE_POWER_PENALTY_THRESHOLD to CRITICAL_POWER_PENALTY_THRESHOLD)
|
|
zap_icon = SLIGHTLY_CHARGED_ZAP_ICON_STATE
|
|
//Uncaps the zap damage, it's maxed by the input power
|
|
//Objects take damage now
|
|
flags |= (ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE)
|
|
zap_count = 3
|
|
if(CRITICAL_POWER_PENALTY_THRESHOLD to INFINITY)
|
|
zap_icon = OVER_9000_ZAP_ICON_STATE
|
|
//It'll stun more now, and damage will hit harder, gloves are no garentee.
|
|
//Machines go boom
|
|
flags |= (ZAP_MOB_STUN | ZAP_MACHINE_EXPLOSIVE | ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE)
|
|
zap_count = 4
|
|
//Now we deal with damage shit
|
|
if (damage > danger_point && prob(20))
|
|
zap_count += 1
|
|
|
|
if(zap_count >= 1)
|
|
playsound(loc, 'sound/items/weapons/emitter2.ogg', 100, TRUE, extrarange = 10)
|
|
var/delta_time = (SSmachines.times_fired - last_high_energy_accumulation_perspective_machines) * SSmachines.wait / (1 SECONDS)
|
|
var/accumulated_energy = accumulate_energy(ZAP_ENERGY_ACCUMULATION_HIGH_ENERGY, energy = clamp(internal_energy * 3200, 6.4e6, 3.2e7) * delta_time)
|
|
if(accumulated_energy)
|
|
for(var/i in 1 to zap_count)
|
|
var/discharged_energy = discharge_energy(ZAP_ENERGY_ACCUMULATION_HIGH_ENERGY, portion = 1 - (1 - ZAP_ENERGY_DISCHARGE_PORTION) ** INVERSE(zap_count))
|
|
supermatter_zap(src, range = range, zap_str = discharged_energy, zap_flags = flags, zap_cutoff = src.zap_cutoff, power_level = internal_energy, zap_icon = src.zap_icon)
|
|
last_high_energy_accumulation_perspective_machines = SSmachines.times_fired
|
|
if(prob(5))
|
|
supermatter_anomaly_gen(get_ranged_target_turf(src, pick(GLOB.cardinals), rand(5, 10)), FLUX_ANOMALY, 3)
|
|
if(prob(5))
|
|
supermatter_anomaly_gen(get_ranged_target_turf(src, pick(GLOB.cardinals), rand(5, 10)), HALLUCINATION_ANOMALY, 3)
|
|
if(internal_energy > SEVERE_POWER_PENALTY_THRESHOLD && prob(5) || prob(1))
|
|
supermatter_anomaly_gen(get_ranged_target_turf(src, pick(GLOB.cardinals), rand(5, 10)), GRAVITATIONAL_ANOMALY, 3)
|
|
if((internal_energy > SEVERE_POWER_PENALTY_THRESHOLD && prob(2)) || (prob(0.3) && internal_energy > POWER_PENALTY_THRESHOLD))
|
|
supermatter_anomaly_gen(get_ranged_target_turf(src, pick(GLOB.cardinals), rand(5, 10)), PYRO_ANOMALY, 3)
|
|
|
|
/obj/machinery/power/supermatter_crystal/proc/supermatter_pull(turf/center, pull_range = 3)
|
|
playsound(center, 'sound/items/weapons/marauder.ogg', 100, TRUE, extrarange = pull_range - world.view)
|
|
for(var/atom/movable/movable_atom in orange(pull_range,center))
|
|
if((movable_atom.anchored || movable_atom.move_resist >= MOVE_FORCE_EXTREMELY_STRONG)) //move resist memes.
|
|
if(istype(movable_atom, /obj/structure/closet))
|
|
var/obj/structure/closet/closet = movable_atom
|
|
closet.open(force = TRUE)
|
|
continue
|
|
if(ismob(movable_atom))
|
|
var/mob/pulled_mob = movable_atom
|
|
if(pulled_mob.mob_negates_gravity())
|
|
continue //You can't pull someone nailed to the deck
|
|
step_towards(movable_atom,center)
|
|
|
|
/proc/supermatter_anomaly_gen(turf/anomalycenter, type = FLUX_ANOMALY, anomalyrange = 5, has_changed_lifespan = TRUE)
|
|
var/turf/local_turf = pick(RANGE_TURFS(anomalyrange, anomalycenter))
|
|
if(!local_turf)
|
|
return
|
|
switch(type)
|
|
if(FLUX_ANOMALY)
|
|
var/explosive = has_changed_lifespan ? FLUX_NO_EMP : FLUX_LIGHT_EMP
|
|
new /obj/effect/anomaly/flux(local_turf, has_changed_lifespan ? rand(25 SECONDS, 35 SECONDS) : null, FALSE, explosive)
|
|
if(GRAVITATIONAL_ANOMALY)
|
|
new /obj/effect/anomaly/grav(local_turf, has_changed_lifespan ? rand(20 SECONDS, 30 SECONDS) : null, FALSE)
|
|
if(PYRO_ANOMALY)
|
|
new /obj/effect/anomaly/pyro(local_turf, has_changed_lifespan ? rand(15 SECONDS, 25 SECONDS) : null, FALSE)
|
|
if(HALLUCINATION_ANOMALY)
|
|
new /obj/effect/anomaly/hallucination/supermatter(local_turf, has_changed_lifespan ? rand(15 SECONDS, 25 SECONDS) : null, FALSE)
|
|
if(VORTEX_ANOMALY)
|
|
new /obj/effect/anomaly/bhole(local_turf, 2 SECONDS, FALSE)
|
|
if(BIOSCRAMBLER_ANOMALY)
|
|
new /obj/effect/anomaly/bioscrambler/docile(local_turf, null, FALSE)
|
|
if(DIMENSIONAL_ANOMALY)
|
|
new /obj/effect/anomaly/dimensional(local_turf, null, FALSE)
|
|
|
|
#undef CHANCE_EQUATION_SLOPE
|
|
#undef INTEGRITY_EXPONENTIAL_DEGREE
|
|
#undef INTEGRITY_MAX_POWER_NUDGE
|
|
#undef INTEGRITY_MIN_NUDGABLE_AMOUNT
|
|
#undef MAX_ACCEPTED_POWER_OUTPUT
|
|
#undef RADIATION_CHANCE_AT_FULL_INTEGRITY
|
|
#undef RADIATION_CHANCE_AT_ZERO_INTEGRITY
|
|
#undef THRESHOLD_EQUATION_SLOPE
|