mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-24 08:02:57 +00:00
* removes double spaces AFTER symbols * Fixing conflicts Co-authored-by: John Willard <53777086+JohnFulpWillard@users.noreply.github.com> Co-authored-by: GoldenAlpharex <jerego1234@hotmail.com>
2126 lines
74 KiB
Plaintext
2126 lines
74 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
|
|
plane = GAME_PLANE
|
|
appearance_flags = TILE_BOUND
|
|
|
|
/// pass_flags that we are. If any of this matches a pass_flag on a moving thing, by default, we let them through.
|
|
var/pass_flags_self = NONE
|
|
|
|
///If non-null, overrides a/an/some in all cases
|
|
var/article
|
|
|
|
///First atom flags var
|
|
var/flags_1 = NONE
|
|
///Intearaction flags
|
|
var/interaction_flags_atom = NONE
|
|
|
|
var/flags_ricochet = NONE
|
|
|
|
///When a projectile tries to ricochet off this atom, the projectile ricochet chance is multiplied by this
|
|
var/receive_ricochet_chance_mod = 1
|
|
///When a projectile ricochets off this atom, it deals the normal damage * this modifier to this atom
|
|
var/receive_ricochet_damage_coeff = 0.33
|
|
|
|
///Reagents holder
|
|
var/datum/reagents/reagents = null
|
|
|
|
///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
|
|
|
|
///Value used to increment ex_act() if reactionary_explosions is on
|
|
var/explosion_block = 0
|
|
|
|
/**
|
|
* 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
|
|
|
|
|
|
/// a very temporary list of overlays to remove
|
|
var/list/remove_overlays
|
|
/// a very temporary list of overlays to add
|
|
var/list/add_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
|
|
|
|
///Proximity monitor associated with this atom
|
|
var/datum/proximity_monitor/proximity_monitor
|
|
///Cooldown tick timer for buckle messages
|
|
var/buckle_message_cooldown = 0
|
|
///Last fingerprints to touch this atom
|
|
var/fingerprintslast
|
|
|
|
var/list/filter_data //For handling persistent filters
|
|
|
|
//List of datums orbiting this atom
|
|
var/datum/component/orbiter/orbiters
|
|
|
|
/// Radiation insulation types
|
|
var/rad_insulation = RAD_NO_INSULATION
|
|
|
|
/// The icon state intended to be used for the acid component. Used to override the default acid overlay icon state.
|
|
var/custom_acid_overlay = null
|
|
|
|
///The custom materials this atom is made of, used by a lot of things like furniture, walls, and floors (if I finish the functionality, that is.)
|
|
///The list referenced by this var can be shared by multiple objects and should not be directly modified. Instead, use [set_custom_materials][/atom/proc/set_custom_materials].
|
|
var/list/datum/material/custom_materials
|
|
///Bitfield for how the atom handles materials.
|
|
var/material_flags = NONE
|
|
///Modifier that raises/lowers the effect of the amount of a material, prevents small and easy to get items from being death machines.
|
|
var/material_modifier = 1
|
|
|
|
var/datum/wires/wires = null
|
|
|
|
var/list/alternate_appearances
|
|
|
|
///Light systems, both shouldn't be active at the same time.
|
|
var/light_system = STATIC_LIGHT
|
|
///Range of the light in tiles. Zero means no light.
|
|
var/light_range = 0
|
|
///Intensity of the light. The stronger, the less shadows you will see on the lit area.
|
|
var/light_power = 1
|
|
///Hexadecimal RGB string representing the colour of the light. White by default.
|
|
var/light_color = COLOR_WHITE
|
|
///Boolean variable for toggleable lights. Has no effect without the proper light_system, light_range and light_power values.
|
|
var/light_on = TRUE
|
|
///Bitflags to determine lighting-related atom properties.
|
|
var/light_flags = NONE
|
|
///Our light source. Don't fuck with this directly unless you have a good reason!
|
|
var/tmp/datum/light_source/light
|
|
///Any light sources that are "inside" of us, for example, if src here was a mob that's carrying a flashlight, that flashlight's light source would be part of this list.
|
|
var/tmp/list/light_sources
|
|
|
|
/// Last name used to calculate a color for the chatmessage overlays
|
|
var/chat_color_name
|
|
/// Last color calculated for the the chatmessage overlays
|
|
var/chat_color
|
|
/// A luminescence-shifted value of the last color calculated for chatmessage overlays
|
|
var/chat_color_darkened
|
|
|
|
///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
|
|
|
|
///The config type to use for greyscaled sprites. Both this and greyscale_colors must be assigned to work.
|
|
var/greyscale_config
|
|
///A string of hex format colors to be used by greyscale sprites, ex: "#0054aa#badcff"
|
|
var/greyscale_colors
|
|
|
|
///Holds merger groups currently active on the atom. Do not access directly, use GetMergeGroup() instead.
|
|
var/list/datum/merger/mergers
|
|
|
|
///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
|
|
///Reference to atom being orbited
|
|
var/atom/orbit_target
|
|
///AI controller that controls this atom. type on init, then turned into an instance during runtime
|
|
var/datum/ai_controller/ai_controller
|
|
|
|
///any atom that uses integrity and can be damaged must set this to true, otherwise the integrity procs will throw an error
|
|
var/uses_integrity = FALSE
|
|
|
|
var/datum/armor/armor
|
|
VAR_PRIVATE/atom_integrity //defaults to max_integrity
|
|
var/max_integrity = 500
|
|
var/integrity_failure = 0 //0 if we have no special broken behavior, otherwise is a percentage of at what point the atom breaks. 0.5 being 50%
|
|
///Damage under this value will be completely ignored
|
|
var/damage_deflection = 0
|
|
|
|
var/resistance_flags = NONE // INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ON_FIRE | UNACIDABLE | ACID_PROOF
|
|
|
|
/**
|
|
* 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, FALSE, 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)
|
|
*
|
|
* 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]
|
|
* * [/turf/open/space/proc/Initialize]
|
|
*/
|
|
/atom/proc/Initialize(mapload, ...)
|
|
SHOULD_NOT_SLEEP(TRUE)
|
|
SHOULD_CALL_PARENT(TRUE)
|
|
if(flags_1 & INITIALIZED_1)
|
|
stack_trace("Warning: [src]([type]) initialized multiple times!")
|
|
flags_1 |= INITIALIZED_1
|
|
|
|
if(loc)
|
|
SEND_SIGNAL(loc, COMSIG_ATOM_CREATED, src) /// Sends a signal that the new atom `src`, has been created at `loc`
|
|
|
|
if(greyscale_config && greyscale_colors)
|
|
update_greyscale()
|
|
|
|
//atom color stuff
|
|
if(color)
|
|
add_atom_colour(color, FIXED_COLOUR_PRIORITY)
|
|
|
|
if (light_system == STATIC_LIGHT && 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(uses_integrity)
|
|
if (islist(armor))
|
|
armor = getArmor(arglist(armor))
|
|
else if (!armor)
|
|
armor = getArmor()
|
|
else if (!istype(armor, /datum/armor))
|
|
stack_trace("Invalid type [armor.type] found in .armor during /atom Initialize()")
|
|
atom_integrity = max_integrity
|
|
|
|
// apply materials properly from the default custom_materials value
|
|
// This MUST come after atom_integrity is set above, as if old materials get removed,
|
|
// atom_integrity is checked against max_integrity and can BREAK the atom.
|
|
// The integrity to max_integrity ratio is still preserved.
|
|
set_custom_materials(custom_materials)
|
|
|
|
ComponentInitialize()
|
|
InitializeAIController()
|
|
|
|
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)
|
|
|
|
QDEL_NULL(light)
|
|
QDEL_NULL(ai_controller)
|
|
|
|
if(smoothing_flags & SMOOTH_QUEUED)
|
|
SSicon_smooth.remove_from_queues(src)
|
|
|
|
return ..()
|
|
|
|
/atom/proc/handle_ricochet(obj/projectile/ricocheting_projectile)
|
|
var/turf/p_turf = get_turf(ricocheting_projectile)
|
|
var/face_direction = get_dir(src, p_turf)
|
|
var/face_angle = dir2angle(face_direction)
|
|
var/incidence_s = GET_ANGLE_OF_INCIDENCE(face_angle, (ricocheting_projectile.Angle + 180))
|
|
var/a_incidence_s = abs(incidence_s)
|
|
if(a_incidence_s > 90 && a_incidence_s < 270)
|
|
return FALSE
|
|
if((ricocheting_projectile.flag in list(BULLET, BOMB)) && ricocheting_projectile.ricochet_incidence_leeway)
|
|
if((a_incidence_s < 90 && a_incidence_s < 90 - ricocheting_projectile.ricochet_incidence_leeway) || (a_incidence_s > 270 && a_incidence_s -270 > ricocheting_projectile.ricochet_incidence_leeway))
|
|
return FALSE
|
|
var/new_angle_s = SIMPLIFY_DEGREES(face_angle + incidence_s)
|
|
ricocheting_projectile.set_angle(new_angle_s)
|
|
return TRUE
|
|
|
|
/// Whether the mover object can avoid being blocked by this atom, while arriving from (or leaving through) the border_dir.
|
|
/atom/proc/CanPass(atom/movable/mover, border_dir)
|
|
SHOULD_CALL_PARENT(TRUE)
|
|
SHOULD_BE_PURE(TRUE)
|
|
if(mover.movement_type & PHASING)
|
|
return TRUE
|
|
. = CanAllowThrough(mover, border_dir)
|
|
// This is cheaper than calling the proc every time since most things dont override CanPassThrough
|
|
if(!mover.generic_canpass)
|
|
return mover.CanPassThrough(src, REVERSE_DIR(border_dir), .)
|
|
|
|
/// Returns true or false to allow the mover to move through src
|
|
/atom/proc/CanAllowThrough(atom/movable/mover, border_dir)
|
|
SHOULD_CALL_PARENT(TRUE)
|
|
//SHOULD_BE_PURE(TRUE)
|
|
if(mover.pass_flags & pass_flags_self)
|
|
return TRUE
|
|
if(mover.throwing && (pass_flags_self & LETPASSTHROW))
|
|
return TRUE
|
|
return !density
|
|
|
|
/**
|
|
* Is this atom currently located on centcom
|
|
*
|
|
* Specifically, is it on the z level and within the centcom areas
|
|
*
|
|
* You can also be in a shuttleshuttle during endgame transit
|
|
*
|
|
* Used in gamemode to identify mobs who have escaped and for some other areas of the code
|
|
* who don't want atoms where they shouldn't be
|
|
*/
|
|
/atom/proc/onCentCom()
|
|
var/turf/current_turf = get_turf(src)
|
|
if(!current_turf)
|
|
return FALSE
|
|
|
|
if(is_reserved_level(current_turf.z))
|
|
for(var/obj/docking_port/mobile/mobile_docking_port as anything in SSshuttle.mobile)
|
|
if(mobile_docking_port.launch_status != ENDGAME_TRANSIT)
|
|
continue
|
|
for(var/area/shuttle/shuttle_area as anything in mobile_docking_port.shuttle_areas)
|
|
if(current_turf in shuttle_area)
|
|
return TRUE
|
|
|
|
if(!is_centcom_level(current_turf.z))//if not, don't bother
|
|
return FALSE
|
|
|
|
//Check for centcom itself
|
|
if(istype(current_turf.loc, /area/centcom))
|
|
return TRUE
|
|
|
|
//Check for centcom shuttles
|
|
for(var/obj/docking_port/mobile/mobile_docking_port as anything in SSshuttle.mobile)
|
|
if(mobile_docking_port.launch_status == ENDGAME_LAUNCHED)
|
|
for(var/place as anything in mobile_docking_port.shuttle_areas)
|
|
var/area/shuttle/shuttle_area = place
|
|
if(current_turf in shuttle_area)
|
|
return TRUE
|
|
|
|
/**
|
|
* Is the atom in any of the centcom syndicate areas
|
|
*
|
|
* Either in the syndie base on centcom, or any of their shuttles
|
|
*
|
|
* Also used in gamemode code for win conditions
|
|
*/
|
|
/atom/proc/onSyndieBase()
|
|
var/turf/current_turf = get_turf(src)
|
|
if(!current_turf)
|
|
return FALSE
|
|
|
|
if(!is_centcom_level(current_turf.z))//if not, don't bother
|
|
return FALSE
|
|
|
|
if(istype(current_turf.loc, /area/shuttle/syndicate) || istype(current_turf.loc, /area/syndicate_mothership) || istype(current_turf.loc, /area/shuttle/assault_pod))
|
|
return TRUE
|
|
|
|
return FALSE
|
|
|
|
/**
|
|
* Is the atom in an away mission
|
|
*
|
|
* Must be in the away mission z-level to return TRUE
|
|
*
|
|
* Also used in gamemode code for win conditions
|
|
*/
|
|
/atom/proc/onAwayMission()
|
|
var/turf/current_turf = get_turf(src)
|
|
if(!current_turf)
|
|
return FALSE
|
|
|
|
if(is_away_level(current_turf.z))
|
|
return TRUE
|
|
|
|
return FALSE
|
|
|
|
|
|
|
|
///This atom has been hit by a hulkified mob in hulk mode (user)
|
|
/atom/proc/attack_hulk(mob/living/carbon/human/user)
|
|
SEND_SIGNAL(src, COMSIG_ATOM_HULK_ATTACK, user)
|
|
|
|
/**
|
|
* Ensure a list of atoms/reagents exists inside this atom
|
|
*
|
|
* Goes throught he list of passed in parts, if they're reagents, adds them to our reagent holder
|
|
* creating the reagent holder if it exists.
|
|
*
|
|
* If the part is a moveable atom and the previous location of the item was a mob/living,
|
|
* it calls the inventory handler transferItemToLoc for that mob/living and transfers the part
|
|
* to this atom
|
|
*
|
|
* Otherwise it simply forceMoves the atom into this atom
|
|
*/
|
|
/atom/proc/CheckParts(list/parts_list, datum/crafting_recipe/current_recipe)
|
|
SEND_SIGNAL(src, COMSIG_ATOM_CHECKPARTS, parts_list, current_recipe)
|
|
if(!parts_list)
|
|
return
|
|
for(var/part in parts_list)
|
|
if(istype(part, /datum/reagent))
|
|
if(!reagents)
|
|
reagents = new()
|
|
reagents.reagent_list.Add(part)
|
|
reagents.conditional_update()
|
|
else if(ismovable(part))
|
|
var/atom/movable/object = part
|
|
if(isliving(object.loc))
|
|
var/mob/living/living = object.loc
|
|
living.transferItemToLoc(object, src)
|
|
else
|
|
object.forceMove(src)
|
|
SEND_SIGNAL(object, COMSIG_ATOM_USED_IN_CRAFT, src)
|
|
parts_list.Cut()
|
|
|
|
///Take air from the passed in gas mixture datum
|
|
/atom/proc/assume_air(datum/gas_mixture/giver)
|
|
return null
|
|
|
|
///Remove air from this atom
|
|
/atom/proc/remove_air(amount)
|
|
return null
|
|
|
|
///Return the current air environment in this atom
|
|
/atom/proc/return_air()
|
|
if(loc)
|
|
return loc.return_air()
|
|
else
|
|
return null
|
|
|
|
///Return the air if we can analyze it
|
|
/atom/proc/return_analyzable_air()
|
|
return null
|
|
|
|
///Check if this atoms eye is still alive (probably)
|
|
/atom/proc/check_eye(mob/user)
|
|
SIGNAL_HANDLER
|
|
return
|
|
|
|
/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 is_refillable() && is_drainable()
|
|
|
|
/// Is this atom injectable into other atoms
|
|
/atom/proc/is_injectable(mob/user, allowmobs = TRUE)
|
|
return reagents && (reagents.flags & (INJECTABLE | REFILLABLE))
|
|
|
|
/// Can we draw from this atom with an injectable atom
|
|
/atom/proc/is_drawable(mob/user, allowmobs = TRUE)
|
|
return reagents && (reagents.flags & (DRAWABLE | DRAINABLE))
|
|
|
|
/// Can this atoms reagents be refilled
|
|
/atom/proc/is_refillable()
|
|
return reagents && (reagents.flags & REFILLABLE)
|
|
|
|
/// Is this atom drainable of reagents
|
|
/atom/proc/is_drainable()
|
|
return reagents && (reagents.flags & DRAINABLE)
|
|
|
|
/** Handles exposing this atom to a list of reagents.
|
|
*
|
|
* Sends COMSIG_ATOM_EXPOSE_REAGENTS
|
|
* Calls expose_atom() for every reagent in the reagent list.
|
|
*
|
|
* Arguments:
|
|
* - [reagents][/list]: The list of reagents the atom is being exposed to.
|
|
* - [source][/datum/reagents]: The reagent holder the reagents are being sourced from.
|
|
* - methods: How the atom is being exposed to the reagents. Bitflags.
|
|
* - volume_modifier: Volume multiplier.
|
|
* - show_message: Whether to display anything to mobs when they are exposed.
|
|
*/
|
|
/atom/proc/expose_reagents(list/reagents, datum/reagents/source, methods=TOUCH, volume_modifier=1, show_message=TRUE)
|
|
. = SEND_SIGNAL(src, COMSIG_ATOM_EXPOSE_REAGENTS, reagents, source, methods, volume_modifier, show_message)
|
|
if(. & COMPONENT_NO_EXPOSE_REAGENTS)
|
|
return
|
|
|
|
SEND_SIGNAL(source, COMSIG_REAGENTS_EXPOSE_ATOM, src, reagents, methods, volume_modifier, show_message)
|
|
for(var/datum/reagent/current_reagent as anything in reagents)
|
|
. |= current_reagent.expose_atom(src, reagents[current_reagent])
|
|
|
|
/// Are you allowed to drop this atom
|
|
/atom/proc/AllowDrop()
|
|
return FALSE
|
|
|
|
///Is this atom within 1 tile of another atom
|
|
/atom/proc/HasProximity(atom/movable/proximity_check_mob as mob|obj)
|
|
return
|
|
|
|
/**
|
|
* React to an EMP of the given severity
|
|
*
|
|
* Default behaviour is to send the [COMSIG_ATOM_EMP_ACT] signal
|
|
*
|
|
* If the signal does not return protection, and there are attached wires then we call
|
|
* [emp_pulse][/datum/wires/proc/emp_pulse] on the wires
|
|
*
|
|
* We then return the protection value
|
|
*/
|
|
/atom/proc/emp_act(severity)
|
|
var/protection = SEND_SIGNAL(src, COMSIG_ATOM_EMP_ACT, severity)
|
|
if(!(protection & EMP_PROTECT_WIRES) && istype(wires))
|
|
wires.emp_pulse()
|
|
return protection // Pass the protection value collected here upwards
|
|
|
|
/**
|
|
* React to a hit by a projectile object
|
|
*
|
|
* Default behaviour is to send the [COMSIG_ATOM_BULLET_ACT] and then call [on_hit][/obj/projectile/proc/on_hit] on the projectile
|
|
*
|
|
* @params
|
|
* P - projectile
|
|
* def_zone - zone hit
|
|
* piercing_hit - is this hit piercing or normal?
|
|
*/
|
|
/atom/proc/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit = FALSE)
|
|
SEND_SIGNAL(src, COMSIG_ATOM_BULLET_ACT, hitting_projectile, def_zone)
|
|
. = hitting_projectile.on_hit(src, 0, def_zone, piercing_hit)
|
|
|
|
///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
|
|
|
|
/**
|
|
* Get the name of this object for examine
|
|
*
|
|
* You can override what is returned from this proc by registering to listen for the
|
|
* [COMSIG_ATOM_GET_EXAMINE_NAME] signal
|
|
*/
|
|
/atom/proc/get_examine_name(mob/user)
|
|
. = "\a [src]"
|
|
var/list/override = list(gender == PLURAL ? "some" : "a", " ", "[name]")
|
|
if(article)
|
|
. = "[article] [src]"
|
|
override[EXAMINE_POSITION_ARTICLE] = article
|
|
if(SEND_SIGNAL(src, COMSIG_ATOM_GET_EXAMINE_NAME, user, override) & COMPONENT_EXNAME_CHANGED)
|
|
. = 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)].<hr>")
|
|
|
|
. += get_name_chaser(user)
|
|
if(desc)
|
|
. += desc
|
|
|
|
if(custom_materials)
|
|
. += "<hr>" //SKYRAT EDIT ADDITION
|
|
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)
|
|
. += "<hr>" //SKYRAT EDIT ADDITION
|
|
if(reagents.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]"
|
|
if(reagents.is_reacting)
|
|
. += span_warning("It is currently reacting!")
|
|
. += span_notice("The solution's pH is [round(reagents.ph, 0.01)] and has a temperature of [reagents.chem_temp]K.")
|
|
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.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, .)
|
|
/**
|
|
* Called when a mob examines (shift click or verb) this atom twice (or more) within EXAMINE_MORE_WINDOW (default 1 second)
|
|
*
|
|
* This is where you can put extra information on something that may be superfluous or not important in critical gameplay
|
|
* moments, while allowing people to manually double-examine to take a closer look
|
|
*
|
|
* Produces a signal [COMSIG_PARENT_EXAMINE_MORE]
|
|
*/
|
|
/atom/proc/examine_more(mob/user)
|
|
. = list()
|
|
SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE_MORE, 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, .)
|
|
|
|
/// Handles updates to greyscale value updates.
|
|
/// The colors argument can be either a list or the full color string.
|
|
/// Child procs should call parent last so the update happens after all changes.
|
|
/atom/proc/set_greyscale(list/colors, new_config)
|
|
SHOULD_CALL_PARENT(TRUE)
|
|
if(istype(colors))
|
|
colors = colors.Join("")
|
|
if(!isnull(colors) && greyscale_colors != colors) // If you want to disable greyscale stuff then give a blank string
|
|
greyscale_colors = colors
|
|
|
|
if(!isnull(new_config) && greyscale_config != new_config)
|
|
greyscale_config = new_config
|
|
|
|
update_greyscale()
|
|
|
|
/// Checks if this atom uses the GAGS system and if so updates the icon
|
|
/atom/proc/update_greyscale()
|
|
SHOULD_CALL_PARENT(TRUE)
|
|
if(greyscale_colors && greyscale_config)
|
|
icon = SSgreyscale.GetColoredIconByType(greyscale_config, greyscale_colors)
|
|
if(!smoothing_flags) // This is a bitfield but we're just checking that some sort of smoothing is happening
|
|
return
|
|
update_atom_colour()
|
|
QUEUE_SMOOTH(src)
|
|
|
|
/**
|
|
* An atom we are buckled or is contained within us has tried to move
|
|
*
|
|
* Default behaviour is to send a warning that the user can't move while buckled as long
|
|
* as the [buckle_message_cooldown][/atom/var/buckle_message_cooldown] has expired (50 ticks)
|
|
*/
|
|
/atom/proc/relaymove(mob/living/user, direction)
|
|
if(SEND_SIGNAL(src, COMSIG_ATOM_RELAYMOVE, user, direction) & COMSIG_BLOCK_RELAYMOVE)
|
|
return
|
|
if(buckle_message_cooldown <= world.time)
|
|
buckle_message_cooldown = world.time + 50
|
|
to_chat(user, span_warning("You can't move while buckled to [src]!"))
|
|
return
|
|
|
|
/**
|
|
* A special case of relaymove() in which the person relaying the move may be "driving" this atom
|
|
*
|
|
* This is a special case for vehicles and ridden animals where the relayed movement may be handled
|
|
* by the riding component attached to this atom. Returns TRUE as long as there's nothing blocking
|
|
* the movement, or FALSE if the signal gets a reply that specifically blocks the movement
|
|
*/
|
|
/atom/proc/relaydrive(mob/living/user, direction)
|
|
return !(SEND_SIGNAL(src, COMSIG_RIDDEN_DRIVER_MOVE, user, direction) & COMPONENT_DRIVER_BLOCK_MOVE)
|
|
|
|
/// Handle what happens when your contents are exploded by a bomb
|
|
/atom/proc/contents_explosion(severity, target)
|
|
return //For handling the effects of explosions on contents that would not normally be effected
|
|
|
|
/**
|
|
* React to being hit by an explosion
|
|
*
|
|
* Default behaviour is to call [contents_explosion][/atom/proc/contents_explosion] and send the [COMSIG_ATOM_EX_ACT] signal
|
|
*/
|
|
/atom/proc/ex_act(severity, target)
|
|
set waitfor = FALSE
|
|
contents_explosion(severity, target)
|
|
SEND_SIGNAL(src, COMSIG_ATOM_EX_ACT, severity, target)
|
|
|
|
/**
|
|
* React to a hit by a blob objecd
|
|
*
|
|
* default behaviour is to send the [COMSIG_ATOM_BLOB_ACT] signal
|
|
*/
|
|
/atom/proc/blob_act(obj/structure/blob/attacking_blob)
|
|
var/blob_act_result = SEND_SIGNAL(src, COMSIG_ATOM_BLOB_ACT, attacking_blob)
|
|
if (blob_act_result & COMPONENT_CANCEL_BLOB_ACT)
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/atom/proc/fire_act(exposed_temperature, exposed_volume)
|
|
SEND_SIGNAL(src, COMSIG_ATOM_FIRE_ACT, exposed_temperature, exposed_volume)
|
|
return
|
|
|
|
/**
|
|
* React to being hit by a thrown object
|
|
*
|
|
* Default behaviour is to call [hitby_react][/atom/proc/hitby_react] on ourselves after 2 seconds if we are dense
|
|
* and under normal gravity.
|
|
*
|
|
* Im not sure why this the case, maybe to prevent lots of hitby's if the thrown object is
|
|
* deleted shortly after hitting something (during explosions or other massive events that
|
|
* throw lots of items around - singularity being a notable example)
|
|
*/
|
|
/atom/proc/hitby(atom/movable/hitting_atom, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
|
|
SEND_SIGNAL(src, COMSIG_ATOM_HITBY, hitting_atom, skipcatch, hitpush, blocked, throwingdatum)
|
|
if(density && !has_gravity(hitting_atom)) //thrown stuff bounces off dense stuff in no grav, unless the thrown stuff ends up inside what it hit(embedding, bola, etc...).
|
|
addtimer(CALLBACK(src, .proc/hitby_react, hitting_atom), 2)
|
|
|
|
/**
|
|
* We have have actually hit the passed in atom
|
|
*
|
|
* Default behaviour is to move back from the item that hit us
|
|
*/
|
|
/atom/proc/hitby_react(atom/movable/harmed_atom)
|
|
if(harmed_atom && isturf(harmed_atom.loc))
|
|
step(harmed_atom, turn(harmed_atom.dir, 180))
|
|
|
|
///Handle the atom being slipped over
|
|
/atom/proc/handle_slip(mob/living/carbon/slipped_carbon, knockdown_amount, obj/slipping_object, lube, paralyze, force_drop)
|
|
return
|
|
|
|
///returns the mob's dna info as a list, to be inserted in an object's blood_DNA list
|
|
/mob/living/proc/get_blood_dna_list()
|
|
if(get_blood_id() != /datum/reagent/blood)
|
|
return
|
|
return list("ANIMAL DNA" = "Y-")
|
|
|
|
///Get the mobs dna list
|
|
/mob/living/carbon/get_blood_dna_list()
|
|
if(get_blood_id() != /datum/reagent/blood)
|
|
return
|
|
var/list/blood_dna = list()
|
|
if(dna)
|
|
blood_dna[dna.unique_enzymes] = dna.blood_type
|
|
else
|
|
blood_dna["UNKNOWN DNA"] = "X*"
|
|
return blood_dna
|
|
|
|
/mob/living/carbon/alien/get_blood_dna_list()
|
|
return list("UNKNOWN DNA" = "X*")
|
|
|
|
/mob/living/silicon/get_blood_dna_list()
|
|
return
|
|
|
|
///to add a mob's dna info into an object's blood_dna list.
|
|
/atom/proc/transfer_mob_blood_dna(mob/living/injected_mob)
|
|
// Returns 0 if we have that blood already
|
|
var/new_blood_dna = injected_mob.get_blood_dna_list()
|
|
if(!new_blood_dna)
|
|
return FALSE
|
|
var/old_length = blood_DNA_length()
|
|
add_blood_DNA(new_blood_dna)
|
|
if(blood_DNA_length() == old_length)
|
|
return FALSE
|
|
return TRUE
|
|
|
|
///to add blood from a mob onto something, and transfer their dna info
|
|
/atom/proc/add_mob_blood(mob/living/injected_mob)
|
|
var/list/blood_dna = injected_mob.get_blood_dna_list()
|
|
if(!blood_dna)
|
|
return FALSE
|
|
return add_blood_DNA(blood_dna)
|
|
|
|
///Is this atom in space
|
|
/atom/proc/isinspace()
|
|
if(isspaceturf(get_turf(src)))
|
|
return TRUE
|
|
else
|
|
return FALSE
|
|
|
|
///Used for making a sound when a mob involuntarily falls into the ground.
|
|
/atom/proc/handle_fall(mob/faller)
|
|
return
|
|
|
|
///Respond to the singularity eating this atom
|
|
/atom/proc/singularity_act()
|
|
return
|
|
|
|
/**
|
|
* Respond to the singularity pulling on us
|
|
*
|
|
* Default behaviour is to send [COMSIG_ATOM_SING_PULL] and return
|
|
*/
|
|
/atom/proc/singularity_pull(obj/singularity/singularity, current_size)
|
|
SEND_SIGNAL(src, COMSIG_ATOM_SING_PULL, singularity, current_size)
|
|
|
|
|
|
/**
|
|
* Respond to acid being used on our atom
|
|
*
|
|
* Default behaviour is to send [COMSIG_ATOM_ACID_ACT] and return
|
|
*/
|
|
/atom/proc/acid_act(acidpwr, acid_volume)
|
|
SEND_SIGNAL(src, COMSIG_ATOM_ACID_ACT, acidpwr, acid_volume)
|
|
return FALSE
|
|
|
|
/**
|
|
* Respond to an emag being used on our atom
|
|
*
|
|
* Default behaviour is to send [COMSIG_ATOM_EMAG_ACT] and return
|
|
*/
|
|
/atom/proc/emag_act(mob/user, obj/item/card/emag/emag_card)
|
|
SEND_SIGNAL(src, COMSIG_ATOM_EMAG_ACT, user, emag_card)
|
|
|
|
/**
|
|
* Respond to narsie eating our atom
|
|
*
|
|
* Default behaviour is to send [COMSIG_ATOM_NARSIE_ACT] and return
|
|
*/
|
|
/atom/proc/narsie_act()
|
|
SEND_SIGNAL(src, COMSIG_ATOM_NARSIE_ACT)
|
|
|
|
|
|
///Return the values you get when an RCD eats you?
|
|
/atom/proc/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd)
|
|
return FALSE
|
|
|
|
|
|
/**
|
|
* Respond to an RCD acting on our item
|
|
*
|
|
* Default behaviour is to send [COMSIG_ATOM_RCD_ACT] and return FALSE
|
|
*/
|
|
/atom/proc/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, passed_mode)
|
|
SEND_SIGNAL(src, COMSIG_ATOM_RCD_ACT, user, the_rcd, passed_mode)
|
|
return FALSE
|
|
|
|
/**
|
|
* Respond to an electric bolt action on our item
|
|
*
|
|
* Default behaviour is to return, we define here to allow for cleaner code later on
|
|
*/
|
|
/atom/proc/zap_act(power, zap_flags)
|
|
return
|
|
|
|
/**
|
|
* Implement the behaviour for when a user click drags a storage object to your atom
|
|
*
|
|
* This behaviour is usually to mass transfer, but this is no longer a used proc as it just
|
|
* calls the underyling /datum/component/storage dump act if a component exists
|
|
*
|
|
* TODO these should be purely component items that intercept the atom clicks higher in the
|
|
* call chain
|
|
*/
|
|
/atom/proc/storage_contents_dump_act(obj/item/storage/src_object, mob/user)
|
|
if(GetComponent(/datum/component/storage))
|
|
return component_storage_contents_dump_act(src_object, user)
|
|
return FALSE
|
|
|
|
/**
|
|
* Implement the behaviour for when a user click drags another storage item to you
|
|
*
|
|
* In this case we get as many of the tiems from the target items compoent storage and then
|
|
* put everything into ourselves (or our storage component)
|
|
*
|
|
* TODO these should be purely component items that intercept the atom clicks higher in the
|
|
* call chain
|
|
*/
|
|
/atom/proc/component_storage_contents_dump_act(datum/component/storage/src_object, mob/user)
|
|
var/list/things = src_object.contents()
|
|
var/datum/progressbar/progress = new(user, things.len, src)
|
|
var/datum/component/storage/STR = GetComponent(/datum/component/storage)
|
|
while (do_after(user, 1 SECONDS, src, NONE, FALSE, CALLBACK(STR, /datum/component/storage.proc/handle_mass_item_insertion, things, src_object, user, progress)))
|
|
stoplag(1)
|
|
progress.end_progress()
|
|
to_chat(user, span_notice("You dump as much of [src_object.parent]'s contents [STR.insert_preposition]to [src] as you can."))
|
|
STR.orient2hud(user)
|
|
src_object.orient2hud(user)
|
|
if(user.active_storage) //refresh the HUD to show the transfered contents
|
|
user.active_storage.close(user)
|
|
user.active_storage.show_to(user)
|
|
return TRUE
|
|
|
|
///Get the best place to dump the items contained in the source storage item?
|
|
/atom/proc/get_dumping_location(obj/item/storage/source,mob/user)
|
|
return null
|
|
|
|
/**
|
|
* This proc is called when an atom in our contents has it's [Destroy][/atom/proc/Destroy] called
|
|
*
|
|
* Default behaviour is to simply send [COMSIG_ATOM_CONTENTS_DEL]
|
|
*/
|
|
/atom/proc/handle_atom_del(atom/deleting_atom)
|
|
SEND_SIGNAL(src, COMSIG_ATOM_CONTENTS_DEL, deleting_atom)
|
|
|
|
/**
|
|
* called when the turf the atom resides on is ChangeTurfed
|
|
*
|
|
* Default behaviour is to loop through atom contents and call their HandleTurfChange() proc
|
|
*/
|
|
/atom/proc/HandleTurfChange(turf/changing_turf)
|
|
for(var/atom/current_atom as anything in src)
|
|
current_atom.HandleTurfChange(changing_turf)
|
|
|
|
/**
|
|
* the vision impairment to give to the mob whose perspective is set to that atom
|
|
*
|
|
* (e.g. an unfocused camera giving you an impaired vision when looking through it)
|
|
*/
|
|
/atom/proc/get_remote_view_fullscreens(mob/user)
|
|
return
|
|
|
|
/**
|
|
* the sight changes to give to the mob whose perspective is set to that atom
|
|
*
|
|
* (e.g. A mob with nightvision loses its nightvision while looking through a normal camera)
|
|
*/
|
|
/atom/proc/update_remote_sight(mob/living/user)
|
|
return
|
|
|
|
|
|
/**
|
|
* Hook for running code when a dir change occurs
|
|
*
|
|
* Not recommended to use, listen for the [COMSIG_ATOM_DIR_CHANGE] signal instead (sent by this proc)
|
|
*/
|
|
/atom/proc/setDir(newdir)
|
|
SHOULD_CALL_PARENT(TRUE)
|
|
SEND_SIGNAL(src, COMSIG_ATOM_DIR_CHANGE, dir, newdir)
|
|
dir = newdir
|
|
|
|
/**
|
|
* Called when the atom log's in or out
|
|
*
|
|
* Default behaviour is to call on_log on the location this atom is in
|
|
*/
|
|
/atom/proc/on_log(login)
|
|
if(loc)
|
|
loc.on_log(login)
|
|
|
|
|
|
/*
|
|
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)
|
|
return
|
|
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()
|
|
color = null
|
|
if(!atom_colours)
|
|
return
|
|
for(var/checked_color in atom_colours)
|
|
if(islist(checked_color))
|
|
var/list/color_list = checked_color
|
|
if(color_list.len)
|
|
color = color_list
|
|
return
|
|
else if(checked_color)
|
|
color = checked_color
|
|
return
|
|
|
|
|
|
/**
|
|
* Wash this atom
|
|
*
|
|
* This will clean it off any temporary stuff like blood. Override this in your item to add custom cleaning behavior.
|
|
* Returns true if any washing was necessary and thus performed
|
|
* Arguments:
|
|
* * clean_types: any of the CLEAN_ constants
|
|
*/
|
|
/atom/proc/wash(clean_types)
|
|
SHOULD_CALL_PARENT(TRUE)
|
|
|
|
. = FALSE
|
|
if(SEND_SIGNAL(src, COMSIG_COMPONENT_CLEAN_ACT, clean_types) & COMPONENT_CLEANED)
|
|
. = TRUE
|
|
|
|
// Basically "if has washable coloration"
|
|
if(length(atom_colours) >= WASHABLE_COLOUR_PRIORITY && atom_colours[WASHABLE_COLOUR_PRIORITY])
|
|
remove_atom_colour(WASHABLE_COLOUR_PRIORITY)
|
|
return TRUE
|
|
|
|
/**
|
|
* call back when a var is edited on this atom
|
|
*
|
|
* Can be used to implement special handling of vars
|
|
*
|
|
* At the atom level, if you edit a var named "color" it will add the atom colour with
|
|
* admin level priority to the atom colours list
|
|
*
|
|
* Also, if GLOB.Debug2 is FALSE, it sets the [ADMIN_SPAWNED_1] flag on [flags_1][/atom/var/flags_1], which signifies
|
|
* the object has been admin edited
|
|
*/
|
|
/atom/vv_edit_var(var_name, var_value)
|
|
switch(var_name)
|
|
if(NAMEOF(src, light_range))
|
|
if(light_system == STATIC_LIGHT)
|
|
set_light(l_range = var_value)
|
|
else
|
|
set_light_range(var_value)
|
|
. = TRUE
|
|
if(NAMEOF(src, light_power))
|
|
if(light_system == STATIC_LIGHT)
|
|
set_light(l_power = var_value)
|
|
else
|
|
set_light_power(var_value)
|
|
. = TRUE
|
|
if(NAMEOF(src, light_color))
|
|
if(light_system == STATIC_LIGHT)
|
|
set_light(l_color = var_value)
|
|
else
|
|
set_light_color(var_value)
|
|
. = TRUE
|
|
if(NAMEOF(src, light_on))
|
|
set_light_on(var_value)
|
|
. = TRUE
|
|
if(NAMEOF(src, light_flags))
|
|
set_light_flags(var_value)
|
|
. = TRUE
|
|
if(NAMEOF(src, smoothing_junction))
|
|
set_smoothed_icon_state(var_value)
|
|
. = TRUE
|
|
if(NAMEOF(src, opacity))
|
|
set_opacity(var_value)
|
|
. = TRUE
|
|
if(NAMEOF(src, base_pixel_x))
|
|
set_base_pixel_x(var_value)
|
|
. = TRUE
|
|
if(NAMEOF(src, base_pixel_y))
|
|
set_base_pixel_y(var_value)
|
|
. = TRUE
|
|
|
|
if(!isnull(.))
|
|
datum_flags |= DF_VAR_EDITED
|
|
return
|
|
|
|
if(!GLOB.Debug2)
|
|
flags_1 |= ADMIN_SPAWNED_1
|
|
|
|
. = ..()
|
|
|
|
switch(var_name)
|
|
if(NAMEOF(src, color))
|
|
add_atom_colour(color, ADMIN_COLOUR_PRIORITY)
|
|
|
|
|
|
/**
|
|
* Return the markup to for the dropdown list for the VV panel for this atom
|
|
*
|
|
* Override in subtypes to add custom VV handling in the VV panel
|
|
*/
|
|
/atom/vv_get_dropdown()
|
|
. = ..()
|
|
VV_DROPDOWN_OPTION("", "---------")
|
|
if(!ismovable(src))
|
|
var/turf/curturf = get_turf(src)
|
|
if(curturf)
|
|
. += "<option value='?_src_=holder;[HrefToken()];adminplayerobservecoodjump=1;X=[curturf.x];Y=[curturf.y];Z=[curturf.z]'>Jump To</option>"
|
|
VV_DROPDOWN_OPTION(VV_HK_MODIFY_TRANSFORM, "Modify Transform")
|
|
VV_DROPDOWN_OPTION(VV_HK_SHOW_HIDDENPRINTS, "Show Hiddenprint log")
|
|
VV_DROPDOWN_OPTION(VV_HK_ADD_REAGENT, "Add Reagent")
|
|
VV_DROPDOWN_OPTION(VV_HK_TRIGGER_EMP, "EMP Pulse")
|
|
VV_DROPDOWN_OPTION(VV_HK_TRIGGER_EXPLOSION, "Explosion")
|
|
VV_DROPDOWN_OPTION(VV_HK_EDIT_FILTERS, "Edit Filters")
|
|
VV_DROPDOWN_OPTION(VV_HK_EDIT_COLOR_MATRIX, "Edit Color as Matrix")
|
|
VV_DROPDOWN_OPTION(VV_HK_ADD_AI, "Add AI controller")
|
|
if(greyscale_colors)
|
|
VV_DROPDOWN_OPTION(VV_HK_MODIFY_GREYSCALE, "Modify greyscale colors")
|
|
|
|
/atom/vv_do_topic(list/href_list)
|
|
. = ..()
|
|
if(href_list[VV_HK_ADD_REAGENT] && check_rights(R_VAREDIT))
|
|
if(!reagents)
|
|
var/amount = input(usr, "Specify the reagent size of [src]", "Set Reagent Size", 50) as num|null
|
|
if(amount)
|
|
create_reagents(amount)
|
|
|
|
if(reagents)
|
|
var/chosen_id
|
|
switch(tgui_alert(usr, "Choose a method.", "Add Reagents", list("Search", "Choose from a list", "I'm feeling lucky")))
|
|
if("Search")
|
|
var/valid_id
|
|
while(!valid_id)
|
|
chosen_id = input(usr, "Enter the ID of the reagent you want to add.", "Search reagents") as null|text
|
|
if(isnull(chosen_id)) //Get me out of here!
|
|
break
|
|
if (!ispath(text2path(chosen_id)))
|
|
chosen_id = pick_closest_path(chosen_id, make_types_fancy(subtypesof(/datum/reagent)))
|
|
if (ispath(chosen_id))
|
|
valid_id = TRUE
|
|
else
|
|
valid_id = TRUE
|
|
if(!valid_id)
|
|
to_chat(usr, span_warning("A reagent with that ID doesn't exist!"))
|
|
if("Choose from a list")
|
|
chosen_id = input(usr, "Choose a reagent to add.", "Choose a reagent.") as null|anything in sort_list(subtypesof(/datum/reagent), /proc/cmp_typepaths_asc)
|
|
if("I'm feeling lucky")
|
|
chosen_id = pick(subtypesof(/datum/reagent))
|
|
if(chosen_id)
|
|
var/amount = input(usr, "Choose the amount to add.", "Choose the amount.", reagents.maximum_volume) as num|null
|
|
if(amount)
|
|
reagents.add_reagent(chosen_id, amount)
|
|
log_admin("[key_name(usr)] has added [amount] units of [chosen_id] to [src]")
|
|
message_admins(span_notice("[key_name(usr)] has added [amount] units of [chosen_id] to [src]"))
|
|
|
|
if(href_list[VV_HK_TRIGGER_EXPLOSION] && check_rights(R_FUN))
|
|
usr.client.cmd_admin_explosion(src)
|
|
|
|
if(href_list[VV_HK_TRIGGER_EMP] && check_rights(R_FUN))
|
|
usr.client.cmd_admin_emp(src)
|
|
|
|
if(href_list[VV_HK_SHOW_HIDDENPRINTS] && check_rights(R_ADMIN))
|
|
usr.client.cmd_show_hiddenprints(src)
|
|
|
|
|
|
if(href_list[VV_HK_ADD_AI])
|
|
if(!check_rights(R_VAREDIT))
|
|
return
|
|
var/result = input(usr, "Choose the AI controller to apply to this atom WARNING: Not all AI works on all atoms.", "AI controller") as null|anything in subtypesof(/datum/ai_controller)
|
|
if(!result)
|
|
return
|
|
ai_controller = new result(src)
|
|
|
|
if(href_list[VV_HK_MODIFY_TRANSFORM] && check_rights(R_VAREDIT))
|
|
var/result = input(usr, "Choose the transformation to apply","Transform Mod") as null|anything in list("Scale","Translate","Rotate")
|
|
var/matrix/M = transform
|
|
if(!result)
|
|
return
|
|
switch(result)
|
|
if("Scale")
|
|
var/x = input(usr, "Choose x mod","Transform Mod") as null|num
|
|
var/y = input(usr, "Choose y mod","Transform Mod") as null|num
|
|
if(isnull(x) || isnull(y))
|
|
return
|
|
transform = M.Scale(x,y)
|
|
if("Translate")
|
|
var/x = input(usr, "Choose x mod (negative = left, positive = right)","Transform Mod") as null|num
|
|
var/y = input(usr, "Choose y mod (negative = down, positive = up)","Transform Mod") as null|num
|
|
if(isnull(x) || isnull(y))
|
|
return
|
|
transform = M.Translate(x,y)
|
|
if("Rotate")
|
|
var/angle = input(usr, "Choose angle to rotate","Transform Mod") as null|num
|
|
if(isnull(angle))
|
|
return
|
|
transform = M.Turn(angle)
|
|
|
|
SEND_SIGNAL(src, COMSIG_ATOM_VV_MODIFY_TRANSFORM)
|
|
|
|
if(href_list[VV_HK_AUTO_RENAME] && check_rights(R_VAREDIT))
|
|
var/newname = input(usr, "What do you want to rename this to?", "Automatic Rename") as null|text
|
|
// Check the new name against the chat filter. If it triggers the IC chat filter, give an option to confirm.
|
|
if(newname && !(is_ic_filtered(newname) || is_soft_ic_filtered(newname) && tgui_alert(usr, "Your selected name contains words restricted by IC chat filters. Confirm this new name?", "IC Chat Filter Conflict", list("Confirm", "Cancel")) != "Confirm"))
|
|
vv_auto_rename(newname)
|
|
|
|
if(href_list[VV_HK_EDIT_FILTERS] && check_rights(R_VAREDIT))
|
|
var/client/C = usr.client
|
|
C?.open_filter_editor(src)
|
|
|
|
if(href_list[VV_HK_EDIT_COLOR_MATRIX] && check_rights(R_VAREDIT))
|
|
var/client/C = usr.client
|
|
C?.open_color_matrix_editor(src)
|
|
|
|
/atom/vv_get_header()
|
|
. = ..()
|
|
var/refid = REF(src)
|
|
. += "[VV_HREF_TARGETREF(refid, VV_HK_AUTO_RENAME, "<b id='name'>[src]</b>")]"
|
|
. += "<br><font size='1'><a href='?_src_=vars;[HrefToken()];rotatedatum=[refid];rotatedir=left'><<</a> <a href='?_src_=vars;[HrefToken()];datumedit=[refid];varnameedit=dir' id='dir'>[dir2text(dir) || dir]</a> <a href='?_src_=vars;[HrefToken()];rotatedatum=[refid];rotatedir=right'>>></a></font>"
|
|
|
|
///Where atoms should drop if taken from this atom
|
|
/atom/proc/drop_location()
|
|
var/atom/location = loc
|
|
if(!location)
|
|
return null
|
|
return location.AllowDrop() ? location : location.drop_location()
|
|
|
|
/atom/proc/vv_auto_rename(newname)
|
|
name = newname
|
|
|
|
/**
|
|
* An atom has entered this atom's contents
|
|
*
|
|
* Default behaviour is to send the [COMSIG_ATOM_ENTERED]
|
|
*/
|
|
/atom/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs)
|
|
SEND_SIGNAL(src, COMSIG_ATOM_ENTERED, arrived, old_loc, old_locs)
|
|
SEND_SIGNAL(arrived, COMSIG_ATOM_ENTERING, src, old_loc, old_locs)
|
|
|
|
/**
|
|
* An atom is attempting to exit this atom's contents
|
|
*
|
|
* Default behaviour is to send the [COMSIG_ATOM_EXIT]
|
|
*/
|
|
/atom/Exit(atom/movable/leaving, direction)
|
|
// Don't call `..()` here, otherwise `Uncross()` gets called.
|
|
// See the doc comment on `Uncross()` to learn why this is bad.
|
|
|
|
if(SEND_SIGNAL(src, COMSIG_ATOM_EXIT, leaving, direction) & COMPONENT_ATOM_BLOCK_EXIT)
|
|
return FALSE
|
|
|
|
return TRUE
|
|
|
|
/**
|
|
* An atom has exited this atom's contents
|
|
*
|
|
* Default behaviour is to send the [COMSIG_ATOM_EXITED]
|
|
*/
|
|
/atom/Exited(atom/movable/gone, direction)
|
|
SEND_SIGNAL(src, COMSIG_ATOM_EXITED, gone, direction)
|
|
|
|
///Return atom temperature
|
|
/atom/proc/return_temperature()
|
|
return
|
|
|
|
/**
|
|
*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.
|
|
*
|
|
* Must return parent proc ..() in the end if overridden
|
|
*/
|
|
/atom/proc/tool_act(mob/living/user, obj/item/tool, tool_type, is_right_clicking)
|
|
var/act_result
|
|
var/signal_result
|
|
if(!is_right_clicking) // Left click first for sensibility
|
|
var/list/processing_recipes = list() //List of recipes that can be mutated by sending the signal
|
|
signal_result = SEND_SIGNAL(src, COMSIG_ATOM_TOOL_ACT(tool_type), user, tool, processing_recipes)
|
|
if(signal_result & COMPONENT_BLOCK_TOOL_ATTACK) // The COMSIG_ATOM_TOOL_ACT signal is blocking the act
|
|
return TOOL_ACT_SIGNAL_BLOCKING
|
|
if(processing_recipes.len)
|
|
process_recipes(user, tool, processing_recipes)
|
|
if(QDELETED(tool))
|
|
return TRUE
|
|
switch(tool_type)
|
|
if(TOOL_CROWBAR)
|
|
act_result = crowbar_act(user, tool)
|
|
if(TOOL_MULTITOOL)
|
|
act_result = multitool_act(user, tool)
|
|
if(TOOL_SCREWDRIVER)
|
|
act_result = screwdriver_act(user, tool)
|
|
if(TOOL_WRENCH)
|
|
act_result = wrench_act(user, tool)
|
|
if(TOOL_WIRECUTTER)
|
|
act_result = wirecutter_act(user, tool)
|
|
if(TOOL_WELDER)
|
|
act_result = welder_act(user, tool)
|
|
if(TOOL_ANALYZER)
|
|
act_result = analyzer_act(user, tool)
|
|
else
|
|
signal_result = SEND_SIGNAL(src, COMSIG_ATOM_SECONDARY_TOOL_ACT(tool_type), user, tool)
|
|
if(signal_result & COMPONENT_BLOCK_TOOL_ATTACK) // The COMSIG_ATOM_TOOL_ACT signal is blocking the act
|
|
return TOOL_ACT_SIGNAL_BLOCKING
|
|
switch(tool_type)
|
|
if(TOOL_CROWBAR)
|
|
act_result = crowbar_act_secondary(user, tool)
|
|
if(TOOL_MULTITOOL)
|
|
act_result = multitool_act_secondary(user, tool)
|
|
if(TOOL_SCREWDRIVER)
|
|
act_result = screwdriver_act_secondary(user, tool)
|
|
if(TOOL_WRENCH)
|
|
act_result = wrench_act_secondary(user, tool)
|
|
if(TOOL_WIRECUTTER)
|
|
act_result = wirecutter_act_secondary(user, tool)
|
|
if(TOOL_WELDER)
|
|
act_result = welder_act_secondary(user, tool)
|
|
if(TOOL_ANALYZER)
|
|
act_result = analyzer_act_secondary(user, tool)
|
|
if(act_result) // A tooltype_act has completed successfully
|
|
return TOOL_ACT_TOOLTYPE_SUCCESS
|
|
|
|
|
|
/atom/proc/process_recipes(mob/living/user, obj/item/processed_object, list/processing_recipes)
|
|
//Only one recipe? use the first
|
|
if(processing_recipes.len == 1)
|
|
StartProcessingAtom(user, processed_object, processing_recipes[1])
|
|
return
|
|
//Otherwise, select one with a radial
|
|
ShowProcessingGui(user, processed_object, processing_recipes)
|
|
|
|
///Creates the radial and processes the selected option
|
|
/atom/proc/ShowProcessingGui(mob/living/user, obj/item/processed_object, list/possible_options)
|
|
var/list/choices_to_options = list() //Dict of object name | dict of object processing settings
|
|
var/list/choices = list()
|
|
|
|
for(var/list/current_option as anything in possible_options)
|
|
var/atom/current_option_type = current_option[TOOL_PROCESSING_RESULT]
|
|
choices_to_options[initial(current_option_type.name)] = current_option
|
|
var/image/option_image = image(icon = initial(current_option_type.icon), icon_state = initial(current_option_type.icon_state))
|
|
choices += list("[initial(current_option_type.name)]" = option_image)
|
|
|
|
var/pick = show_radial_menu(user, src, choices, radius = 36, require_near = TRUE)
|
|
|
|
StartProcessingAtom(user, processed_object, choices_to_options[pick])
|
|
|
|
|
|
/atom/proc/StartProcessingAtom(mob/living/user, obj/item/process_item, list/chosen_option)
|
|
to_chat(user, span_notice("You start working on [src]."))
|
|
if(process_item.use_tool(src, user, chosen_option[TOOL_PROCESSING_TIME], volume=50))
|
|
var/atom/atom_to_create = chosen_option[TOOL_PROCESSING_RESULT]
|
|
var/list/atom/created_atoms = list()
|
|
for(var/i = 1 to chosen_option[TOOL_PROCESSING_AMOUNT])
|
|
var/atom/created_atom = new atom_to_create(drop_location())
|
|
if(custom_materials)
|
|
created_atom.set_custom_materials(custom_materials, 1 / chosen_option[TOOL_PROCESSING_AMOUNT])
|
|
created_atom.pixel_x = pixel_x
|
|
created_atom.pixel_y = pixel_y
|
|
if(i > 1)
|
|
created_atom.pixel_x += rand(-8,8)
|
|
created_atom.pixel_y += rand(-8,8)
|
|
SEND_SIGNAL(created_atom, COMSIG_ATOM_CREATEDBY_PROCESSING, src, chosen_option)
|
|
created_atom.OnCreatedFromProcessing(user, process_item, chosen_option, src)
|
|
to_chat(user, span_notice("You manage to create [chosen_option[TOOL_PROCESSING_AMOUNT]] [initial(atom_to_create.name)]\s from [src]."))
|
|
created_atoms.Add(created_atom)
|
|
SEND_SIGNAL(src, COMSIG_ATOM_PROCESSED, user, process_item, created_atoms)
|
|
UsedforProcessing(user, process_item, chosen_option)
|
|
return
|
|
|
|
/atom/proc/UsedforProcessing(mob/living/user, obj/item/used_item, list/chosen_option)
|
|
qdel(src)
|
|
return
|
|
|
|
/atom/proc/OnCreatedFromProcessing(mob/living/user, obj/item/food, list/chosen_option, atom/original_atom)
|
|
return
|
|
|
|
//! Tool-specific behavior procs.
|
|
///
|
|
|
|
/// Called on an object when a tool with crowbar capabilities is used to left click an object
|
|
/atom/proc/crowbar_act(mob/living/user, obj/item/tool)
|
|
return
|
|
|
|
/// Called on an object when a tool with crowbar capabilities is used to right click an object
|
|
/atom/proc/crowbar_act_secondary(mob/living/user, obj/item/tool)
|
|
return
|
|
|
|
/// Called on an object when a tool with multitool capabilities is used to left click an object
|
|
/atom/proc/multitool_act(mob/living/user, obj/item/tool)
|
|
return
|
|
|
|
/// Called on an object when a tool with multitool capabilities is used to right click an object
|
|
/atom/proc/multitool_act_secondary(mob/living/user, obj/item/tool)
|
|
return
|
|
|
|
///Check if the multitool has an item in it's data buffer
|
|
/atom/proc/multitool_check_buffer(user, obj/item/multitool, silent = FALSE)
|
|
if(!istype(multitool, /obj/item/multitool))
|
|
if(user && !silent)
|
|
to_chat(user, span_warning("[multitool] has no data buffer!"))
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/// Called on an object when a tool with screwdriver capabilities is used to left click an object
|
|
/atom/proc/screwdriver_act(mob/living/user, obj/item/tool)
|
|
return
|
|
|
|
/// Called on an object when a tool with screwdriver capabilities is used to right click an object
|
|
/atom/proc/screwdriver_act_secondary(mob/living/user, obj/item/tool)
|
|
return
|
|
|
|
/// Called on an object when a tool with wrench capabilities is used to left click an object
|
|
/atom/proc/wrench_act(mob/living/user, obj/item/tool)
|
|
return
|
|
|
|
/// Called on an object when a tool with wrench capabilities is used to right click an object
|
|
/atom/proc/wrench_act_secondary(mob/living/user, obj/item/tool)
|
|
return
|
|
|
|
/// Called on an object when a tool with wirecutter capabilities is used to left click an object
|
|
/atom/proc/wirecutter_act(mob/living/user, obj/item/tool)
|
|
return
|
|
|
|
/// Called on an object when a tool with wirecutter capabilities is used to right click an object
|
|
/atom/proc/wirecutter_act_secondary(mob/living/user, obj/item/tool)
|
|
return
|
|
|
|
/// Called on an object when a tool with welder capabilities is used to left click an object
|
|
/atom/proc/welder_act(mob/living/user, obj/item/tool)
|
|
return
|
|
|
|
/// Called on an object when a tool with welder capabilities is used to right click an object
|
|
/atom/proc/welder_act_secondary(mob/living/user, obj/item/tool)
|
|
return
|
|
|
|
/// Called on an object when a tool with analyzer capabilities is used to left click an object
|
|
/atom/proc/analyzer_act(mob/living/user, obj/item/tool)
|
|
return
|
|
|
|
/// Called on an object when a tool with analyzer capabilities is used to right click an object
|
|
/atom/proc/analyzer_act_secondary(mob/living/user, obj/item/tool)
|
|
return
|
|
|
|
///Generate a tag for this atom
|
|
/atom/proc/GenerateTag()
|
|
return
|
|
|
|
///Connect this atom to a shuttle
|
|
/atom/proc/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock)
|
|
return
|
|
|
|
/**
|
|
* Generic logging helper
|
|
*
|
|
* reads the type of the log
|
|
* and writes it to the respective log file
|
|
* unless log_globally is FALSE
|
|
* Arguments:
|
|
* * message - The message being logged
|
|
* * message_type - the type of log the message is(ATTACK, SAY, etc)
|
|
* * color - color of the log text
|
|
* * log_globally - boolean checking whether or not we write this log to the log file
|
|
*/
|
|
/atom/proc/log_message(message, message_type, color=null, log_globally=TRUE)
|
|
if(!log_globally)
|
|
return
|
|
//SKYRAT EDIT ADDITION BEGIN
|
|
#ifndef SPACEMAN_DMM
|
|
if(CONFIG_GET(flag/sql_game_log) && CONFIG_GET(flag/sql_enabled))
|
|
var/datum/db_query/query_sql_log_messages = SSdbcore.NewQuery({"
|
|
INSERT INTO [format_table_name("game_log")] (datetime, round_id, ckey, loc, type, message)
|
|
VALUES (:time, :round_id, :ckey, :loc, :type, :message)
|
|
"}, list("time" = SQLtime(), "round_id" = "[GLOB.round_id]", "ckey" = key_name(src), "loc" = loc_name(src), type = message_type, "message" = message))
|
|
if(!query_sql_log_messages.warn_execute())
|
|
qdel(query_sql_log_messages)
|
|
return
|
|
qdel(query_sql_log_messages)
|
|
if(!CONFIG_GET(flag/file_game_log))
|
|
return
|
|
#endif
|
|
//SKYRAT EDIT ADDITION END
|
|
var/log_text = "[key_name(src)] [message] [loc_name(src)]"
|
|
switch(message_type)
|
|
if(LOG_ATTACK)
|
|
log_attack(log_text)
|
|
if(LOG_SAY)
|
|
log_say(log_text)
|
|
if(LOG_WHISPER)
|
|
log_whisper(log_text)
|
|
if(LOG_EMOTE)
|
|
log_emote(log_text)
|
|
//SKYRAT EDIT ADDITION BEGIN
|
|
if(LOG_SUBTLER)
|
|
log_subtler(log_text)
|
|
if(LOG_AMBITION)
|
|
log_ambition(log_text)
|
|
//SKYRAT EDIT ADDITION END
|
|
if(LOG_DSAY)
|
|
log_dsay(log_text)
|
|
if(LOG_PDA)
|
|
log_pda(log_text)
|
|
if(LOG_CHAT)
|
|
log_chat(log_text)
|
|
if(LOG_COMMENT)
|
|
log_comment(log_text)
|
|
if(LOG_TELECOMMS)
|
|
log_telecomms(log_text)
|
|
if(LOG_ECON)
|
|
log_econ(log_text)
|
|
if(LOG_OOC)
|
|
log_ooc(log_text)
|
|
if(LOG_ADMIN)
|
|
log_admin(log_text)
|
|
if(LOG_ADMIN_PRIVATE)
|
|
log_admin_private(log_text)
|
|
if(LOG_ASAY)
|
|
log_adminsay(log_text)
|
|
if(LOG_OWNERSHIP)
|
|
log_game(log_text)
|
|
if(LOG_GAME)
|
|
log_game(log_text)
|
|
if(LOG_MECHA)
|
|
log_mecha(log_text)
|
|
if(LOG_SHUTTLE)
|
|
log_shuttle(log_text)
|
|
else
|
|
stack_trace("Invalid individual logging type: [message_type]. Defaulting to [LOG_GAME] (LOG_GAME).")
|
|
log_game(log_text)
|
|
|
|
/**
|
|
* Helper for logging chat messages or other logs with arbitrary inputs(e.g. announcements)
|
|
*
|
|
* This proc compiles a log string by prefixing the tag to the message
|
|
* and suffixing what it was forced_by if anything
|
|
* if the message lacks a tag and suffix then it is logged on its own
|
|
* Arguments:
|
|
* * message - The message being logged
|
|
* * message_type - the type of log the message is(ATTACK, SAY, etc)
|
|
* * tag - tag that indicates the type of text(announcement, telepathy, etc)
|
|
* * log_globally - boolean checking whether or not we write this log to the log file
|
|
* * forced_by - source that forced the dialogue if any
|
|
*/
|
|
/atom/proc/log_talk(message, message_type, tag=null, log_globally=TRUE, forced_by=null)
|
|
var/prefix = tag ? "([tag]) " : ""
|
|
var/suffix = forced_by ? " FORCED by [forced_by]" : ""
|
|
log_message("[prefix]\"[message]\"[suffix]", message_type, log_globally=log_globally)
|
|
|
|
/// Helper for logging of messages with only one sender and receiver
|
|
/proc/log_directed_talk(atom/source, atom/target, message, message_type, tag)
|
|
if(!tag)
|
|
stack_trace("Unspecified tag for private message")
|
|
tag = "UNKNOWN"
|
|
|
|
source.log_talk(message, message_type, tag="[tag] to [key_name(target)]")
|
|
if(source != target)
|
|
target.log_talk(message, LOG_VICTIM, tag="[tag] from [key_name(source)]", log_globally=FALSE)
|
|
|
|
/**
|
|
* Log a combat message in the attack log
|
|
*
|
|
* Arguments:
|
|
* * atom/user - argument is the actor performing the action
|
|
* * atom/target - argument is the target of the action
|
|
* * what_done - is a verb describing the action (e.g. punched, throwed, kicked, etc.)
|
|
* * atom/object - is a tool with which the action was made (usually an item)
|
|
* * addition - is any additional text, which will be appended to the rest of the log line
|
|
*/
|
|
/proc/log_combat(atom/user, atom/target, what_done, atom/object=null, addition=null)
|
|
var/ssource = key_name(user)
|
|
var/starget = key_name(target)
|
|
|
|
var/mob/living/living_target = target
|
|
var/hp = istype(living_target) ? " (NEWHP: [living_target.health]) " : ""
|
|
|
|
var/sobject = ""
|
|
if(object)
|
|
sobject = " with [object]"
|
|
var/saddition = ""
|
|
if(addition)
|
|
saddition = " [addition]"
|
|
|
|
var/postfix = "[sobject][saddition][hp]"
|
|
|
|
var/message = "has [what_done] [starget][postfix]"
|
|
user.log_message(message, LOG_ATTACK, color="red")
|
|
|
|
if(user != target)
|
|
var/reverse_message = "has been [what_done] by [ssource][postfix]"
|
|
target.log_message(reverse_message, LOG_VICTIM, color="orange", log_globally=FALSE)
|
|
|
|
/**
|
|
* log_wound() is for when someone is *attacked* and suffers a wound. Note that this only captures wounds from damage, so smites/forced wounds aren't logged, as well as demotions like cuts scabbing over
|
|
*
|
|
* Note that this has no info on the attack that dealt the wound: information about where damage came from isn't passed to the bodypart's damaged proc. When in doubt, check the attack log for attacks at that same time
|
|
* TODO later: Add logging for healed wounds, though that will require some rewriting of healing code to prevent admin heals from spamming the logs. Not high priority
|
|
*
|
|
* Arguments:
|
|
* * victim- The guy who got wounded
|
|
* * suffered_wound- The wound, already applied, that we're logging. It has to already be attached so we can get the limb from it
|
|
* * dealt_damage- How much damage is associated with the attack that dealt with this wound.
|
|
* * dealt_wound_bonus- The wound_bonus, if one was specified, of the wounding attack
|
|
* * dealt_bare_wound_bonus- The bare_wound_bonus, if one was specified *and applied*, of the wounding attack. Not shown if armor was present
|
|
* * base_roll- Base wounding ability of an attack is a random number from 1 to (dealt_damage ** WOUND_DAMAGE_EXPONENT). This is the number that was rolled in there, before mods
|
|
*/
|
|
/proc/log_wound(atom/victim, datum/wound/suffered_wound, dealt_damage, dealt_wound_bonus, dealt_bare_wound_bonus, base_roll)
|
|
if(QDELETED(victim) || !suffered_wound)
|
|
return
|
|
var/message = "has suffered: [suffered_wound][suffered_wound.limb ? " to [suffered_wound.limb.name]" : null]"// maybe indicate if it's a promote/demote?
|
|
|
|
if(dealt_damage)
|
|
message += " | Damage: [dealt_damage]"
|
|
// The base roll is useful since it can show how lucky someone got with the given attack. For example, dealing a cut
|
|
if(base_roll)
|
|
message += " (rolled [base_roll]/[dealt_damage ** WOUND_DAMAGE_EXPONENT])"
|
|
|
|
if(dealt_wound_bonus)
|
|
message += " | WB: [dealt_wound_bonus]"
|
|
|
|
if(dealt_bare_wound_bonus)
|
|
message += " | BWB: [dealt_bare_wound_bonus]"
|
|
|
|
victim.log_message(message, LOG_ATTACK, color="blue")
|
|
|
|
/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
|
|
|
|
/atom/proc/intercept_zImpact(atom/movable/hitting_atom, levels = 1)
|
|
. |= SEND_SIGNAL(src, COMSIG_ATOM_INTERCEPT_Z_FALL, hitting_atom, levels)
|
|
|
|
/// Sets the custom materials for an item.
|
|
/atom/proc/set_custom_materials(list/materials, multiplier = 1)
|
|
if(custom_materials && material_flags & MATERIAL_EFFECTS) //Only runs if custom materials existed at first and affected src.
|
|
for(var/current_material in custom_materials)
|
|
var/datum/material/custom_material = GET_MATERIAL_REF(current_material)
|
|
custom_material.on_removed(src, custom_materials[current_material] * material_modifier, material_flags) //Remove the current materials
|
|
|
|
if(!length(materials))
|
|
custom_materials = null
|
|
return
|
|
|
|
if(material_flags & MATERIAL_EFFECTS)
|
|
for(var/current_material in materials)
|
|
var/datum/material/custom_material = GET_MATERIAL_REF(current_material)
|
|
custom_material.on_applied(src, materials[current_material] * multiplier * material_modifier, material_flags)
|
|
|
|
custom_materials = SSmaterials.FindOrCreateMaterialCombo(materials, multiplier)
|
|
|
|
/**
|
|
* Returns the material composition of the atom.
|
|
*
|
|
* Used when recycling items, specifically to turn alloys back into their component mats.
|
|
*
|
|
* Exists because I'd need to add a way to un-alloy alloys or otherwise deal
|
|
* with people converting the entire stations material supply into alloys.
|
|
*
|
|
* Arguments:
|
|
* - flags: A set of flags determining how exactly the materials are broken down.
|
|
*/
|
|
/atom/proc/get_material_composition(breakdown_flags=NONE)
|
|
. = list()
|
|
if(!(breakdown_flags & BREAKDOWN_INCLUDE_ALCHEMY) && HAS_TRAIT(src, TRAIT_MAT_TRANSMUTED))
|
|
return
|
|
|
|
var/list/cached_materials = custom_materials
|
|
for(var/mat in cached_materials)
|
|
var/datum/material/material = GET_MATERIAL_REF(mat)
|
|
var/list/material_comp = material.return_composition(cached_materials[mat], breakdown_flags)
|
|
for(var/comp_mat in material_comp)
|
|
.[comp_mat] += material_comp[comp_mat]
|
|
|
|
/**
|
|
* Fetches a list of all of the materials this object has of the desired type. Returns null if there is no valid materials of the type
|
|
*
|
|
* Arguments:
|
|
* - [mat_type][/datum/material]: The type of material we are checking for
|
|
* - exact: Whether to search for the _exact_ material type
|
|
* - mat_amount: The minimum required amount of material
|
|
*/
|
|
/atom/proc/has_material_type(datum/material/mat_type, exact=FALSE, mat_amount=0)
|
|
var/list/cached_materials = custom_materials
|
|
if(!length(cached_materials))
|
|
return null
|
|
|
|
var/materials_of_type
|
|
for(var/current_material in cached_materials)
|
|
if(cached_materials[current_material] < mat_amount)
|
|
continue
|
|
var/datum/material/material = GET_MATERIAL_REF(current_material)
|
|
if(exact ? material.type != current_material : !istype(material, mat_type))
|
|
continue
|
|
LAZYSET(materials_of_type, material, cached_materials[current_material])
|
|
|
|
return materials_of_type
|
|
|
|
/**
|
|
* Fetches a list of all of the materials this object has with the desired material category.
|
|
*
|
|
* Arguments:
|
|
* - category: The category to check for
|
|
* - any_flags: Any bitflags that must be present for the category
|
|
* - all_flags: All bitflags that must be present for the category
|
|
* - no_flags: Any bitflags that must not be present for the category
|
|
* - mat_amount: The minimum amount of materials that must be present
|
|
*/
|
|
/atom/proc/has_material_category(category, any_flags=0, all_flags=0, no_flags=0, mat_amount=0)
|
|
var/list/cached_materials = custom_materials
|
|
if(!length(cached_materials))
|
|
return null
|
|
|
|
var/materials_of_category
|
|
for(var/current_material in cached_materials)
|
|
if(cached_materials[current_material] < mat_amount)
|
|
continue
|
|
var/datum/material/material = GET_MATERIAL_REF(current_material)
|
|
var/category_flags = material?.categories[category]
|
|
if(isnull(category_flags))
|
|
continue
|
|
if(any_flags && !(category_flags & any_flags))
|
|
continue
|
|
if(all_flags && (all_flags != (category_flags & all_flags)))
|
|
continue
|
|
if(no_flags && (category_flags & no_flags))
|
|
continue
|
|
LAZYSET(materials_of_category, material, cached_materials[current_material])
|
|
return materials_of_category
|
|
|
|
/**
|
|
* Gets the most common material in the object.
|
|
*/
|
|
/atom/proc/get_master_material()
|
|
var/list/cached_materials = custom_materials
|
|
if(!length(cached_materials))
|
|
return null
|
|
|
|
var/most_common_material = null
|
|
var/max_amount = 0
|
|
for(var/material in cached_materials)
|
|
if(cached_materials[material] > max_amount)
|
|
most_common_material = material
|
|
max_amount = cached_materials[material]
|
|
|
|
if(most_common_material)
|
|
return GET_MATERIAL_REF(most_common_material)
|
|
|
|
/**
|
|
* Gets the total amount of materials in this atom.
|
|
*/
|
|
/atom/proc/get_custom_material_amount()
|
|
return isnull(custom_materials) ? 0 : counterlist_sum(custom_materials)
|
|
|
|
|
|
///Setter for the `density` variable to append behavior related to its changing.
|
|
/atom/proc/set_density(new_value)
|
|
SHOULD_CALL_PARENT(TRUE)
|
|
if(density == new_value)
|
|
return
|
|
. = density
|
|
density = new_value
|
|
|
|
|
|
///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/gravity_turf)
|
|
if(!gravity_turf || !isturf(gravity_turf))
|
|
gravity_turf = get_turf(src)
|
|
|
|
if(!gravity_turf)
|
|
return 0
|
|
|
|
var/list/forced_gravity = list()
|
|
SEND_SIGNAL(src, COMSIG_ATOM_HAS_GRAVITY, gravity_turf, forced_gravity)
|
|
if(!forced_gravity.len)
|
|
SEND_SIGNAL(gravity_turf, 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(gravity_turf)) // Turf never has gravity
|
|
return 0
|
|
if(istype(gravity_turf, /turf/open/openspace)) //openspace in a space area doesn't get gravity
|
|
if(istype(get_area(gravity_turf), /area/space))
|
|
return 0
|
|
|
|
var/area/turf_area = get_area(gravity_turf)
|
|
if(turf_area.has_gravity) // Areas which always has gravity
|
|
return turf_area.has_gravity
|
|
else
|
|
// There's a gravity generator on our z level
|
|
if(GLOB.gravity_generators["[gravity_turf.z]"])
|
|
var/max_grav = 0
|
|
for(var/obj/machinery/gravity_generator/main/main_grav_gen as anything in GLOB.gravity_generators["[gravity_turf.z]"])
|
|
max_grav = max(main_grav_gen.setting,max_grav)
|
|
return max_grav
|
|
return SSmapping.level_trait(gravity_turf.z, ZTRAIT_GRAVITY)
|
|
|
|
/**
|
|
* Causes effects when the atom gets hit by a rust effect from heretics
|
|
*
|
|
* Override this if you want custom behaviour in whatever gets hit by the rust
|
|
*/
|
|
/atom/proc/rust_heretic_act()
|
|
AddElement(/datum/element/rust)
|
|
|
|
/**
|
|
* Used to set something as 'open' if it's being used as a supplypod
|
|
*
|
|
* Override this if you want an atom to be usable as a supplypod.
|
|
*/
|
|
/atom/proc/setOpened()
|
|
return
|
|
|
|
/**
|
|
* Used to set something as 'closed' if it's being used as a supplypod
|
|
*
|
|
* Override this if you want an atom to be usable as a supplypod.
|
|
*/
|
|
/atom/proc/setClosed()
|
|
return
|
|
|
|
|
|
///Called when something resists while this atom is its loc
|
|
/atom/proc/container_resist_act(mob/living/user)
|
|
|
|
/**
|
|
* Used to attempt to charge an object with a payment component.
|
|
*
|
|
* Use this if an atom needs to attempt to charge another atom.
|
|
*/
|
|
/atom/proc/attempt_charge(atom/sender, atom/target, extra_fees = 0)
|
|
return SEND_SIGNAL(sender, COMSIG_OBJ_ATTEMPT_CHARGE, target, extra_fees)
|
|
|
|
///Passes Stat Browser Panel clicks to the game and calls client click on an atom
|
|
/atom/Topic(href, list/href_list)
|
|
. = ..()
|
|
if(!usr?.client)
|
|
return
|
|
var/client/usr_client = usr.client
|
|
var/list/paramslist = list()
|
|
|
|
if(href_list["statpanel_item_click"])
|
|
switch(href_list["statpanel_item_click"])
|
|
if("left")
|
|
paramslist[LEFT_CLICK] = "1"
|
|
if("right")
|
|
paramslist[RIGHT_CLICK] = "1"
|
|
if("middle")
|
|
paramslist[MIDDLE_CLICK] = "1"
|
|
else
|
|
return
|
|
|
|
if(href_list["statpanel_item_shiftclick"])
|
|
paramslist[SHIFT_CLICK] = "1"
|
|
if(href_list["statpanel_item_ctrlclick"])
|
|
paramslist[CTRL_CLICK] = "1"
|
|
if(href_list["statpanel_item_altclick"])
|
|
paramslist[ALT_CLICK] = "1"
|
|
|
|
var/mouseparams = list2params(paramslist)
|
|
usr_client.Click(src, loc, null, mouseparams)
|
|
return TRUE
|
|
|
|
/**
|
|
* Recursive getter method to return a list of all ghosts orbitting this atom
|
|
*
|
|
* This will work fine without manually passing arguments.
|
|
*/
|
|
/atom/proc/get_all_orbiters(list/processed, source = TRUE)
|
|
var/list/output = list()
|
|
if (!processed)
|
|
processed = list()
|
|
if (src in processed)
|
|
return output
|
|
if (!source)
|
|
output += src
|
|
processed += src
|
|
for (var/atom/atom_orbiter as anything in orbiters?.orbiter_list)
|
|
output += atom_orbiter.get_all_orbiters(processed, source = FALSE)
|
|
return output
|
|
|
|
/**
|
|
* Instantiates the AI controller of this atom. Override this if you want to assign variables first.
|
|
*
|
|
* This will work fine without manually passing arguments.
|
|
|
|
+*/
|
|
/atom/proc/InitializeAIController()
|
|
if(ispath(ai_controller))
|
|
ai_controller = new ai_controller(src)
|
|
|
|
/**
|
|
* Point at an atom
|
|
*
|
|
* Intended to enable and standardise the pointing animation for all atoms
|
|
*
|
|
* Not intended as a replacement for the mob verb
|
|
*/
|
|
/atom/movable/proc/point_at(atom/pointed_atom)
|
|
if(!isturf(loc))
|
|
return FALSE
|
|
|
|
var/turf/tile = get_turf(pointed_atom)
|
|
if (!tile)
|
|
return FALSE
|
|
|
|
var/turf/our_tile = get_turf(src)
|
|
var/obj/visual = new /obj/effect/temp_visual/point(our_tile, invisibility)
|
|
|
|
animate(visual, pixel_x = (tile.x - our_tile.x) * world.icon_size + pointed_atom.pixel_x, pixel_y = (tile.y - our_tile.y) * world.icon_size + pointed_atom.pixel_y, time = 1.7, easing = EASE_OUT)
|
|
|
|
return TRUE
|
|
|
|
//Update the screentip to reflect what we're hoverin over
|
|
/atom/MouseEntered(location, control, params)
|
|
. = ..()
|
|
// Statusbar
|
|
status_bar_set_text(usr, name)
|
|
// Screentips
|
|
if(usr?.hud_used)
|
|
if(!usr.hud_used.screentips_enabled || (flags_1 & NO_SCREENTIPS_1))
|
|
usr.hud_used.screentip_text.maptext = ""
|
|
else
|
|
usr.hud_used.screentip_text.maptext = MAPTEXT("<span style='text-align: center'><span style='font-size: 32px'><span style='color:[usr.hud_used.screentip_color]: 32px'>[name]</span>")
|
|
|
|
/// Gets a merger datum representing the connected blob of objects in the allowed_types argument
|
|
/atom/proc/GetMergeGroup(id, list/allowed_types)
|
|
RETURN_TYPE(/datum/merger)
|
|
var/datum/merger/candidate
|
|
if(mergers)
|
|
candidate = mergers[id]
|
|
if(!candidate)
|
|
new /datum/merger(id, allowed_types, src)
|
|
candidate = mergers[id]
|
|
return candidate
|