Merge branch 'master' into tools_andstuff
This commit is contained in:
@@ -317,7 +317,7 @@
|
||||
throwforce = 10
|
||||
throw_range = 3
|
||||
hitsound = 'sound/items/trayhit1.ogg'
|
||||
hit_reaction_chance = 50
|
||||
block_chance = 50
|
||||
custom_materials = list(/datum/material/iron = 2000)
|
||||
var/break_chance = 5 //Likely hood of smashing the chair.
|
||||
var/obj/structure/chair/origin_type = /obj/structure/chair
|
||||
@@ -362,18 +362,17 @@
|
||||
new stack_type(get_turf(loc))
|
||||
qdel(src)
|
||||
|
||||
/obj/item/chair/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
|
||||
if(attack_type == UNARMED_ATTACK && prob(hit_reaction_chance))
|
||||
owner.visible_message("<span class='danger'>[owner] fends off [attack_text] with [src]!</span>")
|
||||
return 1
|
||||
return 0
|
||||
/obj/item/chair/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
|
||||
if(!(attack_type & ATTACK_TYPE_UNARMED))
|
||||
return NONE
|
||||
return ..()
|
||||
|
||||
/obj/item/chair/afterattack(atom/target, mob/living/carbon/user, proximity)
|
||||
. = ..()
|
||||
if(!proximity)
|
||||
return
|
||||
if(prob(break_chance))
|
||||
user.visible_message("<span class='danger'>[user] smashes \the [src] to pieces against \the [target]</span>")
|
||||
user.visible_message("<span class='danger'>[user] smashes [src] to pieces against [target]</span>")
|
||||
if(iscarbon(target))
|
||||
var/mob/living/carbon/C = target
|
||||
if(C.health < C.maxHealth*0.5)
|
||||
|
||||
@@ -16,8 +16,8 @@ LINEN BINS
|
||||
throw_speed = 1
|
||||
throw_range = 2
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
item_color = "white"
|
||||
resistance_flags = FLAMMABLE
|
||||
dying_key = DYE_REGISTRY_BEDSHEET
|
||||
|
||||
dog_fashion = /datum/dog_fashion/head/ghost
|
||||
var/list/dream_messages = list("white")
|
||||
@@ -52,79 +52,66 @@ LINEN BINS
|
||||
|
||||
/obj/item/bedsheet/blue
|
||||
icon_state = "sheetblue"
|
||||
item_color = "blue"
|
||||
dream_messages = list("blue")
|
||||
|
||||
/obj/item/bedsheet/green
|
||||
icon_state = "sheetgreen"
|
||||
item_color = "green"
|
||||
dream_messages = list("green")
|
||||
|
||||
/obj/item/bedsheet/grey
|
||||
icon_state = "sheetgrey"
|
||||
item_color = "grey"
|
||||
dream_messages = list("grey")
|
||||
|
||||
/obj/item/bedsheet/orange
|
||||
icon_state = "sheetorange"
|
||||
item_color = "orange"
|
||||
dream_messages = list("orange")
|
||||
|
||||
/obj/item/bedsheet/purple
|
||||
icon_state = "sheetpurple"
|
||||
item_color = "purple"
|
||||
dream_messages = list("purple")
|
||||
|
||||
/obj/item/bedsheet/patriot
|
||||
name = "patriotic bedsheet"
|
||||
desc = "You've never felt more free than when sleeping on this."
|
||||
icon_state = "sheetUSA"
|
||||
item_color = "sheetUSA"
|
||||
dream_messages = list("America", "freedom", "fireworks", "bald eagles")
|
||||
|
||||
/obj/item/bedsheet/rainbow
|
||||
name = "rainbow bedsheet"
|
||||
desc = "A multicolored blanket. It's actually several different sheets cut up and sewn together."
|
||||
icon_state = "sheetrainbow"
|
||||
item_color = "rainbow"
|
||||
dream_messages = list("red", "orange", "yellow", "green", "blue", "purple", "a rainbow")
|
||||
|
||||
/obj/item/bedsheet/red
|
||||
icon_state = "sheetred"
|
||||
item_color = "red"
|
||||
dream_messages = list("red")
|
||||
|
||||
/obj/item/bedsheet/yellow
|
||||
icon_state = "sheetyellow"
|
||||
item_color = "yellow"
|
||||
dream_messages = list("yellow")
|
||||
|
||||
/obj/item/bedsheet/mime
|
||||
name = "mime's blanket"
|
||||
desc = "A very soothing striped blanket. All the noise just seems to fade out when you're under the covers in this."
|
||||
icon_state = "sheetmime"
|
||||
item_color = "mime"
|
||||
dream_messages = list("silence", "gestures", "a pale face", "a gaping mouth", "the mime")
|
||||
|
||||
/obj/item/bedsheet/clown
|
||||
name = "clown's blanket"
|
||||
desc = "A rainbow blanket with a clown mask woven in. It smells faintly of bananas."
|
||||
icon_state = "sheetclown"
|
||||
item_color = "clown"
|
||||
dream_messages = list("honk", "laughter", "a prank", "a joke", "a smiling face", "the clown")
|
||||
|
||||
/obj/item/bedsheet/captain
|
||||
name = "captain's bedsheet"
|
||||
desc = "It has a Nanotrasen symbol on it, and was woven with a revolutionary new kind of thread guaranteed to have 0.01% permeability for most non-chemical substances, popular among most modern captains."
|
||||
icon_state = "sheetcaptain"
|
||||
item_color = "captain"
|
||||
dream_messages = list("authority", "a golden ID", "sunglasses", "a green disc", "an antique gun", "the captain")
|
||||
|
||||
/obj/item/bedsheet/rd
|
||||
name = "research director's bedsheet"
|
||||
desc = "It appears to have a beaker emblem, and is made out of fire-resistant material, although it probably won't protect you in the event of fires you're familiar with every day."
|
||||
icon_state = "sheetrd"
|
||||
item_color = "director"
|
||||
dream_messages = list("authority", "a silvery ID", "a bomb", "a mech", "a facehugger", "maniacal laughter", "the research director")
|
||||
|
||||
// for Free Golems.
|
||||
@@ -137,111 +124,94 @@ LINEN BINS
|
||||
name = "medical blanket"
|
||||
desc = "It's a sterilized* blanket commonly used in the Medbay. *Sterilization is voided if a virologist is present onboard the station."
|
||||
icon_state = "sheetmedical"
|
||||
item_color = "medical"
|
||||
dream_messages = list("healing", "life", "surgery", "a doctor")
|
||||
|
||||
/obj/item/bedsheet/cmo
|
||||
name = "chief medical officer's bedsheet"
|
||||
desc = "It's a sterilized blanket that has a cross emblem. There's some cat fur on it, likely from Runtime."
|
||||
icon_state = "sheetcmo"
|
||||
item_color = "cmo"
|
||||
dream_messages = list("authority", "a silvery ID", "healing", "life", "surgery", "a cat", "the chief medical officer")
|
||||
|
||||
/obj/item/bedsheet/hos
|
||||
name = "head of security's bedsheet"
|
||||
desc = "It is decorated with a shield emblem. While crime doesn't sleep, you do, but you are still THE LAW!"
|
||||
icon_state = "sheethos"
|
||||
item_color = "hosred"
|
||||
dream_messages = list("authority", "a silvery ID", "handcuffs", "a baton", "a flashbang", "sunglasses", "the head of security")
|
||||
|
||||
/obj/item/bedsheet/hop
|
||||
name = "head of personnel's bedsheet"
|
||||
desc = "It is decorated with a key emblem. For those rare moments when you can rest and cuddle with Ian without someone screaming for you over the radio."
|
||||
icon_state = "sheethop"
|
||||
item_color = "hop"
|
||||
dream_messages = list("authority", "a silvery ID", "obligation", "a computer", "an ID", "a corgi", "the head of personnel")
|
||||
|
||||
/obj/item/bedsheet/ce
|
||||
name = "chief engineer's bedsheet"
|
||||
desc = "It is decorated with a wrench emblem. It's highly reflective and stain resistant, so you don't need to worry about ruining it with oil."
|
||||
icon_state = "sheetce"
|
||||
item_color = "chief"
|
||||
dream_messages = list("authority", "a silvery ID", "the engine", "power tools", "an APC", "a parrot", "the chief engineer")
|
||||
|
||||
/obj/item/bedsheet/qm
|
||||
name = "quartermaster's bedsheet"
|
||||
desc = "It is decorated with a crate emblem in silver lining. It's rather tough, and just the thing to lie on after a hard day of pushing paper."
|
||||
icon_state = "sheetqm"
|
||||
item_color = "qm"
|
||||
dream_messages = list("a grey ID", "a shuttle", "a crate", "a sloth", "the quartermaster")
|
||||
|
||||
/obj/item/bedsheet/brown
|
||||
icon_state = "sheetbrown"
|
||||
item_color = "cargo"
|
||||
dream_messages = list("brown")
|
||||
|
||||
/obj/item/bedsheet/black
|
||||
icon_state = "sheetblack"
|
||||
item_color = "black"
|
||||
dream_messages = list("black")
|
||||
|
||||
/obj/item/bedsheet/centcom
|
||||
name = "\improper CentCom bedsheet"
|
||||
desc = "Woven with advanced nanothread for warmth as well as being very decorated, essential for all officials."
|
||||
icon_state = "sheetcentcom"
|
||||
item_color = "centcom"
|
||||
dream_messages = list("a unique ID", "authority", "artillery", "an ending")
|
||||
|
||||
/obj/item/bedsheet/syndie
|
||||
name = "syndicate bedsheet"
|
||||
desc = "It has a syndicate emblem and it has an aura of evil."
|
||||
icon_state = "sheetsyndie"
|
||||
item_color = "syndie"
|
||||
dream_messages = list("a green disc", "a red crystal", "a glowing blade", "a wire-covered ID")
|
||||
|
||||
/obj/item/bedsheet/cult
|
||||
name = "cultist's bedsheet"
|
||||
desc = "You might dream of Nar'Sie if you sleep with this. It seems rather tattered and glows of an eldritch presence."
|
||||
icon_state = "sheetcult"
|
||||
item_color = "cult"
|
||||
dream_messages = list("a tome", "a floating red crystal", "a glowing sword", "a bloody symbol", "a massive humanoid figure")
|
||||
|
||||
/obj/item/bedsheet/wiz
|
||||
name = "wizard's bedsheet"
|
||||
desc = "A special fabric enchanted with magic so you can have an enchanted night. It even glows!"
|
||||
icon_state = "sheetwiz"
|
||||
item_color = "wiz"
|
||||
dream_messages = list("a book", "an explosion", "lightning", "a staff", "a skeleton", "a robe", "magic")
|
||||
|
||||
/obj/item/bedsheet/nanotrasen
|
||||
name = "nanotrasen bedsheet"
|
||||
desc = "It has the Nanotrasen logo on it and has an aura of duty."
|
||||
icon_state = "sheetNT"
|
||||
item_color = "nanotrasen"
|
||||
dream_messages = list("authority", "an ending")
|
||||
|
||||
/obj/item/bedsheet/ian
|
||||
icon_state = "sheetian"
|
||||
item_color = "ian"
|
||||
dream_messages = list("a dog", "a corgi", "woof", "bark", "arf")
|
||||
|
||||
/obj/item/bedsheet/runtime
|
||||
icon_state = "sheetruntime"
|
||||
item_color = "runtime"
|
||||
dream_messages = list("a kitty", "a cat", "meow", "purr", "nya~")
|
||||
|
||||
/obj/item/bedsheet/pirate
|
||||
name = "pirate's bedsheet"
|
||||
desc = "It has a Jolly Roger emblem on it and has a faint scent of grog."
|
||||
icon_state = "sheetpirate"
|
||||
item_color = "black"
|
||||
dream_messages = list("doing whatever oneself wants", "cause a pirate is free", "being a pirate", "stealing", "landlubbers", "gold", "a buried treasure", "yarr", "avast", "a swashbuckler", "sailing the Seven Seas", "a parrot", "a monkey", "an island", "a talking skull")
|
||||
|
||||
/obj/item/bedsheet/gondola
|
||||
name = "gondola bedsheet"
|
||||
desc = "A precious bedsheet made from the hide of a rare and peculiar critter."
|
||||
icon_state = "sheetgondola"
|
||||
item_color = "cargo"
|
||||
var/g_mouth
|
||||
var/g_eyes
|
||||
|
||||
@@ -252,7 +222,7 @@ LINEN BINS
|
||||
add_overlay(g_mouth)
|
||||
add_overlay(g_eyes)
|
||||
|
||||
/obj/item/bedsheet/gondola/worn_overlays(isinhands = FALSE, icon_file, style_flags = NONE)
|
||||
/obj/item/bedsheet/gondola/worn_overlays(isinhands = FALSE, icon_file, used_state, style_flags = NONE)
|
||||
. = ..()
|
||||
if(!isinhands)
|
||||
. += mutable_appearance(icon_file, g_mouth)
|
||||
@@ -262,14 +232,12 @@ LINEN BINS
|
||||
name = "cosmic space bedsheet"
|
||||
desc = "Made from the dreams of those who wonder at the stars."
|
||||
icon_state = "sheetcosmos"
|
||||
item_color = "cosmos"
|
||||
dream_messages = list("the infinite cosmos", "Hans Zimmer music", "a flight through space", "the galaxy", "being fabulous", "shooting stars")
|
||||
light_power = 2
|
||||
light_range = 1.4
|
||||
|
||||
/obj/item/bedsheet/random
|
||||
icon_state = "random_bedsheet"
|
||||
item_color = "rainbow"
|
||||
name = "random bedsheet"
|
||||
desc = "If you're reading this description ingame, something has gone wrong! Honk!"
|
||||
|
||||
|
||||
@@ -100,3 +100,12 @@
|
||||
if(isliving(A))
|
||||
to_chat(A, "<span class='userdanger'>You're suddenly forced into a tiny, compressed space!</span>")
|
||||
qdel(src)
|
||||
|
||||
/obj/structure/closet/body_bag/containment
|
||||
name = "containment body bag"
|
||||
desc = "A folded heavy body bag designed for the storage and transportation of cadavers with heavy radiation."
|
||||
icon = 'icons/obj/bodybag.dmi'
|
||||
icon_state = "radbodybag"
|
||||
mob_storage_capacity = 1
|
||||
foldedbag_path = /obj/item/bodybag/containment
|
||||
rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE
|
||||
@@ -179,6 +179,7 @@
|
||||
desc = "A crate with a radiation sign on it."
|
||||
name = "radiation crate"
|
||||
icon_state = "radiation"
|
||||
rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE
|
||||
|
||||
/obj/structure/closet/crate/hydroponics
|
||||
name = "hydroponics crate"
|
||||
|
||||
@@ -1,383 +0,0 @@
|
||||
|
||||
#define MUSICIAN_HEARCHECK_MINDELAY 4
|
||||
#define MUSIC_MAXLINES 600
|
||||
#define MUSIC_MAXLINECHARS 150
|
||||
|
||||
/datum/song
|
||||
var/name = "Untitled"
|
||||
var/list/lines = new()
|
||||
var/tempo = 5 // delay between notes
|
||||
|
||||
var/playing = 0 // if we're playing
|
||||
var/help = 0 // if help is open
|
||||
var/edit = 1 // if we're in editing mode
|
||||
var/repeat = 0 // number of times remaining to repeat
|
||||
var/max_repeats = 10 // maximum times we can repeat
|
||||
|
||||
var/instrumentDir = "piano" // the folder with the sounds
|
||||
var/instrumentExt = "ogg" // the file extension
|
||||
var/obj/instrumentObj = null // the associated obj playing the sound
|
||||
var/last_hearcheck = 0
|
||||
var/list/hearing_mobs
|
||||
|
||||
/datum/song/New(dir, obj, ext = "ogg")
|
||||
tempo = sanitize_tempo(tempo)
|
||||
instrumentDir = dir
|
||||
instrumentObj = obj
|
||||
instrumentExt = ext
|
||||
|
||||
/datum/song/Destroy()
|
||||
instrumentObj = null
|
||||
return ..()
|
||||
|
||||
// note is a number from 1-7 for A-G
|
||||
// acc is either "b", "n", or "#"
|
||||
// oct is 1-8 (or 9 for C)
|
||||
/datum/song/proc/playnote(note, acc as text, oct)
|
||||
// handle accidental -> B<>C of E<>F
|
||||
if(acc == "b" && (note == 3 || note == 6)) // C or F
|
||||
if(note == 3)
|
||||
oct--
|
||||
note--
|
||||
acc = "n"
|
||||
else if(acc == "#" && (note == 2 || note == 5)) // B or E
|
||||
if(note == 2)
|
||||
oct++
|
||||
note++
|
||||
acc = "n"
|
||||
else if(acc == "#" && (note == 7)) //G#
|
||||
note = 1
|
||||
acc = "b"
|
||||
else if(acc == "#") // mass convert all sharps to flats, octave jump already handled
|
||||
acc = "b"
|
||||
note++
|
||||
|
||||
// check octave, C is allowed to go to 9
|
||||
if(oct < 1 || (note == 3 ? oct > 9 : oct > 8))
|
||||
return
|
||||
|
||||
// now generate name
|
||||
var/soundfile = "sound/instruments/[instrumentDir]/[ascii2text(note+64)][acc][oct].[instrumentExt]"
|
||||
soundfile = file(soundfile)
|
||||
// make sure the note exists
|
||||
if(!fexists(soundfile))
|
||||
return
|
||||
// and play
|
||||
var/turf/source = get_turf(instrumentObj)
|
||||
if((world.time - MUSICIAN_HEARCHECK_MINDELAY) > last_hearcheck)
|
||||
LAZYCLEARLIST(hearing_mobs)
|
||||
for(var/mob/M in get_hearers_in_view(15, source))
|
||||
if(!M.client || !(M.client.prefs.toggles & SOUND_INSTRUMENTS))
|
||||
continue
|
||||
LAZYADD(hearing_mobs, M)
|
||||
last_hearcheck = world.time
|
||||
|
||||
var/sound/music_played = sound(soundfile)
|
||||
for(var/i in hearing_mobs)
|
||||
var/mob/M = i
|
||||
M.playsound_local(source, null, 100, falloff = 5, S = music_played)
|
||||
|
||||
/datum/song/proc/updateDialog(mob/user)
|
||||
instrumentObj.updateDialog() // assumes it's an object in world, override if otherwise
|
||||
|
||||
/datum/song/proc/shouldStopPlaying(mob/user)
|
||||
if(instrumentObj)
|
||||
if(!user.canUseTopic(instrumentObj, TRUE, FALSE, FALSE, FALSE))
|
||||
return TRUE
|
||||
return !instrumentObj.anchored // add special cases to stop in subclasses
|
||||
else
|
||||
return TRUE
|
||||
|
||||
/datum/song/proc/playsong(mob/user)
|
||||
while(repeat >= 0)
|
||||
var/cur_oct[7]
|
||||
var/cur_acc[7]
|
||||
for(var/i = 1 to 7)
|
||||
cur_oct[i] = 3
|
||||
cur_acc[i] = "n"
|
||||
|
||||
for(var/line in lines)
|
||||
for(var/beat in splittext(lowertext(line), ","))
|
||||
var/list/notes = splittext(beat, "/")
|
||||
for(var/note in splittext(notes[1], "-"))
|
||||
if(!playing || shouldStopPlaying(user))//If the instrument is playing, or special case
|
||||
playing = FALSE
|
||||
hearing_mobs = null
|
||||
return
|
||||
if(!length(note))
|
||||
continue
|
||||
var/cur_note = text2ascii(note) - 96
|
||||
if(cur_note < 1 || cur_note > 7)
|
||||
continue
|
||||
var/notelen = length(note)
|
||||
var/ni = ""
|
||||
for(var/i = length(note[1]) + 1, i <= notelen, i += length(ni))
|
||||
ni = note[i]
|
||||
if(!text2num(ni))
|
||||
if(ni == "#" || ni == "b" || ni == "n")
|
||||
cur_acc[cur_note] = ni
|
||||
else if(ni == "s")
|
||||
cur_acc[cur_note] = "#" // so shift is never required
|
||||
else
|
||||
cur_oct[cur_note] = text2num(ni)
|
||||
if(user.dizziness > 0 && prob(user.dizziness / 2))
|
||||
cur_note = CLAMP(cur_note + rand(round(-user.dizziness / 10), round(user.dizziness / 10)), 1, 7)
|
||||
if(user.dizziness > 0 && prob(user.dizziness / 5))
|
||||
if(prob(30))
|
||||
cur_acc[cur_note] = "#"
|
||||
else if(prob(42))
|
||||
cur_acc[cur_note] = "b"
|
||||
else if(prob(75))
|
||||
cur_acc[cur_note] = "n"
|
||||
playnote(cur_note, cur_acc[cur_note], cur_oct[cur_note])
|
||||
if(notes.len >= 2 && text2num(notes[2]))
|
||||
sleep(sanitize_tempo(tempo / text2num(notes[2])))
|
||||
else
|
||||
sleep(tempo)
|
||||
repeat--
|
||||
hearing_mobs = null
|
||||
playing = FALSE
|
||||
repeat = 0
|
||||
updateDialog(user)
|
||||
|
||||
/datum/song/proc/interact(mob/user)
|
||||
var/dat = ""
|
||||
|
||||
if(lines.len > 0)
|
||||
dat += "<H3>Playback</H3>"
|
||||
if(!playing)
|
||||
dat += "<A href='?src=[REF(src)];play=1'>Play</A> <SPAN CLASS='linkOn'>Stop</SPAN><BR><BR>"
|
||||
dat += "Repeat Song: "
|
||||
dat += repeat > 0 ? "<A href='?src=[REF(src)];repeat=-10'>-</A><A href='?src=[REF(src)];repeat=-1'>-</A>" : "<SPAN CLASS='linkOff'>-</SPAN><SPAN CLASS='linkOff'>-</SPAN>"
|
||||
dat += " [repeat] times "
|
||||
dat += repeat < max_repeats ? "<A href='?src=[REF(src)];repeat=1'>+</A><A href='?src=[REF(src)];repeat=10'>+</A>" : "<SPAN CLASS='linkOff'>+</SPAN><SPAN CLASS='linkOff'>+</SPAN>"
|
||||
dat += "<BR>"
|
||||
else
|
||||
dat += "<SPAN CLASS='linkOn'>Play</SPAN> <A href='?src=[REF(src)];stop=1'>Stop</A><BR>"
|
||||
dat += "Repeats left: <B>[repeat]</B><BR>"
|
||||
if(!edit)
|
||||
dat += "<BR><B><A href='?src=[REF(src)];edit=2'>Show Editor</A></B><BR>"
|
||||
else
|
||||
dat += "<H3>Editing</H3>"
|
||||
dat += "<B><A href='?src=[REF(src)];edit=1'>Hide Editor</A></B>"
|
||||
dat += " <A href='?src=[REF(src)];newsong=1'>Start a New Song</A>"
|
||||
dat += " <A href='?src=[REF(src)];import=1'>Import a Song</A><BR><BR>"
|
||||
var/bpm = round(600 / tempo)
|
||||
dat += "Tempo: <A href='?src=[REF(src)];tempo=[world.tick_lag]'>-</A> [bpm] BPM <A href='?src=[REF(src)];tempo=-[world.tick_lag]'>+</A><BR><BR>"
|
||||
var/linecount = 0
|
||||
for(var/line in lines)
|
||||
linecount += 1
|
||||
dat += "Line [linecount]: <A href='?src=[REF(src)];modifyline=[linecount]'>Edit</A> <A href='?src=[REF(src)];deleteline=[linecount]'>X</A> [line]<BR>"
|
||||
dat += "<A href='?src=[REF(src)];newline=1'>Add Line</A><BR><BR>"
|
||||
if(help)
|
||||
dat += "<B><A href='?src=[REF(src)];help=1'>Hide Help</A></B><BR>"
|
||||
dat += {"
|
||||
Lines are a series of chords, separated by commas (,), each with notes separated by hyphens (-).<br>
|
||||
Every note in a chord will play together, with chord timed by the tempo.<br>
|
||||
<br>
|
||||
Notes are played by the names of the note, and optionally, the accidental, and/or the octave number.<br>
|
||||
By default, every note is natural and in octave 3. Defining otherwise is remembered for each note.<br>
|
||||
Example: <i>C,D,E,F,G,A,B</i> will play a C major scale.<br>
|
||||
After a note has an accidental placed, it will be remembered: <i>C,C4,C,C3</i> is <i>C3,C4,C4,C3</i><br>
|
||||
Chords can be played simply by seperating each note with a hyphon: <i>A-C#,Cn-E,E-G#,Gn-B</i><br>
|
||||
A pause 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<br>
|
||||
defined by tempo / x: <i>C,G/2,E/4</i><br>
|
||||
Combined, an example is: <i>E-E4/4,F#/2,G#/8,B/8,E3-E4/4</i>
|
||||
<br>
|
||||
Lines may be up to [MUSIC_MAXLINECHARS] characters.<br>
|
||||
A song may only contain up to [MUSIC_MAXLINES] lines.<br>
|
||||
"}
|
||||
else
|
||||
dat += "<B><A href='?src=[REF(src)];help=2'>Show Help</A></B><BR>"
|
||||
|
||||
var/datum/browser/popup = new(user, "instrument", instrumentObj.name, 700, 500)
|
||||
popup.set_content(dat)
|
||||
popup.set_title_image(user.browse_rsc_icon(instrumentObj.icon, instrumentObj.icon_state))
|
||||
popup.open()
|
||||
|
||||
/datum/song/proc/ParseSong(text)
|
||||
set waitfor = FALSE
|
||||
//split into lines
|
||||
lines = splittext(text, "\n")
|
||||
if(lines.len)
|
||||
var/bpm_string = "BPM: "
|
||||
if(findtext(lines[1], bpm_string, 1, length(bpm_string) + 1))
|
||||
var/divisor = text2num(copytext(lines[1], length(bpm_string) + 1)) || 120 // default
|
||||
tempo = sanitize_tempo(600 / round(divisor, 1))
|
||||
lines.Cut(1, 2)
|
||||
else
|
||||
tempo = sanitize_tempo(5) // default 120 BPM
|
||||
if(lines.len > MUSIC_MAXLINES)
|
||||
to_chat(usr, "Too many lines!")
|
||||
lines.Cut(MUSIC_MAXLINES + 1)
|
||||
var/linenum = 1
|
||||
for(var/l in lines)
|
||||
if(length_char(l) > MUSIC_MAXLINECHARS)
|
||||
to_chat(usr, "Line [linenum] too long!")
|
||||
lines.Remove(l)
|
||||
else
|
||||
linenum++
|
||||
updateDialog(usr) // make sure updates when complete
|
||||
|
||||
/datum/song/Topic(href, href_list)
|
||||
if(!usr.canUseTopic(instrumentObj, TRUE, FALSE, FALSE, FALSE))
|
||||
usr << browse(null, "window=instrument")
|
||||
usr.unset_machine()
|
||||
return
|
||||
|
||||
instrumentObj.add_fingerprint(usr)
|
||||
|
||||
if(href_list["newsong"])
|
||||
lines = new()
|
||||
tempo = sanitize_tempo(5) // default 120 BPM
|
||||
name = ""
|
||||
|
||||
else if(href_list["import"])
|
||||
var/t = ""
|
||||
do
|
||||
t = html_encode(input(usr, "Please paste the entire song, formatted:", text("[]", name), t) as message)
|
||||
if(!in_range(instrumentObj, usr))
|
||||
return
|
||||
|
||||
if(length_char(t) >= MUSIC_MAXLINES * MUSIC_MAXLINECHARS)
|
||||
var/cont = input(usr, "Your message is too long! Would you like to continue editing it?", "", "yes") in list("yes", "no")
|
||||
if(cont == "no")
|
||||
break
|
||||
while(length_char(t) > MUSIC_MAXLINES * MUSIC_MAXLINECHARS)
|
||||
ParseSong(t)
|
||||
|
||||
else if(href_list["help"])
|
||||
help = text2num(href_list["help"]) - 1
|
||||
|
||||
else if(href_list["edit"])
|
||||
edit = text2num(href_list["edit"]) - 1
|
||||
|
||||
if(href_list["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 += round(text2num(href_list["repeat"]))
|
||||
if(repeat < 0)
|
||||
repeat = 0
|
||||
if(repeat > max_repeats)
|
||||
repeat = max_repeats
|
||||
|
||||
else if(href_list["tempo"])
|
||||
tempo = sanitize_tempo(tempo + text2num(href_list["tempo"]))
|
||||
|
||||
else if(href_list["play"])
|
||||
playing = TRUE
|
||||
spawn()
|
||||
playsong(usr)
|
||||
|
||||
else if(href_list["newline"])
|
||||
var/newline = html_encode(input("Enter your line: ", instrumentObj.name) as text|null)
|
||||
if(!newline || !in_range(instrumentObj, usr))
|
||||
return
|
||||
if(lines.len > MUSIC_MAXLINES)
|
||||
return
|
||||
if(length(newline) > MUSIC_MAXLINECHARS)
|
||||
newline = copytext(newline, 1, MUSIC_MAXLINECHARS)
|
||||
lines.Add(newline)
|
||||
|
||||
else if(href_list["deleteline"])
|
||||
var/num = round(text2num(href_list["deleteline"]))
|
||||
if(num > lines.len || num < 1)
|
||||
return
|
||||
lines.Cut(num, num+1)
|
||||
|
||||
else if(href_list["modifyline"])
|
||||
var/num = round(text2num(href_list["modifyline"]),1)
|
||||
var/content = stripped_input(usr, "Enter your line: ", instrumentObj.name, lines[num], MUSIC_MAXLINECHARS)
|
||||
if(!content || !in_range(instrumentObj, usr))
|
||||
return
|
||||
if(num > lines.len || num < 1)
|
||||
return
|
||||
lines[num] = content
|
||||
|
||||
else if(href_list["stop"])
|
||||
playing = FALSE
|
||||
hearing_mobs = null
|
||||
|
||||
updateDialog(usr)
|
||||
return
|
||||
|
||||
/datum/song/proc/sanitize_tempo(new_tempo)
|
||||
new_tempo = abs(new_tempo)
|
||||
return max(round(new_tempo, world.tick_lag), world.tick_lag)
|
||||
|
||||
// subclass for handheld instruments, like violin
|
||||
/datum/song/handheld
|
||||
|
||||
/datum/song/handheld/updateDialog(mob/user)
|
||||
instrumentObj.interact(user)
|
||||
|
||||
/datum/song/handheld/shouldStopPlaying()
|
||||
if(instrumentObj)
|
||||
return !isliving(instrumentObj.loc)
|
||||
else
|
||||
return TRUE
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/obj/structure/piano
|
||||
name = "space minimoog"
|
||||
icon = 'icons/obj/musician.dmi'
|
||||
icon_state = "minimoog"
|
||||
anchored = TRUE
|
||||
density = TRUE
|
||||
var/datum/song/song
|
||||
|
||||
/obj/structure/piano/unanchored
|
||||
anchored = FALSE
|
||||
|
||||
/obj/structure/piano/New()
|
||||
..()
|
||||
song = new("piano", src)
|
||||
|
||||
if(prob(50) && icon_state == initial(icon_state))
|
||||
name = "space minimoog"
|
||||
desc = "This is a minimoog, like a space piano, but more spacey!"
|
||||
icon_state = "minimoog"
|
||||
else
|
||||
name = "space piano"
|
||||
desc = "This is a space piano, like a regular piano, but always in tune! Even if the musician isn't."
|
||||
icon_state = "piano"
|
||||
|
||||
/obj/structure/piano/Destroy()
|
||||
qdel(song)
|
||||
song = null
|
||||
return ..()
|
||||
|
||||
/obj/structure/piano/Initialize(mapload)
|
||||
. = ..()
|
||||
if(mapload)
|
||||
song.tempo = song.sanitize_tempo(song.tempo) // tick_lag isn't set when the map is loaded
|
||||
|
||||
/obj/structure/piano/attack_hand(mob/user)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
interact(user)
|
||||
|
||||
/obj/structure/piano/attack_paw(mob/user)
|
||||
return attack_hand(user)
|
||||
|
||||
/obj/structure/piano/interact(mob/user)
|
||||
ui_interact(user)
|
||||
|
||||
/obj/structure/piano/ui_interact(mob/user)
|
||||
if(!user || !anchored)
|
||||
return
|
||||
|
||||
if(!user.IsAdvancedToolUser())
|
||||
to_chat(user, "<span class='warning'>You don't have the dexterity to do this!</span>")
|
||||
return 1
|
||||
user.set_machine(src)
|
||||
song.interact(user)
|
||||
|
||||
/obj/structure/piano/wrench_act(mob/living/user, obj/item/I)
|
||||
default_unfasten_wrench(user, I, 40)
|
||||
return TRUE
|
||||
@@ -64,7 +64,7 @@
|
||||
else
|
||||
I.forceMove(drop_location())
|
||||
to_chat(user, "<span class='notice'>You find [I] in the cistern.</span>")
|
||||
w_items -= I.w_class
|
||||
w_items = max(w_items - I.w_class, 0)
|
||||
else
|
||||
open = !open
|
||||
update_icon()
|
||||
@@ -112,12 +112,14 @@
|
||||
. = ..()
|
||||
if (secret_type)
|
||||
secret = new secret_type(src)
|
||||
secret.desc += " It's a secret!"
|
||||
w_items += secret.w_class
|
||||
secret.desc += "" //In case you want to add something to the item that spawns
|
||||
contents += secret
|
||||
|
||||
/obj/structure/toilet/secret/prison
|
||||
secret_type = /obj/effect/spawner/lootdrop/prison_loot_toilet
|
||||
/obj/structure/toilet/secret/LateInitialize()
|
||||
. = ..()
|
||||
w_items = 0 //recalculate total weight thanks to the secret.
|
||||
for(var/obj/item/I in contents)
|
||||
w_items += I.w_class
|
||||
|
||||
/obj/structure/toilet/secret/low_loot
|
||||
secret_type = /obj/effect/spawner/lootdrop/low_loot_toilet
|
||||
@@ -125,6 +127,9 @@
|
||||
/obj/structure/toilet/secret/high_loot
|
||||
secret_type = /obj/effect/spawner/lootdrop/high_loot_toilet
|
||||
|
||||
/obj/structure/toilet/secret/prison
|
||||
secret_type = /obj/effect/spawner/lootdrop/prison_loot_toilet
|
||||
|
||||
/obj/structure/urinal
|
||||
name = "urinal"
|
||||
desc = "The HU-452, an experimental urinal. Comes complete with experimental urinal cake."
|
||||
|
||||
Reference in New Issue
Block a user