diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm
index ee5d3a4f23..89d608b443 100644
--- a/code/__defines/misc.dm
+++ b/code/__defines/misc.dm
@@ -302,4 +302,11 @@ var/global/list/##LIST_NAME = list();\
#define RCD_SHEETS_PER_MATTER_UNIT 4 // Each physical material sheet is worth four matter units.
+<<<<<<< HEAD
#define RCD_MAX_CAPACITY 30 * RCD_SHEETS_PER_MATTER_UNIT
+
+// Radiation 'levels'. Used for the geiger counter, for visuals and sound. They are in different files so this goes here.
+#define RAD_LEVEL_LOW 0.01 // Around the level at which radiation starts to become harmful
+#define RAD_LEVEL_MODERATE 10
+#define RAD_LEVEL_HIGH 25
+#define RAD_LEVEL_VERY_HIGH 50
diff --git a/code/__defines/planets.dm b/code/__defines/planets.dm
index 3c2a1abcfc..da67d42ea2 100644
--- a/code/__defines/planets.dm
+++ b/code/__defines/planets.dm
@@ -9,6 +9,9 @@
#define WEATHER_WINDY "windy"
#define WEATHER_HOT "hot"
#define WEATHER_BLOOD_MOON "blood moon" // For admin fun or cult later on.
+#define WEATHER_EMBERFALL "emberfall" // More adminbuse, from TG. Harmless.
+#define WEATHER_ASH_STORM "ash storm" // Ripped from TG, like the above. Less harmless.
+#define WEATHER_FALLOUT "fallout" // Modified emberfall, actually harmful. Admin only.
#define MOON_PHASE_NEW_MOON "new moon"
#define MOON_PHASE_WAXING_CRESCENT "waxing crescent"
diff --git a/code/_global_vars/mobs.dm b/code/_global_vars/mobs.dm
index a5099a68d2..9bab9366b8 100644
--- a/code/_global_vars/mobs.dm
+++ b/code/_global_vars/mobs.dm
@@ -2,4 +2,7 @@ GLOBAL_LIST_EMPTY(admins) //all clients whom are admins
GLOBAL_PROTECT(admins)
GLOBAL_LIST_EMPTY(deadmins) //all ckeys who have used the de-admin verb.
GLOBAL_LIST_EMPTY(stealthminID)
+<<<<<<< HEAD
GLOBAL_LIST_EMPTY(directory) //all ckeys with associated client
+GLOBAL_LIST_EMPTY(directory) //all ckeys with associated client
+GLOBAL_LIST_EMPTY(players_by_zlevel)
diff --git a/code/_macros.dm b/code/_macros.dm
index 13afdb27b8..e6c6e10636 100644
--- a/code/_macros.dm
+++ b/code/_macros.dm
@@ -62,6 +62,11 @@
#define to_file(file_entry, source_var) file_entry << source_var
#define from_file(file_entry, target_var) file_entry >> target_var
+// From TG, might be useful to have.
+// Didn't port SEND_TEXT() since to_chat() appears to serve the same purpose.
+#define DIRECT_OUTPUT(A, B) A << B
+#define SEND_IMAGE(target, image) DIRECT_OUTPUT(target, image)
+#define SEND_SOUND(target, sound) DIRECT_OUTPUT(target, sound)
#define CanInteract(user, state) (CanUseTopic(user, state) == STATUS_INTERACTIVE)
diff --git a/code/controllers/subsystems/mapping.dm b/code/controllers/subsystems/mapping.dm
index 445a291628..cc69931426 100644
--- a/code/controllers/subsystems/mapping.dm
+++ b/code/controllers/subsystems/mapping.dm
@@ -1,29 +1,30 @@
-// Handles map-related tasks, mostly here to ensure it does so after the MC initializes.
-SUBSYSTEM_DEF(mapping)
- name = "Mapping"
- init_order = INIT_ORDER_MAPPING
- flags = SS_NO_FIRE
-
- var/list/map_templates = list()
- var/dmm_suite/maploader = null
-
-/datum/controller/subsystem/mapping/Initialize(timeofday)
- if(subsystem_initialized)
- return
- maploader = new()
- load_map_templates()
-
- if(config.generate_map)
- // Map-gen is still very specific to the map, however putting it here should ensure it loads in the correct order.
- if(using_map.perform_map_generation())
- using_map.refresh_mining_turfs()
-
-
-/datum/controller/subsystem/mapping/proc/load_map_templates()
- for(var/T in subtypesof(/datum/map_template))
- var/datum/map_template/template = T
- if(!(initial(template.mappath))) // If it's missing the actual path its probably a base type or being used for inheritence.
- continue
- template = new T()
- map_templates[template.name] = template
- return TRUE
+// Handles map-related tasks, mostly here to ensure it does so after the MC initializes.
+SUBSYSTEM_DEF(mapping)
+ name = "Mapping"
+ init_order = INIT_ORDER_MAPPING
+ flags = SS_NO_FIRE
+
+ var/list/map_templates = list()
+ var/dmm_suite/maploader = null
+
+/datum/controller/subsystem/mapping/Initialize(timeofday)
+ if(subsystem_initialized)
+ return
+ world.max_z_changed() // This is to set up the player z-level list, maxz hasn't actually changed (probably)
+ maploader = new()
+ load_map_templates()
+
+ if(config.generate_map)
+ // Map-gen is still very specific to the map, however putting it here should ensure it loads in the correct order.
+ if(using_map.perform_map_generation())
+ using_map.refresh_mining_turfs()
+
+
+/datum/controller/subsystem/mapping/proc/load_map_templates()
+ for(var/T in subtypesof(/datum/map_template))
+ var/datum/map_template/template = T
+ if(!(initial(template.mappath))) // If it's missing the actual path its probably a base type or being used for inheritence.
+ continue
+ template = new T()
+ map_templates[template.name] = template
+ return TRUE
diff --git a/code/datums/callback.dm b/code/datums/callback.dm
index a92a715ece..74d5719ce2 100644
--- a/code/datums/callback.dm
+++ b/code/datums/callback.dm
@@ -166,7 +166,7 @@
// Use sparingly
/world/proc/PushUsr(mob/M, datum/callback/CB)
var/temp = usr
- testing("PushUsr() in use")
+// testing("PushUsr() in use")
usr = M
. = CB.Invoke()
usr = temp
diff --git a/code/datums/looping_sounds/_looping_sound.dm b/code/datums/looping_sounds/_looping_sound.dm
new file mode 100644
index 0000000000..99af50bf42
--- /dev/null
+++ b/code/datums/looping_sounds/_looping_sound.dm
@@ -0,0 +1,111 @@
+/*
+ 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
+
+ start_sound (soundfile) Played before starting the mid_sounds loop
+ start_length (num) How long to wait before starting the main loop after playing start_sound
+
+ end_sound (soundfile) The sound played after the main loop has concluded
+
+ chance (num) Chance per loop to play a mid_sound
+ 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
+ opacity_check (bool) If true, things behind walls/opaque things won't hear the sounds.
+ pref_check (type) If set to a /datum/client_preference type, will check if the hearer has that preference active before playing it to them.
+*/
+/datum/looping_sound
+ var/list/atom/output_atoms
+ var/mid_sounds
+ var/mid_length
+ var/start_sound
+ var/start_length
+ var/end_sound
+ var/chance
+ var/volume = 100
+ var/max_loops
+ var/direct
+ var/opacity_check
+ var/pref_check
+
+ var/timerid
+
+/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
+
+ output_atoms = _output_atoms
+ direct = _direct
+
+ if(start_immediately)
+ start()
+
+/datum/looping_sound/Destroy()
+ stop()
+ output_atoms = null
+ return ..()
+
+/datum/looping_sound/proc/start(atom/add_thing)
+ if(add_thing)
+ output_atoms |= add_thing
+ if(timerid)
+ return
+ on_start()
+
+/datum/looping_sound/proc/stop(atom/remove_thing)
+ if(remove_thing)
+ output_atoms -= remove_thing
+ if(!timerid)
+ return
+ on_stop()
+ deltimer(timerid)
+ timerid = null
+
+/datum/looping_sound/proc/sound_loop(starttime)
+ if(max_loops && world.time >= starttime + mid_length * max_loops)
+ stop()
+ return
+ if(!chance || prob(chance))
+ play(get_sound(starttime))
+ if(!timerid)
+ timerid = addtimer(CALLBACK(src, .proc/sound_loop, world.time), mid_length, TIMER_STOPPABLE | TIMER_LOOP)
+
+/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]
+ if(direct)
+ if(ismob(thing))
+ var/mob/M = thing
+ if(!M.is_preference_enabled(pref_check))
+ continue
+ SEND_SOUND(thing, S)
+ else
+ playsound(thing, S, volume, ignore_walls = !opacity_check, preference = pref_check)
+
+/datum/looping_sound/proc/get_sound(starttime, _mid_sounds)
+ if(!_mid_sounds)
+ . = mid_sounds
+ else
+ . = _mid_sounds
+ while(!isfile(.) && !isnull(.))
+ . = pickweight(.)
+
+/datum/looping_sound/proc/on_start()
+ var/start_wait = 1 // On TG this is 0, however it needs to be 1 to work around an issue.
+ if(start_sound)
+ play(start_sound)
+ start_wait = start_length
+ addtimer(CALLBACK(src, .proc/sound_loop), start_wait)
+
+/datum/looping_sound/proc/on_stop()
+ if(end_sound)
+ play(end_sound)
\ No newline at end of file
diff --git a/code/datums/looping_sounds/item_sounds.dm b/code/datums/looping_sounds/item_sounds.dm
new file mode 100644
index 0000000000..4619acd3b5
--- /dev/null
+++ b/code/datums/looping_sounds/item_sounds.dm
@@ -0,0 +1,29 @@
+/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 = 1 SECOND
+ volume = 25
+ var/last_radiation
+
+/datum/looping_sound/geiger/get_sound(starttime)
+ var/danger
+ switch(last_radiation)
+ if(0 to RAD_LEVEL_MODERATE)
+ danger = 1
+ if(RAD_LEVEL_MODERATE to RAD_LEVEL_HIGH)
+ danger = 2
+ if(RAD_LEVEL_HIGH to RAD_LEVEL_VERY_HIGH)
+ danger = 3
+ if(RAD_LEVEL_VERY_HIGH to INFINITY)
+ danger = 4
+ else
+ return null
+ return ..(starttime, mid_sounds[danger])
+
+/datum/looping_sound/geiger/stop()
+ . = ..()
+ last_radiation = 0
diff --git a/code/datums/looping_sounds/machinery_sounds.dm b/code/datums/looping_sounds/machinery_sounds.dm
new file mode 100644
index 0000000000..8b927b3a54
--- /dev/null
+++ b/code/datums/looping_sounds/machinery_sounds.dm
@@ -0,0 +1,46 @@
+/datum/looping_sound/showering
+ start_sound = 'sound/machines/shower/shower_start.ogg'
+ start_length = 2
+ mid_sounds = list('sound/machines/shower/shower_mid1.ogg'=1,'sound/machines/shower/shower_mid2.ogg'=1,'sound/machines/shower/shower_mid3.ogg'=1)
+ mid_length = 10
+ end_sound = 'sound/machines/shower/shower_end.ogg'
+ volume = 20
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/datum/looping_sound/supermatter
+ mid_sounds = list('sound/machines/sm/supermatter1.ogg'=1,'sound/machines/sm/supermatter2.ogg'=1,'sound/machines/sm/supermatter3.ogg'=1)
+ mid_length = 10
+ volume = 1
+ pref_check = /datum/client_preference/supermatter_hum
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/datum/looping_sound/generator
+ start_sound = 'sound/machines/generator/generator_start.ogg'
+ start_length = 4
+ mid_sounds = list('sound/machines/generator/generator_mid1.ogg'=1, 'sound/machines/generator/generator_mid2.ogg'=1, 'sound/machines/generator/generator_mid3.ogg'=1)
+ mid_length = 4
+ end_sound = 'sound/machines/generator/generator_end.ogg'
+ volume = 40
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+/datum/looping_sound/deep_fryer
+ start_sound = 'sound/machines/fryer/deep_fryer_immerse.ogg' //my immersions
+ start_length = 10
+ mid_sounds = list('sound/machines/fryer/deep_fryer_1.ogg' = 1, 'sound/machines/fryer/deep_fryer_2.ogg' = 1)
+ mid_length = 2
+ end_sound = 'sound/machines/fryer/deep_fryer_emerge.ogg'
+ volume = 15
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/datum/looping_sound/microwave
+ start_sound = 'sound/machines/microwave/microwave-start.ogg'
+ start_length = 10
+ mid_sounds = list('sound/machines/microwave/microwave-mid1.ogg'=10, 'sound/machines/microwave/microwave-mid2.ogg'=1)
+ mid_length = 10
+ end_sound = 'sound/machines/microwave/microwave-end.ogg'
+ volume = 90
\ No newline at end of file
diff --git a/code/datums/looping_sounds/weather_sounds.dm b/code/datums/looping_sounds/weather_sounds.dm
new file mode 100644
index 0000000000..106c25643a
--- /dev/null
+++ b/code/datums/looping_sounds/weather_sounds.dm
@@ -0,0 +1,79 @@
+/datum/looping_sound/weather
+ pref_check = /datum/client_preference/weather_sounds
+
+/datum/looping_sound/weather/outside_blizzard
+ mid_sounds = list(
+ 'sound/effects/weather/snowstorm/outside/active_mid1.ogg' = 1,
+ 'sound/effects/weather/snowstorm/outside/active_mid1.ogg' = 1,
+ 'sound/effects/weather/snowstorm/outside/active_mid1.ogg' = 1
+ )
+ mid_length = 8 SECONDS
+ start_sound = 'sound/effects/weather/snowstorm/outside/active_start.ogg'
+ start_length = 13 SECONDS
+ end_sound = 'sound/effects/weather/snowstorm/outside/active_end.ogg'
+ volume = 80
+
+/datum/looping_sound/weather/inside_blizzard
+ mid_sounds = list(
+ 'sound/effects/weather/snowstorm/inside/active_mid1.ogg' = 1,
+ 'sound/effects/weather/snowstorm/inside/active_mid2.ogg' = 1,
+ 'sound/effects/weather/snowstorm/inside/active_mid3.ogg' = 1
+ )
+ mid_length = 8 SECONDS
+ start_sound = 'sound/effects/weather/snowstorm/inside/active_start.ogg'
+ start_length = 13 SECONDS
+ end_sound = 'sound/effects/weather/snowstorm/inside/active_end.ogg'
+ volume = 60
+
+/datum/looping_sound/weather/outside_snow
+ mid_sounds = list(
+ 'sound/effects/weather/snowstorm/outside/weak_mid1.ogg' = 1,
+ 'sound/effects/weather/snowstorm/outside/weak_mid2.ogg' = 1,
+ 'sound/effects/weather/snowstorm/outside/weak_mid3.ogg' = 1
+ )
+ mid_length = 8 SECONDS
+ start_sound = 'sound/effects/weather/snowstorm/outside/weak_start.ogg'
+ start_length = 13 SECONDS
+ end_sound = 'sound/effects/weather/snowstorm/outside/weak_end.ogg'
+ volume = 50
+
+/datum/looping_sound/weather/inside_snow
+ mid_sounds = list(
+ 'sound/effects/weather/snowstorm/inside/weak_mid1.ogg' = 1,
+ 'sound/effects/weather/snowstorm/inside/weak_mid2.ogg' = 1,
+ 'sound/effects/weather/snowstorm/inside/weak_mid3.ogg' = 1
+ )
+ mid_length = 8 SECONDS
+ start_sound = 'sound/effects/weather/snowstorm/inside/weak_start.ogg'
+ start_length = 13 SECONDS
+ end_sound = 'sound/effects/weather/snowstorm/inside/weak_end.ogg'
+ volume = 30
+
+/datum/looping_sound/weather/wind
+ mid_sounds = list(
+ 'sound/effects/weather/wind/wind_2_1.ogg' = 1,
+ 'sound/effects/weather/wind/wind_2_2.ogg' = 1,
+ 'sound/effects/weather/wind/wind_3_1.ogg' = 1,
+ 'sound/effects/weather/wind/wind_4_1.ogg' = 1,
+ 'sound/effects/weather/wind/wind_4_2.ogg' = 1,
+ 'sound/effects/weather/wind/wind_5_1.ogg' = 1
+ )
+ mid_length = 10 SECONDS // The lengths for the files vary, but the longest is ten seconds, so this will make it sound like intermittent wind.
+ volume = 50
+
+// Don't have special sounds so we just make it quieter indoors.
+/datum/looping_sound/weather/wind/indoors
+ volume = 30
+
+/datum/looping_sound/weather/rain
+ mid_sounds = list(
+ 'sound/effects/weather/acidrain_mid.ogg' = 1
+ )
+ mid_length = 15 SECONDS // The lengths for the files vary, but the longest is ten seconds, so this will make it sound like intermittent wind.
+ start_sound = 'sound/effects/weather/acidrain_start.ogg'
+ start_length = 13 SECONDS
+ end_sound = 'sound/effects/weather/acidrain_end.ogg'
+ volume = 50
+
+/datum/looping_sound/weather/rain/indoors
+ volume = 30
\ No newline at end of file
diff --git a/code/datums/observation/z_moved.dm b/code/datums/observation/z_moved.dm
new file mode 100644
index 0000000000..63b89ba0da
--- /dev/null
+++ b/code/datums/observation/z_moved.dm
@@ -0,0 +1,16 @@
+// Observer Pattern Implementation: Z_Moved
+// Registration type: /atom/movable
+//
+// Raised when: An /atom/movable instance has changed z-levels by any means.
+//
+// Arguments that the called proc should expect:
+// /atom/movable/moving_instance: The instance that moved
+// old_z: The z number before the move.
+// new_z: The z number after the move.
+
+
+GLOBAL_DATUM_INIT(z_moved_event, /decl/observ/z_moved, new)
+
+/decl/observ/z_moved
+ name = "Z_Moved"
+ expected_type = /atom/movable
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index cc7c61aa42..49b83b2d93 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -290,4 +290,11 @@
/atom/movable/proc/adjust_rotation(new_rotation)
icon_rotation = new_rotation
- update_transform()
\ No newline at end of file
+ update_transform()
+
+// Called when something changes z-levels.
+/atom/movable/proc/on_z_change(old_z, new_z)
+ GLOB.z_moved_event.raise_event(src, old_z, new_z)
+ for(var/item in src) // Notify contents of Z-transition. This can be overriden IF we know the items contents do not care.
+ var/atom/movable/AM = item
+ AM.on_z_change(old_z, new_z)
diff --git a/code/game/gamemodes/cult/narsie.dm b/code/game/gamemodes/cult/narsie.dm
index ea866ad856..21ee7ec1df 100644
--- a/code/game/gamemodes/cult/narsie.dm
+++ b/code/game/gamemodes/cult/narsie.dm
@@ -44,7 +44,7 @@ var/global/list/narsie_list = list()
..()
if(announce)
world << "[uppertext(name)] HAS RISEN"
- world << sound('sound/effects/wind/wind_5_1.ogg')
+ world << sound('sound/effects/weather/wind/wind_5_1.ogg')
narsie_spawn_animation()
diff --git a/code/game/gamemodes/cult/runes.dm b/code/game/gamemodes/cult/runes.dm
index 40008c5ecc..1bfe4e0d60 100644
--- a/code/game/gamemodes/cult/runes.dm
+++ b/code/game/gamemodes/cult/runes.dm
@@ -206,7 +206,7 @@ var/list/sacrificed = list()
if(cultists.len >= 9)
if(!narsie_cometh)//so we don't initiate Hell more than one time.
world << "THE VEIL HAS BEEN SHATTERED!"
- world << sound('sound/effects/wind/wind_5_1.ogg')
+ world << sound('sound/effects/weather/wind/wind_5_1.ogg')
SetUniversalState(/datum/universal_state/hell)
narsie_cometh = 1
diff --git a/code/game/objects/effects/map_effects/sound_emitter.dm b/code/game/objects/effects/map_effects/sound_emitter.dm
index 4f30a3fdc6..caa3b08d53 100644
--- a/code/game/objects/effects/map_effects/sound_emitter.dm
+++ b/code/game/objects/effects/map_effects/sound_emitter.dm
@@ -1,117 +1,119 @@
-// Plays a sound at its location every so often.
-/obj/effect/map_effect/interval/sound_emitter
- name = "sound emitter"
- icon_state = "sound_emitter"
- var/list/sounds_to_play = list(null) // List containing sound files or strings of sound groups.
- // A sound or string is picked randomly each run.
-
- var/sound_volume = 50 // How loud the sound is. 0 is silent, and 100 is loudest. Please be reasonable with the volume.
- // Note that things like vacuum may affect the volume heard by other mobs.
-
- var/sound_frequency_variance = TRUE // If the sound will sound somewhat different each time.
- // If a specific frequency is desired, sound_frequency must also be set.
-
- var/sound_extra_range = 0 // Set to make sounds heard from farther away than normal.
-
- var/sound_fallout = 0 // Within the 'fallout distance', the sound stays at the same volume, otherwise it attenuates.
- // Higher numbers make the sound fade out more slowly with distance.
-
- var/sound_global = FALSE // If true, sounds will not be distorted due to the current area's 'sound environment'.
- // It DOES NOT make the sound have a constant volume or z-level wide range, despite the misleading name.
-
- var/sound_frequency = null // Sets a specific custom frequency. sound_frequency_variance must be true as well.
- // If sound_frequency is null, but sound_frequency_variance is true, a semi-random frequency will be chosen to the sound each time.
-
- var/sound_channel = 0 // BYOND allows a sound to play in 1 through 1024 sound channels.
- // 0 will have BYOND give it the lowest available channel, it is not recommended to change this without a good reason.
-
- var/sound_pressure_affected = TRUE // If false, people in low pressure or vacuum will hear the sound.
-
- var/sound_ignore_walls = TRUE // If false, walls will completely muffle the sound.
-
- var/sound_preference = null // Player preference to check before playing this sound to them, if any.
-
-/obj/effect/map_effect/interval/sound_emitter/trigger()
- playsound(
- src,
- pick(sounds_to_play),
- sound_volume,
- sound_frequency_variance,
- sound_extra_range,
- sound_fallout,
- sound_global,
- sound_frequency,
- sound_channel,
- sound_pressure_affected,
- sound_ignore_walls,
- sound_preference
- )
- ..()
-
-/obj/effect/map_effect/interval/sound_emitter/thunder
- sounds_to_play = list("thunder")
- interval_lower_bound = 10 SECONDS
- interval_upper_bound = 15 SECONDS
-
-/obj/effect/map_effect/interval/sound_emitter/geiger
- sounds_to_play = list("geiger")
- interval_lower_bound = 2 SECONDS
- interval_upper_bound = 2 SECONDS
-
-/obj/effect/map_effect/interval/sound_emitter/geiger_weak
- sounds_to_play = list("geiger_weak")
- interval_lower_bound = 1 SECOND
- interval_upper_bound = 1 SECOND
-
-/obj/effect/map_effect/interval/sound_emitter/punching
- sounds_to_play = list("punch")
- interval_lower_bound = 5
- interval_upper_bound = 1 SECOND
-
-/obj/effect/map_effect/interval/sound_emitter/explosions
- sounds_to_play = list("explosion")
- interval_lower_bound = 5 SECONDS
- interval_upper_bound = 10 SECONDS
-
-/obj/effect/map_effect/interval/sound_emitter/distant_explosions
- sounds_to_play = list('sound/effects/explosionfar.ogg')
- interval_lower_bound = 5 SECONDS
- interval_upper_bound = 10 SECONDS
-
-/obj/effect/map_effect/interval/sound_emitter/ballistic_gunfight
- sounds_to_play = list(
- 'sound/weapons/gunshot.ogg',
- 'sound/weapons/deagle.ogg',
- 'sound/weapons/rifleshot.ogg',
- 'sound/weapons/sniper.ogg',
- 'sound/weapons/shotgun.ogg',
- 'sound/weapons/gunshot3.ogg',
- 'sound/weapons/machinegun.ogg'
- )
- interval_lower_bound = 5
- interval_upper_bound = 2 SECONDS
-
-/obj/effect/map_effect/interval/sound_emitter/energy_gunfight
- sounds_to_play = list(
- 'sound/weapons/Taser.ogg',
- 'sound/weapons/laser.ogg',
- 'sound/weapons/eLuger.ogg',
- 'sound/weapons/laser3.ogg',
- 'sound/weapons/pulse.ogg',
- 'sound/weapons/gauss_shoot.ogg',
- 'sound/weapons/emitter.ogg'
- )
- interval_lower_bound = 5
- interval_upper_bound = 2 SECONDS
-
-
-// I'm not sorry.
-/obj/effect/map_effect/interval/sound_emitter/clownsteps
- sounds_to_play = list("clownstep")
- interval_lower_bound = 5
- interval_upper_bound = 1 SECOND
-
-/obj/effect/map_effect/interval/sound_emitter/bikehorns
- sounds_to_play = list('sound/items/bikehorn.ogg')
- interval_lower_bound = 5
- interval_upper_bound = 1 SECOND
+// Plays a sound at its location every so often.
+/obj/effect/map_effect/interval/sound_emitter
+ name = "sound emitter"
+ icon_state = "sound_emitter"
+ var/list/sounds_to_play = list(null) // List containing sound files or strings of sound groups.
+ // A sound or string is picked randomly each run.
+
+ var/sound_volume = 50 // How loud the sound is. 0 is silent, and 100 is loudest. Please be reasonable with the volume.
+ // Note that things like vacuum may affect the volume heard by other mobs.
+
+ var/sound_frequency_variance = TRUE // If the sound will sound somewhat different each time.
+ // If a specific frequency is desired, sound_frequency must also be set.
+
+ var/sound_extra_range = 0 // Set to make sounds heard from farther away than normal.
+
+ var/sound_fallout = 0 // Within the 'fallout distance', the sound stays at the same volume, otherwise it attenuates.
+ // Higher numbers make the sound fade out more slowly with distance.
+
+ var/sound_global = FALSE // If true, sounds will not be distorted due to the current area's 'sound environment'.
+ // It DOES NOT make the sound have a constant volume or z-level wide range, despite the misleading name.
+
+ var/sound_frequency = null // Sets a specific custom frequency. sound_frequency_variance must be true as well.
+ // If sound_frequency is null, but sound_frequency_variance is true, a semi-random frequency will be chosen to the sound each time.
+
+ var/sound_channel = 0 // BYOND allows a sound to play in 1 through 1024 sound channels.
+ // 0 will have BYOND give it the lowest available channel, it is not recommended to change this without a good reason.
+
+ var/sound_pressure_affected = TRUE // If false, people in low pressure or vacuum will hear the sound.
+
+ var/sound_ignore_walls = TRUE // If false, walls will completely muffle the sound.
+
+ var/sound_preference = null // Player preference to check before playing this sound to them, if any.
+
+/obj/effect/map_effect/interval/sound_emitter/trigger()
+ playsound(
+ src,
+ pick(sounds_to_play),
+ sound_volume,
+ sound_frequency_variance,
+ sound_extra_range,
+ sound_fallout,
+ sound_global,
+ sound_frequency,
+ sound_channel,
+ sound_pressure_affected,
+ sound_ignore_walls,
+ sound_preference
+ )
+ ..()
+
+/obj/effect/map_effect/interval/sound_emitter/thunder
+ sounds_to_play = list("thunder")
+ interval_lower_bound = 10 SECONDS
+ interval_upper_bound = 15 SECONDS
+
+/obj/effect/map_effect/interval/sound_emitter/geiger
+ sounds_to_play = list('sound/items/geiger/low1.ogg', 'sound/items/geiger/low2.ogg', 'sound/items/geiger/low3.ogg', 'sound/items/geiger/low4.ogg')
+ interval_lower_bound = 1 SECOND
+ interval_upper_bound = 1 SECOND
+
+/obj/effect/map_effect/interval/sound_emitter/geiger/med
+ sounds_to_play = list('sound/items/geiger/med1.ogg', 'sound/items/geiger/med2.ogg', 'sound/items/geiger/med3.ogg', 'sound/items/geiger/med4.ogg')
+
+/obj/effect/map_effect/interval/sound_emitter/geiger/high
+ sounds_to_play = list('sound/items/geiger/high1.ogg', 'sound/items/geiger/high2.ogg', 'sound/items/geiger/high3.ogg', 'sound/items/geiger/high4.ogg')
+
+/obj/effect/map_effect/interval/sound_emitter/geiger/ext
+ sounds_to_play = list('sound/items/geiger/ext1.ogg', 'sound/items/geiger/ext2.ogg', 'sound/items/geiger/ext3.ogg', 'sound/items/geiger/ext4.ogg')
+
+/obj/effect/map_effect/interval/sound_emitter/punching
+ sounds_to_play = list("punch")
+ interval_lower_bound = 5
+ interval_upper_bound = 1 SECOND
+
+/obj/effect/map_effect/interval/sound_emitter/explosions
+ sounds_to_play = list("explosion")
+ interval_lower_bound = 5 SECONDS
+ interval_upper_bound = 10 SECONDS
+
+/obj/effect/map_effect/interval/sound_emitter/explosions/distant
+ sounds_to_play = list('sound/effects/explosionfar.ogg')
+
+/obj/effect/map_effect/interval/sound_emitter/ballistic_gunfight
+ sounds_to_play = list(
+ 'sound/weapons/gunshot.ogg',
+ 'sound/weapons/deagle.ogg',
+ 'sound/weapons/rifleshot.ogg',
+ 'sound/weapons/sniper.ogg',
+ 'sound/weapons/shotgun.ogg',
+ 'sound/weapons/gunshot3.ogg',
+ 'sound/weapons/machinegun.ogg'
+ )
+ interval_lower_bound = 5
+ interval_upper_bound = 2 SECONDS
+
+/obj/effect/map_effect/interval/sound_emitter/energy_gunfight
+ sounds_to_play = list(
+ 'sound/weapons/Taser.ogg',
+ 'sound/weapons/laser.ogg',
+ 'sound/weapons/eLuger.ogg',
+ 'sound/weapons/laser3.ogg',
+ 'sound/weapons/pulse.ogg',
+ 'sound/weapons/gauss_shoot.ogg',
+ 'sound/weapons/emitter.ogg'
+ )
+ interval_lower_bound = 5
+ interval_upper_bound = 2 SECONDS
+
+
+// I'm not sorry.
+/obj/effect/map_effect/interval/sound_emitter/clownsteps
+ sounds_to_play = list("clownstep")
+ interval_lower_bound = 5
+ interval_upper_bound = 1 SECOND
+
+/obj/effect/map_effect/interval/sound_emitter/bikehorns
+ sounds_to_play = list('sound/items/bikehorn.ogg')
+ interval_lower_bound = 5
+ interval_upper_bound = 1 SECOND
diff --git a/code/game/objects/items/devices/geiger.dm b/code/game/objects/items/devices/geiger.dm
index 9ab63afce7..e26880671f 100644
--- a/code/game/objects/items/devices/geiger.dm
+++ b/code/game/objects/items/devices/geiger.dm
@@ -1,8 +1,3 @@
-#define RAD_LEVEL_LOW 0.01 // Around the level at which radiation starts to become harmful
-#define RAD_LEVEL_MODERATE 10
-#define RAD_LEVEL_HIGH 25
-#define RAD_LEVEL_VERY_HIGH 50
-
//Geiger counter
//Rewritten version of TG's geiger counter
//I opted to show exact radiation levels
@@ -15,12 +10,23 @@
w_class = ITEMSIZE_SMALL
var/scanning = 0
var/radiation_count = 0
+ var/datum/looping_sound/geiger/soundloop
+<<<<<<< HEAD
/obj/item/device/geiger/New()
START_PROCESSING(SSobj, src)
+/obj/item/device/geiger/Destroy()
+=======
+/obj/item/device/geiger/Initialize()
+ START_PROCESSING(SSobj, src)
+ soundloop = new(list(src), FALSE)
+ return ..()
+
/obj/item/device/geiger/Destroy()
STOP_PROCESSING(SSobj, src)
+ QDEL_NULL(soundloop)
+>>>>>>> 5fb77b3... Merge pull request #5791 from Neerti/looping_sounds
return ..()
/obj/item/device/geiger/process()
@@ -31,6 +37,7 @@
return
radiation_count = radiation_repository.get_rads_at_turf(get_turf(src))
update_icon()
+ update_sound()
/obj/item/device/geiger/examine(mob/user)
..(user)
@@ -44,18 +51,24 @@
if(amount > radiation_count)
radiation_count = amount
- var/sound = "geiger"
- if(amount < 5)
- sound = "geiger_weak"
- playsound(src, sound, between(10, 10 + (radiation_count * 4), 100), 0)
- if(sound == "geiger_weak") // A weak geiger sound every two seconds sounds too infrequent.
- spawn(1 SECOND)
- playsound(src, sound, between(10, 10 + (radiation_count * 4), 100), 0)
update_icon()
+ update_sound()
+
+/obj/item/device/geiger/proc/update_sound()
+ var/datum/looping_sound/geiger/loop = soundloop
+ if(!scanning)
+ loop.stop()
+ return
+ if(!radiation_count)
+ loop.stop()
+ return
+ loop.last_radiation = radiation_count
+ loop.start()
/obj/item/device/geiger/attack_self(var/mob/user)
scanning = !scanning
update_icon()
+ update_sound()
to_chat(user, "\icon[src] You switch [scanning ? "on" : "off"] \the [src].")
/obj/item/device/geiger/update_icon()
@@ -76,8 +89,3 @@
icon_state = "geiger_on_4"
if(RAD_LEVEL_VERY_HIGH to INFINITY)
icon_state = "geiger_on_5"
-
-#undef RAD_LEVEL_LOW
-#undef RAD_LEVEL_MODERATE
-#undef RAD_LEVEL_HIGH
-#undef RAD_LEVEL_VERY_HIGH
\ No newline at end of file
diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm
index ff95dd44d8..464cf08f7d 100644
--- a/code/game/objects/structures/watercloset.dm
+++ b/code/game/objects/structures/watercloset.dm
@@ -131,10 +131,16 @@
var/watertemp = "normal" //freezing, normal, or boiling
var/is_washing = 0
var/list/temperature_settings = list("normal" = 310, "boiling" = T0C+100, "freezing" = T0C)
+ var/datum/looping_sound/showering/soundloop
-/obj/machinery/shower/New()
- ..()
+/obj/machinery/shower/Initialize()
create_reagents(50)
+ soundloop = new(list(src), FALSE)
+ return ..()
+
+/obj/machinery/shower/Destroy()
+ QDEL_NULL(soundloop)
+ return ..()
//add heat controls? when emagged, you can freeze to death in it?
@@ -151,11 +157,14 @@
on = !on
update_icon()
if(on)
+ soundloop.start()
if (M.loc == loc)
wash(M)
process_heat(M)
for (var/atom/movable/G in src.loc)
G.clean_blood()
+ else
+ soundloop.stop()
/obj/machinery/shower/attackby(obj/item/I as obj, mob/user as mob)
if(I.type == /obj/item/device/analyzer)
diff --git a/code/game/sound.dm b/code/game/sound.dm
index b9810d7915..b1f0c32f66 100644
--- a/code/game/sound.dm
+++ b/code/game/sound.dm
@@ -23,7 +23,7 @@
if(distance <= maxdistance)
if(T && T.z == turf_source.z)
- M.playsound_local(turf_source, soundin, vol, vary, frequency, falloff, is_global, channel, pressure_affected, S)
+ M.playsound_local(turf_source, soundin, vol, vary, frequency, falloff, is_global, channel, pressure_affected, S, preference)
/mob/proc/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff, is_global, channel = 0, pressure_affected = TRUE, sound/S, preference)
if(!client || ear_deaf > 0)
@@ -130,8 +130,6 @@
if ("fracture") soundin = pick('sound/effects/bonebreak1.ogg','sound/effects/bonebreak2.ogg','sound/effects/bonebreak3.ogg','sound/effects/bonebreak4.ogg')
if ("canopen") soundin = pick('sound/effects/can_open1.ogg','sound/effects/can_open2.ogg','sound/effects/can_open3.ogg','sound/effects/can_open4.ogg')
if ("mechstep") soundin = pick('sound/mecha/mechstep1.ogg', 'sound/mecha/mechstep2.ogg')
- if ("geiger") soundin = pick('sound/items/geiger1.ogg', 'sound/items/geiger2.ogg', 'sound/items/geiger3.ogg', 'sound/items/geiger4.ogg', 'sound/items/geiger5.ogg')
- if ("geiger_weak") soundin = pick('sound/items/geiger_weak1.ogg', 'sound/items/geiger_weak2.ogg', 'sound/items/geiger_weak3.ogg', 'sound/items/geiger_weak4.ogg')
if ("thunder") soundin = pick('sound/effects/thunder/thunder1.ogg', 'sound/effects/thunder/thunder2.ogg', 'sound/effects/thunder/thunder3.ogg', 'sound/effects/thunder/thunder4.ogg',
'sound/effects/thunder/thunder5.ogg', 'sound/effects/thunder/thunder6.ogg', 'sound/effects/thunder/thunder7.ogg', 'sound/effects/thunder/thunder8.ogg', 'sound/effects/thunder/thunder9.ogg',
'sound/effects/thunder/thunder10.ogg')
diff --git a/code/game/turfs/flooring/flooring.dm b/code/game/turfs/flooring/flooring.dm
index 549265e954..2f45eb7b51 100644
--- a/code/game/turfs/flooring/flooring.dm
+++ b/code/game/turfs/flooring/flooring.dm
@@ -74,7 +74,6 @@ var/list/flooring_types
'sound/effects/footstep/snow4.ogg',
'sound/effects/footstep/snow5.ogg'))
-
/decl/flooring/snow/snow2
name = "snow"
desc = "A layer of many tiny bits of frozen water. It's hard to tell how deep it is."
diff --git a/code/modules/client/preference_setup/global/setting_datums.dm b/code/modules/client/preference_setup/global/setting_datums.dm
index 66b57f67f3..347308bae9 100644
--- a/code/modules/client/preference_setup/global/setting_datums.dm
+++ b/code/modules/client/preference_setup/global/setting_datums.dm
@@ -77,6 +77,7 @@ var/list/_client_preferences_by_type
preference_mob << sound(null, repeat = 0, wait = 0, volume = 0, channel = 1)
preference_mob << sound(null, repeat = 0, wait = 0, volume = 0, channel = 2)
+<<<<<<< HEAD
/datum/client_preference/play_jukebox
description ="Play jukebox music"
key = "SOUND_JUKEBOX"
@@ -98,6 +99,20 @@ var/list/_client_preferences_by_type
enabled_description = "Noisy"
disabled_description = "Silent"
//VOREStation Add End
+=======
+/datum/client_preference/weather_sounds
+ description ="Weather sounds"
+ key = "SOUND_WEATHER"
+ enabled_description = "Audible"
+ disabled_description = "Silent"
+
+/datum/client_preference/supermatter_hum
+ description ="Supermatter hum"
+ key = "SOUND_SUPERMATTER"
+ enabled_description = "Audible"
+ disabled_description = "Silent"
+
+>>>>>>> 5fb77b3... Merge pull request #5791 from Neerti/looping_sounds
/datum/client_preference/ghost_ears
description ="Ghost ears"
key = "CHAT_GHOSTEARS"
diff --git a/code/modules/client/preferences_toggle_procs.dm b/code/modules/client/preferences_toggle_procs.dm
index 4e5e5fb51e..51412906d7 100644
--- a/code/modules/client/preferences_toggle_procs.dm
+++ b/code/modules/client/preferences_toggle_procs.dm
@@ -143,7 +143,7 @@
toggle_preference(pref_path)
- to_chat(src,"You will [ (is_preference_enabled(pref_path)) ? "now" : " no longer"] hear MIDIs from admins.")
+ to_chat(src,"You will [ (is_preference_enabled(pref_path)) ? "now" : "no longer"] hear MIDIs from admins.")
prefs.save_preferences()
@@ -158,11 +158,41 @@
toggle_preference(pref_path)
- to_chat(src,"You will [ (is_preference_enabled(pref_path)) ? "now" : " no longer"] hear ambient noise.")
+ to_chat(src,"You will [ (is_preference_enabled(pref_path)) ? "now" : "no longer"] hear ambient noise.")
prefs.save_preferences()
- feedback_add_details("admin_verb","TBeSpecial") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ feedback_add_details("admin_verb","TAmbience") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+
+/client/verb/toggle_weather_sounds()
+ set name = "Toggle Weather Sounds"
+ set category = "Preferences"
+ set desc = "Toggles the ability to hear weather sounds while on a planet."
+
+ var/pref_path = /datum/client_preference/weather_sounds
+
+ toggle_preference(pref_path)
+
+ to_chat(src,"You will [ (is_preference_enabled(pref_path)) ? "now" : "no longer"] hear weather sounds.")
+
+ prefs.save_preferences()
+
+ feedback_add_details("admin_verb","TWeatherSounds") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+
+/client/verb/toggle_supermatter_hum()
+ set name = "Toggle SM Hum" // Avoiding using the full 'Supermatter' name to not conflict with the Setup-Supermatter adminverb.
+ set category = "Preferences"
+ set desc = "Toggles the ability to hear supermatter hums."
+
+ var/pref_path = /datum/client_preference/supermatter_hum
+
+ toggle_preference(pref_path)
+
+ to_chat(src,"You will [ (is_preference_enabled(pref_path)) ? "now" : "no longer"] hear a hum from the supermatter.")
+
+ prefs.save_preferences()
+
+ feedback_add_details("admin_verb","TSupermatterHum") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/verb/toggle_jukebox()
set name = "Toggle Jukebox"
diff --git a/code/modules/food/kitchen/cooking_machines/_cooker.dm b/code/modules/food/kitchen/cooking_machines/_cooker.dm
index 0ff53f4db7..a5215a747e 100644
--- a/code/modules/food/kitchen/cooking_machines/_cooker.dm
+++ b/code/modules/food/kitchen/cooking_machines/_cooker.dm
@@ -35,6 +35,10 @@
cooking_obj = null
return ..()
+/obj/machinery/cooker/proc/set_cooking(new_setting)
+ cooking = new_setting
+ icon_state = new_setting ? on_icon : off_icon
+
/obj/machinery/cooker/examine()
..()
if(cooking_obj && Adjacent(usr))
@@ -113,7 +117,7 @@
user.visible_message("\The [user] puts \the [I] into \the [src].")
cooking_obj = I
cooking_obj.forceMove(src)
- cooking = 1
+ set_cooking(TRUE)
icon_state = on_icon
// Doop de doo. Jeopardy theme goes here.
@@ -123,7 +127,7 @@
if(!cooking_obj || cooking_obj.loc != src)
cooking_obj = null
icon_state = off_icon
- cooking = 0
+ set_cooking(FALSE)
return
// RIP slow-moving held mobs.
@@ -166,7 +170,7 @@
if(!can_burn_food)
icon_state = off_icon
- cooking = 0
+ set_cooking(FALSE)
result.forceMove(get_turf(src))
cooking_obj = null
else
@@ -192,7 +196,7 @@
failed = 1
if(failed)
- cooking = 0
+ set_cooking(FALSE)
icon_state = off_icon
break
@@ -201,7 +205,7 @@
if(cooking_obj && user.Adjacent(src)) //Fixes borgs being able to teleport food in these machines to themselves.
user << "You grab \the [cooking_obj] from \the [src]."
user.put_in_hands(cooking_obj)
- cooking = 0
+ set_cooking(FALSE)
cooking_obj = null
icon_state = off_icon
return
diff --git a/code/modules/food/kitchen/cooking_machines/fryer.dm b/code/modules/food/kitchen/cooking_machines/fryer.dm
index 451f84c2ff..e0dc15cc45 100644
--- a/code/modules/food/kitchen/cooking_machines/fryer.dm
+++ b/code/modules/food/kitchen/cooking_machines/fryer.dm
@@ -8,6 +8,22 @@
off_icon = "fryer_off"
food_color = "#FFAD33"
cooked_sound = 'sound/machines/ding.ogg'
+ var/datum/looping_sound/deep_fryer/fry_loop
+
+/obj/machinery/cooker/fryer/Initialize()
+ fry_loop = new(list(src), FALSE)
+ return ..()
+
+/obj/machinery/cooker/fryer/Destroy()
+ QDEL_NULL(fry_loop)
+ return ..()
+
+/obj/machinery/cooker/fryer/set_cooking(new_setting)
+ ..()
+ if(new_setting)
+ fry_loop.start()
+ else
+ fry_loop.stop()
/obj/machinery/cooker/fryer/cook_mob(var/mob/living/victim, var/mob/user)
@@ -17,16 +33,19 @@
user.visible_message("\The [user] starts pushing \the [victim] into \the [src]!")
icon_state = on_icon
cooking = 1
+ fry_loop.start()
if(!do_mob(user, victim, 20))
cooking = 0
icon_state = off_icon
+ fry_loop.stop()
return
if(!victim || !victim.Adjacent(user))
user << "Your victim slipped free!"
cooking = 0
icon_state = off_icon
+ fry_loop.stop()
return
var/obj/item/organ/external/E
@@ -62,4 +81,5 @@
icon_state = off_icon
cooking = 0
+ fry_loop.stop()
return
diff --git a/code/modules/food/kitchen/microwave.dm b/code/modules/food/kitchen/microwave.dm
index 1ece9225b1..f188e90f9c 100644
--- a/code/modules/food/kitchen/microwave.dm
+++ b/code/modules/food/kitchen/microwave.dm
@@ -1,3 +1,4 @@
+<<<<<<< HEAD
/obj/machinery/microwave
name = "microwave"
icon = 'icons/obj/kitchen.dmi'
@@ -390,3 +391,402 @@
if ("dispose")
dispose()
return
+=======
+/obj/machinery/microwave
+ name = "microwave"
+ icon = 'icons/obj/kitchen.dmi'
+ icon_state = "mw"
+ density = 1
+ anchored = 1
+ use_power = 1
+ idle_power_usage = 5
+ active_power_usage = 100
+ flags = OPENCONTAINER | NOREACT
+ circuit = /obj/item/weapon/circuitboard/microwave
+ var/operating = 0 // Is it on?
+ var/dirty = 0 // = {0..100} Does it need cleaning?
+ var/broken = 0 // ={0,1,2} How broken is it???
+ var/global/list/datum/recipe/available_recipes // List of the recipes you can use
+ var/global/list/acceptable_items // List of the items you can put in
+ var/global/list/acceptable_reagents // List of the reagents you can put in
+ var/global/max_n_of_items = 0
+ var/datum/looping_sound/microwave/soundloop
+
+
+// see code/modules/food/recipes_microwave.dm for recipes
+
+/*******************
+* Initialising
+********************/
+
+/obj/machinery/microwave/Initialize()
+ reagents = new/datum/reagents(100)
+ reagents.my_atom = src
+
+ component_parts = list()
+ component_parts += new /obj/item/weapon/stock_parts/console_screen(src)
+ component_parts += new /obj/item/weapon/stock_parts/motor(src)
+ component_parts += new /obj/item/weapon/stock_parts/capacitor(src)
+
+ if (!available_recipes)
+ available_recipes = new
+ for (var/type in (typesof(/datum/recipe)-/datum/recipe))
+ available_recipes+= new type
+ acceptable_items = new
+ acceptable_reagents = new
+ for (var/datum/recipe/recipe in available_recipes)
+ for (var/item in recipe.items)
+ acceptable_items |= item
+ for (var/reagent in recipe.reagents)
+ acceptable_reagents |= reagent
+ if (recipe.items)
+ max_n_of_items = max(max_n_of_items,recipe.items.len)
+ // This will do until I can think of a fun recipe to use dionaea in -
+ // will also allow anything using the holder item to be microwaved into
+ // impure carbon. ~Z
+ acceptable_items |= /obj/item/weapon/holder
+ acceptable_items |= /obj/item/weapon/reagent_containers/food/snacks/grown
+
+ RefreshParts()
+ soundloop = new(list(src), FALSE)
+ return ..()
+
+/obj/machinery/microwave/Destroy()
+ QDEL_NULL(soundloop)
+ return ..()
+
+/*******************
+* Item Adding
+********************/
+
+/obj/machinery/microwave/attackby(var/obj/item/O as obj, var/mob/user as mob)
+ if(src.broken > 0)
+ if(src.broken == 2 && O.is_screwdriver()) // If it's broken and they're using a screwdriver
+ user.visible_message( \
+ "\The [user] starts to fix part of the microwave.", \
+ "You start to fix part of the microwave." \
+ )
+ playsound(src, O.usesound, 50, 1)
+ if (do_after(user,20 * O.toolspeed))
+ user.visible_message( \
+ "\The [user] fixes part of the microwave.", \
+ "You have fixed part of the microwave." \
+ )
+ src.broken = 1 // Fix it a bit
+ else if(src.broken == 1 && O.is_wrench()) // If it's broken and they're doing the wrench
+ user.visible_message( \
+ "\The [user] starts to fix part of the microwave.", \
+ "You start to fix part of the microwave." \
+ )
+ if (do_after(user,20 * O.toolspeed))
+ user.visible_message( \
+ "\The [user] fixes the microwave.", \
+ "You have fixed the microwave." \
+ )
+ src.icon_state = "mw"
+ src.broken = 0 // Fix it!
+ src.dirty = 0 // just to be sure
+ src.flags = OPENCONTAINER | NOREACT
+ else
+ to_chat(user, "It's broken!")
+ return 1
+ else if(default_deconstruction_screwdriver(user, O))
+ return
+ else if(default_deconstruction_crowbar(user, O))
+ return
+ else if(default_unfasten_wrench(user, O, 10))
+ return
+
+ else if(src.dirty==100) // The microwave is all dirty so can't be used!
+ if(istype(O, /obj/item/weapon/reagent_containers/spray/cleaner) || istype(O, /obj/item/weapon/soap)) // If they're trying to clean it then let them
+ user.visible_message( \
+ "\The [user] starts to clean the microwave.", \
+ "You start to clean the microwave." \
+ )
+ if (do_after(user,20))
+ user.visible_message( \
+ "\The [user] has cleaned the microwave.", \
+ "You have cleaned the microwave." \
+ )
+ src.dirty = 0 // It's clean!
+ src.broken = 0 // just to be sure
+ src.icon_state = "mw"
+ src.flags = OPENCONTAINER | NOREACT
+ else //Otherwise bad luck!!
+ to_chat(user, "It's dirty!")
+ return 1
+ else if(is_type_in_list(O,acceptable_items))
+ if (contents.len>=(max_n_of_items + component_parts.len + 1)) //Adds component_parts to the maximum number of items. The 1 is from the circuit
+ to_chat(user, "This [src] is full of ingredients, you cannot put more.")
+ return 1
+ if(istype(O, /obj/item/stack) && O:get_amount() > 1) // This is bad, but I can't think of how to change it
+ var/obj/item/stack/S = O
+ new O.type (src)
+ S.use(1)
+ user.visible_message( \
+ "\The [user] has added one of [O] to \the [src].", \
+ "You add one of [O] to \the [src].")
+ return
+ else
+ // user.remove_from_mob(O) //This just causes problems so far as I can tell. -Pete
+ user.drop_item()
+ O.loc = src
+ user.visible_message( \
+ "\The [user] has added \the [O] to \the [src].", \
+ "You add \the [O] to \the [src].")
+ return
+ else if(istype(O,/obj/item/weapon/reagent_containers/glass) || \
+ istype(O,/obj/item/weapon/reagent_containers/food/drinks) || \
+ istype(O,/obj/item/weapon/reagent_containers/food/condiment) \
+ )
+ if (!O.reagents)
+ return 1
+ for (var/datum/reagent/R in O.reagents.reagent_list)
+ if (!(R.id in acceptable_reagents))
+ to_chat(user, "Your [O] contains components unsuitable for cookery.")
+ return 1
+ return
+ else if(istype(O,/obj/item/weapon/grab))
+ var/obj/item/weapon/grab/G = O
+ to_chat(user, "This is ridiculous. You can not fit \the [G.affecting] in this [src].")
+ return 1
+ else
+ to_chat(user, "You have no idea what you can cook with this [O].")
+ ..()
+ src.updateUsrDialog()
+
+/obj/machinery/microwave/attack_ai(mob/user as mob)
+ if(istype(user, /mob/living/silicon/robot) && Adjacent(user))
+ attack_hand(user)
+
+/obj/machinery/microwave/attack_hand(mob/user as mob)
+ user.set_machine(src)
+ interact(user)
+
+/*******************
+* Microwave Menu
+********************/
+
+/obj/machinery/microwave/interact(mob/user as mob) // The microwave Menu
+ var/dat = ""
+ if(src.broken > 0)
+ dat = {"Bzzzzttttt"}
+ else if(src.operating)
+ dat = {"Microwaving in progress!
Please wait...!"}
+ else if(src.dirty==100)
+ dat = {"This microwave is dirty!
Please clean it before use!"}
+ else
+ var/list/items_counts = new
+ var/list/items_measures = new
+ var/list/items_measures_p = new
+ for (var/obj/O in ((contents - component_parts) - circuit))
+ var/display_name = O.name
+ if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/egg))
+ items_measures[display_name] = "egg"
+ items_measures_p[display_name] = "eggs"
+ if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/tofu))
+ items_measures[display_name] = "tofu chunk"
+ items_measures_p[display_name] = "tofu chunks"
+ if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/meat)) //any meat
+ items_measures[display_name] = "slab of meat"
+ items_measures_p[display_name] = "slabs of meat"
+ if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/donkpocket))
+ display_name = "Turnovers"
+ items_measures[display_name] = "turnover"
+ items_measures_p[display_name] = "turnovers"
+ if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/carpmeat))
+ items_measures[display_name] = "fillet of meat"
+ items_measures_p[display_name] = "fillets of meat"
+ items_counts[display_name]++
+ for (var/O in items_counts)
+ var/N = items_counts[O]
+ if (!(O in items_measures))
+ dat += {"[capitalize(O)]: [N] [lowertext(O)]\s
"}
+ else
+ if (N==1)
+ dat += {"[capitalize(O)]: [N] [items_measures[O]]
"}
+ else
+ dat += {"[capitalize(O)]: [N] [items_measures_p[O]]
"}
+
+ for (var/datum/reagent/R in reagents.reagent_list)
+ var/display_name = R.name
+ if (R.id == "capsaicin")
+ display_name = "Hotsauce"
+ if (R.id == "frostoil")
+ display_name = "Coldsauce"
+ dat += {"[display_name]: [R.volume] unit\s
"}
+
+ if (items_counts.len==0 && reagents.reagent_list.len==0)
+ dat = {"The microwave is empty
"}
+ else
+ dat = {"Ingredients:
[dat]"}
+ dat += {"