//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 /* * A large number of misc global procs. */ //Checks if all high bits in req_mask are set in bitfield #define BIT_TEST_ALL(bitfield, req_mask) ((~(bitfield) & (req_mask)) == 0) //supposedly the fastest way to do this according to https://gist.github.com/Giacom/be635398926bb463b42a #define RANGE_TURFS(RADIUS, CENTER) \ block( \ locate(max(CENTER.x-(RADIUS),1), max(CENTER.y-(RADIUS),1), CENTER.z), \ locate(min(CENTER.x+(RADIUS),world.maxx), min(CENTER.y+(RADIUS),world.maxy), CENTER.z) \ ) //Inverts the colour of an HTML string /proc/invertHTML(HTMLstring) if (!( istext(HTMLstring) )) CRASH("Given non-text argument!") else if (length(HTMLstring) != 7) CRASH("Given non-HTML argument!") 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) //Returns the middle-most value /proc/dd_range(var/low, var/high, var/num) return max(low,min(high,num)) //Returns whether or not A is the middle most value /proc/InRange(var/A, var/lower, var/upper) if(A < lower) return 0 if(A > upper) return 0 return 1 /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 = FALSE, 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||T.contains_dense_objects())) 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||destination.contains_dense_objects())) return if(destination.x>world.maxx || destination.x<1) return if(destination.y>world.maxy || destination.y<1) return else return return destination /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/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) //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) x-=dyabs px+=sdx py+=sdy line+=locate(px,py,M.z) return line #define LOCATE_COORDS(X, Y, Z) locate(between(1, X, world.maxx), between(1, Y, world.maxy), Z) /proc/getcircle(turf/center, var/radius) //Uses a fast Bresenham rasterization algorithm to return the turfs in a thin circle. if(!radius) return list(center) var/x = 0 var/y = radius var/p = 3 - 2 * radius . = list() while(y >= x) // only formulate 1/8 of circle . += LOCATE_COORDS(center.x - x, center.y - y, center.z) //upper left left . += LOCATE_COORDS(center.x - y, center.y - x, center.z) //upper upper left . += LOCATE_COORDS(center.x + y, center.y - x, center.z) //upper upper right . += LOCATE_COORDS(center.x + x, center.y - y, center.z) //upper right right . += LOCATE_COORDS(center.x - x, center.y + y, center.z) //lower left left . += LOCATE_COORDS(center.x - y, center.y + x, center.z) //lower lower left . += LOCATE_COORDS(center.x + y, center.y + x, center.z) //lower lower right . += LOCATE_COORDS(center.x + x, center.y + y, center.z) //lower right right if(p < 0) p += 4*x++ + 6; else p += 4*(x++ - y--) + 10; #undef LOCATE_COORDS //Returns whether or not a player is a guest using their ckey as an input /proc/IsGuestKey(key) if (findtext(key, "Guest-", 1, 7) != 1) //was findtextEx return 0 var/i = 7, ch, len = length(key) if(copytext(key, 7, 8) == "W") //webclient i++ for (, i <= len, ++i) ch = text2ascii(key, i) if (ch < 48 || ch > 57) return 0 return 1 //Ensure the frequency is within bounds of what it should be sending/recieving at /proc/sanitize_frequency(var/f, var/low = PUBLIC_LOW_FREQ, var/high = PUBLIC_HIGH_FREQ) f = round(f) f = max(low, f) f = min(high, f) if ((f % 2) == 0) //Ensure the last digit is an odd number f += 1 return f //Turns 1479 into 147.9 /proc/format_frequency(var/f) return "[round(f / 10)].[f % 10]" //Opposite of format, returns as a number /proc/unformat_frequency(frequency) frequency = text2num(frequency) return frequency * 10 //This will update a mob's name, real_name, mind.name, data_core records, pda and id //Calling this proc without an oldname will only update the mob and skip updating the pda, id and records ~Carn /mob/proc/fully_replace_character_name(var/oldname,var/newname) if(!newname) return 0 real_name = newname name = newname if(mind) mind.name = newname if(dna) dna.real_name = real_name if(oldname) //update the datacore records! This is goig to be a bit costly. for(var/list/L in list(GLOB.data_core.general, GLOB.data_core.medical, GLOB.data_core.security, GLOB.data_core.locked)) for(var/datum/data/record/R in L) if(R.fields["name"] == oldname) R.fields["name"] = newname break //update our pda and id if we have them on our person var/list/searching = GetAllContents(searchDepth = 3) var/search_id = 1 var/search_pda = 1 for(var/A in searching) if( search_id && istype(A,/obj/item/card/id) ) var/obj/item/card/id/ID = A if(ID.registered_name == oldname) ID.registered_name = newname ID.name = "[newname]'s ID Card ([ID.assignment])" if(!search_pda) break search_id = 0 else if( search_pda && istype(A,/obj/item/pda) ) var/obj/item/pda/PDA = A if(PDA.owner == oldname) PDA.owner = newname PDA.name = "PDA-[newname] ([PDA.ownjob])" if(!search_id) break search_pda = 0 return 1 //Generalised helper proc for letting mobs rename themselves. Used to be clname() and ainame() //Last modified by Carn /mob/proc/rename_self(var/role, var/allow_numbers=0) spawn(0) var/oldname = real_name var/time_passed = world.time var/newname for(var/i=1,i<=3,i++) //we get 3 attempts to pick a suitable name. //newname = tgui_input_text(src,"You are \a [role]. Would you like to change your name to something else?", "Name change",oldname) newname = tgui_input_text(src,"You are \a [role]. Would you like to change your name to something else?", "Name change",oldname, MAX_NAME_LEN) if((world.time-time_passed)>3000) return //took too long newname = sanitizeName(newname, ,allow_numbers) //returns null if the name doesn't meet some basic requirements. Tidies up a few other things like bad-characters. for(var/mob/living/M in GLOB.player_list) if(M == src) continue if(!newname || M.real_name == newname) newname = null break if(newname) break //That's a suitable name! to_chat(src, "Sorry, that [role]-name wasn't appropriate, please try another. It's possibly too long/short, has bad characters or is already taken.") if(!newname) //we'll stick with the oldname then return if(cmptext("ai",role)) if(isAI(src)) var/mob/living/silicon/ai/A = src oldname = null//don't bother with the records update crap //to_world(span_world("[newname] is the AI!")) //world << sound('sound/AI/newai.ogg') // Set eyeobj name A.SetName(newname) fully_replace_character_name(oldname,newname) //Picks a string of symbols to display as the law number for hacked or ion laws /proc/ionnum() return "[pick("1","2","3","4","5","6","7","8","9","0")][pick("!","@","#","$","%","^","&","*")][pick("!","@","#","$","%","^","&","*")][pick("!","@","#","$","%","^","&","*")]" //When an AI is activated, it can choose from a list of non-slaved borgs to have as a slave. /proc/freeborg() var/select = null var/list/borgs = list() for (var/mob/living/silicon/robot/A in GLOB.player_list) if (A.stat == 2 || A.connected_ai || A.scrambledcodes || istype(A,/mob/living/silicon/robot/drone)) continue var/name = "[A.real_name] ([A.modtype] [A.braintype])" borgs[name] = A if (borgs.len) select = tgui_input_list(usr, "Unshackled borg signals detected:", "Borg selection", borgs) if(select) return borgs[select] //When a borg is activated, it can choose which AI it wants to be slaved to /proc/active_ais() . = list() for(var/mob/living/silicon/ai/A in GLOB.living_mob_list) if(A.stat == DEAD) continue if(A.control_disabled == 1) continue . += A return . //Find an active ai with the least borgs. VERBOSE PROCNAME HUH! /proc/select_active_ai_with_fewest_borgs() var/mob/living/silicon/ai/selected var/list/active = active_ais() for(var/mob/living/silicon/ai/A in active) if(!selected || (selected.connected_robots.len > A.connected_robots.len)) selected = A return selected /proc/select_active_ai(var/mob/user) var/list/ais = active_ais() if(ais.len) if(user) . = tgui_input_list(user, "AI signals detected:", "AI selection", ais) else . = pick(ais) return . //Returns a list of all mobs with their name /proc/getmobs() return observe_list_format(sort_mobs()) //Orders mobs by type then by name /proc/sort_mobs() var/list/moblist = list() var/list/sortmob = sort_names(GLOB.mob_list) var/list/after_simplemob_minded = list() //CHOMPEdit for(var/mob/observer/eye/M in sortmob) if (!M.client && !M.disconnect_time) //CHOMPEdit Addition after_simplemob_minded.Add(M) continue moblist.Add(M) for(var/mob/observer/blob/M in sortmob) if (!M.client && !M.disconnect_time) //CHOMPEdit Addition after_simplemob_minded.Add(M) continue moblist.Add(M) for(var/mob/living/silicon/ai/M in sortmob) if (!M.client && !M.disconnect_time) //CHOMPEdit Addition after_simplemob_minded.Add(M) continue moblist.Add(M) for(var/mob/living/silicon/pai/M in sortmob) if (!M.client && !M.disconnect_time) //CHOMPEdit Addition after_simplemob_minded.Add(M) continue moblist.Add(M) for(var/mob/living/silicon/robot/M in sortmob) if (!M.client && !M.disconnect_time) //CHOMPEdit Addition after_simplemob_minded.Add(M) continue moblist.Add(M) var/list/delaylist = list() for(var/mob/living/carbon/human/M in sortmob) if(M.low_sorting_priority && !M.client) delaylist.Add(M) else if (!M.client && !M.disconnect_time) //ChompADD Start after_simplemob_minded.Add(M) continue //ChompADD End moblist.Add(M) moblist.Add(delaylist) for(var/mob/living/carbon/brain/M in sortmob) if (!M.client && !M.disconnect_time) //CHOMPEdit Addition after_simplemob_minded.Add(M) continue moblist.Add(M) for(var/mob/living/carbon/alien/M in sortmob) if (!M.client && !M.disconnect_time) //CHOMPEdit Addition after_simplemob_minded.Add(M) continue moblist.Add(M) for(var/mob/observer/dead/M in sortmob) moblist += M for(var/mob/new_player/M in sortmob) moblist += M for(var/mob/living/simple_mob/M in sortmob) if (!M.client && !M.disconnect_time) //CHOMPEdit Addition after_simplemob_minded.Add(M) continue moblist.Add(M) moblist.Add(after_simplemob_minded) //CHOMPEdit after_simplemob_minded.Cut() //VOREStation Addition Start for(var/mob/living/dominated_brain/M in sortmob) moblist += M return moblist /proc/observe_list_format(input_list) if(!islist(input_list)) return var/list/names = list() var/list/output_list = list() var/list/namecounts = list() var/name for(var/atom/A in input_list) name = A.name if(name in names) namecounts[name]++ name = "[name] ([namecounts[name]])" else names.Add(name) namecounts[name] = 1 if(ismob(A)) var/mob/M = A if(M.real_name && M.real_name != M.name) name += " \[[M.real_name]\]" if(M.stat == DEAD) if(istype(M, /mob/observer/dead/)) name += " \[ghost\]" else name += " \[dead\]" output_list[name] = A return output_list // Format a power value in W, kW, MW, or GW. /proc/DisplayPower(powerused) if(powerused < 1000) //Less than a kW return "[powerused] W" else if(powerused < 1000000) //Less than a MW return "[round((powerused * 0.001),0.01)] kW" else if(powerused < 1000000000) //Less than a GW return "[round((powerused * 0.000001),0.001)] MW" return "[round((powerused * 0.000000001),0.0001)] GW" //Forces a variable to be posative /proc/modulus(var/M) if(M >= 0) return M if(M < 0) return -M // 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) //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) //Will return the contents of an atom recursivly to a depth of 'searchDepth' /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 //Step-towards method of determining whether one atom can see another. Similar to viewers() /proc/can_see(var/atom/source, var/atom/target, var/length=5) // I couldn't 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 if(!current || !target_turf) return 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 /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 //Takes: Anything that could possibly have variables and a varname to check. //Returns: 1 if found, 0 if not. /proc/hasvar(var/datum/A, var/varname) if(A.vars.Find(lowertext(varname))) return 1 else return 0 //Returns: all the areas in the world /proc/return_areas() var/list/area/areas = list() for(var/area/A in world) areas[A.name] = A return areas //Returns: all the areas in the world, sorted. /proc/return_sorted_areas() return sortTim(return_areas(), GLOBAL_PROC_REF(cmp_text_asc)) //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. /proc/get_area_turfs(var/areatype) 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 //Takes: An instance of the area. //Returns: A list of all turfs in that area. //Side note: I don't know why this was never a thing. Did everyone just ignore the Blueprint item?! - C.L. /proc/get_current_area_turfs(var/area/checked_area) if(!checked_area) return null var/list/turfs = new/list() for(var/turf/counted_turfs in checked_area.contents) //Cheap. Efficient. Lovely. turfs += counted_turfs return turfs //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. /proc/get_area_all_atoms(var/areatype) 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 //Takes: Area as an instance of the area. //Returns: A list of all atoms (objs, turfs, mobs) in the selected area. /proc/get_current_area_atoms(var/area/checked_area) if(!checked_area) return null var/list/atoms = new/list() for(var/atom/A in checked_area.contents) 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) 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) //You can stay, though. if(istype(T,/turf/space)) refined_src -= T refined_trg -= B continue moving var/turf/X //New Destination Turf //Are we doing shuttlework? Just to save another type check later. var/shuttlework = 0 //Shuttle turfs handle their own fancy moving. if(istype(T,/turf/simulated/shuttle)) shuttlework = 1 var/turf/simulated/shuttle/SS = T if(!SS.landed_holder) SS.landed_holder = new(SS) X = SS.landed_holder.land_on(B) //Generic non-shuttle turf move. else var/old_dir1 = T.dir var/old_icon_state1 = T.icon_state var/old_icon1 = T.icon var/old_underlays = T.underlays.Copy() var/old_decals = T.decals ? T.decals.Copy() : null X = B.ChangeTurf(T.type) X.set_dir(old_dir1) X.icon_state = old_icon_state1 X.icon = old_icon1 X.copy_overlays(T, TRUE) X.underlays = old_underlays X.decals = old_decals //Move the air from source to dest var/turf/simulated/ST = T if(istype(ST) && ST.zone) var/turf/simulated/SX = X if(!SX.air) SX.make_air() SX.air.copy_from(ST.zone.air) ST.zone.remove(ST) var/z_level_change = FALSE if(T.z != X.z) z_level_change = TRUE //Move the objects. Not forceMove because the object isn't "moving" really, it's supposed to be on the "same" turf. for(var/obj/O in T) O.loc = X if(O.light_system == STATIC_LIGHT) O.update_light() else var/datum/component/overlay_lighting/OL = O.GetComponent(/datum/component/overlay_lighting) OL?.on_parent_moved(O, T, O.dir, TRUE) if(z_level_change) // The objects still need to know if their z-level changed. O.onTransitZ(T.z, X.z) //Move the mobs unless it's an AI eye or other eye type. for(var/mob/M in T) if(isEye(M)) continue // If we need to check for more mobs, I'll add a variable M.loc = X if(z_level_change) // Same goes for mobs. M.onTransitZ(T.z, X.z) if(shuttlework) var/turf/simulated/shuttle/SS = T SS.landed_holder.leave_turf() else if(turftoleave) T.ChangeTurf(turftoleave) else T.ChangeTurf(get_base_turf_by_area(T)) refined_src -= T refined_trg -= B continue moving /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)) var/static/list/blacklisted_var_names = list(BLACKLISTED_COPY_VARS) if(perfectcopy) if((O) && (original)) for(var/V in original.vars) if(!(V in blacklisted_var_names)) 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. // Does *not* affect gases etc; copied turfs will be changed via ChangeTurf, and the dir, icon, and icon_state copied. All other vars will remain default. 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 var/old_overlays = T.overlays.Copy() var/old_underlays = T.underlays.Copy() if(platingRequired) if(istype(B, get_base_turf_by_area(B))) continue moving var/turf/X = B X.ChangeTurf(T.type) X.set_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 X.overlays = old_overlays X.underlays = old_underlays 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(!ismob(M) || isEye(M)) continue // If we need to check for more mobs, I'll add a variable 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 // var/area/AR = X.loc // if(AR.dynamic_lighting) // X.opacity = !X.opacity // X.sd_SetOpacity(!X.opacity) //TODO: rewrite this code so it's not messed by lighting ~Carn toupdate += X refined_src -= T refined_trg -= B continue moving if(toupdate.len) for(var/turf/simulated/T1 in toupdate) SSair.mark_for_update(T1) 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) /proc/view_or_range(distance = world.view , center = usr , type) switch(type) if("view") . = view(distance,center) if("range") . = range(distance,center) return /proc/get_mob_with_client_list() var/list/mobs = list() for(var/mob/M in GLOB.mob_list) if (M.client) mobs += M return mobs /proc/parse_zone(zone) if(zone == BP_R_HAND) return "right hand" else if (zone == BP_L_HAND) return "left hand" else if (zone == BP_L_ARM) return "left arm" else if (zone == BP_R_ARM) return "right arm" else if (zone == BP_L_LEG) return "left leg" else if (zone == BP_R_LEG) return "right leg" else if (zone == BP_L_FOOT) return "left foot" else if (zone == BP_R_FOOT) return "right foot" else if (zone == BP_L_HAND) return "left hand" else if (zone == BP_R_HAND) return "right hand" else if (zone == BP_L_FOOT) return "left foot" else if (zone == BP_R_FOOT) return "right foot" else return zone /proc/get(atom/loc, type) while(loc) if(istype(loc, type)) return loc loc = loc.loc return null /proc/get_turf_or_move(turf/location) return get_turf(location) //Quick type checks for some tools GLOBAL_LIST_INIT(common_tools, list( /obj/item/stack/cable_coil, /obj/item/tool/wrench, /obj/item/weldingtool, /obj/item/tool/screwdriver, /obj/item/tool/wirecutters, /obj/item/multitool, /obj/item/tool/crowbar, /obj/item/tool/transforming)) /proc/istool(O) if(O && is_type_in_list(O, GLOB.common_tools)) return 1 return 0 /proc/is_wire_tool(obj/item/I) if(istype(I, /obj/item/multitool) || I.has_tool_quality(TOOL_WIRECUTTER)) return TRUE if(istype(I, /obj/item/assembly/signaler)) return TRUE return /proc/is_hot(obj/item/W as obj) switch(W.type) if(/obj/item/weldingtool) var/obj/item/weldingtool/WT = W if(WT.isOn()) return 3800 else return 0 if(/obj/item/tool/transforming) var/obj/item/tool/transforming/TT = W if(TT.possible_tooltypes[TT.current_tooltype] == TOOL_WELDER) return 3800 else return 0 if(/obj/item/flame/lighter) if(W:lit) return 1500 else return 0 if(/obj/item/flame/match) if(W:lit) return 1000 else return 0 if(/obj/item/clothing/mask/smokable/cigarette) if(W:lit) return 1000 else return 0 if(/obj/item/pickaxe/plasmacutter) return 3800 if(/obj/item/melee/energy) return 3500 else return 0 //Whether or not the given item counts as sharp in terms of dealing damage /proc/is_sharp(obj/O as obj) if(!O) return FALSE if(O.sharp) return TRUE if(O.edge) return TRUE return FALSE //Whether or not the given item counts as cutting with an edge in terms of removing limbs /proc/has_edge(obj/O as obj) if(!O) return FALSE if(O.edge) return TRUE return FALSE //Returns 1 if the given item is capable of popping things like balloons, inflatable barriers, or cutting police tape. /proc/can_puncture(obj/item/W as obj) // For the record, WHAT THE HELL IS THIS METHOD OF DOING IT? if(!W) return FALSE if(W.sharp) return TRUE return ( \ W.has_tool_quality(TOOL_SCREWDRIVER) || \ istype(W, /obj/item/pen) || \ istype(W, /obj/item/weldingtool) || \ istype(W, /obj/item/flame/lighter/zippo) || \ istype(W, /obj/item/flame/match) || \ istype(W, /obj/item/clothing/mask/smokable/cigarette) || \ istype(W, /obj/item/shovel) \ ) // check if mob is lying down on something we can operate him on. // The RNG with table/rollerbeds comes into play in do_surgery() so that fail_step() can be used instead. /proc/can_operate(mob/living/carbon/M, mob/living/user) if(M != user) . = M.lying else if(user && user.allow_self_surgery && user.a_intent == I_HELP) // You can, technically, always operate on yourself after standing still. Inadvised, but you can. . = TRUE return . // Returns an instance of a valid surgery surface. /mob/living/proc/get_surgery_surface() if(!lying) return null // Not lying down means no surface. var/obj/surface = null for(var/obj/O in loc) // Looks for the best surface. if(O.surgery_odds) if(!surface || surface.surgery_odds < O.surgery_odds) surface = O if(surface) return surface /proc/reverse_direction(dir) return GLOB.reverse_dir[dir] /* Checks if that loc and dir has a item on the wall TODO - Fix this ancient list of wall items. Preferably make it dynamically populated. ~Leshana */ var/list/WALLITEMS = list( /obj/machinery/power/apc, /obj/machinery/alarm, /obj/item/radio/intercom, /obj/structure/frame, /obj/structure/extinguisher_cabinet, /obj/structure/reagent_dispensers/peppertank, /obj/machinery/status_display, /obj/machinery/requests_console, /obj/machinery/light_switch, /obj/structure/sign, /obj/machinery/newscaster, /obj/machinery/firealarm, /obj/structure/noticeboard, /obj/machinery/button/remote, /obj/machinery/computer/security/telescreen, /obj/machinery/embedded_controller/radio, /obj/item/storage/secure/safe, /obj/machinery/door_timer, /obj/machinery/flasher, /obj/machinery/keycard_auth, /obj/structure/mirror, /obj/structure/fireaxecabinet, /obj/machinery/computer/security/telescreen/entertainment, /obj/machinery/doorbell_chime, /obj/machinery/button/doorbell, /obj/machinery/atm, /obj/machinery/recharger/wallcharger, //CHOMPEdit /obj/machinery/computer/guestpass, /obj/item/geiger/wall, /obj/machinery/button/windowtint, /obj/machinery/computer/id_restorer, //CHOMPEdit /obj/machinery/computer/timeclock, /obj/machinery/station_map, /obj/machinery/ai_status_display //CHOMPEdit ) /proc/gotwallitem(loc, dir) for(var/obj/O in loc) for(var/item in WALLITEMS) if(istype(O, item)) //Direction works sometimes if(O.dir == dir) return 1 //Some stuff doesn't use dir properly, so we need to check pixel instead switch(dir) if(SOUTH) if(O.pixel_y > 10) return 1 if(NORTH) if(O.pixel_y < -10) return 1 if(WEST) if(O.pixel_x > 10) return 1 if(EAST) if(O.pixel_x < -10) return 1 //Some stuff is placed directly on the wallturf (signs) for(var/obj/O in get_step(loc, dir)) for(var/item in WALLITEMS) if(istype(O, item)) if(O.pixel_x == 0 && O.pixel_y == 0) return 1 return 0 /proc/format_text(text) return replacetext(replacetext(text,"\proper ",""),"\improper ","") /proc/topic_link(var/datum/D, var/arglist, var/content) if(istype(arglist,/list)) arglist = list2params(arglist) return "[content]" /proc/get_random_colour(var/simple, var/lower=0, var/upper=255) var/colour if(simple) colour = pick(list("FF0000","FF7F00","FFFF00","00FF00","0000FF","4B0082","8F00FF")) else for(var/i=1;i<=3;i++) var/temp_col = "[num2hex(rand(lower,upper))]" if(length(temp_col )<2) temp_col = "0[temp_col]" colour += temp_col return "#[colour]" /proc/color_square(red, green, blue, hex) var/color = hex ? hex : "#[num2hex(red, 2)][num2hex(green, 2)][num2hex(blue, 2)]" return "___" var/mob/dview/dview_mob //Version of view() which ignores darkness, because BYOND doesn't have it. /proc/dview(var/range = world.view, var/center, var/invis_flags = 0) if(!center) return if(!dview_mob) //VOREStation Add: Debugging dview_mob = new dview_mob.loc = center dview_mob.see_invisible = invis_flags . = view(range, dview_mob) dview_mob.loc = null /mob/dview invisibility = INVISIBILITY_ABSTRACT density = FALSE anchored = TRUE simulated = FALSE see_in_dark = 1e6 /atom/proc/get_light_and_color(var/atom/origin) if(origin) color = origin.color set_light(origin.light_range, origin.light_power, origin.light_color) /mob/dview/Initialize(mapload) . = ..() // We don't want to be in any mob lists; we're a dummy not a mob. GLOB.mob_list -= src if(stat == DEAD) GLOB.dead_mob_list -= src else GLOB.living_mob_list -= src /mob/dview/Life() GLOB.mob_list -= src GLOB.dead_mob_list -= src GLOB.living_mob_list -= src /mob/dview/Destroy(var/force) stack_trace("Attempt to delete the dview_mob: [log_info_line(src)]") if (!force) return QDEL_HINT_LETMELIVE global.dview_mob = new return ..() /proc/screen_loc2turf(scr_loc, turf/origin) var/tX = splittext(scr_loc, ",") var/tY = splittext(tX[2], ":") var/tZ = origin.z tY = tY[1] tX = splittext(tX[1], ":") tX = tX[1] tX = max(1, min(world.maxx, origin.x + (text2num(tX) - (world.view + 1)))) tY = max(1, min(world.maxy, origin.y + (text2num(tY) - (world.view + 1)))) return locate(tX, tY, tZ) // Displays something as commonly used (non-submultiples) SI units. /proc/format_SI(var/number, var/symbol) switch(round(abs(number))) if(0 to 1000-1) return "[number] [symbol]" if(1e3 to 1e6-1) return "[round(number / 1000, 0.1)] k[symbol]" // kilo if(1e6 to 1e9-1) return "[round(number / 1e6, 0.1)] M[symbol]" // mega if(1e9 to 1e12-1) // Probably not needed but why not be complete? return "[round(number / 1e9, 0.1)] G[symbol]" // giga if(1e12 to 1e15-1) return "[round(number / 1e12, 0.1)] T[symbol]" // tera //ultra range (no limitations on distance, faster than range for distances > 8); including areas drastically decreases performance /proc/urange(dist=0, atom/center=usr, orange=0, areas=0) if(!dist) if(!orange) return list(center) else return list() var/list/turfs = RANGE_TURFS(dist, center) if(orange) turfs -= get_turf(center) . = list() for(var/turf/T as anything in turfs) . += T . += T.contents if(areas) . |= T.loc #define NOT_FLAG(flag) (!(flag & use_flags)) #define HAS_FLAG(flag) (flag & use_flags) // Checks if user can use this object. Set use_flags to customize what checks are done. // Returns 0 if they can use it, a value representing why they can't if not. // Flags are in `code/__defines/misc.dm` /atom/proc/use_check(mob/user, use_flags = 0, show_messages = FALSE) . = 0 if (NOT_FLAG(USE_ALLOW_NONLIVING) && !isliving(user)) // No message for ghosts. return USE_FAIL_NONLIVING if (NOT_FLAG(USE_ALLOW_NON_ADJACENT) && !Adjacent(user)) if (show_messages) to_chat(user, span_notice("You're too far away from [src] to do that.")) return USE_FAIL_NON_ADJACENT if (NOT_FLAG(USE_ALLOW_DEAD) && user.stat == DEAD) if (show_messages) to_chat(user, span_notice("You can't do that when you're dead.")) return USE_FAIL_DEAD if (NOT_FLAG(USE_ALLOW_INCAPACITATED) && (user.incapacitated())) if (show_messages) to_chat(user, span_notice("You cannot do that in your current state.")) return USE_FAIL_INCAPACITATED if (NOT_FLAG(USE_ALLOW_NON_ADV_TOOL_USR) && !user.IsAdvancedToolUser()) if (show_messages) to_chat(user, span_notice("You don't know how to operate [src].")) return USE_FAIL_NON_ADV_TOOL_USR if (HAS_FLAG(USE_DISALLOW_SILICONS) && issilicon(user)) if (show_messages) to_chat(user, span_notice("You need hands for that.")) return USE_FAIL_IS_SILICON if (HAS_FLAG(USE_FORCE_SRC_IN_USER) && !(src in user)) if (show_messages) to_chat(user, span_notice("You need to be holding [src] to do that.")) return USE_FAIL_NOT_IN_USER #undef NOT_FLAG #undef HAS_FLAG #define VARSET_LIST_CALLBACK(target, var_name, var_value) CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(___callbackvarset), ##target, ##var_name, ##var_value) //dupe code because dm can't handle 3 level deep macros #define VARSET_CALLBACK(datum, var, var_value) CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(___callbackvarset), ##datum, NAMEOF(##datum, ##var), ##var_value) //we'll see about those 3-level deep macros #define VARSET_IN(datum, var, var_value, time) addtimer(VARSET_CALLBACK(datum, var, var_value), time) /proc/___callbackvarset(list_or_datum, var_name, var_value) if(length(list_or_datum)) list_or_datum[var_name] = var_value return var/datum/D = list_or_datum D.vars[var_name] = var_value // Returns direction-string, rounded to multiples of 22.5, from the first parameter to the second // N, NNE, NE, ENE, E, ESE, SE, SSE, S, SSW, SW, WSW, W, WNW, NW, NNW /proc/get_adir(var/turf/A, var/turf/B) var/degree = Get_Angle(A, B) switch(round(degree%360, 22.5)) if(0) return "North" if(22.5) return "North-Northeast" if(45) return "Northeast" if(67.5) return "East-Northeast" if(90) return "East" if(112.5) return "East-Southeast" if(135) return "Southeast" if(157.5) return "South-Southeast" if(180) return "South" if(202.5) return "South-Southwest" if(225) return "Southwest" if(247.5) return "West-Southwest" if(270) return "West" if(292.5) return "West-Northwest" if(315) return "Northwest" if(337.5) return "North-Northwest" /proc/pick_closest_path(value, list/matches = get_fancy_list_of_atom_types()) if (value == FALSE) //nothing should be calling us with a number, so this is safe value = tgui_input_text(usr, "Enter type to find (blank for all, cancel to cancel)", "Search for type") if (isnull(value)) return value = trim(value) if(!isnull(value) && value != "") matches = filter_fancy_list(matches, value) if(matches.len==0) return var/chosen if(matches.len==1) chosen = matches[1] else chosen = tgui_input_list(usr, "Select a type", "Pick Type", matches) if(!chosen) return chosen = matches[chosen] return chosen /proc/get_fancy_list_of_atom_types() var/static/list/pre_generated_list if (!pre_generated_list) //init pre_generated_list = make_types_fancy(typesof(/atom)) return pre_generated_list /proc/get_fancy_list_of_datum_types() var/static/list/pre_generated_list if (!pre_generated_list) //init pre_generated_list = make_types_fancy(sortList(typesof(/datum) - typesof(/atom))) return pre_generated_list /proc/filter_fancy_list(list/L, filter as text) var/list/matches = list() for(var/key in L) var/value = L[key] if(findtext("[key]", filter) || findtext("[value]", filter)) matches[key] = value return matches /proc/make_types_fancy(var/list/types) if (ispath(types)) types = list(types) . = list() for(var/type in types) var/typename = "[type]" var/static/list/TYPES_SHORTCUTS = list( /obj/effect/decal/cleanable = "CLEANABLE", /obj/item/radio/headset = "HEADSET", /obj/item/clothing/head/helmet/space = "SPESSHELMET", /obj/item/book/manual = "MANUAL", /obj/item/reagent_containers/food/drinks = "DRINK", /obj/item/reagent_containers/food = "FOOD", /obj/item/reagent_containers = "REAGENT_CONTAINERS", /obj/machinery/atmospherics = "ATMOS_MECH", /obj/machinery/portable_atmospherics = "PORT_ATMOS", /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack = "MECHA_MISSILE_RACK", /obj/item/mecha_parts/mecha_equipment = "MECHA_EQUIP", /obj/item/organ = "ORGAN", /obj/item = "ITEM", /obj/machinery = "MACHINERY", /obj/effect = "EFFECT", /obj = "O", /datum = "D", /turf/simulated/wall = "S-WALL", /turf/simulated/floor = "S-FLOOR", /turf/simulated = "SIMULATED", /turf/unsimulated/wall = "US-WALL", /turf/unsimulated/floor = "US-FLOOR", /turf/unsimulated = "UNSIMULATED", /turf = "T", /mob/living/carbon = "CARBON", /mob/living/simple_mob = "SIMPLE", /mob/living = "LIVING", /mob = "M" ) for (var/tn in TYPES_SHORTCUTS) if (copytext(typename,1, length("[tn]/")+1)=="[tn]/" /*findtextEx(typename,"[tn]/",1,2)*/ ) typename = TYPES_SHORTCUTS[tn]+copytext(typename,length("[tn]/")) break .[typename] = type /proc/IsValidSrc(datum/D) if(istype(D)) return !QDELETED(D) return FALSE /// No op /proc/pass(...) return //gives us the stack trace from CRASH() without ending the current proc. /proc/stack_trace(msg) CRASH(msg) /datum/proc/stack_trace(msg) CRASH(msg) GLOBAL_REAL_VAR(list/stack_trace_storage) /proc/gib_stack_trace() stack_trace_storage = list() stack_trace() stack_trace_storage.Cut(1, min(3,stack_trace_storage.len)) . = stack_trace_storage stack_trace_storage = null // \ref behaviour got changed in 512 so this is necesary to replicate old behaviour. // If it ever becomes necesary to get a more performant REF(), this lies here in wait // #define REF(thing) (thing && istype(thing, /datum) && (thing:datum_flags & DF_USE_TAG) && thing:tag ? "[thing:tag]" : "\ref[thing]") /proc/REF(input) if(istype(input, /datum)) var/datum/thing = input if(thing.datum_flags & DF_USE_TAG) if(!thing.tag) thing.datum_flags &= ~DF_USE_TAG stack_trace("A ref was requested of an object with DF_USE_TAG set but no tag: [thing]") else return "\[[url_encode(thing.tag)]\]" return "\ref[input]" // Painlessly creates an element. // First argument is where to send the Topic call to when clicked. Should be a reference to an object. This is generally src, but not always. // Second one is for all the params that will be sent. Uses an assoc list (e.g. "value" = "5"). // Note that object refs will be converted to text, as if \ref[thing] was done. To get the ref back on Topic() side, you will need to use locate(). // Third one is the text that will be clickable. /proc/href(href_src, list/href_params, href_text) return "[href_text]" // This is a helper for anything that wants to render the map in TGUI /proc/get_tgui_plane_masters() . = list() // 'Utility' planes . += new /obj/screen/plane_master/fullbright //Lighting system (lighting_overlay objects) . += new /obj/screen/plane_master/lighting //Lighting system (but different!) . += new /obj/screen/plane_master/o_light_visual //Object lighting (using masks) . += new /obj/screen/plane_master/emissive //Emissive overlays . += new /obj/screen/plane_master/ghosts //Ghosts! . += new /obj/screen/plane_master{plane = PLANE_AI_EYE} //AI Eye! . += new /obj/screen/plane_master{plane = PLANE_CH_STATUS} //Status is the synth/human icon left side of medhuds . += new /obj/screen/plane_master{plane = PLANE_CH_HEALTH} //Health bar . += new /obj/screen/plane_master{plane = PLANE_CH_LIFE} //Alive-or-not icon . += new /obj/screen/plane_master{plane = PLANE_CH_ID} //Job ID icon . += new /obj/screen/plane_master{plane = PLANE_CH_WANTED} //Wanted status . += new /obj/screen/plane_master{plane = PLANE_CH_IMPLOYAL} //Loyalty implants . += new /obj/screen/plane_master{plane = PLANE_CH_IMPTRACK} //Tracking implants . += new /obj/screen/plane_master{plane = PLANE_CH_IMPCHEM} //Chemical implants . += new /obj/screen/plane_master{plane = PLANE_CH_SPECIAL} //"Special" role stuff . += new /obj/screen/plane_master{plane = PLANE_CH_STATUS_OOC} //OOC status HUD . += new /obj/screen/plane_master{plane = PLANE_ADMIN1} //For admin use . += new /obj/screen/plane_master{plane = PLANE_ADMIN2} //For admin use . += new /obj/screen/plane_master{plane = PLANE_ADMIN3} //For admin use . += new /obj/screen/plane_master{plane = PLANE_MESONS} //Meson-specific things like open ceilings. . += new /obj/screen/plane_master{plane = PLANE_BUILDMODE} //Things that only show up while in build mode . += new /obj/screen/plane_master{plane = PLANE_JANHUD} // Real tangible stuff planes . += new /obj/screen/plane_master/main{plane = TURF_PLANE} . += new /obj/screen/plane_master/main{plane = OBJ_PLANE} . += new /obj/screen/plane_master/main{plane = MOB_PLANE} . += new /obj/screen/plane_master/cloaked //Cloaked atoms! //VOREStation Add - Random other plane masters . += new /obj/screen/plane_master{plane = PLANE_CH_STATUS_R} //Right-side status icon . += new /obj/screen/plane_master{plane = PLANE_CH_HEALTH_VR} //Health bar but transparent at 100 . += new /obj/screen/plane_master{plane = PLANE_CH_BACKUP} //Backup implant status . += new /obj/screen/plane_master{plane = PLANE_CH_VANTAG} //Vore Antags . += new /obj/screen/plane_master{plane = PLANE_CH_STOMACH} //Stomachs . += new /obj/screen/plane_master{plane = PLANE_AUGMENTED} //Augmented reality . += new /obj/screen/plane_master{plane = PLANE_SOULCATCHER} //Soulcatcher //VOREStation Add End /proc/CallAsync(datum/source, proctype, list/arguments) set waitfor = FALSE return call(source, proctype)(arglist(arguments)) /proc/describeThis(var/datum/D) if(istype(D)) var/msg = "[D.type] - [D]" if(isatom(D)) var/atom/A = D if(!A.z) msg += " - Coords unavailable (in contents?)" if(ismovable(A)) var/turf/T = get_turf(A) if(T) msg += " - Parent turf: [T.x],[T.y],[T.z]" else msg += " - In nullspace" else msg += " - In nullspace" else msg += "- [A.x],[A.y],[A.z]" return msg else if(isnull(D)) return "NULL" else if(istext(D)) return "TEXT: [D]" else if(isnum(D)) return "NUM: [D]" else if(ispath(D)) return "PATH: [D]" else if(islist(D)) return "LIST: [D]" else if(isclient(D)) return "CLIENT: [D]" else return "Unknown data type: [D]" /** * - is_valid_z_level * * Checks if source_loc and checking_loc is both on the station, or on the same z level. * This is because the station's several levels aren't considered the same z, so multi-z stations need this special case. * * Args: * source_loc - turf of the source we're comparing. * checking_loc - turf we are comparing to source_loc. * * returns TRUE if connection is valid, FALSE otherwise. */ /proc/is_valid_z_level(turf/source_loc, turf/checking_loc) return is_on_same_plane_or_station(source_loc.z, checking_loc.z) /** * divides a list of materials uniformly among all contents of the target_object recursively * Used to set materials of printed items with their design cost by taking into consideration their already existing materials * e.g. if 12 iron is to be divided uniformly among 2 objects A, B who's current iron contents are 3 & 7 * Then first we normalize those values i.e. find their weights to decide who gets an higher share of iron * total_sum = 3 + 7 = 10, A = 3/10 = 0.3, B = 7/10 = 0.7 * Then we finally multiply those weights with the user value of 12 we get * A = 0.3 * 12 = 3.6, B = 0.7 * 12 = 8.4 i.e. 3.6 + 8.4 = 12!! * Off course we round the values so we don't have to deal with floating point materials so the actual value * ends being less but that's not an issue * Arguments * * * [custom_materials][list] - the list of materials to set for the object * * multiplier - multiplier passed to set_custom_materials * * [target_object][atom] - the target object who's custom materials we are trying to modify */ /proc/split_materials_uniformly(list/custom_materials, multiplier, obj/target_object) target_object.set_custom_materials(custom_materials) // if(!length(target_object.contents)) //most common case where the object is just 1 thing // target_object.set_custom_materials(custom_materials, multiplier) // return // //Step 1: Get recursive contents of all objects, only filter obj cause that what's material container accepts // var/list/reccursive_contents = target_object.get_all_contents_type(/obj/item) // //Step 2: find the sum of each material type per object and record their amounts into an 2D list // var/list/material_map_sum = list() // var/list/material_map_amounts = list() // for(var/obj/object as anything in reccursive_contents) // var/list/item_materials = object.matter // for(var/mat as anything in custom_materials) // var/mat_amount = 1 //no materials mean we assign this default amount // if(length(item_materials)) // mat_amount = item_materials[mat] || 1 //if this object doesn't have our material type then assign a default value of 1 // //record the sum of mats for normalizing // material_map_sum[mat] += mat_amount // //record the material amount for each item into an 2D list // var/list/mat_list_per_item = material_map_amounts[mat] // if(isnull(mat_list_per_item)) // material_map_amounts[mat] = list(mat_amount) // else // mat_list_per_item += mat_amount // //Step 3: normalize & scale material_map_amounts with material_map_sum // for(var/mat as anything in material_map_amounts) // var/mat_sum = material_map_sum[mat] // var/list/mat_per_item = material_map_amounts[mat] // for(var/i in 1 to mat_per_item.len) // mat_per_item[i] = (mat_per_item[i] / mat_sum) * custom_materials[mat] // //Step 4 flatten the 2D list and assign the final values to each atom // var/index = 1 // for(var/obj/object as anything in reccursive_contents) // var/list/final_material_list = list() // for(var/mat as anything in material_map_amounts) // var/list/mat_per_item = material_map_amounts[mat] // final_material_list[mat] = mat_per_item[index] // object.set_custom_materials(final_material_list, multiplier) // index += 1