/proc/s_es(var/number as num, var/es = 0) if (isnull(number)) return if (number == 1) return null else if (es) return "es" else return "s" /proc/minmax(var/number, var/min, var/max) return max(min(number, max), min) /proc/showadminlist() usr.client.Export("##action=browse_file","config/admins.txt") /proc/showLine(var/atom/start, var/atom/end, s_icon_state = "lght", var/animate = 0, var/get_turfs=1) //Creates and shows a line object. Line object has an "affected" var that contains the cross tiles. var/angle = get_angle(start, end) var/anglemod = (-(angle < 180 ? angle : angle - 360) + 90) //What the fuck am i looking at var/lx = abs(start.x - end.x) var/ly = abs(start.y - end.y) var/beamlen = sqrt((lx * lx) + (ly * ly)) var/obj/beam_dummy/B = new/obj/beam_dummy(get_turf(start)) if(get_turfs) B.affected = castRay(start, anglemod, beamlen) B.origin_angle = angle B.icon_state = s_icon_state B.origin = start B.target = end var/matrix/M if(animate) B.transform = matrix(turn(B.transform, angle), 1, 0.1, MATRIX_SCALE) var/matrix/second = matrix(1, beamlen / 2, MATRIX_SCALE) second.Translate(1, (((beamlen / 2) * 64) / 2)) second.Turn(angle) animate(B, transform = second, time = animate, loop = 1, easing = LINEAR_EASING) else M = B.transform M.Scale(1, beamlen / 2) M.Translate(1, (((beamlen/2) * 64) / 2)) M.Turn(angle) B.transform = M return B var/global/obj/flashDummy /proc/getFlashDummy() if (!flashDummy) flashDummy = new /obj(null) flashDummy.density = 0 flashDummy.opacity = 0 flashDummy.anchored = 1 flashDummy.mouse_opacity = 0 return flashDummy /proc/arcFlashTurf(var/atom/from, var/turf/target, var/wattage) var/obj/O = getFlashDummy() O.loc = target playsound(target, "sound/effects/elec_bigzap.ogg", 30, 1) var/list/affected = DrawLine(from, O, /obj/line_obj/elec ,'icons/obj/projectiles.dmi',"WholeLghtn",1,1,"HalfStartLghtn","HalfEndLghtn",OBJ_LAYER,1,PreloadedIcon='icons/effects/LghtLine.dmi') for(var/obj/Q in affected) spawn(6) pool(Q) for(var/mob/living/M in get_turf(target)) M.shock(from, wattage, "chest", 1, 1) O.loc = null /proc/arcFlash(var/atom/from, var/atom/target, var/wattage) playsound(target, "sound/effects/elec_bigzap.ogg", 30, 1) var/list/affected = DrawLine(from, target, /obj/line_obj/elec ,'icons/obj/projectiles.dmi',"WholeLghtn",1,1,"HalfStartLghtn","HalfEndLghtn",OBJ_LAYER,1,PreloadedIcon='icons/effects/LghtLine.dmi') for(var/obj/O in affected) spawn(6) pool(O) if(wattage && istype(target, /mob/living)) //Probably unsafe. target:shock(from, wattage, "chest", 1, 1) proc/castRay(var/atom/A, var/Angle, var/Distance) //Adapted from some forum stuff. Takes some sort of bizzaro angles ?! Aahhhhh var/list/crossed = list() var/xPlus=cos(Angle) var/yPlus=sin(Angle) var/Runs=round(Distance+0.5) if(!isturf(A)) if(isturf(A.loc)) A=A.loc else return 0 for(var/v=1 to Runs) set background = 1 var/X=A.x+round((xPlus*v)+0.5) var/Y=A.y+round((yPlus*v)+0.5) var/turf/T=locate(X,Y,A.z) if(T) if(!(T in crossed)) crossed.Add(T) return crossed proc/atan2(x, y) if(!x && !y) return 0 return y >= 0 ? arccos(x / sqrt(x * x + y * y)) : -arccos(x / sqrt(x * x + y * y)) proc/get_angle(atom/a, atom/b) return atan2(b.y - a.y, b.x - a.x) /* proc // returns an angle, [0, 360), from given offsets // 0 = north, increases clockwise atan2(x, y) if(!(x || y)) return 1.#IND if(istype(x, /list) && x:len == 2) { y = x[2]; x = x[1] } return x >= 0 ? arccos(y / sqrt(x * x + y * y)) : 360 - arccos(y / sqrt(x * x + y * y)) */ //list2params without the dumb encoding /proc/list2params_noencode(var/list/L) var/strbuild = "" var/first = 1 for(var/x in L) strbuild += "[first?"":"&"][x]=[L[x]]" first = 0 return strbuild /proc/reverse_list(var/list/the_list) var/list/reverse = list() for(var/i = the_list.len, i > 0, i--) reverse.Add(the_list[i]) return reverse /turf/var/movable_area_next_type = null /turf/var/movable_area_prev_type = null /proc/get_steps(var/atom/ref, var/direction, var/numsteps) var/atom/curr = ref for(var/num=0, num world.maxx) return 0 if(A.x < 1) return 0 if(A.y > world.maxy) return 0 if(A.y < 1) return 0 if(A.density) return 0 for(var/atom/curr in A) if(curr.density) return 0 return 1 /proc/do_mob(var/mob/user , var/atom/target as turf|obj|mob, var/time = 30) //This is quite an ugly solution but i refuse to use the old request system. if(!user || !target) return 0 . = 0 var/user_loc = user.loc var/target_loc = target.loc var/holding = user.equipped() sleep(time) if (!user || !target) return 0 if ( user.loc == user_loc && target.loc == target_loc && user.equipped() == holding && !( user.stat ) && ( !user.stunned && !user.weakened && !user.paralysis && !user.lying ) ) return 1 /proc/do_after(mob/M as mob, time as num) if (!ismob(M)) return 0 . = 0 var/turf/T = M.loc var/holding = M.equipped() sleep(time) if ((M.loc == T && M.equipped() == holding && !( M.stat ))) return 1 var/list/hasvar_type_cache = list() /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) return . = 0 if(A.vars.Find(varname)) . = 1 /proc/is_blocked_turf(var/turf/T) // drsingh for cannot read null.density if (!T) return 0 . = 0 if(T.density) . = 1 for(var/atom/A in T) if(A && A.density)//&&A.anchored . = 1 /proc/get_edge_cheap(var/atom/A, var/direction) . = A.loc switch(direction) if(NORTH) . = locate(A.x, world.maxy, A.z) if(NORTHEAST) . = locate(world.maxx, world.maxy, A.z) if(EAST) . = locate(world.maxx, A.y, A.z) if(SOUTHEAST) . = locate(world.maxx, 1, A.z) if(SOUTH) . = locate(A.x, 1, A.z) if(SOUTHWEST) . = locate(1, 1, A.z) if(WEST) . = locate(1, A.y, A.z) if(NORTHWEST) . = locate(1, world.maxy, A.z) /proc/hex2num(hex) if (!( istext(hex) )) CRASH("hex2num not given a hexadecimal string argument (user error)") return . = 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") . += text2num(char) * 16 ** power if("a", "A") . += 16 ** power * 10 if("b", "B") . += 16 ** power * 11 if("c", "C") . += 16 ** power * 12 if("d", "D") . += 16 ** power * 13 if("e", "E") . += 16 ** power * 14 if("f", "F") . += 16 ** power * 15 else CRASH("hex2num given non-hexadecimal string (user error)") return power++ Label_290: i-- /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/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) . += text("[]", val) if(10.0) . += "A" if(11.0) . += "B" if(12.0) . += "C" if(13.0) . += "D" if(14.0) . += "E" if(15.0) . += "F" else power-- while(length(.) < placeholder) . = text("0[]", .) /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) . = text("#[][][]", textr, textg, textb) /proc/shuffle(var/list/shufflelist) if(!shufflelist) return . = list() var/list/old_list = shufflelist.Copy() while(old_list.len) var/item = pick(old_list) . += item if(old_list[item]) .[item] = old_list[item] old_list -= item /proc/uniquelist(var/list/L) . = list() for(var/item in L) if(!(item in .)) . += item /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 t // fuk. /proc/sanitize_noencode(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 t /proc/strip_html(var/t,var/limit=MAX_MESSAGE_LEN, var/no_fucking_autoparse = 0) t = html_decode(copytext(t,1,limit)) if (no_fucking_autoparse == 1) var/list/bad_characters = list("_", "'", "\"", "<", ">", ";", "[", "]", "{", "}", "|", "\\", "/") for(var/c in bad_characters) t = replacetext(t, c, "") 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, ">") . = sanitize(t) /proc/adminscrub(var/t,var/limit=MAX_MESSAGE_LEN) t = html_decode(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, ">") . = html_encode(t) /proc/add_zero(t, u) while (length(t) < u) t = "0[t]" . = t /proc/add_lspace(t, u) while(length(t) < u) t = " [t]" . = t /proc/add_tspace(t, u) while(length(t) < u) t = "[t] " . = 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) . = trim_left(trim_right(text)) /proc/capitalize(var/t as text) . = 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 . = 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 . = sortList(Q) /proc/mergeLists(var/list/L, var/list/R) var/Li=1 var/Ri=1 . = list() while(Li <= L.len && Ri <= R.len) if(sorttext(L[Li], R[Ri]) < 1) var/key = R[Ri] var/ass = !isnum(key) ? R[key] : null //Associative lists. (also hurf durf) . += R[Ri++] if(ass) .[key] = ass else var/key = L[Li] var/ass = !isnum(key) ? L[key] : null //Associative lists. (also hurf durf) . += L[Li++] if(ass) .[key] = ass if(Li <= L.len) . += L.Copy(Li, 0) else . += R.Copy(Ri, 0) /proc/dd_file2list(file_path, separator) if(separator == null) separator = "\n" if(isfile(file_path)) . = file_path else . = file(file_path) . = splittext(file2text(.), separator) /proc/dd_range(var/low, var/high, var/num) . = max(low,min(high,num)) /* /proc/dd_replacetext(text, search_string, replacement_string) . = splittext(text, search_string) . = dd_list2text(., replacement_string) /proc/dd_replaceText(text, search_string, replacement_string) . = splittext(text, search_string) . = dd_list2text(., replacement_string) */ /proc/dd_hasprefix(text, prefix) var/start = 1 var/end = length(prefix) + 1 . = findtext(text, prefix, start, end) /proc/dd_hasPrefix(text, prefix) var/start = 1 var/end = length(prefix) + 1 . = findtext(text, prefix, start, end) //was findtextEx /proc/dd_hassuffix(text, suffix) var/start = length(text) - length(suffix) if(start) . = findtext(text, suffix, start, null) /proc/dd_hasSuffix(text, suffix) var/start = length(text) - length(suffix) if(start) . = 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 /proc/dd_list2text(var/list/the_list, separator) var/total = the_list.len if(!total) return var/count = 2 . = "[the_list[1]]" while(count <= total) if(separator) . += separator . += "[the_list[count]]" count++ */ /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) . = length(message) if(. == length) .= message else if(. > length) .= copytext(message, 1, length + 1) else var/delta = length - . if(delta == 1) . = message + " " else if(delta % 2) . = " " + message delta-- var/spaces = add_lspace("",delta/2-1) . = spaces + . + spaces /proc/dd_limittext(message, length) var/size = length(message) if(size <= length) . = message else .= copytext(message, 1, length + 1) /proc/angle2dir(var/degree) . = NORTH|WEST degree = ((degree+22.5)%365) if(degree < 45) . = NORTH else if(degree < 90) . = NORTH|EAST else if(degree < 135) . = EAST else if(degree < 180) . = SOUTH|EAST else if(degree < 225) . = SOUTH else if(degree < 270) . = SOUTH|WEST else if(degree < 315) . = WEST /proc/angle2text(var/degree) . = dir2text(angle2dir(degree)) /proc/text_input(var/Message, var/Title, var/Default, var/length=MAX_MESSAGE_LEN) . = sanitize(input(Message, Title, Default) as text, length) /proc/scrubbed_input(var/user, var/Message, var/Title, var/Default, var/length=MAX_MESSAGE_LEN) . = strip_html(input(user, Message, Title, Default) as null|text, length) /proc/InRange(var/A, var/lower, var/upper) if(A < lower || A > upper) . = 0 else . = 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! . = 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 . = 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) //Every dyabs steps, step once in y direction y-=dxabs py+=sdy px+=sdx //Step on in x direction . += locate(px,py,M.z)//Add the turf to the list else for(j=0;j=dyabs) x-=dyabs px+=sdx py+=sdy . += locate(px,py,M.z) /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 != 87 && (ch < 48 || ch > 57)) // 87 is W for webclients... spec changed. return 0 return 1 /proc/sanitize_frequency(var/f) . = round(f) . = max(1441, .) // 144.1 . = min(1489, .) // 148.9 if ((. % 2) == 0) . += 1 /proc/format_frequency(var/f) . = "[round(f / 10)].[f % 10]" /proc/sortmobs() var/list/mob_list = list() for(var/mob/living/silicon/ai/M in mobs) mob_list.Add(M) for(var/mob/living/silicon/robot/M in mobs) mob_list.Add(M) for(var/mob/living/silicon/hivebot/M in mobs) mob_list.Add(M) for(var/mob/living/silicon/hive_mainframe/M in mobs) mob_list.Add(M) for(var/mob/living/carbon/human/M in mobs) mob_list.Add(M) for(var/mob/living/critter/C in mobs) if(C.client) mob_list.Add(C) for(var/mob/wraith/M in mobs) mob_list.Add(M) for(var/mob/living/intangible/blob_overmind/M in mobs) mob_list.Add(M) // for(var/mob/living/carbon/alien/M in mobs) // mob_list.Add(M) for(var/mob/dead/observer/M in mobs) mob_list.Add(M) for(var/mob/dead/target_observer/M in mobs) mob_list.Add(M) for(var/mob/new_player/M in mobs) mob_list.Add(M) for(var/mob/living/carbon/wall/M in mobs) 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 //Include details shows traitor status etc //Admins replaces the src ref for links with a placeholder for message_admins //Mentor just changes the private message link /proc/key_name(var/whom, var/include_details = 1, var/admins = 1, var/mentor = 0) 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)) if (istype(whom, /datum/mind)) var/datum/mind/the_mind = whom the_mob = the_mind.current the_key = the_mind.key the_client = the_mind.current ? the_mind.current.client : null if (!the_client && the_key) for (var/client/C in clients) if (C.key == the_key || C.ckey == the_key) the_client = C break else var/datum/the_datum = whom return "*invalid:[the_datum.type]*" else //It's probably just a text string. We're ok with that. for (var/client/C in clients) if (C.key == whom || C.ckey == whom) the_client = C the_key = C.key if (C.mob) the_mob = C.mob break if (C.mob && C.mob.real_name == whom) the_client = C the_key = C.key the_mob = C.mob break var/text = "" if (!the_key) text += "*no client*" else if (!isnull(the_mob)) if(!mentor) text += "" else text += "" if (the_client) if (the_client.holder && the_client.stealth && !include_details) text += "Administrator" else text += "[the_key]" else text += "[the_key] *no client*" if (!isnull(the_mob)) text += "" //Show details for players if (include_details) if (!isnull(the_mob)) text += "/" if (the_mob.real_name) text += the_mob.real_name else if (the_mob.name) text += the_mob.name text += " " if (the_client && !the_client.holder) //only show this stuff for non-admins because admins do a lot of shit while dead and it is unnecessary to show it if (checktraitor(the_mob)) text += "\[T\] " if (the_mob.stat == 2) text += "\[DEAD\] " var/linkSrc if (admins) linkSrc = "%admin_ref%" else linkSrc = "\ref[usr.client.holder]" text += "" return text // 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/datum/ref=null) var/param = "null" if(ref) param = "\ref[ref]" if (user && user.client) winset(user, windowid, "on-close=\".windowclose [param]\"") //boutput(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 //boutput(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) //boutput(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) //boutput(world, "[src] was [src.mob.machine], setting to null") if(src.mob.machine && istype(src.mob.machine, /obj/machinery)) src.mob.machine.current_user = null src.mob.machine = null return /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 if (!M) return null var/atom/loc = M.loc while(!istype(loc, /turf/)) if (!loc) break 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 /proc/CompareList(var/list/biglist, var/list/smalllist) for(var/a in smalllist) if(a in biglist) else return 0 return 1 // 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) // extends pick() to associated lists /proc/alist_pick(var/list/L) if(!L || !L.len) return null return L[pick(L)] /proc/ran_zone(zone, probability) if (probability == null) probability = 90 if (probability == 100) return zone switch(zone) if("chest") if (prob(probability)) return "chest" else var/t = rand(1, 9) if (t < 3) return "head" else if (t < 6) return "l_arm" else if (t < 9) return "r_arm" else return "chest" if("head") if (prob(probability * 0.75)) return "head" else if (prob(60)) return "chest" else return "head" if("l_arm") if (prob(probability * 0.75)) return "l_arm" else if (prob(60)) return "chest" else return "l_arm" if("r_arm") if (prob(probability * 0.75)) return "r_arm" else if (prob(60)) return "chest" else return "r_arm" if("r_leg") if (prob(probability * 0.75)) return "r_leg" else if (prob(60)) return "chest" else return "r_leg" if("l_leg") if (prob(probability * 0.75)) return "l_leg" else if (prob(60)) return "chest" else return "l_leg" return /proc/stars(n, pr) if (pr == null) pr = 25 if (pr <= 0) return null else if (pr >= 100) return n var/te = n var/t = "" n = length(n) var/p = null p = 1 while(p <= n) if ((copytext(te, p, p + 1) == " " || prob(pr))) t = text("[][]", t, copytext(te, p, p + 1)) else t = text("[]*", t) p++ return t /proc/stutter(n) var/te = html_decode(n) var/t = "" n = length(n) var/p = null p = 1 while(p <= n) var/n_letter = copytext(te, p, p + 1) if (prob(80)) if (prob(10)) n_letter = text("[n_letter][n_letter][n_letter][n_letter]") else if (prob(20)) n_letter = text("[n_letter][n_letter][n_letter]") else if (prob(5)) n_letter = null else n_letter = text("[n_letter][n_letter]") t = text("[t][n_letter]") p++ return copytext(sanitize(t),1,MAX_MESSAGE_LEN) /proc/shake_camera(mob/M, duration, strength=1, delay=0.2) spawn(1) if(!M || !M.client || M.shakecamera) return M.shakecamera = 1 var/x var/str = strength for(x=0; x abs (dx)) //slope is above 1:1 (move horizontally in a tie) if(dy > 0) return get_step(start, SOUTH) else return get_step(start, NORTH) else if(dx > 0) return get_step(start, WEST) else return get_step(start, EAST) /proc/parse_zone(zone) if (zone == "l_arm") return "left arm" else if (zone == "r_arm") return "right arm" else if (zone == "l_leg") return "left leg" else if (zone == "r_leg") return "right leg" else return zone /proc/text2dir(direction) switch(uppertext(direction)) if("NORTH") return NORTH if("SOUTH") return SOUTH if("EAST") return EAST if("WEST") return WEST if("NORTHEAST") return NORTHEAST if("NORTHWEST") return NORTHWEST if("SOUTHEAST") return SOUTHEAST if("SOUTHWEST") return SOUTHWEST else return /proc/get_turf(turf/location as turf) while (location) if (istype(location, /turf)) return location location = location.loc return null /proc/dir2text(direction) switch(direction) if(NORTH) return "north" if(SOUTH) return "south" if(EAST) return "east" if(WEST) return "west" if(NORTHEAST) return "northeast" if(SOUTHEAST) return "southeast" if(NORTHWEST) return "northwest" if(SOUTHWEST) return "southwest" else return // Marquesas: added an extra parameter to fix issue with changeling. // Unfortunately, it has to be this extra parameter, otherwise the spawn(0) in the mob say will // cause the mob's name to revert from the one it acquired for mimic voice. /obj/proc/hear_talk(mob/M as mob, text, real_name) /*var/mob/mo = locate(/mob) in src if(mo) var/heardname = M.name if (real_name) heardname = real_name var/rendered = "[heardname]: [text]" mo.show_message(rendered, 2)*/ return /proc/ishex(hex) if (!( istext(hex) )) return 0 hex = lowertext(hex) var/list/hex_list = list("0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f") var/i = null i = length(hex) while(i > 0) var/char = copytext(hex, i, i + 1) if(!(char in hex_list)) return 0 i-- return 1 /proc/format_username(var/playerName) if (!playerName) return "Unknown" var/list/name_temp = splittext(playerName, " ") if (!name_temp.len) playerName = "Unknown" else if (name_temp.len == 1) playerName = name_temp[1] else //Ex: John Smith becomes JSmith playerName = copytext( ( copytext(name_temp[1],1, 2) + name_temp[name_temp.len] ), 1, 16) return lowertext(replacetext(playerName, "/", null)) /proc/engineering_notation(var/value=0 as num) if (!value) return "0 " var/suffix = "" var/power = round( log(10, value) / 3) switch (power) if (-8) suffix = "y" if (-7) suffix = "z" if (-6) suffix = "a" if (-5) suffix = "f" if (-4) suffix = "p" if (-3) suffix = "n" if (-2) suffix = "μ" if (-1) suffix = "m" if (1) suffix = "k" if (2) suffix = "M" if (3) suffix = "G" if (4) suffix = "T" if (5) suffix = "P" if (6) suffix = "E" if (7) suffix = "Z" if (8) suffix = "Y" value = round( (value / (10 ** (3 * power))), 0.001 ) return "[value] [suffix]" /proc/obj_loc_chain(var/atom/movable/whose) if (isturf(whose) || isarea(whose)) return list() if (isturf(whose.loc)) return list() var/list/chain = list() var/atom/movable/M = whose while (ismob(M.loc) || isobj(M.loc)) chain += M.loc M = M.loc return chain /proc/all_hearers(var/range,var/centre) . = list() for(var/atom/A in view(range,centre)) if (ismob(A)) . += A if (isobj(A) || ismob(A)) for(var/mob/M in A.contents) . += M /proc/all_viewers(var/range,var/centre) . = list() for(var/atom/A in view(range,centre)) if (ismob(A)) . += A else if (isobj(A)) for(var/mob/M in A.contents) . += M /proc/all_range(var/range,var/centre) //above two are blocked by opaque objects . = list() for(var/atom/A in range(range,centre)) if (ismob(A)) . += A else if (isobj(A)) for(var/mob/M in A.contents) . += M /proc/all_view(var/range,var/centre) . = view(range,centre) for(var/obj/O in .) for(var/mob/M in O.contents) . += M proc/pickweight(list/L) // make this global var/total = 0 var/item for(item in L) if(!L[item]) L[item] = 1 // if we didn't set a weight, call it 1 total += L[item] total=rand(1, total) for(item in L) total-=L[item] if(total <= 0) return item return null // this should never happen, but it's a fallback /* /proc/shuffle(list/shuffle) for(var/i = 1, i <= shuffle.len, i++) var/pos = rand(1,shuffle.len) var/temp = shuffle[pos] shuffle[pos] = shuffle[i] shuffle[i] = temp return shuffle */ /proc/weightedprob(choices[], weights[]) if(!choices || !weights) return null //Build a range of weights var/max_num = 0 for(var/X in weights) if(isnum(X)) max_num += X //Now roll in the range. var/weighted_num = rand(1,max_num) var/running_total, i //Loop through all possible choices for(i = 1; i <= choices.len; i++) if(i > weights.len) return null running_total += weights[i] //Once the current step is less than the roll, // we have our winner. if(weighted_num <= running_total) return choices[i] return /* Get the highest ancestor of this object in the tree that is an immediate child of a given ancestor. Usage: var/datum/fart/sassy/F = new get_top_parent(F, /datum) //returns a path to /datum/fart */ /proc/get_top_ancestor(var/datum/object, var/ancestor_of_ancestor=/datum) if(!object || !ancestor_of_ancestor) CRASH("Null value parameters in get top ancestor.") if(!ispath(ancestor_of_ancestor)) CRASH("Non-Path ancestor of ancestor parameter supplied.") var/stringancestor = "[ancestor_of_ancestor]" var/stringtype = "[object.type]" var/ancestorposition = findtextEx(stringtype, stringancestor) if(!ancestorposition) return null var/parentstart = ancestorposition + length(stringancestor) + 1 var/parentend = findtextEx(stringtype, "/", parentstart) var/stringtarget = copytext(stringtype, 1, parentend ? parentend : 0) return text2path(stringtarget) /proc/check_facing(var/atom/A, var/atom/B) //////////// safety checks if(!A || !B) return 0 if(A == B) return 0 //////////// if(turn(A.dir,180) == B.dir) //boutput(world, "[A] direction is [A.dir], [B] direction is [B.dir], [A] and [B] are facing eachother") return 1 // they are facing eachother else if(A.dir == B.dir) //boutput(world, "[A] direction is [A.dir], [B] direction is [B.dir], [A] and [B] are facing the same direction") return 2 // they are facing the same direction, else //boutput(world, "[A] direction is [A.dir], [B] direction is [B.dir], [A] and [B] are facing sides") return 3 // they are facing sides //Returns a list of minds that are some type of antagonist role //This may be a stop gap until a better solution can be figured out /proc/get_all_enemies() if(ticker && ticker.mode && ticker.current_state >= GAME_STATE_PLAYING) var/datum/mind/enemies[] = new() var/datum/mind/someEnemies[] = new() //We gotta loop through the modes because someone thought it was a good idea to create new lists for all of them if (istype(ticker.mode, /datum/game_mode/revolution)) someEnemies = ticker.mode:head_revolutionaries for(var/datum/mind/M in someEnemies) if (M.current) enemies += M someEnemies = ticker.mode:revolutionaries for(var/datum/mind/M in someEnemies) if (M.current) enemies += M someEnemies = ticker.mode:get_all_heads() for(var/datum/mind/M in someEnemies) if (M.current) enemies += M else if (istype(ticker.mode, /datum/game_mode/nuclear)) someEnemies = ticker.mode:syndicates for(var/datum/mind/M in someEnemies) if (M.current) enemies += M else if (istype(ticker.mode, /datum/game_mode/spy)) someEnemies = ticker.mode:spies for(var/datum/mind/M in someEnemies) if (M.current) enemies += M someEnemies = ticker.mode:leaders for(var/datum/mind/M in someEnemies) if (M.current) enemies += M else if (istype(ticker.mode, /datum/game_mode/gang)) someEnemies = ticker.mode:leaders for(var/datum/mind/M in someEnemies) if (M.current) enemies += M for(var/datum/mind/G in M.gang.members) //This may be fucked. Dunno how these are stored. enemies += G //Lists we grab regardless of game type //Traitors list is populated during traitor or mixed rounds, however it is created along with the game_mode datum unlike the rest of the lists someEnemies = ticker.mode.traitors for(var/datum/mind/M in someEnemies) if (M.current) enemies += M //Sometimes admins assign traitors, this contains those dudes someEnemies = ticker.mode.Agimmicks for(var/datum/mind/M in someEnemies) if (M.current) enemies += M return enemies else return 0 /proc/GetRedPart(hex) hex = uppertext(hex) var hi = text2ascii(hex, 2) lo = text2ascii(hex, 3) return ( ((hi >= 65 ? hi-55 : hi-48)<<4) | (lo >= 65 ? lo-55 : lo-48) ) /proc/GetGreenPart(hex) hex = uppertext(hex) var hi = text2ascii(hex, 4) lo = text2ascii(hex, 5) return ( ((hi >= 65 ? hi-55 : hi-48)<<4) | (lo >= 65 ? lo-55 : lo-48) ) /proc/GetBluePart(hex) hex = uppertext(hex) var hi = text2ascii(hex, 6) lo = text2ascii(hex, 7) return ( ((hi >= 65 ? hi-55 : hi-48)<<4) | (lo >= 65 ? lo-55 : lo-48) ) /proc/GetColors(hex) hex = uppertext(hex) var hi1 = text2ascii(hex, 2) lo1 = text2ascii(hex, 3) hi2 = text2ascii(hex, 4) lo2 = text2ascii(hex, 5) hi3 = text2ascii(hex, 6) lo3 = text2ascii(hex, 7) return list(((hi1>= 65 ? hi1-55 : hi1-48)<<4) | (lo1 >= 65 ? lo1-55 : lo1-48), ((hi2 >= 65 ? hi2-55 : hi2-48)<<4) | (lo2 >= 65 ? lo2-55 : lo2-48), ((hi3 >= 65 ? hi3-55 : hi3-48)<<4) | (lo3 >= 65 ? lo3-55 : lo3-48)) //Shoves a jump to link or whatever in the thing :effort: /proc/showCoords(x, y, z, plaintext, holder) var text if (plaintext) text += "[x], [y], [z]" else text += "[x],[y],[z]" return text // hi I'm haine -throws more crap onto the pile- /proc/rand_deci(var/num1 = 1, var/num2 = 0, var/num3 = 2, var/num4 = 0) // input num1.num2 and num3.num4 returns a random number between them var/output = text2num("[rand(num1, num2)].[rand(num3, num4)]") return output var/list/easing_types = list( "Linear/0" = LINEAR_EASING, "Sine/1" = SINE_EASING, "Circular/2" = CIRCULAR_EASING, "Cubic/3" = CUBIC_EASING, "Bounce/4" = BOUNCE_EASING, "Elastic/5" = ELASTIC_EASING, "Back/6" = BACK_EASING) var/list/blend_types = list( "Default/0" = BLEND_DEFAULT, "Overlay/1" = BLEND_OVERLAY, "Add/2" = BLEND_ADD, "Subtract/3" = BLEND_SUBTRACT, "Multipy/4" = BLEND_MULTIPLY) var/list/hexpick = list("0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F") var/list/all_functional_reagent_ids = list() proc/get_all_functional_reagent_ids() var/datum/reagent/Read = null for (var/X in typesof(/datum/reagent)) Read = new X all_functional_reagent_ids += Read.id spawn(0) del Read proc/reagent_id_to_name(var/reagent_id) if (!reagent_id || !reagents_cache.len) return var/datum/reagent/R = reagents_cache[reagent_id] if (!R) return "nothing" else return R.name proc/GetOppositeDirection(var/X) if (!isnum(X)) return 1 switch(X) if(1) return 2 if(2) return 1 if(4) return 8 if(8) return 4 if(5) return 10 if(6) return 9 if(9) return 6 if(10) return 5 proc/GetOppositeHorizontalDirection(var/X) if (!isnum(X)) return 1 switch(X) if(4) return 8 if(8) return 4 if(5) return 9 if(6) return 10 if(9) return 5 if(10) return 6 proc/GetOppositeVerticalDirection(var/X) if (!isnum(X)) return 1 switch(X) if(1) return 2 if(2) return 1 if(5) return 6 if(6) return 5 if(9) return 10 if(10) return 9 proc/RarityClassRoll(var/scalemax = 100, var/mod = 0, var/list/category_boundaries) if (!isnum(scalemax) || scalemax <= 0) return 0 if (!isnum(mod)) return 0 if (category_boundaries.len <= 0) return 0 var/picker = rand(1,scalemax) picker += mod var/list_counter = category_boundaries.len for (var/X in category_boundaries) if (!isnum(X)) return 1 if (picker >= X) return list_counter + 1 list_counter-- return 1 /proc/circular_range(var/atom/A,var/size) if (!A || !isnum(size) || size <= 0) return list() var/list/turfs = list() var/turf/center = get_turf(A) var/corner_range = round(size * 1.5) var/total_distance = 0 var/current_range = 0 while (current_range < size - 1) current_range++ total_distance = 0 for (var/turf/T in range(size,center)) if (get_dist(T,center) == current_range) total_distance = abs(center.x - T.x) + abs(center.y - T.y) + (current_range / 2) if (total_distance > corner_range) continue turfs += T return turfs /proc/get_x_percentage_of_y(var/x,var/y) if (!isnum(x) || !isnum(y) || x == 0 || y == 0) return 0 return (x / y) * 100 /proc/get_damage_after_percentage_based_armor_reduction(var/armor,var/damage) if (!isnum(armor) || !isnum(damage) || damage <= 0) return 0 // [13:22] it would be ( (100 - armorpercentage) / 100 ) * damageamount armor = max(0,min(armor,100)) return ((100 - armor) / 100) * damage /proc/get_filtered_atoms_in_touch_range(var/atom/center,var/filter) if (!center) return list() var/list/list_to_return = list() var/target_loc = get_turf(center) for(var/atom/A in range(1,target_loc)) if (ispath(filter)) if (istype(A,filter)) list_to_return += A else list_to_return += A for(var/atom/B in center.contents) if (ispath(filter)) if (istype(B,filter)) list_to_return += B else list_to_return += B return list_to_return /proc/random_color_hex() var/string_to_return = "#" var/loops = 6 while (loops > 0) loops-- string_to_return += pick(hexpick) //boutput(world, "random color hex returning [string_to_return]") return string_to_return /proc/is_valid_color_string(var/string) if (!istext(string)) return 0 if (length(string) != 7) return 0 if (copytext(string,1,2) != "#") return 0 if (!ishex(copytext(string,2,8))) return 0 return 1 /proc/get_digit_from_number(var/number,var/slot) // note this works "backwards", so slot 1 of 52964 would be 4, not 5 if(!isnum(number)) return 0 var/string = num2text(number) string = reverse_text(string) return text2num(copytext(string,slot,slot+1)) /proc/o_clock_time() var/get_hour = text2num(time2text(world.timeofday, "hh")) var/final_hour = get_hour if (get_hour > 12) final_hour = (get_hour - 12) var/get_minutes = text2num(time2text(world.timeofday, "mm")) var/final_minutes = "[get_english_num(get_minutes)] minutes past " switch (get_minutes) if (0) final_minutes = "" if (1) final_minutes = "[get_english_num(get_minutes)] minute past " if (15) final_minutes = "quarter past " if (30) final_minutes = "half past " if (45) if (get_hour > 12) final_hour = (get_hour - 11) else final_hour = (get_hour + 1) final_minutes = "quarter 'til " var/the_time = "[final_minutes][get_english_num(final_hour)] o'clock" return the_time /proc/antag_token_list() //List of all players redeeming antagonist tokens var/list/token_list = list() for(var/mob/new_player/player in mobs) if((player.client) && (player.ready) && ((player.client.using_antag_token))) token_list += player.mind if (!token_list.len) return 0 else return token_list /proc/strip_bad_characters(var/text) var/list/bad_characters = list("_", "'", "\"", "<", ">", ";", "[", "]", "{", "}", "|", "\\", "/") for(var/c in bad_characters) text = replacetext(text, c, " ") return text var/list/english_num = list("0" = "zero", "1" = "one", "2" = "two", "3" = "three", "4" = "four", "5" = "five", "6" = "six", "7" = "seven", "8" = "eight", "9" = "nine",\ "10" = "ten", "11" = "eleven", "12" = "twelve", "13" = "thirteen", "14" = "fourteen", "15" = "fifteen", "16" = "sixteen", "17" = "seventeen", "18" = "eighteen", "19" = "nineteen",\ "20" = "twenty", "30" = "thirty", "40" = "forty", "50" = "fifty", "60" = "sixty", "70" = "seventy", "80" = "eighty", "90" = "ninety") /proc/get_english_num(var/num, var/sep) // can only do up to 999,999 because of scientific notation kicking in after 6 digits if (!num || !english_num.len) return DEBUG_MESSAGE("get_english_num recieves num \"[num]\"") if (istext(num)) num = text2num(num) var/num_return = null if (num == 0) // 0 num_return = "[english_num["[num]"]]" else if ((num >= 1) && (num <= 20)) // 1 to 20 num_return = "[english_num["[num]"]]" else if ((num > 20) && (num < 100)) // 21 to 99 var/tens = text2num(copytext("[num]", 1, 2)) * 10 var/ones = text2num(copytext("[num]", 2)) if (ones <= 0) num_return = "[english_num["[tens]"]]" else num_return = "[english_num["[tens]"]][sep ? sep : " "][english_num["[ones]"]]" else if ((num >= 100) && (num < 1000)) // 100 to 999 var/hundreds = text2num(copytext("[num]", 1, 2)) var/tens = text2num(copytext("[num]", 2)) if (tens <= 0) num_return = "[english_num["[hundreds]"]] hundred" else num_return = "[english_num["[hundreds]"]] hundred and [get_english_num(tens)]" else if ((num >= 1000) && (num < 1000000)) // 1,000 to 999,999 var/thousands = null var/hundreds = null switch (num) if (1000 to 9999) thousands = text2num(copytext("[num]", 1, 2)) hundreds = text2num(copytext("[num]", 2)) if (10000 to 999999) thousands = text2num(copytext("[num]", 1, 3)) hundreds = text2num(copytext("[num]", 3)) if (100000 to 999999) thousands = text2num(copytext("[num]", 1, 4)) hundreds = text2num(copytext("[num]", 4)) if (hundreds <= 0) num_return = "[get_english_num(thousands)] thousand" else if (hundreds < 100) num_return = "[get_english_num(thousands)] thousand and [get_english_num(hundreds)]" else num_return = "[get_english_num(thousands)] thousand, [get_english_num(hundreds)]" if (num_return) DEBUG_MESSAGE("get_english_num returns num \"[num_return]\"") return num_return /proc/mutual_attach(var/atom/A as turf|obj|mob, var/atom/B as turf|obj|mob) if (!A || !B) return if (A:anchored || B:anchored) A:anchored = 1 B:anchored = 1 if (!(B in A.attached_objs)) A.attached_objs.Add(B) if (!(A in B.attached_objs)) B.attached_objs.Add(A) /proc/mutual_detach(var/atom/A as turf|obj|mob, var/atom/B as turf|obj|mob) if (!A || !B) return A:anchored = initial(A:anchored) B:anchored = initial(B:anchored) if (B in A.attached_objs) A.attached_objs.Remove(B) if (A in B.attached_objs) B.attached_objs.Remove(A) /proc/hex2rgb(var/hex) if (!hex) return var/adj = 0 if (copytext(hex, 1, 2) == "#") adj = 1 var/hR = hex2num(copytext(hex, 1 + adj, 3 + adj)) var/hG = hex2num(copytext(hex, 3 + adj, 5 + adj)) var/hB = hex2num(copytext(hex, 5 + adj, 7 + adj)) //DEBUG_MESSAGE("hex2rgb translates [hex] to r[hR] g[hG] b[hB]") return rgb(hR,hG,hB,255) // This function counts a passed job. proc/countJob(rank) var/jobCount = 0 for(var/mob/H in mobs) if(H.mind && H.mind.assigned_role == rank) jobCount++ sleep(-1) return jobCount //alldirs = list(NORTH, SOUTH, EAST, WEST, NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST) /atom/proc/letter_overlay(var/letter as text, var/lcolor, var/dir) if (!letter) // you get something random you shithead letter = pick("A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z") if (!dir) dir = NORTHEAST if (!lcolor) lcolor = rgb(rand(0,255),rand(0,255),rand(0,255)) var/image/B = image('icons/effects/letter_overlay.dmi', loc = src, icon_state = "[letter]2") var/image/L = image('icons/effects/letter_overlay.dmi', loc = src, icon_state = letter) B.color = lcolor var/px = 0 var/py = 0 if (dir & (EAST | WEST)) px = 11 if (dir & WEST) px *= -1 if (dir & (NORTH | SOUTH)) py = 11 if (dir & SOUTH) py *= -1 B.pixel_x = px L.pixel_x = px B.pixel_y = py L.pixel_y = py src.overlays += B src.overlays += L return /atom/proc/debug_loverlay() var/letter = input(usr, "Please select a letter icon to display.", "Select Letter", "A") as null|anything in list("A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z") if (!letter) return var/lcolor = input(usr, "Please enter a color for the icon.", "Input Color", "#FFFFFF") as null|text if (!lcolor) return var/dir = input(usr, "Please select a direction for the icon to display.", "Select Direction", "NORTHEAST") as null|anything in list("NORTH", "SOUTH", "EAST", "WEST", "NORTHEAST", "NORTHWEST", "SOUTHEAST", "SOUTHWEST") if (!dir) return src.letter_overlay(letter, lcolor, text2dir(dir)) // Returns a list of eligible dead players to be respawned as an antagonist or whatever (Convair880). // Text messages: 1: alert | 2: alert (chatbox) | 3: alert acknowledged (chatbox) | 4: no longer eligible (chatbox) | 5: waited too long (chatbox) /proc/dead_player_list(var/return_minds = 0, var/confirmation_spawn = 0, var/list/text_messages = list()) var/list/candidates = list() // Confirmation delay specified, so prompt eligible dead mobs and wait for response. if (confirmation_spawn > 0) var/ghost_timestamp = world.time // Preliminary work. var/text_alert = "Would you like to be respawned? Your name will be added to the list of eligible candidates and may be selected at random by the game." var/text_chat_alert = "You are eligible to be respawned. You have [confirmation_spawn / 10] seconds to respond to the offer." var/text_chat_added = "You have been added to the list of eligible candidates. The game will pick a player soon. Good luck!" var/text_chat_failed = "You are no longer eligible for the offer." var/text_chat_toolate = "You have waited too long to respond to the offer." if (text_messages.len) if (text_messages.len >= 1) text_alert = text_messages[1] if (text_messages.len >= 2) text_chat_alert = text_messages[2] if (text_messages.len >= 3) text_chat_added = text_messages[3] if (text_messages.len >= 4) text_chat_failed = text_messages[4] if (text_messages.len >= 5) text_chat_toolate = text_messages[5] text_alert = strip_html(text_alert, MAX_MESSAGE_LEN, 1) text_chat_alert = "

[strip_html(text_chat_alert, MAX_MESSAGE_LEN)]

" text_chat_added = "

[strip_html(text_chat_added, MAX_MESSAGE_LEN)]

" text_chat_failed = "[strip_html(text_chat_failed, MAX_MESSAGE_LEN)]" text_chat_toolate = "[strip_html(text_chat_toolate, MAX_MESSAGE_LEN)]" // Run prompts. Minds are preferable to mob references because of the confirmation delay. for (var/datum/mind/M in ticker.minds) if (M.current) if (dead_player_list_helper(M.current) != 1) continue spawn (0) // Don't lock up the entire proc. M.current << csound("sound/misc/klaxon.ogg") boutput(M.current, text_chat_alert) if (alert(M.current, text_alert, "Respawn", "Yes", "No") == "Yes") if (ghost_timestamp && world.time > ghost_timestamp + confirmation_spawn) if (M.current) boutput(M.current, text_chat_toolate) return if (dead_player_list_helper(M.current) != 1) if (M.current) boutput(M.current, text_chat_failed) return if (M.current && !(M in candidates)) boutput(M.current, text_chat_added) candidates.Add(M) else return while (ghost_timestamp && world.time < ghost_timestamp + confirmation_spawn) sleep (300) // Filter list again. if (candidates.len) for (var/datum/mind/M2 in candidates) if (!M2.current || !ismob(M2.current) || dead_player_list_helper(M2.current) != 1) candidates.Remove(M2) continue if (candidates.len) if (return_minds == 1) return candidates else var/list/mob/mobs = list() for (var/datum/mind/M3 in candidates) if (M3.current && ismob(M3.current)) if (!(M3.current in mobs)) mobs.Add(M3.current) if (mobs.len) return mobs else return list() else return list() else return list() // Confirmationd delay not specified, return list right away. candidates = list() for (var/mob/O in mobs) if (dead_player_list_helper(O) != 1) continue if (!(O in candidates)) candidates.Add(O) if (return_minds == 1) var/list/datum/mind/minds = list() for (var/mob/M2 in candidates) if (M2.mind && !(M2.mind in minds)) minds.Add(M2.mind) if (minds.len > 0) return minds else return list() if (!candidates.len) return list() else return candidates // So there aren't multiple instances of C&P code (Convair880). /proc/dead_player_list_helper(var/mob/G) if (!G || !ismob(G)) return 0 if (!istype(G, /mob/dead) && !(istype(G, /mob/living) && G.stat == 2)) return 0 if (istype(G, /mob/new_player) || G.respawning) return 0 if (jobban_isbanned(G, "Syndicate")) return 0 if (jobban_isbanned(G, "Special Respawn")) return 0 if (istype(G, /mob/dead)) var/mob/dead/observer/the_ghost = null if (isobserver(G)) the_ghost = G if (istype(G, /mob/dead/target_observer)) var/mob/dead/target_observer/TO = G if (TO.my_ghost && isobserver(TO.my_ghost)) the_ghost = TO.my_ghost if (!the_ghost || !isobserver(the_ghost) || the_ghost.stat != 2 || the_ghost.observe_round) return 0 if (!G.client || G.client && (G.client.suicide || G.client.holder)) // Admins can respawn and make themselves antagonists at any time. return 0 if (!G.mind || G.mind && (G.mind.dnr || !isnull(G.mind.special_role) || G.mind.former_antagonist_roles.len)) // Dead antagonists have had their chance. return 0 return 1 /proc/check_target_immunity(var/atom/target, var/ignore_everything_but_nodamage = 0) var/is_immune = 0 if (isliving(target)) var/mob/living/L = target if (L.stat != 2) if (ignore_everything_but_nodamage == 1) if (L.nodamage) is_immune = 1 else if (L.nodamage || L.spellshield) is_immune = 1 //if (is_immune == 1) // DEBUG_MESSAGE("[L] is immune to damage, aborting.") return is_immune // Their antag status is revoked on death/implant removal/expiration, but we still want them to show up in the game over stats (Convair880). /proc/remove_mindslave_status(var/mob/M, var/slave_type ="", var/removal_type ="") if (!M || !M.mind || !slave_type || !removal_type) return // Find our master's mob reference (if any). var/mob/mymaster = whois_ckey_to_mob_reference(M.mind.master) switch (slave_type) if ("mslave") switch (removal_type) if ("expired") logTheThing("combat", M, mymaster, "'s mindslave implant (implanted by [mymaster ? "%target%" : "*NOKEYFOUND*"]) has worn off.") if ("surgery") logTheThing("combat", M, mymaster, "'s mindslave implant (implanted by [mymaster ? "%target%" : "*NOKEYFOUND*"]) was removed surgically.") if ("override") logTheThing("combat", M, mymaster, "'s mindslave implant (implanted by [mymaster ? "%target%" : "*NOKEYFOUND*"]) was overridden by a different implant.") if ("death") logTheThing("combat", M, mymaster, "(implanted by [mymaster ? "%target%" : "*NOKEYFOUND*"]) has died, removing mindslave status.") else logTheThing("combat", M, mymaster, "'s mindslave implant (implanted by [mymaster ? "%target%" : "*NOKEYFOUND*"]) has vanished mysteriously.") remove_antag(M, null, 1, 0) if (M.mind && ticker.mode && !(M.mind in ticker.mode.former_antagonists)) if (!M.mind.former_antagonist_roles.Find("mindslave")) M.mind.former_antagonist_roles.Add("mindslave") ticker.mode.former_antagonists += M.mind if ("vthrall") switch (removal_type) if ("death") logTheThing("combat", M, mymaster, "(enthralled by [mymaster ? "%target%" : "*NOKEYFOUND*"]) has died, removing vampire thrall status.") else logTheThing("combat", M, mymaster, "(enthralled by [mymaster ? "%target%" : "*NOKEYFOUND*"]) has been freed mysteriously, removing vampire thrall status.") remove_antag(M, null, 1, 0) if (M.mind && ticker.mode && !(M.mind in ticker.mode.former_antagonists)) if (!M.mind.former_antagonist_roles.Find("vampthrall")) M.mind.former_antagonist_roles.Add("vampthrall") ticker.mode.former_antagonists += M.mind // This is only used for spy slaves and mindslaved antagonists at the moment. if ("otherslave") switch (removal_type) if ("expired") logTheThing("combat", M, mymaster, "'s mindslave implant (implanted by [mymaster ? "%target%" : "*NOKEYFOUND*"]) has worn off.") if ("surgery") logTheThing("combat", M, mymaster, "'s mindslave implant (implanted by [mymaster ? "%target%" : "*NOKEYFOUND*"]) was removed surgically.") if ("override") logTheThing("combat", M, mymaster, "'s mindslave implant (implanted by [mymaster ? "%target%" : "*NOKEYFOUND*"]) was overridden by a different implant.") if ("death") logTheThing("combat", M, mymaster, "(enslaved by [mymaster ? "%target%" : "*NOKEYFOUND*"]) has died, removing mindslave status.") else logTheThing("combat", M, mymaster, "(enslaved by [mymaster ? "%target%" : "*NOKEYFOUND*"]) has been freed mysteriously, removing mindslave status.") // Fix for mindslaved traitors etc losing their antagonist status. if (M.mind && (M.mind.special_role == "spyslave")) remove_antag(M, null, 1, 0) else M.mind.master = null if (M.mind && ticker.mode && !(M.mind in ticker.mode.former_antagonists)) if (!M.mind.former_antagonist_roles.Find("mindslave")) M.mind.former_antagonist_roles.Add("mindslave") ticker.mode.former_antagonists += M.mind else logTheThing("debug", M, null, "Convair880: [M] isn't a mindslave or vampire thrall, can't remove mindslave status.") return if (removal_type == "death") boutput(M, "

Since you have died, you are no longer a mindslave! Do not obey your former master's orders even if you've been brought back to life somehow.

") M << browse(grabResource("html/mindslave/death.html"),"window=antagTips;titlebar=1;size=600x400;can_minimize=0;can_resize=0") else if (removal_type == "override") boutput(M, "

Your mindslave implant has been overridden by a new one, cancelling out your former allegiances!

") M << browse(grabResource("html/mindslave/override.html"),"window=antagTips;titlebar=1;size=600x400;can_minimize=0;can_resize=0") else boutput(M, "

Your mind is your own again! You no longer feel the need to obey your former master's orders.

") M << browse(grabResource("html/mindslave/expire.html"),"window=antagTips;titlebar=1;size=600x400;can_minimize=0;can_resize=0") return //Looks up a player based on a string. Searches a shit load of things ~whoa~. Returns a list of mob refs. /proc/whois(target, limit = 0, admin) target = trim(lowertext(target)) if (!target) return 0 var/list/found = list() for (var/mob/M in mobs) if (M.ckey && (limit == 0 || found.len < limit)) if (findtext(M.real_name, target)) found += M else if (findtext(M.ckey, target)) found += M else if (findtext(M.key, target)) found += M else if (M.mind) if (findtext(M.mind.assigned_role, target)) if (M.mind.assigned_role == "MODE") //We matched on the internal MODE job this doesn't fuckin' count continue else found += M else if (findtext(M.mind.special_role, target)) found += M if (found.len > 0) return found else return 0 // An universal ckey -> mob reference lookup proc, adapted from whois() (Convair880). /proc/whois_ckey_to_mob_reference(target as text) if (!target || isnull(target)) return 0 target = lowertext(target) var/mob/our_mob for (var/mob/M in mobs) if ((!isnull(M.ckey) && !isnull(target)) && findtext(M.ckey, target)) //DEBUG_MESSAGE("Whois: match found for [target], it's [M].") our_mob = M break if (our_mob) return our_mob else return 0 /proc/random_hex(var/digits as num) var/list/hex_chars = list("1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "A", "B", "C", "D", "E", "F") // this way numbers like 000F04 are possible if (!digits) digits = 6 var/return_hex = "" for (var/i = 0, i < digits, i++) return_hex += pick(hex_chars) return return_hex //A global cooldown on this so it doesnt destroy the external server var/global/nextDectalkDelay = 5 //seconds var/global/lastDectalkUse = 0 /proc/dectalk(msg) if (!msg) return 0 if (world.timeofday > (lastDectalkUse + (nextDectalkDelay * 10))) lastDectalkUse = world.timeofday msg = copytext(msg, 1, 2000) var/res[] = world.Export("http://tts.spess.cf/tts?words=[url_encode(msg)]") if (!res || !res["CONTENT"]) return 0 var/audio = file2text(res["CONTENT"]) return list("audio" = audio, "message" = msg) else return list("cooldown" = 1) proc/copy_datum_vars(var/atom/from, var/atom/target) if (!target || !from) return for(var/V in from.vars) if (!issaved(from.vars[V])) continue if(V == "type") continue if(V == "parent_type") continue if(V == "vars") continue target.vars[V] = from.vars[V] /proc/hex2color_name(var/hex) if (!hex) return var/adj = 0 if (copytext(hex, 1, 2) == "#") adj = 1 var/hR = hex2num(copytext(hex, 1 + adj, 3 + adj)) var/hG = hex2num(copytext(hex, 3 + adj, 5 + adj)) var/hB = hex2num(copytext(hex, 5 + adj, 7 + adj)) var/datum/color/C = new(hR, hG, hB, 0) var/name = get_nearest_color(C) if (name) return name var/list/uppercase_letters = list("A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z") var/list/lowercase_letters = list("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z") // Helper for blob, wraiths and whoever else might need them (Convair880). /proc/restricted_z_allowed(var/mob/M, var/T) var/list/allowed = list() for (var/areas in typesof(/area/shuttle/escape)) if (!allowed.Find(areas)) allowed.Add(areas) for (var/areas2 in typesof(/area/shuttle_transit_space)) if (!allowed.Find(areas2)) allowed.Add(areas2) if (M && istype(M, /mob/living/intangible/blob_overmind)) var/mob/living/intangible/blob_overmind/B = M if (B.tutorial) return 1 var/area/A if (T && istype(T, /area)) A = T else if (T && isturf(T)) A = get_area(T) if (A && istype(A) && (A.type in allowed)) return 1 return 0 // What sonic grenades and similar functions use. It's a proc to avoid C&P code (Convair880). /proc/sonic_attack_environmental_effect(var/center, var/range, var/list/smash) if (!center || !isnum(range) || range <= 0) return 0 if (!islist(smash) || !smash.len) return 0 var/turf/CT if (isturf(center)) CT = center else if (istype(center, /atom)) CT = get_turf(center) if (!(CT && isturf(CT))) return 0 // No visible_messages here because of text spam. The station has a lot of windows and light fixtures. // And once again, view() proved quite unreliable. for (var/S in smash) if (S == "window") for (var/obj/window/W in range(CT, range)) if (istype(W, /obj/window/reinforced)) continue if (prob(get_dist(W, CT) * 6)) continue W.health = 0 W.smash() if (S == "r_window") for (var/obj/window/reinforced/R in range(CT, range)) if (prob(get_dist(R, CT) * 7)) continue R.health = 0 R.smash() if (S == "light") for (var/obj/machinery/light/L in range(CT, range)) L.broken() if (S == "displaycase") for (var/obj/displaycase/D in range(CT, range)) D.ex_act(1) if (S == "glassware") for (var/obj/item/reagent_containers/glass/G in range(CT, range)) G.smash() for (var/obj/item/reagent_containers/food/drinks/drinkingglass/G2 in range(CT, range)) G2.smash() return 1 /proc/get_hud_style(var/someone) if (!someone) return var/client/C = null if (isclient(someone)) C = someone else if (ismob(someone)) var/mob/M = someone if (M.client) C = M.client if (!C || !C.preferences) return return C.preferences.hud_style