Files
CHOMPStation2/code/defines/procs/helpers.dm
n3ophyt3@gmail.com d83868e9ab Made ion storms more robust
Ion laws are now listed before all other laws, making them the kings of law priority
  AIs can have multiple Ion laws
  They are still resettable as normal

git-svn-id: http://tgstation13.googlecode.com/svn/trunk@648 316c924e-a436-60f5-8080-3fe189b3f50e
2010-12-17 16:52:57 +00:00

832 lines
21 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")
power++
goto Label_290
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++
Label_290:
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(var/t)
var/index = findtext(t, "\n")
while(index)
t = copytext(t, 1, index) + "#" + copytext(t, index+1)
index = findtext(t, "\n")
index = findtext(t, "\t")
while(index)
t = copytext(t, 1, index) + "#" + copytext(t, index+1)
index = findtext(t, "\t")
return html_encode(t)
/proc/strip_html(var/t,var/limit=MAX_MESSAGE_LEN)
t = copytext(t,1,limit)
var/index = findtext(t, "<")
while(index)
t = copytext(t, 1, index) + copytext(t, index+1)
index = findtext(t, "<")
index = findtext(t, ">")
while(index)
t = copytext(t, 1, index) + copytext(t, index+1)
index = findtext(t, ">")
return sanitize(t)
/proc/adminscrub(var/t,var/limit=MAX_MESSAGE_LEN)
t = copytext(t,1,limit)
var/index = findtext(t, "<")
while(index)
t = copytext(t, 1, index) + copytext(t, index+1)
index = findtext(t, "<")
index = findtext(t, ">")
while(index)
t = copytext(t, 1, index) + copytext(t, index+1)
index = findtext(t, ">")
return html_encode(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)
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
while(1)
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
/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/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/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/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 = pick(ai_names)
var/newname = input(M,"You are the AI. Would you like to change your name to something else?", "Name change",randomname)
if (length(newname) == 0)
newname = randomname
if (newname)
if (newname == "Inactive AI")
M << "That name is reserved."
return ainame(M)
for (var/mob/living/silicon/ai/A in world)
if (A.real_name == newname)
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/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.real_name == "Inactive AI")
continue
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/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/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/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
if (include_link && !isnull(the_mob))
if (istext(include_link))
text += "<a href=\"byond://?src=[include_link];priv_msg=\ref[the_mob]\">"
else
text += "<a href=\"byond://?src=\ref[include_link];priv_msg=\ref[the_mob]\">"
if (the_client && the_client.holder && the_client.stealth && !include_name)
text += "Administrator"
else
text += "[the_key]"
if (!isnull(include_link) && !isnull(the_mob))
text += "</a>"
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)
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/mob/M) //gets the location of the turf that the mob is on, or what the mob 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)
//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
*/