Headphones, fix unreachable dropoff threshold, etc.

This commit is contained in:
mochi
2020-07-22 17:50:30 +02:00
parent e554b9f19d
commit 7e3fb680c0
17 changed files with 252 additions and 69 deletions
+1 -1
View File
@@ -19,7 +19,7 @@
#define INSTRUMENT_EXP_FALLOFF_MAX 10
/// Minimum volume for when the sound is considered dead.
#define INSTRUMENT_MIN_SUSTAIN_DROPOFF 0
#define INSTRUMENT_MIN_SUSTAIN_DROPOFF 0.1
#define SUSTAIN_LINEAR 1
#define SUSTAIN_EXPONENTIAL 2
+2 -9
View File
@@ -190,9 +190,6 @@
/datum/action/item_action/toggle_mister
name = "Toggle Mister"
/datum/action/item_action/toggle_headphones
name = "Toggle Headphones"
/datum/action/item_action/toggle_helmet_light
name = "Toggle Helmet Light"
@@ -237,12 +234,8 @@
desc = "Recall yourself, and anyone nearby, to an attuned hierophant beacon at any time.<br>If the beacon is still attached, will detach it."
button_icon_state = "vortex_recall"
/datum/action/item_action/vortex_recall/IsAvailable()
if(istype(target, /obj/item/hierophant_club))
var/obj/item/hierophant_club/H = target
if(H.teleporting)
return 0
return ..()
/datum/action/item_action/change_headphones_song
name = "Change Headphones Song"
/datum/action/item_action/toggle
-18
View File
@@ -8,21 +8,3 @@
strip_delay = 15
put_on_delay = 25
resistance_flags = FLAMMABLE
/obj/item/clothing/ears/headphones
name = "headphones"
desc = "Unce unce unce unce."
var/on = 0
icon_state = "headphones0"
item_state = null
actions_types = list(/datum/action/item_action/toggle_headphones)
/obj/item/clothing/ears/headphones/attack_self(mob/user)
on = !on
icon_state = "headphones[on]"
for(var/X in actions)
var/datum/action/A = X
A.UpdateButtonIcon()
user.update_inv_ears()
@@ -27,12 +27,6 @@
user.visible_message("<span class='suicide'>[user] begins to play 'Gloomy Sunday'! It looks like [user.p_theyre()] trying to commit suicide!</span>")
return BRUTELOSS
/obj/item/instrument/attack_self(mob/user)
if(!user.IsAdvancedToolUser())
to_chat(user, "<span class='warning'>You don't have the dexterity to do this!</span>")
return TRUE
interact(user)
/obj/item/instrument/attack_self(mob/user)
tgui_interact(user)
@@ -0,0 +1,80 @@
/obj/item/clothing/ears/headphones
name = "headphones"
desc = "Unce unce unce unce."
icon_state = "headphones0"
item_state = "headphones0"
actions_types = list(/datum/action/item_action/change_headphones_song)
var/datum/song/headphones/song
/obj/item/clothing/ears/headphones/Initialize(mapload)
. = ..()
song = new(src, "piano") // Piano is the default instrument but all instruments are allowed
song.instrument_range = 0
song.allowed_instrument_ids = SSinstruments.synthesizer_instrument_ids
// To update the icon
RegisterSignal(src, COMSIG_SONG_START, .proc/start_playing)
RegisterSignal(src, COMSIG_SONG_END, .proc/stop_playing)
/obj/item/clothing/ears/headphones/Destroy()
QDEL_NULL(song)
return ..()
/obj/item/clothing/ears/headphones/attack_self(mob/user)
tgui_interact(user)
/obj/item/clothing/ears/headphones/tgui_data(mob/user)
return song.tgui_data(user)
/obj/item/clothing/ears/headphones/tgui_interact(mob/user)
if(should_stop_playing(user) || user.incapacitated())
return
song.tgui_interact(user)
/obj/item/clothing/ears/headphones/tgui_act(action, params)
if(..())
return
return song.tgui_act(action, params)
/obj/item/clothing/ears/headphones/update_icon()
var/mob/living/carbon/human/user = loc
if(istype(user))
user.update_action_buttons_icon()
user.update_inv_ears()
..()
/obj/item/clothing/ears/headphones/item_action_slot_check(slot)
if(slot == slot_l_ear || slot == slot_r_ear)
return TRUE
/**
* Called by a component signal when our song starts playing.
*/
/obj/item/clothing/ears/headphones/proc/start_playing()
icon_state = item_state = "headphones1"
update_icon()
/**
* Called by a component signal when our song stops playing.
*/
/obj/item/clothing/ears/headphones/proc/stop_playing()
icon_state = item_state = "headphones0"
update_icon()
/**
* Whether the headphone's song should stop playing
*
* Arguments:
* * user - The user
*/
/obj/item/clothing/ears/headphones/proc/should_stop_playing(mob/living/carbon/human/user)
return !(src in user) || !istype(user) || !((src == user.l_ear) || (src == user.r_ear))
// special subtype so it uses the correct item type
/datum/song/headphones
/datum/song/headphones/should_stop_playing(mob/user)
. = ..()
if(.)
return TRUE
var/obj/item/clothing/ears/headphones/I = parent
return I.should_stop_playing(user)
@@ -39,7 +39,7 @@
icon_state = "guitar"
item_state = "guitar"
attack_verb = list("played metal on", "serenaded", "crashed", "smashed")
hitsound = 'sound/weapons/stringsmash.ogg'
hitsound = 'sound/weapons/guitarslam.ogg'
allowed_instrument_ids = "guitar"
/obj/item/instrument/eguitar
+14 -9
View File
@@ -115,7 +115,7 @@
/// The kind of sustain we're using
var/sustain_mode = SUSTAIN_LINEAR
/// When a note is considered dead if it is below this in volume
var/sustain_dropoff_volume = 0
var/sustain_dropoff_volume = INSTRUMENT_MIN_SUSTAIN_DROPOFF
/// Total duration of linear sustain for 100 volume note to get to SUSTAIN_DROPOFF
var/sustain_linear_duration = 5
/// Exponential sustain dropoff rate per decisecond
@@ -168,7 +168,9 @@
var/turf/source = get_turf(parent)
for(var/mob/M in GLOB.player_list)
var/dist = get_dist(M, source)
if(dist > instrument_range)
if(dist > instrument_range) // Distance check
continue
if(!isInSight(M, source)) // Visibility check (direct line of sight)
continue
hearing_mobs[M] = dist
var/list/exited = old - hearing_mobs
@@ -251,7 +253,7 @@
* Processes our song.
*/
/datum/song/proc/process_song(wait)
if(!length(compiled_chords) || should_stop_playing(user_playing))
if(!length(compiled_chords) || current_chord > length(compiled_chords) || should_stop_playing(user_playing))
stop_playing()
return
var/list/chord = compiled_chords[current_chord]
@@ -349,26 +351,29 @@
/**
* Setter for setting how low the volume has to get before a note is considered "dead" and dropped
*/
/datum/song/proc/set_dropoff_volume(volume)
/datum/song/proc/set_dropoff_volume(volume, no_refresh = FALSE)
sustain_dropoff_volume = clamp(volume, INSTRUMENT_MIN_SUSTAIN_DROPOFF, 100)
update_sustain()
SStgui.update_uis(parent)
if(!no_refresh)
SStgui.update_uis(parent)
/**
* Setter for setting exponential falloff factor.
*/
/datum/song/proc/set_exponential_drop_rate(drop)
/datum/song/proc/set_exponential_drop_rate(drop, no_refresh = FALSE)
sustain_exponential_dropoff = clamp(drop, INSTRUMENT_EXP_FALLOFF_MIN, INSTRUMENT_EXP_FALLOFF_MAX)
update_sustain()
SStgui.update_uis(parent)
if(!no_refresh)
SStgui.update_uis(parent)
/**
* Setter for setting linear falloff duration.
*/
/datum/song/proc/set_linear_falloff_duration(duration)
/datum/song/proc/set_linear_falloff_duration(duration, no_refresh = FALSE)
sustain_linear_duration = clamp(duration, 0.1, INSTRUMENT_MAX_TOTAL_SUSTAIN)
update_sustain()
SStgui.update_uis(parent)
if(!no_refresh)
SStgui.update_uis(parent)
/datum/song/vv_edit_var(var_name, var_value)
. = ..()
+15 -18
View File
@@ -52,12 +52,12 @@
ui.set_autoupdate(FALSE) // NO!!! Don't auto-update this!!
/datum/song/tgui_act(action, params)
. = TRUE
switch(action)
if("newsong")
lines = new()
tempo = sanitize_tempo(5) // default 120 BPM
name = ""
return TRUE
if("import")
var/t = ""
do
@@ -71,20 +71,17 @@
break
while(length_char(t) > MUSIC_MAXLINES * MUSIC_MAXLINECHARS)
parse_song(t)
return FALSE
if("help")
help = !help
return TRUE
if("edit")
editing = !editing
return TRUE
if("repeat") //Changing this from a toggle to a number of repeats to avoid infinite loops.
if(playing)
return //So that people cant keep adding to repeat. If the do it intentionally, it could result in the server crashing.
repeat = clamp(round(text2num(params["new"])), 0, max_repeats)
return TRUE
if("tempo")
tempo = sanitize_tempo(text2num(params["new"]))
return TRUE
if("play")
INVOKE_ASYNC(src, .proc/start_playing, usr)
if("newline")
@@ -96,13 +93,11 @@
if(length(newline) > MUSIC_MAXLINECHARS)
newline = copytext(newline, 1, MUSIC_MAXLINECHARS)
lines.Add(newline)
return TRUE
if("deleteline")
var/num = round(text2num(params["line"]))
if(num > length(lines) || num < 1)
return
lines.Cut(num, num + 1)
return TRUE
if("modifyline")
var/num = round(text2num(params["line"]))
var/content = stripped_input(usr, "Enter your line: ", parent.name, lines[num], MUSIC_MAXLINECHARS)
@@ -111,20 +106,16 @@
if(num > length(lines) || num < 1)
return
lines[num] = content
return TRUE
if("stop")
stop_playing()
if("setlinearfalloff")
set_linear_falloff_duration(round(text2num(params["new"]) * 10, world.tick_lag))
return TRUE
set_linear_falloff_duration(round(text2num(params["new"]) * 10, world.tick_lag), TRUE)
if("setexpfalloff")
set_exponential_drop_rate(round(text2num(params["new"]), 0.00001))
return TRUE
set_exponential_drop_rate(round(text2num(params["new"]), 0.00001), TRUE)
if("setvolume")
set_volume(round(text2num(params["new"]), 1))
if("setdropoffvolume")
set_dropoff_volume(round(text2num(params["new"]), 0.01))
return TRUE
set_dropoff_volume(round(text2num(params["new"]), 0.01), TRUE)
if("switchinstrument")
if(!length(allowed_instrument_ids))
return
@@ -136,20 +127,25 @@
var/datum/instrument/I = SSinstruments.get_instrument(i)
if(I && I.name == choice)
set_instrument(I)
return TRUE
if("setnoteshift")
note_shift = clamp(round(text2num(params["new"])), note_shift_min, note_shift_max)
return TRUE
if("setsustainmode")
var/static/list/sustain_modes
if(!length(sustain_modes))
sustain_modes = list("Linear" = SUSTAIN_LINEAR, "Exponential" = SUSTAIN_EXPONENTIAL)
var/choice = params["new"]
sustain_mode = sustain_modes[choice] || sustain_mode
return TRUE
if("togglesustainhold")
full_sustain_held_note = !full_sustain_held_note
return TRUE
if("reset")
var/default_instrument = allowed_instrument_ids[1]
if(using_instrument != SSinstruments.instrument_data[default_instrument])
set_instrument(default_instrument)
note_shift = initial(note_shift)
sustain_mode = initial(sustain_mode)
set_linear_falloff_duration(initial(sustain_linear_duration), TRUE)
set_exponential_drop_rate(initial(sustain_exponential_dropoff), TRUE)
set_dropoff_volume(initial(sustain_dropoff_volume), TRUE)
else
return FALSE
parent.add_fingerprint(usr)
@@ -160,6 +156,7 @@
/datum/song/proc/parse_song(text)
set waitfor = FALSE
//split into lines
stop_playing()
lines = splittext(text, "\n")
if(length(lines))
var/bpm_string = "BPM: "
Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

+1
View File
@@ -1649,6 +1649,7 @@
#include "code\modules\instruments\piano.dm"
#include "code\modules\instruments\synth_tones.dm"
#include "code\modules\instruments\objs\items\_instrument.dm"
#include "code\modules\instruments\objs\items\headphones.dm"
#include "code\modules\instruments\objs\items\instruments.dm"
#include "code\modules\instruments\objs\structures\_musician.dm"
#include "code\modules\instruments\objs\structures\piano.dm"
Binary file not shown.
+110 -3
View File
@@ -25,9 +25,15 @@ const InstrumentHelp = (properties, context) => {
return;
}
return (
<Modal maxWidth="75%" mx="auto" py="0" px="0.5rem">
<Section title="Help" level="2">
<Modal
maxWidth="75%"
height={(window.innerHeight * 0.75) + "px"}
mx="auto"
py="0"
px="0.5rem">
<Section height="100%" title="Help" level="2" overflow="auto">
<Box px="0.5rem" mt="-0.5rem">
<h1>Making a Song</h1>
<p>
Lines are a series of chords, separated by commas&nbsp;
<Box as="span" color="highlight">(,)</Box>,
@@ -85,6 +91,101 @@ const InstrumentHelp = (properties, context) => {
<li>A song may only contain up to 1,000 lines.</li>
</ul>
</p>
<p>
Lines are a series of chords, separated by commas&nbsp;
<Box as="span" color="highlight">(,)</Box>,
each with notes seperated by hyphens&nbsp;
<Box as="span" color="highlight">(-)</Box>.
<br />
Every note in a chord will play together,
with the chord timed by the&nbsp;
<Box as="span" color="highlight">tempo</Box> as defined above.
</p>
<p>
Notes are played by the&nbsp;
<Box as="span" color="good">names of the note</Box>,
and optionally, the&nbsp;
<Box as="span" color="average">accidental</Box>,
and/or the <Box as="span" color="bad">octave number</Box>.
<br />
By default, every note is&nbsp;
<Box as="span" color="average">natural</Box> and in&nbsp;
<Box as="span" color="bad">octave 3</Box>.
Defining a different state for either is
remembered for each <Box as="span" color="good">note</Box>.
<ul>
<li>
<Box as="span" color="highlight">Example:</Box>&nbsp;
<i>C,D,E,F,G,A,B</i> will play a&nbsp;
<Box as="span" color="good">C</Box>&nbsp;
<Box as="span" color="average">major</Box> scale.
</li>
<li>
After a note has an&nbsp;
<Box as="span" color="average">accidental</Box> or&nbsp;
<Box as="span" color="bad">octave</Box> placed,
it will be remembered:&nbsp;
<i>C,C4,C#,C3</i> is <i>C3,C4,C4#,C3#</i>
</li>
</ul>
</p>
<p>
<Box as="span" color="highlight">Chords</Box>
&nbsp;can be played simply by seperating each note
with a hyphen: <i>A-C#,Cn-E,E-G#,Gn-B</i>.<br />
A <Box as="span" color="highlight">pause</Box>
&nbsp;may be denoted by an empty chord: <i>C,E,,C,G</i>.
<br />
To make a chord be a different time, end it
with /x, where the chord length will be length defined by&nbsp;
<Box as="span" color="highlight">tempo / x</Box>,&nbsp;
<Box as="span" color="highlight">eg:</Box> <i>C,G/2,E/4</i>.
</p>
<p>
Combined, an example line is: <i>E-E4/4,F#/2,G#/8,B/8,E3-E4/4</i>.
<ul>
<li>Lines may be up to 300 characters.</li>
<li>A song may only contain up to 1,000 lines.</li>
</ul>
</p>
<h1>Instrument Advanced Settings</h1>
<ul>
<li>
<Box as="span" color="label">Type:</Box>
&nbsp;Whether the instrument is legacy or synthesized.<br />
Legacy instruments have a collection of sounds that are
selectively used depending on the note to play.<br />
Synthesized instruments use a base sound and change its pitch to
match the note to play.
</li>
<li>
<Box as="span" color="label">Current:</Box>
&nbsp;Which instrument sample to play. Some instruments
can be tuned to play different samples. Experiment!
</li>
<li>
<Box as="span" color="label">Note Shift/Note Transpose:</Box>
&nbsp;The pitch to apply to all notes of the song.
</li>
<li>
<Box as="span" color="label">Sustain Mode:</Box>
&nbsp;How a played note fades out.<br />
Linear sustain means a note will fade out at a constant rate.
<br />
Exponential sustain means a note will fade out at an
exponential rate, sounding smoother.
</li>
<li>
<Box as="span" color="label">Volume Dropoff Threshold:</Box>
&nbsp;The volume threshold at which a note is fully stopped.
</li>
<li>
<Box as="span" color="label">
Sustain indefinitely last held note:
</Box>
&nbsp;Whether the last note should be sustained indefinitely.
</li>
</ul>
<Button
color="grey"
content="Close"
@@ -315,7 +416,7 @@ const InstrumentStatusAdvanced = (properties, context) => {
<LabeledList.Item label="Volume Dropoff Threshold">
<Slider
animated
minValue="0"
minValue="0.01"
maxValue="100"
value={sustainDropoffVolume}
stepPixelSize="6"
@@ -335,6 +436,12 @@ const InstrumentStatusAdvanced = (properties, context) => {
</Fragment>
)}
</LabeledList>
<Button
icon="redo"
content="Reset to Default"
mt="0.5rem"
onClick={() => act('reset')}
/>
</Section>
</Collapsible>
</Box>
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,5 +1,6 @@
@use 'sass:color';
@use '../colors.scss';
@use '../base.scss';
$color-title: #ffffff !default;
$color-background: rgba(0, 0, 0, 0.33) !default;
@@ -18,6 +19,29 @@ $color-separator: colors.$primary !default;
box-shadow: inset 0 0 5px $color-shadow;
box-sizing: border-box;
// Fancy scrollbar
scrollbar-base-color: color.scale(
base.$color-bg,
$lightness: -25%);
scrollbar-face-color: color.scale(
base.$color-bg,
$lightness: 10%);
scrollbar-3dlight-color: color.scale(
base.$color-bg,
$lightness: 0%);
scrollbar-highlight-color: color.scale(
base.$color-bg,
$lightness: 0%);
scrollbar-track-color: color.scale(
base.$color-bg,
$lightness: -25%);
scrollbar-arrow-color: color.scale(
base.$color-bg,
$lightness: 50%);
scrollbar-shadow-color: color.scale(
base.$color-bg,
$lightness: 10%);
&:last-child {
margin-bottom: 0;
}