diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index 42199174e7..c12ec20de2 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -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" ////////////////////////////////////////////////////////////////// diff --git a/code/controllers/subsystem/processing/weather.dm b/code/controllers/subsystem/processing/weather.dm index ca067953cc..4035149ef2 100644 --- a/code/controllers/subsystem/processing/weather.dm +++ b/code/controllers/subsystem/processing/weather.dm @@ -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 diff --git a/code/controllers/subsystem/sun.dm b/code/controllers/subsystem/sun.dm index 442329cf46..746b1be7a9 100644 --- a/code/controllers/subsystem/sun.dm +++ b/code/controllers/subsystem/sun.dm @@ -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 diff --git a/code/datums/weather/weather_types/radiation_storm.dm b/code/datums/weather/weather_types/radiation_storm.dm index 4638508d1c..337be3e005 100644 --- a/code/datums/weather/weather_types/radiation_storm.dm +++ b/code/datums/weather/weather_types/radiation_storm.dm @@ -22,7 +22,7 @@ target_trait = ZTRAIT_STATION immunity_type = "rad" - + var/radiation_intensity = 100 /datum/weather/rad_storm/telegraph() diff --git a/code/game/gamemodes/bloodsucker/bloodsucker.dm b/code/game/gamemodes/bloodsucker/bloodsucker.dm index c54de16e2e..a16e7eab59 100644 --- a/code/game/gamemodes/bloodsucker/bloodsucker.dm +++ b/code/game/gamemodes/bloodsucker/bloodsucker.dm @@ -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 diff --git a/code/modules/antagonists/bloodsucker/bloodsucker_sunlight.dm b/code/modules/antagonists/bloodsucker/bloodsucker_sunlight.dm index 2cb50c6b3b..f26152f90e 100644 --- a/code/modules/antagonists/bloodsucker/bloodsucker_sunlight.dm +++ b/code/modules/antagonists/bloodsucker/bloodsucker_sunlight.dm @@ -7,98 +7,79 @@ // 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,"Solar Flares will bombard the station with dangerous UV in [TIME_BLOODSUCKER_DAY_WARN / 60] minutes. Prepare to seek cover in a coffin or closet.") // 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,"Solar Flares are about to bombard the station! You have [TIME_BLOODSUCKER_DAY_FINAL_WARN] seconds to find cover!",\ - "In [TIME_BLOODSUCKER_DAY_FINAL_WARN / 10], your master will be at risk of a Solar Flare. Make sure they find cover!") - - // (FINAL LIL WARNING) - while(time_til_cycle > 5) - sleep(10) - if(cancel_me) - return - //sleep(TIME_BLOODSUCKER_DAY_FINAL_WARN - 50) - warn_daylight(3,"Seek cover, for Sol rises!") - - // Part 3: Night Ending - while(time_til_cycle > 0) - sleep(10) - if(cancel_me) - return - //sleep(50) - warn_daylight(4,"Solar flares bombard the station with deadly UV light!
Stay in cover for the next [TIME_BLOODSUCKER_DAY / 60] minutes or risk Final Death!",\ - "Solar flares bombard the station with UV light!") - - // 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) +/obj/effect/sunlight/process() + // Update all Bloodsucker sunlight huds + 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.update_sunlight(max(0, time_til_cycle), amDay) // This pings all HUDs + time_til_cycle-- + if(amDay) + if(time_til_cycle > 0 && time_til_cycle % 4 == 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() + // 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,"The solar flare has ended, and the daylight danger has passed...for now.",\ "The solar flare has ended, and the daylight danger has passed...for now.") 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) - // Update all Bloodsucker sunlight huds + 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)) - bloodsuckerdatum.update_sunlight(max(0, time_til_cycle), amDay) // This pings all HUDs - sleep(10) - time_til_cycle -- + 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,"Solar Flares will bombard the station with dangerous UV in [TIME_BLOODSUCKER_DAY_WARN / 60] minutes. Prepare to seek cover in a coffin or closet.") // 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,"Solar Flares are about to bombard the station! You have [TIME_BLOODSUCKER_DAY_FINAL_WARN] seconds to find cover!",\ + "In [TIME_BLOODSUCKER_DAY_FINAL_WARN / 10], your master will be at risk of a Solar Flare. Make sure they find cover!") + if(5) + warn_daylight(3,"Seek cover, for Sol rises!") + if(0) + warn_daylight(4,"Solar flares bombard the station with deadly UV light!
Stay in cover for the next [TIME_BLOODSUCKER_DAY / 60] minutes or risk Final Death!",\ + "Solar flares bombard the station with UV light!") + 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) diff --git a/code/modules/events/supernova.dm b/code/modules/events/supernova.dm new file mode 100644 index 0000000000..9766ab6212 --- /dev/null +++ b/code/modules/events/supernova.dm @@ -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,"A supernova will bombard the station with dangerous UV in [90 / 60] minutes. Prepare to seek cover in a coffin or closet.") + 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 diff --git a/code/modules/power/solar.dm b/code/modules/power/solar.dm index 855c579926..92aa95bfb7 100644 --- a/code/modules/power/solar.dm +++ b/code/modules/power/solar.dm @@ -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 - if(azimuth_current == sun_azimuth) //just a quick optimization for the most frequent case - . = 1 - else - //dot product of sun and panel -- Lambert's Cosine Law - . = cos(azimuth_current - sun_azimuth) - . = clamp(round(., 0.01), 0, 1) - sunfrac = . + 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 + cur_pow = sun.power_mod + else + //dot product of sun and panel -- Lambert's Cosine Law + 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 diff --git a/code/modules/power/tracker.dm b/code/modules/power/tracker.dm index 0627a55de0..86168979c2 100644 --- a/code/modules/power/tracker.dm +++ b/code/modules/power/tracker.dm @@ -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) diff --git a/tgstation.dme b/tgstation.dme index dde95198e4..7879a87e32 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -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"