mirror of
https://github.com/Citadel-Station-13/Citadel-Station-13-RP.git
synced 2025-12-09 15:28:26 +00:00
* Time to become our TGUI God. * Visually sprucing the copyrights. These shouldn't be ignored :) * babababa * https://github.com/tgstation/tgstation/pull/50422 * dooootdooot * Holy fuck Updates the tools folder Updates our build tooling Updates TGUI MASSIVELY I'm going to go scream in a hole now * ?? * Was it this dum thing? * orrrr * It's this isn't it * Did it manually * hubah * TGUI Changelog * oops * What if I use the original? * Lets try this again * Shit commenting out for now * asdasd * Fuck it use the old one and remember to replace later * Updates yarn.lock * Lets try something horrid * Nope it HATES THAT * fucc * The great eslinting * HOLY SHIT * Final? * ? * asd tgstation/tgstation/pull/59914 tgstation/tgstation/pull/66317 * Improved Asset handling. * Oops * Subsystem stuff * Recompiles the Changelong again. * Finally Fixed Communicators * Compiled Changelogs... AGAIN
1116 lines
34 KiB
Plaintext
1116 lines
34 KiB
Plaintext
/**
|
|
* The base type for nearly all physical objects in SS13
|
|
|
|
* Lots and lots of functionality lives here, although in general we are striving to move
|
|
* as much as possible to the components/elements system
|
|
*/
|
|
/atom
|
|
layer = TURF_LAYER
|
|
var/level = 2
|
|
|
|
/// Default pixel x shifting for the atom's icon.
|
|
var/base_pixel_x = 0
|
|
/// Default pixel y shifting for the atom's icon.
|
|
var/base_pixel_y = 0
|
|
/// Used for changing icon states for different base sprites.
|
|
var/base_icon_state
|
|
/// Atom flags.
|
|
var/flags = NONE
|
|
/// Intearaction flags.
|
|
var/interaction_flags_atom = NONE
|
|
/// Holder for the last time we have been bumped.
|
|
var/last_bumped = 0
|
|
/// Pass flags.
|
|
var/pass_flags = NONE
|
|
/// If true, you can throw things past this atom.
|
|
var/throwpass = FALSE
|
|
/// The higher the germ level, the more germ on the atom.
|
|
var/germ_level = GERM_LEVEL_AMBIENT
|
|
/// Filter for actions - used by lighting overlays.
|
|
var/simulated = TRUE
|
|
/// The 'action' the atom takes to speak.
|
|
var/atom_say_verb = "says"
|
|
/// What icon the atom uses for speechbubbles.
|
|
var/bubble_icon = "normal"
|
|
/// For handling persistent filters
|
|
var/list/filter_data
|
|
/// The orbiter comopnent if we're being orbited.
|
|
var/datum/component/orbiter/orbiters
|
|
|
|
/**
|
|
* used to store the different colors on an atom
|
|
*
|
|
* its inherent color, the colored paint applied on it, special color effect etc...
|
|
*/
|
|
var/list/atom_colours
|
|
|
|
//! ## HUDs
|
|
/// This atom's HUD (med/sec, etc) images. Associative list.
|
|
var/list/image/hud_list = null
|
|
/// HUD images that this atom can provide.
|
|
var/list/hud_possible
|
|
|
|
//! ## TG Smoothing
|
|
/// Icon-smoothing behavior.
|
|
var/smoothing_flags = NONE
|
|
/// What directions this is currently smoothing with. IMPORTANT: This uses the smoothing direction flags as defined in icon_smoothing.dm, instead of the BYOND flags.
|
|
var/smoothing_junction = null //This starts as null for us to know when it's first set, but after that it will hold a 8-bit mask ranging from 0 to 255.
|
|
/// Smoothing variable
|
|
var/top_left_corner
|
|
/// Smoothing variable
|
|
var/top_right_corner
|
|
/// Smoothing variable
|
|
var/bottom_left_corner
|
|
/// Smoothing variable
|
|
var/bottom_right_corner
|
|
/// What smoothing groups does this atom belongs to, to match canSmoothWith. If null, nobody can smooth with it.
|
|
var/list/smoothing_groups = null
|
|
/// List of smoothing groups this atom can smooth with. If this is null and atom is smooth, it smooths only with itself.
|
|
var/list/canSmoothWith = null
|
|
|
|
//! ## Chemistry
|
|
var/datum/reagents/reagents = null
|
|
|
|
//? replaced by OPENCONTAINER flags and atom/proc/is_open_container()
|
|
//var/chem_is_open_container = 0
|
|
|
|
//! ## Detective Work
|
|
/// Used for the duplicate data points kept in the scanners.
|
|
var/list/original_atom
|
|
/// List of all fingerprints on the atom.
|
|
var/list/fingerprints
|
|
/// Same as fingerprints, but only can be seen via VV.
|
|
var/list/fingerprintshidden
|
|
/// Last fingerprints to touch this atom.
|
|
var/fingerprintslast = null
|
|
/// Status flag for if this atom has blood on it.
|
|
var/was_bloodied
|
|
/// Holder for the dna the blood on this atom.
|
|
var/list/blood_DNA
|
|
/// The color of the blood on this atom.
|
|
var/blood_color
|
|
/// Shows up under a UV light.
|
|
var/fluorescent
|
|
|
|
//! ## Overlays
|
|
/// vis overlays managed by SSvis_overlays to automaticaly turn them like other overlays.
|
|
var/list/managed_vis_overlays
|
|
/// Overlays managed by [update_overlays][/atom/proc/update_overlays] to prevent removing overlays that weren't added by the same proc.
|
|
/// Single items are stored on their own, not in a list.
|
|
var/list/managed_overlays
|
|
|
|
/// Our local copy of (non-priority) overlays without byond magic. Use procs in SSoverlays to manipulate.
|
|
var/list/our_overlays
|
|
/// Overlays that should remain on top and not normally removed when using cut_overlay functions, like c4.
|
|
var/list/priority_overlays
|
|
|
|
/* new overlay system
|
|
/// a very temporary list of overlays to remove
|
|
var/list/remove_overlays
|
|
/// a very temporary list of overlays to add
|
|
var/list/add_overlays
|
|
*/
|
|
//! ## Layers
|
|
/// Base layer - defaults to layer.
|
|
var/base_layer
|
|
/// Relative layer - position this atom should be in within things of the same base layer. defaults to 0.
|
|
var/relative_layer = 0
|
|
|
|
/**
|
|
* Called when an atom is created in byond (built in engine proc)
|
|
*
|
|
* Not a lot happens here in SS13 code, as we offload most of the work to the
|
|
* [Intialization][/atom/proc/Initialize] proc, mostly we run the preloader
|
|
* if the preloader is being used and then call [InitAtom][/datum/controller/subsystem/atoms/proc/InitAtom] of which the ultimate
|
|
* result is that the Intialize proc is called.
|
|
*
|
|
* We also generate a tag here if the DF_USE_TAG flag is set on the atom
|
|
*/
|
|
/atom/New(loc, ...)
|
|
//atom creation method that preloads variables at creation
|
|
if(GLOB.use_preloader && (src.type == GLOB._preloader.target_path))//in case the instanciated atom is creating other atoms in New()
|
|
world.preloader_load(src)
|
|
|
|
if(datum_flags & DF_USE_TAG)
|
|
GenerateTag()
|
|
|
|
var/do_initialize = SSatoms.initialized
|
|
if(do_initialize != INITIALIZATION_INSSATOMS)
|
|
args[1] = do_initialize == INITIALIZATION_INNEW_MAPLOAD
|
|
if(SSatoms.InitAtom(src, args))
|
|
//we were deleted
|
|
return
|
|
|
|
/**
|
|
* The primary method that objects are setup in SS13 with
|
|
*
|
|
* we don't use New as we have better control over when this is called and we can choose
|
|
* to delay calls or hook other logic in and so forth
|
|
*
|
|
* During roundstart map parsing, atoms are queued for intialization in the base atom/New(),
|
|
* After the map has loaded, then Initalize is called on all atoms one by one. NB: this
|
|
* is also true for loading map templates as well, so they don't Initalize until all objects
|
|
* in the map file are parsed and present in the world
|
|
*
|
|
* If you're creating an object at any point after SSInit has run then this proc will be
|
|
* immediately be called from New.
|
|
*
|
|
* mapload: This parameter is true if the atom being loaded is either being intialized during
|
|
* the Atom subsystem intialization, or if the atom is being loaded from the map template.
|
|
* If the item is being created at runtime any time after the Atom subsystem is intialized then
|
|
* it's false.
|
|
*
|
|
* The mapload argument occupies the same position as loc when Initialize() is called by New().
|
|
* loc will no longer be needed after it passed New(), and thus it is being overwritten
|
|
* with mapload at the end of atom/New() before this proc (atom/Initialize()) is called.
|
|
*
|
|
* You must always call the parent of this proc, otherwise failures will occur as the item
|
|
* will not be seen as initalized (this can lead to all sorts of strange behaviour, like
|
|
* the item being completely unclickable)
|
|
*
|
|
* !Note: Ignore the note below until the first two lines of the proc are uncommented. -Zandario
|
|
* You must not sleep in this proc, or any subprocs
|
|
*
|
|
* Any parameters from new are passed through (excluding loc), naturally if you're loading from a map
|
|
* there are no other arguments
|
|
*
|
|
* Must return an [initialization hint][INITIALIZE_HINT_NORMAL] or a runtime will occur.
|
|
*
|
|
* !Note: the following functions don't call the base for optimization and must copypasta handling:
|
|
* * [/turf/proc/Initialize]
|
|
*/
|
|
/atom/proc/Initialize(mapload, ...)
|
|
//SHOULD_NOT_SLEEP(TRUE)
|
|
//SHOULD_CALL_PARENT(TRUE)
|
|
if(flags & INITIALIZED)
|
|
stack_trace("Warning: [src]([type]) initialized multiple times!")
|
|
flags |= INITIALIZED
|
|
|
|
//atom color stuff
|
|
if(color)
|
|
add_atom_colour(color, FIXED_COLOUR_PRIORITY)
|
|
|
|
if(light_power && light_range)
|
|
update_light()
|
|
|
|
if (length(smoothing_groups))
|
|
sortTim(smoothing_groups) //In case it's not properly ordered, let's avoid duplicate entries with the same values.
|
|
SET_BITFLAG_LIST(smoothing_groups)
|
|
if (length(canSmoothWith))
|
|
sortTim(canSmoothWith)
|
|
if(canSmoothWith[length(canSmoothWith)] > MAX_S_TURF) //If the last element is higher than the maximum turf-only value, then it must scan turf contents for smoothing targets.
|
|
smoothing_flags |= SMOOTH_OBJ
|
|
SET_BITFLAG_LIST(canSmoothWith)
|
|
|
|
if(opacity && isturf(loc))
|
|
var/turf/T = loc
|
|
T.has_opaque_atom = TRUE // No need to recalculate it in this case, it's guranteed to be on afterwards anyways.
|
|
|
|
ComponentInitialize()
|
|
|
|
return INITIALIZE_HINT_NORMAL
|
|
|
|
/**
|
|
* Late Intialization, for code that should run after all atoms have run Intialization
|
|
*
|
|
* To have your LateIntialize proc be called, your atoms [Initalization][/atom/proc/Initialize]
|
|
* proc must return the hint
|
|
* [INITIALIZE_HINT_LATELOAD] otherwise you will never be called.
|
|
*
|
|
* useful for doing things like finding other machines on GLOB.machines because you can guarantee
|
|
* that all atoms will actually exist in the "WORLD" at this time and that all their Intialization
|
|
* code has been run
|
|
*/
|
|
/atom/proc/LateInitialize()
|
|
set waitfor = FALSE
|
|
|
|
/// Put your [AddComponent] calls here
|
|
/atom/proc/ComponentInitialize()
|
|
return
|
|
|
|
/**
|
|
* Top level of the destroy chain for most atoms
|
|
*
|
|
* Cleans up the following:
|
|
* * Removes alternate apperances from huds that see them
|
|
* * qdels the reagent holder from atoms if it exists
|
|
* * clears the orbiters list
|
|
* * clears overlays and priority overlays
|
|
* * clears the light object
|
|
*/
|
|
/atom/Destroy(force)
|
|
if(alternate_appearances)
|
|
for(var/current_alternate_appearance in alternate_appearances)
|
|
var/datum/atom_hud/alternate_appearance/selected_alternate_appearance = alternate_appearances[current_alternate_appearance]
|
|
selected_alternate_appearance.remove_from_hud(src)
|
|
|
|
if(reagents)
|
|
QDEL_NULL(reagents)
|
|
|
|
orbiters = null // The component is attached to us normaly and will be deleted elsewhere
|
|
|
|
LAZYCLEARLIST(overlays)
|
|
LAZYNULL(managed_overlays)
|
|
|
|
if(light)
|
|
QDEL_NULL(light)
|
|
|
|
if(smoothing_flags & SMOOTH_QUEUED)
|
|
SSicon_smooth.remove_from_queues(src)
|
|
|
|
return ..()
|
|
|
|
/atom/proc/reveal_blood()
|
|
return
|
|
|
|
/// Return flags that should be added to the viewer's sight var.
|
|
// Otherwise return a negative number to indicate that the view should be cancelled.
|
|
/atom/proc/check_eye(user as mob)
|
|
if (istype(user, /mob/living/silicon/ai)) // WHYYYY
|
|
return 0
|
|
return -1
|
|
|
|
/atom/proc/Bumped(atom/movable/bumped_atom)
|
|
set waitfor = FALSE
|
|
SEND_SIGNAL(src, COMSIG_ATOM_BUMPED, bumped_atom)
|
|
|
|
/// Convenience proc to see if a container is open for chemistry handling.
|
|
/atom/proc/is_open_container()
|
|
return flags & OPENCONTAINER
|
|
|
|
///Is this atom within 1 tile of another atom
|
|
/atom/proc/HasProximity(atom/movable/proximity_check_mob as mob|obj)
|
|
return
|
|
|
|
/atom/proc/emp_act(var/severity)
|
|
return
|
|
|
|
|
|
/atom/proc/bullet_act(obj/item/projectile/P, def_zone)
|
|
P.on_hit(src, 0, def_zone)
|
|
. = 0
|
|
|
|
// Called when a blob expands onto the tile the atom occupies.
|
|
/atom/proc/blob_act()
|
|
return
|
|
|
|
///Return true if we're inside the passed in atom
|
|
/atom/proc/in_contents_of(container)//can take class or object instance as argument
|
|
if(ispath(container))
|
|
if(istype(src.loc, container))
|
|
return TRUE
|
|
else if(src in container)
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/*
|
|
* atom/proc/search_contents_for(path,list/filter_path=null)
|
|
* Recursevly searches all atom contens (including contents contents and so on).
|
|
*
|
|
* ARGS: path - search atom contents for atoms of this type
|
|
* list/filter_path - if set, contents of atoms not of types in this list are excluded from search.
|
|
*
|
|
* RETURNS: list of found atoms
|
|
*/
|
|
/atom/proc/search_contents_for(path,list/filter_path=null)
|
|
var/list/found = list()
|
|
for(var/atom/A in src)
|
|
if(istype(A, path))
|
|
found += A
|
|
if(filter_path)
|
|
var/pass = 0
|
|
for(var/type in filter_path)
|
|
pass |= istype(A, type)
|
|
if(!pass)
|
|
continue
|
|
if(A.contents.len)
|
|
found += A.search_contents_for(path,filter_path)
|
|
return found
|
|
|
|
/atom/proc/get_examine_name(mob/user)
|
|
. = "\a [src]"
|
|
var/list/override = list(gender == PLURAL ? "some" : "a", " ", "[name]")
|
|
|
|
var/should_override = FALSE
|
|
|
|
if(SEND_SIGNAL(src, COMSIG_ATOM_GET_EXAMINE_NAME, user, override) & COMPONENT_EXNAME_CHANGED)
|
|
should_override = TRUE
|
|
|
|
|
|
if(blood_DNA && !istype(src, /obj/effect/decal))
|
|
override[EXAMINE_POSITION_BEFORE] = " blood-stained "
|
|
should_override = TRUE
|
|
|
|
if(should_override)
|
|
. = override.Join("")
|
|
|
|
/// Generate the full examine string of this atom (including icon for goonchat)
|
|
/atom/proc/get_examine_string(mob/user, thats = FALSE)
|
|
return "[icon2html(src, user)] [thats? "That's ":""][get_examine_name(user)]"
|
|
|
|
/**
|
|
* Returns an extended list of examine strings for any contained ID cards.
|
|
*
|
|
* Arguments:
|
|
* * user - The user who is doing the examining.
|
|
*/
|
|
/atom/proc/get_id_examine_strings(mob/user)
|
|
. = list()
|
|
return
|
|
|
|
/// Used to insert text after the name but before the description in examine()
|
|
/atom/proc/get_name_chaser(mob/user, list/name_chaser = list())
|
|
return name_chaser
|
|
|
|
/**
|
|
* Called when a mob examines (shift click or verb) this atom
|
|
*
|
|
* Default behaviour is to get the name and icon of the object and it's reagents where
|
|
* the [TRANSPARENT] flag is set on the reagents holder
|
|
*
|
|
* Produces a signal [COMSIG_PARENT_EXAMINE]
|
|
*/
|
|
/atom/proc/examine(mob/user)
|
|
. = list("[get_examine_string(user, TRUE)].")
|
|
|
|
. += get_name_chaser(user)
|
|
if(desc)
|
|
. += desc
|
|
/*
|
|
if(custom_materials)
|
|
var/list/materials_list = list()
|
|
for(var/datum/material/current_material as anything in custom_materials)
|
|
materials_list += "[current_material.name]"
|
|
. += "<u>It is made out of [english_list(materials_list)]</u>."
|
|
*/
|
|
if(reagents)
|
|
if(reagents.reagents_holder_flags & TRANSPARENT)
|
|
. += "It contains:"
|
|
if(length(reagents.reagent_list))
|
|
if(user.can_see_reagents()) //Show each individual reagent
|
|
for(var/datum/reagent/current_reagent as anything in reagents.reagent_list)
|
|
. += "[round(current_reagent.volume, 0.01)] units of [current_reagent.name]"
|
|
else //Otherwise, just show the total volume
|
|
var/total_volume = 0
|
|
for(var/datum/reagent/current_reagent as anything in reagents.reagent_list)
|
|
total_volume += current_reagent.volume
|
|
. += "[total_volume] units of various reagents"
|
|
else
|
|
. += "Nothing."
|
|
else if(reagents.reagents_holder_flags & AMOUNT_VISIBLE)
|
|
if(reagents.total_volume)
|
|
. += SPAN_NOTICE("It has [reagents.total_volume] unit\s left.")
|
|
else
|
|
. += SPAN_DANGER("It's empty.")
|
|
|
|
SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, user, .)
|
|
|
|
/**
|
|
* Updates the appearence of the icon
|
|
*
|
|
* Mostly delegates to update_name, update_desc, and update_icon
|
|
*
|
|
* Arguments:
|
|
* - updates: A set of bitflags dictating what should be updated. Defaults to [ALL]
|
|
*/
|
|
/atom/proc/update_appearance(updates=ALL)
|
|
SHOULD_NOT_SLEEP(TRUE)
|
|
SHOULD_CALL_PARENT(TRUE)
|
|
|
|
. = NONE
|
|
updates &= ~SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_APPEARANCE, updates)
|
|
if(updates & UPDATE_NAME)
|
|
. |= update_name(updates)
|
|
if(updates & UPDATE_DESC)
|
|
. |= update_desc(updates)
|
|
if(updates & UPDATE_ICON)
|
|
. |= update_icon(updates)
|
|
|
|
/// Updates the name of the atom
|
|
/atom/proc/update_name(updates=ALL)
|
|
// SHOULD_CALL_PARENT(TRUE)
|
|
return SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_NAME, updates)
|
|
|
|
/// Updates the description of the atom
|
|
/atom/proc/update_desc(updates=ALL)
|
|
// SHOULD_CALL_PARENT(TRUE)
|
|
return SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_DESC, updates)
|
|
|
|
/// Updates the icon of the atom
|
|
/atom/proc/update_icon(updates=ALL)
|
|
SIGNAL_HANDLER
|
|
// SHOULD_CALL_PARENT(TRUE)
|
|
|
|
. = NONE
|
|
updates &= ~SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_ICON, updates)
|
|
if(updates & UPDATE_ICON_STATE)
|
|
update_icon_state()
|
|
. |= UPDATE_ICON_STATE
|
|
|
|
if(updates & UPDATE_OVERLAYS)
|
|
if(LAZYLEN(managed_vis_overlays))
|
|
SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays)
|
|
|
|
var/list/new_overlays = update_overlays(updates)
|
|
if(managed_overlays)
|
|
cut_overlay(managed_overlays)
|
|
managed_overlays = null
|
|
if(length(new_overlays))
|
|
if (length(new_overlays) == 1)
|
|
managed_overlays = new_overlays[1]
|
|
else
|
|
managed_overlays = new_overlays
|
|
add_overlay(new_overlays)
|
|
. |= UPDATE_OVERLAYS
|
|
|
|
. |= SEND_SIGNAL(src, COMSIG_ATOM_UPDATED_ICON, updates, .)
|
|
|
|
/// Updates the icon state of the atom
|
|
/atom/proc/update_icon_state()
|
|
SHOULD_CALL_PARENT(TRUE)
|
|
return SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_ICON_STATE)
|
|
|
|
/// Updates the overlays of the atom
|
|
/atom/proc/update_overlays()
|
|
SHOULD_CALL_PARENT(TRUE)
|
|
. = list()
|
|
SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_OVERLAYS, .)
|
|
|
|
// called by mobs when e.g. having the atom as their machine, pulledby, loc (AKA mob being inside the atom) or buckled var set.
|
|
// see code/modules/mob/mob_movement.dm for more.
|
|
/atom/proc/relaymove()
|
|
return
|
|
|
|
// Called to set the atom's density and used to add behavior to density changes.
|
|
/atom/proc/set_density(var/new_density)
|
|
if(density == new_density)
|
|
return FALSE
|
|
density = !!new_density // Sanitize to be strictly 0 or 1
|
|
return TRUE
|
|
|
|
// Called to set the atom's invisibility and usd to add behavior to invisibility changes.
|
|
/atom/proc/set_invisibility(var/new_invisibility)
|
|
if(invisibility == new_invisibility)
|
|
return FALSE
|
|
invisibility = new_invisibility
|
|
return TRUE
|
|
|
|
/**
|
|
* React to being hit by an explosion
|
|
*
|
|
* Should be called through the [EX_ACT] wrapper macro.
|
|
* The wrapper takes care of the [COMSIG_ATOM_EX_ACT] signal.
|
|
* as well as calling [/atom/proc/contents_explosion].
|
|
*/
|
|
/atom/proc/ex_act(severity, target)
|
|
set waitfor = FALSE
|
|
|
|
/atom/proc/emag_act(var/remaining_charges, var/mob/user, var/emag_source)
|
|
return -1
|
|
|
|
/atom/proc/fire_act()
|
|
return
|
|
|
|
// Returns an assoc list of RCD information.
|
|
// Example would be: list(RCD_VALUE_MODE = RCD_DECONSTRUCT, RCD_VALUE_DELAY = 50, RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 4)
|
|
// This occurs before rcd_act() is called, and it won't be called if it returns FALSE.
|
|
/atom/proc/rcd_values(mob/living/user, obj/item/rcd/the_rcd, passed_mode)
|
|
return FALSE
|
|
|
|
/atom/proc/rcd_act(mob/living/user, obj/item/rcd/the_rcd, passed_mode)
|
|
return
|
|
|
|
/atom/proc/melt()
|
|
return
|
|
|
|
/atom/proc/hitby(atom/movable/hitting_atom as mob|obj)
|
|
if(density)
|
|
hitting_atom.throwing = 0
|
|
return
|
|
|
|
/atom/proc/add_hiddenprint(mob/living/M as mob)
|
|
if(isnull(M)) return
|
|
if(isnull(M.key)) return
|
|
if (ishuman(M))
|
|
var/mob/living/carbon/human/H = M
|
|
if (!istype(H.dna, /datum/dna))
|
|
return 0
|
|
if (H.gloves)
|
|
if(src.fingerprintslast != H.key)
|
|
src.fingerprintshidden += text("\[[time_stamp()]\] (Wearing gloves). Real name: [], Key: []",H.real_name, H.key)
|
|
src.fingerprintslast = H.key
|
|
return 0
|
|
if (!( src.fingerprints ))
|
|
if(src.fingerprintslast != H.key)
|
|
src.fingerprintshidden += text("\[[time_stamp()]\] Real name: [], Key: []",H.real_name, H.key)
|
|
src.fingerprintslast = H.key
|
|
return 1
|
|
else
|
|
if(src.fingerprintslast != M.key)
|
|
src.fingerprintshidden += text("\[[time_stamp()]\] Real name: [], Key: []",M.real_name, M.key)
|
|
src.fingerprintslast = M.key
|
|
return
|
|
|
|
/atom/proc/add_fingerprint(mob/M, ignoregloves, obj/item/tool)
|
|
if(isnull(M))
|
|
return
|
|
if(isAI(M))
|
|
return
|
|
if(!M || !M.key)
|
|
return
|
|
if(istype(tool) && (tool.flags & NOPRINT))
|
|
return
|
|
if (ishuman(M))
|
|
//Add the list if it does not exist.
|
|
if(!fingerprintshidden)
|
|
fingerprintshidden = list()
|
|
|
|
//Fibers~
|
|
add_fibers(M)
|
|
|
|
//He has no prints!
|
|
if (mFingerprints in M.mutations)
|
|
if(fingerprintslast != M.key)
|
|
fingerprintshidden += "[time_stamp()]: [key_name(M)] (No fingerprints mutation)"
|
|
fingerprintslast = M.key
|
|
return 0 //Now, lets get to the dirty work.
|
|
//First, make sure their DNA makes sense.
|
|
var/mob/living/carbon/human/H = M
|
|
if (!istype(H.dna, /datum/dna) || !H.dna.uni_identity || (length(H.dna.uni_identity) != 32))
|
|
if(!istype(H.dna, /datum/dna))
|
|
H.dna = new /datum/dna(null)
|
|
H.dna.real_name = H.real_name
|
|
H.check_dna()
|
|
|
|
//Now, deal with gloves.
|
|
if (H.gloves && H.gloves != src)
|
|
if(fingerprintslast != H.key)
|
|
fingerprintshidden += "[time_stamp()]: [key_name(H)] (Wearing [H.gloves])"
|
|
fingerprintslast = H.key
|
|
H.gloves.add_fingerprint(M)
|
|
|
|
//Deal with gloves the pass finger/palm prints.
|
|
if(!ignoregloves)
|
|
if(H.gloves && H.gloves != src)
|
|
if(istype(H.gloves, /obj/item/clothing/gloves))
|
|
var/obj/item/clothing/gloves/G = H.gloves
|
|
if(!prob(G.fingerprint_chance))
|
|
return 0
|
|
|
|
//More adminstuffz
|
|
if(fingerprintslast != H.key)
|
|
fingerprintshidden += "[time_stamp()]: [key_name(H)]"
|
|
fingerprintslast = H.key
|
|
|
|
//Make the list if it does not exist.
|
|
if(!fingerprints)
|
|
fingerprints = list()
|
|
|
|
//Hash this shit.
|
|
var/full_print = H.get_full_print()
|
|
|
|
// Add the fingerprints
|
|
//
|
|
if(fingerprints[full_print])
|
|
switch(stringpercent(fingerprints[full_print])) //tells us how many stars are in the current prints.
|
|
|
|
if(28 to 32)
|
|
if(prob(1))
|
|
fingerprints[full_print] = full_print // You rolled a one buddy.
|
|
else
|
|
fingerprints[full_print] = stars(full_print, rand(0,40)) // 24 to 32
|
|
|
|
if(24 to 27)
|
|
if(prob(3))
|
|
fingerprints[full_print] = full_print //Sucks to be you.
|
|
else
|
|
fingerprints[full_print] = stars(full_print, rand(15, 55)) // 20 to 29
|
|
|
|
if(20 to 23)
|
|
if(prob(5))
|
|
fingerprints[full_print] = full_print //Had a good run didn't ya.
|
|
else
|
|
fingerprints[full_print] = stars(full_print, rand(30, 70)) // 15 to 25
|
|
|
|
if(16 to 19)
|
|
if(prob(5))
|
|
fingerprints[full_print] = full_print //Welp.
|
|
else
|
|
fingerprints[full_print] = stars(full_print, rand(40, 100)) // 0 to 21
|
|
|
|
if(0 to 15)
|
|
if(prob(5))
|
|
fingerprints[full_print] = stars(full_print, rand(0,50)) // small chance you can smudge.
|
|
else
|
|
fingerprints[full_print] = full_print
|
|
|
|
else
|
|
fingerprints[full_print] = stars(full_print, rand(0, 20)) //Initial touch, not leaving much evidence the first time.
|
|
|
|
|
|
return 1
|
|
else
|
|
//Smudge up dem prints some
|
|
if(fingerprintslast != M.key)
|
|
fingerprintshidden += "[time_stamp()]: [key_name(M)]"
|
|
fingerprintslast = M.key
|
|
|
|
//Cleaning up shit.
|
|
if(fingerprints && !fingerprints.len)
|
|
qdel(fingerprints)
|
|
return
|
|
|
|
|
|
/atom/proc/transfer_fingerprints_to(var/atom/A)
|
|
|
|
if(!istype(A.fingerprints,/list))
|
|
A.fingerprints = list()
|
|
|
|
if(!istype(A.fingerprintshidden,/list))
|
|
A.fingerprintshidden = list()
|
|
|
|
if(!istype(fingerprintshidden, /list))
|
|
fingerprintshidden = list()
|
|
|
|
//skytodo
|
|
//A.fingerprints |= fingerprints //detective
|
|
//A.fingerprintshidden |= fingerprintshidden //admin
|
|
if(A.fingerprints && fingerprints)
|
|
A.fingerprints |= fingerprints.Copy() //detective
|
|
if(A.fingerprintshidden && fingerprintshidden)
|
|
A.fingerprintshidden |= fingerprintshidden.Copy() //admin A.fingerprintslast = fingerprintslast
|
|
|
|
|
|
/// Returns 1 if made bloody, returns 0 otherwise
|
|
/atom/proc/add_blood(mob/living/carbon/human/M as mob)
|
|
|
|
if(flags & NOBLOODY)
|
|
return 0
|
|
|
|
if(!blood_DNA || !istype(blood_DNA, /list)) //if our list of DNA doesn't exist yet (or isn't a list) initialise it.
|
|
blood_DNA = list()
|
|
|
|
was_bloodied = 1
|
|
if(!blood_color)
|
|
blood_color = "#A10808"
|
|
if(istype(M))
|
|
if (!istype(M.dna, /datum/dna))
|
|
M.dna = new /datum/dna(null)
|
|
M.dna.real_name = M.real_name
|
|
M.check_dna()
|
|
blood_color = M.species.get_blood_colour(M)
|
|
. = 1
|
|
return 1
|
|
|
|
/atom/proc/add_vomit_floor(mob/living/carbon/M as mob, var/toxvomit = 0)
|
|
if( istype(src, /turf/simulated) )
|
|
var/obj/effect/decal/cleanable/vomit/this = new /obj/effect/decal/cleanable/vomit(src)
|
|
this.virus2 = virus_copylist(M.virus2)
|
|
|
|
// Make toxins vomit look different
|
|
if(toxvomit)
|
|
this.icon_state = "vomittox_[pick(1,4)]"
|
|
|
|
/atom/proc/clean_blood()
|
|
if(!simulated)
|
|
return
|
|
fluorescent = 0
|
|
src.germ_level = 0
|
|
if(istype(blood_DNA, /list))
|
|
blood_DNA = null
|
|
return 1
|
|
|
|
/atom/proc/get_global_map_pos()
|
|
if(!islist(global_map) || !length(global_map)) return
|
|
var/cur_x = null
|
|
var/cur_y = null
|
|
var/list/y_arr = null
|
|
for(cur_x=1,cur_x<=global_map.len,cur_x++)
|
|
y_arr = global_map[cur_x]
|
|
cur_y = y_arr.Find(src.z)
|
|
if(cur_y)
|
|
break
|
|
// to_chat(world, "X = [cur_x]; Y = [cur_y]")
|
|
if(cur_x && cur_y)
|
|
return list("x"=cur_x,"y"=cur_y)
|
|
else
|
|
return 0
|
|
|
|
/atom/proc/checkpass(passflag)
|
|
return (pass_flags&passflag)
|
|
|
|
/atom/proc/isinspace()
|
|
if(istype(get_turf(src), /turf/space))
|
|
return 1
|
|
else
|
|
return 0
|
|
|
|
/// Show a message to all mobs and objects in sight of this atom
|
|
/// Use for objects performing visible actions
|
|
/// message is output to anyone who can see, e.g. "The [src] does something!"
|
|
/// blind_message (optional) is what blind people will hear e.g. "You hear something!"
|
|
/atom/proc/visible_message(message, self_message, blind_message, range = world.view)
|
|
|
|
var/list/see
|
|
if(isbelly(loc))
|
|
var/obj/belly/B = loc
|
|
see = B.get_mobs_and_objs_in_belly()
|
|
else
|
|
see = get_mobs_and_objs_in_view_fast(get_turf(src),range,remote_ghosts = FALSE)
|
|
|
|
var/list/seeing_mobs = see["mobs"]
|
|
var/list/seeing_objs = see["objs"]
|
|
|
|
for(var/obj in seeing_objs)
|
|
var/obj/O = obj
|
|
O.show_message(message, 1, blind_message, 2)
|
|
for(var/mob in seeing_mobs)
|
|
var/mob/M = mob
|
|
if(self_message && (M == src))
|
|
M.show_message( self_message, 1, blind_message, 2)
|
|
else if((M.see_invisible >= invisibility) && MOB_CAN_SEE_PLANE(M, plane))
|
|
M.show_message(message, 1, blind_message, 2)
|
|
else if(blind_message)
|
|
M.show_message(blind_message, 2)
|
|
|
|
/// Show a message to all mobs and objects in earshot of this atom
|
|
/// Use for objects performing audible actions
|
|
/// message is the message output to anyone who can hear.
|
|
/// deaf_message (optional) is what deaf people will see.
|
|
/// hearing_distance (optional) is the range, how many tiles away the message can be heard.
|
|
/atom/proc/audible_message(var/message, var/deaf_message, var/hearing_distance)
|
|
|
|
var/range = hearing_distance || world.view
|
|
var/list/hear = get_mobs_and_objs_in_view_fast(get_turf(src),range,remote_ghosts = FALSE)
|
|
|
|
var/list/hearing_mobs = hear["mobs"]
|
|
var/list/hearing_objs = hear["objs"]
|
|
|
|
for(var/obj in hearing_objs)
|
|
var/obj/O = obj
|
|
O.show_message(message, 2, deaf_message, 1)
|
|
|
|
for(var/mob in hearing_mobs)
|
|
var/mob/M = mob
|
|
var/msg = message
|
|
M.show_message(msg, 2, deaf_message, 1)
|
|
|
|
/atom/movable/proc/dropInto(var/atom/destination)
|
|
while(istype(destination))
|
|
var/atom/drop_destination = destination.onDropInto(src)
|
|
if(!istype(drop_destination) || drop_destination == destination)
|
|
return forceMove(destination)
|
|
destination = drop_destination
|
|
return moveToNullspace()
|
|
|
|
/atom/proc/onDropInto(var/atom/movable/AM)
|
|
return // If onDropInto returns null, then dropInto will forceMove AM into us.
|
|
|
|
/atom/movable/onDropInto(var/atom/movable/AM)
|
|
return loc // If onDropInto returns something, then dropInto will attempt to drop AM there.
|
|
|
|
/atom/proc/InsertedContents()
|
|
return contents
|
|
|
|
/atom/proc/drop_location()
|
|
var/atom/L = loc
|
|
if(!L)
|
|
return null
|
|
return L.AllowDrop() ? L : L.drop_location()
|
|
|
|
/atom/proc/AllowDrop()
|
|
return FALSE
|
|
|
|
/atom/proc/get_nametag_name(mob/user)
|
|
return name
|
|
|
|
/atom/proc/get_nametag_desc(mob/user)
|
|
return "" //Desc itself is often too long to use
|
|
|
|
/atom/proc/GenerateTag()
|
|
return
|
|
|
|
/atom/proc/atom_say(message)
|
|
if(!message)
|
|
return
|
|
var/list/speech_bubble_hearers = list()
|
|
for(var/mob/M in get_hearers_in_view(7, src))
|
|
M.show_message("<span class='game say'><span class='name'>[src]</span> [atom_say_verb], \"[message]\"</span>", 2, null, 1)
|
|
if(M.client)
|
|
speech_bubble_hearers += M.client
|
|
|
|
if(length(speech_bubble_hearers))
|
|
var/image/I = generate_speech_bubble(src, "[bubble_icon][say_test(message)]", FLY_LAYER)
|
|
I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA
|
|
INVOKE_ASYNC(GLOBAL_PROC, /.proc/flick_overlay, I, speech_bubble_hearers, 30)
|
|
|
|
/atom/proc/speech_bubble(bubble_state = "", bubble_loc = src, list/bubble_recipients = list())
|
|
return
|
|
|
|
|
|
//! ## Atom Colour Priority System
|
|
/**
|
|
* A System that gives finer control over which atom colour to colour the atom with.
|
|
* The "highest priority" one is always displayed as opposed to the default of
|
|
* "whichever was set last is displayed"
|
|
*/
|
|
|
|
/// Adds an instance of colour_type to the atom's atom_colours list
|
|
/atom/proc/add_atom_colour(coloration, colour_priority)
|
|
if(!atom_colours || !atom_colours.len)
|
|
atom_colours = list()
|
|
atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently.
|
|
if(!coloration)
|
|
return
|
|
if(colour_priority > atom_colours.len)
|
|
return
|
|
atom_colours[colour_priority] = coloration
|
|
update_atom_colour()
|
|
|
|
/// Removes an instance of colour_type from the atom's atom_colours list
|
|
/atom/proc/remove_atom_colour(colour_priority, coloration)
|
|
if(!atom_colours)
|
|
atom_colours = list()
|
|
atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently.
|
|
if(colour_priority > atom_colours.len)
|
|
return
|
|
if(coloration && atom_colours[colour_priority] != coloration)
|
|
return //if we don't have the expected color (for a specific priority) to remove, do nothing
|
|
atom_colours[colour_priority] = null
|
|
update_atom_colour()
|
|
|
|
/// Resets the atom's color to null, and then sets it to the highest priority colour available
|
|
/atom/proc/update_atom_colour()
|
|
if(!atom_colours)
|
|
atom_colours = list()
|
|
atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently.
|
|
color = null
|
|
for(var/C in atom_colours)
|
|
if(islist(C))
|
|
var/list/L = C
|
|
if(L.len)
|
|
color = L
|
|
return
|
|
else if(C)
|
|
color = C
|
|
return
|
|
|
|
/// Setter for the `base_pixel_x` variable to append behavior related to its changing.
|
|
/atom/proc/set_base_pixel_x(new_value)
|
|
if(base_pixel_x == new_value)
|
|
return
|
|
. = base_pixel_x
|
|
base_pixel_x = new_value
|
|
|
|
pixel_x = pixel_x + base_pixel_x - .
|
|
|
|
/// Setter for the `base_pixel_y` variable to append behavior related to its changing.
|
|
/atom/proc/set_base_pixel_y(new_value)
|
|
if(base_pixel_y == new_value)
|
|
return
|
|
. = base_pixel_y
|
|
base_pixel_y = new_value
|
|
|
|
pixel_y = pixel_y + base_pixel_y - .
|
|
|
|
/**
|
|
* Returns true if this atom has gravity for the passed in turf
|
|
*
|
|
* Sends signals COMSIG_ATOM_HAS_GRAVITY and COMSIG_TURF_HAS_GRAVITY, both can force gravity with
|
|
* the forced gravity var
|
|
*
|
|
* Gravity situations:
|
|
* * No gravity if you're not in a turf
|
|
* * No gravity if this atom is in is a space turf
|
|
* * Gravity if the area it's in always has gravity
|
|
* * Gravity if there's a gravity generator on the z level
|
|
* * Gravity if the Z level has an SSMappingTrait for ZTRAIT_GRAVITY
|
|
* * otherwise no gravity
|
|
*/
|
|
/atom/proc/has_gravity(turf/T)
|
|
if(!T || !isturf(T))
|
|
T = get_turf(src)
|
|
|
|
if(!T)
|
|
return 0
|
|
|
|
/*
|
|
var/list/forced_gravity = list()
|
|
SEND_SIGNAL(src, COMSIG_ATOM_HAS_GRAVITY, T, forced_gravity)
|
|
if(!forced_gravity.len)
|
|
SEND_SIGNAL(T, COMSIG_TURF_HAS_GRAVITY, src, forced_gravity)
|
|
if(forced_gravity.len)
|
|
var/max_grav
|
|
for(var/i in forced_gravity)
|
|
max_grav = max(max_grav, i)
|
|
return max_grav
|
|
*/
|
|
|
|
if(isspaceturf(T)) // Turf never has gravity
|
|
return 0
|
|
|
|
var/area/A = get_area(T)
|
|
if(A.has_gravity) // Areas which always has gravity
|
|
return A.has_gravity
|
|
/*
|
|
else
|
|
// There's a gravity generator on our z level
|
|
if(GLOB.gravity_generators["[T.z]"])
|
|
var/max_grav = 0
|
|
for(var/obj/machinery/gravity_generator/main/G in GLOB.gravity_generators["[T.z]"])
|
|
max_grav = max(G.setting,max_grav)
|
|
return max_grav
|
|
*/
|
|
return SSmapping.level_trait(T.z, ZTRAIT_GRAVITY)
|
|
|
|
/atom/proc/is_incorporeal()
|
|
return FALSE
|
|
|
|
/// Tool behavior procedure. Redirects to tool-specific procs by default.
|
|
/// You can override it to catch all tool interactions, for use in complex deconstruction procs.
|
|
/// Just don't forget to return ..() in the end.
|
|
/atom/proc/tool_act(mob/living/user, obj/item/I, tool_type)
|
|
switch(tool_type)
|
|
if(TOOL_CROWBAR)
|
|
return crowbar_act(user, I)
|
|
if(TOOL_MULTITOOL)
|
|
return multitool_act(user, I)
|
|
if(TOOL_SCREWDRIVER)
|
|
return screwdriver_act(user, I)
|
|
if(TOOL_WRENCH)
|
|
return wrench_act(user, I)
|
|
if(TOOL_WIRECUTTER)
|
|
return wirecutter_act(user, I)
|
|
if(TOOL_WELDER)
|
|
return welder_act(user, I)
|
|
if(TOOL_ANALYZER)
|
|
return analyzer_act(user, I)
|
|
|
|
// Tool-specific behavior procs. To be overridden in subtypes.
|
|
/atom/proc/crowbar_act(mob/living/user, obj/item/I)
|
|
return
|
|
|
|
/atom/proc/multitool_act(mob/living/user, obj/item/I)
|
|
return
|
|
|
|
/atom/proc/multitool_check_buffer(user, obj/item/I, silent = FALSE)
|
|
if(!I.tool_behaviour == TOOL_MULTITOOL)
|
|
if(user && !silent)
|
|
to_chat(user, SPAN_WARNING("[I] has no data buffer!"))
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/atom/proc/screwdriver_act(mob/living/user, obj/item/I)
|
|
SEND_SIGNAL(src, COMSIG_ATOM_SCREWDRIVER_ACT, user, I)
|
|
|
|
/atom/proc/wrench_act(mob/living/user, obj/item/I)
|
|
return
|
|
|
|
/atom/proc/wirecutter_act(mob/living/user, obj/item/I)
|
|
return
|
|
|
|
/atom/proc/welder_act(mob/living/user, obj/item/I)
|
|
return
|
|
|
|
/atom/proc/analyzer_act(mob/living/user, obj/item/I)
|
|
return
|
|
|
|
/atom/proc/CheckParts(list/parts_list)
|
|
for(var/A in parts_list)
|
|
if(istype(A, /datum/reagent))
|
|
if(!reagents)
|
|
reagents = new()
|
|
reagents.reagent_list.Add(A)
|
|
reagents.conditional_update()
|
|
else if(ismovable(A))
|
|
var/atom/movable/M = A
|
|
if(isliving(M.loc))
|
|
var/mob/living/L = M.loc
|
|
L.transferItemToLoc(M, src)
|
|
else
|
|
M.forceMove(src)
|
|
|
|
/atom/proc/is_drainable()
|
|
return reagents && (reagents.reagents_holder_flags & DRAINABLE)
|
|
|
|
/atom/proc/add_filter(name,priority,list/params)
|
|
LAZYINITLIST(filter_data)
|
|
var/list/copied_parameters = params.Copy()
|
|
copied_parameters["priority"] = priority
|
|
filter_data[name] = copied_parameters
|
|
update_filters()
|
|
|
|
/atom/proc/update_filters()
|
|
filters = null
|
|
filter_data = sortTim(filter_data, /proc/cmp_filter_data_priority, TRUE)
|
|
for(var/f in filter_data)
|
|
var/list/data = filter_data[f]
|
|
var/list/arguments = data.Copy()
|
|
arguments -= "priority"
|
|
filters += filter(arglist(arguments))
|
|
UNSETEMPTY(filter_data)
|
|
|
|
/atom/proc/transition_filter(name, time, list/new_params, easing, loop)
|
|
var/filter = get_filter(name)
|
|
if(!filter)
|
|
return
|
|
|
|
var/list/old_filter_data = filter_data[name]
|
|
|
|
var/list/params = old_filter_data.Copy()
|
|
for(var/thing in new_params)
|
|
params[thing] = new_params[thing]
|
|
|
|
animate(filter, new_params, time = time, easing = easing, loop = loop)
|
|
for(var/param in params)
|
|
filter_data[name][param] = params[param]
|
|
|
|
/atom/proc/change_filter_priority(name, new_priority)
|
|
if(!filter_data || !filter_data[name])
|
|
return
|
|
|
|
filter_data[name]["priority"] = new_priority
|
|
update_filters()
|
|
|
|
// /obj/item/update_filters()
|
|
// . = ..()
|
|
// update_action_buttons()
|
|
|
|
/atom/proc/get_filter(name)
|
|
if(filter_data && filter_data[name])
|
|
return filters[filter_data.Find(name)]
|
|
|
|
/atom/proc/remove_filter(name_or_names)
|
|
if(!filter_data)
|
|
return
|
|
|
|
var/list/names = islist(name_or_names) ? name_or_names : list(name_or_names)
|
|
|
|
for(var/name in names)
|
|
if(filter_data[name])
|
|
filter_data -= name
|
|
update_filters()
|
|
|
|
/atom/proc/clear_filters()
|
|
filter_data = null
|
|
filters = null
|
|
|
|
/// Sets the new base layer we should be on.
|
|
/atom/proc/set_base_layer(new_layer)
|
|
ASSERT(isnum(new_layer))
|
|
base_layer = new_layer
|
|
// rel layer being null is fine
|
|
layer = base_layer + 0.000001 * relative_layer
|
|
|
|
/// Set the relative layer within our layer we should be on.
|
|
/atom/proc/set_relative_layer(new_layer)
|
|
ASSERT(isnum(new_layer))
|
|
if(isnull(base_layer))
|
|
base_layer = layer
|
|
relative_layer = new_layer
|
|
// base layer being null isn't
|
|
layer = base_layer + 0.000001 * relative_layer
|
|
|
|
/atom/proc/get_cell()
|
|
return
|