Merge branch 'master' into upstream-merge-5985

This commit is contained in:
Novacat
2019-03-28 21:32:18 -04:00
committed by GitHub
1149 changed files with 348994 additions and 35425 deletions

View File

@@ -70,12 +70,12 @@
hidden = 1
/datum/category_item/autolathe/arms/tommymag
name = "Tommygun magazine (.45)"
name = "Tommy Gun magazine (.45)"
path =/obj/item/ammo_magazine/m45tommy
hidden = 1
/datum/category_item/autolathe/arms/tommydrum
name = "Tommygun drum magazine (.45)"
name = "Tommy Gun drum magazine (.45)"
path =/obj/item/ammo_magazine/m45tommydrum
hidden = 1
@@ -234,13 +234,13 @@
hidden = 1
/datum/category_item/autolathe/arms/tommymag
name = "Tommygun magazine (.45)"
name = "Tommy Gun magazine (.45)"
path =/obj/item/ammo_magazine/m45tommy/empty
category = "Arms and Ammunition"
hidden = 1
/datum/category_item/autolathe/arms/tommydrum
name = "Tommygun drum magazine (.45)"
name = "Tommy Gun drum magazine (.45)"
path =/obj/item/ammo_magazine/m45tommydrum/empty
category = "Arms and Ammunition"
hidden = 1

View File

@@ -0,0 +1,3 @@
/datum/category_item/autolathe/general/holocollar
name = "Holo-collar"
path =/obj/item/clothing/accessory/collar/holo

View File

@@ -59,7 +59,7 @@
return ..()
/datum/beam/proc/Draw()
if(QDELETED(target) || !QDELETED(origin))
if(QDELETED(target) || QDELETED(origin))
qdel(src)
return
@@ -144,12 +144,12 @@
// 'Reactive' beam parts do something when touched or stood in.
/obj/effect/ebeam/reactive
/obj/effect/ebeam/reactive/initialize()
processing_objects += src
/obj/effect/ebeam/reactive/Initialize()
START_PROCESSING(SSobj, src)
return ..()
/obj/effect/ebeam/reactive/Destroy()
processing_objects -= src
STOP_PROCESSING(SSobj, src)
return ..()
/obj/effect/ebeam/reactive/on_drawn()

View File

@@ -105,6 +105,10 @@
"}
/datum/browser/proc/open(var/use_onclose = 1)
if(isnull(window_id)) //null check because this can potentially nuke goonchat
WARNING("Browser [title] tried to open with a null ID")
to_chat(user, "<span class='userdanger'>The [title] browser you tried to open failed a sanity check! Please report this on github!</span>")
return
var/window_size = ""
if (width && height)
window_size = "size=[width]x[height];"
@@ -112,9 +116,6 @@
if (use_onclose)
onclose(user, window_id, ref)
/datum/browser/proc/close()
user << browse(null, "window=[window_id]")
// This will allow you to show an icon in the browse window
// This is added to mob so that it can be used without a reference to the browser object
// There is probably a better place for this...
@@ -157,12 +158,12 @@
//world << "OnClose [user]: [windowid] : ["on-close=\".windowclose [param]\""]"
// the on-close client verb
// called when a browser popup window is closed after registering with proc/onclose()
// if a valid atom reference is supplied, call the atom's Topic() with "close=1"
// otherwise, just reset the client mob's machine var.
//
/client/verb/windowclose(var/atomref as text)
set hidden = 1 // hide this verb from the user's panel
set name = ".windowclose" // no autocomplete on cmd line
@@ -182,3 +183,198 @@
//world << "[src] was [src.mob.machine], setting to null"
src.mob.unset_machine()
return
/datum/browser/proc/close()
if(!isnull(window_id))//null check because this can potentially nuke goonchat
user << browse(null, "window=[window_id]")
else
WARNING("Browser [title] tried to close with a null ID")
/datum/browser/modal/alert/New(User,Message,Title,Button1="Ok",Button2,Button3,StealFocus = 1,Timeout=6000)
if (!User)
return
var/output = {"<center><b>[Message]</b></center><br />
<div style="text-align:center">
<a style="font-size:large;float:[( Button2 ? "left" : "right" )]" href="?src=\ref[src];button=1">[Button1]</a>"}
if (Button2)
output += {"<a style="font-size:large;[( Button3 ? "" : "float:right" )]" href="?src=\ref[src];button=2">[Button2]</a>"}
if (Button3)
output += {"<a style="font-size:large;float:right" href="?src=\ref[src];button=3">[Button3]</a>"}
output += {"</div>"}
..(User, ckey("[User]-[Message]-[Title]-[world.time]-[rand(1,10000)]"), Title, 350, 150, src, StealFocus, Timeout)
set_content(output)
/datum/browser/modal/alert/Topic(href,href_list)
if (href_list["close"] || !user || !user.client)
opentime = 0
return
if (href_list["button"])
var/button = text2num(href_list["button"])
if (button <= 3 && button >= 1)
selectedbutton = button
opentime = 0
close()
//designed as a drop in replacement for alert(); functions the same. (outside of needing User specified)
/proc/tgalert(var/mob/User, Message, Title, Button1="Ok", Button2, Button3, StealFocus = 1, Timeout = 6000)
if (!User)
User = usr
switch(askuser(User, Message, Title, Button1, Button2, Button3, StealFocus, Timeout))
if (1)
return Button1
if (2)
return Button2
if (3)
return Button3
//Same shit, but it returns the button number, could at some point support unlimited button amounts.
/proc/askuser(var/mob/User,Message, Title, Button1="Ok", Button2, Button3, StealFocus = 1, Timeout = 6000)
if (!istype(User))
if (istype(User, /client/))
var/client/C = User
User = C.mob
else
return
var/datum/browser/modal/alert/A = new(User, Message, Title, Button1, Button2, Button3, StealFocus, Timeout)
A.open()
A.wait()
if (A.selectedbutton)
return A.selectedbutton
/datum/browser/modal
var/opentime = 0
var/timeout
var/selectedbutton = 0
var/stealfocus
/datum/browser/modal/New(nuser, nwindow_id, ntitle = 0, nwidth = 0, nheight = 0, var/atom/nref = null, StealFocus = 1, Timeout = 6000)
..()
stealfocus = StealFocus
if (!StealFocus)
window_options += "focus=false;"
timeout = Timeout
/datum/browser/modal/close()
.=..()
opentime = 0
/datum/browser/modal/open()
set waitfor = 0
opentime = world.time
if (stealfocus)
. = ..(use_onclose = 1)
else
var/focusedwindow = winget(user, null, "focus")
. = ..(use_onclose = 1)
//waits for the window to show up client side before attempting to un-focus it
//winexists sleeps until it gets a reply from the client, so we don't need to bother sleeping
for (var/i in 1 to 10)
if (user && winexists(user, window_id))
if (focusedwindow)
winset(user, focusedwindow, "focus=true")
else
winset(user, "mapwindow", "focus=true")
break
if (timeout)
addtimer(CALLBACK(src, .proc/close), timeout)
/datum/browser/modal/proc/wait()
while (opentime && selectedbutton <= 0 && (!timeout || opentime+timeout > world.time))
stoplag(1)
/datum/browser/modal/listpicker
var/valueslist = list()
/datum/browser/modal/listpicker/New(User,Message,Title,Button1="Ok",Button2,Button3,StealFocus = 1, Timeout = FALSE,list/values,inputtype="checkbox", width, height, slidecolor)
if (!User)
return
var/output = {"<form><input type="hidden" name="src" value="\ref[src]"><ul class="sparse">"}
if (inputtype == "checkbox" || inputtype == "radio")
for (var/i in values)
var/div_slider = slidecolor
if(!i["allowed_edit"])
div_slider = "locked"
output += {"<li>
<label class="switch">
<input type="[inputtype]" value="1" name="[i["name"]]"[i["checked"] ? " checked" : ""][i["allowed_edit"] ? "" : " onclick='return false' onkeydown='return false'"]>
<div class="slider [div_slider ? "[div_slider]" : ""]"></div>
<span>[i["name"]]</span>
</label>
</li>"}
else
for (var/i in values)
output += {"<li><input id="name="[i["name"]]"" style="width: 50px" type="[type]" name="[i["name"]]" value="[i["value"]]">
<label for="[i["name"]]">[i["name"]]</label></li>"}
output += {"</ul><div style="text-align:center">
<button type="submit" name="button" value="1" style="font-size:large;float:[( Button2 ? "left" : "right" )]">[Button1]</button>"}
if (Button2)
output += {"<button type="submit" name="button" value="2" style="font-size:large;[( Button3 ? "" : "float:right" )]">[Button2]</button>"}
if (Button3)
output += {"<button type="submit" name="button" value="3" style="font-size:large;float:right">[Button3]</button>"}
output += {"</form></div>"}
..(User, ckey("[User]-[Message]-[Title]-[world.time]-[rand(1,10000)]"), Title, width, height, src, StealFocus, Timeout)
set_content(output)
/datum/browser/modal/listpicker/Topic(href,href_list)
if (href_list["close"] || !user || !user.client)
opentime = 0
return
if (href_list["button"])
var/button = text2num(href_list["button"])
if (button <= 3 && button >= 1)
selectedbutton = button
for (var/item in href_list)
switch(item)
if ("close", "button", "src")
continue
else
valueslist[item] = href_list[item]
opentime = 0
close()
/proc/presentpicker(var/mob/User,Message, Title, Button1="Ok", Button2, Button3, StealFocus = 1,Timeout = 6000,list/values, inputtype = "checkbox", width, height, slidecolor)
if (!istype(User))
if (istype(User, /client/))
var/client/C = User
User = C.mob
else
return
var/datum/browser/modal/listpicker/A = new(User, Message, Title, Button1, Button2, Button3, StealFocus,Timeout, values, inputtype, width, height, slidecolor)
A.open()
A.wait()
if (A.selectedbutton)
return list("button" = A.selectedbutton, "values" = A.valueslist)
/proc/input_bitfield(var/mob/User, title, bitfield, current_value, nwidth = 350, nheight = 350, nslidecolor, allowed_edit_list = null)
if (!User || !(bitfield in GLOB.bitfields))
return
var/list/pickerlist = list()
for (var/i in GLOB.bitfields[bitfield])
var/can_edit = 1
if(!isnull(allowed_edit_list) && !(allowed_edit_list & GLOB.bitfields[bitfield][i]))
can_edit = 0
if (current_value & GLOB.bitfields[bitfield][i])
pickerlist += list(list("checked" = 1, "value" = GLOB.bitfields[bitfield][i], "name" = i, "allowed_edit" = can_edit))
else
pickerlist += list(list("checked" = 0, "value" = GLOB.bitfields[bitfield][i], "name" = i, "allowed_edit" = can_edit))
var/list/result = presentpicker(User, "", title, Button1="Save", Button2 = "Cancel", Timeout=FALSE, values = pickerlist, width = nwidth, height = nheight, slidecolor = nslidecolor)
if (islist(result))
if (result["button"] == 2) // If the user pressed the cancel button
return
. = 0
for (var/flag in result["values"])
. |= GLOB.bitfields[bitfield][flag]
else
return

View File

@@ -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

View File

@@ -5,8 +5,9 @@
/datum
var/gc_destroyed //Time when this object was destroyed.
var/list/active_timers //for SStimer
var/weakref/weakref // Holder of weakref instance pointing to this datum
var/is_processing = FALSE // If this datum is in an MC processing list, this will be set to its name.
var/datum_flags = NONE
#ifdef TESTING
var/tmp/running_find_references
@@ -17,7 +18,18 @@
// This should be overridden to remove all references pointing to the object being destroyed.
// Return the appropriate QDEL_HINT; in most cases this is QDEL_HINT_QUEUE.
/datum/proc/Destroy(force=FALSE)
//clear timers
var/list/timers = active_timers
active_timers = null
for(var/thing in timers)
var/datum/timedevent/timer = thing
if (timer.spent)
continue
qdel(timer)
weakref = null // Clear this reference to ensure it's kept for as brief duration as possible.
tag = null
GLOB.nanomanager.close_uis(src)
SSnanoui.close_uis(src)
return QDEL_HINT_QUEUE

68
code/datums/datumvars.dm Normal file
View File

@@ -0,0 +1,68 @@
/datum/proc/CanProcCall(procname)
return TRUE
/datum/proc/can_vv_get(var_name)
return TRUE
/datum/proc/vv_edit_var(var_name, var_value) //called whenever a var is edited
if(var_name == NAMEOF(src, vars) || var_name == NAMEOF(src, parent_type))
return FALSE
vars[var_name] = var_value
datum_flags |= DF_VAR_EDITED
return TRUE
/datum/proc/vv_get_var(var_name)
switch(var_name)
if ("vars")
return debug_variable(var_name, list(), 0, src)
return debug_variable(var_name, vars[var_name], 0, src)
//please call . = ..() first and append to the result, that way parent items are always at the top and child items are further down
//add separaters by doing . += "---"
/datum/proc/vv_get_dropdown()
. = list()
VV_DROPDOWN_OPTION("", "---")
VV_DROPDOWN_OPTION(VV_HK_CALLPROC, "Call Proc")
VV_DROPDOWN_OPTION(VV_HK_MARK, "Mark Object")
VV_DROPDOWN_OPTION(VV_HK_DELETE, "Delete")
VV_DROPDOWN_OPTION(VV_HK_EXPOSE, "Show VV To Player")
//This proc is only called if everything topic-wise is verified. The only verifications that should happen here is things like permission checks!
//href_list is a reference, modifying it in these procs WILL change the rest of the proc in topic.dm of admin/view_variables!
/datum/proc/vv_do_topic(list/href_list)
if(!usr || !usr.client.holder)
return //This is VV, not to be called by anything else.
IF_VV_OPTION(VV_HK_EXPOSE)
if(!check_rights(R_ADMIN, FALSE))
return
var/value = usr.client.vv_get_value(VV_CLIENT)
if (value["class"] != VV_CLIENT)
return
var/client/C = value["value"]
if (!C)
return
var/prompt = alert("Do you want to grant [C] access to view this VV window? (they will not be able to edit or change anysrc nor open nested vv windows unless they themselves are an admin)", "Confirm", "Yes", "No")
if (prompt != "Yes" || !usr.client)
return
message_admins("[key_name_admin(usr)] Showed [key_name_admin(C)] a <a href='?_src_=vars;datumrefresh=\ref[src]'>VV window</a>")
log_admin("Admin [key_name(usr)] Showed [key_name(C)] a VV window of a [src]")
to_chat(C, "[usr.client.holder.fakekey ? "an Administrator" : "[usr.client.key]"] has granted you access to view a View Variables window")
C.debug_variables(src)
IF_VV_OPTION(VV_HK_DELETE)
if(!check_rights(R_DEBUG))
return
usr.client.admin_delete(src)
if (isturf(src)) // show the turf that took its place
usr.client.debug_variables(src)
IF_VV_OPTION(VV_HK_MARK)
usr.client.mark_datum(src)
IF_VV_OPTION(VV_HK_CALLPROC)
usr.client.callproc_datum(src)
/datum/proc/vv_get_header()
. = list()
if(("name" in vars) && !isatom(src))
. += "<b>[vars["name"]]</b><br>"
/datum/proc/on_reagent_change(changetype)
return

View File

@@ -1,3 +1,7 @@
/*
DO NOT USE THIS. THIS IS BEING DEPRECATED BY PROCESSING SUBSYSTEMS (controllers/subsystems/processing) AND TIMERS.
*/
/*
README:
@@ -109,9 +113,6 @@ Data storage vars:
CRASH("The global_iterator loop \ref[src] failed to terminate in designated timeframe. This may be caused by server lagging.")
return 1
proc/process()
return
proc/active()
return control_switch

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -85,7 +85,7 @@
current.verbs -= /datum/changeling/proc/EvolutionMenu
current.mind = null
GLOB.nanomanager.user_transferred(current, new_character) // transfer active NanoUI instances to new user
SSnanoui.user_transferred(current, new_character) // transfer active NanoUI instances to new user
if(new_character.mind) //remove any mind currently in our new body's mind variable
new_character.mind.current = null

View File

@@ -1,13 +0,0 @@
//
// Observer Pattern Implementation: Scheduled task triggered
// Registration type: /datum/scheduled_task
//
// Raised when: When a scheduled task reaches its trigger time.
//
// Arguments that the called proc should expect:
// /datum/scheduled_task/task: The task that reached its trigger time.
var/decl/observ/task_triggered/task_triggered_event = new()
/decl/observ/task_triggered
name = "Task Triggered"
expected_type = /datum/scheduled_task

View File

@@ -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

View File

@@ -52,4 +52,9 @@
name = "Merchant - Vox"
shoes = /obj/item/clothing/shoes/boots/jackboots/toeless
uniform = /obj/item/clothing/under/vox/vox_robes
suit = /obj/item/clothing/suit/armor/vox_scrap
suit = /obj/item/clothing/suit/armor/vox_scrap
/decl/hierarchy/outfit/zaddat
name = "Zaddat Suit"
suit = /obj/item/clothing/suit/space/void/zaddat/
mask = /obj/item/clothing/mask/gas/zaddat

View File

@@ -0,0 +1,227 @@
//Designed for things that need precision trajectories like projectiles.
//Don't use this for anything that you don't absolutely have to use this with (like projectiles!) because it isn't worth using a datum unless you need accuracy down to decimal places in pixels.
//You might see places where it does - 16 - 1. This is intentionally 17 instead of 16, because of how byond's tiles work and how not doing it will result in rounding errors like things getting put on the wrong turf.
#define RETURN_PRECISE_POSITION(A) new /datum/position(A)
#define RETURN_PRECISE_POINT(A) new /datum/point(A)
#define RETURN_POINT_VECTOR(ATOM, ANGLE, SPEED) {new /datum/point/vector(ATOM, null, null, null, null, ANGLE, SPEED)}
#define RETURN_POINT_VECTOR_INCREMENT(ATOM, ANGLE, SPEED, AMT) {new /datum/point/vector(ATOM, null, null, null, null, ANGLE, SPEED, AMT)}
/datum/position //For positions with map x/y/z and pixel x/y so you don't have to return lists. Could use addition/subtraction in the future I guess.
var/x = 0
var/y = 0
var/z = 0
var/pixel_x = 0
var/pixel_y = 0
/datum/position/proc/valid()
return x && y && z && !isnull(pixel_x) && !isnull(pixel_y)
/datum/position/New(_x = 0, _y = 0, _z = 0, _pixel_x = 0, _pixel_y = 0) //first argument can also be a /datum/point.
if(istype(_x, /datum/point))
var/datum/point/P = _x
var/turf/T = P.return_turf()
_x = T.x
_y = T.y
_z = T.z
_pixel_x = P.return_px()
_pixel_y = P.return_py()
else if(istype(_x, /atom))
var/atom/A = _x
_x = A.x
_y = A.y
_z = A.z
_pixel_x = A.pixel_x
_pixel_y = A.pixel_y
x = _x
y = _y
z = _z
pixel_x = _pixel_x
pixel_y = _pixel_y
/datum/position/proc/return_turf()
return locate(x, y, z)
/datum/position/proc/return_px()
return pixel_x
/datum/position/proc/return_py()
return pixel_y
/datum/position/proc/return_point()
return new /datum/point(src)
/proc/point_midpoint_points(datum/point/a, datum/point/b) //Obviously will not support multiZ calculations! Same for the two below.
var/datum/point/P = new
P.x = a.x + (b.x - a.x) / 2
P.y = a.y + (b.y - a.y) / 2
P.z = a.z
return P
/proc/pixel_length_between_points(datum/point/a, datum/point/b)
return sqrt(((b.x - a.x) ** 2) + ((b.y - a.y) ** 2))
/proc/angle_between_points(datum/point/a, datum/point/b)
return ATAN2((b.y - a.y), (b.x - a.x))
/datum/point //A precise point on the map in absolute pixel locations based on world.icon_size. Pixels are FROM THE EDGE OF THE MAP!
var/x = 0
var/y = 0
var/z = 0
/datum/point/proc/valid()
return x && y && z
/datum/point/proc/copy_to(datum/point/p = new)
p.x = x
p.y = y
p.z = z
return p
/datum/point/New(_x, _y, _z, _pixel_x = 0, _pixel_y = 0) //first argument can also be a /datum/position or /atom.
if(istype(_x, /datum/position))
var/datum/position/P = _x
_x = P.x
_y = P.y
_z = P.z
_pixel_x = P.pixel_x
_pixel_y = P.pixel_y
else if(istype(_x, /atom))
var/atom/A = _x
_x = A.x
_y = A.y
_z = A.z
_pixel_x = A.pixel_x
_pixel_y = A.pixel_y
initialize_location(_x, _y, _z, _pixel_x, _pixel_y)
/datum/point/proc/initialize_location(tile_x, tile_y, tile_z, p_x = 0, p_y = 0)
if(!isnull(tile_x))
x = ((tile_x - 1) * world.icon_size) + world.icon_size / 2 + p_x + 1
if(!isnull(tile_y))
y = ((tile_y - 1) * world.icon_size) + world.icon_size / 2 + p_y + 1
if(!isnull(tile_z))
z = tile_z
/datum/point/proc/debug_out()
var/turf/T = return_turf()
return "\ref[src] aX [x] aY [y] aZ [z] pX [return_px()] pY [return_py()] mX [T.x] mY [T.y] mZ [T.z]"
/datum/point/proc/move_atom_to_src(atom/movable/AM)
AM.forceMove(return_turf())
AM.pixel_x = return_px()
AM.pixel_y = return_py()
/datum/point/proc/return_turf()
return locate(CEILING(x / world.icon_size, 1), CEILING(y / world.icon_size, 1), z)
/datum/point/proc/return_coordinates() //[turf_x, turf_y, z]
return list(CEILING(x / world.icon_size, 1), CEILING(y / world.icon_size, 1), z)
/datum/point/proc/return_position()
return new /datum/position(src)
/datum/point/proc/return_px()
return MODULUS(x, world.icon_size) - 16 - 1
/datum/point/proc/return_py()
return MODULUS(y, world.icon_size) - 16 - 1
/datum/point/vector
var/speed = 32 //pixels per iteration
var/iteration = 0
var/angle = 0
var/mpx = 0 //calculated x/y movement amounts to prevent having to do trig every step.
var/mpy = 0
var/starting_x = 0 //just like before, pixels from EDGE of map! This is set in initialize_location().
var/starting_y = 0
var/starting_z = 0
/datum/point/vector/New(_x, _y, _z, _pixel_x = 0, _pixel_y = 0, _angle, _speed, initial_increment = 0)
..()
initialize_trajectory(_speed, _angle)
if(initial_increment)
increment(initial_increment)
/datum/point/vector/initialize_location(tile_x, tile_y, tile_z, p_x = 0, p_y = 0)
. = ..()
starting_x = x
starting_y = y
starting_z = z
/datum/point/vector/copy_to(datum/point/vector/v = new)
..(v)
v.speed = speed
v.iteration = iteration
v.angle = angle
v.mpx = mpx
v.mpy = mpy
v.starting_x = starting_x
v.starting_y = starting_y
v.starting_z = starting_z
return v
/datum/point/vector/proc/initialize_trajectory(pixel_speed, new_angle)
if(!isnull(pixel_speed))
speed = pixel_speed
set_angle(new_angle)
/datum/point/vector/proc/set_angle(new_angle) //calculations use "byond angle" where north is 0 instead of 90, and south is 180 instead of 270.
if(isnull(angle))
return
angle = new_angle
update_offsets()
/datum/point/vector/proc/update_offsets()
mpx = sin(angle) * speed
mpy = cos(angle) * speed
/datum/point/vector/proc/set_speed(new_speed)
if(isnull(new_speed) || speed == new_speed)
return
speed = new_speed
update_offsets()
/datum/point/vector/proc/increment(multiplier = 1)
iteration++
x += mpx * multiplier
y += mpy * multiplier
/datum/point/vector/proc/return_vector_after_increments(amount = 7, multiplier = 1, force_simulate = FALSE)
var/datum/point/vector/v = copy_to()
if(force_simulate)
for(var/i in 1 to amount)
v.increment(multiplier)
else
v.increment(multiplier * amount)
return v
/datum/point/vector/proc/on_z_change()
return
/datum/point/vector/processed //pixel_speed is per decisecond.
var/last_process = 0
var/last_move = 0
var/paused = FALSE
/datum/point/vector/processed/Destroy()
STOP_PROCESSING(SSprojectiles, src)
return ..()
/datum/point/vector/processed/proc/start()
last_process = world.time
last_move = world.time
START_PROCESSING(SSprojectiles, src)
/datum/point/vector/processed/process()
if(paused)
last_move += world.time - last_process
last_process = world.time
return
var/needed_time = world.time - last_move
last_process = world.time
last_move = world.time
increment(needed_time / SSprojectiles.wait)

View File

@@ -39,7 +39,7 @@
shown = 0
client = user.client
progress = Clamp(progress, 0, goal)
progress = CLAMP(progress, 0, goal)
bar.icon_state = "prog_bar_[round(((progress / goal) * 100), 5)]"
if (!shown && user.is_preference_enabled(/datum/client_preference/show_progress_bar))
user.client.images += bar

View File

@@ -42,6 +42,13 @@
containertype = /obj/structure/largecrate/animal/corgi
containername = "Corgi Crate"
/datum/supply_pack/hydro/cat
name = "Cat Crate"
contains = list()
cost = 45
containertype = /obj/structure/largecrate/animal/cat
containername = "Cat Crate"
/datum/supply_pack/hydro/hydroponics
name = "Hydroponics Supply Crate"
contains = list(

View File

@@ -23,6 +23,64 @@
containertype = /obj/structure/closet/crate
containername = "cards crate"
/datum/supply_pack/randomised/misc/dnd
num_contained = 4
contains = list(
/obj/item/toy/character/alien,
/obj/item/toy/character/warrior,
/obj/item/toy/character/cleric,
/obj/item/toy/character/thief,
/obj/item/toy/character/wizard,
/obj/item/toy/character/voidone,
/obj/item/toy/character/lich
)
name = "Miniatures Crate"
cost = 200
containertype = /obj/structure/closet/crate
containername = "Miniature Crate"
/datum/supply_pack/randomised/misc/plushies
num_contained = 5
contains = list(
/obj/item/toy/plushie/nymph,
/obj/item/toy/plushie/mouse,
/obj/item/toy/plushie/kitten,
/obj/item/toy/plushie/lizard,
/obj/item/toy/plushie/spider,
/obj/item/toy/plushie/farwa,
/obj/item/toy/plushie/corgi,
/obj/item/toy/plushie/girly_corgi,
/obj/item/toy/plushie/robo_corgi,
/obj/item/toy/plushie/octopus,
/obj/item/toy/plushie/face_hugger,
/obj/item/toy/plushie/red_fox,
/obj/item/toy/plushie/black_fox,
/obj/item/toy/plushie/marble_fox,
/obj/item/toy/plushie/blue_fox,
/obj/item/toy/plushie/coffee_fox,
/obj/item/toy/plushie/pink_fox,
/obj/item/toy/plushie/purple_fox,
/obj/item/toy/plushie/crimson_fox,
/obj/item/toy/plushie/deer,
/obj/item/toy/plushie/black_cat,
/obj/item/toy/plushie/grey_cat,
/obj/item/toy/plushie/white_cat,
/obj/item/toy/plushie/orange_cat,
/obj/item/toy/plushie/siamese_cat,
/obj/item/toy/plushie/tabby_cat,
/obj/item/toy/plushie/tuxedo_cat,
/obj/item/toy/plushie/squid/green,
/obj/item/toy/plushie/squid/mint,
/obj/item/toy/plushie/squid/blue,
/obj/item/toy/plushie/squid/orange,
/obj/item/toy/plushie/squid/yellow,
/obj/item/toy/plushie/squid/pink
)
name = "Plushies Crate"
cost = 15
containertype = /obj/structure/closet/crate
containername = "Plushies Crate"
/datum/supply_pack/misc/eftpos
contains = list(/obj/item/device/eftpos)
name = "EFTPOS scanner"
@@ -77,3 +135,12 @@
cost = 15
containertype = /obj/structure/closet/crate
containername = "Holoplant crate"
/datum/supply_pack/misc/glucose_hypos
name = "Glucose Hypoinjectors"
contains = list(
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/glucose = 5
)
cost = 25
containertype = "obj/structure/closet/crate"
containername = "Glucose Hypo Crate"

View File

@@ -50,21 +50,17 @@
containertype = /obj/structure/closet/crate
containername = "Action figures crate"
/datum/supply_pack/recreation/characters_vr
name = "Tabletop miniatures"
/datum/supply_pack/recreation/collars
name = "Collar bundle"
contains = list(
/obj/item/weapon/storage/box/characters
/obj/item/clothing/accessory/collar/shock = 1,
/obj/item/clothing/accessory/collar/spike = 1,
/obj/item/clothing/accessory/collar/silver = 1,
/obj/item/clothing/accessory/collar/gold = 1,
/obj/item/clothing/accessory/collar/bell = 1,
/obj/item/clothing/accessory/collar/pink = 1,
/obj/item/clothing/accessory/collar/holo = 1
)
cost = 25
containertype = /obj/structure/closet/crate
containername = "Tabletop miniatures crate"
cost = 50
/datum/supply_pack/randomised/recreation/plushies_vr
name = "Plushies crate"
num_contained = 3
contains = list(
/obj/random/plushie
)
cost = 60
containertype = /obj/structure/closet/crate
containername = "Plushies crate"
containername = "collar crate"

View File

@@ -212,4 +212,15 @@
cost = 50
containertype = "/obj/structure/closet/crate/secure"
containername = "Frontier Mining voidsuit crate"
access = access_mining
access = access_mining
/datum/supply_pack/voidsuits/zaddat
name = "Zaddat Shroud"
contains = list(
/obj/item/clothing/suit/space/void/zaddat = 1,
/obj/item/clothing/mask/gas/zaddat = 1
)
cost = 30
containertype = "/obj/structure/closet/crate"
containername = "Zaddat Shroud crate"
access = null

View File

@@ -40,20 +40,20 @@
path = /obj/item/ammo_magazine/s45/ap
/datum/uplink_item/item/ammo/tommymag
name = "Tommygun Magazine (.45)"
name = "Tommy Gun Magazine (.45)"
path = /obj/item/ammo_magazine/m45tommy
/datum/uplink_item/item/ammo/tommymagap
name = "Tommygun Magazine (.45 AP)"
name = "Tommy Gun Magazine (.45 AP)"
path = /obj/item/ammo_magazine/m45tommy/ap
/datum/uplink_item/item/ammo/tommydrum
name = "Tommygun Drum Magazine (.45)"
name = "Tommy Gun Drum Magazine (.45)"
path = /obj/item/ammo_magazine/m45tommydrum
item_cost = 40
/datum/uplink_item/item/ammo/tommydrumap
name = "Tommygun Drum Magazine (.45 AP)"
name = "Tommy Gun Drum Magazine (.45 AP)"
path = /obj/item/ammo_magazine/m45tommydrum/ap
/datum/uplink_item/item/ammo/darts

View File

@@ -139,7 +139,7 @@
path = /obj/item/weapon/storage/secure/briefcase/fuelrod
/datum/uplink_item/item/visible_weapons/tommygun
name = "Tommygun (.45)" // We're keeping this because it's CLASSY. -Spades
name = "Tommy Gun (.45)" // We're keeping this because it's CLASSY. -Spades
item_cost = 60
path = /obj/item/weapon/gun/projectile/automatic/tommygun

View File

@@ -24,7 +24,7 @@ var/const/WIRE_TRANSMIT = 4
if(WIRE_TRANSMIT)
R.broadcasting = !R.broadcasting && !IsIndexCut(WIRE_SIGNAL)
GLOB.nanomanager.update_uis(holder)
SSnanoui.update_uis(holder)
/datum/wires/radio/UpdateCut(var/index, var/mended)
var/obj/item/device/radio/R = holder
@@ -38,4 +38,4 @@ var/const/WIRE_TRANSMIT = 4
if(WIRE_TRANSMIT)
R.broadcasting = mended && !IsIndexCut(WIRE_SIGNAL)
GLOB.nanomanager.update_uis(holder)
SSnanoui.update_uis(holder)