=0) {
+ elem.className = elem.className.replace('visible','hidden');
+ this.className = this.className.replace('open','closed');
+ this.innerHTML = this.innerHTML.replace('-','+');
+ }
+ else {
+ elem.className = elem.className.replace('hidden','visible');
+ this.className = this.className.replace('closed','open');
+ this.innerHTML = this.innerHTML.replace('+','-');
+ }
+ return false;
+ }
+ })(links\[i\]);
+ }
+ }
}
"}
\ No newline at end of file
diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm
index 58eabbdbcf..e9f50d9212 100644
--- a/code/_onclick/click.dm
+++ b/code/_onclick/click.dm
@@ -321,8 +321,7 @@
return
/atom/proc/ShiftClick(mob/user)
SEND_SIGNAL(src, COMSIG_CLICK_SHIFT, user)
- if(user.client && user.client.eye == user || user.client.eye == user.loc)
- user.examinate(src)
+ user.examinate(src)
return
/*
diff --git a/code/_onclick/hud/plane_master.dm b/code/_onclick/hud/plane_master.dm
index 8ee837a202..b2bc05924c 100644
--- a/code/_onclick/hud/plane_master.dm
+++ b/code/_onclick/hud/plane_master.dm
@@ -16,6 +16,28 @@
//Trust me, you need one. Period. If you don't think you do, you're doing something extremely wrong.
/obj/screen/plane_master/proc/backdrop(mob/mymob)
+/obj/screen/plane_master/openspace
+ name = "open space plane master"
+ plane = FLOOR_OPENSPACE_PLANE
+ appearance_flags = PLANE_MASTER
+ blend_mode = BLEND_MULTIPLY
+ alpha = 255
+
+/obj/screen/plane_master/openspace/backdrop(mob/mymob)
+ filters = list()
+ filters += filter(type = "drop_shadow", color = "#04080FAA", size = -10)
+ filters += filter(type = "drop_shadow", color = "#04080FAA", size = -15)
+ filters += filter(type = "drop_shadow", color = "#04080FAA", size = -20)
+
+/obj/screen/plane_master/proc/outline(_size, _color)
+ filters += filter(type = "outline", size = _size, color = _color)
+
+/obj/screen/plane_master/proc/shadow(_size, _offset = 0, _x = 0, _y = 0, _color = "#04080FAA")
+ filters += filter(type = "drop_shadow", x = _x, y = _y, color = _color, size = _size, offset = _offset)
+
+/obj/screen/plane_master/proc/clear_filters()
+ filters = list()
+
/obj/screen/plane_master/floor
name = "floor plane master"
plane = FLOOR_PLANE
diff --git a/code/controllers/configuration/entries/game_options.dm b/code/controllers/configuration/entries/game_options.dm
index 80c179d639..fdddbda344 100644
--- a/code/controllers/configuration/entries/game_options.dm
+++ b/code/controllers/configuration/entries/game_options.dm
@@ -139,6 +139,14 @@
min_val = 0
max_val = 1
+/datum/config_entry/number/suicide_reenter_round_timer
+ config_entry_value = 30
+ min_val = 0
+
+/datum/config_entry/number/roundstart_suicide_time_limit
+ config_entry_value = 30
+ min_val = 0
+
/datum/config_entry/number/shuttle_refuel_delay
config_entry_value = 12000
min_val = 0
diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm
index aab71fd015..a412ad2f48 100644
--- a/code/controllers/configuration/entries/general.dm
+++ b/code/controllers/configuration/entries/general.dm
@@ -141,6 +141,11 @@
/datum/config_entry/flag/load_legacy_ranks_only //Loads admin ranks only from legacy admin_ranks.txt, while enabled ranks are mirrored to the database
protection = CONFIG_ENTRY_LOCKED
+/datum/config_entry/flag/mentors_mobname_only
+
+/datum/config_entry/flag/mentor_legacy_system //Defines whether the server uses the legacy mentor system with mentors.txt or the SQL system
+ protection = CONFIG_ENTRY_LOCKED
+
/datum/config_entry/string/hostedby
/datum/config_entry/flag/norespawn
diff --git a/code/controllers/subsystem/adjacent_air.dm b/code/controllers/subsystem/adjacent_air.dm
new file mode 100644
index 0000000000..8395eda708
--- /dev/null
+++ b/code/controllers/subsystem/adjacent_air.dm
@@ -0,0 +1,35 @@
+SUBSYSTEM_DEF(adjacent_air)
+ name = "Atmos Adjacency"
+ flags = SS_BACKGROUND
+ runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
+ wait = 10
+ priority = FIRE_PRIORITY_ATMOS_ADJACENCY
+ var/list/queue = list()
+
+/datum/controller/subsystem/adjacent_air/stat_entry()
+#ifdef TESTING
+ ..("P:[length(queue)], S:[GLOB.atmos_adjacent_savings[1]], T:[GLOB.atmos_adjacent_savings[2]]")
+#else
+ ..("P:[length(queue)]")
+#endif
+
+/datum/controller/subsystem/adjacent_air/Initialize()
+ while(length(queue))
+ fire(mc_check = FALSE)
+ return ..()
+
+/datum/controller/subsystem/adjacent_air/fire(resumed = FALSE, mc_check = TRUE)
+
+ var/list/queue = src.queue
+
+ while (length(queue))
+ var/turf/currT = queue[1]
+ queue.Cut(1,2)
+
+ currT.ImmediateCalculateAdjacentTurfs()
+
+ if(mc_check)
+ if(MC_TICK_CHECK)
+ break
+ else
+ CHECK_TICK
\ No newline at end of file
diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm
index efa6b8b59d..af7ae825f4 100644
--- a/code/controllers/subsystem/job.dm
+++ b/code/controllers/subsystem/job.dm
@@ -658,6 +658,46 @@ SUBSYSTEM_DEF(job)
message_admins(msg)
CRASH(msg)
+/datum/controller/subsystem/job/proc/equip_loadout(mob/dead/new_player/N, mob/living/M, equipbackpackstuff)
+ var/mob/the_mob = N
+ if(!the_mob)
+ the_mob = M // cause this doesn't get assigned if player is a latejoiner
+ if(the_mob.client && the_mob.client.prefs && (the_mob.client.prefs.chosen_gear && the_mob.client.prefs.chosen_gear.len))
+ if(!ishuman(M))//no silicons allowed
+ return
+ for(var/i in the_mob.client.prefs.chosen_gear)
+ var/datum/gear/G = i
+ G = GLOB.loadout_items[slot_to_string(initial(G.category))][initial(G.name)]
+ if(!G)
+ continue
+ var/permitted = TRUE
+ if(G.restricted_roles && G.restricted_roles.len && !(M.mind.assigned_role in G.restricted_roles))
+ permitted = FALSE
+ if(G.donoritem && !G.donator_ckey_check(the_mob.client.ckey))
+ permitted = FALSE
+ if(!equipbackpackstuff && G.category == SLOT_IN_BACKPACK)//snowflake check since plopping stuff in the backpack doesnt work for pre-job equip loadout stuffs
+ permitted = FALSE
+ if(equipbackpackstuff && G.category != SLOT_IN_BACKPACK)//ditto
+ permitted = FALSE
+ if(!permitted)
+ continue
+ var/obj/item/I = new G.path
+ if(!M.equip_to_slot_if_possible(I, G.category, disable_warning = TRUE, bypass_equip_delay_self = TRUE)) // If the job's dresscode compliant, try to put it in its slot, first
+ if(iscarbon(M))
+ var/mob/living/carbon/C = M
+ var/obj/item/storage/backpack/B = C.back
+ if(!B || !SEND_SIGNAL(B, COMSIG_TRY_STORAGE_INSERT, I, null, TRUE, TRUE)) // Otherwise, try to put it in the backpack, for carbons.
+ I.forceMove(get_turf(C))
+ else if(!M.equip_to_slot_if_possible(I, SLOT_IN_BACKPACK, disable_warning = TRUE, bypass_equip_delay_self = TRUE)) // Otherwise, try to put it in the backpack
+ I.forceMove(get_turf(M)) // If everything fails, just put it on the floor under the mob.
+
+/datum/controller/subsystem/job/proc/FreeRole(rank)
+ if(!rank)
+ return
+ var/datum/job/job = GetJob(rank)
+ if(!job)
+ return FALSE
+ job.current_positions = max(0, job.current_positions - 1)
///////////////////////////////////
//Keeps track of all living heads//
diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm
index dec6cf8466..ceb2fae998 100644
--- a/code/controllers/subsystem/mapping.dm
+++ b/code/controllers/subsystem/mapping.dm
@@ -14,6 +14,7 @@ SUBSYSTEM_DEF(mapping)
var/list/ruins_templates = list()
var/list/space_ruins_templates = list()
var/list/lava_ruins_templates = list()
+ var/datum/space_level/isolated_ruins_z //Created on demand during ruin loading.
var/list/shuttle_templates = list()
var/list/shelter_templates = list()
@@ -25,6 +26,7 @@ SUBSYSTEM_DEF(mapping)
var/list/datum/turf_reservations //list of turf reservations
var/list/used_turfs = list() //list of turf = datum/turf_reservation
+ var/list/reservation_ready = list()
var/clearing_reserved_turfs = FALSE
// Z-manager stuff
@@ -95,7 +97,7 @@ SUBSYSTEM_DEF(mapping)
// Set up Z-level transitions.
setup_map_transitions()
generate_station_area_list()
- initialize_reserved_level()
+ initialize_reserved_level(transit.z_value)
return ..()
/* Nuke threats, for making the blue tiles on the station go RED
@@ -442,7 +444,7 @@ GLOBAL_LIST_EMPTY(the_station_areas)
GLOB.the_gateway.wait = world.time
/datum/controller/subsystem/mapping/proc/RequestBlockReservation(width, height, z, type = /datum/turf_reservation, turf_type_override, border_type_override)
- UNTIL(initialized && !clearing_reserved_turfs)
+ UNTIL(reservation_ready["[z]"] && !clearing_reserved_turfs)
var/datum/turf_reservation/reserve = new type
if(turf_type_override)
reserve.turf_type = turf_type_override
@@ -454,8 +456,9 @@ GLOBAL_LIST_EMPTY(the_station_areas)
return reserve
//If we didn't return at this point, theres a good chance we ran out of room on the exisiting reserved z levels, so lets try a new one
num_of_res_levels += 1
- var/newReserved = add_new_zlevel("Transit/Reserved [num_of_res_levels]", list(ZTRAIT_RESERVED = TRUE))
- if(reserve.Reserve(width, height, newReserved))
+ var/datum/space_level/newReserved = add_new_zlevel("Transit/Reserved [num_of_res_levels]", list(ZTRAIT_RESERVED = TRUE))
+ initialize_reserved_level(newReserved.z_value)
+ if(reserve.Reserve(width, height, newReserved.z_value))
return reserve
else
if(!level_trait(z, ZTRAIT_RESERVED))
@@ -467,19 +470,22 @@ GLOBAL_LIST_EMPTY(the_station_areas)
QDEL_NULL(reserve)
//This is not for wiping reserved levels, use wipe_reservations() for that.
-/datum/controller/subsystem/mapping/proc/initialize_reserved_level()
+/datum/controller/subsystem/mapping/proc/initialize_reserved_level(z)
UNTIL(!clearing_reserved_turfs) //regardless, lets add a check just in case.
clearing_reserved_turfs = TRUE //This operation will likely clear any existing reservations, so lets make sure nothing tries to make one while we're doing it.
- for(var/i in levels_by_trait(ZTRAIT_RESERVED))
- var/turf/A = get_turf(locate(SHUTTLE_TRANSIT_BORDER,SHUTTLE_TRANSIT_BORDER,i))
- var/turf/B = get_turf(locate(world.maxx - SHUTTLE_TRANSIT_BORDER,world.maxy - SHUTTLE_TRANSIT_BORDER,i))
- var/block = block(A, B)
- for(var/t in block)
- // No need to empty() these, because it's world init and they're
- // already /turf/open/space/basic.
- var/turf/T = t
- T.flags_1 |= UNUSED_RESERVATION_TURF_1
- unused_turfs["[i]"] = block
+ if(!level_trait(z,ZTRAIT_RESERVED))
+ clearing_reserved_turfs = FALSE
+ CRASH("Invalid z level prepared for reservations.")
+ var/turf/A = get_turf(locate(SHUTTLE_TRANSIT_BORDER,SHUTTLE_TRANSIT_BORDER,z))
+ var/turf/B = get_turf(locate(world.maxx - SHUTTLE_TRANSIT_BORDER,world.maxy - SHUTTLE_TRANSIT_BORDER,z))
+ var/block = block(A, B)
+ for(var/t in block)
+ // No need to empty() these, because it's world init and they're
+ // already /turf/open/space/basic.
+ var/turf/T = t
+ T.flags_1 |= UNUSED_RESERVATION_TURF_1
+ unused_turfs["[z]"] = block
+ reservation_ready["[z]"] = TRUE
clearing_reserved_turfs = FALSE
/datum/controller/subsystem/mapping/proc/reserve_turfs(list/turfs)
@@ -513,3 +519,9 @@ GLOBAL_LIST_EMPTY(the_station_areas)
for(var/B in areas)
var/area/A = B
A.reg_in_areas_in_z()
+
+/datum/controller/subsystem/mapping/proc/get_isolated_ruin_z()
+ if(!isolated_ruins_z)
+ isolated_ruins_z = add_new_zlevel("Isolated Ruins/Reserved", list(ZTRAIT_RESERVED = TRUE, ZTRAIT_ISOLATED_RUINS = TRUE))
+ initialize_reserved_level(isolated_ruins_z.z_value)
+ return isolated_ruins_z.z_value
\ No newline at end of file
diff --git a/code/controllers/subsystem/pai.dm b/code/controllers/subsystem/pai.dm
index 2e2f7edd99..18667053d4 100644
--- a/code/controllers/subsystem/pai.dm
+++ b/code/controllers/subsystem/pai.dm
@@ -69,6 +69,10 @@ SUBSYSTEM_DEF(pai)
candidate.comments = copytext(sanitize(candidate.comments),1,MAX_MESSAGE_LEN)
if("submit")
+ if(isobserver(usr))
+ var/mob/dead/observer/O = usr
+ if(!O.can_reenter_round())
+ return FALSE
if(candidate)
candidate.ready = 1
for(var/obj/item/paicard/p in pai_card_list)
@@ -148,6 +152,8 @@ SUBSYSTEM_DEF(pai)
continue
if(!(ROLE_PAI in G.client.prefs.be_special))
continue
+ if(!G.can_reenter_round()) // this should use notify_ghosts() instead one day.
+ return FALSE
to_chat(G, "[user] is requesting a pAI personality! Use the pAI button to submit yourself as one.")
addtimer(CALLBACK(src, .proc/spam_again), spam_delay)
var/list/available = list()
diff --git a/code/controllers/subsystem/research.dm b/code/controllers/subsystem/research.dm
index 05c76619d9..ab27658efd 100644
--- a/code/controllers/subsystem/research.dm
+++ b/code/controllers/subsystem/research.dm
@@ -4,24 +4,32 @@ SUBSYSTEM_DEF(research)
priority = FIRE_PRIORITY_RESEARCH
wait = 10
init_order = INIT_ORDER_RESEARCH
+ //TECHWEB STATIC
+ var/list/techweb_nodes = list() //associative id = node datum
+ var/list/techweb_designs = list() //associative id = node datum
+ var/list/datum/techweb/techwebs = list()
+ var/datum/techweb/science/science_tech
+ var/datum/techweb/admin/admin_tech
+ var/datum/techweb_node/error_node/error_node //These two are what you get if a node/design is deleted and somehow still stored in a console.
+ var/datum/design/error_design/error_design
+
+ //ERROR LOGGING
var/list/invalid_design_ids = list() //associative id = number of times
var/list/invalid_node_ids = list() //associative id = number of times
var/list/invalid_node_boost = list() //associative id = error message
+
var/list/obj/machinery/rnd/server/servers = list()
- var/datum/techweb/science/science_tech
- var/datum/techweb/admin/admin_tech
- var/list/techweb_nodes = list() //associative id = node datum
- var/list/techweb_categories = list() //category name = list(node.id = node)
- var/list/techweb_designs = list() //associative id = node datum
- var/list/techweb_nodes_starting = list() //associative id = node datum
+
+ var/list/techweb_nodes_starting = list() //associative id = TRUE
+ var/list/techweb_categories = list() //category name = list(node.id = TRUE)
var/list/techweb_boost_items = list() //associative double-layer path = list(id = list(point_type = point_discount))
- var/list/techweb_nodes_hidden = list() //Nodes that should be hidden by default.
+ var/list/techweb_nodes_hidden = list() //Node ids that should be hidden by default.
var/list/techweb_point_items = list( //path = list(point type = value)
/obj/item/assembly/signaler/anomaly = list(TECHWEB_POINT_TYPE_GENERIC = 2500),
/obj/item/assembly/signaler/anomaly = list(TECHWEB_POINT_TYPE_GENERIC = 5000), // Cit three more anomalys anomalys
/obj/item/assembly/signaler/anomaly = list(TECHWEB_POINT_TYPE_GENERIC = 7500),
/obj/item/assembly/signaler/anomaly = list(TECHWEB_POINT_TYPE_GENERIC = 10000),
- // - Slime Extracts! -
+ // - Slime Extracts! -
/obj/item/slime_extract/grey = list(TECHWEB_POINT_TYPE_GENERIC = 500), // Adds in slime core deconing
/obj/item/slime_extract/metal = list(TECHWEB_POINT_TYPE_GENERIC = 750),
/obj/item/slime_extract/purple = list(TECHWEB_POINT_TYPE_GENERIC = 750),
@@ -294,6 +302,8 @@ SUBSYSTEM_DEF(research)
science_tech = new /datum/techweb/science
admin_tech = new /datum/techweb/admin
autosort_categories()
+ error_design = new
+ error_node = new
return ..()
/datum/controller/subsystem/research/fire()
@@ -330,6 +340,191 @@ SUBSYSTEM_DEF(research)
for(var/i in techweb_nodes)
var/datum/techweb_node/I = techweb_nodes[i]
if(techweb_categories[I.category])
- techweb_categories[I.category][I.id] = I
+ techweb_categories[I.category][I.id] = TRUE
else
- techweb_categories[I.category] = list(I.id = I)
+ techweb_categories[I.category] = list(I.id = TRUE)
+
+/datum/controller/subsystem/research/proc/techweb_node_by_id(id)
+ return techweb_nodes[id] || error_node
+
+/datum/controller/subsystem/research/proc/techweb_design_by_id(id)
+ return techweb_designs[id] || error_design
+
+/datum/controller/subsystem/research/proc/on_design_deletion(datum/design/D)
+ for(var/i in techweb_nodes)
+ var/datum/techweb_node/TN = techwebs[i]
+ TN.on_design_deletion(TN)
+ for(var/i in techwebs)
+ var/datum/techweb/T = i
+ T.recalculate_nodes(TRUE)
+
+/datum/controller/subsystem/research/proc/on_node_deletion(datum/techweb_node/TN)
+ for(var/i in techweb_nodes)
+ var/datum/techweb_node/TN2 = techwebs[i]
+ TN2.on_node_deletion(TN)
+ for(var/i in techwebs)
+ var/datum/techweb/T = i
+ T.recalculate_nodes(TRUE)
+
+/datum/controller/subsystem/research/proc/initialize_all_techweb_nodes(clearall = FALSE)
+ if(islist(techweb_nodes) && clearall)
+ QDEL_LIST(techweb_nodes)
+ if(islist(techweb_nodes_starting && clearall))
+ techweb_nodes_starting.Cut()
+ var/list/returned = list()
+ for(var/path in subtypesof(/datum/techweb_node))
+ var/datum/techweb_node/TN = path
+ if(isnull(initial(TN.id)))
+ continue
+ TN = new path
+ if(returned[initial(TN.id)])
+ stack_trace("WARNING: Techweb node ID clash with ID [initial(TN.id)] detected! Path: [path]")
+ errored_datums[TN] = initial(TN.id)
+ continue
+ returned[initial(TN.id)] = TN
+ if(TN.starting_node)
+ techweb_nodes_starting[TN.id] = TRUE
+ for(var/id in techweb_nodes)
+ var/datum/techweb_node/TN = techweb_nodes[id]
+ TN.Initialize()
+ techweb_nodes = returned
+ if (!verify_techweb_nodes()) //Verify all nodes have ids and such.
+ stack_trace("Invalid techweb nodes detected")
+ calculate_techweb_nodes()
+ calculate_techweb_boost_list()
+ if (!verify_techweb_nodes()) //Verify nodes and designs have been crosslinked properly.
+ CRASH("Invalid techweb nodes detected")
+
+/datum/controller/subsystem/research/proc/initialize_all_techweb_designs(clearall = FALSE)
+ if(islist(techweb_designs) && clearall)
+ QDEL_LIST(techweb_designs)
+ var/list/returned = list()
+ for(var/path in subtypesof(/datum/design))
+ var/datum/design/DN = path
+ if(isnull(initial(DN.id)))
+ stack_trace("WARNING: Design with null ID detected. Build path: [initial(DN.build_path)]")
+ continue
+ else if(initial(DN.id) == DESIGN_ID_IGNORE)
+ continue
+ DN = new path
+ if(returned[initial(DN.id)])
+ stack_trace("WARNING: Design ID clash with ID [initial(DN.id)] detected! Path: [path]")
+ errored_datums[DN] = initial(DN.id)
+ continue
+ returned[initial(DN.id)] = DN
+ techweb_designs = returned
+ verify_techweb_designs()
+
+/datum/controller/subsystem/research/proc/verify_techweb_nodes()
+ . = TRUE
+ for(var/n in techweb_nodes)
+ var/datum/techweb_node/N = techweb_nodes[n]
+ if(!istype(N))
+ WARNING("Invalid research node with ID [n] detected and removed.")
+ techweb_nodes -= n
+ research_node_id_error(n)
+ . = FALSE
+ for(var/p in N.prereq_ids)
+ var/datum/techweb_node/P = techweb_nodes[p]
+ if(!istype(P))
+ WARNING("Invalid research prerequisite node with ID [p] detected in node [N.display_name]\[[N.id]\] removed.")
+ N.prereq_ids -= p
+ research_node_id_error(p)
+ . = FALSE
+ for(var/d in N.design_ids)
+ var/datum/design/D = techweb_designs[d]
+ if(!istype(D))
+ WARNING("Invalid research design with ID [d] detected in node [N.display_name]\[[N.id]\] removed.")
+ N.design_ids -= d
+ design_id_error(d)
+ . = FALSE
+ for(var/u in N.unlock_ids)
+ var/datum/techweb_node/U = techweb_nodes[u]
+ if(!istype(U))
+ WARNING("Invalid research unlock node with ID [u] detected in node [N.display_name]\[[N.id]\] removed.")
+ N.unlock_ids -= u
+ research_node_id_error(u)
+ . = FALSE
+ for(var/p in N.boost_item_paths)
+ if(!ispath(p))
+ N.boost_item_paths -= p
+ WARNING("[p] is not a valid path.")
+ node_boost_error(N.id, "[p] is not a valid path.")
+ . = FALSE
+ var/list/points = N.boost_item_paths[p]
+ if(islist(points))
+ for(var/i in points)
+ if(!isnum(points[i]))
+ WARNING("[points[i]] is not a valid number.")
+ node_boost_error(N.id, "[points[i]] is not a valid number.")
+ . = FALSE
+ else if(!point_types[i])
+ WARNING("[i] is not a valid point type.")
+ node_boost_error(N.id, "[i] is not a valid point type.")
+ . = FALSE
+ else if(!isnull(points))
+ N.boost_item_paths -= p
+ node_boost_error(N.id, "No valid list.")
+ WARNING("No valid list.")
+ . = FALSE
+ CHECK_TICK
+
+/datum/controller/subsystem/research/proc/verify_techweb_designs()
+ for(var/d in techweb_designs)
+ var/datum/design/D = techweb_designs[d]
+ if(!istype(D))
+ stack_trace("WARNING: Invalid research design with ID [d] detected and removed.")
+ techweb_designs -= d
+ CHECK_TICK
+
+/datum/controller/subsystem/research/proc/research_node_id_error(id)
+ if(invalid_node_ids[id])
+ invalid_node_ids[id]++
+ else
+ invalid_node_ids[id] = 1
+
+/datum/controller/subsystem/research/proc/design_id_error(id)
+ if(invalid_design_ids[id])
+ invalid_design_ids[id]++
+ else
+ invalid_design_ids[id] = 1
+
+/datum/controller/subsystem/research/proc/calculate_techweb_nodes()
+ for(var/design_id in techweb_designs)
+ var/datum/design/D = techweb_designs[design_id]
+ D.unlocked_by.Cut()
+ for(var/node_id in techweb_nodes)
+ var/datum/techweb_node/node = techweb_nodes[node_id]
+ node.unlock_ids = list()
+ for(var/i in node.design_ids)
+ var/datum/design/D = techweb_designs[i]
+ node.design_ids[i] = TRUE
+ D.unlocked_by += node.id
+ if(node.hidden)
+ techweb_nodes_hidden[node.id] = TRUE
+ CHECK_TICK
+ generate_techweb_unlock_linking()
+
+/datum/controller/subsystem/research/proc/generate_techweb_unlock_linking()
+ for(var/node_id in techweb_nodes) //Clear all unlock links to avoid duplication.
+ var/datum/techweb_node/node = techweb_nodes[node_id]
+ node.unlock_ids = list()
+ for(var/node_id in techweb_nodes)
+ var/datum/techweb_node/node = techweb_nodes[node_id]
+ for(var/prereq_id in node.prereq_ids)
+ var/datum/techweb_node/prereq_node = techweb_node_by_id(prereq_id)
+ prereq_node.unlock_ids[node.id] = node
+
+/datum/controller/subsystem/research/proc/calculate_techweb_boost_list(clearall = FALSE)
+ if(clearall)
+ techweb_boost_items = list()
+ for(var/node_id in techweb_nodes)
+ var/datum/techweb_node/node = techweb_nodes[node_id]
+ for(var/path in node.boost_item_paths)
+ if(!ispath(path))
+ continue
+ if(length(techweb_boost_items[path]))
+ techweb_boost_items[path][node.id] = node.boost_item_paths[path]
+ else
+ techweb_boost_items[path] = list(node.id = node.boost_item_paths[path])
+ CHECK_TICK
diff --git a/code/controllers/subsystem/shuttle.dm b/code/controllers/subsystem/shuttle.dm
index 51a2761f74..fe40b3f01f 100644
--- a/code/controllers/subsystem/shuttle.dm
+++ b/code/controllers/subsystem/shuttle.dm
@@ -638,3 +638,11 @@ SUBSYSTEM_DEF(shuttle)
C.update_hidden_docking_ports(remove_images, add_images)
QDEL_LIST(remove_images)
+
+/datum/controller/subsystem/shuttle/proc/autoEnd() //CIT CHANGE - allows shift to end after 2 hours have passed.
+ if((world.realtime - SSshuttle.realtimeofstart) > auto_call && EMERGENCY_IDLE_OR_RECALLED) //2 hours
+ SSshuttle.emergency.request(silent = TRUE)
+ priority_announce("The shift has come to an end and the shuttle called. [seclevel2num(get_security_level()) == SEC_LEVEL_RED ? "Red Alert state confirmed: Dispatching priority shuttle. " : "" ]It will arrive in [emergency.timeLeft(600)] minutes.", null, "shuttlecalled", "Priority")
+ log_game("Round time limit reached. Shuttle has been auto-called.")
+ message_admins("Round time limit reached. Shuttle called.")
+ emergencyNoRecall = TRUE
diff --git a/code/controllers/subsystem/throwing.dm b/code/controllers/subsystem/throwing.dm
index 4b91944ec8..48da15f67e 100644
--- a/code/controllers/subsystem/throwing.dm
+++ b/code/controllers/subsystem/throwing.dm
@@ -147,6 +147,11 @@ SUBSYSTEM_DEF(throwing)
if (callback)
callback.Invoke()
+ if(!thrownthing.zfalling) // I don't think you can zfall while thrown but hey, just in case.
+ var/turf/T = get_turf(thrownthing)
+ if(T && thrownthing.has_gravity(T))
+ T.zFall(thrownthing)
+
qdel(src)
/datum/thrownthing/proc/hit_atom(atom/A)
diff --git a/code/controllers/subsystem/traumas.dm b/code/controllers/subsystem/traumas.dm
index a525eea0b2..33798d74f7 100644
--- a/code/controllers/subsystem/traumas.dm
+++ b/code/controllers/subsystem/traumas.dm
@@ -40,7 +40,7 @@ SUBSYSTEM_DEF(traumas)
"cats" = strings(PHOBIA_FILE, "cats"),
"syndicate"= strings(PHOBIA_FILE, "syndicate"),
"eye" = strings(PHOBIA_FILE, "eye")
- )
+ )
phobia_mobs = list("spiders" = typecacheof(list(/mob/living/simple_animal/hostile/poison/giant_spider)),
"security" = typecacheof(list(/mob/living/simple_animal/bot/secbot, /mob/living/simple_animal/bot/ed209)),
diff --git a/code/controllers/subsystem/weather.dm b/code/controllers/subsystem/weather.dm
index 2391b4c839..5e44f2e4c3 100644
--- a/code/controllers/subsystem/weather.dm
+++ b/code/controllers/subsystem/weather.dm
@@ -74,10 +74,10 @@ SUBSYSTEM_DEF(weather)
next_hit_by_zlevel["[z]"] = null
/datum/controller/subsystem/weather/proc/get_weather(z, area/active_area)
- var/datum/weather/A
- for(var/V in processing)
- var/datum/weather/W = V
- if((z in W.impacted_z_levels) && W.area_type == active_area.type)
- A = W
- break
- return A
+ var/datum/weather/A
+ for(var/V in processing)
+ var/datum/weather/W = V
+ if((z in W.impacted_z_levels) && W.area_type == active_area.type)
+ A = W
+ break
+ return A
diff --git a/code/datums/armor.dm b/code/datums/armor.dm
index cbf4b76c60..85915395f8 100644
--- a/code/datums/armor.dm
+++ b/code/datums/armor.dm
@@ -1,70 +1,70 @@
#define ARMORID "armor-[melee]-[bullet]-[laser]-[energy]-[bomb]-[bio]-[rad]-[fire]-[acid]-[magic]"
/proc/getArmor(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0, magic = 0)
- . = locate(ARMORID)
- if (!.)
- . = new /datum/armor(melee, bullet, laser, energy, bomb, bio, rad, fire, acid, magic)
+ . = locate(ARMORID)
+ if (!.)
+ . = new /datum/armor(melee, bullet, laser, energy, bomb, bio, rad, fire, acid, magic)
/datum/armor
- datum_flags = DF_USE_TAG
- var/melee
- var/bullet
- var/laser
- var/energy
- var/bomb
- var/bio
- var/rad
- var/fire
- var/acid
- var/magic
+ datum_flags = DF_USE_TAG
+ var/melee
+ var/bullet
+ var/laser
+ var/energy
+ var/bomb
+ var/bio
+ var/rad
+ var/fire
+ var/acid
+ var/magic
/datum/armor/New(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0, magic = 0)
- src.melee = melee
- src.bullet = bullet
- src.laser = laser
- src.energy = energy
- src.bomb = bomb
- src.bio = bio
- src.rad = rad
- src.fire = fire
- src.acid = acid
- src.magic = magic
- tag = ARMORID
+ src.melee = melee
+ src.bullet = bullet
+ src.laser = laser
+ src.energy = energy
+ src.bomb = bomb
+ src.bio = bio
+ src.rad = rad
+ src.fire = fire
+ src.acid = acid
+ src.magic = magic
+ tag = ARMORID
/datum/armor/proc/modifyRating(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0, magic = 0)
- return getArmor(src.melee+melee, src.bullet+bullet, src.laser+laser, src.energy+energy, src.bomb+bomb, src.bio+bio, src.rad+rad, src.fire+fire, src.acid+acid, src.magic+magic)
+ return getArmor(src.melee+melee, src.bullet+bullet, src.laser+laser, src.energy+energy, src.bomb+bomb, src.bio+bio, src.rad+rad, src.fire+fire, src.acid+acid, src.magic+magic)
/datum/armor/proc/modifyAllRatings(modifier = 0)
- return getArmor(melee+modifier, bullet+modifier, laser+modifier, energy+modifier, bomb+modifier, bio+modifier, rad+modifier, fire+modifier, acid+modifier, magic+modifier)
+ return getArmor(melee+modifier, bullet+modifier, laser+modifier, energy+modifier, bomb+modifier, bio+modifier, rad+modifier, fire+modifier, acid+modifier, magic+modifier)
/datum/armor/proc/setRating(melee, bullet, laser, energy, bomb, bio, rad, fire, acid, magic)
- return getArmor((isnull(melee) ? src.melee : melee),\
- (isnull(bullet) ? src.bullet : bullet),\
- (isnull(laser) ? src.laser : laser),\
- (isnull(energy) ? src.energy : energy),\
- (isnull(bomb) ? src.bomb : bomb),\
- (isnull(bio) ? src.bio : bio),\
- (isnull(rad) ? src.rad : rad),\
- (isnull(fire) ? src.fire : fire),\
- (isnull(acid) ? src.acid : acid),\
- (isnull(magic) ? src.magic : magic))
+ return getArmor((isnull(melee) ? src.melee : melee),\
+ (isnull(bullet) ? src.bullet : bullet),\
+ (isnull(laser) ? src.laser : laser),\
+ (isnull(energy) ? src.energy : energy),\
+ (isnull(bomb) ? src.bomb : bomb),\
+ (isnull(bio) ? src.bio : bio),\
+ (isnull(rad) ? src.rad : rad),\
+ (isnull(fire) ? src.fire : fire),\
+ (isnull(acid) ? src.acid : acid),\
+ (isnull(magic) ? src.magic : magic))
/datum/armor/proc/getRating(rating)
- return vars[rating]
+ return vars[rating]
/datum/armor/proc/getList()
- return list("melee" = melee, "bullet" = bullet, "laser" = laser, "energy" = energy, "bomb" = bomb, "bio" = bio, "rad" = rad, "fire" = fire, "acid" = acid, "magic" = magic)
+ return list("melee" = melee, "bullet" = bullet, "laser" = laser, "energy" = energy, "bomb" = bomb, "bio" = bio, "rad" = rad, "fire" = fire, "acid" = acid, "magic" = magic)
/datum/armor/proc/attachArmor(datum/armor/AA)
- return getArmor(melee+AA.melee, bullet+AA.bullet, laser+AA.laser, energy+AA.energy, bomb+AA.bomb, bio+AA.bio, rad+AA.rad, fire+AA.fire, acid+AA.acid, magic+AA.magic)
+ return getArmor(melee+AA.melee, bullet+AA.bullet, laser+AA.laser, energy+AA.energy, bomb+AA.bomb, bio+AA.bio, rad+AA.rad, fire+AA.fire, acid+AA.acid, magic+AA.magic)
/datum/armor/proc/detachArmor(datum/armor/AA)
- return getArmor(melee-AA.melee, bullet-AA.bullet, laser-AA.laser, energy-AA.energy, bomb-AA.bomb, bio-AA.bio, rad-AA.rad, fire-AA.fire, acid-AA.acid, magic-AA.magic)
+ return getArmor(melee-AA.melee, bullet-AA.bullet, laser-AA.laser, energy-AA.energy, bomb-AA.bomb, bio-AA.bio, rad-AA.rad, fire-AA.fire, acid-AA.acid, magic-AA.magic)
/datum/armor/vv_edit_var(var_name, var_value)
- if (var_name == NAMEOF(src, tag))
- return FALSE
- . = ..()
- tag = ARMORID // update tag in case armor values were edited
+ if (var_name == NAMEOF(src, tag))
+ return FALSE
+ . = ..()
+ tag = ARMORID // update tag in case armor values were edited
#undef ARMORID
diff --git a/code/datums/components/mood.dm b/code/datums/components/mood.dm
index a3a9d96a55..b46919a6c6 100644
--- a/code/datums/components/mood.dm
+++ b/code/datums/components/mood.dm
@@ -125,6 +125,9 @@
screen_obj.icon_state = "mood[mood_level]"
/datum/component/mood/process() //Called on SSmood process
+ if(QDELETED(parent)) // workaround to an obnoxious sneaky periodical runtime.
+ qdel(src)
+ return
var/mob/living/owner = parent
switch(mood_level)
@@ -249,7 +252,7 @@
RegisterSignal(screen_obj, COMSIG_CLICK, .proc/hud_click)
/datum/component/mood/proc/unmodify_hud(datum/source)
- if(!screen_obj)
+ if(!screen_obj || !parent)
return
var/mob/living/owner = parent
var/datum/hud/hud = owner.hud_used
diff --git a/code/datums/components/riding.dm b/code/datums/components/riding.dm
index dcdbd5ca4b..c05b07a693 100644
--- a/code/datums/components/riding.dm
+++ b/code/datums/components/riding.dm
@@ -310,10 +310,10 @@
if(!user.put_in_hands(inhand, TRUE))
qdel(inhand) // it isn't going to be added to offhands anyway
break
- LAZYADD(equipped, src)
+ LAZYADD(equipped, inhand)
var/amount_equipped = LAZYLEN(equipped)
if(amount_equipped)
- LAZYADD(offhands[L], amount_equipped)
+ LAZYADD(offhands[L], equipped)
if(amount_equipped >= amount_required)
return TRUE
unequip_buckle_inhands(L)
diff --git a/code/datums/components/waddling.dm b/code/datums/components/waddling.dm
index a1f538e4dd..f09a92c91c 100644
--- a/code/datums/components/waddling.dm
+++ b/code/datums/components/waddling.dm
@@ -1,15 +1,15 @@
/datum/component/waddling
- dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
+ dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
/datum/component/waddling/Initialize()
- if(!isliving(parent))
- return COMPONENT_INCOMPATIBLE
- RegisterSignal(parent, list(COMSIG_MOVABLE_MOVED), .proc/Waddle)
+ if(!isliving(parent))
+ return COMPONENT_INCOMPATIBLE
+ RegisterSignal(parent, list(COMSIG_MOVABLE_MOVED), .proc/Waddle)
/datum/component/waddling/proc/Waddle()
- var/mob/living/L = parent
- if(L.incapacitated() || L.lying)
- return
- animate(L, pixel_z = 4, time = 0)
- animate(pixel_z = 0, transform = turn(matrix(), pick(-12, 0, 12)), time=2)
- animate(pixel_z = 0, transform = matrix(), time = 0)
+ var/mob/living/L = parent
+ if(L.incapacitated() || L.lying)
+ return
+ animate(L, pixel_z = 4, time = 0)
+ animate(pixel_z = 0, transform = turn(matrix(), pick(-12, 0, 12)), time=2)
+ animate(pixel_z = 0, transform = matrix(), time = 0)
diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm
index cfbd8be32c..e44134f01f 100644
--- a/code/datums/datumvars.dm
+++ b/code/datums/datumvars.dm
@@ -875,14 +875,14 @@
return
// text2num conveniently returns a null on invalid values
O.armor = O.armor.setRating(melee = text2num(result["values"]["melee"]),\
- bullet = text2num(result["values"]["bullet"]),\
- laser = text2num(result["values"]["laser"]),\
- energy = text2num(result["values"]["energy"]),\
- bomb = text2num(result["values"]["bomb"]),\
- bio = text2num(result["values"]["bio"]),\
- rad = text2num(result["values"]["rad"]),\
- fire = text2num(result["values"]["fire"]),\
- acid = text2num(result["values"]["acid"]))
+ bullet = text2num(result["values"]["bullet"]),\
+ laser = text2num(result["values"]["laser"]),\
+ energy = text2num(result["values"]["energy"]),\
+ bomb = text2num(result["values"]["bomb"]),\
+ bio = text2num(result["values"]["bio"]),\
+ rad = text2num(result["values"]["rad"]),\
+ fire = text2num(result["values"]["fire"]),\
+ acid = text2num(result["values"]["acid"]))
log_admin("[key_name(usr)] modified the armor on [O] ([O.type]) to melee: [O.armor.melee], bullet: [O.armor.bullet], laser: [O.armor.laser], energy: [O.armor.energy], bomb: [O.armor.bomb], bio: [O.armor.bio], rad: [O.armor.rad], fire: [O.armor.fire], acid: [O.armor.acid]")
message_admins("[key_name_admin(usr)] modified the armor on [O] ([O.type]) to melee: [O.armor.melee], bullet: [O.armor.bullet], laser: [O.armor.laser], energy: [O.armor.energy], bomb: [O.armor.bomb], bio: [O.armor.bio], rad: [O.armor.rad], fire: [O.armor.fire], acid: [O.armor.acid]")
else
diff --git a/code/datums/embedding_behavior.dm b/code/datums/embedding_behavior.dm
index f631b9b1c4..a261ea48c6 100644
--- a/code/datums/embedding_behavior.dm
+++ b/code/datums/embedding_behavior.dm
@@ -1,53 +1,53 @@
#define EMBEDID "embed-[embed_chance]-[embedded_fall_chance]-[embedded_pain_chance]-[embedded_pain_multiplier]-[embedded_fall_pain_multiplier]-[embedded_impact_pain_multiplier]-[embedded_unsafe_removal_pain_multiplier]-[embedded_unsafe_removal_time]"
/proc/getEmbeddingBehavior(embed_chance = EMBED_CHANCE,
- embedded_fall_chance = EMBEDDED_ITEM_FALLOUT,
- embedded_pain_chance = EMBEDDED_PAIN_CHANCE,
- embedded_pain_multiplier = EMBEDDED_PAIN_MULTIPLIER,
- embedded_fall_pain_multiplier = EMBEDDED_FALL_PAIN_MULTIPLIER,
- embedded_impact_pain_multiplier = EMBEDDED_IMPACT_PAIN_MULTIPLIER,
- embedded_unsafe_removal_pain_multiplier = EMBEDDED_UNSAFE_REMOVAL_PAIN_MULTIPLIER,
- embedded_unsafe_removal_time = EMBEDDED_UNSAFE_REMOVAL_TIME)
- . = locate(EMBEDID)
- if (!.)
- . = new /datum/embedding_behavior(embed_chance, embedded_fall_chance, embedded_pain_chance, embedded_pain_multiplier, embedded_fall_pain_multiplier, embedded_impact_pain_multiplier, embedded_unsafe_removal_pain_multiplier, embedded_unsafe_removal_time)
+ embedded_fall_chance = EMBEDDED_ITEM_FALLOUT,
+ embedded_pain_chance = EMBEDDED_PAIN_CHANCE,
+ embedded_pain_multiplier = EMBEDDED_PAIN_MULTIPLIER,
+ embedded_fall_pain_multiplier = EMBEDDED_FALL_PAIN_MULTIPLIER,
+ embedded_impact_pain_multiplier = EMBEDDED_IMPACT_PAIN_MULTIPLIER,
+ embedded_unsafe_removal_pain_multiplier = EMBEDDED_UNSAFE_REMOVAL_PAIN_MULTIPLIER,
+ embedded_unsafe_removal_time = EMBEDDED_UNSAFE_REMOVAL_TIME)
+ . = locate(EMBEDID)
+ if (!.)
+ . = new /datum/embedding_behavior(embed_chance, embedded_fall_chance, embedded_pain_chance, embedded_pain_multiplier, embedded_fall_pain_multiplier, embedded_impact_pain_multiplier, embedded_unsafe_removal_pain_multiplier, embedded_unsafe_removal_time)
/datum/embedding_behavior
- var/embed_chance
- var/embedded_fall_chance
- var/embedded_pain_chance
- var/embedded_pain_multiplier //The coefficient of multiplication for the damage this item does while embedded (this*w_class)
- var/embedded_fall_pain_multiplier //The coefficient of multiplication for the damage this item does when falling out of a limb (this*w_class)
- var/embedded_impact_pain_multiplier //The coefficient of multiplication for the damage this item does when first embedded (this*w_class)
- var/embedded_unsafe_removal_pain_multiplier //The coefficient of multiplication for the damage removing this without surgery causes (this*w_class)
- var/embedded_unsafe_removal_time //A time in ticks, multiplied by the w_class.
+ var/embed_chance
+ var/embedded_fall_chance
+ var/embedded_pain_chance
+ var/embedded_pain_multiplier //The coefficient of multiplication for the damage this item does while embedded (this*w_class)
+ var/embedded_fall_pain_multiplier //The coefficient of multiplication for the damage this item does when falling out of a limb (this*w_class)
+ var/embedded_impact_pain_multiplier //The coefficient of multiplication for the damage this item does when first embedded (this*w_class)
+ var/embedded_unsafe_removal_pain_multiplier //The coefficient of multiplication for the damage removing this without surgery causes (this*w_class)
+ var/embedded_unsafe_removal_time //A time in ticks, multiplied by the w_class.
/datum/embedding_behavior/New(embed_chance = EMBED_CHANCE,
- embedded_fall_chance = EMBEDDED_ITEM_FALLOUT,
- embedded_pain_chance = EMBEDDED_PAIN_CHANCE,
- embedded_pain_multiplier = EMBEDDED_PAIN_MULTIPLIER,
- embedded_fall_pain_multiplier = EMBEDDED_FALL_PAIN_MULTIPLIER,
- embedded_impact_pain_multiplier = EMBEDDED_IMPACT_PAIN_MULTIPLIER,
- embedded_unsafe_removal_pain_multiplier = EMBEDDED_UNSAFE_REMOVAL_PAIN_MULTIPLIER,
- embedded_unsafe_removal_time = EMBEDDED_UNSAFE_REMOVAL_TIME)
- src.embed_chance = embed_chance
- src.embedded_fall_chance = embedded_fall_chance
- src.embedded_pain_chance = embedded_pain_chance
- src.embedded_pain_multiplier = embedded_pain_multiplier
- src.embedded_fall_pain_multiplier = embedded_fall_pain_multiplier
- src.embedded_impact_pain_multiplier = embedded_impact_pain_multiplier
- src.embedded_unsafe_removal_pain_multiplier = embedded_unsafe_removal_pain_multiplier
- src.embedded_unsafe_removal_time = embedded_unsafe_removal_time
- tag = EMBEDID
+ embedded_fall_chance = EMBEDDED_ITEM_FALLOUT,
+ embedded_pain_chance = EMBEDDED_PAIN_CHANCE,
+ embedded_pain_multiplier = EMBEDDED_PAIN_MULTIPLIER,
+ embedded_fall_pain_multiplier = EMBEDDED_FALL_PAIN_MULTIPLIER,
+ embedded_impact_pain_multiplier = EMBEDDED_IMPACT_PAIN_MULTIPLIER,
+ embedded_unsafe_removal_pain_multiplier = EMBEDDED_UNSAFE_REMOVAL_PAIN_MULTIPLIER,
+ embedded_unsafe_removal_time = EMBEDDED_UNSAFE_REMOVAL_TIME)
+ src.embed_chance = embed_chance
+ src.embedded_fall_chance = embedded_fall_chance
+ src.embedded_pain_chance = embedded_pain_chance
+ src.embedded_pain_multiplier = embedded_pain_multiplier
+ src.embedded_fall_pain_multiplier = embedded_fall_pain_multiplier
+ src.embedded_impact_pain_multiplier = embedded_impact_pain_multiplier
+ src.embedded_unsafe_removal_pain_multiplier = embedded_unsafe_removal_pain_multiplier
+ src.embedded_unsafe_removal_time = embedded_unsafe_removal_time
+ tag = EMBEDID
/datum/embedding_behavior/proc/setRating(embed_chance, embedded_fall_chance, embedded_pain_chance, embedded_pain_multiplier, embedded_fall_pain_multiplier, embedded_impact_pain_multiplier, embedded_unsafe_removal_pain_multiplier, embedded_unsafe_removal_time)
- return getEmbeddingBehavior((isnull(embed_chance) ? src.embed_chance : embed_chance),\
- (isnull(embedded_fall_chance) ? src.embedded_fall_chance : embedded_fall_chance),\
- (isnull(embedded_pain_chance) ? src.embedded_pain_chance : embedded_pain_chance),\
- (isnull(embedded_pain_multiplier) ? src.embedded_pain_multiplier : embedded_pain_multiplier),\
- (isnull(embedded_fall_pain_multiplier) ? src.embedded_fall_pain_multiplier : embedded_fall_pain_multiplier),\
- (isnull(embedded_impact_pain_multiplier) ? src.embedded_impact_pain_multiplier : embedded_impact_pain_multiplier),\
- (isnull(embedded_unsafe_removal_pain_multiplier) ? src.embedded_unsafe_removal_pain_multiplier : embedded_unsafe_removal_pain_multiplier),\
- (isnull(embedded_unsafe_removal_time) ? src.embedded_unsafe_removal_time : embedded_unsafe_removal_time))
+ return getEmbeddingBehavior((isnull(embed_chance) ? src.embed_chance : embed_chance),\
+ (isnull(embedded_fall_chance) ? src.embedded_fall_chance : embedded_fall_chance),\
+ (isnull(embedded_pain_chance) ? src.embedded_pain_chance : embedded_pain_chance),\
+ (isnull(embedded_pain_multiplier) ? src.embedded_pain_multiplier : embedded_pain_multiplier),\
+ (isnull(embedded_fall_pain_multiplier) ? src.embedded_fall_pain_multiplier : embedded_fall_pain_multiplier),\
+ (isnull(embedded_impact_pain_multiplier) ? src.embedded_impact_pain_multiplier : embedded_impact_pain_multiplier),\
+ (isnull(embedded_unsafe_removal_pain_multiplier) ? src.embedded_unsafe_removal_pain_multiplier : embedded_unsafe_removal_pain_multiplier),\
+ (isnull(embedded_unsafe_removal_time) ? src.embedded_unsafe_removal_time : embedded_unsafe_removal_time))
#undef EMBEDID
diff --git a/code/datums/mood_events/generic_negative_events.dm b/code/datums/mood_events/generic_negative_events.dm
index f747c563ad..f4ca3a8ebd 100644
--- a/code/datums/mood_events/generic_negative_events.dm
+++ b/code/datums/mood_events/generic_negative_events.dm
@@ -5,8 +5,8 @@
mood_change = -1
/datum/mood_event/broken_vow //Used for when mimes break their vow of silence
- description = "I have brought shame upon my name, and betrayed my fellow mimes by breaking our sacred vow...\n"
- mood_change = -8
+ description = "I have brought shame upon my name, and betrayed my fellow mimes by breaking our sacred vow...\n"
+ mood_change = -8
/datum/mood_event/on_fire
description = "I'M ON FIRE!!!\n"
@@ -55,14 +55,14 @@
timeout = 2 MINUTES
/datum/mood_event/shameful_suicide //suicide_acts that return SHAME, like sord
- description = "I can't even end it all!\n"
- mood_change = -10
- timeout = 1 MINUTES
+ description = "I can't even end it all!\n"
+ mood_change = -10
+ timeout = 1 MINUTES
/datum/mood_event/dismembered
- description = "AHH! I WAS USING THAT LIMB!\n"
- mood_change = -8
- timeout = 2400
+ description = "AHH! I WAS USING THAT LIMB!\n"
+ mood_change = -8
+ timeout = 2400
/datum/mood_event/noshoes
description = "I am a disgrace to comedy everywhere!\n"
@@ -92,20 +92,20 @@
mood_change = 2
/datum/mood_event/brain_damage
- mood_change = -3
+ mood_change = -3
/datum/mood_event/brain_damage/add_effects()
- var/damage_message = pick_list_replacements(BRAIN_DAMAGE_FILE, "brain_damage")
- description = "Hurr durr... [damage_message]\n"
+ var/damage_message = pick_list_replacements(BRAIN_DAMAGE_FILE, "brain_damage")
+ description = "Hurr durr... [damage_message]\n"
/datum/mood_event/hulk //Entire duration of having the hulk mutation
- description = "HULK SMASH!\n"
- mood_change = -4
+ description = "HULK SMASH!\n"
+ mood_change = -4
/datum/mood_event/epilepsy //Only when the mutation causes a seizure
- description = "I should have paid attention to the epilepsy warning.\n"
- mood_change = -3
- timeout = 3000
+ description = "I should have paid attention to the epilepsy warning.\n"
+ mood_change = -3
+ timeout = 3000
/datum/mood_event/nyctophobia
description = "It sure is dark around here...\n"
diff --git a/code/datums/mood_events/generic_positive_events.dm b/code/datums/mood_events/generic_positive_events.dm
index 422ec4476c..678802aed9 100644
--- a/code/datums/mood_events/generic_positive_events.dm
+++ b/code/datums/mood_events/generic_positive_events.dm
@@ -80,12 +80,12 @@
description = "Heh...hehehe...hehe...\n"
mood_change = 4
- /datum/mood_event/chemical_laughter
+/datum/mood_event/chemical_laughter
description = "Laughter really is the best medicine! Or is it?\n"
mood_change = 4
timeout = 3 MINUTES
- /datum/mood_event/chemical_superlaughter
+/datum/mood_event/chemical_superlaughter
description = "*WHEEZE*\n"
mood_change = 12
timeout = 3 MINUTES
diff --git a/code/datums/ruins/lavaland.dm b/code/datums/ruins/lavaland.dm
index 18efae955e..4f18e6fd62 100644
--- a/code/datums/ruins/lavaland.dm
+++ b/code/datums/ruins/lavaland.dm
@@ -163,6 +163,13 @@
suffix = "lavaland_surface_xeno_nest.dmm"
cost = 20 */
+/datum/map_template/ruin/lavaland/alien_nest
+ name = "Alien Nest"
+ id = "alien-nest"
+ description = "Not even Necropolis is safe from alien infestation. The competition for hosts has locked the legion and aliens in an endless conflict that can only be resolved by a PKA."
+ suffix = "lavaland_surface_alien_nest.dmm"
+ cost = 20
+
/datum/map_template/ruin/lavaland/fountain
name = "Fountain Hall"
id = "fountain"
diff --git a/code/datums/weather/weather_types/ash_storm.dm b/code/datums/weather/weather_types/ash_storm.dm
index 345212cc1b..3247b890c6 100644
--- a/code/datums/weather/weather_types/ash_storm.dm
+++ b/code/datums/weather/weather_types/ash_storm.dm
@@ -80,7 +80,7 @@
return TRUE
if(ishuman(L)) //Are you immune?
var/mob/living/carbon/human/H = L
- var/thermal_protection = H.get_thermal_protection()
+ var/thermal_protection = H.easy_thermal_protection()
if(thermal_protection >= FIRE_IMMUNITY_MAX_TEMP_PROTECT)
return TRUE
if(isliving(L))// if we're a non immune mob inside an immune mob we have to reconsider if that mob is immune to protect ourselves
diff --git a/code/datums/wires/airlock.dm b/code/datums/wires/airlock.dm
index 26942a1ba6..db6d80f9bb 100644
--- a/code/datums/wires/airlock.dm
+++ b/code/datums/wires/airlock.dm
@@ -1,10 +1,35 @@
/datum/wires/airlock
holder_type = /obj/machinery/door/airlock
- proper_name = "Airlock"
+ proper_name = "Generic Airlock"
+ var/wiretype
/datum/wires/airlock/secure
randomize = TRUE
+/datum/wires/airlock/command
+ proper_name = "Command Airlock"
+ wiretype = "commandairlock"
+
+/datum/wires/airlock/security
+ proper_name = "Security Airlock"
+ wiretype = "securityairlock"
+
+/datum/wires/airlock/engineering
+ proper_name = "Engineering Airlock"
+ wiretype = "engineeringairlock"
+
+/datum/wires/airlock/science
+ proper_name = "Science Airlock"
+ wiretype = "scienceairlock"
+
+/datum/wires/airlock/medical
+ proper_name = "Medical Airlock"
+ wiretype = "medicalairlock"
+
+/datum/wires/airlock/cargo
+ proper_name = "Cargo Airlock"
+ wiretype = "cargoairlock"
+
/datum/wires/airlock/New(atom/holder)
wires = list(
WIRE_POWER1, WIRE_POWER2,
@@ -14,7 +39,16 @@
WIRE_ZAP1, WIRE_ZAP2
)
add_duds(2)
- ..()
+ . = ..()
+ if(randomize || !wiretype)
+ return
+ if(!GLOB.wire_color_directory[wiretype])
+ colors = list()
+ randomize()
+ GLOB.wire_color_directory[wiretype] = colors
+ GLOB.wire_name_directory[wiretype] = proper_name
+ else
+ colors = GLOB.wire_color_directory[wiretype]
/datum/wires/airlock/interactable(mob/user)
var/obj/machinery/door/airlock/A = holder
diff --git a/code/game/area/Space_Station_13_areas.dm b/code/game/area/Space_Station_13_areas.dm
index 798cd9c026..fa66306302 100644
--- a/code/game/area/Space_Station_13_areas.dm
+++ b/code/game/area/Space_Station_13_areas.dm
@@ -250,7 +250,12 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/maintenance/disposal/incinerator
name = "Incinerator"
icon_state = "disposal"
+/area/maintenance/bar
+ name = "Maintenance Bar"
+ icon_state = "maintbar"
+/area/maintenance/bar/cafe
+ name = "Abandoned Cafe"
//Hallway
@@ -499,6 +504,16 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
name = "Abandoned Theatre"
icon_state = "Theatre"
+/area/crew_quarters/theatre/clown
+ name = "Clown's Office"
+
+/area/crew_quarters/theatre/mime
+ name = "Mime's Office"
+
+/area/crew_quarters/cryopod
+ name = "Cryogenics"
+ icon_state = "cryosleep"
+
/area/library
name = "Library"
icon_state = "library"
diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm
index dfbb59811b..612c3cba42 100644
--- a/code/game/area/areas.dm
+++ b/code/game/area/areas.dm
@@ -53,8 +53,6 @@
var/parallax_movedir = 0
- var/global/global_uid = 0
- var/uid
var/list/ambientsounds = GENERIC
flags_1 = CAN_BE_DIRTY_1
@@ -96,7 +94,6 @@ GLOBAL_LIST_EMPTY(teleportlocs)
/area/Initialize()
icon_state = ""
layer = AREA_LAYER
- uid = ++global_uid
map_name = name // Save the initial (the name set in the map) name of the area.
canSmoothWithAreas = typecacheof(canSmoothWithAreas)
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 64ff2437d3..e8651ba93d 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -186,6 +186,10 @@
else
M.forceMove(src)
+//common name
+/atom/proc/update_multiz(prune_on_fail = FALSE)
+ return FALSE
+
/atom/proc/assume_air(datum/gas_mixture/giver)
qdel(giver)
return null
@@ -832,4 +836,7 @@ Proc for attack log creation, because really why not
if(filter_data && filter_data[name])
filter_data -= name
update_filters()
- return TRUE
\ No newline at end of file
+ return TRUE
+
+/atom/proc/intercept_zImpact(atom/movable/AM, levels = 1)
+ . |= SEND_SIGNAL(src, COMSIG_ATOM_INTERCEPT_Z_FALL, AM, levels)
\ No newline at end of file
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 0957db5fcb..ef20b6e35e 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -34,6 +34,51 @@
var/datum/component/orbiter/orbiting
var/can_be_z_moved = TRUE
+ var/zfalling = FALSE
+
+/atom/movable/proc/can_zFall(turf/source, levels = 1, turf/target, direction)
+ if(!direction)
+ direction = DOWN
+ if(!source)
+ source = get_turf(src)
+ if(!source)
+ return FALSE
+ if(!target)
+ target = get_step_multiz(source, direction)
+ if(!target)
+ return FALSE
+ return !(movement_type & FLYING) && has_gravity(source) && !throwing
+
+/atom/movable/proc/onZImpact(turf/T, levels)
+ var/atom/highest = T
+ for(var/i in T.contents)
+ var/atom/A = i
+ if(!A.density)
+ continue
+ if(isobj(A) || ismob(A))
+ if(A.layer > highest.layer)
+ highest = A
+ INVOKE_ASYNC(src, .proc/SpinAnimation, 5, 2)
+ throw_impact(highest)
+ return TRUE
+
+//For physical constraints to travelling up/down.
+/atom/movable/proc/can_zTravel(turf/destination, direction)
+ var/turf/T = get_turf(src)
+ if(!T)
+ return FALSE
+ if(!direction)
+ if(!destination)
+ return FALSE
+ direction = get_dir(T, destination)
+ if(direction != UP && direction != DOWN)
+ return FALSE
+ if(!destination)
+ destination = get_step_multiz(src, direction)
+ if(!destination)
+ return FALSE
+ return T.zPassOut(src, direction, destination) && destination.zPassIn(src, direction, T)
+
/atom/movable/vv_edit_var(var_name, var_value)
var/static/list/banned_edits = list("step_x", "step_y", "step_size")
var/static/list/careful_edits = list("bound_x", "bound_y", "bound_width", "bound_height")
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
index 0e3160d741..9d4960858d 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
@@ -25,9 +25,14 @@
var/makeBody = TRUE
/datum/dynamic_ruleset/midround/trim_candidates()
+ //
+ // All you need to know is that here, the candidates list contains 4 lists itself, indexed with the following defines:
+ // Candidates = list(CURRENT_LIVING_PLAYERS, CURRENT_LIVING_ANTAGS, CURRENT_DEAD_PLAYERS, CURRENT_OBSERVERS)
+ // So for example you can get the list of all current dead players with var/list/dead_players = candidates[CURRENT_DEAD_PLAYERS]
+ // Make sure to properly typecheck the mobs in those lists, as the dead_players list could contain ghosts, or dead players still in their bodies.
+ // We're still gonna trim the obvious (mobs without clients, jobbanned players, etc)
living_players = trim_list(mode.current_players[CURRENT_LIVING_PLAYERS])
living_antags = trim_list(mode.current_players[CURRENT_LIVING_ANTAGS])
- dead_players = trim_list(mode.current_players[CURRENT_DEAD_PLAYERS])
list_observers = trim_list(mode.current_players[CURRENT_OBSERVERS])
/datum/dynamic_ruleset/midround/proc/trim_list(list/L = list())
diff --git a/code/game/gamemodes/sandbox/h_sandbox.dm b/code/game/gamemodes/sandbox/h_sandbox.dm
index 64aaeebacd..d54dda5353 100644
--- a/code/game/gamemodes/sandbox/h_sandbox.dm
+++ b/code/game/gamemodes/sandbox/h_sandbox.dm
@@ -25,14 +25,14 @@ GLOBAL_VAR_INIT(hsboxspawn, TRUE)
var/canisterinfo = null
var/hsbinfo = null
//items that shouldn't spawn on the floor because they would bug or act weird
- var/global/list/spawn_forbidden = list(
+ var/static/list/spawn_forbidden = list(
/obj/item/tk_grab, /obj/item/implant, // not implanter, the actual thing that is inside you
/obj/item/assembly, /obj/item/onetankbomb, /obj/item/pda/ai,
/obj/item/smallDelivery, /obj/item/projectile,
/obj/item/borg/sight, /obj/item/borg/stun, /obj/item/robot_module)
/datum/hSB/proc/update()
- var/global/list/hrefs = list(
+ var/static/list/hrefs = list(
"Space Gear",
"Suit Up (Space Travel Gear)" = "hsbsuit",
"Spawn Gas Mask" = "hsbspawn&path=[/obj/item/clothing/mask/gas]",
diff --git a/code/game/machinery/aug_manipulator.dm b/code/game/machinery/aug_manipulator.dm
index 99200e510e..8419f5803c 100644
--- a/code/game/machinery/aug_manipulator.dm
+++ b/code/game/machinery/aug_manipulator.dm
@@ -16,8 +16,8 @@
to_chat(user, "Alt-click to eject the limb.")
/obj/machinery/aug_manipulator/Initialize()
- initial_icon_state = initial(icon_state)
- return ..()
+ initial_icon_state = initial(icon_state)
+ return ..()
/obj/machinery/aug_manipulator/update_icon()
cut_overlays()
diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm
index 1c6e21491c..bdb90c4c97 100644
--- a/code/game/machinery/autolathe.dm
+++ b/code/game/machinery/autolathe.dm
@@ -176,7 +176,7 @@
matching_designs.Cut()
for(var/v in stored_research.researched_designs)
- var/datum/design/D = stored_research.researched_designs[v]
+ var/datum/design/D = SSresearch.techweb_design_by_id(v)
if(findtext(D.name,href_list["to_search"]))
matching_designs.Add(D)
updateUsrDialog()
@@ -252,7 +252,7 @@
dat += materials_printout()
for(var/v in stored_research.researched_designs)
- var/datum/design/D = stored_research.researched_designs[v]
+ var/datum/design/D = SSresearch.techweb_design_by_id(v)
if(!(selected_category in D.category))
continue
@@ -366,7 +366,7 @@
/obj/machinery/autolathe/proc/adjust_hacked(state)
hacked = state
for(var/id in SSresearch.techweb_designs)
- var/datum/design/D = SSresearch.techweb_designs[id]
+ var/datum/design/D = SSresearch.techweb_design_by_id(id)
if((D.build_type & AUTOLATHE) && ("hacked" in D.category))
if(hacked)
stored_research.add_design(D)
diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm
index f6655acfc4..e65da0fcff 100644
--- a/code/game/machinery/cloning.dm
+++ b/code/game/machinery/cloning.dm
@@ -354,7 +354,8 @@
O.organ_flags &= ~ORGAN_FROZEN
unattached_flesh.Cut()
mess = FALSE
- new /obj/effect/gibspawner/generic(get_turf(src))
+ if(mob_occupant)
+ mob_occupant.spawn_gibs()
audible_message("You hear a splat.")
update_icon()
return
diff --git a/code/game/machinery/computer/Operating.dm b/code/game/machinery/computer/Operating.dm
index c317cbba0d..858a0a995b 100644
--- a/code/game/machinery/computer/Operating.dm
+++ b/code/game/machinery/computer/Operating.dm
@@ -32,7 +32,7 @@
/obj/machinery/computer/operating/proc/sync_surgeries()
for(var/i in linked_techweb.researched_designs)
- var/datum/design/surgery/D = linked_techweb.researched_designs[i]
+ var/datum/design/surgery/D = SSresearch.techweb_design_by_id(i)
if(!istype(D))
continue
advanced_surgeries |= D.surgery
diff --git a/code/game/machinery/computer/dna_console.dm b/code/game/machinery/computer/dna_console.dm
index 9d49574317..394cc4593d 100644
--- a/code/game/machinery/computer/dna_console.dm
+++ b/code/game/machinery/computer/dna_console.dm
@@ -468,7 +468,7 @@
var/len = length(viable_occupant.dna.uni_identity)
num = WRAP(num, 1, len+1)
num = randomize_radiation_accuracy(num, radduration + (connected.precision_coeff ** 2), len) //Each manipulator level above 1 makes randomization as accurate as selected time + manipulator lvl^2
- //Value is this high for the same reason as with laser - not worth the hassle of upgrading if the bonus is low
+ //Value is this high for the same reason as with laser - not worth the hassle of upgrading if the bonus is low
var/block = round((num-1)/DNA_BLOCK_SIZE)+1
var/subblock = num - block*DNA_BLOCK_SIZE
last_change = "UI #[block]-[subblock]; "
diff --git a/code/game/machinery/computer/teleporter.dm b/code/game/machinery/computer/teleporter.dm
index b5869ef36b..21fb70c38c 100644
--- a/code/game/machinery/computer/teleporter.dm
+++ b/code/game/machinery/computer/teleporter.dm
@@ -130,12 +130,12 @@
L[avoid_assoc_duplicate_keys(A.name, areaindex)] = R
for(var/obj/item/implant/tracking/I in GLOB.tracked_implants)
- if(!I.imp_in || !isliving(I.imp_in))
+ if(!I.imp_in || !I.allow_teleport || !isliving(I.imp_in))
continue
else
var/mob/living/M = I.imp_in
if(M.stat == DEAD)
- if(M.timeofdeath + 6000 < world.time)
+ if(M.timeofdeath + I.lifespan_postmortem < world.time)
continue
if(is_eligible(M))
L[avoid_assoc_duplicate_keys(M.real_name, areaindex)] = M
diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm
index 3ff515f4f5..98dd91d3bd 100644
--- a/code/game/machinery/cryopod.dm
+++ b/code/game/machinery/cryopod.dm
@@ -408,7 +408,7 @@
// Ghost and delete the mob.
if(!mob_occupant.get_ghost(1))
- mob_occupant.ghostize(0) // Players who cryo out may not re-enter the round
+ mob_occupant.ghostize(FALSE, penalize = TRUE)
QDEL_NULL(occupant)
open_machine()
diff --git a/code/game/machinery/dance_machine.dm b/code/game/machinery/dance_machine.dm
index 1c6d713437..91306585b8 100644
--- a/code/game/machinery/dance_machine.dm
+++ b/code/game/machinery/dance_machine.dm
@@ -364,7 +364,7 @@
//for(var/mob/living/carbon/NS in rangers)
// NS.resting = !NS.resting
// NS.update_canmove()
- time--
+ time--
/obj/machinery/jukebox/disco/proc/dance5(var/mob/living/M)
animate(M, transform = matrix(180, MATRIX_ROTATE), time = 1, loop = 0)
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index 365d19b826..f85fb3f310 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -83,6 +83,7 @@
var/boltDown = 'sound/machines/boltsdown.ogg'
var/noPower = 'sound/machines/doorclick.ogg'
var/previous_airlock = /obj/structure/door_assembly //what airlock assembly mineral plating was applied to
+ var/wiretypepath = /datum/wires/airlock // which set of per round randomized wires this airlock type has.
var/airlock_material //material of inner filling; if its an airlock with glass, this should be set to "glass"
var/overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi'
var/note_overlay_file = 'icons/obj/doors/airlocks/station/overlays.dmi' //Used for papers and photos pinned to the airlock
diff --git a/code/game/machinery/doors/airlock_types.dm b/code/game/machinery/doors/airlock_types.dm
index becd89063f..59b49d5828 100644
--- a/code/game/machinery/doors/airlock_types.dm
+++ b/code/game/machinery/doors/airlock_types.dm
@@ -8,16 +8,19 @@
/obj/machinery/door/airlock/command
icon = 'icons/obj/doors/airlocks/station/command.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_com
+ wiretypepath = /datum/wires/airlock/command
normal_integrity = 450
/obj/machinery/door/airlock/security
icon = 'icons/obj/doors/airlocks/station/security.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_sec
+ wiretypepath = /datum/wires/airlock/security
normal_integrity = 450
/obj/machinery/door/airlock/engineering
icon = 'icons/obj/doors/airlocks/station/engineering.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_eng
+ wiretypepath = /datum/wires/airlock/engineering
/obj/machinery/door/airlock/engineering/abandoned
abandoned = TRUE
@@ -25,6 +28,7 @@
/obj/machinery/door/airlock/medical
icon = 'icons/obj/doors/airlocks/station/medical.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_med
+ wiretypepath = /datum/wires/airlock/medical
/obj/machinery/door/airlock/maintenance
name = "maintenance access"
@@ -44,11 +48,13 @@
name = "mining airlock"
icon = 'icons/obj/doors/airlocks/station/mining.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_min
+ wiretypepath = /datum/wires/airlock/cargo
/obj/machinery/door/airlock/atmos
name = "atmospherics airlock"
icon = 'icons/obj/doors/airlocks/station/atmos.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_atmo
+ wiretypepath = /datum/wires/airlock/engineering
/obj/machinery/door/airlock/atmos/abandoned
abandoned = TRUE
@@ -56,6 +62,7 @@
/obj/machinery/door/airlock/research
icon = 'icons/obj/doors/airlocks/station/research.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_research
+ wiretypepath = /datum/wires/airlock/science
/obj/machinery/door/airlock/freezer
name = "freezer airlock"
@@ -65,10 +72,12 @@
/obj/machinery/door/airlock/science
icon = 'icons/obj/doors/airlocks/station/science.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_science
+ wiretypepath = /datum/wires/airlock/science
/obj/machinery/door/airlock/virology
icon = 'icons/obj/doors/airlocks/station/virology.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_viro
+ wiretypepath = /datum/wires/airlock/medical
//////////////////////////////////
/*
@@ -376,6 +385,7 @@
icon = 'icons/obj/doors/airlocks/vault/vault.dmi'
overlays_file = 'icons/obj/doors/airlocks/vault/overlays.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_vault
+ wiretypepath = /datum/wires/airlock/secure
explosion_block = 2
normal_integrity = 400 // reverse engieneerd: 400 * 1.5 (sec lvl 6) = 600 = original
security_level = 6
diff --git a/code/game/machinery/launch_pad.dm b/code/game/machinery/launch_pad.dm
index db59f3f413..5418dcdb97 100644
--- a/code/game/machinery/launch_pad.dm
+++ b/code/game/machinery/launch_pad.dm
@@ -153,11 +153,11 @@
var/obj/item/storage/briefcase/launchpad/briefcase
/obj/machinery/launchpad/briefcase/Initialize(mapload, briefcase)
- . = ..()
- if(!briefcase)
- log_game("[src] has been spawned without a briefcase.")
- return INITIALIZE_HINT_QDEL
- src.briefcase = briefcase
+ . = ..()
+ if(!briefcase)
+ log_game("[src] has been spawned without a briefcase.")
+ return INITIALIZE_HINT_QDEL
+ src.briefcase = briefcase
/obj/machinery/launchpad/briefcase/Destroy()
QDEL_NULL(briefcase)
diff --git a/code/game/machinery/limbgrower.dm b/code/game/machinery/limbgrower.dm
index 88ab4ec6f8..794509801b 100644
--- a/code/game/machinery/limbgrower.dm
+++ b/code/game/machinery/limbgrower.dm
@@ -178,7 +178,7 @@
dat += materials_printout()
for(var/v in stored_research.researched_designs)
- var/datum/design/D = stored_research.researched_designs[v]
+ var/datum/design/D = SSresearch.techweb_design_by_id(v)
if(!(selected_category in D.category))
continue
if(disabled || !can_build(D))
@@ -221,7 +221,7 @@
if(obj_flags & EMAGGED)
return
for(var/id in SSresearch.techweb_designs)
- var/datum/design/D = SSresearch.techweb_designs[id]
+ var/datum/design/D = SSresearch.techweb_design_by_id(id)
if((D.build_type & LIMBGROWER) && ("emagged" in D.category))
stored_research.add_design(D)
to_chat(user, "A warning flashes onto the screen, stating that safety overrides have been deactivated!")
diff --git a/code/game/machinery/recycler.dm b/code/game/machinery/recycler.dm
index 1a8bc7ece4..73aadc99d0 100644
--- a/code/game/machinery/recycler.dm
+++ b/code/game/machinery/recycler.dm
@@ -109,7 +109,7 @@
var/atom/movable/AM = i
var/obj/item/bodypart/head/as_head = AM
var/obj/item/mmi/as_mmi = AM
- var/brain_holder = istype(AM, /obj/item/organ/brain) || (istype(as_head) && as_head.brain) || (istype(as_mmi) && as_mmi.brain) || isbrain(AM)
+ var/brain_holder = istype(AM, /obj/item/organ/brain) || (istype(as_head) && as_head.brain) || (istype(as_mmi) && as_mmi.brain) || isbrain(AM) || istype(AM, /obj/item/dullahan_relay)
if(brain_holder)
emergency_stop(AM)
else if(isliving(AM))
diff --git a/code/game/machinery/syndicatebomb.dm b/code/game/machinery/syndicatebomb.dm
index d7c2a5734b..4a97aa1775 100644
--- a/code/game/machinery/syndicatebomb.dm
+++ b/code/game/machinery/syndicatebomb.dm
@@ -1,5 +1,5 @@
#define BUTTON_COOLDOWN 60 // cant delay the bomb forever
-#define BUTTON_DELAY 50 //five seconds
+#define BUTTON_DELAY 20 // two seconds
/obj/machinery/syndicatebomb
icon = 'icons/obj/assemblies.dmi'
@@ -500,7 +500,7 @@
/obj/item/syndicatedetonator
name = "big red button"
- desc = "Your standard issue bomb synchronizing button. Five second safety delay to prevent 'accidents'."
+ desc = "Your standard issue bomb synchronizing button. Two second safety delay to prevent 'accidents'."
icon = 'icons/obj/assemblies.dmi'
icon_state = "bigred"
item_state = "electronic"
diff --git a/code/game/mecha/combat/honker.dm b/code/game/mecha/combat/honker.dm
index ed29809f91..3a3d98ad1e 100644
--- a/code/game/mecha/combat/honker.dm
+++ b/code/game/mecha/combat/honker.dm
@@ -57,19 +57,19 @@
[js_byjax]
[js_dropdowns]
function SSticker() {
- setInterval(function(){
- window.location='byond://?src=[REF(src)]&update_content=1';
- document.body.style.color = get_rand_color_string();
- document.body.style.background = get_rand_color_string();
- }, 1000);
+ setInterval(function(){
+ window.location='byond://?src=[REF(src)]&update_content=1';
+ document.body.style.color = get_rand_color_string();
+ document.body.style.background = get_rand_color_string();
+ }, 1000);
}
function get_rand_color_string() {
- var color = new Array;
- for(var i=0;i<3;i++){
- color.push(Math.floor(Math.random()*255));
- }
- return "rgb("+color.toString()+")";
+ var color = new Array;
+ for(var i=0;i<3;i++){
+ color.push(Math.floor(Math.random()*255));
+ }
+ return "rgb("+color.toString()+")";
}
window.onload = function() {
diff --git a/code/game/mecha/mech_fabricator.dm b/code/game/mecha/mech_fabricator.dm
index 4acf7981bc..417fefce6e 100644
--- a/code/game/mecha/mech_fabricator.dm
+++ b/code/game/mecha/mech_fabricator.dm
@@ -34,12 +34,12 @@
)
/obj/machinery/mecha_part_fabricator/Initialize()
- var/datum/component/material_container/materials = AddComponent(/datum/component/material_container,
- list(MAT_METAL, MAT_GLASS, MAT_SILVER, MAT_GOLD, MAT_DIAMOND, MAT_PLASMA, MAT_URANIUM, MAT_BANANIUM, MAT_TITANIUM, MAT_BLUESPACE), 0,
- TRUE, /obj/item/stack, CALLBACK(src, .proc/is_insertion_ready), CALLBACK(src, .proc/AfterMaterialInsert))
- materials.precise_insertion = TRUE
- stored_research = new
- return ..()
+ var/datum/component/material_container/materials = AddComponent(/datum/component/material_container,
+ list(MAT_METAL, MAT_GLASS, MAT_SILVER, MAT_GOLD, MAT_DIAMOND, MAT_PLASMA, MAT_URANIUM, MAT_BANANIUM, MAT_TITANIUM, MAT_BLUESPACE), 0,
+ TRUE, /obj/item/stack, CALLBACK(src, .proc/is_insertion_ready), CALLBACK(src, .proc/AfterMaterialInsert))
+ materials.precise_insertion = TRUE
+ stored_research = new
+ return ..()
/obj/machinery/mecha_part_fabricator/RefreshParts()
var/T = 0
@@ -85,7 +85,7 @@
/obj/machinery/mecha_part_fabricator/proc/output_parts_list(set_name)
var/output = ""
for(var/v in stored_research.researched_designs)
- var/datum/design/D = stored_research.researched_designs[v]
+ var/datum/design/D = SSresearch.techweb_design_by_id(v)
if(D.build_type & MECHFAB)
if(!(set_name in D.category))
continue
@@ -166,7 +166,7 @@
/obj/machinery/mecha_part_fabricator/proc/add_part_set_to_queue(set_name)
if(set_name in part_sets)
for(var/v in stored_research.researched_designs)
- var/datum/design/D = stored_research.researched_designs[v]
+ var/datum/design/D = SSresearch.techweb_design_by_id(v)
if(D.build_type & MECHFAB)
if(set_name in D.category)
add_to_queue(D)
@@ -324,7 +324,7 @@
if(href_list["part"])
var/T = afilter.getStr("part")
for(var/v in stored_research.researched_designs)
- var/datum/design/D = stored_research.researched_designs[v]
+ var/datum/design/D = SSresearch.techweb_design_by_id(v)
if(D.build_type & MECHFAB)
if(D.id == T)
if(!processing_queue)
@@ -335,7 +335,7 @@
if(href_list["add_to_queue"])
var/T = afilter.getStr("add_to_queue")
for(var/v in stored_research.researched_designs)
- var/datum/design/D = stored_research.researched_designs[v]
+ var/datum/design/D = SSresearch.techweb_design_by_id(v)
if(D.build_type & MECHFAB)
if(D.id == T)
add_to_queue(D)
@@ -373,7 +373,7 @@
if(href_list["part_desc"])
var/T = afilter.getStr("part_desc")
for(var/v in stored_research.researched_designs)
- var/datum/design/D = stored_research.researched_designs[v]
+ var/datum/design/D = SSresearch.techweb_design_by_id(v)
if(D.build_type & MECHFAB)
if(D.id == T)
var/obj/part = D.build_path
diff --git a/code/game/mecha/mecha_topic.dm b/code/game/mecha/mecha_topic.dm
index 79ee7435e4..f9e04990fc 100644
--- a/code/game/mecha/mecha_topic.dm
+++ b/code/game/mecha/mecha_topic.dm
@@ -21,9 +21,9 @@
[js_byjax]
[js_dropdowns]
function SSticker() {
- setInterval(function(){
- window.location='byond://?src=[REF(src)]&update_content=1';
- }, 1000);
+ setInterval(function(){
+ window.location='byond://?src=[REF(src)]&update_content=1';
+ }, 1000);
}
window.onload = function() {
diff --git a/code/game/objects/effects/decals/decal.dm b/code/game/objects/effects/decals/decal.dm
index b6a3c7cfef..2fa7277d8b 100644
--- a/code/game/objects/effects/decals/decal.dm
+++ b/code/game/objects/effects/decals/decal.dm
@@ -15,7 +15,7 @@
qdel(src)
/obj/effect/decal/proc/NeverShouldHaveComeHere(turf/T)
- return isspaceturf(T) || isclosedturf(T) || islava(T) || istype(T, /turf/open/water) || ischasm(T)
+ return isclosedturf(T) || isgroundlessturf(T)
/obj/effect/decal/ex_act(severity, target)
qdel(src)
diff --git a/code/game/objects/effects/decals/turfdecal/weather.dm b/code/game/objects/effects/decals/turfdecal/weather.dm
index 9e8da6a3f8..52c1a165f2 100644
--- a/code/game/objects/effects/decals/turfdecal/weather.dm
+++ b/code/game/objects/effects/decals/turfdecal/weather.dm
@@ -9,4 +9,17 @@
/obj/effect/turf_decal/weather/snow/corner
name = "snow corner piece"
icon = 'icons/turf/snow.dmi'
- icon_state = "snow_corner"
\ No newline at end of file
+ icon_state = "snow_corner"
+
+/obj/effect/turf_decal/weather/dirt
+ name = "dirt siding"
+ icon = 'icons/turf/decals.dmi'
+ icon_state = "dirt_side"
+
+/obj/effect/turf_decal/weather/sand
+ name = "sand siding"
+ icon = 'icons/misc/beach.dmi'
+ icon_state = "sand_side"
+
+/obj/effect/turf_decal/weather/sand/light
+ icon_state = "lightsand_side"
diff --git a/code/game/objects/effects/effects.dm b/code/game/objects/effects/effects.dm
index 410923fe25..d48013d472 100644
--- a/code/game/objects/effects/effects.dm
+++ b/code/game/objects/effects/effects.dm
@@ -57,6 +57,9 @@
/obj/effect/abstract/singularity_act()
return
+/obj/effect/abstract/has_gravity(turf/T)
+ return FALSE
+
/obj/effect/dummy/singularity_pull()
return
diff --git a/code/game/objects/effects/spawners/gibspawner.dm b/code/game/objects/effects/spawners/gibspawner.dm
index dd39bc567a..fe1590caba 100644
--- a/code/game/objects/effects/spawners/gibspawner.dm
+++ b/code/game/objects/effects/spawners/gibspawner.dm
@@ -32,7 +32,8 @@
var/list/dna_to_add //find the dna to pass to the spawned gibs. do note this can be null if the mob doesn't have blood. add_blood_DNA() has built in null handling.
var/body_coloring = ""
if(source_mob)
- dna_to_add = source_mob.get_blood_dna_list() //ez pz
+ if(!issilicon(source_mob))
+ dna_to_add = source_mob.get_blood_dna_list() //ez pz
if(ishuman(source_mob))
var/mob/living/carbon/human/H = source_mob
if(H.dna.species.use_skintones)
@@ -51,15 +52,11 @@
body_coloring = "#[skintone2hex(H.skin_tone)]"
else
body_coloring = "#[H.dna.features["mcolor"]]"
- qdel(H)
else
dna_to_add = temp_mob.get_blood_dna_list()
- qdel(temp_mob)
else if(!issilicon(temp_mob))
dna_to_add = temp_mob.get_blood_dna_list()
- qdel(temp_mob)
- else
- qdel(temp_mob)
+ qdel(temp_mob)
else
dna_to_add = list("Non-human DNA" = random_blood_type()) //else, generate a random bloodtype for it.
diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm
index c92721082c..08a3501be6 100644
--- a/code/game/objects/effects/spiders.dm
+++ b/code/game/objects/effects/spiders.dm
@@ -201,7 +201,7 @@
S.directive = directive
if(player_spiders)
S.playable_spider = TRUE
- notify_ghosts("Spider [S.name] can be controlled", null, enter_link="(Click to play)", source=S, action=NOTIFY_ATTACK, ignore_key = POLL_IGNORE_SPIDER)
+ notify_ghosts("Spider [S.name] can be controlled", null, enter_link="(Click to play)", source=S, action=NOTIFY_ATTACK, ignore_key = POLL_IGNORE_SPIDER, ignore_dnr_observers = TRUE)
qdel(src)
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 953036e970..4f472c6477 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -202,7 +202,9 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
var/list/boostable_nodes = techweb_item_boost_check(src)
if (boostable_nodes)
for(var/id in boostable_nodes)
- var/datum/techweb_node/node = SSresearch.techweb_nodes[id]
+ var/datum/techweb_node/node = SSresearch.techweb_node_by_id(id)
+ if(!node)
+ continue
research_msg += sep
research_msg += node.display_name
sep = ", "
diff --git a/code/game/objects/items/RCD.dm b/code/game/objects/items/RCD.dm
index 8763089464..134b921666 100644
--- a/code/game/objects/items/RCD.dm
+++ b/code/game/objects/items/RCD.dm
@@ -163,6 +163,8 @@ RLD
var/use_one_access = 0 //If the airlock should require ALL or only ONE of the listed accesses.
var/delay_mod = 1
var/canRturf = FALSE //Variable for R walls to deconstruct them
+ var/adjacency_check = TRUE //Wheter it checks if the tool has to be in our hands or not. Wsed for the aux base construction drone's internal RCD
+
/obj/item/construction/rcd/suicide_act(mob/user)
user.visible_message("[user] sets the RCD to 'Wall' and points it down [user.p_their()] throat! It looks like [user.p_theyre()] trying to commit suicide..")
@@ -273,7 +275,7 @@ RLD
/obj/item/construction/rcd/proc/check_menu(mob/living/user)
if(!istype(user))
return FALSE
- if(user.incapacitated() || !user.Adjacent(src))
+ if(user.incapacitated() || (adjacency_check && !user.Adjacent(src)))
return FALSE
return TRUE
@@ -286,7 +288,7 @@ RLD
"SOUTH" = image(icon = 'icons/mob/radial.dmi', icon_state = "csouth"),
"WEST" = image(icon = 'icons/mob/radial.dmi', icon_state = "cwest")
)
- var/computerdirs = show_radial_menu(user, src, computer_dirs, custom_check = CALLBACK(src, .proc/check_menu, user), require_near = TRUE, tooltips = TRUE)
+ var/computerdirs = show_radial_menu(user, src, computer_dirs, custom_check = CALLBACK(src, .proc/check_menu, user), require_near = adjacency_check, tooltips = TRUE)
if(!check_menu(user))
return
switch(computerdirs)
@@ -345,13 +347,13 @@ RLD
"External Maintenance" = get_airlock_image(/obj/machinery/door/airlock/maintenance/external/glass)
)
- var/airlockcat = show_radial_menu(user, src, solid_or_glass_choices, custom_check = CALLBACK(src, .proc/check_menu, user), require_near = TRUE)
+ var/airlockcat = show_radial_menu(user, src, solid_or_glass_choices, custom_check = CALLBACK(src, .proc/check_menu, user), require_near = adjacency_check)
if(!check_menu(user))
return
switch(airlockcat)
if("Solid")
if(advanced_airlock_setting == 1)
- var/airlockpaint = show_radial_menu(user, src, solid_choices, radius = 42, custom_check = CALLBACK(src, .proc/check_menu, user), require_near = TRUE)
+ var/airlockpaint = show_radial_menu(user, src, solid_choices, radius = 42, custom_check = CALLBACK(src, .proc/check_menu, user), require_near = adjacency_check)
if(!check_menu(user))
return
switch(airlockpaint)
@@ -396,7 +398,7 @@ RLD
if("Glass")
if(advanced_airlock_setting == 1)
- var/airlockpaint = show_radial_menu(user, src , glass_choices, radius = 42, custom_check = CALLBACK(src, .proc/check_menu, user), require_near = TRUE)
+ var/airlockpaint = show_radial_menu(user, src , glass_choices, radius = 42, custom_check = CALLBACK(src, .proc/check_menu, user), require_near = adjacency_check)
if(!check_menu(user))
return
switch(airlockpaint)
diff --git a/code/game/objects/items/circuitboards/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machine_circuitboards.dm
index 1059a310eb..6989557127 100644
--- a/code/game/objects/items/circuitboards/machine_circuitboards.dm
+++ b/code/game/objects/items/circuitboards/machine_circuitboards.dm
@@ -983,3 +983,12 @@
name = "Ore Silo (Machine Board)"
build_path = /obj/machinery/ore_silo
req_components = list()
+
+/obj/item/circuitboard/machine/autobottler
+ name = "Auto-Bottler (Machine Board)"
+ build_path = /obj/machinery/rnd/production/protolathe/department/autobottler //Manips make you print things cheaper, even chems
+ req_components = list(/obj/item/stock_parts/matter_bin = 5,
+ /obj/item/stack/sheet/glass = 2,
+ /obj/item/stock_parts/capacitor = 1,
+ /obj/item/stack/cable_coil = 5,
+ /obj/item/reagent_containers/glass/beaker = 6) //So it can hold lots of chems
\ No newline at end of file
diff --git a/code/game/objects/items/devices/gps.dm b/code/game/objects/items/devices/gps.dm
index 9d1b670e71..1ba0e525fa 100644
--- a/code/game/objects/items/devices/gps.dm
+++ b/code/game/objects/items/devices/gps.dm
@@ -15,6 +15,8 @@ GLOBAL_LIST_EMPTY(GPS_list)
/obj/item/gps/examine(mob/user)
..()
+ var/turf/curr = get_turf(src)
+ to_chat(user, "The screen says: [get_area_name(curr, TRUE)] ([curr.x], [curr.y], [curr.z])")
to_chat(user, "Alt-click to switch it [tracking ? "off":"on"].")
/obj/item/gps/Initialize()
diff --git a/code/game/objects/items/implants/implant_track.dm b/code/game/objects/items/implants/implant_track.dm
index 4b7ae3bbac..4f81432a39 100644
--- a/code/game/objects/items/implants/implant_track.dm
+++ b/code/game/objects/items/implants/implant_track.dm
@@ -1,7 +1,19 @@
/obj/item/implant/tracking
name = "tracking implant"
desc = "Track with this."
- activated = 0
+ activated = FALSE
+ var/lifespan_postmortem = 10 MINUTES //for how many deciseconds after user death will the implant work?
+ var/allow_teleport = TRUE //will people implanted with this act as teleporter beacons?
+
+/obj/item/implant/tracking/c38
+ name = "TRAC implant"
+ desc = "A smaller tracking implant that supplies power for only a few minutes."
+ var/lifespan = 5 MINUTES //how many deciseconds does the implant last?
+ allow_teleport = FALSE
+
+/obj/item/implant/tracking/c38/Initialize()
+ . = ..()
+ QDEL_IN(src, lifespan)
/obj/item/implant/tracking/Initialize()
. = ..()
@@ -45,7 +57,7 @@
var/dat = {"Implant Specifications:
Name: Tracking Beacon
Life: 10 minutes after death of host
- Important Notes: None
+ Important Notes: Implant also works as a teleporter beacon.
Implant Details:
Function: Continuously transmits low power signal. Useful for tracking.
diff --git a/code/game/objects/items/manuals.dm b/code/game/objects/items/manuals.dm
index 6ae34e9ca2..d038ea7b4a 100644
--- a/code/game/objects/items/manuals.dm
+++ b/code/game/objects/items/manuals.dm
@@ -260,7 +260,7 @@
function pageloaded(myframe) {
document.getElementById("loading").style.display = "none";
myframe.style.display = "inline";
- }
+ }
You start skimming through the manual...
@@ -295,7 +295,7 @@
function pageloaded(myframe) {
document.getElementById("loading").style.display = "none";
myframe.style.display = "block";
- }
+ }
You start skimming through the manual...
diff --git a/code/game/objects/items/melee/misc.dm b/code/game/objects/items/melee/misc.dm
index 10b84917bb..d7c2f7f4f6 100644
--- a/code/game/objects/items/melee/misc.dm
+++ b/code/game/objects/items/melee/misc.dm
@@ -265,7 +265,7 @@
if (B && !QDELETED(B))
H.internal_organs -= B
qdel(B)
- new /obj/effect/gibspawner/generic(get_turf(H), H.dna)
+ H.spawn_gibs()
return (BRUTELOSS)
/obj/item/melee/classic_baton/telescopic/attack_self(mob/user)
diff --git a/code/game/objects/items/plushes.dm b/code/game/objects/items/plushes.dm
index cece0068b8..90e2efabc4 100644
--- a/code/game/objects/items/plushes.dm
+++ b/code/game/objects/items/plushes.dm
@@ -996,12 +996,12 @@
attack_verb = list("PR'd", "coded", "remembered")
/obj/item/toy/plush/catgirl/fermis
- name = "medcat plushie"
- desc = "An affectionate stuffed toy that resembles a certain medcat, comes complete with battery operated wagging tail!! You get the impression she's cheering you on to to find happiness and be kind to people."
- icon_state = "fermis"
- item_state = "fermis"
- attack_verb = list("cuddled", "petpatted", "wigglepurred")
- squeak_override = list('modular_citadel/sound/voice/merowr.ogg' = 1)
+ name = "medcat plushie"
+ desc = "An affectionate stuffed toy that resembles a certain medcat, comes complete with battery operated wagging tail!! You get the impression she's cheering you on to to find happiness and be kind to people."
+ icon_state = "fermis"
+ item_state = "fermis"
+ attack_verb = list("cuddled", "petpatted", "wigglepurred")
+ squeak_override = list('modular_citadel/sound/voice/merowr.ogg' = 1)
/obj/item/toy/plush/catgirl/mariaf
desc = "An adorable stuffed toy that resembles a very tall cat girl."
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index c085306892..0aa19b13ef 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -10,6 +10,7 @@
* Runed Metal (cult)
* Brass (clockwork cult)
* Bronze (bake brass)
+ * Cotton/Duracotton
*/
/*
@@ -186,31 +187,37 @@ GLOBAL_LIST_INIT(plasteel_recipes, list ( \
*/
GLOBAL_LIST_INIT(wood_recipes, list ( \
new/datum/stack_recipe("wooden sandals", /obj/item/clothing/shoes/sandal, 1), \
+ new/datum/stack_recipe("tiki mask", /obj/item/clothing/mask/gas/tiki_mask, 2), \
new/datum/stack_recipe("wood floor tile", /obj/item/stack/tile/wood, 1, 4, 20), \
new/datum/stack_recipe("wood table frame", /obj/structure/table_frame/wood, 2, time = 10), \
+ null, \
new/datum/stack_recipe("rifle stock", /obj/item/weaponcrafting/stock, 10, time = 40), \
new/datum/stack_recipe("rolling pin", /obj/item/kitchen/rollingpin, 2, time = 30), \
+ new/datum/stack_recipe("wooden buckler", /obj/item/shield/riot/buckler, 20, time = 40), \
+ new/datum/stack_recipe("baseball bat", /obj/item/melee/baseball_bat, 5, time = 15),\
+ null, \
new/datum/stack_recipe("wooden chair", /obj/structure/chair/wood/, 3, time = 10, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("winged wooden chair", /obj/structure/chair/wood/wings, 3, time = 10, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("plywood chair", /obj/structure/chair/comfy/plywood, 4, time = 10, one_per_turf = TRUE, on_floor = TRUE), \
+ null, \
new/datum/stack_recipe("wooden barricade", /obj/structure/barricade/wooden, 5, time = 50, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("wooden door", /obj/structure/mineral_door/wood, 10, time = 20, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("rustic wooden door", /obj/structure/mineral_door/woodrustic, 10, time = 20, one_per_turf = TRUE, on_floor = TRUE), \
+ null, \
+ new/datum/stack_recipe("wooden barrel", /obj/structure/fermenting_barrel, 10, time = 20, one_per_turf = TRUE, on_floor = TRUE),\
new/datum/stack_recipe("coffin", /obj/structure/closet/crate/coffin, 5, time = 15, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("book case", /obj/structure/bookcase, 4, time = 15, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("drying rack", /obj/machinery/smartfridge/drying_rack, 10, time = 15, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("dog bed", /obj/structure/bed/dogbed, 10, time = 10, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("dresser", /obj/structure/dresser, 10, time = 15, one_per_turf = TRUE, on_floor = TRUE), \
- new/datum/stack_recipe("picture frame", /obj/item/wallframe/picture, 1, time = 10),\
- new/datum/stack_recipe("display case chassis", /obj/structure/displaycase_chassis, 5, one_per_turf = TRUE, on_floor = TRUE), \
- new/datum/stack_recipe("loom", /obj/structure/loom, 10, time = 15, one_per_turf = TRUE, on_floor = TRUE), \
- new/datum/stack_recipe("wooden buckler", /obj/item/shield/riot/buckler, 20, time = 40), \
- new/datum/stack_recipe("apiary", /obj/structure/beebox, 40, time = 50),\
- new/datum/stack_recipe("tiki mask", /obj/item/clothing/mask/gas/tiki_mask, 2), \
- new/datum/stack_recipe("honey frame", /obj/item/honey_frame, 5, time = 10),\
new/datum/stack_recipe("ore box", /obj/structure/ore_box, 4, time = 50, one_per_turf = TRUE, on_floor = TRUE),\
new/datum/stack_recipe("wooden crate", /obj/structure/closet/crate/wooden, 6, time = 50, one_per_turf = TRUE, on_floor = TRUE),\
- new/datum/stack_recipe("baseball bat", /obj/item/melee/baseball_bat, 5, time = 15),\
+ new/datum/stack_recipe("display case chassis", /obj/structure/displaycase_chassis, 5, one_per_turf = TRUE, on_floor = TRUE), \
+ new/datum/stack_recipe("loom", /obj/structure/loom, 10, time = 15, one_per_turf = TRUE, on_floor = TRUE), \
+ new/datum/stack_recipe("apiary", /obj/structure/beebox, 40, time = 50),\
+ null, \
+ new/datum/stack_recipe("picture frame", /obj/item/wallframe/picture, 1, time = 10),\
+ new/datum/stack_recipe("honey frame", /obj/item/honey_frame, 5, time = 10),\
))
/obj/item/stack/sheet/mineral/wood
@@ -272,6 +279,9 @@ GLOBAL_LIST_INIT(cloth_recipes, list ( \
resistance_flags = FLAMMABLE
force = 0
throwforce = 0
+ pull_effort = 90
+ is_fabric = TRUE
+ loom_result = /obj/item/stack/sheet/silk
merge_type = /obj/item/stack/sheet/cloth
/obj/item/stack/sheet/cloth/Initialize(mapload, new_amount, merge = TRUE)
@@ -281,6 +291,22 @@ GLOBAL_LIST_INIT(cloth_recipes, list ( \
/obj/item/stack/sheet/cloth/ten
amount = 10
+/obj/item/stack/sheet/cloth/thirty
+ amount = 30
+
+/obj/item/stack/sheet/silk
+ name = "silk"
+ desc = "A long soft material. This one is just made out of cotton rather then any spiders or wyrms"
+ singular_name = "silk sheet"
+ icon_state = "sheet-silk"
+ item_state = "sheet-cloth"
+ novariants = TRUE
+ merge_type = /obj/item/stack/sheet/silk
+
+//obj/item/stack/sheet/silk/Initialize(mapload, new_amount, merge = TRUE)
+// recipes = GLOB.silk_recipes
+// return ..()
+
//Durathread fuck slash-asterisk comments
GLOBAL_LIST_INIT(durathread_recipes, list ( \
new/datum/stack_recipe("durathread jumpsuit", /obj/item/clothing/under/durathread, 4, time = 40),
@@ -649,6 +675,12 @@ new /datum/stack_recipe("paper frame door", /obj/structure/mineral_door/paperfra
pull_effort = 30
loom_result = /obj/item/stack/sheet/cloth
+/obj/item/stack/sheet/cotton/ten
+ amount = 10
+
+/obj/item/stack/sheet/cotton/thirty
+ amount = 30
+
/obj/item/stack/sheet/cotton/durathread
name = "raw durathread bundle"
desc = "A bundle of raw durathread ready to be spun on the loom."
diff --git a/code/game/objects/items/storage/dakis.dm b/code/game/objects/items/storage/dakis.dm
index 2703581a94..1939593c8e 100644
--- a/code/game/objects/items/storage/dakis.dm
+++ b/code/game/objects/items/storage/dakis.dm
@@ -7,7 +7,7 @@
desc = "A large pillow depicting a girl in a compromising position. Featuring as many dimensions as you."
icon = 'icons/obj/daki.dmi'
icon_state = "daki_base"
- slot_flags = SLOT_BACK
+ slot_flags = ITEM_SLOT_BACK
var/cooldowntime = 20
var/static/list/dakimakura_options = list("Callie","Casca","Chaika","Elisabeth","Foxy Grandpa","Haruko","Holo","Ian","Jolyne","Kurisu","Marie","Mugi","Nar'Sie","Patchouli","Plutia","Rei","Reisen","Naga","Squid","Squigly","Tomoko","Toriel","Umaru","Yaranaika","Yoko") //Kurisu is the ideal girl." - Me, Logos.
diff --git a/code/game/objects/items/teleportation.dm b/code/game/objects/items/teleportation.dm
index e16b0dd690..e0e875b739 100644
--- a/code/game/objects/items/teleportation.dm
+++ b/code/game/objects/items/teleportation.dm
@@ -79,7 +79,7 @@
continue
var/mob/living/M = W.imp_in
if (M.stat == DEAD)
- if (M.timeofdeath + 6000 < world.time)
+ if (M.timeofdeath + W.lifespan_postmortem < world.time)
continue
var/turf/tr = get_turf(M)
diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm
index de5a53fd9c..d78338d390 100644
--- a/code/game/objects/items/toys.dm
+++ b/code/game/objects/items/toys.dm
@@ -1293,8 +1293,8 @@
var/toysound = 'sound/machines/click.ogg'
/obj/item/toy/figure/New()
- desc = "A \"Space Life\" brand [src]."
- ..()
+ desc = "A \"Space Life\" brand [src]."
+ ..()
/obj/item/toy/figure/attack_self(mob/user as mob)
if(cooldown <= world.time)
@@ -1517,3 +1517,18 @@
/obj/item/toy/dummy/GetVoice()
return doll_name
+
+/obj/item/toy/seashell
+ name = "seashell"
+ desc = "May you always have a shell in your pocket and sand in your shoes. Whatever that's supposed to mean."
+ icon = 'icons/misc/beach.dmi'
+ icon_state = "shell1"
+ var/static/list/possible_colors = list("" = 2, COLOR_PURPLE_GRAY = 1, COLOR_OLIVE = 1, COLOR_PALE_BLUE_GRAY = 1, COLOR_RED_GRAY = 1)
+
+/obj/item/toy/seashell/Initialize()
+ . = ..()
+ pixel_x = rand(-5, 5)
+ pixel_y = rand(-5, 5)
+ icon_state = "shell[rand(1,3)]"
+ color = pickweight(possible_colors)
+ setDir(pick(GLOB.cardinals))
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/freezer.dm b/code/game/objects/structures/crates_lockers/closets/secure/freezer.dm
index 9dab3679fa..dd72eb6b5d 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/freezer.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/freezer.dm
@@ -61,6 +61,11 @@
..()
for(var/i = 0, i < 4, i++)
new /obj/item/reagent_containers/food/snacks/meat/slab/monkey(src)
+
+/obj/structure/closet/secure_closet/freezer/meat/open
+ req_access = null
+ locked = FALSE
+
/obj/structure/closet/secure_closet/freezer/fridge
name = "refrigerator"
@@ -73,6 +78,10 @@
for(var/i = 0, i < 2, i++)
new /obj/item/storage/fancy/egg_box(src)
+/obj/structure/closet/secure_closet/freezer/fridge/open
+ req_access = null
+ locked = FALSE
+
/obj/structure/closet/secure_closet/freezer/money
name = "freezer"
desc = "This contains cold hard cash."
diff --git a/code/game/objects/structures/fluff.dm b/code/game/objects/structures/fluff.dm
index baf0cf312f..736e58143e 100644
--- a/code/game/objects/structures/fluff.dm
+++ b/code/game/objects/structures/fluff.dm
@@ -182,3 +182,59 @@
icon_state = "snowlegion"
anchored = TRUE
deconstructible = FALSE
+
+/obj/structure/fluff/big_chain
+ name = "giant chain"
+ desc = "A towering link of chains leading up to the ceiling."
+ icon = 'icons/effects/32x96.dmi'
+ icon_state = "chain"
+ layer = ABOVE_OBJ_LAYER
+ anchored = TRUE
+ density = TRUE
+ deconstructible = FALSE
+
+/obj/structure/fluff/railing
+ name = "railing"
+ desc = "Basic railing meant to protect idiots like you from falling."
+ icon = 'icons/obj/fluff.dmi'
+ icon_state = "railing"
+ density = TRUE
+ anchored = TRUE
+ deconstructible = FALSE
+
+/obj/structure/fluff/railing/corner
+ icon_state = "railing_corner"
+ density = FALSE
+
+/obj/structure/fluff/beach_towel
+ name = "beach towel"
+ desc = "A towel decorated in various beach-themed designs."
+ icon = 'icons/obj/fluff.dmi'
+ icon_state = "railing"
+ density = FALSE
+ anchored = TRUE
+ deconstructible = FALSE
+
+/obj/structure/fluff/beach_umbrella
+ name = "beach umbrella"
+ desc = "A fancy umbrella designed to keep the sun off beach-goers."
+ icon = 'icons/obj/fluff.dmi'
+ icon_state = "brella"
+ density = FALSE
+ anchored = TRUE
+ deconstructible = FALSE
+
+/obj/structure/fluff/beach_umbrella/security
+ icon_state = "hos_brella"
+
+/obj/structure/fluff/beach_umbrella/science
+ icon_state = "rd_brella"
+
+/obj/structure/fluff/beach_umbrella/engine
+ icon_state = "ce_brella"
+
+/obj/structure/fluff/beach_umbrella/cap
+ icon_state = "cap_brella"
+
+/obj/structure/fluff/beach_umbrella/syndi
+ icon_state = "syndi_brella"
diff --git a/code/game/objects/structures/ghost_role_spawners.dm b/code/game/objects/structures/ghost_role_spawners.dm
index b820e93c7b..10f1f30e13 100644
--- a/code/game/objects/structures/ghost_role_spawners.dm
+++ b/code/game/objects/structures/ghost_role_spawners.dm
@@ -66,7 +66,7 @@
. = ..()
var/area/A = get_area(src)
if(A)
- notify_ghosts("An ash walker egg is ready to hatch in \the [A.name].", source = src, action=NOTIFY_ATTACK, flashwindow = FALSE, ignore_key = POLL_IGNORE_ASHWALKER)
+ notify_ghosts("An ash walker egg is ready to hatch in \the [A.name].", source = src, action=NOTIFY_ATTACK, flashwindow = FALSE, ignore_key = POLL_IGNORE_ASHWALKER, ignore_dnr_observers = TRUE)
/datum/outfit/ashwalker
name ="Ashwalker"
@@ -133,7 +133,7 @@
. = ..()
var/area/A = get_area(src)
if(!mapload && A)
- notify_ghosts("\A [initial(species.prefix)] golem shell has been completed in \the [A.name].", source = src, action=NOTIFY_ATTACK, flashwindow = FALSE, ignore_key = POLL_IGNORE_GOLEM)
+ notify_ghosts("\A [initial(species.prefix)] golem shell has been completed in \the [A.name].", source = src, action=NOTIFY_ATTACK, flashwindow = FALSE, ignore_key = POLL_IGNORE_GOLEM, ignore_dnr_observers = TRUE)
if(has_owner && creator)
flavour_text = "You are a Golem. You move slowly, but are highly resistant to heat and cold as well as blunt trauma. You are unable to wear clothes, but can still use most tools. \
Serve [creator], and assist [creator.p_them()] in completing [creator.p_their()] goals at any cost."
@@ -372,7 +372,7 @@
flavour_text = "You have been given a reprieve from your eternity of torment, to be [owner.name]'s friend for [owner.p_their()] short mortal coil. Be aware that if you do not live up to [owner.name]'s expectations, they can send you back to hell with a single thought. [owner.name]'s death will also return you to hell."
var/area/A = get_area(src)
if(!mapload && A)
- notify_ghosts("\A friendship shell has been completed in \the [A.name].", source = src, action=NOTIFY_ATTACK, flashwindow = FALSE)
+ notify_ghosts("\A friendship shell has been completed in \the [A.name].", source = src, action=NOTIFY_ATTACK, flashwindow = FALSE, ignore_dnr_observers = TRUE)
objectives = "Be [owner.name]'s friend, and keep [owner.name] alive, so you don't get sent back to hell."
spell = summoning_spell
diff --git a/code/game/objects/structures/lattice.dm b/code/game/objects/structures/lattice.dm
index d6f304653d..a6ba6424b1 100644
--- a/code/game/objects/structures/lattice.dm
+++ b/code/game/objects/structures/lattice.dm
@@ -100,6 +100,7 @@
number_of_rods = 2
smooth = SMOOTH_TRUE
canSmoothWith = null
+ obj_flags = CAN_BE_HIT | BLOCK_Z_FALL
/obj/structure/lattice/catwalk/deconstruction_hints(mob/user)
to_chat(user, "The supporting rods look like they could be cut.")
diff --git a/code/game/objects/structures/signs/signs_maps.dm b/code/game/objects/structures/signs/signs_maps.dm
index f3188b90c2..1e3bfd6d36 100644
--- a/code/game/objects/structures/signs/signs_maps.dm
+++ b/code/game/objects/structures/signs/signs_maps.dm
@@ -50,7 +50,8 @@
name = "bar"
desc = "A direction sign, pointing out which way the Bar is."
icon_state = "direction_bar"
- /obj/structure/sign/directions/cafe
+
+/obj/structure/sign/directions/cafe
name = "cafe"
desc = "A direction sign, pointing out which way the Cafe is."
icon_state = "direction_cafe"
diff --git a/code/game/objects/structures/stairs.dm b/code/game/objects/structures/stairs.dm
new file mode 100644
index 0000000000..bd657fe1e8
--- /dev/null
+++ b/code/game/objects/structures/stairs.dm
@@ -0,0 +1,130 @@
+#define STAIR_TERMINATOR_AUTOMATIC 0
+#define STAIR_TERMINATOR_NO 1
+#define STAIR_TERMINATOR_YES 2
+
+// dir determines the direction of travel to go upwards (due to lack of sprites, currently only 1 and 2 make sense)
+// stairs require /turf/open/openspace as the tile above them to work
+// multiple stair objects can be chained together; the Z level transition will happen on the final stair object in the chain
+
+/obj/structure/stairs
+ name = "stairs"
+ icon = 'icons/obj/stairs.dmi'
+ icon_state = "stairs"
+ anchored = TRUE
+
+ var/force_open_above = FALSE // replaces the turf above this stair obj with /turf/open/openspace
+ var/terminator_mode = STAIR_TERMINATOR_AUTOMATIC
+ var/turf/listeningTo
+
+/obj/structure/stairs/Initialize(mapload)
+ if(force_open_above)
+ force_open_above()
+ build_signal_listener()
+ update_surrounding()
+ return ..()
+
+/obj/structure/stairs/Destroy()
+ listeningTo = null
+ return ..()
+
+/obj/structure/stairs/Move() //Look this should never happen but...
+ . = ..()
+ if(force_open_above)
+ build_signal_listener()
+ update_surrounding()
+
+/obj/structure/stairs/proc/update_surrounding()
+ update_icon()
+ for(var/i in GLOB.cardinals)
+ var/turf/T = get_step(get_turf(src), i)
+ var/obj/structure/stairs/S = locate() in T
+ if(S)
+ S.update_icon()
+
+/obj/structure/stairs/Uncross(atom/movable/AM, turf/newloc)
+ if(!newloc || !AM)
+ return ..()
+ if(!isobserver(AM) && isTerminator() && (get_dir(src, newloc) == dir))
+ stair_ascend(AM)
+ return FALSE
+ return ..()
+
+/obj/structure/stairs/Cross(atom/movable/AM)
+ if(isTerminator() && (get_dir(src, AM) == dir))
+ return FALSE
+ return ..()
+
+/obj/structure/stairs/update_icon()
+ if(isTerminator())
+ icon_state = "stairs_t"
+ else
+ icon_state = "stairs"
+
+/obj/structure/stairs/proc/stair_ascend(atom/movable/AM)
+ var/turf/checking = get_step_multiz(get_turf(src), UP)
+ if(!istype(checking))
+ return
+ if(!checking.zPassIn(AM, UP, get_turf(src)))
+ return
+ var/turf/target = get_step_multiz(get_turf(src), (dir|UP))
+ if(istype(target) && !target.can_zFall(AM, null, get_step_multiz(target, DOWN))) //Don't throw them into a tile that will just dump them back down.
+ if(isliving(AM))
+ var/mob/living/L = AM
+ var/pulling = L.pulling
+ if(pulling)
+ L.pulling.forceMove(target)
+ L.forceMove(target)
+ L.start_pulling(pulling)
+ else
+ AM.forceMove(target)
+
+/obj/structure/stairs/vv_edit_var(var_name, var_value)
+ . = ..()
+ if(!.)
+ return
+ if(var_name != NAMEOF(src, force_open_above))
+ return
+ if(!var_value)
+ if(listeningTo)
+ UnregisterSignal(listeningTo, COMSIG_TURF_MULTIZ_NEW)
+ listeningTo = null
+ else
+ build_signal_listener()
+ force_open_above()
+
+/obj/structure/stairs/proc/build_signal_listener()
+ if(listeningTo)
+ UnregisterSignal(listeningTo, COMSIG_TURF_MULTIZ_NEW)
+ var/turf/open/openspace/T = get_step_multiz(get_turf(src), UP)
+ RegisterSignal(T, COMSIG_TURF_MULTIZ_NEW, .proc/on_multiz_new)
+ listeningTo = T
+
+/obj/structure/stairs/proc/force_open_above()
+ var/turf/open/openspace/T = get_step_multiz(get_turf(src), UP)
+ if(T && !istype(T))
+ T.ChangeTurf(/turf/open/openspace)
+
+/obj/structure/stairs/proc/on_multiz_new(turf/source, dir)
+ if(dir == UP)
+ var/turf/open/openspace/T = get_step_multiz(get_turf(src), UP)
+ if(T && !istype(T))
+ T.ChangeTurf(/turf/open/openspace)
+
+/obj/structure/stairs/intercept_zImpact(atom/movable/AM, levels = 1)
+ . = ..()
+ if(isTerminator())
+ . |= FALL_INTERCEPTED | FALL_NO_MESSAGE
+
+/obj/structure/stairs/proc/isTerminator() //If this is the last stair in a chain and should move mobs up
+ if(terminator_mode != STAIR_TERMINATOR_AUTOMATIC)
+ return (terminator_mode == STAIR_TERMINATOR_YES)
+ var/turf/T = get_turf(src)
+ if(!T)
+ return FALSE
+ var/turf/them = get_step(T, dir)
+ if(!them)
+ return FALSE
+ for(var/obj/structure/stairs/S in them)
+ if(S.dir == dir)
+ return FALSE
+ return TRUE
diff --git a/code/game/turfs/change_turf.dm b/code/game/turfs/change_turf.dm
index f1c5080c8f..6a055bbd35 100644
--- a/code/game/turfs/change_turf.dm
+++ b/code/game/turfs/change_turf.dm
@@ -15,7 +15,7 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list(
if(turf_type)
var/turf/newT = ChangeTurf(turf_type, baseturf_type, flags)
SSair.remove_from_active(newT)
- newT.CalculateAdjacentTurfs()
+ CALCULATE_ADJACENT_TURFS(newT)
SSair.add_to_active(newT,1)
/turf/proc/copyTurf(turf/T)
@@ -140,6 +140,8 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list(
newTurf.air = stashed_air
SSair.add_to_active(newTurf)
else
+ if(ispath(path,/turf/closed))
+ flags |= CHANGETURF_RECALC_ADJACENT
return ..()
// Take off the top layer turf and replace it with the next baseturf down
@@ -263,7 +265,10 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list(
//If you modify this function, ensure it works correctly with lateloaded map templates.
/turf/proc/AfterChange(flags) //called after a turf has been replaced in ChangeTurf()
levelupdate()
- CalculateAdjacentTurfs()
+ if(flags & CHANGETURF_RECALC_ADJACENT)
+ ImmediateCalculateAdjacentTurfs()
+ else
+ CALCULATE_ADJACENT_TURFS(src)
//update firedoor adjacency
var/list/turfs_to_check = get_adjacent_open_turfs(src) | src
diff --git a/code/game/turfs/open.dm b/code/game/turfs/open.dm
index 96c43c4106..efee9cfa5a 100644
--- a/code/game/turfs/open.dm
+++ b/code/game/turfs/open.dm
@@ -16,6 +16,22 @@
if(wet)
AddComponent(/datum/component/wet_floor, wet, INFINITY, 0, INFINITY, TRUE)
+//direction is direction of travel of A
+/turf/open/zPassIn(atom/movable/A, direction, turf/source)
+ return (direction == DOWN)
+
+//direction is direction of travel of A
+/turf/open/zPassOut(atom/movable/A, direction, turf/destination)
+ return (direction == UP)
+
+//direction is direction of travel of air
+/turf/open/zAirIn(direction, turf/source)
+ return (direction == DOWN)
+
+//direction is direction of travel of air
+/turf/open/zAirOut(direction, turf/source)
+ return (direction == UP)
+
/turf/open/MouseDrop_T(atom/dropping, mob/user)
. = ..()
if(dropping == user && isliving(user))
@@ -184,43 +200,14 @@
update_visuals()
current_cycle = times_fired
-
- //cache some vars
- var/list/atmos_adjacent_turfs = src.atmos_adjacent_turfs
-
- for(var/direction in GLOB.cardinals)
- var/turf/open/enemy_tile = get_step(src, direction)
- if(!istype(enemy_tile))
- if (atmos_adjacent_turfs)
- atmos_adjacent_turfs -= enemy_tile
- continue
+ ImmediateCalculateAdjacentTurfs()
+ for(var/i in atmos_adjacent_turfs)
+ var/turf/open/enemy_tile = i
var/datum/gas_mixture/enemy_air = enemy_tile.return_air()
-
- //only check this turf, if it didn't check us when it was initalized
- if(enemy_tile.current_cycle < times_fired)
- if(CANATMOSPASS(src, enemy_tile))
- LAZYINITLIST(atmos_adjacent_turfs)
- LAZYINITLIST(enemy_tile.atmos_adjacent_turfs)
- atmos_adjacent_turfs[enemy_tile] = TRUE
- enemy_tile.atmos_adjacent_turfs[src] = TRUE
- else
- if (atmos_adjacent_turfs)
- atmos_adjacent_turfs -= enemy_tile
- if (enemy_tile.atmos_adjacent_turfs)
- enemy_tile.atmos_adjacent_turfs -= src
- UNSETEMPTY(enemy_tile.atmos_adjacent_turfs)
- continue
- else
- if (!atmos_adjacent_turfs || !atmos_adjacent_turfs[enemy_tile])
- continue
-
if(!excited && air.compare(enemy_air))
//testing("Active turf found. Return value of compare(): [is_active]")
excited = TRUE
SSair.active_turfs |= src
- UNSETEMPTY(atmos_adjacent_turfs)
- if (atmos_adjacent_turfs)
- src.atmos_adjacent_turfs = atmos_adjacent_turfs
/turf/open/proc/GetHeatCapacity()
. = air.heat_capacity()
diff --git a/code/game/turfs/openspace/openspace.dm b/code/game/turfs/openspace/openspace.dm
new file mode 100644
index 0000000000..e68aabcd08
--- /dev/null
+++ b/code/game/turfs/openspace/openspace.dm
@@ -0,0 +1,134 @@
+/turf/open/openspace
+ name = "open space"
+ desc = "Watch your step!"
+ icon_state = "grey"
+ baseturfs = /turf/open/openspace
+ CanAtmosPassVertical = ATMOS_PASS_YES
+ //mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ var/can_cover_up = TRUE
+ var/can_build_on = TRUE
+
+/turf/open/openspace/debug/update_multiz()
+ ..()
+ return TRUE
+
+/turf/open/openspace/Initialize() // handle plane and layer here so that they don't cover other obs/turfs in Dream Maker
+ . = ..()
+ plane = FLOOR_OPENSPACE_PLANE
+ layer = OPENSPACE_LAYER
+ return INITIALIZE_HINT_LATELOAD
+
+/turf/open/openspace/LateInitialize()
+ update_multiz(TRUE, TRUE)
+
+/turf/open/openspace/Destroy()
+ vis_contents.len = 0
+ return ..()
+
+/turf/open/openspace/update_multiz(prune_on_fail = FALSE, init = FALSE)
+ . = ..()
+ var/turf/T = below()
+ if(!T)
+ vis_contents.len = 0
+ if(prune_on_fail)
+ ChangeTurf(/turf/open/floor/plating)
+ return FALSE
+ if(init)
+ vis_contents += T
+ return TRUE
+
+/turf/open/openspace/multiz_turf_del(turf/T, dir)
+ if(dir != DOWN)
+ return
+ update_multiz()
+
+/turf/open/openspace/multiz_turf_new(turf/T, dir)
+ if(dir != DOWN)
+ return
+ update_multiz()
+
+/turf/open/openspace/zAirIn()
+ return TRUE
+
+/turf/open/openspace/zAirOut()
+ return TRUE
+
+/turf/open/openspace/zPassIn(atom/movable/A, direction, turf/source)
+ return TRUE
+
+/turf/open/openspace/zPassOut(atom/movable/A, direction, turf/destination)
+ if(A.anchored)
+ return FALSE
+ for(var/obj/O in contents)
+ if(O.obj_flags & BLOCK_Z_FALL)
+ return FALSE
+ return TRUE
+
+/turf/open/openspace/proc/CanCoverUp()
+ return can_cover_up
+
+/turf/open/openspace/proc/CanBuildHere()
+ return can_build_on
+
+/turf/open/openspace/attackby(obj/item/C, mob/user, params)
+ ..()
+ if(!CanBuildHere())
+ return
+ if(istype(C, /obj/item/stack/rods))
+ var/obj/item/stack/rods/R = C
+ var/obj/structure/lattice/L = locate(/obj/structure/lattice, src)
+ var/obj/structure/lattice/catwalk/W = locate(/obj/structure/lattice/catwalk, src)
+ if(W)
+ to_chat(user, "There is already a catwalk here!")
+ return
+ if(L)
+ if(R.use(1))
+ to_chat(user, "You construct a catwalk.")
+ playsound(src, 'sound/weapons/genhit.ogg', 50, 1)
+ new/obj/structure/lattice/catwalk(src)
+ else
+ to_chat(user, "You need two rods to build a catwalk!")
+ return
+ if(R.use(1))
+ to_chat(user, "You construct a lattice.")
+ playsound(src, 'sound/weapons/genhit.ogg', 50, 1)
+ ReplaceWithLattice()
+ else
+ to_chat(user, "You need one rod to build a lattice.")
+ return
+ if(istype(C, /obj/item/stack/tile/plasteel))
+ if(!CanCoverUp())
+ return
+ var/obj/structure/lattice/L = locate(/obj/structure/lattice, src)
+ if(L)
+ var/obj/item/stack/tile/plasteel/S = C
+ if(S.use(1))
+ qdel(L)
+ playsound(src, 'sound/weapons/genhit.ogg', 50, 1)
+ to_chat(user, "You build a floor.")
+ PlaceOnTop(/turf/open/floor/plating)
+ else
+ to_chat(user, "You need one floor tile to build a floor!")
+ else
+ to_chat(user, "The plating is going to need some support! Place metal rods first.")
+
+/turf/open/openspace/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd)
+ if(!CanBuildHere())
+ return FALSE
+
+ switch(the_rcd.mode)
+ if(RCD_FLOORWALL)
+ var/obj/structure/lattice/L = locate(/obj/structure/lattice, src)
+ if(L)
+ return list("mode" = RCD_FLOORWALL, "delay" = 0, "cost" = 1)
+ else
+ return list("mode" = RCD_FLOORWALL, "delay" = 0, "cost" = 3)
+ return FALSE
+
+/turf/open/openspace/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, passed_mode)
+ switch(passed_mode)
+ if(RCD_FLOORWALL)
+ to_chat(user, "You build a floor.")
+ PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
+ return TRUE
+ return FALSE
diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm
index 9ebe8f3c19..a5931abf9d 100644
--- a/code/game/turfs/space/space.dm
+++ b/code/game/turfs/space/space.dm
@@ -12,7 +12,7 @@
var/destination_x
var/destination_y
- var/global/datum/gas_mixture/immutable/space/space_gas = new
+ var/static/datum/gas_mixture/immutable/space/space_gas = new
plane = PLANE_SPACE
layer = SPACE_LAYER
light_power = 0.25
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index ebf6f6626c..d618e457b0 100755
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -39,7 +39,7 @@
return FALSE
. = ..()
-/turf/Initialize()
+/turf/Initialize(mapload)
if(flags_1 & INITIALIZED_1)
stack_trace("Warning: [src]([type]) initialized multiple times!")
flags_1 |= INITIALIZED_1
@@ -62,12 +62,21 @@
add_overlay(/obj/effect/fullbright)
if(requires_activation)
- CalculateAdjacentTurfs()
+ CALCULATE_ADJACENT_TURFS(src)
SSair.add_to_active(src)
if (light_power && light_range)
update_light()
+ var/turf/T = SSmapping.get_turf_above(src)
+ if(T)
+ T.multiz_turf_new(src, DOWN)
+ SEND_SIGNAL(T, COMSIG_TURF_MULTIZ_NEW, src, DOWN)
+ T = SSmapping.get_turf_below(src)
+ if(T)
+ T.multiz_turf_new(src, UP)
+ SEND_SIGNAL(T, COMSIG_TURF_MULTIZ_NEW, src, UP)
+
if (opacity)
has_opaque_atom = TRUE
@@ -76,13 +85,19 @@
return INITIALIZE_HINT_NORMAL
/turf/proc/Initalize_Atmos(times_fired)
- CalculateAdjacentTurfs()
+ CALCULATE_ADJACENT_TURFS(src)
/turf/Destroy(force)
. = QDEL_HINT_IWILLGC
if(!changing_turf)
stack_trace("Incorrect turf deletion")
changing_turf = FALSE
+ var/turf/T = SSmapping.get_turf_above(src)
+ if(T)
+ T.multiz_turf_del(src, DOWN)
+ T = SSmapping.get_turf_below(src)
+ if(T)
+ T.multiz_turf_del(src, UP)
if(force)
..()
//this will completely wipe turf state
@@ -105,6 +120,60 @@
return
user.Move_Pulled(src)
+/turf/proc/multiz_turf_del(turf/T, dir)
+
+/turf/proc/multiz_turf_new(turf/T, dir)
+
+//zPassIn doesn't necessarily pass an atom!
+//direction is direction of travel of air
+/turf/proc/zPassIn(atom/movable/A, direction, turf/source)
+ return FALSE
+
+//direction is direction of travel of air
+/turf/proc/zPassOut(atom/movable/A, direction, turf/destination)
+ return FALSE
+
+//direction is direction of travel of air
+/turf/proc/zAirIn(direction, turf/source)
+ return FALSE
+
+//direction is direction of travel of air
+/turf/proc/zAirOut(direction, turf/source)
+ return FALSE
+
+/turf/proc/zImpact(atom/movable/A, levels = 1, turf/prev_turf)
+ var/flags = NONE
+ var/mov_name = A.name
+ for(var/i in contents)
+ var/atom/thing = i
+ flags |= thing.intercept_zImpact(A, levels)
+ if(flags & FALL_STOP_INTERCEPTING)
+ break
+ if(prev_turf && !(flags & FALL_NO_MESSAGE))
+ prev_turf.visible_message("[mov_name] falls through [prev_turf]!")
+ if(flags & FALL_INTERCEPTED)
+ return
+ if(zFall(A, ++levels))
+ return FALSE
+ A.visible_message("[A] crashes into [src]!")
+ A.onZImpact(src, levels)
+ return TRUE
+
+/turf/proc/can_zFall(atom/movable/A, levels = 1, turf/target)
+ return zPassOut(A, DOWN, target) && target.zPassIn(A, DOWN, src)
+
+/turf/proc/zFall(atom/movable/A, levels = 1, force = FALSE)
+ var/turf/target = get_step_multiz(src, DOWN)
+ if(!target || (!isobj(A) && !ismob(A)))
+ return FALSE
+ if(!force && (!can_zFall(A, levels, target) || !A.can_zFall(src, levels, target, DOWN)))
+ return FALSE
+ A.zfalling = TRUE
+ A.forceMove(target)
+ A.zfalling = FALSE
+ target.zImpact(A, levels, src)
+ return TRUE
+
/turf/proc/handleRCL(obj/item/twohanded/rcl/C, mob/user)
if(C.loaded)
for(var/obj/structure/cable/LC in src)
@@ -196,6 +265,9 @@
if(O.obj_flags & FROZEN)
O.make_unfrozen()
+ if(!AM.zfalling)
+ zFall(AM)
+
/turf/proc/is_plasteel_floor()
return FALSE
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index d7dd8db08d..7a222d1ebc 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -949,7 +949,7 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
| Uniform: |
- [uniform_select]
+ [uniform_select]
|
diff --git a/code/modules/admin/verbs/spawnobjasmob.dm b/code/modules/admin/verbs/spawnobjasmob.dm
index 621e6c3618..1d3cf95222 100644
--- a/code/modules/admin/verbs/spawnobjasmob.dm
+++ b/code/modules/admin/verbs/spawnobjasmob.dm
@@ -16,10 +16,10 @@
var/obj/chosen_obj = text2path(chosen)
var/list/settings = list(
- "mainsettings" = list(
- "name" = list("desc" = "Name", "type" = "string", "value" = "Bob"),
+ "mainsettings" = list(
+ "name" = list("desc" = "Name", "type" = "string", "value" = "Bob"),
"maxhealth" = list("desc" = "Max. health", "type" = "number", "value" = 100),
- "access" = list("desc" = "Access ID", "type" = "datum", "path" = "/obj/item/card/id", "value" = "Default"),
+ "access" = list("desc" = "Access ID", "type" = "datum", "path" = "/obj/item/card/id", "value" = "Default"),
"objtype" = list("desc" = "Base obj type", "type" = "datum", "path" = "/obj", "value" = "[chosen]"),
"googlyeyes" = list("desc" = "Googly eyes", "type" = "boolean", "value" = "No"),
"disableai" = list("desc" = "Disable AI", "type" = "boolean", "value" = "Yes"),
@@ -27,7 +27,7 @@
"dropitem" = list("desc" = "Drop obj on death", "type" = "boolean", "value" = "Yes"),
"mobtype" = list("desc" = "Base mob type", "type" = "datum", "path" = "/mob/living/simple_animal/hostile/mimic/copy", "value" = "/mob/living/simple_animal/hostile/mimic/copy"),
"ckey" = list("desc" = "ckey", "type" = "ckey", "value" = "none"),
- )
+ )
)
var/list/prefreturn = presentpreflikepicker(usr,"Customize mob", "Customize mob", Button1="Ok", width = 450, StealFocus = 1,Timeout = 0, settings=settings)
diff --git a/code/modules/antagonists/abductor/equipment/abduction_gear.dm b/code/modules/antagonists/abductor/equipment/abduction_gear.dm
index c1088cbb3d..37652d1887 100644
--- a/code/modules/antagonists/abductor/equipment/abduction_gear.dm
+++ b/code/modules/antagonists/abductor/equipment/abduction_gear.dm
@@ -393,23 +393,22 @@
name = "Dissection Guide"
icon_state = "alienpaper_words"
info = {"Dissection for Dummies
-
-
- 1.Acquire fresh specimen.
- 2.Put the specimen on operating table.
- 3.Apply surgical drapes, preparing for experimental dissection.
- 4.Apply scalpel to specimen's torso.
- 5.Clamp bleeders on specimen's torso with a hemostat.
- 6.Retract skin of specimen's torso with a retractor.
- 7.Apply scalpel again to specimen's torso.
- 8.Search through the specimen's torso with your hands to remove any superfluous organs.
- 9.Insert replacement gland (Retrieve one from gland storage).
- 10.Consider dressing the specimen back to not disturb the habitat.
- 11.Put the specimen in the experiment machinery.
- 12.Choose one of the machine options. The target will be analyzed and teleported to the selected drop-off point.
- 13.You will receive one supply credit, and the subject will be counted towards your quota.
-
-Congratulations! You are now trained for invasive xenobiology research!"}
+
+ 1.Acquire fresh specimen.
+ 2.Put the specimen on operating table.
+ 3.Apply surgical drapes, preparing for experimental dissection.
+ 4.Apply scalpel to specimen's torso.
+ 5.Clamp bleeders on specimen's torso with a hemostat.
+ 6.Retract skin of specimen's torso with a retractor.
+ 7.Apply scalpel again to specimen's torso.
+ 8.Search through the specimen's torso with your hands to remove any superfluous organs.
+ 9.Insert replacement gland (Retrieve one from gland storage).
+ 10.Consider dressing the specimen back to not disturb the habitat.
+ 11.Put the specimen in the experiment machinery.
+ 12.Choose one of the machine options. The target will be analyzed and teleported to the selected drop-off point.
+ 13.You will receive one supply credit, and the subject will be counted towards your quota.
+
+ Congratulations! You are now trained for invasive xenobiology research!"}
/obj/item/paper/guides/antag/abductor/update_icon()
return
diff --git a/code/modules/antagonists/clockcult/clock_items/clockwork_slab.dm b/code/modules/antagonists/clockcult/clock_items/clockwork_slab.dm
index ec712f2eec..7066109979 100644
--- a/code/modules/antagonists/clockcult/clock_items/clockwork_slab.dm
+++ b/code/modules/antagonists/clockcult/clock_items/clockwork_slab.dm
@@ -64,7 +64,7 @@
/obj/item/clockwork/slab/cyborg/janitor //six scriptures, plus a fabricator
quickbound = list(/datum/clockwork_scripture/abscond, /datum/clockwork_scripture/create_object/replicant, /datum/clockwork_scripture/create_object/sigil_of_transgression, \
- /datum/clockwork_scripture/create_object/stargazer, /datum/clockwork_scripture/create_object/ocular_warden, /datum/clockwork_scripture/create_object/mania_motor)
+ /datum/clockwork_scripture/create_object/stargazer, /datum/clockwork_scripture/create_object/ocular_warden, /datum/clockwork_scripture/create_object/mania_motor)
/obj/item/clockwork/slab/cyborg/service //six scriptures, plus xray vision
quickbound = list(/datum/clockwork_scripture/abscond, /datum/clockwork_scripture/create_object/replicant,/datum/clockwork_scripture/create_object/stargazer, \
diff --git a/code/modules/antagonists/clockcult/clock_items/construct_chassis.dm b/code/modules/antagonists/clockcult/clock_items/construct_chassis.dm
index 2be0fdde11..f53796f02a 100644
--- a/code/modules/antagonists/clockcult/clock_items/construct_chassis.dm
+++ b/code/modules/antagonists/clockcult/clock_items/construct_chassis.dm
@@ -15,7 +15,7 @@
. = ..()
var/area/A = get_area(src)
if(A && construct_type)
- notify_ghosts("A [construct_name] chassis has been created in [A.name]!", 'sound/magic/clockwork/fellowship_armory.ogg', source = src, action = NOTIFY_ATTACK, flashwindow = FALSE, ignore_key = POLL_IGNORE_CONSTRUCT)
+ notify_ghosts("A [construct_name] chassis has been created in [A.name]!", 'sound/magic/clockwork/fellowship_armory.ogg', source = src, action = NOTIFY_ATTACK, flashwindow = FALSE, ignore_key = POLL_IGNORE_CONSTRUCT, ignore_dnr_observers = TRUE)
GLOB.poi_list += src
LAZYADD(GLOB.mob_spawners[name], src)
@@ -39,7 +39,9 @@
. = ..()
//ATTACK GHOST IGNORING PARENT RETURN VALUE
-/obj/item/clockwork/construct_chassis/attack_ghost(mob/user)
+/obj/item/clockwork/construct_chassis/attack_ghost(mob/dead/observer/user)
+ if(!user.can_reenter_round())
+ return FALSE
if(!SSticker.mode)
to_chat(user, "You cannot use that before the game has started.")
return
diff --git a/code/modules/antagonists/cult/cult_items.dm b/code/modules/antagonists/cult/cult_items.dm
index 06ea2cbe0a..943e76b4ee 100644
--- a/code/modules/antagonists/cult/cult_items.dm
+++ b/code/modules/antagonists/cult/cult_items.dm
@@ -493,7 +493,7 @@
desc = "You peer within this smokey orb and glimpse terrible fates befalling the escape shuttle."
icon = 'icons/obj/cult.dmi'
icon_state ="shuttlecurse"
- var/global/curselimit = 0
+ var/static/curselimit = 0
/obj/item/shuttle_curse/attack_self(mob/living/user)
if(!iscultist(user))
@@ -530,7 +530,7 @@
playsound(user.loc, 'sound/effects/glassbr1.ogg', 50, 1)
qdel(src)
sleep(20)
- var/global/list/curses
+ var/static/list/curses
if(!curses)
curses = list("A fuel technician just slit his own throat and begged for death.",
"The shuttle's navigation programming was replaced by a file containing just two words: IT COMES.",
diff --git a/code/modules/antagonists/cult/runes.dm b/code/modules/antagonists/cult/runes.dm
index f0fc59834c..1cbf267277 100644
--- a/code/modules/antagonists/cult/runes.dm
+++ b/code/modules/antagonists/cult/runes.dm
@@ -249,7 +249,7 @@ structure_check() searches for nearby cultist structures required for the invoca
convertee.adjustFireLoss(-(burndamage * 0.75))
convertee.visible_message("[convertee] writhes in pain \
[brutedamage || burndamage ? "even as [convertee.p_their()] wounds heal and close" : "as the markings below [convertee.p_them()] glow a bloody red"]!", \
- "AAAAAAAAAAAAAA-")
+ "AAAAAAAAAAAAAA-")
SSticker.mode.add_cultist(convertee.mind, 1)
new /obj/item/melee/cultblade/dagger(get_turf(src))
convertee.mind.special_role = ROLE_CULTIST
@@ -848,10 +848,10 @@ structure_check() searches for nearby cultist structures required for the invoca
fail_invoke()
log_game("Manifest rune failed - too many summoned ghosts")
return list()
- notify_ghosts("Manifest rune invoked in [get_area(src)].", 'sound/effects/ghost2.ogg', source = src)
+ notify_ghosts("Manifest rune invoked in [get_area(src)].", 'sound/effects/ghost2.ogg', source = src, ignore_dnr_observers = TRUE)
var/list/ghosts_on_rune = list()
for(var/mob/dead/observer/O in T)
- if(O.client && !jobban_isbanned(O, ROLE_CULTIST) && !QDELETED(src) && !QDELETED(O))
+ if(!QDELETED(O) && O.client && !jobban_isbanned(O, ROLE_CULTIST) && !QDELETED(src) && O.can_reenter_round())
ghosts_on_rune += O
if(!ghosts_on_rune.len)
to_chat(user, "There are no spirits near [src]!")
diff --git a/code/modules/antagonists/nukeop/equipment/nuclear_challenge.dm b/code/modules/antagonists/nukeop/equipment/nuclear_challenge.dm
index 219084d3e0..0a223f8b10 100644
--- a/code/modules/antagonists/nukeop/equipment/nuclear_challenge.dm
+++ b/code/modules/antagonists/nukeop/equipment/nuclear_challenge.dm
@@ -64,7 +64,7 @@ GLOBAL_VAR_INIT(war_declared, FALSE)
for(var/obj/machinery/computer/camera_advanced/shuttle_docker/D in GLOB.jam_on_wardec)
D.jammed = TRUE
- GLOB.war_declared = TRUE
+ GLOB.war_declared = TRUE
var/list/nukeops = get_antag_minds(/datum/antagonist/nukeop)
var/actual_players = GLOB.joined_player_list.len - nukeops.len
var/tc_malus = 0
diff --git a/code/modules/antagonists/swarmer/swarmer.dm b/code/modules/antagonists/swarmer/swarmer.dm
index 87cee7586d..ce455d9e67 100644
--- a/code/modules/antagonists/swarmer/swarmer.dm
+++ b/code/modules/antagonists/swarmer/swarmer.dm
@@ -33,7 +33,7 @@
. = ..()
var/area/A = get_area(src)
if(A)
- notify_ghosts("A swarmer shell has been created in [A.name].", 'sound/effects/bin_close.ogg', source = src, action = NOTIFY_ATTACK, flashwindow = FALSE)
+ notify_ghosts("A swarmer shell has been created in [A.name].", 'sound/effects/bin_close.ogg', source = src, action = NOTIFY_ATTACK, flashwindow = FALSE, ignore_dnr_observers = TRUE)
/obj/effect/mob_spawn/swarmer/attack_hand(mob/living/user)
. = ..()
diff --git a/code/modules/antagonists/wizard/equipment/artefact.dm b/code/modules/antagonists/wizard/equipment/artefact.dm
index 38aa0ad7f5..f14872a0a4 100644
--- a/code/modules/antagonists/wizard/equipment/artefact.dm
+++ b/code/modules/antagonists/wizard/equipment/artefact.dm
@@ -340,9 +340,9 @@
to_chat(victim, "You feel a dark presence from [A.name]")
/obj/item/voodoo/suicide_act(mob/living/carbon/user)
- user.visible_message("[user] links the voodoo doll to [user.p_them()]self and sits on it, infinitely crushing [user.p_them()]self! It looks like [user.p_theyre()] trying to commit suicide!")
- user.gib()
- return(BRUTELOSS)
+ user.visible_message("[user] links the voodoo doll to [user.p_them()]self and sits on it, infinitely crushing [user.p_them()]self! It looks like [user.p_theyre()] trying to commit suicide!")
+ user.gib()
+ return(BRUTELOSS)
/obj/item/voodoo/fire_act(exposed_temperature, exposed_volume)
if(target)
diff --git a/code/modules/antagonists/wizard/equipment/spellbook.dm b/code/modules/antagonists/wizard/equipment/spellbook.dm
index 25b1cbb7af..2ca6d86e11 100644
--- a/code/modules/antagonists/wizard/equipment/spellbook.dm
+++ b/code/modules/antagonists/wizard/equipment/spellbook.dm
@@ -695,16 +695,16 @@
dat += {"
-
+ body { font-size: 80%; font-family: 'Lucida Grande', Verdana, Arial, Sans-Serif; }
+ ul#tabs { list-style-type: none; margin: 30px 0 0 0; padding: 0 0 0.3em 0; }
+ ul#tabs li { display: inline; }
+ ul#tabs li a { color: #42454a; background-color: #dedbde; border: 1px solid #c9c3ba; border-bottom: none; padding: 0.3em; text-decoration: none; }
+ ul#tabs li a:hover { background-color: #f1f0ee; }
+ ul#tabs li a.selected { color: #000; background-color: #f1f0ee; font-weight: bold; padding: 0.7em 0.3em 0.38em 0.3em; }
+ div.tabContent { border: 1px solid #c9c3ba; padding: 0.5em; background-color: #f1f0ee; }
+ div.tabContent.hide { display: none; }
+
+
"}
dat += {"[content]