mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-10 18:22:39 +00:00
Modifies do_after to something that fires a lot less, and is hopefully more robust against infinite loops. It is now theoretically possible to run around and then come back to the same place and have it complete, but that's only really valid for extremely long times (like handcuff removal) and if you get lucky and dodge one of the timed checks. git-svn-id: http://tgstation13.googlecode.com/svn/trunk@3421 316c924e-a436-60f5-8080-3fe189b3f50e
1768 lines
48 KiB
Plaintext
1768 lines
48 KiB
Plaintext
/proc/hex2num(hex)
|
||
|
||
if (!( istext(hex) ))
|
||
CRASH("hex2num not given a hexadecimal string argument (user error)")
|
||
return
|
||
var/num = 0
|
||
var/power = 0
|
||
var/i = null
|
||
i = length(hex)
|
||
while(i > 0)
|
||
var/char = copytext(hex, i, i + 1)
|
||
switch(char)
|
||
if("0")
|
||
//Apparently, switch works with empty statements, yay! If that doesn't work, blame me, though. -- Urist
|
||
if("9", "8", "7", "6", "5", "4", "3", "2", "1")
|
||
num += text2num(char) * 16 ** power
|
||
if("a", "A")
|
||
num += 16 ** power * 10
|
||
if("b", "B")
|
||
num += 16 ** power * 11
|
||
if("c", "C")
|
||
num += 16 ** power * 12
|
||
if("d", "D")
|
||
num += 16 ** power * 13
|
||
if("e", "E")
|
||
num += 16 ** power * 14
|
||
if("f", "F")
|
||
num += 16 ** power * 15
|
||
else
|
||
CRASH("hex2num given non-hexadecimal string (user error)")
|
||
return
|
||
power++
|
||
i--
|
||
return num
|
||
|
||
/proc/num2hex(num, placeholder)
|
||
|
||
if (placeholder == null)
|
||
placeholder = 2
|
||
if (!( isnum(num) ))
|
||
CRASH("num2hex not given a numeric argument (user error)")
|
||
return
|
||
if (!( num ))
|
||
return "0"
|
||
var/hex = ""
|
||
var/i = 0
|
||
while(16 ** i < num)
|
||
i++
|
||
var/power = null
|
||
power = i - 1
|
||
while(power >= 0)
|
||
var/val = round(num / 16 ** power)
|
||
num -= val * 16 ** power
|
||
switch(val)
|
||
if(9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0, 0.0)
|
||
hex += text("[]", val)
|
||
if(10.0)
|
||
hex += "A"
|
||
if(11.0)
|
||
hex += "B"
|
||
if(12.0)
|
||
hex += "C"
|
||
if(13.0)
|
||
hex += "D"
|
||
if(14.0)
|
||
hex += "E"
|
||
if(15.0)
|
||
hex += "F"
|
||
else
|
||
power--
|
||
while(length(hex) < placeholder)
|
||
hex = text("0[]", hex)
|
||
return hex
|
||
|
||
/proc/invertHTML(HTMLstring)
|
||
|
||
if (!( istext(HTMLstring) ))
|
||
CRASH("Given non-text argument!")
|
||
return
|
||
else
|
||
if (length(HTMLstring) != 7)
|
||
CRASH("Given non-HTML argument!")
|
||
return
|
||
var/textr = copytext(HTMLstring, 2, 4)
|
||
var/textg = copytext(HTMLstring, 4, 6)
|
||
var/textb = copytext(HTMLstring, 6, 8)
|
||
var/r = hex2num(textr)
|
||
var/g = hex2num(textg)
|
||
var/b = hex2num(textb)
|
||
textr = num2hex(255 - r)
|
||
textg = num2hex(255 - g)
|
||
textb = num2hex(255 - b)
|
||
if (length(textr) < 2)
|
||
textr = text("0[]", textr)
|
||
if (length(textg) < 2)
|
||
textr = text("0[]", textg)
|
||
if (length(textb) < 2)
|
||
textr = text("0[]", textb)
|
||
return text("#[][][]", textr, textg, textb)
|
||
return
|
||
|
||
/proc/shuffle(var/list/shufflelist)
|
||
if(!shufflelist)
|
||
return
|
||
var/list/new_list = list()
|
||
var/list/old_list = shufflelist.Copy()
|
||
while(old_list.len)
|
||
var/item = pick(old_list)
|
||
new_list += item
|
||
old_list -= item
|
||
return new_list
|
||
|
||
/proc/uniquelist(var/list/L)
|
||
var/list/K = list()
|
||
for(var/item in L)
|
||
if(!(item in K))
|
||
K += item
|
||
return K
|
||
|
||
/proc/sanitize_simple(var/t,var/list/repl_chars = list("\n"="#","\t"="#","<22>"="<22>"))
|
||
for(var/char in repl_chars)
|
||
var/index = findtext(t, char)
|
||
while(index)
|
||
t = copytext(t, 1, index) + repl_chars[char] + copytext(t, index+1)
|
||
index = findtext(t, char)
|
||
return t
|
||
|
||
/proc/strip_html_simple(var/t,var/limit=MAX_MESSAGE_LEN)
|
||
var/list/strip_chars = list("<",">")
|
||
t = copytext(t,1,limit)
|
||
for(var/char in strip_chars)
|
||
var/index = findtext(t, char)
|
||
while(index)
|
||
t = copytext(t, 1, index) + copytext(t, index+1)
|
||
index = findtext(t, char)
|
||
return t
|
||
|
||
/proc/sanitize(var/t,var/list/repl_chars = null)
|
||
return html_encode(sanitize_simple(t,repl_chars))
|
||
|
||
/proc/strip_html(var/t,var/limit=MAX_MESSAGE_LEN)
|
||
return sanitize(strip_html_simple(t))
|
||
|
||
/proc/adminscrub(var/t,var/limit=MAX_MESSAGE_LEN)
|
||
return html_encode(strip_html_simple(t))
|
||
|
||
/proc/add_zero(t, u)
|
||
while (length(t) < u)
|
||
t = "0[t]"
|
||
return t
|
||
|
||
/proc/add_lspace(t, u)
|
||
while(length(t) < u)
|
||
t = " [t]"
|
||
return t
|
||
|
||
/proc/add_tspace(t, u)
|
||
while(length(t) < u)
|
||
t = "[t] "
|
||
return t
|
||
|
||
/proc/trim_left(text)
|
||
for (var/i = 1 to length(text))
|
||
if (text2ascii(text, i) > 32)
|
||
return copytext(text, i)
|
||
return ""
|
||
|
||
/proc/trim_right(text)
|
||
for (var/i = length(text), i > 0, i--)
|
||
if (text2ascii(text, i) > 32)
|
||
return copytext(text, 1, i + 1)
|
||
|
||
return ""
|
||
|
||
/proc/trim(text)
|
||
return trim_left(trim_right(text))
|
||
|
||
/proc/capitalize(var/t as text)
|
||
return uppertext(copytext(t, 1, 2)) + copytext(t, 2)
|
||
|
||
/proc/sortList(var/list/L)
|
||
if(L.len < 2)
|
||
return L
|
||
var/middle = L.len / 2 + 1 // Copy is first,second-1
|
||
return mergeLists(sortList(L.Copy(0,middle)), sortList(L.Copy(middle))) //second parameter null = to end of list
|
||
|
||
/proc/sortNames(var/list/L)
|
||
var/list/Q = new()
|
||
for(var/atom/x in L)
|
||
Q[x.name] = x
|
||
return sortList(Q)
|
||
|
||
/proc/mergeLists(var/list/L, var/list/R)
|
||
var/Li=1
|
||
var/Ri=1
|
||
var/list/result = new()
|
||
while(Li <= L.len && Ri <= R.len)
|
||
if(sorttext(L[Li], R[Ri]) < 1)
|
||
result += R[Ri++]
|
||
else
|
||
result += L[Li++]
|
||
|
||
if(Li <= L.len)
|
||
return (result + L.Copy(Li, 0))
|
||
return (result + R.Copy(Ri, 0))
|
||
|
||
/proc/dd_file2list(file_path, separator)
|
||
var/file
|
||
if(separator == null)
|
||
separator = "\n"
|
||
if(isfile(file_path))
|
||
file = file_path
|
||
else
|
||
file = file(file_path)
|
||
return dd_text2list(file2text(file), separator)
|
||
|
||
/proc/dd_range(var/low, var/high, var/num)
|
||
return max(low,min(high,num))
|
||
|
||
/proc/dd_replacetext(text, search_string, replacement_string)
|
||
if(!text || !istext(text) || !search_string || !istext(search_string) || !istext(replacement_string))
|
||
return null
|
||
var/textList = dd_text2list(text, search_string)
|
||
return dd_list2text(textList, replacement_string)
|
||
|
||
/proc/dd_replaceText(text, search_string, replacement_string)
|
||
var/textList = dd_text2List(text, search_string)
|
||
return dd_list2text(textList, replacement_string)
|
||
|
||
/proc/dd_hasprefix(text, prefix)
|
||
var/start = 1
|
||
var/end = length(prefix) + 1
|
||
return findtext(text, prefix, start, end)
|
||
|
||
/proc/dd_hasPrefix(text, prefix)
|
||
var/start = 1
|
||
var/end = length(prefix) + 1
|
||
return findtext(text, prefix, start, end) //was findtextEx
|
||
|
||
/proc/dd_hassuffix(text, suffix)
|
||
var/start = length(text) - length(suffix)
|
||
if(start)
|
||
return findtext(text, suffix, start, null)
|
||
return
|
||
|
||
/proc/dd_hasSuffix(text, suffix)
|
||
var/start = length(text) - length(suffix)
|
||
if(start)
|
||
return findtext(text, suffix, start, null) //was findtextEx
|
||
|
||
/proc/dd_text2list(text, separator, var/list/withinList)
|
||
var/textlength = length(text)
|
||
var/separatorlength = length(separator)
|
||
if(withinList && !withinList.len) withinList = null
|
||
var/list/textList = new()
|
||
var/searchPosition = 1
|
||
var/findPosition = 1
|
||
var/loops = 0
|
||
while(1)
|
||
if(loops >= 1000)
|
||
break
|
||
loops++
|
||
|
||
findPosition = findtext(text, separator, searchPosition, 0)
|
||
var/buggyText = copytext(text, searchPosition, findPosition)
|
||
if(!withinList || (buggyText in withinList)) textList += "[buggyText]"
|
||
if(!findPosition) return textList
|
||
searchPosition = findPosition + separatorlength
|
||
if(searchPosition > textlength)
|
||
textList += ""
|
||
return textList
|
||
return
|
||
|
||
/proc/dd_text2List(text, separator, var/list/withinList)
|
||
var/textlength = length(text)
|
||
var/separatorlength = length(separator)
|
||
if(withinList && !withinList.len) withinList = null
|
||
var/list/textList = new()
|
||
var/searchPosition = 1
|
||
var/findPosition = 1
|
||
while(1)
|
||
findPosition = findtext(text, separator, searchPosition, 0) //was findtextEx
|
||
var/buggyText = copytext(text, searchPosition, findPosition)
|
||
if(!withinList || (buggyText in withinList)) textList += "[buggyText]"
|
||
if(!findPosition) return textList
|
||
searchPosition = findPosition + separatorlength
|
||
if(searchPosition > textlength)
|
||
textList += ""
|
||
return textList
|
||
return
|
||
|
||
/proc/dd_list2text(var/list/the_list, separator)
|
||
var/total = the_list.len
|
||
if(!total)
|
||
return
|
||
var/count = 2
|
||
var/newText = "[the_list[1]]"
|
||
while(count <= total)
|
||
if(separator)
|
||
newText += separator
|
||
newText += "[the_list[count]]"
|
||
count++
|
||
return newText
|
||
|
||
//slower then dd_list2text, but correctly processes associative lists.
|
||
proc/tg_list2text(list/list, glue=",")
|
||
if(!istype(list) || !list.len)
|
||
return
|
||
var/output
|
||
for(var/i=1 to list.len)
|
||
output += (i!=1? glue : null)+(!isnull(list["[list[i]]"])?"[list["[list[i]]"]]":"[list[i]]")
|
||
return output
|
||
|
||
|
||
//tg_text2list is faster then dd_text2list
|
||
//not case sensitive version
|
||
proc/tg_text2list(string, separator=",")
|
||
if(!string)
|
||
return
|
||
var/list/output = new
|
||
var/seplength = length(separator)
|
||
var/strlength = length(string)
|
||
var/prev = 1
|
||
var/index
|
||
do
|
||
index = findtext(string, separator, prev, 0)
|
||
output += copytext(string, prev, index)
|
||
if(!index)
|
||
break
|
||
prev = index+seplength
|
||
if(prev>strlength)
|
||
break
|
||
while(index)
|
||
return output
|
||
|
||
//case sensitive version
|
||
proc/tg_extext2list(string, separator=",")
|
||
if(!string)
|
||
return
|
||
var/list/output = new
|
||
var/seplength = length(separator)
|
||
var/strlength = length(string)
|
||
var/prev = 1
|
||
var/index
|
||
do
|
||
index = findtextEx(string, separator, prev, 0)
|
||
output += copytext(string, prev, index)
|
||
if(!index)
|
||
break
|
||
prev = index+seplength
|
||
if(prev>strlength)
|
||
break
|
||
while(index)
|
||
return output
|
||
|
||
/proc/english_list(var/list/input, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "," )
|
||
var/total = input.len
|
||
if (!total)
|
||
return "[nothing_text]"
|
||
else if (total == 1)
|
||
return "[input[1]]"
|
||
else if (total == 2)
|
||
return "[input[1]][and_text][input[2]]"
|
||
else
|
||
var/output = ""
|
||
var/index = 1
|
||
while (index < total)
|
||
if (index == total - 1)
|
||
comma_text = final_comma_text
|
||
|
||
output += "[input[index]][comma_text]"
|
||
index++
|
||
|
||
return "[output][and_text][input[index]]"
|
||
|
||
/proc/dd_centertext(message, length)
|
||
var/new_message = message
|
||
var/size = length(message)
|
||
var/delta = length - size
|
||
if(size == length)
|
||
return new_message
|
||
if(size > length)
|
||
return copytext(new_message, 1, length + 1)
|
||
if(delta == 1)
|
||
return new_message + " "
|
||
if(delta % 2)
|
||
new_message = " " + new_message
|
||
delta--
|
||
var/spaces = add_lspace("",delta/2-1)
|
||
return spaces + new_message + spaces
|
||
|
||
/proc/dd_limittext(message, length)
|
||
var/size = length(message)
|
||
if(size <= length)
|
||
return message
|
||
return copytext(message, 1, length + 1)
|
||
|
||
/proc/angle2dir(var/degree)
|
||
degree = ((degree+22.5)%365)
|
||
if(degree < 45) return NORTH
|
||
if(degree < 90) return NORTH|EAST
|
||
if(degree < 135) return EAST
|
||
if(degree < 180) return SOUTH|EAST
|
||
if(degree < 225) return SOUTH
|
||
if(degree < 270) return SOUTH|WEST
|
||
if(degree < 315) return WEST
|
||
return NORTH|WEST
|
||
|
||
/proc/angle2text(var/degree)
|
||
return dir2text(angle2dir(degree))
|
||
|
||
/proc/Get_Angle(atom/movable/start,atom/movable/end)//For beams.
|
||
if(!start || !end) return 0
|
||
var/dy
|
||
var/dx
|
||
dy=(32*end.y+end.pixel_y)-(32*start.y+start.pixel_y)
|
||
dx=(32*end.x+end.pixel_x)-(32*start.x+start.pixel_x)
|
||
if(!dy)
|
||
return (dx>=0)?90:270
|
||
.=arctan(dx/dy)
|
||
if(dy<0)
|
||
.+=180
|
||
else if(dx<0)
|
||
.+=360
|
||
|
||
//Returns location. Returns null if no location was found.
|
||
/proc/get_teleport_loc(turf/location,mob/target,distance = 1, density = 0, errorx = 0, errory = 0, eoffsetx = 0, eoffsety = 0)
|
||
/*
|
||
Location where the teleport begins, target that will teleport, distance to go, density checking 0/1(yes/no).
|
||
Random error in tile placement x, error in tile placement y, and block offset.
|
||
Block offset tells the proc how to place the box. Behind teleport location, relative to starting location, forward, etc.
|
||
Negative values for offset are accepted, think of it in relation to North, -x is west, -y is south. Error defaults to positive.
|
||
Turf and target are seperate in case you want to teleport some distance from a turf the target is not standing on or something.
|
||
*/
|
||
|
||
var/dirx = 0//Generic location finding variable.
|
||
var/diry = 0
|
||
|
||
var/xoffset = 0//Generic counter for offset location.
|
||
var/yoffset = 0
|
||
|
||
var/b1xerror = 0//Generic placing for point A in box. The lower left.
|
||
var/b1yerror = 0
|
||
var/b2xerror = 0//Generic placing for point B in box. The upper right.
|
||
var/b2yerror = 0
|
||
|
||
errorx = abs(errorx)//Error should never be negative.
|
||
errory = abs(errory)
|
||
//var/errorxy = round((errorx+errory)/2)//Used for diagonal boxes.
|
||
|
||
switch(target.dir)//This can be done through equations but switch is the simpler method. And works fast to boot.
|
||
//Directs on what values need modifying.
|
||
if(1)//North
|
||
diry+=distance
|
||
yoffset+=eoffsety
|
||
xoffset+=eoffsetx
|
||
b1xerror-=errorx
|
||
b1yerror-=errory
|
||
b2xerror+=errorx
|
||
b2yerror+=errory
|
||
if(2)//South
|
||
diry-=distance
|
||
yoffset-=eoffsety
|
||
xoffset+=eoffsetx
|
||
b1xerror-=errorx
|
||
b1yerror-=errory
|
||
b2xerror+=errorx
|
||
b2yerror+=errory
|
||
if(4)//East
|
||
dirx+=distance
|
||
yoffset+=eoffsetx//Flipped.
|
||
xoffset+=eoffsety
|
||
b1xerror-=errory//Flipped.
|
||
b1yerror-=errorx
|
||
b2xerror+=errory
|
||
b2yerror+=errorx
|
||
if(8)//West
|
||
dirx-=distance
|
||
yoffset-=eoffsetx//Flipped.
|
||
xoffset+=eoffsety
|
||
b1xerror-=errory//Flipped.
|
||
b1yerror-=errorx
|
||
b2xerror+=errory
|
||
b2yerror+=errorx
|
||
|
||
var/turf/destination=locate(location.x+dirx,location.y+diry,location.z)
|
||
|
||
if(destination)//If there is a destination.
|
||
if(errorx||errory)//If errorx or y were specified.
|
||
var/destination_list[] = list()//To add turfs to list.
|
||
//destination_list = new()
|
||
/*This will draw a block around the target turf, given what the error is.
|
||
Specifying the values above will basically draw a different sort of block.
|
||
If the values are the same, it will be a square. If they are different, it will be a rectengle.
|
||
In either case, it will center based on offset. Offset is position from center.
|
||
Offset always calculates in relation to direction faced. In other words, depending on the direction of the teleport,
|
||
the offset should remain positioned in relation to destination.*/
|
||
|
||
var/turf/center = locate((destination.x+xoffset),(destination.y+yoffset),location.z)//So now, find the new center.
|
||
|
||
//Now to find a box from center location and make that our destination.
|
||
for(var/turf/T in block(locate(center.x+b1xerror,center.y+b1yerror,location.z), locate(center.x+b2xerror,center.y+b2yerror,location.z) ))
|
||
if(density&&T.density) continue//If density was specified.
|
||
if(T.x>world.maxx || T.x<1) continue//Don't want them to teleport off the map.
|
||
if(T.y>world.maxy || T.y<1) continue
|
||
destination_list += T
|
||
if(destination_list.len)
|
||
destination = pick(destination_list)
|
||
else return
|
||
|
||
else//Same deal here.
|
||
if(density&&destination.density) return
|
||
if(destination.x>world.maxx || destination.x<1) return
|
||
if(destination.y>world.maxy || destination.y<1) return
|
||
else return
|
||
|
||
return destination
|
||
|
||
/proc/text_input(var/Message, var/Title, var/Default, var/length=MAX_MESSAGE_LEN)
|
||
return sanitize(input(Message, Title, Default) as text, length)
|
||
|
||
/proc/scrub_input(var/Message, var/Title, var/Default, var/length=MAX_MESSAGE_LEN)
|
||
return strip_html(input(Message,Title,Default) as text, length)
|
||
|
||
/proc/InRange(var/A, var/lower, var/upper)
|
||
if(A < lower) return 0
|
||
if(A > upper) return 0
|
||
return 1
|
||
|
||
/proc/LinkBlocked(turf/A, turf/B)
|
||
if(A == null || B == null) return 1
|
||
var/adir = get_dir(A,B)
|
||
var/rdir = get_dir(B,A)
|
||
if((adir & (NORTH|SOUTH)) && (adir & (EAST|WEST))) // diagonal
|
||
var/iStep = get_step(A,adir&(NORTH|SOUTH))
|
||
if(!LinkBlocked(A,iStep) && !LinkBlocked(iStep,B)) return 0
|
||
|
||
var/pStep = get_step(A,adir&(EAST|WEST))
|
||
if(!LinkBlocked(A,pStep) && !LinkBlocked(pStep,B)) return 0
|
||
return 1
|
||
|
||
if(DirBlocked(A,adir)) return 1
|
||
if(DirBlocked(B,rdir)) return 1
|
||
return 0
|
||
|
||
|
||
/proc/DirBlocked(turf/loc,var/dir)
|
||
for(var/obj/structure/window/D in loc)
|
||
if(!D.density) continue
|
||
if(D.dir == SOUTHWEST) return 1
|
||
if(D.dir == dir) return 1
|
||
|
||
for(var/obj/machinery/door/D in loc)
|
||
if(!D.density) continue
|
||
if(istype(D, /obj/machinery/door/window))
|
||
if((dir & SOUTH) && (D.dir & (EAST|WEST))) return 1
|
||
if((dir & EAST ) && (D.dir & (NORTH|SOUTH))) return 1
|
||
else return 1 // it's a real, air blocking door
|
||
return 0
|
||
|
||
/proc/TurfBlockedNonWindow(turf/loc)
|
||
for(var/obj/O in loc)
|
||
if(O.density && !istype(O, /obj/structure/window))
|
||
return 1
|
||
return 0
|
||
|
||
/proc/sign(x) //Should get bonus points for being the most compact code in the world!
|
||
return x!=0?x/abs(x):0 //((x<0)?-1:((x>0)?1:0))
|
||
|
||
/* //Kelson's version (doesn't work)
|
||
/proc/getline(atom/M,atom/N)
|
||
if(!M || !M.loc) return
|
||
if(!N || !N.loc) return
|
||
if(M.z != N.z) return
|
||
var/line = new/list()
|
||
|
||
var/dx = abs(M.x - N.x)
|
||
var/dy = abs(M.y - N.y)
|
||
var/cx = M.x < N.x ? 1 : -1
|
||
var/cy = M.y < N.y ? 1 : -1
|
||
var/slope = dy ? dx/dy : INFINITY
|
||
|
||
var/tslope = slope
|
||
var/turf/tloc = M.loc
|
||
|
||
while(tloc != N.loc)
|
||
if(tslope>0)
|
||
--tslope
|
||
tloc = locate(tloc.x+cx,tloc.y,tloc.z)
|
||
else
|
||
tslope += slope
|
||
tloc = locate(tloc.x,tloc.y+cy,tloc.z)
|
||
line += tloc
|
||
return line
|
||
*/
|
||
|
||
/proc/getline(atom/M,atom/N)//Ultra-Fast Bresenham Line-Drawing Algorithm
|
||
var/px=M.x //starting x
|
||
var/py=M.y
|
||
var/line[] = list(locate(px,py,M.z))
|
||
var/dx=N.x-px //x distance
|
||
var/dy=N.y-py
|
||
var/dxabs=abs(dx)//Absolute value of x distance
|
||
var/dyabs=abs(dy)
|
||
var/sdx=sign(dx) //Sign of x distance (+ or -)
|
||
var/sdy=sign(dy)
|
||
var/x=dxabs>>1 //Counters for steps taken, setting to distance/2
|
||
var/y=dyabs>>1 //Bit-shifting makes me l33t. It also makes getline() unnessecarrily fast.
|
||
var/j //Generic integer for counting
|
||
if(dxabs>=dyabs) //x distance is greater than y
|
||
for(j=0;j<dxabs;j++)//It'll take dxabs steps to get there
|
||
y+=dyabs
|
||
if(y>=dxabs) //Every dyabs steps, step once in y direction
|
||
y-=dxabs
|
||
py+=sdy
|
||
px+=sdx //Step on in x direction
|
||
line+=locate(px,py,M.z)//Add the turf to the list
|
||
else
|
||
for(j=0;j<dyabs;j++)
|
||
x+=dxabs
|
||
if(x>=dyabs)
|
||
x-=dyabs
|
||
px+=sdx
|
||
py+=sdy
|
||
line+=locate(px,py,M.z)
|
||
return line
|
||
|
||
/proc/IsGuestKey(key)
|
||
if (findtext(key, "Guest-", 1, 7) != 1) //was findtextEx
|
||
return 0
|
||
|
||
var/i, ch, len = length(key)
|
||
|
||
for (i = 7, i <= len, ++i)
|
||
ch = text2ascii(key, i)
|
||
if (ch < 48 || ch > 57)
|
||
return 0
|
||
|
||
return 1
|
||
|
||
/proc/pickweight(list/L)
|
||
var/total = 0
|
||
var/item
|
||
for (item in L)
|
||
if (!L[item])
|
||
L[item] = 1
|
||
total += L[item]
|
||
|
||
total = rand(1, total)
|
||
for (item in L)
|
||
total -=L [item]
|
||
if (total <= 0)
|
||
return item
|
||
|
||
return null
|
||
|
||
/proc/sanitize_frequency(var/f)
|
||
f = round(f)
|
||
f = max(1441, f) // 144.1
|
||
f = min(1489, f) // 148.9
|
||
if ((f % 2) == 0)
|
||
f += 1
|
||
return f
|
||
|
||
/proc/format_frequency(var/f)
|
||
return "[round(f / 10)].[f % 10]"
|
||
|
||
/proc/ainame(var/mob/M as mob)
|
||
var/randomname = M.name
|
||
var/time_passed = world.time//Pretty basic but it'll do. It's still possible to bypass this by return ainame().
|
||
var/newname = input(M,"You are the AI. Would you like to change your name to something else?", "Name change",randomname)
|
||
if((world.time-time_passed)>200)//If more than 20 game seconds passed.
|
||
M << "You took too long to decide. Default name selected."
|
||
return
|
||
|
||
if (length(newname) == 0)
|
||
newname = randomname
|
||
|
||
if (newname)
|
||
if (newname == "Inactive AI")//Keeping this here to prevent dumb.
|
||
M << "That name is reserved."
|
||
return ainame(M)
|
||
for (var/mob/living/silicon/ai/A in world)
|
||
if (A.real_name == newname&&newname!=randomname)
|
||
M << "There's already an AI with that name."
|
||
return ainame(M)
|
||
if (length(newname) >= 26)
|
||
newname = copytext(newname, 1, 26)
|
||
newname = dd_replacetext(newname, ">", "'")
|
||
M.real_name = newname
|
||
M.name = newname
|
||
|
||
/proc/clname(var/mob/M as mob) //--All praise goes to NEO|Phyte, all blame goes to DH, and it was Cindi-Kate's idea
|
||
var/randomname = pick(clown_names)
|
||
var/newname = input(M,"You are the clown. Would you like to change your name to something else?", "Name change",randomname)
|
||
var/oldname = M.real_name
|
||
|
||
if (length(newname) == 0)
|
||
newname = randomname
|
||
|
||
if (newname)
|
||
var/badname = 0
|
||
switch(newname)
|
||
if("Unknown") badname = 1
|
||
if("floor") badname = 1
|
||
if("wall") badname = 1
|
||
if("r-wall") badname = 1
|
||
if("space") badname = 1
|
||
if("_") badname = 1
|
||
|
||
if(badname)
|
||
M << "That name is reserved."
|
||
return clname(M)
|
||
for (var/mob/A in world)
|
||
if(A.real_name == newname)
|
||
M << "That name is reserved."
|
||
return clname(M)
|
||
if(length(newname) >= 26)
|
||
newname = copytext(newname, 1, 26)
|
||
newname = dd_replacetext(newname, ">", "'")
|
||
M.real_name = newname
|
||
M.name = newname
|
||
|
||
for (var/obj/item/device/pda/pda in M.contents)
|
||
if (pda.owner == oldname)
|
||
pda.owner = newname
|
||
pda.name = "PDA-[newname] ([pda.ownjob])"
|
||
break
|
||
for(var/obj/item/weapon/card/id/id in M.contents)
|
||
if(id.registered == oldname)
|
||
id.registered = newname
|
||
id.name = "[id.registered]'s ID Card ([id.assignment])"
|
||
break
|
||
|
||
/proc/ionnum()
|
||
return "[pick("!","@","#","$","%","^","&","*")][pick(pick("!","@","#","$","%","^","&","*"))][pick(pick("!","@","#","$","%","^","&","*"))][pick(pick("!","@","#","$","%","^","&","*"))]"
|
||
|
||
/proc/freeborg()
|
||
var/select = null
|
||
var/list/names = list()
|
||
var/list/borgs = list()
|
||
var/list/namecounts = list()
|
||
for (var/mob/living/silicon/robot/A in world)
|
||
var/name = A.real_name
|
||
if (A.stat == 2)
|
||
continue
|
||
if (A.connected_ai)
|
||
continue
|
||
else
|
||
if(A.module)
|
||
name += " ([A.module.name])"
|
||
names.Add(name)
|
||
namecounts[name] = 1
|
||
borgs[name] = A
|
||
|
||
if (borgs.len)
|
||
select = input("Unshackled borg signals detected:", "Borg selection", null, null) as null|anything in borgs
|
||
return borgs[select]
|
||
|
||
/proc/activeais()
|
||
var/select = null
|
||
var/list/names = list()
|
||
var/list/ais = list()
|
||
var/list/namecounts = list()
|
||
for (var/mob/living/silicon/ai/A in world)
|
||
var/name = A.real_name
|
||
if (A.stat == 2)
|
||
continue
|
||
if (A.control_disabled == 1)
|
||
continue
|
||
else
|
||
names.Add(name)
|
||
namecounts[name] = 1
|
||
ais[name] = A
|
||
|
||
if (ais.len)
|
||
select = input("AI signals detected:", "AI selection") in ais
|
||
return ais[select]
|
||
|
||
/proc/getmobs()
|
||
|
||
var/list/mobs = sortmobs()
|
||
var/list/names = list()
|
||
var/list/creatures = list()
|
||
var/list/namecounts = list()
|
||
for(var/mob/M in mobs)
|
||
var/name = M.name
|
||
if (name in names)
|
||
namecounts[name]++
|
||
name = "[name] ([namecounts[name]])"
|
||
else
|
||
names.Add(name)
|
||
namecounts[name] = 1
|
||
if (M.real_name && M.real_name != M.name)
|
||
name += " \[[M.real_name]\]"
|
||
if (M.stat == 2)
|
||
if(istype(M, /mob/dead/observer/))
|
||
name += " \[ghost\]"
|
||
else
|
||
name += " \[dead\]"
|
||
creatures[name] = M
|
||
|
||
return creatures
|
||
|
||
/proc/sortmobs()
|
||
|
||
var/list/mob_list = list()
|
||
for(var/mob/living/silicon/ai/M in world)
|
||
mob_list.Add(M)
|
||
for(var/mob/living/silicon/pai/M in world)
|
||
mob_list.Add(M)
|
||
for(var/mob/living/silicon/robot/M in world)
|
||
mob_list.Add(M)
|
||
for(var/mob/living/carbon/human/M in world)
|
||
mob_list.Add(M)
|
||
for(var/mob/living/carbon/brain/M in world)
|
||
mob_list.Add(M)
|
||
for(var/mob/living/carbon/alien/M in world)
|
||
mob_list.Add(M)
|
||
for(var/mob/dead/observer/M in world)
|
||
mob_list.Add(M)
|
||
for(var/mob/new_player/M in world)
|
||
mob_list.Add(M)
|
||
for(var/mob/living/carbon/monkey/M in world)
|
||
mob_list.Add(M)
|
||
for(var/mob/living/carbon/metroid/M in world)
|
||
mob_list.Add(M)
|
||
for(var/mob/living/simple_animal/M in world)
|
||
mob_list.Add(M)
|
||
// for(var/mob/living/silicon/hivebot/M in world)
|
||
// mob_list.Add(M)
|
||
// for(var/mob/living/silicon/hive_mainframe/M in world)
|
||
// mob_list.Add(M)
|
||
return mob_list
|
||
|
||
/proc/convert2energy(var/M)
|
||
var/E = M*(SPEED_OF_LIGHT_SQ)
|
||
return E
|
||
|
||
/proc/convert2mass(var/E)
|
||
var/M = E/(SPEED_OF_LIGHT_SQ)
|
||
return M
|
||
|
||
/proc/modulus(var/M)
|
||
if(M >= 0)
|
||
return M
|
||
if(M < 0)
|
||
return -M
|
||
|
||
|
||
/proc/key_name(var/whom, var/include_link = null, var/include_name = 1)
|
||
var/mob/the_mob = null
|
||
var/client/the_client = null
|
||
var/the_key = ""
|
||
|
||
if (isnull(whom))
|
||
return "*null*"
|
||
else if (istype(whom, /client))
|
||
the_client = whom
|
||
the_mob = the_client.mob
|
||
the_key = the_client.key
|
||
else if (ismob(whom))
|
||
the_mob = whom
|
||
the_client = the_mob.client
|
||
the_key = the_mob.key
|
||
else if (istype(whom, /datum))
|
||
var/datum/the_datum = whom
|
||
return "*invalid:[the_datum.type]*"
|
||
else
|
||
return "*invalid*"
|
||
|
||
var/text = ""
|
||
|
||
if (!the_key)
|
||
text += "*no client*"
|
||
else
|
||
var/linked = 1
|
||
if (include_link && !isnull(the_mob))
|
||
if (istext(include_link))
|
||
text += "<a href=\"byond://?src=[include_link];priv_msg=\ref[the_client]\">"
|
||
else
|
||
if(ismob(include_link))
|
||
var/mob/MM = include_link
|
||
if(MM.client)
|
||
text += "<a href=\"byond://?src=\ref[MM.client];priv_msg=\ref[the_client]\">"
|
||
else
|
||
linked = 0
|
||
else if (istype(include_link, /client))
|
||
text += "<a href=\"byond://?src=\ref[include_link];priv_msg=\ref[the_client]\">"
|
||
else
|
||
linked = 0
|
||
|
||
if (the_client && the_client.holder && the_client.stealth && !include_name)
|
||
text += "Administrator"
|
||
else
|
||
text += "[the_key]"
|
||
|
||
if (!isnull(include_link) && !isnull(the_mob))
|
||
if(linked)
|
||
text += "</a>"
|
||
else
|
||
text += " (DC)"
|
||
|
||
if (include_name && !isnull(the_mob))
|
||
if (the_mob.real_name)
|
||
text += "/([the_mob.real_name])"
|
||
else if (the_mob.name)
|
||
text += "/([the_mob.name])"
|
||
|
||
return text
|
||
|
||
/proc/key_name_admin(var/whom, var/include_name = 1)
|
||
return key_name(whom, "%admin_ref%", include_name)
|
||
|
||
|
||
// Registers the on-close verb for a browse window (client/verb/.windowclose)
|
||
// this will be called when the close-button of a window is pressed.
|
||
//
|
||
// This is usually only needed for devices that regularly update the browse window,
|
||
// e.g. canisters, timers, etc.
|
||
//
|
||
// windowid should be the specified window name
|
||
// e.g. code is : user << browse(text, "window=fred")
|
||
// then use : onclose(user, "fred")
|
||
//
|
||
// Optionally, specify the "ref" parameter as the controlled atom (usually src)
|
||
// to pass a "close=1" parameter to the atom's Topic() proc for special handling.
|
||
// Otherwise, the user mob's machine var will be reset directly.
|
||
//
|
||
/proc/onclose(mob/user, windowid, var/atom/ref=null)
|
||
if(!user.client) return
|
||
var/param = "null"
|
||
if(ref)
|
||
param = "\ref[ref]"
|
||
|
||
winset(user, windowid, "on-close=\".windowclose [param]\"")
|
||
|
||
//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
|
||
|
||
//world << "windowclose: [atomref]"
|
||
if(atomref!="null") // if passed a real atomref
|
||
var/hsrc = locate(atomref) // find the reffed atom
|
||
var/href = "close=1"
|
||
if(hsrc)
|
||
//world << "[src] Topic [href] [hsrc]"
|
||
usr = src.mob
|
||
src.Topic(href, params2list(href), hsrc) // this will direct to the atom's
|
||
return // Topic() proc via client.Topic()
|
||
|
||
// no atomref specified (or not found)
|
||
// so just reset the user mob's machine var
|
||
if(src && src.mob)
|
||
//world << "[src] was [src.mob.machine], setting to null"
|
||
src.mob.machine = null
|
||
return
|
||
|
||
/proc/reverselist(var/list/input)
|
||
var/list/output = new/list()
|
||
for(var/A in input)
|
||
output += A
|
||
return output
|
||
|
||
/proc/get_turf_loc(var/atom/movable/M) //gets the location of the turf that the atom is on, or what the atom is in is on, etc
|
||
//in case they're in a closet or sleeper or something
|
||
var/atom/loc = M.loc
|
||
while(!istype(loc, /turf/))
|
||
loc = loc.loc
|
||
return loc
|
||
|
||
// returns the turf located at the map edge in the specified direction relative to A
|
||
// used for mass driver
|
||
/proc/get_edge_target_turf(var/atom/A, var/direction)
|
||
|
||
var/turf/target = locate(A.x, A.y, A.z)
|
||
if(!A || !target)
|
||
return 0
|
||
//since NORTHEAST == NORTH & EAST, etc, doing it this way allows for diagonal mass drivers in the future
|
||
//and isn't really any more complicated
|
||
|
||
// Note diagonal directions won't usually be accurate
|
||
if(direction & NORTH)
|
||
target = locate(target.x, world.maxy, target.z)
|
||
if(direction & SOUTH)
|
||
target = locate(target.x, 1, target.z)
|
||
if(direction & EAST)
|
||
target = locate(world.maxx, target.y, target.z)
|
||
if(direction & WEST)
|
||
target = locate(1, target.y, target.z)
|
||
|
||
return target
|
||
|
||
// returns turf relative to A in given direction at set range
|
||
// result is bounded to map size
|
||
// note range is non-pythagorean
|
||
// used for disposal system
|
||
/proc/get_ranged_target_turf(var/atom/A, var/direction, var/range)
|
||
|
||
var/x = A.x
|
||
var/y = A.y
|
||
if(direction & NORTH)
|
||
y = min(world.maxy, y + range)
|
||
if(direction & SOUTH)
|
||
y = max(1, y - range)
|
||
if(direction & EAST)
|
||
x = min(world.maxx, x + range)
|
||
if(direction & WEST)
|
||
x = max(1, x - range)
|
||
|
||
return locate(x,y,A.z)
|
||
|
||
|
||
// returns turf relative to A offset in dx and dy tiles
|
||
// bound to map limits
|
||
/proc/get_offset_target_turf(var/atom/A, var/dx, var/dy)
|
||
var/x = min(world.maxx, max(1, A.x + dx))
|
||
var/y = min(world.maxy, max(1, A.y + dy))
|
||
return locate(x,y,A.z)
|
||
|
||
/*
|
||
/proc/dir2text(var/d)
|
||
var/dir
|
||
switch(d)
|
||
if(1)
|
||
dir = "NORTH"
|
||
if(2)
|
||
dir = "SOUTH"
|
||
if(4)
|
||
dir = "EAST"
|
||
if(8)
|
||
dir = "WEST"
|
||
if(5)
|
||
dir = "NORTHEAST"
|
||
if(6)
|
||
dir = "SOUTHEAST"
|
||
if(9)
|
||
dir = "NORTHWEST"
|
||
if(10)
|
||
dir = "SOUTHWEST"
|
||
else
|
||
dir = null
|
||
return dir
|
||
*/
|
||
|
||
//Makes sure MIDDLE is between LOW and HIGH. If not, it adjusts it. Returns the adjusted value.
|
||
/proc/between(var/low, var/middle, var/high)
|
||
return max(min(middle, high), low)
|
||
|
||
proc/arctan(x)
|
||
var/y=arcsin(x/sqrt(1+x*x))
|
||
return y
|
||
|
||
//returns random gauss number
|
||
proc/GaussRand(var/sigma)
|
||
var/x,y,rsq
|
||
do
|
||
x=2*rand()-1
|
||
y=2*rand()-1
|
||
rsq=x*x+y*y
|
||
while(rsq>1 || !rsq)
|
||
return sigma*y*sqrt(-2*log(rsq)/rsq)
|
||
|
||
//returns random gauss number, rounded to 'roundto'
|
||
proc/GaussRandRound(var/sigma,var/roundto)
|
||
return round(GaussRand(sigma),roundto)
|
||
|
||
proc/anim(turf/location as turf,target as mob|obj,a_icon,a_icon_state as text,flick_anim as text,sleeptime = 0,direction as num)
|
||
//This proc throws up either an icon or an animation for a specified amount of time.
|
||
//The variables should be apparent enough.
|
||
var/atom/movable/overlay/animation = new(location)
|
||
if(direction)
|
||
animation.dir = direction
|
||
animation.icon = a_icon
|
||
animation.layer = target:layer+1
|
||
if(a_icon_state)
|
||
animation.icon_state = a_icon_state
|
||
else
|
||
animation.icon_state = "blank"
|
||
animation.master = target
|
||
flick(flick_anim, animation)
|
||
sleep(max(sleeptime, 15))
|
||
del(animation)
|
||
|
||
//returns list element or null. Should prevent "index out of bounds" error.
|
||
proc/listgetindex(var/list/list,index)
|
||
if(istype(list) && list.len)
|
||
if(isnum(index))
|
||
if(InRange(index,1,list.len))
|
||
return list[index]
|
||
else if(index in list)
|
||
return list[index]
|
||
return
|
||
|
||
proc/islist(list/list)
|
||
if(istype(list))
|
||
return 1
|
||
return 0
|
||
|
||
proc/isemptylist(list/list)
|
||
if(!list.len)
|
||
return 1
|
||
return 0
|
||
|
||
proc/clearlist(list/list)
|
||
if(istype(list))
|
||
list.len = 0
|
||
return
|
||
|
||
proc/listclearnulls(list/list)
|
||
if(istype(list))
|
||
while(null in list)
|
||
list -= null
|
||
return
|
||
|
||
/atom/proc/GetAllContents(searchDepth = 5)
|
||
var/list/toReturn = list()
|
||
|
||
for(var/atom/part in contents)
|
||
toReturn += part
|
||
if(part.contents.len && searchDepth)
|
||
toReturn += part.GetAllContents(searchDepth - 1)
|
||
|
||
return toReturn
|
||
|
||
|
||
//WIP
|
||
|
||
/*
|
||
* Returns list containing all the entries present in both lists
|
||
* If either of arguments is not a list, returns null
|
||
*/
|
||
/proc/intersectlist(var/list/first, var/list/second)
|
||
if(!islist(first) || !islist(second))
|
||
return
|
||
return first & second
|
||
|
||
/*
|
||
* Returns list containing all the entries from first list that are not present in second.
|
||
* If skiprep = 1, repeated elements are treated as one.
|
||
* If either of arguments is not a list, returns null
|
||
*/
|
||
/proc/difflist(var/list/first, var/list/second, var/skiprep=0)
|
||
if(!islist(first) || !islist(second))
|
||
return
|
||
var/list/result = new
|
||
if(skiprep)
|
||
for(var/e in first)
|
||
if(!(e in result) && !(e in second))
|
||
result += e
|
||
else
|
||
result = first - second
|
||
return result
|
||
|
||
/*
|
||
* Returns list containing entries that are in either list but not both.
|
||
* If skipref = 1, repeated elements are treated as one.
|
||
* If either of arguments is not a list, returns null
|
||
*/
|
||
/proc/uniquemergelist(var/list/first, var/list/second, var/skiprep=0)
|
||
if(!islist(first) || !islist(second))
|
||
return
|
||
var/list/result = new
|
||
if(skiprep)
|
||
result = difflist(first, second, skiprep)+difflist(second, first, skiprep)
|
||
else
|
||
result = first ^ second
|
||
return result
|
||
|
||
/proc/pick_n_take(list/listfrom)
|
||
if (listfrom.len > 0)
|
||
var/picked = pick(listfrom)
|
||
listfrom -= picked
|
||
return picked
|
||
return null
|
||
|
||
/proc/pop(list/listfrom)
|
||
if (listfrom.len > 0)
|
||
var/picked = listfrom[listfrom.len]
|
||
listfrom.len--
|
||
return picked
|
||
return null
|
||
|
||
|
||
/proc/can_see(var/atom/source, var/atom/target, var/length=5) // I couldnt be arsed to do actual raycasting :I This is horribly inaccurate.
|
||
var/turf/current = get_turf(source)
|
||
var/turf/target_turf = get_turf(target)
|
||
var/steps = 0
|
||
|
||
while(current != target_turf)
|
||
if(steps > length) return 0
|
||
if(current.opacity) return 0
|
||
for(var/atom/A in current)
|
||
if(A.opacity) return 0
|
||
current = get_step_towards(current, target_turf)
|
||
steps++
|
||
|
||
return 1
|
||
|
||
|
||
/mob/proc/get_equipped_items()
|
||
var/list/items = new/list()
|
||
|
||
if(hasvar(src,"back")) if(src:back) items += src:back
|
||
if(hasvar(src,"belt")) if(src:belt) items += src:belt
|
||
if(hasvar(src,"ears")) if(src:ears) items += src:ears
|
||
if(hasvar(src,"glasses")) if(src:glasses) items += src:glasses
|
||
if(hasvar(src,"gloves")) if(src:gloves) items += src:gloves
|
||
if(hasvar(src,"head")) if(src:head) items += src:head
|
||
if(hasvar(src,"shoes")) if(src:shoes) items += src:shoes
|
||
if(hasvar(src,"wear_id")) if(src:wear_id) items += src:wear_id
|
||
if(hasvar(src,"wear_mask")) if(src:wear_mask) items += src:wear_mask
|
||
if(hasvar(src,"wear_suit")) if(src:wear_suit) items += src:wear_suit
|
||
// if(hasvar(src,"w_radio")) if(src:w_radio) items += src:w_radio commenting this out since headsets go on your ears now PLEASE DON'T BE MAD KEELIN
|
||
if(hasvar(src,"w_uniform")) if(src:w_uniform) items += src:w_uniform
|
||
|
||
//if(hasvar(src,"l_hand")) if(src:l_hand) items += src:l_hand
|
||
//if(hasvar(src,"r_hand")) if(src:r_hand) items += src:r_hand
|
||
|
||
return items
|
||
|
||
/proc/is_blocked_turf(var/turf/T)
|
||
var/cant_pass = 0
|
||
if(T.density) cant_pass = 1
|
||
for(var/atom/A in T)
|
||
if(A.density)//&&A.anchored
|
||
cant_pass = 1
|
||
return cant_pass
|
||
|
||
/proc/get_step_towards2(var/atom/ref , var/atom/trg)
|
||
var/base_dir = get_dir(ref, get_step_towards(ref,trg))
|
||
var/turf/temp = get_step_towards(ref,trg)
|
||
|
||
if(is_blocked_turf(temp))
|
||
var/dir_alt1 = turn(base_dir, 90)
|
||
var/dir_alt2 = turn(base_dir, -90)
|
||
var/turf/turf_last1 = temp
|
||
var/turf/turf_last2 = temp
|
||
var/free_tile = null
|
||
var/breakpoint = 0
|
||
|
||
while(!free_tile && breakpoint < 10)
|
||
if(!is_blocked_turf(turf_last1))
|
||
free_tile = turf_last1
|
||
break
|
||
if(!is_blocked_turf(turf_last2))
|
||
free_tile = turf_last2
|
||
break
|
||
turf_last1 = get_step(turf_last1,dir_alt1)
|
||
turf_last2 = get_step(turf_last2,dir_alt2)
|
||
breakpoint++
|
||
|
||
if(!free_tile) return get_step(ref, base_dir)
|
||
else return get_step_towards(ref,free_tile)
|
||
|
||
else return get_step(ref, base_dir)
|
||
|
||
/proc/do_mob(var/mob/user , var/mob/target, var/time = 30) //This is quite an ugly solution but i refuse to use the old request system.
|
||
if(!user || !target) return 0
|
||
var/user_loc = user.loc
|
||
var/target_loc = target.loc
|
||
var/holding = user.equipped()
|
||
sleep(time)
|
||
if ( user.loc == user_loc && target.loc == target_loc && user.equipped() == holding && !( user.stat ) && ( !user.stunned && !user.weakened && !user.paralysis && !user.lying ) )
|
||
return 1
|
||
else
|
||
return 0
|
||
/*
|
||
/proc/do_after(mob/M as mob, time as num)
|
||
if(!M)
|
||
return 0
|
||
var/turf/T = M.loc
|
||
var/holding = M.equipped()
|
||
for(var/i=0, i<time)
|
||
if(M)
|
||
if ((M.loc == T && M.equipped() == holding && !( M.stat )))
|
||
i++
|
||
sleep(1)
|
||
else
|
||
return 0
|
||
return 1
|
||
*/
|
||
|
||
/proc/do_after(var/mob/user as mob, delay as num, var/numticks = 5) // Replacing the upper one with this one because Byond keeps feeling that the upper one is an infinate loop
|
||
if(!user || isnull(user)) // This one should have less temptation
|
||
return 0
|
||
if(numticks == 0)
|
||
return 0
|
||
|
||
var/delayfraction = delay/numticks
|
||
var/turf/T = user.loc
|
||
var/holding = user.equipped()
|
||
|
||
for(var/i = 0, i<numticks, i++)
|
||
sleep(delayfraction)
|
||
if(!src || !user || !user.canmove || !(user.loc == T) || !(user.equipped() == holding))
|
||
return 0
|
||
|
||
return 1
|
||
|
||
/proc/hasvar(var/datum/A, var/varname)
|
||
//Takes: Anything that could possibly have variables and a varname to check.
|
||
//Returns: 1 if found, 0 if not.
|
||
//Notes: Do i really need to explain this?
|
||
if(A.vars.Find(lowertext(varname))) return 1
|
||
else return 0
|
||
|
||
/proc/get_areas(var/areatype)
|
||
//Takes: Area type as text string or as typepath OR an instance of the area.
|
||
//Returns: A list of all areas of that type in the world.
|
||
//Notes: Simple!
|
||
if(!areatype) return null
|
||
if(istext(areatype)) areatype = text2path(areatype)
|
||
if(isarea(areatype))
|
||
var/area/areatemp = areatype
|
||
areatype = areatemp.type
|
||
|
||
var/list/areas = new/list()
|
||
for(var/area/N in world)
|
||
if(istype(N, areatype)) areas += N
|
||
return areas
|
||
|
||
/proc/get_area_turfs(var/areatype)
|
||
//Takes: Area type as text string or as typepath OR an instance of the area.
|
||
//Returns: A list of all turfs in areas of that type of that type in the world.
|
||
//Notes: Simple!
|
||
|
||
if(!areatype) return null
|
||
if(istext(areatype)) areatype = text2path(areatype)
|
||
if(isarea(areatype))
|
||
var/area/areatemp = areatype
|
||
areatype = areatemp.type
|
||
|
||
var/list/turfs = new/list()
|
||
for(var/area/N in world)
|
||
if(istype(N, areatype))
|
||
for(var/turf/T in N) turfs += T
|
||
return turfs
|
||
|
||
/proc/get_area_all_atoms(var/areatype)
|
||
//Takes: Area type as text string or as typepath OR an instance of the area.
|
||
//Returns: A list of all atoms (objs, turfs, mobs) in areas of that type of that type in the world.
|
||
//Notes: Simple!
|
||
|
||
if(!areatype) return null
|
||
if(istext(areatype)) areatype = text2path(areatype)
|
||
if(isarea(areatype))
|
||
var/area/areatemp = areatype
|
||
areatype = areatemp.type
|
||
|
||
var/list/atoms = new/list()
|
||
for(var/area/N in world)
|
||
if(istype(N, areatype))
|
||
for(var/atom/A in N)
|
||
atoms += A
|
||
return atoms
|
||
|
||
/datum/coords //Simple datum for storing coordinates.
|
||
var/x_pos = null
|
||
var/y_pos = null
|
||
var/z_pos = null
|
||
|
||
/area/proc/move_contents_to(var/area/A, var/turftoleave=null, var/direction = null)
|
||
//Takes: Area. Optional: turf type to leave behind.
|
||
//Returns: Nothing.
|
||
//Notes: Attempts to move the contents of one area to another area.
|
||
// Movement based on lower left corner. Tiles that do not fit
|
||
// into the new area will not be moved.
|
||
|
||
if(!A || !src) return 0
|
||
|
||
var/list/turfs_src = get_area_turfs(src.type)
|
||
var/list/turfs_trg = get_area_turfs(A.type)
|
||
|
||
var/src_min_x = 0
|
||
var/src_min_y = 0
|
||
for (var/turf/T in turfs_src)
|
||
if(T.x < src_min_x || !src_min_x) src_min_x = T.x
|
||
if(T.y < src_min_y || !src_min_y) src_min_y = T.y
|
||
|
||
var/trg_min_x = 0
|
||
var/trg_min_y = 0
|
||
for (var/turf/T in turfs_trg)
|
||
if(T.x < trg_min_x || !trg_min_x) trg_min_x = T.x
|
||
if(T.y < trg_min_y || !trg_min_y) trg_min_y = T.y
|
||
|
||
var/list/refined_src = new/list()
|
||
for(var/turf/T in turfs_src)
|
||
refined_src += T
|
||
refined_src[T] = new/datum/coords
|
||
var/datum/coords/C = refined_src[T]
|
||
C.x_pos = (T.x - src_min_x)
|
||
C.y_pos = (T.y - src_min_y)
|
||
|
||
var/list/refined_trg = new/list()
|
||
for(var/turf/T in turfs_trg)
|
||
refined_trg += T
|
||
refined_trg[T] = new/datum/coords
|
||
var/datum/coords/C = refined_trg[T]
|
||
C.x_pos = (T.x - trg_min_x)
|
||
C.y_pos = (T.y - trg_min_y)
|
||
|
||
var/list/fromupdate = new/list()
|
||
var/list/toupdate = new/list()
|
||
|
||
moving:
|
||
for (var/turf/T in refined_src)
|
||
var/datum/coords/C_src = refined_src[T]
|
||
for (var/turf/B in refined_trg)
|
||
var/datum/coords/C_trg = refined_trg[B]
|
||
if(C_src.x_pos == C_trg.x_pos && C_src.y_pos == C_trg.y_pos)
|
||
|
||
var/old_dir1 = T.dir
|
||
var/old_icon_state1 = T.icon_state
|
||
var/old_icon1 = T.icon
|
||
|
||
var/turf/X = new T.type(B)
|
||
X.dir = old_dir1
|
||
X.icon_state = old_icon_state1
|
||
X.icon = old_icon1 //Shuttle floors are in shuttle.dmi while the defaults are floors.dmi
|
||
|
||
/* Quick visual fix for some weird shuttle corner artefacts when on transit space tiles */
|
||
if(direction && findtext(X.icon_state, "swall_s"))
|
||
|
||
// Spawn a new shuttle corner object
|
||
var/obj/corner = new()
|
||
corner.loc = X
|
||
corner.density = 1
|
||
corner.anchored = 1
|
||
corner.icon = X.icon
|
||
corner.icon_state = dd_replacetext(X.icon_state, "_s", "_f")
|
||
corner.tag = "delete me"
|
||
corner.name = "wall"
|
||
|
||
// Find a new turf to take on the property of
|
||
var/turf/nextturf = get_step(corner, direction)
|
||
if(!nextturf || !istype(nextturf, /turf/space))
|
||
nextturf = get_step(corner, turn(direction, 180))
|
||
|
||
|
||
// Take on the icon of a neighboring scrolling space icon
|
||
X.icon = nextturf.icon
|
||
X.icon_state = nextturf.icon_state
|
||
|
||
|
||
for(var/obj/O in T)
|
||
|
||
// Reset the shuttle corners
|
||
if(O.tag == "delete me")
|
||
X.icon = 'shuttle.dmi'
|
||
X.icon_state = dd_replacetext(O.icon_state, "_f", "_s") // revert the turf to the old icon_state
|
||
X.name = "wall"
|
||
del(O) // prevents multiple shuttle corners from stacking
|
||
continue
|
||
if(!istype(O,/obj)) continue
|
||
O.loc = X
|
||
for(var/mob/M in T)
|
||
if(!istype(M,/mob)) continue
|
||
M.loc = X
|
||
|
||
var/area/AR = X.loc
|
||
|
||
if(AR.sd_lighting)
|
||
X.opacity = !X.opacity
|
||
X.sd_SetOpacity(!X.opacity)
|
||
|
||
toupdate += X
|
||
|
||
if(turftoleave)
|
||
var/turf/ttl = new turftoleave(T)
|
||
|
||
var/area/AR2 = ttl.loc
|
||
|
||
if(AR2.sd_lighting)
|
||
ttl.opacity = !ttl.opacity
|
||
ttl.sd_SetOpacity(!ttl.opacity)
|
||
|
||
fromupdate += ttl
|
||
|
||
else
|
||
T.ReplaceWithSpace()
|
||
|
||
refined_src -= T
|
||
refined_trg -= B
|
||
continue moving
|
||
|
||
var/list/doors = new/list()
|
||
|
||
if(toupdate.len)
|
||
for(var/turf/simulated/T1 in toupdate)
|
||
for(var/obj/machinery/door/D2 in T1)
|
||
doors += D2
|
||
if(T1.parent)
|
||
air_master.groups_to_rebuild += T1.parent
|
||
else
|
||
air_master.tiles_to_update += T1
|
||
|
||
if(fromupdate.len)
|
||
for(var/turf/simulated/T2 in fromupdate)
|
||
for(var/obj/machinery/door/D2 in T2)
|
||
doors += D2
|
||
if(T2.parent)
|
||
air_master.groups_to_rebuild += T2.parent
|
||
else
|
||
air_master.tiles_to_update += T2
|
||
|
||
for(var/obj/O in doors)
|
||
O:update_nearby_tiles(1)
|
||
|
||
|
||
|
||
proc/DuplicateObject(obj/original, var/perfectcopy = 0 , var/sameloc = 0)
|
||
if(!original)
|
||
return null
|
||
|
||
var/obj/O = null
|
||
|
||
if(sameloc)
|
||
O=new original.type(original.loc)
|
||
else
|
||
O=new original.type(locate(0,0,0))
|
||
|
||
if(perfectcopy)
|
||
if((O) && (original))
|
||
for(var/V in original.vars)
|
||
if(!(V in list("type","loc","locs","vars", "parent", "parent_type","verbs","ckey","key")))
|
||
O.vars[V] = original.vars[V]
|
||
return O
|
||
|
||
|
||
/area/proc/copy_contents_to(var/area/A , var/platingRequired = 0 )
|
||
//Takes: Area. Optional: If it should copy to areas that don't have plating
|
||
//Returns: Nothing.
|
||
//Notes: Attempts to move the contents of one area to another area.
|
||
// Movement based on lower left corner. Tiles that do not fit
|
||
// into the new area will not be moved.
|
||
|
||
if(!A || !src) return 0
|
||
|
||
var/list/turfs_src = get_area_turfs(src.type)
|
||
var/list/turfs_trg = get_area_turfs(A.type)
|
||
|
||
var/src_min_x = 0
|
||
var/src_min_y = 0
|
||
for (var/turf/T in turfs_src)
|
||
if(T.x < src_min_x || !src_min_x) src_min_x = T.x
|
||
if(T.y < src_min_y || !src_min_y) src_min_y = T.y
|
||
|
||
var/trg_min_x = 0
|
||
var/trg_min_y = 0
|
||
for (var/turf/T in turfs_trg)
|
||
if(T.x < trg_min_x || !trg_min_x) trg_min_x = T.x
|
||
if(T.y < trg_min_y || !trg_min_y) trg_min_y = T.y
|
||
|
||
var/list/refined_src = new/list()
|
||
for(var/turf/T in turfs_src)
|
||
refined_src += T
|
||
refined_src[T] = new/datum/coords
|
||
var/datum/coords/C = refined_src[T]
|
||
C.x_pos = (T.x - src_min_x)
|
||
C.y_pos = (T.y - src_min_y)
|
||
|
||
var/list/refined_trg = new/list()
|
||
for(var/turf/T in turfs_trg)
|
||
refined_trg += T
|
||
refined_trg[T] = new/datum/coords
|
||
var/datum/coords/C = refined_trg[T]
|
||
C.x_pos = (T.x - trg_min_x)
|
||
C.y_pos = (T.y - trg_min_y)
|
||
|
||
var/list/toupdate = new/list()
|
||
|
||
var/copiedobjs = list()
|
||
|
||
|
||
moving:
|
||
for (var/turf/T in refined_src)
|
||
var/datum/coords/C_src = refined_src[T]
|
||
for (var/turf/B in refined_trg)
|
||
var/datum/coords/C_trg = refined_trg[B]
|
||
if(C_src.x_pos == C_trg.x_pos && C_src.y_pos == C_trg.y_pos)
|
||
|
||
var/old_dir1 = T.dir
|
||
var/old_icon_state1 = T.icon_state
|
||
var/old_icon1 = T.icon
|
||
|
||
if(platingRequired)
|
||
if(istype(B, /turf/space))
|
||
continue moving
|
||
|
||
var/turf/X = new T.type(B)
|
||
X.dir = old_dir1
|
||
X.icon_state = old_icon_state1
|
||
X.icon = old_icon1 //Shuttle floors are in shuttle.dmi while the defaults are floors.dmi
|
||
|
||
|
||
var/list/objs = new/list()
|
||
var/list/newobjs = new/list()
|
||
var/list/mobs = new/list()
|
||
var/list/newmobs = new/list()
|
||
|
||
for(var/obj/O in T)
|
||
|
||
if(!istype(O,/obj))
|
||
continue
|
||
|
||
objs += O
|
||
|
||
|
||
for(var/obj/O in objs)
|
||
newobjs += DuplicateObject(O , 1)
|
||
|
||
|
||
for(var/obj/O in newobjs)
|
||
O.loc = X
|
||
|
||
for(var/mob/M in T)
|
||
|
||
if(!istype(M,/mob))
|
||
continue
|
||
|
||
mobs += M
|
||
|
||
for(var/mob/M in mobs)
|
||
newmobs += DuplicateObject(M , 1)
|
||
|
||
for(var/mob/M in newmobs)
|
||
M.loc = X
|
||
|
||
copiedobjs += newobjs
|
||
copiedobjs += newmobs
|
||
|
||
|
||
|
||
for(var/V in T.vars)
|
||
if(!(V in list("type","loc","locs","vars", "parent", "parent_type","verbs","ckey","key","x","y","z","contents", "luminosity", "sd_light_spill",)))
|
||
X.vars[V] = T.vars[V]
|
||
|
||
var/area/AR = X.loc
|
||
|
||
if(AR.sd_lighting)
|
||
X.opacity = !X.opacity
|
||
X.sd_SetOpacity(!X.opacity)
|
||
|
||
toupdate += X
|
||
|
||
refined_src -= T
|
||
refined_trg -= B
|
||
continue moving
|
||
|
||
|
||
|
||
|
||
var/list/doors = new/list()
|
||
|
||
if(toupdate.len)
|
||
for(var/turf/simulated/T1 in toupdate)
|
||
for(var/obj/machinery/door/D2 in T1)
|
||
doors += D2
|
||
if(T1.parent)
|
||
air_master.groups_to_rebuild += T1.parent
|
||
else
|
||
air_master.tiles_to_update += T1
|
||
|
||
for(var/obj/O in doors)
|
||
O:update_nearby_tiles(1)
|
||
|
||
|
||
|
||
|
||
return copiedobjs
|
||
|
||
|
||
|
||
|
||
|
||
|
||
proc/get_cardinal_dir(atom/A, atom/B)
|
||
var/dx = abs(B.x - A.x)
|
||
var/dy = abs(B.y - A.y)
|
||
return get_dir(A, B) & (rand() * (dx+dy) < dy ? 3 : 12)
|
||
|
||
//return either pick(list) or null if list is not of type /list or empty
|
||
proc/safepick(list/list)
|
||
if(!islist(list) || !list.len)
|
||
return
|
||
return pick(list)
|
||
|
||
//chances are 1:value. anyprob(1) will always return true
|
||
proc/anyprob(value)
|
||
return (rand(1,value)==value)
|
||
|
||
proc/view_or_range(distance = world.view , center = usr , type)
|
||
switch(type)
|
||
if("view")
|
||
. = view(distance,center)
|
||
if("range")
|
||
. = range(distance,center)
|
||
return
|
||
|
||
proc/oview_or_orange(distance = world.view , center = usr , type)
|
||
switch(type)
|
||
if("view")
|
||
. = oview(distance,center)
|
||
if("range")
|
||
. = orange(distance,center)
|
||
return
|
||
|
||
/proc/stringsplit(txt, character)
|
||
var
|
||
cur_text = txt
|
||
last_found = 1
|
||
found_char = findtext(cur_text,character)
|
||
list/list = list()
|
||
if(found_char)
|
||
var/fs = copytext(cur_text,last_found,found_char)
|
||
list += fs
|
||
last_found = found_char+length(character)
|
||
found_char = findtext(cur_text,character,last_found)
|
||
while(found_char)
|
||
var
|
||
found_string = copytext(cur_text,last_found,found_char)
|
||
last_found = found_char+length(character)
|
||
list += found_string
|
||
found_char = findtext(cur_text,character,last_found)
|
||
list += copytext(cur_text,last_found,length(cur_text)+1)
|
||
return list
|
||
|
||
/proc/stringmerge(var/text,var/compare,replace = "*")
|
||
//This proc fills in all spaces with the "replace" var (* by default) with whatever
|
||
//is in the other string at the same spot (assuming it is not a replace char).
|
||
//This is used for fingerprints
|
||
var/newtext = text
|
||
if(lentext(text) != lentext(compare))
|
||
return 0
|
||
for(var/i = 1, i < lentext(text), i++)
|
||
var/a = copytext(text,i,i+1)
|
||
var/b = copytext(compare,i,i+1)
|
||
//if it isn't both the same letter, or if they are both the replacement character
|
||
//(no way to know what it was supposed to be)
|
||
if(a != b)
|
||
if(a == replace) //if A is the replacement char
|
||
newtext = copytext(newtext,1,i) + b + copytext(newtext, i+1)
|
||
else if(b == replace) //if B is the replacement char
|
||
newtext = copytext(newtext,1,i) + a + copytext(newtext, i+1)
|
||
else //The lists disagree, Uh-oh!
|
||
return 0
|
||
return newtext
|
||
|
||
/proc/stringpercent(var/text,character = "*")
|
||
//This proc returns the number of chars of the string that is the character
|
||
//This is used for detective work to determine fingerprint completion.
|
||
if(!text || !character)
|
||
return 0
|
||
var/count = 0
|
||
for(var/i = 1, i <= lentext(text), i++)
|
||
var/a = copytext(text,i,i+1)
|
||
if(a == character)
|
||
count++
|
||
return count |