From 7f925c3c2bd1bc5ecdbfe25c7e889d187e8d8e20 Mon Sep 17 00:00:00 2001 From: vode-code <65709050+vode-code@users.noreply.github.com> Date: Fri, 4 Jul 2025 16:10:08 -0700 Subject: [PATCH 01/44] mouse lifetime limit fixed (#8610) --- code/modules/mob/living/simple_animal/friendly/mouse.dm | 3 --- 1 file changed, 3 deletions(-) diff --git a/code/modules/mob/living/simple_animal/friendly/mouse.dm b/code/modules/mob/living/simple_animal/friendly/mouse.dm index cc3da5e05aa..e4680aebb17 100644 --- a/code/modules/mob/living/simple_animal/friendly/mouse.dm +++ b/code/modules/mob/living/simple_animal/friendly/mouse.dm @@ -86,9 +86,6 @@ squeals++ last_squealgain = world.time - else - if ((world.time - timeofdeath) > decompose_time) - dust() //Pixel offsetting as they scamper around From 8fc29226ac0238800cb40f530c2d8491718be0ca Mon Sep 17 00:00:00 2001 From: "Nestor Jr." Date: Fri, 4 Jul 2025 23:10:50 +0000 Subject: [PATCH 02/44] Automatic changelog generation for PR #8610 [ci skip] --- html/changelogs/AutoChangeLog-pr-8610.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-8610.yml diff --git a/html/changelogs/AutoChangeLog-pr-8610.yml b/html/changelogs/AutoChangeLog-pr-8610.yml new file mode 100644 index 00000000000..f81c629263e --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8610.yml @@ -0,0 +1,4 @@ +author: Chickenish +delete-after: true +changes: + - rscadd: Mice can now live. From 356ebb41937359087a3dabec9802887ae393ef0d Mon Sep 17 00:00:00 2001 From: vode-code <65709050+vode-code@users.noreply.github.com> Date: Wed, 9 Jul 2025 04:59:01 -0700 Subject: [PATCH 03/44] Mining generation fixes (#8618) * deepdrill now waits until map is loaded to define cave_gen * cave system now shares responsibility for defining ore data enables the existence of multiple processing units --- code/modules/mining/drilling/deep_drill.dm | 2 +- code/modules/mining/machine_processing.dm | 5 +++-- code/modules/random_map/automata/caves.dm | 4 ++++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/code/modules/mining/drilling/deep_drill.dm b/code/modules/mining/drilling/deep_drill.dm index 0c4f10389a2..f496ec8dda5 100644 --- a/code/modules/mining/drilling/deep_drill.dm +++ b/code/modules/mining/drilling/deep_drill.dm @@ -43,7 +43,7 @@ MATERIAL_PLASTIC = /obj/item/ore/coal ) -/obj/machinery/mining/deep_drill/Initialize() +/obj/machinery/mining/deep_drill/LateInitialize() . = ..() cave_gen = locate(/obj/cave_generator) update_icon() diff --git a/code/modules/mining/machine_processing.dm b/code/modules/mining/machine_processing.dm index fdbdc12c46b..de862f2b8ff 100644 --- a/code/modules/mining/machine_processing.dm +++ b/code/modules/mining/machine_processing.dm @@ -149,8 +149,9 @@ for(var/oretype in typesof(/ore)-/ore) var/ore/OD = new oretype() ore_data[OD.name] = OD - ores_processing[OD.name] = 0 - ores_stored[OD.name] = 0 + for(var/ore/OD in ore_data) + ores_processing[OD.name] = 0 + ores_stored[OD.name] = 0 spawn() //Locate our output and input machinery. diff --git a/code/modules/random_map/automata/caves.dm b/code/modules/random_map/automata/caves.dm index 5dac23f32e5..1695af497ea 100644 --- a/code/modules/random_map/automata/caves.dm +++ b/code/modules/random_map/automata/caves.dm @@ -40,6 +40,10 @@ // Create ore turfs. /datum/random_map/automata/cave_system/cleanup() + if(!ore_data || !ore_data.len) + for(var/oretype in typesof(/ore)-/ore) + var/ore/OD = new oretype() + ore_data[OD.name] = OD var/ore_count = round(map.len/20) while((ore_count>0) && (ore_turfs.len>0)) if(!priority_process) sleep(-1) From 439807dc7ac25eadeff6016c76051c285b94632a Mon Sep 17 00:00:00 2001 From: vode-code <65709050+vode-code@users.noreply.github.com> Date: Wed, 9 Jul 2025 04:59:36 -0700 Subject: [PATCH 04/44] Unknown Roachmind Fixes (#8611) * syntax fix to commandchain * input fix in commandchain * fill a few gaps * kaiser properly steps down upon death done slightly better --- .../carbon/superior_animal/roach/roach.dm | 4 ++-- .../carbon/superior_animal/roach/roachmind.dm | 2 +- .../superior_animal/roach/types/kaiser.dm | 19 +++++++++++++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/code/modules/mob/living/carbon/superior_animal/roach/roach.dm b/code/modules/mob/living/carbon/superior_animal/roach/roach.dm index 5de1e56f001..771cb0e171d 100644 --- a/code/modules/mob/living/carbon/superior_animal/roach/roach.dm +++ b/code/modules/mob/living/carbon/superior_animal/roach/roach.dm @@ -120,7 +120,7 @@ comrade.joinOvermind(overseer) overseer.leader = comrade else if(!overseer && comrade.overseer) // or if they have an overmind and we don't, we join - joinOvermind(overseer) + joinOvermind(comrade.overseer) if(/mob/living/carbon/superior_animal/roach/kaiser) // only one possibility requires code with kaiser if(istype(comrade, /mob/living/carbon/superior_animal/roach/fuhrer)) if(overseer && comrade.overseer) @@ -131,5 +131,5 @@ else if(overseer && !comrade.overseer) comrade.joinOvermind(overseer) else - if(!overseer && istype(comrade, /mob/living/carbon/superior_animal/roach/fuhrer) || istype(comrade, /mob/living/carbon/superior_animal/roach/kaiser) && comrade.overseer) + if(!overseer && (istype(comrade, /mob/living/carbon/superior_animal/roach/fuhrer) || istype(comrade, /mob/living/carbon/superior_animal/roach/kaiser)) && comrade.overseer) joinOvermind(comrade.overseer) diff --git a/code/modules/mob/living/carbon/superior_animal/roach/roachmind.dm b/code/modules/mob/living/carbon/superior_animal/roach/roachmind.dm index 5709077ab16..4df847b22bc 100644 --- a/code/modules/mob/living/carbon/superior_animal/roach/roachmind.dm +++ b/code/modules/mob/living/carbon/superior_animal/roach/roachmind.dm @@ -33,7 +33,7 @@ var/list/highest[] // an overmind ruled by a fuhrer is subordinate to one ruled by a kaiser. if(istype(leader, /mob/living/carbon/superior_animal/roach/kaiser)) highest += src - if(!istype(collatewith.leader, /mob/living/carbon/superior_animal/roach/kaiser)) + if(istype(collatewith.leader, /mob/living/carbon/superior_animal/roach/kaiser)) highest += collatewith if(length(highest) == 1) if(highest[1] == src) diff --git a/code/modules/mob/living/carbon/superior_animal/roach/types/kaiser.dm b/code/modules/mob/living/carbon/superior_animal/roach/types/kaiser.dm index 37951ccf651..45c8c8adb7e 100644 --- a/code/modules/mob/living/carbon/superior_animal/roach/types/kaiser.dm +++ b/code/modules/mob/living/carbon/superior_animal/roach/types/kaiser.dm @@ -106,6 +106,25 @@ Has ability of every roach. gas_sac.clear_reagents() return TRUE +/mob/living/carbon/superior_animal/roach/kaiser/leaveOvermind() + var/mob/living/carbon/superior_animal/roach/backup + overseer.removeHarrier(src) + for(var/mob/living/carbon/superior_animal/roach/tocheck in overseer.members) + if(istype(tocheck, /mob/living/carbon/superior_animal/roach/kaiser)) + overseer.leader = tocheck + overseer?.casualties.Remove(src) + overseer = null + return + else if(istype(tocheck, /mob/living/carbon/superior_animal/roach/fuhrer)) + backup = tocheck + for(var/datum/overmind/roachmind/subordinate in overseer.subordinates) //by this point if there even is a new leader, it is a "mere fuhrer". + subordinate.superior = null + overseer.subordinates.Cut() + overseer.leader = backup + if(!backup && !QDELETED(overseer)) // kaiser is always the leader + qdel(overseer) // disband + overseer = null + /mob/living/carbon/superior_animal/roach/kaiser/findTarget() . = ..() if(. && gas_attack()) From 4be4b72cf05a661839fdd291973f97b18bc794be Mon Sep 17 00:00:00 2001 From: vode-code <65709050+vode-code@users.noreply.github.com> Date: Wed, 9 Jul 2025 05:00:21 -0700 Subject: [PATCH 05/44] Cleaned stationary air tank code (#8595) * pipe air tanks moved to own type stationary air tank logic remade checks for pipe being of type tank removed maps updated * hotfix --- cev_eris.dme | 1 + code/ATMOSPHERICS/pipes.dm | 164 ------------- code/ATMOSPHERICS/tanks.dm | 221 ++++++++++++++++++ .../objects/items/devices/pipe_painter.dm | 2 +- maps/main_ship/eris_classic.dmm | 60 ++--- maps/main_ship/eris_smol.dmm | 42 ++-- maps/shuttle/hulk.dmm | 4 +- maps/shuttle/nerd.dmm | 2 +- maps/submaps/centcomm.dmm | 2 +- maps/submaps/pulsar.dmm | 2 +- 10 files changed, 279 insertions(+), 221 deletions(-) create mode 100644 code/ATMOSPHERICS/tanks.dm diff --git a/cev_eris.dme b/cev_eris.dme index 232ffbd9467..fcaf0286fd2 100644 --- a/cev_eris.dme +++ b/cev_eris.dme @@ -171,6 +171,7 @@ #include "code\ATMOSPHERICS\he_pipes.dm" #include "code\ATMOSPHERICS\mainspipe.dm" #include "code\ATMOSPHERICS\pipes.dm" +#include "code\ATMOSPHERICS\tanks.dm" #include "code\ATMOSPHERICS\components\portables_connector.dm" #include "code\ATMOSPHERICS\components\tvalve.dm" #include "code\ATMOSPHERICS\components\valve.dm" diff --git a/code/ATMOSPHERICS/pipes.dm b/code/ATMOSPHERICS/pipes.dm index ae48ca0d338..f86def37c21 100644 --- a/code/ATMOSPHERICS/pipes.dm +++ b/code/ATMOSPHERICS/pipes.dm @@ -72,8 +72,6 @@ return QDEL_HINT_QUEUE /obj/machinery/atmospherics/pipe/attackby(obj/item/I, mob/user) - if (istype(src, /obj/machinery/atmospherics/pipe/tank)) - return ..() if (istype(src, /obj/machinery/atmospherics/pipe/vent)) return ..() @@ -130,8 +128,6 @@ */ /obj/machinery/atmospherics/pipe/color_cache_name(var/obj/machinery/atmospherics/node) - if(istype(src, /obj/machinery/atmospherics/pipe/tank)) - return ..() if(istype(node, /obj/machinery/atmospherics/pipe/manifold) || istype(node, /obj/machinery/atmospherics/pipe/manifold4w)) if(pipe_color == node.pipe_color) @@ -1034,166 +1030,6 @@ icon_connect_type = "-supply" color = PIPE_COLOR_BLUE - -/obj/machinery/atmospherics/pipe/tank - icon = 'icons/atmos/tank.dmi' - icon_state = "air_map" - - name = "Pressure Tank" - desc = "A large vessel containing pressurized gas." - - volume = 10000 //in liters, 1 meters by 1 meters by 2 meters ~tweaked it a little to simulate a pressure tank without needing to recode them yet - var/start_pressure = 25*ONE_ATMOSPHERE - - level = BELOW_PLATING_LEVEL - dir = SOUTH - initialize_directions = SOUTH - density = TRUE - layer = ABOVE_WINDOW_LAYER - -/obj/machinery/atmospherics/pipe/tank/LateInitialize() - icon_state = "air" - initialize_directions = dir - ..() - -/obj/machinery/atmospherics/pipe/tank/Process() - if(!parent) - ..() - else - . = PROCESS_KILL - -/obj/machinery/atmospherics/pipe/tank/Destroy() - if(node1) - node1.disconnect(src) - - . = ..() - -/obj/machinery/atmospherics/pipe/tank/pipeline_expansion() - return list(node1) - -/obj/machinery/atmospherics/pipe/tank/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - add_underlay(T, node1, dir) - -/obj/machinery/atmospherics/pipe/tank/hide() - update_underlays() - -/obj/machinery/atmospherics/pipe/tank/atmos_init() - var/connect_direction = dir - - for(var/obj/machinery/atmospherics/target in get_step(src, connect_direction)) - if(target.initialize_directions & get_dir(target, src)) - if (check_connect_types(target, src)) - node1 = target - break - - update_underlays() - -/obj/machinery/atmospherics/pipe/tank/disconnect(obj/machinery/atmospherics/reference) - if(reference == node1) - if(istype(node1, /obj/machinery/atmospherics/pipe)) - QDEL_NULL(parent) - node1 = null - - update_underlays() - - return null - -/obj/machinery/atmospherics/pipe/tank/attackby(var/obj/item/W as obj, var/mob/user as mob) - if(istype(W, /obj/item/device/pipe_painter)) - return - -/obj/machinery/atmospherics/pipe/tank/air - name = "Pressure Tank (Air)" - icon_state = "air_map" - -/obj/machinery/atmospherics/pipe/tank/air/LateInitialize() - air_temporary = new - air_temporary.volume = volume - air_temporary.temperature = T20C - - air_temporary.adjust_multi("oxygen", (start_pressure*O2STANDARD)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature), \ - "nitrogen",(start_pressure*N2STANDARD)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature)) - - - ..() - icon_state = "air" - -/obj/machinery/atmospherics/pipe/tank/oxygen - name = "Pressure Tank (Oxygen)" - icon_state = "o2_map" - -/obj/machinery/atmospherics/pipe/tank/oxygen/LateInitialize() - air_temporary = new - air_temporary.volume = volume - air_temporary.temperature = T20C - - air_temporary.adjust_gas("oxygen", (start_pressure)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature)) - - ..() - icon_state = "o2" - -/obj/machinery/atmospherics/pipe/tank/nitrogen - name = "Pressure Tank (Nitrogen)" - icon_state = "n2_map" - -/obj/machinery/atmospherics/pipe/tank/nitrogen/LateInitialize() - air_temporary = new - air_temporary.volume = volume - air_temporary.temperature = T20C - - air_temporary.adjust_gas("nitrogen", (start_pressure)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature)) - - ..() - icon_state = "n2" - -/obj/machinery/atmospherics/pipe/tank/carbon_dioxide - name = "Pressure Tank (Carbon Dioxide)" - icon_state = "co2_map" - -/obj/machinery/atmospherics/pipe/tank/carbon_dioxide/LateInitialize() - air_temporary = new - air_temporary.volume = volume - air_temporary.temperature = T20C - - air_temporary.adjust_gas("carbon_dioxide", (start_pressure)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature)) - - ..() - icon_state = "co2" - -/obj/machinery/atmospherics/pipe/tank/plasma - name = "Pressure Tank (Plasma)" - description_antag = "Will blind people if they do not wear face-covering gear" - icon_state = "plasma_map" - -/obj/machinery/atmospherics/pipe/tank/plasma/LateInitialize() - air_temporary = new - air_temporary.volume = volume - air_temporary.temperature = T20C - - air_temporary.adjust_gas("plasma", (start_pressure)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature)) - - ..() - icon_state = "plasma" - -/obj/machinery/atmospherics/pipe/tank/nitrous_oxide - name = "Pressure Tank (Nitrous Oxide)" - icon_state = "n2o_map" - -/obj/machinery/atmospherics/pipe/tank/nitrous_oxide/LateInitialize() - air_temporary = new - air_temporary.volume = volume - air_temporary.temperature = T0C - - air_temporary.adjust_gas("sleeping_agent", (start_pressure)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature)) - - ..() - icon_state = "n2o" - /obj/machinery/atmospherics/pipe/vent icon = 'icons/obj/atmospherics/pipe_vent.dmi' icon_state = "intact" diff --git a/code/ATMOSPHERICS/tanks.dm b/code/ATMOSPHERICS/tanks.dm new file mode 100644 index 00000000000..21c30552bfb --- /dev/null +++ b/code/ATMOSPHERICS/tanks.dm @@ -0,0 +1,221 @@ +/obj/machinery/atmospherics/tank + icon = 'icons/atmos/tank.dmi' + icon_state = "air_map" + + name = "Pressure Tank" + desc = "A large vessel containing pressurized gas." + + var/volume = 10000 //in liters, 1 meters by 1 meters by 2 meters ~tweaked it a little to simulate a pressure tank without needing to recode them yet + var/start_pressure = 25*ONE_ATMOSPHERE + + level = BELOW_PLATING_LEVEL + dir = SOUTH + initialize_directions = SOUTH + density = TRUE + layer = ABOVE_WINDOW_LAYER + var/datum/gas_mixture/air_temporary // used when reconstructing a pipeline that broke + + var/obj/machinery/atmospherics/node + + var/datum/pipe_network/network + + use_power = NO_POWER_USE + + can_buckle = TRUE + buckle_require_restraints = 1 + buckle_lying = -1 + +/obj/machinery/atmospherics/tank/drain_power() + return -1 + +/obj/machinery/atmospherics/tank/LateInitialize() + icon_state = "air" + initialize_directions = dir + ..() + +/obj/machinery/atmospherics/tank/hides_under_flooring() + return level != 2 + +/obj/machinery/atmospherics/tank/return_air() + return air_temporary + +/obj/machinery/atmospherics/tank/build_network() + if(!network && node) + network = new /datum/pipe_network() + network.normal_members += src + network.build_network(node, src) + +/obj/machinery/atmospherics/tank/return_network(obj/machinery/atmospherics/reference) + build_network() + + if(reference==node) + return network + + return null + +/obj/machinery/atmospherics/tank/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network) + if(network == old_network) + network = new_network + + return TRUE + +/obj/machinery/atmospherics/tank/return_network_air(datum/pipe_network/reference) + var/list/results = list() + + if(air_temporary) + results += air_temporary + + return results + + +/obj/machinery/atmospherics/tank/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) + if(reference == node) + network = new_network + + if(new_network.normal_members.Find(src)) + return 0 + + new_network.normal_members += src + + return null + +/obj/machinery/atmospherics/tank/Process() + if(!network) + ..() + network?.update = 1 + else + . = PROCESS_KILL + +/obj/machinery/atmospherics/tank/Destroy() + if(node) + node.disconnect(src) + QDEL_NULL(network) + + if(air_temporary) + loc.assume_air(air_temporary) + QDEL_NULL(air_temporary) + + loc = null + node = null + + . = ..() + return QDEL_HINT_QUEUE + +/obj/machinery/atmospherics/tank/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + add_underlay(T, node, dir) + +/obj/machinery/atmospherics/tank/hide() + update_underlays() + +/obj/machinery/atmospherics/tank/atmos_init() + if(node) return + var/connect_direction = dir + + for(var/obj/machinery/atmospherics/target in get_step(src, connect_direction)) + if(target.initialize_directions & get_dir(target, src)) + if (check_connect_types(target, src)) + node = target + break + + update_underlays() + +/obj/machinery/atmospherics/tank/disconnect(obj/machinery/atmospherics/reference) + if(reference==node) + QDEL_NULL(network) + node = null + + update_underlays() + + return null + +/obj/machinery/atmospherics/tank/air + name = "Pressure Tank (Air)" + icon_state = "air_map" + +/obj/machinery/atmospherics/tank/air/LateInitialize() + air_temporary = new + air_temporary.volume = volume + air_temporary.temperature = T20C + + air_temporary.adjust_multi("oxygen", (start_pressure*O2STANDARD)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature), \ + "nitrogen",(start_pressure*N2STANDARD)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature)) + + + ..() + icon_state = "air" + +/obj/machinery/atmospherics/tank/oxygen + name = "Pressure Tank (Oxygen)" + icon_state = "o2_map" + +/obj/machinery/atmospherics/tank/oxygen/LateInitialize() + air_temporary = new + air_temporary.volume = volume + air_temporary.temperature = T20C + + air_temporary.adjust_gas("oxygen", (start_pressure)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature)) + + ..() + icon_state = "o2" + +/obj/machinery/atmospherics/tank/nitrogen + name = "Pressure Tank (Nitrogen)" + icon_state = "n2_map" + +/obj/machinery/atmospherics/tank/nitrogen/LateInitialize() + air_temporary = new + air_temporary.volume = volume + air_temporary.temperature = T20C + + air_temporary.adjust_gas("nitrogen", (start_pressure)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature)) + + ..() + icon_state = "n2" + +/obj/machinery/atmospherics/tank/carbon_dioxide + name = "Pressure Tank (Carbon Dioxide)" + icon_state = "co2_map" + +/obj/machinery/atmospherics/tank/carbon_dioxide/LateInitialize() + air_temporary = new + air_temporary.volume = volume + air_temporary.temperature = T20C + + air_temporary.adjust_gas("carbon_dioxide", (start_pressure)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature)) + + ..() + icon_state = "co2" + +/obj/machinery/atmospherics/tank/plasma + name = "Pressure Tank (Plasma)" + description_antag = "Will blind people if they do not wear face-covering gear" + icon_state = "plasma_map" + +/obj/machinery/atmospherics/tank/plasma/LateInitialize() + air_temporary = new + air_temporary.volume = volume + air_temporary.temperature = T20C + + air_temporary.adjust_gas("plasma", (start_pressure)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature)) + + ..() + icon_state = "plasma" + +/obj/machinery/atmospherics/tank/nitrous_oxide + name = "Pressure Tank (Nitrous Oxide)" + icon_state = "n2o_map" + +/obj/machinery/atmospherics/tank/nitrous_oxide/LateInitialize() + air_temporary = new + air_temporary.volume = volume + air_temporary.temperature = T0C + + air_temporary.adjust_gas("sleeping_agent", (start_pressure)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature)) + + ..() + icon_state = "n2o" \ No newline at end of file diff --git a/code/game/objects/items/devices/pipe_painter.dm b/code/game/objects/items/devices/pipe_painter.dm index 386216bf09b..31d157d3bcd 100644 --- a/code/game/objects/items/devices/pipe_painter.dm +++ b/code/game/objects/items/devices/pipe_painter.dm @@ -17,7 +17,7 @@ if(!proximity) return - if(!istype(A,/obj/machinery/atmospherics/pipe) || istype(A,/obj/machinery/atmospherics/pipe/tank) || istype(A,/obj/machinery/atmospherics/pipe/vent) || istype(A,/obj/machinery/atmospherics/pipe/simple/heat_exchanging) || istype(A,/obj/machinery/atmospherics/pipe/simple/insulated) || !in_range(user, A)) + if(!istype(A,/obj/machinery/atmospherics/pipe) || istype(A,/obj/machinery/atmospherics/pipe/vent) || istype(A,/obj/machinery/atmospherics/pipe/simple/heat_exchanging) || istype(A,/obj/machinery/atmospherics/pipe/simple/insulated) || !in_range(user, A)) return var/obj/machinery/atmospherics/pipe/P = A diff --git a/maps/main_ship/eris_classic.dmm b/maps/main_ship/eris_classic.dmm index 0f321fdc1e8..575d9db0a60 100644 --- a/maps/main_ship/eris_classic.dmm +++ b/maps/main_ship/eris_classic.dmm @@ -398,7 +398,7 @@ /turf/floor/tiled/techmaint_panels, /area/eris/maintenance/section1deck5central) "abm" = ( -/obj/machinery/atmospherics/pipe/tank/air, +/obj/machinery/atmospherics/tank/air, /turf/floor/tiled/techmaint, /area/eris/maintenance/section2deck5starboard) "abn" = ( @@ -1014,7 +1014,7 @@ /turf/floor/tiled/dark, /area/eris/security/prison) "acE" = ( -/obj/machinery/atmospherics/pipe/tank/air, +/obj/machinery/atmospherics/tank/air, /obj/machinery/camera/network/security{ dir = 8 }, @@ -6302,7 +6302,7 @@ /turf/floor/tiled/steel, /area/eris/hallway/side/eschangarb) "apa" = ( -/obj/machinery/atmospherics/pipe/tank/air{ +/obj/machinery/atmospherics/tank/air{ dir = 1; initialize_directions = 0 }, @@ -6865,7 +6865,7 @@ /turf/floor/tiled/dark, /area/eris/rnd/podbay) "aqs" = ( -/obj/machinery/atmospherics/pipe/tank/air, +/obj/machinery/atmospherics/tank/air, /turf/floor/tiled/dark, /area/eris/rnd/podbay) "aqt" = ( @@ -7554,7 +7554,7 @@ /turf/wall/low/with_glass/smart, /area/eris/hallway/side/section3starboard) "arR" = ( -/obj/machinery/atmospherics/pipe/tank/air{ +/obj/machinery/atmospherics/tank/air{ dir = 8 }, /turf/floor/tiled/techmaint, @@ -10595,7 +10595,7 @@ /turf/floor/plating/under, /area/eris/maintenance/section4deck5port) "azX" = ( -/obj/machinery/atmospherics/pipe/tank/oxygen{ +/obj/machinery/atmospherics/tank/oxygen{ dir = 4 }, /turf/floor/tiled/steel/brown_platform, @@ -11043,7 +11043,7 @@ /turf/floor/tiled/steel/cargo, /area/eris/maintenance/section3deck2starboard) "aAW" = ( -/obj/machinery/atmospherics/pipe/tank/plasma{ +/obj/machinery/atmospherics/tank/plasma{ dir = 4 }, /turf/floor/tiled/steel/brown_platform, @@ -14625,7 +14625,7 @@ /turf/floor/tiled/steel, /area/eris/hallway/side/eschangarb) "aLw" = ( -/obj/machinery/atmospherics/pipe/tank/air, +/obj/machinery/atmospherics/tank/air, /turf/floor/plating/under, /area/eris/maintenance/section3deck1central) "aLx" = ( @@ -15834,7 +15834,7 @@ /turf/floor/tiled/steel/bluecorner, /area/eris/security/prisoncells) "aPv" = ( -/obj/machinery/atmospherics/pipe/tank/air{ +/obj/machinery/atmospherics/tank/air{ dir = 8 }, /turf/floor/tiled/dark/gray_platform, @@ -16436,7 +16436,7 @@ /turf/wall, /area/eris/maintenance/section4deck5port) "aQw" = ( -/obj/machinery/atmospherics/pipe/tank/air{ +/obj/machinery/atmospherics/tank/air{ dir = 8 }, /turf/floor/plating, @@ -19232,7 +19232,7 @@ /turf/open, /area/eris/security/prisoncells) "aXa" = ( -/obj/machinery/atmospherics/pipe/tank/air{ +/obj/machinery/atmospherics/tank/air{ dir = 4 }, /turf/floor/plating/under, @@ -19313,7 +19313,7 @@ /turf/floor/plating/under, /area/eris/maintenance/section1deck4central) "aXm" = ( -/obj/machinery/atmospherics/pipe/tank/air{ +/obj/machinery/atmospherics/tank/air{ dir = 8 }, /turf/floor/plating/under, @@ -23081,7 +23081,7 @@ /turf/floor/tiled/techmaint, /area/eris/maintenance/section2deck4central) "beN" = ( -/obj/machinery/atmospherics/pipe/tank/air, +/obj/machinery/atmospherics/tank/air, /turf/floor/plating/under, /area/eris/maintenance/section2deck4central) "beO" = ( @@ -23090,7 +23090,7 @@ /turf/floor/plating/under, /area/eris/maintenance/section2deck4starboard) "beP" = ( -/obj/machinery/atmospherics/pipe/tank/air, +/obj/machinery/atmospherics/tank/air, /turf/floor/tiled/techmaint, /area/eris/maintenance/section3deck1central) "beQ" = ( @@ -30376,7 +30376,7 @@ /turf/floor/tiled/steel, /area/eris/hallway/main/section1) "bwC" = ( -/obj/machinery/atmospherics/pipe/tank/air{ +/obj/machinery/atmospherics/tank/air{ dir = 4 }, /obj/structure/railing, @@ -31242,7 +31242,7 @@ /turf/floor/tiled/steel, /area/eris/hallway/main/section2) "byA" = ( -/obj/machinery/atmospherics/pipe/tank/air{ +/obj/machinery/atmospherics/tank/air{ dir = 8 }, /obj/structure/railing, @@ -33830,7 +33830,7 @@ /turf/floor/plating, /area/eris/maintenance/section4deck4port) "bFc" = ( -/obj/machinery/atmospherics/pipe/tank/air{ +/obj/machinery/atmospherics/tank/air{ dir = 4 }, /obj/structure/railing{ @@ -34134,7 +34134,7 @@ }, /area/eris/command/tcommsat/chamber) "bFH" = ( -/obj/machinery/atmospherics/pipe/tank/air{ +/obj/machinery/atmospherics/tank/air{ dir = 8 }, /obj/structure/railing{ @@ -34869,7 +34869,7 @@ /turf/floor/wood, /area/eris/crew_quarters/hydroponics) "bHr" = ( -/obj/machinery/atmospherics/pipe/tank/air{ +/obj/machinery/atmospherics/tank/air{ dir = 4 }, /turf/floor/tiled/steel, @@ -58029,7 +58029,7 @@ /turf/floor/tiled/steel/techfloor_grid, /area/eris/engineering/breakroom) "cNa" = ( -/obj/machinery/atmospherics/pipe/tank/air{ +/obj/machinery/atmospherics/tank/air{ dir = 4 }, /turf/floor/plating/under, @@ -58918,13 +58918,13 @@ }, /area/shuttle/mining/station) "cPO" = ( -/obj/machinery/atmospherics/pipe/tank/air, +/obj/machinery/atmospherics/tank/air, /turf/shuttle/floor/mining{ icon_state = "12,21" }, /area/shuttle/mining/station) "cPP" = ( -/obj/machinery/atmospherics/pipe/tank/air, +/obj/machinery/atmospherics/tank/air, /turf/shuttle/floor/mining{ icon_state = "13,21" }, @@ -61159,7 +61159,7 @@ /turf/floor/tiled/techmaint, /area/eris/maintenance/section4deck3port) "cWa" = ( -/obj/machinery/atmospherics/pipe/tank/air{ +/obj/machinery/atmospherics/tank/air{ dir = 1; initialize_directions = 0 }, @@ -74419,7 +74419,7 @@ /turf/floor/plating/under, /area/eris/medical/medbay) "dBk" = ( -/obj/machinery/atmospherics/pipe/tank/oxygen{ +/obj/machinery/atmospherics/tank/oxygen{ dir = 8 }, /turf/floor/tiled/white/brown_platform, @@ -76993,7 +76993,7 @@ /area/eris/maintenance/section4deck2port) "dHx" = ( /obj/effect/floor_decal/rust, -/obj/machinery/atmospherics/pipe/tank/air{ +/obj/machinery/atmospherics/tank/air{ dir = 1; initialize_directions = 0 }, @@ -81558,7 +81558,7 @@ /turf/floor/tiled/techmaint, /area/eris/maintenance/section2deck1starboard) "dUS" = ( -/obj/machinery/atmospherics/pipe/tank/air, +/obj/machinery/atmospherics/tank/air, /turf/floor/tiled/techmaint, /area/eris/maintenance/section2deck1port) "dUT" = ( @@ -82964,7 +82964,7 @@ /turf/wall/low/with_glass/reinforced, /area/eris/quartermaster/office) "dYk" = ( -/obj/machinery/atmospherics/pipe/tank/air{ +/obj/machinery/atmospherics/tank/air{ dir = 8 }, /obj/structure/railing{ @@ -88887,7 +88887,7 @@ }, /area/shuttle/research/station) "enr" = ( -/obj/machinery/atmospherics/pipe/tank/air, +/obj/machinery/atmospherics/tank/air, /turf/shuttle/floor/science{ icon_state = "5,5" }, @@ -92886,7 +92886,7 @@ /turf/floor/tiled/steel, /area/eris/hallway/side/section3starboard) "exh" = ( -/obj/machinery/atmospherics/pipe/tank/air{ +/obj/machinery/atmospherics/tank/air{ dir = 8 }, /obj/machinery/alarm{ @@ -95710,7 +95710,7 @@ /turf/floor/tiled/white, /area/eris/rnd/xenobiology/xenoflora) "eDP" = ( -/obj/machinery/atmospherics/pipe/tank/air, +/obj/machinery/atmospherics/tank/air, /obj/structure/sign/atmos_air{ pixel_y = 32 }, diff --git a/maps/main_ship/eris_smol.dmm b/maps/main_ship/eris_smol.dmm index c5cfdf7338e..0ef1e18d883 100644 --- a/maps/main_ship/eris_smol.dmm +++ b/maps/main_ship/eris_smol.dmm @@ -1035,7 +1035,7 @@ /turf/floor/tiled/dark/panels, /area/eris/crew_quarters/firing_range) "acF" = ( -/obj/machinery/atmospherics/pipe/tank/air{ +/obj/machinery/atmospherics/tank/air{ dir = 4 }, /turf/floor/plating/under, @@ -1170,7 +1170,7 @@ /turf/floor/plating/under, /area/eris/neotheology/bioreactor) "acV" = ( -/obj/machinery/atmospherics/pipe/tank/air{ +/obj/machinery/atmospherics/tank/air{ dir = 8 }, /turf/floor/plating/under, @@ -13403,7 +13403,7 @@ /turf/floor/plating/under, /area/eris/maintenance/section2deck3port) "aGf" = ( -/obj/machinery/atmospherics/pipe/tank/air{ +/obj/machinery/atmospherics/tank/air{ dir = 8 }, /turf/floor/plating/under, @@ -20903,7 +20903,7 @@ /turf/floor/plating/under, /area/eris/maintenance/section2deck3port) "aYq" = ( -/obj/machinery/atmospherics/pipe/tank/air{ +/obj/machinery/atmospherics/tank/air{ dir = 8 }, /obj/structure/railing{ @@ -39583,7 +39583,7 @@ /turf/floor/tiled/steel/bar_dance, /area/eris/command/tcommsat/chamber) "bQs" = ( -/obj/machinery/atmospherics/pipe/tank/air{ +/obj/machinery/atmospherics/tank/air{ dir = 1; initialize_directions = 0 }, @@ -42835,7 +42835,7 @@ /turf/floor/plating/under, /area/eris/maintenance/section1deck1central) "bYe" = ( -/obj/machinery/atmospherics/pipe/tank/plasma{ +/obj/machinery/atmospherics/tank/plasma{ dir = 8 }, /turf/floor/tiled/techmaint_cargo, @@ -44844,7 +44844,7 @@ /turf/floor/tiled/steel, /area/eris/crew_quarters/fitness) "cdj" = ( -/obj/machinery/atmospherics/pipe/tank/air{ +/obj/machinery/atmospherics/tank/air{ dir = 1; initialize_directions = 0 }, @@ -50458,7 +50458,7 @@ /turf/floor/tiled/dark/cargo, /area/eris/crew_quarters/hydroponics) "crf" = ( -/obj/machinery/atmospherics/pipe/tank/air, +/obj/machinery/atmospherics/tank/air, /turf/floor/plating/under, /area/eris/maintenance/section2deck4port) "crg" = ( @@ -51700,7 +51700,7 @@ /turf/floor/plating/under, /area/eris/maintenance/disposal) "cum" = ( -/obj/machinery/atmospherics/pipe/tank/air{ +/obj/machinery/atmospherics/tank/air{ dir = 4; start_pressure = 740.5 }, @@ -55477,7 +55477,7 @@ /turf/floor/grass, /area/eris/crew_quarters/hydroponics/garden) "cDv" = ( -/obj/machinery/atmospherics/pipe/tank/oxygen, +/obj/machinery/atmospherics/tank/oxygen, /turf/floor/plating/under, /area/eris/crew_quarters/sleep/cryo) "cDw" = ( @@ -58144,13 +58144,13 @@ }, /area/shuttle/mining/station) "cJE" = ( -/obj/machinery/atmospherics/pipe/tank/air, +/obj/machinery/atmospherics/tank/air, /turf/shuttle/floor/mining{ icon_state = "12,21" }, /area/shuttle/mining/station) "cJF" = ( -/obj/machinery/atmospherics/pipe/tank/air, +/obj/machinery/atmospherics/tank/air, /turf/shuttle/floor/mining{ icon_state = "13,21" }, @@ -58184,7 +58184,7 @@ /turf/floor/plating/under, /area/eris/maintenance/section2deck1starboard) "cJL" = ( -/obj/machinery/atmospherics/pipe/tank/air{ +/obj/machinery/atmospherics/tank/air{ dir = 1; initialize_directions = 0 }, @@ -60188,7 +60188,7 @@ }, /area/shuttle/research/station) "cPi" = ( -/obj/machinery/atmospherics/pipe/tank/air, +/obj/machinery/atmospherics/tank/air, /turf/shuttle/floor/science{ icon_state = "5,5" }, @@ -60767,7 +60767,7 @@ /turf/floor/tiled/steel/gray_platform, /area/eris/crew_quarters/pubeva) "cQM" = ( -/obj/machinery/atmospherics/pipe/tank/oxygen{ +/obj/machinery/atmospherics/tank/oxygen{ dir = 4 }, /turf/floor/tiled/steel/brown_perforated, @@ -61041,7 +61041,7 @@ /turf/floor/plating/under, /area/eris/storage/tech) "cRy" = ( -/obj/machinery/atmospherics/pipe/tank/plasma{ +/obj/machinery/atmospherics/tank/plasma{ dir = 4 }, /turf/floor/tiled/steel/brown_perforated, @@ -63034,7 +63034,7 @@ /turf/floor/tiled/steel/monofloor, /area/eris/hallway/side/docks) "cXh" = ( -/obj/machinery/atmospherics/pipe/tank/air{ +/obj/machinery/atmospherics/tank/air{ dir = 4 }, /obj/structure/railing{ @@ -63082,7 +63082,7 @@ /turf/floor/plating/under, /area/eris/maintenance/section2deck2port) "cXl" = ( -/obj/machinery/atmospherics/pipe/tank/air{ +/obj/machinery/atmospherics/tank/air{ dir = 4 }, /obj/structure/railing, @@ -63820,7 +63820,7 @@ /turf/floor/tiled/white/brown_perforated, /area/eris/medical/medbreak) "cYR" = ( -/obj/machinery/atmospherics/pipe/tank/oxygen{ +/obj/machinery/atmospherics/tank/oxygen{ dir = 8 }, /obj/structure/railing, @@ -68339,7 +68339,7 @@ /turf/floor/tiled/steel/brown_perforated, /area/eris/quartermaster/miningdock) "djx" = ( -/obj/machinery/atmospherics/pipe/tank/air, +/obj/machinery/atmospherics/tank/air, /obj/structure/railing, /turf/floor/plating/under, /area/eris/maintenance/section2deck1starboard) @@ -73992,7 +73992,7 @@ /turf/floor/tiled/steel/cargo, /area/eris/engineering/foyer) "dwN" = ( -/obj/machinery/atmospherics/pipe/tank/air, +/obj/machinery/atmospherics/tank/air, /obj/machinery/alarm{ pixel_y = 26 }, diff --git a/maps/shuttle/hulk.dmm b/maps/shuttle/hulk.dmm index b98edd87c54..f5aace8a896 100644 --- a/maps/shuttle/hulk.dmm +++ b/maps/shuttle/hulk.dmm @@ -117,7 +117,7 @@ "sL" = (/turf/shuttle/wall/mining{icon_state = "1,19"},/area/space) "te" = (/obj/structure/shuttle_part/mining{icon_state = "5,1"},/turf/space,/area/space) "tf" = (/obj/machinery/atmospherics/unary/vent_pump/on{dir = 8},/turf/shuttle/floor/mining{icon_state = "10,14"},/area/space) -"tC" = (/obj/machinery/atmospherics/pipe/tank/air,/turf/shuttle/floor/mining{icon_state = "12,21"},/area/space) +"tC" = (/obj/machinery/atmospherics/tank/air,/turf/shuttle/floor/mining{icon_state = "12,21"},/area/space) "tW" = (/turf/shuttle/wall/mining{icon_state = "15,6"; opacity = 0},/area/space) "tZ" = (/obj/structure/table/standard,/obj/item/device/lighting/toggleable/lamp,/turf/shuttle/floor/mining{icon_state = "5,16"},/area/space) "ud" = (/obj/structure/shuttle_part/mining{icon_state = "10,0"},/turf/template_noop,/area/space) @@ -369,7 +369,7 @@ "Yf" = (/obj/structure/shuttle_part/mining{icon_state = "12,0"},/turf/template_noop,/area/space) "Yg" = (/obj/structure/shuttle_part/mining{icon_state = "3,14"},/turf/space,/area/space) "Yk" = (/turf/shuttle/wall/mining{icon_state = "7,18"},/area/space) -"Yq" = (/obj/machinery/atmospherics/pipe/tank/air,/turf/shuttle/floor/mining{icon_state = "13,21"},/area/space) +"Yq" = (/obj/machinery/atmospherics/tank/air,/turf/shuttle/floor/mining{icon_state = "13,21"},/area/space) "Yu" = (/turf/shuttle/wall/mining{icon_state = "11,22"},/area/space) "YK" = (/turf/shuttle/floor/mining{icon_state = "12,13"},/area/space) "YN" = (/obj/structure/cable/yellow{d1 = 4; d2 = 8; icon_state = "4-8"},/obj/machinery/light/small,/turf/shuttle/floor/mining{icon_state = "4,4"},/area/space) diff --git a/maps/shuttle/nerd.dmm b/maps/shuttle/nerd.dmm index 5e12ce90452..f9fa15dcf67 100644 --- a/maps/shuttle/nerd.dmm +++ b/maps/shuttle/nerd.dmm @@ -10,7 +10,7 @@ "bz" = (/turf/shuttle/wall/science{icon_state = "10,13"},/area/space) "bI" = (/obj/structure/shuttle_part/science{icon_state = "11,0"},/turf/template_noop,/area/space) "bK" = (/obj/machinery/light/small{dir = 1},/turf/shuttle/floor/science{icon_state = "10,12"},/area/space) -"cl" = (/obj/machinery/atmospherics/pipe/tank/air,/turf/shuttle/floor/science{icon_state = "5,5"},/area/space) +"cl" = (/obj/machinery/atmospherics/tank/air,/turf/shuttle/floor/science{icon_state = "5,5"},/area/space) "co" = (/turf/shuttle/wall/science{icon_state = "8,0"},/area/space) "cI" = (/obj/structure/table/standard,/obj/machinery/camera/network/research_outpost{dir = 1},/obj/machinery/recharger,/turf/shuttle/floor/science{icon_state = "7,17"},/area/space) "cM" = (/obj/machinery/atmospherics/pipe/simple/hidden/universal,/turf/shuttle/floor/science{icon_state = "5,4"},/area/space) diff --git a/maps/submaps/centcomm.dmm b/maps/submaps/centcomm.dmm index 40c1ed3fbd0..a4adab0ae35 100644 --- a/maps/submaps/centcomm.dmm +++ b/maps/submaps/centcomm.dmm @@ -2983,7 +2983,7 @@ /turf/space, /area/space) "oz" = ( -/obj/machinery/atmospherics/pipe/tank/air{ +/obj/machinery/atmospherics/tank/air{ dir = 4; start_pressure = 740.5 }, diff --git a/maps/submaps/pulsar.dmm b/maps/submaps/pulsar.dmm index 3a8846416dc..9afa0f086a7 100644 --- a/maps/submaps/pulsar.dmm +++ b/maps/submaps/pulsar.dmm @@ -1141,7 +1141,7 @@ /turf/floor/plating/under, /area/outpost/pulsar/maintenance) "LT" = ( -/obj/machinery/atmospherics/pipe/tank/air{ +/obj/machinery/atmospherics/tank/air{ dir = 8 }, /turf/floor/tiled/steel/brown_platform, From adae85b8ff49a48faca52d73c24eef7754d74901 Mon Sep 17 00:00:00 2001 From: vode-code <65709050+vode-code@users.noreply.github.com> Date: Wed, 9 Jul 2025 05:00:43 -0700 Subject: [PATCH 06/44] Dropped parameter fixed (#8612) * candle burnout drop now gives correct param to proc * slime toxin drop proc gives M as param instead of nothing * kleptomania fix --- code/game/objects/items/weapons/candle.dm | 2 +- code/modules/mob/inventory.dm | 2 +- code/modules/reagents/reagents/toxins.dm | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/code/game/objects/items/weapons/candle.dm b/code/game/objects/items/weapons/candle.dm index d490e7e0c13..0a9a68243c2 100644 --- a/code/game/objects/items/weapons/candle.dm +++ b/code/game/objects/items/weapons/candle.dm @@ -55,7 +55,7 @@ if(!wax) new/obj/item/trash/candle(src.loc) if(ismob(loc)) - src.dropped(usr) + src.dropped(loc) qdel(src) update_icon() if(istype(loc, /turf)) //start a fire if possible diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm index c647f21637c..26b9b9510bd 100644 --- a/code/modules/mob/inventory.dm +++ b/code/modules/mob/inventory.dm @@ -44,7 +44,7 @@ W.forceMove(get_turf(src)) W.layer = initial(W.layer) W.set_plane(initial(W.plane)) - W.dropped(usr) + W.dropped(src) // Removes an item from inventory and places it in the target atom. // If canremove or other conditions need to be checked then use unEquip instead. diff --git a/code/modules/reagents/reagents/toxins.dm b/code/modules/reagents/reagents/toxins.dm index 9183eb1161f..1a76b18968a 100644 --- a/code/modules/reagents/reagents/toxins.dm +++ b/code/modules/reagents/reagents/toxins.dm @@ -564,7 +564,7 @@ to_chat(M, SPAN_DANGER("Your flesh rapidly mutates!")) for(var/obj/item/W in H) //Check all items on the person if(istype(W, /obj/item/organ/external/robotic) || istype(W, /obj/item/implant)) //drop prosthetic limbs and implants, you are a slime now. - W.dropped() + W.dropped(M) H.set_species(SPECIES_SLIME) /datum/reagent/toxin/aslimetoxin @@ -595,7 +595,7 @@ if(istype(W, /obj/item/implant) || istype(W, /obj/item/organ/external/robotic)) //Check if item is implant or prosthetic if(istype(W, /obj/item/implant/core_implant/cruciform)) //If cruciform is present victim is gibbed instead of transformed cruciformed = TRUE - W.dropped() //use the baseline dropped() + W.dropped(M) //use the baseline dropped() continue W.layer = initial(W.layer) W.loc = M.loc From 7c51b581e12749afffd55d17383f2057e70ef064 Mon Sep 17 00:00:00 2001 From: "Nestor Jr." Date: Wed, 9 Jul 2025 12:00:54 +0000 Subject: [PATCH 07/44] Automatic changelog generation for PR #8595 [ci skip] --- html/changelogs/AutoChangeLog-pr-8595.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-8595.yml diff --git a/html/changelogs/AutoChangeLog-pr-8595.yml b/html/changelogs/AutoChangeLog-pr-8595.yml new file mode 100644 index 00000000000..29a9194ce41 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8595.yml @@ -0,0 +1,4 @@ +author: Chickenish +delete-after: true +changes: + - refactor: stationary Tanks refactored to unique type From e1e9a4764fed7ff6279bcb8d22696181494171a7 Mon Sep 17 00:00:00 2001 From: vode-code <65709050+vode-code@users.noreply.github.com> Date: Wed, 9 Jul 2025 05:01:04 -0700 Subject: [PATCH 08/44] autodoc now heals Iwounds correctly (#8596) * autodoc now heals internal wounds correctly internal organ rejuv now refreshes blood value autodoc now applies CE_ANTITOX while administering anti-tox * continues healing until finished * fixes index runtime in Process() in autodoc.dm --- code/modules/organs/internal/_internal.dm | 1 + code/modules/surgery/autodoc.dm | 26 ++++++++++++----------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/code/modules/organs/internal/_internal.dm b/code/modules/organs/internal/_internal.dm index 069c688417f..25db5140a56 100644 --- a/code/modules/organs/internal/_internal.dm +++ b/code/modules/organs/internal/_internal.dm @@ -405,6 +405,7 @@ /obj/item/organ/internal/rejuvenate() status = null + current_blood = initial(current_blood) for(var/woundtype in wounddatums) remove_wound(wounddatums[woundtype]) diff --git a/code/modules/surgery/autodoc.dm b/code/modules/surgery/autodoc.dm index 821ad4939e5..cb80d952e44 100644 --- a/code/modules/surgery/autodoc.dm +++ b/code/modules/surgery/autodoc.dm @@ -116,6 +116,7 @@ if(patchnote.surgery_operations & AUTODOC_TOXIN) to_chat(patient, SPAN_NOTICE("Administering anti-toxin to patient.")) patient.adjustToxLoss(-damage_heal_amount) + patient.add_chemical_effect(CE_ANTITOX, damage_heal_amount/10) if(!patient.getToxLoss()) patchnote.surgery_operations &= ~AUTODOC_TOXIN @@ -132,8 +133,8 @@ else if (patchnote.surgery_operations & AUTODOC_BLOOD) to_chat(patient, SPAN_NOTICE("Administering blood IV to patient.")) var/datum/reagent/organic/blood/blood = patient.vessel.reagent_list[1] - blood.volume += damage_heal_amount - if(blood.volume >= patient.vessel.total_volume) + blood.volume = min(blood.volume + damage_heal_amount, patient.vessel.maximum_volume) + if(blood.volume == patient.vessel.maximum_volume) patchnote.surgery_operations &= ~AUTODOC_BLOOD else if(patchnote.surgery_operations & AUTODOC_DAMAGE) @@ -164,30 +165,31 @@ if(istype(patchnote.organ, /obj/item/organ/internal)) var/obj/item/organ/internal/I = patchnote.organ to_chat(patient, SPAN_NOTICE("Treating internal wounds in the patient's [I.name].")) - SEND_SIGNAL_OLD(I, COMSIG_IWOUND_TREAT, TRUE, TRUE) - patchnote.surgery_operations &= ~AUTODOC_INTERNAL_WOUNDS + var/datum/internal_wound/wound = I.wounddatums[pick(I.wounddatums)] + if(istype(wound)) + wound.treatment(TRUE, TRUE) + if(!length(I.wounddatums)) patchnote.surgery_operations &= ~AUTODOC_INTERNAL_WOUNDS /datum/autodoc/Process() if(!patient) stop() + else if(current_step > picked_patchnotes.len) + stop() + scan_user(patient) + return while(!(picked_patchnotes[current_step].surgery_operations)) - if(current_step + 1 > picked_patchnotes.len) + current_step++ + if(current_step > picked_patchnotes.len) stop() scan_user(patient) return - else - current_step++ if(world.time > (start_op_time + processing_speed)) start_op_time = world.time patient.updatehealth() if(process_note(picked_patchnotes[current_step])) - if(current_step + 1 > picked_patchnotes.len) - stop() - scan_user(patient) - else - current_step++ + current_step++ /datum/autodoc/proc/fail() current_step++ From 6051e32294dd4617267fd958afd0baf485059535 Mon Sep 17 00:00:00 2001 From: "Nestor Jr." Date: Wed, 9 Jul 2025 12:01:21 +0000 Subject: [PATCH 09/44] Automatic changelog generation for PR #8612 [ci skip] --- html/changelogs/AutoChangeLog-pr-8612.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-8612.yml diff --git a/html/changelogs/AutoChangeLog-pr-8612.yml b/html/changelogs/AutoChangeLog-pr-8612.yml new file mode 100644 index 00000000000..d3e8bac789c --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8612.yml @@ -0,0 +1,4 @@ +author: Chickenish +delete-after: true +changes: + - bugfix: Holding a candle as it runs out of wax now functions properly. From 11444619513827f9b0976f8d103e43bf53b53b77 Mon Sep 17 00:00:00 2001 From: vode-code <65709050+vode-code@users.noreply.github.com> Date: Wed, 9 Jul 2025 05:01:33 -0700 Subject: [PATCH 10/44] hysteric and selfharm breakdowns now nullcheck after delay (#8613) --- code/modules/sanity/breakdowns.dm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/modules/sanity/breakdowns.dm b/code/modules/sanity/breakdowns.dm index 0d4a81d8745..ea95d2ac83c 100644 --- a/code/modules/sanity/breakdowns.dm +++ b/code/modules/sanity/breakdowns.dm @@ -201,7 +201,7 @@ /datum/breakdown/negative/selfharm/occur() spawn(delay) - ++holder.owner.suppress_communication + ++holder?.owner.suppress_communication return ..() /datum/breakdown/negative/selfharm/conclude() @@ -240,9 +240,9 @@ /datum/breakdown/negative/hysteric/occur() spawn(delay) - holder.owner.SetWeakened(4) - holder.owner.SetStunned(4) - ++holder.owner.suppress_communication + holder?.owner.SetWeakened(4) + holder?.owner.SetStunned(4) + ++holder?.owner.suppress_communication return ..() /datum/breakdown/negative/hysteric/conclude() From f2190c4f1319551ec5aee3c8ad19fff7355dede3 Mon Sep 17 00:00:00 2001 From: "Nestor Jr." Date: Wed, 9 Jul 2025 12:01:38 +0000 Subject: [PATCH 11/44] Automatic changelog generation for PR #8596 [ci skip] --- html/changelogs/AutoChangeLog-pr-8596.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-8596.yml diff --git a/html/changelogs/AutoChangeLog-pr-8596.yml b/html/changelogs/AutoChangeLog-pr-8596.yml new file mode 100644 index 00000000000..a275c006c75 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8596.yml @@ -0,0 +1,4 @@ +author: Chickenish +delete-after: true +changes: + - bugfix: fixed Autodoc From 9678fbc564fc69e918cee4420872454d4de8e93a Mon Sep 17 00:00:00 2001 From: "Nestor Jr." Date: Wed, 9 Jul 2025 12:02:04 +0000 Subject: [PATCH 12/44] Automatic changelog generation for PR #8613 [ci skip] --- html/changelogs/AutoChangeLog-pr-8613.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-8613.yml diff --git a/html/changelogs/AutoChangeLog-pr-8613.yml b/html/changelogs/AutoChangeLog-pr-8613.yml new file mode 100644 index 00000000000..04d1924f576 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8613.yml @@ -0,0 +1,5 @@ +author: Chickenish +delete-after: true +changes: + - bugfix: self-harm and hysteric breakdowns no longer cause a runtime when + they begin shortly before the associated human is obliterated. From 5cffc7f4dbd083bba5c0b148ecd66c6f7ac066eb Mon Sep 17 00:00:00 2001 From: vode-code <65709050+vode-code@users.noreply.github.com> Date: Fri, 18 Jul 2025 07:39:58 -0700 Subject: [PATCH 13/44] added turf check to get_export_price_multiplier (#8619) --- code/controllers/subsystems/trade.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/controllers/subsystems/trade.dm b/code/controllers/subsystems/trade.dm index 764d7bd4bb3..a14e2fc89a7 100644 --- a/code/controllers/subsystems/trade.dm +++ b/code/controllers/subsystems/trade.dm @@ -541,7 +541,7 @@ SUBSYSTEM_DEF(trade) create_log_entry("Export", guild_account.get_name(), invoice_contents_info, cost, FALSE, get_turf(senderBeacon)) /datum/controller/subsystem/trade/proc/get_export_price_multiplier(atom/movable/target) - if(!target || target.anchored) + if(!target || isturf(target) || target.anchored) return NONEXPORTABLE . = EXPORTABLE From 63e0f44f07c95dcada32fb88f49fc52a3fc498bf Mon Sep 17 00:00:00 2001 From: vode-code <65709050+vode-code@users.noreply.github.com> Date: Fri, 18 Jul 2025 08:00:23 -0700 Subject: [PATCH 14/44] Bleeding Limb Help-Grab rework and Wound-Clamping nerf (#8617) * special grab of bleedstop effect now clamps wound instead of old code clamped wounds are now unclamped when re-opened * pressured bodypart stops bleeding immediately --- code/modules/mob/mob_grab_specials.dm | 4 ++-- code/modules/organs/wound.dm | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/code/modules/mob/mob_grab_specials.dm b/code/modules/mob/mob_grab_specials.dm index 027cb9ce6e3..29b394e0ebc 100644 --- a/code/modules/mob/mob_grab_specials.dm +++ b/code/modules/mob/mob_grab_specials.dm @@ -74,8 +74,8 @@ else visible_message(SPAN_NOTICE("[user] finishes putting pressure on [H]'s wounds.")) for(var/datum/wound/W in bodypart.wounds) - W.current_stage++ - W.bleed_timer -= 5 + W.clamped = TRUE + bodypart.stopBleeding() //do not kill the grab diff --git a/code/modules/organs/wound.dm b/code/modules/organs/wound.dm index 3c0e65a0e9a..37d86a53b89 100644 --- a/code/modules/organs/wound.dm +++ b/code/modules/organs/wound.dm @@ -143,6 +143,7 @@ proc/open_wound(damage) src.damage += damage bleed_timer += damage + clamped = FALSE while(src.current_stage > 1 && src.damage_list[current_stage-1] <= src.damage / src.amount) src.current_stage-- From a492bbe5b489dc23d1dd0537480f2194f7cb5a26 Mon Sep 17 00:00:00 2001 From: "Nestor Jr." Date: Fri, 18 Jul 2025 15:00:55 +0000 Subject: [PATCH 15/44] Automatic changelog generation for PR #8617 [ci skip] --- html/changelogs/AutoChangeLog-pr-8617.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-8617.yml diff --git a/html/changelogs/AutoChangeLog-pr-8617.yml b/html/changelogs/AutoChangeLog-pr-8617.yml new file mode 100644 index 00000000000..688c64b11bf --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8617.yml @@ -0,0 +1,5 @@ +author: Chickenish +delete-after: true +changes: + - balance: Clamped wounds do not stay clamped when re-opened. + - balance: Pressuring wounds now clamps the wound until re-opened. From 5e2273f9491af7c47067bcea6c1f52b1547378d0 Mon Sep 17 00:00:00 2001 From: vode-code <65709050+vode-code@users.noreply.github.com> Date: Fri, 18 Jul 2025 08:01:09 -0700 Subject: [PATCH 16/44] non-tool tool no longer runtimes on failure (#8616) --- code/game/objects/items/weapons/tools/_tools.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/objects/items/weapons/tools/_tools.dm b/code/game/objects/items/weapons/tools/_tools.dm index 84553c2a515..be2bc5a2dfd 100644 --- a/code/game/objects/items/weapons/tools/_tools.dm +++ b/code/game/objects/items/weapons/tools/_tools.dm @@ -529,7 +529,7 @@ failtypes["damage"] = 2.5 // You can only fail with tools you are holding - if(user && T.loc == user) + if(user && loc == user) failtypes["slip"] = 2 failtypes["swing"] = 1 if(ishuman(user)) From b2bc4de5819f008eae55cdeea507f78f3dee24c9 Mon Sep 17 00:00:00 2001 From: vode-code <65709050+vode-code@users.noreply.github.com> Date: Tue, 19 Aug 2025 20:36:57 -0700 Subject: [PATCH 17/44] Coins Modernized (#8586) * all variant coins now have price tags all variant coins now have materials coin name typo fix moneybag now uses defines throughout Topic switch iron coin replaced with plasteel variant * all variant coins now have price tags all variant coins now have materials coin name typo fix moneybag now uses defines throughout Topic switch iron coin replaced with plasteel variant * update moneybag plasteel coin now referenced by name coins now move into turf regardless of current loc of moneybag --- code/__DEFINES/misc.dm | 4 ++-- .../objects/items/weapons/storage/wallets.dm | 2 +- code/modules/mining/abandonedcrates.dm | 2 +- code/modules/mining/coins.dm | 18 ++++++++++++++++-- code/modules/mining/money_bag.dm | 18 +++++++++--------- 5 files changed, 29 insertions(+), 15 deletions(-) diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index 81457891953..7ed87bd3a0d 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -106,10 +106,10 @@ #define COIN_GOLD "Gold coin" #define COIN_SILVER "Silver coin" #define COIN_DIAMOND "Diamond coin" -#define COIN_IRON "Iron coin" +#define COIN_PLASTEEL "Plasteel coin" #define COIN_PLASMA "Solid plasma coin" #define COIN_URANIUM "Uranium coin" -#define COIN_PLATINUM "Platunum coin" +#define COIN_PLATINUM "Platinum coin" #define SHARD_SHARD "shard" #define SHARD_SHRAPNEL "shrapnel" diff --git a/code/game/objects/items/weapons/storage/wallets.dm b/code/game/objects/items/weapons/storage/wallets.dm index 4fbe7fa18fe..e4794fea619 100644 --- a/code/game/objects/items/weapons/storage/wallets.dm +++ b/code/game/objects/items/weapons/storage/wallets.dm @@ -86,7 +86,7 @@ if(prob(50)) to_add = pick(/obj/item/spacecash/bundle/c10,/obj/item/spacecash/bundle/c100,/obj/item/spacecash/bundle/c1000,/obj/item/spacecash/bundle/c20,/obj/item/spacecash/bundle/c200,/obj/item/spacecash/bundle/c50,/obj/item/spacecash/bundle/c500) new to_add(src) - to_add = pick(/obj/item/coin/silver, /obj/item/coin/silver, /obj/item/coin/gold, /obj/item/coin/iron, /obj/item/coin/iron, /obj/item/coin/iron) + to_add = pick(/obj/item/coin/silver, /obj/item/coin/silver, /obj/item/coin/gold, /obj/item/coin/plasteel, /obj/item/coin/plasteel, /obj/item/coin/plasteel) new to_add(src) if(prob(20)) new /obj/item/card/id/randomassistant(src) diff --git a/code/modules/mining/abandonedcrates.dm b/code/modules/mining/abandonedcrates.dm index 882dfea2999..632fed74e37 100644 --- a/code/modules/mining/abandonedcrates.dm +++ b/code/modules/mining/abandonedcrates.dm @@ -68,7 +68,7 @@ if(63 to 64) var/t = rand(4,7) for(var/i = 0, i < t, ++i) - var/newcoin = pick(/obj/item/coin/silver, /obj/item/coin/silver, /obj/item/coin/silver, /obj/item/coin/iron, /obj/item/coin/iron, /obj/item/coin/iron, /obj/item/coin/gold, /obj/item/coin/diamond, /obj/item/coin/plasma, /obj/item/coin/uranium, /obj/item/coin/platinum) + var/newcoin = pick(/obj/item/coin/silver, /obj/item/coin/silver, /obj/item/coin/silver, /obj/item/coin/plasteel, /obj/item/coin/plasteel, /obj/item/coin/plasteel, /obj/item/coin/gold, /obj/item/coin/diamond, /obj/item/coin/plasma, /obj/item/coin/uranium, /obj/item/coin/platinum) new newcoin(src) if(65 to 68) var/t = rand(4,7) diff --git a/code/modules/mining/coins.dm b/code/modules/mining/coins.dm index 1a1b318014c..434638d6e99 100644 --- a/code/modules/mining/coins.dm +++ b/code/modules/mining/coins.dm @@ -20,30 +20,44 @@ /obj/item/coin/gold name = COIN_GOLD icon_state = "coin_gold" + matter = list(MATERIAL_GOLD = 0.2) + price_tag = 10 /obj/item/coin/silver name = COIN_SILVER icon_state = "coin_silver" + matter = list(MATERIAL_SILVER = 0.2) + price_tag = 8 /obj/item/coin/diamond name = COIN_DIAMOND icon_state = "coin_diamond" + matter = list(MATERIAL_DIAMOND = 0.2) + price_tag = 20 -/obj/item/coin/iron - name = COIN_IRON +/obj/item/coin/plasteel + name = COIN_PLASTEEL icon_state = "coin_iron" + matter = list(MATERIAL_PLASTEEL = 0.2) + price_tag = 6 /obj/item/coin/plasma name = COIN_PLASMA icon_state = "coin_plasma" + matter = list(MATERIAL_PLASMA = 0.2) + price_tag = 6 /obj/item/coin/uranium name = COIN_URANIUM icon_state = "coin_uranium" + matter = list(MATERIAL_URANIUM = 0.2) + price_tag = 10 /obj/item/coin/platinum name = COIN_PLATINUM icon_state = "coin_adamantine" + matter = list(MATERIAL_PLATINUM = 0.2) + price_tag = 16 /obj/item/coin/attackby(obj/item/W as obj, mob/user as mob) if(istype(W,/obj/item/stack/cable_coil)) diff --git a/code/modules/mining/money_bag.dm b/code/modules/mining/money_bag.dm index 03690ad9128..bfa17850c02 100644 --- a/code/modules/mining/money_bag.dm +++ b/code/modules/mining/money_bag.dm @@ -15,7 +15,7 @@ var/amt_gold = 0 var/amt_silver = 0 var/amt_diamond = 0 - var/amt_iron = 0 + var/amt_plasteel = 0 var/amt_plasma = 0 var/amt_uranium = 0 @@ -24,8 +24,8 @@ amt_diamond++; if (istype(C,/obj/item/coin/plasma)) amt_plasma++; - if (istype(C,/obj/item/coin/iron)) - amt_iron++; + if (istype(C,/obj/item/coin/plasteel)) + amt_plasteel++; if (istype(C,/obj/item/coin/silver)) amt_silver++; if (istype(C,/obj/item/coin/gold)) @@ -38,8 +38,8 @@ dat += text("Gold coins: [amt_gold] Remove one
") if (amt_silver) dat += text("Silver coins: [amt_silver] Remove one
") - if (amt_iron) - dat += text("Metal coins: [amt_iron] Remove one
") + if (amt_plasteel) + dat += text("Plasteel coins: [amt_plasteel] Remove one
") if (amt_diamond) dat += text("Diamond coins: [amt_diamond] Remove one
") if (amt_plasma) @@ -73,17 +73,17 @@ COIN = locate(/obj/item/coin/gold,src.contents) if(MATERIAL_SILVER) COIN = locate(/obj/item/coin/silver,src.contents) - if("iron") - COIN = locate(/obj/item/coin/iron,src.contents) + if(MATERIAL_PLASTEEL) + COIN = locate(/obj/item/coin/plasteel,src.contents) if(MATERIAL_DIAMOND) COIN = locate(/obj/item/coin/diamond,src.contents) - if("plasma") + if(MATERIAL_PLASMA) COIN = locate(/obj/item/coin/plasma,src.contents) if(MATERIAL_URANIUM) COIN = locate(/obj/item/coin/uranium,src.contents) if(!COIN) return - COIN.loc = src.loc + COIN.forceMove(get_turf(src)) return From 699f3b6b171e33fab5bfca2f6b2fe688076bbf65 Mon Sep 17 00:00:00 2001 From: vode-code <65709050+vode-code@users.noreply.github.com> Date: Tue, 19 Aug 2025 20:37:30 -0700 Subject: [PATCH 18/44] Water tank explosion runtime fix (#8624) * water does not wet floors in the deletion queue * flat dough now has nutriment data * undo that --- code/modules/reagents/reagents/core.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/reagents/reagents/core.dm b/code/modules/reagents/reagents/core.dm index b3e3c62df42..e907e0945f7 100644 --- a/code/modules/reagents/reagents/core.dm +++ b/code/modules/reagents/reagents/core.dm @@ -79,7 +79,7 @@ if (prob(5)) T.visible_message(SPAN_WARNING("The water sizzles as it lands on \the [T]!")) - else if(volume >= 10) + else if(volume >= 10 && !QDELING(T)) T.wet_floor(1) return TRUE From 372cabc859a019dab38125b5ace3f4d18c5b0c30 Mon Sep 17 00:00:00 2001 From: "Nestor Jr." Date: Wed, 20 Aug 2025 03:37:36 +0000 Subject: [PATCH 19/44] Automatic changelog generation for PR #8586 [ci skip] --- html/changelogs/AutoChangeLog-pr-8586.yml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-8586.yml diff --git a/html/changelogs/AutoChangeLog-pr-8586.yml b/html/changelogs/AutoChangeLog-pr-8586.yml new file mode 100644 index 00000000000..db1ee805e87 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8586.yml @@ -0,0 +1,9 @@ +author: Chickenish +delete-after: true +changes: + - rscadd: Added Plasteel Coin + - rscdel: Removed Iron Coin + - bugfix: Coins now moderately valuable for use and trade. + - spellcheck: Platinum coin no longer spelled as Platunum coin. + - code_imp: Moneybag no longer shoves coins in the person holding the + moneybag. From 8d2edcbc5ab70f89a8501559c4ead71336bbdcb4 Mon Sep 17 00:00:00 2001 From: vode-code <65709050+vode-code@users.noreply.github.com> Date: Tue, 9 Sep 2025 15:19:15 -0700 Subject: [PATCH 20/44] Qdel fixes (#8625) * fixes qdel hints for guns, internal organs, and atmospheric and computer-type machinery * missed two * s0 much to miss * dismantle_wall no longer qdels turf --- code/ATMOSPHERICS/atmospherics.dm | 2 +- .../components/binary_devices/binary_atmos_base.dm | 2 +- code/ATMOSPHERICS/components/unary/unary_base.dm | 2 +- code/game/machinery/alarm.dm | 2 +- code/game/machinery/computer/computer.dm | 2 +- code/game/machinery/doors/door.dm | 2 +- code/game/turfs/simulated/walls.dm | 4 +++- code/modules/organs/internal/_internal.dm | 2 +- code/modules/projectiles/gun.dm | 2 +- 9 files changed, 11 insertions(+), 9 deletions(-) diff --git a/code/ATMOSPHERICS/atmospherics.dm b/code/ATMOSPHERICS/atmospherics.dm index 317e27d4d4c..d9467989c30 100644 --- a/code/ATMOSPHERICS/atmospherics.dm +++ b/code/ATMOSPHERICS/atmospherics.dm @@ -48,7 +48,7 @@ Pipelines + Other Objects -> Pipe network /obj/machinery/atmospherics/Destroy() GLOB.atmos_machinery -= src - ..() + . = ..() /obj/machinery/atmospherics/proc/atmos_init() return diff --git a/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm b/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm index 96d914be2ab..f6cd55f77cb 100644 --- a/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm +++ b/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm @@ -56,7 +56,7 @@ node2.disconnect(src) QDEL_NULL(network2) node2 = null - ..() + . = ..() /obj/machinery/atmospherics/binary/atmos_init() diff --git a/code/ATMOSPHERICS/components/unary/unary_base.dm b/code/ATMOSPHERICS/components/unary/unary_base.dm index aa62e93caea..3c5508530ae 100644 --- a/code/ATMOSPHERICS/components/unary/unary_base.dm +++ b/code/ATMOSPHERICS/components/unary/unary_base.dm @@ -28,7 +28,7 @@ node1.disconnect(src) QDEL_NULL(network) node1 = null - ..() + . = ..() /obj/machinery/atmospherics/unary/atmos_init() diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm index 12f408b42ef..622b5b938d9 100644 --- a/code/game/machinery/alarm.dm +++ b/code/game/machinery/alarm.dm @@ -1193,7 +1193,7 @@ FIRE ALARM /obj/machinery/firealarm/Destroy() GLOB.firealarm_list -= src - ..() + . = ..() /* FIRE ALARM CIRCUIT diff --git a/code/game/machinery/computer/computer.dm b/code/game/machinery/computer/computer.dm index 7e5e36a2e6b..c2da5fc236f 100644 --- a/code/game/machinery/computer/computer.dm +++ b/code/game/machinery/computer/computer.dm @@ -22,7 +22,7 @@ /obj/machinery/computer/Destroy() GLOB.computer_list -= src - ..() + . = ..() /obj/machinery/computer/Process() if(stat & (NOPOWER|BROKEN)) diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 5741b5f4dd3..030d1994869 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -51,7 +51,7 @@ /obj/machinery/door/Destroy() GLOB.all_doors -= src - ..() + . = ..() /obj/machinery/door/can_prevent_fall(above) return above ? density : null diff --git a/code/game/turfs/simulated/walls.dm b/code/game/turfs/simulated/walls.dm index a9c97abf497..5adc512afab 100644 --- a/code/game/turfs/simulated/walls.dm +++ b/code/game/turfs/simulated/walls.dm @@ -69,7 +69,9 @@ girder.is_low = is_low_wall girder.is_reinforced = is_reinforced girder.update_icon() - qdel(src) + remove_neighbour_connections() + ChangeTurf(/turf/floor/plating) + updateVisibility(src) /turf/wall/get_matter() return list(MATERIAL_STEEL = 5) diff --git a/code/modules/organs/internal/_internal.dm b/code/modules/organs/internal/_internal.dm index 25db5140a56..447f0bea084 100644 --- a/code/modules/organs/internal/_internal.dm +++ b/code/modules/organs/internal/_internal.dm @@ -56,7 +56,7 @@ UnregisterSignal(src, COMSIG_IORGAN_ADD_WOUND) UnregisterSignal(src, COMSIG_IORGAN_REMOVE_WOUND) UnregisterSignal(src, COMSIG_IORGAN_REFRESH_SELF) - ..() + . = ..() /obj/item/organ/internal/removed() UnregisterSignal(parent, COMSIG_IORGAN_WOUND_COUNT) diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 68adc53e1f1..e3e6e8c9b8c 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -230,7 +230,7 @@ if(flashlight_attachment) flashlight_attachment.forceMove(get_turf(src)) flashlight_attachment = null - ..() + . = ..() /obj/item/gun/proc/set_item_state(state, hands = TRUE, back = FALSE, onsuit = FALSE) var/wield_state From 4c76da880a1272c06766291c0fc69a6662b1b0fc Mon Sep 17 00:00:00 2001 From: vode-code <65709050+vode-code@users.noreply.github.com> Date: Tue, 9 Sep 2025 15:19:30 -0700 Subject: [PATCH 21/44] fixes missing parenthesis (#8626) --- code/modules/mob/living/carbon/human/human_damage.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human_damage.dm index d38ef6b67fb..5ac925a2773 100644 --- a/code/modules/mob/living/carbon/human/human_damage.dm +++ b/code/modules/mob/living/carbon/human/human_damage.dm @@ -345,7 +345,7 @@ This function restores all organs. organ = def_zone else if(!def_zone) - if(!damagetype == TOX) // global application + if(!(damagetype == TOX)) // global application def_zone = ran_zone(def_zone) organ = get_organ(check_zone(def_zone)) From 51c157f8ab067efbe73631654158ca8d12794f2b Mon Sep 17 00:00:00 2001 From: "Nestor Jr." Date: Tue, 9 Sep 2025 22:19:56 +0000 Subject: [PATCH 22/44] Automatic changelog generation for PR #8625 [ci skip] --- html/changelogs/AutoChangeLog-pr-8625.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-8625.yml diff --git a/html/changelogs/AutoChangeLog-pr-8625.yml b/html/changelogs/AutoChangeLog-pr-8625.yml new file mode 100644 index 00000000000..a7f91c96f66 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8625.yml @@ -0,0 +1,5 @@ +author: Chickenish +delete-after: true +changes: + - bugfix: dismantling walls no longer results in GC errors. + - code_imp: Qdel hints are now better From cab55365c48ff2d5f2ef16fcae44cfc1525b01d2 Mon Sep 17 00:00:00 2001 From: "Nestor Jr." Date: Tue, 9 Sep 2025 22:19:58 +0000 Subject: [PATCH 23/44] Automatic changelog generation for PR #8626 [ci skip] --- html/changelogs/AutoChangeLog-pr-8626.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-8626.yml diff --git a/html/changelogs/AutoChangeLog-pr-8626.yml b/html/changelogs/AutoChangeLog-pr-8626.yml new file mode 100644 index 00000000000..21592f367e8 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8626.yml @@ -0,0 +1,4 @@ +author: Chickenish +delete-after: true +changes: + - bugfix: fixed a mistake with vague attack logic From 998e60b1d0a4b6716aa61edd76d6f69346e9420d Mon Sep 17 00:00:00 2001 From: vode-code <65709050+vode-code@users.noreply.github.com> Date: Tue, 9 Sep 2025 15:25:01 -0700 Subject: [PATCH 24/44] Rigged flashlights no longer runtime during Process() (#8620) * adds null check to flashlight process after rigged cell can explode * Update code/game/objects/items/devices/lighting/toggleable/flashlight.dm Co-authored-by: SirRichardFrancis <65828539+SirRichardFrancis@users.noreply.github.com> --------- Co-authored-by: Humonitarian <61586732+Humonitarian@users.noreply.github.com> Co-authored-by: SirRichardFrancis <65828539+SirRichardFrancis@users.noreply.github.com> --- .../objects/items/devices/lighting/toggleable/flashlight.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/objects/items/devices/lighting/toggleable/flashlight.dm b/code/game/objects/items/devices/lighting/toggleable/flashlight.dm index 4287451346b..35293fa94ec 100644 --- a/code/game/objects/items/devices/lighting/toggleable/flashlight.dm +++ b/code/game/objects/items/devices/lighting/toggleable/flashlight.dm @@ -243,7 +243,7 @@ if(ismob(src.loc)) to_chat(src.loc, SPAN_WARNING("Your flashlight dies. You are alone now.")) turn_off() - else if(cell.percent() <= 25) + else if(cell && (cell.percent() <= 25)) apply_power_deficiency() /obj/item/device/lighting/toggleable/flashlight/attack(mob/living/M, mob/living/user) From 73c452a1efc8b5b7400c2b59829b3408dff7b19f Mon Sep 17 00:00:00 2001 From: vode-code <65709050+vode-code@users.noreply.github.com> Date: Tue, 9 Sep 2025 15:25:56 -0700 Subject: [PATCH 25/44] carrion maw updated (#8621) * carrion maw devouring can now transfer dna disguises carrion puddle now actually uses its special behavior instead of giving it to solid biomass. carrion maw chemgain fixed maxed carrion hunger ensures human hunger * carrions can now eat from holders carrions can now be carried carrions now become a dead carrion core organ upon dying this is accompanied by a screech carrion parts are now worth TECH_BIO 5 uncommented post_drop timer, replacing spawn() call * fixed * this is safer * proper handling --- .../implants/carrion/identity_spider.dm | 2 +- code/modules/mob/holder.dm | 16 ++++-- .../mob/living/simple_animal/spider_core.dm | 8 ++- code/modules/organs/internal/carrion.dm | 51 ++++++++++++++----- .../internal/internal_organ_processes.dm | 4 ++ 5 files changed, 63 insertions(+), 18 deletions(-) diff --git a/code/game/objects/items/weapons/implant/implants/carrion/identity_spider.dm b/code/game/objects/items/weapons/implant/implants/carrion/identity_spider.dm index 8be0c312aa4..5248bde7c36 100644 --- a/code/game/objects/items/weapons/implant/implants/carrion/identity_spider.dm +++ b/code/game/objects/items/weapons/implant/implants/carrion/identity_spider.dm @@ -8,7 +8,7 @@ if(!owner_mob) return if(wearer) - if(wearer.type == /mob/living/carbon/human) + if(ishuman(wearer)) var/obj/item/organ/internal/carrion/core/C = owner_mob.random_organ_by_process(BP_SPCORE) C.absorbed_dna |= wearer.real_name to_chat(owner_mob, SPAN_NOTICE("You absorb [wearer]'s DNA")) diff --git a/code/modules/mob/holder.dm b/code/modules/mob/holder.dm index bf61af693ba..6cf3d470d69 100755 --- a/code/modules/mob/holder.dm +++ b/code/modules/mob/holder.dm @@ -136,10 +136,8 @@ var/list/holder_mob_icon_cache = list() //Repeat this check //If we're still on the turf a few frames later, then we have actually been dropped or thrown //Release the mob accordingly - //addtimer(CALLBACK(src, PROC_REF(post_drop)), 3) + addtimer(CALLBACK(src, PROC_REF(post_drop)), 3) //TODO: Uncomment the above once addtimer is ported - spawn(3) - post_drop() return if (istype(loc, /obj/item/storage)) //The second drop reads the container its placed into as the location @@ -581,6 +579,18 @@ var/list/holder_mob_icon_cache = list() return I return null +//Holders for mice +/obj/item/holder/carrion + name = "spider core" + desc = "A horrifying face on spider-like legs." + desc_dead = "A dead bio-weapon, it probably tastes horrible." + icon = 'icons/mob/animal.dmi' + icon_state = "spider_core" + slot_flags = SLOT_HEAD + origin_tech = list(TECH_BIO = 5) + w_class = ITEM_SIZE_NORMAL + + /* //Lizards diff --git a/code/modules/mob/living/simple_animal/spider_core.dm b/code/modules/mob/living/simple_animal/spider_core.dm index 4f2a0cc6204..0359f5a965a 100644 --- a/code/modules/mob/living/simple_animal/spider_core.dm +++ b/code/modules/mob/living/simple_animal/spider_core.dm @@ -24,6 +24,7 @@ hunger_enabled = FALSE pass_flags = PASSTABLE universal_understand = 1 + holder_type = /obj/item/holder/carrion density = TRUE //Should be 0, but then these things would be a nightmare to kill. faction = "spiders" @@ -35,7 +36,12 @@ /mob/living/simple_animal/spider_core/proc/generate_body)) /mob/living/simple_animal/spider_core/death() - gibs(loc, null, /obj/effect/gibspawner/generic, "#666600", "#666600") + var/obj/item/organ/internal/core = locate(/obj/item/organ/internal/carrion/core) in contents + if(core) + core.forceMove(loc) + core.status |= ORGAN_DEAD // todo: make wound somehow + core.refresh_damage() + playsound(loc, 'sound/voice/shriek1.ogg', 50) qdel(src) diff --git a/code/modules/organs/internal/carrion.dm b/code/modules/organs/internal/carrion.dm index 67fdc9aa3ab..2891cdc4219 100644 --- a/code/modules/organs/internal/carrion.dm +++ b/code/modules/organs/internal/carrion.dm @@ -32,6 +32,7 @@ /obj/item/organ/internal/carrion max_damage = 15 //resilient scanner_hidden = TRUE //sneaky + origin_tech = list(TECH_BIO = 5) /obj/item/organ/internal/carrion/chemvessel name = "chemical vessel" @@ -376,12 +377,17 @@ to_chat(owner, SPAN_WARNING("You can't eat nothing.")) return - if(istype(food, /obj/item/grab)) + var/obj/item/holder/overcomplicated = istype(food, /obj/item/holder) ? food : null + if(istype(food, /obj/item/grab) || overcomplicated && ishuman(overcomplicated.contained)) var/obj/item/grab/grab = food - var/mob/living/carbon/human/H = grab.affecting - if (grab.state < GRAB_AGGRESSIVE) - to_chat(owner, SPAN_WARNING("Your grip upon [H.name] is too weak.")) - return + var/mob/living/carbon/human/H + if(istype(grab)) + H = grab.affecting + if (grab.state < GRAB_AGGRESSIVE) + to_chat(owner, SPAN_WARNING("Your grip upon [H.name] is too weak.")) + return + else if(overcomplicated) + H = overcomplicated.contained if(istype(H)) var/obj/item/organ/external/E = H.get_organ(owner.targeted_organ) if (tearing) // one at a time, thank you. @@ -401,8 +407,7 @@ for (var/obj/item/organ/internal/to_blacklist in E.internal_organs) if (istype(to_blacklist, /obj/item/organ/internal/bone/)) blacklist += to_blacklist - continue - if (istype(to_blacklist, /obj/item/organ/internal/vital/brain/)) + else if (istype(to_blacklist, /obj/item/organ/internal/vital/brain/)) blacklist += to_blacklist// removing bones from a valid_organs list based on var/list/valid_organs = E.internal_organs - blacklist// E.internal_organs gibs the victim. if (!valid_organs.len) @@ -419,7 +424,7 @@ to_chat(owner, SPAN_WARNING("You can only tear flesh out of humanoids!")) return - if(istype(food, /obj/item/organ) || istype(food, /obj/item/reagent_containers/food/snacks/meat)) + if(istype(food, /obj/item/organ) || istype(food, /obj/item/reagent_containers/food/snacks/meat) || istype(food, /obj/item/holder)) var/geneticpointgain = 0 var/chemgain = 0 var/taste_description = "" @@ -437,11 +442,15 @@ var/obj/item/organ/internal/carrion/core/G = owner.random_organ_by_process(BP_SPCORE) if(O in G.associated_carrion_organs) taste_description = "albeit delicious, your own organs carry no new genetic material" + chemgain = 50 else owner.carrion_hunger += 3 geneticpointgain = 4 chemgain = 50 taste_description = "carrion organs taste heavenly, you need more!" + if(istype(O, /obj/item/organ/internal/carrion/core)) + var/obj/item/organ/internal/carrion/core/devoured = O + G.absorbed_dna |= devoured.absorbed_dna else if(istype(O, /obj/item/organ/internal)) var/organ_rotten = FALSE if (O.status & ORGAN_DEAD) @@ -464,9 +473,25 @@ taste_description = "human meat is satisfying." else - chemgain = 5 - owner.carrion_hunger -= 1 //Prevents meat eating spam for infinate chems - taste_description = "this meat is bland." + if(istype(food, /obj/item/holder/carrion)) + owner.carrion_hunger += 9 + geneticpointgain = 10 + chemgain = 50 + var/obj/item/holder/spiderholder = food + var/mob/living/simple_animal/spider_core/tastyspider = spiderholder.contained + var/obj/item/organ/internal/carrion/core/devoured = locate(/obj/item/organ/internal/carrion/core) in tastyspider.contents + var/obj/item/organ/internal/carrion/core/C = owner.random_organ_by_process(BP_SPCORE) + if(devoured && C) + C.absorbed_dna |= devoured.absorbed_dna + taste_description = "carrions taste heavenly, if only there was more!" + qdel(tastyspider) + else + if(istype(food, /obj/item/holder)) + var/obj/item/holder/bland_animal = food + qdel(bland_animal.contained) + chemgain = 5 + owner.carrion_hunger -= 1 //Prevents meat eating spam for infinate chems + taste_description = "this meat is bland." var/obj/item/organ/internal/carrion/core/C = owner.random_organ_by_process(BP_SPCORE) if(C) @@ -477,7 +502,7 @@ var/chemvessel_efficiency = owner.get_organ_efficiency(OP_CHEMICALS) if(chemvessel_efficiency > 1) - owner.carrion_stored_chemicals = min(owner.carrion_stored_chemicals + 0.01 * chemvessel_efficiency , 0.5 * chemvessel_efficiency) + owner.carrion_stored_chemicals = min(owner.carrion_stored_chemicals + 0.01 * chemvessel_efficiency * chemgain , 0.5 * chemvessel_efficiency) to_chat(owner, SPAN_NOTICE("You consume \the [food], [taste_description].")) visible_message(SPAN_DANGER("[owner] devours \the [food]!")) @@ -555,7 +580,7 @@ continue toxin_attack(creature, rand(1, 3)) -/obj/effect/decal/cleanable/solid_biomass/attackby(var/obj/item/I, var/mob/user) +/obj/effect/decal/cleanable/carrion_puddle/attackby(var/obj/item/I, var/mob/user) if(istype(I, /obj/item/mop) || istype(I, /obj/item/soap)) to_chat(user, SPAN_NOTICE("You started cleaning this [src].")) if(do_after(user, 3 SECONDS, src)) diff --git a/code/modules/organs/internal/internal_organ_processes.dm b/code/modules/organs/internal/internal_organ_processes.dm index 1b9b97dd5b5..05590ed2015 100644 --- a/code/modules/organs/internal/internal_organ_processes.dm +++ b/code/modules/organs/internal/internal_organ_processes.dm @@ -259,6 +259,10 @@ if(carrion_hunger < max_hunger) carrion_hunger = min(carrion_hunger + (round(1* (maw_efficiency / 100))), max_hunger) else + if(ingested.has_reagent("nutriment", 1)) + ingested.remove_reagent("nutriment", 1) + else + nutrition = max(0, nutrition - 20) // equivalent to a unit of nutriment to_chat(src, SPAN_WARNING("Your hunger is restless!")) carrion_last_hunger = world.time From 1d891a6b30950b615fc7489a66af4fee7398b4fb Mon Sep 17 00:00:00 2001 From: vode-code <65709050+vode-code@users.noreply.github.com> Date: Tue, 9 Sep 2025 15:26:12 -0700 Subject: [PATCH 26/44] tasteless nutriment no longer runtimes (#8615) * tasteless nutriment no longer runtimes * flat dough now has nutriment data --- code/modules/mob/living/carbon/taste.dm | 2 ++ code/modules/reagents/reagent_containers/food/snacks.dm | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/code/modules/mob/living/carbon/taste.dm b/code/modules/mob/living/carbon/taste.dm index e723e0b9ff3..39a4a498c85 100644 --- a/code/modules/mob/living/carbon/taste.dm +++ b/code/modules/mob/living/carbon/taste.dm @@ -23,6 +23,8 @@ calculate text size per text. continue if(R.id == "nutriment") var/list/t = R.get_data() + if(!t) // no data, nutriment made from no source? + continue for(var/i in 1 to t.len) var/A = t[i] if(!(A in tastes)) diff --git a/code/modules/reagents/reagent_containers/food/snacks.dm b/code/modules/reagents/reagent_containers/food/snacks.dm index deb9e45fa08..69cc3f6a172 100644 --- a/code/modules/reagents/reagent_containers/food/snacks.dm +++ b/code/modules/reagents/reagent_containers/food/snacks.dm @@ -3489,7 +3489,9 @@ slice_path = /obj/item/reagent_containers/food/snacks/doughslice slices_num = 3 center_of_mass = list("x"=16, "y"=16) - preloaded_reagents = list("protein" = 1, "nutriment" = 3) + nutriment_desc = list("dough" = 3) + nutriment_amt = 3 + preloaded_reagents = list("protein" = 1) taste_tag = list(BLAND_FOOD,FLOURY_FOOD) /obj/item/reagent_containers/food/snacks/doughslice From de2cc2e3193370d86bec5258fc2fd98a4eda6c00 Mon Sep 17 00:00:00 2001 From: "Nestor Jr." Date: Tue, 9 Sep 2025 22:26:30 +0000 Subject: [PATCH 27/44] Automatic changelog generation for PR #8621 [ci skip] --- html/changelogs/AutoChangeLog-pr-8621.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-8621.yml diff --git a/html/changelogs/AutoChangeLog-pr-8621.yml b/html/changelogs/AutoChangeLog-pr-8621.yml new file mode 100644 index 00000000000..9961f8f1c58 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8621.yml @@ -0,0 +1,10 @@ +author: Chickenish +delete-after: true +changes: + - rscadd: Eating another carrion's core with your maw gives you the other + carrion's disguises + - balance: carrion over-hunger is now punished by making your human form + hungry + - tweak: carrion maw now works with holders. + - tweak: carrions now drop as an inert item if the core is killed. + - bugfix: eating with carrion maw provides proper chem value From b2e8b58ecc28788068f8ed83d5807ea269312c00 Mon Sep 17 00:00:00 2001 From: vode-code <65709050+vode-code@users.noreply.github.com> Date: Tue, 9 Sep 2025 15:26:45 -0700 Subject: [PATCH 28/44] type mismatch fixed (#8614) --- code/modules/mob/living/carbon/human/human_defense.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 771b32d155c..8cc45281e7d 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -37,7 +37,7 @@ meteor_act SP.desc = "[SP.desc] It looks like it was fired from [P.shot_from]." SP.loc = organ if(length(P.matter)) - SP.material = P.matter[1] + SP.material = get_material_by_name(P.matter[1]) SP.amount = P.matter[SP.material] // amount no longer randomized organ.embed(SP) From ec08c4e6664419aa7073fee9fbddc0ad64fb9fb5 Mon Sep 17 00:00:00 2001 From: "Nestor Jr." Date: Tue, 9 Sep 2025 22:26:52 +0000 Subject: [PATCH 29/44] Automatic changelog generation for PR #8615 [ci skip] --- html/changelogs/AutoChangeLog-pr-8615.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-8615.yml diff --git a/html/changelogs/AutoChangeLog-pr-8615.yml b/html/changelogs/AutoChangeLog-pr-8615.yml new file mode 100644 index 00000000000..02eafbaaede --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8615.yml @@ -0,0 +1,5 @@ +author: Chickenish +delete-after: true +changes: + - bugfix: nutriment made via roach chem decay is now fully drinkable. + - bugfix: Flat Dough and derivatives are now more edible. From 93374ff9fae7197cb1384473d6a305706ad2f406 Mon Sep 17 00:00:00 2001 From: vode-code <65709050+vode-code@users.noreply.github.com> Date: Tue, 9 Sep 2025 15:27:07 -0700 Subject: [PATCH 30/44] make_plating in floor attackby now uses TRUE define (#8590) crowbar removal of damaged floor now produces a shard instead of nothing --- code/game/turfs/simulated/floor_attackby.dm | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/code/game/turfs/simulated/floor_attackby.dm b/code/game/turfs/simulated/floor_attackby.dm index e365e0b9081..fafb07e083f 100644 --- a/code/game/turfs/simulated/floor_attackby.dm +++ b/code/game/turfs/simulated/floor_attackby.dm @@ -128,7 +128,7 @@ if(is_damaged()) if(I.use_tool(user, src, flooring.removal_time, tool_type, FAILCHANCE_VERY_EASY, required_stat = STAT_MEC)) to_chat(user, SPAN_NOTICE("You remove the broken [flooring.descriptor].")) - make_plating() + make_plating(TRUE, null, TRUE) return else if(flooring.flags & TURF_IS_FRAGILE) if(I.use_tool(user, src, flooring.removal_time, tool_type, FAILCHANCE_VERY_EASY, required_stat = STAT_MEC)) @@ -138,7 +138,7 @@ else if(flooring.flags & TURF_REMOVE_CROWBAR) if(I.use_tool(user, src, flooring.removal_time, tool_type, FAILCHANCE_VERY_EASY, required_stat = STAT_MEC)) to_chat(user, SPAN_NOTICE("You lever off the [flooring.descriptor].")) - make_plating(1) + make_plating(TRUE) return return @@ -146,21 +146,21 @@ if((!(is_damaged()) && !is_plating()) || flooring.flags & TURF_REMOVE_SCREWDRIVER) if(I.use_tool(user, src, flooring.removal_time*1.5, tool_type, FAILCHANCE_VERY_EASY, required_stat = STAT_MEC)) to_chat(user, SPAN_NOTICE("You unscrew and remove the [flooring.descriptor].")) - make_plating(1) + make_plating(TRUE) return if(QUALITY_BOLT_TURNING) if(flooring.flags & TURF_REMOVE_WRENCH) if(I.use_tool(user, src, flooring.removal_time, tool_type, FAILCHANCE_NORMAL, required_stat = STAT_MEC)) to_chat(user, SPAN_NOTICE("You unwrench and remove the [flooring.descriptor].")) - make_plating(1) + make_plating(TRUE) return if(QUALITY_SHOVELING) if(flooring.flags & TURF_REMOVE_SHOVEL) if(I.use_tool(user, src, flooring.removal_time, tool_type, FAILCHANCE_VERY_EASY, required_stat = STAT_MEC)) to_chat(user, SPAN_NOTICE("You shovel off the [flooring.descriptor].")) - make_plating(1) + make_plating(TRUE) return if(QUALITY_WELDING) @@ -179,7 +179,7 @@ to_chat(user, SPAN_NOTICE("You start cutting through the [flooring.descriptor].")) if(I.use_tool(user, src, flooring.removal_time, tool_type, FAILCHANCE_NORMAL, required_stat = STAT_MEC)) to_chat(user, SPAN_NOTICE("You cut through and remove the [flooring.descriptor].")) - make_plating(1) + make_plating(TRUE) if(ABORT_CHECK) return From 447112eeeaa8804542502a8615f25cc3ca055cc2 Mon Sep 17 00:00:00 2001 From: vode-code <65709050+vode-code@users.noreply.github.com> Date: Tue, 9 Sep 2025 15:27:25 -0700 Subject: [PATCH 31/44] modular boltgun!!! (#8570) * modular boltgun consolidation of bolt_act logic rectification of bayonet AP addition AMR kit update gun part modular interaction gun part stat update stock size update * refresh upgrades doesn't reset stock fold * AMR scope is no longer missing mechanics * oversight --- cev_eris.dme | 2 + code/__DEFINES/items.dm | 18 ++ code/__DEFINES/weapons.dm | 1 + code/datums/craft/gun_parts.dm | 213 +++++++++++++++++- code/datums/craft/recipes/guns.dm | 4 +- .../items/weapons/storage/uplink_kits.dm | 8 +- .../objects/items/weapons/tools/knives.dm | 2 +- .../items/weapons/tools/mods/_upgrades.dm | 45 +++- code/modules/projectiles/gun.dm | 1 + .../guns/projectile/battle_rifle/boltgun.dm | 8 +- .../projectiles/guns/projectile/flaregun.dm | 4 +- .../projectiles/guns/projectile/modular.dm | 110 ++++++++- .../guns/projectile/modular/interactions.dm | 99 ++++++++ .../guns/projectile/modular/modular_bolt.dm | 83 +++++++ .../guns/projectile/shotgun/doublebarrel.dm | 1 - .../projectiles/guns/projectile/sniper.dm | 4 +- icons/obj/crafts.dmi | Bin 8184 -> 8325 bytes icons/obj/guns/projectile/modular/bolt.dmi | Bin 0 -> 12446 bytes 18 files changed, 575 insertions(+), 28 deletions(-) create mode 100644 code/modules/projectiles/guns/projectile/modular/interactions.dm create mode 100644 code/modules/projectiles/guns/projectile/modular/modular_bolt.dm create mode 100644 icons/obj/guns/projectile/modular/bolt.dmi diff --git a/cev_eris.dme b/cev_eris.dme index fcaf0286fd2..28b181ae6ef 100644 --- a/cev_eris.dme +++ b/cev_eris.dme @@ -2466,7 +2466,9 @@ #include "code\modules\projectiles\guns\projectile\battle_rifle\boltgun.dm" #include "code\modules\projectiles\guns\projectile\battle_rifle\kovacs.dm" #include "code\modules\projectiles\guns\projectile\battle_rifle\levergun.dm" +#include "code\modules\projectiles\guns\projectile\modular\interactions.dm" #include "code\modules\projectiles\guns\projectile\modular\modular_AK.dm" +#include "code\modules\projectiles\guns\projectile\modular\modular_bolt.dm" #include "code\modules\projectiles\guns\projectile\modular\modular_MK58.dm" #include "code\modules\projectiles\guns\projectile\modular\modular_wintermute.dm" #include "code\modules\projectiles\guns\projectile\other\RPG.dm" diff --git a/code/__DEFINES/items.dm b/code/__DEFINES/items.dm index e288b79ab3d..4f682d97fa1 100644 --- a/code/__DEFINES/items.dm +++ b/code/__DEFINES/items.dm @@ -63,6 +63,9 @@ #define GUN_UPGRADE_OVERCHARGE_MAX "overcharge_max_mult" #define GUN_UPGRADE_OVERCHARGE_RATE "overcharge_rate_mult" #define GUN_UPGRADE_ONEHANDPENALTY "onehandpenalty_mult" +#define GUN_UPGRADE_MOVEPENALTY "movementpenalty_mult" +#define GUN_UPGRADE_RECOILBUILDUP "recoilbuildup_mult" +#define GUN_UPGRADE_BASESLOW "slowdown_base_add" //Int additive #define GUN_UPGRADE_DAMAGEMOD_PLUS "damage_plus" @@ -81,14 +84,29 @@ #define GUN_UPGRADE_OFFSET "offset" //Constant offset, in degrees #define GUN_UPGRADE_ZOOM "zoom" +#define GUN_UPGRADE_SCOPEVISION "scopeseeinvis" +#define GUN_UPGRADE_SCOPECORRECTION "scopeoffset" +#define GUN_UPGRADE_DARKSCOPE "scopedarksight" //Type configuration +#define GUN_UPGRADE_REPLACE_INTERACTIONS "interactions" #define GUN_UPGRADE_DEFINE_OK_CALIBERS "ok_calibers" #define GUN_UPGRADE_DEFINE_CALIBER "caliber" #define GUN_UPGRADE_DEFINE_MAG_WELL "mag_well" #define GUN_UPGRADE_DEFINE_STOCK "stock" #define GUN_UPGRADE_FIREMODES "firemodes" #define GUN_UPGRADE_DEFINE_GRIP "grip" +#define GUN_UPGRADE_DEFINE_LOADER "loadtype" +#define GUN_UPGRADE_DENY_MAG "no_mag" +#define GUN_UPGRADE_DEFINE_WCLASS "add_wclass" +#define GUN_UPGRADE_SCOPE_POWER "scope_power" + +//Gun Interaction flags +#define GI_ATTACKSELF 1 +#define GI_LOAD 2 +#define GI_UNLOAD 4 +#define GI_SPIN 8 +#define GI_SPECIAL 16 //boolean #define GUN_UPGRADE_SILENCER "silencable" diff --git a/code/__DEFINES/weapons.dm b/code/__DEFINES/weapons.dm index c737c7ce3e1..ea223064507 100644 --- a/code/__DEFINES/weapons.dm +++ b/code/__DEFINES/weapons.dm @@ -107,3 +107,4 @@ #define RIFLE_RECOIL(x) list(0.8 *x, 5, 2.4*x ) #define LMG_RECOIL(x) list(0.7 *x, 8, 3*x ) #define HMG_RECOIL(x) list(0.6 *x, 12, 3.6*x ) +#define SLATE_RECOIL(x) list(1 *x, 1, 1 *x) diff --git a/code/datums/craft/gun_parts.dm b/code/datums/craft/gun_parts.dm index e98868bd083..95bca9de5af 100644 --- a/code/datums/craft/gun_parts.dm +++ b/code/datums/craft/gun_parts.dm @@ -67,7 +67,7 @@ semi accepts weird caliber - +1 points w_class = ITEM_SIZE_SMALL matter = list(MATERIAL_PLASTEEL = 5) generic = TRUE - var/part_overlay + var/part_overlay = "" var/part_itemstring var/needs_grip_type generic = FALSE @@ -75,6 +75,7 @@ semi accepts weird caliber - +1 points var/datum/component/item_upgrade/I // For changing stats when needed var/old_quality = 0 var/max_quality = 2 + var/interactions // Bonuses from forging/type or maluses from printing var/cheap = FALSE // Set this to true for cheap variants @@ -82,8 +83,11 @@ semi accepts weird caliber - +1 points /obj/item/part/gun/modular/New(location) ..() I = AddComponent(/datum/component/item_upgrade) + if(interactions) + interactions = new interactions(src) I.weapon_upgrades = list( - UPGRADE_MAXUPGRADES = 1 // Since this takes an upgrade slot, we want to give it back + UPGRADE_MAXUPGRADES = 1, // Since this takes an upgrade slot, we want to give it back + GUN_UPGRADE_REPLACE_INTERACTIONS = interactions // used for overriding gun behavior ) I.req_gun_tags = list(GUN_MODULAR) I.removable = MOD_INTEGRAL // Will get unique removal handling when we get there, until then works by disassembling the frame @@ -374,21 +378,34 @@ semi accepts weird caliber - +1 points price_tag = 100 rarity_value = 6 var/list/accepted_calibers = list(CAL_PISTOL, CAL_MAGNUM, CAL_SRIFLE, CAL_CLRIFLE, CAL_LRIFLE, CAL_SHOTGUN) + var/loader = MAGAZINE var/mag_well = MAG_WELL_GENERIC var/divisor_bonus = 0 var/recoil_bonus = 0 var/damage_bonus = 0 + var/onehandpenalty = 0 + var/max_shells = 0 var/list/bonus_firemodes = list() + var/no_internal_mag = FALSE + + /obj/item/part/gun/modular/mechanism/New(location, var/quality = 0) ..(quality) I.weapon_upgrades[GUN_UPGRADE_FIREMODES] = bonus_firemodes I.weapon_upgrades[GUN_UPGRADE_DEFINE_MAG_WELL] = mag_well I.weapon_upgrades[GUN_UPGRADE_DEFINE_OK_CALIBERS] = accepted_calibers + I.weapon_upgrades[GUN_UPGRADE_DEFINE_LOADER] = loader + if(divisor_bonus) I.weapon_upgrades[GUN_UPGRADE_PEN_MULT] = divisor_bonus if(recoil_bonus) I.weapon_upgrades[GUN_UPGRADE_RECOIL] = recoil_bonus + if(max_shells) + I.weapon_upgrades[GUN_UPGRADE_MAGUP] = max_shells + if(no_internal_mag) + I.weapon_upgrades[GUN_UPGRADE_DENY_MAG] = TRUE + I.gun_loc_tag = PART_MECHANISM /obj/item/part/gun/modular/mechanism/set_quality(var/quality = 0) @@ -419,6 +436,10 @@ semi accepts weird caliber - +1 points name = "revolver mechanism" desc = "All the bits that makes the bullet go bang, rolling round and round." icon_state = "mechanism_revolver" + max_shells = 6 + +/obj/item/part/gun/modular/mechanism/revolver/seven + max_shells = 7 /obj/item/part/gun/modular/mechanism/shotgun name = "shotgun mechanism" @@ -565,6 +586,43 @@ semi accepts weird caliber - +1 points desc = "All the bits that makes the bullet go bang, slow and methodical." icon_state = "mechanism_boltaction" matter = list(MATERIAL_STEEL = 3) + accepted_calibers = list(CAL_LRIFLE, CAL_SRIFLE, CAL_PISTOL, CAL_MAGNUM) // used by boltrifles + loader = SINGLE_CASING|SPEEDLOADER + interactions = /datum/guninteraction/bolted + max_shells = 10 + divisor_bonus = 0.3 + damage_bonus = 0.4 + +/obj/item/part/gun/modular/mechanism/boltgun/power + accepted_calibers = list(CAL_SRIFLE, CAL_MAGNUM) + max_shells = 6 + divisor_bonus = 1 + damage_bonus = 0.8 + +/obj/item/part/gun/modular/mechanism/boltgun/heavy + name = "heavy manual-action mechanism" + desc = "All the bits that makes the bullet go bang, slow and methodical; for larger calibers." + matter = list(MATERIAL_STEEL = 4) + accepted_calibers = list(CAL_SHOTGUN, CAL_MAGNUM, CAL_FLARE, CAL_ANTIM) // used by AMRs + part_overlay = "big_" // the full entry is handled by the guninteraction + loader = SINGLE_CASING + max_shells = 1 + divisor_bonus = 0 + damage_bonus = 1 + +/obj/item/part/gun/modular/mechanism/boltgun/junk + name = "handmade manual-action mechanism" + max_shells = 5 + divisor_bonus = -0.3 + damage_bonus = 0.5 + +/obj/item/part/gun/modular/mechanism/levergun + name = "break-action mechanism" + desc = "This one breaks in half!" + // no icon state available + matter = list(MATERIAL_STEEL = 4) + accepted_calibers = list(CAL_MAGNUM, CAL_SHOTGUN) // used by leverguns only + max_shells = 7 /obj/item/part/gun/modular/mechanism/autorifle/steel name = "cheap self-loading mechanism" @@ -583,10 +641,25 @@ semi accepts weird caliber - +1 points price_tag = 200 rarity_value = 15 var/caliber = CAL_357 + var/speed + var/onehandpenalty + var/recoilbuildup + var/pierce + var/basemove /obj/item/part/gun/modular/barrel/New(location, var/quality = 0) ..(quality) I.weapon_upgrades[GUN_UPGRADE_DEFINE_CALIBER] = caliber + if(!isnull(speed)) + I.weapon_upgrades[GUN_UPGRADE_STEPDELAY_MULT] = speed + if(onehandpenalty) + I.weapon_upgrades[GUN_UPGRADE_ONEHANDPENALTY] = onehandpenalty + if(recoilbuildup) + I.weapon_upgrades[GUN_UPGRADE_RECOILBUILDUP] = recoilbuildup + if(pierce) + I.weapon_upgrades[GUN_UPGRADE_PIERC_MULT] = pierce + if(basemove) + I.weapon_upgrades[GUN_UPGRADE_BASESLOW] = basemove I.gun_loc_tag = PART_BARREL @@ -632,6 +705,13 @@ semi accepts weird caliber - +1 points caliber = CAL_SRIFLE part_overlay = "well_srifle" +/obj/item/part/gun/modular/barrel/srifle/long + name = "long .20 barrel" + desc = "A gun barrel, which keeps the bullet going in the right direction efficiently. Chambered in .20 caliber." + onehandpenalty = 1.2 + recoilbuildup = 1.2 + speed = 0.8 + /obj/item/part/gun/modular/barrel/clrifle name = ".25 barrel" desc = "A gun barrel, which keeps the bullet going in the right direction. Chambered in .25 caliber." @@ -671,10 +751,21 @@ semi accepts weird caliber - +1 points name = ".60 barrel" desc = "A gun barrel, which keeps the bullet going in the right direction. Chambered in .60 caliber." icon_state = "barrel_60" - matter = list(MATERIAL_PLASTEEL = 16) + matter = list(MATERIAL_PLASTEEL = 10) caliber = CAL_ANTIM part_overlay = "well_amr" +/obj/item/part/gun/modular/barrel/antim/long + name = "long .60 barrel" + desc = "A long gun barrel, which keeps the bullet moving in the right direction efficiently. Chambered in .60 caliber." + matter = list(MATERIAL_PLASTEEL = 15) + speed = 0 + part_overlay = "well_amr_long" + onehandpenalty = 2 + recoilbuildup = 2 + pierce = 6 + basemove = 3 + // steel barrels /obj/item/part/gun/modular/barrel/pistol/steel name = "cheap .35 barrel" @@ -720,8 +811,124 @@ semi accepts weird caliber - +1 points generic = FALSE part_overlay = "stock" needs_grip_type = TRUE + var/brace = 0 + var/recoilbuildup + var/movementcost + var/onehandpenalty + var/wclassmod = 1 /obj/item/part/gun/modular/stock/New(location, var/quality = 0) ..() // No stat change, so no need for price change either I.weapon_upgrades[GUN_UPGRADE_DEFINE_STOCK] = TRUE I.gun_loc_tag = PART_STOCK + + if(movementcost) + I.weapon_upgrades[GUN_UPGRADE_MOVEPENALTY] = movementcost + if(recoilbuildup) + I.weapon_upgrades[GUN_UPGRADE_RECOILBUILDUP] = recoilbuildup + if(onehandpenalty) + I.weapon_upgrades[GUN_UPGRADE_ONEHANDPENALTY] = onehandpenalty + I.weapon_upgrades[GUN_UPGRADE_DEFINE_WCLASS] = wclassmod + +/obj/item/part/gun/modular/stock/heavy + recoilbuildup = 0.7 + movementcost = 8 + onehandpenalty = 3 + +/obj/item/part/gun/modular/stock/longrifle + recoilbuildup = 0.8 + movementcost = 5 + onehandpenalty = 2.4 + +/obj/item/part/gun/modular/sights + name = "ironsights" + desc = "A set of sights for aiming through." + var/list/scopes = list() + interactions = /datum/guninteraction/zoomed + var/scopeaccuracy + var/scopeseeinvis + var/darksight + var/list/powerboost + +/obj/item/part/gun/modular/sights/New(location, var/quality = 0) + ..(quality) + I.weapon_upgrades[GUN_UPGRADE_ZOOM] = scopes + if(scopeseeinvis) + I.weapon_upgrades[GUN_UPGRADE_SCOPEVISION] = scopeseeinvis + if(scopeaccuracy) + I.weapon_upgrades[GUN_UPGRADE_SCOPECORRECTION] = scopeaccuracy + if(darksight) + I.weapon_upgrades[GUN_UPGRADE_DARKSCOPE] = darksight + if(powerboost) + I.weapon_upgrades[GUN_UPGRADE_SCOPE_POWER] = powerboost + I.gun_loc_tag = GUN_SCOPE + +/obj/item/part/gun/modular/sights/scopesmall // Arasaka boltgun + name = "small scope" + desc = "A sight for aiming through. This one is on the smaller side." + icon_state = "scope_small" + scopes = list(0.5) + part_overlay = "scope_small" + +/obj/item/part/gun/modular/sights/scopebig // Kadmin + name = "medium scope" + desc = "A sight for aiming through. This one is medium." + icon_state = "scope_big" + scopes = list(0.8) + part_overlay = "scope_big" + +/obj/item/part/gun/modular/sights/customizable + +/obj/item/part/gun/modular/sights/customizable/attackby(obj/item/Item, mob/living/user) + if(istype(Item, /obj/item/clothing/glasses/powered/thermal)) + user.visible_message(SPAN_NOTICE("[user] inserts \a [Item] into [src]."), SPAN_NOTICE("You insert [Item] into \the [src]."), "You hear a faint click.", 5) + I.weapon_upgrades[GUN_UPGRADE_THERMAL] = TRUE + user.drop_from_inventory(Item, src) + else if(Item.has_quality(QUALITY_SCREW_DRIVING)) + var/list/choices = contents.Copy() + choices += "Cancel" + var/obj/toremove = input("Which upgrade would you like to try to remove?","Removing Upgrades") in choices + if(toremove == "Cancel") + return TRUE + else if(!isnull(toremove)) + toremove.forceMove(get_turf(src)) + if(istype(toremove, /obj/item/clothing/glasses/powered/thermal)) + I.weapon_upgrades[GUN_UPGRADE_THERMAL] = FALSE + to_chat(user, SPAN_NOTICE("You successfully remove [toremove] from \the [src]!")) + + +/obj/item/part/gun/modular/sights/customizable/scopeheavy // AMR + name = "sniper scope" + desc = "A large adjustable sight for aiming through. Provides night vision. Can add advanced lenses to improve vision." + icon_state = "scope_heavy" + scopes = list(1,2) + part_overlay = "scope_heavy" + darksight = 7 + scopeseeinvis = SEE_INVISIBLE_NOLIGHTING + scopeaccuracy = 8 + powerboost = list(0.2, 0.4) + interactions = /datum/guninteraction/zoomed/multizoom + +/obj/item/part/gun/modular/bayonet + name = "integrated bayonet" + desc = "A bayonet designed for a firmer attachment, applied during assembly." + matter = list(MATERIAL_PLASTEEL = 3, MATERIAL_STEEL = 2) + icon = 'icons/obj/weapons.dmi' + icon_state = "dagger" + part_overlay = "bayonet" + var/damagedone = 6 + +/obj/item/part/gun/modular/bayonet/New(location, var/quality = 0) + ..(quality) + I.weapon_upgrades = list( + GUN_UPGRADE_BAYONET = TRUE, + GUN_UPGRADE_MELEEDAMAGE = damagedone, + GUN_UPGRADE_MELEEPENETRATION = ARMOR_PEN_MODERATE, + GUN_UPGRADE_OFFSET = 4 + ) + +/obj/item/part/gun/modular/bayonet/steel + name = "cheap integrated bayonet" + desc = "A bayonet designed for a firmer attachment, applied during assembly. This one's made from cheap steel." + matter = list(MATERIAL_STEEL = 2) + damagedone = 3 diff --git a/code/datums/craft/recipes/guns.dm b/code/datums/craft/recipes/guns.dm index 86d2824ac06..439cb6ee0a4 100644 --- a/code/datums/craft/recipes/guns.dm +++ b/code/datums/craft/recipes/guns.dm @@ -27,8 +27,8 @@ name = "HM BR \"Riose\"" result = /obj/item/gun/projectile/boltgun/handmade steps = list( - list(CRAFT_MATERIAL, 10, MATERIAL_STEEL), - list(CRAFT_MATERIAL, 10, MATERIAL_WOOD), + list(CRAFT_MATERIAL, 13, MATERIAL_STEEL), + list(CRAFT_MATERIAL, 5, MATERIAL_WOOD), list(QUALITY_HAMMERING, 10), list(QUALITY_ADHESIVE, 15, 70) ) diff --git a/code/game/objects/items/weapons/storage/uplink_kits.dm b/code/game/objects/items/weapons/storage/uplink_kits.dm index c08f5f1a525..ee19caa1e5d 100644 --- a/code/game/objects/items/weapons/storage/uplink_kits.dm +++ b/code/game/objects/items/weapons/storage/uplink_kits.dm @@ -245,10 +245,12 @@ /obj/item/storage/briefcase/antimaterial_rifle/populate_contents() new /obj/item/ammo_casing/antim(src) - new /obj/item/part/gun/frame/heavysniper(src) + new /obj/item/gun/projectile/automatic/modular/bolt/sniper(src) new /obj/item/part/gun/modular/grip/serb(src) - new /obj/item/part/gun/modular/mechanism/boltgun(src) - new /obj/item/part/gun/modular/barrel/antim(src) + new /obj/item/part/gun/modular/mechanism/boltgun/heavy(src) + new /obj/item/part/gun/modular/barrel/antim/long(src) + new /obj/item/part/gun/modular/stock/heavy(src) + new /obj/item/part/gun/modular/sights/customizable/scopeheavy(src) /obj/item/storage/box/syndie_kit/toxin name = "toxin kit" diff --git a/code/game/objects/items/weapons/tools/knives.dm b/code/game/objects/items/weapons/tools/knives.dm index 9185df41241..ce70ea421b5 100644 --- a/code/game/objects/items/weapons/tools/knives.dm +++ b/code/game/objects/items/weapons/tools/knives.dm @@ -32,7 +32,7 @@ I.weapon_upgrades = list( GUN_UPGRADE_BAYONET = TRUE, GUN_UPGRADE_MELEEDAMAGE = 5, - GUN_UPGRADE_MELEEPENETRATION = ARMOR_PEN_MODERATE, + GUN_UPGRADE_MELEEPENETRATION = (ARMOR_PEN_MODERATE-1), GUN_UPGRADE_OFFSET = 4 ) I.gun_loc_tag = GUN_UNDERBARREL diff --git a/code/game/objects/items/weapons/tools/mods/_upgrades.dm b/code/game/objects/items/weapons/tools/mods/_upgrades.dm index de7453f9590..dee5b963e1f 100644 --- a/code/game/objects/items/weapons/tools/mods/_upgrades.dm +++ b/code/game/objects/items/weapons/tools/mods/_upgrades.dm @@ -255,6 +255,7 @@ P.forceMove(get_turf(I)) UnregisterSignal(I, COMSIG_ADDVAL) UnregisterSignal(I, COMSIG_APPVAL) + reset_owner() /datum/component/item_upgrade/proc/apply_values(atom/holder) //SIGNAL_HANDLER @@ -327,7 +328,7 @@ G.pierce_multiplier += weapon_upgrades[GUN_UPGRADE_PIERC_MULT] if(weapon_upgrades[GUN_UPGRADE_RICO_MULT]) G.ricochet_multiplier += weapon_upgrades[GUN_UPGRADE_RICO_MULT] - if(weapon_upgrades[GUN_UPGRADE_STEPDELAY_MULT]) + if(!isnull(weapon_upgrades[GUN_UPGRADE_STEPDELAY_MULT])) // so AMR barrel can 0x G.proj_step_multiplier *= weapon_upgrades[GUN_UPGRADE_STEPDELAY_MULT] if(weapon_upgrades[GUN_UPGRADE_FIRE_DELAY_MULT]) G.fire_delay *= weapon_upgrades[GUN_UPGRADE_FIRE_DELAY_MULT] @@ -375,6 +376,12 @@ if(ismob(G.loc)) var/mob/user = G.loc user.update_action_buttons() + if(weapon_upgrades[GUN_UPGRADE_SCOPEVISION]) + G.see_invisible_gun = max(G.see_invisible_gun, weapon_upgrades[GUN_UPGRADE_SCOPEVISION]) // scope stats should take best of class + if(weapon_upgrades[GUN_UPGRADE_SCOPECORRECTION]) + G.scoped_offset_reduction = max(G.scoped_offset_reduction, weapon_upgrades[GUN_UPGRADE_SCOPECORRECTION]) + if(weapon_upgrades[GUN_UPGRADE_DARKSCOPE]) + G.darkness_view = max(G.darkness_view, weapon_upgrades[GUN_UPGRADE_DARKSCOPE]) if(weapon_upgrades[GUN_UPGRADE_THERMAL]) G.vision_flags = SEE_MOBS if(weapon_upgrades[GUN_UPGRADE_GILDED]) @@ -388,6 +395,11 @@ G.armor_divisor += weapon_upgrades[GUN_UPGRADE_MELEEPENETRATION] if(weapon_upgrades[GUN_UPGRADE_ONEHANDPENALTY]) G.recoil = G.recoil.modifyRating(_one_hand_penalty = weapon_upgrades[GUN_UPGRADE_ONEHANDPENALTY]) + if(weapon_upgrades[GUN_UPGRADE_MOVEPENALTY]) + G.recoil = G.recoil.modifyRating(_brace_penalty = weapon_upgrades[GUN_UPGRADE_MOVEPENALTY]) + if(weapon_upgrades[GUN_UPGRADE_RECOILBUILDUP]) + G.recoil = G.recoil.modifyRating(_recoil_buildup = weapon_upgrades[GUN_UPGRADE_RECOILBUILDUP]) + if(weapon_upgrades[GUN_UPGRADE_DNALOCK]) G.dna_compare_samples = TRUE @@ -430,6 +442,14 @@ M.verbs += /obj/item/gun/projectile/automatic/modular/proc/quick_fold // Grant the verb for folding stocks if(weapon_upgrades[GUN_UPGRADE_DEFINE_GRIP]) M.grip_type = weapon_upgrades[GUN_UPGRADE_DEFINE_GRIP] + if(weapon_upgrades[GUN_UPGRADE_DEFINE_LOADER]) + M.load_method = weapon_upgrades[GUN_UPGRADE_DEFINE_LOADER] + if(weapon_upgrades[GUN_UPGRADE_DENY_MAG]) + M.no_internal_mag = TRUE + if(weapon_upgrades[GUN_UPGRADE_DEFINE_WCLASS]) + M.w_class += weapon_upgrades[GUN_UPGRADE_DEFINE_WCLASS] + if(weapon_upgrades[GUN_UPGRADE_SCOPE_POWER]) + M.scope_damage_adds.Add(weapon_upgrades[GUN_UPGRADE_SCOPE_POWER]) for(var/datum/firemode/F in G.firemodes) apply_values_firemode(F) @@ -440,6 +460,29 @@ if(weapon_upgrades[GUN_UPGRADE_FIREMODES]) for(var/FM in weapon_upgrades[GUN_UPGRADE_FIREMODES]) G.add_firemode(FM) + if(istype(G, /obj/item/gun/projectile/automatic/modular)) + if(weapon_upgrades[GUN_UPGRADE_REPLACE_INTERACTIONS]) + var/obj/item/gun/projectile/automatic/modular/interactive = G + var/datum/gunoverrides/overrided = interactive.overridedatum + if(overrided) + var/datum/guninteraction/interaction = weapon_upgrades[GUN_UPGRADE_REPLACE_INTERACTIONS] + interaction.parentgun = G + if(islist(overrided.priorities["[interaction.priority]"])) + var/list/currentpriority = overrided.priorities["[interaction.priority]"] + currentpriority.Add(interaction) + else + overrided.priorities["[interaction.priority]"] = list(interaction) + +/datum/component/item_upgrade/proc/reset_owner() + if(weapon_upgrades[GUN_UPGRADE_REPLACE_INTERACTIONS]) + var/datum/guninteraction/interaction = weapon_upgrades[GUN_UPGRADE_REPLACE_INTERACTIONS] + if(istype(interaction)) + interaction.parentgun = null + if(weapon_upgrades[GUN_UPGRADE_DEFINE_WCLASS]) + if(istype(parent, /obj/item/part/gun/modular/stock)) + var/obj/item/part/gun/modular/stock/base = parent + weapon_upgrades[GUN_UPGRADE_DEFINE_WCLASS] = base.wclassmod + /datum/component/item_upgrade/proc/apply_values_firemode(datum/firemode/F) for(var/i in F.settings) diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index e3e6e8c9b8c..a846b75f986 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -1017,6 +1017,7 @@ sharp = initial(sharp) braceable = initial(braceable) recoil = getRecoil(init_recoil[1], init_recoil[2], init_recoil[3]) + w_class = initial(w_class) attack_verb = list() if(LAZYLEN(custom_default)) // this override is used by the artwork_revolver for RNG gun stats diff --git a/code/modules/projectiles/guns/projectile/battle_rifle/boltgun.dm b/code/modules/projectiles/guns/projectile/battle_rifle/boltgun.dm index 90694a08547..6630573aa64 100644 --- a/code/modules/projectiles/guns/projectile/battle_rifle/boltgun.dm +++ b/code/modules/projectiles/guns/projectile/battle_rifle/boltgun.dm @@ -77,14 +77,14 @@ playsound(src.loc, 'sound/weapons/guns/interact/rifle_boltback.ogg', 75, 1) bolt_open = !bolt_open if(bolt_open) - if(contents.len) + if(length(loaded)) if(chambered) to_chat(user, SPAN_NOTICE("You work the [message] open, ejecting [chambered]!")) chambered.forceMove(get_turf(src)) loaded -= chambered chambered = null else - var/obj/item/ammo_casing/B = loaded[loaded.len] + var/obj/item/ammo_casing/B = loaded[1] if(!B.is_caseless) to_chat(user, SPAN_NOTICE("You work the [message] open, ejecting [B]!")) B.forceMove(get_turf(src)) @@ -196,7 +196,7 @@ icon = 'icons/obj/guns/projectile/riose.dmi' icon_state = "boltgun_hand" item_suffix = "_hand" - matter = list(MATERIAL_STEEL = 10, MATERIAL_WOOD = 5) + matter = list(MATERIAL_STEEL = 13, MATERIAL_WOOD = 5) wielded_item_state = "_doble_hand" w_class = ITEM_SIZE_HUGE slot_flags = SLOT_BACK @@ -221,7 +221,7 @@ matter = list(MATERIAL_STEEL = 5) resultvars = list(/obj/item/gun/projectile/boltgun/handmade) gripvars = list(/obj/item/part/gun/modular/grip/wood) - mechanismvar = /obj/item/part/gun/modular/mechanism/boltgun + mechanismvar = /obj/item/part/gun/modular/mechanism/boltgun/junk barrelvars = list(/obj/item/part/gun/modular/barrel/lrifle/steel, /obj/item/part/gun/modular/barrel/srifle/steel, /obj/item/part/gun/modular/barrel/clrifle/steel) //// OBREZ //// diff --git a/code/modules/projectiles/guns/projectile/flaregun.dm b/code/modules/projectiles/guns/projectile/flaregun.dm index bf1f285697b..5a2c1eea232 100644 --- a/code/modules/projectiles/guns/projectile/flaregun.dm +++ b/code/modules/projectiles/guns/projectile/flaregun.dm @@ -45,14 +45,14 @@ playsound(loc, 'sound/weapons/guns/interact/rev_cock.ogg', 75, 1) bolt_open = !bolt_open if(bolt_open) - if(loaded.len) + if(length(loaded)) if(chambered) to_chat(user, SPAN_NOTICE("You snap the barrel open, ejecting [chambered]!")) chambered.forceMove(get_turf(src)) loaded -= chambered chambered = null else - var/obj/item/ammo_casing/shell = loaded[loaded.len] + var/obj/item/ammo_casing/shell = loaded[1] to_chat(user, SPAN_NOTICE("You snap the barrel open, ejecting [shell]!")) shell.forceMove(get_turf(src)) loaded -= shell diff --git a/code/modules/projectiles/guns/projectile/modular.dm b/code/modules/projectiles/guns/projectile/modular.dm index f5ea3f514c0..0d230834c44 100644 --- a/code/modules/projectiles/guns/projectile/modular.dm +++ b/code/modules/projectiles/guns/projectile/modular.dm @@ -51,6 +51,9 @@ max_upgrades = 6 + var/list/scope_damage_adds = list() + var/datum/gunoverrides/overridedatum + /obj/item/gun/projectile/automatic/modular/Initialize() gun_tags += GUN_MODULAR @@ -59,6 +62,7 @@ var/obj/item/part/gun/modular/new_part = new partPath(quality = gun_parts[partPath]) if(!new_part.I.rapid_apply(src)) visible_message(SPAN_WARNING("Something seems wrong... Maybe you should ask a professional for help?")) + overridedatum = new() refresh_upgrades() . = ..() update_icon() @@ -80,7 +84,12 @@ verbs -= MODULAR_VERBS // Removes all modularized verbs grip_type = initial(grip_type) good_calibers = list() // Won't ever be redefined, mechanism determines this, and when no mechanism is installed, we don't want anything here anyways + no_internal_mag = initial(no_internal_mag) + scope_damage_adds = list() + overridedatum?.reset() // clear first ..() + reset_action_buttons() + overridedatum?.cycle() // then use an assignment sort name = get_initial_name() /obj/item/gun/projectile/automatic/modular/update_icon() // V2 @@ -157,6 +166,75 @@ // Interactions +/datum/gunoverrides + var/list/priorities = list() + +/datum/gunoverrides/proc/reset() + for(var/list/wiped in priorities) + wiped.Cut() + priorities.Cut() + +/datum/gunoverrides/proc/call_Flag(obj/item/load, user, flag) + for(var/key in priorities) + var/list/priority = priorities[key] + var/done = FALSE + for(var/datum/guninteraction/tocheck in priority) // highest numbers first. + if(tocheck.interactionflags & flag) + switch(flag) + if(GI_ATTACKSELF) + if(tocheck.attack_self(user)) + done = TRUE + if(GI_LOAD) + if(tocheck.load_ammo(load, user)) + done = TRUE + if(GI_UNLOAD) + if(tocheck.unload_ammo(user)) + done = TRUE + if(GI_SPECIAL) + if(tocheck.special_check(user)) + done = TRUE + if(done) + return TRUE + +/datum/gunoverrides/proc/cycle() + var/list/slate = list() + for(var/number = 4, number >= 0, number -= 1) + slate["[number]"] = priorities["[number]"] ? priorities["[number]"] : null + priorities = slate.Copy() + +/obj/item/gun/projectile/automatic/modular/attack_self(mob/user) + if(!overridedatum.call_Flag(user = user, flag = GI_ATTACKSELF)) + . = ..() + +/obj/item/gun/projectile/automatic/modular/load_ammo(obj/item/A, mob/user) + if(!overridedatum.call_Flag(load = A, user = user, flag = GI_LOAD)) + . = ..() + + +/obj/item/gun/projectile/automatic/modular/unload_ammo(mob/user, allow_dump) + if(!overridedatum.call_Flag(user = user, flag = GI_UNLOAD)) + . = ..() + +/obj/item/gun/projectile/automatic/modular/special_check(mob/user) + if(!overridedatum.call_Flag(user = user, flag = GI_SPECIAL)) + . = ..() + +/obj/item/gun/projectile/automatic/modular/hand_spin(mob/living/carbon/caller) + overridedatum.call_Flag(user = caller, flag = GI_SPIN) + +/obj/item/gun/projectile/automatic/modular/proc/reset_action_buttons() + for(var/key in overridedatum.priorities) + var/list/priority = overridedatum.priorities[key] + for(var/datum/guninteraction/tocheck in priority) // highest numbers first. + if(tocheck.action_button_name && tocheck.action_button_proc) + action_button_name = tocheck.action_button_name + action_button_proc = tocheck.action_button_proc + return TRUE // can only have one button + // if we didn't override them + action_button_name = initial(action_button_name) + action_button_proc = initial(action_button_proc) + qdel(action) + /obj/item/gun/projectile/automatic/modular/can_interact(mob/user) if((!ishuman(user) && (loc != user)) || user.stat || user.restrained()) return 1 @@ -194,16 +272,19 @@ /obj/item/gun/projectile/automatic/modular/proc/fold(user) if(PARTMOD_FOLDING_STOCK & spriteTags) - if(PARTMOD_FOLDING_STOCK & statusTags) - to_chat(user, SPAN_NOTICE("You fold the stock on \the [src].")) - statusTags -= PARTMOD_FOLDING_STOCK - w_class = initial(w_class) - else - to_chat(user, SPAN_NOTICE("You unfold the stock on \the [src].")) - statusTags |= PARTMOD_FOLDING_STOCK - w_class = initial(w_class) + 1 - + for(var/obj/item/part/gun/modular/stock/toedit in gun_parts) // only gonna edit one, doing this to find it + if(PARTMOD_FOLDING_STOCK & statusTags) + to_chat(user, SPAN_NOTICE("You fold the stock on \the [src].")) + statusTags -= PARTMOD_FOLDING_STOCK + toedit.I.weapon_upgrades[GUN_UPGRADE_DEFINE_WCLASS] = 0 + else + to_chat(user, SPAN_NOTICE("You unfold the stock on \the [src].")) + statusTags |= PARTMOD_FOLDING_STOCK + toedit.I.weapon_upgrades[GUN_UPGRADE_DEFINE_WCLASS] = toedit.wclassmod + break + refresh_upgrades() + playsound(loc, 'sound/weapons/guns/interact/selector.ogg', 100, 1) update_icon() @@ -218,3 +299,14 @@ return FALSE load_ammo(I, user) update_held_icon() + +/obj/item/gun/projectile/automatic/modular/zoom(tileoffset, viewsize, stayzoomed) + ..() + refresh_upgrades() + if(zoom) + var/currentzoom = zoom_factors.Find(active_zoom_factor) + var/extra_damage + if(scope_damage_adds[currentzoom]) + extra_damage = scope_damage_adds[currentzoom] + damage_multiplier += extra_damage + diff --git a/code/modules/projectiles/guns/projectile/modular/interactions.dm b/code/modules/projectiles/guns/projectile/modular/interactions.dm new file mode 100644 index 00000000000..92cc43349c8 --- /dev/null +++ b/code/modules/projectiles/guns/projectile/modular/interactions.dm @@ -0,0 +1,99 @@ +/datum/guninteraction + var/obj/item/gun/projectile/automatic/modular/parentgun + var/obj/item/part/gun/modular/parentpart + var/priority = 0 + var/interactionflags + var/action_button_name = "" + var/action_button_proc = "" + +/datum/guninteraction/proc/attack_self(mob/user) + +/datum/guninteraction/proc/update_icon() + +/datum/guninteraction/proc/load_ammo(var/obj/item/A, mob/user) + +/datum/guninteraction/proc/unload_ammo(mob/user, var/allow_dump=1) + +/datum/guninteraction/proc/special_check(mob/user) + +/datum/guninteraction/New(newparent) + parentpart = newparent + +/datum/guninteraction/bolted + var/bolt_open = FALSE + var/message = "bolt" + interactionflags = GI_ATTACKSELF|GI_LOAD|GI_UNLOAD|GI_SPECIAL + + +/datum/guninteraction/bolted/New(newparent) + ..() + parentpart.part_overlay = jointext(list(initial(parentpart.part_overlay),"bolt_closed"), "") + +/datum/guninteraction/bolted/attack_self(mob/user) + bolt_act(user) + . = TRUE + +/datum/guninteraction/bolted/update_icon() + if(bolt_open) + parentpart.part_overlay = jointext(list(initial(parentpart.part_overlay),"bolt_open"), "") + else + parentpart.part_overlay = jointext(list(initial(parentpart.part_overlay),"bolt_closed"), "") + parentgun.update_icon() + +/datum/guninteraction/bolted/load_ammo(var/obj/item/A, mob/user) + if(!bolt_open) + return TRUE // disable operations + +/datum/guninteraction/bolted/unload_ammo(mob/user, var/allow_dump=1) + if(!bolt_open) + return TRUE // disable operations + + +/datum/guninteraction/bolted/special_check(mob/user) + if(bolt_open) + to_chat(user, SPAN_WARNING("You can't fire [parentgun] while the [message] is open!")) + return TRUE + +/datum/guninteraction/bolted/proc/bolt_act(mob/living/user) + + playsound(parentgun.loc, 'sound/weapons/guns/interact/rifle_boltback.ogg', 75, 1) + bolt_open = !bolt_open + if(bolt_open) + if(length(parentgun.loaded)||parentgun.chambered) + if(parentgun.chambered) + to_chat(user, SPAN_NOTICE("You work the [message] open, ejecting [parentgun.chambered]!")) + parentgun.chambered.forceMove(get_turf(parentgun)) + parentgun.loaded -= parentgun.chambered + parentgun.chambered = null + else + var/obj/item/ammo_casing/B = parentgun.loaded[1] + if(!B.is_caseless) + to_chat(user, SPAN_NOTICE("You work the [message] open, ejecting [B]!")) + B.forceMove(get_turf(parentgun)) + parentgun.loaded -= B + else + to_chat(user, SPAN_NOTICE("You work the [message] open.")) + B = parentgun.loaded[1] + parentgun.loaded -= B + qdel(B) + else + to_chat(user, SPAN_NOTICE("You work the [message] open.")) + else + to_chat(user, SPAN_NOTICE("You work the [message] closed.")) + playsound(parentgun.loc, 'sound/weapons/guns/interact/rifle_boltforward.ogg', 75, 1) + bolt_open = 0 + parentgun.add_fingerprint(user) + update_icon() + +/datum/guninteraction/zoomed + priority = 1 + interactionflags = GI_ATTACKSELF + +/datum/guninteraction/zoomed/attack_self(mob/user) + if(parentgun?.zoom) + parentgun.toggle_scope(user) + return TRUE + +/datum/guninteraction/zoomed/multizoom + action_button_name = "Switch zoom level" + action_button_proc = "switch_zoom" diff --git a/code/modules/projectiles/guns/projectile/modular/modular_bolt.dm b/code/modules/projectiles/guns/projectile/modular/modular_bolt.dm new file mode 100644 index 00000000000..ab1bb03f566 --- /dev/null +++ b/code/modules/projectiles/guns/projectile/modular/modular_bolt.dm @@ -0,0 +1,83 @@ +/obj/item/gun/projectile/automatic/modular/bolt // frame + name = "Excelsior BR .30 \"Kardashev-Mosin\"" + desc = "Weapon for hunting, or endless trench warfare. \ + If you’re on a budget, it’s a darn good rifle for just about everything." + icon = 'icons/obj/guns/projectile/modular/bolt.dmi' + slot_flags = SLOT_BACK + origin_tech = list(TECH_COMBAT = 2, TECH_MATERIAL = 2) + w_class = ITEM_SIZE_BULKY + force = WEAPON_FORCE_ROBUST + handle_casings = HOLD_CASINGS + load_method = SINGLE_CASING|SPEEDLOADER + matter = list(MATERIAL_PLASTEEL = 5, MATERIAL_PLASTIC = 4) + price_tag = 900 + spawn_blacklisted = TRUE + twohanded = TRUE + spriteTagBans = PARTMOD_FOLDING_STOCK + init_recoil = SLATE_RECOIL(1.5) + required_parts = list(/obj/item/part/gun/modular/barrel = 0, /obj/item/part/gun/modular/grip = 0, /obj/item/part/gun/modular/stock = 0,\ + /obj/item/part/gun/modular/mechanism/boltgun = 0, /obj/item/part/gun/modular/sights = -1, /obj/item/part/gun/modular/bayonet = -1) + init_firemodes = list() // boltguns don't have firemodes! + +/obj/item/gun/projectile/automatic/modular/bolt/get_initial_name() + if(grip_type) + switch(grip_type) + if("wood") + if(caliber == CAL_SRIFLE) + return "FS SR .20 \"Arasaka\"" // or small + else + return "SA SR [caliber] \"Novakovic\"" // we love/fear them all + if("black") + return "NT SR [caliber] \"Eradicator\"" + if("rubber") + return "FS SR [caliber] \"Kadmin\"" + if("excelsior") + return "Excelsior SR [caliber] \"Kardashev-Mosin\"" + if("serbian") + return "SA AMR [caliber] \"Hristov\"" // big + if("makeshift") + return "HM SR [caliber] \"Riose\"" + else + return "SR [caliber] \"Boltie\"" + +/obj/item/gun/projectile/automatic/modular/bolt/excel + gun_parts = list(/obj/item/part/gun/modular/grip/excel = 0, /obj/item/part/gun/modular/mechanism/boltgun = 0, /obj/item/part/gun/modular/barrel/lrifle/steel = 0, \ + /obj/item/part/gun/modular/stock/longrifle = 0, /obj/item/part/gun/modular/bayonet = 0) + +/obj/item/gun/projectile/automatic/modular/bolt/serbian + desc = "Weapon for hunting, or endless trench warfare. \ + If you’re on a budget, it’s a darn good rifle for just about everything. \ + This copy, in fact, is a reverse-engineered poor-quality copy of a more perfect copy of an ancient rifle" + init_recoil = SLATE_RECOIL(1.7) + spawn_blacklisted = FALSE + +/obj/item/gun/projectile/automatic/modular/bolt/serbian/finished + gun_parts = list(/obj/item/part/gun/modular/grip/wood = 1, /obj/item/part/gun/modular/mechanism/boltgun = 0,\ + /obj/item/part/gun/modular/barrel/lrifle/steel = 0, /obj/item/part/gun/modular/stock/longrifle = 0, /obj/item/part/gun/modular/bayonet/steel = 0) + +/obj/item/gun/projectile/automatic/modular/bolt/fs + gun_parts = list(/obj/item/part/gun/modular/grip/rubber = 0, /obj/item/part/gun/modular/mechanism/boltgun = 0, /obj/item/part/gun/modular/barrel/srifle/long = 0,\ + /obj/item/part/gun/modular/stock/longrifle = 0, /obj/item/part/gun/modular/sights/scopebig = 0) + +/obj/item/gun/projectile/automatic/modular/bolt/fs/civilian + gun_parts = list(/obj/item/part/gun/modular/grip/wood = -1, /obj/item/part/gun/modular/mechanism/boltgun = 0, /obj/item/part/gun/modular/barrel/srifle/long = 0,\ + /obj/item/part/gun/modular/stock/longrifle = 0, /obj/item/part/gun/modular/sights/scopesmall = 0) // nerfed the stock to simulate civilian shittyness that apparently increases recoil by 1/9th + spawn_blacklisted = FALSE + +/obj/item/gun/projectile/automatic/modular/bolt/handmade + desc = "A handmade bolt action rifle, made from junk and some spare parts." + spawn_blacklisted = FALSE + matter = list(MATERIAL_STEEL = 4) + init_recoil = SLATE_RECOIL(2) // lower quality frame + price_tag = 800 + +/obj/item/gun/projectile/automatic/modular/bolt/handmade/finished + gun_parts = list(/obj/item/part/gun/modular/grip/makeshift = 0, /obj/item/part/gun/modular/mechanism/boltgun/junk = 0, /obj/item/part/gun/modular/barrel/lrifle/steel = 0,\ + /obj/item/part/gun/modular/stock/longrifle = 0, /obj/item/part/gun/modular/bayonet = 0) + +/obj/item/gun/projectile/automatic/modular/bolt/sniper + desc = "A portable anti-armour rifle, it was originally designed for use against armoured exosuits. It is capable of punching through windows and non-reinforced walls with ease, but suffers from overpenetration at close range. Fires armor piercing .60 shells." + +/obj/item/gun/projectile/automatic/modular/bolt/sniper/finished + gun_parts = list(/obj/item/part/gun/modular/grip/serb = 0, /obj/item/part/gun/modular/mechanism/boltgun/heavy = 0, /obj/item/part/gun/modular/barrel/antim/long = 0,\ + /obj/item/part/gun/modular/stock/heavy = 0, /obj/item/part/gun/modular/sights/customizable/scopeheavy = 0) diff --git a/code/modules/projectiles/guns/projectile/shotgun/doublebarrel.dm b/code/modules/projectiles/guns/projectile/shotgun/doublebarrel.dm index 55441bf8449..e1e211f294c 100644 --- a/code/modules/projectiles/guns/projectile/shotgun/doublebarrel.dm +++ b/code/modules/projectiles/guns/projectile/shotgun/doublebarrel.dm @@ -64,7 +64,6 @@ else playsound(src.loc, 'sound/weapons/guns/interact/shotgun_close.ogg', 75, 1) to_chat(user, SPAN_NOTICE("You snap the barrel closed")) - bolt_open = 0 add_fingerprint(user) update_icon() diff --git a/code/modules/projectiles/guns/projectile/sniper.dm b/code/modules/projectiles/guns/projectile/sniper.dm index 6e3a79cf5e6..66bd2daa097 100644 --- a/code/modules/projectiles/guns/projectile/sniper.dm +++ b/code/modules/projectiles/guns/projectile/sniper.dm @@ -83,14 +83,14 @@ playsound(src.loc, 'sound/weapons/guns/interact/rifle_boltback.ogg', 75, 1) bolt_open = !bolt_open if(bolt_open) - if(contents.len) + if(length(loaded)) if(chambered) to_chat(user, SPAN_NOTICE("You work the bolt open, ejecting [chambered]!")) chambered.forceMove(get_turf(src)) loaded -= chambered chambered = null else - var/obj/item/ammo_casing/B = loaded[loaded.len] + var/obj/item/ammo_casing/B = loaded[1] to_chat(user, SPAN_NOTICE("You work the bolt open, ejecting [B]!")) B.forceMove(get_turf(src)) loaded -= B diff --git a/icons/obj/crafts.dmi b/icons/obj/crafts.dmi index 7f510b84c7e37b249bd78629ed8d933ff40839df..80c310ea350f583516c1c4c416c8c087524397ce 100644 GIT binary patch delta 8027 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 delta 7887 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 qS#e*8%aA%;)WSl7BwdPzo|C(lwH1!T$jy69gUr diff --git a/icons/obj/guns/projectile/modular/bolt.dmi b/icons/obj/guns/projectile/modular/bolt.dmi new file mode 100644 index 0000000000000000000000000000000000000000..bbbfef4d598539f473e199202d062d91af466ca9 GIT binary patch literal 12446 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 SKp=hnTJ?d3Qn|v5SN{cUI!l=V literal 0 HcmV?d00001 From f7076537437c71dd2747024d502e9930e2265eb0 Mon Sep 17 00:00:00 2001 From: "Nestor Jr." Date: Tue, 9 Sep 2025 22:27:38 +0000 Subject: [PATCH 32/44] Automatic changelog generation for PR #8590 [ci skip] --- html/changelogs/AutoChangeLog-pr-8590.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-8590.yml diff --git a/html/changelogs/AutoChangeLog-pr-8590.yml b/html/changelogs/AutoChangeLog-pr-8590.yml new file mode 100644 index 00000000000..3905262bbaf --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8590.yml @@ -0,0 +1,5 @@ +author: Chickenish +delete-after: true +changes: + - tweak: 'balance: Prying damaged flooring now produces scrap metal instead of deleting + the floor with no returns.' From 844315521de66b70edc9c6a3b543b86ae0b87749 Mon Sep 17 00:00:00 2001 From: "Nestor Jr." Date: Tue, 9 Sep 2025 22:28:00 +0000 Subject: [PATCH 33/44] Automatic changelog generation for PR #8570 [ci skip] --- html/changelogs/AutoChangeLog-pr-8570.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-8570.yml diff --git a/html/changelogs/AutoChangeLog-pr-8570.yml b/html/changelogs/AutoChangeLog-pr-8570.yml new file mode 100644 index 00000000000..5fd14d077a1 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8570.yml @@ -0,0 +1,7 @@ +author: chickenish, bobob +delete-after: true +changes: + - rscadd: Added modular bolt-gun config + - tweak: tweaked Riose cost + - bugfix: fixed clrifle bolt action ammo choice discrepancy + - imageadd: added modular boltgun parts From 23629976ba1c9cdf998bc69b0de717bf75a88904 Mon Sep 17 00:00:00 2001 From: vode-code <65709050+vode-code@users.noreply.github.com> Date: Tue, 9 Sep 2025 15:31:21 -0700 Subject: [PATCH 34/44] Data Core Must Die (#8594) * the datacore is dead, long live the crew record file list apparently this stuff dates back to may 2012 new savefile version clears out old records preferences reworked security records character name replacer proc updates renamed items reversed records sort polarity reworked respawn character proc adds setup disks for preset console programs reduced security and medical file cabinet data to that which could be reasonably accurate for an entire round deleted unused real rank system fixed potential fake crew arrival runtime cargo positions now named guild positions and JOBS_CARGO is now JOBS_GUILD nonhuman positions now named silicon positions and JOBS_NONHUMAN is now JOBS_SILICON world manifest now checks department rather than preset job names updated PAI general, medical, and security records access program fixed freelancer perk define name added green thumb and neat job perk defines deleted duplicate records customization file numbered 5 pruned record customization WIP * return to default for datacore purge update note: setup disks merged seperately security notes simplified --------- Co-authored-by: Humonitarian <61586732+Humonitarian@users.noreply.github.com> --- cev_eris.dme | 5 +- code/__DEFINES/jobs.dm | 4 +- code/__DEFINES/perks.dm | 4 +- code/__HELPERS/manifest.dm | 6 +- code/__HELPERS/names.dm | 4 +- code/__HELPERS/unsorted.dm | 23 +- code/controllers/subsystems/ticker.dm | 1 - code/datums/datacore.dm | 454 ------------ code/datums/setup_option/backgrounds/fate.dm | 2 +- code/datums/topic/admin.dm | 8 +- code/datums/topic/world.dm | 35 +- code/datums/uplink/announcements.dm | 52 +- code/defines/procs/announce.dm | 5 +- code/game/jobs/jobs.dm | 6 +- code/game/machinery/computer/medical.dm | 519 -------------- code/game/machinery/computer/security.dm | 658 ------------------ code/game/machinery/hologram.dm | 5 +- code/game/objects/items/devices/uplink.dm | 8 +- .../circuitboards/computer/computer.dm | 6 - code/modules/admin/DB ban/functions.dm | 2 +- .../admin_secrets/show_crew_manifest.dm | 2 +- code/modules/admin/verbs/randomverbs.dm | 24 +- .../asset_cache/assets/legacyuiicons.dm | 5 + .../preference_setup/background/03_records.dm | 109 ++- .../preference_setup/general/05_records.dm | 64 -- .../loadout/lists/accessories.dm | 2 +- .../preference_setup/loadout/lists/suits.dm | 4 +- code/modules/client/preferences.dm | 5 +- code/modules/client/preferences_savefile.dm | 2 +- code/modules/economy/Accounts.dm | 6 +- code/modules/mob/hear_say.dm | 5 +- .../mob/living/carbon/human/examine.dm | 13 +- code/modules/mob/living/carbon/human/human.dm | 265 +++---- .../mob/living/carbon/human/human_defines.dm | 3 - code/modules/mob/living/living.dm | 13 +- code/modules/mob/living/silicon/ai/ai.dm | 4 +- code/modules/mob/living/silicon/pai/pai.dm | 12 +- .../living/silicon/pai/software_modules.dm | 98 ++- code/modules/mob/mob_helpers.dm | 6 +- code/modules/mob/new_player/new_player.dm | 1 - .../computers/modular_computer/core.dm | 2 + .../modular_computers/file_system/program.dm | 5 +- .../file_system/programs/command/card.dm | 2 +- .../file_system/programs/generic/records.dm | 135 ++-- .../file_system/reports/crew_record.dm | 173 ++++- .../file_system/reports/report.dm | 17 +- .../file_system/reports/report_field.dm | 149 +++- code/modules/nano/modules/tgui_type.dm | 92 +++ code/modules/paperwork/filingcabinet.dm | 45 +- code/modules/research/designs/circuits.dm | 10 - code/modules/research/nodes/biotech.dm | 6 +- icons/ui_icons/dmis/uiicons16.dmi | Bin 0 -> 6642 bytes maps/main_ship/eris_classic.dmm | 19 +- nano/templates/crew_records.tmpl | 21 + nano/templates/pai_medrecords.tmpl | 54 +- nano/templates/pai_secrecords.tmpl | 34 +- tgui/packages/tgui/interfaces/Computer.tsx | 169 +++++ tgui/packages/tgui/interfaces/CrewRecords.tsx | 332 +++++++++ tgui/public/tgui.bundle.js | 2 +- 59 files changed, 1398 insertions(+), 2319 deletions(-) delete mode 100644 code/datums/datacore.dm delete mode 100644 code/game/machinery/computer/medical.dm delete mode 100644 code/game/machinery/computer/security.dm create mode 100644 code/modules/asset_cache/assets/legacyuiicons.dm delete mode 100644 code/modules/client/preference_setup/general/05_records.dm create mode 100644 code/modules/nano/modules/tgui_type.dm create mode 100644 icons/ui_icons/dmis/uiicons16.dmi create mode 100644 tgui/packages/tgui/interfaces/Computer.tsx create mode 100644 tgui/packages/tgui/interfaces/CrewRecords.tsx diff --git a/cev_eris.dme b/cev_eris.dme index 28b181ae6ef..95a6ad58348 100644 --- a/cev_eris.dme +++ b/cev_eris.dme @@ -281,7 +281,6 @@ #include "code\datums\callback.dm" #include "code\datums\category.dm" #include "code\datums\contract.dm" -#include "code\datums\datacore.dm" #include "code\datums\datum.dm" #include "code\datums\datum_click_handlers.dm" #include "code\datums\datum_hud.dm" @@ -751,14 +750,12 @@ #include "code\game\machinery\computer\computer.dm" #include "code\game\machinery\computer\guestpass.dm" #include "code\game\machinery\computer\law.dm" -#include "code\game\machinery\computer\medical.dm" #include "code\game\machinery\computer\message.dm" #include "code\game\machinery\computer\Operating.dm" #include "code\game\machinery\computer\pod.dm" #include "code\game\machinery\computer\prisoner.dm" #include "code\game\machinery\computer\prisonshuttle.dm" #include "code\game\machinery\computer\robot.dm" -#include "code\game\machinery\computer\security.dm" #include "code\game\machinery\computer\station_alert.dm" #include "code\game\machinery\doors\airlock.dm" #include "code\game\machinery\doors\airlock_control.dm" @@ -1458,6 +1455,7 @@ #include "code\modules\asset_cache\asset_cache_item.dm" #include "code\modules\asset_cache\asset_list.dm" #include "code\modules\asset_cache\asset_list_items.dm" +#include "code\modules\asset_cache\assets\legacyuiicons.dm" #include "code\modules\asset_cache\assets\sanity.dm" #include "code\modules\asset_cache\assets\sheetmaterials.dm" #include "code\modules\asset_cache\assets\stats.dm" @@ -2277,6 +2275,7 @@ #include "code\modules\nano\modules\human_appearance.dm" #include "code\modules\nano\modules\law_manager.dm" #include "code\modules\nano\modules\nano_module.dm" +#include "code\modules\nano\modules\tgui_type.dm" #include "code\modules\onestar\generator.dm" #include "code\modules\onestar\os_cash.dm" #include "code\modules\onestar\os_turret.dm" diff --git a/code/__DEFINES/jobs.dm b/code/__DEFINES/jobs.dm index 18a82d9b01c..2bebcf467e3 100644 --- a/code/__DEFINES/jobs.dm +++ b/code/__DEFINES/jobs.dm @@ -8,10 +8,10 @@ #define JOBS_MEDICAL "Moebius Biolab Officer","Moebius Doctor","Moebius Psychiatrist","Moebius Chemist","Moebius Paramedic","Moebius Bio-Engineer" #define JOBS_SCIENCE "Moebius Expedition Overseer","Moebius Scientist","Moebius Roboticist" #define JOBS_MOEBIUS "Moebius Biolab Officer","Moebius Doctor","Moebius Psychiatrist","Moebius Chemist","Moebius Paramedic","Moebius Bio-Engineer","Moebius Expedition Overseer","Moebius Scientist","Moebius Roboticist" -#define JOBS_CARGO "Guild Merchant","Guild Technician","Guild Miner", +#define JOBS_GUILD "Guild Merchant","Guild Technician","Guild Miner", #define JOBS_CIVILIAN "Club Manager","Club Worker","Club Artist",ASSISTANT_TITLE #define JOBS_CHURCH "NeoTheology Preacher","NeoTheology Acolyte","NeoTheology Agrolyte","NeoTheology Custodian" -#define JOBS_NONHUMAN "AI","Robot","pAI" +#define JOBS_SILICON "AI","Robot","pAI" #define CREDITS "¢" #define CREDS "¢" diff --git a/code/__DEFINES/perks.dm b/code/__DEFINES/perks.dm index 210eaa70c3e..d6571683e42 100644 --- a/code/__DEFINES/perks.dm +++ b/code/__DEFINES/perks.dm @@ -1,6 +1,6 @@ //fate perks #define PERK_PAPER_WORM /datum/perk/fate/paper_worm -#define PERK_FREELACER /datum/perk/fate/freelancer +#define PERK_FREELANCER /datum/perk/fate/freelancer #define PERK_NIHILIST /datum/perk/fate/nihilist #define PERK_MORALIST /datum/perk/fate/moralist #define PERK_ALCOHOLIC /datum/perk/fate/alcoholic @@ -49,6 +49,8 @@ #define PERK_CODESPEAK_COP /datum/perk/codespeak #define PERK_CODESPEAK_SERB /datum/perk/codespeak/serbian #define PERK_TECHNOMANCER /datum/perk/inspiration +#define PERK_NEAT /datum/perk/neat +#define PERK_GREEN_THUMB /datum/perk/greenthumb #define PERK_CLUB /datum/perk/job/club #define PERK_CHANNELING /datum/perk/channeling diff --git a/code/__HELPERS/manifest.dm b/code/__HELPERS/manifest.dm index 334e07960d3..2215f1bbb96 100644 --- a/code/__HELPERS/manifest.dm +++ b/code/__HELPERS/manifest.dm @@ -142,13 +142,13 @@ "sec" = filtered_nano_crew_manifest(security_positions),\ "eng" = filtered_nano_crew_manifest(engineering_positions),\ "med" = filtered_nano_crew_manifest(medical_positions),\ - "sup" = filtered_nano_crew_manifest(cargo_positions),\ + "sup" = filtered_nano_crew_manifest(guild_positions),\ "chr" = filtered_nano_crew_manifest(church_positions),\ - "bot" = silicon_nano_crew_manifest(nonhuman_positions),\ + "bot" = silicon_nano_crew_manifest(silicon_positions),\ "civ" = filtered_nano_crew_manifest(civilian_positions)\ ) /proc/flat_nano_crew_manifest() . = list() . += filtered_nano_crew_manifest(null, TRUE) - . += silicon_nano_crew_manifest(nonhuman_positions) \ No newline at end of file + . += silicon_nano_crew_manifest(silicon_positions) \ No newline at end of file diff --git a/code/__HELPERS/names.dm b/code/__HELPERS/names.dm index c87268b454f..2c8a171b4e2 100644 --- a/code/__HELPERS/names.dm +++ b/code/__HELPERS/names.dm @@ -94,8 +94,8 @@ var/syndicate_code_response//Code response for contractors. var/locations[] = LAZYLEN(SSmapping.main_ship_areas_by_name) ? SSmapping.main_ship_areas_by_name : drinks // If null, defaults to drinks instead. var/names[] = list() - for(var/datum/data/record/t in data_core.general)//Picks from crew manifest. - names += t.fields["name"] + for(var/datum/computer_file/report/crew_record/t in GLOB.all_crew_records)//Picks from crew manifest. + names += t.get_name() var/maxwords = words//Extra var to check for duplicates. diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index 0785b29e9fc..7a6bf8d5007 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -276,26 +276,21 @@ Turf and target are seperate in case you want to teleport some distance from a t mind.name = newname if(oldname) - //update the datacore records! This is goig to be a bit costly. - for(var/list/L in list(data_core.general, data_core.medical, data_core.security, data_core.locked)) - for(var/datum/data/record/R in L) - if(R.fields["name"] == oldname) - R.fields["name"] = newname - break + for(var/datum/computer_file/report/crew_record/R in GLOB.all_crew_records) + if(R.get_name() == oldname) + R.set_name(newname) //update our pda and id if we have them on our person - var/list/searching = GetAllContents(searchDepth = 3) - var/search_id = 1 - var/search_pda = 1 - - for(var/A in searching) - if( search_id && istype(A,/obj/item/card/id) ) + for(var/obj/A in GetAllContents(searchDepth = 3)) + if(istype(A,/obj/item/card/id)) var/obj/item/card/id/ID = A if(ID.registered_name == oldname) ID.registered_name = newname ID.name = "[newname]'s ID Card ([ID.assignment])" - if(!search_pda) break - search_id = 0 + if(istype(A.loc, /obj/item/modular_computer)) + var/obj/item/modular_computer/tolabel = A.loc + tolabel.update_label() + break return 1 diff --git a/code/controllers/subsystems/ticker.dm b/code/controllers/subsystems/ticker.dm index 1777bd31dcf..1fe7dfc2abc 100644 --- a/code/controllers/subsystems/ticker.dm +++ b/code/controllers/subsystems/ticker.dm @@ -241,7 +241,6 @@ SUBSYSTEM_DEF(ticker) if(!H.mind || player_is_antag(H.mind, only_offstation_roles = 1) || !SSjob.ShouldCreateRecords(H.mind.assigned_role)) continue CreateModularRecord(H) - data_core.manifest() CHECK_TICK diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm deleted file mode 100644 index 5fbabd76693..00000000000 --- a/code/datums/datacore.dm +++ /dev/null @@ -1,454 +0,0 @@ -var/global/list/PDA_Manifest = list() -var/global/ManifestJSON - -/hook/startup/proc/createDatacore() - data_core = new /datum/datacore() - return 1 - -/datum/datacore - var/name = "datacore" - var/medical[] = list() - var/general[] = list() - var/security[] = list() - //This list tracks characters spawned in the world and cannot be modified in-game. Currently referenced by respawn_character(). - var/locked[] = list() - - -/datum/datacore/proc/get_manifest(monochrome, OOC) - var/list/heads = new() - var/list/sec = new() - var/list/eng = new() - var/list/med = new() - var/list/sci = new() - var/list/car = new() - var/list/chr = new() - var/list/civ = new() - var/list/bot = new() - var/list/misc = new() - var/list/isactive = new() - var/dat = {" - - - - "} - var/even = 0 - // sort mobs - for(var/datum/data/record/t in data_core.general) - var/name = t.fields["name"] - var/rank = t.fields["rank"] - var/real_rank = make_list_rank(t.fields["real_rank"]) - - if(OOC) - var/active = 0 - for(var/mob/M in GLOB.player_list) - if(M.real_name == name && M.client && M.client.inactivity <= 10 * 60 * 10) - active = 1 - break - isactive[name] = active ? "Active" : "Inactive" - else - isactive[name] = t.fields["p_stat"] - //world << "[name]: [rank]" - //cael - to prevent multiple appearances of a player/job combination, add a continue after each line - var/department = 0 - if(real_rank in command_positions) - heads[name] = rank - department = 1 - if(real_rank in security_positions) - sec[name] = rank - department = 1 - if(real_rank in engineering_positions) - eng[name] = rank - department = 1 - if(real_rank in medical_positions) - med[name] = rank - department = 1 - if(real_rank in science_positions) - sci[name] = rank - department = 1 - if(real_rank in cargo_positions) - car[name] = rank - department = 1 - if(real_rank in church_positions) - chr[name] = rank - department = 1 - if(real_rank in civilian_positions) - civ[name] = rank - department = 1 - if(!department && !(name in heads)) - misc[name] = rank - - // Synthetics don't have actual records, so we will pull them from here. -/* for(var/mob/living/silicon/ai/ai in SSmobs.mob_list) - bot[ai.name] = "Artificial Intelligence" - - for(var/mob/living/silicon/robot/robot in SSmobs.mob_list) - // No combat/syndicate cyborgs, no drones. - if(robot.module && robot.module.hide_on_manifest) - continue - - bot[robot.name] = "[robot.modtype] [robot.braintype]"*/ - - if(bot.len > 0) - dat += "" - for(name in bot) - dat += "" - even = !even - - if(heads.len > 0) - dat += "" - for(name in heads) - dat += "" - even = !even - if(sec.len > 0) - dat += "" - for(name in sec) - dat += "" - even = !even - if(eng.len > 0) - dat += "" - for(name in eng) - dat += "" - even = !even - if(med.len > 0) - dat += "" - for(name in med) - dat += "" - even = !even - if(sci.len > 0) - dat += "" - for(name in sci) - dat += "" - even = !even - if(car.len > 0) - dat += "" - for(name in car) - dat += "" - even = !even - if(chr.len > 0) - dat += "" - for(name in chr) - dat += "" - even = !even - if(civ.len > 0) - dat += "" - for(name in civ) - dat += "" - even = !even - if(misc.len > 0) - dat += "" - for(name in misc) - dat += "" - even = !even - - dat += "
NameRankActivity
Silicon
[name][bot[name]][isactive[name]]
Heads
[name][heads[name]][isactive[name]]
Security
[name][sec[name]][isactive[name]]
Engineering
[name][eng[name]][isactive[name]]
Medical
[name][med[name]][isactive[name]]
Science
[name][sci[name]][isactive[name]]
Guild
[name][car[name]][isactive[name]]
Church
[name][chr[name]][isactive[name]]
Civilian
[name][civ[name]][isactive[name]]
Miscellaneous
[name][misc[name]][isactive[name]]
" - dat = replacetext(dat, "\n", "") // so it can be placed on paper correctly - dat = replacetext(dat, "\t", "") - return dat - -/datum/datacore/proc/manifest() - spawn() - for(var/mob/living/carbon/human/H in GLOB.player_list) - manifest_inject(H) - return - -/datum/datacore/proc/manifest_modify(var/name, var/assignment) - ResetPDAManifest() - var/datum/data/record/foundrecord - var/real_title = assignment - - for(var/datum/data/record/t in data_core.general) - if (t) - if(t.fields["name"] == name) - foundrecord = t - break - - if(foundrecord) - foundrecord.fields["rank"] = assignment - foundrecord.fields["real_rank"] = real_title - -/datum/datacore/proc/manifest_inject(var/mob/living/carbon/human/H) - if(H.mind && !player_is_antag(H.mind, only_offstation_roles = 1) && H.job != ASSISTANT_TITLE) - var/assignment = GetAssignment(H) - - var/id = generate_record_id() - //General Record - var/datum/data/record/G = CreateGeneralRecord(H, id) - G.fields["name"] = H.real_name - G.fields["real_rank"] = H.mind.assigned_role - G.fields["rank"] = assignment - G.fields["age"] = H.age - G.fields["fingerprint"] = H.fingers_trace - if(H.mind.initial_account) - G.fields["pay_account"] = H.mind.initial_account.account_number ? H.mind.initial_account.account_number : "N/A" - G.fields["email"] = H.mind.initial_email_login["login"] - G.fields["p_stat"] = "Active" - G.fields["m_stat"] = "Stable" - G.fields["sex"] = H.gender - if(H.gen_record && !jobban_isbanned(H, "Records")) - G.fields["notes"] = H.gen_record - - //Medical Record - var/datum/data/record/M = CreateMedicalRecord(H.real_name, id) - M.fields["b_type"] = H.b_type - M.fields["b_dna"] = H.dna_trace - //M.fields["id_gender"] = gender2text(H.identifying_gender) - if(H.med_record && !jobban_isbanned(H, "Records")) - M.fields["notes"] = H.med_record - - //Security Record - var/datum/data/record/S = CreateSecurityRecord(H.real_name, id) - if(H.sec_record && !jobban_isbanned(H, "Records")) - S.fields["notes"] = H.sec_record - - //Locked Record - var/datum/data/record/L = new() - L.fields["id"] = md5("[H.real_name][H.mind.assigned_role]") - L.fields["name"] = H.real_name - L.fields["rank"] = H.mind.assigned_role - L.fields["age"] = H.age - L.fields["fingerprint"] = H.fingers_trace - L.fields["sex"] = H.gender - L.fields["b_type"] = H.b_type - L.fields["b_dna"] = H.dna_trace - L.fields["image"] = getFlatIcon(H) //This is god-awful - if(H.exploit_record && !jobban_isbanned(H, "Records")) - L.fields["exploit_record"] = H.exploit_record - else - L.fields["exploit_record"] = "No additional information acquired." - locked += L - return - -/proc/generate_record_id() - return add_zero(num2hex(rand(1, 65535)), 4) //no point generating higher numbers because of the limitations of num2hex - -/proc/get_id_photo(var/mob/living/carbon/human/H, var/assigned_role) - - var/icon/preview_icon = new(H.stand_icon) - var/icon/temp - - var/datum/sprite_accessory/hair_style = GLOB.hair_styles_list[H.h_style] - if(hair_style) - temp = new/icon(hair_style.icon, hair_style.icon_state) - temp.Blend(H.hair_color, ICON_ADD) - - hair_style = GLOB.facial_hair_styles_list[H.h_style] - if(hair_style) - var/icon/facial = new/icon(hair_style.icon, hair_style.icon_state) - facial.Blend(H.facial_color, ICON_ADD) - temp.Blend(facial, ICON_OVERLAY) - - preview_icon.Blend(temp, ICON_OVERLAY) - - - var/datum/job/J = SSjob.GetJob(H.mind.assigned_role) - if(J) - var/t_state - temp = new /icon('icons/inventory/uniform/mob.dmi', t_state) - - temp.Blend(new /icon('icons/inventory/feet/mob.dmi', t_state), ICON_OVERLAY) - else - temp = new /icon('icons/inventory/uniform/mob.dmi', "grey") - temp.Blend(new /icon('icons/inventory/feet/mob.dmi', "black"), ICON_OVERLAY) - - preview_icon.Blend(temp, ICON_OVERLAY) - - qdel(temp) - - return preview_icon - -/datum/datacore/proc/CreateGeneralRecord(var/mob/living/carbon/human/H, var/id) - ResetPDAManifest() - var/icon/front - var/icon/side - if(H) - front = getFlatIcon(H, SOUTH) - side = getFlatIcon(H, WEST) - else - var/mob/living/carbon/human/dummy = new() - front = new(get_id_photo(dummy), dir = SOUTH) - side = new(get_id_photo(dummy), dir = WEST) - qdel(dummy) - - if(!id) id = text("[]", add_zero(num2hex(rand(1, 1.6777215E7)), 6)) - var/datum/data/record/G = new /datum/data/record() - G.name = "Employee Record #[id]" - G.fields["name"] = "New Record" - G.fields["id"] = id - G.fields["rank"] = "Unassigned" - G.fields["real_rank"] = "Unassigned" - G.fields["sex"] = "Male" - G.fields["age"] = "Unknown" - G.fields["fingerprint"] = "Unknown" - G.fields["p_stat"] = "Active" - G.fields["m_stat"] = "Stable" - G.fields["species"] = SPECIES_HUMAN - G.fields["photo_front"] = front - G.fields["photo_side"] = side - G.fields["notes"] = "No notes found." - general += G - - return G - -/datum/datacore/proc/CreateSecurityRecord(var/name, var/id) - ResetPDAManifest() - var/datum/data/record/R = new /datum/data/record() - R.name = "Security Record #[id]" - R.fields["name"] = name - R.fields["id"] = id - R.fields["criminal"] = "None" - R.fields["mi_crim"] = "None" - R.fields["mi_crim_d"] = "No minor crime convictions." - R.fields["ma_crim"] = "None" - R.fields["ma_crim_d"] = "No major crime convictions." - R.fields["notes"] = "No notes." - R.fields["notes"] = "No notes." - data_core.security += R - - return R - -/datum/datacore/proc/CreateMedicalRecord(var/name, var/id) - ResetPDAManifest() - var/datum/data/record/M = new() - M.name = "Medical Record #[id]" - M.fields["id"] = id - M.fields["name"] = name - M.fields["b_type"] = "AB+" - M.fields["b_dna"] = md5(name) - M.fields["mi_dis"] = "None" - M.fields["mi_dis_d"] = "No minor disabilities have been declared." - M.fields["ma_dis"] = "None" - M.fields["ma_dis_d"] = "No major disabilities have been diagnosed." - M.fields["alg"] = "None" - M.fields["alg_d"] = "No allergies have been detected in this patient." - M.fields["cdi"] = "None" - M.fields["cdi_d"] = "No diseases have been diagnosed at the moment." - M.fields["notes"] = "No notes found." - data_core.medical += M - - return M - -/datum/datacore/proc/ResetPDAManifest() - if(PDA_Manifest.len) - PDA_Manifest.Cut() - -/proc/find_general_record(field, value) - return find_record(field, value, data_core.general) - -/proc/find_medical_record(field, value) - return find_record(field, value, data_core.medical) - -/proc/find_security_record(field, value) - return find_record(field, value, data_core.security) - -/proc/find_record(field, value, list/L) - for(var/datum/data/record/R in L) - if(R.fields[field] == value) - return R - -/*/proc/GetAssignment(var/mob/living/carbon/human/H) - if(H.mind.assigned_role) - return H.mind.assigned_role - else if(H.job) - return H.job - else - return "Unassigned" -*/ -/var/list/acting_rank_prefixes = list("acting", "temporary", "interim", "provisional") - -/proc/make_list_rank(rank) - for(var/prefix in acting_rank_prefixes) - if(findtext(rank, "[prefix] ", 1, 2+length(prefix))) - return copytext(rank, 2+length(prefix)) - return rank - -/datum/datacore/proc/get_manifest_json() - if(PDA_Manifest.len) - return - var/heads[0] - var/sec[0] - var/eng[0] - var/med[0] - var/sci[0] - var/chr[0] - var/civ[0] - var/bot[0] - var/misc[0] - for(var/datum/data/record/t in data_core.general) - var/name = sanitize(t.fields["name"]) - var/rank = sanitize(t.fields["rank"]) - var/real_rank = make_list_rank(t.fields["real_rank"]) - - var/isactive = t.fields["p_stat"] - var/department = 0 - var/depthead = 0 // Department Heads will be placed at the top of their lists. - if(real_rank in command_positions) - heads[++heads.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - depthead = 1 - if(rank=="Captain" && heads.len != 1) - heads.Swap(1, heads.len) - - if(real_rank in security_positions) - sec[++sec.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - if(depthead && sec.len != 1) - sec.Swap(1, sec.len) - - if(real_rank in engineering_positions) - eng[++eng.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - if(depthead && eng.len != 1) - eng.Swap(1, eng.len) - - if(real_rank in medical_positions) - med[++med.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - if(depthead && med.len != 1) - med.Swap(1, med.len) - - if(real_rank in science_positions) - sci[++sci.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - if(depthead && sci.len != 1) - sci.Swap(1, sci.len) - - if(real_rank in church_positions) - chr[++chr.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - if(depthead && chr.len != 1) - chr.Swap(1, chr.len) - - if(real_rank in civilian_positions) - civ[++civ.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - if(depthead && civ.len != 1) - civ.Swap(1, civ.len) - - if(real_rank in nonhuman_positions) - bot[++bot.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - - if(!department && !(name in heads)) - misc[++misc.len] = list("name" = name, "rank" = rank, "active" = isactive) - - - PDA_Manifest = list( - "heads" = heads, - "sec" = sec, - "eng" = eng, - "med" = med, - "sci" = sci, - "chr" = chr, - "civ" = civ, - "bot" = bot, - "misc" = misc - ) - ManifestJSON = json_encode(PDA_Manifest) - return diff --git a/code/datums/setup_option/backgrounds/fate.dm b/code/datums/setup_option/backgrounds/fate.dm index 8093c95bbc3..3cda4c9d8e7 100644 --- a/code/datums/setup_option/backgrounds/fate.dm +++ b/code/datums/setup_option/backgrounds/fate.dm @@ -26,7 +26,7 @@ You were always on the move, looking for a brighter future on the other side. \ And because of that you never specialised as much as you should, but have broader array of other skills." - perks = list(PERK_FREELACER) + perks = list(PERK_FREELANCER) /datum/category_item/setup_option/background/fate/nihilist name = "Nihilist" diff --git a/code/datums/topic/admin.dm b/code/datums/topic/admin.dm index 8200320a178..f3af9e40f72 100644 --- a/code/datums/topic/admin.dm +++ b/code/datums/topic/admin.dm @@ -387,8 +387,8 @@ body += source.formatJobGroup(M, "Church Positions", "ecd37d", "churchdept", church_positions) //Civilian (Grey) body += source.formatJobGroup(M, "Civilian Positions", "dddddd", "civiliandept", civilian_positions) - //Non-Human (Green) - body += source.formatJobGroup(M, "Non-human Positions", "ccffcc", "nonhumandept", nonhuman_positions + "Antag HUD") + //Silicon (Green) + body += source.formatJobGroup(M, "Non-human Positions", "ccffcc", "silicondept", silicon_positions + "Antag HUD") //Antagonist (Orange) var/jobban_list = list() @@ -459,9 +459,9 @@ var/datum/job/temp = SSjob.GetJob(jobPos) if(!temp) continue joblist += temp.title - if("nonhumandept") + if("silicondept") joblist += "pAI" - for(var/jobPos in nonhuman_positions) + for(var/jobPos in silicon_positions) if(!jobPos) continue var/datum/job/temp = SSjob.GetJob(jobPos) if(!temp) continue diff --git a/code/datums/topic/world.dm b/code/datums/topic/world.dm index f596fd2b7b5..0d9b2df53c0 100644 --- a/code/datums/topic/world.dm +++ b/code/datums/topic/world.dm @@ -94,31 +94,16 @@ /datum/world_topic/manifest/Run(list/input) var/list/positions = list() - var/list/set_names = list( - "heads" = command_positions, - "sec" = security_positions, - "eng" = engineering_positions, - "med" = medical_positions, - "sci" = science_positions, - "car" = cargo_positions, - "civ" = civilian_positions, - "chr" = church_positions, - "bot" = nonhuman_positions - ) - - for(var/datum/data/record/t in data_core.general) - var/name = t.fields["name"] - var/rank = t.fields["rank"] - var/real_rank = make_list_rank(t.fields["real_rank"]) - - var/department = FALSE - for(var/k in set_names) - if(real_rank in set_names[k]) - if(!positions[k]) - positions[k] = list() - positions[k][name] = rank - department = TRUE - if(!department) + + for(var/datum/computer_file/report/crew_record/t in GLOB.all_crew_records) + var/name = t.get_name() + var/rank = t.get_job() + + var/department = t.get_department() + + if(department && department != "Unset") + positions[department][name] = rank + else if(!positions["misc"]) positions["misc"] = list() positions["misc"][name] = rank diff --git a/code/datums/uplink/announcements.dm b/code/datums/uplink/announcements.dm index ae51fc9b055..694d0563cb3 100644 --- a/code/datums/uplink/announcements.dm +++ b/code/datums/uplink/announcements.dm @@ -38,48 +38,42 @@ return 0 var/obj/item/card/id/I = user.GetIdCard() - var/datum/data/record/random_general_record - var/datum/data/record/random_medical_record - if(data_core.general.len) - random_general_record = pick(data_core.general) - random_medical_record = find_medical_record("id", random_general_record.fields["id"]) + var/datum/computer_file/report/crew_record/random_record + if(GLOB.all_crew_records.len) + random_record = pick(GLOB.all_crew_records) - var/datum/data/record/general = data_core.CreateGeneralRecord(user) + var/datum/computer_file/report/crew_record/general = new() if(I) - general.fields["age"] = I.age - general.fields["rank"] = I.assignment - general.fields["real_rank"] = I.assignment - general.fields["name"] = I.registered_name - general.fields["sex"] = I.sex + general.set_age(I.age) + general.set_job(I.assignment) + general.set_name(I.registered_name) + general.set_sex(I.sex) else var/mob/living/carbon/human/H if(ishuman(user)) H = user - general.fields["age"] = H.age + general.set_age(H.age) else - general.fields["age"] = initial(H.age) + general.set_age(initial(H.age)) var/assignment = GetAssignment(user) - general.fields["rank"] = assignment - general.fields["real_rank"] = assignment - general.fields["name"] = user.real_name - general.fields["sex"] = capitalize(user.gender) + general.set_job(assignment) + general.set_department(args["department"]) + general.set_name(user.real_name) + general.set_sex(capitalize(user.gender)) - general.fields["species"] = user.get_species() - var/datum/data/record/medical = data_core.CreateMedicalRecord(general.fields["name"], general.fields["id"]) - data_core.CreateSecurityRecord(general.fields["name"], general.fields["id"]) + general.set_species(user.get_species()) - if(!random_general_record) - general.fields["fingerprint"] = random_general_record.fields["fingerprint"] - if(random_medical_record) - medical.fields["b_type"] = random_medical_record.fields["b_type"] - medical.fields["b_dna"] = random_medical_record.fields["b_type"] + if(random_record) + general.set_fingerprint(random_record.get_fingerprint()) + general.set_bloodtype(random_record.get_bloodtype()) + general.set_dna(random_record.get_bloodtype()) if(I) - general.fields["fingerprint"] = I.fingerprint_hash - medical.fields["b_type"] = I.blood_type - medical.fields["b_dna"] = I.dna_hash + general.set_fingerprint(I.fingerprint_hash) + general.set_bloodtype(I.blood_type) + general.set_dna(I.dna_hash) - AnnounceArrival(general.fields["name"], general.fields["rank"], "has completed cryogenic revival") + AnnounceArrival(general.get_name(), general.get_job(), "has completed cryogenic revival") return 1 /datum/uplink_item/abstract/announcements/fake_ion_storm diff --git a/code/defines/procs/announce.dm b/code/defines/procs/announce.dm index 414f37d219f..8c30760d562 100644 --- a/code/defines/procs/announce.dm +++ b/code/defines/procs/announce.dm @@ -119,4 +119,7 @@ datum/announcement/proc/Log(message as text, message_title as text) if(issilicon(character)) global_announcer.autosay("A new [rank] [join_message].", ANNOUNCER_NAME, use_text_to_speech = TRUE) else - global_announcer.autosay("[character.real_name], [rank], [join_message].", ANNOUNCER_NAME, use_text_to_speech = TRUE) + if(istype(character)) + global_announcer.autosay("[character.real_name], [rank], [join_message].", ANNOUNCER_NAME, use_text_to_speech = TRUE) + else if(istext(character)) + global_announcer.autosay("[character], [rank], [join_message].", ANNOUNCER_NAME, use_text_to_speech = TRUE) // send the text diff --git a/code/game/jobs/jobs.dm b/code/game/jobs/jobs.dm index 4fe53b73120..89df2e1fc66 100644 --- a/code/game/jobs/jobs.dm +++ b/code/game/jobs/jobs.dm @@ -78,7 +78,7 @@ var/list/science_positions = list(JOBS_SCIENCE) var/list/moebius_positions = list(JOBS_MOEBIUS) //BS12 EDIT -var/list/cargo_positions = list(JOBS_CARGO) +var/list/guild_positions = list(JOBS_GUILD) var/list/church_positions = list(JOBS_CHURCH) @@ -90,8 +90,8 @@ var/list/civilian_positions = list(JOBS_CIVILIAN) var/list/security_positions = list(JOBS_SECURITY) var/list/armory_positions = list(JOBS_ARMORY) -var/list/nonhuman_positions = list(JOBS_NONHUMAN) +var/list/silicon_positions = list(JOBS_SILICON) /proc/guest_jobbans(var/job) - return ((job in command_positions) || (job in nonhuman_positions) || (job in armory_positions)) + return ((job in command_positions) || (job in silicon_positions) || (job in armory_positions)) diff --git a/code/game/machinery/computer/medical.dm b/code/game/machinery/computer/medical.dm deleted file mode 100644 index c657a915e49..00000000000 --- a/code/game/machinery/computer/medical.dm +++ /dev/null @@ -1,519 +0,0 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 - -/obj/machinery/computer/med_data//TODO:SANITY - name = "medical records console" - desc = "Used to view, edit and maintain medical records." - icon_keyboard = "med_key" - icon_screen = "medcomp" - light_color = COLOR_LIGHTING_GREEN_MACHINERY - req_one_access = list(access_moebius, access_forensics_lockers) - circuit = /obj/item/electronics/circuitboard/med_data - var/obj/item/card/id/scan - var/authenticated - var/rank - var/screen - var/datum/data/record/active1 - var/datum/data/record/active2 - var/a_id - var/temp - var/printing - -/obj/machinery/computer/med_data/verb/eject_id() - set category = "Object" - set name = "Eject ID Card" - set src in oview(1) - - if(!usr || usr.stat || usr.lying) return - - if(scan) - to_chat(usr, "You remove \the [scan] from \the [src].") - scan.loc = get_turf(src) - if(!usr.get_active_hand() && ishuman(usr)) - usr.put_in_hands(scan) - scan = null - else - to_chat(usr, "There is nothing to remove from the console.") - return - -/obj/machinery/computer/med_data/attackby(var/obj/item/O, var/mob/user) - if(istype(O, /obj/item/card/id) && !scan && user.unEquip(O)) - O.loc = src - scan = O - to_chat(user, "You insert \the [O].") - else - ..() - -/obj/machinery/computer/med_data/attack_hand(mob/user as mob) - if(..()) - return - nano_ui_interact(user) - -/obj/machinery/computer/med_data/nano_ui_interact(mob/user) - var/dat - if (src.temp) - dat = text("[src.temp]

Clear Screen") - else - dat = text("Confirm Identity: []
", src, (src.scan ? text("[]", src.scan.name) : "----------")) - if (src.authenticated) - switch(src.screen) - if(1) - dat += {" -Search Records -
List Records -
Medbot Tracking -
-
Record Maintenance -
{Log Out}
-"} - if(2) - dat += "Record List:
" - if(!isnull(data_core.general)) - for(var/datum/data/record/R in sortRecord(data_core.general)) - dat += text("[]: []
", src, R, R.fields["id"], R.fields["name"]) - //Foreach goto(132) - dat += text("
Back", src) - if(3) - dat += text("Records Maintenance
\nBackup To Disk
\nUpload From disk
\nDelete All Records
\n
\nBack", src, src, src, src) - if(4) - var/icon/front = active1.fields["photo_front"] - var/icon/side = active1.fields["photo_side"] - user << browse_rsc(front, "front.png") - user << browse_rsc(side, "side.png") - dat += "
Medical Record

" - if ((istype(src.active1, /datum/data/record) && data_core.general.Find(src.active1))) - dat += "
Name: [active1.fields["name"]] \ - ID: [active1.fields["id"]]
\n \ - Sex: [active1.fields["sex"]]
\n \ - Age: [active1.fields["age"]]
\n \ - Fingerprint: [active1.fields["fingerprint"]]
\n \ - Physical Status: [active1.fields["p_stat"]]
\n \ - Mental Status: [active1.fields["m_stat"]]
\ - Photo:
" - else - dat += "General Record Lost!
" - if ((istype(src.active2, /datum/data/record) && data_core.medical.Find(src.active2))) - dat += text("
\n
Medical Data

\nBlood Type: []
\nDNA: []
\n
\nMinor Disabilities: []
\nDetails: []
\n
\nMajor Disabilities: []
\nDetails: []
\n
\nAllergies: []
\nDetails: []
\n
\nCurrent Diseases: [] (per disease info placed in log/comment section)
\nDetails: []
\n
\nImportant Notes:
\n\t[]
\n
\n
Comments/Log

", src, src.active2.fields["b_type"], src, src.active2.fields["b_dna"], src, src.active2.fields["mi_dis"], src, src.active2.fields["mi_dis_d"], src, src.active2.fields["ma_dis"], src, src.active2.fields["ma_dis_d"], src, src.active2.fields["alg"], src, src.active2.fields["alg_d"], src, src.active2.fields["cdi"], src, src.active2.fields["cdi_d"], src, decode(src.active2.fields["notes"])) - var/counter = 1 - while(src.active2.fields[text("com_[]", counter)]) - dat += text("[]
Delete Entry

", src.active2.fields[text("com_[]", counter)], src, counter) - counter++ - dat += text("Add Entry

", src) - dat += text("Delete Record (Medical Only)

", src) - else - dat += "Medical Record Lost!
" - dat += text("New Record

") - dat += text("\nPrint Record
\nBack
", src, src) - if(6) - dat += "
Medical Robot Monitor
" - dat += "Back" - dat += "
Medical Robots:" - var/bdat - for(var/mob/living/bot/medbot/M in world) - - if(M.z != src.z) continue //only find medibots on the same z-level as the computer - var/turf/bl = get_turf(M) - if(bl) //if it can't find a turf for the medibot, then it probably shouldn't be showing up - bdat += "[M.name] - \[[bl.x],[bl.y]\] - [M.on ? "Online" : "Offline"]
" - if((!isnull(M.reagent_glass)) && M.use_beaker) - bdat += "Reservoir: \[[M.reagent_glass.reagents.total_volume]/[M.reagent_glass.reagents.maximum_volume]\]
" - else - bdat += "Using Internal Synthesizer.
" - if(!bdat) - dat += "
None detected
" - else - dat += "
[bdat]" - - else - else - dat += text("{Log In}", src) - user << browse(text("Medical Records[]", dat), "window=med_rec") - onclose(user, "med_rec") - return - -/obj/machinery/computer/med_data/Topic(href, href_list) - if(..()) - return 1 - - if (!( data_core.general.Find(src.active1) )) - src.active1 = null - - if (!( data_core.medical.Find(src.active2) )) - src.active2 = null - - if ((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (issilicon(usr))) - usr.set_machine(src) - - if (href_list["temp"]) - src.temp = null - - if (href_list["scan"]) - if (src.scan) - - if(ishuman(usr)) - scan.loc = usr.loc - - if(!usr.get_active_hand()) - usr.put_in_hands(scan) - - scan = null - - else - src.scan.loc = src.loc - src.scan = null - - else - var/obj/item/I = usr.get_active_hand() - if (istype(I, /obj/item/card/id)) - usr.drop_item() - I.loc = src - src.scan = I - - else if (href_list["logout"]) - src.authenticated = null - src.screen = null - src.active1 = null - src.active2 = null - - else if (href_list["login"]) - - if (isAI(usr)) - src.active1 = null - src.active2 = null - src.authenticated = usr.name - src.rank = "AI" - src.screen = 1 - - else if (isrobot(usr)) - src.active1 = null - src.active2 = null - src.authenticated = usr.name - var/mob/living/silicon/robot/R = usr - src.rank = "[R.modtype] [R.braintype]" - src.screen = 1 - - else if (istype(src.scan, /obj/item/card/id)) - src.active1 = null - src.active2 = null - - if (src.check_access(src.scan)) - src.authenticated = src.scan.registered_name - src.rank = src.scan.assignment - src.screen = 1 - - if (src.authenticated) - - if(href_list["screen"]) - src.screen = text2num(href_list["screen"]) - if(src.screen < 1) - src.screen = 1 - - src.active1 = null - src.active2 = null - - if (href_list["del_all"]) - src.temp = text("Are you sure you wish to delete all records?
\n\tYes
\n\tNo
", src, src) - - if (href_list["del_all2"]) - for(var/datum/data/record/R in data_core.medical) - //R = null - qdel(R) - //Foreach goto(494) - src.temp = "All records deleted." - - if (href_list["field"]) - var/a1 = src.active1 - var/a2 = src.active2 - switch(href_list["field"]) - if("fingerprint") - if (istype(src.active1, /datum/data/record)) - var/t1 = sanitize(input("Please input fingerprint hash:", "Med. records", src.active1.fields["fingerprint"], null) as text) - if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!issilicon(usr))) || src.active1 != a1)) - return - src.active1.fields["fingerprint"] = t1 - if("sex") - if (istype(src.active1, /datum/data/record)) - if (src.active1.fields["sex"] == "Male") - src.active1.fields["sex"] = "Female" - else - src.active1.fields["sex"] = "Male" - if("age") - if (istype(src.active1, /datum/data/record)) - var/t1 = input("Please input age:", "Med. records", src.active1.fields["age"], null) as num - if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!issilicon(usr))) || src.active1 != a1)) - return - src.active1.fields["age"] = t1 - if("mi_dis") - if (istype(src.active2, /datum/data/record)) - var/t1 = sanitize(input("Please input minor disabilities list:", "Med. records", src.active2.fields["mi_dis"], null) as text) - if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!issilicon(usr))) || src.active2 != a2)) - return - src.active2.fields["mi_dis"] = t1 - if("mi_dis_d") - if (istype(src.active2, /datum/data/record)) - var/t1 = sanitize(input("Please summarize minor dis.:", "Med. records", src.active2.fields["mi_dis_d"], null) as message) - if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!issilicon(usr))) || src.active2 != a2)) - return - src.active2.fields["mi_dis_d"] = t1 - if("ma_dis") - if (istype(src.active2, /datum/data/record)) - var/t1 = sanitize(input("Please input major diabilities list:", "Med. records", src.active2.fields["ma_dis"], null) as text) - if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!issilicon(usr))) || src.active2 != a2)) - return - src.active2.fields["ma_dis"] = t1 - if("ma_dis_d") - if (istype(src.active2, /datum/data/record)) - var/t1 = sanitize(input("Please summarize major dis.:", "Med. records", src.active2.fields["ma_dis_d"], null) as message) - if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!issilicon(usr))) || src.active2 != a2)) - return - src.active2.fields["ma_dis_d"] = t1 - if("alg") - if (istype(src.active2, /datum/data/record)) - var/t1 = sanitize(input("Please state allergies:", "Med. records", src.active2.fields["alg"], null) as text) - if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!issilicon(usr))) || src.active2 != a2)) - return - src.active2.fields["alg"] = t1 - if("alg_d") - if (istype(src.active2, /datum/data/record)) - var/t1 = sanitize(input("Please summarize allergies:", "Med. records", src.active2.fields["alg_d"], null) as message) - if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!issilicon(usr))) || src.active2 != a2)) - return - src.active2.fields["alg_d"] = t1 - if("cdi") - if (istype(src.active2, /datum/data/record)) - var/t1 = sanitize(input("Please state diseases:", "Med. records", src.active2.fields["cdi"], null) as text) - if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!issilicon(usr))) || src.active2 != a2)) - return - src.active2.fields["cdi"] = t1 - if("cdi_d") - if (istype(src.active2, /datum/data/record)) - var/t1 = sanitize(input("Please summarize diseases:", "Med. records", src.active2.fields["cdi_d"], null) as message) - if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!issilicon(usr))) || src.active2 != a2)) - return - src.active2.fields["cdi_d"] = t1 - if("notes") - if (istype(src.active2, /datum/data/record)) - var/t1 = sanitize(input("Please summarize notes:", "Med. records", html_decode(src.active2.fields["notes"]), null) as message, extra = 0) - if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!issilicon(usr))) || src.active2 != a2)) - return - src.active2.fields["notes"] = t1 - if("p_stat") - if (istype(src.active1, /datum/data/record)) - src.temp = text("Physical Condition:
\n\t*Deceased*
\n\t*SSD*
\n\tActive
\n\tPhysically Unfit
\n\tDisabled
", src, src, src, src, src) - if("m_stat") - if (istype(src.active1, /datum/data/record)) - src.temp = text("Mental Condition:
\n\t*Insane*
\n\t*Unstable*
\n\t*Watch*
\n\tStable
", src, src, src, src) - if("b_type") - if (istype(src.active2, /datum/data/record)) - src.temp = text("Blood Type:
\n\tA- A+
\n\tB- B+
\n\tAB- AB+
\n\tO- O+
", src, src, src, src, src, src, src, src) - if("b_dna") - if (istype(src.active2, /datum/data/record)) - var/t1 = sanitize(input("Please input DNA hash:", "Med. records", src.active2.fields["b_dna"], null) as text) - if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!issilicon(usr))) || src.active2 != a2)) - return - src.active2.fields["b_dna"] = t1 - else - - if (href_list["p_stat"]) - if (src.active1) - switch(href_list["p_stat"]) - if("deceased") - src.active1.fields["p_stat"] = "*Deceased*" - if("ssd") - src.active1.fields["p_stat"] = "*SSD*" - if("active") - src.active1.fields["p_stat"] = "Active" - if("unfit") - src.active1.fields["p_stat"] = "Physically Unfit" - if("disabled") - src.active1.fields["p_stat"] = "Disabled" - if(PDA_Manifest.len) - PDA_Manifest.Cut() - - if (href_list["m_stat"]) - if (src.active1) - switch(href_list["m_stat"]) - if("insane") - src.active1.fields["m_stat"] = "*Insane*" - if("unstable") - src.active1.fields["m_stat"] = "*Unstable*" - if("watch") - src.active1.fields["m_stat"] = "*Watch*" - if("stable") - src.active1.fields["m_stat"] = "Stable" - - - if (href_list["b_type"]) - if (src.active2) - switch(href_list["b_type"]) - if("an") - src.active2.fields["b_type"] = "A-" - if("bn") - src.active2.fields["b_type"] = "B-" - if("abn") - src.active2.fields["b_type"] = "AB-" - if("on") - src.active2.fields["b_type"] = "O-" - if("ap") - src.active2.fields["b_type"] = "A+" - if("bp") - src.active2.fields["b_type"] = "B+" - if("abp") - src.active2.fields["b_type"] = "AB+" - if("op") - src.active2.fields["b_type"] = "O+" - - - if (href_list["del_r"]) - if (src.active2) - src.temp = text("Are you sure you wish to delete the record (Medical Portion Only)?
\n\tYes
\n\tNo
", src, src) - - if (href_list["del_r2"]) - if (src.active2) - //src.active2 = null - qdel(src.active2) - - if (href_list["d_rec"]) - var/datum/data/record/R = locate(href_list["d_rec"]) - var/datum/data/record/M = locate(href_list["d_rec"]) - if (!( data_core.general.Find(R) )) - src.temp = "Record Not Found!" - return - for(var/datum/data/record/E in data_core.medical) - if ((E.fields["name"] == R.fields["name"] || E.fields["id"] == R.fields["id"])) - M = E - else - //Foreach continue //goto(2540) - src.active1 = R - src.active2 = M - src.screen = 4 - - if (href_list["new"]) - if ((istype(src.active1, /datum/data/record) && !( istype(src.active2, /datum/data/record) ))) - var/datum/data/record/R = new /datum/data/record( ) - R.fields["name"] = src.active1.fields["name"] - R.fields["id"] = src.active1.fields["id"] - R.name = text("Medical Record #[]", R.fields["id"]) - R.fields["b_type"] = "Unknown" - R.fields["b_dna"] = "Unknown" - R.fields["mi_dis"] = "None" - R.fields["mi_dis_d"] = "No minor disabilities have been declared." - R.fields["ma_dis"] = "None" - R.fields["ma_dis_d"] = "No major disabilities have been diagnosed." - R.fields["alg"] = "None" - R.fields["alg_d"] = "No allergies have been detected in this patient." - R.fields["cdi"] = "None" - R.fields["cdi_d"] = "No diseases have been diagnosed at the moment." - R.fields["notes"] = "No notes." - data_core.medical += R - src.active2 = R - src.screen = 4 - - if (href_list["add_c"]) - if (!( istype(src.active2, /datum/data/record) )) - return - var/a2 = src.active2 - var/t1 = sanitize(input("Add Comment:", "Med. records", null, null) as message) - if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!issilicon(usr))) || src.active2 != a2)) - return - var/counter = 1 - while(src.active2.fields[text("com_[]", counter)]) - counter++ - src.active2.fields[text("com_[counter]")] = text("Made by [authenticated] ([rank]) on [time2text(world.realtime, "DDD MMM DD")] [stationtime2text()], [game_year]
[t1]") - - if (href_list["del_c"]) - if ((istype(src.active2, /datum/data/record) && src.active2.fields[text("com_[]", href_list["del_c"])])) - src.active2.fields[text("com_[]", href_list["del_c"])] = "Deleted" - - if (href_list["search"]) - var/t1 = input("Search String: (Name, DNA, or ID)", "Med. records", null, null) as text - if ((!( t1 ) || usr.stat || !( src.authenticated ) || usr.restrained() || ((!in_range(src, usr)) && (!issilicon(usr))))) - return - src.active1 = null - src.active2 = null - t1 = lowertext(t1) - for(var/datum/data/record/R in data_core.medical) - if ((lowertext(R.fields["name"]) == t1 || t1 == lowertext(R.fields["id"]) || t1 == lowertext(R.fields["b_dna"]))) - src.active2 = R - else - //Foreach continue //goto(3229) - if (!( src.active2 )) - src.temp = text("Could not locate record [].", t1) - else - for(var/datum/data/record/E in data_core.general) - if ((E.fields["name"] == src.active2.fields["name"] || E.fields["id"] == src.active2.fields["id"])) - src.active1 = E - else - //Foreach continue //goto(3334) - src.screen = 4 - - if (href_list["print_p"]) - if (!( src.printing )) - src.printing = 1 - var/datum/data/record/record1 - var/datum/data/record/record2 - if ((istype(src.active1, /datum/data/record) && data_core.general.Find(src.active1))) - record1 = active1 - if ((istype(src.active2, /datum/data/record) && data_core.medical.Find(src.active2))) - record2 = active2 - sleep(50) - var/obj/item/paper/P = new /obj/item/paper( src.loc ) - P.info = "
Medical Record

" - if (record1) - P.info += text("Name: [] ID: []
\nSex: []
\nAge: []
\nFingerprint: []
\nPhysical Status: []
\nMental Status: []
", record1.fields["name"], record1.fields["id"], record1.fields["sex"], record1.fields["age"], record1.fields["fingerprint"], record1.fields["p_stat"], record1.fields["m_stat"]) - P.name = text("Medical Record ([])", record1.fields["name"]) - else - P.info += "General Record Lost!
" - P.name = "Medical Record" - if (record2) - P.info += text("
\n
Medical Data

\nBlood Type: []
\nDNA: []
\n
\nMinor Disabilities: []
\nDetails: []
\n
\nMajor Disabilities: []
\nDetails: []
\n
\nAllergies: []
\nDetails: []
\n
\nCurrent Diseases: [] (per disease info placed in log/comment section)
\nDetails: []
\n
\nImportant Notes:
\n\t[]
\n
\n
Comments/Log

", record2.fields["b_type"], record2.fields["b_dna"], record2.fields["mi_dis"], record2.fields["mi_dis_d"], record2.fields["ma_dis"], record2.fields["ma_dis_d"], record2.fields["alg"], record2.fields["alg_d"], record2.fields["cdi"], record2.fields["cdi_d"], decode(record2.fields["notes"])) - var/counter = 1 - while(record2.fields[text("com_[]", counter)]) - P.info += text("[]
", record2.fields[text("com_[]", counter)]) - counter++ - else - P.info += "Medical Record Lost!
" - P.info += "" - src.printing = null - - src.add_fingerprint(usr) - src.updateUsrDialog() - return - -/obj/machinery/computer/med_data/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - - for(var/datum/data/record/R in data_core.medical) - if(prob(10/severity)) - switch(rand(1,6)) - if(1) - R.fields["name"] = "[pick(pick(GLOB.first_names_male), pick(GLOB.first_names_female))] [pick(GLOB.last_names)]" - if(2) - R.fields["sex"] = pick("Male", "Female") - if(3) - R.fields["age"] = rand(5, 85) - if(4) - R.fields["b_type"] = pick("A-", "B-", "AB-", "O-", "A+", "B+", "AB+", "O+") - if(5) - R.fields["p_stat"] = pick("*SSD*", "Active", "Physically Unfit", "Disabled") - if(PDA_Manifest.len) - PDA_Manifest.Cut() - if(6) - R.fields["m_stat"] = pick("*Insane*", "*Unstable*", "*Watch*", "Stable") - continue - - else if(prob(1)) - qdel(R) - continue - - ..(severity) - - -/obj/machinery/computer/med_data/laptop - name = "Medical Laptop" - desc = "A cheap laptop." - icon_state = "laptop" - icon_keyboard = "laptop_key" - icon_screen = "medlaptop" - CheckFaceFlag = 0 diff --git a/code/game/machinery/computer/security.dm b/code/game/machinery/computer/security.dm deleted file mode 100644 index 62cd2c4e956..00000000000 --- a/code/game/machinery/computer/security.dm +++ /dev/null @@ -1,658 +0,0 @@ -/obj/machinery/computer/secure_data - name = "security records console" - desc = "Used to view, edit and maintain security records" - icon_keyboard = "security_key" - icon_screen = "security" - light_color = COLOR_LIGHTING_SCI_BRIGHT - req_one_access = list(access_security) - circuit = /obj/item/electronics/circuitboard/secure_data - var/obj/item/card/id/scan - var/authenticated - var/rank - var/screen - var/datum/data/record/active1 - var/datum/data/record/active2 - var/a_id - var/temp - var/printing - var/can_change_id = 0 - var/list/Perp - var/tempname - //Sorting Variables - var/sortBy = "name" - var/order = 1 // -1 = Descending - 1 = Ascending - -/obj/machinery/computer/secure_data/verb/eject_id() - set category = "Object" - set name = "Eject ID Card" - set src in oview(1) - - if(!usr || usr.stat || usr.lying) return - - if(scan) - to_chat(usr, "You remove \the [scan] from \the [src].") - scan.loc = get_turf(src) - if(!usr.get_active_hand() && ishuman(usr)) - usr.put_in_hands(scan) - scan = null - else - to_chat(usr, "There is nothing to remove from the console.") - return - -/obj/machinery/computer/secure_data/attackby(obj/item/O as obj, user as mob) - if(istype(O, /obj/item/card/id) && !scan) - usr.drop_item() - O.loc = src - scan = O - to_chat(user, "You insert [O].") - ..() - -//Someone needs to break down the dat += into chunks instead of long ass lines. -/obj/machinery/computer/secure_data/attack_hand(mob/user) - if(..()) - return - nano_ui_interact(user) - -/obj/machinery/computer/secure_data/nano_ui_interact(user) - if (src.z > 6) - to_chat(user, "Unable to establish a connection: You're too far away from the station!") - return - - var/dat - if (temp) - dat = text("[]

Clear Screen", temp, src) - else - dat = text("Confirm Identity: []
", src, (scan ? text("[]", scan.name) : "----------")) - if (authenticated) - switch(screen) - if(1) - dat += {"

"} - dat += text("Search Records
", src) - dat += text("New Record
", src) - dat += {" -

- - - - -
Records:
- - - - - - - -"} - if(!isnull(data_core.general)) - for(var/datum/data/record/R in sortRecord(data_core.general, sortBy, order)) - var/crimstat = "" - for(var/datum/data/record/E in data_core.security) - if ((E.fields["name"] == R.fields["name"] && E.fields["id"] == R.fields["id"])) - crimstat = E.fields["criminal"] - var/background - switch(crimstat) - if("*Arrest*") - background = "'background-color:#DC143C;'" - if("Incarcerated") - background = "'background-color:#CD853F;'" - if("Parolled") - background = "'background-color:#CD853F;'" - if("Released") - background = "'background-color:#3BB9FF;'" - if("None") - background = "'background-color:#00FF7F;'" - if("") - background = "'background-color:#FFFFFF;'" - crimstat = "No Record." - dat += text("", background, src, R, R.fields["name"]) - dat += text("", R.fields["id"]) - dat += text("", R.fields["rank"]) - dat += text("", R.fields["fingerprint"]) - dat += text("", crimstat) - dat += "
NameIDRankFingerprintsCriminal Status
[][][][][]

" - dat += text("Record Maintenance

", src) - dat += text("{Log Out}",src) - if(2) - dat += "Records Maintenance
" - dat += "
Delete All Records

Back" - if(3) - dat += "
Security Record

" - if ((istype(active1, /datum/data/record) && data_core.general.Find(active1))) - user << browse_rsc(active1.fields["photo_front"], "front.png") - user << browse_rsc(active1.fields["photo_side"], "side.png") - dat += {" - -
- Name: [active1.fields["name"]]
- ID: [active1.fields["id"]]
- Sex: [active1.fields["sex"]]
- Age: [active1.fields["age"]]
- Rank: [active1.fields["rank"]]
- Fingerprint: [active1.fields["fingerprint"]]
- Physical Status: [active1.fields["p_stat"]]
- Mental Status: [active1.fields["m_stat"]]
Photo:
- -

- Update front photo

- Update side photo
-
- "} - else - dat += "General Record Lost!
" - if ((istype(active2, /datum/data/record) && data_core.security.Find(active2))) - dat += "
\n
Security Data

\nCriminal Status: \ - [active2.fields["criminal"]]
\n
\n \ - Minor Crimes: [active2.fields["mi_crim"]]
\n \ - Details: [active2.fields["mi_crim_d"]]
\n
\n\ - Major Crimes: [active2.fields["ma_crim"]]
\n \ - Details: [active2.fields["ma_crim_d"]]
\n
\n \ - Important Notes:
\n\t[decode(active2.fields["notes"])]
\n
\n\ -
Comments/Log

" - var/counter = 1 - while(active2.fields["com_[counter]"]) - dat += text("[]
Delete Entry

", active2.fields["com_[counter]"], src, counter) - counter++ - dat += "Add Entry

" - dat += "Delete Record (Security Only)

" - else - dat += "Security Record Lost!
" - dat += "New Security Record

" - dat += "Delete Record (ALL)

\ - Print Record
\ - Print Wanted Poster
\ - Back
" - if(4) - if(!Perp.len) - dat += text("ERROR. String could not be located.

Back", src) - else - dat += {" - -
Search Results for '[tempname]':
- - - - - - - - - "} - for(var/i=1, i<=Perp.len, i += 2) - var/crimstat = "" - var/datum/data/record/R = Perp[i] - if(istype(Perp[i+1],/datum/data/record/)) - var/datum/data/record/E = Perp[i+1] - crimstat = E.fields["criminal"] - var/background - switch(crimstat) - if("*Arrest*") - background = "'background-color:#DC143C;'" - if("Incarcerated") - background = "'background-color:#CD853F;'" - if("Parolled") - background = "'background-color:#CD853F;'" - if("Released") - background = "'background-color:#3BB9FF;'" - if("None") - background = "'background-color:#00FF7F;'" - if("") - background = "'background-color:#FFFFFF;'" - crimstat = "No Record." - dat += text("", background, src, R, R.fields["name"]) - dat += text("", R.fields["id"]) - dat += text("", R.fields["rank"]) - dat += text("", R.fields["fingerprint"]) - dat += text("", crimstat) - dat += "
NameIDRankFingerprintsCriminal Status
[][][][][]

" - dat += text("
Return to index.", src) - else - else - dat += text("{Log In}", src) - user << browse(text("Security Records[]", dat), "window=secure_rec;size=600x400") - onclose(user, "secure_rec") - return - -/*Revised /N -I can't be bothered to look more of the actual code outside of switch but that probably needs revising too. -What a mess.*/ -/obj/machinery/computer/secure_data/Topic(href, href_list) - if(..()) - return 1 - if (!( data_core.general.Find(active1) )) - active1 = null - if (!( data_core.security.Find(active2) )) - active2 = null - if ((usr.contents.Find(src) || (in_range(src, usr) && istype(loc, /turf))) || (issilicon(usr))) - usr.set_machine(src) - switch(href_list["choice"]) -// SORTING! - if("Sorting") - // Reverse the order if clicked twice - if(sortBy == href_list["sort"]) - if(order == 1) - order = -1 - else - order = 1 - else - // New sorting order! - sortBy = href_list["sort"] - order = initial(order) -//BASIC FUNCTIONS - if("Clear Screen") - temp = null - - if ("Return") - screen = 1 - active1 = null - active2 = null - - if("Confirm Identity") - if (scan) - if(ishuman(usr) && !usr.get_active_hand()) - usr.put_in_hands(scan) - else - scan.loc = get_turf(src) - scan = null - else - var/obj/item/I = usr.get_active_hand() - if (istype(I, /obj/item/card/id) && usr.unEquip(I)) - I.loc = src - scan = I - - if("Log Out") - authenticated = null - screen = null - active1 = null - active2 = null - - if("Log In") - if (isAI(usr)) - src.active1 = null - src.active2 = null - src.authenticated = usr.name - src.rank = "AI" - src.screen = 1 - else if (isrobot(usr)) - src.active1 = null - src.active2 = null - src.authenticated = usr.name - var/mob/living/silicon/robot/R = usr - src.rank = "[R.modtype] [R.braintype]" - src.screen = 1 - else if (istype(scan, /obj/item/card/id)) - active1 = null - active2 = null - if(check_access(scan)) - authenticated = scan.registered_name - rank = scan.assignment - screen = 1 -//RECORD FUNCTIONS - if("Search Records") - var/t1 = input("Search String: (Partial Name or ID or Fingerprints or Rank)", "Secure. records", null, null) as text - if ((!( t1 ) || usr.stat || !( authenticated ) || usr.restrained() || !in_range(src, usr))) - return - Perp = new/list() - t1 = lowertext(t1) - var/list/components = splittext(t1, " ") - if(components.len > 5) - return //Lets not let them search too greedily. - for(var/datum/data/record/R in data_core.general) - var/temptext = R.fields["name"] + " " + R.fields["id"] + " " + R.fields["fingerprint"] + " " + R.fields["rank"] - for(var/i = 1, i<=components.len, i++) - if(findtext(temptext,components[i])) - var/prelist = new/list(2) - prelist[1] = R - Perp += prelist - for(var/i = 1, i<=Perp.len, i+=2) - for(var/datum/data/record/E in data_core.security) - var/datum/data/record/R = Perp[i] - if ((E.fields["name"] == R.fields["name"] && E.fields["id"] == R.fields["id"])) - Perp[i+1] = E - tempname = t1 - screen = 4 - - if("Record Maintenance") - screen = 2 - active1 = null - active2 = null - - if ("Browse Record") - var/datum/data/record/R = locate(href_list["d_rec"]) - var/S = locate(href_list["d_rec"]) - if (!( data_core.general.Find(R) )) - temp = "Record Not Found!" - else - for(var/datum/data/record/E in data_core.security) - if ((E.fields["name"] == R.fields["name"] || E.fields["id"] == R.fields["id"])) - S = E - active1 = R - active2 = S - screen = 3 - -/* if ("Search Fingerprints") - var/t1 = input("Search String: (Fingerprint)", "Secure. records", null, null) as text - if ((!( t1 ) || usr.stat || !( authenticated ) || usr.restrained() || (!in_range(src, usr)) && (!issilicon(usr)))) - return - active1 = null - active2 = null - t1 = lowertext(t1) - for(var/datum/data/record/R in data_core.general) - if (lowertext(R.fields["fingerprint"]) == t1) - active1 = R - if (!( active1 )) - temp = text("Could not locate record [].", t1) - else - for(var/datum/data/record/E in data_core.security) - if ((E.fields["name"] == active1.fields["name"] || E.fields["id"] == active1.fields["id"])) - active2 = E - screen = 3 */ - - if ("Print Record") - if (!( printing )) - printing = 1 - var/datum/data/record/record1 = null - var/datum/data/record/record2 = null - if ((istype(active1, /datum/data/record) && data_core.general.Find(active1))) - record1 = active1 - if ((istype(active2, /datum/data/record) && data_core.security.Find(active2))) - record2 = active2 - sleep(50) - var/obj/item/paper/P = new /obj/item/paper( loc ) - P.info = "
Security Record

" - if (record1) - P.info += {" - Name: [record1.fields["name"]] ID: [record1.fields["id"]]
- Sex: [record1.fields["sex"]]
- Age: [record1.fields["age"]]
- Fingerprint: [record1.fields["fingerprint"]]
- Physical Status: [record1.fields["p_stat"]]
- Mental Status: [record1.fields["m_stat"]]
- "} - P.name = "Security Record ([record1.fields["name"]])" - else - P.info += "General Record Lost!
" - P.name = "Security Record" - if (record2) - P.info += {" -
Security Data

- Criminal Status: [record2.fields["criminal"]]

- Minor Crimes: [record2.fields["mi_crim"]]
- Details: [record2.fields["mi_crim_d"]]

- Major Crimes: [record2.fields["ma_crim"]]
- Details: [record2.fields["ma_crim_d"]]

- Important Notes:
- \t[decode(record2.fields["notes"])]

-
Comments/Log

- "} - var/counter = 1 - while(record2.fields[text("com_[]", counter)]) - P.info += text("[]
", record2.fields[text("com_[]", counter)]) - counter++ - else - P.info += "Security Record Lost!
" - P.info += "" - printing = null - updateUsrDialog() - - if ("Print Poster") - if(!printing) - var/wanted_name = sanitizeName(input("Please enter an alias for the criminal:", "Print Wanted Poster", active1.fields["name"]) as text, MAX_NAME_LEN, 1) - if(wanted_name) - var/default_description = "A poster declaring [wanted_name] to be a dangerous individual, wanted by Nanotrasen. Report any sightings to security immediately." - var/major_crimes = active2.fields["ma_crim"] - var/minor_crimes = active2.fields["mi_crim"] - default_description += "\n[wanted_name] is wanted for the following crimes:\n" - default_description += "\nMinor Crimes:\n[minor_crimes]\n[active2.fields["mi_crim_d"]]\n" - default_description += "\nMajor Crimes:\n[major_crimes]\n[active2.fields["ma_crim_d"]]\n" - printing = 1 - spawn(30) - playsound(loc, 'sound/items/poster_being_created.ogg', 100, 1) - if((istype(active1, /datum/data/record) && data_core.general.Find(active1)))//make sure the record still exists. - new /obj/item/contraband/poster/wanted(src.loc, active1.fields["photo_front"], wanted_name, default_description) - printing = 0 -//RECORD DELETE - if ("Delete All Records") - temp = "" - temp += "Are you sure you wish to delete all Security records?
" - temp += "Yes
" - temp += "No" - - if ("Purge All Records") - for(var/datum/data/record/R in data_core.security) - qdel(R) - temp = "All Security records deleted." - - if ("Add Entry") - if (!( istype(active2, /datum/data/record) )) - return - var/a2 = active2 - var/t1 = sanitize(input("Add Comment:", "Secure. records", null, null) as message) - if ((!( t1 ) || !( authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!issilicon(usr))) || active2 != a2)) - return - var/counter = 1 - while(active2.fields[text("com_[]", counter)]) - counter++ - active2.fields[text("com_[counter]")] = text("Made by [authenticated] ([rank]) on [time2text(world.realtime, "DDD MMM DD")] [stationtime2text()], [game_year]
[t1]") - - if ("Delete Record (ALL)") - if (active1) - temp = "
Are you sure you wish to delete the record (ALL)?
" - temp += "Yes
" - temp += "No" - - if ("Delete Record (Security)") - if (active2) - temp = "
Are you sure you wish to delete the record (Security Portion Only)?
" - temp += "Yes
" - temp += "No" - - if ("Delete Entry") - if ((istype(active2, /datum/data/record) && active2.fields[text("com_[]", href_list["del_c"])])) - active2.fields[text("com_[]", href_list["del_c"])] = "Deleted" -//RECORD CREATE - if ("New Record (Security)") - if ((istype(active1, /datum/data/record) && !( istype(active2, /datum/data/record) ))) - active2 = data_core.CreateSecurityRecord(active1.fields["name"], active1.fields["id"]) - screen = 3 - - if ("New Record (General)") - active1 = data_core.CreateGeneralRecord() - active2 = null - -//FIELD FUNCTIONS - if ("Edit") - if (is_not_allowed(usr)) - return - var/a1 = active1 - var/a2 = active2 - switch(href_list["field"]) - if("name") - if (istype(active1, /datum/data/record)) - var/t1 = sanitizeName(input("Please input name:", "Secure. records", active1.fields["name"], null) as text) - if (!t1 || active1 != a1) - return - active1.fields["name"] = t1 - if("id") - if (istype(active2, /datum/data/record)) - var/t1 = sanitize(input("Please input id:", "Secure. records", active1.fields["id"], null) as text) - if (!t1 || active1 != a1) - return - active1.fields["id"] = t1 - if("fingerprint") - if (istype(active1, /datum/data/record)) - var/t1 = sanitize(input("Please input fingerprint hash:", "Secure. records", active1.fields["fingerprint"], null) as text) - if (!t1 || active1 != a1) - return - active1.fields["fingerprint"] = t1 - if("sex") - if (istype(active1, /datum/data/record)) - if (active1.fields["sex"] == "Male") - active1.fields["sex"] = "Female" - else - active1.fields["sex"] = "Male" - if("age") - if (istype(active1, /datum/data/record)) - var/t1 = input("Please input age:", "Secure. records", active1.fields["age"], null) as num - if (!t1 || active1 != a1) - return - active1.fields["age"] = t1 - if("mi_crim") - if (istype(active2, /datum/data/record)) - var/t1 = sanitize(input("Please input minor disabilities list:", "Secure. records", active2.fields["mi_crim"], null) as text) - if (!t1 || active2 != a2) - return - active2.fields["mi_crim"] = t1 - if("mi_crim_d") - if (istype(active2, /datum/data/record)) - var/t1 = sanitize(input("Please summarize minor dis.:", "Secure. records", active2.fields["mi_crim_d"], null) as message) - if (!t1 || active2 != a2) - return - active2.fields["mi_crim_d"] = t1 - if("ma_crim") - if (istype(active2, /datum/data/record)) - var/t1 = sanitize(input("Please input major diabilities list:", "Secure. records", active2.fields["ma_crim"], null) as text) - if (!t1 || active2 != a2) - return - active2.fields["ma_crim"] = t1 - if("ma_crim_d") - if (istype(active2, /datum/data/record)) - var/t1 = sanitize(input("Please summarize major dis.:", "Secure. records", active2.fields["ma_crim_d"], null) as message) - if (!t1 || active2 != a2) - return - active2.fields["ma_crim_d"] = t1 - if("notes") - if (istype(active2, /datum/data/record)) - var/t1 = sanitize(input("Please summarize notes:", "Secure. records", html_decode(active2.fields["notes"]), null) as message, extra = 0) - if (!t1 || active2 != a2) - return - active2.fields["notes"] = t1 - if("criminal") - if (istype(active2, /datum/data/record)) - temp = "
Criminal Status:
" - temp += "" - if("rank") - var/list/L = list( "First Officer", "Captain", "AI" ) - //This was so silly before the change. Now it actually works without beating your head against the keyboard. /N - if ((istype(active1, /datum/data/record) && L.Find(rank))) - temp = "
Rank:
" - temp += "
    " - for(var/rank in GLOB.joblist) - temp += "
  • [rank]
  • " - temp += "
" - else - alert(usr, "You do not have the required rank to do this!") - if("species") - if (istype(active1, /datum/data/record)) - var/t1 = sanitize(input("Please enter race:", "General records", active1.fields["species"], null) as message) - if (!t1 || active1 != a1) - return - active1.fields["species"] = t1 - if("photo front") - var/icon/photo = get_photo(usr) - if(photo) - active1.fields["photo_front"] = photo - if("photo side") - var/icon/photo = get_photo(usr) - if(photo) - active1.fields["photo_side"] = photo - - -//TEMPORARY MENU FUNCTIONS - else//To properly clear as per clear screen. - temp=null - switch(href_list["choice"]) - if ("Change Rank") - if (active1) - active1.fields["rank"] = href_list["rank"] - if(href_list["rank"] in GLOB.joblist) - active1.fields["real_rank"] = href_list["real_rank"] - - if ("Change Criminal Status") - if (active2) - for(var/mob/living/carbon/human/H in GLOB.player_list) - BITSET(H.hud_updateflag, WANTED_HUD) - switch(href_list["criminal2"]) - if("none") - active2.fields["criminal"] = "None" - if("arrest") - active2.fields["criminal"] = "*Arrest*" - if("incarcerated") - active2.fields["criminal"] = "Incarcerated" - if("parolled") - active2.fields["criminal"] = "Parolled" - if("released") - active2.fields["criminal"] = "Released" - - if ("Delete Record (Security) Execute") - if (active2) - qdel(active2) - - if ("Delete Record (ALL) Execute") - if (active1) - for(var/datum/data/record/R in data_core.medical) - if ((R.fields["name"] == active1.fields["name"] || R.fields["id"] == active1.fields["id"])) - qdel(R) - else - qdel(active1) - if (active2) - qdel(active2) - else - temp = "This function does not appear to be working at the moment. Our apologies." - - add_fingerprint(usr) - updateUsrDialog() - return - -/obj/machinery/computer/secure_data/proc/is_not_allowed(var/mob/user) - return !src.authenticated || user.stat || user.restrained() || (!in_range(src, user) && (!issilicon(user))) - -/obj/machinery/computer/secure_data/proc/get_photo(var/mob/user) - if(istype(user.get_active_hand(), /obj/item/photo)) - var/obj/item/photo/photo = user.get_active_hand() - return photo.img - if(issilicon(user)) - var/mob/living/silicon/tempAI = usr - var/obj/item/photo/selection = tempAI.GetPicture() - if (selection) - return selection.img - -/obj/machinery/computer/secure_data/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - - for(var/datum/data/record/R in data_core.security) - if(prob(10/severity)) - switch(rand(1,6)) - if(1) - R.fields["name"] = "[pick(pick(GLOB.first_names_male), pick(GLOB.first_names_female))] [pick(GLOB.last_names)]" - if(2) - R.fields["sex"] = pick("Male", "Female") - if(3) - R.fields["age"] = rand(5, 85) - if(4) - R.fields["criminal"] = pick("None", "*Arrest*", "Incarcerated", "Parolled", "Released") - if(5) - R.fields["p_stat"] = pick("*Unconcious*", "Active", "Physically Unfit") - if(PDA_Manifest.len) - PDA_Manifest.Cut() - if(6) - R.fields["m_stat"] = pick("*Insane*", "*Unstable*", "*Watch*", "Stable") - continue - - else if(prob(1)) - qdel(R) - continue - - ..(severity) - -/obj/machinery/computer/secure_data/detective_computer - icon = 'icons/obj/computer.dmi' - icon_state = "messyfiles" diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm index 84ef9938851..cf5b00ad86d 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/hologram.dm @@ -241,9 +241,8 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ var/obj/effect/overlay/hologram = new(T)//Spawn a blank effect at the location. if(caller_id) var/icon/tempicon = new - for(var/datum/data/record/t in data_core.locked) - if(t.fields["name"]==caller_id.name) - tempicon = t.fields["image"] + var/datum/computer_file/report/crew_record/t = get_crewmember_record(caller_id.name) + tempicon = t.photo_front hologram.overlays += getHologramIcon(icon(tempicon)) // Add the callers image as an overlay to keep coloration! else hologram.overlays += A.holo_icon // Add the AI's configured holo Icon diff --git a/code/game/objects/items/devices/uplink.dm b/code/game/objects/items/devices/uplink.dm index aa9da5de9d1..01cd5aaaf64 100644 --- a/code/game/objects/items/devices/uplink.dm +++ b/code/game/objects/items/devices/uplink.dm @@ -234,14 +234,14 @@ A list of items and costs is stored under the datum of every game mode, alongsid nanoui_data["items"] = items else if(nanoui_menu == 2) var/permanentData[0] - for(var/datum/data/record/L in sortRecord(data_core.locked)) - permanentData[++permanentData.len] = list(Name = L.fields["name"],"id" = L.fields["id"]) + for(var/datum/computer_file/report/crew_record/L in GLOB.all_crew_records) + permanentData[++permanentData.len] = list(Name = L.get_name(), "id" = jointext(L.get_name(), L.get_job())) nanoui_data["exploit_records"] = permanentData else if(nanoui_menu == 21) nanoui_data["exploit_exists"] = 0 - for(var/datum/data/record/L in data_core.locked) - if(L.fields["id"] == exploit_id) + for(var/datum/computer_file/report/crew_record/L in GLOB.all_crew_records) + if(jointext(L.get_name(), L.get_job()) == exploit_id) // datacore used name and job to make their IDs, so this would be the same level of specificity as it was in datacore nanoui_data["exploit"] = list() // Setting this to equal L.fields passes it's variables that are lists as reference instead of value. // We trade off being able to automatically add shit for more control over what gets passed to json // and if it's sanitized for html. diff --git a/code/game/objects/items/weapons/circuitboards/computer/computer.dm b/code/game/objects/items/weapons/circuitboards/computer/computer.dm index f5be72a64bc..9717514e5d0 100644 --- a/code/game/objects/items/weapons/circuitboards/computer/computer.dm +++ b/code/game/objects/items/weapons/circuitboards/computer/computer.dm @@ -18,9 +18,6 @@ build_path = /obj/machinery/computer/borgupload origin_tech = list(TECH_DATA = 4) -/obj/item/electronics/circuitboard/med_data - name = T_BOARD("medical records console") - build_path = /obj/machinery/computer/med_data /obj/item/electronics/circuitboard/communications name = T_BOARD("command and communications console") @@ -33,9 +30,6 @@ build_path = /obj/machinery/computer/teleporter origin_tech = list(TECH_DATA = 2, TECH_BLUESPACE = 2) -/obj/item/electronics/circuitboard/secure_data - name = T_BOARD("security records console") - build_path = /obj/machinery/computer/secure_data /obj/item/electronics/circuitboard/atmos_alert rarity_value = 13.3 diff --git a/code/modules/admin/DB ban/functions.dm b/code/modules/admin/DB ban/functions.dm index 9bfa1824162..2cfe8bb1ec2 100644 --- a/code/modules/admin/DB ban/functions.dm +++ b/code/modules/admin/DB ban/functions.dm @@ -341,7 +341,7 @@ datum/admins/proc/DB_ban_unban_by_id(var/id) output += "" for(var/j in get_all_jobs()) output += "" - for(var/j in nonhuman_positions) + for(var/j in silicon_positions) output += "" var/list/bantypes = list("contractor","carrion","operative","revolutionary","cultist","wizard") //For legacy bans. for(var/ban_type in GLOB.antag_bantypes) // Grab other bans. diff --git a/code/modules/admin/secrets/admin_secrets/show_crew_manifest.dm b/code/modules/admin/secrets/admin_secrets/show_crew_manifest.dm index 3cbe5062e71..e2c57b04d1e 100644 --- a/code/modules/admin/secrets/admin_secrets/show_crew_manifest.dm +++ b/code/modules/admin/secrets/admin_secrets/show_crew_manifest.dm @@ -7,6 +7,6 @@ return var/dat dat += "

Crew Manifest

" - dat += data_core.get_manifest() + dat += html_crew_manifest(TRUE) user << browse(dat, "window=manifest;size=370x420;can_close=1") diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index 6231512f112..03737d21e38 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -321,23 +321,8 @@ Contractors and the like can also be revived with the previous role mostly intac var/mob/living/carbon/human/new_character = new()//The mob being spawned. - var/datum/data/record/record_found //Referenced to later to either randomize or not randomize the character. - if(G_found.mind && !G_found.mind.active) //mind isn't currently in use by someone/something - /*Try and locate a record for the person being respawned through data_core. - This isn't an exact science but it does the trick more often than not.*/ - var/id = md5("[G_found.real_name][G_found.mind.assigned_role]") - for(var/datum/data/record/t in data_core.locked) - if(t.fields["id"]==id) - record_found = t//We shall now reference the record. - break - - if(record_found)//If they have a record we can determine a few things. - new_character.real_name = record_found.fields["name"] - new_character.gender = record_found.fields["sex"] - new_character.age = record_found.fields["age"] - new_character.b_type = record_found.fields["b_type"] - new_character.dna_trace = record_found.fields["b_dna"] - new_character.fingers_trace = record_found.fields["fingerprint"] + if(G_found.client?.prefs)//If they have a record we can determine a few things. + G_found.client?.prefs.copy_to(new_character) else new_character.gender = pick(MALE,FEMALE) var/datum/preferences/A = new() @@ -378,12 +363,13 @@ Contractors and the like can also be revived with the previous role mostly intac //Now for special roles and equipment. SSjob.EquipRank(new_character, new_character.mind.assigned_role) + var/datum/computer_file/report/crew_record/record_found = get_crewmember_record(new_character.name) //Announces the character on all the systems, based on the record. if(!issilicon(new_character))//If they are not a cyborg/AI. if(!record_found && !player_is_antag(new_character.mind, only_offstation_roles = 1)) //If there are no records for them. If they have a record, this info is already in there. MODE people are not announced anyway. //Power to the user! - if(alert(new_character,"Warning: No data core entry detected. Would you like to announce the arrival of this character by adding them to various databases, such as medical records?",,"No","Yes")=="Yes") - data_core.manifest_inject(new_character) + if(alert(new_character,"Warning: No record entry detected. Would you like to announce the arrival of this character by adding them to various databases, such as medical records?",,"No","Yes")=="Yes") + CreateModularRecord(new_character) if(alert(new_character,"Would you like an active AI to announce this character?",,"No","Yes")=="Yes") call(/proc/AnnounceArrival)(new_character, new_character.mind.assigned_role) diff --git a/code/modules/asset_cache/assets/legacyuiicons.dm b/code/modules/asset_cache/assets/legacyuiicons.dm new file mode 100644 index 00000000000..5051a76d70a --- /dev/null +++ b/code/modules/asset_cache/assets/legacyuiicons.dm @@ -0,0 +1,5 @@ +/datum/asset/spritesheet/legacyicons + name = "oldicons" + +/datum/asset/spritesheet/legacyicons/create_spritesheets() + InsertAll("", 'icons/ui_icons/dmis/uiicons16.dmi') diff --git a/code/modules/client/preference_setup/background/03_records.dm b/code/modules/client/preference_setup/background/03_records.dm index f65d4407d45..59d34b28a9b 100644 --- a/code/modules/client/preference_setup/background/03_records.dm +++ b/code/modules/client/preference_setup/background/03_records.dm @@ -15,9 +15,6 @@ from_file(S["memory"],pref.memory) /datum/category_item/player_setup_item/background/records/save_character(var/savefile/S) - to_file(S["med_record"],pref.med_record) - to_file(S["sec_record"],pref.sec_record) - to_file(S["gen_record"],pref.gen_record) to_file(S["memory"],pref.memory) /datum/category_item/player_setup_item/background/records/content(var/mob/user) @@ -26,33 +23,30 @@ if(jobban_isbanned(user, "Records")) . += "You are banned from using character records.
" else - . += "Medical Records: " - . += "[TextPreview(pref.med_record,40)]
" - . += "Employment Records: " - . += "[TextPreview(pref.gen_record,40)]
" - . += "Security Records: " - . += "[TextPreview(pref.sec_record,40)]
" + if(pref.med_record) + . += "Medical Records: " + . += "[TextPreview(pref.med_record,40)]
" + if(pref.gen_record) + . += "Employment Records: " + . += "[TextPreview(pref.gen_record,40)]
" + if(pref.sec_record) + . += "Security Records: " + . += "[TextPreview(pref.sec_record,40)]
" . += "Memory: " . += "[TextPreview(pref.memory,40)]
" . = jointext(.,null) /datum/category_item/player_setup_item/background/records/OnTopic(var/href,var/list/href_list, var/mob/user) if(href_list["set_medical_records"]) - var/new_medical = sanitize(input(user,"Enter medical information here.",CHARACTER_PREFERENCE_INPUT_TITLE, html_decode(pref.med_record)) as message|null, MAX_PAPER_MESSAGE_LEN, extra = 0) - if(!isnull(new_medical) && !jobban_isbanned(user, "Records") && CanUseTopic(user)) - pref.med_record = new_medical + input(user,"Read medical information here.",CHARACTER_PREFERENCE_INPUT_TITLE, html_decode(pref.med_record)) as message|null return TOPIC_REFRESH else if(href_list["set_general_records"]) - var/new_general = sanitize(input(user,"Enter employment information here.",CHARACTER_PREFERENCE_INPUT_TITLE, html_decode(pref.gen_record)) as message|null, MAX_PAPER_MESSAGE_LEN, extra = 0) - if(!isnull(new_general) && !jobban_isbanned(user, "Records") && CanUseTopic(user)) - pref.gen_record = new_general + input(user,"Read employment information here.",CHARACTER_PREFERENCE_INPUT_TITLE, html_decode(pref.gen_record)) as message|null return TOPIC_REFRESH else if(href_list["set_security_records"]) - var/sec_medical = sanitize(input(user,"Enter security information here.",CHARACTER_PREFERENCE_INPUT_TITLE, html_decode(pref.sec_record)) as message|null, MAX_PAPER_MESSAGE_LEN, extra = 0) - if(!isnull(sec_medical) && !jobban_isbanned(user, "Records") && CanUseTopic(user)) - pref.sec_record = sec_medical + input(user,"Read security information here.",CHARACTER_PREFERENCE_INPUT_TITLE, html_decode(pref.sec_record)) as message|null return TOPIC_REFRESH else if(href_list["set_memory"]) @@ -62,3 +56,82 @@ return TOPIC_REFRESH . = ..() + +/datum/category_item/player_setup_item/background/records/proc/load_record(savefile/S, recordtype) + var/recordgiven = S["[recordtype]"] + if(S["version"] >= 19) + if(!recordgiven || !(recordtype in pref.vars)) + return FALSE + + if(istype(recordgiven, /datum/storedrecord)) + pref.vars[recordtype] = recordgiven + else + if(istext(recordgiven) && (recordtype in pref.vars)) + pref.vars[recordtype] = recordgiven + + + +/datum/storedrecord + +/datum/storedrecord/proc/transfertocomputer(datum/report_field/tofile) + // nobody here. +/datum/storedrecord/security + var/list/crimes = list() + var/list/evidence = list() + var/list/locations = list() + + +/datum/storedrecord/security/transfertocomputer(datum/report_field/tofile) + if(!istype(tofile, /datum/report_field/arraylinkage)) + return FALSE + var/datum/report_field/arraylinkage/toset = tofile + toset.arrays = list() + toset.arrays["crimes"] = crimes // edit these appropriately on any changes + toset.arrays["evidence"] = evidence // don't forget to synch these indexes + toset.arrays["locations"] = locations + +/datum/storedrecord/security/default // used by default + crimes = list("No crimes on record.") + evidence = list("N/A.") + locations = list("N/A.") + + +/datum/storedrecord/medical + var/list/prosthetics = list() + var/list/wounds = list() + var/list/bodystate = list() + var/list/chemhistory = list() + var/list/psychological = list() + + +/datum/storedrecord/medical/transfertocomputer(datum/report_field/tofile) + if(!istype(tofile, /datum/report_field/arrayclump)) + return FALSE + var/datum/report_field/arrayclump/toset = tofile + var/list/tochange = toset.value + tochange["prosthetics"] = prosthetics // change these appropriately as well + tochange["wounds"] = wounds // but as an arrayclump, they don't need synchronized indexing + tochange["Body state"] = bodystate + tochange["chemhistory"] = chemhistory + tochange["psychological"] = psychological + + +/datum/storedrecord/medical/default // used by default + prosthetics = list("No prosthetics on record.") + wounds = list("No wounds on record.") + bodystate = list("Alive at time of writing.") + chemhistory = list("Chemical record is clean.") + psychological = list("No psychological profiling has been done at time of writing.") + + +/datum/storedrecord/general + var/background // background is defined by fates + var/origin // origin is defined by origin + + + + +/datum/storedrecord/general/default // used by default + background = list("Unremarkable.") + origin = list("Origin unknown.") + diff --git a/code/modules/client/preference_setup/general/05_records.dm b/code/modules/client/preference_setup/general/05_records.dm deleted file mode 100644 index 28767cd3fa4..00000000000 --- a/code/modules/client/preference_setup/general/05_records.dm +++ /dev/null @@ -1,64 +0,0 @@ -/datum/preferences - var/med_record = "" - var/sec_record = "" - var/gen_record = "" - var/memory = "" - -/datum/category_item/player_setup_item/physical/records - name = "Records" - sort_order = 5 - -/datum/category_item/player_setup_item/physical/records/load_character(var/savefile/S) - from_file(S["med_record"],pref.med_record) - from_file(S["sec_record"],pref.sec_record) - from_file(S["gen_record"],pref.gen_record) - from_file(S["memory"],pref.memory) - -/datum/category_item/player_setup_item/physical/records/save_character(var/savefile/S) - to_file(S["med_record"],pref.med_record) - to_file(S["sec_record"],pref.sec_record) - to_file(S["gen_record"],pref.gen_record) - to_file(S["memory"],pref.memory) - -/datum/category_item/player_setup_item/physical/records/content(var/mob/user) - . = list() - . += "
Records:
" - if(jobban_isbanned(user, "Records")) - . += "You are banned from using character records.
" - else - . += "Medical Records: " - . += "[TextPreview(pref.med_record,40)]
" - . += "Employment Records: " - . += "[TextPreview(pref.gen_record,40)]
" - . += "Security Records: " - . += "[TextPreview(pref.sec_record,40)]
" - . += "Memory: " - . += "[TextPreview(pref.memory,40)]
" - . = jointext(.,null) - -/datum/category_item/player_setup_item/physical/records/OnTopic(var/href,var/list/href_list, var/mob/user) - if(href_list["set_medical_records"]) - var/new_medical = sanitize(input(user,"Enter medical information here.",CHARACTER_PREFERENCE_INPUT_TITLE, html_decode(pref.med_record)) as message|null, MAX_PAPER_MESSAGE_LEN, extra = 0) - if(!isnull(new_medical) && !jobban_isbanned(user, "Records") && CanUseTopic(user)) - pref.med_record = new_medical - return TOPIC_REFRESH - - else if(href_list["set_general_records"]) - var/new_general = sanitize(input(user,"Enter employment information here.",CHARACTER_PREFERENCE_INPUT_TITLE, html_decode(pref.gen_record)) as message|null, MAX_PAPER_MESSAGE_LEN, extra = 0) - if(!isnull(new_general) && !jobban_isbanned(user, "Records") && CanUseTopic(user)) - pref.gen_record = new_general - return TOPIC_REFRESH - - else if(href_list["set_security_records"]) - var/sec_medical = sanitize(input(user,"Enter security information here.",CHARACTER_PREFERENCE_INPUT_TITLE, html_decode(pref.sec_record)) as message|null, MAX_PAPER_MESSAGE_LEN, extra = 0) - if(!isnull(sec_medical) && !jobban_isbanned(user, "Records") && CanUseTopic(user)) - pref.sec_record = sec_medical - return TOPIC_REFRESH - - else if(href_list["set_memory"]) - var/memes = sanitize(input(user,"Enter memorized information here.",CHARACTER_PREFERENCE_INPUT_TITLE, html_decode(pref.memory)) as message|null, MAX_PAPER_MESSAGE_LEN, extra = 0) - if(!isnull(memes) && CanUseTopic(user)) - pref.memory = memes - return TOPIC_REFRESH - - . = ..() diff --git a/code/modules/client/preference_setup/loadout/lists/accessories.dm b/code/modules/client/preference_setup/loadout/lists/accessories.dm index 8ef7c68fba2..939ae4b7dae 100644 --- a/code/modules/client/preference_setup/loadout/lists/accessories.dm +++ b/code/modules/client/preference_setup/loadout/lists/accessories.dm @@ -43,7 +43,7 @@ /datum/gear/accessory/guild display_name = "armband, Aster's Guild" path = /obj/item/clothing/accessory/armband/cargo - allowed_roles = list(JOBS_CARGO) + allowed_roles = list(JOBS_GUILD) /datum/gear/accessory/engineering display_name = "armband, Technomancer League" diff --git a/code/modules/client/preference_setup/loadout/lists/suits.dm b/code/modules/client/preference_setup/loadout/lists/suits.dm index aae61f2a909..2915bdfa18f 100644 --- a/code/modules/client/preference_setup/loadout/lists/suits.dm +++ b/code/modules/client/preference_setup/loadout/lists/suits.dm @@ -137,10 +137,10 @@ /datum/gear/suit/guild/black display_name = "black guild coat" path = /obj/item/clothing/suit/storage/cargo_jacket/black - allowed_roles = list(JOBS_CARGO) + allowed_roles = list(JOBS_GUILD) /datum/gear/suit/guild/black/old display_name = "old black guild coat" path = /obj/item/clothing/suit/storage/cargo_jacket/black/old - allowed_roles = list(JOBS_CARGO) + allowed_roles = list(JOBS_GUILD) diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index d147644c094..1c99753d81d 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -239,10 +239,7 @@ for(var/lang in alternate_languages) character.add_language(lang) - - character.med_record = med_record - character.sec_record = sec_record - character.gen_record = gen_record + character.exploit_record = exploit_record if(!character.isSynthetic()) character.nutrition = rand(250, 450) diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index 3868750180a..c60ce12189c 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -1,5 +1,5 @@ #define SAVEFILE_VERSION_MIN 8 -#define SAVEFILE_VERSION_MAX 18 +#define SAVEFILE_VERSION_MAX 19 /datum/preferences/proc/load_path(ckey,filename="preferences.sav") if(!ckey) diff --git a/code/modules/economy/Accounts.dm b/code/modules/economy/Accounts.dm index 9bd0818e2bf..56100ca2687 100644 --- a/code/modules/economy/Accounts.dm +++ b/code/modules/economy/Accounts.dm @@ -37,8 +37,12 @@ return owner_name //Attempts to return the associated data record for this account +// expensive to run, store the result from this. /datum/money_account/proc/get_record() - return find_general_record("pay_account", account_number) + for(var/datum/computer_file/report/crew_record/CR in GLOB.all_crew_records) + var/accountnum = CR.get_account() + if(account_number == accountnum) + return CR /datum/transaction var/target_name = "" diff --git a/code/modules/mob/hear_say.dm b/code/modules/mob/hear_say.dm index a2c889b6124..f35152a5ca2 100644 --- a/code/modules/mob/hear_say.dm +++ b/code/modules/mob/hear_say.dm @@ -108,9 +108,8 @@ var/mob/living/carbon/human/H = speaker if(H.voice) speaker_name = H.voice - for(var/datum/data/record/G in data_core.general) - if(G.fields["name"] == speaker_name) - return H.rank_prefix_name(speaker_name) + if(get_crewmember_record(speaker_name)) + return H.rank_prefix_name(speaker_name) return voice_name ? voice_name : speaker_name diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index 74137ba2483..76fbc1a113e 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -294,7 +294,8 @@ criminal = R ? R.get_criminalStatus() : "None" msg += "Criminal status: \[[criminal]\]\n" - msg += "Security records: \[View\] \[Add comment\]\n" + msg += {"Security records: \[View\] \[View Brief\] + \[Add Note\]\n"} if(hasHUD(user, "medical")) var/perpname = "wot" @@ -306,15 +307,11 @@ perpname = id_card.registered_name else perpname = src.name - - for (var/datum/data/record/E in data_core.general) - if (E.fields["name"] == perpname) - for (var/datum/data/record/R in data_core.general) - if (R.fields["id"] == E.fields["id"]) - medical = R.fields["p_stat"] + var/datum/computer_file/report/crew_record/E = get_crewmember_record(perpname) + medical = E.get_status() msg += "Physical status: \[[medical]\]\n" - msg += "Medical records: \[View\] \[Add comment\]\n" + msg += "Medical records: \[View\]\n" var/obj/item/clothing/under/U = w_uniform if(U && istype(U) && U.sensor_mode >= 2) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index e4a204b26bc..7103825d880 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -425,21 +425,20 @@ var/list/rank_prefix = list(\ if(perpname) var/datum/computer_file/report/crew_record/R = get_crewmember_record(perpname) if(R) + modified = TRUE var/setcriminal = input(usr, "Specify a new criminal status for this person.", "Security HUD", R.get_criminalStatus()) in GLOB.security_statuses + "Cancel" - if(hasHUD(usr, "security")) - if(setcriminal != "Cancel") - R.set_criminalStatus(setcriminal) - modified = TRUE - BITSET(hud_updateflag, WANTED_HUD) - if(ishuman(usr)) - var/mob/living/carbon/human/U = usr - U.handle_regular_hud_updates() - if(isrobot(usr)) - var/mob/living/silicon/robot/U = usr - U.handle_regular_hud_updates() + if(setcriminal != "Cancel") + R.set_criminalStatus(setcriminal) + BITSET(hud_updateflag, WANTED_HUD) + if(ishuman(usr)) + var/mob/living/carbon/human/U = usr + U.handle_regular_hud_updates() + if(isrobot(usr)) + var/mob/living/silicon/robot/U = usr + U.handle_regular_hud_updates() if(!modified) - to_chat(usr, "\red Unable to locate a data core entry for this person.") + to_chat(usr, "\red Unable to locate a criminal record for this person.") if(href_list["secrecord"]) if(hasHUD(usr,"security")) @@ -452,52 +451,50 @@ var/list/rank_prefix = list(\ else perpname = src.name - for(var/datum/data/record/E in data_core.general) - if(E.fields["name"] == perpname) - for(var/datum/data/record/R in data_core.security) - if(R.fields["id"] == E.fields["id"]) - if(hasHUD(usr,"security")) - to_chat(usr, "Name: [R.fields["name"]] Criminal Status: [R.fields["criminal"]]") - to_chat(usr, "Minor Crimes: [R.fields["mi_crim"]]") - to_chat(usr, "Details: [R.fields["mi_crim_d"]]") - to_chat(usr, "Major Crimes: [R.fields["ma_crim"]]") - to_chat(usr, "Details: [R.fields["ma_crim_d"]]") - to_chat(usr, "Notes: [R.fields["notes"]]") - to_chat(usr, "\[View Comment Log\]") - read = 1 + var/datum/computer_file/report/crew_record/CR = get_crewmember_record(perpname) + if(CR) + to_chat(usr, "Name: [CR.get_name()] Criminal Status: [CR.get_criminalStatus()]") + var/datum/report_field/array/tofetch = CR.get_linkage_secNotes() + var/list/Briefing = tofetch.value_list + if(Briefing.len > 0) + var/list/combined = list() + for(var/iterator = min(Briefing.len-1, 3), iterator > -1, iterator -= 1) + combined.Add(Briefing[Briefing.len - iterator]) + to_chat(usr, "Briefing latest: "+combined.Join(", ")) + to_chat(usr, "\[Add to Brief\]") + to_chat(usr, "\[View Full Brief\]") + read = 1 if(!read) - to_chat(usr, "\red Unable to locate a data core entry for this person.") + to_chat(usr, "\red Unable to locate a criminal record for this person.") - if(href_list["secrecordComment"]) + if(href_list["viewbrief"]) if(hasHUD(usr,"security")) var/perpname = "wot" - var/read = 0 - var/obj/item/card/id/id = GetIdCard() - if(istype(id)) - perpname = id.registered_name + if(wear_id) + var/obj/item/card/id/id + if(istype(wear_id, /obj/item/modular_computer/pda)) + id = wear_id.GetIdCard() + if(!id) + id = get_idcard() + if(id) + perpname = id.registered_name else perpname = src.name - for(var/datum/data/record/E in data_core.general) - if(E.fields["name"] == perpname) - for(var/datum/data/record/R in data_core.security) - if(R.fields["id"] == E.fields["id"]) - if(hasHUD(usr,"security")) - read = 1 - var/counter = 1 - while(R.fields[text("com_[]", counter)]) - to_chat(usr, text("[]", R.fields[text("com_[]", counter)])) - counter++ - if(counter == 1) - to_chat(usr, "No comment found") - to_chat(usr, "\[Add comment\]") - - if(!read) - to_chat(usr, "\red Unable to locate a data core entry for this person.") + var/datum/computer_file/report/crew_record/E = get_crewmember_record(perpname) + if(!E) + to_chat(usr, SPAN_DANGER("Unable to locate a record for this person.")) + return + var/datum/report_field/array/tofetch = E.get_linkage_secNotes() + var/list/Briefing = tofetch.value_list + if(Briefing.len > 0) + to_chat(usr, SPAN_NOTICE("Brief: " + Briefing.Join(", "))) + else + to_chat(usr, SPAN_NOTICE("[perpname] does not have a brief.")) - if(href_list["secrecordadd"]) + if(href_list["secnoteadd"]) if(hasHUD(usr,"security")) var/perpname = "wot" if(wear_id) @@ -506,32 +503,27 @@ var/list/rank_prefix = list(\ id = wear_id.GetIdCard() if(!id) id = get_idcard() - if(id) - perpname = id.registered_name + if(id) + perpname = id.registered_name else perpname = src.name - for(var/datum/data/record/E in data_core.general) - if(E.fields["name"] == perpname) - for(var/datum/data/record/R in data_core.security) - if(R.fields["id"] == E.fields["id"]) - if(hasHUD(usr,"security")) - var/t1 = sanitize(input("Add Comment:", "Sec. records", null, null) as message) - if( !(t1) || usr.stat || usr.restrained() || !(hasHUD(usr,"security")) ) - return - var/counter = 1 - while(R.fields[text("com_[]", counter)]) - counter++ - if(ishuman(usr)) - var/mob/living/carbon/human/U = usr - R.fields[text("com_[counter]")] = text("Made by [U.get_authentification_name()] ([U.get_assignment()]) on [time2text(world.realtime, "DDD MMM DD hh:mm:ss")], [game_year]
[t1]") - if(isrobot(usr)) - var/mob/living/silicon/robot/U = usr - R.fields[text("com_[counter]")] = text("Made by [U.name] ([U.modtype] [U.braintype]) on [time2text(world.realtime, "DDD MMM DD hh:mm:ss")], [game_year]
[t1]") + + var/datum/computer_file/report/crew_record/E = get_crewmember_record(perpname) + if(!E) + to_chat(usr, SPAN_WARNING("Record for [perpname] not found!")) + return + var/t1 = sanitize(input("Add to Brief:", "Sec. records", null, null) as text) + if( !(t1) || usr.stat || usr.restrained() || !(hasHUD(usr,"security")) ) + return + + var/datum/report_field/array/recordnote = E.get_linkage_secNotes() + + if(recordnote) + recordnote.add_value(t1) if(href_list["medical"]) if(hasHUD(usr,"medical")) var/perpname = "wot" - var/modified = 0 var/obj/item/card/id/id = GetIdCard() if(istype(id)) @@ -539,126 +531,49 @@ var/list/rank_prefix = list(\ else perpname = src.name - for(var/datum/data/record/E in data_core.general) - if(E.fields["name"] == perpname) - for(var/datum/data/record/R in data_core.general) - if(R.fields["id"] == E.fields["id"]) - - var/setmedical = input(usr, "Specify a new medical status for this person.", "Medical HUD", R.fields["p_stat"]) in list("*SSD*", "*Deceased*", "Physically Unfit", "Active", "Disabled", "Cancel") - - if(hasHUD(usr,"medical")) - if(setmedical != "Cancel") - R.fields["p_stat"] = setmedical - modified = 1 - if(PDA_Manifest.len) - PDA_Manifest.Cut() + var/datum/computer_file/report/crew_record/E = get_crewmember_record(perpname) + if(E) + var/setmedical = input(usr, "Specify a new medical status for this person.", "Medical HUD", E.get_status()) in GLOB.physical_statuses - spawn() - if(ishuman(usr)) - var/mob/living/carbon/human/U = usr - U.handle_regular_hud_updates() - if(isrobot(usr)) - var/mob/living/silicon/robot/U = usr - U.handle_regular_hud_updates() + if(hasHUD(usr,"medical")) + if(setmedical != "Cancel") + E.set_status(setmedical) - if(!modified) - to_chat(usr, "\red Unable to locate a data core entry for this person.") - - if(href_list["medrecord"]) - if(hasHUD(usr,"medical")) - var/perpname = "wot" - var/read = 0 + spawn() + if(ishuman(usr)) + var/mob/living/carbon/human/U = usr + U.handle_regular_hud_updates() + if(isrobot(usr)) + var/mob/living/silicon/robot/U = usr + U.handle_regular_hud_updates() - if(wear_id) - var/obj/item/card/id/id - if(istype(wear_id, /obj/item/modular_computer/pda)) - id = wear_id.GetIdCard() - if(!id) - id = get_idcard() - if(id) - perpname = id.registered_name else - perpname = src.name - for(var/datum/data/record/E in data_core.general) - if(E.fields["name"] == perpname) - for(var/datum/data/record/R in data_core.medical) - if(R.fields["id"] == E.fields["id"]) - if(hasHUD(usr,"medical")) - to_chat(usr, "Name: [R.fields["name"]] Blood Type: [R.fields["b_type"]]") - to_chat(usr, "DNA: [R.fields["b_dna"]]") - to_chat(usr, "Minor Disabilities: [R.fields["mi_dis"]]") - to_chat(usr, "Details: [R.fields["mi_dis_d"]]") - to_chat(usr, "Major Disabilities: [R.fields["ma_dis"]]") - to_chat(usr, "Details: [R.fields["ma_dis_d"]]") - to_chat(usr, "Notes: [R.fields["notes"]]") - to_chat(usr, "\[View Comment Log\]") - read = 1 - - if(!read) - to_chat(usr, "\red Unable to locate a data core entry for this person.") + to_chat(usr, "\red Unable to locate a medical record for this person.") - if(href_list["medrecordComment"]) + if(href_list["medrecord"]) if(hasHUD(usr,"medical")) var/perpname = "wot" var/read = 0 - if(wear_id) - var/obj/item/card/id/id - if(istype(wear_id, /obj/item/modular_computer/pda)) - id = wear_id.GetIdCard() - if(!id) - id = get_idcard() - if(id) - perpname = id.registered_name + var/obj/item/card/id/id = GetIdCard() + if(istype(id)) + perpname = id.registered_name else perpname = src.name - for(var/datum/data/record/E in data_core.general) - if(E.fields["name"] == perpname) - for(var/datum/data/record/R in data_core.medical) - if(R.fields["id"] == E.fields["id"]) - if(hasHUD(usr,"medical")) - read = 1 - var/counter = 1 - while(R.fields[text("com_[]", counter)]) - to_chat(usr, text("[]", R.fields[text("com_[]", counter)])) - counter++ - if(counter == 1) - to_chat(usr, "No comment found") - to_chat(usr, "\[Add comment\]") + var/datum/computer_file/report/crew_record/E = get_crewmember_record(perpname) + var/datum/report_field/arrayclump/M = E.get_linkage_medRecord() + if(E) + to_chat(usr, "Name: [E.get_name()] Blood Type: [E.get_bloodtype()]") + to_chat(usr, "DNA: [E.get_dna()]") + to_chat(usr, "Prosthetics: [M.value["prosthetics"]]") + to_chat(usr, "Recorded Wounds: [M.value["wounds"]]") + to_chat(usr, "Autopsy: [M.value["Body state"]]") + to_chat(usr, "Psychological Profile: [M.value["psychological"]]") + to_chat(usr, "Chemical History: [M.value["chemhistory"]]") + read = 1 if(!read) - to_chat(usr, "\red Unable to locate a data core entry for this person.") - - if(href_list["medrecordadd"]) - if(hasHUD(usr,"medical")) - var/perpname = "wot" - if(wear_id) - var/obj/item/card/id/id - if(istype(wear_id, /obj/item/modular_computer/pda)) - id = wear_id.GetIdCard() - if(!id) - id = get_idcard() - if(id) - perpname = id.registered_name - else - perpname = src.name - for(var/datum/data/record/E in data_core.general) - if(E.fields["name"] == perpname) - for(var/datum/data/record/R in data_core.medical) - if(R.fields["id"] == E.fields["id"]) - if(hasHUD(usr,"medical")) - var/t1 = sanitize(input("Add Comment:", "Med. records", null, null) as message) - if( !(t1) || usr.stat || usr.restrained() || !(hasHUD(usr,"medical")) ) - return - var/counter = 1 - while(R.fields[text("com_[]", counter)]) - counter++ - if(ishuman(usr)) - var/mob/living/carbon/human/U = usr - R.fields[text("com_[counter]")] = text("Made by [U.get_authentification_name()] ([U.get_assignment()]) on [time2text(world.realtime, "DDD MMM DD hh:mm:ss")], [game_year]
[t1]") - if(isrobot(usr)) - var/mob/living/silicon/robot/U = usr - R.fields[text("com_[counter]")] = text("Made by [U.name] ([U.modtype] [U.braintype]) on [time2text(world.realtime, "DDD MMM DD hh:mm:ss")], [game_year]
[t1]") + to_chat(usr, "\red Unable to locate a medical record for this person.") if(href_list["lookitem"]) var/obj/item/I = locate(href_list["lookitem"]) diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index f10fe52c7a6..de1bf065ae6 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -84,9 +84,6 @@ var/equipment_prescription // Eye prescription granted by equipped items var/list/equipment_overlays = list() // Extra overlays from equipped items - var/med_record = "" - var/sec_record = "" - var/gen_record = "" var/exploit_record = "" var/stance_damage = 0 //Whether this mob's ability to stand has been affected diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 50684da1ed4..cd8fa61de9d 100755 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -500,17 +500,8 @@ default behaviour is: // Delete them from datacore. - if(PDA_Manifest.len) - PDA_Manifest.Cut() - for(var/datum/data/record/R in data_core.medical) - if ((R.fields["name"] == src.real_name)) - qdel(R) - for(var/datum/data/record/T in data_core.security) - if ((T.fields["name"] == src.real_name)) - qdel(T) - for(var/datum/data/record/G in data_core.general) - if ((G.fields["name"] == src.real_name)) - qdel(G) + var/datum/todelete = get_crewmember_record(name) + qdel(todelete) //This should guarantee that ghosts don't spawn. src.ckey = null diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index a4387a8c287..711932dc7b6 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -550,8 +550,8 @@ var/list/ai_verbs_default = list( var/personnel_list[] = list() - for(var/datum/data/record/t in data_core.locked)//Look in data core locked. - personnel_list["[t.fields["name"]]: [t.fields["rank"]]"] = t.fields["image"]//Pull names, rank, and image. + for(var/datum/computer_file/report/crew_record/t in GLOB.all_crew_records) + personnel_list["[t.get_name()]: [t.get_job()]"] = t.photo_front//Pull names, rank, and image. if(personnel_list.len) input = input("Select a crew member:") as null|anything in personnel_list diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm index adb433f583e..eec8f2cf1fd 100644 --- a/code/modules/mob/living/silicon/pai/pai.dm +++ b/code/modules/mob/living/silicon/pai/pai.dm @@ -58,12 +58,10 @@ var/medHUD = FALSE // Toggles whether the Medical HUD is active or not var/medical_cannotfind = 0 - var/datum/data/record/medicalActive1 // Datacore record declarations for record software - var/datum/data/record/medicalActive2 + var/datum/data/record/medicalActive // Datacore record declarations for record software var/security_cannotfind = 0 - var/datum/data/record/securityActive1 // Could probably just combine all these into one - var/datum/data/record/securityActive2 + var/datum/data/record/securityActive var/obj/machinery/door/hackdoor // The airlock being hacked var/hackprogress = 0 // Possible values: 0 - 1000, >= 1000 means the hack is complete and will be reset upon next check @@ -158,11 +156,9 @@ set category = "pAI Commands" set name = "Reset Records Software" - securityActive1 = null - securityActive2 = null + securityActive = null security_cannotfind = 0 - medicalActive1 = null - medicalActive2 = null + medicalActive = null medical_cannotfind = 0 SSnano.update_uis(src) to_chat(usr, SPAN_NOTICE("You reset your record-viewing software.")) diff --git a/code/modules/mob/living/silicon/pai/software_modules.dm b/code/modules/mob/living/silicon/pai/software_modules.dm index 7855c6da1a5..7e9f4306c97 100644 --- a/code/modules/mob/living/silicon/pai/software_modules.dm +++ b/code/modules/mob/living/silicon/pai/software_modules.dm @@ -118,21 +118,19 @@ ram_cost = 5 id = "manifest" toggle = 0 + var/datum/nano_module/crew_manifest/manifestmodule + var/datum/topic_manager/program/mandatorymanager - on_ui_interact(mob/living/silicon/pai/user, datum/nanoui/ui=null, force_open=1) - data_core.get_manifest_json() - - var/data[0] - // This is dumb, but NanoUI breaks if it has no data to send - data["manifest"] = list("__json_cache" = ManifestJSON) + New() + mandatorymanager = new() + manifestmodule = new(src, mandatorymanager) + + Destroy() + . = ..() + QDEL_NULL(manifestmodule) - ui = SSnano.try_update_ui(user, user, id, ui, data, force_open) - if(!ui) - // Don't copy-paste this unless you're making a pAI software module! - ui = new(user, user, id, "pai_manifest.tmpl", "Crew Manifest", 450, 600) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) + on_ui_interact(mob/living/silicon/pai/user, datum/nanoui/ui=null, force_open=NANOUI_REINITIALIZE) + manifestmodule.nano_ui_interact(user, ui, "main", force_open) // transfers applicable vars to manifestmodule /datum/pai_software/med_records name = "Medical Records" @@ -144,18 +142,23 @@ var/data[0] var/records[0] - for(var/datum/data/record/general in sortRecord(data_core.general)) + for(var/datum/computer_file/report/crew_record/general in GLOB.all_crew_records) var/record[0] - record["name"] = general.fields["name"] + record["name"] = general.get_name() record["ref"] = "\ref[general]" records[++records.len] = record data["records"] = records - var/datum/data/record/G = user.medicalActive1 - var/datum/data/record/M = user.medicalActive2 - data["general"] = G ? G.fields : null - data["medical"] = M ? M.fields : null + var/datum/computer_file/report/crew_record/G = user.securityActive + var/datum/report_field/arrayclump/M = G?.get_linkage_medRecord() + data["general"] = G ? list("name" = G.get_name(), "sex" = G.get_sex(), "species" = G.get_species(), "age" = G.get_age(), \ + "job" = G.get_job(), "fingerprint" = G.get_fingerprint(), "status" = G.get_status(), "bloodtype" = G.get_bloodtype()) : null + data["prosthetics"] = M ? M.value["prosthetics"] : null + data["wounds"] = M ? M.value["wounds"] : null + data["Body state"] = M ? M.value["body state"] : null + data["chemhistory"] = M ? M.value["chemhistory"] : null + data["psychological"] = M ? M.value["psychological"] : null data["could_not_find"] = user.medical_cannotfind ui = SSnano.try_update_ui(user, user, id, ui, data, force_open) @@ -169,24 +172,15 @@ Topic(href, href_list) var/mob/living/silicon/pai/P = usr if(!istype(P)) return - + if(href_list["select"]) - var/datum/data/record/record = locate(href_list["select"]) - if(record) - var/datum/data/record/R = record - var/datum/data/record/M = null - if (!( data_core.general.Find(R) )) - P.medical_cannotfind = 1 - else - P.medical_cannotfind = 0 - for(var/datum/data/record/E in data_core.medical) - if ((E.fields["name"] == R.fields["name"] || E.fields["id"] == R.fields["id"])) - M = E - P.medicalActive1 = R - P.medicalActive2 = M + var/datum/computer_file/report/crew_record/record = locate(href_list["select"]) + if(istype(record)) + P.medical_cannotfind = 0 + P.medicalActive = record else P.medical_cannotfind = 1 - return 1 + return TRUE /datum/pai_software/sec_records name = "Security Records" @@ -198,18 +192,18 @@ var/data[0] var/records[0] - for(var/datum/data/record/general in sortRecord(data_core.general)) + for(var/datum/computer_file/report/crew_record/general in GLOB.all_crew_records) var/record[0] - record["name"] = general.fields["name"] + record["name"] = general.get_name() record["ref"] = "\ref[general]" records[++records.len] = record data["records"] = records - var/datum/data/record/G = user.securityActive1 - var/datum/data/record/S = user.securityActive2 - data["general"] = G ? G.fields : null - data["security"] = S ? S.fields : null + var/datum/computer_file/report/crew_record/G = user.securityActive + data["general"] = G ? list("name" = G.get_name(), "sex" = G.get_sex(), "species" = G.get_species(), "age" = G.get_age(), \ + "job" = G.get_job(), "fingerprint" = G.get_fingerprint(), "status" = G.get_status(), "criminalStatus" = G.get_criminalStatus(), \ + "secNotes" = G.get_linkage_secNotes()) : null data["could_not_find"] = user.security_cannotfind ui = SSnano.try_update_ui(user, user, id, ui, data, force_open) @@ -225,26 +219,14 @@ if(!istype(P)) return if(href_list["select"]) - var/datum/data/record/record = locate(href_list["select"]) - if(record) - var/datum/data/record/R = record - var/datum/data/record/S = null - if (!( data_core.general.Find(R) )) - P.securityActive1 = null - P.securityActive2 = null - P.security_cannotfind = 1 - else - P.security_cannotfind = 0 - for(var/datum/data/record/E in data_core.security) - if ((E.fields["name"] == R.fields["name"] || E.fields["id"] == R.fields["id"])) - S = E - P.securityActive1 = R - P.securityActive2 = S + var/datum/computer_file/report/crew_record/record = locate(href_list["select"]) + if(istype(record)) + P.security_cannotfind = 0 + P.securityActive = record else - P.securityActive1 = null - P.securityActive2 = null + P.securityActive = null P.security_cannotfind = 1 - return 1 + return TRUE /datum/pai_software/door_jack name = "Door Jack" diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index ea7c4bebbf3..bce2ea9e511 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -490,11 +490,11 @@ proc/is_blind(A) if(id) perpname = id.registered_name - var/datum/data/record/R = find_security_record("name", perpname) - if(check_records && !R) + var/datum/computer_file/report/crew_record/CR = get_crewmember_record(perpname) + if(check_records && !CR) threatcount += 4 - if(check_arrest && R && (R.fields["criminal"] == "*Arrest*")) + if(check_arrest && CR && (CR?.get_criminalStatus() == "*Arrest*")) // why is paperwork's folly being translated to electronic format? threatcount += 4 return threatcount diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index cee4bf2988a..4fae2968ad0 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -318,7 +318,6 @@ if(SSjob.ShouldCreateRecords(job.title)) if(character.mind.assigned_role != "Robot") CreateModularRecord(character) - data_core.manifest_inject(character) matchmaker.do_matchmaking() SSticker.minds += character.mind//Cyborgs and AIs handle this in the transform proc. //TODO!!!!! ~Carn diff --git a/code/modules/modular_computers/computers/modular_computer/core.dm b/code/modules/modular_computers/computers/modular_computer/core.dm index 1a01fb8f1d1..22c3b472a3f 100644 --- a/code/modules/modular_computers/computers/modular_computer/core.dm +++ b/code/modules/modular_computers/computers/modular_computer/core.dm @@ -308,6 +308,8 @@ SetName(initial(name)) /obj/item/modular_computer/proc/update_uis() + if(active_program && istype(active_program?.nanomodule_path, /datum/nano_module/tgui)) + return FALSE if(active_program) //Should we update program ui or computer ui? SSnano.update_uis(active_program) if(active_program.NM) diff --git a/code/modules/modular_computers/file_system/program.dm b/code/modules/modular_computers/file_system/program.dm index 6e3a4dd9015..04407afb0f1 100644 --- a/code/modules/modular_computers/file_system/program.dm +++ b/code/modules/modular_computers/file_system/program.dm @@ -195,7 +195,10 @@ if(ui) ui.close() return computer.nano_ui_interact(user) - if(istype(NM)) + if(istype(NM, /datum/nano_module/tgui)) + NM.ui_interact(user) + return FALSE + else if(istype(NM)) NM.nano_ui_interact(user, ui_key, null, force_open) return 0 return 1 diff --git a/code/modules/modular_computers/file_system/programs/command/card.dm b/code/modules/modular_computers/file_system/programs/command/card.dm index 4835caca081..3fc804a66de 100644 --- a/code/modules/modular_computers/file_system/programs/command/card.dm +++ b/code/modules/modular_computers/file_system/programs/command/card.dm @@ -62,7 +62,7 @@ data["security_jobs"] = format_jobs(security_positions) //data["exploration_jobs"] = format_jobs(exploration_positions) data["service_jobs"] = format_jobs(civilian_positions) - data["supply_jobs"] = format_jobs(cargo_positions) + data["supply_jobs"] = format_jobs(guild_positions) data["church_jobs"] = format_jobs(church_positions) //data["civilian_jobs"] = format_jobs(civilian_positions) data["centcom_jobs"] = format_jobs(get_all_centcom_jobs()) diff --git a/code/modules/modular_computers/file_system/programs/generic/records.dm b/code/modules/modular_computers/file_system/programs/generic/records.dm index 2836b1a741b..01fb6a442d1 100644 --- a/code/modules/modular_computers/file_system/programs/generic/records.dm +++ b/code/modules/modular_computers/file_system/programs/generic/records.dm @@ -1,3 +1,5 @@ + +#define NULL_RECORD list("name" = null, "uid" = null, "creator" = null, "file_time" = null, "fields" = null, "access" = null, "access_edit" = null) /datum/computer_file/program/records filename = "crewrecords" filedesc = "Crew Records" @@ -7,22 +9,22 @@ size = 14 requires_ntnet = TRUE available_on_ntnet = TRUE - nanomodule_path = /datum/nano_module/records + nanomodule_path = /datum/nano_module/tgui/records usage_flags = PROGRAM_ALL -/datum/nano_module/records +/datum/nano_module/tgui/records name = "Crew Records" var/datum/computer_file/report/crew_record/active_record var/message = null -/datum/nano_module/records/nano_ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = NANOUI_FOCUS, state = GLOB.default_state) +/datum/nano_module/tgui/records/ui_data(mob/user) var/list/data = host.initial_data() var/list/user_access = get_record_access(user) data["message"] = message if(active_record) - user << browse_rsc(active_record.photo_front, "front_[active_record.uid].png") - user << browse_rsc(active_record.photo_side, "side_[active_record.uid].png") + data["front_pic"] = icon2base64html(active_record.photo_front) + data["side_pic"] = icon2base64html(active_record.photo_side) data["pic_edit"] = check_access(user, access_heads) || check_access(user, access_security) data += active_record.generate_nano_data(user_access) else @@ -38,16 +40,20 @@ data["creation"] = check_access(user, access_heads) data["dnasearch"] = check_access(user, access_moebius) || check_access(user, access_forensics_lockers) data["fingersearch"] = check_access(user, access_security) + data += NULL_RECORD + return data +#undef NULL_RECORD + + - ui = SSnano.try_update_ui(user, src, ui_key, ui, data, force_open) +/datum/nano_module/tgui/records/ui_interact(mob/user, datum/tgui/ui = null) + ui = SStgui.try_update_ui(user, src, ui || currentui) if (!ui) - ui = new(user, src, ui_key, "crew_records.tmpl", name, 700, 540, state = state) - ui.auto_update_layout = 1 - ui.set_initial_data(data) - ui.open() + currentui = new(user, src, "CrewRecords", name) + currentui.open() -/datum/nano_module/records/proc/get_record_access(var/mob/user) +/datum/nano_module/tgui/records/proc/get_record_access(var/mob/user) var/list/user_access = using_access || user.GetAccess() var/obj/item/modular_computer/PC = nano_host() @@ -57,7 +63,7 @@ return user_access -/datum/nano_module/records/proc/edit_field(var/mob/user, var/field_ID) +/datum/nano_module/tgui/records/proc/edit_field(var/mob/user, var/field_ID) var/datum/computer_file/report/crew_record/R = active_record if(!R) return @@ -69,67 +75,68 @@ return F.ask_value(user) -/datum/nano_module/records/Topic(href, href_list) +/datum/nano_module/tgui/records/ui_act(action, params) if(..()) return 1 - if(href_list["clear_active"]) - active_record = null - return 1 - if(href_list["clear_message"]) - message = null - return 1 - if(href_list["set_active"]) - var/ID = text2num(href_list["set_active"]) - for(var/datum/computer_file/report/crew_record/R in GLOB.all_crew_records) - if(R.uid == ID) - active_record = R - break - return 1 - if(href_list["new_record"]) - if(!check_access(usr, access_heads)) - to_chat(usr, "Access Denied.") - return - active_record = new/datum/computer_file/report/crew_record() - GLOB.all_crew_records.Add(active_record) - return 1 - if(href_list["print_active"]) - if(!active_record) - return - print_text(record_to_html(active_record, get_record_access(usr)), usr) - return 1 - if(href_list["search"]) - var/field_name = href_list["search"] - var/search = sanitize(input("Enter the value for search for.") as null|text) - if(!search) + switch(action) + + if("clear_active") + active_record = null return 1 - for(var/datum/computer_file/report/crew_record/R in GLOB.all_crew_records) - var/datum/report_field/field = R.field_from_name(field_name) - if(lowertext(field.get_value()) == lowertext(search)) - active_record = R + if("clear_message") + message = null + return 1 + if("set_active") + var/ID = text2num(params["set_active"]) + for(var/datum/computer_file/report/crew_record/R in GLOB.all_crew_records) + if(R.uid == ID) + active_record = R + break + return 1 + if("new_record") + if(!check_access(usr, access_heads)) + to_chat(usr, "Access Denied.") + return + active_record = new/datum/computer_file/report/crew_record() + GLOB.all_crew_records.Add(active_record) + return 1 + if("print_active") + if(!active_record) + return + print_text(record_to_html(active_record, get_record_access(usr)), usr) + return 1 + if("search") + var/field_name = params["search"] + var/search = sanitize(input("Enter the value for search for.") as null|text) + if(!search) return 1 - message = "Unable to find record containing '[search]'" - return 1 + for(var/datum/computer_file/report/crew_record/R in GLOB.all_crew_records) + var/datum/report_field/field = R.field_from_name(field_name) + if(lowertext(field.get_value()) == lowertext(search)) + active_record = R + return 1 + message = "Unable to find record containing '[search]'" + return 1 var/datum/computer_file/report/crew_record/R = active_record if(!istype(R)) return 1 - if(href_list["edit_photo_front"]) - var/photo = get_photo(usr) - if(photo && active_record) - active_record.photo_front = photo - nano_ui_interact(usr) - return 1 - if(href_list["edit_photo_side"]) - var/photo = get_photo(usr) - if(photo && active_record) - active_record.photo_side = photo - nano_ui_interact(usr) - return 1 - if(href_list["edit_field"]) - edit_field(usr, text2num(href_list["edit_field"])) - return 1 + switch(action) + if("edit_photo_front") + var/photo = get_photo(usr) + if(photo && active_record) + active_record.photo_front = photo + return 1 + if("edit_photo_side") + var/photo = get_photo(usr) + if(photo && active_record) + active_record.photo_side = photo + return 1 + if("edit_field") + edit_field(usr, text2num(params["edit_field"])) + return 1 -/datum/nano_module/records/proc/get_photo(var/mob/user) +/datum/nano_module/tgui/records/proc/get_photo(var/mob/user) if(istype(user.get_active_hand(), /obj/item/photo)) var/obj/item/photo/photo = user.get_active_hand() return photo.img diff --git a/code/modules/modular_computers/file_system/reports/crew_record.dm b/code/modules/modular_computers/file_system/reports/crew_record.dm index dda21f39149..1a058588995 100644 --- a/code/modules/modular_computers/file_system/reports/crew_record.dm +++ b/code/modules/modular_computers/file_system/reports/crew_record.dm @@ -53,34 +53,131 @@ GLOBAL_VAR_INIT(arrest_security_status, "Arrest") // Medical record set_bloodtype(H ? H.b_type : "Unset") - set_medRecord((H && H.med_record && !jobban_isbanned(H, "Records") ? html_decode(H.med_record) : "No record supplied")) + var/datum/preferences/loadedprefs = H?.client?.prefs + var/datum/report_field/arrayclump/medRecord = get_linkage_medRecord() + if(H) + var/list/wounds = list() + var/list/prosthetics = list() + var/list/scannedlimbs = list() + for(var/datum/organ_description/OD in H.species.has_limbs) // default limbs for species + if(H.organs_by_name[OD.organ_tag]) + if(istype(H.organs_by_name[OD.organ_tag], OD.default_type)) // these lists contain abnormalilities, so normalities are skipped. + continue + else + var/obj/item/organ/external/organthing = H.organs_by_name[OD.organ_tag] + if(organthing.is_stump()) + wounds.Add("[organthing.name] instead of [OD.name]") + else + prosthetics.Add(organthing.name) + + scannedlimbs.Add(organthing) + // TODO: add organ checks once FBPs are better coded + else + wounds.Add("Missing [OD.name]") + var/list/addictions = list() + for(var/datum/reagent/addicted in H.metabolism_effects.addiction_list) + addictions.Add("Observed addicted to [addicted.name]") + + var/bodystate + switch(H.stat) + if(DEAD) + bodystate = list("Dead at time of writing") + if(UNCONSCIOUS) + if(H.getOxyLoss() > H.species.total_health/2) + bodystate = list("Comatose at time of writing") + else + bodystate = list("Alive at time of writing") + if(CONSCIOUS) + bodystate = list("Alive at time of writing") + + medRecord.value["wounds"] = wounds.len ? wounds : list("No wounds on record.") + medRecord.value["prosthetics"] = prosthetics.len ? prosthetics : list("No prosthetics on record.") + medRecord.value["Body state"] = bodystate ? bodystate : list("\[Data Missing\]") + medRecord.value["chemhistory"] = addictions.len ? addictions : list("Chemical record is clean.") + + var/list/psychological = list() + for(var/datum/perk/profile in H.stats.perks) + switch(profile.type) + if(PERK_NIHILIST) + psychological.Add("Has an empty outlook on life.") + if(PERK_MORALIST) + psychological.Add("High morale in groups.") + if(PERK_DRUG_ADDICT) + psychological.Add("Easily addicted, remains addicted indefinitely.") + if(PERK_ALCOHOLIC) + psychological.Add("Psychologically dependent on alcohol.") + if(PERK_REJECTED_GENIUS, PERK_NOBLE) + psychological.Add("Mentally destabilized by minor stressing factors.") + if(PERK_RAT) + psychological.Add("Innately stressed by environments, to some degree.") + if(PERK_PAPER_WORM) + psychological.Add("Accustomed to over-stress.") + if(PERK_OBORIN_SYNDROME) + psychological.Add("Innately resilient to mental harm.") + if(PERK_LOWBORN, PERK_VAGABOND) + psychological |= "Accustomed to bad living conditions." + if(PERK_SURVIVOR) + psychological.Add("Accustomed to the presence of death.") + if(PERK_NEAT) + psychological.Add("Accustomed to the presence of grime, and comforted by its removal.") + if(PERK_GREEN_THUMB) + psychological.Add("Comforted by gardening.") + if(PERK_CLUB) + psychological.Add("Provides a comfortingly professional presence.") + if(PERK_CHAINGUN_SMOKER) + psychological.Add("Invigorated by smoking.") + if(PERK_CHARMING_PERSONALITY) + psychological.Add("Provides a comfortingly charming presence.") + if(PERK_HORRIBLE_DEEDS) + psychological.Add("UNSETTLING.") + if(PERK_TERRIBLE_FATE) + psychological.Add("More afraid of death than usual.") + if(PERK_BALLS_OF_PLASTEEL) + psychological.Add("Higher pain tolerance than otherwise indicated.") + if(PERK_ARTIST) + psychological.Add("Looks at life as art.") + + medRecord.value["psychological"] = psychological.len ? psychological : list("No psychological abnormality at time of writing.") + else // if there isn't a human + var/datum/storedrecord/medical/default/medrecord = new() // just use the defaults + medrecord.transfertocomputer(medRecord) + qdel(medrecord) // Security record set_criminalStatus(GLOB.default_security_status) set_dna(H ? H.dna_trace : "") set_fingerprint(H ? H.fingers_trace : "") - set_secRecord(H && H.sec_record && !jobban_isbanned(H, "Records") ? html_decode(H.sec_record) : "No record supplied") - // Employment record - var/employment_record = "No record supplied" if(H) - if(H.gen_record && !jobban_isbanned(H, "Records")) - employment_record = html_decode(H.gen_record) - if(H.client && H.client.prefs) - var/list/qualifications - if(LAZYLEN(qualifications)) - employment_record = "[employment_record ? "[employment_record]\[br\]" : ""][jointext(qualifications, "\[br\]>")]" - set_emplRecord(employment_record) - - - if(H) - var/stats = list() - for(var/statName in ALL_STATS) - var/points = H.stats.getStat(statName,pure = TRUE) - if(points > STAT_LEVEL_NONE) - stats += "[statName]: [points] ([statPointsToLevel(points)])" + for(var/datum/perk/fate/profile in H.stats.perks) + switch(profile) + if(PERK_PAPER_WORM) + set_emplRecord("Experienced in bureacracy.") + if(PERK_FREELANCER) + set_emplRecord("Wide variety of employments.") + if(PERK_DRUG_ADDICT) + set_emplRecord("Has a history of drug addiction.") + if(PERK_ALCOHOLIC) + set_emplRecord("Is an alcoholic.") + if(PERK_NOBLE) + set_emplRecord("Is of noble origin.") + if(PERK_LOWBORN) + set_emplRecord("Was hired from poverty.") + if(!get_emplRecord()) + set_emplRecord("Standard background.") + else + set_emplRecord("No record supplied.") - set_skillset(jointext(stats,"\n")) + if(loadedprefs) + var/originfound + for(var/datum/category_group/setup_option_category/background/origin/OriginBG in loadedprefs.setup_options) + if(loadedprefs.loaded_character[OriginBG.name]) + originfound = OriginBG.name + break + if(originfound) + set_origin(originfound) + else + set_origin("Origin not on record.") // Antag record set_antagRecord(H && H.exploit_record && !jobban_isbanned(H, "Records") ? html_decode(H.exploit_record) : "") @@ -96,16 +193,16 @@ GLOBAL_VAR_INIT(arrest_security_status, "Arrest") /proc/SortModularRecords() // improved bubble sort - if(GLOB.all_crew_records.len > 1) - for(var/i = 1, i <= GLOB.all_crew_records.len, i++) - var/flag = FALSE - for(var/j = 1, j <= GLOB.all_crew_records.len - 1, j++) - var/datum/computer_file/report/crew_record/CR = GLOB.all_crew_records[j] - var/datum/computer_file/report/crew_record/CR_NEXT = GLOB.all_crew_records[j+1] - if(sorttext(CR.get_name(), CR_NEXT.get_name()) == -1) - flag = TRUE - GLOB.all_crew_records.Swap(j,j+1) - if(!flag) + if(GLOB.all_crew_records.len > 1) // if list is big enough to sort + for(var/i = 1, i <= GLOB.all_crew_records.len, i++) // for each entry in list + var/flag = FALSE // define a var as false + for(var/j = GLOB.all_crew_records.len-1, j >= 1, j--) // cycle through all entries, + var/datum/computer_file/report/crew_record/CR = GLOB.all_crew_records[j] // starting from the one before the last + var/datum/computer_file/report/crew_record/CR_NEXT = GLOB.all_crew_records[j+1] // and the last + if(sorttext(CR.get_name(), CR_NEXT.get_name()) == 1) // if the one before the last is bigger than the last + flag = TRUE // reset the var to true + GLOB.all_crew_records.Swap(j,j+1) // and swap the last and the one before the last + if(!flag) // if no work was done, stop break // Gets crew records filtered by set of positions @@ -161,9 +258,13 @@ GLOBAL_VAR_INIT(arrest_security_status, "Arrest") #define GETTER_SETTER(PATH, KEY) /datum/computer_file/report/crew_record/proc/get_##KEY(){var/datum/report_field/F = locate(/datum/report_field/##PATH/##KEY) in fields; if(F) return F.get_value()} \ /datum/computer_file/report/crew_record/proc/set_##KEY(given_value){var/datum/report_field/F = locate(/datum/report_field/##PATH/##KEY) in fields; if(F) F.set_value(given_value)} +#define SELF_GETTER(PATH, KEY) /datum/computer_file/report/crew_record/proc/get_linkage_##KEY(){return locate(/datum/report_field/##PATH/##KEY) in fields} #define SETUP_FIELD(NAME, KEY, PATH, ACCESS, ACCESS_EDIT) GETTER_SETTER(PATH, KEY); /datum/report_field/##PATH/##KEY;\ /datum/computer_file/report/crew_record/generate_fields(){..(); var/datum/report_field/##KEY = add_field(/datum/report_field/##PATH/##KEY, ##NAME);\ KEY.set_access(ACCESS, ACCESS_EDIT || ACCESS || access_heads)} +#define SETUP_ARRAYFIELD(NAME, KEY, PATH, ACCESS, ACCESS_EDIT) SELF_GETTER(PATH, KEY); /datum/report_field/##PATH/##KEY;\ +/datum/computer_file/report/crew_record/generate_fields(){..(); var/datum/report_field/##KEY = add_field(/datum/report_field/##PATH/##KEY, ##NAME);\ +KEY.set_access(ACCESS, ACCESS_EDIT || ACCESS || access_heads)} // Fear not the preprocessor, for it is a friend. To add a field, use one of these, depending on value type and if you need special access to see it. // It will also create getter/setter procs for record datum, named like /get_[key here]() /set_[key_here](value) e.g. get_name() set_name(value) @@ -174,6 +275,8 @@ KEY.set_access(ACCESS, ACCESS_EDIT || ACCESS || access_heads)} #define FIELD_LIST(NAME, KEY, OPTIONS, ACCESS, ACCESS_EDIT) FIELD_LIST_EDIT(NAME, KEY, OPTIONS, ACCESS, ACCESS_EDIT) #define FIELD_LIST_EDIT(NAME, KEY, OPTIONS, ACCESS, ACCESS_EDIT) SETUP_FIELD(NAME, KEY, options/crew_record, ACCESS, ACCESS_EDIT);\ /datum/report_field/options/crew_record/##KEY/get_options(){return OPTIONS} +#define ARRAYFIELD_SINGLE(NAME, KEY, ACCESS, ACCESS_EDIT) SETUP_ARRAYFIELD(NAME, KEY, array/crew_record, ACCESS, ACCESS_EDIT) +#define ARRAYFIELD_CLUMP(NAME, KEY, ACCESS, ACCESS_EDIT) SETUP_ARRAYFIELD(NAME, KEY, arrayclump/crew_record, ACCESS, ACCESS_EDIT) // GENERIC RECORDS FIELD_SHORT("Name", name, null, access_change_ids) @@ -189,17 +292,17 @@ FIELD_NUM("Account",account, null, access_change_ids) // MEDICAL RECORDS FIELD_LIST("Blood Type", bloodtype, GLOB.blood_types, access_moebius, access_moebius) -FIELD_LONG("Medical Record", medRecord, access_moebius, access_moebius) +ARRAYFIELD_CLUMP("Medical Record", medRecord, access_moebius, access_moebius) // SECURITY RECORDS FIELD_LIST("Criminal Status", criminalStatus, GLOB.security_statuses, access_security, access_security) -FIELD_LONG("Security Record", secRecord, access_security, access_security) +ARRAYFIELD_SINGLE("Brief", secNotes, access_security, access_security) FIELD_SHORT("DNA", dna, access_security, access_security) FIELD_SHORT("Fingerprint", fingerprint, access_security, access_security) // EMPLOYMENT RECORDS FIELD_LONG("Employment Record", emplRecord, access_heads, access_heads) -FIELD_LONG("Qualifications", skillset, access_heads, access_heads) +FIELD_LONG("Place of origin", origin, access_heads, access_heads) // ANTAG RECORDS FIELD_LONG("Exploitable Information", antagRecord, access_syndicate, access_syndicate) @@ -240,9 +343,13 @@ FIELD_LONG("Exploitable Information", antagRecord, access_syndicate, access_synd . |= BR.name */ #undef GETTER_SETTER +#undef SELF_GETTER #undef SETUP_FIELD +#undef SETUP_ARRAYFIELD #undef FIELD_SHORT #undef FIELD_LONG #undef FIELD_NUM #undef FIELD_LIST #undef FIELD_LIST_EDIT +#undef ARRAYFIELD_SINGLE +#undef ARRAYFIELD_CLUMP diff --git a/code/modules/modular_computers/file_system/reports/report.dm b/code/modules/modular_computers/file_system/reports/report.dm index 9df7d118d09..739c004893f 100644 --- a/code/modules/modular_computers/file_system/reports/report.dm +++ b/code/modules/modular_computers/file_system/reports/report.dm @@ -133,7 +133,22 @@ If the override option is set to 0, the access supplied will instead be added as dat["access"] = field.verify_access(given_access) dat["access_edit"] = field.verify_access_edit(given_access) dat["name"] = field.display_name() - dat["value"] = field.get_value() + + if(istype(field, /datum/report_field/array)) + var/datum/report_field/array/arrayfield = field + dat["list_value"] = arrayfield.value_list.Copy() + dat["list_clumps"] = null // must override with null values or else react gets weird + dat["value"] = null + else if(istype(field, /datum/report_field/arrayclump)) + var/datum/report_field/arrayclump/clumpfield = field + dat["list_value"] = null + dat["list_clumps"] = clumpfield.value + dat["value"] = null + else + dat["list_value"] = null + dat["list_clumps"] = null + dat["value"] = field.get_value() + dat["can_edit"] = field.can_edit dat["needs_big_box"] = field.needs_big_box dat["ignore_value"] = field.ignore_value diff --git a/code/modules/modular_computers/file_system/reports/report_field.dm b/code/modules/modular_computers/file_system/reports/report_field.dm index b36c199e1d9..df2e78a1327 100644 --- a/code/modules/modular_computers/file_system/reports/report_field.dm +++ b/code/modules/modular_computers/file_system/reports/report_field.dm @@ -211,4 +211,151 @@ Basic field subtypes. value_list.Remove(given_value) /datum/report_field/array/ask_value(mob/user) - add_value(input(user, "Add value", "") as null|text) + var/inputchoice = input(user, "Add or Remove value", "Field Edit", "Add") in list("Add", "Remove") + if(inputchoice == "Add") + var/toadd = input(user, "Add value", "Field Input") as null|text + if(!isnull(toadd)) + add_value(toadd) + else if(inputchoice == "Remove") + remove_value(input(user, "Choose value to remove", "Field Reduce") as null|anything in value_list ) + + + +/datum/report_field/arraylinkage + var/list/arrays = list() + ignore_value = TRUE // value is too generic to use + +// retrieves index of key +// can retrieve additional entries of same index using additionalarrays param +/datum/report_field/arraylinkage/proc/retrieve_index_of_array(key, index = 0, additionalarrays = list()) + if(!(key && (arrays.len > 0) && index > 0)) // can we access? + return FALSE + var/primeretrieved = arrays[key] // key's array + if(!(index && primeretrieved && length(primeretrieved) >= index)) // can we access the index asked for using the key? + return FALSE + var/list/toreturn = list(primeretrieved[index]) + for(var/arrayexists in additionalarrays) // all linked arrays we also want index of + if((arrayexists in arrays) && length(arrays[arrayexists]) >= index) + var/list/toadd = arrays[arrayexists] + toreturn.Add(toadd[index]) + else + return FALSE // we really don't want input from bad sources. + . = toreturn // succeeded all the checks? caller's will be done. + +// adds one index to the end of each list +// sets the contents of each list for the created index on demand using either keys or arrangement of any given input of proper size +/datum/report_field/arraylinkage/proc/add_index(list/new_entries, keyed = FALSE) + if(!new_entries || new_entries.len > arrays.len) + return FALSE + if(new_entries.len != arrays.len) // only accept using list-order based indexing when it has a 1 to 1 ratio + keyed = TRUE + var/count = 1 + if(keyed) + for(var/key in arrays) // unfitting keys are discarded, but can still disqualify a list. + var/list/currentarray = arrays[key] + if(new_entries[key]) + currentarray.Add(new_entries[key]) + else + currentarray.Add(null) // all arrays must have the same indexing for the linkage to work. + . = TRUE + else + for(var/entry in new_entries) // sanitize before it's t oo late + if(new_entries[entry]) + CRASH("keyed outside of key mode in add_index") + for(var/entry in new_entries) + var/list/currentarray = arrays?[count] + if(!currentarray) // in case a check breaks + return + count ++ + currentarray.Add(entry) + . = TRUE + +// set one entry in one list +/datum/report_field/arraylinkage/proc/edit_index(key, setto, index) + if(!arrays[key] || length(arrays[key]) < index || !islist(arrays[key])) + return FALSE + var/list/accessed = arrays[key] + if(accessed) + accessed[index] = setto + . = TRUE + +// delete one index of all lists in arraylinkage +/datum/report_field/arraylinkage/proc/remove_index(index) + if(!length(arrays) || length(arrays[1]) < index) // check if there are arrays and the first one is long enough + return FALSE // hopefully all arrays have the same length as the first one + for(var/list/arraytocut in arrays) + arraytocut.Cut(index, index+1) + . = TRUE + +/datum/report_field/arraylinkage/ask_value(mob/user) + +/datum/report_field/arrayclump // these arrays are explicitly unlinked and are stored here for categorization + value = list() // only input key=array as entry + +/datum/report_field/arrayclump/set_value(given_value) + if(!islist(given_value)) + return FALSE + . = ..() + +/datum/report_field/arrayclump/ask_value(mob/user) + . = TRUE + var/list/clumpvalue = value + if(!istype(clumpvalue)) + return FALSE + var/list/entries = clumpvalue.Copy() + entries.Add("") + + var/entrychoice = input(user, "Choose Entry Category", "Entry Category") as null | anything in entries + if(entrychoice == "" ) + var/entryname = input(user, "Choose Entry Name", "Entry Name") as text | null + entryname = sanitizeSafe(entryname) + if(!istext(entryname)) + to_chat(user, SPAN_NOTICE("Edit canceled successfully.")) + return FALSE + if(clumpvalue[entryname]) // only replace entry with alert + if(alert(user, "Replace old entry?", "Entry Replacement", "Yes", "No") == "No") + to_chat(user, SPAN_NOTICE("Edit canceled successfully.")) + return FALSE + var/startingvalue = input(user, "Create Entry", "Entry Creation") as text | null + startingvalue = sanitizeSafe(startingvalue) + if(!istext(startingvalue)) + to_chat(user, SPAN_NOTICE("Edit canceled successfully.")) + return FALSE + to_chat(user, SPAN_NOTICE("You have successfully added an entry to [name].")) + value[entryname] = list(startingvalue) + + else if(entrychoice) + var/list/selections = clumpvalue[entrychoice] + selections = selections.Copy() // don't edit the original list + selections.Add("") + var/selectionchoice = input(user, "Select Entry", "Entry") as null | anything in selections + if(!istext(selectionchoice)) + return FALSE + if(selectionchoice == "") + var/nextvalue = input(user, "Add Value", "Value") as text | null + nextvalue = sanitizeSafe(nextvalue) + if(!istext(nextvalue)) + to_chat(user, SPAN_NOTICE("Addition canceled successfully.")) + return FALSE + else + var/list/entrytoaddto = clumpvalue[entrychoice] + if(istype(entrytoaddto)) + entrytoaddto.Add(nextvalue) + else + CRASH("Type Mismatch in clump list") + + else + var/newvalue = input(user, "New Value", "Value") as text | null + newvalue = sanitizeSafe(newvalue) + if(!istext(newvalue)) + to_chat(user, SPAN_NOTICE("Replacement canceled successfully.")) + return FALSE + var/list/intermediaryvalue = clumpvalue[entrychoice] + if(istype(intermediaryvalue)) + var/indexhound = clumpvalue.Find(intermediaryvalue) // the Hound finds the value we splice + intermediaryvalue.Splice(indexhound, indexhound+1, newvalue) + else + CRASH("Type Mismatch in clump list") + + + diff --git a/code/modules/nano/modules/tgui_type.dm b/code/modules/nano/modules/tgui_type.dm new file mode 100644 index 00000000000..e605715301b --- /dev/null +++ b/code/modules/nano/modules/tgui_type.dm @@ -0,0 +1,92 @@ +/datum/nano_module/tgui + var/datum/tgui/currentui // gotta save it, we can't get it any other way + var/show_map ; var/map_z_level // the sins of our predecessors, brought to the present by the sins of our own + +/datum/nano_module/tgui/ui_host(mob/user) + return nano_host() + +/datum/nano_module/tgui/ui_close(mob/user) + . = ..() + currentui = null // must clear reference to ui + +/datum/nano_module/tgui/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/spritesheet/legacyicons) + ) + + +/datum/nano_module/tgui/ui_act(action, params) + . = ..() + + if(istype(host, /datum/computer_file/program)) + var/datum/computer_file/program/program = host + switch(action) + if("PC_exit") + program.computer.kill_program() + return TRUE + if("PC_enable_component") + var/obj/item/computer_hardware/H = program.computer.find_hardware_by_name(params["component"]) + if(istype(H) && !H.enabled) + H.enabled = TRUE + H.enabled() + . = TRUE + if("PC_disable_component") + var/obj/item/computer_hardware/H = program.computer.find_hardware_by_name(params["component"]) + if(istype(H) && H.enabled) + H.enabled = FALSE + H.disabled() + . = TRUE + if("PC_toggle_component") + var/obj/item/computer_hardware/H = program.computer.find_hardware_by_name(params["component"]) + if(istype(H)) + H.enabled = !H.enabled + if(H.enabled) + H.enabled() + else + H.disabled() + . = TRUE + if("PC_shutdown") + program.computer.shutdown_computer() + return TRUE + if("PC_minimize") + var/mob/user = usr + program.computer.minimize_program(user) + + if("PC_killprogram") + var/prog_name = params["PC_killprogram"] + var/obj/item/computer_hardware/hard_drive/prog_disk = locate(params["disk"]) in program.computer + if(!prog_disk) + return TRUE + + for(var/p in program.computer.all_threads) + var/datum/computer_file/program/PRG = p + if(PRG.program_state == PROGRAM_STATE_KILLED) + continue + + if(PRG.filename == prog_name && (PRG in prog_disk.stored_files)) + PRG.kill_program(forced=TRUE) + to_chat(usr, SPAN_NOTICE("Program [PRG.filename].[PRG.filetype] has been killed.")) + . = TRUE + + if("PC_runprogram") + var/obj/item/computer_hardware/hard_drive/prog_disk = locate(params["disk"]) in program.computer + return program.computer.run_program(params["PC_runprogram"], prog_disk) + + if("PC_setautorun") + if(!program.computer.hard_drive) + return + program.computer.set_autorun(params["PC_setautorun"]) + if("PC_terminal") + program.computer.open_terminal(usr) + return TRUE + +/datum/nano_module/tgui/ui_status(mob/user, datum/ui_state/state) + . = ..(user, state ? state : GLOB.default_state) + if(. > UI_DISABLED && istype(host, /datum/computer_file/program)) + var/datum/computer_file/program/relevant = host + if(relevant.program_state == PROGRAM_STATE_KILLED) + . = UI_CLOSE + else if(relevant.program_state == PROGRAM_STATE_BACKGROUND) + . = min(., UI_UPDATE) // theoretically might be necessary with a new system, if redundant just collapse this with the killing check. + + diff --git a/code/modules/paperwork/filingcabinet.dm b/code/modules/paperwork/filingcabinet.dm index db805e53355..481428103a6 100644 --- a/code/modules/paperwork/filingcabinet.dm +++ b/code/modules/paperwork/filingcabinet.dm @@ -107,22 +107,12 @@ /obj/structure/filingcabinet/security/populate() if(virgin) - for(var/datum/data/record/G in data_core.general) - var/datum/data/record/S - for(var/datum/data/record/R in data_core.security) - if((R.fields["name"] == G.fields["name"] || R.fields["id"] == G.fields["id"])) - S = R - break + for(var/datum/computer_file/report/crew_record/G in GLOB.all_crew_records) var/obj/item/paper/P = new /obj/item/paper(src) P.info = "
Security Record

" - P.info += "Name: [G.fields["name"]] ID: [G.fields["id"]]
\nSex: [G.fields["sex"]]
\nAge: [G.fields["age"]]
\nFingerprint: [G.fields["fingerprint"]]
\nPhysical Status: [G.fields["p_stat"]]
\nMental Status: [G.fields["m_stat"]]
" - P.info += "
\n
Security Data

\nCriminal Status: [S.fields["criminal"]]
\n
\nMinor Crimes: [S.fields["mi_crim"]]
\nDetails: [S.fields["mi_crim_d"]]
\n
\nMajor Crimes: [S.fields["ma_crim"]]
\nDetails: [S.fields["ma_crim_d"]]
\n
\nImportant Notes:
\n\t[S.fields["notes"]]
\n
\n
Comments/Log

" - var/counter = 1 - while(S.fields["com_[counter]"]) - P.info += "[S.fields["com_[counter]"]]
" - counter++ + P.info += "Name: [G.get_name()]
\nSex: [G.get_sex()]
\nAge: [G.get_age()]
\nFingerprint: [G.get_fingerprint()]
" P.info += "" - P.name = "Security Record ([G.fields["name"]])" + P.name = "Security Record ([G.get_name()])" virgin = 0 //tabbing here is correct- it's possible for people to try and use it //before the records have been generated, so we do this inside the loop. @@ -142,24 +132,21 @@ /obj/structure/filingcabinet/medical/populate() if(virgin) - for(var/datum/data/record/G in data_core.general) - var/datum/data/record/M - for(var/datum/data/record/R in data_core.medical) - if((R.fields["name"] == G.fields["name"] || R.fields["id"] == G.fields["id"])) - M = R - break + for(var/datum/computer_file/report/crew_record/G in GLOB.all_crew_records) + var/datum/report_field/arrayclump/M = G.get_linkage_medRecord() if(M) var/obj/item/paper/P = new /obj/item/paper(src) - P.info = "
Medical Record

" - P.info += "Name: [G.fields["name"]] ID: [G.fields["id"]]
\nSex: [G.fields["sex"]]
\nAge: [G.fields["age"]]
\nFingerprint: [G.fields["fingerprint"]]
\nPhysical Status: [G.fields["p_stat"]]
\nMental Status: [G.fields["m_stat"]]
" - - P.info += "
\n
Medical Data

\nBlood Type: [M.fields["b_type"]]
\nDNA: [M.fields["b_dna"]]
\n
\nMinor Disabilities: [M.fields["mi_dis"]]
\nDetails: [M.fields["mi_dis_d"]]
\n
\nMajor Disabilities: [M.fields["ma_dis"]]
\nDetails: [M.fields["ma_dis_d"]]
\n
\nAllergies: [M.fields["alg"]]
\nDetails: [M.fields["alg_d"]]
\n
\nCurrent Diseases: [M.fields["cdi"]] (per disease info placed in log/comment section)
\nDetails: [M.fields["cdi_d"]]
\n
\nImportant Notes:
\n\t[M.fields["notes"]]
\n
\n
Comments/Log

" - var/counter = 1 - while(M.fields["com_[counter]"]) - P.info += "[M.fields["com_[counter]"]]
" - counter++ - P.info += "" - P.name = "Medical Record ([G.fields["name"]])" + var/list/infostoadd = list() + infostoadd += "
Medical Record

" + infostoadd += "Name: [G.get_name()]
\nSex: [G.get_sex()]
\nAge: [G.get_age()]
" + + infostoadd += "
\n
Medical Data

\nBlood Type: [G.get_bloodtype()]
\nDNA: [G.get_dna()]
" + infostoadd += "
\nProsthetics: [M.value["prosthetics"]]
\nWounds: [M.value["wounds"]]
\n
\nAutopsy: [M.value["Body state"]]
" + infostoadd +="
\nChemical History: [M.value["chemhistory"]]
\nPsychological Profile: [M.value["psychological"]]
" + + infostoadd += "" + P.name = "Medical Record ([G.get_name()])" + P.info = infostoadd.Join() // minor optimization virgin = 0 //tabbing here is correct- it's possible for people to try and use it //before the records have been generated, so we do this inside the loop. diff --git a/code/modules/research/designs/circuits.dm b/code/modules/research/designs/circuits.dm index f5c38fe0f0e..d424223f6d9 100644 --- a/code/modules/research/designs/circuits.dm +++ b/code/modules/research/designs/circuits.dm @@ -36,11 +36,6 @@ sort_string = "MABBA" category = CAT_MISC -/datum/design/research/circuit/secdata - name = "security records console" - build_path = /obj/item/electronics/circuitboard/secure_data - sort_string = "DABAA" - category = CAT_COMP /datum/design/research/circuit/prisonmanage name = "prisoner management console" @@ -48,11 +43,6 @@ sort_string = "DACAA" category = CAT_COMP -/datum/design/research/circuit/med_data - name = "medical records console" - build_path = /obj/item/electronics/circuitboard/med_data - sort_string = "FAAAA" - category = CAT_COMP /datum/design/research/circuit/operating name = "patient monitoring console" diff --git a/code/modules/research/nodes/biotech.dm b/code/modules/research/nodes/biotech.dm index 609699af18e..b0c65c29102 100644 --- a/code/modules/research/nodes/biotech.dm +++ b/code/modules/research/nodes/biotech.dm @@ -17,7 +17,7 @@ /datum/technology/basic_med_machines name = "Basic Medical Machines" - desc = "Basic medical databases and surgical monitoring." + desc = "Basic surgical monitoring." tech_type = RESEARCH_BIOTECH x = 0.25 @@ -28,9 +28,7 @@ required_tech_levels = list() cost = 200 - unlocks_designs = list( /datum/design/research/circuit/med_data, - /datum/design/research/circuit/operating - ) + unlocks_designs = list(/datum/design/research/circuit/operating) /* /datum/technology/virology name = "Virology" diff --git a/icons/ui_icons/dmis/uiicons16.dmi b/icons/ui_icons/dmis/uiicons16.dmi new file mode 100644 index 0000000000000000000000000000000000000000..7b5d90ace41bbb78e4f6fad05779eb75da5deccc GIT binary patch literal 6642 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 literal 0 HcmV?d00001 diff --git a/maps/main_ship/eris_classic.dmm b/maps/main_ship/eris_classic.dmm index 575d9db0a60..f9f6358a91a 100644 --- a/maps/main_ship/eris_classic.dmm +++ b/maps/main_ship/eris_classic.dmm @@ -102856,6 +102856,14 @@ }, /turf/floor/plating/under, /area/eris/maintenance/sorter) +"qmi" = ( +/obj/item/cell/medium, +/obj/item/cell/medium, +/obj/item/computer_hardware/processor_unit, +/obj/item/computer_hardware/tesla_link, +/obj/item/computer_hardware/tesla_link, +/turf/floor/hull, +/area/space) "qmJ" = ( /obj/machinery/door/blast/shutters{ dir = 4; @@ -104952,6 +104960,13 @@ }, /turf/floor/plating/under, /area/eris/maintenance/section3deck5port) +"tZv" = ( +/obj/item/computer_hardware/network_card/wired, +/obj/item/computer_hardware/network_card/wired, +/obj/item/computer_hardware/scanner/paper, +/obj/item/computer_hardware/scanner/paper, +/turf/floor/hull, +/area/space) "tZw" = ( /obj/structure/cable/green{ d1 = 4; @@ -205242,7 +205257,7 @@ iEk cBO cwU abF -abF +qmi abF cwU cwU @@ -205444,7 +205459,7 @@ cBO cos cwU abF -abF +tZv abF abF cwU diff --git a/nano/templates/crew_records.tmpl b/nano/templates/crew_records.tmpl index 543879ebe0d..009496403e1 100644 --- a/nano/templates/crew_records.tmpl +++ b/nano/templates/crew_records.tmpl @@ -28,6 +28,27 @@ {{if value.needs_big_box}}
{{:value.value}} + {{else}} + {{if value.list_value}} + {{for value.list_value}} + {{:value}} + {{/for}} + {{else}} + {{if value.list_clumps}} + {{for value.list_clumps}} + {{for value}} + {{:value}} + {{/for}} + {{/for}} + {{else}} + {{if value.links}} + {{for 1 to value.links.len}} + {{for value.links}} + + {{/if}} + {{/if}} + {{/if}} + {{/if}}
{{else}}
{{:value.value}}
diff --git a/nano/templates/pai_medrecords.tmpl b/nano/templates/pai_medrecords.tmpl index 37b1d7061ca..b45d66cd2c9 100644 --- a/nano/templates/pai_medrecords.tmpl +++ b/nano/templates/pai_medrecords.tmpl @@ -15,10 +15,6 @@ code/modules/mob/living/silicon/pai/software_modules.dm
Name
{{:data.general.name}}
-
-
Record ID
-
{{:data.general.id}}
-
Sex
{{:data.general.sex}}
@@ -32,50 +28,52 @@ code/modules/mob/living/silicon/pai/software_modules.dm
{{:data.general.age}}
-
Rank
-
{{:data.general.rank}}
+
Job Title
+
{{:data.general.job}}
Fingerprint
{{:data.general.fingerprint}}
-
Physical Status
-
{{:data.general.p_stat}}
+
=Status
+
{{:data.general.status}}
-
Mental Status
-
{{:data.general.m_stat}}
+
Blood Type
+
{{:data.general.bloodtype}}
{{/if}} {{if data.medical}}
-
Blood Type
-
{{:data.medical.b_type}}
-
-
-
Minor Disabilities
-
{{:data.medical.mi_dis}}
-
{{:data.medical.mi_dis_d}}
+
Prosthetics
+ {{for data.medical.value.prosthetics}} +
{{:value}}
+ {{/for}}
-
Major Disabilities
-
{{:data.medical.ma_dis}}
-
{{:data.medical.ma_dis_d}}
+
Wounds
+ {{for data.medical.value.wounds}} +
{{:value}}
+ {{/for}}
-
Allergies
-
{{:data.medical.alg}}
-
{{:data.medical.alg_d}}
+
Body State
+ {{for data.medical.value.body state}} +
{{:value}}
+ {{/for}}
-
Current Diseases
-
{{:data.medical.cdi}}
-
{{:data.medical.cdi_d}}
+
Chemical History
+ {{for data.medical.value.chemhistory}} +
{{:value}}
+ {{/for}}
-
Important Notes
-
{{:data.medical.notes}}
+
Psychological Profile
+ {{for data.medical.value.psychological}} +
{{:value}}
+ {{/for}}
{{/if}} diff --git a/nano/templates/pai_secrecords.tmpl b/nano/templates/pai_secrecords.tmpl index e139034c0de..cdb3321a07c 100644 --- a/nano/templates/pai_secrecords.tmpl +++ b/nano/templates/pai_secrecords.tmpl @@ -15,10 +15,6 @@ code/modules/mob/living/silicon/pai/software_modules.dm
Name
{{:data.general.name}}
-
-
Record ID
-
{{:data.general.id}}
-
Sex
{{:data.general.sex}}
@@ -32,40 +28,24 @@ code/modules/mob/living/silicon/pai/software_modules.dm
{{:data.general.age}}
-
Rank
-
{{:data.general.rank}}
+
Job Title
+
{{:data.general.job}}
Fingerprint
{{:data.general.fingerprint}}
-
Physical Status
-
{{:data.general.p_stat}}
+
Status
+
{{:data.general.status}}
-
-
Mental Status
-
{{:data.general.m_stat}}
-
-{{/if}} -{{if data.security}}
Criminal Status
-
{{:data.security.criminal}}
-
-
-
Minor Crimes
-
{{:data.security.mi_crim}}
-
{{:data.security.mi_crim_d}}
-
-
-
Major Crimes
-
{{:data.security.ma_crim}}
-
{{:data.security.ma_crim_d}}
-
+
{{:data.general.criminalStatus }}
+
Important Notes
-
{{:data.security.notes}}
+
{{:data.general.secNotes}}
{{/if}} diff --git a/tgui/packages/tgui/interfaces/Computer.tsx b/tgui/packages/tgui/interfaces/Computer.tsx new file mode 100644 index 00000000000..cd4fa1f534f --- /dev/null +++ b/tgui/packages/tgui/interfaces/Computer.tsx @@ -0,0 +1,169 @@ +import { BooleanLike } from 'common/react'; +import { Button, Box, Table } from '../components'; +import { TableCell, TableRow } from '../components/Table'; +import { sendAct } from '../backend'; + +export interface ComputerInterface { + PC_batteryicon: string; + PC_batterypercent: string; + PC_showbatteryicon: BooleanLike; + PC_light_name: string; + PC_light_on: BooleanLike; + PC_apclinkicon: string; + PC_ntneticon: string; + has_gps: BooleanLike; + gps_icon: string; + gps_data: string; + PC_programheaders: Programheader[]; + PC_stationtime: string; + PC_hasheader: BooleanLike; + PC_showexitprogram: BooleanLike; + mapZLevels: number[]; + mapZLevel: number; +} + +type Programheader = { + icon: string; +}; + +export const ProgramShell = (props, context) => { + const { + PC_batteryicon, + PC_batterypercent, + PC_showbatteryicon, + PC_light_name, + PC_light_on, + PC_apclinkicon, + PC_ntneticon, + has_gps, + gps_icon, + gps_data, + PC_programheaders, + PC_stationtime, + PC_hasheader, + PC_showexitprogram, + mapZLevels, + mapZLevel, + } = props; + const act = sendAct; + return ( + <> + + { + // Add a template with the key "headerContent" to have it rendered here --> + } + {PC_hasheader} + + + + + + {PC_batteryicon && PC_showbatteryicon && ( + + + + )} + {PC_batterypercent && PC_showbatteryicon && ( + + {PC_batterypercent} + + )} + {PC_ntneticon && ( + + + + )} + {PC_apclinkicon && ( + + + + )} + {PC_stationtime && ( + + {PC_stationtime} + + )} + {PC_programheaders && + PC_programheaders.map((mapped) => ( + + + + ))} + +
+
+
+ + + + {PC_light_name && ( + +
+
+ + {has_gps && ( + + + + + + + {gps_data} + +
+
+ )} +
+
+ + + + {PC_showexitprogram && ( + + + + +
+
+ )} + {PC_showexitprogram || } + + + Initiating... + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/CrewRecords.tsx b/tgui/packages/tgui/interfaces/CrewRecords.tsx new file mode 100644 index 00000000000..3cb3f0bc944 --- /dev/null +++ b/tgui/packages/tgui/interfaces/CrewRecords.tsx @@ -0,0 +1,332 @@ +import { BooleanLike } from 'common/react'; +import { useBackend, sendAct } from '../backend'; +import { Button, Box, Divider, Table } from '../components'; +import { GameIcon } from '../components/GameIcon'; +import { Window } from '../layouts'; +import { ComputerInterface, ProgramShell } from './Computer'; +import { classes } from 'common/react'; + +interface CrewRecordsInterface extends RecordConcrete, ComputerInterface { + message: string; + front_pic: string; + side_pic: string; + pic_edit: BooleanLike; + all_records: RecordAbstract[]; + creation: BooleanLike; + dnasearch: BooleanLike; + fingersearch: BooleanLike; +} + +interface RecordConcrete { + name: string; + uid: number; + creator: string; + filetime: string; + fields: RecordField[]; + access: BooleanLike; + access_edit: BooleanLike; +} +type RecordField = { + access: BooleanLike; + access_edit: BooleanLike; + name: string; + value: string | number | object; + list_value: any[]; + list_clumps: object[]; + can_edit: BooleanLike; + needs_big_box: BooleanLike; + ignore_value: BooleanLike; + ID: number; +}; + +type RecordAbstract = { + name: string; + rank: string; + id: number; +}; + +export const CrewRecords = (props, context) => { + const { act, data } = useBackend(context); + const { + message, + uid, + fields, + front_pic, + side_pic, + pic_edit, + all_records, + creation, + dnasearch, + fingersearch, + } = data; + return ( + + + {ProgramShell(props, context)} + {message &&