mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2025-12-20 15:21:29 +00:00
1740 lines
51 KiB
Plaintext
1740 lines
51 KiB
Plaintext
|
|
//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
|
|
|
|
// 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/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, 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 [target].", "<span class='notice'>You use the analyzer on [target].</span>")
|
|
var/pressure = air_contents.return_pressure()
|
|
var/total_moles = air_contents.total_moles()
|
|
|
|
to_chat(user, "<span class='notice'>Results of analysis of [bicon(icon)] [target].</span>")
|
|
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)
|
|
|
|
to_chat(user, "<span class='notice'>Pressure: [round(pressure,0.1)] kPa</span>")
|
|
to_chat(user, "<span class='notice'>Nitrogen: [round(n2_concentration*100)] %</span>")
|
|
to_chat(user, "<span class='notice'>Oxygen: [round(o2_concentration*100)] %</span>")
|
|
to_chat(user, "<span class='notice'>CO2: [round(co2_concentration*100)] %</span>")
|
|
to_chat(user, "<span class='notice'>Plasma: [round(plasma_concentration*100)] %</span>")
|
|
if(unknown_concentration>0.01)
|
|
to_chat(user, "<span class='danger'>Unknown: [round(unknown_concentration*100)] %</span>")
|
|
to_chat(user, "<span class='notice'>Temperature: [round(air_contents.temperature-T0C)] °C</span>")
|
|
else
|
|
to_chat(user, "<span class='notice'>[target] is empty!</span>")
|
|
return
|
|
|
|
//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
|
|
|
|
/*
|
|
Returns 1 if the chain up to the area contains the given typepath
|
|
0 otherwise
|
|
*/
|
|
/atom/proc/is_found_within(var/typepath)
|
|
var/atom/A = src
|
|
while(A.loc)
|
|
if(istype(A.loc, typepath))
|
|
return 1
|
|
A = A.loc
|
|
return 0
|
|
|
|
// 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)
|
|
|
|
//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, var/atom/newloc = null)
|
|
if(!original)
|
|
return null
|
|
|
|
var/obj/O = null
|
|
|
|
if(sameloc)
|
|
O=new original.type(original.loc)
|
|
else
|
|
O=new original.type(newloc)
|
|
|
|
if(perfectcopy)
|
|
if((O) && (original))
|
|
var/static/list/forbidden_vars = list("type","loc","locs","vars", "parent","parent_type", "verbs","ckey","key","power_supply","contents","reagents","stat","x","y","z","group")
|
|
|
|
for(var/V in original.vars - forbidden_vars)
|
|
if(istype(original.vars[V],/list))
|
|
var/list/L = original.vars[V]
|
|
O.vars[V] = L.Copy()
|
|
else if(istype(original.vars[V],/datum))
|
|
continue // this would reference the original's object, that will break when it is used or deleted.
|
|
else
|
|
O.vars[V] = original.vars[V]
|
|
if(istype(O))
|
|
O.update_icon()
|
|
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", "group")))
|
|
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
|
|
|
|
//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 "<a href='?src=\ref[D];[arglist]'>[content]</a>"
|
|
|
|
|
|
|
|
/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_random_chemical(var/is_plant = 0)
|
|
var/list/blocked = blocked_chems.Copy() //blocked_chems list is found in code/_globalvars/lists/reagents.dm
|
|
if(is_plant)
|
|
blocked.Add(plant_blocked_chems.Copy()) //plant_blocked_chems list is found in code/_globalvars/lists/reagents.dm
|
|
var/picked_chem = pick(chemical_reagents_list)
|
|
if(blocked.Find(picked_chem))
|
|
return get_random_chemical(is_plant)
|
|
return picked_chem
|
|
|
|
/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
|
|
|
|
//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/var/atom/orbiting = null
|
|
|
|
//A: atom to orbit
|
|
//radius: range to orbit at, radius of the circle formed by orbiting
|
|
//clockwise: whether you orbit clockwise or anti clockwise
|
|
//rotation_speed: how fast to rotate
|
|
//rotation_segments: the resolution of the orbit circle, less = a more block circle, this can be used to produce hexagons (6 segments) triangles (3 segments), and so on, 36 is the best default.
|
|
//pre_rotation: Chooses to rotate src 90 degress towards the orbit dir (clockwise/anticlockwise), useful for things to go "head first" like ghosts
|
|
//lockinorbit: Forces src to always be on A's turf, otherwise the orbit cancels when src gets too far away (eg: ghosts)
|
|
|
|
/atom/movable/proc/orbit(atom/A, radius = 10, clockwise = FALSE, rotation_speed = 20, rotation_segments = 36, pre_rotation = TRUE, lockinorbit = FALSE)
|
|
if(!istype(A))
|
|
return
|
|
|
|
if(orbiting)
|
|
stop_orbit()
|
|
|
|
orbiting = A
|
|
var/matrix/initial_transform = matrix(transform)
|
|
var/lastloc = loc
|
|
|
|
//Head first!
|
|
if(pre_rotation)
|
|
var/matrix/M = matrix(transform)
|
|
var/pre_rot = 90
|
|
if(!clockwise)
|
|
pre_rot = -90
|
|
M.Turn(pre_rot)
|
|
transform = M
|
|
|
|
var/matrix/shift = matrix(transform)
|
|
shift.Translate(0,radius)
|
|
transform = shift
|
|
|
|
SpinAnimation(rotation_speed, -1, clockwise, rotation_segments)
|
|
|
|
//we stack the orbits up client side, so we can assign this back to normal server side without it breaking the orbit
|
|
transform = initial_transform
|
|
while(orbiting && orbiting == A && A.loc)
|
|
var/targetloc = get_turf(A)
|
|
if(!lockinorbit && loc != lastloc && loc != targetloc)
|
|
break
|
|
loc = targetloc
|
|
lastloc = loc
|
|
sleep(0.6)
|
|
|
|
if(orbiting == A) //make sure we haven't started orbiting something else.
|
|
orbiting = null
|
|
SpinAnimation(0,0)
|
|
|
|
|
|
|
|
/atom/movable/proc/stop_orbit()
|
|
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
|
|
|
|
//similar function to RANGE_TURFS(), but will search spiralling outwards from the center (like the above, but only turfs)
|
|
/proc/spiral_range_turfs(dist=0, center=usr, orange=0)
|
|
if(!dist)
|
|
if(!orange)
|
|
return list(center)
|
|
else
|
|
return list()
|
|
|
|
var/turf/t_center = get_turf(center)
|
|
if(!t_center)
|
|
return list()
|
|
|
|
var/list/L = list()
|
|
var/turf/T
|
|
var/y
|
|
var/x
|
|
var/c_dist = 1
|
|
|
|
if(!orange)
|
|
L += t_center
|
|
|
|
while( c_dist <= dist )
|
|
y = t_center.y + c_dist
|
|
x = t_center.x - c_dist + 1
|
|
for(x in x to t_center.x+c_dist)
|
|
T = locate(x,y,t_center.z)
|
|
if(T)
|
|
L += T
|
|
|
|
y = t_center.y + c_dist - 1
|
|
x = t_center.x + c_dist
|
|
for(y in t_center.y-c_dist to y)
|
|
T = locate(x,y,t_center.z)
|
|
if(T)
|
|
L += T
|
|
|
|
y = t_center.y - c_dist
|
|
x = t_center.x + c_dist - 1
|
|
for(x in t_center.x-c_dist to x)
|
|
T = locate(x,y,t_center.z)
|
|
if(T)
|
|
L += T
|
|
|
|
y = t_center.y - c_dist + 1
|
|
x = t_center.x - c_dist
|
|
for(y in y to t_center.y+c_dist)
|
|
T = locate(x,y,t_center.z)
|
|
if(T)
|
|
L += T
|
|
c_dist++
|
|
|
|
return L
|
|
|
|
//ultra range (no limitations on distance, faster than range for distances > 8); including areas drastically decreases performance
|
|
/proc/urange(dist=0, atom/center=usr, orange=0, areas=0)
|
|
if(!dist)
|
|
if(!orange)
|
|
return list(center)
|
|
else
|
|
return list()
|
|
|
|
var/list/turfs = RANGE_TURFS(dist, center)
|
|
if(orange)
|
|
turfs -= get_turf(center)
|
|
. = list()
|
|
for(var/V in turfs)
|
|
var/turf/T = V
|
|
. += T
|
|
. += T.contents
|
|
if(areas)
|
|
. |= T.loc
|
|
|
|
/proc/turf_clear(turf/T)
|
|
for(var/atom/A in T)
|
|
if(A.simulated)
|
|
return 0
|
|
return 1
|
|
|
|
/proc/screen_loc2turf(scr_loc, turf/origin)
|
|
var/tX = splittext(scr_loc, ",")
|
|
var/tY = splittext(tX[2], ":")
|
|
var/tZ = origin.z
|
|
tY = tY[1]
|
|
tX = splittext(tX[1], ":")
|
|
tX = tX[1]
|
|
tX = max(1, min(world.maxx, origin.x + (text2num(tX) - (world.view + 1))))
|
|
tY = max(1, min(world.maxy, origin.y + (text2num(tY) - (world.view + 1))))
|
|
return locate(tX, tY, tZ)
|
|
|
|
/proc/get_closest_atom(type, list, source)
|
|
var/closest_atom
|
|
var/closest_distance
|
|
for(var/A in list)
|
|
if(!istype(A, type))
|
|
continue
|
|
var/distance = get_dist(source, A)
|
|
if(!closest_distance)
|
|
closest_distance = distance
|
|
closest_atom = A
|
|
else
|
|
if(closest_distance > distance)
|
|
closest_distance = distance
|
|
closest_atom = A
|
|
return closest_atom
|
|
|
|
/proc/pick_closest_path(value)
|
|
var/list/matches = get_fancy_list_of_types()
|
|
if(!isnull(value) && value!="")
|
|
matches = filter_fancy_list(matches, value)
|
|
|
|
if(matches.len==0)
|
|
return
|
|
|
|
var/chosen
|
|
if(matches.len==1)
|
|
chosen = matches[1]
|
|
else
|
|
chosen = input("Select an atom type", "Spawn Atom", matches[1]) as null|anything in matches
|
|
if(!chosen)
|
|
return
|
|
chosen = matches[chosen]
|
|
return chosen
|
|
|
|
|
|
var/list/TYPES_SHORTCUTS = list(
|
|
/obj/effect/decal/cleanable = "CLEANABLE",
|
|
/obj/item/device/radio/headset = "HEADSET",
|
|
/obj/item/clothing/head/helmet/space = "SPESSHELMET",
|
|
/obj/item/weapon/book/manual = "MANUAL",
|
|
/obj/item/weapon/reagent_containers/food/drinks = "DRINK", //longest paths comes first
|
|
/obj/item/weapon/reagent_containers/food = "FOOD",
|
|
/obj/item/weapon/reagent_containers = "REAGENT_CONTAINERS",
|
|
/obj/machinery/atmospherics = "ATMOS",
|
|
/obj/machinery/portable_atmospherics = "PORT_ATMOS",
|
|
// /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/missile_rack = "MECHA_MISSILE_RACK",
|
|
/obj/item/mecha_parts/mecha_equipment = "MECHA_EQUIP",
|
|
// /obj/item/organ/internal = "ORGAN_INT",
|
|
)
|
|
|
|
var/global/list/g_fancy_list_of_types = null
|
|
/proc/get_fancy_list_of_types()
|
|
if(isnull(g_fancy_list_of_types)) //init
|
|
var/list/temp = sortList(subtypesof(/atom) - typesof(/area) - /atom/movable)
|
|
g_fancy_list_of_types = new(temp.len)
|
|
for(var/type in temp)
|
|
var/typename = "[type]"
|
|
for(var/tn in TYPES_SHORTCUTS)
|
|
if(copytext(typename,1, length("[tn]/")+1)=="[tn]/" /*findtextEx(typename,"[tn]/",1,2)*/ )
|
|
typename = TYPES_SHORTCUTS[tn]+copytext(typename,length("[tn]/"))
|
|
break
|
|
g_fancy_list_of_types[typename] = type
|
|
return g_fancy_list_of_types
|
|
|
|
/proc/filter_fancy_list(list/L, filter as text)
|
|
var/list/matches = new
|
|
for(var/key in L)
|
|
var/value = L[key]
|
|
if(findtext("[key]", filter) || findtext("[value]", filter))
|
|
matches[key] = value
|
|
return matches
|
|
|
|
//Key thing that stops lag. Cornerstone of performance in ss13, Just sitting here, in unsorted.dm.
|
|
/proc/stoplag()
|
|
. = 1
|
|
sleep(world.tick_lag)
|
|
if(world.tick_usage > TICK_LIMIT_TO_RUN) //woke up, still not enough tick, sleep for more.
|
|
. += 2
|
|
sleep(world.tick_lag*2)
|
|
if(world.tick_usage > TICK_LIMIT_TO_RUN) //woke up, STILL not enough tick, sleep for more.
|
|
. += 4
|
|
sleep(world.tick_lag*4)
|
|
//you might be thinking of adding more steps to this, or making it use a loop and a counter var
|
|
// not worth it. |