Merge pull request #14306 from Putnam3145/supernova

Adds a supernova event, with tweaks to allow this
This commit is contained in:
silicons
2021-03-10 01:32:52 -07:00
committed by GitHub
10 changed files with 201 additions and 157 deletions

View File

@@ -22,7 +22,7 @@
#define COMPONENT_GLOB_BLOCK_CINEMATIC 1
// signals from globally accessible objects
/// from SSsun when the sun changes position : (azimuth)
/// from SSsun when the sun changes position : (primary_sun, suns)
#define COMSIG_SUN_MOVED "sun_moved"
//////////////////////////////////////////////////////////////////

View File

@@ -70,3 +70,8 @@ PROCESSING_SUBSYSTEM_DEF(weather)
A = W
break
return A
/datum/controller/subsystem/processing/weather/proc/get_weather_by_type(datum/weather/weather_datum_type)
for(var/V in processing)
if(istype(V,weather_datum_type))
return V

View File

@@ -1,32 +1,63 @@
#define OCCLUSION_DISTANCE 20
/datum/sun
var/azimuth = 0 // clockwise, top-down rotation from 0 (north) to 359
var/power_mod = 1 // how much power this sun is outputting relative to standard
/datum/sun/vv_edit_var(var_name, var_value)
. = ..()
if(var_name == NAMEOF(src, azimuth))
SSsun.complete_movement()
/atom/proc/check_obscured(datum/sun/sun, distance = OCCLUSION_DISTANCE)
var/target_x = round(sin(sun.azimuth), 0.01)
var/target_y = round(cos(sun.azimuth), 0.01)
var/x_hit = x
var/y_hit = y
var/turf/hit
for(var/run in 1 to distance)
x_hit += target_x
y_hit += target_y
hit = locate(round(x_hit, 1), round(y_hit, 1), z)
if(hit.opacity)
return TRUE
if(hit.x == 1 || hit.x == world.maxx || hit.y == 1 || hit.y == world.maxy) //edge of the map
break
return FALSE
SUBSYSTEM_DEF(sun)
name = "Sun"
wait = 1 MINUTES
flags = SS_NO_TICK_CHECK
var/azimuth = 0 ///clockwise, top-down rotation from 0 (north) to 359
var/list/datum/sun/suns = list()
var/datum/sun/primary_sun
var/azimuth_mod = 1 ///multiplier against base_rotation
var/base_rotation = 6 ///base rotation in degrees per fire
/datum/controller/subsystem/sun/Initialize(start_timeofday)
azimuth = rand(0, 359)
primary_sun = new
suns += primary_sun
primary_sun.azimuth = rand(0, 359)
azimuth_mod = round(rand(50, 200)/100, 0.01) // 50% - 200% of standard rotation
if(prob(50))
azimuth_mod *= -1
return ..()
/datum/controller/subsystem/sun/fire(resumed = FALSE)
azimuth += azimuth_mod * base_rotation
azimuth = round(azimuth, 0.01)
if(azimuth >= 360)
azimuth -= 360
if(azimuth < 0)
azimuth += 360
for(var/S in suns)
var/datum/sun/sun = S
sun.azimuth += azimuth_mod * base_rotation
sun.azimuth = round(sun.azimuth, 0.01)
if(sun.azimuth >= 360)
sun.azimuth -= 360
if(sun.azimuth < 0)
sun.azimuth += 360
complete_movement()
/datum/controller/subsystem/sun/proc/complete_movement()
SEND_SIGNAL(src, COMSIG_SUN_MOVED, azimuth)
SEND_SIGNAL(src, COMSIG_SUN_MOVED, primary_sun, suns)
/datum/controller/subsystem/sun/vv_edit_var(var_name, var_value)
. = ..()
if(var_name == NAMEOF(src, azimuth))
complete_movement()
#undef OCCLUSION_DISTANCE

View File

@@ -87,7 +87,7 @@
// Init Sunlight (called from datum_bloodsucker.on_gain(), in case game mode isn't even Bloodsucker
/datum/game_mode/proc/check_start_sunlight()
// Already Sunlight (and not about to cancel)
if(istype(bloodsucker_sunlight) && !bloodsucker_sunlight.cancel_me)
if(istype(bloodsucker_sunlight))
return
bloodsucker_sunlight = new ()
@@ -97,7 +97,6 @@
if(!istype(bloodsucker_sunlight))
return
if(bloodsuckers.len <= 0)
bloodsucker_sunlight.cancel_me = TRUE
qdel(bloodsucker_sunlight)
bloodsucker_sunlight = null

View File

@@ -7,89 +7,19 @@
// Over Time, tick down toward a "Solar Flare" of UV buffeting the station. This period is harmful to vamps.
/obj/effect/sunlight
//var/amDay = FALSE
var/cancel_me = FALSE
var/amDay = FALSE
var/time_til_cycle = 0
var/nightime_duration = 900 //15 Minutes
var/nighttime_duration = 900 //15 Minutes
var/issued_XP = FALSE
/obj/effect/sunlight/Initialize()
. = ..()
INVOKE_ASYNC(src, .proc/countdown)
INVOKE_ASYNC(src, .proc/hud_tick)
/obj/effect/sunlight/proc/countdown()
set waitfor = FALSE
/obj/effect/sunlight/proc/start_countdown()
START_PROCESSING(SSweather, src) //it counts as weather right
time_til_cycle = nighttime_duration
while(!cancel_me)
time_til_cycle = nightime_duration
// Part 1: Night (all is well)
while(time_til_cycle > TIME_BLOODSUCKER_DAY_WARN)
sleep(10)
if(cancel_me)
return
//sleep(TIME_BLOODSUCKER_NIGHT - TIME_BLOODSUCKER_DAY_WARN)
warn_daylight(1,"<span class = 'danger'>Solar Flares will bombard the station with dangerous UV in [TIME_BLOODSUCKER_DAY_WARN / 60] minutes. <b>Prepare to seek cover in a coffin or closet.</b></span>") // time2text <-- use Help On
give_home_power() // Give VANISHING ACT power to all vamps with a lair!
// Part 2: Night Ending
while(time_til_cycle > TIME_BLOODSUCKER_DAY_FINAL_WARN)
sleep(10)
if(cancel_me)
return
//sleep(TIME_BLOODSUCKER_DAY_WARN - TIME_BLOODSUCKER_DAY_FINAL_WARN)
message_admins("BLOODSUCKER NOTICE: Daylight beginning in [TIME_BLOODSUCKER_DAY_FINAL_WARN] seconds.)")
warn_daylight(2,"<span class = 'userdanger'>Solar Flares are about to bombard the station! You have [TIME_BLOODSUCKER_DAY_FINAL_WARN] seconds to find cover!</span>",\
"<span class = 'danger'>In [TIME_BLOODSUCKER_DAY_FINAL_WARN / 10], your master will be at risk of a Solar Flare. Make sure they find cover!</span>")
// (FINAL LIL WARNING)
while(time_til_cycle > 5)
sleep(10)
if(cancel_me)
return
//sleep(TIME_BLOODSUCKER_DAY_FINAL_WARN - 50)
warn_daylight(3,"<span class = 'userdanger'>Seek cover, for Sol rises!</span>")
// Part 3: Night Ending
while(time_til_cycle > 0)
sleep(10)
if(cancel_me)
return
//sleep(50)
warn_daylight(4,"<span class = 'userdanger'>Solar flares bombard the station with deadly UV light!</span><br><span class = ''>Stay in cover for the next [TIME_BLOODSUCKER_DAY / 60] minutes or risk Final Death!</span>",\
"<span class = 'danger'>Solar flares bombard the station with UV light!</span>")
// Part 4: Day
amDay = TRUE
message_admins("BLOODSUCKER NOTICE: Daylight Beginning (Lasts for [TIME_BLOODSUCKER_DAY / 60] minutes.)")
time_til_cycle = TIME_BLOODSUCKER_DAY
sleep(10) // One second grace period.
//var/daylight_time = TIME_BLOODSUCKER_DAY
var/issued_XP = FALSE
while(time_til_cycle > 0)
punish_vamps()
sleep(TIME_BLOODSUCKER_BURN_INTERVAL)
if(cancel_me)
return
//daylight_time -= TIME_BLOODSUCKER_BURN_INTERVAL
// Issue Level Up!
if(!issued_XP && time_til_cycle <= 5)
issued_XP = TRUE
vamps_rank_up()
warn_daylight(5,"<span class = 'announce'>The solar flare has ended, and the daylight danger has passed...for now.</span>",\
"<span class = 'announce'>The solar flare has ended, and the daylight danger has passed...for now.</span>")
amDay = FALSE
day_end() // Remove VANISHING ACT power from all vamps who have it! Clear Warnings (sunlight, locker protection)
nightime_duration += 100 //Each day makes the night a minute longer.
message_admins("BLOODSUCKER NOTICE: Daylight Ended. Resetting to Night (Lasts for [nightime_duration / 60] minutes.)")
/obj/effect/sunlight/proc/hud_tick()
set waitfor = FALSE
while(!cancel_me)
/obj/effect/sunlight/process()
// Update all Bloodsucker sunlight huds
for(var/datum/mind/M in SSticker.mode.bloodsuckers)
if(!istype(M) || !istype(M.current))
@@ -97,8 +27,59 @@
var/datum/antagonist/bloodsucker/bloodsuckerdatum = M.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
if(istype(bloodsuckerdatum))
bloodsuckerdatum.update_sunlight(max(0, time_til_cycle), amDay) // This pings all HUDs
sleep(10)
time_til_cycle --
time_til_cycle--
if(amDay)
if(time_til_cycle > 0 && time_til_cycle % 4 == 0)
punish_vamps()
if(!issued_XP && time_til_cycle <= 5)
issued_XP = TRUE
// Cycle through all vamp antags and check if they're inside a closet.
for(var/datum/mind/M in SSticker.mode.bloodsuckers)
if(!istype(M) || !istype(M.current))
continue
var/datum/antagonist/bloodsucker/bloodsuckerdatum = M.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
if(istype(bloodsuckerdatum))
bloodsuckerdatum.RankUp() // Rank up! Must still be in a coffin to level!
warn_daylight(5,"<span class = 'announce'>The solar flare has ended, and the daylight danger has passed...for now.</span>",\
"<span class = 'announce'>The solar flare has ended, and the daylight danger has passed...for now.</span>")
amDay = FALSE
issued_XP = FALSE
for(var/datum/mind/M in SSticker.mode.bloodsuckers)
if(!istype(M) || !istype(M.current))
continue
var/datum/antagonist/bloodsucker/bloodsuckerdatum = M.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
if(!istype(bloodsuckerdatum))
continue
// Reset Warnings
bloodsuckerdatum.warn_sun_locker = FALSE
bloodsuckerdatum.warn_sun_burn = FALSE
// Remove Dawn Powers
for(var/datum/action/bloodsucker/P in bloodsuckerdatum.powers)
if(istype(P, /datum/action/bloodsucker/gohome))
bloodsuckerdatum.powers -= P
P.Remove(M.current)
nighttime_duration += 100 //Each day makes the night a minute longer.
time_til_cycle = nighttime_duration
message_admins("BLOODSUCKER NOTICE: Daylight Ended. Resetting to Night (Lasts for [nighttime_duration / 60] minutes.)")
else
switch(time_til_cycle)
if(TIME_BLOODSUCKER_DAY_WARN)
//sleep(TIME_BLOODSUCKER_NIGHT - TIME_BLOODSUCKER_DAY_WARN)
warn_daylight(1,"<span class = 'danger'>Solar Flares will bombard the station with dangerous UV in [TIME_BLOODSUCKER_DAY_WARN / 60] minutes. <b>Prepare to seek cover in a coffin or closet.</b></span>") // time2text <-- use Help On
give_home_power() // Give VANISHING ACT power to all vamps with a lair!
if(TIME_BLOODSUCKER_DAY_FINAL_WARN)
message_admins("BLOODSUCKER NOTICE: Daylight beginning in [TIME_BLOODSUCKER_DAY_FINAL_WARN] seconds.)")
warn_daylight(2,"<span class = 'userdanger'>Solar Flares are about to bombard the station! You have [TIME_BLOODSUCKER_DAY_FINAL_WARN] seconds to find cover!</span>",\
"<span class = 'danger'>In [TIME_BLOODSUCKER_DAY_FINAL_WARN / 10], your master will be at risk of a Solar Flare. Make sure they find cover!</span>")
if(5)
warn_daylight(3,"<span class = 'userdanger'>Seek cover, for Sol rises!</span>")
if(0)
warn_daylight(4,"<span class = 'userdanger'>Solar flares bombard the station with deadly UV light!</span><br><span class = ''>Stay in cover for the next [TIME_BLOODSUCKER_DAY / 60] minutes or risk Final Death!</span>",\
"<span class = 'danger'>Solar flares bombard the station with UV light!</span>")
amDay = TRUE
message_admins("BLOODSUCKER NOTICE: Daylight Beginning (Lasts for [TIME_BLOODSUCKER_DAY / 60] minutes.)")
time_til_cycle = TIME_BLOODSUCKER_DAY
/obj/effect/sunlight/proc/warn_daylight(danger_level =0, vampwarn = "", vassalwarn = "")
for(var/datum/mind/M in SSticker.mode.bloodsuckers)
@@ -162,32 +143,6 @@
M.current.updatehealth()
SEND_SIGNAL(M.current, COMSIG_ADD_MOOD_EVENT, "vampsleep", /datum/mood_event/daylight_2)
/obj/effect/sunlight/proc/day_end()
for(var/datum/mind/M in SSticker.mode.bloodsuckers)
if(!istype(M) || !istype(M.current))
continue
var/datum/antagonist/bloodsucker/bloodsuckerdatum = M.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
if(!istype(bloodsuckerdatum))
continue
// Reset Warnings
bloodsuckerdatum.warn_sun_locker = FALSE
bloodsuckerdatum.warn_sun_burn = FALSE
// Remove Dawn Powers
for(var/datum/action/bloodsucker/P in bloodsuckerdatum.powers)
if(istype(P, /datum/action/bloodsucker/gohome))
bloodsuckerdatum.powers -= P
P.Remove(M.current)
/obj/effect/sunlight/proc/vamps_rank_up()
set waitfor = FALSE
// Cycle through all vamp antags and check if they're inside a closet.
for(var/datum/mind/M in SSticker.mode.bloodsuckers)
if(!istype(M) || !istype(M.current))
continue
var/datum/antagonist/bloodsucker/bloodsuckerdatum = M.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
if(istype(bloodsuckerdatum))
bloodsuckerdatum.RankUp() // Rank up! Must still be in a coffin to level!
/obj/effect/sunlight/proc/give_home_power()
// It's late...! Give the "Vanishing Act" gohome power to bloodsuckers.
for(var/datum/mind/M in SSticker.mode.bloodsuckers)

View File

@@ -0,0 +1,67 @@
/datum/round_event_control/supernova
name = "Supernova"
typepath = /datum/round_event/supernova
weight = 10
max_occurrences = 2
min_players = 2
/datum/round_event/supernova
announceWhen = 40
startWhen = 1
endWhen = 300
var/power = 1
var/datum/sun/supernova
var/storm_count = 0
/datum/round_event/supernova/setup()
announceWhen = rand(4, 60)
supernova = new
SSsun.suns += supernova
if(prob(50))
power = rand(5,100) / 100
else
power = rand(5,5000) / 100
supernova.azimuth = rand(0, 359)
supernova.power_mod = 0
/datum/round_event/supernova/announce()
var/message = "Our tachyon-doppler array has detected a supernova in your vicinity. Peak flux from the supernova estimated to be [round(power,0.1)] times current solar flux. [power > 4 ? "Short burts of radiation may be possible, so please prepare accordingly." : ""]"
if(prob(power * 25))
priority_announce(message)
else
print_command_report(message)
/datum/round_event/supernova/start()
supernova.power_mod = 0.00000002 * power
var/explosion_size = rand(1000000000, 999999999)
var/turf/epicenter = get_turf_in_angle(supernova.azimuth, SSmapping.get_station_center(), world.maxx / 2)
for(var/array in GLOB.doppler_arrays)
var/obj/machinery/doppler_array/A = array
A.sense_explosion(epicenter, explosion_size/2, explosion_size, 0, 107000000 / power, explosion_size/2, explosion_size, 0)
if(power > 1 && SSticker.mode.bloodsucker_sunlight?.time_til_cycle > 90)
var/obj/effect/sunlight/sucker_light = SSticker.mode.bloodsucker_sunlight
sucker_light.time_til_cycle = 90
sucker_light.warn_daylight(1,"<span class = 'danger'>A supernova will bombard the station with dangerous UV in [90 / 60] minutes. <b>Prepare to seek cover in a coffin or closet.</b></span>")
sucker_light.give_home_power()
/datum/round_event/supernova/tick()
var/midpoint = (endWhen-startWhen)/2
switch(activeFor)
if(startWhen to midpoint)
supernova.power_mod = min(supernova.power_mod*1.2, power)
if(endWhen-10 to endWhen)
supernova.power_mod /= 4
if(prob(round(supernova.power_mod / 2)) && storm_count < 3 && !SSweather.get_weather_by_type(/datum/weather/rad_storm))
SSweather.run_weather(/datum/weather/rad_storm/supernova)
storm_count++
/datum/round_event/supernova/end()
SSsun.suns -= supernova
qdel(supernova)
/datum/weather/rad_storm/supernova
weather_duration_lower = 50
weather_duration_lower = 100
telegraph_duration = 100
radiation_intensity = 50

View File

@@ -1,5 +1,4 @@
#define SOLAR_GEN_RATE 1500
#define OCCLUSION_DISTANCE 20
/obj/machinery/power/solar
name = "solar panel"
@@ -14,8 +13,8 @@
integrity_failure = 0.33
var/id
var/obscured = FALSE
var/sunfrac = 0 //[0-1] measure of obscuration -- multipllier against power generation
var/list/obscured = list()
var/total_flux = 0 // multipllier against power generation -- measured by obscuration of all suns
var/azimuth_current = 0 //[0-360) degrees, which direction are we facing?
var/azimuth_target = 0 //same but what way we're going to face next time we turn
var/obj/machinery/power/solar_control/control
@@ -133,40 +132,28 @@
///trace towards sun to see if we're in shadow
/obj/machinery/power/solar/proc/occlusion_setup()
obscured = TRUE
var/distance = OCCLUSION_DISTANCE
var/target_x = round(sin(SSsun.azimuth), 0.01)
var/target_y = round(cos(SSsun.azimuth), 0.01)
var/x_hit = x
var/y_hit = y
var/turf/hit
for(var/run in 1 to distance)
x_hit += target_x
y_hit += target_y
hit = locate(round(x_hit, 1), round(y_hit, 1), z)
if(hit.opacity)
return
if(hit.x == 1 || hit.x == world.maxx || hit.y == 1 || hit.y == world.maxy) //edge of the map
break
obscured = FALSE
obscured = list()
for(var/S in SSsun.suns)
if(check_obscured(S))
obscured |= S
///calculates the fraction of the sunlight that the panel receives
/obj/machinery/power/solar/proc/update_solar_exposure()
needs_to_update_solar_exposure = FALSE
sunfrac = 0
if(obscured)
return 0
var/sun_azimuth = SSsun.azimuth
total_flux = 0
for(var/S in SSsun.suns)
if(S in obscured)
continue
var/datum/sun/sun = S
var/sun_azimuth = sun.azimuth
var/cur_pow = 0
if(azimuth_current == sun_azimuth) //just a quick optimization for the most frequent case
. = 1
cur_pow = sun.power_mod
else
//dot product of sun and panel -- Lambert's Cosine Law
. = cos(azimuth_current - sun_azimuth)
. = clamp(round(., 0.01), 0, 1)
sunfrac = .
cur_pow = cos(azimuth_current - sun_azimuth) * sun.power_mod
cur_pow = clamp(round(cur_pow, 0.01), 0, 1)
total_flux += cur_pow
/obj/machinery/power/solar/process()
if(stat & BROKEN)
@@ -177,10 +164,10 @@
update_turn()
if(needs_to_update_solar_exposure)
update_solar_exposure()
if(sunfrac <= 0)
if(total_flux <= 0)
return
var/sgen = SOLAR_GEN_RATE * sunfrac * efficiency
var/sgen = SOLAR_GEN_RATE * total_flux * efficiency
add_avail(sgen)
if(control)
control.gen += sgen
@@ -385,7 +372,7 @@
track = mode
if(mode == SOLAR_TRACK_AUTO)
if(connected_tracker)
connected_tracker.sun_update(SSsun, SSsun.azimuth)
connected_tracker.sun_update(SSsun, SSsun.primary_sun, SSsun.suns)
else
track = SOLAR_TRACK_OFF
return TRUE
@@ -483,4 +470,3 @@ Congratulations, you should have a working solar array. If you are having troubl
"}
#undef SOLAR_GEN_RATE
#undef OCCLUSION_DISTANCE

View File

@@ -41,10 +41,10 @@
control = null
///Tell the controller to turn the solar panels
/obj/machinery/power/tracker/proc/sun_update(datum/source, azimuth)
setDir(angle2dir(azimuth))
/obj/machinery/power/tracker/proc/sun_update(datum/source, datum/sun/primary_sun, list/datum/sun/suns)
setDir(angle2dir(primary_sun.azimuth))
if(control && control.track == SOLAR_TRACK_AUTO)
control.set_panels(azimuth)
control.set_panels(primary_sun.azimuth)
/obj/machinery/power/tracker/proc/Make(obj/item/solar_assembly/S)
if(!S)

View File

@@ -2069,6 +2069,7 @@
#include "code\modules\events\spontaneous_appendicitis.dm"
#include "code\modules\events\stray_cargo.dm"
#include "code\modules\events\supermatter_surge.dm"
#include "code\modules\events\supernova.dm"
#include "code\modules\events\travelling_trader.dm"
#include "code\modules\events\vent_clog.dm"
#include "code\modules\events\wisdomcow.dm"