Files
Yogstation/code/__HELPERS/game.dm
2023-10-31 13:08:02 -05:00

772 lines
25 KiB
Plaintext

//supposedly the fastest way to do this according to https://gist.github.com/Giacom/be635398926bb463b42a
#define RANGE_TURFS(RADIUS, CENTER) \
block( \
locate(max(CENTER.x-(RADIUS),1), max(CENTER.y-(RADIUS),1), CENTER.z), \
locate(min(CENTER.x+(RADIUS),world.maxx), min(CENTER.y+(RADIUS),world.maxy), CENTER.z) \
)
#define Z_TURFS(ZLEVEL) block(locate(1,1,ZLEVEL), locate(world.maxx, world.maxy, ZLEVEL))
#define CULT_POLL_WAIT 2400
/// Returns a list of turfs similar to CORNER_BLOCK but with offsets
#define CORNER_BLOCK_OFFSET(corner, width, height, offset_x, offset_y) ( \
(block(locate(corner.x + offset_x, corner.y + offset_y, corner.z), \
locate(min(corner.x + (width - 1) + offset_x, world.maxx), \
min(corner.y + (height - 1) + offset_y, world.maxy), corner.z))))
/// Returns an outline (neighboring turfs) of the given block
#define CORNER_OUTLINE(corner, width, height) ( \
CORNER_BLOCK_OFFSET(corner, width + 2, 1, -1, -1) + \
CORNER_BLOCK_OFFSET(corner, width + 2, 1, -1, height) + \
CORNER_BLOCK_OFFSET(corner, 1, height, -1, 0) + \
CORNER_BLOCK_OFFSET(corner, 1, height, width, 0))
/proc/get_area_name(atom/X, format_text = FALSE, is_sensor = FALSE)
var/area/A = isarea(X) ? X : get_area(X)
if(!A)
return null
var/name = A.name
if (is_sensor && !A.show_on_sensors)
name = Gibberish(name, TRUE, 90)
return format_text ? format_text(name) : name
/proc/get_areas_in_range(dist=0, atom/center=usr)
if(!dist)
var/turf/T = get_turf(center)
return T ? list(T.loc) : list()
if(!center)
return list()
var/list/turfs = RANGE_TURFS(dist, center)
var/list/areas = list()
for(var/V in turfs)
var/turf/T = V
areas |= T.loc
return areas
/proc/get_adjacent_areas(atom/center)
. = list(get_area(get_ranged_target_turf(center, NORTH, 1)),
get_area(get_ranged_target_turf(center, SOUTH, 1)),
get_area(get_ranged_target_turf(center, EAST, 1)),
get_area(get_ranged_target_turf(center, WEST, 1)))
listclearnulls(.)
/proc/get_open_turf_in_dir(atom/center, dir)
var/turf/open/T = get_ranged_target_turf(center, dir, 1)
if(istype(T))
return T
/proc/get_adjacent_open_turfs(atom/center)
. = list(get_open_turf_in_dir(center, NORTH),
get_open_turf_in_dir(center, SOUTH),
get_open_turf_in_dir(center, EAST),
get_open_turf_in_dir(center, WEST))
listclearnulls(.)
/proc/get_adjacent_open_areas(atom/center)
. = list()
var/list/adjacent_turfs = get_adjacent_open_turfs(center)
for(var/I in adjacent_turfs)
. |= get_area(I)
/**
* Get a bounding box of a list of atoms.
*
* Arguments:
* - atoms - List of atoms. Can accept output of view() and range() procs.
*
* Returns: list(x1, y1, x2, y2)
*/
/proc/get_bbox_of_atoms(list/atoms)
var/list/list_x = list()
var/list/list_y = list()
for(var/_a in atoms)
var/atom/a = _a
list_x += a.x
list_y += a.y
return list(
min(list_x),
min(list_y),
max(list_x),
max(list_y))
// Like view but bypasses luminosity check
/proc/get_hear(range, atom/source)
var/lum = source.luminosity
source.luminosity = 6
var/list/heard = view(range, source)
source.luminosity = lum
return heard
/proc/alone_in_area(area/the_area, mob/must_be_alone, check_type = /mob/living/carbon)
var/area/our_area = get_area(the_area)
for(var/C in GLOB.alive_mob_list)
if(!istype(C, check_type))
continue
if(C == must_be_alone)
continue
if(our_area == get_area(C))
return 0
return 1
//We used to use linear regression to approximate the answer, but Mloc realized this was actually faster.
//And lo and behold, it is, and it's more accurate to boot.
/proc/cheap_hypotenuse(Ax,Ay,Bx,By)
return sqrt(abs(Ax - Bx)**2 + abs(Ay - By)**2) //A squared + B squared = C squared
/proc/circlerange(center=usr,radius=3)
var/turf/centerturf = get_turf(center)
var/list/turfs = new/list()
var/rsq = radius * (radius+0.5)
for(var/atom/T in range(radius, centerturf))
var/dx = T.x - centerturf.x
var/dy = T.y - centerturf.y
if(dx*dx + dy*dy <= rsq)
turfs += T
//turfs += centerturf
return turfs
/proc/circleview(center=usr,radius=3)
var/turf/centerturf = get_turf(center)
var/list/atoms = new/list()
var/rsq = radius * (radius+0.5)
for(var/atom/A in view(radius, centerturf))
var/dx = A.x - centerturf.x
var/dy = A.y - centerturf.y
if(dx*dx + dy*dy <= rsq)
atoms += A
//turfs += centerturf
return atoms
/proc/get_dist_euclidian(atom/Loc1 as turf|mob|obj,atom/Loc2 as turf|mob|obj)
var/dx = Loc1.x - Loc2.x
var/dy = Loc1.y - Loc2.y
var/dist = sqrt(dx**2 + dy**2)
return dist
///Returns a list of turfs around a center based on RANGE_TURFS()
/proc/circle_range_turfs(center = usr, radius = 3)
var/turf/center_turf = get_turf(center)
var/list/turfs = new/list()
var/rsq = radius * (radius + 0.5)
for(var/turf/checked_turf as anything in RANGE_TURFS(radius, center_turf))
var/dx = checked_turf.x - center_turf.x
var/dy = checked_turf.y - center_turf.y
if(dx * dx + dy * dy <= rsq)
turfs += checked_turf
return turfs
/proc/circleviewturfs(center=usr,radius=3) //Is there even a diffrence between this proc and circle_range_turfs()? // Yes
var/turf/centerturf = get_turf(center)
var/list/turfs = new/list()
var/rsq = radius * (radius+0.5)
for(var/turf/T in view(radius, centerturf))
var/dx = T.x - centerturf.x
var/dy = T.y - centerturf.y
if(dx*dx + dy*dy <= rsq)
turfs += T
return turfs
//This is the new version of recursive_mob_check, used for say().
//The other proc was left intact because morgue trays use it.
//Sped this up again for real this time
/proc/recursive_hear_check(O)
var/list/processing_list = list(O)
. = list()
var/i = 0
while(i < length(processing_list))
var/atom/A = processing_list[++i]
if(A.flags_1 & HEAR_1)
. += A
processing_list += A.contents
/** recursive_organ_check
* inputs: O (object to start with)
* outputs:
* description: A pseudo-recursive loop based off of the recursive mob check, this check looks for any organs held
* within 'O', toggling their frozen flag. This check excludes items held within other safe organ
* storage units, so that only the lowest level of container dictates whether we do or don't decompose
*/
/proc/recursive_organ_check(atom/O)
var/list/processing_list = list(O)
var/list/processed_list = list()
var/index = 1
var/obj/item/organ/found_organ
while(index <= length(processing_list))
var/atom/A = processing_list[index]
if(istype(A, /obj/item/organ))
found_organ = A
found_organ.organ_flags ^= ORGAN_FROZEN
else if(istype(A, /mob/living/carbon))
var/mob/living/carbon/Q = A
for(var/organ in Q.internal_organs)
found_organ = organ
found_organ.organ_flags ^= ORGAN_FROZEN
for(var/atom/B in A) //objects held within other objects are added to the processing list, unless that object is something that can hold organs safely
if(!processed_list[B] && !istype(B, /obj/structure/closet/crate/freezer) && !istype(B, /obj/structure/closet/secure_closet/freezer))
processing_list+= B
index++
processed_list[A] = A
return
// Better recursive loop, technically sort of not actually recursive cause that shit is retarded, enjoy.
//No need for a recursive limit either
/proc/recursive_mob_check(atom/O,client_check=1,sight_check=1,include_radio=1)
var/list/processing_list = list(O)
var/list/processed_list = list()
var/list/found_mobs = list()
while(processing_list.len)
var/atom/A = processing_list[1]
var/passed = 0
if(ismob(A))
var/mob/A_tmp = A
passed=1
if(client_check && !A_tmp.client)
passed=0
if(sight_check && !isInSight(A_tmp, O))
passed=0
else if(include_radio && istype(A, /obj/item/radio))
passed=1
if(sight_check && !isInSight(A, O))
passed=0
if(passed)
found_mobs |= A
for(var/atom/B in A)
if(!processed_list[B])
processing_list |= B
processing_list.Cut(1, 2)
processed_list[A] = A
return found_mobs
/proc/get_hearers_in_view(R, atom/source)
// Returns a list of hearers in view(R) from source (ignoring luminosity). Used in saycode.
var/turf/T = get_turf(source)
. = list()
if(!T)
return
var/list/processing_list = list()
if (R == 0) // if the range is zero, we know exactly where to look for, we can skip view
processing_list += T.contents // We can shave off one iteration by assuming turfs cannot hear
else // A variation of get_hear inlined here to take advantage of the compiler's fastpath for obj/mob in view
var/lum = T.luminosity
T.luminosity = 6 // This is the maximum luminosity
for(var/mob/M in view(R, T))
processing_list += M
for(var/obj/O in view(R, T))
processing_list += O
T.luminosity = lum
var/i = 0
while(i < length(processing_list)) // recursive_hear_check inlined here
var/atom/A = processing_list[++i]
if(A.flags_1 & HEAR_1)
. += A
processing_list += A.contents
/proc/get_mobs_in_radio_ranges(list/obj/item/radio/radios)
. = list()
// Returns a list of mobs who can hear any of the radios given in @radios
for(var/obj/item/radio/R in radios)
if(R)
. |= get_hearers_in_view(R.canhear_range, R)
#define SIGNV(X) ((X<0)?-1:1)
/proc/inLineOfSight(X1,Y1,X2,Y2,Z=1,PX1=16.5,PY1=16.5,PX2=16.5,PY2=16.5)
var/turf/T
if(X1==X2)
if(Y1==Y2)
return 1 //Light cannot be blocked on same tile
else
var/s = SIGN(Y2-Y1)
Y1+=s
while(Y1!=Y2)
T=locate(X1,Y1,Z)
if(T.opacity)
return 0
Y1+=s
else
var/m=(32*(Y2-Y1)+(PY2-PY1))/(32*(X2-X1)+(PX2-PX1))
var/b=(Y1+PY1/32-0.015625)-m*(X1+PX1/32-0.015625) //In tiles
var/signX = SIGN(X2-X1)
var/signY = SIGN(Y2-Y1)
if(X1<X2)
b+=m
while(X1!=X2 || Y1!=Y2)
if(round(m*X1+b-Y1))
Y1+=signY //Line exits tile vertically
else
X1+=signX //Line exits tile horizontally
T=locate(X1,Y1,Z)
if(T.opacity)
return 0
return 1
#undef SIGNV
/proc/isInSight(atom/A, atom/B)
var/turf/Aturf = get_turf(A)
var/turf/Bturf = get_turf(B)
if(!Aturf || !Bturf)
return 0
if(inLineOfSight(Aturf.x,Aturf.y, Bturf.x,Bturf.y,Aturf.z))
return 1
else
return 0
/proc/get_cardinal_step_away(atom/start, atom/finish) //returns the position of a step from start away from finish, in one of the cardinal directions
//returns only NORTH, SOUTH, EAST, or WEST
var/dx = finish.x - start.x
var/dy = finish.y - start.y
if(abs(dy) > abs (dx)) //slope is above 1:1 (move horizontally in a tie)
if(dy > 0)
return get_step(start, SOUTH)
else
return get_step(start, NORTH)
else
if(dx > 0)
return get_step(start, WEST)
else
return get_step(start, EAST)
/proc/try_move_adjacent(atom/movable/AM)
var/turf/T = get_turf(AM)
for(var/direction in GLOB.cardinals)
if(AM.Move(get_step(T, direction)))
break
/proc/get_mob_by_key(key)
var/ckey = ckey(key)
for(var/i in GLOB.player_list)
var/mob/M = i
if(M.ckey == ckey)
return M
return null
/proc/considered_alive(datum/mind/M, enforce_human = TRUE)
if(M && M.current)
if(enforce_human)
var/mob/living/carbon/human/H
if(ishuman(M.current))
H = M.current
return M.current.stat != DEAD && !issilicon(M.current) && !isbrain(M.current) && (!H || H.dna.species.id != "memezombies")
else if(isliving(M.current))
if(isAI(M.current))
var/mob/living/silicon/ai/AI = M.current
if(AI.is_dying)
return FALSE
return M.current.stat != DEAD
return FALSE
/proc/considered_afk(datum/mind/M)
return !M || !M.current || !M.current.client || M.current.client.is_afk()
/proc/ScreenText(obj/O, maptext="", screen_loc="CENTER-7,CENTER-7", maptext_height=480, maptext_width=480)
if(!isobj(O))
O = new /atom/movable/screen/text()
O.maptext = maptext
O.maptext_height = maptext_height
O.maptext_width = maptext_width
O.screen_loc = screen_loc
return O
/// Removes an image from a client's `.images`. Useful as a callback.
/proc/remove_image_from_client(image/image, client/remove_from)
remove_from?.images -= image
/proc/remove_images_from_clients(image/I, list/show_to)
for(var/client/C in show_to)
C.images -= I
/proc/flick_overlay(image/I, list/show_to, duration)
for(var/client/C in show_to)
C.images += I
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(remove_images_from_clients), I, show_to), duration, TIMER_CLIENT_TIME)
/proc/flick_overlay_view(image/I, atom/target, duration) //wrapper for the above, flicks to everyone who can see the target atom
var/list/viewing = list()
for(var/m in viewers(target))
var/mob/M = m
if(M.client)
viewing += M.client
flick_overlay(I, viewing, duration)
/proc/get_active_player_count(alive_check = 0, afk_check = 0, human_check = 0)
// Get active players who are playing in the round
var/active_players = 0
for(var/i = 1; i <= GLOB.player_list.len; i++)
var/mob/M = GLOB.player_list[i]
if(M && M.client)
if(alive_check && M.stat)
continue
else if(afk_check && M.client.is_afk())
continue
else if(human_check && !ishuman(M))
continue
else if(isnewplayer(M)) // exclude people in the lobby
continue
else if(isobserver(M)) // Ghosts are fine if they were playing once (didn't start as observers)
var/mob/dead/observer/O = M
if(O.started_as_observer) // Exclude people who started as observers
continue
active_players++
return active_players
/proc/showCandidatePollWindow(mob/M, poll_time, Question, list/candidates, ignore_category, time_passed, flashwindow = TRUE)
set waitfor = 0
SEND_SOUND(M, 'sound/misc/notice3.ogg') //Alerting them to their consideration
if(flashwindow)
window_flash(M.client)
var/list/answers = ignore_category ? list("Yes", "No", "Never for this round") : list("Yes", "No")
switch(tgui_alert(M, Question, "A limited-time offer!", answers, poll_time, autofocus = FALSE))
if("Yes")
to_chat(M, span_notice("Choice registered: Yes."))
if(time_passed + poll_time <= world.time)
to_chat(M, span_danger("Sorry, you answered too late to be considered!"))
SEND_SOUND(M, 'sound/machines/buzz-sigh.ogg')
candidates -= M
else
candidates += M
if("No")
to_chat(M, span_danger("Choice registered: No."))
candidates -= M
if("Never for this round")
var/list/L = GLOB.poll_ignore[ignore_category]
if(!L)
GLOB.poll_ignore[ignore_category] = list()
GLOB.poll_ignore[ignore_category] += M.ckey
to_chat(M, span_danger("Choice registered: Never for this round."))
candidates -= M
else
candidates -= M
/**
* Poll all ghosts for looking for a candidate
*
* Poll all ghosts a question
* returns people who voted yes in a list
* Arguments:
* * Question: String, what do you want to ask them
* * jobbanType: List, Which roles/jobs to exclude from being asked
* * gametypeCheck: Datum, Check if they have the time required for that role
* * be_special_flag: Bool, Only notify ghosts with special antag on
* * poll_time: Integer, How long to poll for in deciseconds(0.1s)
* * ignore_category: Define, ignore_category: People with this category(defined in poll_ignore.dm) turned off dont get the message
* * flashwindow: Bool, Flash their window to grab their attention
*/
/proc/pollGhostCandidates(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, ignore_category = null, flashwindow = TRUE)
var/list/candidates = list()
if(!(GLOB.ghost_role_flags & GHOSTROLE_STATION_SENTIENCE))
return candidates
for(var/mob/dead/observer/G in GLOB.player_list)
candidates += G
return pollCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category, flashwindow, candidates)
/**
* Poll all mentor ghosts for looking for a candidate
*
* Poll all mentor ghosts a question
* returns people who voted yes in a list
* Arguments:
* * Question: String, what do you want to ask them
* * jobbanType: List, Which roles/jobs to exclude from being asked
* * gametypeCheck: Datum, Check if they have the time required for that role
* * be_special_flag: Bool, Only notify ghosts with special antag on
* * poll_time: Integer, How long to poll for in deciseconds(0.1s)
* * ignore_category: Define, ignore_category: People with this category(defined in poll_ignore.dm) turned off dont get the message
* * flashwindow: Bool, Flash their window to grab their attention
*/
/proc/pollMentorGhostCandidates(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, ignore_category = null, flashwindow = TRUE)
var/list/candidates = list()
if(!(GLOB.ghost_role_flags & GHOSTROLE_STATION_SENTIENCE))
return candidates
for(var/mob/dead/observer/G in GLOB.player_list)
if(is_mentor(G))
candidates += G
return pollCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category, flashwindow, candidates)
/**
* Poll all in the group for a candidate
*
* Poll group for question
* returns people who voted yes in a list
* Arguments:
* * Question: String, what do you want to ask them
* * jobbanType: List, Which roles/jobs to exclude from being asked
* * gametypeCheck: Datum, Check if they have the time required for that role
* * be_special_flag: Bool, Only notify ghosts with special antag on
* * poll_time: Integer, How long to poll for in deciseconds(0.1s)
* * ignore_category: Define, ignore_category: People with this category(defined in poll_ignore.dm) turned off dont get the message
* * flashwindow: Bool, Flash their window to grab their attention
* * group: List, Group of people to poll. list of datum/minds
*/
/proc/pollCandidates(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, ignore_category = null, flashwindow = TRUE, list/group = null)
var/time_passed = world.time
if (!Question)
Question = "Would you like to be a special role?"
var/list/result = list()
for(var/m in group)
var/mob/M = m
if(!M.key || !M.client || (ignore_category && GLOB.poll_ignore[ignore_category] && (M.ckey in GLOB.poll_ignore[ignore_category])))
continue
if(be_special_flag)
if(!(M.client.prefs) || !(be_special_flag in M.client.prefs.be_special))
continue
if(gametypeCheck)
if(!gametypeCheck.age_check(M.client))
continue
if(jobbanType)
if(is_banned_from(M.ckey, list(jobbanType, ROLE_SYNDICATE)) || QDELETED(M))
continue
showCandidatePollWindow(M, poll_time, Question, result, ignore_category, time_passed, flashwindow)
sleep(poll_time)
//Check all our candidates, to make sure they didn't log off or get deleted during the wait period.
for(var/mob/M in result)
if(!M.key || !M.client)
result -= M
listclearnulls(result)
return result
/**
* Poll ghosts to take control of a mob
*
* Poll ghosts for mob control
* returns people who voted yes in a list
* Arguments:
* * Question: String, what do you want to ask them
* * jobbanType: List, Which roles/jobs to exclude from being asked
* * gametypeCheck: Datum, Check if they have the time required for that role
* * be_special_flag: Bool, Only notify ghosts with special antag on
* * poll_time: Integer, How long to poll for in deciseconds(0.1s)
* * M: Mob, /mob to offer
* * ignore_category: Unknown
*/
/proc/pollCandidatesForMob(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, mob/M, ignore_category = null)
var/list/L = pollGhostCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category)
if(!M || QDELETED(M) || !M.loc)
return list()
return L
/**
* Poll mentor ghosts to take control of a mob
*
* Poll mentor ghosts for mob control
* returns people who voted yes in a list
* Arguments:
* * Question: String, what do you want to ask them
* * jobbanType: List, Which roles/jobs to exclude from being asked
* * gametypeCheck: Datum, Check if they have the time required for that role
* * be_special_flag: Bool, Only notify ghosts with special antag on
* * poll_time: Integer, How long to poll for in deciseconds(0.1s)
* * M: Mob, /mob to offer
* * ignore_category: Unknown
*/
/proc/pollMentorCandidatesForMob(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, mob/M, ignore_category = null)
var/list/L = pollMentorGhostCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category)
if(!M || QDELETED(M) || !M.loc)
return list()
return L
/**
* Poll ghosts to take control of a mob
*
* Poll ghosts for mob control
* returns people who voted yes in a list
* Arguments:
* * Question: String, what do you want to ask them
* * jobbanType: List, Which roles/jobs to exclude from being asked
* * gametypeCheck: Datum, Check if they have the time required for that role
* * be_special_flag: Bool, Only notify ghosts with special antag on
* * poll_time: Integer, How long to poll for in deciseconds(0.1s)
* * mobs: List, list of mobs to offer up
* * ignore_category: Unknown
*/
/proc/pollCandidatesForMobs(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, list/mobs, ignore_category = null)
var/list/L = pollGhostCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category)
var/i=1
for(var/v in mobs)
var/atom/A = v
if(!A || QDELETED(A) || !A.loc)
mobs.Cut(i,i+1)
else
++i
return L
/proc/makeBody(mob/dead/observer/G_found) // Uses stripped down and bastardized code from respawn character
if(!G_found || !G_found.key)
return
//First we spawn a dude.
var/mob/living/carbon/human/new_character = new//The mob being spawned.
SSjob.SendToLateJoin(new_character)
G_found.client.prefs.apply_prefs_to(new_character)
new_character.dna.update_dna_identity()
new_character.key = G_found.key
return new_character
/proc/send_to_playing_players(thing) //sends a whatever to all playing players; use instead of to_chat(world, where needed)
for(var/M in GLOB.player_list)
if(M && !isnewplayer(M))
to_chat(M, thing)
/proc/window_flash(client/C, ignorepref = FALSE)
if(ismob(C))
var/mob/M = C
if(M.client)
C = M.client
if(!C || (!C.prefs.read_preference(/datum/preference/toggle/window_flashing) && !ignorepref))
return
winset(C, "mainwindow", "flash=5")
//Recursively checks if an item is inside a given type, even through layers of storage. Returns the atom if it finds it.
/proc/recursive_loc_check(atom/movable/target, type)
var/atom/A = target
if(istype(A, type))
return A
while(!istype(A.loc, type))
if(!A.loc)
return
A = A.loc
return A.loc
/proc/AnnounceArrival(mob/living/carbon/human/character, rank)
if(!SSticker.IsRoundInProgress() || QDELETED(character))
return
var/area/A = get_area(character)
if(character.mind.role_alt_title)
rank = character.mind.role_alt_title
deadchat_broadcast(" has arrived at the station at [span_name("[A.name]")].", "<span class='game'>[span_name("[character.real_name]")] ([rank])", follow_target = character, message_type=DEADCHAT_ARRIVALRATTLE)
if((!GLOB.announcement_systems.len) || (!character.mind))
return
if((character.mind.assigned_role == "Cyborg") || (character.mind.assigned_role == character.mind.special_role))
return
var/obj/machinery/announcement_system/announcer = pick(GLOB.announcement_systems)
announcer.announce("ARRIVAL", character.real_name, rank, list()) //make the list empty to make it announce it in common
/proc/lavaland_equipment_pressure_check(turf/T)
. = FALSE
if(!istype(T))
return
var/datum/gas_mixture/environment = T.return_air()
if(!istype(environment))
return
var/pressure = environment.return_pressure()
if(pressure <= LAVALAND_EQUIPMENT_EFFECT_PRESSURE)
. = TRUE
/proc/ispipewire(item)
var/static/list/pire_wire = list(
/obj/machinery/atmospherics,
/obj/structure/disposalpipe,
/obj/structure/cable
)
return (is_type_in_list(item, pire_wire))
// Find a obstruction free turf that's within the range of the center. Can also condition on if it is of a certain area type.
/proc/find_obstruction_free_location(range, atom/center, area/specific_area)
var/list/turfs = RANGE_TURFS(range, center)
var/list/possible_loc = list()
for(var/turf/found_turf in turfs)
var/area/turf_area = get_area(found_turf)
// We check if both the turf is a floor, and that it's actually in the area.
// We also want a location that's clear of any obstructions.
if (specific_area)
if (!istype(turf_area, specific_area))
continue
if (!isspaceturf(found_turf))
if (!found_turf.is_blocked_turf())
possible_loc.Add(found_turf)
// Need at least one free location.
if (possible_loc.len < 1)
return FALSE
return pick(possible_loc)
/proc/power_fail(duration_min, duration_max)
var/list/data_core_areas = list()
for(var/obj/machinery/ai/data_core/core as anything in GLOB.data_cores)
if(!core.valid_data_core())
continue
if(!isarea(core.loc))
continue
var/area/A = core.loc
data_core_areas[A.type] = TRUE
for(var/P in GLOB.apcs_list)
var/obj/machinery/power/apc/C = P
if(C.cell && SSmapping.level_trait(C.z, ZTRAIT_STATION))
var/area/A = C.area
if(GLOB.typecache_powerfailure_safe_areas[A.type])
continue
if(data_core_areas[A.type])
continue
C.energy_fail(rand(duration_min,duration_max))
/// For legacy procs using addtimer in callbacks. Don't use this.
/proc/_addtimer_here(callback, time)
addtimer(callback, time)