mirror of
https://github.com/VOREStation/VOREStation.git
synced 2026-08-21 19:19:12 +01:00
Areas can now be soundproofed to block sounds from other areas from entering or sounds within the area from leaving. Will send upstream tomorrow.
529 lines
16 KiB
Plaintext
529 lines
16 KiB
Plaintext
// Areas.dm
|
|
|
|
/area
|
|
var/fire = null
|
|
var/atmos = 1
|
|
var/atmosalm = 0
|
|
var/poweralm = 1
|
|
var/party = null
|
|
level = null
|
|
name = "Unknown"
|
|
icon = 'icons/turf/areas.dmi'
|
|
icon_state = "unknown"
|
|
plane = PLANE_LIGHTING_ABOVE //In case we color them
|
|
luminosity = 0
|
|
mouse_opacity = 0
|
|
var/lightswitch = 1
|
|
|
|
var/eject = null
|
|
|
|
var/debug = 0
|
|
var/requires_power = 1
|
|
var/always_unpowered = 0 //this gets overriden to 1 for space in area/New()
|
|
|
|
// Power channel status - Is it currently energized?
|
|
var/power_equip = TRUE
|
|
var/power_light = TRUE
|
|
var/power_environ = TRUE
|
|
|
|
// Oneoff power usage - Used once and cleared each power cycle
|
|
var/oneoff_equip = 0
|
|
var/oneoff_light = 0
|
|
var/oneoff_environ = 0
|
|
|
|
// Continuous "static" power usage - Do not update these directly!
|
|
var/static_equip = 0
|
|
var/static_light = 0
|
|
var/static_environ = 0
|
|
|
|
var/music = null
|
|
var/has_gravity = 1
|
|
var/secret_name = FALSE // This tells certain things that display areas' names that they shouldn't display this area's name.
|
|
var/obj/machinery/power/apc/apc = null
|
|
var/no_air = null
|
|
// var/list/lights // list of all lights on this area
|
|
var/list/all_doors = null //Added by Strumpetplaya - Alarm Change - Contains a list of doors adjacent to this area
|
|
var/firedoors_closed = 0
|
|
var/list/ambience = list()
|
|
var/list/forced_ambience = null
|
|
var/sound_env = STANDARD_STATION
|
|
var/turf/base_turf //The base turf type of the area, which can be used to override the z-level's base turf
|
|
var/forbid_events = FALSE // If true, random events will not start inside this area.
|
|
var/no_spoilers = FALSE // If true, makes it much more difficult to see what is inside an area with things like mesons.
|
|
var/soundproofed = FALSE // If true, blocks sounds from other areas and prevents hearers on other areas from hearing the sounds within.
|
|
|
|
/area/Initialize()
|
|
. = ..()
|
|
|
|
luminosity = !(dynamic_lighting)
|
|
icon_state = ""
|
|
|
|
return INITIALIZE_HINT_LATELOAD // Areas tradiationally are initialized AFTER other atoms.
|
|
|
|
/area/LateInitialize()
|
|
if(!requires_power || !apc)
|
|
power_light = 0
|
|
power_equip = 0
|
|
power_environ = 0
|
|
power_change() // all machines set to current power level, also updates lighting icon
|
|
if(no_spoilers)
|
|
set_spoiler_obfuscation(TRUE)
|
|
return INITIALIZE_HINT_LATELOAD
|
|
|
|
// Changes the area of T to A. Do not do this manually.
|
|
// Area is expected to be a non-null instance.
|
|
/proc/ChangeArea(var/turf/T, var/area/A)
|
|
if(!istype(A))
|
|
CRASH("Area change attempt failed: invalid area supplied.")
|
|
var/area/old_area = get_area(T)
|
|
if(old_area == A)
|
|
return
|
|
// NOTE: BayStation calles area.Exited/Entered for the TURF T. So far we don't do that.s
|
|
// NOTE: There probably won't be any atoms in these turfs, but just in case we should call these procs.
|
|
A.contents.Add(T)
|
|
if(old_area)
|
|
// Handle dynamic lighting update if
|
|
if(T.dynamic_lighting && old_area.dynamic_lighting != A.dynamic_lighting)
|
|
if(A.dynamic_lighting)
|
|
T.lighting_build_overlay()
|
|
else
|
|
T.lighting_clear_overlay()
|
|
for(var/atom/movable/AM in T)
|
|
old_area.Exited(AM, A)
|
|
for(var/atom/movable/AM in T)
|
|
A.Entered(AM, old_area)
|
|
for(var/obj/machinery/M in T)
|
|
M.area_changed(old_area, A)
|
|
|
|
/area/proc/get_contents()
|
|
return contents
|
|
|
|
/area/proc/get_cameras()
|
|
var/list/cameras = list()
|
|
for (var/obj/machinery/camera/C in src)
|
|
cameras += C
|
|
return cameras
|
|
|
|
/area/proc/atmosalert(danger_level, var/alarm_source)
|
|
if (danger_level == 0)
|
|
atmosphere_alarm.clearAlarm(src, alarm_source)
|
|
else
|
|
var/obj/machinery/alarm/atmosalarm = alarm_source //maybe other things can trigger these, who knows
|
|
if(istype(atmosalarm))
|
|
atmosphere_alarm.triggerAlarm(src, alarm_source, severity = danger_level, hidden = atmosalarm.alarms_hidden)
|
|
else
|
|
atmosphere_alarm.triggerAlarm(src, alarm_source, severity = danger_level)
|
|
|
|
//Check all the alarms before lowering atmosalm. Raising is perfectly fine.
|
|
for (var/obj/machinery/alarm/AA in src)
|
|
if (!(AA.stat & (NOPOWER|BROKEN)) && !AA.shorted && AA.report_danger_level)
|
|
danger_level = max(danger_level, AA.danger_level)
|
|
|
|
if(danger_level != atmosalm)
|
|
atmosalm = danger_level
|
|
//closing the doors on red and opening on green provides a bit of hysteresis that will hopefully prevent fire doors from opening and closing repeatedly due to noise
|
|
if (danger_level < 1 || danger_level >= 2)
|
|
firedoors_update()
|
|
|
|
for (var/obj/machinery/alarm/AA in src)
|
|
AA.update_icon()
|
|
|
|
return 1
|
|
return 0
|
|
|
|
// Either close or open firedoors depending on current alert statuses
|
|
/area/proc/firedoors_update()
|
|
if(fire || party || atmosalm)
|
|
firedoors_close()
|
|
// VOREStation Edit - Make the lights colored!
|
|
if(fire)
|
|
for(var/obj/machinery/light/L in src)
|
|
L.set_alert_fire()
|
|
else if(atmosalm)
|
|
for(var/obj/machinery/light/L in src)
|
|
L.set_alert_atmos()
|
|
// VOREStation Edit End
|
|
else
|
|
firedoors_open()
|
|
// VOREStation Edit - Put the lights back!
|
|
for(var/obj/machinery/light/L in src)
|
|
L.reset_alert()
|
|
// VOREStation Edit End
|
|
|
|
// Close all firedoors in the area
|
|
/area/proc/firedoors_close()
|
|
if(!firedoors_closed)
|
|
firedoors_closed = TRUE
|
|
if(!all_doors)
|
|
return
|
|
for(var/obj/machinery/door/firedoor/E in all_doors)
|
|
if(!E.blocked)
|
|
if(E.operating)
|
|
E.nextstate = FIREDOOR_CLOSED
|
|
else if(!E.density)
|
|
spawn(0)
|
|
E.close()
|
|
|
|
// Open all firedoors in the area
|
|
/area/proc/firedoors_open()
|
|
if(firedoors_closed)
|
|
firedoors_closed = FALSE
|
|
if(!all_doors)
|
|
return
|
|
for(var/obj/machinery/door/firedoor/E in all_doors)
|
|
if(!E.blocked)
|
|
if(E.operating)
|
|
E.nextstate = FIREDOOR_OPEN
|
|
else if(E.density)
|
|
spawn(0)
|
|
E.open()
|
|
|
|
|
|
/area/proc/fire_alert()
|
|
if(!fire)
|
|
fire = 1 //used for firedoor checks
|
|
updateicon()
|
|
firedoors_update()
|
|
|
|
/area/proc/fire_reset()
|
|
if (fire)
|
|
fire = 0 //used for firedoor checks
|
|
updateicon()
|
|
firedoors_update()
|
|
|
|
/area/proc/readyalert()
|
|
if(!eject)
|
|
eject = 1
|
|
updateicon()
|
|
return
|
|
|
|
/area/proc/readyreset()
|
|
if(eject)
|
|
eject = 0
|
|
updateicon()
|
|
return
|
|
|
|
/area/proc/partyalert()
|
|
if (!( party ))
|
|
party = 1
|
|
updateicon()
|
|
firedoors_update()
|
|
return
|
|
|
|
/area/proc/partyreset()
|
|
if (party)
|
|
party = 0
|
|
updateicon()
|
|
firedoors_update()
|
|
return
|
|
|
|
/area/proc/updateicon()
|
|
if ((fire || eject || party) && (!requires_power||power_environ) && !istype(src, /area/space))//If it doesn't require power, can still activate this proc.
|
|
if(fire && !eject && !party)
|
|
icon_state = null // Let lights take care of it
|
|
/*else if(atmosalm && !fire && !eject && !party)
|
|
icon_state = "bluenew"*/
|
|
else if(!fire && eject && !party)
|
|
icon_state = "red"
|
|
else if(party && !fire && !eject)
|
|
icon_state = "party"
|
|
else
|
|
icon_state = "blue-red"
|
|
else
|
|
// new lighting behaviour with obj lights
|
|
icon_state = null
|
|
|
|
|
|
/*
|
|
#define EQUIP 1
|
|
#define LIGHT 2
|
|
#define ENVIRON 3
|
|
*/
|
|
|
|
/area/proc/powered(var/chan) // return true if the area has power to given channel
|
|
|
|
if(!requires_power)
|
|
return 1
|
|
if(always_unpowered)
|
|
return 0
|
|
switch(chan)
|
|
if(EQUIP)
|
|
return power_equip
|
|
if(LIGHT)
|
|
return power_light
|
|
if(ENVIRON)
|
|
return power_environ
|
|
|
|
return 0
|
|
|
|
// called when power status changes
|
|
/area/proc/power_change()
|
|
for(var/obj/machinery/M in src) // for each machine in the area
|
|
M.power_change() // reverify power status (to update icons etc.)
|
|
if (fire || eject || party)
|
|
updateicon()
|
|
|
|
/area/proc/usage(var/chan, var/include_static = TRUE)
|
|
var/used = 0
|
|
switch(chan)
|
|
if(LIGHT)
|
|
used += oneoff_light + (include_static * static_light)
|
|
if(EQUIP)
|
|
used += oneoff_equip + (include_static * static_equip)
|
|
if(ENVIRON)
|
|
used += oneoff_environ + (include_static * static_environ)
|
|
if(TOTAL)
|
|
used += oneoff_light + (include_static * static_light)
|
|
used += oneoff_equip + (include_static * static_equip)
|
|
used += oneoff_environ + (include_static * static_environ)
|
|
return used
|
|
|
|
// Helper for APCs; will generally be called every tick.
|
|
/area/proc/clear_usage()
|
|
oneoff_equip = 0
|
|
oneoff_light = 0
|
|
oneoff_environ = 0
|
|
|
|
// Use this for a one-time power draw from the area, typically for non-machines.
|
|
/area/proc/use_power_oneoff(var/amount, var/chan)
|
|
switch(chan)
|
|
if(EQUIP)
|
|
oneoff_equip += amount
|
|
if(LIGHT)
|
|
oneoff_light += amount
|
|
if(ENVIRON)
|
|
oneoff_environ += amount
|
|
return amount
|
|
|
|
// This is used by machines to properly update the area of power changes.
|
|
/area/proc/power_use_change(old_amount, new_amount, chan)
|
|
use_power_static(new_amount - old_amount, chan) // Simultaneously subtract old_amount and add new_amount.
|
|
|
|
// Not a proc you want to use directly unless you know what you are doing; see use_power_oneoff above instead.
|
|
/area/proc/use_power_static(var/amount, var/chan)
|
|
switch(chan)
|
|
if(EQUIP)
|
|
static_equip += amount
|
|
if(LIGHT)
|
|
static_light += amount
|
|
if(ENVIRON)
|
|
static_environ += amount
|
|
|
|
// This recomputes the continued power usage; can be used for testing or error recovery, but is not called every tick.
|
|
/area/proc/retally_power()
|
|
static_equip = 0
|
|
static_light = 0
|
|
static_environ = 0
|
|
for(var/obj/machinery/M in src)
|
|
switch(M.power_channel)
|
|
if(EQUIP)
|
|
static_equip += M.get_power_usage()
|
|
if(LIGHT)
|
|
static_light += M.get_power_usage()
|
|
if(ENVIRON)
|
|
static_environ += M.get_power_usage()
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
/area/vv_get_dropdown()
|
|
. = ..()
|
|
VV_DROPDOWN_OPTION("check_static_power", "Check Static Power")
|
|
|
|
/area/vv_do_topic(list/href_list)
|
|
. = ..()
|
|
IF_VV_OPTION("check_static_power")
|
|
if(!check_rights(R_DEBUG))
|
|
return
|
|
src.check_static_power(usr)
|
|
href_list["datumrefresh"] = "\ref[src]"
|
|
|
|
// Debugging proc to report if static power is correct or not.
|
|
/area/proc/check_static_power(var/user)
|
|
set name = "Check Static Power"
|
|
var/actual_static_equip = static_equip
|
|
var/actual_static_light = static_light
|
|
var/actual_static_environ = static_environ
|
|
retally_power()
|
|
if(user)
|
|
var/list/report = list("[src] ([type]) static power tally:")
|
|
report += "EQUIP: Actual: [actual_static_equip] Correct: [static_equip] Difference: [actual_static_equip - static_equip]"
|
|
report += "LIGHT: Actual: [actual_static_light] Correct: [static_light] Difference: [actual_static_light - static_light]"
|
|
report += "ENVIRON: Actual: [actual_static_environ] Correct: [static_environ] Difference: [actual_static_environ - static_environ]"
|
|
to_chat(user, report.Join("\n"))
|
|
return (actual_static_equip == static_equip && actual_static_light == static_light && actual_static_environ == static_environ)
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
var/list/mob/living/forced_ambiance_list = new
|
|
|
|
/area/Entered(A)
|
|
if(!istype(A,/mob/living)) return
|
|
|
|
var/mob/living/L = A
|
|
if(!L.ckey) return
|
|
|
|
if(!L.lastarea)
|
|
L.lastarea = get_area(L.loc)
|
|
var/area/newarea = get_area(L.loc)
|
|
var/area/oldarea = L.lastarea
|
|
if((oldarea.has_gravity == 0) && (newarea.has_gravity == 1) && (L.m_intent == "run")) // Being ready when you change areas gives you a chance to avoid falling all together.
|
|
thunk(L)
|
|
L.update_floating( L.Check_Dense_Object() )
|
|
|
|
L.lastarea = newarea
|
|
L.lastareachange = world.time
|
|
play_ambience(L, initial = TRUE)
|
|
if(no_spoilers)
|
|
L.disable_spoiler_vision()
|
|
|
|
/area/proc/play_ambience(var/mob/living/L, initial = TRUE)
|
|
// Ambience goes down here -- make sure to list each area seperately for ease of adding things in later, thanks! Note: areas adjacent to each other should have the same sounds to prevent cutoff when possible.- LastyScratch
|
|
if(!(L && L.is_preference_enabled(/datum/client_preference/play_ambiance)))
|
|
return
|
|
|
|
var/volume_mod = L.get_preference_volume_channel(VOLUME_CHANNEL_AMBIENCE)
|
|
|
|
// If we previously were in an area with force-played ambiance, stop it.
|
|
if((L in forced_ambiance_list) && initial)
|
|
L << sound(null, channel = CHANNEL_AMBIENCE_FORCED)
|
|
forced_ambiance_list -= L
|
|
|
|
if(forced_ambience)
|
|
if(L in forced_ambiance_list)
|
|
return
|
|
if(forced_ambience.len)
|
|
forced_ambiance_list |= L
|
|
var/sound/chosen_ambiance = pick(forced_ambience)
|
|
if(!istype(chosen_ambiance))
|
|
chosen_ambiance = sound(chosen_ambiance, repeat = 1, wait = 0, volume = 25, channel = CHANNEL_AMBIENCE_FORCED)
|
|
chosen_ambiance.volume *= volume_mod
|
|
L << chosen_ambiance
|
|
else
|
|
L << sound(null, channel = CHANNEL_AMBIENCE_FORCED)
|
|
else if(src.ambience.len)
|
|
var/ambience_odds = L?.client.prefs.ambience_chance
|
|
if(prob(ambience_odds) && (world.time >= L.client.time_last_ambience_played + 1 MINUTE))
|
|
var/sound = pick(ambience)
|
|
L << sound(sound, repeat = 0, wait = 0, volume = 50 * volume_mod, channel = CHANNEL_AMBIENCE)
|
|
L.client.time_last_ambience_played = world.time
|
|
|
|
/area/proc/gravitychange(var/gravitystate = 0)
|
|
src.has_gravity = gravitystate
|
|
|
|
for(var/mob/M in src)
|
|
if(has_gravity)
|
|
thunk(M)
|
|
M.update_floating( M.Check_Dense_Object() )
|
|
M.update_gravity(has_gravity)
|
|
|
|
/area/proc/thunk(mob)
|
|
if(istype(get_turf(mob), /turf/space)) // Can't fall onto nothing.
|
|
return
|
|
|
|
if(istype(mob,/mob/living/carbon/human/))
|
|
var/mob/living/carbon/human/H = mob
|
|
if(H.buckled)
|
|
return // Being buckled to something solid keeps you in place.
|
|
if(istype(H.shoes, /obj/item/clothing/shoes/magboots) && (H.shoes.item_flags & NOSLIP))
|
|
return
|
|
|
|
if(H.m_intent == "run")
|
|
H.AdjustStunned(6)
|
|
H.AdjustWeakened(6)
|
|
else
|
|
H.AdjustStunned(3)
|
|
H.AdjustWeakened(3)
|
|
to_chat(mob, "<span class='notice'>The sudden appearance of gravity makes you fall to the floor!</span>")
|
|
playsound(mob, "bodyfall", 50, 1)
|
|
|
|
/area/proc/prison_break(break_lights = TRUE, open_doors = TRUE, open_blast_doors = TRUE)
|
|
var/obj/machinery/power/apc/theAPC = get_apc()
|
|
if(theAPC.operating)
|
|
if(break_lights)
|
|
for(var/obj/machinery/power/apc/temp_apc in src)
|
|
temp_apc.overload_lighting(70)
|
|
if(open_doors)
|
|
for(var/obj/machinery/door/airlock/temp_airlock in src)
|
|
temp_airlock.prison_open()
|
|
for(var/obj/machinery/door/window/temp_windoor in src)
|
|
temp_windoor.open()
|
|
if(open_blast_doors)
|
|
for(var/obj/machinery/door/blast/temp_blast in src)
|
|
temp_blast.open()
|
|
|
|
/area/has_gravity()
|
|
return has_gravity
|
|
|
|
/area/space/has_gravity()
|
|
return 0
|
|
|
|
/proc/has_gravity(atom/AT, turf/T)
|
|
if(!T)
|
|
T = get_turf(AT)
|
|
var/area/A = get_area(T)
|
|
if(A && A.has_gravity())
|
|
return 1
|
|
return 0
|
|
|
|
/area/proc/shuttle_arrived()
|
|
return TRUE
|
|
|
|
/area/proc/shuttle_departed()
|
|
return TRUE
|
|
|
|
/area/AllowDrop()
|
|
CRASH("Bad op: area/AllowDrop() called")
|
|
|
|
/area/drop_location()
|
|
CRASH("Bad op: area/drop_location() called")
|
|
|
|
/*Adding a wizard area teleport list because motherfucking lag -- Urist*/
|
|
/*I am far too lazy to make it a proper list of areas so I'll just make it run the usual telepot routine at the start of the game*/
|
|
var/list/teleportlocs = list()
|
|
|
|
/hook/startup/proc/setupTeleportLocs()
|
|
for(var/area/AR in world)
|
|
if(istype(AR, /area/shuttle) || istype(AR, /area/syndicate_station) || istype(AR, /area/wizard_station)) continue
|
|
if(teleportlocs.Find(AR.name)) continue
|
|
var/turf/picked = pick(get_area_turfs(AR.type))
|
|
if (picked.z in using_map.station_levels)
|
|
teleportlocs += AR.name
|
|
teleportlocs[AR.name] = AR
|
|
|
|
teleportlocs = sortAssoc(teleportlocs)
|
|
|
|
return 1
|
|
|
|
var/list/ghostteleportlocs = list()
|
|
|
|
/hook/startup/proc/setupGhostTeleportLocs()
|
|
for(var/area/AR in world)
|
|
if(ghostteleportlocs.Find(AR.name)) continue
|
|
if(istype(AR, /area/aisat) || istype(AR, /area/derelict) || istype(AR, /area/tdome) || istype(AR, /area/shuttle/specops/centcom))
|
|
ghostteleportlocs += AR.name
|
|
ghostteleportlocs[AR.name] = AR
|
|
var/turf/picked = pick(get_area_turfs(AR.type))
|
|
if (picked.z in using_map.player_levels)
|
|
ghostteleportlocs += AR.name
|
|
ghostteleportlocs[AR.name] = AR
|
|
|
|
ghostteleportlocs = sortAssoc(ghostteleportlocs)
|
|
|
|
return 1
|
|
|
|
/area/proc/get_name()
|
|
if(secret_name)
|
|
return "Unknown Area"
|
|
return name
|
|
|
|
GLOBAL_DATUM(spoiler_obfuscation_image, /image)
|
|
|
|
/area/proc/set_spoiler_obfuscation(should_obfuscate)
|
|
if(!GLOB.spoiler_obfuscation_image)
|
|
GLOB.spoiler_obfuscation_image = image(icon = 'icons/misc/static.dmi')
|
|
GLOB.spoiler_obfuscation_image.plane = PLANE_MESONS
|
|
|
|
if(should_obfuscate)
|
|
add_overlay(GLOB.spoiler_obfuscation_image)
|
|
else
|
|
cut_overlay(GLOB.spoiler_obfuscation_image) |