//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 /* * A large number of misc global procs. */ /* Get the direction of startObj relative to endObj. * Return values: To the right, 1. Below, 2. To the left, 3. Above, 4. Not found adjacent in cardinal directions, 0. */ /proc/getRelativeDirection(var/atom/movable/startObj, var/atom/movable/endObj) if(endObj.x == startObj.x + 1 && endObj.y == startObj.y) return EAST if(endObj.x == startObj.x - 1 && endObj.y == startObj.y) return WEST if(endObj.y == startObj.y + 1 && endObj.x == startObj.x) return NORTH if(endObj.y == startObj.y - 1 && endObj.x == startObj.x) return SOUTH return 0 //Inverts the colour of an HTML string /proc/invertHTML(HTMLstring) if (!( istext(HTMLstring) )) CRASH("Given non-text argument!") return else if (length(HTMLstring) != 7) CRASH("Given non-HTML argument!") return var/textr = copytext(HTMLstring, 2, 4) var/textg = copytext(HTMLstring, 4, 6) var/textb = copytext(HTMLstring, 6, 8) var/r = hex2num(textr) var/g = hex2num(textg) var/b = hex2num(textb) textr = num2hex(255 - r) textg = num2hex(255 - g) textb = num2hex(255 - b) if (length(textr) < 2) textr = text("0[]", textr) if (length(textg) < 2) textr = text("0[]", textg) if (length(textb) < 2) textr = text("0[]", textb) return text("#[][][]", textr, textg, textb) return //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 = 0, errorx = 0, errory = 0, eoffsetx = 0, eoffsety = 0) /* Location where the teleport begins, target that will teleport, distance to go, density checking 0/1(yes/no). Random error in tile placement x, error in tile placement y, and block offset. Block offset tells the proc how to place the box. Behind teleport location, relative to starting location, forward, etc. Negative values for offset are accepted, think of it in relation to North, -x is west, -y is south. Error defaults to positive. Turf and target are seperate in case you want to teleport some distance from a turf the target is not standing on or something. */ var/dirx = 0//Generic location finding variable. var/diry = 0 var/xoffset = 0//Generic counter for offset location. var/yoffset = 0 var/b1xerror = 0//Generic placing for point A in box. The lower left. var/b1yerror = 0 var/b2xerror = 0//Generic placing for point B in box. The upper right. var/b2yerror = 0 errorx = abs(errorx)//Error should never be negative. errory = abs(errory) //var/errorxy = round((errorx+errory)/2)//Used for diagonal boxes. switch(target.dir)//This can be done through equations but switch is the simpler method. And works fast to boot. //Directs on what values need modifying. if(1)//North diry+=distance yoffset+=eoffsety xoffset+=eoffsetx b1xerror-=errorx b1yerror-=errory b2xerror+=errorx b2yerror+=errory if(2)//South diry-=distance yoffset-=eoffsety xoffset+=eoffsetx b1xerror-=errorx b1yerror-=errory b2xerror+=errorx b2yerror+=errory if(4)//East dirx+=distance yoffset+=eoffsetx//Flipped. xoffset+=eoffsety b1xerror-=errory//Flipped. b1yerror-=errorx b2xerror+=errory b2yerror+=errorx if(8)//West dirx-=distance yoffset-=eoffsetx//Flipped. xoffset+=eoffsety b1xerror-=errory//Flipped. b1yerror-=errorx b2xerror+=errory b2yerror+=errorx var/turf/destination=locate(location.x+dirx,location.y+diry,location.z) if(destination)//If there is a destination. if(errorx||errory)//If errorx or y were specified. var/destination_list[] = list()//To add turfs to list. //destination_list = new() /*This will draw a block around the target turf, given what the error is. Specifying the values above will basically draw a different sort of block. If the values are the same, it will be a square. If they are different, it will be a rectengle. In either case, it will center based on offset. Offset is position from center. Offset always calculates in relation to direction faced. In other words, depending on the direction of the teleport, the offset should remain positioned in relation to destination.*/ var/turf/center = locate((destination.x+xoffset),(destination.y+yoffset),location.z)//So now, find the new center. //Now to find a box from center location and make that our destination. for(var/turf/T in block(locate(center.x+b1xerror,center.y+b1yerror,location.z), locate(center.x+b2xerror,center.y+b2yerror,location.z) )) if(density&&T.density) continue//If density was specified. if(T.x>world.maxx || T.x<1) continue//Don't want them to teleport off the map. if(T.y>world.maxy || T.y<1) continue destination_list += T if(destination_list.len) destination = pick(destination_list) else return else//Same deal here. if(density&&destination.density) return if(destination.x>world.maxx || destination.x<1) return if(destination.y>world.maxy || destination.y<1) return else return return destination /////////////////// //A* helpers procs /////////////////// // Returns true if a link between A and B is blocked // Movement through doors allowed if ID has access /proc/LinkBlockedWithAccess(turf/A, turf/B, obj/item/weapon/card/id/ID) if(A == null || B == null) return 1 var/adir = get_dir(A,B) var/rdir = get_dir(B,A) if(adir & (adir-1)) // diagonal var/turf/iStep = get_step(A,adir&(NORTH|SOUTH)) if(!iStep.density && !LinkBlockedWithAccess(A,iStep, ID) && !LinkBlockedWithAccess(iStep,B,ID)) return 0 var/turf/pStep = get_step(A,adir&(EAST|WEST)) if(!pStep.density && !LinkBlockedWithAccess(A,pStep,ID) && !LinkBlockedWithAccess(pStep,B,ID)) return 0 return 1 if(DirBlockedWithAccess(A,adir, ID)) return 1 if(DirBlockedWithAccess(B,rdir, ID)) return 1 for(var/obj/O in B) if(O.density && !istype(O, /obj/machinery/door) && !(O.flags & ON_BORDER)) return 1 return 0 // Returns true if direction is blocked from loc // Checks doors against access with given ID /proc/DirBlockedWithAccess(turf/loc,var/dir,var/obj/item/weapon/card/id/ID) for(var/obj/structure/window/D in loc) if(!D.density) continue if(D.dir == SOUTHWEST) return 1 //full-tile window if(D.dir == dir) return 1 //matching border window for(var/obj/machinery/door/D in loc) if(!D.CanAStarPass(ID,dir)) return 1 return 0 // Returns true if a link between A and B is blocked // Movement through doors allowed if door is open /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 & (adir-1)) //diagonal var/turf/iStep = get_step(A,adir & (NORTH|SOUTH)) //check the north/south component if(!iStep.density && !LinkBlocked(A,iStep) && !LinkBlocked(iStep,B)) return 0 var/turf/pStep = get_step(A,adir & (EAST|WEST)) //check the east/west component if(!pStep.density && !LinkBlocked(A,pStep) && !LinkBlocked(pStep,B)) return 0 return 1 if(DirBlocked(A,adir)) return 1 if(DirBlocked(B,rdir)) return 1 for(var/obj/O in B) if(O.density && !istype(O, /obj/machinery/door) && !(O.flags & ON_BORDER)) return 1 return 0 // Returns true if a link is blocked and neither location has something climbable on // Used for inventory checks /proc/LinkBlockedUnclimbable(turf/A, turf/B) if (!LinkBlocked(A, B)) return 0 for (var/obj/structure/S in A.contents) if(S.climbable) return 0 for (var/obj/structure/S in B.contents) if(S.climbable) return 0 return 1 // Returns true if direction is blocked from loc // Checks if doors are open /proc/DirBlocked(turf/loc,var/dir) for(var/obj/structure/window/D in loc) if(!D.density) continue if(D.is_fulltile()) return 1 if(D.dir == dir) return 1 for(var/obj/machinery/door/D in loc) if(!D.density)//if the door is open continue else return 1 // if closed, it's a real, air blocking door return 0 ///////////////////////////////////////////////////////////////////////// /proc/sign(x) return x!=0?x/abs(x):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 //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, ch, len = length(key) for (i = 7, 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]" /obj/proc/atmosanalyzer_scan(var/datum/gas_mixture/air_contents, mob/user, var/obj/target = src) var/obj/icon = target user.visible_message("[user] has used the analyzer on \icon[icon] [target].", "You use the analyzer on \icon[icon] [target].") var/pressure = air_contents.return_pressure() var/total_moles = air_contents.total_moles() user << "Results of analysis of \icon[icon] [target]." if(total_moles>0) var/o2_concentration = air_contents.oxygen/total_moles var/n2_concentration = air_contents.nitrogen/total_moles var/co2_concentration = air_contents.carbon_dioxide/total_moles var/plasma_concentration = air_contents.toxins/total_moles var/unknown_concentration = 1-(o2_concentration+n2_concentration+co2_concentration+plasma_concentration) user << "Pressure: [round(pressure,0.1)] kPa" user << "Nitrogen: [round(n2_concentration*100)] %" user << "Oxygen: [round(o2_concentration*100)] %" user << "CO2: [round(co2_concentration*100)] %" user << "Plasma: [round(plasma_concentration*100)] %" if(unknown_concentration>0.01) user << "Unknown: [round(unknown_concentration*100)] %" user << "Temperature: [round(air_contents.temperature-T0C)] °C" else user << "[target] is empty!" return //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(isrobot(src)) var/mob/living/silicon/robot/R = src if(oldname != real_name) R.notify_ai(3, oldname, newname) R.custom_name = newname R.updatename() if(oldname) //update the datacore records! This is goig to be a bit costly. for(var/list/L in list(data_core.general,data_core.medical,data_core.security,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/weapon/card/id) ) var/obj/item/weapon/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/device/pda) ) var/obj/item/device/pda/PDA = A if(PDA.owner == oldname) PDA.owner = newname PDA.name = "PDA-[newname] ([PDA.ownjob])" if(!search_id) break search_pda = 0 //Fixes renames not being reflected in objective text var/list/O = subtypesof(/datum/objective) var/length var/pos for(var/datum/objective/objective in O) if(objective.target != mind) continue length = lentext(oldname) pos = findtextEx(objective.explanation_text, oldname) objective.explanation_text = copytext(objective.explanation_text, 1, pos)+newname+copytext(objective.explanation_text, pos+length) 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 = input(src,"You are a [role]. Would you like to change your name to something else?", "Name change",oldname) as text if((world.time-time_passed)>300) return //took too long newname = reject_bad_name(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 player_list) if(M == src) continue if(!newname || M.real_name == newname) newname = null break if(newname) break //That's a suitable name! 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 //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("!","@","#","$","%","^","&","*")][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 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 = input("Unshackled borg signals detected:", "Borg selection", null, null) as null|anything in borgs 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 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 > A.connected_robots)) selected = A return selected /proc/select_active_ai(var/mob/user) var/list/ais = active_ais() if(ais.len) if(user) . = input(usr,"AI signals detected:", "AI selection") in ais else . = pick(ais) return . /proc/get_sorted_mobs() var/list/old_list = getmobs() var/list/AI_list = list() var/list/Dead_list = list() var/list/keyclient_list = list() var/list/key_list = list() var/list/logged_list = list() for(var/named in old_list) var/mob/M = old_list[named] if(issilicon(M)) AI_list |= M else if(isobserver(M) || M.stat == 2) Dead_list |= M else if(M.key && M.client) keyclient_list |= M else if(M.key) key_list |= M else logged_list |= M old_list.Remove(named) var/list/new_list = list() new_list += AI_list new_list += keyclient_list new_list += key_list new_list += logged_list new_list += Dead_list return new_list //Returns a list of all mobs with their name /proc/getmobs() var/list/mobs = sortmobs() var/list/names = list() var/list/creatures = list() var/list/namecounts = list() for(var/mob/M in mobs) var/name = M.name if (name in names) namecounts[name]++ name = "[name] ([namecounts[name]])" else names.Add(name) namecounts[name] = 1 if (M.real_name && M.real_name != M.name) name += " \[[M.real_name]\]" if (M.stat == 2) if(istype(M, /mob/dead/observer/)) name += " \[ghost\]" else name += " \[dead\]" creatures[name] = M return creatures //Orders mobs by type then by name /proc/sortmobs() var/list/moblist = list() var/list/sortmob = sortAtom(mob_list) for(var/mob/living/silicon/ai/M in sortmob) moblist.Add(M) if(M.eyeobj) moblist.Add(M.eyeobj) for(var/mob/living/silicon/pai/M in sortmob) moblist.Add(M) for(var/mob/living/silicon/robot/M in sortmob) moblist.Add(M) for(var/mob/living/carbon/human/M in sortmob) moblist.Add(M) for(var/mob/living/carbon/brain/M in sortmob) moblist.Add(M) for(var/mob/living/carbon/alien/M in sortmob) moblist.Add(M) for(var/mob/dead/observer/M in sortmob) moblist.Add(M) for(var/mob/new_player/M in sortmob) moblist.Add(M) for(var/mob/living/carbon/slime/M in sortmob) moblist.Add(M) for(var/mob/living/simple_animal/M in sortmob) moblist.Add(M) return moblist //E = MC^2 /proc/convert2energy(var/M) var/E = M*(SPEED_OF_LIGHT_SQ) return E //M = E/C^2 /proc/convert2mass(var/E) var/M = E/(SPEED_OF_LIGHT_SQ) return M //Forces a variable to be posative /proc/modulus(var/M) if(M >= 0) return M if(M < 0) return -M /proc/get_mob_by_ckey(key) if(!key) return for(var/mob/M in mob_list) if(M.ckey == key) return M // Returns the atom sitting on the turf. // For example, using this on a disk, which is in a bag, on a mob, will return the mob because it's on the turf. /proc/get_atom_on_turf(var/atom/movable/M) var/atom/loc = M while(loc && loc.loc && !istype(loc.loc, /turf/)) loc = loc.loc return loc // 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. // 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) //Makes sure MIDDLE is between LOW and HIGH. If not, it adjusts it. Returns the adjusted value. /proc/between(var/low, var/middle, var/high) return max(min(middle, high), low) proc/arctan(x) var/y=arcsin(x/sqrt(1+x*x)) return y //returns random gauss number proc/GaussRand(var/sigma) var/x,y,rsq do x=2*rand()-1 y=2*rand()-1 rsq=x*x+y*y while(rsq>1 || !rsq) return sigma*y*sqrt(-2*log(rsq)/rsq) //returns random gauss number, rounded to 'roundto' proc/GaussRandRound(var/sigma,var/roundto) return round(GaussRand(sigma),roundto) proc/anim(turf/location as turf,target as mob|obj,a_icon,a_icon_state as text,flick_anim as text,sleeptime = 0,direction as num) //This proc throws up either an icon or an animation for a specified amount of time. //The variables should be apparent enough. var/atom/movable/overlay/animation = new(location) if(direction) animation.dir = direction animation.icon = a_icon animation.layer = target:layer+1 if(a_icon_state) animation.icon_state = a_icon_state else animation.icon_state = "blank" animation.master = target flick(flick_anim, animation) sleep(max(sleeptime, 15)) qdel(animation) //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 couldnt be arsed to do actual raycasting :I This is horribly inaccurate. var/turf/current = get_turf(source) var/turf/target_turf = get_turf(target) var/steps = 0 while(current != target_turf) if(steps > length) return 0 if(current.opacity) return 0 for(var/atom/A in current) if(A.opacity) return 0 current = get_step_towards(current, target_turf) steps++ return 1 /proc/is_blocked_turf(var/turf/T) var/cant_pass = 0 if(T.density) cant_pass = 1 for(var/atom/A in T) if(A.density)//&&A.anchored cant_pass = 1 return cant_pass /proc/get_step_towards2(var/atom/ref , var/atom/trg) var/base_dir = get_dir(ref, get_step_towards(ref,trg)) var/turf/temp = get_step_towards(ref,trg) if(is_blocked_turf(temp)) var/dir_alt1 = turn(base_dir, 90) var/dir_alt2 = turn(base_dir, -90) var/turf/turf_last1 = temp var/turf/turf_last2 = temp var/free_tile = null var/breakpoint = 0 while(!free_tile && breakpoint < 10) if(!is_blocked_turf(turf_last1)) free_tile = turf_last1 break if(!is_blocked_turf(turf_last2)) free_tile = turf_last2 break turf_last1 = get_step(turf_last1,dir_alt1) turf_last2 = get_step(turf_last2,dir_alt2) breakpoint++ if(!free_tile) return get_step(ref, base_dir) else return get_step_towards(ref,free_tile) else return get_step(ref, base_dir) /proc/do_mob(var/mob/user , var/mob/target, var/time = 30, numticks = 5, var/uninterruptible = 0) //This is quite an ugly solution but i refuse to use the old request system. if(!user || !target) return 0 if(numticks == 0) return 0 var/user_loc = user.loc var/target_loc = target.loc var/holding = user.get_active_hand() var/timefraction = round(time/numticks) var/image/progbar for(var/i = 1 to numticks) if(user.client) progbar = make_progress_bar(i, numticks, target) user.client.images |= progbar sleep(timefraction) if(!user || !target) if(user && user.client) user.client.images -= progbar return 0 if (!uninterruptible && (user.loc != user_loc || target.loc != target_loc || user.get_active_hand() != holding || user.stat || user.stunned || user.weakened || user.paralysis || user.lying)) if(user && user.client) user.client.images -= progbar return 0 if(user && user.client) user.client.images -= progbar if(user && user.client) user.client.images -= progbar return 1 /proc/make_progress_bar(var/current_number, var/goal_number, var/atom/target) if(current_number && goal_number && target) var/image/progbar progbar = image("icon" = 'icons/effects/doafter_icon.dmi', "loc" = target, "icon_state" = "prog_bar_0") progbar.icon_state = "prog_bar_[round(((current_number / goal_number) * 100), 10)]" progbar.pixel_y = 32 return progbar /proc/do_after(mob/user, delay, numticks = 5, needhand = 1, atom/target = null) if(!user) return 0 if(numticks == 0) return 0 var/atom/Tloc = null if(target) Tloc = target.loc var/delayfraction = round(delay/numticks) var/atom/Uloc = user.loc var/holding = user.get_active_hand() var/holdingnull = 1 //User is not holding anything if(holding) holdingnull = 0 //User is holding a tool of some kind var/image/progbar for (var/i = 1 to numticks) if(user.client) progbar = make_progress_bar(i, numticks, target) user.client.images |= progbar sleep(delayfraction) if(!user || user.stat || user.weakened || user.stunned || !(user.loc == Uloc)) if(user && user.client) user.client.images -= progbar return 0 if(Tloc && (!target || Tloc != target.loc)) //Tloc not set when we don't want to track target if(user && user.client) user.client.images -= progbar return 0 // Target no longer exists or has moved if(needhand) //This might seem like an odd check, but you can still need a hand even when it's empty //i.e the hand is used to insert some item/tool into the construction if(!holdingnull) if(!holding) if(user && user.client) user.client.images -= progbar return 0 if(user.get_active_hand() != holding) if(user && user.client) user.client.images -= progbar return 0 if(user && user.client) user.client.images -= progbar if(user && user.client) user.client.images -= progbar if(user && user.client) user.client.images -= progbar return 1 //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 return areas //Returns: all the areas in the world, sorted. /proc/return_sorted_areas() return sortAtom(return_areas()) //Takes: Area type as text string or as typepath OR an instance of the area. //Returns: A list of all areas of that type in the world. /proc/get_areas(var/areatype) if(!areatype) return null if(istext(areatype)) areatype = text2path(areatype) if(isarea(areatype)) var/area/areatemp = areatype areatype = areatemp.type var/list/areas = new/list() for(var/area/N in world) if(istype(N, areatype)) areas += N return areas //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: 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 /datum/coords //Simple datum for storing coordinates. var/x_pos = null var/y_pos = null var/z_pos = null /area/proc/move_contents_to(var/area/A, var/turftoleave=null, var/direction = null) //Takes: Area. Optional: turf type to leave behind. //Returns: Nothing. //Notes: Attempts to move the contents of one area to another area. // Movement based on lower left corner. Tiles that do not fit // into the new area will not be moved. if(!A || !src) return 0 var/list/turfs_src = get_area_turfs(src.type) var/list/turfs_trg = get_area_turfs(A.type) var/src_min_x = 0 var/src_min_y = 0 for (var/turf/T in turfs_src) if(T.x < src_min_x || !src_min_x) src_min_x = T.x if(T.y < src_min_y || !src_min_y) src_min_y = T.y var/trg_min_x = 0 var/trg_min_y = 0 for (var/turf/T in turfs_trg) if(T.x < trg_min_x || !trg_min_x) trg_min_x = T.x if(T.y < trg_min_y || !trg_min_y) trg_min_y = T.y var/list/refined_src = new/list() for(var/turf/T in turfs_src) refined_src += T refined_src[T] = new/datum/coords var/datum/coords/C = refined_src[T] C.x_pos = (T.x - src_min_x) C.y_pos = (T.y - src_min_y) var/list/refined_trg = new/list() for(var/turf/T in turfs_trg) refined_trg += T refined_trg[T] = new/datum/coords var/datum/coords/C = refined_trg[T] C.x_pos = (T.x - trg_min_x) C.y_pos = (T.y - trg_min_y) var/list/fromupdate = new/list() var/list/toupdate = new/list() moving: for (var/turf/T in refined_src) var/datum/coords/C_src = refined_src[T] for (var/turf/B in refined_trg) var/datum/coords/C_trg = refined_trg[B] if(C_src.x_pos == C_trg.x_pos && C_src.y_pos == C_trg.y_pos) var/old_dir1 = T.dir var/old_icon_state1 = T.icon_state var/old_icon1 = T.icon var/turf/X = B.ChangeTurf(T.type) X.dir = old_dir1 X.icon_state = old_icon_state1 X.icon = old_icon1 //Shuttle floors are in shuttle.dmi while the defaults are floors.dmi // Give the new turf our air, if simulated if(istype(X, /turf/simulated) && istype(T, /turf/simulated)) var/turf/simulated/sim = X sim.copy_air_with_tile(T) /* Quick visual fix for some weird shuttle corner artefacts when on transit space tiles */ if(direction && findtext(X.icon_state, "swall_s")) // Spawn a new shuttle corner object var/obj/corner = new() corner.loc = X corner.density = 1 corner.anchored = 1 corner.icon = X.icon corner.icon_state = replacetext(X.icon_state, "_s", "_f") corner.tag = "delete me" corner.name = "wall" // Find a new turf to take on the property of var/turf/nextturf = get_step(corner, direction) if(!nextturf || !istype(nextturf, /turf/space)) nextturf = get_step(corner, turn(direction, 180)) // Take on the icon of a neighboring scrolling space icon X.icon = nextturf.icon X.icon_state = nextturf.icon_state for(var/obj/O in T) // Reset the shuttle corners if(O.tag == "delete me") X.icon = 'icons/turf/shuttle.dmi' X.icon_state = replacetext(O.icon_state, "_f", "_s") // revert the turf to the old icon_state X.name = "wall" qdel(O) // prevents multiple shuttle corners from stacking continue if(!istype(O,/obj)) continue O.loc.Exited(O) O.setLoc(X,teleported=1) O.loc.Entered(O) for(var/mob/M in T) if(!M.move_on_shuttle) continue M.loc = X // var/area/AR = X.loc // if(AR.lighting_use_dynamic) //TODO: rewrite this code so it's not messed by lighting ~Carn // X.opacity = !X.opacity // X.set_opacity(!X.opacity) toupdate += X if(turftoleave) fromupdate += T.ChangeTurf(turftoleave) else T.ChangeTurf(/turf/space) refined_src -= T refined_trg -= B continue moving if(toupdate.len) for(var/turf/simulated/T1 in toupdate) air_master.remove_from_active(T1) T1.CalculateAdjacentTurfs() air_master.add_to_active(T1,1) if(fromupdate.len) for(var/turf/simulated/T2 in fromupdate) air_master.remove_from_active(T2) T2.CalculateAdjacentTurfs() air_master.add_to_active(T2,1) proc/DuplicateObject(obj/original, var/perfectcopy = 0 , var/sameloc = 0) if(!original) return null var/obj/O = null if(sameloc) O=new original.type(original.loc) else O=new original.type(locate(0,0,0)) if(perfectcopy) if((O) && (original)) for(var/V in original.vars) if(!(V in list("type","loc","locs","vars", "parent", "parent_type","verbs","ckey","key"))) O.vars[V] = original.vars[V] return O /area/proc/copy_contents_to(var/area/A , var/platingRequired = 0 ) //Takes: Area. Optional: If it should copy to areas that don't have plating //Returns: Nothing. //Notes: Attempts to move the contents of one area to another area. // Movement based on lower left corner. Tiles that do not fit // into the new area will not be moved. if(!A || !src) return 0 var/list/turfs_src = get_area_turfs(src.type) var/list/turfs_trg = get_area_turfs(A.type) var/src_min_x = 0 var/src_min_y = 0 for (var/turf/T in turfs_src) if(T.x < src_min_x || !src_min_x) src_min_x = T.x if(T.y < src_min_y || !src_min_y) src_min_y = T.y var/trg_min_x = 0 var/trg_min_y = 0 for (var/turf/T in turfs_trg) if(T.x < trg_min_x || !trg_min_x) trg_min_x = T.x if(T.y < trg_min_y || !trg_min_y) trg_min_y = T.y var/list/refined_src = new/list() for(var/turf/T in turfs_src) refined_src += T refined_src[T] = new/datum/coords var/datum/coords/C = refined_src[T] C.x_pos = (T.x - src_min_x) C.y_pos = (T.y - src_min_y) var/list/refined_trg = new/list() for(var/turf/T in turfs_trg) refined_trg += T refined_trg[T] = new/datum/coords var/datum/coords/C = refined_trg[T] C.x_pos = (T.x - trg_min_x) C.y_pos = (T.y - trg_min_y) var/list/toupdate = new/list() var/copiedobjs = list() moving: for (var/turf/T in refined_src) var/datum/coords/C_src = refined_src[T] for (var/turf/B in refined_trg) var/datum/coords/C_trg = refined_trg[B] if(C_src.x_pos == C_trg.x_pos && C_src.y_pos == C_trg.y_pos) var/old_dir1 = T.dir var/old_icon_state1 = T.icon_state var/old_icon1 = T.icon if(platingRequired) if(istype(B, /turf/space)) continue moving var/turf/X = new T.type(B) X.dir = old_dir1 X.icon_state = old_icon_state1 X.icon = old_icon1 //Shuttle floors are in shuttle.dmi while the defaults are floors.dmi var/list/objs = new/list() var/list/newobjs = new/list() var/list/mobs = new/list() var/list/newmobs = new/list() for(var/obj/O in T) if(!istype(O,/obj)) continue objs += O for(var/obj/O in objs) newobjs += DuplicateObject(O , 1) for(var/obj/O in newobjs) O.loc = X for(var/mob/M in T) if(!M.move_on_shuttle) continue mobs += M for(var/mob/M in mobs) newmobs += DuplicateObject(M , 1) for(var/mob/M in newmobs) M.loc = X copiedobjs += newobjs copiedobjs += newmobs for(var/V in T.vars) if(!(V in list("type","loc","locs","vars", "parent", "parent_type","verbs","ckey","key","x","y","z","contents", "luminosity"))) X.vars[V] = T.vars[V] // var/area/AR = X.loc // if(AR.lighting_use_dynamic) // X.opacity = !X.opacity // X.sd_set_opacity(!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) T1.CalculateAdjacentTurfs() air_master.add_to_active(T1,1) return copiedobjs proc/get_cardinal_dir(atom/A, atom/B) var/dx = abs(B.x - A.x) var/dy = abs(B.y - A.y) return get_dir(A, B) & (rand() * (dx+dy) < dy ? 3 : 12) //chances are 1:value. anyprob(1) will always return true proc/anyprob(value) return (rand(1,value)==value) proc/view_or_range(distance = world.view , center = usr , type) switch(type) if("view") . = view(distance,center) if("range") . = range(distance,center) return proc/oview_or_orange(distance = world.view , center = usr , type) switch(type) if("view") . = oview(distance,center) if("range") . = orange(distance,center) return proc/get_mob_with_client_list() var/list/mobs = list() for(var/mob/M in mob_list) if (M.client) mobs += M return mobs /proc/parse_zone(zone) if(zone == "r_hand") return "right hand" else if (zone == "l_hand") return "left hand" else 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 if (zone == "l_foot") return "left foot" else if (zone == "r_foot") return "right foot" else if (zone == "l_hand") return "left hand" else if (zone == "r_hand") return "right hand" else if (zone == "l_foot") return "left foot" else if (zone == "r_foot") return "right foot" else return zone /proc/get_turf(atom/A) if (!istype(A)) return for(A, A && !isturf(A), A=A.loc); //semicolon is for the empty statement return A //Finds the distance between two atoms, in pixels //centered = 0 counts from turf edge to edge //centered = 1 counts from turf center to turf center //of course mathematically this is just adding world.icon_size on again /proc/getPixelDistance(var/atom/A, var/atom/B, var/centered = 1) if(!istype(A)||!istype(B)) return 0 . = bounds_dist(A, B) + sqrt((((A.pixel_x+B.pixel_x)**2) + ((A.pixel_y+B.pixel_y)**2))) if(centered) . += world.icon_size /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 var/global/list/common_tools = list( /obj/item/stack/cable_coil, /obj/item/weapon/wrench, /obj/item/weapon/weldingtool, /obj/item/weapon/screwdriver, /obj/item/weapon/wirecutters, /obj/item/device/multitool, /obj/item/weapon/crowbar) /proc/istool(O) if(O && is_type_in_list(O, common_tools)) return 1 return 0 /proc/iswrench(O) if(istype(O, /obj/item/weapon/wrench)) return 1 return 0 /proc/iswelder(O) if(istype(O, /obj/item/weapon/weldingtool)) return 1 return 0 /proc/iscoil(O) if(istype(O, /obj/item/stack/cable_coil)) return 1 return 0 /proc/iswirecutter(O) if(istype(O, /obj/item/weapon/wirecutters)) return 1 return 0 /proc/isscrewdriver(O) if(istype(O, /obj/item/weapon/screwdriver)) return 1 return 0 /proc/ismultitool(O) if(istype(O, /obj/item/device/multitool)) return 1 return 0 /proc/iscrowbar(O) if(istype(O, /obj/item/weapon/crowbar)) return 1 return 0 /proc/iswire(O) if(istype(O, /obj/item/stack/cable_coil)) return 1 return 0 /proc/is_hot(obj/item/W as obj) if(istype(W, /obj/item/weapon/weldingtool)) var/obj/item/weapon/weldingtool/O = W if(O.isOn()) return 3800 else return 0 if(istype(W, /obj/item/weapon/lighter)) var/obj/item/weapon/lighter/O = W if(O.lit) return 1500 else return 0 if(istype(W, /obj/item/weapon/match)) var/obj/item/weapon/match/O = W if(O.lit == 1) return 1000 else return 0 if(istype(W, /obj/item/clothing/mask/cigarette)) var/obj/item/clothing/mask/cigarette/O = W if(O.lit) return 1000 else return 0 if(istype(W, /obj/item/candle)) var/obj/item/candle/O = W if(O.lit) return 1000 else return 0 if(istype(W, /obj/item/device/flashlight/flare)) var/obj/item/device/flashlight/flare/O = W if(O.on) return 1000 else return 0 if(istype(W, /obj/item/weapon/gun/energy/plasmacutter)) return 3800 if(istype(W, /obj/item/weapon/melee/energy)) var/obj/item/weapon/melee/energy/O = W if(O.active) return 3500 else return 0 if(istype(W, /obj/item/device/assembly/igniter)) return 1000 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 0 if (O.sharp) return 1 if (O.edge) return 1 return 0 //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 0 if (O.edge) return 1 return 0 //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(!istype(W)) return 0 if(!W) return 0 if(W.sharp) return 1 return ( \ W.sharp || \ istype(W, /obj/item/weapon/screwdriver) || \ istype(W, /obj/item/weapon/pen) || \ istype(W, /obj/item/weapon/weldingtool) || \ istype(W, /obj/item/weapon/lighter/zippo) || \ istype(W, /obj/item/weapon/match) || \ istype(W, /obj/item/clothing/mask/cigarette) || \ istype(W, /obj/item/weapon/shovel) \ ) /proc/is_surgery_tool(obj/item/W as obj) return ( \ istype(W, /obj/item/weapon/scalpel) || \ istype(W, /obj/item/weapon/hemostat) || \ istype(W, /obj/item/weapon/retractor) || \ istype(W, /obj/item/weapon/cautery) || \ istype(W, /obj/item/weapon/bonegel) || \ istype(W, /obj/item/weapon/bonesetter) ) //check if mob is lying down on something we can operate him on. /proc/can_operate(mob/living/carbon/M) return (locate(/obj/machinery/optable, M.loc) && M.resting) || \ (locate(/obj/structure/stool/bed/roller, M.loc) && \ (M.buckled || M.lying || M.weakened || M.stunned || M.paralysis || M.sleeping || M.stat)) && prob(75) || \ (locate(/obj/structure/table/, M.loc) && \ (M.lying || M.weakened || M.stunned || M.paralysis || M.sleeping || M.stat) && prob(66)) /proc/reverse_direction(var/dir) switch(dir) if(NORTH) return SOUTH if(NORTHEAST) return SOUTHWEST if(EAST) return WEST if(SOUTHEAST) return NORTHWEST if(SOUTH) return NORTH if(SOUTHWEST) return NORTHEAST if(WEST) return EAST if(NORTHWEST) return SOUTHEAST /* Checks if that loc and dir has a item on the wall */ var/list/WALLITEMS = list( "/obj/machinery/power/apc", "/obj/machinery/alarm", "/obj/item/device/radio/intercom", "/obj/structure/extinguisher_cabinet", "/obj/structure/reagent_dispensers/peppertank", "/obj/machinery/status_display", "/obj/machinery/requests_console", "/obj/machinery/light_switch", "/obj/effect/sign", "/obj/machinery/newscaster", "/obj/machinery/firealarm", "/obj/structure/noticeboard", "/obj/machinery/door_control", "/obj/machinery/computer/security/telescreen", "/obj/machinery/embedded_controller/radio/simple_vent_controller", "/obj/item/weapon/storage/secure/safe", "/obj/machinery/door_timer", "/obj/machinery/flasher", "/obj/machinery/keycard_auth", "/obj/structure/mirror", "/obj/structure/closet/fireaxecabinet", "/obj/machinery/computer/security/telescreen/entertainment", "/obj/structure/sign" ) /proc/gotwallitem(loc, dir) for(var/obj/O in loc) for(var/item in WALLITEMS) if(istype(O, text2path(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, text2path(item))) if(abs(O.pixel_x) <= 10 && abs(O.pixel_y) <= 10) return 1 return 0 proc/get_angle(atom/a, atom/b) return atan2(b.y - a.y, b.x - a.x) 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/rotate_icon(file, state, step = 1, aa = FALSE) var icon/base = icon(file, state) var w, h, w2, h2 if(aa) aa ++ w = base.Width() w2 = w * aa h = base.Height() h2 = h * aa var icon{result = icon(base); temp} for(var/angle in 0 to 360 step step) if(angle == 0 ) continue if(angle == 360) continue temp = icon(base) if(aa) temp.Scale(w2, h2) temp.Turn(angle) if(aa) temp.Scale(w, h) result.Insert(temp, "[angle]") return result /proc/format_text(text) return replacetext(replacetext(text,"\proper ",""),"\improper ","") /* Standard way to write links -Sayu */ /proc/topic_link(var/datum/D, var/arglist, var/content) if(istype(arglist,/list)) arglist = list2params(arglist) return "[content]" /proc/get_location_accessible(mob/M, location) var/covered_locations = 0 //based on body_parts_covered var/face_covered = 0 //based on flags_inv var/eyesmouth_covered = 0 //based on flags if(iscarbon(M)) var/mob/living/carbon/C = M for(var/obj/item/clothing/I in list(C.back, C.wear_mask)) covered_locations |= I.body_parts_covered face_covered |= I.flags_inv eyesmouth_covered |= I.flags if(ishuman(C)) var/mob/living/carbon/human/H = C for(var/obj/item/I in list(H.wear_suit, H.w_uniform, H.shoes, H.belt, H.gloves, H.glasses, H.head, H.r_ear, H.l_ear)) covered_locations |= I.body_parts_covered face_covered |= I.flags_inv eyesmouth_covered |= I.flags switch(location) if("head") if(covered_locations & HEAD) return 0 if("eyes") if(covered_locations & HEAD || face_covered & HIDEEYES || eyesmouth_covered & GLASSESCOVERSEYES) return 0 if("mouth") if(covered_locations & HEAD || face_covered & HIDEFACE || eyesmouth_covered & MASKCOVERSMOUTH) return 0 if("chest") if(covered_locations & UPPER_TORSO) return 0 if("groin") if(covered_locations & LOWER_TORSO) return 0 if("l_arm") if(covered_locations & ARM_LEFT) return 0 if("r_arm") if(covered_locations & ARM_RIGHT) return 0 if("l_leg") if(covered_locations & LEG_LEFT) return 0 if("r_leg") if(covered_locations & LEG_RIGHT) return 0 if("l_hand") if(covered_locations & HAND_LEFT) return 0 if("r_hand") if(covered_locations & HAND_RIGHT) return 0 if("l_foot") if(covered_locations & FOOT_LEFT) return 0 if("r_foot") if(covered_locations & FOOT_RIGHT) return 0 return 1 proc/check_target_facings(mob/living/initator, mob/living/target) /*This can be used to add additional effects on interactions between mobs depending on how the mobs are facing each other, such as adding a crit damage to blows to the back of a guy's head. Given how click code currently works (Nov '13), the initiating mob will be facing the target mob most of the time That said, this proc should not be used if the change facing proc of the click code is overriden at the same time*/ if(!!isliving(target) || target.lying || istype(target, /mob/living/silicon/pai)) //Make sure we are not doing this for things that can't have a logical direction to the players given that the target would be on their side return if(initator.dir == target.dir) //mobs are facing the same direction return 1 if(initator.dir + 4 == target.dir || initator.dir - 4 == target.dir) //mobs are facing each other return 2 if(initator.dir + 2 == target.dir || initator.dir - 2 == target.dir || initator.dir + 6 == target.dir || initator.dir - 6 == target.dir) //Initating mob is looking at the target, while the target mob is looking in a direction perpendicular to the 1st return 3 atom/proc/GetTypeInAllContents(typepath) var/list/processing_list = list(src) var/list/processed = list() var/atom/found = null while(processing_list.len && found==null) var/atom/A = processing_list[1] if(istype(A, typepath)) found = A processing_list -= A for(var/atom/a in A) if(!(a in processed)) processing_list |= a processed |= A return found /proc/random_step(atom/movable/AM, steps, chance) var/initial_chance = chance while(steps > 0) if(prob(chance)) step(AM, pick(alldirs)) chance = max(chance - (initial_chance / steps), 0) steps-- /proc/get_random_colour(var/simple, var/lower, var/upper) 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/get_distant_turf(var/turf/T,var/direction,var/distance) if(!T || !direction || !distance) return var/dest_x = T.x var/dest_y = T.y var/dest_z = T.z if(direction & NORTH) dest_y = min(world.maxy, dest_y+distance) if(direction & SOUTH) dest_y = max(0, dest_y-distance) if(direction & EAST) dest_x = min(world.maxy, dest_x+distance) if(direction & WEST) dest_x = max(0, dest_x-distance) return locate(dest_x,dest_y,dest_z) var/mob/dview/dview_mob = new //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 dview_mob.loc = center dview_mob.see_invisible = invis_flags . = view(range, dview_mob) dview_mob.loc = null /mob/dview invisibility = 101 density = 0 anchored = 1 simulated = 0 see_in_dark = 1e6 /mob/dview/New() //For whatever reason, if this isn't called, then BYOND will throw a type mismatch runtime when attempting to add this to the mobs list. -Fox /proc/IsValidSrc(var/A) if(istype(A, /datum)) var/datum/B = A return isnull(B.gcDestroyed) if(istype(A, /client)) return 1 return 0 //ORBITS /atom/movable/var/atom/orbiting = null //This is just so you can stop an orbit. //orbit() can run without it (swap orbiting for A) //but then you can never stop it and that's just silly. /atom/movable/proc/orbit(atom/A, radius = 10, clockwise = 1, angle_increment = 15) if(!istype(A)) return orbiting = A var/angle = 0 var/matrix/initial_transform = matrix(transform) spawn while(orbiting) loc = orbiting.loc angle += angle_increment var/matrix/shift = matrix(initial_transform) shift.Translate(radius,0) if(clockwise) shift.Turn(angle) else shift.Turn(-angle) animate(src,transform = shift,2) sleep(0.6) //the effect breaks above 0.6 delay animate(src,transform = initial_transform,2) /atom/movable/proc/stop_orbit() if(orbiting) loc = get_turf(orbiting) orbiting = null //Centers an image. //Requires: //The Image //The x dimension of the icon file used in the image //The y dimension of the icon file used in the image // eg: center_image(I, 32,32) // eg2: center_image(I, 96,96) /proc/center_image(var/image/I, x_dimension = 0, y_dimension = 0) if(!I) return if(!x_dimension || !y_dimension) return //Get out of here, punk ass kids calling procs needlessly if((x_dimension == world.icon_size) && (y_dimension == world.icon_size)) return I //Offset the image so that it's bottom left corner is shifted this many pixels //This makes it infinitely easier to draw larger inhands/images larger than world.iconsize //but still use them in game var/x_offset = -((x_dimension/world.icon_size)-1)*(world.icon_size*0.5) var/y_offset = -((y_dimension/world.icon_size)-1)*(world.icon_size*0.5) //Correct values under world.icon_size if(x_dimension < world.icon_size) x_offset *= -1 if(y_dimension < world.icon_size) y_offset *= -1 I.pixel_x = x_offset I.pixel_y = y_offset return I