You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1330 lines
44 KiB

  1. /**
  2. * The base type for nearly all physical objects in SS13
  3. * Lots and lots of functionality lives here, although in general we are striving to move
  4. * as much as possible to the components/elements system
  5. */
  6. /atom
  7. layer = TURF_LAYER
  8. plane = GAME_PLANE
  9. ///If non-null, overrides a/an/some in all cases
  10. var/article
  11. ///First atom flags var
  12. var/flags_1 = NONE
  13. ///Intearaction flags
  14. var/interaction_flags_atom = NONE
  15. var/flags_ricochet = NONE
  16. ///When a projectile tries to ricochet off this atom, the projectile ricochet chance is multiplied by this
  17. var/ricochet_chance_mod = 1
  18. ///When a projectile ricochets off this atom, it deals the normal damage * this modifier to this atom
  19. var/ricochet_damage_mod = 0.33
  20. ///Reagents holder
  21. var/datum/reagents/reagents = null
  22. ///This atom's HUD (med/sec, etc) images. Associative list.
  23. var/list/image/hud_list = null
  24. ///HUD images that this atom can provide.
  25. var/list/hud_possible
  26. ///Value used to increment ex_act() if reactionary_explosions is on
  27. var/explosion_block = 0
  28. /**
  29. * used to store the different colors on an atom
  30. *
  31. * its inherent color, the colored paint applied on it, special color effect etc...
  32. */
  33. var/list/atom_colours
  34. ///overlays that should remain on top and not normally removed when using cut_overlay functions, like c4.
  35. var/list/priority_overlays
  36. /// a very temporary list of overlays to remove
  37. var/list/remove_overlays
  38. /// a very temporary list of overlays to add
  39. var/list/add_overlays
  40. ///vis overlays managed by SSvis_overlays to automaticaly turn them like other overlays
  41. var/list/managed_vis_overlays
  42. ///overlays managed by [update_overlays][/atom/proc/update_overlays] to prevent removing overlays that weren't added by the same proc
  43. var/list/managed_overlays
  44. ///Proximity monitor associated with this atom
  45. var/datum/proximity_monitor/proximity_monitor
  46. ///Cooldown tick timer for buckle messages
  47. var/buckle_message_cooldown = 0
  48. ///Last fingerprints to touch this atom
  49. var/fingerprintslast
  50. var/list/filter_data //For handling persistent filters
  51. ///Economy cost of item
  52. var/custom_price
  53. ///Economy cost of item in premium vendor
  54. var/custom_premium_price
  55. ///Whether spessmen with an ID with an age below AGE_MINOR (20 by default) can buy this item
  56. var/age_restricted = FALSE
  57. //List of datums orbiting this atom
  58. var/datum/component/orbiter/orbiters
  59. /// Will move to flags_1 when i can be arsed to (2019, has not done so)
  60. var/rad_flags = NONE
  61. /// Radiation insulation types
  62. var/rad_insulation = RAD_NO_INSULATION
  63. ///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.)
  64. var/list/custom_materials
  65. ///Bitfield for how the atom handles materials.
  66. var/material_flags = NONE
  67. ///Modifier that raises/lowers the effect of the amount of a material, prevents small and easy to get items from being death machines.
  68. var/material_modifier = 1
  69. var/datum/wires/wires = null
  70. var/list/alternate_appearances
  71. ///Mobs that are currently do_after'ing this atom, to be cleared from on Destroy()
  72. var/list/targeted_by
  73. /// Last appearance of the atom for demo saving purposes
  74. var/image/demo_last_appearance
  75. /**
  76. * Called when an atom is created in byond (built in engine proc)
  77. *
  78. * Not a lot happens here in SS13 code, as we offload most of the work to the
  79. * [Intialization][/atom/proc/Initialize] proc, mostly we run the preloader
  80. * if the preloader is being used and then call [InitAtom][/datum/controller/subsystem/atoms/proc/InitAtom] of which the ultimate
  81. * result is that the Intialize proc is called.
  82. *
  83. * We also generate a tag here if the DF_USE_TAG flag is set on the atom
  84. */
  85. /atom/New(loc, ...)
  86. //atom creation method that preloads variables at creation
  87. if(GLOB.use_preloader && (src.type == GLOB._preloader.target_path))//in case the instanciated atom is creating other atoms in New()
  88. world.preloader_load(src)
  89. if(datum_flags & DF_USE_TAG)
  90. GenerateTag()
  91. var/do_initialize = SSatoms.initialized
  92. if(do_initialize != INITIALIZATION_INSSATOMS)
  93. args[1] = do_initialize == INITIALIZATION_INNEW_MAPLOAD
  94. if(SSatoms.InitAtom(src, args))
  95. //we were deleted
  96. return
  97. SSdemo.mark_new(src)
  98. /**
  99. * The primary method that objects are setup in SS13 with
  100. *
  101. * we don't use New as we have better control over when this is called and we can choose
  102. * to delay calls or hook other logic in and so forth
  103. *
  104. * During roundstart map parsing, atoms are queued for intialization in the base atom/New(),
  105. * After the map has loaded, then Initalize is called on all atoms one by one. NB: this
  106. * is also true for loading map templates as well, so they don't Initalize until all objects
  107. * in the map file are parsed and present in the world
  108. *
  109. * If you're creating an object at any point after SSInit has run then this proc will be
  110. * immediately be called from New.
  111. *
  112. * mapload: This parameter is true if the atom being loaded is either being intialized during
  113. * the Atom subsystem intialization, or if the atom is being loaded from the map template.
  114. * If the item is being created at runtime any time after the Atom subsystem is intialized then
  115. * it's false.
  116. *
  117. * You must always call the parent of this proc, otherwise failures will occur as the item
  118. * will not be seen as initalized (this can lead to all sorts of strange behaviour, like
  119. * the item being completely unclickable)
  120. *
  121. * You must not sleep in this proc, or any subprocs
  122. *
  123. * Any parameters from new are passed through (excluding loc), naturally if you're loading from a map
  124. * there are no other arguments
  125. *
  126. * Must return an [initialization hint][INITIALIZE_HINT_NORMAL] or a runtime will occur.
  127. *
  128. * Note: the following functions don't call the base for optimization and must copypasta handling:
  129. * * [/turf/Initialize]
  130. * * [/turf/open/space/Initialize]
  131. */
  132. /atom/proc/Initialize(mapload, ...)
  133. SHOULD_NOT_SLEEP(TRUE)
  134. SHOULD_CALL_PARENT(TRUE)
  135. if(flags_1 & INITIALIZED_1)
  136. stack_trace("Warning: [src]([type]) initialized multiple times!")
  137. flags_1 |= INITIALIZED_1
  138. if(loc)
  139. SEND_SIGNAL(loc, COMSIG_ATOM_CREATED, src) /// Sends a signal that the new atom `src`, has been created at `loc`
  140. //atom color stuff
  141. if(color)
  142. add_atom_colour(color, FIXED_COLOUR_PRIORITY)
  143. if (light_power && light_range)
  144. update_light()
  145. if (opacity && isturf(loc))
  146. var/turf/T = loc
  147. T.has_opaque_atom = TRUE // No need to recalculate it in this case, it's guaranteed to be on afterwards anyways.
  148. if (canSmoothWith)
  149. canSmoothWith = typelist("canSmoothWith", canSmoothWith)
  150. var/temp_list = list()
  151. for(var/i in custom_materials)
  152. temp_list[SSmaterials.GetMaterialRef(i)] = custom_materials[i] //Get the proper instanced version
  153. custom_materials = null //Null the list to prepare for applying the materials properly
  154. set_custom_materials(temp_list)
  155. ComponentInitialize()
  156. return INITIALIZE_HINT_NORMAL
  157. /**
  158. * Late Intialization, for code that should run after all atoms have run Intialization
  159. *
  160. * To have your LateIntialize proc be called, your atoms [Initalization][/atom/proc/Initialize]
  161. * proc must return the hint
  162. * [INITIALIZE_HINT_LATELOAD] otherwise you will never be called.
  163. *
  164. * useful for doing things like finding other machines on GLOB.machines because you can guarantee
  165. * that all atoms will actually exist in the "WORLD" at this time and that all their Intialization
  166. * code has been run
  167. */
  168. /atom/proc/LateInitialize()
  169. set waitfor = FALSE
  170. /// Put your [AddComponent] calls here
  171. /atom/proc/ComponentInitialize()
  172. return
  173. /**
  174. * Top level of the destroy chain for most atoms
  175. *
  176. * Cleans up the following:
  177. * * Removes alternate apperances from huds that see them
  178. * * qdels the reagent holder from atoms if it exists
  179. * * clears the orbiters list
  180. * * clears overlays and priority overlays
  181. * * clears the light object
  182. */
  183. /atom/Destroy()
  184. if(alternate_appearances)
  185. for(var/K in alternate_appearances)
  186. var/datum/atom_hud/alternate_appearance/AA = alternate_appearances[K]
  187. AA.remove_from_hud(src)
  188. if(reagents)
  189. qdel(reagents)
  190. orbiters = null // The component is attached to us normaly and will be deleted elsewhere
  191. LAZYCLEARLIST(overlays)
  192. LAZYCLEARLIST(priority_overlays)
  193. for(var/i in targeted_by)
  194. var/mob/M = i
  195. LAZYREMOVE(M.do_afters, src)
  196. targeted_by = null
  197. QDEL_NULL(light)
  198. return ..()
  199. /atom/proc/handle_ricochet(obj/projectile/P)
  200. var/turf/p_turf = get_turf(P)
  201. var/face_direction = get_dir(src, p_turf)
  202. var/face_angle = dir2angle(face_direction)
  203. var/incidence_s = GET_ANGLE_OF_INCIDENCE(face_angle, (P.Angle + 180))
  204. var/a_incidence_s = abs(incidence_s)
  205. if(a_incidence_s > 90 && a_incidence_s < 270)
  206. return FALSE
  207. if((P.flag in list("bullet", "bomb")) && P.ricochet_incidence_leeway)
  208. if((a_incidence_s < 90 && a_incidence_s < 90 - P.ricochet_incidence_leeway) || (a_incidence_s > 270 && a_incidence_s -270 > P.ricochet_incidence_leeway))
  209. return
  210. var/new_angle_s = SIMPLIFY_DEGREES(face_angle + incidence_s)
  211. P.setAngle(new_angle_s)
  212. return TRUE
  213. ///Can the mover object pass this atom, while heading for the target turf
  214. /atom/proc/CanPass(atom/movable/mover, turf/target)
  215. SHOULD_CALL_PARENT(TRUE)
  216. SHOULD_BE_PURE(TRUE)
  217. if(mover.movement_type & UNSTOPPABLE)
  218. return TRUE
  219. . = CanAllowThrough(mover, target)
  220. // This is cheaper than calling the proc every time since most things dont override CanPassThrough
  221. if(!mover.generic_canpass)
  222. return mover.CanPassThrough(src, target, .)
  223. /// Returns true or false to allow the mover to move through src
  224. /atom/proc/CanAllowThrough(atom/movable/mover, turf/target)
  225. SHOULD_CALL_PARENT(TRUE)
  226. SHOULD_BE_PURE(TRUE)
  227. return !density
  228. /**
  229. * Is this atom currently located on centcom
  230. *
  231. * Specifically, is it on the z level and within the centcom areas
  232. *
  233. * You can also be in a shuttleshuttle during endgame transit
  234. *
  235. * Used in gamemode to identify mobs who have escaped and for some other areas of the code
  236. * who don't want atoms where they shouldn't be
  237. */
  238. /atom/proc/onCentCom()
  239. var/turf/T = get_turf(src)
  240. if(!T)
  241. return FALSE
  242. if(is_reserved_level(T.z))
  243. for(var/A in SSshuttle.mobile)
  244. var/obj/docking_port/mobile/M = A
  245. if(M.launch_status == ENDGAME_TRANSIT)
  246. for(var/place in M.shuttle_areas)
  247. var/area/shuttle/shuttle_area = place
  248. if(T in shuttle_area)
  249. return TRUE
  250. if(!is_centcom_level(T.z))//if not, don't bother
  251. return FALSE
  252. //Check for centcom itself
  253. if(istype(T.loc, /area/centcom))
  254. return TRUE
  255. //Check for centcom shuttles
  256. for(var/A in SSshuttle.mobile)
  257. var/obj/docking_port/mobile/M = A
  258. if(M.launch_status == ENDGAME_LAUNCHED)
  259. for(var/place in M.shuttle_areas)
  260. var/area/shuttle/shuttle_area = place
  261. if(T in shuttle_area)
  262. return TRUE
  263. /**
  264. * Is the atom in any of the centcom syndicate areas
  265. *
  266. * Either in the syndie base on centcom, or any of their shuttles
  267. *
  268. * Also used in gamemode code for win conditions
  269. */
  270. /atom/proc/onSyndieBase()
  271. var/turf/T = get_turf(src)
  272. if(!T)
  273. return FALSE
  274. if(!is_centcom_level(T.z))//if not, don't bother
  275. return FALSE
  276. if(istype(T.loc, /area/shuttle/syndicate) || istype(T.loc, /area/syndicate_mothership) || istype(T.loc, /area/shuttle/assault_pod))
  277. return TRUE
  278. return FALSE
  279. /**
  280. * Is the atom in an away mission
  281. *
  282. * Must be in the away mission z-level to return TRUE
  283. *
  284. * Also used in gamemode code for win conditions
  285. */
  286. /atom/proc/onAwayMission()
  287. var/turf/T = get_turf(src)
  288. if(!T)
  289. return FALSE
  290. if(is_away_level(T.z))
  291. return TRUE
  292. return FALSE
  293. ///This atom has been hit by a hulkified mob in hulk mode (user)
  294. /atom/proc/attack_hulk(mob/living/carbon/human/user)
  295. SEND_SIGNAL(src, COMSIG_ATOM_HULK_ATTACK, user)
  296. /**
  297. * Ensure a list of atoms/reagents exists inside this atom
  298. *
  299. * Goes throught he list of passed in parts, if they're reagents, adds them to our reagent holder
  300. * creating the reagent holder if it exists.
  301. *
  302. * If the part is a moveable atom and the previous location of the item was a mob/living,
  303. * it calls the inventory handler transferItemToLoc for that mob/living and transfers the part
  304. * to this atom
  305. *
  306. * Otherwise it simply forceMoves the atom into this atom
  307. */
  308. /atom/proc/CheckParts(list/parts_list)
  309. for(var/A in parts_list)
  310. if(istype(A, /datum/reagent))
  311. if(!reagents)
  312. reagents = new()
  313. reagents.reagent_list.Add(A)
  314. reagents.conditional_update()
  315. else if(ismovable(A))
  316. var/atom/movable/M = A
  317. if(isliving(M.loc))
  318. var/mob/living/L = M.loc
  319. L.transferItemToLoc(M, src)
  320. else
  321. M.forceMove(src)
  322. ///Hook for multiz???
  323. /atom/proc/update_multiz(prune_on_fail = FALSE)
  324. return FALSE
  325. ///Take air from the passed in gas mixture datum
  326. /atom/proc/assume_air(datum/gas_mixture/giver)
  327. qdel(giver)
  328. return null
  329. ///Remove air from this atom
  330. /atom/proc/remove_air(amount)
  331. return null
  332. ///Return the current air environment in this atom
  333. /atom/proc/return_air()
  334. if(loc)
  335. return loc.return_air()
  336. else
  337. return null
  338. ///Return the air if we can analyze it
  339. /atom/proc/return_analyzable_air()
  340. return null
  341. ///Check if this atoms eye is still alive (probably)
  342. /atom/proc/check_eye(mob/user)
  343. return
  344. /atom/proc/Bumped(atom/movable/AM)
  345. set waitfor = FALSE
  346. SEND_SIGNAL(src, COMSIG_ATOM_BUMPED, AM)
  347. /// Convenience proc to see if a container is open for chemistry handling
  348. /atom/proc/is_open_container()
  349. return is_refillable() && is_drainable()
  350. /// Is this atom injectable into other atoms
  351. /atom/proc/is_injectable(mob/user, allowmobs = TRUE)
  352. return reagents && (reagents.flags & (INJECTABLE | REFILLABLE))
  353. /// Can we draw from this atom with an injectable atom
  354. /atom/proc/is_drawable(mob/user, allowmobs = TRUE)
  355. return reagents && (reagents.flags & (DRAWABLE | DRAINABLE))
  356. /// Can this atoms reagents be refilled
  357. /atom/proc/is_refillable()
  358. return reagents && (reagents.flags & REFILLABLE)
  359. /// Is this atom drainable of reagents
  360. /atom/proc/is_drainable()
  361. return reagents && (reagents.flags & DRAINABLE)
  362. /// Are you allowed to drop this atom
  363. /atom/proc/AllowDrop()
  364. return FALSE
  365. /atom/proc/CheckExit()
  366. return 1
  367. ///Is this atom within 1 tile of another atom
  368. /atom/proc/HasProximity(atom/movable/AM as mob|obj)
  369. return
  370. /**
  371. * React to an EMP of the given severity
  372. *
  373. * Default behaviour is to send the [COMSIG_ATOM_EMP_ACT] signal
  374. *
  375. * If the signal does not return protection, and there are attached wires then we call
  376. * [emp_pulse][/datum/wires/proc/emp_pulse] on the wires
  377. *
  378. * We then return the protection value
  379. */
  380. /atom/proc/emp_act(severity)
  381. var/protection = SEND_SIGNAL(src, COMSIG_ATOM_EMP_ACT, severity)
  382. if(!(protection & EMP_PROTECT_WIRES) && istype(wires))
  383. wires.emp_pulse()
  384. return protection // Pass the protection value collected here upwards
  385. /**
  386. * React to a hit by a projectile object
  387. *
  388. * Default behaviour is to send the [COMSIG_ATOM_BULLET_ACT] and then call [on_hit][/obj/projectile/proc/on_hit] on the projectile
  389. */
  390. /atom/proc/bullet_act(obj/projectile/P, def_zone)
  391. SEND_SIGNAL(src, COMSIG_ATOM_BULLET_ACT, P, def_zone)
  392. . = P.on_hit(src, 0, def_zone)
  393. ///Return true if we're inside the passed in atom
  394. /atom/proc/in_contents_of(container)//can take class or object instance as argument
  395. if(ispath(container))
  396. if(istype(src.loc, container))
  397. return TRUE
  398. else if(src in container)
  399. return TRUE
  400. return FALSE
  401. /**
  402. * Get the name of this object for examine
  403. *
  404. * You can override what is returned from this proc by registering to listen for the
  405. * [COMSIG_ATOM_GET_EXAMINE_NAME] signal
  406. */
  407. /atom/proc/get_examine_name(mob/user)
  408. . = "\a [src]"
  409. var/list/override = list(gender == PLURAL ? "some" : "a", " ", "[name]")
  410. if(article)
  411. . = "[article] [src]"
  412. override[EXAMINE_POSITION_ARTICLE] = article
  413. if(SEND_SIGNAL(src, COMSIG_ATOM_GET_EXAMINE_NAME, user, override) & COMPONENT_EXNAME_CHANGED)
  414. . = override.Join("")
  415. ///Generate the full examine string of this atom (including icon for goonchat)
  416. /atom/proc/get_examine_string(mob/user, thats = FALSE)
  417. return "[icon2html(src, user)] [thats? "That's ":""][get_examine_name(user)]"
  418. /**
  419. * Called when a mob examines (shift click or verb) this atom
  420. *
  421. * Default behaviour is to get the name and icon of the object and it's reagents where
  422. * the [TRANSPARENT] flag is set on the reagents holder
  423. *
  424. * Produces a signal [COMSIG_PARENT_EXAMINE]
  425. */
  426. /atom/proc/examine(mob/user)
  427. . = list("[get_examine_string(user, TRUE)].")
  428. if(desc)
  429. . += desc
  430. if(custom_materials)
  431. var/list/materials_list = list()
  432. for(var/i in custom_materials)
  433. var/datum/material/M = i
  434. materials_list += "[M.name]"
  435. . += "<u>It is made out of [english_list(materials_list)]</u>."
  436. if(reagents)
  437. if(reagents.flags & TRANSPARENT)
  438. . += "It contains:"
  439. if(length(reagents.reagent_list))
  440. if(user.can_see_reagents()) //Show each individual reagent
  441. for(var/datum/reagent/R in reagents.reagent_list)
  442. . += "[R.volume] units of [R.name]"
  443. else //Otherwise, just show the total volume
  444. var/total_volume = 0
  445. for(var/datum/reagent/R in reagents.reagent_list)
  446. total_volume += R.volume
  447. . += "[total_volume] units of various reagents"
  448. else
  449. . += "Nothing."
  450. else if(reagents.flags & AMOUNT_VISIBLE)
  451. if(reagents.total_volume)
  452. . += "<span class='notice'>It has [reagents.total_volume] unit\s left.</span>"
  453. else
  454. . += "<span class='danger'>It's empty.</span>"
  455. SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, user, .)
  456. /// Updates the icon of the atom
  457. /atom/proc/update_icon()
  458. var/signalOut = SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_ICON)
  459. . = FALSE
  460. if(!(signalOut & COMSIG_ATOM_NO_UPDATE_ICON_STATE))
  461. update_icon_state()
  462. . = TRUE
  463. if(!(signalOut & COMSIG_ATOM_NO_UPDATE_OVERLAYS))
  464. var/list/new_overlays = update_overlays()
  465. if(managed_overlays)
  466. cut_overlay(managed_overlays)
  467. managed_overlays = null
  468. if(length(new_overlays))
  469. managed_overlays = new_overlays
  470. add_overlay(new_overlays)
  471. . = TRUE
  472. SSdemo.mark_dirty(src)
  473. SEND_SIGNAL(src, COMSIG_ATOM_UPDATED_ICON, signalOut, .)
  474. /// Updates the icon state of the atom
  475. /atom/proc/update_icon_state()
  476. /// Updates the overlays of the atom
  477. /atom/proc/update_overlays()
  478. SHOULD_CALL_PARENT(1)
  479. . = list()
  480. SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_OVERLAYS, .)
  481. /**
  482. * An atom we are buckled or is contained within us has tried to move
  483. *
  484. * Default behaviour is to send a warning that the user can't move while buckled as long
  485. * as the [buckle_message_cooldown][/atom/var/buckle_message_cooldown] has expired (50 ticks)
  486. */
  487. /atom/proc/relaymove(mob/user)
  488. if(buckle_message_cooldown <= world.time)
  489. buckle_message_cooldown = world.time + 50
  490. to_chat(user, "<span class='warning'>You can't move while buckled to [src]!</span>")
  491. return
  492. /// Handle what happens when your contents are exploded by a bomb
  493. /atom/proc/contents_explosion(severity, target)
  494. return //For handling the effects of explosions on contents that would not normally be effected
  495. /**
  496. * React to being hit by an explosion
  497. *
  498. * Default behaviour is to call [contents_explosion][/atom/proc/contents_explosion] and send the [COMSIG_ATOM_EX_ACT] signal
  499. */
  500. /atom/proc/ex_act(severity, target)
  501. set waitfor = FALSE
  502. contents_explosion(severity, target)
  503. SEND_SIGNAL(src, COMSIG_ATOM_EX_ACT, severity, target)
  504. /**
  505. * React to a hit by a blob objecd
  506. *
  507. * default behaviour is to send the [COMSIG_ATOM_BLOB_ACT] signal
  508. */
  509. /atom/proc/blob_act(obj/structure/blob/B)
  510. SEND_SIGNAL(src, COMSIG_ATOM_BLOB_ACT, B)
  511. return
  512. /atom/proc/fire_act(exposed_temperature, exposed_volume)
  513. SEND_SIGNAL(src, COMSIG_ATOM_FIRE_ACT, exposed_temperature, exposed_volume)
  514. return
  515. /**
  516. * React to being hit by a thrown object
  517. *
  518. * Default behaviour is to call [hitby_react][/atom/proc/hitby_react] on ourselves after 2 seconds if we are dense
  519. * and under normal gravity.
  520. *
  521. * Im not sure why this the case, maybe to prevent lots of hitby's if the thrown object is
  522. * deleted shortly after hitting something (during explosions or other massive events that
  523. * throw lots of items around - singularity being a notable example)
  524. */
  525. /atom/proc/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
  526. if(density && !has_gravity(AM)) //thrown stuff bounces off dense stuff in no grav, unless the thrown stuff ends up inside what it hit(embedding, bola, etc...).
  527. addtimer(CALLBACK(src, .proc/hitby_react, AM), 2)
  528. /**
  529. * We have have actually hit the passed in atom
  530. *
  531. * Default behaviour is to move back from the item that hit us
  532. */
  533. /atom/proc/hitby_react(atom/movable/AM)
  534. if(AM && isturf(AM.loc))
  535. step(AM, turn(AM.dir, 180))
  536. ///Handle the atom being slipped over
  537. /atom/proc/handle_slip(mob/living/carbon/C, knockdown_amount, obj/O, lube, paralyze, force_drop)
  538. return
  539. ///returns the mob's dna info as a list, to be inserted in an object's blood_DNA list
  540. /mob/living/proc/get_blood_dna_list()
  541. if(get_blood_id() != /datum/reagent/blood)
  542. return
  543. return list("ANIMAL DNA" = "Y-")
  544. ///Get the mobs dna list
  545. /mob/living/carbon/get_blood_dna_list()
  546. if(get_blood_id() != /datum/reagent/blood)
  547. return
  548. var/list/blood_dna = list()
  549. if(dna)
  550. blood_dna[dna.unique_enzymes] = dna.blood_type
  551. else
  552. blood_dna["UNKNOWN DNA"] = "X*"
  553. return blood_dna
  554. /mob/living/carbon/alien/get_blood_dna_list()
  555. return list("UNKNOWN DNA" = "X*")
  556. /mob/living/silicon/get_blood_dna_list()
  557. return list("MOTOR OIL" = "SAE 5W-30") //just a little flavor text.
  558. ///to add a mob's dna info into an object's blood_dna list.
  559. /atom/proc/transfer_mob_blood_dna(mob/living/L)
  560. // Returns 0 if we have that blood already
  561. var/new_blood_dna = L.get_blood_dna_list()
  562. if(!new_blood_dna)
  563. return FALSE
  564. var/old_length = blood_DNA_length()
  565. add_blood_DNA(new_blood_dna)
  566. if(blood_DNA_length() == old_length)
  567. return FALSE
  568. return TRUE
  569. ///to add blood from a mob onto something, and transfer their dna info
  570. /atom/proc/add_mob_blood(mob/living/M)
  571. var/list/blood_dna = M.get_blood_dna_list()
  572. if(!blood_dna)
  573. return FALSE
  574. return add_blood_DNA(blood_dna)
  575. ///Is this atom in space
  576. /atom/proc/isinspace()
  577. if(isspaceturf(get_turf(src)))
  578. return TRUE
  579. else
  580. return FALSE
  581. ///Used for making a sound when a mob involuntarily falls into the ground.
  582. /atom/proc/handle_fall(mob/faller)
  583. return
  584. ///Respond to the singularity eating this atom
  585. /atom/proc/singularity_act()
  586. return
  587. /**
  588. * Respond to the singularity pulling on us
  589. *
  590. * Default behaviour is to send [COMSIG_ATOM_SING_PULL] and return
  591. */
  592. /atom/proc/singularity_pull(obj/singularity/S, current_size)
  593. SEND_SIGNAL(src, COMSIG_ATOM_SING_PULL, S, current_size)
  594. /**
  595. * Respond to acid being used on our atom
  596. *
  597. * Default behaviour is to send [COMSIG_ATOM_ACID_ACT] and return
  598. */
  599. /atom/proc/acid_act(acidpwr, acid_volume)
  600. SEND_SIGNAL(src, COMSIG_ATOM_ACID_ACT, acidpwr, acid_volume)
  601. /**
  602. * Respond to an emag being used on our atom
  603. *
  604. * Default behaviour is to send [COMSIG_ATOM_EMAG_ACT] and return
  605. */
  606. /atom/proc/emag_act(mob/user)
  607. SEND_SIGNAL(src, COMSIG_ATOM_EMAG_ACT, user)
  608. /**
  609. * Respond to a radioactive wave hitting this atom
  610. *
  611. * Default behaviour is to send [COMSIG_ATOM_RAD_ACT] and return
  612. */
  613. /atom/proc/rad_act(strength)
  614. SEND_SIGNAL(src, COMSIG_ATOM_RAD_ACT, strength)
  615. /**
  616. * Respond to narsie eating our atom
  617. *
  618. * Default behaviour is to send [COMSIG_ATOM_NARSIE_ACT] and return
  619. */
  620. /atom/proc/narsie_act()
  621. SEND_SIGNAL(src, COMSIG_ATOM_NARSIE_ACT)
  622. ///Return the values you get when an RCD eats you?
  623. /atom/proc/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd)
  624. return FALSE
  625. /**
  626. * Respond to an RCD acting on our item
  627. *
  628. * Default behaviour is to send [COMSIG_ATOM_RCD_ACT] and return FALSE
  629. */
  630. /atom/proc/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, passed_mode)
  631. SEND_SIGNAL(src, COMSIG_ATOM_RCD_ACT, user, the_rcd, passed_mode)
  632. return FALSE
  633. /**
  634. * Implement the behaviour for when a user click drags a storage object to your atom
  635. *
  636. * This behaviour is usually to mass transfer, but this is no longer a used proc as it just
  637. * calls the underyling /datum/component/storage dump act if a component exists
  638. *
  639. * TODO these should be purely component items that intercept the atom clicks higher in the
  640. * call chain
  641. */
  642. /atom/proc/storage_contents_dump_act(obj/item/storage/src_object, mob/user)
  643. if(GetComponent(/datum/component/storage))
  644. return component_storage_contents_dump_act(src_object, user)
  645. return FALSE
  646. /**
  647. * Implement the behaviour for when a user click drags another storage item to you
  648. *
  649. * In this case we get as many of the tiems from the target items compoent storage and then
  650. * put everything into ourselves (or our storage component)
  651. *
  652. * TODO these should be purely component items that intercept the atom clicks higher in the
  653. * call chain
  654. */
  655. /atom/proc/component_storage_contents_dump_act(datum/component/storage/src_object, mob/user)
  656. var/list/things = src_object.contents()
  657. var/datum/progressbar/progress = new(user, things.len, src)
  658. var/datum/component/storage/STR = GetComponent(/datum/component/storage)
  659. while (do_after(user, 10, TRUE, src, FALSE, CALLBACK(STR, /datum/component/storage.proc/handle_mass_item_insertion, things, src_object, user, progress)))
  660. stoplag(1)
  661. progress.end_progress()
  662. to_chat(user, "<span class='notice'>You dump as much of [src_object.parent]'s contents [STR.insert_preposition]to [src] as you can.</span>")
  663. STR.orient2hud(user)
  664. src_object.orient2hud(user)
  665. if(user.active_storage) //refresh the HUD to show the transfered contents
  666. user.active_storage.close(user)
  667. user.active_storage.show_to(user)
  668. return TRUE
  669. ///Get the best place to dump the items contained in the source storage item?
  670. /atom/proc/get_dumping_location(obj/item/storage/source,mob/user)
  671. return null
  672. /**
  673. * This proc is called when an atom in our contents has it's [Destroy][/atom/Destroy] called
  674. *
  675. * Default behaviour is to simply send [COMSIG_ATOM_CONTENTS_DEL]
  676. */
  677. /atom/proc/handle_atom_del(atom/A)
  678. SEND_SIGNAL(src, COMSIG_ATOM_CONTENTS_DEL, A)
  679. /**
  680. * called when the turf the atom resides on is ChangeTurfed
  681. *
  682. * Default behaviour is to loop through atom contents and call their HandleTurfChange() proc
  683. */
  684. /atom/proc/HandleTurfChange(turf/T)
  685. for(var/a in src)
  686. var/atom/A = a
  687. A.HandleTurfChange(T)
  688. /**
  689. * the vision impairment to give to the mob whose perspective is set to that atom
  690. *
  691. * (e.g. an unfocused camera giving you an impaired vision when looking through it)
  692. */
  693. /atom/proc/get_remote_view_fullscreens(mob/user)
  694. return
  695. /**
  696. * the sight changes to give to the mob whose perspective is set to that atom
  697. *
  698. * (e.g. A mob with nightvision loses its nightvision while looking through a normal camera)
  699. */
  700. /atom/proc/update_remote_sight(mob/living/user)
  701. return
  702. /**
  703. * Hook for running code when a dir change occurs
  704. *
  705. * Not recommended to use, listen for the [COMSIG_ATOM_DIR_CHANGE] signal instead (sent by this proc)
  706. */
  707. /atom/proc/setDir(newdir)
  708. SHOULD_CALL_PARENT(TRUE)
  709. SEND_SIGNAL(src, COMSIG_ATOM_DIR_CHANGE, dir, newdir)
  710. dir = newdir
  711. SSdemo.mark_dirty(src)
  712. ///Handle melee attack by a mech
  713. /atom/proc/mech_melee_attack(obj/mecha/M)
  714. return
  715. /**
  716. * Called when the atom log's in or out
  717. *
  718. * Default behaviour is to call on_log on the location this atom is in
  719. */
  720. /atom/proc/on_log(login)
  721. if(loc)
  722. loc.on_log(login)
  723. /*
  724. Atom Colour Priority System
  725. A System that gives finer control over which atom colour to colour the atom with.
  726. The "highest priority" one is always displayed as opposed to the default of
  727. "whichever was set last is displayed"
  728. */
  729. ///Adds an instance of colour_type to the atom's atom_colours list
  730. /atom/proc/add_atom_colour(coloration, colour_priority)
  731. if(!atom_colours || !atom_colours.len)
  732. atom_colours = list()
  733. atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently.
  734. if(!coloration)
  735. return
  736. if(colour_priority > atom_colours.len)
  737. return
  738. atom_colours[colour_priority] = coloration
  739. update_atom_colour()
  740. ///Removes an instance of colour_type from the atom's atom_colours list
  741. /atom/proc/remove_atom_colour(colour_priority, coloration)
  742. if(!atom_colours)
  743. atom_colours = list()
  744. atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently.
  745. if(colour_priority > atom_colours.len)
  746. return
  747. if(coloration && atom_colours[colour_priority] != coloration)
  748. return //if we don't have the expected color (for a specific priority) to remove, do nothing
  749. atom_colours[colour_priority] = null
  750. update_atom_colour()
  751. ///Resets the atom's color to null, and then sets it to the highest priority colour available
  752. /atom/proc/update_atom_colour()
  753. if(!atom_colours)
  754. atom_colours = list()
  755. atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently.
  756. color = null
  757. for(var/C in atom_colours)
  758. if(islist(C))
  759. var/list/L = C
  760. if(L.len)
  761. color = L
  762. return
  763. else if(C)
  764. color = C
  765. return
  766. ///Proc for being washed by a shower
  767. /atom/proc/washed(var/atom/washer)
  768. . = SEND_SIGNAL(src, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
  769. remove_atom_colour(WASHABLE_COLOUR_PRIORITY)
  770. var/datum/component/radioactive/healthy_green_glow = GetComponent(/datum/component/radioactive)
  771. if(!healthy_green_glow || QDELETED(healthy_green_glow))
  772. return
  773. var/strength = healthy_green_glow.strength
  774. if(strength <= RAD_BACKGROUND_RADIATION)
  775. qdel(healthy_green_glow)
  776. return
  777. healthy_green_glow.strength -= max(0, (healthy_green_glow.strength - (RAD_BACKGROUND_RADIATION * 2)) * 0.2)
  778. /**
  779. * call back when a var is edited on this atom
  780. *
  781. * Can be used to implement special handling of vars
  782. *
  783. * At the atom level, if you edit a var named "color" it will add the atom colour with
  784. * admin level priority to the atom colours list
  785. *
  786. * Also, if GLOB.Debug2 is FALSE, it sets the [ADMIN_SPAWNED_1] flag on [flags_1][/atom/var/flags_1], which signifies
  787. * the object has been admin edited
  788. */
  789. /atom/vv_edit_var(var_name, var_value)
  790. if(!GLOB.Debug2)
  791. flags_1 |= ADMIN_SPAWNED_1
  792. . = ..()
  793. switch(var_name)
  794. if("color")
  795. add_atom_colour(color, ADMIN_COLOUR_PRIORITY)
  796. /**
  797. * Return the markup to for the dropdown list for the VV panel for this atom
  798. *
  799. * Override in subtypes to add custom VV handling in the VV panel
  800. */
  801. /atom/vv_get_dropdown()
  802. . = ..()
  803. VV_DROPDOWN_OPTION("", "---------")
  804. if(!ismovable(src))
  805. var/turf/curturf = get_turf(src)
  806. if(curturf)
  807. . += "<option value='?_src_=holder;[HrefToken()];adminplayerobservecoodjump=1;X=[curturf.x];Y=[curturf.y];Z=[curturf.z]'>Jump To</option>"
  808. VV_DROPDOWN_OPTION(VV_HK_MODIFY_TRANSFORM, "Modify Transform")
  809. VV_DROPDOWN_OPTION(VV_HK_ADD_REAGENT, "Add Reagent")
  810. VV_DROPDOWN_OPTION(VV_HK_TRIGGER_EMP, "EMP Pulse")
  811. VV_DROPDOWN_OPTION(VV_HK_TRIGGER_EXPLOSION, "Explosion")
  812. /atom/vv_do_topic(list/href_list)
  813. . = ..()
  814. if(href_list[VV_HK_ADD_REAGENT] && check_rights(R_VAREDIT))
  815. if(!reagents)
  816. var/amount = input(usr, "Specify the reagent size of [src]", "Set Reagent Size", 50) as num|null
  817. if(amount)
  818. create_reagents(amount)
  819. if(reagents)
  820. var/chosen_id
  821. switch(alert(usr, "Choose a method.", "Add Reagents", "Search", "Choose from a list", "I'm feeling lucky"))
  822. if("Search")
  823. var/valid_id
  824. while(!valid_id)
  825. chosen_id = input(usr, "Enter the ID of the reagent you want to add.", "Search reagents") as null|text
  826. if(isnull(chosen_id)) //Get me out of here!
  827. break
  828. if (!ispath(text2path(chosen_id)))
  829. chosen_id = pick_closest_path(chosen_id, make_types_fancy(subtypesof(/datum/reagent)))
  830. if (ispath(chosen_id))
  831. valid_id = TRUE
  832. else
  833. valid_id = TRUE
  834. if(!valid_id)
  835. to_chat(usr, "<span class='warning'>A reagent with that ID doesn't exist!</span>")
  836. if("Choose from a list")
  837. chosen_id = input(usr, "Choose a reagent to add.", "Choose a reagent.") as null|anything in sortList(subtypesof(/datum/reagent), /proc/cmp_typepaths_asc)
  838. if("I'm feeling lucky")
  839. chosen_id = pick(subtypesof(/datum/reagent))
  840. if(chosen_id)
  841. var/amount = input(usr, "Choose the amount to add.", "Choose the amount.", reagents.maximum_volume) as num|null
  842. if(amount)
  843. reagents.add_reagent(chosen_id, amount)
  844. log_admin("[key_name(usr)] has added [amount] units of [chosen_id] to [src]")
  845. message_admins("<span class='notice'>[key_name(usr)] has added [amount] units of [chosen_id] to [src]</span>")
  846. if(href_list[VV_HK_TRIGGER_EXPLOSION] && check_rights(R_FUN))
  847. usr.client.cmd_admin_explosion(src)
  848. if(href_list[VV_HK_TRIGGER_EMP] && check_rights(R_FUN))
  849. usr.client.cmd_admin_emp(src)
  850. if(href_list[VV_HK_MODIFY_TRANSFORM] && check_rights(R_VAREDIT))
  851. var/result = input(usr, "Choose the transformation to apply","Transform Mod") as null|anything in list("Scale","Translate","Rotate")
  852. var/matrix/M = transform
  853. switch(result)
  854. if("Scale")
  855. var/x = input(usr, "Choose x mod","Transform Mod") as null|num
  856. var/y = input(usr, "Choose y mod","Transform Mod") as null|num
  857. if(!isnull(x) && !isnull(y))
  858. transform = M.Scale(x,y)
  859. if("Translate")
  860. var/x = input(usr, "Choose x mod","Transform Mod") as null|num
  861. var/y = input(usr, "Choose y mod","Transform Mod") as null|num
  862. if(!isnull(x) && !isnull(y))
  863. transform = M.Translate(x,y)
  864. if("Rotate")
  865. var/angle = input(usr, "Choose angle to rotate","Transform Mod") as null|num
  866. if(!isnull(angle))
  867. transform = M.Turn(angle)
  868. if(href_list[VV_HK_AUTO_RENAME] && check_rights(R_VAREDIT))
  869. var/newname = input(usr, "What do you want to rename this to?", "Automatic Rename") as null|text
  870. if(newname)
  871. vv_auto_rename(newname)
  872. /atom/vv_get_header()
  873. . = ..()
  874. var/refid = REF(src)
  875. . += "[VV_HREF_TARGETREF(refid, VV_HK_AUTO_RENAME, "<b id='name'>[src]</b>")]"
  876. . += "<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>"
  877. ///Where atoms should drop if taken from this atom
  878. /atom/proc/drop_location()
  879. var/atom/L = loc
  880. if(!L)
  881. return null
  882. return L.AllowDrop() ? L : L.drop_location()
  883. /atom/proc/vv_auto_rename(newname)
  884. name = newname
  885. /**
  886. * An atom has entered this atom's contents
  887. *
  888. * Default behaviour is to send the [COMSIG_ATOM_ENTERED]
  889. */
  890. /atom/Entered(atom/movable/AM, atom/oldLoc)
  891. SEND_SIGNAL(src, COMSIG_ATOM_ENTERED, AM, oldLoc)
  892. /**
  893. * An atom is attempting to exit this atom's contents
  894. *
  895. * Default behaviour is to send the [COMSIG_ATOM_EXIT]
  896. *
  897. * Return value should be set to FALSE if the moving atom is unable to leave,
  898. * otherwise leave value the result of the parent call
  899. */
  900. /atom/Exit(atom/movable/AM, atom/newLoc)
  901. . = ..()
  902. if(SEND_SIGNAL(src, COMSIG_ATOM_EXIT, AM, newLoc) & COMPONENT_ATOM_BLOCK_EXIT)
  903. return FALSE
  904. /**
  905. * An atom has exited this atom's contents
  906. *
  907. * Default behaviour is to send the [COMSIG_ATOM_EXITED]
  908. */
  909. /atom/Exited(atom/movable/AM, atom/newLoc)
  910. SEND_SIGNAL(src, COMSIG_ATOM_EXITED, AM, newLoc)
  911. ///Return atom temperature
  912. /atom/proc/return_temperature()
  913. return
  914. /**
  915. *Tool behavior procedure. Redirects to tool-specific procs by default.
  916. *
  917. * You can override it to catch all tool interactions, for use in complex deconstruction procs.
  918. *
  919. * Must return parent proc ..() in the end if overridden
  920. */
  921. /atom/proc/tool_act(mob/living/user, obj/item/I, tool_type)
  922. switch(tool_type)
  923. if(TOOL_CROWBAR)
  924. . |= crowbar_act(user, I)
  925. if(TOOL_MULTITOOL)
  926. . |= multitool_act(user, I)
  927. if(TOOL_SCREWDRIVER)
  928. . |= screwdriver_act(user, I)
  929. if(TOOL_WRENCH)
  930. . |= wrench_act(user, I)
  931. if(TOOL_WIRECUTTER)
  932. . |= wirecutter_act(user, I)
  933. if(TOOL_WELDER)
  934. . |= welder_act(user, I)
  935. if(TOOL_ANALYZER)
  936. . |= analyzer_act(user, I)
  937. if(. & COMPONENT_BLOCK_TOOL_ATTACK)
  938. return TRUE
  939. //! Tool-specific behavior procs. They send signals, so try to call ..()
  940. ///
  941. ///Crowbar act
  942. /atom/proc/crowbar_act(mob/living/user, obj/item/I)
  943. return SEND_SIGNAL(src, COMSIG_ATOM_CROWBAR_ACT, user, I)
  944. ///Multitool act
  945. /atom/proc/multitool_act(mob/living/user, obj/item/I)
  946. return SEND_SIGNAL(src, COMSIG_ATOM_MULTITOOL_ACT, user, I)
  947. ///Check if the multitool has an item in it's data buffer
  948. /atom/proc/multitool_check_buffer(user, obj/item/I, silent = FALSE)
  949. if(!istype(I, /obj/item/multitool))
  950. if(user && !silent)
  951. to_chat(user, "<span class='warning'>[I] has no data buffer!</span>")
  952. return FALSE
  953. return TRUE
  954. ///Screwdriver act
  955. /atom/proc/screwdriver_act(mob/living/user, obj/item/I)
  956. return SEND_SIGNAL(src, COMSIG_ATOM_SCREWDRIVER_ACT, user, I)
  957. ///Wrench act
  958. /atom/proc/wrench_act(mob/living/user, obj/item/I)
  959. return SEND_SIGNAL(src, COMSIG_ATOM_WRENCH_ACT, user, I)
  960. ///Wirecutter act
  961. /atom/proc/wirecutter_act(mob/living/user, obj/item/I)
  962. return SEND_SIGNAL(src, COMSIG_ATOM_WIRECUTTER_ACT, user, I)
  963. ///Welder act
  964. /atom/proc/welder_act(mob/living/user, obj/item/I)
  965. return SEND_SIGNAL(src, COMSIG_ATOM_WELDER_ACT, user, I)
  966. ///Analyzer act
  967. /atom/proc/analyzer_act(mob/living/user, obj/item/I)
  968. return SEND_SIGNAL(src, COMSIG_ATOM_ANALYSER_ACT, user, I)
  969. ///Generate a tag for this atom
  970. /atom/proc/GenerateTag()
  971. return
  972. ///Connect this atom to a shuttle
  973. /atom/proc/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock, idnum, override=FALSE)
  974. return
  975. /// Generic logging helper
  976. /atom/proc/log_message(message, message_type, color=null, log_globally=TRUE)
  977. if(!log_globally)
  978. return
  979. var/log_text = "[key_name(src)] [message] [loc_name(src)]"
  980. switch(message_type)
  981. if(LOG_ATTACK)
  982. log_attack(log_text)
  983. if(LOG_SAY)
  984. log_say(log_text)
  985. if(LOG_WHISPER)
  986. log_whisper(log_text)
  987. if(LOG_EMOTE)
  988. log_emote(log_text)
  989. if(LOG_DSAY)
  990. log_dsay(log_text)
  991. if(LOG_PDA)
  992. log_pda(log_text)
  993. if(LOG_CHAT)
  994. log_chat(log_text)
  995. if(LOG_COMMENT)
  996. log_comment(log_text)
  997. if(LOG_TELECOMMS)
  998. log_telecomms(log_text)
  999. if(LOG_OOC)
  1000. log_ooc(log_text)
  1001. if(LOG_ADMIN)
  1002. log_admin(log_text)
  1003. if(LOG_ADMIN_PRIVATE)
  1004. log_admin_private(log_text)
  1005. if(LOG_ASAY)
  1006. log_adminsay(log_text)
  1007. if(LOG_OWNERSHIP)
  1008. log_game(log_text)
  1009. if(LOG_GAME)
  1010. log_game(log_text)
  1011. if(LOG_MECHA)
  1012. log_mecha(log_text)
  1013. if(LOG_SHUTTLE)
  1014. log_shuttle(log_text)
  1015. else
  1016. stack_trace("Invalid individual logging type: [message_type]. Defaulting to [LOG_GAME] (LOG_GAME).")
  1017. log_game(log_text)
  1018. /// Helper for logging chat messages or other logs with arbitrary inputs (e.g. announcements)
  1019. /atom/proc/log_talk(message, message_type, tag=null, log_globally=TRUE, forced_by=null)
  1020. var/prefix = tag ? "([tag]) " : ""
  1021. var/suffix = forced_by ? " FORCED by [forced_by]" : ""
  1022. log_message("[prefix]\"[message]\"[suffix]", message_type, log_globally=log_globally)
  1023. /// Helper for logging of messages with only one sender and receiver
  1024. /proc/log_directed_talk(atom/source, atom/target, message, message_type, tag)
  1025. if(!tag)
  1026. stack_trace("Unspecified tag for private message")
  1027. tag = "UNKNOWN"
  1028. source.log_talk(message, message_type, tag="[tag] to [key_name(target)]")
  1029. if(source != target)
  1030. target.log_talk(message, message_type, tag="[tag] from [key_name(source)]", log_globally=FALSE)
  1031. /**
  1032. * Log a combat message in the attack log
  1033. *
  1034. * Arguments:
  1035. * * atom/user - argument is the actor performing the action
  1036. * * atom/target - argument is the target of the action
  1037. * * what_done - is a verb describing the action (e.g. punched, throwed, kicked, etc.)
  1038. * * atom/object - is a tool with which the action was made (usually an item)
  1039. * * addition - is any additional text, which will be appended to the rest of the log line
  1040. */
  1041. /proc/log_combat(atom/user, atom/target, what_done, atom/object=null, addition=null)
  1042. var/ssource = key_name(user)
  1043. var/starget = key_name(target)
  1044. var/mob/living/living_target = target
  1045. var/hp = istype(living_target) ? " (NEWHP: [living_target.health]) " : ""
  1046. var/sobject = ""
  1047. if(object)
  1048. sobject = " with [object]"
  1049. var/saddition = ""
  1050. if(addition)
  1051. saddition = " [addition]"
  1052. var/postfix = "[sobject][saddition][hp]"
  1053. var/message = "has [what_done] [starget][postfix]"
  1054. user.log_message(message, LOG_ATTACK, color="red")
  1055. if(user != target)
  1056. var/reverse_message = "has been [what_done] by [ssource][postfix]"
  1057. target.log_message(reverse_message, LOG_ATTACK, color="orange", log_globally=FALSE)
  1058. /atom/movable/proc/add_filter(name,priority,list/params)
  1059. LAZYINITLIST(filter_data)
  1060. var/list/p = params.Copy()
  1061. p["priority"] = priority
  1062. filter_data[name] = p
  1063. update_filters()
  1064. /atom/movable/proc/update_filters()
  1065. filters = null
  1066. filter_data = sortTim(filter_data, /proc/cmp_filter_data_priority, TRUE)
  1067. for(var/f in filter_data)
  1068. var/list/data = filter_data[f]
  1069. var/list/arguments = data.Copy()
  1070. arguments -= "priority"
  1071. filters += filter(arglist(arguments))
  1072. /atom/movable/proc/get_filter(name)
  1073. if(filter_data && filter_data[name])
  1074. return filters[filter_data.Find(name)]
  1075. /atom/proc/intercept_zImpact(atom/movable/AM, levels = 1)
  1076. . |= SEND_SIGNAL(src, COMSIG_ATOM_INTERCEPT_Z_FALL, AM, levels)
  1077. ///Sets the custom materials for an item.
  1078. /atom/proc/set_custom_materials(list/materials, multiplier = 1)
  1079. if(!materials)
  1080. materials = custom_materials
  1081. if(custom_materials) //Only runs if custom materials existed at first. Should usually be the case but check anyways
  1082. for(var/i in custom_materials)
  1083. var/datum/material/custom_material = SSmaterials.GetMaterialRef(i)
  1084. custom_material.on_removed(src, material_flags) //Remove the current materials
  1085. if(!length(materials))
  1086. return
  1087. custom_materials = list() //Reset the list
  1088. for(var/x in materials)
  1089. var/datum/material/custom_material = SSmaterials.GetMaterialRef(x)
  1090. if(!(material_flags & MATERIAL_NO_EFFECTS))
  1091. custom_material.on_applied(src, materials[custom_material] * multiplier * material_modifier, material_flags)
  1092. custom_materials[custom_material] += materials[x] * multiplier
  1093. /**
  1094. * Returns true if this atom has gravity for the passed in turf
  1095. *
  1096. * Sends signals [COMSIG_ATOM_HAS_GRAVITY] and [COMSIG_TURF_HAS_GRAVITY], both can force gravity with
  1097. * the forced gravity var
  1098. *
  1099. * Gravity situations:
  1100. * * No gravity if you're not in a turf
  1101. * * No gravity if this atom is in is a space turf
  1102. * * Gravity if the area it's in always has gravity
  1103. * * Gravity if there's a gravity generator on the z level
  1104. * * Gravity if the Z level has an SSMappingTrait for ZTRAIT_GRAVITY
  1105. * * otherwise no gravity
  1106. */
  1107. /atom/proc/has_gravity(turf/T)
  1108. if(!T || !isturf(T))
  1109. T = get_turf(src)
  1110. if(!T)
  1111. return 0
  1112. var/list/forced_gravity = list()
  1113. SEND_SIGNAL(src, COMSIG_ATOM_HAS_GRAVITY, T, forced_gravity)
  1114. if(!forced_gravity.len)
  1115. SEND_SIGNAL(T, COMSIG_TURF_HAS_GRAVITY, src, forced_gravity)
  1116. if(forced_gravity.len)
  1117. var/max_grav
  1118. for(var/i in forced_gravity)
  1119. max_grav = max(max_grav, i)
  1120. return max_grav
  1121. if(isspaceturf(T)) // Turf never has gravity
  1122. return FALSE
  1123. if(istype(T, /turf/open/openspace)) //openspace in a space area doesn't get gravity
  1124. if(istype(get_area(T), /area/space))
  1125. return FALSE
  1126. var/area/A = get_area(T)
  1127. if(A.has_gravity) // Areas which always has gravity
  1128. return A.has_gravity
  1129. else
  1130. // There's a gravity generator on our z level
  1131. if(GLOB.gravity_generators["[T.z]"])
  1132. var/max_grav = 0
  1133. for(var/obj/machinery/gravity_generator/main/G in GLOB.gravity_generators["[T.z]"])
  1134. max_grav = max(G.setting,max_grav)
  1135. return max_grav
  1136. return SSmapping.level_trait(T.z, ZTRAIT_GRAVITY)