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.
 
 
 
 
 
 

554 lines
17 KiB

  1. /turf
  2. icon = 'icons/turf/floors.dmi'
  3. var/intact = 1
  4. // baseturfs can be either a list or a single turf type.
  5. // In class definition like here it should always be a single type.
  6. // A list will be created in initialization that figures out the baseturf's baseturf etc.
  7. // In the case of a list it is sorted from bottom layer to top.
  8. // This shouldn't be modified directly, use the helper procs.
  9. var/list/baseturfs = /turf/baseturf_bottom
  10. var/temperature = T20C
  11. var/to_be_destroyed = 0 //Used for fire, if a melting temperature was reached, it will be destroyed
  12. var/max_fire_temperature_sustained = 0 //The max temperature of the fire which it was subjected to
  13. var/blocks_air = FALSE
  14. flags_1 = CAN_BE_DIRTY_1
  15. var/list/image/blueprint_data //for the station blueprints, images of objects eg: pipes
  16. var/explosion_level = 0 //for preventing explosion dodging
  17. var/explosion_id = 0
  18. var/requires_activation //add to air processing after initialize?
  19. var/changing_turf = FALSE
  20. var/bullet_bounce_sound = 'sound/weapons/gun/general/mag_bullet_remove.ogg' //sound played when a shell casing is ejected ontop of the turf.
  21. var/bullet_sizzle = FALSE //used by ammo_casing/bounce_away() to determine if the shell casing should make a sizzle sound when it's ejected over the turf
  22. //IE if the turf is supposed to be water, set TRUE.
  23. var/tiled_dirt = FALSE // use smooth tiled dirt decal
  24. vis_flags = VIS_INHERIT_PLANE|VIS_INHERIT_ID //when this be added to vis_contents of something it inherit something.plane and be associated with something on clicking, important for visualisation of turf in openspace and interraction with openspace that show you turf.
  25. /turf/vv_edit_var(var_name, new_value)
  26. var/static/list/banned_edits = list("x", "y", "z")
  27. if(var_name in banned_edits)
  28. return FALSE
  29. . = ..()
  30. /**
  31. * Turf Initialize
  32. *
  33. * Doesn't call parent, see [/atom/proc/Initialize]
  34. */
  35. /turf/Initialize(mapload)
  36. SHOULD_CALL_PARENT(FALSE)
  37. if(flags_1 & INITIALIZED_1)
  38. stack_trace("Warning: [src]([type]) initialized multiple times!")
  39. flags_1 |= INITIALIZED_1
  40. // by default, vis_contents is inherited from the turf that was here before
  41. vis_contents.Cut()
  42. assemble_baseturfs()
  43. levelupdate()
  44. if(smooth)
  45. queue_smooth(src)
  46. visibilityChanged()
  47. for(var/atom/movable/AM in src)
  48. Entered(AM)
  49. var/area/A = loc
  50. if(!IS_DYNAMIC_LIGHTING(src) && IS_DYNAMIC_LIGHTING(A))
  51. add_overlay(/obj/effect/fullbright)
  52. if(requires_activation)
  53. CALCULATE_ADJACENT_TURFS(src)
  54. SSair.add_to_active(src)
  55. if (light_power && light_range)
  56. update_light()
  57. var/turf/T = SSmapping.get_turf_above(src)
  58. if(T)
  59. T.multiz_turf_new(src, DOWN)
  60. SEND_SIGNAL(T, COMSIG_TURF_MULTIZ_NEW, src, DOWN)
  61. T = SSmapping.get_turf_below(src)
  62. if(T)
  63. T.multiz_turf_new(src, UP)
  64. SEND_SIGNAL(T, COMSIG_TURF_MULTIZ_NEW, src, UP)
  65. if (opacity)
  66. has_opaque_atom = TRUE
  67. if(custom_materials)
  68. var/temp_list = list()
  69. for(var/i in custom_materials)
  70. temp_list[SSmaterials.GetMaterialRef(i)] = custom_materials[i] //Get the proper instanced version
  71. custom_materials = null //Null the list to prepare for applying the materials properly
  72. set_custom_materials(temp_list)
  73. ComponentInitialize()
  74. return INITIALIZE_HINT_NORMAL
  75. /turf/proc/Initalize_Atmos(times_fired)
  76. CALCULATE_ADJACENT_TURFS(src)
  77. /turf/Destroy(force)
  78. . = QDEL_HINT_IWILLGC
  79. if(!changing_turf)
  80. stack_trace("Incorrect turf deletion")
  81. changing_turf = FALSE
  82. var/turf/T = SSmapping.get_turf_above(src)
  83. if(T)
  84. T.multiz_turf_del(src, DOWN)
  85. T = SSmapping.get_turf_below(src)
  86. if(T)
  87. T.multiz_turf_del(src, UP)
  88. if(force)
  89. ..()
  90. //this will completely wipe turf state
  91. var/turf/B = new world.turf(src)
  92. for(var/A in B.contents)
  93. qdel(A)
  94. return
  95. SSair.remove_from_active(src)
  96. visibilityChanged()
  97. QDEL_LIST(blueprint_data)
  98. flags_1 &= ~INITIALIZED_1
  99. requires_activation = FALSE
  100. ..()
  101. /turf/attack_hand(mob/user)
  102. . = ..()
  103. if(.)
  104. return
  105. user.Move_Pulled(src)
  106. /turf/proc/multiz_turf_del(turf/T, dir)
  107. /turf/proc/multiz_turf_new(turf/T, dir)
  108. //zPassIn doesn't necessarily pass an atom!
  109. //direction is direction of travel of air
  110. /turf/proc/zPassIn(atom/movable/A, direction, turf/source)
  111. return FALSE
  112. //direction is direction of travel of air
  113. /turf/proc/zPassOut(atom/movable/A, direction, turf/destination)
  114. return FALSE
  115. //direction is direction of travel of air
  116. /turf/proc/zAirIn(direction, turf/source)
  117. return FALSE
  118. //direction is direction of travel of air
  119. /turf/proc/zAirOut(direction, turf/source)
  120. return FALSE
  121. /turf/proc/zImpact(atom/movable/A, levels = 1, turf/prev_turf)
  122. var/flags = NONE
  123. var/mov_name = A.name
  124. for(var/i in contents)
  125. var/atom/thing = i
  126. flags |= thing.intercept_zImpact(A, levels)
  127. if(flags & FALL_STOP_INTERCEPTING)
  128. break
  129. if(prev_turf && !(flags & FALL_NO_MESSAGE))
  130. prev_turf.visible_message("<span class='danger'>[mov_name] falls through [prev_turf]!</span>")
  131. if(flags & FALL_INTERCEPTED)
  132. return
  133. if(zFall(A, ++levels))
  134. return FALSE
  135. A.visible_message("<span class='danger'>[A] crashes into [src]!</span>")
  136. A.onZImpact(src, levels)
  137. return TRUE
  138. /turf/proc/can_zFall(atom/movable/A, levels = 1, turf/target)
  139. SHOULD_BE_PURE(TRUE)
  140. return zPassOut(A, DOWN, target) && target.zPassIn(A, DOWN, src)
  141. /turf/proc/zFall(atom/movable/A, levels = 1, force = FALSE)
  142. var/turf/target = get_step_multiz(src, DOWN)
  143. if(!target || (!isobj(A) && !ismob(A)))
  144. return FALSE
  145. if(!force && (!can_zFall(A, levels, target) || !A.can_zFall(src, levels, target, DOWN)))
  146. return FALSE
  147. A.zfalling = TRUE
  148. A.forceMove(target)
  149. A.zfalling = FALSE
  150. target.zImpact(A, levels, src)
  151. return TRUE
  152. /turf/proc/handleRCL(obj/item/rcl/C, mob/user)
  153. if(C.loaded)
  154. for(var/obj/structure/pipe_cleaner/LC in src)
  155. if(!LC.d1 || !LC.d2)
  156. LC.handlecable(C, user)
  157. return
  158. C.loaded.place_turf(src, user)
  159. if(C.wiring_gui_menu)
  160. C.wiringGuiUpdate(user)
  161. C.is_empty(user)
  162. /turf/attackby(obj/item/C, mob/user, params)
  163. if(..())
  164. return TRUE
  165. if(can_lay_cable() && istype(C, /obj/item/stack/cable_coil))
  166. var/obj/item/stack/cable_coil/coil = C
  167. coil.place_turf(src, user)
  168. return TRUE
  169. else if(can_have_cabling() && istype(C, /obj/item/stack/pipe_cleaner_coil))
  170. var/obj/item/stack/pipe_cleaner_coil/coil = C
  171. for(var/obj/structure/pipe_cleaner/LC in src)
  172. if(!LC.d1 || !LC.d2)
  173. LC.attackby(C, user)
  174. return
  175. coil.place_turf(src, user)
  176. return TRUE
  177. else if(istype(C, /obj/item/rcl))
  178. handleRCL(C, user)
  179. return FALSE
  180. //There's a lot of QDELETED() calls here if someone can figure out how to optimize this but not runtime when something gets deleted by a Bump/CanPass/Cross call, lemme know or go ahead and fix this mess - kevinz000
  181. /turf/Enter(atom/movable/mover, atom/oldloc)
  182. // Do not call ..()
  183. // Byond's default turf/Enter() doesn't have the behaviour we want with Bump()
  184. // By default byond will call Bump() on the first dense object in contents
  185. // Here's hoping it doesn't stay like this for years before we finish conversion to step_
  186. var/atom/firstbump
  187. var/canPassSelf = CanPass(mover, src)
  188. if(canPassSelf || (mover.movement_type & UNSTOPPABLE))
  189. for(var/i in contents)
  190. if(QDELETED(mover))
  191. return FALSE //We were deleted, do not attempt to proceed with movement.
  192. if(i == mover || i == mover.loc) // Multi tile objects and moving out of other objects
  193. continue
  194. var/atom/movable/thing = i
  195. if(!thing.Cross(mover))
  196. if(QDELETED(mover)) //Mover deleted from Cross/CanPass, do not proceed.
  197. return FALSE
  198. if((mover.movement_type & UNSTOPPABLE))
  199. mover.Bump(thing)
  200. continue
  201. else
  202. if(!firstbump || ((thing.layer > firstbump.layer || thing.flags_1 & ON_BORDER_1) && !(firstbump.flags_1 & ON_BORDER_1)))
  203. firstbump = thing
  204. if(QDELETED(mover)) //Mover deleted from Cross/CanPass/Bump, do not proceed.
  205. return FALSE
  206. if(!canPassSelf) //Even if mover is unstoppable they need to bump us.
  207. firstbump = src
  208. if(firstbump)
  209. mover.Bump(firstbump)
  210. return (mover.movement_type & UNSTOPPABLE)
  211. return TRUE
  212. /turf/Exit(atom/movable/mover, atom/newloc)
  213. . = ..()
  214. if(!. || QDELETED(mover))
  215. return FALSE
  216. for(var/i in contents)
  217. if(i == mover)
  218. continue
  219. var/atom/movable/thing = i
  220. if(!thing.Uncross(mover, newloc))
  221. if(thing.flags_1 & ON_BORDER_1)
  222. mover.Bump(thing)
  223. if(!(mover.movement_type & UNSTOPPABLE))
  224. return FALSE
  225. if(QDELETED(mover))
  226. return FALSE //We were deleted.
  227. /turf/Entered(atom/movable/AM)
  228. ..()
  229. if(explosion_level && AM.ex_check(explosion_id))
  230. AM.ex_act(explosion_level)
  231. // If an opaque movable atom moves around we need to potentially update visibility.
  232. if (AM.opacity)
  233. has_opaque_atom = TRUE // Make sure to do this before reconsider_lights(), incase we're on instant updates. Guaranteed to be on in this case.
  234. reconsider_lights()
  235. /turf/open/Entered(atom/movable/AM)
  236. ..()
  237. //melting
  238. if(isobj(AM) && air && air.temperature > T0C)
  239. var/obj/O = AM
  240. if(O.obj_flags & FROZEN)
  241. O.make_unfrozen()
  242. if(!AM.zfalling)
  243. zFall(AM)
  244. // A proc in case it needs to be recreated or badmins want to change the baseturfs
  245. /turf/proc/assemble_baseturfs(turf/fake_baseturf_type)
  246. var/static/list/created_baseturf_lists = list()
  247. var/turf/current_target
  248. if(fake_baseturf_type)
  249. if(length(fake_baseturf_type)) // We were given a list, just apply it and move on
  250. baseturfs = fake_baseturf_type
  251. return
  252. current_target = fake_baseturf_type
  253. else
  254. if(length(baseturfs))
  255. return // No replacement baseturf has been given and the current baseturfs value is already a list/assembled
  256. if(!baseturfs)
  257. current_target = initial(baseturfs) || type // This should never happen but just in case...
  258. stack_trace("baseturfs var was null for [type]. Failsafe activated and it has been given a new baseturfs value of [current_target].")
  259. else
  260. current_target = baseturfs
  261. // If we've made the output before we don't need to regenerate it
  262. if(created_baseturf_lists[current_target])
  263. var/list/premade_baseturfs = created_baseturf_lists[current_target]
  264. if(length(premade_baseturfs))
  265. baseturfs = premade_baseturfs.Copy()
  266. else
  267. baseturfs = premade_baseturfs
  268. return baseturfs
  269. var/turf/next_target = initial(current_target.baseturfs)
  270. //Most things only have 1 baseturf so this loop won't run in most cases
  271. if(current_target == next_target)
  272. baseturfs = current_target
  273. created_baseturf_lists[current_target] = current_target
  274. return current_target
  275. var/list/new_baseturfs = list(current_target)
  276. for(var/i=0;current_target != next_target;i++)
  277. if(i > 100)
  278. // A baseturfs list over 100 members long is silly
  279. // Because of how this is all structured it will only runtime/message once per type
  280. stack_trace("A turf <[type]> created a baseturfs list over 100 members long. This is most likely an infinite loop.")
  281. message_admins("A turf <[type]> created a baseturfs list over 100 members long. This is most likely an infinite loop.")
  282. break
  283. new_baseturfs.Insert(1, next_target)
  284. current_target = next_target
  285. next_target = initial(current_target.baseturfs)
  286. baseturfs = new_baseturfs
  287. created_baseturf_lists[new_baseturfs[new_baseturfs.len]] = new_baseturfs.Copy()
  288. return new_baseturfs
  289. /turf/proc/levelupdate()
  290. for(var/obj/O in src)
  291. if(O.flags_1 & INITIALIZED_1)
  292. SEND_SIGNAL(O, COMSIG_OBJ_HIDE, intact)
  293. // override for space turfs, since they should never hide anything
  294. /turf/open/space/levelupdate()
  295. return
  296. // Removes all signs of lattice on the pos of the turf -Donkieyo
  297. /turf/proc/RemoveLattice()
  298. var/obj/structure/lattice/L = locate(/obj/structure/lattice, src)
  299. if(L && (L.flags_1 & INITIALIZED_1))
  300. qdel(L)
  301. /turf/proc/Bless()
  302. new /obj/effect/blessing(src)
  303. /turf/storage_contents_dump_act(datum/component/storage/src_object, mob/user)
  304. . = ..()
  305. if(.)
  306. return
  307. if(length(src_object.contents()))
  308. to_chat(usr, "<span class='notice'>You start dumping out the contents...</span>")
  309. if(!do_after(usr,20,target=src_object.parent))
  310. return FALSE
  311. var/list/things = src_object.contents()
  312. var/datum/progressbar/progress = new(user, things.len, src)
  313. while (do_after(usr, 10, TRUE, src, FALSE, CALLBACK(src_object, /datum/component/storage.proc/mass_remove_from_storage, src, things, progress)))
  314. stoplag(1)
  315. progress.end_progress()
  316. return TRUE
  317. //////////////////////////////
  318. //Distance procs
  319. //////////////////////////////
  320. //Distance associates with all directions movement
  321. /turf/proc/Distance(var/turf/T)
  322. return get_dist(src,T)
  323. // This Distance proc assumes that only cardinal movement is
  324. // possible. It results in more efficient (CPU-wise) pathing
  325. // for bots and anything else that only moves in cardinal dirs.
  326. /turf/proc/Distance_cardinal(turf/T)
  327. if(!src || !T)
  328. return FALSE
  329. return abs(x - T.x) + abs(y - T.y)
  330. ////////////////////////////////////////////////////
  331. /turf/singularity_act()
  332. if(intact)
  333. for(var/obj/O in contents) //this is for deleting things like wires contained in the turf
  334. if(O.invisibility == INVISIBILITY_MAXIMUM)
  335. O.singularity_act()
  336. ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
  337. return(2)
  338. /turf/proc/can_have_cabling()
  339. return TRUE
  340. /turf/proc/can_lay_cable()
  341. return can_have_cabling() & !intact
  342. /turf/proc/visibilityChanged()
  343. GLOB.cameranet.updateVisibility(src)
  344. // The cameranet usually handles this for us, but if we've just been
  345. // recreated we should make sure we have the cameranet vis_contents.
  346. var/datum/camerachunk/C = GLOB.cameranet.chunkGenerated(x, y, z)
  347. if(C)
  348. if(C.obscuredTurfs[src])
  349. vis_contents += GLOB.cameranet.vis_contents_objects
  350. else
  351. vis_contents -= GLOB.cameranet.vis_contents_objects
  352. /turf/proc/burn_tile()
  353. /turf/proc/is_shielded()
  354. /turf/contents_explosion(severity, target)
  355. for(var/V in contents)
  356. var/atom/A = V
  357. if(!QDELETED(A))
  358. if(ismovable(A))
  359. var/atom/movable/AM = A
  360. if(!AM.ex_check(explosion_id))
  361. continue
  362. A.ex_act(severity, target)
  363. CHECK_TICK
  364. /turf/narsie_act(force, ignore_mobs, probability = 20)
  365. . = (prob(probability) || force)
  366. for(var/I in src)
  367. var/atom/A = I
  368. if(ignore_mobs && ismob(A))
  369. continue
  370. if(ismob(A) || .)
  371. A.narsie_act()
  372. /turf/proc/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir)
  373. underlay_appearance.icon = icon
  374. underlay_appearance.icon_state = icon_state
  375. underlay_appearance.dir = adjacency_dir
  376. return TRUE
  377. /turf/proc/add_blueprints(var/atom/movable/AM)
  378. var/image/I = new
  379. I.appearance = AM.appearance
  380. I.appearance_flags = RESET_COLOR|RESET_ALPHA|RESET_TRANSFORM
  381. I.loc = src
  382. I.setDir(AM.dir)
  383. I.alpha = 128
  384. LAZYADD(blueprint_data, I)
  385. /turf/proc/add_blueprints_preround(atom/movable/AM)
  386. if(!SSticker.HasRoundStarted())
  387. if(AM.layer == WIRE_LAYER) //wires connect to adjacent positions after its parent init, meaning we need to wait (in this case, until smoothing) to take its image
  388. SSicon_smooth.blueprint_queue += AM
  389. else
  390. add_blueprints(AM)
  391. /turf/proc/is_transition_turf()
  392. return
  393. /turf/acid_act(acidpwr, acid_volume)
  394. . = 1
  395. var/acid_type = /obj/effect/acid
  396. if(acidpwr >= 200) //alien acid power
  397. acid_type = /obj/effect/acid/alien
  398. var/has_acid_effect = FALSE
  399. for(var/obj/O in src)
  400. if(istype(O, acid_type))
  401. var/obj/effect/acid/A = O
  402. A.acid_level = min(acid_volume * acidpwr, 12000)//capping acid level to limit power of the acid
  403. has_acid_effect = 1
  404. continue
  405. O.acid_act(acidpwr, acid_volume)
  406. if(!has_acid_effect)
  407. new acid_type(src, acidpwr, acid_volume)
  408. /turf/proc/acid_melt()
  409. return
  410. /turf/handle_fall(mob/faller)
  411. if(has_gravity(src))
  412. playsound(src, "bodyfall", 50, TRUE)
  413. faller.drop_all_held_items()
  414. /turf/proc/photograph(limit=20)
  415. var/image/I = new()
  416. I.add_overlay(src)
  417. for(var/V in contents)
  418. var/atom/A = V
  419. if(A.invisibility)
  420. continue
  421. I.add_overlay(A)
  422. if(limit)
  423. limit--
  424. else
  425. return I
  426. return I
  427. /turf/AllowDrop()
  428. return TRUE
  429. /turf/proc/add_vomit_floor(mob/living/M, toxvomit = NONE, purge = FALSE)
  430. var/obj/effect/decal/cleanable/vomit/V = new /obj/effect/decal/cleanable/vomit(src, M.get_static_viruses())
  431. //if the vomit combined, apply toxicity and reagents to the old vomit
  432. if (QDELETED(V))
  433. V = locate() in src
  434. if(!V)
  435. return
  436. // Make toxins and blazaam vomit look different
  437. if(toxvomit == VOMIT_PURPLE)
  438. V.icon_state = "vomitpurp_[pick(1,4)]"
  439. else if (toxvomit == VOMIT_TOXIC)
  440. V.icon_state = "vomittox_[pick(1,4)]"
  441. if (iscarbon(M))
  442. var/mob/living/carbon/C = M
  443. if(C.reagents)
  444. clear_reagents_to_vomit_pool(C,V, purge)
  445. /proc/clear_reagents_to_vomit_pool(mob/living/carbon/M, obj/effect/decal/cleanable/vomit/V, purge = FALSE)
  446. var/chemicals_lost = M.reagents.total_volume / 10
  447. if(purge)
  448. chemicals_lost = (2 * M.reagents.total_volume)/3 //For detoxification surgery, we're manually pumping the stomach out of chemcials, so it's far more efficient.
  449. M.reagents.trans_to(V, chemicals_lost, transfered_by = M)
  450. for(var/datum/reagent/R in M.reagents.reagent_list) //clears the stomach of anything that might be digested as food
  451. if(istype(R, /datum/reagent/consumable) || purge)
  452. var/datum/reagent/consumable/nutri_check = R
  453. if(nutri_check.nutriment_factor >0)
  454. M.reagents.remove_reagent(R.type, min(R.volume, 10))
  455. //Whatever happens after high temperature fire dies out or thermite reaction works.
  456. //Should return new turf
  457. /turf/proc/Melt()
  458. return ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
  459. /turf/bullet_act(obj/projectile/P)
  460. . = ..()
  461. if(. != BULLET_ACT_FORCE_PIERCE)
  462. . = BULLET_ACT_TURF
  463. /turf/setDir()
  464. . = ..()
  465. SSdemo.mark_turf(src)