mirror of
https://github.com/vgstation-coders/vgstation13.git
synced 2025-12-09 16:14:13 +00:00
1456 lines
40 KiB
Plaintext
1456 lines
40 KiB
Plaintext
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31
|
|
|
|
/*
|
|
* A large number of misc global procs.
|
|
*/
|
|
|
|
/proc/Get_Angle(atom/movable/start,atom/movable/end)//For beams.
|
|
if(!start || !end) return 0
|
|
var/dy
|
|
var/dx
|
|
dy=(32*end.y+end.pixel_y)-(32*start.y+start.pixel_y)
|
|
dx=(32*end.x+end.pixel_x)-(32*start.x+start.pixel_x)
|
|
if(!dy)
|
|
return (dx>=0)?90:270
|
|
.=arctan(dx/dy)
|
|
if(dy<0)
|
|
.+=180
|
|
else if(dx<0)
|
|
.+=360
|
|
|
|
//Returns location. Returns null if no location was found.
|
|
/proc/get_teleport_loc(turf/location,mob/target,distance = 1, density = 0, errorx = 0, errory = 0, eoffsetx = 0, eoffsety = 0)
|
|
/*
|
|
Location where the teleport begins, target that will teleport, distance to go, density checking 0/1(yes/no).
|
|
Random error in tile placement x, error in tile placement y, and block offset.
|
|
Block offset tells the proc how to place the box. Behind teleport location, relative to starting location, forward, etc.
|
|
Negative values for offset are accepted, think of it in relation to North, -x is west, -y is south. Error defaults to positive.
|
|
Turf and target are seperate in case you want to teleport some distance from a turf the target is not standing on or something.
|
|
*/
|
|
|
|
var/dirx = 0//Generic location finding variable.
|
|
var/diry = 0
|
|
|
|
var/xoffset = 0//Generic counter for offset location.
|
|
var/yoffset = 0
|
|
|
|
var/b1xerror = 0//Generic placing for point A in box. The lower left.
|
|
var/b1yerror = 0
|
|
var/b2xerror = 0//Generic placing for point B in box. The upper right.
|
|
var/b2yerror = 0
|
|
|
|
errorx = abs(errorx)//Error should never be negative.
|
|
errory = abs(errory)
|
|
//var/errorxy = round((errorx+errory)/2)//Used for diagonal boxes.
|
|
|
|
switch(target.dir)//This can be done through equations but switch is the simpler method. And works fast to boot.
|
|
//Directs on what values need modifying.
|
|
if(1)//North
|
|
diry+=distance
|
|
yoffset+=eoffsety
|
|
xoffset+=eoffsetx
|
|
b1xerror-=errorx
|
|
b1yerror-=errory
|
|
b2xerror+=errorx
|
|
b2yerror+=errory
|
|
if(2)//South
|
|
diry-=distance
|
|
yoffset-=eoffsety
|
|
xoffset+=eoffsetx
|
|
b1xerror-=errorx
|
|
b1yerror-=errory
|
|
b2xerror+=errorx
|
|
b2yerror+=errory
|
|
if(4)//East
|
|
dirx+=distance
|
|
yoffset+=eoffsetx//Flipped.
|
|
xoffset+=eoffsety
|
|
b1xerror-=errory//Flipped.
|
|
b1yerror-=errorx
|
|
b2xerror+=errory
|
|
b2yerror+=errorx
|
|
if(8)//West
|
|
dirx-=distance
|
|
yoffset-=eoffsetx//Flipped.
|
|
xoffset+=eoffsety
|
|
b1xerror-=errory//Flipped.
|
|
b1yerror-=errorx
|
|
b2xerror+=errory
|
|
b2yerror+=errorx
|
|
|
|
var/turf/destination=locate(location.x+dirx,location.y+diry,location.z)
|
|
|
|
if(destination)//If there is a destination.
|
|
if(errorx||errory)//If errorx or y were specified.
|
|
var/destination_list[] = list()//To add turfs to list.
|
|
//destination_list = new()
|
|
/*This will draw a block around the target turf, given what the error is.
|
|
Specifying the values above will basically draw a different sort of block.
|
|
If the values are the same, it will be a square. If they are different, it will be a rectengle.
|
|
In either case, it will center based on offset. Offset is position from center.
|
|
Offset always calculates in relation to direction faced. In other words, depending on the direction of the teleport,
|
|
the offset should remain positioned in relation to destination.*/
|
|
|
|
var/turf/center = locate((destination.x+xoffset),(destination.y+yoffset),location.z)//So now, find the new center.
|
|
|
|
//Now to find a box from center location and make that our destination.
|
|
for(var/turf/T in block(locate(center.x+b1xerror,center.y+b1yerror,location.z), locate(center.x+b2xerror,center.y+b2yerror,location.z) ))
|
|
if(density&&T.density) continue//If density was specified.
|
|
if(T.x>world.maxx || T.x<1) continue//Don't want them to teleport off the map.
|
|
if(T.y>world.maxy || T.y<1) continue
|
|
destination_list += T
|
|
if(destination_list.len)
|
|
destination = pick(destination_list)
|
|
else return
|
|
|
|
else//Same deal here.
|
|
if(density&&destination.density) return
|
|
if(destination.x>world.maxx || destination.x<1) return
|
|
if(destination.y>world.maxy || destination.y<1) return
|
|
else return
|
|
|
|
return destination
|
|
|
|
/proc/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;j++)//It'll take dxabs steps to get there
|
|
y+=dyabs
|
|
if(y>=dxabs) //Every dyabs steps, step once in y direction
|
|
y-=dxabs
|
|
py+=sdy
|
|
px+=sdx //Step on in x direction
|
|
line+=locate(px,py,M.z)//Add the turf to the list
|
|
else
|
|
for(j=0;j<dyabs;j++)
|
|
x+=dxabs
|
|
if(x>=dyabs)
|
|
x-=dyabs
|
|
px+=sdx
|
|
py+=sdy
|
|
line+=locate(px,py,M.z)
|
|
return line
|
|
|
|
//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)
|
|
f = Clamp(round(f), 1201, 1599) // 120.1, 159.9
|
|
|
|
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)
|
|
f = text2num(f)
|
|
return "[round(f / 10)].[f % 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(oldname, 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 going to be a bit costly.
|
|
*/
|
|
for (var/list/L in list(data_core.general, data_core.medical, data_core.security,data_core.locked))
|
|
if (L)
|
|
var/datum/data/record/R = find_record("name", oldname, L)
|
|
|
|
if (R)
|
|
R.fields["name"] = newname
|
|
|
|
// update our pda and id if we have them on our person
|
|
var/search_id = TRUE
|
|
|
|
var/search_pda = TRUE
|
|
|
|
for (var/object in get_contents_in_object(src))
|
|
if (search_id && istype(object, /obj/item/weapon/card/id))
|
|
var/obj/item/weapon/card/id/ID = object
|
|
|
|
if (ID.registered_name == oldname)
|
|
ID.registered_name = newname
|
|
ID.name = "[newname]'s ID Card ([ID.assignment])"
|
|
|
|
if (!search_pda)
|
|
break
|
|
|
|
search_id = FALSE
|
|
else if (search_pda && istype(object, /obj/item/device/pda))
|
|
var/obj/item/device/pda/PDA = object
|
|
|
|
if (PDA.owner == oldname)
|
|
PDA.owner = newname
|
|
PDA.name = "PDA-[newname] ([PDA.ownjob])"
|
|
|
|
if (!search_id)
|
|
break
|
|
|
|
search_pda = FALSE
|
|
|
|
// fixes renames not being reflected in objective text
|
|
var/length
|
|
var/position
|
|
|
|
for (var/datum/mind/mind in ticker.minds)
|
|
if (mind)
|
|
for (var/datum/objective/objective in mind.objectives)
|
|
if (objective && objective.target == mind)
|
|
length = length(oldname)
|
|
position = findtextEx(objective.explanation_text, oldname)
|
|
objective.explanation_text = copytext(objective.explanation_text, 1, position) + newname + copytext(objective.explanation_text, position + 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!
|
|
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_chat(world, "<b>[newname] is the AI!</b>")
|
|
// to_chat(world, sound('sound/AI/newAI.ogg'))
|
|
// Set eyeobj name
|
|
if(A.eyeobj)
|
|
A.eyeobj.name = "[newname] (AI Eye)"
|
|
|
|
// Set ai pda name
|
|
if(A.aiPDA)
|
|
A.aiPDA.owner = newname
|
|
A.aiPDA.name = newname + " (" + A.aiPDA.ownjob + ")"
|
|
|
|
|
|
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(DEAD == A.stat || A.connected_ai || A.scrambledcodes)
|
|
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 = sortNames(mob_list)
|
|
for(var/mob/living/silicon/ai/M in sortmob)
|
|
moblist.Add(M)
|
|
for(var/mob/camera/M in sortmob)
|
|
moblist.Add(M)
|
|
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/monkey/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)
|
|
// for(var/mob/living/silicon/hivebot/M in world)
|
|
// mob_list.Add(M)
|
|
// for(var/mob/living/silicon/hive_mainframe/M in world)
|
|
// mob_list.Add(M)
|
|
return 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
|
|
|
|
/proc/key_name(var/whom, var/include_link = null, var/include_name = 1)
|
|
var/mob/M
|
|
var/client/C
|
|
var/key
|
|
|
|
if(!whom) return "*null*"
|
|
if(istype(whom, /client))
|
|
C = whom
|
|
M = C.mob
|
|
key = C.key
|
|
else if(ismob(whom))
|
|
M = whom
|
|
C = M.client
|
|
key = M.key
|
|
else if(istype(whom, /datum))
|
|
var/datum/D = whom
|
|
return "*invalid:[D.type]*"
|
|
else
|
|
return "*invalid*"
|
|
|
|
. = ""
|
|
|
|
if(key)
|
|
if(include_link && C)
|
|
. += "<a href='?priv_msg=\ref[C]'>"
|
|
|
|
if(C && C.holder && C.holder.fakekey && !include_name)
|
|
. += "Administrator"
|
|
else
|
|
. += key
|
|
|
|
if(include_link)
|
|
if(C) . += "</a>"
|
|
else . += " (DC)"
|
|
else
|
|
. += "*no key*"
|
|
|
|
if(include_name && M)
|
|
if(M.real_name)
|
|
. += "/([M.real_name])"
|
|
else if(M.name)
|
|
. += "/([M.name])"
|
|
|
|
return .
|
|
|
|
/proc/key_name_admin(var/whom, var/include_name = 1)
|
|
return key_name(whom, 1, include_name)
|
|
|
|
// 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
|
|
|
|
|
|
// Registers the on-close verb for a browse window (client/verb/.windowclose)
|
|
// this will be called when the close-button of a window is pressed.
|
|
//
|
|
// This is usually only needed for devices that regularly update the browse window,
|
|
// e.g. canisters, timers, etc.
|
|
//
|
|
// windowid should be the specified window name
|
|
// e.g. code is : user << browse(text, "window=fred")
|
|
// then use : onclose(user, "fred")
|
|
//
|
|
// Optionally, specify the "ref" parameter as the controlled atom (usually src)
|
|
// to pass a "close=1" parameter to the atom's Topic() proc for special handling.
|
|
// Otherwise, the user mob's machine var will be reset directly.
|
|
//
|
|
/proc/onclose(mob/user, windowid, var/atom/ref=null)
|
|
if(!user.client) return
|
|
var/param = "null"
|
|
if(ref)
|
|
param = "\ref[ref]"
|
|
|
|
winset(user, windowid, "on-close=\".windowclose [param]\"")
|
|
|
|
// to_chat(world, "OnClose [user]: [windowid] : ["on-close=\".windowclose [param]\""]")
|
|
|
|
|
|
// the on-close client verb
|
|
// called when a browser popup window is closed after registering with proc/onclose()
|
|
// if a valid atom reference is supplied, call the atom's Topic() with "close=1"
|
|
// otherwise, just reset the client mob's machine var.
|
|
//
|
|
/client/verb/windowclose(var/atomref as text)
|
|
set hidden = 1 // hide this verb from the user's panel
|
|
set name = ".windowclose" // no autocomplete on cmd line
|
|
|
|
// to_chat(world, "windowclose: [atomref]")
|
|
if(atomref!="null") // if passed a real atomref
|
|
var/hsrc = locate(atomref) // find the reffed atom
|
|
var/href = "close=1"
|
|
if(hsrc)
|
|
// to_chat(world, "[src] Topic [href] [hsrc]")
|
|
usr = src.mob
|
|
src.Topic(href, params2list(href), hsrc) // this will direct to the atom's
|
|
return // Topic() proc via client.Topic()
|
|
|
|
// no atomref specified (or not found)
|
|
// so just reset the user mob's machine var
|
|
if(src && src.mob)
|
|
// to_chat(world, "[src] was [src.mob.machine], setting to null")
|
|
src.mob.unset_machine()
|
|
return
|
|
|
|
// 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(atom/A, dx, dy)
|
|
var/x = Clamp(A.x + dx, 1, world.maxx)
|
|
var/y = Clamp(A.y + dy, 1, world.maxy)
|
|
|
|
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)
|
|
|
|
//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/delay = 30, var/numticks = 10) //This is quite an ugly solution but i refuse to use the old request system.
|
|
if(!user || !target) return 0
|
|
var/user_loc = user.loc
|
|
var/target_loc = target.loc
|
|
var/holding = user.get_active_hand()
|
|
var/delayfraction = round(delay/numticks)
|
|
var/image/progbar
|
|
if(user && user.client && user.client.prefs.progress_bars)
|
|
if(!progbar)
|
|
progbar = image("icon" = 'icons/effects/doafter_icon.dmi', "loc" = target, "icon_state" = "prog_bar_0")
|
|
progbar.pixel_y = 32
|
|
//if(!barbar)
|
|
//barbar = image("icon" = 'icons/effects/doafter_icon.dmi', "loc" = user, "icon_state" = "none")
|
|
//barbar.pixel_y = 36
|
|
//var/oldstate
|
|
for (var/i = 1 to numticks)
|
|
if(user && user.client && user.client.prefs.progress_bars && progbar)
|
|
//oldstate = progbar.icon_state
|
|
progbar.icon_state = "prog_bar_[round(((i / numticks) * 100), 10)]"
|
|
user.client.images |= progbar
|
|
sleep(delayfraction)
|
|
if(!user || !target)
|
|
if(progbar)
|
|
progbar.icon_state = "prog_bar_stopped"
|
|
spawn(2)
|
|
if(user && user.client) user.client.images -= progbar
|
|
if(progbar) progbar.loc = null
|
|
return 0
|
|
if ( user.loc != user_loc || target.loc != target_loc || user.get_active_hand() != holding || ( user.stat ) || ( user.stunned || user.weakened || user.paralysis || user.lying ) )
|
|
if(progbar)
|
|
progbar.icon_state = "prog_bar_stopped"
|
|
spawn(2)
|
|
if(user && user.client) user.client.images -= progbar
|
|
if(progbar) progbar.loc = null
|
|
return 0
|
|
if(user && user.client) user.client.images -= progbar
|
|
if(progbar) progbar.loc = null
|
|
return 1
|
|
|
|
/proc/do_after(var/mob/user as mob, var/atom/target, var/delay as num, var/numticks = 10, var/needhand = TRUE)
|
|
if(!user || isnull(user))
|
|
return 0
|
|
if(numticks == 0)
|
|
return 0
|
|
|
|
var/delayfraction = round(delay/numticks)
|
|
var/Location = user.loc
|
|
var/holding = user.get_active_hand()
|
|
var/image/progbar
|
|
//var/image/barbar
|
|
if(user && user.client && user.client.prefs.progress_bars && target)
|
|
if(!progbar)
|
|
progbar = image("icon" = 'icons/effects/doafter_icon.dmi', "loc" = target, "icon_state" = "prog_bar_0")
|
|
progbar.pixel_y = 32
|
|
//if(!barbar)
|
|
//barbar = image("icon" = 'icons/effects/doafter_icon.dmi', "loc" = target, "icon_state" = "none")
|
|
//barbar.pixel_y = 36
|
|
//var/oldstate
|
|
for (var/i = 1 to numticks)
|
|
if(user && user.client && user.client.prefs.progress_bars && target)
|
|
if(!progbar)
|
|
progbar = image("icon" = 'icons/effects/doafter_icon.dmi', "loc" = target, "icon_state" = "prog_bar_0")
|
|
//oldstate = progbar.icon_state
|
|
progbar.icon_state = "prog_bar_[round(((i / numticks) * 100), 10)]"
|
|
user.client.images |= progbar
|
|
sleep(delayfraction)
|
|
//if(user.client && progbar.icon_state != oldstate)
|
|
//user.client.images.Remove(progbar)
|
|
if(!user || user.stat || user.weakened || user.stunned || !(user.loc == Location))
|
|
if(progbar)
|
|
progbar.icon_state = "prog_bar_stopped"
|
|
spawn(2)
|
|
if(user && user.client) user.client.images -= progbar
|
|
if(progbar) progbar.loc = null
|
|
return 0
|
|
if(needhand && !(user.get_active_hand() == holding)) //Sometimes you don't want the user to have to keep their active hand
|
|
if(progbar)
|
|
progbar.icon_state = "prog_bar_stopped"
|
|
spawn(2)
|
|
if(user && user.client) user.client.images -= progbar
|
|
if(progbar) progbar.loc = null
|
|
return 0
|
|
if(user && user.client) user.client.images -= progbar
|
|
if(progbar) progbar.loc = null
|
|
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 sortedAreas list if populated
|
|
//else populates the list first before returning it
|
|
/proc/SortAreas()
|
|
for(var/area/A in areas)
|
|
sortedAreas.Add(A)
|
|
|
|
sortTim(sortedAreas, /proc/cmp_name_asc)
|
|
|
|
/area/proc/addSorted()
|
|
sortedAreas.Add(src)
|
|
sortTim(sortedAreas, /proc/cmp_name_asc)
|
|
|
|
//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/theareas = new/list()
|
|
for(var/area/N in areas)
|
|
if(istype(N, areatype)) theareas += N
|
|
return theareas
|
|
|
|
//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 areas)
|
|
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 areas)
|
|
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
|
|
|
|
/datum/coords/New(var/x as num, var/y as num, var/z as num)
|
|
.=..()
|
|
x_pos = x
|
|
y_pos = y
|
|
z_pos = z
|
|
|
|
/datum/coords/proc/equal_to(var/datum/coords/C)
|
|
if(src.x_pos==C.x_pos && src.y_pos==C.y_pos && src.z_pos==C.z_pos)
|
|
return 1
|
|
return 0
|
|
|
|
/datum/coords/proc/subtract(var/datum/coords/C)
|
|
var/datum/coords/CR = new(x_pos-C.x_pos,y_pos-C.y_pos,z_pos-C.z_pos)
|
|
return CR
|
|
|
|
/datum/coords/proc/add(var/datum/coords/C)
|
|
var/datum/coords/CR = new(x_pos+C.x_pos,y_pos+C.y_pos,z_pos+C.z_pos)
|
|
return CR
|
|
|
|
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","group")))
|
|
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 = 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
|
|
|
|
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.can_shuttle_move())
|
|
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_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
|
|
|
|
|
|
|
|
|
|
var/list/doors = new/list()
|
|
|
|
if(toupdate.len)
|
|
for(var/turf/simulated/T1 in toupdate)
|
|
for(var/obj/machinery/door/D2 in T1)
|
|
doors += D2
|
|
/*if(T1.parent)
|
|
air_master.groups_to_rebuild += T1.parent
|
|
else
|
|
air_master.mark_for_update(T1)*/
|
|
|
|
for(var/obj/O in doors)
|
|
O:update_nearby_tiles()
|
|
|
|
|
|
|
|
|
|
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)
|
|
switch(zone)
|
|
if ("r_hand")
|
|
return "right hand"
|
|
if ("l_hand")
|
|
return "left hand"
|
|
if ("l_arm")
|
|
return "left arm"
|
|
if ("r_arm")
|
|
return "right arm"
|
|
if ("l_leg")
|
|
return "left leg"
|
|
if ("r_leg")
|
|
return "right leg"
|
|
if ("l_foot")
|
|
return "left foot"
|
|
if ("r_foot")
|
|
return "right foot"
|
|
else
|
|
return zone
|
|
|
|
/proc/get_turf(const/atom/O)
|
|
if (isnull(O) || isarea(O) || !istype(O))
|
|
return
|
|
var/atom/A
|
|
for(A=O, A && !isturf(A), A=A.loc); // semicolon is for the empty statement
|
|
return A
|
|
|
|
/proc/get(atom/loc, type)
|
|
while(loc)
|
|
if(istype(loc, type))
|
|
return loc
|
|
loc = loc.loc
|
|
return null
|
|
|
|
//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/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 (ishuman(M) && M.lying && \
|
|
locate(/obj/machinery/optable, M.loc) || \
|
|
(locate(/obj/structure/bed/roller, M.loc) && prob(75)) || \
|
|
(locate(/obj/structure/table/, M.loc) && 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/structure/sign", "obj/structure/painting"
|
|
)
|
|
/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/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/has_edge(obj/O as obj)
|
|
if (!O) return 0
|
|
if(O.edge) return 1
|
|
return 0
|
|
|
|
proc/find_holder_of_type(var/atom/reference,var/typepath) //Returns the first object holder of the type you specified
|
|
var/atom/location = reference.loc //ie /mob to find the first mob holding it
|
|
while(!istype(location,/turf) && !istype(location,null))
|
|
if(istype(location,typepath))
|
|
return location
|
|
location = location.loc
|
|
return 0
|
|
|
|
/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 (I actually suggested it but it was tagged redundant, BUT HEARERS IS A T- /rant).
|
|
/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
|
|
see_in_dark = 1e6
|
|
anchored = 1
|
|
flags = INVULNERABLE
|
|
|
|
//Gets the Z level datum for this atom's Z level
|
|
/proc/get_z_level(var/atom/A)
|
|
var/z
|
|
if(istype(A, /atom/movable))
|
|
var/turf/T = get_turf(A)
|
|
if(!T)
|
|
return null
|
|
z = T.z
|
|
else
|
|
z = A.z
|
|
|
|
. = map.zLevels[z]
|
|
|
|
/proc/get_dir_cardinal(var/atom/T1,var/atom/T2)
|
|
if(!T1 || !T2)
|
|
return null
|
|
|
|
var/direc = get_dir(T1,T2)
|
|
|
|
if(direc in cardinal)
|
|
return direc
|
|
|
|
switch(direc)
|
|
if(NORTHEAST)
|
|
if((T2.x - T1.x) > (T2.y - T1.y))
|
|
return EAST
|
|
else
|
|
return NORTH
|
|
if(SOUTHEAST)
|
|
if((T2.x - T1.x) > ((T2.y - T1.y)*-1))
|
|
return EAST
|
|
else
|
|
return SOUTH
|
|
if(NORTHWEST)
|
|
if(((T2.x - T1.x)*-1) > (T2.y - T1.y))
|
|
return WEST
|
|
else
|
|
return NORTH
|
|
if(SOUTHWEST)
|
|
if((T2.x - T1.x) > (T2.y - T1.y))
|
|
return WEST
|
|
else
|
|
return SOUTH
|
|
else
|
|
return null
|
|
|
|
/proc/adjustAngle(angle)
|
|
angle = round(angle) + 45
|
|
if(angle > 180)
|
|
angle -= 180
|
|
else
|
|
angle += 180
|
|
if(!angle)
|
|
angle = 1
|
|
/*if(angle < 0)
|
|
//angle = (round(abs(get_angle(A, user))) + 45) - 90
|
|
angle = round(angle) + 45 + 180
|
|
else
|
|
angle = round(angle) + 45*/
|
|
return angle
|
|
|
|
/proc/print_runtime(exception/e)
|
|
world.log << "[time_stamp()] Runtime detected\n[e] at [e.file]:[e.line]\n [e.desc]"
|
|
|
|
/proc/transfer_fingerprints(atom/A,atom/B)//synchronizes the fingerprints between two atoms. Useful when you have two different atoms actually being different states of a same object.
|
|
if(!A || !B)
|
|
return
|
|
B.fingerprints = A.fingerprints
|
|
B.fingerprintshidden = A.fingerprintshidden
|
|
B.fingerprintslast = A.fingerprintslast
|
|
|
|
/world/Error(exception/e)
|
|
print_runtime(e)
|
|
..()
|
|
|
|
//Checks if any of the atoms in the turf are dense
|
|
//Returns 1 is anything is dense, 0 otherwise
|
|
/turf/proc/has_dense_content()
|
|
for(var/atom/turf_contents in contents)
|
|
if(turf_contents.density)
|
|
return 1
|
|
return 0
|
|
|
|
/proc/multinum_display(var/number,var/digits)//multinum_display(42,4) = "0042"; multinum_display(-137,6) = "-000137"; multinum_display(4572,3) = "999"
|
|
var/result = ""
|
|
if((digits < 1))
|
|
return "0"
|
|
var/abs = abs(number)
|
|
if(abs > (10**digits))
|
|
for(var/D=0;D<digits;D++)
|
|
result += "9"
|
|
if(number<0)
|
|
result = "-[result]"
|
|
return result
|
|
var/number_digits = 1
|
|
for(var/N = abs;N >= 10; N = N/10)
|
|
number_digits++
|
|
var/additional_digits = digits-number_digits
|
|
for(var/i=0;i<additional_digits;i++)
|
|
result += "0"
|
|
result += "[number]"
|
|
if(number<0)
|
|
result = "-[result]"
|
|
return result
|
|
|
|
/proc/spiral_block(var/turf/epicenter,var/max_range,var/inward=0,var/draw_red=0)//alternative to block. instead of being listed from bottom to top, turfs are listed spiraling inward/outward.
|
|
var/list/spiraled_turfs = list()
|
|
|
|
//epicenter coordinates
|
|
var/x0 = epicenter.x
|
|
var/y0 = epicenter.y
|
|
var/z0 = epicenter.z
|
|
|
|
//world limits
|
|
var/south_limit = 1 - y0
|
|
var/west_limit = 1 - x0
|
|
var/north_limit = world.maxy - y0
|
|
var/east_limit = world.maxx - x0
|
|
|
|
var/max_steps = (max_range*2 + 1) * (max_range*2 + 1)
|
|
|
|
var/pointer_x = 0
|
|
var/pointer_y = 0
|
|
var/segment = 0
|
|
var/movement_dir = NORTH
|
|
var/segment_length = 1
|
|
|
|
if(inward)
|
|
pointer_x = -max_range
|
|
pointer_y = -max_range
|
|
segment_length = max_range*2+1
|
|
segment = 1
|
|
|
|
for(var/sstep=max_steps-1;sstep>=0;sstep--)
|
|
if((pointer_x >= west_limit) && (pointer_x <= east_limit) && (pointer_y >= south_limit) && (pointer_y <= north_limit))//are we inside the map's boundaries
|
|
var/turf/T = locate(x0+pointer_x,y0+pointer_y,z0)
|
|
spiraled_turfs += T
|
|
if(draw_red)
|
|
T.color = "red"
|
|
if(sstep && ((sstep%segment_length) == 0))
|
|
switch(movement_dir)//clockwise spiral
|
|
if(NORTH)
|
|
movement_dir = EAST
|
|
if(EAST)
|
|
movement_dir = SOUTH
|
|
if(SOUTH)
|
|
movement_dir = WEST
|
|
if(WEST)
|
|
movement_dir = NORTH
|
|
if(!segment)
|
|
segment = 1
|
|
else
|
|
segment = 0
|
|
segment_length--
|
|
|
|
switch(movement_dir)
|
|
if(NORTH)
|
|
pointer_y++
|
|
if(EAST)
|
|
pointer_x++
|
|
if(SOUTH)
|
|
pointer_y--
|
|
if(WEST)
|
|
pointer_x--
|
|
if(draw_red)
|
|
sleep(1)
|
|
else
|
|
for(var/sstep in 1 to max_steps)
|
|
if((pointer_x >= west_limit) && (pointer_x <= east_limit) && (pointer_y >= south_limit) && (pointer_y <= north_limit))//are we inside the map's boundaries
|
|
var/turf/T = locate(x0+pointer_x,y0+pointer_y,z0)
|
|
spiraled_turfs += T
|
|
if(draw_red)
|
|
T.color = "red"
|
|
|
|
switch(movement_dir)
|
|
if(NORTH)
|
|
pointer_y++
|
|
if(EAST)
|
|
pointer_x++
|
|
if(SOUTH)
|
|
pointer_y--
|
|
if(WEST)
|
|
pointer_x--
|
|
|
|
if((sstep%segment_length) == 0)
|
|
switch(movement_dir)//clockwise spiral
|
|
if(NORTH)
|
|
movement_dir = EAST
|
|
if(EAST)
|
|
movement_dir = SOUTH
|
|
if(SOUTH)
|
|
movement_dir = WEST
|
|
if(WEST)
|
|
movement_dir = NORTH
|
|
if(!segment)
|
|
segment = 1
|
|
else
|
|
segment = 0
|
|
segment_length++
|
|
if(draw_red)
|
|
sleep(1)
|
|
|
|
if(draw_red)
|
|
sleep(30)
|
|
for(var/turf/T in spiraled_turfs)
|
|
T.color = null
|
|
|
|
return spiraled_turfs
|
|
|
|
// Use this to send to a client's chat, no exceptions (except this proc itself).
|
|
/proc/to_chat(var/thing, var/output)
|
|
thing << output
|