mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-07-18 11:30:35 +01:00
ecd88fee14
Fixes this test fail. One of our unit tests is randomly spawning a GPS in a null location, which causes a runtime error when the GPS attempts to print the coordinates of said null location. I've added sanity checking to prevent this obnoxious test fail. <img width="1379" height="759" alt="image" src="https://github.com/user-attachments/assets/d5c15892-7272-4d24-886b-5c30432a35e5" />
456 lines
13 KiB
Plaintext
456 lines
13 KiB
Plaintext
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31
|
|
|
|
/proc/max_default_z_level()
|
|
var/max_z = 0
|
|
for(var/z in SSmapping.levels_by_trait(ZTRAIT_STATION))
|
|
max_z = max(z, max_z)
|
|
for(var/z in SSatlas.current_map.admin_levels)
|
|
max_z = max(z, max_z)
|
|
for(var/z in SSatlas.current_map.player_levels)
|
|
max_z = max(z, max_z)
|
|
return max_z
|
|
|
|
/**
|
|
* Get area by its name.
|
|
*
|
|
* * N - Exact name being searched
|
|
*/
|
|
/proc/get_area_name(N)
|
|
for(var/area/A in get_sorted_areas())
|
|
if(A.name == N)
|
|
return A
|
|
return 0
|
|
|
|
/proc/get_area_master(const/O)
|
|
var/area/A = get_area(O)
|
|
if (isarea(A))
|
|
return A
|
|
|
|
/**
|
|
* For use only on the Horizon.
|
|
* Prepends a given area's name with its department and deck # for presentation. Returns a string.
|
|
* Example of a fully configured area name output:
|
|
* Engineering (Atmospherics) - Deck 1 - Combustion Turbine - Port Amidships, Aft
|
|
*/
|
|
/proc/get_area_display_name(var/area/A, var/show_dept = TRUE, var/show_subdept = TRUE, var/show_deck = TRUE, var/show_location = TRUE, var/show_hidden_depts = FALSE)
|
|
if (!A) // Fallback case.
|
|
return "Unknown Area"
|
|
if(!is_station_area(A))
|
|
return A.name
|
|
var/horizon_deck = A.horizon_deck
|
|
var/location_ew = A.location_ew
|
|
var/location_ns = A.location_ns
|
|
var/department = A.department
|
|
var/subdepartment = A.subdepartment
|
|
var/output = ""
|
|
|
|
// All maintenance areas should, by name, be self-identifying as being maints. We usually don't care to see it.
|
|
// Likewise public areas are self-evident.
|
|
if(horizon_deck && show_deck)
|
|
output += "Deck [num2text(horizon_deck)] - "
|
|
if(!show_hidden_depts && (department == LOC_MAINTENANCE || department == LOC_PUBLIC))
|
|
department = null
|
|
if(department && show_dept)
|
|
output += "[department]"
|
|
if(subdepartment && show_subdept)
|
|
output += " ([subdepartment])"
|
|
output += " - "
|
|
|
|
output += "[A.name]"
|
|
|
|
if((location_ew || location_ns) && show_location)
|
|
output += " ("
|
|
if(location_ew)
|
|
output += "[location_ew]"
|
|
if(location_ns)
|
|
output += ", "
|
|
if(location_ns)
|
|
output += "[location_ns]"
|
|
output += ")"
|
|
|
|
return output
|
|
|
|
/proc/in_range(source, user)
|
|
if(get_dist(source, user) <= 1)
|
|
return 1
|
|
|
|
// Not in range and not telekinetic
|
|
return 0
|
|
|
|
/**
|
|
* Will recursively loop through an atom's locs until it finds the atom loc above a turf or its target_atom
|
|
*/
|
|
/proc/recursive_loc_turf_check(var/atom/O, var/recursion_limit = 3, var/atom/target_atom)
|
|
if(recursion_limit <= 0 || isturf(O.loc) || O == target_atom)
|
|
return O
|
|
else
|
|
O = O.loc
|
|
recursion_limit--
|
|
return recursive_loc_turf_check(O, recursion_limit)
|
|
|
|
/**
|
|
* Get the last `/atom` before reaching a `/turf` following the `.loc` nesting
|
|
*
|
|
* Eg. if A is inside B that is inside C that is on a turf, it will return C
|
|
*
|
|
* * source - The source on which to start the search for, either an `/obj` or `/mob`
|
|
*/
|
|
/proc/get_last_atom_before_turf(atom/source)
|
|
SHOULD_NOT_SLEEP(TRUE)
|
|
RETURN_TYPE(/atom)
|
|
|
|
if(istype(source, /area))
|
|
stack_trace("Areas are not supported for this!")
|
|
return FALSE
|
|
|
|
if(istype(source, /turf))
|
|
stack_trace("Turfs are not supported for this!")
|
|
return FALSE
|
|
|
|
|
|
var/atom/last_atom_before_turf = source
|
|
|
|
while(istype(last_atom_before_turf) && !isturf(last_atom_before_turf.loc))
|
|
if(!(last_atom_before_turf.loc))
|
|
stack_trace("Somehow we reached a null loc without finding a turf on our path, possibly the object is in nullspace, or something has gone terribly wrong!")
|
|
return FALSE
|
|
|
|
last_atom_before_turf = last_atom_before_turf.loc
|
|
|
|
return last_atom_before_turf
|
|
|
|
/**
|
|
* Returns the position of a step from start away from finish, in one of the cardinal directions
|
|
*/
|
|
/proc/get_cardinal_step_away(atom/start, atom/finish)
|
|
// Returns only NORTH, SOUTH, EAST, or WEST
|
|
var/dx = finish.x - start.x
|
|
var/dy = finish.y - start.y
|
|
// Slope is above 1:1 (move horizontally in a tie)
|
|
if(abs(dy) > abs (dx))
|
|
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/get_mob_by_key(var/key)
|
|
for(var/mob/M in GLOB.mob_list)
|
|
if(M.ckey == lowertext(key))
|
|
return M
|
|
return null
|
|
|
|
|
|
// Will return a list of active candidates. It increases the buffer 5 times until it finds a candidate which is active within the buffer.
|
|
/proc/get_active_candidates(var/buffer = 1)
|
|
|
|
var/list/candidates = list() //List of candidate KEYS to assume control of the new larva ~Carn
|
|
var/i = 0
|
|
while(candidates.len <= 0 && i < 5)
|
|
for(var/mob/abstract/ghost/observer/G in GLOB.player_list)
|
|
if(((G.client.inactivity/10)/60) <= buffer + i) // the most active players are more likely to become an alien
|
|
if(!(G.mind && G.mind.current && G.mind.current.stat != DEAD))
|
|
candidates += G.key
|
|
i++
|
|
return candidates
|
|
|
|
// Same as above but for alien candidates.
|
|
|
|
/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
|
|
|
|
/datum/projectile_data
|
|
var/src_x
|
|
var/src_y
|
|
var/time
|
|
var/distance
|
|
var/power_x
|
|
var/power_y
|
|
var/dest_x
|
|
var/dest_y
|
|
|
|
/datum/projectile_data/New(var/src_x, var/src_y, var/time, var/distance, \
|
|
var/power_x, var/power_y, var/dest_x, var/dest_y)
|
|
src.src_x = src_x
|
|
src.src_y = src_y
|
|
src.time = time
|
|
src.distance = distance
|
|
src.power_x = power_x
|
|
src.power_y = power_y
|
|
src.dest_x = dest_x
|
|
src.dest_y = dest_y
|
|
|
|
/proc/projectile_trajectory(var/src_x, var/src_y, var/rotation, var/angle, var/power)
|
|
|
|
var/g = 9.81
|
|
var/h = 10
|
|
var/power_x = power * cos(angle)
|
|
var/power_y = power * sin(angle)
|
|
var/time = (power_y + sqrt((power_y*power_y)+(2*g*h)))/g
|
|
var/distance = time * power_x
|
|
|
|
var/dest_x = src_x + distance*sin(rotation);
|
|
var/dest_y = src_y + distance*cos(rotation);
|
|
|
|
return new /datum/projectile_data(src_x, src_y, time, distance, power_x, power_y, dest_x, dest_y)
|
|
|
|
/proc/GetRedPart(const/hexa)
|
|
return hex2num(copytext(hexa,2,4))
|
|
|
|
/proc/GetGreenPart(const/hexa)
|
|
return hex2num(copytext(hexa,4,6))
|
|
|
|
/proc/GetBluePart(const/hexa)
|
|
return hex2num(copytext(hexa,6,8))
|
|
|
|
/proc/GetHexColors(const/hexa)
|
|
return list(
|
|
GetRedPart(hexa),
|
|
GetGreenPart(hexa),
|
|
GetBluePart(hexa)
|
|
)
|
|
|
|
/proc/MixColors(const/list/colors)
|
|
var/list/reds = list()
|
|
var/list/blues = list()
|
|
var/list/greens = list()
|
|
var/list/weights = list()
|
|
|
|
for (var/i = 0, ++i <= colors.len)
|
|
reds.Add(GetRedPart(colors[i]))
|
|
blues.Add(GetBluePart(colors[i]))
|
|
greens.Add(GetGreenPart(colors[i]))
|
|
weights.Add(1)
|
|
|
|
var/r = mixOneColor(weights, reds)
|
|
var/g = mixOneColor(weights, greens)
|
|
var/b = mixOneColor(weights, blues)
|
|
return rgb(r,g,b)
|
|
|
|
/proc/mixOneColor(var/list/weight, var/list/color)
|
|
if (!weight || !color || length(weight)!=length(color))
|
|
return 0
|
|
|
|
var/contents = length(weight)
|
|
var/i
|
|
|
|
//normalize weights
|
|
var/listsum = 0
|
|
for(i=1; i<=contents; i++)
|
|
listsum += weight[i]
|
|
for(i=1; i<=contents; i++)
|
|
weight[i] /= listsum
|
|
|
|
//mix them
|
|
var/mixedcolor = 0
|
|
for(i=1; i<=contents; i++)
|
|
mixedcolor += weight[i]*color[i]
|
|
mixedcolor = round(mixedcolor)
|
|
|
|
//until someone writes a formal proof for this algorithm, let's keep this in
|
|
// if(mixedcolor<0x00 || mixedcolor>0xFF)
|
|
// return 0
|
|
//that's not the kind of operation we are running here, nerd
|
|
mixedcolor=min(max(mixedcolor,0),255)
|
|
|
|
return mixedcolor
|
|
|
|
/**
|
|
* Gets the highest and lowest pressures from the tiles in GLOB.cardinals directions
|
|
* around us, then checks the difference.
|
|
*/
|
|
/proc/getOPressureDifferential(var/turf/loc)
|
|
var/minp=16777216;
|
|
var/maxp=0;
|
|
for(var/dir in GLOB.cardinals)
|
|
var/turf/simulated/T=get_turf(get_step(loc,dir))
|
|
var/cp=0
|
|
if(T && istype(T) && T.zone)
|
|
var/datum/gas_mixture/environment = T.return_air()
|
|
cp = environment.return_pressure()
|
|
else
|
|
if(istype(T,/turf/simulated))
|
|
continue
|
|
if(cp<minp)minp=cp
|
|
if(cp>maxp)maxp=cp
|
|
return abs(minp-maxp)
|
|
|
|
/proc/convert_k2c(var/temp)
|
|
return ((temp - T0C))
|
|
|
|
/proc/convert_c2k(var/temp)
|
|
return ((temp + T0C))
|
|
|
|
/proc/getCardinalAirInfo(var/turf/loc, var/list/stats=list("temperature"))
|
|
var/list/temps = new/list(4)
|
|
for(var/dir in GLOB.cardinals)
|
|
var/direction
|
|
switch(dir)
|
|
if(NORTH)
|
|
direction = 1
|
|
if(SOUTH)
|
|
direction = 2
|
|
if(EAST)
|
|
direction = 3
|
|
if(WEST)
|
|
direction = 4
|
|
var/turf/simulated/T=get_turf(get_step(loc,dir))
|
|
var/list/rstats = new /list(stats.len)
|
|
if(T && istype(T) && T.zone)
|
|
var/datum/gas_mixture/environment = T.return_air()
|
|
for(var/i=1;i<=stats.len;i++)
|
|
if(stats[i] == "pressure")
|
|
rstats[i] = environment.return_pressure()
|
|
else
|
|
rstats[i] = environment.vars[stats[i]]
|
|
else if(istype(T, /turf/simulated))
|
|
rstats = null // Exclude zone (wall, door, etc).
|
|
else if(istype(T, /turf))
|
|
// Should still work. (/turf/return_air())
|
|
var/datum/gas_mixture/environment = T.return_air()
|
|
for(var/i=1;i<=stats.len;i++)
|
|
if(stats[i] == "pressure")
|
|
rstats[i] = environment.return_pressure()
|
|
else
|
|
rstats[i] = environment.vars[stats[i]]
|
|
temps[direction] = rstats
|
|
return temps
|
|
|
|
/proc/MinutesToTicks(var/minutes)
|
|
return SecondsToTicks(60 * minutes)
|
|
|
|
/proc/SecondsToTicks(var/seconds)
|
|
return seconds * 10
|
|
|
|
/proc/round_is_spooky(var/spookiness_threshold = GLOB.config.cult_ghostwriter_req_cultists)
|
|
if(enabled_spooking)
|
|
return 1
|
|
else
|
|
return (GLOB.cult.current_antagonists.len > spookiness_threshold)
|
|
|
|
/**
|
|
* Adds an image to a client's `.images`. Useful as a callback.
|
|
*/
|
|
/proc/add_image_to_client(image/image_to_remove, client/add_to)
|
|
add_to?.images += image_to_remove
|
|
|
|
/**
|
|
* Like add_image_to_client, but will add the image from a list of clients
|
|
*/
|
|
/proc/add_image_to_clients(image/image_to_remove, list/show_to)
|
|
for(var/client/add_to in show_to)
|
|
add_to.images += image_to_remove
|
|
|
|
/**
|
|
* Removes an image from a client's `.images`. Useful as a callback.
|
|
*/
|
|
/proc/remove_image_from_client(image/image_to_remove, client/remove_from)
|
|
remove_from?.images -= image_to_remove
|
|
|
|
/**
|
|
* Like remove_image_from_client, but will remove the image from a list of clients
|
|
*/
|
|
/proc/remove_image_from_clients(image/image_to_remove, list/hide_from)
|
|
for(var/client/remove_from in hide_from)
|
|
remove_from.images -= image_to_remove
|
|
|
|
/**
|
|
* Add an image to a list of clients and calls a proc to remove it after a duration
|
|
*/
|
|
/proc/flick_overlay_global(image/image_to_show, list/show_to, duration)
|
|
if(!show_to || !length(show_to) || !image_to_show)
|
|
return
|
|
for(var/client/add_to in show_to)
|
|
add_to.images += image_to_show
|
|
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(remove_image_from_clients), image_to_show, show_to), duration, TIMER_CLIENT_TIME)
|
|
|
|
/**
|
|
* Helper atom that copies an appearance and exists for a period
|
|
* A list of `/atom/movable` this visual was or will be added into the `vis_contents` of
|
|
*/
|
|
/atom/movable/flick_visual
|
|
|
|
VAR_PROTECTED/list/atom/movable/owners = null
|
|
|
|
/atom/movable/flick_visual/New(loc, list/atom/movable/owners)
|
|
. = ..()
|
|
if(!islist(owners))
|
|
stack_trace("/atom/movable/flick_visual requires a list of owners this was or will be beamed to, to avoid harddels!")
|
|
|
|
src.owners = owners
|
|
|
|
/**
|
|
* Remove us from the vis_contents of the owners, so we can be garbage collected
|
|
* "As anything" is important here, as our "atom/movable" could also be a turf, as per https://secure.byond.com/docs/ref/index.html#/atom/var/vis_contents
|
|
*/
|
|
/atom/movable/flick_visual/Destroy(force)
|
|
for(var/atom/movable/an_owner as anything in src.owners)
|
|
an_owner.vis_contents -= src
|
|
src.owners = null
|
|
|
|
. = ..()
|
|
|
|
/**
|
|
* Flicks a certain overlay onto an atom, handling icon_state strings
|
|
*/
|
|
/atom/proc/flick_overlay(image_to_show, list/show_to, duration, layer)
|
|
var/image/passed_image = \
|
|
istext(image_to_show) \
|
|
? image(icon, src, image_to_show, layer) \
|
|
: image_to_show
|
|
|
|
flick_overlay_global(passed_image, show_to, duration)
|
|
|
|
/**
|
|
* Takes the passed in MA/icon_state, mirrors it onto ourselves, and displays that in world for duration seconds
|
|
* Returns the displayed object, you can animate it and all, but you don't own it, we'll delete it after the duration
|
|
*/
|
|
/atom/proc/flick_overlay_view(mutable_appearance/display, duration)
|
|
if(!display)
|
|
return null
|
|
|
|
var/mutable_appearance/passed_appearance = \
|
|
istext(display) \
|
|
? mutable_appearance(icon, display, layer) \
|
|
: display
|
|
|
|
// If you don't give it a layer, we assume you want it to layer on top of this atom
|
|
// Because this is vis_contents, we need to set the layer manually (you can just set it as you want on return if this is a problem)
|
|
if(passed_appearance.layer == FLOAT_LAYER)
|
|
passed_appearance.layer = layer + 0.1
|
|
|
|
var/atom/movable/lies_to_children = src
|
|
|
|
/// This is faster then pooling. I promise
|
|
var/atom/movable/flick_visual/visual = new(null, list(lies_to_children))
|
|
visual.appearance = passed_appearance
|
|
visual.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
|
// I hate /area
|
|
lies_to_children.vis_contents += visual
|
|
QDEL_IN_CLIENT_TIME(visual, duration)
|
|
return visual
|
|
|
|
/area/flick_overlay_view(mutable_appearance/display, duration)
|
|
return
|
|
|
|
/**
|
|
* Makes people's byond icon flash on the taskbar.
|
|
*/
|
|
/proc/window_flash(client/C)
|
|
if(ismob(C))
|
|
var/mob/M = C
|
|
if(M.client)
|
|
C = M.client
|
|
if(!C)
|
|
return
|
|
winset(C, "mainwindow", "flash=5")
|