Merge remote-tracking branch 'upstream/master' into ambitioncheck

This commit is contained in:
keronshb
2022-02-22 17:10:25 -05:00
1045 changed files with 68632 additions and 58299 deletions
+100 -50
View File
@@ -1,17 +1,18 @@
/*
* Holds procs to help with list operations
* Contains groups:
* Misc
* Sorting
* Misc
* Sorting
*/
/*
* Misc
*/
#define LAZYINITLIST(L) if (!L) L = list()
#define LAZYINITLIST(L) if (!L) { L = list(); }
#define UNSETEMPTY(L) if (L && !length(L)) L = null
#define LAZYCOPY(L) (L ? L.Copy() : list() )
///Like LAZYCOPY - copies an input list if the list has entries, If it doesn't the assigned list is nulled
#define LAZYLISTDUPLICATE(L) (L ? L.Copy() : null )
#define LAZYREMOVE(L, I) if(L) { L -= I; if(!length(L)) { L = null; } }
//ambition start
#define LAZYCUT(L, S, E) if((length(L) >= S) && (E == 0 || length(L) >= (E - 1))) { L.Cut(S, E); if(!length(L)) { L = null; } }
@@ -19,17 +20,31 @@
#define LAZYADD(L, I) if(!L) { L = list(); } L += I;
#define LAZYOR(L, I) if(!L) { L = list(); } L |= I;
#define LAZYFIND(L, V) (L ? L.Find(V) : 0)
///returns L[I] if L exists and I is a valid index of L, runtimes if L is not a list
#define LAZYACCESS(L, I) (L ? (isnum(I) ? (I > 0 && I <= length(L) ? L[I] : null) : L[I]) : null)
#define LAZYSET(L, K, V) if(!L) { L = list(); } L[K] = V;
#define LAZYLEN(L) length(L)
///This is used to add onto lazy assoc list when the value you're adding is a /list/. This one has extra safety over lazyaddassoc because the value could be null (and thus cant be used to += objects)
#define LAZYADDASSOCLIST(L, K, V) if(!L) { L = list(); } L[K] += list(V);
//Sets a list to null
#define LAZYNULL(L) L = null
#define LAZYCLEARLIST(L) if(L) L.Cut()
#define SANITIZE_LIST(L) ( islist(L) ? L : list() )
#define reverseList(L) reverseRange(L.Copy())
#define LAZYADDASSOC_TG(L, K, V) if(!L) { L = list(); } L[K] += V;
///This is used to add onto lazy assoc list when the value you're adding is a /list/. This one has extra safety over lazyaddassoc because the value could be null (and thus cant be used to += objects)
#define LAZYADDASSOC(L, K, V) if(!L) { L = list(); } L[K] += list(V);
#define LAZYREMOVEASSOC(L, K, V) if(L) { if(L[K]) { L[K] -= V; if(!length(L[K])) L -= K; } if(!length(L)) L = null; }
#define LAZYACCESSASSOC(L, I, K) L ? L[I] ? L[I][K] ? L[I][K] : null : null : null
#define QDEL_LAZYLIST(L) for(var/I in L) qdel(I); L = null;
//These methods don't null the list
#define LAZYCOPY(L) (L ? L.Copy() : list() ) //Use LAZYLISTDUPLICATE instead if you want it to null with no entries
#define LAZYCLEARLIST(L) if(L) L.Cut() // Consider LAZYNULL instead
#define SANITIZE_LIST(L) ( islist(L) ? L : list() )
#define reverseList(L) reverseRange(L.Copy())
/// Performs an insertion on the given lazy list with the given key and value. If the value already exists, a new one will not be made.
#define LAZYORASSOCLIST(lazy_list, key, value) \
LAZYINITLIST(lazy_list); \
LAZYINITLIST(lazy_list[key]); \
lazy_list[key] |= value;
/// Passed into BINARY_INSERT to compare keys
#define COMPARE_KEY __BIN_LIST[__BIN_MID]
@@ -72,7 +87,7 @@
} while(FALSE)
//Returns a list in plain english as a string
/proc/english_list(list/input, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "")
/proc/english_list(list/input, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "" )
var/total = length(input)
switch(total)
if (0)
@@ -95,6 +110,7 @@
/**
* English_list but associative supporting. Higher overhead.
* @depricated
*/
/proc/english_list_assoc(list/input, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "")
var/total = length(input)
@@ -122,6 +138,7 @@
return "[output][and_text][input[index]]"
//Returns list element or null. Should prevent "index out of bounds" error.
/// @depricated
/proc/listgetindex(list/L, index)
if(LAZYLEN(L))
if(isnum(index) && ISINTEGER(index))
@@ -132,17 +149,19 @@
return
//Return either pick(list) or null if list is not of type /list or is empty
/// @depricated
/proc/safepick(list/L)
if(LAZYLEN(L))
return pick(L)
//Checks if the list is empty
/// @depricated
/proc/isemptylist(list/L)
if(!L.len)
return TRUE
return FALSE
//Checks for specific types in a list
//Checks for specific types in a listc
/proc/is_type_in_list(atom/A, list/L)
if(!LAZYLEN(L) || !A)
return FALSE
@@ -183,43 +202,45 @@
/proc/typecache_filter_list_reverse(list/atoms, list/typecache)
RETURN_TYPE(/list)
. = list()
for(var/atom/A as anything in atoms)
if(!typecache[A.type])
. += A
for(var/atom/atom as anything in atoms)
if(!typecache[atom.type])
. += atom
/proc/typecache_filter_multi_list_exclusion(list/atoms, list/typecache_include, list/typecache_exclude)
. = list()
for(var/atom/A as anything in atoms)
if(typecache_include[A.type] && !typecache_exclude[A.type])
. += A
for(var/atom/atom as anything in atoms)
if(typecache_include[atom.type] && !typecache_exclude[atom.type])
. += atom
//Like typesof() or subtypesof(), but returns a typecache instead of a list
///Like typesof() or subtypesof(), but returns a typecache instead of a list
/proc/typecacheof(path, ignore_root_path, only_root_path = FALSE)
if(ispath(path))
var/list/types = list()
var/list/types
var/list/output = list()
if(only_root_path)
types = list(path)
output[path] = TRUE
else
types = ignore_root_path ? subtypesof(path) : typesof(path)
var/list/L = list()
for(var/T in types)
L[T] = TRUE
return L
for(var/T in types)
output[T] = TRUE
return output
else if(islist(path))
var/list/pathlist = path
var/list/L = list()
var/list/output = list()
if(ignore_root_path)
for(var/P in pathlist)
for(var/T in subtypesof(P))
L[T] = TRUE
for(var/current_path in pathlist)
for(var/subtype in subtypesof(current_path))
output[subtype] = TRUE
return output
if(only_root_path)
for(var/current_path in pathlist)
output[current_path] = TRUE
else
for(var/P in pathlist)
if(only_root_path)
L[P] = TRUE
else
for(var/T in typesof(P))
L[T] = TRUE
return L
for(var/current_path in pathlist)
for(var/subpath in typesof(current_path))
output[subpath] = TRUE
return output
/proc/typecacheof_assoc_list(list/pathlist, ignore_root_path = FALSE)
. = list()
@@ -278,9 +299,10 @@
//Picks a random element from a list based on a weighting system:
//1. Adds up the total of weights for each element
//2. Gets the total from 0% to 100% of previous total value.
//2. Gets a number between 1 and that total
//3. For each element in the list, subtracts its weighting from that number
//4. If that makes the number 0 or less, return that element.
//Will output null sometimes if you use decimals (e.g. 0.1 instead of 10) as rand() uses integers, not floats
/proc/pickweight(list/L, base_weight = 1)
var/total = 0
var/item
@@ -289,12 +311,14 @@
L[item] = base_weight
total += L[item]
total = rand() * total
total = rand(base_weight, total)
for (item in L)
total -= L[item]
total -=L [item]
if (total <= 0)
return item
return null
/proc/pickweightAllowZero(list/L) //The original pickweight proc will sometimes pick entries with zero weight. I'm not sure if changing the original will break anything, so I left it be.
var/total = 0
var/item
@@ -366,13 +390,13 @@
//Results will have a length of quality
return results
//Pick a random element from the list and remove it from the list.
/// Pick a random element from the list and remove it from the list.
/proc/pick_n_take(list/L)
RETURN_TYPE(L[_].type)
if(L.len)
var/picked = rand(1,L.len)
. = L[picked]
L.Cut(picked,picked+1) //Cut is far more efficient that Remove()
L.Cut(picked,picked+1) //Cut is far more efficient that Remove()
//Pick a random element from the list by weight and remove it from the list.
//Result is returned as a list in the format list(key, value)
@@ -468,7 +492,7 @@
//uses sortList() but uses the var's name specifically. This should probably be using mergeAtom() instead
/proc/sortNames(list/L, order=1)
return sortTim(L, order >= 0 ? /proc/cmp_name_asc : /proc/cmp_name_dsc)
return sortTim(L.Copy(), order >= 0 ? /proc/cmp_name_asc : /proc/cmp_name_dsc)
//Converts a bitfield to a list of numbers (or words if a wordlist is provided)
@@ -504,10 +528,12 @@
if (L[i] == val)
.++
/// Returns datum/data/record
/proc/find_record(field, value, list/L)
for(var/datum/data/record/R in L)
if(R.fields[field] == value)
return R
return null
//Move a single element from position fromIndex within a list, to position toIndex
@@ -517,10 +543,10 @@
//fromIndex and toIndex must be in the range [1,L.len+1]
//This will preserve associations ~Carnie
/proc/moveElement(list/L, fromIndex, toIndex)
if(fromIndex == toIndex || fromIndex+1 == toIndex) //no need to move
if(fromIndex == toIndex || fromIndex+1 == toIndex) //no need to move
return
if(fromIndex > toIndex)
++fromIndex //since a null will be inserted before fromIndex, the index needs to be nudged right by one
++fromIndex //since a null will be inserted before fromIndex, the index needs to be nudged right by one
L.Insert(toIndex, null)
L.Swap(fromIndex, toIndex)
@@ -532,10 +558,10 @@
//This will preserve associations ~Carnie
/proc/moveRange(list/L, fromIndex, toIndex, len=1)
var/distance = abs(toIndex - fromIndex)
if(len >= distance) //there are more elements to be moved than the distance to be moved. Therefore the same result can be achieved (with fewer operations) by moving elements between where we are and where we are going. The result being, our range we are moving is shifted left or right by dist elements
if(len >= distance) //there are more elements to be moved than the distance to be moved. Therefore the same result can be achieved (with fewer operations) by moving elements between where we are and where we are going. The result being, our range we are moving is shifted left or right by dist elements
if(fromIndex <= toIndex)
return //no need to move
fromIndex += len //we want to shift left instead of right
return //no need to move
fromIndex += len //we want to shift left instead of right
for(var/i=0, i<distance, ++i)
L.Insert(fromIndex, null)
@@ -555,7 +581,7 @@
//Note: if the two ranges overlap, only the destination order will be preserved fully, since some elements will be within both ranges ~Carnie
/proc/swapRange(list/L, fromIndex, toIndex, len=1)
var/distance = abs(toIndex - fromIndex)
if(len > distance) //there is an overlap, therefore swapping each element will require more swaps than inserting new elements
if(len > distance) //there is an overlap, therefore swapping each element will require more swaps than inserting new elements
if(fromIndex < toIndex)
toIndex += len
else
@@ -601,6 +627,12 @@
if(D.vars[varname] == value)
return D
//remove all nulls from a list
/proc/removeNullsFromList(list/L)
while(L.Remove(null))
continue
return L
//Copies a list, and all lists inside it recusively
//Does not copy any other reference type
/proc/deepCopyList(list/l)
@@ -633,11 +665,6 @@
used_key_list[input_key] = 1
return input_key
#if DM_VERSION > 514
#error Remie said that lummox was adding a way to get a lists
#error contents via list.values, if that is true remove this
#error otherwise, update the version and bug lummox
#endif
//Flattens a keyed list into a list of it's contents
/proc/flatten_list(list/key_list)
if(!islist(key_list))
@@ -714,6 +741,29 @@
ret += key
return ret
/proc/compare_list(list/l,list/d)
if(!islist(l) || !islist(d))
return FALSE
if(l.len != d.len)
return FALSE
for(var/i in 1 to l.len)
if(l[i] != d[i])
return FALSE
return TRUE
#define LAZY_LISTS_OR(left_list, right_list)\
( length(left_list)\
? length(right_list)\
? (left_list | right_list)\
: left_list.Copy()\
: length(right_list)\
? right_list.Copy()\
: null\
)
/proc/is_type_in_ref_list(path, list/L)
if(!ispath(path))//not a path
return
+13
View File
@@ -88,6 +88,19 @@
if (CONFIG_GET(flag/log_access))
WRITE_LOG(GLOB.world_game_log, "ACCESS: [text]")
/**
* Writes to a special log file if the log_suspicious_login config flag is set,
* which is intended to contain all logins that failed under suspicious circumstances.
*
* Mirrors this log entry to log_access when access_log_mirror is TRUE, so this proc
* doesn't need to be used alongside log_access and can replace it where appropriate.
*/
/proc/log_suspicious_login(text, access_log_mirror = TRUE)
if (CONFIG_GET(flag/log_suspicious_login))
WRITE_LOG(GLOB.world_suspicious_login_log, "SUSPICIOUS_ACCESS: [text]")
if(access_log_mirror)
log_access(text)
/proc/log_law(text)
if (CONFIG_GET(flag/log_law))
WRITE_LOG(GLOB.world_game_log, "LAW: [text]")
+5 -1
View File
@@ -396,12 +396,16 @@
/proc/ScreenText(obj/O, maptext="", screen_loc="CENTER-7,CENTER-7", maptext_height=480, maptext_width=480)
if(!isobj(O))
O = new /atom/movable/screen/text()
O.maptext = maptext
O.maptext = MAPTEXT(maptext)
O.maptext_height = maptext_height
O.maptext_width = maptext_width
O.screen_loc = screen_loc
return O
/// Removes an image from a client's `.images`. Useful as a callback.
/proc/remove_image_from_client(image/image, client/remove_from)
remove_from?.images -= image
/proc/remove_images_from_clients(image/I, list/show_to)
for(var/client/C in show_to)
C.images -= I
-10
View File
@@ -86,16 +86,6 @@
// Keybindings
init_keybindings()
//Uplink Items
for(var/path in subtypesof(/datum/uplink_item))
var/datum/uplink_item/I = path
if(!initial(I.item)) //We add categories to a separate list.
GLOB.uplink_categories |= initial(I.category)
continue
GLOB.uplink_items += path
//(sub)typesof entries are listed by the order they are loaded in the code, so we'll have to rearrange them here.
GLOB.uplink_items = sortList(GLOB.uplink_items, /proc/cmp_uplink_items_dsc)
init_subtypes(/datum/crafting_recipe, GLOB.crafting_recipes)
INVOKE_ASYNC(GLOBAL_PROC, /proc/init_ref_coin_values) //so the current procedure doesn't sleep because of UNTIL()
+1 -1
View File
@@ -992,7 +992,7 @@ world
letter = lowertext(letter)
var/image/text_image = new(loc = A)
text_image.maptext = "<font size = 4>[letter]</font>"
text_image.maptext = MAPTEXT("<font size = 4>[letter]</font>")
text_image.pixel_x = 7
text_image.pixel_y = 5
qdel(atom_icon)
@@ -1,85 +1,3 @@
/matrix/proc/TurnTo(old_angle, new_angle)
. = new_angle - old_angle
Turn(.) //BYOND handles cases such as -270, 360, 540 etc. DOES NOT HANDLE 180 TURNS WELL, THEY TWEEN AND LOOK LIKE SHIT
/atom/proc/SpinAnimation(speed = 10, loops = -1, clockwise = 1, segments = 3, parallel = TRUE)
if(!segments)
return
var/segment = 360/segments
if(!clockwise)
segment = -segment
var/list/matrices = list()
for(var/i in 1 to segments-1)
var/matrix/M = matrix(transform)
M.Turn(segment*i)
matrices += M
var/matrix/last = matrix(transform)
matrices += last
speed /= segments
if(parallel)
animate(src, transform = matrices[1], time = speed, loops , flags = ANIMATION_PARALLEL)
else
animate(src, transform = matrices[1], time = speed, loops)
for(var/i in 2 to segments) //2 because 1 is covered above
animate(transform = matrices[i], time = speed)
//doesn't have an object argument because this is "Stacking" with the animate call above
//3 billion% intentional
//Dumps the matrix data in format a-f
/matrix/proc/tolist()
. = list()
. += a
. += b
. += c
. += d
. += e
. += f
//Dumps the matrix data in a matrix-grid format
/*
a d 0
b e 0
c f 1
*/
/matrix/proc/togrid()
. = list()
. += a
. += d
. += 0
. += b
. += e
. += 0
. += c
. += f
. += 1
//The X pixel offset of this matrix
/matrix/proc/get_x_shift()
. = c
//The Y pixel offset of this matrix
/matrix/proc/get_y_shift()
. = f
/matrix/proc/get_x_skew()
. = b
/matrix/proc/get_y_skew()
. = d
//Skews a matrix in a particular direction
//Missing arguments are treated as no skew in that direction
//As Rotation is defined as a scale+skew, these procs will break any existing rotation
//Unless the result is multiplied against the current matrix
/matrix/proc/set_skew(x = 0, y = 0)
b = x
d = y
/////////////////////
// COLOUR MATRICES //
/////////////////////
@@ -114,12 +32,64 @@ list(0.393,0.349,0.272,0, 0.769,0.686,0.534,0, 0.189,0.168,0.131,0, 0,0,0,1, 0,0
return list(R + value,R,R,0, G,G + value,G,0, B,B,B + value,0, 0,0,0,1, 0,0,0,0)
/**
* Exxagerates or removes colors
*/
/proc/color_matrix_saturation_percent(percent)
if(percent == 0)
return color_matrix_identity()
percent = clamp(percent, -100, 100)
if(percent > 0)
percent *= 3
var/x = 1 + percent / 100
var/inv = 1 - x
var/R = LUMA_R * inv
var/G = LUMA_G * inv
var/B = LUMA_B * inv
return list(R + x,R,R, G,G + x,G, B,B,B + x)
//Changes distance colors have from rgb(127,127,127) grey
//1 is identity. 0 makes everything grey >1 blows out colors and greys
/proc/color_matrix_contrast(value)
var/add = (1 - value) / 2
return list(value,0,0,0, 0,value,0,0, 0,0,value,0, 0,0,0,1, add,add,add,0)
/**
* Exxagerates or removes brightness
*/
/proc/color_matrix_contrast_percent(percent)
var/static/list/delta_index = list(
0, 0.01, 0.02, 0.04, 0.05, 0.06, 0.07, 0.08, 0.1, 0.11,
0.12, 0.14, 0.15, 0.16, 0.17, 0.18, 0.20, 0.21, 0.22, 0.24,
0.25, 0.27, 0.28, 0.30, 0.32, 0.34, 0.36, 0.38, 0.40, 0.42,
0.44, 0.46, 0.48, 0.5, 0.53, 0.56, 0.59, 0.62, 0.65, 0.68,
0.71, 0.74, 0.77, 0.80, 0.83, 0.86, 0.89, 0.92, 0.95, 0.98,
1.0, 1.06, 1.12, 1.18, 1.24, 1.30, 1.36, 1.42, 1.48, 1.54,
1.60, 1.66, 1.72, 1.78, 1.84, 1.90, 1.96, 2.0, 2.12, 2.25,
2.37, 2.50, 2.62, 2.75, 2.87, 3.0, 3.2, 3.4, 3.6, 3.8,
4.0, 4.3, 4.7, 4.9, 5.0, 5.5, 6.0, 6.5, 6.8, 7.0,
7.3, 7.5, 7.8, 8.0, 8.4, 8.7, 9.0, 9.4, 9.6, 9.8,
10.0)
percent = clamp(percent, -100, 100)
if(percent == 0)
return color_matrix_identity()
var/x = 0
if (percent < 0)
x = 127 + percent / 100 * 127;
else
x = percent % 1
if(x == 0)
x = delta_index[percent]
else
x = delta_index[percent] * (1-x) + delta_index[percent+1] * x//use linear interpolation for more granularity.
x = x * 127 + 127
var/mult = x / 127
var/add = 0.5 * (127-x) / 255
return list(mult,0,0, 0,mult,0, 0,0,mult, add,add,add)
//Moves all colors angle degrees around the color wheel while maintaining intensity of the color and not affecting greys
//0 is identity, 120 moves reds to greens, 240 moves reds to blues
/proc/color_matrix_rotate_hue(angle)
@@ -134,6 +104,26 @@ round(cos_inv_third+sqrt3_sin, 0.001), round(cos_inv_third-sqrt3_sin, 0.001), ro
0,0,0,1,
0,0,0,0)
/**
* Moves all colors angle degrees around the color wheel while maintaining intensity of the color and not affecting whites
* TODO: Need a version that only affects one color (ie shift red to blue but leave greens and blues alone)
*/
/proc/color_matrix_rotation(angle)
if(angle == 0)
return color_matrix_identity()
angle = clamp(angle, -180, 180)
var/cos = cos(angle)
var/sin = sin(angle)
var/constA = 0.143
var/constB = 0.140
var/constC = -0.283
return list(
LUMA_R + cos * (1-LUMA_R) + sin * -LUMA_R, LUMA_R + cos * -LUMA_R + sin * constA, LUMA_R + cos * -LUMA_R + sin * -(1-LUMA_R),
LUMA_G + cos * -LUMA_G + sin * -LUMA_G, LUMA_G + cos * (1-LUMA_G) + sin * constB, LUMA_G + cos * -LUMA_G + sin * LUMA_G,
LUMA_B + cos * -LUMA_B + sin * (1-LUMA_B), LUMA_B + cos * -LUMA_B + sin * constC, LUMA_B + cos * (1-LUMA_B) + sin * LUMA_B
)
//These next three rotate values about one axis only
//x is the red axis, y is the green axis, z is the blue axis.
/proc/color_matrix_rotate_x(angle)
@@ -183,3 +173,9 @@ round(cos_inv_third+sqrt3_sin, 0.001), round(cos_inv_third-sqrt3_sin, 0.001), ro
*/
/proc/rgb_construct_color_matrix(rr = 1, rg, rb, gr, gg = 1, gb, br, bg, bb = 1, cr, cg, cb)
return list(rr, rg, rb, gr, gg, gb, br, bg, bb, cr, cg, cb)
/**
* Assembles a color matrix, defaulting to identity
*/
/proc/rgba_construct_color_matrix(rr = 1, rg, rb, ra, gr, gg = 1, gb, ga, br, bg, bb = 1, ba, ar, ag, ab, aa = 1, cr, cg, cb, ca)
return list(rr, rg, rb, ra, gr, gg, gb, ga, br, bg, bb, ba, ar, ag, ab, aa, cr, cg, cb, ca)
@@ -0,0 +1,86 @@
/matrix/proc/TurnTo(old_angle, new_angle)
. = new_angle - old_angle
Turn(.) //BYOND handles cases such as -270, 360, 540 etc. DOES NOT HANDLE 180 TURNS WELL, THEY TWEEN AND LOOK LIKE SHIT
/atom/proc/SpinAnimation(speed = 10, loops = -1, clockwise = 1, segments = 3, parallel = TRUE)
if(!segments)
return
var/segment = 360/segments
if(!clockwise)
segment = -segment
var/list/matrices = list()
for(var/i in 1 to segments-1)
var/matrix/M = matrix(transform)
M.Turn(segment*i)
matrices += M
var/matrix/last = matrix(transform)
matrices += last
speed /= segments
if(parallel)
animate(src, transform = matrices[1], time = speed, loops , flags = ANIMATION_PARALLEL)
else
animate(src, transform = matrices[1], time = speed, loops)
for(var/i in 2 to segments) //2 because 1 is covered above
animate(transform = matrices[i], time = speed)
//doesn't have an object argument because this is "Stacking" with the animate call above
//3 billion% intentional
//Dumps the matrix data in format a-f
/matrix/proc/tolist()
. = list()
. += a
. += b
. += c
. += d
. += e
. += f
//Dumps the matrix data in a matrix-grid format
/*
a d 0
b e 0
c f 1
*/
/matrix/proc/togrid()
. = list()
. += a
. += d
. += 0
. += b
. += e
. += 0
. += c
. += f
. += 1
//The X pixel offset of this matrix
/matrix/proc/get_x_shift()
. = c
//The Y pixel offset of this matrix
/matrix/proc/get_y_shift()
. = f
/matrix/proc/get_x_skew()
. = b
/matrix/proc/get_y_skew()
. = d
//Skews a matrix in a particular direction
//Missing arguments are treated as no skew in that direction
//As Rotation is defined as a scale+skew, these procs will break any existing rotation
//Unless the result is multiplied against the current matrix
/matrix/proc/set_skew(x = 0, y = 0)
b = x
d = y
/**
* constructs a transform matrix, defaulting to identity
*/
/proc/transform_matrix_construct(a = 1, b, c, d = 1, e, f)
return matrix(a, b, c, d, e, f)
+15
View File
@@ -458,6 +458,21 @@ GLOBAL_LIST_EMPTY(species_datums)
if(!HAS_TRAIT(L, TRAIT_PASSTABLE))
L.pass_flags &= ~PASSTABLE
/proc/dance_rotate(atom/movable/AM, datum/callback/callperrotate, set_original_dir=FALSE)
set waitfor = FALSE
var/originaldir = AM.dir
for(var/i in list(NORTH,SOUTH,EAST,WEST,EAST,SOUTH,NORTH,SOUTH,EAST,WEST,EAST,SOUTH))
if(!AM)
return
AM.setDir(i)
callperrotate?.Invoke()
sleep(1)
if(set_original_dir)
AM.setDir(originaldir)
/// Gets the client of the mob, allowing for mocking of the client.
/// You only need to use this if you know you're going to be mocking clients somewhere else.
#define GET_CLIENT(mob) (##mob.client || ##mob.mock_client)
//check if the person is dead, not sure where to put this
#define IS_DEAD_OR_INCAP(source) (source.incapacitated() || source.stat)
+47 -5
View File
@@ -1,8 +1,10 @@
/proc/priority_announce(text, title = "", sound = "attention", type , sender_override)
/proc/priority_announce(text, title = "", sound, type , sender_override, has_important_message)
if(!text)
return
var/announcement
if(!sound)
sound = "attention"
if(type == "Priority")
announcement += "<h1 class='alert'>Priority Announcement</h1>"
@@ -26,7 +28,11 @@
else
GLOB.news_network.SubmitArticle(title + "<br><br>" + text, "Central Command", "Station Announcements", null)
announcement += "<br><span class='alert'>[html_encode(text)]</span><br>"
///If the announcer overrides alert messages, use that message.
// if(SSstation.announcer.custom_alert_message && !has_important_message)
// announcement += SSstation.announcer.custom_alert_message
// else
announcement += "<br>[span_alert("[html_encode(text)]")]<br>"
announcement += "<br>"
var/s = sound(get_announcer_sound(sound))
@@ -127,12 +133,44 @@
if("welcome")
. = 'sound/announcer/medibot/welcome.ogg'
/**
* Summon the crew for an emergency meeting
*
* Teleports the crew to a specified area, and tells everyone (via an announcement) who called the meeting. Should only be used during april fools!
* Arguments:
* * user - Mob who called the meeting
* * button_zone - Area where the meeting was called and where everyone will get teleported to
*/
/proc/call_emergency_meeting(mob/living/user, area/button_zone)
var/meeting_sound = sound('sound/misc/emergency_meeting.ogg')
var/announcement
announcement += "<h1 class='alert'>Captain Alert</h1>"
announcement += "<br>[span_alert("[user] has called an Emergency Meeting!")]<br><br>"
for(var/mob/mob_to_teleport in GLOB.player_list) //gotta make sure the whole crew's here!
if(isnewplayer(mob_to_teleport) || iscameramob(mob_to_teleport))
continue
to_chat(mob_to_teleport, announcement)
SEND_SOUND(mob_to_teleport, meeting_sound) //no preferences here, you must hear the funny sound
mob_to_teleport.overlay_fullscreen("emergency_meeting", /atom/movable/screen/fullscreen/scaled/emergency_meeting, 1)
addtimer(CALLBACK(mob_to_teleport, /mob/.proc/clear_fullscreen, "emergency_meeting"), 3 SECONDS)
if (is_station_level(mob_to_teleport.z)) //teleport the mob to the crew meeting
var/turf/target
var/list/turf_list = get_area_turfs(button_zone)
while (!target && turf_list.len)
target = pick_n_take(turf_list)
if (isclosedturf(target))
target = null
continue
mob_to_teleport.forceMove(target)
/proc/print_command_report(text = "", title = null, announce=TRUE)
if(!title)
title = "Classified [command_name()] Update"
if(announce)
priority_announce("A report has been downloaded and printed out at all communications consoles.", "Incoming Classified Message", "commandreport")
priority_announce("A report has been downloaded and printed out at all communications consoles.", "Incoming Classified Message", "commandreport", has_important_message = TRUE)
var/datum/comm_message/M = new
M.title = title
@@ -140,13 +178,17 @@
SScommunications.send_message(M)
/proc/minor_announce(message, title = "Attention:", alert)
/proc/minor_announce(message, title = "Attention:", alert, html_encode = TRUE)
if(!message)
return
if (html_encode)
title = html_encode(title)
message = html_encode(message)
for(var/mob/M in GLOB.player_list)
if(!isnewplayer(M) && M.can_hear())
to_chat(M, "<span class='big bold'><font color = red>[html_encode(title)]</font color><BR>[html_encode(message)]</span><BR>")
to_chat(M, "[span_minorannounce("<font color = red>[title]</font color><BR>[message]")]<BR>")
if(M.client.prefs.toggles & SOUND_ANNOUNCEMENTS)
if(alert)
SEND_SOUND(M, sound('sound/misc/notice1.ogg'))
+17 -3
View File
@@ -1,4 +1,4 @@
// Ensure the frequency is within bounds of what it should be sending/receiving at
/// Ensure the frequency is within bounds of what it should be sending/receiving at
/proc/sanitize_frequency(frequency, free = FALSE)
frequency = round(frequency)
if(free)
@@ -8,12 +8,26 @@
if(!(. % 2)) // Ensure the last digit is an odd number
. += 1
// Format frequency by moving the decimal.
/// Format frequency by moving the decimal.
/proc/format_frequency(frequency)
frequency = text2num(frequency)
return "[round(frequency / 10)].[frequency % 10]"
//Opposite of format, returns as a number
///Opposite of format, returns as a number
/proc/unformat_frequency(frequency)
frequency = text2num(frequency)
return frequency * 10
///returns a random unused frequency between MIN_FREE_FREQ & MAX_FREE_FREQ if free = TRUE, and MIN_FREQ & MAX_FREQ if FALSE
/proc/return_unused_frequency(free = FALSE)
var/start = free ? MIN_FREE_FREQ : MIN_FREQ
var/end = free ? MAX_FREE_FREQ : MAX_FREQ
var/freq_to_check = 0
do
freq_to_check = rand(start, end)
if(!(freq_to_check % 2)) // Ensure the last digit is an odd number
freq_to_check++
while((freq_to_check == 0) || ("[freq_to_check]" in GLOB.reverseradiochannels))
return freq_to_check
+2 -2
View File
@@ -590,8 +590,8 @@
var/list/all_teams = list()
var/list/all_antagonists = list()
// for(var/datum/team/A in GLOB.antagonist_teams)
// all_teams |= A
for(var/datum/team/A in GLOB.antagonist_teams)
all_teams |= A
for(var/datum/antagonist/A in GLOB.antagonists)
if(!A.owner)
+2 -2
View File
@@ -591,9 +591,9 @@
switch(child)
if(/datum)
return null
if(/obj || /mob)
if(/obj, /mob)
return /atom/movable
if(/area || /turf)
if(/area, /turf)
return /atom
else
return /datum
+3 -3
View File
@@ -663,10 +663,10 @@ Turf and target are separate in case you want to teleport some distance from a t
return locate(final_x, final_y, T.z)
//Finds the distance between two atoms, in pixels
//centered = 0 counts from turf edge to edge
//centered = 1 counts from turf center to turf center
//centered = FALSE counts from turf edge to edge
//centered = TRUE counts from turf center to turf center
//of course mathematically this is just adding world.icon_size on again
/proc/getPixelDistance(atom/A, atom/B, centered = 1)
/proc/getPixelDistance(atom/A, atom/B, centered = TRUE)
if(!istype(A)||!istype(B))
return 0
. = bounds_dist(A, B) + sqrt((((A.pixel_x+B.pixel_x)**2) + ((A.pixel_y+B.pixel_y)**2)))