diff --git a/baystation12.dme b/baystation12.dme
index aedb30841e3..3f02c6ad164 100644
--- a/baystation12.dme
+++ b/baystation12.dme
@@ -48,6 +48,8 @@
#include "code\_onclick\hud\other_mobs.dm"
#include "code\_onclick\hud\robot.dm"
#include "code\_onclick\hud\screen_objects.dm"
+#include "code\ATMOSPHERICS\_atmos_setup.dm"
+#include "code\ATMOSPHERICS\_atmospherics_helpers.dm"
#include "code\ATMOSPHERICS\atmospherics.dm"
#include "code\ATMOSPHERICS\datum_pipe_network.dm"
#include "code\ATMOSPHERICS\datum_pipeline.dm"
@@ -82,6 +84,7 @@
#include "code\ATMOSPHERICS\components\unary\vent_scrubber.dm"
#include "code\controllers\_DynamicAreaLighting_TG.dm"
#include "code\controllers\autotransfer.dm"
+#include "code\controllers\communications.dm"
#include "code\controllers\configuration.dm"
#include "code\controllers\emergency_shuttle_controller.dm"
#include "code\controllers\failsafe.dm"
@@ -173,7 +176,6 @@
#include "code\game\asteroid.dm"
#include "code\game\atoms.dm"
#include "code\game\atoms_movable.dm"
-#include "code\game\communications.dm"
#include "code\game\response_team.dm"
#include "code\game\shuttle_engines.dm"
#include "code\game\skincmd.dm"
@@ -205,6 +207,7 @@
#include "code\game\gamemodes\blob\blobs\factory.dm"
#include "code\game\gamemodes\blob\blobs\node.dm"
#include "code\game\gamemodes\blob\blobs\shield.dm"
+#include "code\game\gamemodes\calamity\calamity.dm"
#include "code\game\gamemodes\changeling\changeling.dm"
#include "code\game\gamemodes\changeling\changeling_powers.dm"
#include "code\game\gamemodes\changeling\modularchangling.dm"
@@ -297,7 +300,6 @@
#include "code\game\machinery\doppler_array.dm"
#include "code\game\machinery\flasher.dm"
#include "code\game\machinery\floodlight.dm"
-#include "code\game\machinery\Freezer.dm"
#include "code\game\machinery\hologram.dm"
#include "code\game\machinery\holosign.dm"
#include "code\game\machinery\igniter.dm"
@@ -310,14 +312,12 @@
#include "code\game\machinery\newscaster.dm"
#include "code\game\machinery\OpTable.dm"
#include "code\game\machinery\overview.dm"
-#include "code\game\machinery\podmen.dm"
#include "code\game\machinery\portable_turret.dm"
#include "code\game\machinery\recharger.dm"
#include "code\game\machinery\rechargestation.dm"
#include "code\game\machinery\requests_console.dm"
#include "code\game\machinery\robot_fabricator.dm"
#include "code\game\machinery\seed_extractor.dm"
-#include "code\game\machinery\shieldgen.dm"
#include "code\game\machinery\Sleeper.dm"
#include "code\game\machinery\spaceheater.dm"
#include "code\game\machinery\status_display.dm"
@@ -334,7 +334,6 @@
#include "code\game\machinery\atmoalter\portable_atmospherics.dm"
#include "code\game\machinery\atmoalter\pump.dm"
#include "code\game\machinery\atmoalter\scrubber.dm"
-#include "code\game\machinery\atmoalter\zvent.dm"
#include "code\game\machinery\bots\bots.dm"
#include "code\game\machinery\bots\cleanbot.dm"
#include "code\game\machinery\bots\ed209bot.dm"
@@ -366,7 +365,6 @@
#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\power.dm"
#include "code\game\machinery\computer\prisoner.dm"
#include "code\game\machinery\computer\prisonshuttle.dm"
#include "code\game\machinery\computer\robot.dm"
@@ -415,6 +413,7 @@
#include "code\game\magic\Uristrunes.dm"
#include "code\game\mecha\mech_bay.dm"
#include "code\game\mecha\mech_fabricator.dm"
+#include "code\game\mecha\mech_sensor.dm"
#include "code\game\mecha\mecha.dm"
#include "code\game\mecha\mecha_construction_paths.dm"
#include "code\game\mecha\mecha_control_console.dm"
@@ -485,6 +484,7 @@
#include "code\game\objects\items\devices\debugger.dm"
#include "code\game\objects\items\devices\flash.dm"
#include "code\game\objects\items\devices\flashlight.dm"
+#include "code\game\objects\items\devices\floor_painter.dm"
#include "code\game\objects\items\devices\lightreplacer.dm"
#include "code\game\objects\items\devices\megaphone.dm"
#include "code\game\objects\items\devices\modkit.dm"
@@ -746,6 +746,7 @@
#include "code\modules\client\client defines.dm"
#include "code\modules\client\client procs.dm"
#include "code\modules\client\preferences.dm"
+#include "code\modules\client\preferences_factions.dm"
#include "code\modules\client\preferences_gear.dm"
#include "code\modules\client\preferences_savefile.dm"
#include "code\modules\client\preferences_spawnpoints.dm"
@@ -803,10 +804,11 @@
#include "code\modules\customitems\item_defines.dm"
#include "code\modules\customitems\item_spawning.dm"
#include "code\modules\destilery\main.dm"
-#include "code\modules\detectivework\detective_work.dm"
#include "code\modules\detectivework\evidence.dm"
#include "code\modules\detectivework\footprints_and_rag.dm"
+#include "code\modules\detectivework\forensics.dm"
#include "code\modules\detectivework\scanner.dm"
+#include "code\modules\detectivework\scanning_console.dm"
#include "code\modules\economy\Accounts.dm"
#include "code\modules\economy\Accounts_DB.dm"
#include "code\modules\economy\ATM.dm"
@@ -857,7 +859,11 @@
#include "code\modules\hydroponics\grown_inedible.dm"
#include "code\modules\hydroponics\hydro_tools.dm"
#include "code\modules\hydroponics\hydro_tray.dm"
+#include "code\modules\hydroponics\seed_datums.dm"
+#include "code\modules\hydroponics\seed_machines.dm"
+#include "code\modules\hydroponics\seed_mobs.dm"
#include "code\modules\hydroponics\seeds.dm"
+#include "code\modules\hydroponics\vines.dm"
#include "code\modules\library\lib_items.dm"
#include "code\modules\library\lib_machines.dm"
#include "code\modules\library\lib_readme.dm"
@@ -982,6 +988,7 @@
#include "code\modules\mob\living\carbon\metroid\login.dm"
#include "code\modules\mob\living\carbon\metroid\metroid.dm"
#include "code\modules\mob\living\carbon\metroid\powers.dm"
+#include "code\modules\mob\living\carbon\metroid\say.dm"
#include "code\modules\mob\living\carbon\metroid\subtypes.dm"
#include "code\modules\mob\living\carbon\metroid\update_icons.dm"
#include "code\modules\mob\living\carbon\monkey\death.dm"
@@ -1032,6 +1039,7 @@
#include "code\modules\mob\living\silicon\robot\laws.dm"
#include "code\modules\mob\living\silicon\robot\life.dm"
#include "code\modules\mob\living\silicon\robot\login.dm"
+#include "code\modules\mob\living\silicon\robot\photos.dm"
#include "code\modules\mob\living\silicon\robot\robot.dm"
#include "code\modules\mob\living\silicon\robot\robot_damage.dm"
#include "code\modules\mob\living\silicon\robot\robot_items.dm"
@@ -1090,6 +1098,7 @@
#include "code\modules\nano\JSON Writer.dm"
#include "code\modules\nano\nanoexternal.dm"
#include "code\modules\nano\nanomanager.dm"
+#include "code\modules\nano\nanomapgen.dm"
#include "code\modules\nano\nanoui.dm"
#include "code\modules\organs\blood.dm"
#include "code\modules\organs\organ.dm"
@@ -1108,6 +1117,7 @@
#include "code\modules\paperwork\pen.dm"
#include "code\modules\paperwork\photocopier.dm"
#include "code\modules\paperwork\photography.dm"
+#include "code\modules\paperwork\silicon_photography.dm"
#include "code\modules\paperwork\stamps.dm"
#include "code\modules\power\apc.dm"
#include "code\modules\power\batteryrack.dm"
@@ -1122,6 +1132,9 @@
#include "code\modules\power\lighting.dm"
#include "code\modules\power\port_gen.dm"
#include "code\modules\power\power.dm"
+#include "code\modules\power\power_monitor.dm"
+#include "code\modules\power\powernet.dm"
+#include "code\modules\power\profiling.dm"
#include "code\modules\power\smes.dm"
#include "code\modules\power\solar.dm"
#include "code\modules\power\switch.dm"
@@ -1306,6 +1319,13 @@
#include "code\modules\scripting\Scanner\Tokens.dm"
#include "code\modules\security levels\keycard authentication.dm"
#include "code\modules\security levels\security levels.dm"
+#include "code\modules\shieldgen\circuits_and_designs.dm"
+#include "code\modules\shieldgen\emergency_shield.dm"
+#include "code\modules\shieldgen\energy_field.dm"
+#include "code\modules\shieldgen\sheldwallgen.dm"
+#include "code\modules\shieldgen\shield_capacitor.dm"
+#include "code\modules\shieldgen\shield_gen.dm"
+#include "code\modules\shieldgen\shield_gen_external.dm"
#include "code\modules\shuttles\antagonist.dm"
#include "code\modules\shuttles\departmental.dm"
#include "code\modules\shuttles\escape_pods.dm"
@@ -1379,11 +1399,6 @@
#include "code\WorkInProgress\Cael_Aislinn\Rust\gyrotron_controller.dm"
#include "code\WorkInProgress\Cael_Aislinn\Rust\radiation.dm"
#include "code\WorkInProgress\Cael_Aislinn\Rust\virtual_particle_catcher.dm"
-#include "code\WorkInProgress\Cael_Aislinn\ShieldGen\circuits_and_designs.dm"
-#include "code\WorkInProgress\Cael_Aislinn\ShieldGen\energy_field.dm"
-#include "code\WorkInProgress\Cael_Aislinn\ShieldGen\shield_capacitor.dm"
-#include "code\WorkInProgress\Cael_Aislinn\ShieldGen\shield_gen.dm"
-#include "code\WorkInProgress\Cael_Aislinn\ShieldGen\shield_gen_external.dm"
#include "code\WorkInProgress\Cael_Aislinn\Supermatter\LaserComputer.dm"
#include "code\WorkInProgress\Cael_Aislinn\Supermatter\ZeroPointLaser.dm"
#include "code\WorkInProgress\Chinsky\ashtray.dm"
@@ -1426,7 +1441,8 @@
#include "code\WorkInProgress\SkyMarshal\Ultralight_procs.dm"
#include "code\WorkInProgress\Wrongnumber\weldbackpack.dm"
#include "code\ZAS\_docs.dm"
-#include "code\ZAS\_gas_mixture.dm"
+#include "code\ZAS\_gas_mixture_xgm.dm"
+#include "code\ZAS\_xgm_gas_data.dm"
#include "code\ZAS\Airflow.dm"
#include "code\ZAS\Atom.dm"
#include "code\ZAS\Connection.dm"
@@ -1436,6 +1452,7 @@
#include "code\ZAS\Debug.dm"
#include "code\ZAS\Diagnostic.dm"
#include "code\ZAS\Fire.dm"
+#include "code\ZAS\Gas.dm"
#include "code\ZAS\Phoron.dm"
#include "code\ZAS\Turf.dm"
#include "code\ZAS\Variable Settings.dm"
diff --git a/code/ATMOSPHERICS/_atmos_setup.dm b/code/ATMOSPHERICS/_atmos_setup.dm
new file mode 100644
index 00000000000..a7585bf7ccc
--- /dev/null
+++ b/code/ATMOSPHERICS/_atmos_setup.dm
@@ -0,0 +1,215 @@
+//--------------------------------------------
+// Pipe colors
+//
+// Add them here and to the pipe_colors list
+// to automatically add them to all relevant
+// atmospherics devices.
+//--------------------------------------------
+
+#define PIPE_COLOR_GREY "#ffffff" //yes white is grey
+#define PIPE_COLOR_RED "#ff0000"
+#define PIPE_COLOR_BLUE "#0000ff"
+#define PIPE_COLOR_CYAN "#00ffff"
+#define PIPE_COLOR_GREEN "#00ff00"
+#define PIPE_COLOR_YELLOW "#ffcc00"
+#define PIPE_COLOR_PURPLE "#5c1ec0"
+
+var/global/list/pipe_colors = list("grey" = PIPE_COLOR_GREY, "red" = PIPE_COLOR_RED, "blue" = PIPE_COLOR_BLUE, "cyan" = PIPE_COLOR_CYAN, "green" = PIPE_COLOR_GREEN, "yellow" = PIPE_COLOR_YELLOW, "purple" = PIPE_COLOR_PURPLE)
+
+/proc/pipe_color_lookup(var/color)
+ for(var/C in pipe_colors)
+ if(color == pipe_colors[C])
+ return "[C]"
+
+/proc/pipe_color_check(var/color)
+ if(!color)
+ return 1
+ for(var/C in pipe_colors)
+ if(color == pipe_colors[C])
+ return 1
+ return 0
+
+//--------------------------------------------
+// Icon cache generation
+//--------------------------------------------
+
+/datum/pipe_icon_manager
+ var/list/pipe_icons[]
+ var/list/manifold_icons[]
+ var/list/device_icons[]
+ var/list/underlays_down[]
+ var/list/underlays_exposed[]
+ var/list/underlays_intact[]
+ var/list/pipe_underlays_exposed[]
+ var/list/pipe_underlays_intact[]
+ var/list/omni_icons[]
+
+/datum/pipe_icon_manager/New()
+ check_icons()
+
+/datum/pipe_icon_manager/proc/get_atmos_icon(var/device, var/dir, var/color, var/state)
+ check_icons()
+
+ device = "[device]"
+ state = "[state]"
+ color = "[color]"
+ dir = "[dir]"
+
+ switch(device)
+ if("pipe")
+ return pipe_icons[state + color]
+ if("manifold")
+ return manifold_icons[state + color]
+ if("device")
+ return device_icons[state]
+ if("omni")
+ return omni_icons[state]
+ if("underlay_intact")
+ return underlays_intact[dir + color]
+ if("underlay_exposed")
+ return underlays_exposed[dir + color]
+ if("underlay_down")
+ return underlays_down[dir + color]
+ if("pipe_underlay_exposed")
+ return pipe_underlays_exposed[dir + color]
+ if("pipe_underlay_intact")
+ return pipe_underlays_intact[dir + color]
+
+/datum/pipe_icon_manager/proc/check_icons()
+ if(!pipe_icons)
+ gen_pipe_icons()
+ if(!manifold_icons)
+ gen_manifold_icons()
+ if(!device_icons)
+ gen_device_icons()
+ if(!omni_icons)
+ gen_omni_icons()
+ if(!underlays_intact || !underlays_down || !underlays_exposed || !pipe_underlays_exposed || !pipe_underlays_intact)
+ gen_underlay_icons()
+
+/datum/pipe_icon_manager/proc/gen_pipe_icons()
+ if(!pipe_icons)
+ pipe_icons = new()
+
+ var/icon/pipe = new('icons/atmos/pipes.dmi')
+
+ for(var/state in pipe.IconStates())
+ if(!state || findtext(state, "map"))
+ continue
+
+ var/cache_name = state
+ var/image/I = image('icons/atmos/pipes.dmi', icon_state = state)
+ pipe_icons[cache_name] = I
+
+ for(var/pipe_color in pipe_colors)
+ I = image('icons/atmos/pipes.dmi', icon_state = state)
+ I.color = pipe_colors[pipe_color]
+ pipe_icons[state + "[pipe_colors[pipe_color]]"] = I
+
+ pipe = new ('icons/atmos/heat.dmi')
+ for(var/state in pipe.IconStates())
+ if(!state || findtext(state, "map"))
+ continue
+ pipe_icons["hepipe" + state] = image('icons/atmos/heat.dmi', icon_state = state)
+
+ pipe = new ('icons/atmos/junction.dmi')
+ for(var/state in pipe.IconStates())
+ if(!state || findtext(state, "map"))
+ continue
+ pipe_icons["hejunction" + state] = image('icons/atmos/junction.dmi', icon_state = state)
+
+
+/datum/pipe_icon_manager/proc/gen_manifold_icons()
+ if(!manifold_icons)
+ manifold_icons = new()
+
+ var/icon/pipe = new('icons/atmos/manifold.dmi')
+
+ for(var/state in pipe.IconStates())
+ if(findtext(state, "clamps"))
+ var/image/I = image('icons/atmos/manifold.dmi', icon_state = state)
+ manifold_icons[state] = I
+ continue
+
+ if(state == "core" || state == "4way")
+ var/image/I = image('icons/atmos/manifold.dmi', icon_state = state)
+ manifold_icons[state] = I
+ for(var/pipe_color in pipe_colors)
+ I = image('icons/atmos/manifold.dmi', icon_state = state)
+ I.color = pipe_colors[pipe_color]
+ manifold_icons[state + pipe_colors[pipe_color]] = I
+
+/datum/pipe_icon_manager/proc/gen_device_icons()
+ if(!device_icons)
+ device_icons = new()
+
+ var/icon/device
+
+ device = new('icons/atmos/vent_pump.dmi')
+ for(var/state in device.IconStates())
+ if(!state || findtext(state, "map"))
+ continue
+ device_icons["vent" + state] = image('icons/atmos/vent_pump.dmi', icon_state = state)
+
+ device = new('icons/atmos/vent_scrubber.dmi')
+ for(var/state in device.IconStates())
+ if(!state || findtext(state, "map"))
+ continue
+ device_icons["scrubber" + state] = image('icons/atmos/vent_scrubber.dmi', icon_state = state)
+
+/datum/pipe_icon_manager/proc/gen_omni_icons()
+ if(!omni_icons)
+ omni_icons = new()
+
+ var/icon/omni = new('icons/atmos/omni_devices.dmi')
+
+ for(var/state in omni.IconStates())
+ if(!state || findtext(state, "map"))
+ continue
+ omni_icons[state] = image('icons/atmos/omni_devices.dmi', icon_state = state)
+
+/datum/pipe_icon_manager/proc/gen_underlay_icons()
+ if(!underlays_intact)
+ underlays_intact = new()
+ if(!underlays_exposed)
+ underlays_exposed = new()
+ if(!underlays_down)
+ underlays_down = new()
+ if(!pipe_underlays_exposed)
+ pipe_underlays_exposed = new()
+ if(!pipe_underlays_intact)
+ pipe_underlays_intact = new()
+
+ var/icon/pipe = new('icons/atmos/pipe_underlays.dmi')
+
+ for(var/state in pipe.IconStates())
+ if(state == "")
+ continue
+
+ for(var/D in cardinal)
+ var/image/I = image('icons/atmos/pipe_underlays.dmi', icon_state = state, dir = D)
+ switch(state)
+ if("intact")
+ underlays_intact["[D]"] = I
+ if("exposed")
+ underlays_exposed["[D]"] = I
+ if("down")
+ underlays_down["[D]"] = I
+ if("pipe_exposed")
+ pipe_underlays_exposed["[D]"] = I
+ if("pipe_intact")
+ pipe_underlays_intact["[D]"] = I
+ for(var/pipe_color in pipe_colors)
+ I = image('icons/atmos/pipe_underlays.dmi', icon_state = state, dir = D)
+ I.color = pipe_colors[pipe_color]
+ switch(state)
+ if("intact")
+ underlays_intact["[D]" + pipe_colors[pipe_color]] = I
+ if("exposed")
+ underlays_exposed["[D]" + pipe_colors[pipe_color]] = I
+ if("down")
+ underlays_down["[D]" + pipe_colors[pipe_color]] = I
+ if("pipe_exposed")
+ pipe_underlays_exposed["[D]" + pipe_colors[pipe_color]] = I
+ if("pipe_intact")
+ pipe_underlays_intact["[D]" + pipe_colors[pipe_color]] = I
\ No newline at end of file
diff --git a/code/ATMOSPHERICS/_atmospherics_helpers.dm b/code/ATMOSPHERICS/_atmospherics_helpers.dm
new file mode 100644
index 00000000000..d7d665f6c29
--- /dev/null
+++ b/code/ATMOSPHERICS/_atmospherics_helpers.dm
@@ -0,0 +1,423 @@
+/*
+ Atmos processes
+
+ These procs generalize various processes used by atmos machinery, such as pumping, filtering, or scrubbing gas, allowing them to be reused elsewhere.
+ If no gas was moved/pumped/filtered/whatever, they return a negative number.
+ Otherwise they return the amount of energy needed to do whatever it is they do (equivalently power if done over 1 second).
+ In the case of free-flowing gas you can do things with gas and still use 0 power, hence the distinction between negative and non-negative return values.
+*/
+
+
+/obj/machinery/atmospherics/var/last_flow_rate = 0
+/obj/machinery/portable_atmospherics/var/last_flow_rate = 0
+
+
+/obj/machinery/atmospherics/var/debug = 0
+
+/obj/machinery/atmospherics/verb/toggle_debug()
+ set name = "Toggle Debug Messages"
+ set category = "Debug"
+ set src in view()
+ debug = !debug
+ usr << "[src]: Debug messages toggled [debug? "on" : "off"]."
+
+//Generalized gas pumping proc.
+//Moves gas from one gas_mixture to another and returns the amount of power needed (assuming 1 second), or -1 if no gas was pumped.
+//transfer_moles - Limits the amount of moles to transfer. The actual amount of gas moved may also be limited by available_power, if given.
+//available_power - the maximum amount of power that may be used when moving gas. If null then the transfer is not limited by power.
+/proc/pump_gas(var/obj/machinery/M, var/datum/gas_mixture/source, var/datum/gas_mixture/sink, var/transfer_moles = null, var/available_power = null)
+ if (source.total_moles < MINUMUM_MOLES_TO_PUMP) //if we cant transfer enough gas just stop to avoid further processing
+ return -1
+
+ if (!transfer_moles)
+ transfer_moles = source.total_moles
+ else
+ transfer_moles = min(source.total_moles, transfer_moles)
+
+ //Calculate the amount of energy required and limit transfer_moles based on available power
+ var/specific_power = calculate_specific_power(source, sink)/ATMOS_PUMP_EFFICIENCY //this has to be calculated before we modify any gas mixtures
+ if (!isnull(available_power) && specific_power > 0)
+ transfer_moles = min(transfer_moles, available_power / specific_power)
+
+ if (transfer_moles < MINUMUM_MOLES_TO_PUMP) //if we cant transfer enough gas just stop to avoid further processing
+ return -1
+
+ //Update flow rate meter
+ if (istype(M, /obj/machinery/atmospherics))
+ var/obj/machinery/atmospherics/A = M
+ A.last_flow_rate = (transfer_moles/source.total_moles)*source.volume //group_multiplier gets divided out here
+
+ if (A.debug)
+ A.visible_message("[A]: source entropy: [round(source.specific_entropy(), 0.01)] J/Kmol --> sink entropy: [round(sink.specific_entropy(), 0.01)] J/Kmol")
+ A.visible_message("[A]: specific entropy change = [round(sink.specific_entropy() - source.specific_entropy(), 0.01)] J/Kmol")
+ A.visible_message("[A]: specific power = [round(specific_power, 0.1)] W/mol")
+ A.visible_message("[A]: moles transferred = [transfer_moles] mol")
+
+ if (istype(M, /obj/machinery/portable_atmospherics))
+ var/obj/machinery/portable_atmospherics/P = M
+ P.last_flow_rate = (transfer_moles/source.total_moles)*source.volume //group_multiplier gets divided out here
+
+ var/datum/gas_mixture/removed = source.remove(transfer_moles)
+ if (!removed) //Just in case
+ return -1
+
+ var/power_draw = specific_power*transfer_moles
+ if (power_draw > 0)
+ removed.add_thermal_energy(power_draw * config.atmos_machine_heat) //1st law - energy is conserved
+
+ sink.merge(removed)
+
+ return power_draw
+
+//Generalized gas scrubbing proc.
+//Selectively moves specified gasses one gas_mixture to another and returns the amount of power needed (assuming 1 second), or -1 if no gas was filtered.
+//filtering - A list of gasids to be scrubbed from source
+//total_transfer_moles - Limits the amount of moles to scrub. The actual amount of gas scrubbed may also be limited by available_power, if given.
+//available_power - the maximum amount of power that may be used when scrubbing gas. If null then the scrubbing is not limited by power.
+/proc/scrub_gas(var/obj/machinery/M, var/list/filtering, var/datum/gas_mixture/source, var/datum/gas_mixture/sink, var/total_transfer_moles = null, var/available_power = null)
+ if (source.total_moles < MINUMUM_MOLES_TO_FILTER) //if we cant transfer enough gas just stop to avoid further processing
+ return -1
+
+ filtering = filtering & source.gas //only filter gasses that are actually there. DO NOT USE &=
+
+ //Determine the specific power of each filterable gas type, and the total amount of filterable gas (gasses selected to be scrubbed)
+ var/total_filterable_moles = 0 //the total amount of filterable gas
+ var/list/specific_power_gas = list() //the power required to remove one mole of pure gas, for each gas type
+ for (var/g in filtering)
+ if (source.gas[g] < MINUMUM_MOLES_TO_FILTER)
+ continue
+
+ var/specific_power = calculate_specific_power_gas(g, source, sink)/ATMOS_FILTER_EFFICIENCY
+ specific_power_gas[g] = specific_power
+ total_filterable_moles += source.gas[g]
+
+ if (total_filterable_moles < MINUMUM_MOLES_TO_FILTER) //if we cant transfer enough gas just stop to avoid further processing
+ return -1
+
+ //now that we know the total amount of filterable gas, we can calculate the amount of power needed to scrub one mole of gas
+ var/total_specific_power = 0 //the power required to remove one mole of filterable gas
+ for (var/g in filtering)
+ var/ratio = source.gas[g]/total_filterable_moles //this converts the specific power per mole of pure gas to specific power per mole of scrubbed gas
+ total_specific_power = specific_power_gas[g]*ratio
+
+ //Figure out how much of each gas to filter
+ if (!total_transfer_moles)
+ total_transfer_moles = total_filterable_moles
+ else
+ total_transfer_moles = min(total_transfer_moles, total_filterable_moles)
+
+ //limit transfer_moles based on available power
+ if (!isnull(available_power) && total_specific_power > 0)
+ total_transfer_moles = min(total_transfer_moles, available_power/total_specific_power)
+
+ if (total_transfer_moles < MINUMUM_MOLES_TO_FILTER) //if we cant transfer enough gas just stop to avoid further processing
+ return -1
+
+ //Update flow rate var
+ if (istype(M, /obj/machinery/atmospherics))
+ var/obj/machinery/atmospherics/A = M
+ A.last_flow_rate = (total_transfer_moles/source.total_moles)*source.volume //group_multiplier gets divided out here
+ if (istype(M, /obj/machinery/portable_atmospherics))
+ var/obj/machinery/portable_atmospherics/P = M
+ P.last_flow_rate = (total_transfer_moles/source.total_moles)*source.volume //group_multiplier gets divided out here
+
+ var/power_draw = 0
+ for (var/g in filtering)
+ var/transfer_moles = source.gas[g]
+ //filter gas in proportion to the mole ratio
+ transfer_moles = min(transfer_moles, total_transfer_moles*(source.gas[g]/total_filterable_moles))
+
+ //use update=0. All the filtered gasses are supposed to be added simultaneously, so we update after the for loop.
+ source.adjust_gas(g, -transfer_moles, update=0)
+ sink.adjust_gas_temp(g, transfer_moles, source.temperature, update=0)
+
+ power_draw += specific_power_gas[g]*transfer_moles
+
+ //Remix the resulting gases
+ sink.update_values()
+ source.update_values()
+
+ if (power_draw > 0)
+ sink.add_thermal_energy(power_draw * config.atmos_machine_heat) //gotta conserve that energy
+
+ return power_draw
+
+//Generalized gas filtering proc.
+//Filtering is a bit different from scrubbing. Instead of selectively moving the targeted gas types from one gas mix to another, filtering splits
+//the input gas into two outputs: one that contains /only/ the targeted gas types, and another that completely clean of the targeted gas types.
+//filtering - A list of gasids to be filtered. These gasses get moved to sink_filtered, while the other gasses get moved to sink_clean.
+//total_transfer_moles - Limits the amount of moles to input. The actual amount of gas filtered may also be limited by available_power, if given.
+//available_power - the maximum amount of power that may be used when filtering gas. If null then the filtering is not limited by power.
+/proc/filter_gas(var/obj/machinery/M, var/list/filtering, var/datum/gas_mixture/source, var/datum/gas_mixture/sink_filtered, var/datum/gas_mixture/sink_clean, var/total_transfer_moles = null, var/available_power = null)
+ if (source.total_moles < MINUMUM_MOLES_TO_FILTER) //if we cant transfer enough gas just stop to avoid further processing
+ return -1
+
+ filtering = filtering & source.gas //only filter gasses that are actually there. DO NOT USE &=
+
+ var/total_specific_power = 0 //the power required to remove one mole of input gas
+ var/total_filterable_moles = 0 //the total amount of filterable gas
+ var/total_unfilterable_moles = 0 //the total amount of non-filterable gas
+ var/list/specific_power_gas = list() //the power required to remove one mole of pure gas, for each gas type
+ for (var/g in source.gas)
+ if (source.gas[g] < MINUMUM_MOLES_TO_FILTER)
+ continue
+
+ if (g in filtering)
+ specific_power_gas[g] = calculate_specific_power_gas(g, source, sink_filtered)/ATMOS_FILTER_EFFICIENCY
+ total_filterable_moles += source.gas[g]
+ else
+ specific_power_gas[g] = calculate_specific_power_gas(g, source, sink_clean)/ATMOS_FILTER_EFFICIENCY
+ total_unfilterable_moles += source.gas[g]
+
+ var/ratio = source.gas[g]/source.total_moles //converts the specific power per mole of pure gas to specific power per mole of input gas mix
+ total_specific_power = specific_power_gas[g]*ratio
+
+ //Figure out how much of each gas to filter
+ if (!total_transfer_moles)
+ total_transfer_moles = source.total_moles
+ else
+ total_transfer_moles = min(total_transfer_moles, source.total_moles)
+
+ //limit transfer_moles based on available power
+ if (!isnull(available_power) && total_specific_power > 0)
+ total_transfer_moles = min(total_transfer_moles, available_power/total_specific_power)
+
+ if (total_transfer_moles < MINUMUM_MOLES_TO_FILTER) //if we cant transfer enough gas just stop to avoid further processing
+ return -1
+
+ //Update flow rate var
+ if (istype(M, /obj/machinery/atmospherics))
+ var/obj/machinery/atmospherics/A = M
+ A.last_flow_rate = (total_transfer_moles/source.total_moles)*source.volume //group_multiplier gets divided out here
+ if (istype(M, /obj/machinery/portable_atmospherics))
+ var/obj/machinery/portable_atmospherics/P = M
+ P.last_flow_rate = (total_transfer_moles/source.total_moles)*source.volume //group_multiplier gets divided out here
+
+ var/datum/gas_mixture/removed = source.remove(total_transfer_moles)
+ if (!removed) //Just in case
+ return -1
+
+ var/filtered_power_used = 0 //power used to move filterable gas to sink_filtered
+ var/unfiltered_power_used = 0 //power used to move unfilterable gas to sink_clean
+ for (var/g in removed.gas)
+ var/power_used = specific_power_gas[g]*removed.gas[g]
+
+ if (g in filtering)
+ //use update=0. All the filtered gasses are supposed to be added simultaneously, so we update after the for loop.
+ sink_filtered.adjust_gas_temp(g, removed.gas[g], removed.temperature, update=0)
+ removed.adjust_gas(g, -removed.gas[g], update=0)
+ filtered_power_used += power_used
+ else
+ unfiltered_power_used += power_used
+
+ sink_filtered.update_values()
+ removed.update_values()
+ sink_clean.merge(removed)
+
+ //1LTD energy is conserved
+ if (filtered_power_used > 0)
+ sink_filtered.add_thermal_energy(filtered_power_used * config.atmos_machine_heat)
+ if (unfiltered_power_used > 0)
+ sink_clean.add_thermal_energy(unfiltered_power_used * config.atmos_machine_heat)
+
+ return filtered_power_used + unfiltered_power_used
+
+//For omni devices. Instead filtering is an associative list mapping gasids to gas mixtures.
+//I don't like the copypasta, but I decided to keep both versions of gas filtering as filter_gas is slightly faster (doesn't create as many temporary lists, doesn't call update_values() as much)
+//filter_gas can be removed and replaced with this proc if need be.
+/proc/filter_gas_multi(var/obj/machinery/M, var/list/filtering, var/datum/gas_mixture/source, var/datum/gas_mixture/sink_clean, var/total_transfer_moles = null, var/available_power = null)
+ if (source.total_moles < MINUMUM_MOLES_TO_FILTER) //if we cant transfer enough gas just stop to avoid further processing
+ return -1
+
+ filtering = filtering & source.gas //only filter gasses that are actually there. DO NOT USE &=
+
+ var/total_specific_power = 0 //the power required to remove one mole of input gas
+ var/total_filterable_moles = 0 //the total amount of filterable gas
+ var/total_unfilterable_moles = 0 //the total amount of non-filterable gas
+ var/list/specific_power_gas = list() //the power required to remove one mole of pure gas, for each gas type
+ for (var/g in source.gas)
+ if (source.gas[g] < MINUMUM_MOLES_TO_FILTER)
+ continue
+
+ if (g in filtering)
+ var/datum/gas_mixture/sink_filtered = filtering[g]
+ specific_power_gas[g] = calculate_specific_power_gas(g, source, sink_filtered)/ATMOS_FILTER_EFFICIENCY
+ total_filterable_moles += source.gas[g]
+ else
+ specific_power_gas[g] = calculate_specific_power_gas(g, source, sink_clean)/ATMOS_FILTER_EFFICIENCY
+ total_unfilterable_moles += source.gas[g]
+
+ var/ratio = source.gas[g]/source.total_moles //converts the specific power per mole of pure gas to specific power per mole of input gas mix
+ total_specific_power = specific_power_gas[g]*ratio
+
+ //Figure out how much of each gas to filter
+ if (!total_transfer_moles)
+ total_transfer_moles = source.total_moles
+ else
+ total_transfer_moles = min(total_transfer_moles, source.total_moles)
+
+ //limit transfer_moles based on available power
+ if (!isnull(available_power) && total_specific_power > 0)
+ total_transfer_moles = min(total_transfer_moles, available_power/total_specific_power)
+
+ if (total_transfer_moles < MINUMUM_MOLES_TO_FILTER) //if we cant transfer enough gas just stop to avoid further processing
+ return -1
+
+ //Update Flow Rate var
+ if (istype(M, /obj/machinery/atmospherics))
+ var/obj/machinery/atmospherics/A = M
+ A.last_flow_rate = (total_transfer_moles/source.total_moles)*source.volume //group_multiplier gets divided out here
+ if (istype(M, /obj/machinery/portable_atmospherics))
+ var/obj/machinery/portable_atmospherics/P = M
+ P.last_flow_rate = (total_transfer_moles/source.total_moles)*source.volume //group_multiplier gets divided out here
+
+ var/datum/gas_mixture/removed = source.remove(total_transfer_moles)
+ if (!removed) //Just in case
+ return -1
+
+ var/list/filtered_power_used = list() //power used to move filterable gas to the filtered gas mixes
+ var/unfiltered_power_used = 0 //power used to move unfilterable gas to sink_clean
+ for (var/g in removed.gas)
+ var/power_used = specific_power_gas[g]*removed.gas[g]
+
+ if (g in filtering)
+ var/datum/gas_mixture/sink_filtered = filtering[g]
+ //use update=0. All the filtered gasses are supposed to be added simultaneously, so we update after the for loop.
+ sink_filtered.adjust_gas_temp(g, removed.gas[g], removed.temperature, update=1)
+ removed.adjust_gas(g, -removed.gas[g], update=0)
+ if (power_used)
+ filtered_power_used[sink_filtered] = power_used
+ else
+ unfiltered_power_used += power_used
+
+ removed.update_values()
+ sink_clean.merge(removed)
+
+ //1st LTD energy is conserved
+ var/power_draw = unfiltered_power_used
+ for (var/datum/gas_mixture/sink_filtered in filtered_power_used)
+ power_draw += filtered_power_used[sink_filtered]
+ sink_filtered.add_thermal_energy(filtered_power_used[sink_filtered] * config.atmos_machine_heat)
+ if (unfiltered_power_used > 0)
+ sink_clean.add_thermal_energy(unfiltered_power_used * config.atmos_machine_heat)
+
+ return power_draw
+
+//Similar deal as the other atmos process procs.
+//mix_sources maps input gas mixtures to mix ratios. The mix ratios MUST add up to 1.
+/proc/mix_gas(var/obj/machinery/M, var/list/mix_sources, var/datum/gas_mixture/sink, var/total_transfer_moles = null, var/available_power = null)
+ if (!mix_sources.len)
+ return -1
+
+ var/total_specific_power = 0 //the power needed to mix one mole of gas
+ var/total_mixing_moles = null //the total amount of gas that can be mixed, given our mix ratios
+ var/total_input_volume = 0 //for flow rate calculation
+ var/total_input_moles = 0 //for flow rate calculation
+ var/list/source_specific_power = list()
+ for (var/datum/gas_mixture/source in mix_sources)
+ if (source.total_moles < MINUMUM_MOLES_TO_FILTER)
+ return -1 //either mix at the set ratios or mix no gas at all
+
+ var/mix_ratio = mix_sources[source]
+ if (!mix_ratio)
+ continue //this gas is not being mixed in
+
+ //mixing rate is limited by the source with the least amount of available gas
+ var/this_mixing_moles = source.total_moles/mix_ratio
+ if (isnull(total_mixing_moles) || total_mixing_moles > this_mixing_moles)
+ total_mixing_moles = this_mixing_moles
+
+ source_specific_power[source] = calculate_specific_power(source, sink)*mix_ratio/ATMOS_FILTER_EFFICIENCY
+ total_specific_power += source_specific_power[source]
+ total_input_volume += source.volume
+ total_input_moles += source.total_moles
+
+ if (total_mixing_moles < MINUMUM_MOLES_TO_FILTER) //if we cant transfer enough gas just stop to avoid further processing
+ return -1
+
+ if (!total_transfer_moles)
+ total_transfer_moles = total_mixing_moles
+ else
+ total_transfer_moles = min(total_mixing_moles, total_transfer_moles)
+
+ //limit transfer_moles based on available power
+ if (!isnull(available_power) && total_specific_power > 0)
+ total_transfer_moles = min(total_transfer_moles, available_power / total_specific_power)
+
+ if (total_transfer_moles < MINUMUM_MOLES_TO_FILTER) //if we cant transfer enough gas just stop to avoid further processing
+ return -1
+
+ //Update flow rate var
+ if (istype(M, /obj/machinery/atmospherics))
+ var/obj/machinery/atmospherics/A = M
+ A.last_flow_rate = (total_transfer_moles/total_input_moles)*total_input_volume //group_multiplier gets divided out here
+ if (istype(M, /obj/machinery/portable_atmospherics))
+ var/obj/machinery/portable_atmospherics/P = M
+ P.last_flow_rate = (total_transfer_moles/total_input_moles)*total_input_volume //group_multiplier gets divided out here
+
+ var/total_power_draw = 0
+ for (var/datum/gas_mixture/source in mix_sources)
+ var/mix_ratio = mix_sources[source]
+ if (!mix_ratio)
+ continue
+
+ var/transfer_moles = total_transfer_moles * mix_ratio
+
+ var/datum/gas_mixture/removed = source.remove(transfer_moles)
+
+ var/power_draw = transfer_moles * source_specific_power[source]
+ removed.add_thermal_energy(power_draw * config.atmos_machine_heat) //conservation of energy
+ total_power_draw += power_draw
+
+ sink.merge(removed)
+
+ return total_power_draw
+
+/*
+ Helper procs for various things.
+*/
+
+//Calculates the amount of power needed to move one mole from source to sink.
+/proc/calculate_specific_power(datum/gas_mixture/source, datum/gas_mixture/sink)
+ //Calculate the amount of energy required
+ var/air_temperature = (sink.temperature > 0)? sink.temperature : source.temperature
+ var/specific_entropy = sink.specific_entropy() - source.specific_entropy() //sink is gaining moles, source is loosing
+ var/specific_power = 0 // W/mol
+
+ //If specific_entropy is < 0 then power is required to move gas
+ if (specific_entropy < 0)
+ specific_power = -specific_entropy*air_temperature //how much power we need per mole
+
+ return specific_power
+
+//Calculates the amount of power needed to move one mole of a certain gas from source to sink.
+/proc/calculate_specific_power_gas(var/gasid, datum/gas_mixture/source, datum/gas_mixture/sink)
+ //Calculate the amount of energy required
+ var/air_temperature = (sink.temperature > 0)? sink.temperature : source.temperature
+ var/specific_entropy = sink.specific_entropy_gas(gasid) - source.specific_entropy_gas(gasid) //sink is gaining moles, source is loosing
+ var/specific_power = 0 // W/mol
+
+ //If specific_entropy is < 0 then power is required to move gas
+ if (specific_entropy < 0)
+ specific_power = -specific_entropy*air_temperature //how much power we need per mole
+
+ return specific_power
+
+//This proc handles power usages.
+//Calling update_use_power() or use_power() too often will result in lag since updating area power can be costly.
+//This proc implements an approximation scheme that will cause area power updates to be triggered less often.
+//By having atmos machinery use this proc it is easy to change the power usage approximation for all atmos machines
+/obj/machinery/proc/handle_power_draw(var/usage_amount)
+ //This code errs on the side of using more power. Using this will mean that sometimes atmos machines use more power than they need, but won't get power for free.
+ if (usage_amount > idle_power_usage)
+ update_use_power(2)
+ else
+ if (use_power >= 2)
+ use_power = 1 //Don't update here. We will use more power than we are supposed to, but trigger less area power updates.
+ else
+ update_use_power(1)
+
+ switch (use_power)
+ if (0) return 0
+ if (1) return idle_power_usage
+ if (2 to INFINITY) return max(idle_power_usage, usage_amount)
\ No newline at end of file
diff --git a/code/ATMOSPHERICS/atmospherics.dm b/code/ATMOSPHERICS/atmospherics.dm
index 1b89db43be1..a33b79158e9 100644
--- a/code/ATMOSPHERICS/atmospherics.dm
+++ b/code/ATMOSPHERICS/atmospherics.dm
@@ -9,49 +9,96 @@ Pipes -> Pipelines
Pipelines + Other Objects -> Pipe network
*/
-
-obj/machinery/atmospherics
+/obj/machinery/atmospherics
anchored = 1
idle_power_usage = 0
active_power_usage = 0
power_channel = ENVIRON
var/nodealert = 0
+ var/initialize_directions = 0
+ var/pipe_color
+
+ var/global/datum/pipe_icon_manager/icon_manager
+/obj/machinery/atmospherics/New()
+ if(!icon_manager)
+ icon_manager = new()
+
+ if(!pipe_color)
+ pipe_color = color
+ color = null
+
+ if(!pipe_color_check(pipe_color))
+ pipe_color = null
+ ..()
-obj/machinery/atmospherics/var/initialize_directions = 0
-obj/machinery/atmospherics/var/pipe_color
+/obj/machinery/atmospherics/attackby(atom/A, mob/user as mob)
+ if(istype(A, /obj/item/device/pipe_painter))
+ return
+ ..()
-obj/machinery/atmospherics/process()
+/obj/machinery/atmospherics/proc/add_underlay(var/turf/T, var/obj/machinery/atmospherics/node, var/direction)
+ if(node)
+ if(T.intact && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe))
+ underlays += icon_manager.get_atmos_icon("underlay_down", direction, color_cache_name(node))
+ else
+ underlays += icon_manager.get_atmos_icon("underlay_intact", direction, color_cache_name(node))
+ else
+ underlays += icon_manager.get_atmos_icon("underlay_exposed", direction, pipe_color)
+
+/obj/machinery/atmospherics/proc/update_underlays()
+ if(check_icon_cache())
+ return 1
+ else
+ return 0
+
+/obj/machinery/atmospherics/proc/check_icon_cache(var/safety = 0)
+ if(!istype(icon_manager))
+ if(!safety) //to prevent infinite loops
+ icon_manager = new()
+ check_icon_cache(1)
+ return 0
+
+ return 1
+
+/obj/machinery/atmospherics/proc/color_cache_name(var/obj/machinery/atmospherics/node)
+ //Don't use this for standard pipes
+ if(!istype(node))
+ return null
+
+ return node.pipe_color
+
+/obj/machinery/atmospherics/process()
build_network()
-obj/machinery/atmospherics/proc/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference)
+/obj/machinery/atmospherics/proc/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference)
// Check to see if should be added to network. Add self if so and adjust variables appropriately.
// Note don't forget to have neighbors look as well!
return null
-obj/machinery/atmospherics/proc/build_network()
+/obj/machinery/atmospherics/proc/build_network()
// Called to build a network from this node
return null
-obj/machinery/atmospherics/proc/return_network(obj/machinery/atmospherics/reference)
+/obj/machinery/atmospherics/proc/return_network(obj/machinery/atmospherics/reference)
// Returns pipe_network associated with connection to reference
// Notes: should create network if necessary
// Should never return null
return null
-obj/machinery/atmospherics/proc/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network)
+/obj/machinery/atmospherics/proc/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network)
// Used when two pipe_networks are combining
-obj/machinery/atmospherics/proc/return_network_air(datum/network/reference)
+/obj/machinery/atmospherics/proc/return_network_air(datum/network/reference)
// Return a list of gas_mixture(s) in the object
// associated with reference pipe_network for use in rebuilding the networks gases list
// Is permitted to return null
-obj/machinery/atmospherics/proc/disconnect(obj/machinery/atmospherics/reference)
+/obj/machinery/atmospherics/proc/disconnect(obj/machinery/atmospherics/reference)
-obj/machinery/atmospherics/update_icon()
+/obj/machinery/atmospherics/update_icon()
return null
\ No newline at end of file
diff --git a/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm b/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm
index 9d2a8e32e8a..52f57b29582 100644
--- a/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm
+++ b/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm
@@ -76,6 +76,7 @@ obj/machinery/atmospherics/binary
break
update_icon()
+ update_underlays()
build_network()
if(!network1 && node1)
@@ -127,4 +128,7 @@ obj/machinery/atmospherics/binary
del(network2)
node2 = null
+ update_icon()
+ update_underlays()
+
return null
\ No newline at end of file
diff --git a/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm b/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm
index ac451871dc2..c31f6f92b78 100644
--- a/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm
+++ b/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm
@@ -1,6 +1,18 @@
+#define DEFAULT_PRESSURE_DELTA 10000
+
+#define EXTERNAL_PRESSURE_BOUND ONE_ATMOSPHERE
+#define INTERNAL_PRESSURE_BOUND 0
+#define PRESSURE_CHECKS 1
+
+#define PRESSURE_CHECK_EXTERNAL 1
+#define PRESSURE_CHECK_INPUT 2
+#define PRESSURE_CHECK_OUTPUT 4
+
+#undefine
+
/obj/machinery/atmospherics/binary/dp_vent_pump
- icon = 'icons/obj/atmospherics/dp_vent_pump.dmi'
- icon_state = "off"
+ icon = 'icons/atmos/vent_pump.dmi'
+ icon_state = "map_dp_vent"
//node2 is output port
//node1 is input port
@@ -10,190 +22,246 @@
level = 1
- high_volume
- name = "Large Dual Port Air Vent"
-
- New()
- ..()
-
- air1.volume = 1000
- air2.volume = 1000
+ use_power = 1
+ idle_power_usage = 150 //internal circuitry, friction losses and stuff
+ active_power_usage = 7500 //This also doubles as a measure of how powerful the pump is, in Watts. 7500 W ~ 10 HP
+ var/last_power_draw = 0
var/on = 0
var/pump_direction = 1 //0 = siphoning, 1 = releasing
- var/external_pressure_bound = ONE_ATMOSPHERE
- var/input_pressure_min = 0
- var/output_pressure_max = 0
-
- var/pressure_checks = 1
- //1: Do not pass external_pressure_bound
- //2: Do not pass input_pressure_min
- //4: Do not pass output_pressure_max
-
- update_icon()
- if(on)
- if(pump_direction)
- icon_state = "[level == 1 && istype(loc, /turf/simulated) ? "h" : "" ]out"
- else
- icon_state = "[level == 1 && istype(loc, /turf/simulated) ? "h" : "" ]in"
- else
- icon_state = "[level == 1 && istype(loc, /turf/simulated) ? "h" : "" ]off"
- on = 0
-
- return
-
- hide(var/i) //to make the little pipe section invisible, the icon changes.
- if(on)
- if(pump_direction)
- icon_state = "[i == 1 && istype(loc, /turf/simulated) ? "h" : "" ]out"
- else
- icon_state = "[i == 1 && istype(loc, /turf/simulated) ? "h" : "" ]in"
- else
- icon_state = "[i == 1 && istype(loc, /turf/simulated) ? "h" : "" ]off"
- on = 0
- return
-
- process()
- ..()
-
- if(!on)
- return 0
-
- var/datum/gas_mixture/environment = loc.return_air()
- var/environment_pressure = environment.return_pressure()
-
- if(pump_direction) //input -> external
- var/pressure_delta = 10000
-
- if(pressure_checks&1)
- pressure_delta = min(pressure_delta, (external_pressure_bound - environment_pressure))
- if(pressure_checks&2)
- pressure_delta = min(pressure_delta, (air1.return_pressure() - input_pressure_min))
-
- if(pressure_delta > 0)
- if(air1.temperature > 0)
- var/transfer_moles = pressure_delta*environment.volume/(air1.temperature * R_IDEAL_GAS_EQUATION)
-
- var/datum/gas_mixture/removed = air1.remove(transfer_moles)
-
- loc.assume_air(removed)
-
- if(network1)
- network1.update = 1
-
- else //external -> output
- var/pressure_delta = 10000
-
- if(pressure_checks&1)
- pressure_delta = min(pressure_delta, (environment_pressure - external_pressure_bound))
- if(pressure_checks&4)
- pressure_delta = min(pressure_delta, (output_pressure_max - air2.return_pressure()))
-
- if(pressure_delta > 0)
- if(environment.temperature > 0)
- var/transfer_moles = pressure_delta*air2.volume/(environment.temperature * R_IDEAL_GAS_EQUATION)
-
- var/datum/gas_mixture/removed = loc.remove_air(transfer_moles)
-
- air2.merge(removed)
-
- if(network2)
- network2.update = 1
-
- return 1
-
- //Radio remote control
-
- proc
- set_frequency(new_frequency)
- radio_controller.remove_object(src, frequency)
- frequency = new_frequency
- if(frequency)
- radio_connection = radio_controller.add_object(src, frequency, filter = RADIO_ATMOSIA)
-
- broadcast_status()
- if(!radio_connection)
- return 0
-
- var/datum/signal/signal = new
- signal.transmission_method = 1 //radio signal
- signal.source = src
-
- signal.data = list(
- "tag" = id,
- "device" = "ADVP",
- "power" = on,
- "direction" = pump_direction?("release"):("siphon"),
- "checks" = pressure_checks,
- "input" = input_pressure_min,
- "output" = output_pressure_max,
- "external" = external_pressure_bound,
- "sigtype" = "status"
- )
- radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA)
-
- return 1
+ var/external_pressure_bound = EXTERNAL_PRESSURE_BOUND
+ var/input_pressure_min = INTERNAL_PRESSURE_BOUND
+ var/output_pressure_max = DEFAULT_PRESSURE_DELTA
var/frequency = 0
var/id = null
var/datum/radio_frequency/radio_connection
- initialize()
- ..()
- if(frequency)
- set_frequency(frequency)
+ var/pressure_checks = PRESSURE_CHECK_EXTERNAL
+ //1: Do not pass external_pressure_bound
+ //2: Do not pass input_pressure_min
+ //4: Do not pass output_pressure_max
- receive_signal(datum/signal/signal)
+/obj/machinery/atmospherics/binary/dp_vent_pump/New()
+ ..()
+ air1.volume = ATMOS_DEFAULT_VOLUME_PUMP
+ air2.volume = ATMOS_DEFAULT_VOLUME_PUMP
+ icon = null
- if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command"))
- return 0
- if("power" in signal.data)
- on = text2num(signal.data["power"])
+/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume
+ name = "Large Dual Port Air Vent"
- if("power_toggle" in signal.data)
- on = !on
+/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume/New()
+ ..()
+ air1.volume = ATMOS_DEFAULT_VOLUME_PUMP + 800
+ air2.volume = ATMOS_DEFAULT_VOLUME_PUMP + 800
- if("direction" in signal.data)
- pump_direction = text2num(signal.data["direction"])
+/obj/machinery/atmospherics/binary/dp_vent_pump/update_icon(var/safety = 0)
+ if(!check_icon_cache())
+ return
- if("checks" in signal.data)
- pressure_checks = text2num(signal.data["checks"])
+ overlays.Cut()
+
+ var/vent_icon = "vent"
- if("purge" in signal.data)
- pressure_checks &= ~1
- pump_direction = 0
+ var/turf/T = get_turf(src)
+ if(!istype(T))
+ return
- if("stabalize" in signal.data)
- pressure_checks |= 1
- pump_direction = 1
+ if(T.intact && node1 && node2 && node1.level == 1 && node2.level == 1 && istype(node1, /obj/machinery/atmospherics/pipe) && istype(node2, /obj/machinery/atmospherics/pipe))
+ vent_icon += "h"
+
+ if(!powered())
+ vent_icon += "off"
+ else
+ vent_icon += "[on ? "[pump_direction ? "out" : "in"]" : "off"]"
- if("set_input_pressure" in signal.data)
- input_pressure_min = between(
- 0,
- text2num(signal.data["set_input_pressure"]),
- ONE_ATMOSPHERE*50
- )
+ overlays += icon_manager.get_atmos_icon("device", , , vent_icon)
- if("set_output_pressure" in signal.data)
- output_pressure_max = between(
- 0,
- text2num(signal.data["set_output_pressure"]),
- ONE_ATMOSPHERE*50
- )
+/obj/machinery/atmospherics/binary/dp_vent_pump/update_underlays()
+ if(..())
+ underlays.Cut()
+ var/turf/T = get_turf(src)
+ if(!istype(T))
+ return
+ if(T.intact && node1 && node2 && node1.level == 1 && node2.level == 1 && istype(node1, /obj/machinery/atmospherics/pipe) && istype(node2, /obj/machinery/atmospherics/pipe))
+ return
+ else
+ add_underlay(T, node1, turn(dir, -180))
+ add_underlay(T, node2, dir)
- if("set_external_pressure" in signal.data)
- external_pressure_bound = between(
- 0,
- text2num(signal.data["set_external_pressure"]),
- ONE_ATMOSPHERE*50
- )
+/obj/machinery/atmospherics/binary/dp_vent_pump/hide(var/i)
+ update_icon()
+ update_underlays()
- if("status" in signal.data)
- spawn(2)
- broadcast_status()
- return //do not update_icon
- //if(signal.data["tag"])
+/obj/machinery/atmospherics/binary/dp_vent_pump/process()
+ ..()
+
+ if(stat & (NOPOWER|BROKEN) || !on)
+ update_use_power(0) //usually we get here because a player turned a pump off - definitely want to update.
+ last_power_draw = 0
+ last_flow_rate = 0
+ return 0
+
+ var/datum/gas_mixture/environment = loc.return_air()
+
+ var/power_draw = -1
+
+ //Figure out the target pressure difference
+ var/pressure_delta = get_pressure_delta(environment)
+
+ if(pressure_delta > 0.5)
+ if(pump_direction) //internal -> external
+ if (node1 && (environment.temperature || air1.temperature))
+ var/output_volume = environment.volume * environment.group_multiplier
+ var/air_temperature = environment.temperature? environment.temperature : air1.temperature
+ var/transfer_moles = pressure_delta*output_volume/(air_temperature * R_IDEAL_GAS_EQUATION)
+
+ power_draw = pump_gas(src, air1, environment, transfer_moles, active_power_usage)
+
+ if(power_draw >= 0 && network1)
+ network1.update = 1
+ else //external -> internal
+ if (node2 && (environment.temperature || air2.temperature))
+ var/output_volume = air2.volume + (network2? network2.volume : 0)
+ var/air_temperature = air2.temperature? air2.temperature : environment.temperature
+ var/transfer_moles = pressure_delta*output_volume/(air_temperature * R_IDEAL_GAS_EQUATION)
+
+ //limit flow rate from turfs
+ transfer_moles = min(transfer_moles, environment.total_moles*air2.volume/environment.volume) //group_multiplier gets divided out here
+
+ power_draw = pump_gas(src, environment, air2, transfer_moles, active_power_usage)
+
+ if(power_draw >= 0 && network2)
+ network2.update = 1
+
+ if (power_draw < 0)
+ last_power_draw = 0
+ last_flow_rate = 0
+ //update_use_power(0)
+ use_power = 0 //don't force update - easier on CPU
+ else
+ last_power_draw = handle_power_draw(power_draw)
+
+ return 1
+
+/obj/machinery/atmospherics/binary/dp_vent_pump/proc/get_pressure_delta(datum/gas_mixture/environment)
+ var/pressure_delta = DEFAULT_PRESSURE_DELTA
+ var/environment_pressure = environment.return_pressure()
+
+ if(pump_direction) //internal -> external
+ if(pressure_checks & PRESSURE_CHECK_EXTERNAL)
+ pressure_delta = min(pressure_delta, external_pressure_bound - environment_pressure) //increasing the pressure here
+ if(pressure_checks & PRESSURE_CHECK_INPUT)
+ pressure_delta = min(pressure_delta, air1.return_pressure() - input_pressure_min) //decreasing the pressure here
+ else //external -> internal
+ if(pressure_checks & PRESSURE_CHECK_EXTERNAL)
+ pressure_delta = min(pressure_delta, environment_pressure - external_pressure_bound) //decreasing the pressure here
+ if(pressure_checks & PRESSURE_CHECK_OUTPUT)
+ pressure_delta = min(pressure_delta, output_pressure_max - air2.return_pressure()) //increasing the pressure here
+
+ return pressure_delta
+
+
+//Radio remote control
+
+/obj/machinery/atmospherics/binary/dp_vent_pump/proc/set_frequency(new_frequency)
+ radio_controller.remove_object(src, frequency)
+ frequency = new_frequency
+ if(frequency)
+ radio_connection = radio_controller.add_object(src, frequency, filter = RADIO_ATMOSIA)
+
+/obj/machinery/atmospherics/binary/dp_vent_pump/proc/broadcast_status()
+ if(!radio_connection)
+ return 0
+
+ var/datum/signal/signal = new
+ signal.transmission_method = 1 //radio signal
+ signal.source = src
+
+ signal.data = list(
+ "tag" = id,
+ "device" = "ADVP",
+ "power" = on,
+ "direction" = pump_direction?("release"):("siphon"),
+ "checks" = pressure_checks,
+ "input" = input_pressure_min,
+ "output" = output_pressure_max,
+ "external" = external_pressure_bound,
+ "sigtype" = "status"
+ )
+ radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA)
+
+ return 1
+
+/obj/machinery/atmospherics/binary/dp_vent_pump/initialize()
+ ..()
+ if(frequency)
+ set_frequency(frequency)
+
+/obj/machinery/atmospherics/binary/dp_vent_pump/examine()
+ set src in oview(1)
+ ..()
+
+ if (get_dist(usr, src) <= 1)
+ usr << "A small gauge in the corner reads [round(last_flow_rate, 0.1)] L/s; [round(last_power_draw)] W"
+
+/obj/machinery/atmospherics/unary/vent_pump/power_change()
+ var/old_stat = stat
+ ..()
+ if(old_stat != stat)
+ update_icon()
+
+/obj/machinery/atmospherics/binary/dp_vent_pump/receive_signal(datum/signal/signal)
+ if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command"))
+ return 0
+ if(signal.data["power"])
+ on = text2num(signal.data["power"])
+
+ if(signal.data["power_toggle"])
+ on = !on
+
+ if(signal.data["direction"])
+ pump_direction = text2num(signal.data["direction"])
+
+ if(signal.data["checks"])
+ pressure_checks = text2num(signal.data["checks"])
+
+ if(signal.data["purge"])
+ pressure_checks &= ~1
+ pump_direction = 0
+
+ if(signal.data["stabalize"])
+ pressure_checks |= 1
+ pump_direction = 1
+
+ if(signal.data["set_input_pressure"])
+ input_pressure_min = between(
+ 0,
+ text2num(signal.data["set_input_pressure"]),
+ ONE_ATMOSPHERE*50
+ )
+
+ if(signal.data["set_output_pressure"])
+ output_pressure_max = between(
+ 0,
+ text2num(signal.data["set_output_pressure"]),
+ ONE_ATMOSPHERE*50
+ )
+
+ if(signal.data["set_external_pressure"])
+ external_pressure_bound = between(
+ 0,
+ text2num(signal.data["set_external_pressure"]),
+ ONE_ATMOSPHERE*50
+ )
+
+ if(signal.data["status"])
spawn(2)
broadcast_status()
- update_icon()
\ No newline at end of file
+ return //do not update_icon
+
+ spawn(2)
+ broadcast_status()
+ update_icon()
\ No newline at end of file
diff --git a/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm b/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm
index a126928e2da..d714d7ce1f9 100644
--- a/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm
+++ b/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm
@@ -1,188 +1,264 @@
-obj/machinery/atmospherics/binary/passive_gate
- //Tries to achieve target pressure at output (like a normal pump) except
- // Uses no power but can not transfer gases from a low pressure area to a high pressure area
- icon = 'icons/obj/atmospherics/passive_gate.dmi'
- icon_state = "intact_off"
+#define REGULATE_NONE 0
+#define REGULATE_INPUT 1 //shuts off when input side is below the target pressure
+#define REGULATE_OUTPUT 2 //shuts off when output side is above the target pressure
- name = "Passive gate"
- desc = "A one-way air valve that does not require power"
+#undefine
- var/on = 0
+/obj/machinery/atmospherics/binary/passive_gate
+ icon = 'icons/atmos/passive_gate.dmi'
+ icon_state = "map"
+ level = 1
+
+ name = "pressure regulator"
+ desc = "A one-way air valve that can be used to regulate input or output pressure, and flow rate. Does not require power."
+
+ use_power = 0
+
+ var/on = 0 //doesn't actually use power. this is just whether the valve is open or not
var/target_pressure = ONE_ATMOSPHERE
-
+ var/max_pressure_setting = 15000 //kPa
+ var/set_flow_rate = ATMOS_DEFAULT_VOLUME_PUMP * 2.5
+ var/regulate_mode = REGULATE_OUTPUT
+
+ var/flowing = 0 //for icons - becomes zero if the valve closes itself due to regulation mode
+
var/frequency = 0
var/id = null
var/datum/radio_frequency/radio_connection
- update_icon()
- if(stat & NOPOWER)
- icon_state = "intact_off"
- else if(node1 && node2)
- icon_state = "intact_[on?("on"):("off")]"
- else
- if(node1)
- icon_state = "exposed_1_off"
- else if(node2)
- icon_state = "exposed_2_off"
- else
- icon_state = "exposed_3_off"
- return
+/obj/machinery/atmospherics/binary/passive_gate/New()
+ ..()
+ air1.volume = ATMOS_DEFAULT_VOLUME_PUMP * 2.5
+ air2.volume = ATMOS_DEFAULT_VOLUME_PUMP * 2.5
- process()
- ..()
- if(!on)
- return 0
+/obj/machinery/atmospherics/binary/passive_gate/update_icon()
+ icon_state = (on && flowing)? "on" : "off"
- var/output_starting_pressure = air2.return_pressure()
- var/input_starting_pressure = air1.return_pressure()
+/obj/machinery/atmospherics/binary/passive_gate/update_underlays()
+ if(..())
+ underlays.Cut()
+ var/turf/T = get_turf(src)
+ if(!istype(T))
+ return
+ add_underlay(T, node1, turn(dir, 180))
+ add_underlay(T, node2, dir)
- if(output_starting_pressure >= min(target_pressure,input_starting_pressure-10))
- //No need to pump gas if target is already reached or input pressure is too low
- //Need at least 10 KPa difference to overcome friction in the mechanism
- return 1
+/obj/machinery/atmospherics/binary/passive_gate/hide(var/i)
+ update_underlays()
- //Calculate necessary moles to transfer using PV = nRT
- if((air1.total_moles() > 0) && (air1.temperature>0))
- var/pressure_delta = min(target_pressure - output_starting_pressure, (input_starting_pressure - output_starting_pressure)/2)
- //Can not have a pressure delta that would cause output_pressure > input_pressure
+/obj/machinery/atmospherics/binary/passive_gate/process()
+ ..()
+ if(!on)
+ last_flow_rate = 0
+ return 0
- var/transfer_moles = pressure_delta*air2.volume/(air1.temperature * R_IDEAL_GAS_EQUATION)
-
- //Actually transfer the gas
- var/datum/gas_mixture/removed = air1.remove(transfer_moles)
- air2.merge(removed)
+ var/output_starting_pressure = air2.return_pressure()
+ var/input_starting_pressure = air1.return_pressure()
+ var/pressure_delta
+ switch (regulate_mode)
+ if (REGULATE_INPUT)
+ pressure_delta = input_starting_pressure - target_pressure
+ if (REGULATE_OUTPUT)
+ pressure_delta = target_pressure - output_starting_pressure
+
+ var/flowing_old = flowing
+ if((regulate_mode == REGULATE_NONE || pressure_delta > 0.01) && (air1.temperature > 0 || air2.temperature > 0)) //since it's basically a valve, it makes sense to check both temperatures
+ flowing = 1
+
+ //flow rate limit
+ var/transfer_moles = (set_flow_rate/air1.volume)*air1.total_moles
+
+ //Figure out how much gas to transfer to meet the target pressure.
+ switch (regulate_mode)
+ if (REGULATE_INPUT)
+ var/air_temperature = (air1.temperature > 0)? air1.temperature : air2.temperature
+ var/input_volume = air1.volume + (network1? network1.volume : 0)
+ transfer_moles = min(transfer_moles, pressure_delta*input_volume/(air_temperature * R_IDEAL_GAS_EQUATION))
+ if (REGULATE_OUTPUT)
+ var/air_temperature = (air2.temperature > 0)? air2.temperature : air1.temperature
+ var/output_volume = air2.volume + (network2? network2.volume : 0)
+
+ transfer_moles = min(transfer_moles, pressure_delta*output_volume/(air_temperature * R_IDEAL_GAS_EQUATION))
+
+ //pump_gas() will return a negative number if no flow occurred
+ var/flow = pump_gas(src, air1, air2, transfer_moles, available_power=0) //available_power=0 means we only move gas if it would flow naturally
+
+ if (flow >= 0)
if(network1)
network1.update = 1
if(network2)
network2.update = 1
+ else
+ flowing = 0
+ last_flow_rate = 0
+
+ if (flowing != flowing_old)
+ update_icon()
- //Radio remote control
+//Radio remote control
- proc
- set_frequency(new_frequency)
- radio_controller.remove_object(src, frequency)
- frequency = new_frequency
- if(frequency)
- radio_connection = radio_controller.add_object(src, frequency, filter = RADIO_ATMOSIA)
+/obj/machinery/atmospherics/binary/passive_gate/proc/set_frequency(new_frequency)
+ radio_controller.remove_object(src, frequency)
+ frequency = new_frequency
+ if(frequency)
+ radio_connection = radio_controller.add_object(src, frequency, filter = RADIO_ATMOSIA)
- broadcast_status()
- if(!radio_connection)
- return 0
+/obj/machinery/atmospherics/binary/passive_gate/proc/broadcast_status()
+ if(!radio_connection)
+ return 0
- var/datum/signal/signal = new
- signal.transmission_method = 1 //radio signal
- signal.source = src
+ var/datum/signal/signal = new
+ signal.transmission_method = 1 //radio signal
+ signal.source = src
- signal.data = list(
- "tag" = id,
- "device" = "AGP",
- "power" = on,
- "target_output" = target_pressure,
- "sigtype" = "status"
- )
+ signal.data = list(
+ "tag" = id,
+ "device" = "AGP",
+ "power" = on,
+ "target_output" = target_pressure,
+ "regulate_mode" = regulate_mode,
+ "set_flow_rate" = set_flow_rate,
+ "sigtype" = "status"
+ )
- radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA)
+ radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA)
- return 1
+ return 1
- interact(mob/user as mob)
- var/dat = {"Power: [on?"On":"Off"]
- Desirable output pressure:
- [round(target_pressure,0.1)]kPa | Change
- "}
+/obj/machinery/atmospherics/binary/passive_gate/initialize()
+ ..()
+ if(frequency)
+ set_frequency(frequency)
- user << browse("
[src.name] control[dat]", "window=atmo_pump")
- onclose(user, "atmo_pump")
+/obj/machinery/atmospherics/binary/passive_gate/receive_signal(datum/signal/signal)
+ if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command"))
+ return 0
- initialize()
- ..()
- if(frequency)
- set_frequency(frequency)
+ if("power" in signal.data)
+ on = text2num(signal.data["power"])
- receive_signal(datum/signal/signal)
- if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command"))
- return 0
+ if("power_toggle" in signal.data)
+ on = !on
- if("power" in signal.data)
- on = text2num(signal.data["power"])
+ if("set_target_pressure" in signal.data)
+ target_pressure = between(
+ 0,
+ text2num(signal.data["set_target_pressure"]),
+ max_pressure_setting
+ )
- if("power_toggle" in signal.data)
- on = !on
+ if("set_regulate_mode" in signal.data)
+ regulate_mode = text2num(signal.data["set_regulate_mode"])
- if("set_output_pressure" in signal.data)
- target_pressure = between(
- 0,
- text2num(signal.data["set_output_pressure"]),
- ONE_ATMOSPHERE*50
- )
-
- if("status" in signal.data)
- spawn(2)
- broadcast_status()
- return //do not update_icon
+ if("set_flow_rate" in signal.data)
+ regulate_mode = text2num(signal.data["set_flow_rate"])
+ if("status" in signal.data)
spawn(2)
broadcast_status()
- update_icon()
+ return //do not update_icon
+
+ spawn(2)
+ broadcast_status()
+ update_icon()
+ return
+
+/obj/machinery/atmospherics/binary/passive_gate/attack_hand(user as mob)
+ if(..())
+ return
+ src.add_fingerprint(usr)
+ if(!src.allowed(user))
+ user << "\red Access denied."
+ return
+ usr.set_machine(src)
+ ui_interact(user)
+ return
+
+/obj/machinery/atmospherics/binary/passive_gate/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
+ if(stat & (BROKEN|NOPOWER))
return
+ // this is the data which will be sent to the ui
+ var/data[0]
+
+ data = list(
+ "on" = on,
+ "pressure_set" = round(target_pressure*100), //Nano UI can't handle rounded non-integers, apparently.
+ "max_pressure" = max_pressure_setting,
+ "input_pressure" = round(air1.return_pressure()*100),
+ "output_pressure" = round(air2.return_pressure()*100),
+ "regulate_mode" = regulate_mode,
+ "set_flow_rate" = round(set_flow_rate*10),
+ "last_flow_rate" = round(last_flow_rate*10),
+ )
+
+ // update the ui if it exists, returns null if no ui is passed/found
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
+ if (!ui)
+ // the ui does not exist, so we'll create a new() one
+ // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
+ ui = new(user, src, ui_key, "pressure_regulator.tmpl", name, 470, 370)
+ ui.set_initial_data(data) // when the ui is first opened this is the data it will use
+ ui.open() // open the new ui window
+ ui.set_auto_update(1) // auto update every Master Controller tick
- attack_hand(user as mob)
- if(..())
- return
- src.add_fingerprint(usr)
- if(!src.allowed(user))
- user << "\red Access denied."
- return
- usr.set_machine(src)
- interact(user)
- return
-
- Topic(href,href_list)
- if(..()) return
- if(href_list["power"])
- on = !on
- if(href_list["set_press"])
- var/new_pressure = input(usr,"Enter new output pressure (0-4500kPa)","Pressure control",src.target_pressure) as num
- src.target_pressure = max(0, min(4500, new_pressure))
- usr.set_machine(src)
- src.update_icon()
- src.updateUsrDialog()
- return
-
- power_change()
- ..()
- update_icon()
-
-
-
- attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
- if (!istype(W, /obj/item/weapon/wrench))
- return ..()
- if (on)
- user << "\red You cannot unwrench this [src], turn it off first."
- return 1
- var/turf/T = src.loc
- if (level==1 && isturf(T) && T.intact)
- user << "\red You must remove the plating first."
- return 1
- var/datum/gas_mixture/int_air = return_air()
- var/datum/gas_mixture/env_air = loc.return_air()
- if ((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE)
- user << "\red You cannot unwrench this [src], it too exerted due to internal pressure."
- add_fingerprint(user)
- return 1
- playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
- user << "\blue You begin to unfasten \the [src]..."
- if (do_after(user, 40))
- user.visible_message( \
- "[user] unfastens \the [src].", \
- "\blue You have unfastened \the [src].", \
- "You hear ratchet.")
- new /obj/item/pipe(loc, make_from=src)
- del(src)
+/obj/machinery/atmospherics/binary/passive_gate/Topic(href,href_list)
+ if(..()) return
+
+ if(href_list["toggle_valve"])
+ on = !on
+
+ if(href_list["regulate_mode"])
+ switch(href_list["regulate_mode"])
+ if ("off") regulate_mode = REGULATE_NONE
+ if ("input") regulate_mode = REGULATE_INPUT
+ if ("output") regulate_mode = REGULATE_OUTPUT
+
+ switch(href_list["set_press"])
+ if ("min")
+ target_pressure = 0
+ if ("max")
+ target_pressure = max_pressure_setting
+ if ("set")
+ var/new_pressure = input(usr,"Enter new output pressure (0-[max_pressure_setting]kPa)","Pressure Control",src.target_pressure) as num
+ src.target_pressure = between(0, new_pressure, max_pressure_setting)
+
+ switch(href_list["set_flow_rate"])
+ if ("min")
+ set_flow_rate = 0
+ if ("max")
+ set_flow_rate = air1.volume
+ if ("set")
+ var/new_flow_rate = input(usr,"Enter new flow rate limit (0-[air1.volume]kPa)","Flow Rate Control",src.set_flow_rate) as num
+ src.set_flow_rate = between(0, new_flow_rate, air1.volume)
+
+ usr.set_machine(src) //Is this even needed with NanoUI?
+ src.update_icon()
+ src.add_fingerprint(usr)
+ return
+/obj/machinery/atmospherics/binary/passive_gate/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
+ if (!istype(W, /obj/item/weapon/wrench))
+ return ..()
+ if (on)
+ user << "\red You cannot unwrench this [src], turn it off first."
+ return 1
+ var/datum/gas_mixture/int_air = return_air()
+ var/datum/gas_mixture/env_air = loc.return_air()
+ if ((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE)
+ user << "\red You cannot unwrench this [src], it too exerted due to internal pressure."
+ add_fingerprint(user)
+ return 1
+ playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
+ user << "\blue You begin to unfasten \the [src]..."
+ if (do_after(user, 40))
+ user.visible_message( \
+ "[user] unfastens \the [src].", \
+ "\blue You have unfastened \the [src].", \
+ "You hear ratchet.")
+ new /obj/item/pipe(loc, make_from=src)
+ del(src)
diff --git a/code/ATMOSPHERICS/components/binary_devices/pump.dm b/code/ATMOSPHERICS/components/binary_devices/pump.dm
index 0a0a56fb747..fbcd70f4e29 100644
--- a/code/ATMOSPHERICS/components/binary_devices/pump.dm
+++ b/code/ATMOSPHERICS/components/binary_devices/pump.dm
@@ -12,194 +12,238 @@ Thus, the two variables affect pump operation are set in New():
but overall network volume is also increased as this increases...
*/
-obj/machinery/atmospherics/binary/pump
- icon = 'icons/obj/atmospherics/pump.dmi'
- icon_state = "intact_off"
+/obj/machinery/atmospherics/binary/pump
+ icon = 'icons/atmos/pump.dmi'
+ icon_state = "map_off"
+ level = 1
- name = "Gas pump"
+ name = "gas pump"
desc = "A pump"
var/on = 0
var/target_pressure = ONE_ATMOSPHERE
+ //var/max_volume_transfer = 10000
+
+ use_power = 1
+ idle_power_usage = 150 //internal circuitry, friction losses and stuff
+ active_power_usage = 7500 //This also doubles as a measure of how powerful the pump is, in Watts. 7500 W ~ 10 HP
+
+ var/last_power_draw = 0 //for UI
+ var/max_pressure_setting = 15000 //kPa
+
var/frequency = 0
var/id = null
var/datum/radio_frequency/radio_connection
- highcap
- name = "High capacity gas pump"
- desc = "A high capacity pump"
+/obj/machinery/atmospherics/binary/pump/New()
+ ..()
+ air1.volume = ATMOS_DEFAULT_VOLUME_PUMP
+ air2.volume = ATMOS_DEFAULT_VOLUME_PUMP
- target_pressure = 15000000
+/obj/machinery/atmospherics/binary/pump/on
+ icon_state = "map_on"
+ on = 1
- on
- on = 1
- icon_state = "intact_on"
- update_icon()
- if(stat & NOPOWER)
- icon_state = "intact_off"
- else if(node1 && node2)
- icon_state = "intact_[on?("on"):("off")]"
- else
- if(node1)
- icon_state = "exposed_1_off"
- else if(node2)
- icon_state = "exposed_2_off"
- else
- icon_state = "exposed_3_off"
+/obj/machinery/atmospherics/binary/pump/update_icon()
+ if(!powered())
+ icon_state = "off"
+ else
+ icon_state = "[on ? "on" : "off"]"
+
+/obj/machinery/atmospherics/binary/pump/update_underlays()
+ if(..())
+ underlays.Cut()
+ var/turf/T = get_turf(src)
+ if(!istype(T))
+ return
+ add_underlay(T, node1, turn(dir, -180))
+ add_underlay(T, node2, dir)
+
+/obj/machinery/atmospherics/binary/pump/hide(var/i)
+ update_underlays()
+
+/obj/machinery/atmospherics/binary/pump/process()
+ if((stat & (NOPOWER|BROKEN)) || !on)
+ update_use_power(0) //usually we get here because a player turned a pump off - definitely want to update.
+ last_power_draw = 0
+ last_flow_rate = 0
return
- process()
-// ..()
- if(stat & (NOPOWER|BROKEN))
- return
- if(!on)
- return 0
+ var/power_draw = -1
+ var/pressure_delta = target_pressure - air2.return_pressure()
- var/output_starting_pressure = air2.return_pressure()
+ if(pressure_delta > 0.01 && air1.temperature > 0)
+ //Figure out how much gas to transfer to meet the target pressure.
+ var/air_temperature = (air2.temperature > 0)? air2.temperature : air1.temperature
+ var/output_volume = air2.volume + (network2? network2.volume : 0)
+
+ //get the number of moles that would have to be transfered to bring sink to the target pressure
+ var/transfer_moles = pressure_delta*output_volume/(air_temperature * R_IDEAL_GAS_EQUATION)
+
+ power_draw = pump_gas(src, air1, air2, transfer_moles, active_power_usage)
+
+ if (power_draw < 0)
+ //update_use_power(0)
+ use_power = 0 //don't force update - easier on CPU
+ last_power_draw = 0
+ last_flow_rate = 0
+ else
+ last_power_draw = handle_power_draw(power_draw)
+
+ if(network1)
+ network1.update = 1
- if( (target_pressure - output_starting_pressure) < 0.01)
- //No need to pump gas if target is already reached!
- return 1
+ if(network2)
+ network2.update = 1
- //Calculate necessary moles to transfer using PV=nRT
- if((air1.total_moles() > 0) && (air1.temperature>0))
- var/pressure_delta = target_pressure - output_starting_pressure
- var/transfer_moles = pressure_delta*air2.volume/(air1.temperature * R_IDEAL_GAS_EQUATION)
+ return 1
- //Actually transfer the gas
- var/datum/gas_mixture/removed = air1.remove(transfer_moles)
- air2.merge(removed)
+//Radio remote control
- if(network1)
- network1.update = 1
+/obj/machinery/atmospherics/binary/pump/proc/set_frequency(new_frequency)
+ radio_controller.remove_object(src, frequency)
+ frequency = new_frequency
+ if(frequency)
+ radio_connection = radio_controller.add_object(src, frequency, filter = RADIO_ATMOSIA)
- if(network2)
- network2.update = 1
+/obj/machinery/atmospherics/binary/pump/proc/broadcast_status()
+ if(!radio_connection)
+ return 0
- return 1
+ var/datum/signal/signal = new
+ signal.transmission_method = 1 //radio signal
+ signal.source = src
- //Radio remote control
+ signal.data = list(
+ "tag" = id,
+ "device" = "AGP",
+ "power" = on,
+ "target_output" = target_pressure,
+ "sigtype" = "status"
+ )
- proc
- set_frequency(new_frequency)
- radio_controller.remove_object(src, frequency)
- frequency = new_frequency
- if(frequency)
- radio_connection = radio_controller.add_object(src, frequency, filter = RADIO_ATMOSIA)
+ radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA)
- broadcast_status()
- if(!radio_connection)
- return 0
+ return 1
- var/datum/signal/signal = new
- signal.transmission_method = 1 //radio signal
- signal.source = src
+/obj/machinery/atmospherics/binary/pump/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
+ if(stat & (BROKEN|NOPOWER))
+ return
- signal.data = list(
- "tag" = id,
- "device" = "AGP",
- "power" = on,
- "target_output" = target_pressure,
- "sigtype" = "status"
- )
+ // this is the data which will be sent to the ui
+ var/data[0]
+
+ data = list(
+ "on" = on,
+ "pressure_set" = round(target_pressure*100), //Nano UI can't handle rounded non-integers, apparently.
+ "max_pressure" = max_pressure_setting,
+ "last_flow_rate" = round(last_flow_rate*10),
+ "last_power_draw" = round(last_power_draw),
+ "max_power_draw" = active_power_usage,
+ )
- radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA)
+ // update the ui if it exists, returns null if no ui is passed/found
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
+ if (!ui)
+ // the ui does not exist, so we'll create a new() one
+ // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
+ ui = new(user, src, ui_key, "gas_pump.tmpl", name, 470, 290)
+ ui.set_initial_data(data) // when the ui is first opened this is the data it will use
+ ui.open() // open the new ui window
+ ui.set_auto_update(1) // auto update every Master Controller tick
- return 1
+/obj/machinery/atmospherics/binary/pump/initialize()
+ ..()
+ if(frequency)
+ set_frequency(frequency)
- interact(mob/user as mob)
- var/dat = {"Power: [on?"On":"Off"]
- Desirable output pressure:
- [round(target_pressure,0.1)]kPa | Change
- "}
+/obj/machinery/atmospherics/binary/pump/receive_signal(datum/signal/signal)
+ if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command"))
+ return 0
- user << browse("[src.name] control[dat]", "window=atmo_pump")
- onclose(user, "atmo_pump")
+ if(signal.data["power"])
+ if(text2num(signal.data["power"]))
+ on = 1
+ else
+ on = 0
- initialize()
- ..()
- if(frequency)
- set_frequency(frequency)
+ if("power_toggle" in signal.data)
+ on = !on
- receive_signal(datum/signal/signal)
- if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command"))
- return 0
-
- if("power" in signal.data)
- on = text2num(signal.data["power"])
-
- if("power_toggle" in signal.data)
- on = !on
-
- if("set_output_pressure" in signal.data)
- target_pressure = between(
- 0,
- text2num(signal.data["set_output_pressure"]),
- ONE_ATMOSPHERE*50
- )
-
- if("status" in signal.data)
- spawn(2)
- broadcast_status()
- return //do not update_icon
+ if(signal.data["set_output_pressure"])
+ target_pressure = between(
+ 0,
+ text2num(signal.data["set_output_pressure"]),
+ ONE_ATMOSPHERE*50
+ )
+ if(signal.data["status"])
spawn(2)
broadcast_status()
- update_icon()
+ return //do not update_icon
+
+ spawn(2)
+ broadcast_status()
+ update_icon()
+ return
+
+/obj/machinery/atmospherics/binary/pump/attack_hand(user as mob)
+ if(..())
return
-
-
- attack_hand(user as mob)
- if(..())
- return
- src.add_fingerprint(usr)
- if(!src.allowed(user))
- user << "\red Access denied."
- return
- usr.set_machine(src)
- interact(user)
+ src.add_fingerprint(usr)
+ if(!src.allowed(user))
+ user << "\red Access denied."
return
+ usr.set_machine(src)
+ ui_interact(user)
+ return
- Topic(href,href_list)
- if(..()) return
- if(href_list["power"])
- on = !on
- if(href_list["set_press"])
- var/new_pressure = input(usr,"Enter new output pressure (0-4500kPa)","Pressure control",src.target_pressure) as num
- src.target_pressure = max(0, min(4500, new_pressure))
- usr.set_machine(src)
- src.update_icon()
- src.updateUsrDialog()
- return
+/obj/machinery/atmospherics/binary/pump/Topic(href,href_list)
+ if(..()) return
+
+ if(href_list["power"])
+ on = !on
+
+ switch(href_list["set_press"])
+ if ("min")
+ target_pressure = 0
+ if ("max")
+ target_pressure = max_pressure_setting
+ if ("set")
+ var/new_pressure = input(usr,"Enter new output pressure (0-[max_pressure_setting]kPa)","Pressure control",src.target_pressure) as num
+ src.target_pressure = between(0, new_pressure, max_pressure_setting)
+
+ usr.set_machine(src)
+ src.add_fingerprint(usr)
+
+ src.update_icon()
- power_change()
- ..()
+/obj/machinery/atmospherics/binary/pump/power_change()
+ var/old_stat = stat
+ ..()
+ if(old_stat != stat)
update_icon()
- attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
- if (!istype(W, /obj/item/weapon/wrench))
- return ..()
- if (!(stat & NOPOWER) && on)
- user << "\red You cannot unwrench this [src], turn it off first."
- return 1
- var/turf/T = src.loc
- if (level==1 && isturf(T) && T.intact)
- user << "\red You must remove the plating first."
- return 1
- var/datum/gas_mixture/int_air = return_air()
- var/datum/gas_mixture/env_air = loc.return_air()
- if ((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE)
- user << "\red You cannot unwrench this [src], it too exerted due to internal pressure."
- add_fingerprint(user)
- return 1
- playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
- user << "\blue You begin to unfasten \the [src]..."
- if (do_after(user, 40))
- user.visible_message( \
- "[user] unfastens \the [src].", \
- "\blue You have unfastened \the [src].", \
- "You hear ratchet.")
- new /obj/item/pipe(loc, make_from=src)
- del(src)
+/obj/machinery/atmospherics/binary/pump/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
+ if (!istype(W, /obj/item/weapon/wrench))
+ return ..()
+ if (!(stat & NOPOWER) && on)
+ user << "\red You cannot unwrench this [src], turn it off first."
+ return 1
+ var/datum/gas_mixture/int_air = return_air()
+ var/datum/gas_mixture/env_air = loc.return_air()
+ if ((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE)
+ user << "\red You cannot unwrench this [src], it too exerted due to internal pressure."
+ add_fingerprint(user)
+ return 1
+ playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
+ user << "\blue You begin to unfasten \the [src]..."
+ if (do_after(user, 40))
+ user.visible_message( \
+ "[user] unfastens \the [src].", \
+ "\blue You have unfastened \the [src].", \
+ "You hear ratchet.")
+ new /obj/item/pipe(loc, make_from=src)
+ del(src)
diff --git a/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm b/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm
index e2dddfa97c1..b3bee0d12a8 100644
--- a/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm
+++ b/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm
@@ -1,199 +1,19 @@
-/*
-Every cycle, the pump uses the air in air_in to try and make air_out the perfect pressure.
+/obj/machinery/atmospherics/binary/pump/high_power
+ icon = 'icons/atmos/volume_pump.dmi'
+ icon_state = "map_off"
+ level = 1
-node1, air1, network1 correspond to input
-node2, air2, network2 correspond to output
+ name = "high power gas pump"
+ desc = "A pump. Has double the power rating of the standard gas pump."
-Thus, the two variables affect pump operation are set in New():
- air1.volume
- This is the volume of gas available to the pump that may be transfered to the output
- air2.volume
- Higher quantities of this cause more air to be perfected later
- but overall network volume is also increased as this increases...
-*/
+ active_power_usage = 15000 //This also doubles as a measure of how powerful the pump is, in Watts. 15000 W ~ 20 HP
-obj/machinery/atmospherics/binary/volume_pump
- icon = 'icons/obj/atmospherics/volume_pump.dmi'
- icon_state = "intact_off"
-
- name = "Volumetric gas pump"
- desc = "A volumetric pump"
-
- var/on = 0
- var/transfer_rate = 200
-
- var/frequency = 0
- var/id = null
- var/datum/radio_frequency/radio_connection
-
- on
- on = 1
- icon_state = "intact_on"
-
- update_icon()
- if(stat & NOPOWER)
- icon_state = "intact_off"
- else if(node1 && node2)
- icon_state = "intact_[on?("on"):("off")]"
- else
- if(node1)
- icon_state = "exposed_1_off"
- else if(node2)
- icon_state = "exposed_2_off"
- else
- icon_state = "exposed_3_off"
- return
-
- process()
-// ..()
- if(stat & (NOPOWER|BROKEN))
- return
- if(!on)
- return 0
-
-// Pump mechanism just won't do anything if the pressure is too high/too low
-
- var/input_starting_pressure = air1.return_pressure()
- var/output_starting_pressure = air2.return_pressure()
-
- if((input_starting_pressure < 0.01) || (output_starting_pressure > 9000))
- return 1
-
- var/transfer_ratio = max(1, transfer_rate/air1.volume)
-
- var/datum/gas_mixture/removed = air1.remove_ratio(transfer_ratio)
-
- air2.merge(removed)
-
- if(network1)
- network1.update = 1
-
- if(network2)
- network2.update = 1
-
- return 1
-
- proc
- set_frequency(new_frequency)
- radio_controller.remove_object(src, frequency)
- frequency = new_frequency
- if(frequency)
- radio_connection = radio_controller.add_object(src, frequency)
-
- broadcast_status()
- if(!radio_connection)
- return 0
-
- var/datum/signal/signal = new
- signal.transmission_method = 1 //radio signal
- signal.source = src
-
- signal.data = list(
- "tag" = id,
- "device" = "APV",
- "power" = on,
- "transfer_rate" = transfer_rate,
- "sigtype" = "status"
- )
- radio_connection.post_signal(src, signal)
-
- return 1
-
- interact(mob/user as mob)
- var/dat = {"Power: [on?"On":"Off"]
- Desirable output flow:
- [round(transfer_rate,1)]l/s | Change
- "}
-
- user << browse("[src.name] control[dat]", "window=atmo_pump")
- onclose(user, "atmo_pump")
-
-
-
- initialize()
- ..()
-
- set_frequency(frequency)
-
- receive_signal(datum/signal/signal)
- if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command"))
- return 0
-
- if("power" in signal.data)
- on = text2num(signal.data["power"])
-
- if("power_toggle" in signal.data)
- on = !on
-
- if("set_transfer_rate" in signal.data)
- transfer_rate = between(
- 0,
- text2num(signal.data["set_transfer_rate"]),
- air1.volume
- )
-
- if("status" in signal.data)
- spawn(2)
- broadcast_status()
- return //do not update_icon
-
- spawn(2)
- broadcast_status()
- update_icon()
-
-
- attack_hand(user as mob)
- if(..())
- return
- src.add_fingerprint(usr)
- if(!src.allowed(user))
- user << "\red Access denied."
- return
- usr.set_machine(src)
- interact(user)
- return
-
- Topic(href,href_list)
- if(..()) return
- if(href_list["power"])
- on = !on
- if(href_list["set_transfer_rate"])
- var/new_transfer_rate = input(usr,"Enter new output volume (0-200l/s)","Flow control",src.transfer_rate) as num
- src.transfer_rate = max(0, min(200, new_transfer_rate))
- usr.set_machine(src)
- src.update_icon()
- src.updateUsrDialog()
- return
-
- power_change()
- ..()
- update_icon()
-
-
-
- attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
- if (!istype(W, /obj/item/weapon/wrench))
- return ..()
- if (!(stat & NOPOWER) && on)
- user << "\red You cannot unwrench this [src], turn it off first."
- return 1
- var/turf/T = src.loc
- if (level==1 && isturf(T) && T.intact)
- user << "\red You must remove the plating first."
- return 1
- var/datum/gas_mixture/int_air = return_air()
- var/datum/gas_mixture/env_air = loc.return_air()
- if ((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE)
- user << "\red You cannot unwrench this [src], it too exerted due to internal pressure."
- add_fingerprint(user)
- return 1
- playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
- user << "\blue You begin to unfasten \the [src]..."
- if (do_after(user, 40))
- user.visible_message( \
- "[user] unfastens \the [src].", \
- "\blue You have unfastened \the [src].", \
- "You hear ratchet.")
- new /obj/item/pipe(loc, make_from=src)
- del(src)
+/obj/machinery/atmospherics/binary/pump/high_power/on
+ on = 1
+ icon_state = "map_on"
+/obj/machinery/atmospherics/binary/pump/high_power/update_icon()
+ if(!powered())
+ icon_state = "off"
+ else
+ icon_state = "[on ? "on" : "off"]"
\ No newline at end of file
diff --git a/code/ATMOSPHERICS/components/omni_devices/_omni_extras.dm b/code/ATMOSPHERICS/components/omni_devices/_omni_extras.dm
index ae827881ad5..a1913709246 100644
--- a/code/ATMOSPHERICS/components/omni_devices/_omni_extras.dm
+++ b/code/ATMOSPHERICS/components/omni_devices/_omni_extras.dm
@@ -11,33 +11,6 @@
#define ATM_P 6 //Phoron
#define ATM_N2O 7
-//--------------------------------------------
-// Omni device cached icon list
-//--------------------------------------------
-var/global/list/omni_icons[]
-
-/proc/gen_omni_icons()
- omni_icons = new()
- var/icon/omni = new('icons/obj/atmospherics/omni_devices.dmi')
-
- for(var/state in omni.IconStates())
- if(!state || findtext(state, "map"))
- continue
-
- var/image/I = image('icons/obj/atmospherics/omni_devices.dmi', icon_state = state)
-
- if(findtext(state, "pipe"))
- for(var/pipe_color in pipe_colors)
- I = image('icons/obj/atmospherics/omni_devices.dmi', icon_state = state)
- I.color = pipe_colors[pipe_color]
- var/cache_name = state
- if(I.color)
- cache_name += "_[pipe_colors[pipe_color]]"
- omni_icons[cache_name] = I
- else
- omni_icons[state] = I
-
-
//--------------------------------------------
// Omni port datum
//
@@ -83,16 +56,6 @@ var/global/list/omni_icons[]
// Need to find somewhere else for these
//--------------------------------------------
-#define PIPE_COLOR_RED "#ff0000"
-#define PIPE_COLOR_BLUE "#0000ff"
-#define PIPE_COLOR_CYAN "#00ffff"
-#define PIPE_COLOR_GREEN "#00ff00"
-#define PIPE_COLOR_YELLOW "#ffcc00"
-#define PIPE_COLOR_PURPLE "#5c1ec0"
-
-var/global/list/pipe_colors = list("grey" = null, "red" = PIPE_COLOR_RED, "blue" = PIPE_COLOR_BLUE, "cyan" = PIPE_COLOR_CYAN, "green" = PIPE_COLOR_GREEN, "yellow" = PIPE_COLOR_YELLOW, "purple" = PIPE_COLOR_PURPLE)
-
-
//returns a text string based on the direction flag input
// if capitalize is true, it will return the string capitalized
// otherwise it will return the direction string in lower case
@@ -127,4 +90,20 @@ var/global/list/pipe_colors = list("grey" = null, "red" = PIPE_COLOR_RED, "blue"
if("west")
return WEST
else
- return 0
\ No newline at end of file
+ return 0
+
+/proc/mode_to_gasid(var/mode)
+ switch(mode)
+ if(ATM_O2)
+ return "oxygen"
+ if(ATM_N2)
+ return "nitrogen"
+ if(ATM_CO2)
+ return "carbon_dioxide"
+ if(ATM_P)
+ return "phoron"
+ if(ATM_N2O)
+ return "sleeping_agent"
+ else
+ return null
+
\ No newline at end of file
diff --git a/code/ATMOSPHERICS/components/omni_devices/filter.dm b/code/ATMOSPHERICS/components/omni_devices/filter.dm
index d52090b0c7c..e5188b7da49 100644
--- a/code/ATMOSPHERICS/components/omni_devices/filter.dm
+++ b/code/ATMOSPHERICS/components/omni_devices/filter.dm
@@ -8,6 +8,21 @@
var/list/filters = new()
var/datum/omni_port/input
var/datum/omni_port/output
+
+ use_power = 1
+ idle_power_usage = 150 //internal circuitry, friction losses and stuff
+ active_power_usage = 7500 //This also doubles as a measure of how powerful the filter is, in Watts. 7500 W ~ 10 HP
+
+ var/max_flow_rate = 200
+ var/set_flow_rate = 200
+
+ var/list/filtering_outputs = list() //maps gasids to gas_mixtures
+
+/obj/machinery/atmospherics/omni/filter/New()
+ ..()
+ rebuild_filtering_list()
+ for(var/datum/omni_port/P in ports)
+ P.air.volume = ATMOS_DEFAULT_VOLUME_FILTER
/obj/machinery/atmospherics/omni/filter/Del()
input = null
@@ -37,89 +52,56 @@
/obj/machinery/atmospherics/omni/filter/error_check()
if(!input || !output || !filters)
return 1
- if(filters.len < 1 || filters.len > 2) //requires 1 or 2 filters ~otherwise why are you using a filter?
+ if(filters.len < 1) //requires at least 1 filter ~otherwise why are you using a filter?
return 1
return 0
/obj/machinery/atmospherics/omni/filter/process()
..()
- if(!on)
- return 0
+ if(error_check())
+ on = 0
- if(!input || !output)
+ if((stat & (NOPOWER|BROKEN)) || !on)
+ update_use_power(0) //usually we get here because a player turned a pump off - definitely want to update.
+ last_flow_rate = 0
return
-
+
var/datum/gas_mixture/output_air = output.air //BYOND doesn't like referencing "output.air.return_pressure()" so we need to make a direct reference
var/datum/gas_mixture/input_air = input.air // it's completely happy with them if they're in a loop though i.e. "P.air.return_pressure()"... *shrug*
-
- var/output_pressure = output_air.return_pressure()
- if(output_pressure >= target_pressure)
- return
- for(var/datum/omni_port/P in filters)
- if(P.air.return_pressure() >= target_pressure)
- return
-
- var/pressure_delta = target_pressure - output_pressure
-
- if(input_air.return_temperature() > 0)
- input.transfer_moles = pressure_delta * output_air.volume / (input_air.return_temperature() * R_IDEAL_GAS_EQUATION)
-
- if(input.transfer_moles > 0)
- var/datum/gas_mixture/removed = input_air.remove(input.transfer_moles)
+ //Figure out the amount of moles to transfer
+ var/transfer_moles = (set_flow_rate/input_air.volume)*input_air.total_moles
+
+ var/power_draw = -1
+ if (transfer_moles > MINUMUM_MOLES_TO_FILTER)
+ power_draw = filter_gas_multi(src, filtering_outputs, input_air, output_air, transfer_moles, active_power_usage)
+
+ if (power_draw < 0)
+ //update_use_power(0)
+ use_power = 0 //don't force update - easier on CPU
+ last_flow_rate = 0
+ else
+ handle_power_draw(power_draw)
- if(!removed)
- return
-
- for(var/datum/omni_port/P in filters)
- var/datum/gas_mixture/filtered_out = new
- filtered_out.temperature = removed.return_temperature()
-
- switch(P.mode)
- if(ATM_O2)
- filtered_out.oxygen = removed.oxygen
- removed.oxygen = 0
- if(ATM_N2)
- filtered_out.nitrogen = removed.nitrogen
- removed.nitrogen = 0
- if(ATM_CO2)
- filtered_out.carbon_dioxide = removed.carbon_dioxide
- removed.carbon_dioxide = 0
- if(ATM_P)
- filtered_out.phoron = removed.phoron
- removed.phoron = 0
- if(ATM_N2O)
- if(removed.trace_gases.len>0)
- for(var/datum/gas/sleeping_agent/trace_gas in removed.trace_gases)
- if(istype(trace_gas))
- removed.trace_gases -= trace_gas
- filtered_out.trace_gases += trace_gas
- else
- filtered_out = null
-
- P.air.merge(filtered_out)
- if(P.network)
- P.network.update = 1
-
- output_air.merge(removed)
- if(output.network)
- output.network.update = 1
-
- input.transfer_moles = 0
if(input.network)
input.network.update = 1
+ if(output.network)
+ output.network.update = 1
+ for(var/datum/omni_port/P in filters)
+ if(P.network)
+ P.network.update = 1
+
+ return 1
- return
-
-/obj/machinery/atmospherics/omni/filter/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null)
+/obj/machinery/atmospherics/omni/filter/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
usr.set_machine(src)
var/list/data = new()
data = build_uidata()
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, data)
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "omni_filter.tmpl", "Omni Filter Control", 330, 330)
@@ -161,7 +143,8 @@
if(portData.len)
data["ports"] = portData
if(output)
- data["pressure"] = target_pressure
+ data["set_flow_rate"] = round(set_flow_rate*10) //because nanoui can't handle rounded decimals.
+ data["last_flow_rate"] = round(last_flow_rate*10)
return data
@@ -196,9 +179,9 @@
//only allows config changes when in configuring mode ~otherwise you'll get weird pressure stuff going on
if(configuring && !on)
switch(href_list["command"])
- if("set_pressure")
- var/new_pressure = input(usr,"Enter new output pressure (0-4500kPa)","Pressure control",target_pressure) as num
- target_pressure = between(0, new_pressure, 4500)
+ if("set_flow_rate")
+ var/new_flow_rate = input(usr,"Enter new flow rate limit (0-[max_flow_rate]L/s)","Flow Rate Control",set_flow_rate) as num
+ set_flow_rate = between(0, new_flow_rate, max_flow_rate)
if("switch_mode")
switch_mode(dir_flag(href_list["dir"]), mode_return_switch(href_list["mode"]))
if("switch_filter")
@@ -257,6 +240,7 @@
target_port.mode = mode
if(target_port.mode != previous_mode)
handle_port_change(target_port)
+ rebuild_filtering_list()
else
return
else
@@ -268,9 +252,16 @@
P.mode = previous_mode
if(P.mode != old_mode)
handle_port_change(P)
-
+
update_ports()
+/obj/machinery/atmospherics/omni/filter/proc/rebuild_filtering_list()
+ filtering_outputs.Cut()
+ for(var/datum/omni_port/P in ports)
+ var/gasid = mode_to_gasid(P.mode)
+ if(gasid)
+ filtering_outputs[gasid] = P.air
+
/obj/machinery/atmospherics/omni/filter/proc/handle_port_change(var/datum/omni_port/P)
switch(P.mode)
if(ATM_NONE)
diff --git a/code/ATMOSPHERICS/components/omni_devices/mixer.dm b/code/ATMOSPHERICS/components/omni_devices/mixer.dm
index 64af4057b00..89c148a943c 100644
--- a/code/ATMOSPHERICS/components/omni_devices/mixer.dm
+++ b/code/ATMOSPHERICS/components/omni_devices/mixer.dm
@@ -5,14 +5,23 @@
name = "omni gas mixer"
icon_state = "map_mixer"
+ use_power = 1
+ idle_power_usage = 150 //internal circuitry, friction losses and stuff
+ active_power_usage = 3700 //This also doubles as a measure of how powerful the mixer is, in Watts. 3700 W ~ 5 HP
+
var/list/inputs = new()
var/datum/omni_port/output
//setup tags for initial concentration values (must be decimal)
- var/tag_north_con
+ var/tag_north_con
var/tag_south_con
var/tag_east_con
var/tag_west_con
+
+ var/max_flow_rate = 200
+ var/set_flow_rate = 200
+
+ var/list/mixing_inputs = list()
/obj/machinery/atmospherics/omni/mixer/New()
..()
@@ -36,13 +45,11 @@
if(tag_west_con && tag_west == 1)
P.concentration = tag_west_con
con += max(0, tag_west_con)
+
+ for(var/datum/omni_port/P in ports)
+ P.air.volume = ATMOS_DEFAULT_VOLUME_MIXER
- //mappers who are bad at maths will be punished (total concentration must be 100%)
- if(con != 1)
- tag_north_con = null
- tag_south_con = null
- tag_east_con = null
- tag_west_con = null
+ rebuild_mixing_inputs()
/obj/machinery/atmospherics/omni/mixer/Del()
inputs.Cut()
@@ -57,7 +64,6 @@
if(inputs.Find(P))
inputs -= P
- P.air.volume = 200
switch(P.mode)
if(ATM_INPUT)
inputs += P
@@ -69,7 +75,7 @@
P.concentration = 1 / max(1, inputs.len)
if(output)
- output.air.volume *= 0.75 * inputs.len
+ output.air.volume = ATMOS_DEFAULT_VOLUME_MIXER * 0.75 * inputs.len
output.concentration = 1
/obj/machinery/atmospherics/omni/mixer/proc/mapper_set()
@@ -78,73 +84,62 @@
/obj/machinery/atmospherics/omni/mixer/error_check()
if(!output || !inputs)
return 1
- if(inputs.len < 2 || inputs.len > 3) //requires 2 or 3 inputs ~otherwise why are you using a mixer?
+ if(inputs.len < 2) //requires at least 2 inputs ~otherwise why are you using a mixer?
+ return 1
+
+ //concentration must add to 1
+ var/total = 0
+ for (var/datum/omni_port/P in inputs)
+ total += P.concentration
+
+ if (total != 1)
return 1
return 0
/obj/machinery/atmospherics/omni/mixer/process()
..()
- if(!on)
- return 0
+ if(error_check())
+ on = 0
+
+ if((stat & (NOPOWER|BROKEN)) || !on)
+ update_use_power(0) //usually we get here because a player turned a pump off - definitely want to update.
+ last_flow_rate = 0
+ return
+
+ //Figure out the amount of moles to transfer
+ var/transfer_moles = 0
+ for (var/datum/omni_port/P in inputs)
+ transfer_moles += (set_flow_rate*P.concentration/P.air.volume)*P.air.total_moles
+
+ var/power_draw = -1
+ if (transfer_moles > MINUMUM_MOLES_TO_FILTER)
+ power_draw = mix_gas(src, mixing_inputs, output, transfer_moles, active_power_usage)
- var/datum/gas_mixture/output_air = output.air
- var/output_pressure = output_air.return_pressure()
-
-
- if(output_pressure >= target_pressure * 0.999)
- //No need to mix if target is already full! - 0.1% margin of error so we minimize processing minor gas volumes
- return 1
-
- //Calculate necessary moles to transfer using PV=nRT
-
- var/pressure_delta = target_pressure - output_pressure
-
- for(var/datum/omni_port/P in inputs)
- if(P.air.return_temperature() > 0)
- P.transfer_moles = (P.concentration * pressure_delta) * output_air.return_volume() / (P.air.return_temperature() * R_IDEAL_GAS_EQUATION)
-
- var/ratio_check = null
-
- for(var/datum/omni_port/P in inputs)
- if(!P.transfer_moles)
- return
- if(P.air.total_moles() < P.transfer_moles)
- ratio_check = 1
- continue
-
- if(ratio_check)
- var/list/ratio_list = new()
+ if (power_draw < 0)
+ //update_use_power(0)
+ use_power = 0 //don't force update - easier on CPU
+ last_flow_rate = 0
+ else
+ handle_power_draw(power_draw)
+
for(var/datum/omni_port/P in inputs)
- ratio_list.Add(P.air.total_moles() / P.transfer_moles)
-
- var/ratio = min(ratio_list)
-
- for(var/datum/omni_port/P in inputs)
- P.transfer_moles *= ratio
-
-
-
- for(var/datum/omni_port/P in inputs)
- if(P.transfer_moles > 0)
- output_air.merge(P.air.remove(P.transfer_moles))
- if(P.network)
+ if(P.concentration && P.network)
P.network.update = 1
- P.transfer_moles = 0
-
- if(output.network)
- output.network.update = 1
+ if(output.network)
+ output.network.update = 1
+
return 1
-/obj/machinery/atmospherics/omni/mixer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null)
+/obj/machinery/atmospherics/omni/mixer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
usr.set_machine(src)
var/list/data = new()
data = build_uidata()
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, data)
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "omni_mixer.tmpl", "Omni Mixer Control", 360, 330)
@@ -180,7 +175,8 @@
if(portData.len)
data["ports"] = portData
if(output)
- data["pressure"] = target_pressure
+ data["set_flow_rate"] = round(set_flow_rate*10) //because nanoui can't handle rounded decimals.
+ data["last_flow_rate"] = round(last_flow_rate*10)
return data
@@ -201,9 +197,9 @@
//only allows config changes when in configuring mode ~otherwise you'll get weird pressure stuff going on
if(configuring && !on)
switch(href_list["command"])
- if("set_pressure")
- var/new_pressure = input(usr,"Enter new output pressure (0-4500kPa)","Pressure control",target_pressure) as num
- target_pressure = between(0, new_pressure, 4500)
+ if("set_flow_rate")
+ var/new_flow_rate = input(usr,"Enter new flow rate limit (0-[max_flow_rate]L/s)","Flow Rate Control",set_flow_rate) as num
+ set_flow_rate = between(0, new_flow_rate, max_flow_rate)
if("switch_mode")
switch_mode(dir_flag(href_list["dir"]), href_list["mode"])
if("switch_con")
@@ -253,6 +249,7 @@
P.update = 1
update_ports()
+ rebuild_mixing_inputs()
/obj/machinery/atmospherics/omni/mixer/proc/change_concentration(var/port = NORTH)
tag_north_con = null
@@ -294,6 +291,13 @@
P.concentration = new_con
else if(!P.con_lock)
P.concentration = remain_con
+
+ rebuild_mixing_inputs()
+
+/obj/machinery/atmospherics/omni/mixer/proc/rebuild_mixing_inputs()
+ mixing_inputs.Cut()
+ for(var/datum/omni_port/P in inputs)
+ mixing_inputs[P.air] = P.concentration
/obj/machinery/atmospherics/omni/mixer/proc/con_lock(var/port = NORTH)
for(var/datum/omni_port/P in inputs)
diff --git a/code/ATMOSPHERICS/components/omni_devices/omni_base.dm b/code/ATMOSPHERICS/components/omni_devices/omni_base.dm
index 0e46546a20a..5a64a517cae 100644
--- a/code/ATMOSPHERICS/components/omni_devices/omni_base.dm
+++ b/code/ATMOSPHERICS/components/omni_devices/omni_base.dm
@@ -3,14 +3,15 @@
//--------------------------------------------
/obj/machinery/atmospherics/omni
name = "omni device"
- icon = 'icons/obj/atmospherics/omni_devices.dmi'
+ icon = 'icons/atmos/omni_devices.dmi'
icon_state = "base"
use_power = 1
initialize_directions = 0
+ level = 1
var/on = 0
var/configuring = 0
- var/target_pressure = ONE_ATMOSPHERE
+ //var/target_pressure = ONE_ATMOSPHERE //a base type as abstract as this should NOT be making these kinds of assumptions
var/tag_north = ATM_NONE
var/tag_south = ATM_NONE
@@ -70,12 +71,6 @@
update_icon()
/obj/machinery/atmospherics/omni/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
- if(istype(W, /obj/item/device/pipe_painter)) //for updating the color of connected pipe ends
- for(var/datum/omni_port/P in ports)
- P.update = 1
- update_ports()
- return
-
if(!istype(W, /obj/item/weapon/wrench))
return ..()
@@ -106,8 +101,8 @@
return
/obj/machinery/atmospherics/omni/proc/build_icons()
- if(!omni_icons)
- gen_omni_icons()
+ if(!check_icon_cache())
+ return
var/core_icon = null
if(istype(src, /obj/machinery/atmospherics/omni/mixer))
@@ -119,13 +114,16 @@
//directional icons are layers 1-4, with the core icon on layer 5
if(core_icon)
- overlays_off[5] = omni_icons[core_icon]
- overlays_on[5] = omni_icons[core_icon + "_glow"]
+ overlays_off[5] = icon_manager.get_atmos_icon("omni", , , core_icon)
+ overlays_on[5] = icon_manager.get_atmos_icon("omni", , , core_icon + "_glow")
- overlays_error[1] = omni_icons[core_icon]
- overlays_error[2] = omni_icons["error"]
+ overlays_error[1] = icon_manager.get_atmos_icon("omni", , , core_icon)
+ overlays_error[2] = icon_manager.get_atmos_icon("omni", , , "error")
/obj/machinery/atmospherics/omni/proc/update_port_icons()
+ if(!check_icon_cache())
+ return
+
for(var/datum/omni_port/P in ports)
if(P.update)
var/ref_layer = 0
@@ -145,11 +143,11 @@
var/list/port_icons = select_port_icons(P)
if(port_icons)
if(P.node)
- underlays_current[ref_layer] = omni_icons[port_icons["pipe_icon"]]
+ underlays_current[ref_layer] = port_icons["pipe_icon"]
else
underlays_current[ref_layer] = null
- overlays_off[ref_layer] = omni_icons[port_icons["off_icon"]]
- overlays_on[ref_layer] = omni_icons[port_icons["on_icon"]]
+ overlays_off[ref_layer] = port_icons["off_icon"]
+ overlays_on[ref_layer] = port_icons["on_icon"]
else
underlays_current[ref_layer] = null
overlays_off[ref_layer] = null
@@ -175,14 +173,29 @@
if(ATM_O2 to ATM_N2O)
ic_on += "_filter"
ic_off += "_out"
-
- var/pipe_state = ic_dir + "_pipe"
- if(P.node)
- if(P.node.color)
- pipe_state += "_[P.node.color]"
+ ic_on = icon_manager.get_atmos_icon("omni", , , ic_on)
+ ic_off = icon_manager.get_atmos_icon("omni", , , ic_off)
+
+ var/pipe_state
+ var/turf/T = get_turf(src)
+ if(!istype(T))
+ return
+ if(T.intact && istype(P.node, /obj/machinery/atmospherics/pipe) && P.node.level == 1 )
+ pipe_state = icon_manager.get_atmos_icon("underlay_down", P.dir, color_cache_name(P.node))
+ else
+ pipe_state = icon_manager.get_atmos_icon("underlay_intact", P.dir, color_cache_name(P.node))
+
return list("on_icon" = ic_on, "off_icon" = ic_off, "pipe_icon" = pipe_state)
+/obj/machinery/atmospherics/omni/update_underlays()
+ for(var/datum/omni_port/P in ports)
+ P.update = 1
+ update_ports()
+
+/obj/machinery/atmospherics/omni/hide(var/i)
+ update_underlays()
+
/obj/machinery/atmospherics/omni/proc/update_ports()
sort_ports()
update_port_icons()
diff --git a/code/ATMOSPHERICS/components/portables_connector.dm b/code/ATMOSPHERICS/components/portables_connector.dm
index 9ecc82b3244..75d32718fa9 100644
--- a/code/ATMOSPHERICS/components/portables_connector.dm
+++ b/code/ATMOSPHERICS/components/portables_connector.dm
@@ -1,6 +1,6 @@
/obj/machinery/atmospherics/portables_connector
- icon = 'icons/obj/atmospherics/portables_connector.dmi'
- icon_state = "intact"
+ icon = 'icons/atmos/connector.dmi'
+ icon_state = "map_connector"
name = "Connector Port"
desc = "For connecting portables devices related to atmospherics control."
@@ -16,142 +16,139 @@
var/on = 0
use_power = 0
- level = 0
+ level = 1
- New()
- initialize_directions = dir
- ..()
+/obj/machinery/atmospherics/portables_connector/New()
+ initialize_directions = dir
+ ..()
- update_icon()
- if(node)
- icon_state = "[level == 1 && istype(loc, /turf/simulated) ? "h" : "" ]intact"
- dir = get_dir(src, node)
- else
- icon_state = "exposed"
+/obj/machinery/atmospherics/portables_connector/update_icon()
+ icon_state = "connector"
+/obj/machinery/atmospherics/portables_connector/update_underlays()
+ if(..())
+ underlays.Cut()
+ var/turf/T = get_turf(src)
+ if(!istype(T))
+ return
+ add_underlay(T, node, dir)
+
+/obj/machinery/atmospherics/portables_connector/hide(var/i)
+ update_underlays()
+
+/obj/machinery/atmospherics/portables_connector/process()
+ ..()
+ if(!on)
return
-
- hide(var/i) //to make the little pipe section invisible, the icon changes.
- if(node)
- icon_state = "[i == 1 && istype(loc, /turf/simulated) ? "h" : "" ]intact"
- dir = get_dir(src, node)
- else
- icon_state = "exposed"
-
- process()
- ..()
- if(!on)
- return
- if(!connected_device)
- on = 0
- return
- if(network)
- network.update = 1
- return 1
+ if(!connected_device)
+ on = 0
+ return
+ if(network)
+ network.update = 1
+ return 1
// Housekeeping and pipe network stuff below
- network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference)
- if(reference == node)
- network = new_network
+/obj/machinery/atmospherics/portables_connector/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
+ if(new_network.normal_members.Find(src))
+ return 0
- new_network.normal_members += src
+ new_network.normal_members += src
- return null
+ return null
- Del()
- loc = null
+/obj/machinery/atmospherics/portables_connector/Del()
+ loc = null
- if(connected_device)
- connected_device.disconnect()
+ if(connected_device)
+ connected_device.disconnect()
- if(node)
- node.disconnect(src)
- del(network)
+ if(node)
+ node.disconnect(src)
+ del(network)
- node = null
+ node = null
- ..()
+ ..()
- initialize()
- if(node) return
+/obj/machinery/atmospherics/portables_connector/initialize()
+ if(node) return
- var/node_connect = dir
+ var/node_connect = dir
- for(var/obj/machinery/atmospherics/target in get_step(src,node_connect))
- if(target.initialize_directions & get_dir(target,src))
- node = target
- break
+ for(var/obj/machinery/atmospherics/target in get_step(src,node_connect))
+ if(target.initialize_directions & get_dir(target,src))
+ node = target
+ break
- update_icon()
+ update_icon()
+ update_underlays()
+/obj/machinery/atmospherics/portables_connector/build_network()
+ if(!network && node)
+ network = new /datum/pipe_network()
+ network.normal_members += src
+ network.build_network(node, src)
+
+
+/obj/machinery/atmospherics/portables_connector/return_network(obj/machinery/atmospherics/reference)
build_network()
- if(!network && node)
- network = new /datum/pipe_network()
- network.normal_members += src
- network.build_network(node, src)
+
+ if(reference==node)
+ return network
+
+ if(reference==connected_device)
+ return network
+
+ return null
+
+/obj/machinery/atmospherics/portables_connector/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network)
+ if(network == old_network)
+ network = new_network
+
+ return 1
+
+/obj/machinery/atmospherics/portables_connector/return_network_air(datum/pipe_network/reference)
+ var/list/results = list()
+
+ if(connected_device)
+ results += connected_device.air_contents
+
+ return results
+
+/obj/machinery/atmospherics/portables_connector/disconnect(obj/machinery/atmospherics/reference)
+ if(reference==node)
+ del(network)
+ node = null
+
+ update_underlays()
+
+ return null
- return_network(obj/machinery/atmospherics/reference)
- build_network()
-
- if(reference==node)
- return network
-
- if(reference==connected_device)
- return network
-
- return null
-
- reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network)
- if(network == old_network)
- network = new_network
-
+/obj/machinery/atmospherics/portables_connector/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
+ if (!istype(W, /obj/item/weapon/wrench))
+ return ..()
+ if (connected_device)
+ user << "\red You cannot unwrench this [src], dettach [connected_device] first."
return 1
-
- return_network_air(datum/pipe_network/reference)
- var/list/results = list()
-
- if(connected_device)
- results += connected_device.air_contents
-
- return results
-
- disconnect(obj/machinery/atmospherics/reference)
- if(reference==node)
- del(network)
- node = null
-
- return null
-
-
- attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
- if (!istype(W, /obj/item/weapon/wrench))
- return ..()
- if (connected_device)
- user << "\red You cannot unwrench this [src], dettach [connected_device] first."
- return 1
- if (locate(/obj/machinery/portable_atmospherics, src.loc))
- return 1
- var/turf/T = src.loc
- if (level==1 && isturf(T) && T.intact)
- user << "\red You must remove the plating first."
- return 1
- var/datum/gas_mixture/int_air = return_air()
- var/datum/gas_mixture/env_air = loc.return_air()
- if ((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE)
- user << "\red You cannot unwrench this [src], it too exerted due to internal pressure."
- add_fingerprint(user)
- return 1
- playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
- user << "\blue You begin to unfasten \the [src]..."
- if (do_after(user, 40))
- user.visible_message( \
- "[user] unfastens \the [src].", \
- "\blue You have unfastened \the [src].", \
- "You hear ratchet.")
- new /obj/item/pipe(loc, make_from=src)
- del(src)
+ if (locate(/obj/machinery/portable_atmospherics, src.loc))
+ return 1
+ var/datum/gas_mixture/int_air = return_air()
+ var/datum/gas_mixture/env_air = loc.return_air()
+ if ((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE)
+ user << "\red You cannot unwrench this [src], it too exerted due to internal pressure."
+ add_fingerprint(user)
+ return 1
+ playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
+ user << "\blue You begin to unfasten \the [src]..."
+ if (do_after(user, 40))
+ user.visible_message( \
+ "[user] unfastens \the [src].", \
+ "\blue You have unfastened \the [src].", \
+ "You hear ratchet.")
+ new /obj/item/pipe(loc, make_from=src)
+ del(src)
diff --git a/code/ATMOSPHERICS/components/trinary_devices/filter.dm b/code/ATMOSPHERICS/components/trinary_devices/filter.dm
index 16a863a2e1f..7590c453e44 100755
--- a/code/ATMOSPHERICS/components/trinary_devices/filter.dm
+++ b/code/ATMOSPHERICS/components/trinary_devices/filter.dm
@@ -1,124 +1,103 @@
-obj/machinery/atmospherics/trinary/filter
- icon = 'icons/obj/atmospherics/filter.dmi'
- icon_state = "intact_off"
+/obj/machinery/atmospherics/trinary/filter
+ icon = 'icons/atmos/filter.dmi'
+ icon_state = "map"
density = 0
+ level = 1
name = "Gas filter"
+
+ use_power = 1
+ idle_power_usage = 150 //internal circuitry, friction losses and stuff
+ active_power_usage = 7500 //This also doubles as a measure of how powerful the filter is, in Watts. 7500 W ~ 10 HP
- var/on = 0
var/temp = null // -- TLE
- var/target_pressure = ONE_ATMOSPHERE
+ var/set_flow_rate = ATMOS_DEFAULT_VOLUME_FILTER
+ /*
+ Filter types:
+ -1: Nothing
+ 0: Phoron: Phoron, Oxygen Agent B
+ 1: Oxygen: Oxygen ONLY
+ 2: Nitrogen: Nitrogen ONLY
+ 3: Carbon Dioxide: Carbon Dioxide ONLY
+ 4: Sleeping Agent (N2O)
+ */
var/filter_type = 0
-/*
-Filter types:
--1: Nothing
- 0: Phoron: Phoron, Oxygen Agent B
- 1: Oxygen: Oxygen ONLY
- 2: Nitrogen: Nitrogen ONLY
- 3: Carbon Dioxide: Carbon Dioxide ONLY
- 4: Sleeping Agent (N2O)
-*/
+ var/list/filtered_out = list()
+
var/frequency = 0
var/datum/radio_frequency/radio_connection
- proc
- set_frequency(new_frequency)
- radio_controller.remove_object(src, frequency)
- frequency = new_frequency
- if(frequency)
- radio_connection = radio_controller.add_object(src, frequency, RADIO_ATMOSIA)
+/obj/machinery/atmospherics/trinary/filter/proc/set_frequency(new_frequency)
+ radio_controller.remove_object(src, frequency)
+ frequency = new_frequency
+ if(frequency)
+ radio_connection = radio_controller.add_object(src, frequency, RADIO_ATMOSIA)
- New()
- if(radio_controller)
- initialize()
- ..()
+/obj/machinery/atmospherics/trinary/filter/New()
+ ..()
+ air1.volume = ATMOS_DEFAULT_VOLUME_FILTER
+ air2.volume = ATMOS_DEFAULT_VOLUME_FILTER
+ air3.volume = ATMOS_DEFAULT_VOLUME_FILTER
+
+ if(radio_controller)
+ initialize()
- update_icon()
- if(stat & NOPOWER)
- icon_state = "intact_off"
- else if(node2 && node3 && node1)
- icon_state = "intact_[on?("on"):("off")]"
+/obj/machinery/atmospherics/trinary/filter/update_icon()
+ if(istype(src, /obj/machinery/atmospherics/trinary/filter/m_filter))
+ icon_state = "m"
+ else
+ icon_state = ""
+
+ if(!powered())
+ icon_state += "off"
+ else if(node2 && node3 && node1)
+ icon_state += on ? "on" : "off"
+ else
+ icon_state += "off"
+ on = 0
+
+/obj/machinery/atmospherics/trinary/filter/update_underlays()
+ if(..())
+ underlays.Cut()
+ var/turf/T = get_turf(src)
+ if(!istype(T))
+ return
+
+ add_underlay(T, node1, turn(dir, -180))
+
+ if(istype(src, /obj/machinery/atmospherics/trinary/filter/m_filter))
+ add_underlay(T, node2, turn(dir, 90))
else
- icon_state = "intact_off"
- on = 0
+ add_underlay(T, node2, turn(dir, -90))
+ add_underlay(T, node3, dir)
+
+/obj/machinery/atmospherics/trinary/filter/hide(var/i)
+ update_underlays()
+
+/obj/machinery/atmospherics/trinary/filter/power_change()
+ var/old_stat = stat
+ ..()
+ if(old_stat != stat)
+ update_icon()
+
+/obj/machinery/atmospherics/trinary/filter/process()
+ ..()
+ if((stat & (NOPOWER|BROKEN)) || !on)
+ update_use_power(0) //usually we get here because a player turned a pump off - definitely want to update.
+ last_flow_rate = 0
return
-
- power_change()
- var/old_stat = stat
- ..()
- if(old_stat != stat)
- update_icon()
-
- process()
- ..()
- if(!on)
- return 0
-
- var/output_starting_pressure = air3.return_pressure()
-
- if(output_starting_pressure >= target_pressure || air2.return_pressure() >= target_pressure )
- //No need to mix if target is already full!
- return 1
-
- //Calculate necessary moles to transfer using PV=nRT
-
- var/pressure_delta = target_pressure - output_starting_pressure
- var/transfer_moles
-
- if(air1.temperature > 0)
- transfer_moles = pressure_delta*air3.volume/(air1.temperature * R_IDEAL_GAS_EQUATION)
-
- //Actually transfer the gas
-
- if(transfer_moles > 0)
- var/datum/gas_mixture/removed = air1.remove(transfer_moles)
-
- if(!removed)
- return
- var/datum/gas_mixture/filtered_out = new
- filtered_out.temperature = removed.temperature
-
- switch(filter_type)
- if(0) //removing hydrocarbons
- filtered_out.phoron = removed.phoron
- removed.phoron = 0
-
- if(removed.trace_gases.len>0)
- for(var/datum/gas/trace_gas in removed.trace_gases)
- if(istype(trace_gas, /datum/gas/oxygen_agent_b))
- removed.trace_gases -= trace_gas
- filtered_out.trace_gases += trace_gas
-
- if(1) //removing O2
- filtered_out.oxygen = removed.oxygen
- removed.oxygen = 0
-
- if(2) //removing N2
- filtered_out.nitrogen = removed.nitrogen
- removed.nitrogen = 0
-
- if(3) //removing CO2
- filtered_out.carbon_dioxide = removed.carbon_dioxide
- removed.carbon_dioxide = 0
-
- if(4)//removing N2O
- if(removed.trace_gases.len>0)
- for(var/datum/gas/trace_gas in removed.trace_gases)
- if(istype(trace_gas, /datum/gas/sleeping_agent))
- removed.trace_gases -= trace_gas
- filtered_out.trace_gases += trace_gas
-
- else
- filtered_out = null
-
-
- air2.merge(filtered_out)
- air3.merge(removed)
-
+
+ //Figure out the amount of moles to transfer
+ var/transfer_moles = (set_flow_rate/air1.volume)*air1.total_moles
+
+ var/power_draw = -1
+ if (transfer_moles > MINUMUM_MOLES_TO_FILTER)
+ power_draw = filter_gas(src, filtered_out, air1, air2, air3, transfer_moles, active_power_usage)
+
if(network2)
network2.update = 1
@@ -128,37 +107,40 @@ Filter types:
if(network1)
network1.update = 1
+ if (power_draw < 0)
+ //update_use_power(0)
+ use_power = 0 //don't force update - easier on CPU
+ last_flow_rate = 0
+ else
+ handle_power_draw(power_draw)
+
+ return 1
+
+/obj/machinery/atmospherics/trinary/filter/initialize()
+ set_frequency(frequency)
+ ..()
+
+/obj/machinery/atmospherics/trinary/filter/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
+ if (!istype(W, /obj/item/weapon/wrench))
+ return ..()
+ var/datum/gas_mixture/int_air = return_air()
+ var/datum/gas_mixture/env_air = loc.return_air()
+ if ((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE)
+ user << "\red You cannot unwrench this [src], it too exerted due to internal pressure."
+ add_fingerprint(user)
return 1
-
- initialize()
- set_frequency(frequency)
- ..()
-
- attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
- if (!istype(W, /obj/item/weapon/wrench))
- return ..()
- var/turf/T = src.loc
- if (level==1 && isturf(T) && T.intact)
- user << "\red You must remove the plating first."
- return 1
- var/datum/gas_mixture/int_air = return_air()
- var/datum/gas_mixture/env_air = loc.return_air()
- if ((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE)
- user << "\red You cannot unwrench this [src], it too exerted due to internal pressure."
- add_fingerprint(user)
- return 1
- playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
- user << "\blue You begin to unfasten \the [src]..."
- if (do_after(user, 40))
- user.visible_message( \
- "[user] unfastens \the [src].", \
- "\blue You have unfastened \the [src].", \
- "You hear ratchet.")
- new /obj/item/pipe(loc, make_from=src)
- del(src)
+ playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
+ user << "\blue You begin to unfasten \the [src]..."
+ if (do_after(user, 40))
+ user.visible_message( \
+ "[user] unfastens \the [src].", \
+ "\blue You have unfastened \the [src].", \
+ "You hear ratchet.")
+ new /obj/item/pipe(loc, make_from=src)
+ del(src)
-obj/machinery/atmospherics/trinary/filter/attack_hand(user as mob) // -- TLE
+/obj/machinery/atmospherics/trinary/filter/attack_hand(user as mob) // -- TLE
if(..())
return
@@ -194,8 +176,10 @@ obj/machinery/atmospherics/trinary/filter/attack_hand(user as mob) // -- TLE
Carbon Dioxide Nitrous Oxide Nothing
- Desirable output pressure:
- [src.target_pressure]kPa | Change
+
+ Set Flow Rate Limit:
+ [src.set_flow_rate]L/s | Change
+ Flow rate: [round(last_flow_rate, 0.1)]L/s
"}
/*
user << browse("[src.name] control[dat]","window=atmo_filter")
@@ -211,18 +195,33 @@ obj/machinery/atmospherics/trinary/filter/attack_hand(user as mob) // -- TLE
onclose(user, "atmo_filter")
return
-obj/machinery/atmospherics/trinary/filter/Topic(href, href_list) // -- TLE
+/obj/machinery/atmospherics/trinary/filter/Topic(href, href_list) // -- TLE
if(..())
return
usr.set_machine(src)
src.add_fingerprint(usr)
if(href_list["filterset"])
- src.filter_type = text2num(href_list["filterset"])
+ filter_type = text2num(href_list["filterset"])
+
+ filtered_out.Cut() //no need to create new lists unnecessarily
+ switch(filter_type)
+ if(0) //removing hydrocarbons
+ filtered_out += "phoron"
+ filtered_out += "oxygen_agent_b"
+ if(1) //removing O2
+ filtered_out += "oxygen"
+ if(2) //removing N2
+ filtered_out += "nitrogen"
+ if(3) //removing CO2
+ filtered_out += "carbon_dioxide"
+ if(4)//removing N2O
+ filtered_out += "sleeping_agent"
+
if (href_list["temp"])
src.temp = null
- if(href_list["set_press"])
- var/new_pressure = input(usr,"Enter new output pressure (0-4500kPa)","Pressure control",src.target_pressure) as num
- src.target_pressure = max(0, min(4500, new_pressure))
+ if(href_list["set_flow_rate"])
+ var/new_flow_rate = input(usr,"Enter new flow rate (0-[air1.volume]L/s)","Flow Rate Control",src.set_flow_rate) as num
+ src.set_flow_rate = max(0, min(air1.volume, new_flow_rate))
if(href_list["power"])
on=!on
src.update_icon()
@@ -234,9 +233,8 @@ obj/machinery/atmospherics/trinary/filter/Topic(href, href_list) // -- TLE
*/
return
-obj/machinery/atmospherics/trinary/filter/m_filter
- icon = 'icons/obj/atmospherics/m_filter.dmi'
- icon_state = "intact_off"
+/obj/machinery/atmospherics/trinary/filter/m_filter
+ icon_state = "mmap"
dir = SOUTH
initialize_directions = SOUTH|NORTH|EAST
@@ -253,7 +251,9 @@ obj/machinery/atmospherics/trinary/filter/m_filter/New()
if(WEST)
initialize_directions = WEST|SOUTH|EAST
-obj/machinery/atmospherics/trinary/filter/m_filter/initialize()
+/obj/machinery/atmospherics/trinary/filter/m_filter/initialize()
+ set_frequency(frequency)
+
if(node1 && node2 && node3) return
var/node1_connect = turn(dir, -180)
@@ -276,3 +276,4 @@ obj/machinery/atmospherics/trinary/filter/m_filter/initialize()
break
update_icon()
+ update_underlays()
diff --git a/code/ATMOSPHERICS/components/trinary_devices/mixer.dm b/code/ATMOSPHERICS/components/trinary_devices/mixer.dm
index 4d94acbfba3..051a2ced27f 100644
--- a/code/ATMOSPHERICS/components/trinary_devices/mixer.dm
+++ b/code/ATMOSPHERICS/components/trinary_devices/mixer.dm
@@ -1,169 +1,185 @@
-obj/machinery/atmospherics/trinary/mixer
- icon = 'icons/obj/atmospherics/mixer.dmi'
- icon_state = "intact_off"
+/obj/machinery/atmospherics/trinary/mixer
+ icon = 'icons/atmos/mixer.dmi'
+ icon_state = "map"
density = 0
+ level = 1
name = "Gas mixer"
- var/on = 0
+ use_power = 1
+ idle_power_usage = 150 //internal circuitry, friction losses and stuff
+ active_power_usage = 3700 //This also doubles as a measure of how powerful the mixer is, in Watts. 3700 W ~ 5 HP
- var/target_pressure = ONE_ATMOSPHERE
+ var/set_flow_rate = ATMOS_DEFAULT_VOLUME_MIXER
+ var/list/mixing_inputs
+
+ //for mapping
var/node1_concentration = 0.5
var/node2_concentration = 0.5
//node 3 is the outlet, nodes 1 & 2 are intakes
- update_icon()
- if(stat & NOPOWER)
- icon_state = "intact_off"
- else if(node2 && node3 && node1)
- icon_state = "intact_[on?("on"):("off")]"
+/obj/machinery/atmospherics/trinary/mixer/update_icon(var/safety = 0)
+ if(istype(src, /obj/machinery/atmospherics/trinary/mixer/m_mixer))
+ icon_state = "m"
+ else if(istype(src, /obj/machinery/atmospherics/trinary/mixer/t_mixer))
+ icon_state = "t"
+ else
+ icon_state = ""
+
+ if(!powered())
+ icon_state += "off"
+ else if(node2 && node3 && node1)
+ icon_state += on ? "on" : "off"
+ else
+ icon_state += "off"
+ on = 0
+
+/obj/machinery/atmospherics/trinary/mixer/update_underlays()
+ if(..())
+ underlays.Cut()
+ var/turf/T = get_turf(src)
+ if(!istype(T))
+ return
+
+ if(istype(src, /obj/machinery/atmospherics/trinary/mixer/t_mixer))
+ add_underlay(T, node1, turn(dir, -90))
else
- icon_state = "intact_off"
- on = 0
+ add_underlay(T, node1, turn(dir, -180))
+ if(istype(src, /obj/machinery/atmospherics/trinary/mixer/m_mixer) || istype(src, /obj/machinery/atmospherics/trinary/mixer/t_mixer))
+ add_underlay(T, node2, turn(dir, 90))
+ else
+ add_underlay(T, node2, turn(dir, -90))
+
+ add_underlay(T, node3, dir)
+
+/obj/machinery/atmospherics/trinary/mixer/hide(var/i)
+ update_underlays()
+
+/obj/machinery/atmospherics/trinary/mixer/power_change()
+ var/old_stat = stat
+ ..()
+ if(old_stat != stat)
+ update_icon()
+
+/obj/machinery/atmospherics/trinary/mixer/New()
+ ..()
+ air1.volume = ATMOS_DEFAULT_VOLUME_MIXER
+ air2.volume = ATMOS_DEFAULT_VOLUME_MIXER
+ air3.volume = ATMOS_DEFAULT_VOLUME_MIXER * 1.5
+
+/obj/machinery/atmospherics/trinary/mixer/process()
+ ..()
+
+ //For some reason this doesn't work even in initialize(), so it goes here.
+ if (!mixing_inputs)
+ mixing_inputs = list(src.air1 = node1_concentration, src.air2 = node2_concentration)
+
+ if((stat & (NOPOWER|BROKEN)) || !on)
+ update_use_power(0) //usually we get here because a player turned a pump off - definitely want to update.
+ last_flow_rate = 0
return
-
- power_change()
- var/old_stat = stat
- ..()
- if(old_stat != stat)
- update_icon()
-
- New()
- ..()
- air3.volume = 300
-
- process()
- ..()
- if(!on)
- return 0
-
- var/output_starting_pressure = air3.return_pressure()
-
- if(output_starting_pressure >= target_pressure)
- //No need to mix if target is already full!
- return 1
-
- //Calculate necessary moles to transfer using PV=nRT
-
- var/pressure_delta = target_pressure - output_starting_pressure
- var/transfer_moles1 = 0
- var/transfer_moles2 = 0
-
- if(air1.temperature > 0)
- transfer_moles1 = (node1_concentration*pressure_delta)*air3.volume/(air1.temperature * R_IDEAL_GAS_EQUATION)
-
- if(air2.temperature > 0)
- transfer_moles2 = (node2_concentration*pressure_delta)*air3.volume/(air2.temperature * R_IDEAL_GAS_EQUATION)
-
- var/air1_moles = air1.total_moles()
- var/air2_moles = air2.total_moles()
-
- if((air1_moles < transfer_moles1) || (air2_moles < transfer_moles2))
- if(!transfer_moles1 || !transfer_moles2) return
- var/ratio = min(air1_moles/transfer_moles1, air2_moles/transfer_moles2)
-
- transfer_moles1 *= ratio
- transfer_moles2 *= ratio
-
- //Actually transfer the gas
-
- if(transfer_moles1 > 0)
- var/datum/gas_mixture/removed1 = air1.remove(transfer_moles1)
- air3.merge(removed1)
-
- if(transfer_moles2 > 0)
- var/datum/gas_mixture/removed2 = air2.remove(transfer_moles2)
- air3.merge(removed2)
-
- if(network1 && transfer_moles1)
+
+ //Figure out the amount of moles to transfer
+ var/transfer_moles = (set_flow_rate*mixing_inputs[air1]/air1.volume)*air1.total_moles + (set_flow_rate*mixing_inputs[air1]/air2.volume)*air2.total_moles
+
+ var/power_draw = -1
+ if (transfer_moles > MINUMUM_MOLES_TO_FILTER)
+ power_draw = mix_gas(src, mixing_inputs, air3, transfer_moles, active_power_usage)
+
+ if(network1 && mixing_inputs[air1])
network1.update = 1
- if(network2 && transfer_moles2)
+ if(network2 && mixing_inputs[air2])
network2.update = 1
if(network3)
network3.update = 1
+ if (power_draw < 0)
+ //update_use_power(0)
+ use_power = 0 //don't force update - easier on CPU
+ last_flow_rate = 0
+ else
+ handle_power_draw(power_draw)
+
+ return 1
+
+/obj/machinery/atmospherics/trinary/mixer/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
+ if (!istype(W, /obj/item/weapon/wrench))
+ return ..()
+ var/datum/gas_mixture/int_air = return_air()
+ var/datum/gas_mixture/env_air = loc.return_air()
+ if ((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE)
+ user << "\red You cannot unwrench this [src], it too exerted due to internal pressure."
+ add_fingerprint(user)
return 1
+ playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
+ user << "\blue You begin to unfasten \the [src]..."
+ if (do_after(user, 40))
+ user.visible_message( \
+ "[user] unfastens \the [src].", \
+ "\blue You have unfastened \the [src].", \
+ "You hear ratchet.")
+ new /obj/item/pipe(loc, make_from=src)
+ del(src)
- attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
- if (!istype(W, /obj/item/weapon/wrench))
- return ..()
- var/turf/T = src.loc
- if (level==1 && isturf(T) && T.intact)
- user << "\red You must remove the plating first."
- return 1
- var/datum/gas_mixture/int_air = return_air()
- var/datum/gas_mixture/env_air = loc.return_air()
- if ((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE)
- user << "\red You cannot unwrench this [src], it too exerted due to internal pressure."
- add_fingerprint(user)
- return 1
- playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
- user << "\blue You begin to unfasten \the [src]..."
- if (do_after(user, 40))
- user.visible_message( \
- "[user] unfastens \the [src].", \
- "\blue You have unfastened \the [src].", \
- "You hear ratchet.")
- new /obj/item/pipe(loc, make_from=src)
- del(src)
-
- attack_hand(user as mob)
- if(..())
- return
- src.add_fingerprint(usr)
- if(!src.allowed(user))
- user << "\red Access denied."
- return
- usr.set_machine(src)
- var/dat = {"Power: [on?"On":"Off"]
- Desirable output pressure:
- [target_pressure]kPa | Change
-
- Node 1 Concentration:
- -
- -
- [node1_concentration]([node1_concentration*100]%)
- +
- +
-
- Node 2 Concentration:
- -
- -
- [node2_concentration]([node2_concentration*100]%)
- +
- +
- "}
-
- user << browse("[src.name] control[dat]", "window=atmo_mixer")
- onclose(user, "atmo_mixer")
+/obj/machinery/atmospherics/trinary/mixer/attack_hand(user as mob)
+ if(..())
return
-
- Topic(href,href_list)
- if(..()) return
- if(href_list["power"])
- on = !on
- if(href_list["set_press"])
- var/new_pressure = input(usr,"Enter new output pressure (0-4500kPa)","Pressure control",src.target_pressure) as num
- src.target_pressure = max(0, min(4500, new_pressure))
- if(href_list["node1_c"])
- var/value = text2num(href_list["node1_c"])
- src.node1_concentration = max(0, min(1, src.node1_concentration + value))
- src.node2_concentration = max(0, min(1, src.node2_concentration - value))
- if(href_list["node2_c"])
- var/value = text2num(href_list["node2_c"])
- src.node2_concentration = max(0, min(1, src.node2_concentration + value))
- src.node1_concentration = max(0, min(1, src.node1_concentration - value))
- src.update_icon()
- src.updateUsrDialog()
+ src.add_fingerprint(usr)
+ if(!src.allowed(user))
+ user << "\red Access denied."
return
+ usr.set_machine(src)
+ var/dat = {"Power: [on?"On":"Off"]
+ Set Flow Rate Limit:
+ [set_flow_rate]L/s | Change
+
+ Flow Rate: [round(last_flow_rate, 0.1)]L/s
+
+ Node 1 Concentration:
+ -
+ -
+ [mixing_inputs[air1]]([mixing_inputs[air1]*100]%)
+ +
+ +
+
+ Node 2 Concentration:
+ -
+ -
+ [mixing_inputs[air2]]([mixing_inputs[air2]*100]%)
+ +
+ +
+ "}
+
+ user << browse("[src.name] control[dat]", "window=atmo_mixer")
+ onclose(user, "atmo_mixer")
+ return
+
+/obj/machinery/atmospherics/trinary/mixer/Topic(href,href_list)
+ if(..()) return
+ if(href_list["power"])
+ on = !on
+ if(href_list["set_press"])
+ var/max_flow_rate = min(air1.volume, air2.volume)
+ var/new_flow_rate = input(usr,"Enter new flow rate limit (0-[max_flow_rate]L/s)","Flow Rate Control",src.set_flow_rate) as num
+ src.set_flow_rate = max(0, min(max_flow_rate, new_flow_rate))
+ if(href_list["node1_c"])
+ var/value = text2num(href_list["node1_c"])
+ src.mixing_inputs[air1] = max(0, min(1, src.mixing_inputs[air1] + value))
+ src.mixing_inputs[air2] = 1.0 - mixing_inputs[air1]
+ if(href_list["node2_c"])
+ var/value = text2num(href_list["node2_c"])
+ src.mixing_inputs[air2] = max(0, min(1, src.mixing_inputs[air2] + value))
+ src.mixing_inputs[air1] = 1.0 - mixing_inputs[air2]
+ src.update_icon()
+ src.updateUsrDialog()
+ return
obj/machinery/atmospherics/trinary/mixer/t_mixer
- icon = 'icons/obj/atmospherics/t_mixer.dmi'
- icon_state = "intact_off"
+ icon_state = "tmap"
dir = SOUTH
initialize_directions = SOUTH|EAST|WEST
@@ -183,6 +199,7 @@ obj/machinery/atmospherics/trinary/mixer/t_mixer/New()
initialize_directions = WEST|NORTH|SOUTH
obj/machinery/atmospherics/trinary/mixer/t_mixer/initialize()
+ ..()
if(node1 && node2 && node3) return
var/node1_connect = turn(dir, -90)
@@ -205,10 +222,10 @@ obj/machinery/atmospherics/trinary/mixer/t_mixer/initialize()
break
update_icon()
+ update_underlays()
obj/machinery/atmospherics/trinary/mixer/m_mixer
- icon = 'icons/obj/atmospherics/m_mixer.dmi'
- icon_state = "intact_off"
+ icon_state = "mmap"
dir = SOUTH
initialize_directions = SOUTH|NORTH|EAST
@@ -228,6 +245,7 @@ obj/machinery/atmospherics/trinary/mixer/m_mixer/New()
initialize_directions = WEST|SOUTH|EAST
obj/machinery/atmospherics/trinary/mixer/m_mixer/initialize()
+ ..()
if(node1 && node2 && node3) return
var/node1_connect = turn(dir, -180)
@@ -249,4 +267,5 @@ obj/machinery/atmospherics/trinary/mixer/m_mixer/initialize()
node3 = target
break
- update_icon()
\ No newline at end of file
+ update_icon()
+ update_underlays()
diff --git a/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm b/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm
index d3f1116a430..655e58ea384 100644
--- a/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm
+++ b/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm
@@ -2,6 +2,8 @@ obj/machinery/atmospherics/trinary
dir = SOUTH
initialize_directions = SOUTH|NORTH|WEST
use_power = 1
+
+ var/on = 0
var/datum/gas_mixture/air1
var/datum/gas_mixture/air2
@@ -94,6 +96,7 @@ obj/machinery/atmospherics/trinary
break
update_icon()
+ update_underlays()
build_network()
if(!network1 && node1)
@@ -160,5 +163,7 @@ obj/machinery/atmospherics/trinary
else if(reference==node3)
del(network3)
node3 = null
+
+ update_underlays()
return null
\ No newline at end of file
diff --git a/code/ATMOSPHERICS/components/tvalve.dm b/code/ATMOSPHERICS/components/tvalve.dm
index 83f592c315f..603feb02949 100644
--- a/code/ATMOSPHERICS/components/tvalve.dm
+++ b/code/ATMOSPHERICS/components/tvalve.dm
@@ -1,10 +1,11 @@
-obj/machinery/atmospherics/tvalve
- icon = 'icons/obj/atmospherics/valve.dmi'
- icon_state = "tvalve0"
+/obj/machinery/atmospherics/tvalve
+ icon = 'icons/atmos/tvalve.dmi'
+ icon_state = "map_tvalve0"
name = "manual switching valve"
desc = "A pipe valve"
+ level = 1
dir = SOUTH
initialize_directions = SOUTH|NORTH|WEST
@@ -19,415 +20,466 @@ obj/machinery/atmospherics/tvalve
var/datum/pipe_network/network_node2
var/datum/pipe_network/network_node3
- update_icon(animation)
- if(animation)
- flick("tvalve[src.state][!src.state]",src)
+/obj/machinery/atmospherics/tvalve/bypass
+ icon_state = "map_tvalve1"
+ state = 1
+
+/obj/machinery/atmospherics/tvalve/update_icon(animation)
+ if(animation)
+ flick("tvalve[src.state][!src.state]",src)
+ else
+ icon_state = "tvalve[state]"
+
+/obj/machinery/atmospherics/tvalve/update_underlays()
+ if(..())
+ underlays.Cut()
+ var/turf/T = get_turf(src)
+ if(!istype(T))
+ return
+ add_underlay(T, node1, turn(dir, -180))
+
+ if(istype(src, /obj/machinery/atmospherics/tvalve/mirrored))
+ add_underlay(T, node2, turn(dir, 90))
else
- icon_state = "tvalve[state]"
+ add_underlay(T, node2, turn(dir, -90))
+
+ add_underlay(T, node3, dir)
- New()
- initialize_directions()
- ..()
-
- proc/initialize_directions()
- switch(dir)
- if(NORTH)
- initialize_directions = SOUTH|NORTH|EAST
- if(SOUTH)
- initialize_directions = NORTH|SOUTH|WEST
- if(EAST)
- initialize_directions = WEST|EAST|SOUTH
- if(WEST)
- initialize_directions = EAST|WEST|NORTH
-
- network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference)
- if(reference == node1)
- network_node1 = new_network
- if(state)
- network_node2 = new_network
- else
- network_node3 = new_network
- else if(reference == node2)
- network_node2 = new_network
- if(state)
- network_node1 = new_network
- else if(reference == node3)
- network_node3 = new_network
- if(!state)
- network_node1 = new_network
-
- if(new_network.normal_members.Find(src))
- return 0
-
- new_network.normal_members += src
-
- if(state)
- if(reference == node1)
- if(node2)
- return node2.network_expand(new_network, src)
- else if(reference == node2)
- if(node1)
- return node1.network_expand(new_network, src)
- else
- if(reference == node1)
- if(node3)
- return node3.network_expand(new_network, src)
- else if(reference == node3)
- if(node1)
- return node1.network_expand(new_network, src)
-
- return null
-
- Del()
- loc = null
-
- if(node1)
- node1.disconnect(src)
- del(network_node1)
- if(node2)
- node2.disconnect(src)
- del(network_node2)
- if(node3)
- node3.disconnect(src)
- del(network_node3)
-
- node1 = null
- node2 = null
- node3 = null
-
- ..()
-
- proc/go_to_side()
-
- if(state) return 0
-
- state = 1
- update_icon()
-
- if(network_node1)
- del(network_node1)
- if(network_node3)
- del(network_node3)
- build_network()
-
- if(network_node1&&network_node2)
- network_node1.merge(network_node2)
- network_node2 = network_node1
-
- if(network_node1)
- network_node1.update = 1
- else if(network_node2)
- network_node2.update = 1
-
- return 1
-
- proc/go_straight()
-
- if(!state)
- return 0
-
- state = 0
- update_icon()
-
- if(network_node1)
- del(network_node1)
- if(network_node2)
- del(network_node2)
- build_network()
-
- if(network_node1&&network_node3)
- network_node1.merge(network_node3)
- network_node3 = network_node1
-
- if(network_node1)
- network_node1.update = 1
- else if(network_node3)
- network_node3.update = 1
-
- return 1
-
- attack_ai(mob/user as mob)
- return
-
- attack_paw(mob/user as mob)
- return attack_hand(user)
-
- attack_hand(mob/user as mob)
- src.add_fingerprint(usr)
- update_icon(1)
- sleep(10)
- if (src.state)
- src.go_straight()
- else
- src.go_to_side()
-
- process()
- ..()
- machines.Remove(src)
-
-/* if(open && (!node1 || !node2))
- close()
- if(!node1)
- if(!nodealert)
- //world << "Missing node from [src] at [src.x],[src.y],[src.z]"
- nodealert = 1
- else if (!node2)
- if(!nodealert)
- //world << "Missing node from [src] at [src.x],[src.y],[src.z]"
- nodealert = 1
- else if (nodealert)
- nodealert = 0
-*/
-
- return
-
- initialize()
- var/node1_dir
- var/node2_dir
- var/node3_dir
-
- node1_dir = turn(dir, 180)
- node2_dir = turn(dir, -90)
- node3_dir = dir
-
- for(var/obj/machinery/atmospherics/target in get_step(src,node1_dir))
- if(target.initialize_directions & get_dir(target,src))
- node1 = target
- break
- for(var/obj/machinery/atmospherics/target in get_step(src,node2_dir))
- if(target.initialize_directions & get_dir(target,src))
- node2 = target
- break
- for(var/obj/machinery/atmospherics/target in get_step(src,node3_dir))
- if(target.initialize_directions & get_dir(target,src))
- node3 = target
- break
-
- build_network()
- if(!network_node1 && node1)
- network_node1 = new /datum/pipe_network()
- network_node1.normal_members += src
- network_node1.build_network(node1, src)
-
- if(!network_node2 && node2)
- network_node2 = new /datum/pipe_network()
- network_node2.normal_members += src
- network_node2.build_network(node2, src)
-
- if(!network_node3 && node3)
- network_node3 = new /datum/pipe_network()
- network_node3.normal_members += src
- network_node3.build_network(node3, src)
-
-
- return_network(obj/machinery/atmospherics/reference)
- build_network()
-
- if(reference==node1)
- return network_node1
-
- if(reference==node2)
- return network_node2
-
- if(reference==node3)
- return network_node3
-
- return null
-
- reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network)
- if(network_node1 == old_network)
- network_node1 = new_network
- if(network_node2 == old_network)
- network_node2 = new_network
- if(network_node3 == old_network)
- network_node3 = new_network
-
- return 1
-
- return_network_air(datum/network/reference)
- return null
-
- disconnect(obj/machinery/atmospherics/reference)
- if(reference==node1)
- del(network_node1)
- node1 = null
-
- else if(reference==node2)
- del(network_node2)
- node2 = null
-
- else if(reference==node3)
- del(network_node3)
- node2 = null
-
- return null
-
- digital // can be controlled by AI
- name = "digital switching valve"
- desc = "A digitally controlled valve."
- icon = 'icons/obj/atmospherics/digital_valve.dmi'
-
- attack_ai(mob/user as mob)
- return src.attack_hand(user)
-
- attack_hand(mob/user as mob)
- if(!src.allowed(user))
- user << "\red Access denied."
- return
- ..()
-
- //Radio remote control
-
- proc
- set_frequency(new_frequency)
- radio_controller.remove_object(src, frequency)
- frequency = new_frequency
- if(frequency)
- radio_connection = radio_controller.add_object(src, frequency, RADIO_ATMOSIA)
-
- var/frequency = 0
- var/id = null
- var/datum/radio_frequency/radio_connection
-
- initialize()
- ..()
- if(frequency)
- set_frequency(frequency)
-
- receive_signal(datum/signal/signal)
- if(!signal.data["tag"] || (signal.data["tag"] != id))
- return 0
-
- switch(signal.data["command"])
- if("valve_open")
- if(!state)
- go_to_side()
-
- if("valve_close")
- if(state)
- go_straight()
-
- if("valve_toggle")
- if(state)
- go_straight()
- else
- go_to_side()
-
- attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
- if (!istype(W, /obj/item/weapon/wrench))
- return ..()
- if (istype(src, /obj/machinery/atmospherics/tvalve/digital))
- user << "\red You cannot unwrench this [src], it's too complicated."
- return 1
- var/turf/T = src.loc
- if (level==1 && isturf(T) && T.intact)
- user << "\red You must remove the plating first."
- return 1
- var/datum/gas_mixture/int_air = return_air()
- var/datum/gas_mixture/env_air = loc.return_air()
- if ((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE)
- user << "\red You cannot unwrench this [src], it too exerted due to internal pressure."
- add_fingerprint(user)
- return 1
- playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
- user << "\blue You begin to unfasten \the [src]..."
- if (do_after(user, 40))
- user.visible_message( \
- "[user] unfastens \the [src].", \
- "\blue You have unfastened \the [src].", \
- "You hear ratchet.")
- new /obj/item/pipe(loc, make_from=src)
- del(src)
-
-obj/machinery/atmospherics/tvalve/mirrored
- icon_state = "tvalvem0"
+/obj/machinery/atmospherics/tvalve/hide(var/i)
+ update_underlays()
+/obj/machinery/atmospherics/tvalve/New()
initialize_directions()
- switch(dir)
- if(NORTH)
- initialize_directions = SOUTH|NORTH|WEST
- if(SOUTH)
- initialize_directions = NORTH|SOUTH|EAST
- if(EAST)
- initialize_directions = WEST|EAST|NORTH
- if(WEST)
- initialize_directions = EAST|WEST|SOUTH
+ ..()
- initialize()
- var/node1_dir
- var/node2_dir
- var/node3_dir
+/obj/machinery/atmospherics/tvalve/proc/initialize_directions()
+ switch(dir)
+ if(NORTH)
+ initialize_directions = SOUTH|NORTH|EAST
+ if(SOUTH)
+ initialize_directions = NORTH|SOUTH|WEST
+ if(EAST)
+ initialize_directions = WEST|EAST|SOUTH
+ if(WEST)
+ initialize_directions = EAST|WEST|NORTH
- node1_dir = turn(dir, 180)
- node2_dir = turn(dir, 90)
- node3_dir = dir
-
- for(var/obj/machinery/atmospherics/target in get_step(src,node1_dir))
- if(target.initialize_directions & get_dir(target,src))
- node1 = target
- break
- for(var/obj/machinery/atmospherics/target in get_step(src,node2_dir))
- if(target.initialize_directions & get_dir(target,src))
- node2 = target
- break
- for(var/obj/machinery/atmospherics/target in get_step(src,node3_dir))
- if(target.initialize_directions & get_dir(target,src))
- node3 = target
- break
-
- update_icon(animation)
- if(animation)
- flick("tvalvem[src.state][!src.state]",src)
+/obj/machinery/atmospherics/tvalve/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference)
+ if(reference == node1)
+ network_node1 = new_network
+ if(state)
+ network_node2 = new_network
else
- icon_state = "tvalvem[state]"
+ network_node3 = new_network
+ else if(reference == node2)
+ network_node2 = new_network
+ if(state)
+ network_node1 = new_network
+ else if(reference == node3)
+ network_node3 = new_network
+ if(!state)
+ network_node1 = new_network
- digital // can be controlled by AI
- name = "digital switching valve"
- desc = "A digitally controlled valve."
- icon = 'icons/obj/atmospherics/digital_valve.dmi'
+ if(new_network.normal_members.Find(src))
+ return 0
- attack_ai(mob/user as mob)
- return src.attack_hand(user)
+ new_network.normal_members += src
- attack_hand(mob/user as mob)
- if(!src.allowed(user))
- user << "\red Access denied."
- return
- ..()
+ if(state)
+ if(reference == node1)
+ if(node2)
+ return node2.network_expand(new_network, src)
+ else if(reference == node2)
+ if(node1)
+ return node1.network_expand(new_network, src)
+ else
+ if(reference == node1)
+ if(node3)
+ return node3.network_expand(new_network, src)
+ else if(reference == node3)
+ if(node1)
+ return node1.network_expand(new_network, src)
- //Radio remote control -eh?
+ return null
- proc
- set_frequency(new_frequency)
- radio_controller.remove_object(src, frequency)
- frequency = new_frequency
- if(frequency)
- radio_connection = radio_controller.add_object(src, frequency, RADIO_ATMOSIA)
+/obj/machinery/atmospherics/tvalve/Del()
+ loc = null
- var/frequency = 0
- var/id = null
- var/datum/radio_frequency/radio_connection
+ if(node1)
+ node1.disconnect(src)
+ del(network_node1)
+ if(node2)
+ node2.disconnect(src)
+ del(network_node2)
+ if(node3)
+ node3.disconnect(src)
+ del(network_node3)
- initialize()
- ..()
- if(frequency)
- set_frequency(frequency)
+ node1 = null
+ node2 = null
+ node3 = null
- receive_signal(datum/signal/signal)
- if(!signal.data["tag"] || (signal.data["tag"] != id))
- return 0
+ ..()
- switch(signal.data["command"])
- if("valve_open")
- if(!state)
- go_to_side()
+/obj/machinery/atmospherics/tvalve/proc/go_to_side()
- if("valve_close")
- if(state)
- go_straight()
+ if(state) return 0
- if("valve_toggle")
- if(state)
- go_straight()
- else
- go_to_side()
\ No newline at end of file
+ state = 1
+ update_icon()
+
+ if(network_node1)
+ del(network_node1)
+ if(network_node3)
+ del(network_node3)
+ build_network()
+
+ if(network_node1&&network_node2)
+ network_node1.merge(network_node2)
+ network_node2 = network_node1
+
+ if(network_node1)
+ network_node1.update = 1
+ else if(network_node2)
+ network_node2.update = 1
+
+ return 1
+
+/obj/machinery/atmospherics/tvalve/proc/go_straight()
+
+ if(!state)
+ return 0
+
+ state = 0
+ update_icon()
+
+ if(network_node1)
+ del(network_node1)
+ if(network_node2)
+ del(network_node2)
+ build_network()
+
+ if(network_node1&&network_node3)
+ network_node1.merge(network_node3)
+ network_node3 = network_node1
+
+ if(network_node1)
+ network_node1.update = 1
+ else if(network_node3)
+ network_node3.update = 1
+
+ return 1
+
+/obj/machinery/atmospherics/tvalve/attack_ai(mob/user as mob)
+ return
+
+/obj/machinery/atmospherics/tvalve/attack_paw(mob/user as mob)
+ return attack_hand(user)
+
+/obj/machinery/atmospherics/tvalve/attack_hand(mob/user as mob)
+ src.add_fingerprint(usr)
+ update_icon(1)
+ sleep(10)
+ if (src.state)
+ src.go_straight()
+ else
+ src.go_to_side()
+
+/obj/machinery/atmospherics/tvalve/process()
+ ..()
+ . = PROCESS_KILL
+ //machines.Remove(src)
+
+ return
+
+/obj/machinery/atmospherics/tvalve/initialize()
+ var/node1_dir
+ var/node2_dir
+ var/node3_dir
+
+ node1_dir = turn(dir, 180)
+ node2_dir = turn(dir, -90)
+ node3_dir = dir
+
+ for(var/obj/machinery/atmospherics/target in get_step(src,node1_dir))
+ if(target.initialize_directions & get_dir(target,src))
+ node1 = target
+ break
+ for(var/obj/machinery/atmospherics/target in get_step(src,node2_dir))
+ if(target.initialize_directions & get_dir(target,src))
+ node2 = target
+ break
+ for(var/obj/machinery/atmospherics/target in get_step(src,node3_dir))
+ if(target.initialize_directions & get_dir(target,src))
+ node3 = target
+ break
+
+ update_icon()
+ update_underlays()
+
+/obj/machinery/atmospherics/tvalve/build_network()
+ if(!network_node1 && node1)
+ network_node1 = new /datum/pipe_network()
+ network_node1.normal_members += src
+ network_node1.build_network(node1, src)
+
+ if(!network_node2 && node2)
+ network_node2 = new /datum/pipe_network()
+ network_node2.normal_members += src
+ network_node2.build_network(node2, src)
+
+ if(!network_node3 && node3)
+ network_node3 = new /datum/pipe_network()
+ network_node3.normal_members += src
+ network_node3.build_network(node3, src)
+
+
+/obj/machinery/atmospherics/tvalve/return_network(obj/machinery/atmospherics/reference)
+ build_network()
+
+ if(reference==node1)
+ return network_node1
+
+ if(reference==node2)
+ return network_node2
+
+ if(reference==node3)
+ return network_node3
+
+ return null
+
+/obj/machinery/atmospherics/tvalve/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network)
+ if(network_node1 == old_network)
+ network_node1 = new_network
+ if(network_node2 == old_network)
+ network_node2 = new_network
+ if(network_node3 == old_network)
+ network_node3 = new_network
+
+ return 1
+
+/obj/machinery/atmospherics/tvalve/return_network_air(datum/network/reference)
+ return null
+
+/obj/machinery/atmospherics/tvalve/disconnect(obj/machinery/atmospherics/reference)
+ if(reference==node1)
+ del(network_node1)
+ node1 = null
+
+ else if(reference==node2)
+ del(network_node2)
+ node2 = null
+
+ else if(reference==node3)
+ del(network_node3)
+ node2 = null
+
+ update_underlays()
+
+ return null
+
+/obj/machinery/atmospherics/tvalve/digital // can be controlled by AI
+ name = "digital switching valve"
+ desc = "A digitally controlled valve."
+ icon = 'icons/atmos/digital_tvalve.dmi'
+
+ var/frequency = 0
+ var/id = null
+ var/datum/radio_frequency/radio_connection
+
+/obj/machinery/atmospherics/tvalve/digital/bypass
+ icon_state = "map_tvalve1"
+ state = 1
+
+/obj/machinery/atmospherics/tvalve/digital/power_change()
+ var/old_stat = stat
+ ..()
+ if(old_stat != stat)
+ update_icon()
+
+/obj/machinery/atmospherics/tvalve/digital/update_icon()
+ ..()
+ if(!powered())
+ icon_state = "tvalvenopower"
+
+/obj/machinery/atmospherics/tvalve/digital/attack_ai(mob/user as mob)
+ return src.attack_hand(user)
+
+/obj/machinery/atmospherics/tvalve/digital/attack_hand(mob/user as mob)
+ if(!powered())
+ return
+ if(!src.allowed(user))
+ user << "\red Access denied."
+ return
+ ..()
+
+//Radio remote control
+
+/obj/machinery/atmospherics/tvalve/digital/proc/set_frequency(new_frequency)
+ radio_controller.remove_object(src, frequency)
+ frequency = new_frequency
+ if(frequency)
+ radio_connection = radio_controller.add_object(src, frequency, RADIO_ATMOSIA)
+
+
+
+/obj/machinery/atmospherics/tvalve/digital/initialize()
+ ..()
+ if(frequency)
+ set_frequency(frequency)
+
+/obj/machinery/atmospherics/tvalve/digital/receive_signal(datum/signal/signal)
+ if(!signal.data["tag"] || (signal.data["tag"] != id))
+ return 0
+
+ switch(signal.data["command"])
+ if("valve_open")
+ if(!state)
+ go_to_side()
+
+ if("valve_close")
+ if(state)
+ go_straight()
+
+ if("valve_toggle")
+ if(state)
+ go_straight()
+ else
+ go_to_side()
+
+/obj/machinery/atmospherics/tvalve/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
+ if (!istype(W, /obj/item/weapon/wrench))
+ return ..()
+ if (istype(src, /obj/machinery/atmospherics/tvalve/digital))
+ user << "\red You cannot unwrench this [src], it's too complicated."
+ return 1
+ var/datum/gas_mixture/int_air = return_air()
+ var/datum/gas_mixture/env_air = loc.return_air()
+ if ((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE)
+ user << "\red You cannot unwrench this [src], it too exerted due to internal pressure."
+ add_fingerprint(user)
+ return 1
+ playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
+ user << "\blue You begin to unfasten \the [src]..."
+ if (do_after(user, 40))
+ user.visible_message( \
+ "[user] unfastens \the [src].", \
+ "\blue You have unfastened \the [src].", \
+ "You hear ratchet.")
+ new /obj/item/pipe(loc, make_from=src)
+ del(src)
+
+/obj/machinery/atmospherics/tvalve/mirrored
+ icon_state = "map_tvalvem0"
+
+/obj/machinery/atmospherics/tvalve/mirrored/bypass
+ icon_state = "map_tvalvem1"
+ state = 1
+
+/obj/machinery/atmospherics/tvalve/mirrored/initialize_directions()
+ switch(dir)
+ if(NORTH)
+ initialize_directions = SOUTH|NORTH|WEST
+ if(SOUTH)
+ initialize_directions = NORTH|SOUTH|EAST
+ if(EAST)
+ initialize_directions = WEST|EAST|NORTH
+ if(WEST)
+ initialize_directions = EAST|WEST|SOUTH
+
+/obj/machinery/atmospherics/tvalve/mirrored/initialize()
+ var/node1_dir
+ var/node2_dir
+ var/node3_dir
+
+ node1_dir = turn(dir, 180)
+ node2_dir = turn(dir, 90)
+ node3_dir = dir
+
+ for(var/obj/machinery/atmospherics/target in get_step(src,node1_dir))
+ if(target.initialize_directions & get_dir(target,src))
+ node1 = target
+ break
+ for(var/obj/machinery/atmospherics/target in get_step(src,node2_dir))
+ if(target.initialize_directions & get_dir(target,src))
+ node2 = target
+ break
+ for(var/obj/machinery/atmospherics/target in get_step(src,node3_dir))
+ if(target.initialize_directions & get_dir(target,src))
+ node3 = target
+ break
+
+ update_icon()
+ update_underlays()
+
+/obj/machinery/atmospherics/tvalve/mirrored/update_icon(animation)
+ if(animation)
+ flick("tvalvem[src.state][!src.state]",src)
+ else
+ icon_state = "tvalvem[state]"
+
+/obj/machinery/atmospherics/tvalve/mirrored/digital // can be controlled by AI
+ name = "digital switching valve"
+ desc = "A digitally controlled valve."
+ icon = 'icons/atmos/digital_tvalve.dmi'
+
+ var/frequency = 0
+ var/id = null
+ var/datum/radio_frequency/radio_connection
+
+/obj/machinery/atmospherics/tvalve/mirrored/digital/bypass
+ icon_state = "map_tvalvem1"
+ state = 1
+
+/obj/machinery/atmospherics/tvalve/mirrored/digital/power_change()
+ var/old_stat = stat
+ ..()
+ if(old_stat != stat)
+ update_icon()
+
+/obj/machinery/atmospherics/tvalve/mirrored/digital/update_icon()
+ ..()
+ if(!powered())
+ icon_state = "tvalvemnopower"
+
+/obj/machinery/atmospherics/tvalve/mirrored/digital/attack_ai(mob/user as mob)
+ return src.attack_hand(user)
+
+/obj/machinery/atmospherics/tvalve/mirrored/digital/attack_hand(mob/user as mob)
+ if(!powered())
+ return
+ if(!src.allowed(user))
+ user << "\red Access denied."
+ return
+ ..()
+
+//Radio remote control -eh?
+
+/obj/machinery/atmospherics/tvalve/mirrored/digital/proc/set_frequency(new_frequency)
+ radio_controller.remove_object(src, frequency)
+ frequency = new_frequency
+ if(frequency)
+ radio_connection = radio_controller.add_object(src, frequency, RADIO_ATMOSIA)
+
+/obj/machinery/atmospherics/tvalve/mirrored/digital/initialize()
+ ..()
+ if(frequency)
+ set_frequency(frequency)
+
+/obj/machinery/atmospherics/tvalve/mirrored/digital/receive_signal(datum/signal/signal)
+ if(!signal.data["tag"] || (signal.data["tag"] != id))
+ return 0
+
+ switch(signal.data["command"])
+ if("valve_open")
+ if(!state)
+ go_to_side()
+
+ if("valve_close")
+ if(state)
+ go_straight()
+
+ if("valve_toggle")
+ if(state)
+ go_straight()
+ else
+ go_to_side()
\ No newline at end of file
diff --git a/code/ATMOSPHERICS/components/unary/cold_sink.dm b/code/ATMOSPHERICS/components/unary/cold_sink.dm
index ef1d5f60bea..bd3c3b17965 100644
--- a/code/ATMOSPHERICS/components/unary/cold_sink.dm
+++ b/code/ATMOSPHERICS/components/unary/cold_sink.dm
@@ -1,41 +1,207 @@
-/obj/machinery/atmospherics/unary/cold_sink
- icon = 'icons/obj/atmospherics/cold_sink.dmi'
- icon_state = "intact_off"
- density = 1
- use_power = 1
+//TODO: Put this under a common parent type with heaters to cut down on the copypasta
+#define FREEZER_PERF_MULT 2.5
- name = "Cold Sink"
+/obj/machinery/atmospherics/unary/freezer
+ name = "gas cooling system"
desc = "Cools gas when connected to pipe network"
+ icon = 'icons/obj/Cryogenic2.dmi'
+ icon_state = "freezer_0"
+ density = 1
+
+ anchored = 1.0
+
+ var/heatsink_temperature = T20C //the constant temperature resevoir into which the freezer pumps heat. Probably the hull of the station or something.
+ var/internal_volume = 600 //L
var/on = 0
+ use_power = 0
+ idle_power_usage = 5 //5 Watts for thermostat related circuitry
+ active_power_usage //50 kW. The power rating of the freezer
- var/current_temperature = T20C
- var/current_heat_capacity = 50000 //totally random
+ var/max_power_usage = 20000 //power rating when the usage is turned up to 100
+ var/power_setting = 100
+
+ var/set_temperature = T20C //thermostat
+ var/cooling = 0
+ var/opened = 0 //for deconstruction
+
+/obj/machinery/atmospherics/unary/freezer/New()
+ ..()
+ air_contents.volume = internal_volume
+ initialize_directions = dir
+
+ component_parts = list()
+ component_parts += new /obj/item/weapon/circuitboard/unary_atmos/cooler(src)
+ component_parts += new /obj/item/weapon/stock_parts/matter_bin(src)
+ component_parts += new /obj/item/weapon/stock_parts/capacitor(src)
+ component_parts += new /obj/item/weapon/stock_parts/capacitor(src)
+ component_parts += new /obj/item/weapon/stock_parts/manipulator(src)
+
+ active_power_usage = max_power_usage * (power_setting/100)
+
+/obj/machinery/atmospherics/unary/freezer/initialize()
+ if(node) return
+
+ var/node_connect = dir
+
+ for(var/obj/machinery/atmospherics/target in get_step(src,node_connect))
+ if(target.initialize_directions & get_dir(target,src))
+ node = target
+ break
update_icon()
- if(node)
- icon_state = "intact_[on?("on"):("off")]"
+
+
+/obj/machinery/atmospherics/unary/freezer/update_icon()
+ if(src.node)
+ if(src.on && cooling)
+ icon_state = "freezer_1"
else
- icon_state = "exposed"
+ icon_state = "freezer"
+ else
+ icon_state = "freezer_0"
+ return
- on = 0
+/obj/machinery/atmospherics/unary/freezer/attack_ai(mob/user as mob)
+ src.ui_interact(user)
+/obj/machinery/atmospherics/unary/freezer/attack_paw(mob/user as mob)
+ src.ui_interact(user)
+
+/obj/machinery/atmospherics/unary/freezer/attack_hand(mob/user as mob)
+ src.ui_interact(user)
+
+/obj/machinery/atmospherics/unary/freezer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
+ // this is the data which will be sent to the ui
+ var/data[0]
+ data["on"] = on ? 1 : 0
+ data["gasPressure"] = round(air_contents.return_pressure())
+ data["gasTemperature"] = round(air_contents.temperature)
+ data["minGasTemperature"] = 0
+ data["maxGasTemperature"] = round(T20C+500)
+ data["targetGasTemperature"] = round(set_temperature)
+ data["powerSetting"] = power_setting
+
+ var/temp_class = "good"
+ if (air_contents.temperature > (T0C - 20))
+ temp_class = "bad"
+ else if (air_contents.temperature < (T0C - 20) && air_contents.temperature > (T0C - 100))
+ temp_class = "average"
+ data["gasTemperatureClass"] = temp_class
+
+ // update the ui if it exists, returns null if no ui is passed/found
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
+ if (!ui)
+ // the ui does not exist, so we'll create a new() one
+ // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
+ ui = new(user, src, ui_key, "freezer.tmpl", "Gas Cooling System", 440, 300)
+ // when the ui is first opened this is the data it will use
+ ui.set_initial_data(data)
+ // open the new ui window
+ ui.open()
+ // auto update every Master Controller tick
+ ui.set_auto_update(1)
+
+/obj/machinery/atmospherics/unary/freezer/Topic(href, href_list)
+ if (href_list["toggleStatus"])
+ src.on = !src.on
+ update_icon()
+ update_use_power(on)
+ if(href_list["temp"])
+ var/amount = text2num(href_list["temp"])
+ if(amount > 0)
+ src.set_temperature = min(src.set_temperature+amount, 1000)
+ else
+ src.set_temperature = max(src.set_temperature+amount, 0)
+ if(href_list["setPower"]) //setting power to 0 is redundant anyways
+ var/new_setting = between(0, text2num(href_list["setPower"]), 100)
+ set_power_level(new_setting)
+
+ src.add_fingerprint(usr)
+ return 1
+
+/obj/machinery/atmospherics/unary/freezer/process()
+ ..()
+ if(stat & (NOPOWER|BROKEN) || !on)
+ cooling = 0
+ update_use_power(0)
+ update_icon()
return
- process()
- ..()
- if(!on || !network)
- return 0
- var/air_heat_capacity = air_contents.heat_capacity()
- var/combined_heat_capacity = current_heat_capacity + air_heat_capacity
- var/old_temperature = air_contents.temperature
+ if (network && air_contents.temperature > set_temperature)
+ cooling = 1
+ update_use_power(2)
- if(combined_heat_capacity > 0)
- var/combined_energy = current_temperature*current_heat_capacity + air_heat_capacity*air_contents.temperature
- air_contents.temperature = combined_energy/combined_heat_capacity
+ var/heat_transfer = max( -air_contents.get_thermal_energy_change(set_temperature - 5), 0 )
- //todo: have current temperature affected. require power to bring down current temperature again
+ //Assume the heat is being pumped into the hull which is fixed at heatsink_temperature
+ //not /really/ proper thermodynamics but whatever
+ var/cop = FREEZER_PERF_MULT * air_contents.temperature/heatsink_temperature //heatpump coefficient of performance from thermodynamics -> power used = heat_transfer/cop
+ heat_transfer = min(heat_transfer, cop * active_power_usage) //limit heat transfer by available power
- if(abs(old_temperature-air_contents.temperature) > 1)
- network.update = 1
- return 1
\ No newline at end of file
+ var/removed = -air_contents.add_thermal_energy(-heat_transfer) //remove the heat
+ if (debug)
+ visible_message("[src]: Removing [removed] W.")
+
+ network.update = 1
+ else
+ cooling = 0
+ update_use_power(1)
+
+ update_icon()
+
+//upgrading parts
+/obj/machinery/atmospherics/unary/freezer/RefreshParts()
+ ..()
+ var/cap_rating = 0
+ var/cap_count = 0
+ var/manip_rating = 0
+ var/manip_count = 0
+ var/bin_rating = 0
+ var/bin_count = 0
+
+ for(var/obj/item/weapon/stock_parts/P in component_parts)
+ if(istype(P, /obj/item/weapon/stock_parts/capacitor))
+ cap_rating += P.rating
+ cap_count++
+ if(istype(P, /obj/item/weapon/stock_parts/manipulator))
+ manip_rating += P.rating
+ manip_count++
+ if(istype(P, /obj/item/weapon/stock_parts/matter_bin))
+ bin_rating += P.rating
+ bin_count++
+ cap_rating /= cap_count
+ bin_rating /= bin_count
+ manip_rating /= manip_count
+
+ active_power_usage = initial(active_power_usage)*cap_rating //more powerful
+ heatsink_temperature = initial(heatsink_temperature)/((manip_rating+bin_rating)/2) //more efficient
+ air_contents.volume = max(initial(internal_volume) - 200, 0) + 200*bin_rating
+ set_power_level(power_setting)
+
+/obj/machinery/atmospherics/unary/freezer/proc/set_power_level(var/new_power_setting)
+ power_setting = new_power_setting
+
+ var/old_power_usage = active_power_usage
+ active_power_usage = max_power_usage * (power_setting/100)
+
+ if (use_power >= 2 && old_power_usage != active_power_usage)
+ force_power_update()
+
+//dismantling code. copied from autolathe
+/obj/machinery/atmospherics/unary/freezer/attackby(var/obj/item/O as obj, var/mob/user as mob)
+ if(istype(O, /obj/item/weapon/screwdriver))
+ opened = !opened
+ user << "You [opened ? "open" : "close"] the maintenance hatch of [src]."
+ return
+
+ if (opened && istype(O, /obj/item/weapon/crowbar))
+ dismantle()
+ return
+
+ ..()
+
+/obj/machinery/atmospherics/unary/freezer/examine()
+ ..()
+ if (opened)
+ usr << "The maintenance hatch is open."
\ No newline at end of file
diff --git a/code/ATMOSPHERICS/components/unary/heat_source.dm b/code/ATMOSPHERICS/components/unary/heat_source.dm
index 2d3e9b67a8e..707e740f66e 100644
--- a/code/ATMOSPHERICS/components/unary/heat_source.dm
+++ b/code/ATMOSPHERICS/components/unary/heat_source.dm
@@ -1,43 +1,189 @@
-/obj/machinery/atmospherics/unary/heat_reservoir
-//currently the same code as cold_sink but anticipating process() changes
+//TODO: Put this under a common parent type with freezers to cut down on the copypasta
- icon = 'icons/obj/atmospherics/cold_sink.dmi'
- icon_state = "intact_off"
+/obj/machinery/atmospherics/unary/heater
+ name = "gas heating system"
+ desc = "Heats gas when connected to a pipe network"
+ icon = 'icons/obj/Cryogenic2.dmi'
+ icon_state = "heater_0"
density = 1
- use_power = 1
- name = "Heat Reservoir"
- desc = "Heats gas when connected to pipe network"
+ anchored = 1.0
+
+ var/set_temperature = T20C //thermostat
+ var/max_temperature = T20C + 680
+ var/internal_volume = 600 //L
var/on = 0
+ use_power = 0
+ idle_power_usage = 5 //5 Watts for thermostat related circuitry
+ active_power_usage //50 kW. The power rating of the heater
+
+ var/max_power_usage = 20000 //power rating when the usage is turned up to 100
+ var/power_setting = 100
- var/current_temperature = T20C
- var/current_heat_capacity = 50000 //totally random
+ var/heating = 0 //mainly for icon updates
+ var/opened = 0 //for deconstruction
+
+/obj/machinery/atmospherics/unary/heater/New()
+ ..()
+ air_contents.volume = internal_volume
+ initialize_directions = dir
+
+ component_parts = list()
+ component_parts += new /obj/item/weapon/circuitboard/unary_atmos/heater(src)
+ component_parts += new /obj/item/weapon/stock_parts/matter_bin(src)
+ component_parts += new /obj/item/weapon/stock_parts/capacitor(src)
+ component_parts += new /obj/item/weapon/stock_parts/capacitor(src)
+
+ active_power_usage = max_power_usage * (power_setting/100)
+
+/obj/machinery/atmospherics/unary/heater/initialize()
+ if(node) return
+
+ var/node_connect = dir
+
+ for(var/obj/machinery/atmospherics/target in get_step(src,node_connect))
+ if(target.initialize_directions & get_dir(target,src))
+ node = target
+ break
update_icon()
- if(node)
- icon_state = "intact_[on?("on"):("off")]"
+
+
+/obj/machinery/atmospherics/unary/heater/update_icon()
+ if(src.node)
+ if(src.on && src.heating)
+ icon_state = "heater_1"
else
- icon_state = "exposed"
+ icon_state = "heater"
+ else
+ icon_state = "heater_0"
+ return
- on = 0
+/obj/machinery/atmospherics/unary/heater/process()
+ ..()
+
+ if(stat & (NOPOWER|BROKEN) || !on)
+ heating = 0
+ update_use_power(0)
+ update_icon()
return
- process()
- ..()
- if(!on || !network)
- return 0
- var/air_heat_capacity = air_contents.heat_capacity()
- var/combined_heat_capacity = current_heat_capacity + air_heat_capacity
- var/old_temperature = air_contents.temperature
+ if (network && air_contents.total_moles && air_contents.temperature < set_temperature)
+ update_use_power(2)
+ air_contents.add_thermal_energy(active_power_usage)
- if(combined_heat_capacity > 0)
- var/combined_energy = current_temperature*current_heat_capacity + air_heat_capacity*air_contents.temperature
- air_contents.temperature = combined_energy/combined_heat_capacity
+ heating = 1
+ network.update = 1
+ else
+ heating = 0
+ update_use_power(1)
- //todo: have current temperature affected. require power to bring up current temperature again
+ update_icon()
- if(abs(old_temperature-air_contents.temperature) > 1)
- network.update = 1
- return 1
\ No newline at end of file
+/obj/machinery/atmospherics/unary/heater/attack_ai(mob/user as mob)
+ src.ui_interact(user)
+
+/obj/machinery/atmospherics/unary/heater/attack_paw(mob/user as mob)
+ src.ui_interact(user)
+
+/obj/machinery/atmospherics/unary/heater/attack_hand(mob/user as mob)
+ src.ui_interact(user)
+
+/obj/machinery/atmospherics/unary/heater/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
+ // this is the data which will be sent to the ui
+ var/data[0]
+ data["on"] = on ? 1 : 0
+ data["gasPressure"] = round(air_contents.return_pressure())
+ data["gasTemperature"] = round(air_contents.temperature)
+ data["minGasTemperature"] = 0
+ data["maxGasTemperature"] = round(max_temperature)
+ data["targetGasTemperature"] = round(set_temperature)
+ data["powerSetting"] = power_setting
+
+ var/temp_class = "normal"
+ if (air_contents.temperature > (T20C+40))
+ temp_class = "bad"
+ data["gasTemperatureClass"] = temp_class
+
+ // update the ui if it exists, returns null if no ui is passed/found
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
+ if (!ui)
+ // the ui does not exist, so we'll create a new() one
+ // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
+ ui = new(user, src, ui_key, "freezer.tmpl", "Gas Heating System", 440, 300)
+ // when the ui is first opened this is the data it will use
+ ui.set_initial_data(data)
+ // open the new ui window
+ ui.open()
+ // auto update every Master Controller tick
+ ui.set_auto_update(1)
+
+/obj/machinery/atmospherics/unary/heater/Topic(href, href_list)
+ if (href_list["toggleStatus"])
+ src.on = !src.on
+ update_use_power(on)
+ update_icon()
+ if(href_list["temp"])
+ var/amount = text2num(href_list["temp"])
+ if(amount > 0)
+ src.set_temperature = min(src.set_temperature+amount, max_temperature)
+ else
+ src.set_temperature = max(src.set_temperature+amount, 0)
+ if(href_list["setPower"]) //setting power to 0 is redundant anyways
+ var/new_setting = between(0, text2num(href_list["setPower"]), 100)
+ set_power_level(new_setting)
+
+ src.add_fingerprint(usr)
+ return 1
+
+//upgrading parts
+/obj/machinery/atmospherics/unary/heater/RefreshParts()
+ ..()
+
+ var/cap_rating = 0
+ var/cap_count = 0
+ var/bin_rating = 0
+ var/bin_count = 0
+ for(var/obj/item/weapon/stock_parts/P in component_parts)
+ if(istype(P, /obj/item/weapon/stock_parts/capacitor))
+ cap_rating += P.rating
+ cap_count++
+ if(istype(P, /obj/item/weapon/stock_parts/matter_bin))
+ bin_rating += P.rating
+ bin_count++
+ cap_rating /= cap_count
+ bin_rating /= bin_count
+
+ max_power_usage = initial(max_power_usage)*cap_rating
+ max_temperature = max(initial(max_temperature) - T20C, 0)*((bin_rating*2 + cap_rating)/3) + T20C
+ air_contents.volume = max(initial(internal_volume) - 200, 0) + 200*bin_rating
+ set_power_level(power_setting)
+
+/obj/machinery/atmospherics/unary/heater/proc/set_power_level(var/new_power_setting)
+ power_setting = new_power_setting
+
+ var/old_power_usage = active_power_usage
+ active_power_usage = max_power_usage * (power_setting/100)
+
+ if (use_power >= 2 && old_power_usage != active_power_usage)
+ force_power_update()
+
+//dismantling code. copied from autolathe
+/obj/machinery/atmospherics/unary/heater/attackby(var/obj/item/O as obj, var/mob/user as mob)
+ if(istype(O, /obj/item/weapon/screwdriver))
+ opened = !opened
+ user << "You [opened ? "open" : "close"] the maintenance hatch of [src]."
+ return
+
+ if (opened && istype(O, /obj/item/weapon/crowbar))
+ dismantle()
+ return
+
+ ..()
+
+/obj/machinery/atmospherics/unary/heater/examine()
+ ..()
+ if (opened)
+ usr << "The maintenance hatch is open."
\ No newline at end of file
diff --git a/code/ATMOSPHERICS/components/unary/outlet_injector.dm b/code/ATMOSPHERICS/components/unary/outlet_injector.dm
index f070882ba82..d603da8cd14 100644
--- a/code/ATMOSPHERICS/components/unary/outlet_injector.dm
+++ b/code/ATMOSPHERICS/components/unary/outlet_injector.dm
@@ -1,15 +1,24 @@
+//Basically a one way passive valve. If the pressure inside is greater than the environment then gas will flow passively,
+//but it does not permit gas to flow back from the environment into the injector. Can be turned off to prevent any gas flow.
+//When it receives the "inject" signal, it will try to pump it's entire contents into the environment regardless of pressure, using power.
+
/obj/machinery/atmospherics/unary/outlet_injector
- icon = 'icons/obj/atmospherics/outlet_injector.dmi'
- icon_state = "off"
+ icon = 'icons/atmos/injector.dmi'
+ icon_state = "map_injector"
use_power = 1
+ layer = 3
- name = "Air Injector"
- desc = "Has a valve and pump attached to it"
+ name = "air injector"
+ desc = "Passively injects air into its surroundings. Has a valve attached to it that can control flow rate."
+ use_power = 1
+ idle_power_usage = 150 //internal circuitry, friction losses and stuff
+ active_power_usage = 15000 //This also doubles as a measure of how powerful the pump is, in Watts. 15000 W ~ 20 HP
+
var/on = 0
var/injecting = 0
- var/volume_rate = 50
+ var/volume_rate = 50 //flow rate limit
var/frequency = 0
var/id = null
@@ -17,131 +26,136 @@
level = 1
- update_icon()
- if(node)
- if(on && !(stat & NOPOWER))
- icon_state = "[level == 1 && istype(loc, /turf/simulated) ? "h" : "" ]on"
- else
- icon_state = "[level == 1 && istype(loc, /turf/simulated) ? "h" : "" ]off"
- else
- icon_state = "exposed"
- on = 0
+/obj/machinery/atmospherics/unary/outlet_injector/New()
+ ..()
+ air_contents.volume = ATMOS_DEFAULT_VOLUME_PUMP + 500 //Give it a small reservoir for injecting. Also allows it to have a higher flow rate limit than vent pumps, to differentiate injectors a bit more.
- return
+/obj/machinery/atmospherics/unary/outlet_injector/update_icon()
+ if(!powered())
+ icon_state = "off"
+ else
+ icon_state = "[on ? "on" : "off"]"
- power_change()
- var/old_stat = stat
- ..()
- if(old_stat != stat)
- update_icon()
-
-
- process()
- ..()
- injecting = 0
-
- if(!on || stat & NOPOWER)
- return 0
-
- if(air_contents.temperature > 0)
- var/transfer_moles = (air_contents.return_pressure())*volume_rate/(air_contents.temperature * R_IDEAL_GAS_EQUATION)
-
- var/datum/gas_mixture/removed = air_contents.remove(transfer_moles)
-
- loc.assume_air(removed)
-
- if(network)
- network.update = 1
-
- return 1
-
- proc/inject()
- if(on || injecting)
- return 0
-
- injecting = 1
-
- if(air_contents.temperature > 0)
- var/transfer_moles = (air_contents.return_pressure())*volume_rate/(air_contents.temperature * R_IDEAL_GAS_EQUATION)
-
- var/datum/gas_mixture/removed = air_contents.remove(transfer_moles)
-
- loc.assume_air(removed)
-
- if(network)
- network.update = 1
-
- flick("inject", src)
-
- proc
- set_frequency(new_frequency)
- radio_controller.remove_object(src, frequency)
- frequency = new_frequency
- if(frequency)
- radio_connection = radio_controller.add_object(src, frequency)
-
- broadcast_status()
- if(!radio_connection)
- return 0
-
- var/datum/signal/signal = new
- signal.transmission_method = 1 //radio signal
- signal.source = src
-
- signal.data = list(
- "tag" = id,
- "device" = "AO",
- "power" = on,
- "volume_rate" = volume_rate,
- //"timestamp" = world.time,
- "sigtype" = "status"
- )
-
- radio_connection.post_signal(src, signal)
-
- return 1
-
- initialize()
- ..()
-
- set_frequency(frequency)
-
- receive_signal(datum/signal/signal)
- if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command"))
- return 0
-
- if("power" in signal.data)
- on = text2num(signal.data["power"])
-
- if("power_toggle" in signal.data)
- on = !on
-
- if("inject" in signal.data)
- spawn inject()
+/obj/machinery/atmospherics/unary/outlet_injector/update_underlays()
+ if(..())
+ underlays.Cut()
+ var/turf/T = get_turf(src)
+ if(!istype(T))
return
+ add_underlay(T, node, dir)
- if("set_volume_rate" in signal.data)
- var/number = text2num(signal.data["set_volume_rate"])
- volume_rate = between(0, number, air_contents.volume)
-
- if("status" in signal.data)
- spawn(2)
- broadcast_status()
- return //do not update_icon
-
- //log_admin("DEBUG \[[world.timeofday]\]: outlet_injector/receive_signal: unknown command \"[signal.data["command"]]\"\n[signal.debug_print()]")
- //return
- spawn(2)
- broadcast_status()
+/obj/machinery/atmospherics/unary/outlet_injector/power_change()
+ var/old_stat = stat
+ ..()
+ if(old_stat != stat)
update_icon()
- hide(var/i) //to make the little pipe section invisible, the icon changes.
- if(node)
- if(on)
- icon_state = "[i == 1 && istype(loc, /turf/simulated) ? "h" : "" ]on"
- else
- icon_state = "[i == 1 && istype(loc, /turf/simulated) ? "h" : "" ]off"
- else
- icon_state = "[i == 1 && istype(loc, /turf/simulated) ? "h" : "" ]exposed"
- on = 0
- return
\ No newline at end of file
+/obj/machinery/atmospherics/unary/outlet_injector/process()
+ ..()
+ injecting = 0
+
+ if((stat & (NOPOWER|BROKEN)) || !on)
+ update_use_power(0) //usually we get here because a player turned a pump off - definitely want to update.
+ last_flow_rate = 0
+ return
+
+ var/power_draw = -1
+ var/datum/gas_mixture/environment = loc.return_air()
+
+ if(environment && air_contents.temperature > 0)
+ var/transfer_moles = (volume_rate/air_contents.volume)*air_contents.total_moles //apply flow rate limit
+ power_draw = pump_gas(src, air_contents, environment, transfer_moles, active_power_usage)
+
+ if (power_draw < 0)
+ //update_use_power(0)
+ use_power = 0 //don't force update - easier on CPU
+ last_flow_rate = 0
+ else
+ handle_power_draw(power_draw)
+
+ if(network)
+ network.update = 1
+
+ return 1
+
+/obj/machinery/atmospherics/unary/outlet_injector/proc/inject()
+ if(on || injecting || (stat & NOPOWER))
+ return 0
+
+ var/datum/gas_mixture/environment = loc.return_air()
+ if (!environment)
+ return 0
+
+ injecting = 1
+
+ if(air_contents.temperature > 0)
+ var/power_used = pump_gas(src, air_contents, environment, air_contents.total_moles, active_power_usage)
+ use_power(power_used)
+
+ if(network)
+ network.update = 1
+
+ flick("inject", src)
+
+/obj/machinery/atmospherics/unary/outlet_injector/proc/set_frequency(new_frequency)
+ radio_controller.remove_object(src, frequency)
+ frequency = new_frequency
+ if(frequency)
+ radio_connection = radio_controller.add_object(src, frequency)
+
+/obj/machinery/atmospherics/unary/outlet_injector/proc/broadcast_status()
+ if(!radio_connection)
+ return 0
+
+ var/datum/signal/signal = new
+ signal.transmission_method = 1 //radio signal
+ signal.source = src
+
+ signal.data = list(
+ "tag" = id,
+ "device" = "AO",
+ "power" = on,
+ "volume_rate" = volume_rate,
+ "sigtype" = "status"
+ )
+
+ radio_connection.post_signal(src, signal)
+
+ return 1
+
+/obj/machinery/atmospherics/unary/outlet_injector/initialize()
+ ..()
+
+ set_frequency(frequency)
+
+/obj/machinery/atmospherics/unary/outlet_injector/receive_signal(datum/signal/signal)
+ if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command"))
+ return 0
+
+ if(signal.data["power"])
+ on = text2num(signal.data["power"])
+ update_use_power(on)
+
+ if(signal.data["power_toggle"])
+ on = !on
+ update_use_power(on)
+
+ if(signal.data["inject"])
+ spawn inject()
+ return
+
+ if(signal.data["set_volume_rate"])
+ var/number = text2num(signal.data["set_volume_rate"])
+ volume_rate = between(0, number, air_contents.volume)
+
+ if(signal.data["status"])
+ spawn(2)
+ broadcast_status()
+ return //do not update_icon
+
+ spawn(2)
+ broadcast_status()
+ update_icon()
+
+/obj/machinery/atmospherics/unary/outlet_injector/hide(var/i)
+ update_underlays()
\ No newline at end of file
diff --git a/code/ATMOSPHERICS/components/unary/oxygen_generator.dm b/code/ATMOSPHERICS/components/unary/oxygen_generator.dm
index 9a58be7c302..14bbb46bbaa 100644
--- a/code/ATMOSPHERICS/components/unary/oxygen_generator.dm
+++ b/code/ATMOSPHERICS/components/unary/oxygen_generator.dm
@@ -33,7 +33,7 @@ obj/machinery/atmospherics/unary/oxygen_generator
if(!on)
return 0
- var/total_moles = air_contents.total_moles()
+ var/total_moles = air_contents.total_moles
if(total_moles < oxygen_content)
var/current_heat_capacity = air_contents.heat_capacity()
@@ -41,7 +41,7 @@ obj/machinery/atmospherics/unary/oxygen_generator
var/added_oxygen = oxygen_content - total_moles
air_contents.temperature = (current_heat_capacity*air_contents.temperature + 20*added_oxygen*T0C)/(current_heat_capacity+20*added_oxygen)
- air_contents.oxygen += added_oxygen
+ air_contents.adjust_gas("oxygen", added_oxygen)
if(network)
network.update = 1
diff --git a/code/ATMOSPHERICS/components/unary/unary_base.dm b/code/ATMOSPHERICS/components/unary/unary_base.dm
index b755071487e..2c5f63f6079 100644
--- a/code/ATMOSPHERICS/components/unary/unary_base.dm
+++ b/code/ATMOSPHERICS/components/unary/unary_base.dm
@@ -50,6 +50,7 @@
break
update_icon()
+ update_underlays()
build_network()
if(!network && node)
@@ -85,4 +86,7 @@
del(network)
node = null
+ update_icon()
+ update_underlays()
+
return null
\ No newline at end of file
diff --git a/code/ATMOSPHERICS/components/unary/vent_pump.dm b/code/ATMOSPHERICS/components/unary/vent_pump.dm
index f4e9cb97509..73c5663f460 100644
--- a/code/ATMOSPHERICS/components/unary/vent_pump.dm
+++ b/code/ATMOSPHERICS/components/unary/vent_pump.dm
@@ -1,16 +1,24 @@
+#define DEFAULT_PRESSURE_DELTA 10000
+
#define EXTERNAL_PRESSURE_BOUND ONE_ATMOSPHERE
#define INTERNAL_PRESSURE_BOUND 0
#define PRESSURE_CHECKS 1
+
+#define PRESSURE_CHECK_EXTERNAL 1
+#define PRESSURE_CHECK_INTERNAL 2
+
#undefine
/obj/machinery/atmospherics/unary/vent_pump
- icon = 'icons/obj/atmospherics/vent_pump.dmi'
- icon_state = "off"
+ icon = 'icons/atmos/vent_pump.dmi'
+ icon_state = "map_vent"
name = "Air Vent"
desc = "Has a valve and pump attached to it"
use_power = 1
-
+ idle_power_usage = 150 //internal circuitry, friction losses and stuff
+ active_power_usage = 7500 //This also doubles as a measure of how powerful the pump is, in Watts. 7500 W ~ 10 HP
+
var/area/initial_loc
level = 1
var/area_uid
@@ -19,6 +27,8 @@
var/on = 0
var/pump_direction = 1 //0 = siphoning, 1 = releasing
+ var/last_power_draw = 0
+
var/external_pressure_bound = EXTERNAL_PRESSURE_BOUND
var/internal_pressure_bound = INTERNAL_PRESSURE_BOUND
@@ -40,316 +50,367 @@
var/radio_filter_out
var/radio_filter_in
- on
- on = 1
- icon_state = "out"
+/obj/machinery/atmospherics/unary/vent_pump/on
+ on = 1
+ icon_state = "map_vent_out"
- siphon
- pump_direction = 0
- icon_state = "off"
+/obj/machinery/atmospherics/unary/vent_pump/siphon
+ pump_direction = 0
- on
- on = 1
- icon_state = "in"
+/obj/machinery/atmospherics/unary/vent_pump/siphon/on
+ on = 1
+ icon_state = "map_vent_in"
- New()
- initial_loc = get_area(loc)
- if (initial_loc.master)
- initial_loc = initial_loc.master
- area_uid = initial_loc.uid
- if (!id_tag)
- assign_uid()
- id_tag = num2text(uid)
- if(ticker && ticker.current_state == 3)//if the game is running
- src.initialize()
- src.broadcast_status()
- ..()
+/obj/machinery/atmospherics/unary/vent_pump/New()
+ ..()
+ air_contents.volume = ATMOS_DEFAULT_VOLUME_PUMP
+
+ icon = null
+ initial_loc = get_area(loc)
+ if (initial_loc.master)
+ initial_loc = initial_loc.master
+ area_uid = initial_loc.uid
+ if (!id_tag)
+ assign_uid()
+ id_tag = num2text(uid)
+ if(ticker && ticker.current_state == 3)//if the game is running
+ src.initialize()
+ src.broadcast_status()
- high_volume
- name = "Large Air Vent"
- power_channel = EQUIP
- New()
- ..()
- air_contents.volume = 1000
+/obj/machinery/atmospherics/unary/vent_pump/high_volume
+ name = "Large Air Vent"
+ power_channel = EQUIP
+ active_power_usage = 15000 //15 kW ~ 20 HP
- update_icon()
- if(welded)
- icon_state = "[level == 1 && istype(loc, /turf/simulated) ? "h" : "" ]weld"
- return
- if(on && !(stat & (NOPOWER|BROKEN)))
- if(pump_direction)
- icon_state = "[level == 1 && istype(loc, /turf/simulated) ? "h" : "" ]out"
- else
- icon_state = "[level == 1 && istype(loc, /turf/simulated) ? "h" : "" ]in"
- else
- icon_state = "[level == 1 && istype(loc, /turf/simulated) ? "h" : "" ]off"
+/obj/machinery/atmospherics/unary/vent_pump/high_volume/New()
+ ..()
+ air_contents.volume = ATMOS_DEFAULT_VOLUME_PUMP + 800
+/obj/machinery/atmospherics/unary/vent_pump/engine
+ name = "Engine Core Vent"
+ power_channel = ENVIRON
+ active_power_usage = 15000 //15 kW ~ 20 HP
+
+/obj/machinery/atmospherics/unary/vent_pump/engine/New()
+ ..()
+ air_contents.volume = ATMOS_DEFAULT_VOLUME_PUMP + 500 //meant to match air injector
+
+/obj/machinery/atmospherics/unary/vent_pump/update_icon(var/safety = 0)
+ if(!check_icon_cache())
+ return
+ if (!node)
+ on = 0
+
+ overlays.Cut()
+
+ var/vent_icon = "vent"
+
+ var/turf/T = get_turf(src)
+ if(!istype(T))
return
- process()
- ..()
- if(stat & (NOPOWER|BROKEN))
+ if(T.intact && node && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe))
+ vent_icon += "h"
+
+ if(welded)
+ vent_icon += "weld"
+ else if(!powered())
+ vent_icon += "off"
+ else
+ vent_icon += "[on ? "[pump_direction ? "out" : "in"]" : "off"]"
+
+ overlays += icon_manager.get_atmos_icon("device", , , vent_icon)
+
+/obj/machinery/atmospherics/unary/vent_pump/update_underlays()
+ if(..())
+ underlays.Cut()
+ var/turf/T = get_turf(src)
+ if(!istype(T))
return
- if (!node)
- on = 0
- //broadcast_status() // from now air alarm/control computer should request update purposely --rastaf0
- if(!on)
- return 0
+ if(T.intact && node && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe))
+ return
+ else
+ add_underlay(T, node, dir)
- if(welded)
- return 0
+/obj/machinery/atmospherics/unary/vent_pump/hide()
+ update_icon()
+ update_underlays()
- var/datum/gas_mixture/environment = loc.return_air()
- var/environment_pressure = environment.return_pressure()
+/obj/machinery/atmospherics/unary/vent_pump/proc/can_pump()
+ if(stat & (NOPOWER|BROKEN))
+ return 0
+ if(!on)
+ return 0
+ if(welded)
+ return 0
+ return 1
- if(pump_direction) //internal -> external
- var/pressure_delta = 10000
+/obj/machinery/atmospherics/unary/vent_pump/process()
+ ..()
+
+ if (!node)
+ on = 0
+ if(!can_pump())
+ update_use_power(0) //usually we get here because a player turned a pump off - definitely want to update.
+ last_power_draw = 0
+ last_flow_rate = 0
+ return 0
- if(pressure_checks&1)
- pressure_delta = min(pressure_delta, (external_pressure_bound - environment_pressure))
- if(pressure_checks&2)
- pressure_delta = min(pressure_delta, (air_contents.return_pressure() - internal_pressure_bound))
+ var/datum/gas_mixture/environment = loc.return_air()
- if(pressure_delta > 0.5)
- if(air_contents.temperature > 0)
- var/transfer_moles = pressure_delta*environment.volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION)
-
- var/datum/gas_mixture/removed = air_contents.remove(transfer_moles)
-
- loc.assume_air(removed)
-
- if(network)
- network.update = 1
+ var/power_draw = -1
+
+ //Figure out the target pressure difference
+ var/pressure_delta = get_pressure_delta(environment)
+ //src.visible_message("DEBUG >>> [src]: pressure_delta = [pressure_delta]")
+ if((environment.temperature || air_contents.temperature) && pressure_delta > 0.5)
+ if(pump_direction) //internal -> external
+ var/output_volume = environment.volume * environment.group_multiplier
+ var/air_temperature = environment.temperature? environment.temperature : air_contents.temperature
+ var/transfer_moles = pressure_delta*output_volume/(air_temperature * R_IDEAL_GAS_EQUATION)
+ //src.visible_message("DEBUG >>> [src]: output_volume = [output_volume]L; air_temperature = [air_temperature]K; transfer_moles = [transfer_moles] mol")
+
+ power_draw = pump_gas(src, air_contents, environment, transfer_moles, active_power_usage)
else //external -> internal
- var/pressure_delta = 10000
- if(pressure_checks&1)
- pressure_delta = min(pressure_delta, (environment_pressure - external_pressure_bound))
- if(pressure_checks&2)
- pressure_delta = min(pressure_delta, (internal_pressure_bound - air_contents.return_pressure()))
+ var/output_volume = air_contents.volume + (network? network.volume : 0)
+ var/air_temperature = air_contents.temperature? air_contents.temperature : environment.temperature
+ var/transfer_moles = pressure_delta*output_volume/(air_temperature * R_IDEAL_GAS_EQUATION)
+
+ //limit flow rate from turfs
+ transfer_moles = min(transfer_moles, environment.total_moles*air_contents.volume/environment.volume) //group_multiplier gets divided out here
+
+ power_draw = pump_gas(src, environment, air_contents, transfer_moles, active_power_usage)
- if(pressure_delta > 0.5)
- if(environment.temperature > 0)
- var/transfer_moles = pressure_delta*air_contents.volume/(environment.temperature * R_IDEAL_GAS_EQUATION)
+ if (power_draw < 0)
+ last_power_draw = 0
+ last_flow_rate = 0
+ //update_use_power(0)
+ use_power = 0 //don't force update - easier on CPU
+ else
+ last_power_draw = handle_power_draw(power_draw)
+ if(network)
+ network.update = 1
+
+ return 1
- var/datum/gas_mixture/removed = loc.remove_air(transfer_moles)
- if (isnull(removed)) //in space
- return
+/obj/machinery/atmospherics/unary/vent_pump/proc/get_pressure_delta(datum/gas_mixture/environment)
+ var/pressure_delta = DEFAULT_PRESSURE_DELTA
+ var/environment_pressure = environment.return_pressure()
+
+ if(pump_direction) //internal -> external
+ if(pressure_checks & PRESSURE_CHECK_EXTERNAL)
+ pressure_delta = min(pressure_delta, external_pressure_bound - environment_pressure) //increasing the pressure here
+ if(pressure_checks & PRESSURE_CHECK_INTERNAL)
+ pressure_delta = min(pressure_delta, air_contents.return_pressure() - internal_pressure_bound) //decreasing the pressure here
+ else //external -> internal
+ if(pressure_checks & PRESSURE_CHECK_EXTERNAL)
+ pressure_delta = min(pressure_delta, environment_pressure - external_pressure_bound) //decreasing the pressure here
+ if(pressure_checks & PRESSURE_CHECK_INTERNAL)
+ pressure_delta = min(pressure_delta, internal_pressure_bound - air_contents.return_pressure()) //increasing the pressure here
+
+ return pressure_delta
- air_contents.merge(removed)
+//Radio remote control
- if(network)
- network.update = 1
+/obj/machinery/atmospherics/unary/vent_pump/proc/set_frequency(new_frequency)
+ radio_controller.remove_object(src, frequency)
+ frequency = new_frequency
+ if(frequency)
+ radio_connection = radio_controller.add_object(src, frequency,radio_filter_in)
- return 1
+/obj/machinery/atmospherics/unary/vent_pump/proc/broadcast_status()
+ if(!radio_connection)
+ return 0
- //Radio remote control
+ var/datum/signal/signal = new
+ signal.transmission_method = 1 //radio signal
+ signal.source = src
- proc
- set_frequency(new_frequency)
- radio_controller.remove_object(src, frequency)
- frequency = new_frequency
- if(frequency)
- radio_connection = radio_controller.add_object(src, frequency,radio_filter_in)
+ signal.data = list(
+ "area" = src.area_uid,
+ "tag" = src.id_tag,
+ "device" = "AVP",
+ "power" = on,
+ "direction" = pump_direction?("release"):("siphon"),
+ "checks" = pressure_checks,
+ "internal" = internal_pressure_bound,
+ "external" = external_pressure_bound,
+ "timestamp" = world.time,
+ "sigtype" = "status",
+ "power_draw" = last_power_draw,
+ "flow_rate" = last_flow_rate,
+ )
- broadcast_status()
- if(!radio_connection)
- return 0
+ if(!initial_loc.air_vent_names[id_tag])
+ var/new_name = "[initial_loc.name] Vent Pump #[initial_loc.air_vent_names.len+1]"
+ initial_loc.air_vent_names[id_tag] = new_name
+ src.name = new_name
+ initial_loc.air_vent_info[id_tag] = signal.data
- var/datum/signal/signal = new
- signal.transmission_method = 1 //radio signal
- signal.source = src
+ radio_connection.post_signal(src, signal, radio_filter_out)
- signal.data = list(
- "area" = src.area_uid,
- "tag" = src.id_tag,
- "device" = "AVP",
- "power" = on,
- "direction" = pump_direction?("release"):("siphon"),
- "checks" = pressure_checks,
- "internal" = internal_pressure_bound,
- "external" = external_pressure_bound,
- "timestamp" = world.time,
- "sigtype" = "status"
- )
-
- if(!initial_loc.air_vent_names[id_tag])
- var/new_name = "[initial_loc.name] Vent Pump #[initial_loc.air_vent_names.len+1]"
- initial_loc.air_vent_names[id_tag] = new_name
- src.name = new_name
- initial_loc.air_vent_info[id_tag] = signal.data
-
- radio_connection.post_signal(src, signal, radio_filter_out)
-
- return 1
+ return 1
- initialize()
- ..()
+/obj/machinery/atmospherics/unary/vent_pump/initialize()
+ ..()
- //some vents work his own spesial way
- radio_filter_in = frequency==1439?(RADIO_FROM_AIRALARM):null
- radio_filter_out = frequency==1439?(RADIO_TO_AIRALARM):null
- if(frequency)
- set_frequency(frequency)
+ //some vents work his own spesial way
+ radio_filter_in = frequency==1439?(RADIO_FROM_AIRALARM):null
+ radio_filter_out = frequency==1439?(RADIO_TO_AIRALARM):null
+ if(frequency)
+ set_frequency(frequency)
- receive_signal(datum/signal/signal)
- if(stat & (NOPOWER|BROKEN))
- return
- //log_admin("DEBUG \[[world.timeofday]\]: /obj/machinery/atmospherics/unary/vent_pump/receive_signal([signal.debug_print()])")
- if(!signal.data["tag"] || (signal.data["tag"] != id_tag) || (signal.data["sigtype"]!="command"))
- return 0
+/obj/machinery/atmospherics/unary/vent_pump/receive_signal(datum/signal/signal)
+ if(stat & (NOPOWER|BROKEN))
+ return
+ //log_admin("DEBUG \[[world.timeofday]\]: /obj/machinery/atmospherics/unary/vent_pump/receive_signal([signal.debug_print()])")
+ if(!signal.data["tag"] || (signal.data["tag"] != id_tag) || (signal.data["sigtype"]!="command"))
+ return 0
- if(signal.data["purge"] != null)
- pressure_checks &= ~1
- pump_direction = 0
+ if(signal.data["purge"] != null)
+ pressure_checks &= ~1
+ pump_direction = 0
- if(signal.data["stabalize"] != null)
- pressure_checks |= 1
- pump_direction = 1
+ if(signal.data["stabalize"] != null)
+ pressure_checks |= 1
+ pump_direction = 1
- if(signal.data["power"] != null)
- on = text2num(signal.data["power"])
+ if(signal.data["power"] != null)
+ on = text2num(signal.data["power"])
- if(signal.data["power_toggle"] != null)
- on = !on
+ if(signal.data["power_toggle"] != null)
+ on = !on
- if(signal.data["checks"] != null)
- if (signal.data["checks"] == "default")
- pressure_checks = pressure_checks_default
- else
- pressure_checks = text2num(signal.data["checks"])
+ if(signal.data["checks"] != null)
+ if (signal.data["checks"] == "default")
+ pressure_checks = pressure_checks_default
+ else
+ pressure_checks = text2num(signal.data["checks"])
- if(signal.data["checks_toggle"] != null)
- pressure_checks = (pressure_checks?0:3)
+ if(signal.data["checks_toggle"] != null)
+ pressure_checks = (pressure_checks?0:3)
+
+ if(signal.data["direction"] != null)
+ pump_direction = text2num(signal.data["direction"])
- if(signal.data["direction"] != null)
- pump_direction = text2num(signal.data["direction"])
-
- if(signal.data["set_internal_pressure"] != null)
- if (signal.data["set_internal_pressure"] == "default")
- internal_pressure_bound = internal_pressure_bound_default
- else
- internal_pressure_bound = between(
- 0,
- text2num(signal.data["set_internal_pressure"]),
- ONE_ATMOSPHERE*50
- )
-
- if(signal.data["set_external_pressure"] != null)
- if (signal.data["set_external_pressure"] == "default")
- external_pressure_bound = external_pressure_bound_default
- else
- external_pressure_bound = between(
- 0,
- text2num(signal.data["set_external_pressure"]),
- ONE_ATMOSPHERE*50
- )
-
- if(signal.data["adjust_internal_pressure"] != null)
+ if(signal.data["set_internal_pressure"] != null)
+ if (signal.data["set_internal_pressure"] == "default")
+ internal_pressure_bound = internal_pressure_bound_default
+ else
internal_pressure_bound = between(
0,
- internal_pressure_bound + text2num(signal.data["adjust_internal_pressure"]),
+ text2num(signal.data["set_internal_pressure"]),
ONE_ATMOSPHERE*50
)
- if(signal.data["adjust_external_pressure"] != null)
-
-
+ if(signal.data["set_external_pressure"] != null)
+ if (signal.data["set_external_pressure"] == "default")
+ external_pressure_bound = external_pressure_bound_default
+ else
external_pressure_bound = between(
0,
- external_pressure_bound + text2num(signal.data["adjust_external_pressure"]),
+ text2num(signal.data["set_external_pressure"]),
ONE_ATMOSPHERE*50
)
- if(signal.data["init"] != null)
- name = signal.data["init"]
- return
+ if(signal.data["adjust_internal_pressure"] != null)
+ internal_pressure_bound = between(
+ 0,
+ internal_pressure_bound + text2num(signal.data["adjust_internal_pressure"]),
+ ONE_ATMOSPHERE*50
+ )
- if(signal.data["status"] != null)
- spawn(2)
- broadcast_status()
- return //do not update_icon
+ if(signal.data["adjust_external_pressure"] != null)
- //log_admin("DEBUG \[[world.timeofday]\]: vent_pump/receive_signal: unknown command \"[signal.data["command"]]\"\n[signal.debug_print()]")
+
+ external_pressure_bound = between(
+ 0,
+ external_pressure_bound + text2num(signal.data["adjust_external_pressure"]),
+ ONE_ATMOSPHERE*50
+ )
+
+ if(signal.data["init"] != null)
+ name = signal.data["init"]
+ return
+
+ if(signal.data["status"] != null)
spawn(2)
broadcast_status()
- update_icon()
- return
+ return //do not update_icon
- hide(var/i) //to make the little pipe section invisible, the icon changes.
- if(welded)
- icon_state = "[i == 1 && istype(loc, /turf/simulated) ? "h" : "" ]weld"
- return
- if(on&&node)
- if(pump_direction)
- icon_state = "[i == 1 && istype(loc, /turf/simulated) ? "h" : "" ]out"
- else
- icon_state = "[i == 1 && istype(loc, /turf/simulated) ? "h" : "" ]in"
- else
- icon_state = "[i == 1 && istype(loc, /turf/simulated) ? "h" : "" ]off"
- on = 0
- return
+ //log_admin("DEBUG \[[world.timeofday]\]: vent_pump/receive_signal: unknown command \"[signal.data["command"]]\"\n[signal.debug_print()]")
+ spawn(2)
+ broadcast_status()
+ update_icon()
+ return
- attackby(obj/item/W, mob/user)
- if(istype(W, /obj/item/weapon/weldingtool))
- var/obj/item/weapon/weldingtool/WT = W
- if (WT.remove_fuel(0,user))
- user << "\blue Now welding the vent."
- if(do_after(user, 20))
- if(!src || !WT.isOn()) return
- playsound(src.loc, 'sound/items/Welder2.ogg', 50, 1)
- if(!welded)
- user.visible_message("[user] welds the vent shut.", "You weld the vent shut.", "You hear welding.")
- welded = 1
- update_icon()
- else
- user.visible_message("[user] unwelds the vent.", "You unweld the vent.", "You hear welding.")
- welded = 0
- update_icon()
+/obj/machinery/atmospherics/unary/vent_pump/attackby(obj/item/W, mob/user)
+ if(istype(W, /obj/item/weapon/weldingtool))
+ var/obj/item/weapon/weldingtool/WT = W
+ if (WT.remove_fuel(0,user))
+ user << "\blue Now welding the vent."
+ if(do_after(user, 20))
+ if(!src || !WT.isOn()) return
+ playsound(src.loc, 'sound/items/Welder2.ogg', 50, 1)
+ if(!welded)
+ user.visible_message("[user] welds the vent shut.", "You weld the vent shut.", "You hear welding.")
+ welded = 1
+ update_icon()
else
- user << "\blue The welding tool needs to be on to start this task."
+ user.visible_message("[user] unwelds the vent.", "You unweld the vent.", "You hear welding.")
+ welded = 0
+ update_icon()
else
- user << "\blue You need more welding fuel to complete this task."
- return 1
- examine()
- set src in oview(1)
- ..()
- if(welded)
- usr << "It seems welded shut."
-
- power_change()
- if(powered(power_channel))
- stat &= ~NOPOWER
+ user << "\blue The welding tool needs to be on to start this task."
else
- stat |= NOPOWER
+ user << "\blue You need more welding fuel to complete this task."
+ return 1
+ else
+ ..()
+
+/obj/machinery/atmospherics/unary/vent_pump/examine()
+ set src in oview(1)
+ ..()
+ if (get_dist(usr, src) <= 1)
+ usr << "A small gauge in the corner reads [round(last_flow_rate, 0.1)] L/s; [round(last_power_draw)] W"
+ else
+ usr << "You are too far away to read the gauge."
+ if(welded)
+ usr << "It seems welded shut."
+
+/obj/machinery/atmospherics/unary/vent_pump/power_change()
+ var/old_stat = stat
+ ..()
+ if(old_stat != stat)
update_icon()
- attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
- if (!istype(W, /obj/item/weapon/wrench))
- return ..()
- if (!(stat & NOPOWER) && on)
- user << "\red You cannot unwrench this [src], turn it off first."
- return 1
- var/turf/T = src.loc
- if (level==1 && isturf(T) && T.intact)
- user << "\red You must remove the plating first."
- return 1
- var/datum/gas_mixture/int_air = return_air()
- var/datum/gas_mixture/env_air = loc.return_air()
- if ((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE)
- user << "\red You cannot unwrench this [src], it too exerted due to internal pressure."
- add_fingerprint(user)
- return 1
- playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
- user << "\blue You begin to unfasten \the [src]..."
- if (do_after(user, 40))
- user.visible_message( \
- "[user] unfastens \the [src].", \
- "\blue You have unfastened \the [src].", \
- "You hear ratchet.")
- new /obj/item/pipe(loc, make_from=src)
- del(src)
+/obj/machinery/atmospherics/unary/vent_pump/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
+ if (!istype(W, /obj/item/weapon/wrench))
+ return ..()
+ if (!(stat & NOPOWER) && on)
+ user << "\red You cannot unwrench this [src], turn it off first."
+ return 1
+ var/turf/T = src.loc
+ if (node && node.level==1 && isturf(T) && T.intact)
+ user << "\red You must remove the plating first."
+ return 1
+ var/datum/gas_mixture/int_air = return_air()
+ var/datum/gas_mixture/env_air = loc.return_air()
+ if ((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE)
+ user << "\red You cannot unwrench this [src], it too exerted due to internal pressure."
+ add_fingerprint(user)
+ return 1
+ playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
+ user << "\blue You begin to unfasten \the [src]..."
+ if (do_after(user, 40))
+ user.visible_message( \
+ "[user] unfastens \the [src].", \
+ "\blue You have unfastened \the [src].", \
+ "You hear ratchet.")
+ new /obj/item/pipe(loc, make_from=src)
+ del(src)
/obj/machinery/atmospherics/unary/vent_pump/Del()
if(initial_loc)
diff --git a/code/ATMOSPHERICS/components/unary/vent_scrubber.dm b/code/ATMOSPHERICS/components/unary/vent_scrubber.dm
index 3845b9ad61b..300a8a09ff5 100644
--- a/code/ATMOSPHERICS/components/unary/vent_scrubber.dm
+++ b/code/ATMOSPHERICS/components/unary/vent_scrubber.dm
@@ -1,10 +1,13 @@
/obj/machinery/atmospherics/unary/vent_scrubber
- icon = 'icons/obj/atmospherics/vent_scrubber.dmi'
- icon_state = "off"
+ icon = 'icons/atmos/vent_scrubber.dmi'
+ icon_state = "map_scrubber"
name = "Air Scrubber"
desc = "Has a valve and pump attached to it"
use_power = 1
+ idle_power_usage = 150 //internal circuitry, friction losses and stuff
+ active_power_usage = 7500 //This also doubles as a measure of how powerful the pump is, in Watts. 7500 W ~ 10 HP
+ var/last_power_draw = 0
level = 1
@@ -15,255 +18,246 @@
var/on = 0
var/scrubbing = 1 //0 = siphoning, 1 = scrubbing
- var/scrub_CO2 = 1
- var/scrub_Toxins = 0
- var/scrub_N2O = 0
+ var/list/scrubbing_gas = list()
- var/volume_rate = 120
var/panic = 0 //is this scrubber panicked?
var/area_uid
var/radio_filter_out
var/radio_filter_in
- New()
- initial_loc = get_area(loc)
- if (initial_loc.master)
- initial_loc = initial_loc.master
- area_uid = initial_loc.uid
- if (!id_tag)
- assign_uid()
- id_tag = num2text(uid)
- if(ticker && ticker.current_state == 3)//if the game is running
- src.initialize()
- src.broadcast_status()
- ..()
+/obj/machinery/atmospherics/unary/vent_scrubber/New()
+ ..()
+ air_contents.volume = ATMOS_DEFAULT_VOLUME_FILTER
+
+ icon = null
+ initial_loc = get_area(loc)
+ if (initial_loc.master)
+ initial_loc = initial_loc.master
+ area_uid = initial_loc.uid
+ if (!id_tag)
+ assign_uid()
+ id_tag = num2text(uid)
+ if(ticker && ticker.current_state == 3)//if the game is running
+ src.initialize()
+ src.broadcast_status()
+
+/obj/machinery/atmospherics/unary/vent_scrubber/update_icon(var/safety = 0)
+ if(!check_icon_cache())
+ return
+
+ overlays.Cut()
+
+ var/scrubber_icon = "scrubber"
+
+ var/turf/T = get_turf(src)
+ if(!istype(T))
+ return
+
+ if(!powered())
+ scrubber_icon += "off"
+ else
+ scrubber_icon += "[on ? "[scrubbing ? "on" : "in"]" : "off"]"
+
+ overlays += icon_manager.get_atmos_icon("device", , , scrubber_icon)
+
+/obj/machinery/atmospherics/unary/vent_scrubber/update_underlays()
+ if(..())
+ underlays.Cut()
+ var/turf/T = get_turf(src)
+ if(!istype(T))
+ return
+ if(T.intact && node && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe))
+ return
+ else
+ add_underlay(T, node, dir)
+
+/obj/machinery/atmospherics/unary/vent_scrubber/proc/set_frequency(new_frequency)
+ radio_controller.remove_object(src, frequency)
+ frequency = new_frequency
+ radio_connection = radio_controller.add_object(src, frequency, radio_filter_in)
+
+/obj/machinery/atmospherics/unary/vent_scrubber/proc/broadcast_status()
+ if(!radio_connection)
+ return 0
+
+ var/datum/signal/signal = new
+ signal.transmission_method = 1 //radio signal
+ signal.source = src
+ signal.data = list(
+ "area" = area_uid,
+ "tag" = id_tag,
+ "device" = "AScr",
+ "timestamp" = world.time,
+ "power" = on,
+ "scrubbing" = scrubbing,
+ "panic" = panic,
+ "filter_co2" = ("carbon_dioxide" in scrubbing_gas),
+ "filter_phoron" = ("phoron" in scrubbing_gas),
+ "filter_n2o" = ("sleeping_agent" in scrubbing_gas),
+ "sigtype" = "status"
+ )
+ if(!initial_loc.air_scrub_names[id_tag])
+ var/new_name = "[initial_loc.name] Air Scrubber #[initial_loc.air_scrub_names.len+1]"
+ initial_loc.air_scrub_names[id_tag] = new_name
+ src.name = new_name
+ initial_loc.air_scrub_info[id_tag] = signal.data
+ radio_connection.post_signal(src, signal, radio_filter_out)
+
+ return 1
+
+/obj/machinery/atmospherics/unary/vent_scrubber/initialize()
+ ..()
+ radio_filter_in = frequency==initial(frequency)?(RADIO_FROM_AIRALARM):null
+ radio_filter_out = frequency==initial(frequency)?(RADIO_TO_AIRALARM):null
+ if (frequency)
+ set_frequency(frequency)
+
+/obj/machinery/atmospherics/unary/vent_scrubber/process()
+ ..()
+ if (!node)
+ on = 0
+ //broadcast_status()
+ if(!on || (stat & (NOPOWER|BROKEN)))
+ update_use_power(0) //we got here because a player turned a pump off - definitely want to update.
+ last_flow_rate = 0
+ last_power_draw = 0
+ return 0
+
+ var/datum/gas_mixture/environment = loc.return_air()
+
+ var/power_draw = -1
+ if(scrubbing)
+ //limit flow rate from turfs
+ var/transfer_moles = min(environment.total_moles, environment.total_moles*MAX_SCRUBBER_FLOWRATE/environment.volume) //group_multiplier gets divided out here
+
+ power_draw = scrub_gas(src, scrubbing_gas, environment, air_contents, transfer_moles, active_power_usage)
+ else //Just siphon all air
+ //limit flow rate from turfs
+ var/transfer_moles = min(environment.total_moles, environment.total_moles*MAX_SIPHON_FLOWRATE/environment.volume) //group_multiplier gets divided out here
+
+ power_draw = pump_gas(src, environment, air_contents, transfer_moles, active_power_usage)
+
+ if (power_draw < 0)
+ //update_use_power(0)
+ use_power = 0 //don't force update. Sure, we will continue to use power even though we're not pumping anything, but it is easier on the CPU
+ last_power_draw = 0
+ last_flow_rate = 0
+ else
+ last_power_draw = handle_power_draw(power_draw)
+
+ if(network)
+ network.update = 1
+
+ return 1
+
+/obj/machinery/atmospherics/unary/vent_scrubber/hide(var/i) //to make the little pipe section invisible, the icon changes.
update_icon()
- if(node && on && !(stat & (NOPOWER|BROKEN)))
- if(scrubbing)
- icon_state = "[level == 1 && istype(loc, /turf/simulated) ? "h" : "" ]on"
- else
- icon_state = "[level == 1 && istype(loc, /turf/simulated) ? "h" : "" ]in"
+
+/obj/machinery/atmospherics/unary/vent_scrubber/receive_signal(datum/signal/signal)
+ if(stat & (NOPOWER|BROKEN))
+ return
+ if(!signal.data["tag"] || (signal.data["tag"] != id_tag) || (signal.data["sigtype"]!="command"))
+ return 0
+
+ if(signal.data["power"] != null)
+ on = text2num(signal.data["power"])
+ if(signal.data["power_toggle"] != null)
+ on = !on
+
+ if(signal.data["panic_siphon"]) //must be before if("scrubbing" thing
+ panic = text2num(signal.data["panic_siphon"] != null)
+ if(panic)
+ on = 1
+ scrubbing = 0
else
- icon_state = "[level == 1 && istype(loc, /turf/simulated) ? "h" : "" ]off"
+ scrubbing = 1
+ if(signal.data["toggle_panic_siphon"] != null)
+ panic = !panic
+ if(panic)
+ on = 1
+ scrubbing = 0
+ else
+ scrubbing = 1
+
+ if(signal.data["scrubbing"] != null)
+ scrubbing = text2num(signal.data["scrubbing"])
+ if(signal.data["toggle_scrubbing"])
+ scrubbing = !scrubbing
+
+ var/list/toggle = list()
+
+ if(!isnull(signal.data["co2_scrub"]) && text2num(signal.data["co2_scrub"]) != ("carbon_dioxide" in scrubbing_gas))
+ toggle += "carbon_dioxide"
+ else if(signal.data["toggle_co2_scrub"])
+ toggle += "carbon_dioxide"
+
+ if(!isnull(signal.data["tox_scrub"]) && text2num(signal.data["tox_scrub"]) != ("phoron" in scrubbing_gas))
+ toggle += "phoron"
+ else if(signal.data["toggle_tox_scrub"])
+ toggle += "phoron"
+
+ if(!isnull(signal.data["n2o_scrub"]) && text2num(signal.data["n2o_scrub"]) != ("sleeping_agent" in scrubbing_gas))
+ toggle += "sleeping_agent"
+ else if(signal.data["toggle_n2o_scrub"])
+ toggle += "sleeping_agent"
+
+ scrubbing_gas ^= toggle
+
+ if(signal.data["init"] != null)
+ name = signal.data["init"]
return
- proc
- set_frequency(new_frequency)
- radio_controller.remove_object(src, frequency)
- frequency = new_frequency
- radio_connection = radio_controller.add_object(src, frequency, radio_filter_in)
-
- broadcast_status()
- if(!radio_connection)
- return 0
-
- var/datum/signal/signal = new
- signal.transmission_method = 1 //radio signal
- signal.source = src
- signal.data = list(
- "area" = area_uid,
- "tag" = id_tag,
- "device" = "AScr",
- "timestamp" = world.time,
- "power" = on,
- "scrubbing" = scrubbing,
- "panic" = panic,
- "filter_co2" = scrub_CO2,
- "filter_phoron" = scrub_Toxins,
- "filter_n2o" = scrub_N2O,
- "sigtype" = "status"
- )
- if(!initial_loc.air_scrub_names[id_tag])
- var/new_name = "[initial_loc.name] Air Scrubber #[initial_loc.air_scrub_names.len+1]"
- initial_loc.air_scrub_names[id_tag] = new_name
- src.name = new_name
- initial_loc.air_scrub_info[id_tag] = signal.data
- radio_connection.post_signal(src, signal, radio_filter_out)
-
- return 1
-
- initialize()
- ..()
- radio_filter_in = frequency==initial(frequency)?(RADIO_FROM_AIRALARM):null
- radio_filter_out = frequency==initial(frequency)?(RADIO_TO_AIRALARM):null
- if (frequency)
- set_frequency(frequency)
-
- process()
- ..()
- if(stat & (NOPOWER|BROKEN))
- return
- if (!node)
- on = 0
- //broadcast_status()
- if(!on)
- return 0
-
-
- var/datum/gas_mixture/environment = loc.return_air()
-
- if(scrubbing)
- if((environment.phoron>0.001) || (environment.carbon_dioxide>0.001) || (environment.trace_gases.len>0))
- var/transfer_moles = min(1, volume_rate/environment.volume)*environment.total_moles()
-
- //Take a gas sample
- var/datum/gas_mixture/removed = loc.remove_air(transfer_moles)
- if (isnull(removed)) //in space
- return
-
- //Filter it
- var/datum/gas_mixture/filtered_out = new
- filtered_out.temperature = removed.temperature
- if(scrub_Toxins)
- filtered_out.phoron = removed.phoron
- removed.phoron = 0
- if(scrub_CO2)
- filtered_out.carbon_dioxide = removed.carbon_dioxide
- removed.carbon_dioxide = 0
-
- if(removed.trace_gases.len>0)
- for(var/datum/gas/trace_gas in removed.trace_gases)
- if(istype(trace_gas, /datum/gas/oxygen_agent_b))
- removed.trace_gases -= trace_gas
- filtered_out.trace_gases += trace_gas
- else if(istype(trace_gas, /datum/gas/sleeping_agent) && scrub_N2O)
- removed.trace_gases -= trace_gas
- filtered_out.trace_gases += trace_gas
-
-
- //Remix the resulting gases
- air_contents.merge(filtered_out)
-
- loc.assume_air(removed)
-
- if(network)
- network.update = 1
-
- else //Just siphoning all air
- if (air_contents.return_pressure()>=50*ONE_ATMOSPHERE)
- return
-
- var/transfer_moles = environment.total_moles()*(volume_rate/environment.volume)
-
- var/datum/gas_mixture/removed = loc.remove_air(transfer_moles)
-
- air_contents.merge(removed)
-
- if(network)
- network.update = 1
-
- return 1
-/* //unused piece of code
- hide(var/i) //to make the little pipe section invisible, the icon changes.
- if(on&&node)
- if(scrubbing)
- icon_state = "[i == 1 && istype(loc, /turf/simulated) ? "h" : "" ]on"
- else
- icon_state = "[i == 1 && istype(loc, /turf/simulated) ? "h" : "" ]in"
- else
- icon_state = "[i == 1 && istype(loc, /turf/simulated) ? "h" : "" ]off"
- on = 0
- return
-*/
-
- receive_signal(datum/signal/signal)
- if(stat & (NOPOWER|BROKEN))
- return
- if(!signal.data["tag"] || (signal.data["tag"] != id_tag) || (signal.data["sigtype"]!="command"))
- return 0
-
- if(signal.data["power"] != null)
- on = text2num(signal.data["power"])
- if(signal.data["power_toggle"] != null)
- on = !on
-
- if(signal.data["panic_siphon"]) //must be before if("scrubbing" thing
- panic = text2num(signal.data["panic_siphon"] != null)
- if(panic)
- on = 1
- scrubbing = 0
- volume_rate = 2000
- else
- scrubbing = 1
- volume_rate = initial(volume_rate)
- if(signal.data["toggle_panic_siphon"] != null)
- panic = !panic
- if(panic)
- on = 1
- scrubbing = 0
- volume_rate = 2000
- else
- scrubbing = 1
- volume_rate = initial(volume_rate)
-
- if(signal.data["scrubbing"] != null)
- scrubbing = text2num(signal.data["scrubbing"])
- if(signal.data["toggle_scrubbing"])
- scrubbing = !scrubbing
-
- if(signal.data["co2_scrub"] != null)
- scrub_CO2 = text2num(signal.data["co2_scrub"])
- if(signal.data["toggle_co2_scrub"])
- scrub_CO2 = !scrub_CO2
-
- if(signal.data["tox_scrub"] != null)
- scrub_Toxins = text2num(signal.data["tox_scrub"])
- if(signal.data["toggle_tox_scrub"])
- scrub_Toxins = !scrub_Toxins
-
- if(signal.data["n2o_scrub"] != null)
- scrub_N2O = text2num(signal.data["n2o_scrub"])
- if(signal.data["toggle_n2o_scrub"])
- scrub_N2O = !scrub_N2O
-
- if(signal.data["init"] != null)
- name = signal.data["init"]
- return
-
- if(signal.data["status"] != null)
- spawn(2)
- broadcast_status()
- return //do not update_icon
-
-// log_admin("DEBUG \[[world.timeofday]\]: vent_scrubber/receive_signal: unknown command \"[signal.data["command"]]\"\n[signal.debug_print()]")
+ if(signal.data["status"] != null)
spawn(2)
broadcast_status()
- update_icon()
- return
+ return //do not update_icon
- power_change()
- if(powered(power_channel))
- stat &= ~NOPOWER
- else
- stat |= NOPOWER
+// log_admin("DEBUG \[[world.timeofday]\]: vent_scrubber/receive_signal: unknown command \"[signal.data["command"]]\"\n[signal.debug_print()]")
+ spawn(2)
+ broadcast_status()
+ update_icon()
+ return
+
+/obj/machinery/atmospherics/unary/vent_scrubber/power_change()
+ var/old_stat = stat
+ ..()
+ if(old_stat != stat)
update_icon()
- attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
- if (!istype(W, /obj/item/weapon/wrench))
- return ..()
- if (!(stat & NOPOWER) && on)
- user << "\red You cannot unwrench this [src], turn it off first."
- return 1
- var/turf/T = src.loc
- if (level==1 && isturf(T) && T.intact)
- user << "\red You must remove the plating first."
- return 1
- var/datum/gas_mixture/int_air = return_air()
- var/datum/gas_mixture/env_air = loc.return_air()
- if ((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE)
- user << "\red You cannot unwrench this [src], it too exerted due to internal pressure."
- add_fingerprint(user)
- return 1
- playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
- user << "\blue You begin to unfasten \the [src]..."
- if (do_after(user, 40))
- user.visible_message( \
- "[user] unfastens \the [src].", \
- "\blue You have unfastened \the [src].", \
- "You hear ratchet.")
- new /obj/item/pipe(loc, make_from=src)
- del(src)
+/obj/machinery/atmospherics/unary/vent_scrubber/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
+ if (!istype(W, /obj/item/weapon/wrench))
+ return ..()
+ if (!(stat & NOPOWER) && on)
+ user << "\red You cannot unwrench this [src], turn it off first."
+ return 1
+ var/turf/T = src.loc
+ if (node && node.level==1 && isturf(T) && T.intact)
+ user << "\red You must remove the plating first."
+ return 1
+ var/datum/gas_mixture/int_air = return_air()
+ var/datum/gas_mixture/env_air = loc.return_air()
+ if ((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE)
+ user << "\red You cannot unwrench this [src], it too exerted due to internal pressure."
+ add_fingerprint(user)
+ return 1
+ playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
+ user << "\blue You begin to unfasten \the [src]..."
+ if (do_after(user, 40))
+ user.visible_message( \
+ "[user] unfastens \the [src].", \
+ "\blue You have unfastened \the [src].", \
+ "You hear ratchet.")
+ new /obj/item/pipe(loc, make_from=src)
+ del(src)
+
+/obj/machinery/atmospherics/unary/vent_scrubber/examine()
+ set src in oview(1)
+ ..()
+ if (get_dist(usr, src) <= 1)
+ usr << "A small gauge in the corner reads [round(last_flow_rate, 0.1)] L/s; [round(last_power_draw)] W"
+ else
+ usr << "You are too far away to read the gauge."
/obj/machinery/atmospherics/unary/vent_scrubber/Del()
if(initial_loc)
diff --git a/code/ATMOSPHERICS/components/valve.dm b/code/ATMOSPHERICS/components/valve.dm
index 09d0b64f866..75282275ebd 100644
--- a/code/ATMOSPHERICS/components/valve.dm
+++ b/code/ATMOSPHERICS/components/valve.dm
@@ -1,10 +1,11 @@
-obj/machinery/atmospherics/valve
- icon = 'icons/obj/atmospherics/valve.dmi'
- icon_state = "valve0"
+/obj/machinery/atmospherics/valve
+ icon = 'icons/atmos/valve.dmi'
+ icon_state = "map_valve0"
name = "manual valve"
desc = "A pipe valve"
+ level = 1
dir = SOUTH
initialize_directions = SOUTH|NORTH
@@ -17,322 +18,297 @@ obj/machinery/atmospherics/valve
var/datum/pipe_network/network_node1
var/datum/pipe_network/network_node2
- open
- open = 1
- icon_state = "valve1"
+/obj/machinery/atmospherics/valve/open
+ open = 1
+ icon_state = "map_valve1"
- update_icon(animation)
- if(animation)
- flick("valve[src.open][!src.open]",src)
- else
- icon_state = "valve[open]"
+/obj/machinery/atmospherics/valve/update_icon(animation)
+ if(animation)
+ flick("valve[src.open][!src.open]",src)
+ else
+ icon_state = "valve[open]"
- New()
- switch(dir)
- if(NORTH || SOUTH)
- initialize_directions = NORTH|SOUTH
- if(EAST || WEST)
- initialize_directions = EAST|WEST
- ..()
+/obj/machinery/atmospherics/valve/update_underlays()
+ if(..())
+ underlays.Cut()
+ var/turf/T = get_turf(src)
+ if(!istype(T))
+ return
+ add_underlay(T, node1, get_dir(src, node1))
+ add_underlay(T, node2, get_dir(src, node2))
- network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference)
+/obj/machinery/atmospherics/valve/hide(var/i)
+ update_underlays()
+/obj/machinery/atmospherics/valve/New()
+ switch(dir)
+ if(NORTH || SOUTH)
+ initialize_directions = NORTH|SOUTH
+ if(EAST || WEST)
+ initialize_directions = EAST|WEST
+ ..()
- if(reference == node1)
- network_node1 = new_network
- if(open)
- network_node2 = new_network
- else if(reference == node2)
- network_node2 = new_network
- if(open)
- network_node1 = new_network
-
- if(new_network.normal_members.Find(src))
- return 0
-
- new_network.normal_members += src
-
+/obj/machinery/atmospherics/valve/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference)
+ if(reference == node1)
+ network_node1 = new_network
if(open)
- if(reference == node1)
- if(node2)
- return node2.network_expand(new_network, src)
- else if(reference == node2)
- if(node1)
- return node1.network_expand(new_network, src)
+ network_node2 = new_network
+ else if(reference == node2)
+ network_node2 = new_network
+ if(open)
+ network_node1 = new_network
- return null
+ if(new_network.normal_members.Find(src))
+ return 0
- Del()
- loc = null
+ new_network.normal_members += src
- if(node1)
- node1.disconnect(src)
- del(network_node1)
- if(node2)
- node2.disconnect(src)
- del(network_node2)
+ if(open)
+ if(reference == node1)
+ if(node2)
+ return node2.network_expand(new_network, src)
+ else if(reference == node2)
+ if(node1)
+ return node1.network_expand(new_network, src)
+ return null
+
+/obj/machinery/atmospherics/valve/Del()
+ loc = null
+
+ if(node1)
+ node1.disconnect(src)
+ del(network_node1)
+ if(node2)
+ node2.disconnect(src)
+ del(network_node2)
+
+ node1 = null
+ node2 = null
+
+ ..()
+
+/obj/machinery/atmospherics/valve/proc/open()
+ if(open) return 0
+
+ open = 1
+ update_icon()
+
+ if(network_node1&&network_node2)
+ network_node1.merge(network_node2)
+ network_node2 = network_node1
+
+ if(network_node1)
+ network_node1.update = 1
+ else if(network_node2)
+ network_node2.update = 1
+
+ return 1
+
+/obj/machinery/atmospherics/valve/proc/close()
+ if(!open)
+ return 0
+
+ open = 0
+ update_icon()
+
+ if(network_node1)
+ del(network_node1)
+ if(network_node2)
+ del(network_node2)
+
+ build_network()
+
+ return 1
+
+/obj/machinery/atmospherics/valve/proc/normalize_dir()
+ if(dir==3)
+ dir = 1
+ else if(dir==12)
+ dir = 4
+
+/obj/machinery/atmospherics/valve/attack_ai(mob/user as mob)
+ return
+
+/obj/machinery/atmospherics/valve/attack_paw(mob/user as mob)
+ return attack_hand(user)
+
+/obj/machinery/atmospherics/valve/attack_hand(mob/user as mob)
+ src.add_fingerprint(usr)
+ update_icon(1)
+ sleep(10)
+ if (src.open)
+ src.close()
+ else
+ src.open()
+
+/obj/machinery/atmospherics/valve/process()
+ ..()
+ . = PROCESS_KILL
+
+ return
+
+/obj/machinery/atmospherics/valve/initialize()
+ normalize_dir()
+
+ var/node1_dir
+ var/node2_dir
+
+ for(var/direction in cardinal)
+ if(direction&initialize_directions)
+ if (!node1_dir)
+ node1_dir = direction
+ else if (!node2_dir)
+ node2_dir = direction
+
+ for(var/obj/machinery/atmospherics/target in get_step(src,node1_dir))
+ if(target.initialize_directions & get_dir(target,src))
+ node1 = target
+ break
+ for(var/obj/machinery/atmospherics/target in get_step(src,node2_dir))
+ if(target.initialize_directions & get_dir(target,src))
+ node2 = target
+ break
+
+ build_network()
+
+ update_icon()
+ update_underlays()
+
+ if(openDuringInit)
+ close()
+ open()
+ openDuringInit = 0
+
+/obj/machinery/atmospherics/valve/build_network()
+ if(!network_node1 && node1)
+ network_node1 = new /datum/pipe_network()
+ network_node1.normal_members += src
+ network_node1.build_network(node1, src)
+
+ if(!network_node2 && node2)
+ network_node2 = new /datum/pipe_network()
+ network_node2.normal_members += src
+ network_node2.build_network(node2, src)
+
+/obj/machinery/atmospherics/valve/return_network(obj/machinery/atmospherics/reference)
+ build_network()
+
+ if(reference==node1)
+ return network_node1
+
+ if(reference==node2)
+ return network_node2
+
+ return null
+
+/obj/machinery/atmospherics/valve/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network)
+ if(network_node1 == old_network)
+ network_node1 = new_network
+ if(network_node2 == old_network)
+ network_node2 = new_network
+
+ return 1
+
+/obj/machinery/atmospherics/valve/return_network_air(datum/network/reference)
+ return null
+
+/obj/machinery/atmospherics/valve/disconnect(obj/machinery/atmospherics/reference)
+ if(reference==node1)
+ del(network_node1)
node1 = null
+
+ else if(reference==node2)
+ del(network_node2)
node2 = null
- ..()
+ update_underlays()
- proc/open()
+ return null
- if(open) return 0
+/obj/machinery/atmospherics/valve/digital // can be controlled by AI
+ name = "digital valve"
+ desc = "A digitally controlled valve."
+ icon = 'icons/atmos/digital_valve.dmi'
+
+ var/frequency = 0
+ var/id = null
+ var/datum/radio_frequency/radio_connection
- open = 1
+/obj/machinery/atmospherics/valve/digital/attack_ai(mob/user as mob)
+ return src.attack_hand(user)
+
+/obj/machinery/atmospherics/valve/digital/attack_hand(mob/user as mob)
+ if(!powered())
+ return
+ if(!src.allowed(user))
+ user << "\red Access denied."
+ return
+ ..()
+
+/obj/machinery/atmospherics/valve/digital/open
+ open = 1
+ icon_state = "map_valve1"
+
+/obj/machinery/atmospherics/valve/digital/power_change()
+ var/old_stat = stat
+ ..()
+ if(old_stat != stat)
update_icon()
- if(network_node1&&network_node2)
- network_node1.merge(network_node2)
- network_node2 = network_node1
+/obj/machinery/atmospherics/valve/digital/update_icon()
+ ..()
+ if(!powered())
+ icon_state = "valve[open]nopower"
- if(network_node1)
- network_node1.update = 1
- else if(network_node2)
- network_node2.update = 1
+/obj/machinery/atmospherics/valve/digital/proc/set_frequency(new_frequency)
+ radio_controller.remove_object(src, frequency)
+ frequency = new_frequency
+ if(frequency)
+ radio_connection = radio_controller.add_object(src, frequency, RADIO_ATMOSIA)
- return 1
+/obj/machinery/atmospherics/valve/digital/initialize()
+ ..()
+ if(frequency)
+ set_frequency(frequency)
- proc/close()
+/obj/machinery/atmospherics/valve/digital/receive_signal(datum/signal/signal)
+ if(!signal.data["tag"] || (signal.data["tag"] != id))
+ return 0
- if(!open)
- return 0
+ switch(signal.data["command"])
+ if("valve_open")
+ if(!open)
+ open()
- open = 0
- update_icon()
+ if("valve_close")
+ if(open)
+ close()
- if(network_node1)
- del(network_node1)
- if(network_node2)
- del(network_node2)
-
- build_network()
-
- return 1
-
- proc/normalize_dir()
- if(dir==3)
- dir = 1
- else if(dir==12)
- dir = 4
-
- attack_ai(mob/user as mob)
- return
-
- attack_paw(mob/user as mob)
- return attack_hand(user)
-
- attack_hand(mob/user as mob)
- src.add_fingerprint(usr)
- update_icon(1)
- sleep(10)
- if (src.open)
- src.close()
- else
- src.open()
-
- process()
- ..()
- . = PROCESS_KILL
-
-/* if(open && (!node1 || !node2))
- close()
- if(!node1)
- if(!nodealert)
- //world << "Missing node from [src] at [src.x],[src.y],[src.z]"
- nodealert = 1
- else if (!node2)
- if(!nodealert)
- //world << "Missing node from [src] at [src.x],[src.y],[src.z]"
- nodealert = 1
- else if (nodealert)
- nodealert = 0
-*/
-
- return
-
- initialize()
- normalize_dir()
-
- var/node1_dir
- var/node2_dir
-
- for(var/direction in cardinal)
- if(direction&initialize_directions)
- if (!node1_dir)
- node1_dir = direction
- else if (!node2_dir)
- node2_dir = direction
-
- for(var/obj/machinery/atmospherics/target in get_step(src,node1_dir))
- if(target.initialize_directions & get_dir(target,src))
- node1 = target
- break
- for(var/obj/machinery/atmospherics/target in get_step(src,node2_dir))
- if(target.initialize_directions & get_dir(target,src))
- node2 = target
- break
-
- build_network()
-
- if(openDuringInit)
- close()
- open()
- openDuringInit = 0
-
-/*
- var/connect_directions
- switch(dir)
- if(NORTH)
- connect_directions = NORTH|SOUTH
- if(SOUTH)
- connect_directions = NORTH|SOUTH
- if(EAST)
- connect_directions = EAST|WEST
- if(WEST)
- connect_directions = EAST|WEST
+ if("valve_toggle")
+ if(open)
+ close()
else
- connect_directions = dir
-
- for(var/direction in cardinal)
- if(direction&connect_directions)
- for(var/obj/machinery/atmospherics/target in get_step(src,direction))
- if(target.initialize_directions & get_dir(target,src))
- connect_directions &= ~direction
- node1 = target
- break
- if(node1)
- break
-
- for(var/direction in cardinal)
- if(direction&connect_directions)
- for(var/obj/machinery/atmospherics/target in get_step(src,direction))
- if(target.initialize_directions & get_dir(target,src))
- node2 = target
- break
- if(node1)
- break
-*/
- build_network()
- if(!network_node1 && node1)
- network_node1 = new /datum/pipe_network()
- network_node1.normal_members += src
- network_node1.build_network(node1, src)
-
- if(!network_node2 && node2)
- network_node2 = new /datum/pipe_network()
- network_node2.normal_members += src
- network_node2.build_network(node2, src)
-
-
- return_network(obj/machinery/atmospherics/reference)
- build_network()
-
- if(reference==node1)
- return network_node1
-
- if(reference==node2)
- return network_node2
-
- return null
-
- reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network)
- if(network_node1 == old_network)
- network_node1 = new_network
- if(network_node2 == old_network)
- network_node2 = new_network
+ open()
+/obj/machinery/atmospherics/valve/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
+ if (!istype(W, /obj/item/weapon/wrench))
+ return ..()
+ if (istype(src, /obj/machinery/atmospherics/valve/digital))
+ user << "\red You cannot unwrench this [src], it's too complicated."
return 1
-
- return_network_air(datum/network/reference)
- return null
-
- disconnect(obj/machinery/atmospherics/reference)
- if(reference==node1)
- del(network_node1)
- node1 = null
-
- else if(reference==node2)
- del(network_node2)
- node2 = null
-
- return null
-
- digital // can be controlled by AI
- name = "digital valve"
- desc = "A digitally controlled valve."
- icon = 'icons/obj/atmospherics/digital_valve.dmi'
-
- attack_ai(mob/user as mob)
- return src.attack_hand(user)
-
- attack_hand(mob/user as mob)
- if(!src.allowed(user))
- user << "\red Access denied."
- return
- ..()
-
- //Radio remote control
-
- proc
- set_frequency(new_frequency)
- radio_controller.remove_object(src, frequency)
- frequency = new_frequency
- if(frequency)
- radio_connection = radio_controller.add_object(src, frequency, RADIO_ATMOSIA)
-
- var/frequency = 0
- var/id = null
- var/datum/radio_frequency/radio_connection
-
- initialize()
- ..()
- if(frequency)
- set_frequency(frequency)
-
- receive_signal(datum/signal/signal)
- if(!signal.data["tag"] || (signal.data["tag"] != id))
- return 0
-
- switch(signal.data["command"])
- if("valve_open")
- if(!open)
- open()
-
- if("valve_close")
- if(open)
- close()
-
- if("valve_toggle")
- if(open)
- close()
- else
- open()
-
- attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
- if (!istype(W, /obj/item/weapon/wrench))
- return ..()
- if (istype(src, /obj/machinery/atmospherics/valve/digital))
- user << "\red You cannot unwrench this [src], it's too complicated."
- return 1
- var/turf/T = src.loc
- if (level==1 && isturf(T) && T.intact)
- user << "\red You must remove the plating first."
- return 1
- var/datum/gas_mixture/int_air = return_air()
- var/datum/gas_mixture/env_air = loc.return_air()
- if ((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE)
- user << "\red You cannot unwrench this [src], it too exerted due to internal pressure."
- add_fingerprint(user)
- return 1
- playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
- user << "\blue You begin to unfasten \the [src]..."
- if (do_after(user, 40))
- user.visible_message( \
- "[user] unfastens \the [src].", \
- "\blue You have unfastened \the [src].", \
- "You hear ratchet.")
- new /obj/item/pipe(loc, make_from=src)
- del(src)
+ var/datum/gas_mixture/int_air = return_air()
+ var/datum/gas_mixture/env_air = loc.return_air()
+ if ((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE)
+ user << "\red You cannot unwrench this [src], it too exerted due to internal pressure."
+ add_fingerprint(user)
+ return 1
+ playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
+ user << "\blue You begin to unfasten \the [src]..."
+ if (do_after(user, 40))
+ user.visible_message( \
+ "[user] unfastens \the [src].", \
+ "\blue You have unfastened \the [src].", \
+ "You hear ratchet.")
+ new /obj/item/pipe(loc, make_from=src)
+ del(src)
\ No newline at end of file
diff --git a/code/ATMOSPHERICS/datum_pipe_network.dm b/code/ATMOSPHERICS/datum_pipe_network.dm
index 168e5521b6e..4098c15c31b 100644
--- a/code/ATMOSPHERICS/datum_pipe_network.dm
+++ b/code/ATMOSPHERICS/datum_pipe_network.dm
@@ -2,16 +2,17 @@ var/global/list/datum/pipe_network/pipe_networks = list()
datum/pipe_network
var/list/datum/gas_mixture/gases = list() //All of the gas_mixtures continuously connected in this network
+ var/volume = 0 //caches the total volume for atmos machines to use in gas calculations
var/list/obj/machinery/atmospherics/normal_members = list()
var/list/datum/pipeline/line_members = list()
//membership roster to go through for updates and what not
var/update = 1
- var/datum/gas_mixture/air_transient = null
+ //var/datum/gas_mixture/air_transient = null
New()
- air_transient = new()
+ //air_transient = new()
..()
@@ -61,6 +62,7 @@ datum/pipe_network
//Go through membership roster and make sure gases is up to date
gases = list()
+ volume = 0
for(var/obj/machinery/atmospherics/normal_member in normal_members)
var/result = normal_member.return_network_air(src)
@@ -68,137 +70,9 @@ datum/pipe_network
for(var/datum/pipeline/line_member in line_members)
gases += line_member.air
+
+ for(var/datum/gas_mixture/air in gases)
+ volume += air.volume
proc/reconcile_air()
- //Perfectly equalize all gases members instantly
-
- //Calculate totals from individual components
- var/total_thermal_energy = 0
- var/total_heat_capacity = 0
-
- air_transient.volume = 0
-
- air_transient.oxygen = 0
- air_transient.nitrogen = 0
- air_transient.phoron = 0
- air_transient.carbon_dioxide = 0
-
-
- air_transient.trace_gases = list()
-
- for(var/datum/gas_mixture/gas in gases)
- air_transient.volume += gas.volume
- var/temp_heatcap = gas.heat_capacity()
- total_thermal_energy += gas.temperature*temp_heatcap
- total_heat_capacity += temp_heatcap
-
- air_transient.oxygen += gas.oxygen
- air_transient.nitrogen += gas.nitrogen
- air_transient.phoron += gas.phoron
- air_transient.carbon_dioxide += gas.carbon_dioxide
-
- if(gas.trace_gases.len)
- for(var/datum/gas/trace_gas in gas.trace_gases)
- var/datum/gas/corresponding = locate(trace_gas.type) in air_transient.trace_gases
- if(!corresponding)
- corresponding = new trace_gas.type()
- air_transient.trace_gases += corresponding
-
- corresponding.moles += trace_gas.moles
-
- if(air_transient.volume > 0)
-
- if(total_heat_capacity > 0)
- air_transient.temperature = total_thermal_energy/total_heat_capacity
-
- //Allow air mixture to react
- if(air_transient.react())
- update = 1
-
- else
- air_transient.temperature = 0
-
- //Update individual gas_mixtures by volume ratio
- for(var/datum/gas_mixture/gas in gases)
- gas.oxygen = air_transient.oxygen*gas.volume/air_transient.volume
- gas.nitrogen = air_transient.nitrogen*gas.volume/air_transient.volume
- gas.phoron = air_transient.phoron*gas.volume/air_transient.volume
- gas.carbon_dioxide = air_transient.carbon_dioxide*gas.volume/air_transient.volume
-
- gas.temperature = air_transient.temperature
-
- if(air_transient.trace_gases.len)
- for(var/datum/gas/trace_gas in air_transient.trace_gases)
- var/datum/gas/corresponding = locate(trace_gas.type) in gas.trace_gases
- if(!corresponding)
- corresponding = new trace_gas.type()
- gas.trace_gases += corresponding
-
- corresponding.moles = trace_gas.moles*gas.volume/air_transient.volume
- gas.update_values()
- air_transient.update_values()
- return 1
-
-proc/equalize_gases(datum/gas_mixture/list/gases)
- //Perfectly equalize all gases members instantly
-
- //Calculate totals from individual components
- var/total_volume = 0
- var/total_thermal_energy = 0
- var/total_heat_capacity = 0
-
- var/total_oxygen = 0
- var/total_nitrogen = 0
- var/total_phoron = 0
- var/total_carbon_dioxide = 0
-
- var/list/total_trace_gases = list()
-
- for(var/datum/gas_mixture/gas in gases)
- total_volume += gas.volume
- var/temp_heatcap = gas.heat_capacity()
- total_thermal_energy += gas.temperature*temp_heatcap
- total_heat_capacity += temp_heatcap
-
- total_oxygen += gas.oxygen
- total_nitrogen += gas.nitrogen
- total_phoron += gas.phoron
- total_carbon_dioxide += gas.carbon_dioxide
-
- if(gas.trace_gases.len)
- for(var/datum/gas/trace_gas in gas.trace_gases)
- var/datum/gas/corresponding = locate(trace_gas.type) in total_trace_gases
- if(!corresponding)
- corresponding = new trace_gas.type()
- total_trace_gases += corresponding
-
- corresponding.moles += trace_gas.moles
-
- if(total_volume > 0)
-
- //Calculate temperature
- var/temperature = 0
-
- if(total_heat_capacity > 0)
- temperature = total_thermal_energy/total_heat_capacity
-
- //Update individual gas_mixtures by volume ratio
- for(var/datum/gas_mixture/gas in gases)
- gas.oxygen = total_oxygen*gas.volume/total_volume
- gas.nitrogen = total_nitrogen*gas.volume/total_volume
- gas.phoron = total_phoron*gas.volume/total_volume
- gas.carbon_dioxide = total_carbon_dioxide*gas.volume/total_volume
-
- gas.temperature = temperature
-
- if(total_trace_gases.len)
- for(var/datum/gas/trace_gas in total_trace_gases)
- var/datum/gas/corresponding = locate(trace_gas.type) in gas.trace_gases
- if(!corresponding)
- corresponding = new trace_gas.type()
- gas.trace_gases += corresponding
-
- corresponding.moles = trace_gas.moles*gas.volume/total_volume
- gas.update_values()
-
- return 1
\ No newline at end of file
+ equalize_gases(gases)
diff --git a/code/ATMOSPHERICS/datum_pipeline.dm b/code/ATMOSPHERICS/datum_pipeline.dm
index b7444d00df8..aa4473babd5 100644
--- a/code/ATMOSPHERICS/datum_pipeline.dm
+++ b/code/ATMOSPHERICS/datum_pipeline.dm
@@ -1,4 +1,3 @@
-#define STEFAN_BOLTZMANN_CONSTANT 0.0000000567
datum/pipeline
var/datum/gas_mixture/air
@@ -37,22 +36,9 @@ datum/pipeline
for(var/obj/machinery/atmospherics/pipe/member in members)
member.air_temporary = new
+ member.air_temporary.copy_from(air)
member.air_temporary.volume = member.volume
-
- member.air_temporary.oxygen = air.oxygen*member.volume/air.volume
- member.air_temporary.nitrogen = air.nitrogen*member.volume/air.volume
- member.air_temporary.phoron = air.phoron*member.volume/air.volume
- member.air_temporary.carbon_dioxide = air.carbon_dioxide*member.volume/air.volume
-
- member.air_temporary.temperature = air.temperature
-
- if(air.trace_gases.len)
- for(var/datum/gas/trace_gas in air.trace_gases)
- var/datum/gas/corresponding = new trace_gas.type()
- member.air_temporary.trace_gases += corresponding
-
- corresponding.moles = trace_gas.moles*member.volume/air.volume
- member.air_temporary.update_values()
+ member.air_temporary.multiply(member.volume / air.volume)
proc/build_pipeline(obj/machinery/atmospherics/pipe/base)
air = new
@@ -213,10 +199,15 @@ datum/pipeline
air.temperature -= heat/total_heat_capacity
if(network)
network.update = 1
-
- proc/radiate_heat(surface, thermal_conductivity)
- var/total_heat_capacity = air.heat_capacity()
- var/heat = STEFAN_BOLTZMANN_CONSTANT * surface * air.temperature ** 4 * thermal_conductivity
- air.temperature = max(0, air.temperature - heat / total_heat_capacity)
+
+ //surface must be the surface area in m^2
+ proc/radiate_heat_to_space(surface, thermal_conductivity)
+ var/gas_density = air.total_moles/air.volume
+ thermal_conductivity *= min(gas_density / ( RADIATOR_OPTIMUM_PRESSURE/(R_IDEAL_GAS_EQUATION*T20C) ), 1)
+
+ //if the h/e pipes radiate less than the AVERAGE_SOLAR_RADIATION, then they will heat up, otherwise they will cool down. It turns out the critical temperature is -26 C
+ var/heat_gain = surface*(AVERAGE_SOLAR_RADIATION - STEFAN_BOLTZMANN_CONSTANT*thermal_conductivity*(air.temperature - COSMIC_RADIATION_TEMPERATURE) ** 4)
+
+ air.add_thermal_energy(heat_gain)
if(network)
network.update = 1
\ No newline at end of file
diff --git a/code/ATMOSPHERICS/he_pipes.dm b/code/ATMOSPHERICS/he_pipes.dm
index a8e7e06bad6..c22c483e8c4 100644
--- a/code/ATMOSPHERICS/he_pipes.dm
+++ b/code/ATMOSPHERICS/he_pipes.dm
@@ -1,10 +1,11 @@
obj/machinery/atmospherics/pipe/simple/heat_exchanging
- icon = 'icons/obj/pipes/heat.dmi'
+ icon = 'icons/atmos/heat.dmi'
icon_state = "intact"
+ pipe_icon = "hepipe"
level = 2
var/initialize_directions_he
- var/surface = 2
+ var/surface = 2 //surface area in m^2
minimum_temperature_difference = 20
thermal_conductivity = OPEN_HEAT_TRANSFER_COEFFICIENT
@@ -35,6 +36,10 @@ obj/machinery/atmospherics/pipe/simple/heat_exchanging
if(target.initialize_directions_he & get_dir(target,src))
node2 = target
break
+ if(!node1 && !node2)
+ del(src)
+ return
+
update_icon()
return
@@ -53,13 +58,14 @@ obj/machinery/atmospherics/pipe/simple/heat_exchanging
var/datum/gas_mixture/pipe_air = return_air()
if(abs(environment_temperature-pipe_air.temperature) > minimum_temperature_difference)
parent.temperature_interact(loc, volume, thermal_conductivity)
- else
- parent.radiate_heat(surface, 1)
+ else if(istype(loc, /turf/space/))
+ parent.radiate_heat_to_space(surface, 1)
obj/machinery/atmospherics/pipe/simple/heat_exchanging/junction
- icon = 'icons/obj/pipes/junction.dmi'
+ icon = 'icons/atmos/junction.dmi'
icon_state = "intact"
+ pipe_icon = "hejunction"
level = 2
minimum_temperature_difference = 300
thermal_conductivity = WALL_HEAT_TRANSFER_COEFFICIENT
@@ -82,16 +88,6 @@ obj/machinery/atmospherics/pipe/simple/heat_exchanging/junction
initialize_directions_he = WEST
// BubbleWrap END
- update_icon()
- if(node1&&node2)
- icon_state = "intact"
- else
- var/have_node1 = node1?1:0
- var/have_node2 = node2?1:0
- icon_state = "exposed[have_node1][have_node2]"
- if(!node1&&!node2)
- del(src)
-
initialize()
for(var/obj/machinery/atmospherics/target in get_step(src,initialize_directions))
if(target.initialize_directions & get_dir(target,src))
@@ -102,5 +98,9 @@ obj/machinery/atmospherics/pipe/simple/heat_exchanging/junction
node2 = target
break
+ if(!node1&&!node2)
+ del(src)
+ return
+
update_icon()
return
diff --git a/code/ATMOSPHERICS/mapdebugging.dm b/code/ATMOSPHERICS/mapdebugging.dm
deleted file mode 100644
index 2dc39497136..00000000000
--- a/code/ATMOSPHERICS/mapdebugging.dm
+++ /dev/null
@@ -1,16 +0,0 @@
-client/verb/discon_pipes()
- set name = "Show Disconnected Pipes"
- set category = "Debug"
-
- for(var/obj/machinery/atmospherics/pipe/simple/P in world)
- if(!P.node1 || !P.node2)
- usr << "[P], [P.x], [P.y], [P.z], [P.loc.loc]"
-
- for(var/obj/machinery/atmospherics/pipe/manifold/P in world)
- if(!P.node1 || !P.node2 || !P.node3)
- usr << "[P], [P.x], [P.y], [P.z], [P.loc.loc]"
-
- for(var/obj/machinery/atmospherics/pipe/manifold4w/P in world)
- if(!P.node1 || !P.node2 || !P.node3 || !P.node4)
- usr << "[P], [P.x], [P.y], [P.z], [P.loc.loc]"
-//With thanks to mini. Check before use, uncheck after. Do Not Use on a live server.
\ No newline at end of file
diff --git a/code/ATMOSPHERICS/pipes.dm b/code/ATMOSPHERICS/pipes.dm
index 11daee64c3d..898f1965517 100644
--- a/code/ATMOSPHERICS/pipes.dm
+++ b/code/ATMOSPHERICS/pipes.dm
@@ -1,4 +1,4 @@
-obj/machinery/atmospherics/pipe
+/obj/machinery/atmospherics/pipe
var/datum/gas_mixture/air_temporary //used when reconstructing a pipeline that broke
var/datum/pipeline/parent
@@ -12,51 +12,57 @@ obj/machinery/atmospherics/pipe
var/alert_pressure = 80*ONE_ATMOSPHERE
//minimum pressure before check_pressure(...) should be called
-obj/machinery/atmospherics/pipe/proc/pipeline_expansion()
+/obj/machinery/atmospherics/pipe/New()
+ ..()
+ //so pipes under walls are hidden
+ if(istype(get_turf(src), /turf/simulated/wall) || istype(get_turf(src), /turf/simulated/shuttle/wall) || istype(get_turf(src), /turf/unsimulated/wall))
+ level = 1
+
+/obj/machinery/atmospherics/pipe/proc/pipeline_expansion()
return null
-obj/machinery/atmospherics/pipe/proc/check_pressure(pressure)
+/obj/machinery/atmospherics/pipe/proc/check_pressure(pressure)
//Return 1 if parent should continue checking other pipes
//Return null if parent should stop checking other pipes. Recall: del(src) will by default return null
return 1
-obj/machinery/atmospherics/pipe/return_air()
+/obj/machinery/atmospherics/pipe/return_air()
if(!parent)
parent = new /datum/pipeline()
parent.build_pipeline(src)
return parent.air
-obj/machinery/atmospherics/pipe/build_network()
+/obj/machinery/atmospherics/pipe/build_network()
if(!parent)
parent = new /datum/pipeline()
parent.build_pipeline(src)
return parent.return_network()
-obj/machinery/atmospherics/pipe/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference)
+/obj/machinery/atmospherics/pipe/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference)
if(!parent)
parent = new /datum/pipeline()
parent.build_pipeline(src)
return parent.network_expand(new_network, reference)
-obj/machinery/atmospherics/pipe/return_network(obj/machinery/atmospherics/reference)
+/obj/machinery/atmospherics/pipe/return_network(obj/machinery/atmospherics/reference)
if(!parent)
parent = new /datum/pipeline()
parent.build_pipeline(src)
return parent.return_network(reference)
-obj/machinery/atmospherics/pipe/Del()
+/obj/machinery/atmospherics/pipe/Del()
del(parent)
if(air_temporary)
loc.assume_air(air_temporary)
..()
-obj/machinery/atmospherics/pipe/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
+/obj/machinery/atmospherics/pipe/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
if (istype(src, /obj/machinery/atmospherics/pipe/tank))
return ..()
if (istype(src, /obj/machinery/atmospherics/pipe/vent))
@@ -91,14 +97,49 @@ obj/machinery/atmospherics/pipe/attackby(var/obj/item/weapon/W as obj, var/mob/u
del(meter)
del(src)
+/obj/machinery/atmospherics/proc/change_color(var/new_color)
+ //only pass valid pipe colors please ~otherwise your pipe will turn invisible
+ if(!pipe_color_check(new_color))
+ return
-obj/machinery/atmospherics/pipe/simple
- icon = 'icons/obj/pipes.dmi'
+ pipe_color = new_color
+ update_icon()
+/obj/machinery/atmospherics/pipe/add_underlay(var/obj/machinery/atmospherics/node, var/direction)
+ if(istype(src, /obj/machinery/atmospherics/pipe/tank)) //todo: move tanks to unary devices
+ return ..()
+
+ if(node)
+ var/temp_dir = get_dir(src, node)
+ underlays += icon_manager.get_atmos_icon("pipe_underlay_intact", temp_dir, color_cache_name(node))
+ return temp_dir
+ else if(direction)
+ underlays += icon_manager.get_atmos_icon("pipe_underlay_exposed", direction, pipe_color)
+ else
+ return null
+
+/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)
+ return node.pipe_color
+ else
+ return null
+ else if(istype(node, /obj/machinery/atmospherics/pipe/simple))
+ return node.pipe_color
+ else
+ return pipe_color
+
+/obj/machinery/atmospherics/pipe/simple
+ icon = 'icons/atmos/pipes.dmi'
+ icon_state = ""
+ var/pipe_icon = "" //what kind of pipe it is and from which dmi is the icon manager getting its icons, "" for simple pipes, "hepipe" for HE pipes, "hejunction" for HE junctions
name = "pipe"
desc = "A one meter section of regular pipe"
- volume = 70
+ volume = ATMOS_DEFAULT_VOLUME_PIPE
dir = SOUTH
initialize_directions = SOUTH|NORTH
@@ -113,12 +154,16 @@ obj/machinery/atmospherics/pipe/simple
var/fatigue_pressure = 55*ONE_ATMOSPHERE
alert_pressure = 55*ONE_ATMOSPHERE
-
level = 1
-obj/machinery/atmospherics/pipe/simple/New()
+/obj/machinery/atmospherics/pipe/simple/New()
..()
+
+ // Pipe colors and icon states are handled by an image cache - so color and icon should
+ // be null. For mapping purposes color is defined in the object definitions.
+ icon = null
alpha = 255
+
switch(dir)
if(SOUTH || NORTH)
initialize_directions = SOUTH|NORTH
@@ -133,53 +178,18 @@ obj/machinery/atmospherics/pipe/simple/New()
if(SOUTHWEST)
initialize_directions = SOUTH|WEST
-
-obj/machinery/atmospherics/pipe/simple/hide(var/i)
+/obj/machinery/atmospherics/pipe/simple/hide(var/i)
if(level == 1 && istype(loc, /turf/simulated))
invisibility = i ? 101 : 0
update_icon()
-obj/machinery/atmospherics/pipe/simple/process()
+/obj/machinery/atmospherics/pipe/simple/process()
if(!parent) //This should cut back on the overhead calling build_network thousands of times per cycle
..()
else
. = PROCESS_KILL
- /*if(!node1)
- parent.mingle_with_turf(loc, volume)
- if(!nodealert)
- //world << "Missing node from [src] at [src.x],[src.y],[src.z]"
- nodealert = 1
-
- else if(!node2)
- parent.mingle_with_turf(loc, volume)
- if(!nodealert)
- //world << "Missing node from [src] at [src.x],[src.y],[src.z]"
- nodealert = 1
- else if (nodealert)
- nodealert = 0
-
-
- else if(parent)
- var/environment_temperature = 0
-
- if(istype(loc, /turf/simulated/))
- if(loc:blocks_air)
- environment_temperature = loc:temperature
- else
- var/datum/gas_mixture/environment = loc.return_air()
- environment_temperature = environment.temperature
-
- else
- environment_temperature = loc:temperature
-
- var/datum/gas_mixture/pipe_air = return_air()
-
- if(abs(environment_temperature-pipe_air.temperature) > minimum_temperature_difference)
- parent.temperature_interact(loc, volume, thermal_conductivity)
- */ //Screw you heat lag
-
-obj/machinery/atmospherics/pipe/simple/check_pressure(pressure)
+/obj/machinery/atmospherics/pipe/simple/check_pressure(pressure)
var/datum/gas_mixture/environment = loc.return_air()
var/pressure_difference = pressure - environment.return_pressure()
@@ -194,7 +204,7 @@ obj/machinery/atmospherics/pipe/simple/check_pressure(pressure)
else return 1
-obj/machinery/atmospherics/pipe/simple/proc/burst()
+/obj/machinery/atmospherics/pipe/simple/proc/burst()
src.visible_message("\red \bold [src] bursts!");
playsound(src.loc, 'sound/effects/bang.ogg', 25, 1)
var/datum/effect/effect/system/smoke_spread/smoke = new
@@ -202,13 +212,13 @@ obj/machinery/atmospherics/pipe/simple/proc/burst()
smoke.start()
del(src)
-obj/machinery/atmospherics/pipe/simple/proc/normalize_dir()
+/obj/machinery/atmospherics/pipe/simple/proc/normalize_dir()
if(dir==3)
dir = 1
else if(dir==12)
dir = 4
-obj/machinery/atmospherics/pipe/simple/Del()
+/obj/machinery/atmospherics/pipe/simple/Del()
if(node1)
node1.disconnect(src)
if(node2)
@@ -216,42 +226,42 @@ obj/machinery/atmospherics/pipe/simple/Del()
..()
-obj/machinery/atmospherics/pipe/simple/pipeline_expansion()
+/obj/machinery/atmospherics/pipe/simple/pipeline_expansion()
return list(node1, node2)
-obj/machinery/atmospherics/pipe/simple/update_icon()
- switch(pipe_color)
- if ("red") color = COLOR_RED
- if ("blue") color = COLOR_BLUE
- if ("cyan") color = COLOR_CYAN
- if ("green") color = COLOR_GREEN
- if ("yellow") color = "#FFCC00"
- if ("purple") color = "#5C1EC0"
- if ("grey") color = null
+/obj/machinery/atmospherics/pipe/simple/change_color(var/new_color)
+ ..()
+ //for updating connected atmos device pipes (i.e. vents, manifolds, etc)
+ if(node1)
+ node1.update_underlays()
+ if(node2)
+ node2.update_underlays()
- if(node1 && node2)
- icon_state = "intact[invisibility ? "-f" : "" ]"
+/obj/machinery/atmospherics/pipe/simple/update_icon(var/safety = 0)
+ if(!check_icon_cache())
+ return
- //var/node1_direction = get_dir(src, node1)
- //var/node2_direction = get_dir(src, node2)
+ alpha = 255
- //dir = node1_direction|node2_direction
+ overlays.Cut()
+ if(!node1 && !node2)
+ var/turf/T = get_turf(src)
+ new /obj/item/pipe(loc, make_from=src)
+ for (var/obj/machinery/meter/meter in T)
+ if (meter.target == src)
+ new /obj/item/pipe_meter(T)
+ del(meter)
+ del(src)
+ else if(node1 && node2)
+ overlays += icon_manager.get_atmos_icon("pipe", , pipe_color, pipe_icon + "intact")
else
- if(!node1 && !node2)
- var/turf/T = get_turf(src)
- new /obj/item/pipe(loc, make_from=src)
- for (var/obj/machinery/meter/meter in T)
- if (meter.target == src)
- new /obj/item/pipe_meter(T)
- del(meter)
- del(src)
- var/have_node1 = node1?1:0
- var/have_node2 = node2?1:0
- icon_state = "exposed[have_node1][have_node2][invisibility ? "-f" : "" ]"
+ overlays += icon_manager.get_atmos_icon("pipe", , pipe_color, pipe_icon + "exposed[node1?1:0][node2?1:0]")
+/obj/machinery/atmospherics/pipe/simple/update_underlays()
+ return
-obj/machinery/atmospherics/pipe/simple/initialize()
+/obj/machinery/atmospherics/pipe/simple/initialize()
normalize_dir()
var/node1_dir
var/node2_dir
@@ -276,12 +286,12 @@ obj/machinery/atmospherics/pipe/simple/initialize()
del(src)
return
- var/turf/T = src.loc // hide if turf is not intact
- hide(T.intact)
+ var/turf/T = get_turf(src)
+ if(istype(T))
+ hide(T.intact)
update_icon()
- //update_icon()
-obj/machinery/atmospherics/pipe/simple/disconnect(obj/machinery/atmospherics/reference)
+/obj/machinery/atmospherics/pipe/simple/disconnect(obj/machinery/atmospherics/reference)
if(reference == node1)
if(istype(node1, /obj/machinery/atmospherics/pipe))
del(parent)
@@ -296,55 +306,56 @@ obj/machinery/atmospherics/pipe/simple/disconnect(obj/machinery/atmospherics/ref
return null
-
-
-
-obj/machinery/atmospherics/pipe/simple/visible
- level = 2
+/obj/machinery/atmospherics/pipe/simple/visible
icon_state = "intact"
+ level = 2
-obj/machinery/atmospherics/pipe/simple/visible/scrubbers
- name="Scrubbers pipe"
- color=COLOR_RED
+/obj/machinery/atmospherics/pipe/simple/visible/scrubbers
+ name = "Scrubbers pipe"
+ color = PIPE_COLOR_RED
-obj/machinery/atmospherics/pipe/simple/visible/supply
- name="Air supply pipe"
- color=COLOR_BLUE
+/obj/machinery/atmospherics/pipe/simple/visible/supply
+ name = "Air supply pipe"
+ color = PIPE_COLOR_BLUE
-obj/machinery/atmospherics/pipe/simple/visible/yellow
- color="#FFCC00"
+/obj/machinery/atmospherics/pipe/simple/visible/yellow
+ color = PIPE_COLOR_YELLOW
-obj/machinery/atmospherics/pipe/simple/visible/cyan
- color=COLOR_CYAN
+/obj/machinery/atmospherics/pipe/simple/visible/cyan
+ color = PIPE_COLOR_CYAN
-obj/machinery/atmospherics/pipe/simple/visible/green
- color=COLOR_GREEN
+/obj/machinery/atmospherics/pipe/simple/visible/green
+ color = PIPE_COLOR_GREEN
+/obj/machinery/atmospherics/pipe/simple/visible/purple
+ color = PIPE_COLOR_PURPLE
-obj/machinery/atmospherics/pipe/simple/hidden
+/obj/machinery/atmospherics/pipe/simple/hidden
+ icon_state = "intact"
level = 1
- icon_state = "intact-f"
- alpha = 192 //set for the benefit of mapping - this is reset to opaque when the pipe is spawned in game
+ alpha = 128 //set for the benefit of mapping - this is reset to opaque when the pipe is spawned in game
-obj/machinery/atmospherics/pipe/simple/hidden/scrubbers
- name="Scrubbers pipe"
- color=COLOR_RED
+/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers
+ name = "Scrubbers pipe"
+ color = PIPE_COLOR_RED
-obj/machinery/atmospherics/pipe/simple/hidden/supply
- name="Air supply pipe"
- color=COLOR_BLUE
+/obj/machinery/atmospherics/pipe/simple/hidden/supply
+ name = "Air supply pipe"
+ color = PIPE_COLOR_BLUE
-obj/machinery/atmospherics/pipe/simple/hidden/yellow
- color="#FFCC00"
+/obj/machinery/atmospherics/pipe/simple/hidden/yellow
+ color = PIPE_COLOR_YELLOW
-obj/machinery/atmospherics/pipe/simple/hidden/cyan
- color=COLOR_CYAN
+/obj/machinery/atmospherics/pipe/simple/hidden/cyan
+ color = PIPE_COLOR_CYAN
-obj/machinery/atmospherics/pipe/simple/hidden/green
- color=COLOR_GREEN
+/obj/machinery/atmospherics/pipe/simple/hidden/green
+ color = PIPE_COLOR_GREEN
+/obj/machinery/atmospherics/pipe/simple/hidden/purple
+ color = PIPE_COLOR_PURPLE
-obj/machinery/atmospherics/pipe/simple/insulated
+/obj/machinery/atmospherics/pipe/simple/insulated
icon = 'icons/obj/atmospherics/red_pipe.dmi'
icon_state = "intact"
@@ -357,13 +368,13 @@ obj/machinery/atmospherics/pipe/simple/insulated
level = 2
-obj/machinery/atmospherics/pipe/manifold
- icon = 'icons/obj/atmospherics/pipe_manifold.dmi'
-
+/obj/machinery/atmospherics/pipe/manifold
+ icon = 'icons/atmos/manifold.dmi'
+ icon_state = ""
name = "pipe manifold"
desc = "A manifold composed of regular pipes"
- volume = 105
+ volume = ATMOS_DEFAULT_VOLUME_PIPE * 1.5
dir = SOUTH
initialize_directions = EAST|NORTH|WEST
@@ -375,8 +386,11 @@ obj/machinery/atmospherics/pipe/manifold
level = 1
layer = 2.4 //under wires with their 2.44
-obj/machinery/atmospherics/pipe/manifold/New()
+/obj/machinery/atmospherics/pipe/manifold/New()
+ ..()
alpha = 255
+ icon = null
+
switch(dir)
if(NORTH)
initialize_directions = EAST|SOUTH|WEST
@@ -387,43 +401,21 @@ obj/machinery/atmospherics/pipe/manifold/New()
if(WEST)
initialize_directions = NORTH|EAST|SOUTH
- ..()
-
-
-
-obj/machinery/atmospherics/pipe/manifold/hide(var/i)
+/obj/machinery/atmospherics/pipe/manifold/hide(var/i)
if(level == 1 && istype(loc, /turf/simulated))
invisibility = i ? 101 : 0
update_icon()
-obj/machinery/atmospherics/pipe/manifold/pipeline_expansion()
+/obj/machinery/atmospherics/pipe/manifold/pipeline_expansion()
return list(node1, node2, node3)
-obj/machinery/atmospherics/pipe/manifold/process()
+/obj/machinery/atmospherics/pipe/manifold/process()
if(!parent)
..()
else
. = PROCESS_KILL
-/*
- if(!node1)
- parent.mingle_with_turf(loc, 70)
- if(!nodealert)
- //world << "Missing node from [src] at [src.x],[src.y],[src.z]"
- nodealert = 1
- else if(!node2)
- parent.mingle_with_turf(loc, 70)
- if(!nodealert)
- //world << "Missing node from [src] at [src.x],[src.y],[src.z]"
- nodealert = 1
- else if(!node3)
- parent.mingle_with_turf(loc, 70)
- if(!nodealert)
- //world << "Missing node from [src] at [src.x],[src.y],[src.z]"
- nodealert = 1
- else if (nodealert)
- nodealert = 0
-*/
-obj/machinery/atmospherics/pipe/manifold/Del()
+
+/obj/machinery/atmospherics/pipe/manifold/Del()
if(node1)
node1.disconnect(src)
if(node2)
@@ -433,7 +425,7 @@ obj/machinery/atmospherics/pipe/manifold/Del()
..()
-obj/machinery/atmospherics/pipe/manifold/disconnect(obj/machinery/atmospherics/reference)
+/obj/machinery/atmospherics/pipe/manifold/disconnect(obj/machinery/atmospherics/reference)
if(reference == node1)
if(istype(node1, /obj/machinery/atmospherics/pipe))
del(parent)
@@ -453,40 +445,51 @@ obj/machinery/atmospherics/pipe/manifold/disconnect(obj/machinery/atmospherics/r
..()
-obj/machinery/atmospherics/pipe/manifold/update_icon()
- if(node1&&node2&&node3)
- switch(pipe_color)
- if ("red") color = COLOR_RED
- if ("blue") color = COLOR_BLUE
- if ("cyan") color = COLOR_CYAN
- if ("green") color = COLOR_GREEN
- if ("yellow") color = "#FFCC00"
- if ("purple") color = "#5C1EC0"
- if ("grey") color = null
- icon_state = "manifold[invisibility ? "-f" : "" ]"
+/obj/machinery/atmospherics/pipe/manifold/change_color(var/new_color)
+ ..()
+ //for updating connected atmos device pipes (i.e. vents, manifolds, etc)
+ if(node1)
+ node1.update_underlays()
+ if(node2)
+ node2.update_underlays()
+ if(node3)
+ node3.update_underlays()
+/obj/machinery/atmospherics/pipe/manifold/update_icon(var/safety = 0)
+ if(!check_icon_cache())
+ return
+
+ alpha = 255
+
+ if(!node1 && !node2 && !node3)
+ var/turf/T = get_turf(src)
+ new /obj/item/pipe(loc, make_from=src)
+ for (var/obj/machinery/meter/meter in T)
+ if (meter.target == src)
+ new /obj/item/pipe_meter(T)
+ del(meter)
+ del(src)
else
- var/connected = 0
- var/unconnected = 0
- var/connect_directions = (NORTH|SOUTH|EAST|WEST)&(~dir)
+ overlays.Cut()
+ overlays += icon_manager.get_atmos_icon("manifold", , pipe_color, "core")
+ overlays += icon_manager.get_atmos_icon("manifold", , , "clamps")
+ underlays.Cut()
- if(node1)
- connected |= get_dir(src, node1)
- if(node2)
- connected |= get_dir(src, node2)
- if(node3)
- connected |= get_dir(src, node3)
+ var/list/directions = list(NORTH, SOUTH, EAST, WEST)
+ directions -= dir
- unconnected = (~connected)&(connect_directions)
+ directions -= add_underlay(node1)
+ directions -= add_underlay(node2)
+ directions -= add_underlay(node3)
- icon_state = "manifold_[connected]_[unconnected]"
+ for(var/D in directions)
+ add_underlay(,D)
- if(!connected)
- del(src)
+/obj/machinery/atmospherics/pipe/manifold/update_underlays()
+ ..()
+ update_icon()
- return
-
-obj/machinery/atmospherics/pipe/manifold/initialize()
+/obj/machinery/atmospherics/pipe/manifold/initialize()
var/connect_directions = (NORTH|SOUTH|EAST|WEST)&(~dir)
for(var/direction in cardinal)
@@ -521,65 +524,71 @@ obj/machinery/atmospherics/pipe/manifold/initialize()
if (node3)
break
- var/turf/T = src.loc // hide if turf is not intact
- hide(T.intact)
- //update_icon()
+ if(!node1 && !node2 && !node3)
+ del(src)
+ return
+
+ var/turf/T = get_turf(src)
+ if(istype(T))
+ hide(T.intact)
update_icon()
-
-obj/machinery/atmospherics/pipe/manifold/visible
+/obj/machinery/atmospherics/pipe/manifold/visible
+ icon_state = "map"
level = 2
- icon_state = "manifold"
-obj/machinery/atmospherics/pipe/manifold/visible/supply
- name="Air supply pipe"
- color=COLOR_BLUE
+/obj/machinery/atmospherics/pipe/manifold/visible/scrubbers
+ name = "Scrubbers pipe"
+ color = PIPE_COLOR_RED
-obj/machinery/atmospherics/pipe/manifold/visible/scrubbers
- name="Scrubbers pipe"
- color=COLOR_RED
+/obj/machinery/atmospherics/pipe/manifold/visible/supply
+ name = "Air supply pipe"
+ color = PIPE_COLOR_BLUE
-obj/machinery/atmospherics/pipe/manifold/visible/yellow
- color="#FFCC00"
+/obj/machinery/atmospherics/pipe/manifold/visible/yellow
+ color = PIPE_COLOR_YELLOW
-obj/machinery/atmospherics/pipe/manifold/visible/cyan
- color=COLOR_CYAN
+/obj/machinery/atmospherics/pipe/manifold/visible/cyan
+ color = PIPE_COLOR_CYAN
-obj/machinery/atmospherics/pipe/manifold/visible/green
- color=COLOR_GREEN
+/obj/machinery/atmospherics/pipe/manifold/visible/green
+ color = PIPE_COLOR_GREEN
+/obj/machinery/atmospherics/pipe/manifold/visible/purple
+ color = PIPE_COLOR_PURPLE
-obj/machinery/atmospherics/pipe/manifold/hidden
+/obj/machinery/atmospherics/pipe/manifold/hidden
+ icon_state = "map"
level = 1
- icon_state = "manifold-f"
- alpha = 192 //set for the benefit of mapping - this is reset to opaque when the pipe is spawned in game
+ alpha = 128 //set for the benefit of mapping - this is reset to opaque when the pipe is spawned in game
-obj/machinery/atmospherics/pipe/manifold/hidden/supply
- name="Air supply pipe"
- color=COLOR_BLUE
+/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers
+ name = "Scrubbers pipe"
+ color = PIPE_COLOR_RED
-obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers
- name="Scrubbers pipe"
- color = COLOR_RED
+/obj/machinery/atmospherics/pipe/manifold/hidden/supply
+ name = "Air supply pipe"
+ color = PIPE_COLOR_BLUE
-obj/machinery/atmospherics/pipe/manifold/hidden/yellow
- color="#FFCC00"
+/obj/machinery/atmospherics/pipe/manifold/hidden/yellow
+ color = PIPE_COLOR_YELLOW
-obj/machinery/atmospherics/pipe/manifold/hidden/cyan
- color=COLOR_CYAN
+/obj/machinery/atmospherics/pipe/manifold/hidden/cyan
+ color = PIPE_COLOR_CYAN
-obj/machinery/atmospherics/pipe/manifold/hidden/green
- color=COLOR_GREEN
+/obj/machinery/atmospherics/pipe/manifold/hidden/green
+ color = PIPE_COLOR_GREEN
+/obj/machinery/atmospherics/pipe/manifold/hidden/purple
+ color = PIPE_COLOR_PURPLE
-
-obj/machinery/atmospherics/pipe/manifold4w
- icon = 'icons/obj/atmospherics/pipe_manifold.dmi'
-
+/obj/machinery/atmospherics/pipe/manifold4w
+ icon = 'icons/atmos/manifold.dmi'
+ icon_state = ""
name = "4-way pipe manifold"
desc = "A manifold composed of regular pipes"
- volume = 140
+ volume = ATMOS_DEFAULT_VOLUME_PIPE * 2
dir = SOUTH
initialize_directions = NORTH|SOUTH|EAST|WEST
@@ -592,43 +601,21 @@ obj/machinery/atmospherics/pipe/manifold4w
level = 1
layer = 2.4 //under wires with their 2.44
-obj/machinery/atmospherics/pipe/manifold4w/New()
+/obj/machinery/atmospherics/pipe/manifold4w/New()
..()
alpha = 255
+ icon = null
-obj/machinery/atmospherics/pipe/manifold4w/hide(var/i)
- if(level == 1 && istype(loc, /turf/simulated))
- invisibility = i ? 101 : 0
- update_icon()
-
-obj/machinery/atmospherics/pipe/manifold4w/pipeline_expansion()
+/obj/machinery/atmospherics/pipe/manifold4w/pipeline_expansion()
return list(node1, node2, node3, node4)
-obj/machinery/atmospherics/pipe/manifold4w/process()
+/obj/machinery/atmospherics/pipe/manifold4w/process()
if(!parent)
..()
else
. = PROCESS_KILL
-/*
- if(!node1)
- parent.mingle_with_turf(loc, 70)
- if(!nodealert)
- //world << "Missing node from [src] at [src.x],[src.y],[src.z]"
- nodealert = 1
- else if(!node2)
- parent.mingle_with_turf(loc, 70)
- if(!nodealert)
- //world << "Missing node from [src] at [src.x],[src.y],[src.z]"
- nodealert = 1
- else if(!node3)
- parent.mingle_with_turf(loc, 70)
- if(!nodealert)
- //world << "Missing node from [src] at [src.x],[src.y],[src.z]"
- nodealert = 1
- else if (nodealert)
- nodealert = 0
-*/
-obj/machinery/atmospherics/pipe/manifold4w/Del()
+
+/obj/machinery/atmospherics/pipe/manifold4w/Del()
if(node1)
node1.disconnect(src)
if(node2)
@@ -640,7 +627,7 @@ obj/machinery/atmospherics/pipe/manifold4w/Del()
..()
-obj/machinery/atmospherics/pipe/manifold4w/disconnect(obj/machinery/atmospherics/reference)
+/obj/machinery/atmospherics/pipe/manifold4w/disconnect(obj/machinery/atmospherics/reference)
if(reference == node1)
if(istype(node1, /obj/machinery/atmospherics/pipe))
del(parent)
@@ -665,37 +652,58 @@ obj/machinery/atmospherics/pipe/manifold4w/disconnect(obj/machinery/atmospherics
..()
-obj/machinery/atmospherics/pipe/manifold4w/update_icon()
- overlays.Cut()
- if(node1&&node2&&node3&&node4)
- switch(pipe_color)
- if ("red") color = COLOR_RED
- if ("blue") color = COLOR_BLUE
- if ("cyan") color = COLOR_CYAN
- if ("green") color = COLOR_GREEN
- if ("yellow") color = "#FFCC00"
- if ("purple") color = "#5C1EC0"
- if ("grey") color = null
- icon_state = "manifold4w[invisibility ? "-f" : "" ]"
+/obj/machinery/atmospherics/pipe/manifold4w/change_color(var/new_color)
+ ..()
+ //for updating connected atmos device pipes (i.e. vents, manifolds, etc)
+ if(node1)
+ node1.update_underlays()
+ if(node2)
+ node2.update_underlays()
+ if(node3)
+ node3.update_underlays()
+ if(node4)
+ node4.update_underlays()
+/obj/machinery/atmospherics/pipe/manifold4w/update_icon(var/safety = 0)
+ if(!check_icon_cache())
+ return
+
+ alpha = 255
+
+ if(!node1 && !node2 && !node3 && !node4)
+ var/turf/T = get_turf(src)
+ new /obj/item/pipe(loc, make_from=src)
+ for (var/obj/machinery/meter/meter in T)
+ if (meter.target == src)
+ new /obj/item/pipe_meter(T)
+ del(meter)
+ del(src)
else
- icon_state = "manifold4w_ex"
- var/icon/con = new/icon('icons/obj/atmospherics/pipe_manifold.dmi',"manifold4w_con") //Since 4-ways are supposed to be directionless, they need an overlay instead it seems.
+ overlays.Cut()
+ overlays += icon_manager.get_atmos_icon("manifold", , pipe_color, "4way")
+ overlays += icon_manager.get_atmos_icon("manifold", , , "clamps_4way")
+ underlays.Cut()
- if(node1)
- overlays += new/image(con,dir=1)
- if(node2)
- overlays += new/image(con,dir=2)
- if(node3)
- overlays += new/image(con,dir=4)
- if(node4)
- overlays += new/image(con,dir=8)
+ var/list/directions = list(NORTH, SOUTH, EAST, WEST)
- if(!node1 && !node2 && !node3 && !node4)
- del(src)
- return
+ directions -= add_underlay(node1)
+ directions -= add_underlay(node2)
+ directions -= add_underlay(node3)
+ directions -= add_underlay(node4)
-obj/machinery/atmospherics/pipe/manifold4w/initialize()
+ for(var/D in directions)
+ add_underlay(,D)
+
+/obj/machinery/atmospherics/pipe/manifold4w/update_underlays()
+ ..()
+ update_icon()
+
+/obj/machinery/atmospherics/pipe/manifold4w/hide(var/i)
+ if(level == 1 && istype(loc, /turf/simulated))
+ invisibility = i ? 101 : 0
+ update_icon()
+
+/obj/machinery/atmospherics/pipe/manifold4w/initialize()
for(var/obj/machinery/atmospherics/target in get_step(src,1))
if(target.initialize_directions & 2)
@@ -717,58 +725,65 @@ obj/machinery/atmospherics/pipe/manifold4w/initialize()
node4 = target
break
- var/turf/T = src.loc // hide if turf is not intact
- hide(T.intact)
- //update_icon()
+ if(!node1 && !node2 && !node3 && !node4)
+ del(src)
+ return
+
+ var/turf/T = get_turf(src)
+ if(istype(T))
+ hide(T.intact)
update_icon()
-
-obj/machinery/atmospherics/pipe/manifold4w/visible
+/obj/machinery/atmospherics/pipe/manifold4w/visible
+ icon_state = "map_4way"
level = 2
- icon_state = "manifold4w"
-obj/machinery/atmospherics/pipe/manifold4w/visible/supply
- name="Air supply pipe"
- color=COLOR_BLUE
+/obj/machinery/atmospherics/pipe/manifold4w/visible/scrubbers
+ name = "Scrubbers pipe"
+ color = PIPE_COLOR_RED
-obj/machinery/atmospherics/pipe/manifold4w/visible/scrubbers
- name="Scrubbers pipe"
- color=COLOR_RED
+/obj/machinery/atmospherics/pipe/manifold4w/visible/supply
+ name = "Air supply pipe"
+ color = PIPE_COLOR_BLUE
-obj/machinery/atmospherics/pipe/manifold4w/visible/yellow
- color="#FFCC00"
+/obj/machinery/atmospherics/pipe/manifold4w/visible/yellow
+ color = PIPE_COLOR_YELLOW
-obj/machinery/atmospherics/pipe/manifold4w/visible/cyan
- color=COLOR_CYAN
+/obj/machinery/atmospherics/pipe/manifold4w/visible/cyan
+ color = PIPE_COLOR_CYAN
-obj/machinery/atmospherics/pipe/manifold4w/visible/green
- color=COLOR_GREEN
+/obj/machinery/atmospherics/pipe/manifold4w/visible/green
+ color = PIPE_COLOR_GREEN
+/obj/machinery/atmospherics/pipe/manifold4w/visible/purple
+ color = PIPE_COLOR_PURPLE
-obj/machinery/atmospherics/pipe/manifold4w/hidden
+/obj/machinery/atmospherics/pipe/manifold4w/hidden
+ icon_state = "map_4way"
level = 1
- icon_state = "manifold4w-f"
- alpha = 192 //set for the benefit of mapping - this is reset to opaque when the pipe is spawned in game
+ alpha = 128 //set for the benefit of mapping - this is reset to opaque when the pipe is spawned in game
-obj/machinery/atmospherics/pipe/manifold4w/hidden/supply
- name="Air supply pipe"
- color=COLOR_BLUE
+/obj/machinery/atmospherics/pipe/manifold4w/hidden/scrubbers
+ name = "Scrubbers pipe"
+ color = PIPE_COLOR_RED
-obj/machinery/atmospherics/pipe/manifold4w/hidden/scrubbers
- name="Scrubbers pipe"
- color=COLOR_RED
+/obj/machinery/atmospherics/pipe/manifold4w/hidden/supply
+ name = "Air supply pipe"
+ color = PIPE_COLOR_BLUE
-obj/machinery/atmospherics/pipe/manifold4w/hidden/yellow
- color="#FFCC00"
+/obj/machinery/atmospherics/pipe/manifold4w/hidden/yellow
+ color = PIPE_COLOR_YELLOW
-obj/machinery/atmospherics/pipe/manifold4w/hidden/cyan
- color=COLOR_CYAN
+/obj/machinery/atmospherics/pipe/manifold4w/hidden/cyan
+ color = PIPE_COLOR_CYAN
-obj/machinery/atmospherics/pipe/manifold4w/hidden/green
- color=COLOR_GREEN
+/obj/machinery/atmospherics/pipe/manifold4w/hidden/green
+ color = PIPE_COLOR_GREEN
+/obj/machinery/atmospherics/pipe/manifold4w/hidden/purple
+ color = PIPE_COLOR_PURPLE
-obj/machinery/atmospherics/pipe/cap
+/obj/machinery/atmospherics/pipe/cap
name = "pipe endcap"
desc = "An endcap for pipes"
icon = 'icons/obj/pipes.dmi'
@@ -783,7 +798,7 @@ obj/machinery/atmospherics/pipe/cap
var/obj/machinery/atmospherics/node
-obj/machinery/atmospherics/pipe/cap/New()
+/obj/machinery/atmospherics/pipe/cap/New()
..()
switch(dir)
if(SOUTH)
@@ -795,26 +810,26 @@ obj/machinery/atmospherics/pipe/cap/New()
if(EAST)
initialize_directions = WEST
-obj/machinery/atmospherics/pipe/cap/hide(var/i)
+/obj/machinery/atmospherics/pipe/cap/hide(var/i)
if(level == 1 && istype(loc, /turf/simulated))
invisibility = i ? 101 : 0
update_icon()
-obj/machinery/atmospherics/pipe/cap/pipeline_expansion()
+/obj/machinery/atmospherics/pipe/cap/pipeline_expansion()
return list(node)
-obj/machinery/atmospherics/pipe/cap/process()
+/obj/machinery/atmospherics/pipe/cap/process()
if(!parent)
..()
else
. = PROCESS_KILL
-obj/machinery/atmospherics/pipe/cap/Del()
+/obj/machinery/atmospherics/pipe/cap/Del()
if(node)
node.disconnect(src)
..()
-obj/machinery/atmospherics/pipe/cap/disconnect(obj/machinery/atmospherics/reference)
+/obj/machinery/atmospherics/pipe/cap/disconnect(obj/machinery/atmospherics/reference)
if(reference == node)
if(istype(node, /obj/machinery/atmospherics/pipe))
del(parent)
@@ -824,13 +839,13 @@ obj/machinery/atmospherics/pipe/cap/disconnect(obj/machinery/atmospherics/refere
..()
-obj/machinery/atmospherics/pipe/cap/update_icon()
+/obj/machinery/atmospherics/pipe/cap/update_icon()
overlays = new()
icon_state = "cap[invisibility ? "-f" : ""]"
return
-obj/machinery/atmospherics/pipe/cap/initialize()
+/obj/machinery/atmospherics/pipe/cap/initialize()
for(var/obj/machinery/atmospherics/target in get_step(src, dir))
if(target.initialize_directions & get_dir(target,src))
node = target
@@ -838,151 +853,66 @@ obj/machinery/atmospherics/pipe/cap/initialize()
var/turf/T = src.loc // hide if turf is not intact
hide(T.intact)
- //update_icon()
update_icon()
-obj/machinery/atmospherics/pipe/cap/visible
+/obj/machinery/atmospherics/pipe/cap/visible
level = 2
icon_state = "cap"
-obj/machinery/atmospherics/pipe/cap/hidden
+/obj/machinery/atmospherics/pipe/cap/hidden
level = 1
icon_state = "cap-f"
-obj/machinery/atmospherics/pipe/tank
- icon = 'icons/obj/atmospherics/pipe_tank.dmi'
- icon_state = "intact"
+/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 = 2000 //in liters, 1 meters by 1 meters by 2 meters
+ 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 = 1
dir = SOUTH
initialize_directions = SOUTH
density = 1
var/obj/machinery/atmospherics/node1
-obj/machinery/atmospherics/pipe/tank/New()
+/obj/machinery/atmospherics/pipe/tank/New()
+ icon_state = "air"
initialize_directions = dir
..()
-obj/machinery/atmospherics/pipe/tank/process()
+/obj/machinery/atmospherics/pipe/tank/process()
if(!parent)
..()
else
. = PROCESS_KILL
-/* if(!node1)
- parent.mingle_with_turf(loc, 200)
- if(!nodealert)
- //world << "Missing node from [src] at [src.x],[src.y],[src.z]"
- nodealert = 1
- else if (nodealert)
- nodealert = 0
-*/
-obj/machinery/atmospherics/pipe/tank/carbon_dioxide
- name = "Pressure Tank (Carbon Dioxide)"
- New()
- air_temporary = new
- air_temporary.volume = volume
- air_temporary.temperature = T20C
-
- air_temporary.carbon_dioxide = (25*ONE_ATMOSPHERE)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature)
-
- ..()
-
-obj/machinery/atmospherics/pipe/tank/phoron
- icon = 'icons/obj/atmospherics/orange_pipe_tank.dmi'
- name = "Pressure Tank (Phoron)"
-
- New()
- air_temporary = new
- air_temporary.volume = volume
- air_temporary.temperature = T20C
-
- air_temporary.phoron = (25*ONE_ATMOSPHERE)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature)
-
- ..()
-
-obj/machinery/atmospherics/pipe/tank/oxygen_agent_b
- icon = 'icons/obj/atmospherics/red_orange_pipe_tank.dmi'
- name = "Pressure Tank (Oxygen + Phoron)"
-
- New()
- air_temporary = new
- air_temporary.volume = volume
- air_temporary.temperature = T0C
-
- var/datum/gas/oxygen_agent_b/trace_gas = new
- trace_gas.moles = (25*ONE_ATMOSPHERE)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature)
-
- air_temporary.trace_gases += trace_gas
-
- ..()
-
-obj/machinery/atmospherics/pipe/tank/oxygen
- icon = 'icons/obj/atmospherics/blue_pipe_tank.dmi'
- name = "Pressure Tank (Oxygen)"
-
- New()
- air_temporary = new
- air_temporary.volume = volume
- air_temporary.temperature = T20C
-
- air_temporary.oxygen = (25*ONE_ATMOSPHERE)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature)
-
- ..()
-
-obj/machinery/atmospherics/pipe/tank/nitrogen
- icon = 'icons/obj/atmospherics/red_pipe_tank.dmi'
- name = "Pressure Tank (Nitrogen)"
-
- New()
- air_temporary = new
- air_temporary.volume = volume
- air_temporary.temperature = T20C
-
- air_temporary.nitrogen = (25*ONE_ATMOSPHERE)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature)
-
- ..()
-
-obj/machinery/atmospherics/pipe/tank/air
- icon = 'icons/obj/atmospherics/red_pipe_tank.dmi'
- name = "Pressure Tank (Air)"
-
- New()
- air_temporary = new
- air_temporary.volume = volume
- air_temporary.temperature = T20C
-
- air_temporary.oxygen = (25*ONE_ATMOSPHERE*O2STANDARD)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature)
- air_temporary.nitrogen = (25*ONE_ATMOSPHERE*N2STANDARD)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature)
-
- ..()
-
-obj/machinery/atmospherics/pipe/tank/Del()
+/obj/machinery/atmospherics/pipe/tank/Del()
if(node1)
node1.disconnect(src)
..()
-obj/machinery/atmospherics/pipe/tank/pipeline_expansion()
+/obj/machinery/atmospherics/pipe/tank/pipeline_expansion()
return list(node1)
-obj/machinery/atmospherics/pipe/tank/update_icon()
- if(node1)
- icon_state = "intact"
+/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)
- dir = get_dir(src, node1)
-
- else
- icon_state = "exposed"
-
-obj/machinery/atmospherics/pipe/tank/initialize()
+/obj/machinery/atmospherics/pipe/tank/hide()
+ update_underlays()
+/obj/machinery/atmospherics/pipe/tank/initialize()
var/connect_direction = dir
for(var/obj/machinery/atmospherics/target in get_step(src,connect_direction))
@@ -990,47 +920,125 @@ obj/machinery/atmospherics/pipe/tank/initialize()
node1 = target
break
- update_icon()
+ update_underlays()
-obj/machinery/atmospherics/pipe/tank/disconnect(obj/machinery/atmospherics/reference)
+/obj/machinery/atmospherics/pipe/tank/disconnect(obj/machinery/atmospherics/reference)
if(reference == node1)
if(istype(node1, /obj/machinery/atmospherics/pipe))
del(parent)
node1 = null
- update_icon()
+ update_underlays()
return null
-obj/machinery/atmospherics/pipe/tank/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
- if (istype(W, /obj/item/device/analyzer) && get_dist(user, src) <= 1)
+/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
+
+ if(istype(W, /obj/item/device/analyzer) && in_range(user, src))
for (var/mob/O in viewers(user, null))
O << "\red [user] has used the analyzer on \icon[icon]"
var/pressure = parent.air.return_pressure()
- var/total_moles = parent.air.total_moles()
+ var/total_moles = parent.air.total_moles
user << "\blue Results of analysis of \icon[icon]"
if (total_moles>0)
- var/o2_concentration = parent.air.oxygen/total_moles
- var/n2_concentration = parent.air.nitrogen/total_moles
- var/co2_concentration = parent.air.carbon_dioxide/total_moles
- var/phoron_concentration = parent.air.phoron/total_moles
-
- var/unknown_concentration = 1-(o2_concentration+n2_concentration+co2_concentration+phoron_concentration)
-
user << "\blue Pressure: [round(pressure,0.1)] kPa"
- user << "\blue Nitrogen: [round(n2_concentration*100)]%"
- user << "\blue Oxygen: [round(o2_concentration*100)]%"
- user << "\blue CO2: [round(co2_concentration*100)]%"
- user << "\blue Phoron: [round(phoron_concentration*100)]%"
- if(unknown_concentration>0.01)
- user << "\red Unknown: [round(unknown_concentration*100)]%"
+ for(var/g in parent.air.gas)
+ user << "\blue [gas_data.name[g]]: [round((parent.air.gas[g] / total_moles) * 100)]%"
user << "\blue Temperature: [round(parent.air.temperature-T0C)]°C"
else
user << "\blue Tank is empty!"
-obj/machinery/atmospherics/pipe/vent
+/obj/machinery/atmospherics/pipe/tank/air
+ name = "Pressure Tank (Air)"
+ icon_state = "air_map"
+
+/obj/machinery/atmospherics/pipe/tank/air/New()
+ 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/New()
+ 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/New()
+ 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/New()
+ 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/phoron
+ name = "Pressure Tank (Phoron)"
+ icon_state = "phoron_map"
+
+/obj/machinery/atmospherics/pipe/tank/phoron/New()
+ air_temporary = new
+ air_temporary.volume = volume
+ air_temporary.temperature = T20C
+
+ air_temporary.adjust_gas("phoron", (start_pressure)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature))
+
+ ..()
+ icon_state = "phoron"
+
+/obj/machinery/atmospherics/pipe/tank/nitrous_oxide
+ name = "Pressure Tank (Nitrous Oxide)"
+ icon_state = "n2o_map"
+
+/obj/machinery/atmospherics/pipe/tank/nitrous_oxide/New()
+ 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"
@@ -1048,15 +1056,15 @@ obj/machinery/atmospherics/pipe/vent
var/obj/machinery/atmospherics/node1
-obj/machinery/atmospherics/pipe/vent/New()
+/obj/machinery/atmospherics/pipe/vent/New()
initialize_directions = dir
..()
-obj/machinery/atmospherics/pipe/vent/high_volume
+/obj/machinery/atmospherics/pipe/vent/high_volume
name = "Larger vent"
volume = 1000
-obj/machinery/atmospherics/pipe/vent/process()
+/obj/machinery/atmospherics/pipe/vent/process()
if(!parent)
if(build_killswitch <= 0)
. = PROCESS_KILL
@@ -1066,24 +1074,17 @@ obj/machinery/atmospherics/pipe/vent/process()
return
else
parent.mingle_with_turf(loc, volume)
-/*
- if(!node1)
- if(!nodealert)
- //world << "Missing node from [src] at [src.x],[src.y],[src.z]"
- nodealert = 1
- else if (nodealert)
- nodealert = 0
-*/
-obj/machinery/atmospherics/pipe/vent/Del()
+
+/obj/machinery/atmospherics/pipe/vent/Del()
if(node1)
node1.disconnect(src)
..()
-obj/machinery/atmospherics/pipe/vent/pipeline_expansion()
+/obj/machinery/atmospherics/pipe/vent/pipeline_expansion()
return list(node1)
-obj/machinery/atmospherics/pipe/vent/update_icon()
+/obj/machinery/atmospherics/pipe/vent/update_icon()
if(node1)
icon_state = "intact"
@@ -1092,7 +1093,7 @@ obj/machinery/atmospherics/pipe/vent/update_icon()
else
icon_state = "exposed"
-obj/machinery/atmospherics/pipe/vent/initialize()
+/obj/machinery/atmospherics/pipe/vent/initialize()
var/connect_direction = dir
for(var/obj/machinery/atmospherics/target in get_step(src,connect_direction))
@@ -1102,7 +1103,7 @@ obj/machinery/atmospherics/pipe/vent/initialize()
update_icon()
-obj/machinery/atmospherics/pipe/vent/disconnect(obj/machinery/atmospherics/reference)
+/obj/machinery/atmospherics/pipe/vent/disconnect(obj/machinery/atmospherics/reference)
if(reference == node1)
if(istype(node1, /obj/machinery/atmospherics/pipe))
del(parent)
@@ -1112,7 +1113,7 @@ obj/machinery/atmospherics/pipe/vent/disconnect(obj/machinery/atmospherics/refer
return null
-obj/machinery/atmospherics/pipe/vent/hide(var/i) //to make the little pipe section invisible, the icon changes.
+/obj/machinery/atmospherics/pipe/vent/hide(var/i) //to make the little pipe section invisible, the icon changes.
if(node1)
icon_state = "[i == 1 && istype(loc, /turf/simulated) ? "h" : "" ]intact"
dir = get_dir(src, node1)
diff --git a/code/WorkInProgress/Cael_Aislinn/Jungle/jungle_plants.dm b/code/WorkInProgress/Cael_Aislinn/Jungle/jungle_plants.dm
index 5a0f955df30..3f1a7c2f4bb 100644
--- a/code/WorkInProgress/Cael_Aislinn/Jungle/jungle_plants.dm
+++ b/code/WorkInProgress/Cael_Aislinn/Jungle/jungle_plants.dm
@@ -65,7 +65,6 @@ var/jungle_plants_init = 0
reagent_effects = shuffle(reagent_effects)
/obj/item/weapon/reagent_containers/food/snacks/grown/jungle_fruit
- seed = ""
name = "jungle fruit"
desc = "It smells weird and looks off."
icon = 'code/WorkInProgress/Cael_Aislinn/Jungle/jungle.dmi'
diff --git a/code/WorkInProgress/Cael_Aislinn/Rust/core_field.dm b/code/WorkInProgress/Cael_Aislinn/Rust/core_field.dm
index c31d6138dff..cd397e10958 100644
--- a/code/WorkInProgress/Cael_Aislinn/Rust/core_field.dm
+++ b/code/WorkInProgress/Cael_Aislinn/Rust/core_field.dm
@@ -140,20 +140,19 @@ Deuterium-tritium fusion: 4.5 x 10^7 K
//the amount of phoron pulled in each update is relative to the field strength, with 50T (max field strength) = 100% of area covered by the field
//at minimum strength, 0.25% of the field volume is pulled in per update (?)
//have a max of 1000 moles suspended
- if(held_phoron.phoron < transfer_ratio * 1000)
+ if(held_phoron.gas["phoron"] < transfer_ratio * 1000)
var/moles_covered = environment.return_pressure()*volume_covered/(environment.temperature * R_IDEAL_GAS_EQUATION)
//world << "\blue moles_covered: [moles_covered]"
//
var/datum/gas_mixture/gas_covered = environment.remove(moles_covered)
var/datum/gas_mixture/phoron_captured = new /datum/gas_mixture()
//
- phoron_captured.phoron = round(gas_covered.phoron * transfer_ratio)
+ phoron_captured.gas["phoron"] = round(gas_covered.gas["phoron"] * transfer_ratio)
//world << "\blue[phoron_captured.phoron] moles of phoron captured"
phoron_captured.temperature = gas_covered.temperature
phoron_captured.update_values()
//
- gas_covered.phoron -= phoron_captured.phoron
- gas_covered.update_values()
+ gas_covered.adjust_gas("phoron", -phoron_captured.gas["phoron"])
//
held_phoron.merge(phoron_captured)
//
@@ -171,7 +170,7 @@ Deuterium-tritium fusion: 4.5 x 10^7 K
//change held phoron temp according to energy levels
//SPECIFIC_HEAT_TOXIN
- if(mega_energy > 0 && held_phoron.phoron)
+ if(mega_energy > 0 && held_phoron.gas["phoron"])
var/heat_capacity = held_phoron.heat_capacity()//200 * number of phoron moles
if(heat_capacity > 0.0003) //formerly MINIMUM_HEAT_CAPACITY
held_phoron.temperature = (heat_capacity + mega_energy * 35000)/heat_capacity
@@ -179,7 +178,7 @@ Deuterium-tritium fusion: 4.5 x 10^7 K
//if there is too much phoron in the field, lose some
/*if( held_phoron.phoron > (MOLES_CELLSTANDARD * 7) * (50 / field_strength) )
LosePhoron()*/
- if(held_phoron.phoron > 1)
+ if(held_phoron.gas["phoron"] > 1)
//lose a random amount of phoron back into the air, increased by the field strength (want to switch this over to frequency eventually)
var/loss_ratio = rand() * (0.05 + (0.05 * 50 / field_strength))
//world << "lost [loss_ratio*100]% of held phoron"
@@ -187,16 +186,16 @@ Deuterium-tritium fusion: 4.5 x 10^7 K
var/datum/gas_mixture/phoron_lost = new
phoron_lost.temperature = held_phoron.temperature
//
- phoron_lost.phoron = held_phoron.phoron * loss_ratio
+ phoron_lost.gas["phoron"] = held_phoron.gas["phoron"] * loss_ratio
//phoron_lost.update_values()
- held_phoron.phoron -= held_phoron.phoron * loss_ratio
+ held_phoron.gas["phoron"] -= held_phoron.gas["phoron"] * loss_ratio
//held_phoron.update_values()
//
environment.merge(phoron_lost)
radiation += loss_ratio * mega_energy * 0.1
mega_energy -= loss_ratio * mega_energy * 0.1
else
- held_phoron.phoron = 0
+ held_phoron.gas["phoron"] = null
//held_phoron.update_values()
//handle some reactants formatting
diff --git a/code/WorkInProgress/Cael_Aislinn/ShieldGen/shield_gen_external.dm b/code/WorkInProgress/Cael_Aislinn/ShieldGen/shield_gen_external.dm
deleted file mode 100644
index dab223aeb64..00000000000
--- a/code/WorkInProgress/Cael_Aislinn/ShieldGen/shield_gen_external.dm
+++ /dev/null
@@ -1,44 +0,0 @@
-//---------- external shield generator
-//generates an energy field that loops around any built up area in space (is useless inside) halts movement and airflow, is blocked by walls, windows, airlocks etc
-
-/obj/machinery/shield_gen/external
- name = "hull shield generator"
-
-/obj/machinery/shield_gen/external/New()
- ..()
-
-/obj/machinery/shield_gen/external/get_shielded_turfs()
- var
- list
- open = list(get_turf(src))
- closed = list()
-
- while(open.len)
- for(var/turf/T in open)
- for(var/turf/O in orange(1, T))
- if(get_dist(O,src) > field_radius)
- continue
- var/add_this_turf = 0
- if(istype(O,/turf/space))
- for(var/turf/simulated/G in orange(1, O))
- add_this_turf = 1
- break
-
- //uncomment this for structures (but not lattices) to be surrounded by shield as well
- /*if(!add_this_turf)
- for(var/obj/structure/S in orange(1, O))
- if(!istype(S, /obj/structure/lattice))
- add_this_turf = 1
- break
- if(add_this_turf)
- for(var/obj/structure/S in O)
- if(!istype(S, /obj/structure/lattice))
- add_this_turf = 0
- break*/
-
- if(add_this_turf && !(O in open) && !(O in closed))
- open += O
- open -= T
- closed += T
-
- return closed
diff --git a/code/WorkInProgress/Chinsky/overmap/ships/computers/engine_control.dm b/code/WorkInProgress/Chinsky/overmap/ships/computers/engine_control.dm
index 07d6a4579be..41f2c4cdf9d 100644
--- a/code/WorkInProgress/Chinsky/overmap/ships/computers/engine_control.dm
+++ b/code/WorkInProgress/Chinsky/overmap/ships/computers/engine_control.dm
@@ -30,7 +30,7 @@
ui_interact(user)
-/obj/machinery/computer/engines/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null)
+/obj/machinery/computer/engines/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
if(!linked)
return
@@ -50,7 +50,7 @@
data["engines_info"] = enginfo
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, data)
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "engines_control.tmpl", "[linked.name] Engines Control", 380, 530)
ui.set_initial_data(data)
diff --git a/code/WorkInProgress/Chinsky/overmap/ships/computers/helm.dm b/code/WorkInProgress/Chinsky/overmap/ships/computers/helm.dm
index f81e569abcf..d78934f5749 100644
--- a/code/WorkInProgress/Chinsky/overmap/ships/computers/helm.dm
+++ b/code/WorkInProgress/Chinsky/overmap/ships/computers/helm.dm
@@ -70,7 +70,7 @@
ui_interact(user)
-/obj/machinery/computer/helm/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null)
+/obj/machinery/computer/helm/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
if(!linked)
return
@@ -101,7 +101,7 @@
data["locations"] = locations
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, data)
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "helm.tmpl", "[linked.name] Helm Control", 380, 530)
ui.set_initial_data(data)
diff --git a/code/WorkInProgress/Chinsky/overmap/ships/computers/shuttle.dm b/code/WorkInProgress/Chinsky/overmap/ships/computers/shuttle.dm
index c63ad2d763a..4ebc9469da3 100644
--- a/code/WorkInProgress/Chinsky/overmap/ships/computers/shuttle.dm
+++ b/code/WorkInProgress/Chinsky/overmap/ships/computers/shuttle.dm
@@ -44,7 +44,7 @@
var/datum/shuttle/ferry/shuttle = shuttle_controller.shuttles[shuttle_tag]
return shuttle && destination && get_dist(home, destination) <= shuttle.range
-/obj/machinery/computer/shuttle_control/explore/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null)
+/obj/machinery/computer/shuttle_control/explore/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
var/data[0]
var/datum/shuttle/ferry/shuttle = shuttle_controller.shuttles[shuttle_tag]
if (!istype(shuttle))
@@ -103,7 +103,7 @@
"can_force" = can_go && shuttle.can_force(),
)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, data)
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "shuttle_control_console_exploration.tmpl", "[shuttle_tag] Shuttle Control", 470, 310)
diff --git a/code/WorkInProgress/Ported/policetape.dm b/code/WorkInProgress/Ported/policetape.dm
index 12059c63edd..6b40b370ca7 100644
--- a/code/WorkInProgress/Ported/policetape.dm
+++ b/code/WorkInProgress/Ported/policetape.dm
@@ -97,8 +97,8 @@
//is_blocked_turf(var/turf/T)
usr << "\blue You finish placing the [src]." //Git Test
-/obj/item/taperoll/afterattack(var/atom/A, mob/user as mob)
- if (istype(A, /obj/machinery/door/airlock))
+/obj/item/taperoll/afterattack(var/atom/A, mob/user as mob, proximity)
+ if (proximity && istype(A, /obj/machinery/door/airlock))
var/turf/T = get_turf(A)
var/obj/item/tape/P = new tape_type(T.x,T.y,T.z)
P.loc = locate(T.x,T.y,T.z)
diff --git a/code/WorkInProgress/Sigyn/Department Sec/jobs.dm b/code/WorkInProgress/Sigyn/Department Sec/jobs.dm
index 66e14b040c3..7f98a7b6ec2 100644
--- a/code/WorkInProgress/Sigyn/Department Sec/jobs.dm
+++ b/code/WorkInProgress/Sigyn/Department Sec/jobs.dm
@@ -83,9 +83,9 @@ proc/assign_sec_to_department(var/mob/living/carbon/human/H)
else
H.equip_to_slot_or_del(new /obj/item/weapon/storage/box/survival(H.back), slot_in_backpack)
H.equip_to_slot_or_del(new /obj/item/weapon/handcuffs(H), slot_in_backpack)
- var/obj/item/weapon/implant/loyalty/L = new/obj/item/weapon/implant/loyalty(H)
- L.imp_in = H
- L.implanted = 1
+
+ implant_loyalty(H)
+
return 1
/obj/item/device/radio/headset/headset_sec/department/New()
diff --git a/code/WorkInProgress/Sigyn/Softcurity/jobs.dm b/code/WorkInProgress/Sigyn/Softcurity/jobs.dm
index 912118ae2fc..a47e4ea43e0 100644
--- a/code/WorkInProgress/Sigyn/Softcurity/jobs.dm
+++ b/code/WorkInProgress/Sigyn/Softcurity/jobs.dm
@@ -23,9 +23,9 @@
H.equip_to_slot_or_del(new /obj/item/weapon/gun/energy/taser(H), slot_s_store)
H.equip_to_slot_or_del(new /obj/item/weapon/storage/box/survival(H.back), slot_in_backpack)
H.equip_to_slot_or_del(new /obj/item/weapon/handcuffs(H), slot_in_backpack)
- var/obj/item/weapon/implant/loyalty/L = new/obj/item/weapon/implant/loyalty(H)
- L.imp_in = H
- L.implanted = 1
+
+ H.implant_loyalty(src) // Will not do so if config is set to disallow.
+
return 1
@@ -52,9 +52,9 @@
H.equip_to_slot_or_del(new /obj/item/device/flash(H), slot_l_store)
H.equip_to_slot_or_del(new /obj/item/weapon/storage/box/survival(H.back), slot_in_backpack)
H.equip_to_slot_or_del(new /obj/item/weapon/handcuffs(H), slot_in_backpack)
- var/obj/item/weapon/implant/loyalty/L = new/obj/item/weapon/implant/loyalty(H)
- L.imp_in = H
- L.implanted = 1
+
+ H.implant_loyalty(src) // // Will not do so if config is set to disallow.
+
return 1
@@ -89,9 +89,7 @@
H.equip_to_slot_or_del(new /obj/item/weapon/storage/box/evidence(H), slot_in_backpack)
H.equip_to_slot_or_del(new /obj/item/device/detective_scanner(H), slot_in_backpack)
- var/obj/item/weapon/implant/loyalty/L = new/obj/item/weapon/implant/loyalty(H)
- L.imp_in = H
- L.implanted = 1
+ H.implant_loyalty(src) // Will not do so if config is set to disallow.
return 1
@@ -118,9 +116,9 @@
H.equip_to_slot_or_del(new /obj/item/device/flash(H), slot_l_store)
H.equip_to_slot_or_del(new /obj/item/weapon/storage/box/survival(H.back), slot_in_backpack)
H.equip_to_slot_or_del(new /obj/item/weapon/handcuffs(H), slot_in_backpack)
- var/obj/item/weapon/implant/loyalty/L = new/obj/item/weapon/implant/loyalty(H)
- L.imp_in = H
- L.implanted = 1
+
+ H.implant_loyalty(src) // Will not do so if config is set to disallow.
+
return 1
/datum/job/hop
diff --git a/code/WorkInProgress/Sigyn/Softcurity/secure_closet.dm b/code/WorkInProgress/Sigyn/Softcurity/secure_closet.dm
index 88f06f98e7c..e74c6fce6c4 100644
--- a/code/WorkInProgress/Sigyn/Softcurity/secure_closet.dm
+++ b/code/WorkInProgress/Sigyn/Softcurity/secure_closet.dm
@@ -70,7 +70,7 @@
new /obj/item/weapon/storage/backpack/satchel_sec(src)
new /obj/item/weapon/cartridge/hos(src)
new /obj/item/device/radio/headset/heads/hos(src)
- new /obj/item/weapon/storage/lockbox/loyalty(src)
+ if (config.use_loyalty_implants) new /obj/item/weapon/storage/lockbox/loyalty(src)
new /obj/item/weapon/storage/flashbang_kit(src)
new /obj/item/weapon/storage/belt/security(src)
new /obj/item/device/flash(src)
diff --git a/code/WorkInProgress/computer3/laptop.dm b/code/WorkInProgress/computer3/laptop.dm
index 006d3a4db5e..52d996827b6 100644
--- a/code/WorkInProgress/computer3/laptop.dm
+++ b/code/WorkInProgress/computer3/laptop.dm
@@ -164,15 +164,15 @@
return
if(use_power && istype(battery) && battery.charge > 0)
if(use_power == 1)
- battery.use(idle_power_usage)
+ battery.use(idle_power_usage*CELLRATE) //idle and active_power_usage are in WATTS. battery.use() expects CHARGE.
else
- battery.use(active_power_usage)
+ battery.use(active_power_usage*CELLRATE)
return 1
return 0
use_power(var/amount, var/chan = -1)
if(battery && battery.charge > 0)
- battery.use(amount)
+ battery.use(amount*CELLRATE)
power_change()
if( !battery || battery.charge <= 0 )
diff --git a/code/WorkInProgress/kilakk/responseteam.dm b/code/WorkInProgress/kilakk/responseteam.dm
index 6faafad342f..8e0cdbc6389 100644
--- a/code/WorkInProgress/kilakk/responseteam.dm
+++ b/code/WorkInProgress/kilakk/responseteam.dm
@@ -231,9 +231,7 @@ var/global/admin_emergency_team = 0 // Used for admin-spawned response teams
// equip_to_slot_or_del(new /obj/item/weapon/storage/firstaid/regular(src), slot_in_backpack) // Regular medkit
// Loyalty implants
- var/obj/item/weapon/implant/loyalty/L = new/obj/item/weapon/implant/loyalty(src)
- L.imp_in = src
- L.implanted = 1
+ implant_loyalty(src)
// ID cards
var/obj/item/weapon/card/id/E = new(src)
diff --git a/code/ZAS/ConnectionGroup.dm b/code/ZAS/ConnectionGroup.dm
index 481492e7e29..7fab5e94adc 100644
--- a/code/ZAS/ConnectionGroup.dm
+++ b/code/ZAS/ConnectionGroup.dm
@@ -86,7 +86,8 @@ Class Procs:
/connection_edge/proc/tick()
/connection_edge/proc/flow(list/movable, differential, repelled)
- for(var/atom/movable/M in movable)
+ for(var/i = 1; i <= movable.len; i++)
+ var/atom/movable/M = movable[i]
//If they're already being tossed, don't do it again.
if(M.last_airflow > world.time - vsc.airflow_delay) continue
@@ -156,7 +157,7 @@ Class Procs:
return
//air_master.equalize(A, B)
- ShareRatio(A.air,B.air,coefficient)
+ A.air.share_ratio(B.air, coefficient)
air_master.mark_zone_update(A)
air_master.mark_zone_update(B)
//world << "equalized."
@@ -215,7 +216,7 @@ Class Procs:
return
//world << "[id]: Tick [air_master.current_cycle]: To [B]!"
//A.air.mimic(B, coefficient)
- ShareSpace(A.air,air,dbg_out)
+ A.air.share_space(air, dbg_out)
air_master.mark_zone_update(A)
var/differential = A.air.return_pressure() - air.return_pressure()
@@ -224,196 +225,6 @@ Class Procs:
var/list/attracted = A.movables()
flow(attracted, abs(differential), differential < 0)
-var/list/sharing_lookup_table = list(0.30, 0.40, 0.48, 0.54, 0.60, 0.66)
-
-proc/ShareRatio(datum/gas_mixture/A, datum/gas_mixture/B, connecting_tiles)
- //Shares a specific ratio of gas between mixtures using simple weighted averages.
- var
- //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
- ratio = sharing_lookup_table[6]
- //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
-
- size = max(1,A.group_multiplier)
- share_size = max(1,B.group_multiplier)
-
- full_oxy = A.oxygen * size
- full_nitro = A.nitrogen * size
- full_co2 = A.carbon_dioxide * size
- full_phoron = A.phoron * size
-
- full_heat_capacity = A.heat_capacity() * size
-
- s_full_oxy = B.oxygen * share_size
- s_full_nitro = B.nitrogen * share_size
- s_full_co2 = B.carbon_dioxide * share_size
- s_full_phoron = B.phoron * share_size
-
- s_full_heat_capacity = B.heat_capacity() * share_size
-
- oxy_avg = (full_oxy + s_full_oxy) / (size + share_size)
- nit_avg = (full_nitro + s_full_nitro) / (size + share_size)
- co2_avg = (full_co2 + s_full_co2) / (size + share_size)
- phoron_avg = (full_phoron + s_full_phoron) / (size + share_size)
-
- temp_avg = (A.temperature * full_heat_capacity + B.temperature * s_full_heat_capacity) / (full_heat_capacity + s_full_heat_capacity)
-
- //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
- if(sharing_lookup_table.len >= connecting_tiles) //6 or more interconnecting tiles will max at 42% of air moved per tick.
- ratio = sharing_lookup_table[connecting_tiles]
- //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
-
- A.oxygen = max(0, (A.oxygen - oxy_avg) * (1-ratio) + oxy_avg )
- A.nitrogen = max(0, (A.nitrogen - nit_avg) * (1-ratio) + nit_avg )
- A.carbon_dioxide = max(0, (A.carbon_dioxide - co2_avg) * (1-ratio) + co2_avg )
- A.phoron = max(0, (A.phoron - phoron_avg) * (1-ratio) + phoron_avg )
-
- A.temperature = max(0, (A.temperature - temp_avg) * (1-ratio) + temp_avg )
-
- B.oxygen = max(0, (B.oxygen - oxy_avg) * (1-ratio) + oxy_avg )
- B.nitrogen = max(0, (B.nitrogen - nit_avg) * (1-ratio) + nit_avg )
- B.carbon_dioxide = max(0, (B.carbon_dioxide - co2_avg) * (1-ratio) + co2_avg )
- B.phoron = max(0, (B.phoron - phoron_avg) * (1-ratio) + phoron_avg )
-
- B.temperature = max(0, (B.temperature - temp_avg) * (1-ratio) + temp_avg )
-
- for(var/datum/gas/G in A.trace_gases)
- var/datum/gas/H = locate(G.type) in B.trace_gases
- if(H)
- var/G_avg = (G.moles*size + H.moles*share_size) / (size+share_size)
- G.moles = (G.moles - G_avg) * (1-ratio) + G_avg
-
- H.moles = (H.moles - G_avg) * (1-ratio) + G_avg
- else
- H = new G.type
- B.trace_gases += H
- var/G_avg = (G.moles*size) / (size+share_size)
- G.moles = (G.moles - G_avg) * (1-ratio) + G_avg
- H.moles = (H.moles - G_avg) * (1-ratio) + G_avg
-
- for(var/datum/gas/G in B.trace_gases)
- var/datum/gas/H = locate(G.type) in A.trace_gases
- if(!H)
- H = new G.type
- A.trace_gases += H
- var/G_avg = (G.moles*size) / (size+share_size)
- G.moles = (G.moles - G_avg) * (1-ratio) + G_avg
- H.moles = (H.moles - G_avg) * (1-ratio) + G_avg
-
- A.update_values()
- B.update_values()
-
- if(A.compare(B)) return 1
- else return 0
-
-proc/ShareSpace(datum/gas_mixture/A, list/unsimulated_tiles, dbg_output)
- //A modified version of ShareRatio for spacing gas at the same rate as if it were going into a large airless room.
- if(!unsimulated_tiles)
- return 0
-
- var
- unsim_oxygen = 0
- unsim_nitrogen = 0
- unsim_co2 = 0
- unsim_phoron = 0
- unsim_heat_capacity = 0
- unsim_temperature = 0
-
- size = max(1,A.group_multiplier)
-
- var/tileslen
- var/share_size
-
- if(istype(unsimulated_tiles, /datum/gas_mixture))
- var/datum/gas_mixture/avg_unsim = unsimulated_tiles
- unsim_oxygen = avg_unsim.oxygen
- unsim_co2 = avg_unsim.carbon_dioxide
- unsim_nitrogen = avg_unsim.nitrogen
- unsim_phoron = avg_unsim.phoron
- unsim_temperature = avg_unsim.temperature
- share_size = max(1, max(size + 3, 1) + avg_unsim.group_multiplier)
- tileslen = avg_unsim.group_multiplier
-
- if(dbg_output)
- world << "O2: [unsim_oxygen] N2: [unsim_nitrogen] Size: [share_size] Tiles: [tileslen]"
-
- else if(istype(unsimulated_tiles, /list))
- if(!unsimulated_tiles.len)
- return 0
- // We use the same size for the potentially single space tile
- // as we use for the entire room. Why is this?
- // Short answer: We do not want larger rooms to depressurize more
- // slowly than small rooms, preserving our good old "hollywood-style"
- // oh-shit effect when large rooms get breached, but still having small
- // rooms remain pressurized for long enough to make escape possible.
- share_size = max(1, max(size + 3, 1) + unsimulated_tiles.len)
- var/correction_ratio = share_size / unsimulated_tiles.len
-
- for(var/turf/T in unsimulated_tiles)
- unsim_oxygen += T.oxygen
- unsim_co2 += T.carbon_dioxide
- unsim_nitrogen += T.nitrogen
- unsim_phoron += T.phoron
- unsim_temperature += T.temperature/unsimulated_tiles.len
-
- //These values require adjustment in order to properly represent a room of the specified size.
- unsim_oxygen *= correction_ratio
- unsim_co2 *= correction_ratio
- unsim_nitrogen *= correction_ratio
- unsim_phoron *= correction_ratio
- tileslen = unsimulated_tiles.len
-
- else //invalid input type
- return 0
-
- unsim_heat_capacity = HEAT_CAPACITY_CALCULATION(unsim_oxygen, unsim_co2, unsim_nitrogen, unsim_phoron)
-
- var
- ratio = sharing_lookup_table[6]
-
- old_pressure = A.return_pressure()
-
- full_oxy = A.oxygen * size
- full_nitro = A.nitrogen * size
- full_co2 = A.carbon_dioxide * size
- full_phoron = A.phoron * size
-
- full_heat_capacity = A.heat_capacity() * size
-
- oxy_avg = (full_oxy + unsim_oxygen*share_size) / (size + share_size)
- nit_avg = (full_nitro + unsim_nitrogen*share_size) / (size + share_size)
- co2_avg = (full_co2 + unsim_co2*share_size) / (size + share_size)
- phoron_avg = (full_phoron + unsim_phoron*share_size) / (size + share_size)
-
- temp_avg = 0
-
- if((full_heat_capacity + unsim_heat_capacity) > 0)
- temp_avg = (A.temperature * full_heat_capacity + unsim_temperature * unsim_heat_capacity) / (full_heat_capacity + unsim_heat_capacity)
-
- if(sharing_lookup_table.len >= tileslen) //6 or more interconnecting tiles will max at 42% of air moved per tick.
- ratio = sharing_lookup_table[tileslen]
-
- if(dbg_output)
- world << "Ratio: [ratio]"
- world << "Avg O2: [oxy_avg] N2: [nit_avg]"
-
- A.oxygen = max(0, (A.oxygen - oxy_avg) * (1 - ratio) + oxy_avg )
- A.nitrogen = max(0, (A.nitrogen - nit_avg) * (1 - ratio) + nit_avg )
- A.carbon_dioxide = max(0, (A.carbon_dioxide - co2_avg) * (1 - ratio) + co2_avg )
- A.phoron = max(0, (A.phoron - phoron_avg) * (1 - ratio) + phoron_avg )
-
- A.temperature = max(TCMB, (A.temperature - temp_avg) * (1 - ratio) + temp_avg )
-
- for(var/datum/gas/G in A.trace_gases)
- var/G_avg = (G.moles * size) / (size + share_size)
- G.moles = (G.moles - G_avg) * (1 - ratio) + G_avg
-
- A.update_values()
-
- if(dbg_output) world << "Result: [abs(old_pressure - A.return_pressure())] kPa"
-
- return abs(old_pressure - A.return_pressure())
-
-
proc/ShareHeat(datum/gas_mixture/A, datum/gas_mixture/B, connecting_tiles)
//This implements a simplistic version of the Stefan-Boltzmann law.
var/energy_delta = ((A.temperature - B.temperature) ** 4) * 5.6704e-8 * connecting_tiles * 2.5
diff --git a/code/ZAS/Controller.dm b/code/ZAS/Controller.dm
index cef6b068a84..756887cc09c 100644
--- a/code/ZAS/Controller.dm
+++ b/code/ZAS/Controller.dm
@@ -73,6 +73,7 @@ Class Procs:
//Geometry updates lists
/datum/controller/air_system/var/list/tiles_to_update = list()
/datum/controller/air_system/var/list/zones_to_update = list()
+/datum/controller/air_system/var/list/active_fire_zones = list()
/datum/controller/air_system/var/list/active_hotspots = list()
/datum/controller/air_system/var/active_zones = 0
@@ -171,9 +172,16 @@ Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_coun
for(var/connection_edge/edge in edges)
edge.tick()
- //Process fires.
+ //Process fire zones.
if(.)
- tick_progress = "processing fire"
+ tick_progress = "processing fire zones"
+
+ for(var/zone/Z in active_fire_zones)
+ Z.process_fire()
+
+ //Process hotspots.
+ if(.)
+ tick_progress = "processing hotspots"
for(var/obj/fire/fire in active_hotspots)
fire.process()
diff --git a/code/ZAS/Diagnostic.dm b/code/ZAS/Diagnostic.dm
index 31deb69bc52..1d4cc1a3883 100644
--- a/code/ZAS/Diagnostic.dm
+++ b/code/ZAS/Diagnostic.dm
@@ -19,7 +19,8 @@ client/proc/Zone_Info(turf/T as null|turf)
mob << "No zone here."
var/datum/gas_mixture/mix = T.return_air()
mob << "[mix.return_pressure()] kPa [mix.temperature]C"
- mob << "O2: [mix.oxygen] N2: [mix.nitrogen] CO2: [mix.carbon_dioxide] TX: [mix.phoron]"
+ for(var/g in mix.gas)
+ mob << "[g]: [mix.gas[g]]\n"
else
if(zone_debug_images)
for(var/zone in zone_debug_images)
diff --git a/code/ZAS/Fire.dm b/code/ZAS/Fire.dm
index 2d181dc6009..24aa1d2a514 100644
--- a/code/ZAS/Fire.dm
+++ b/code/ZAS/Fire.dm
@@ -10,6 +10,7 @@ Attach to transfer valve and open. BOOM.
*/
+/turf/var/obj/fire/fire = null
//Some legacy definitions so fires can be started.
atom/proc/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume)
@@ -34,12 +35,54 @@ turf/simulated/hotspot_expose(exposed_temperature, exposed_volume, soh)
if(air_contents.check_combustability(liquid))
igniting = 1
- if(! (locate(/obj/fire) in src))
-
- new /obj/fire(src,1000)
-
+ create_fire(1000)
return igniting
+/zone/proc/process_fire()
+ if(!air.check_combustability())
+ for(var/turf/simulated/T in fire_tiles)
+ if(istype(T.fire))
+ T.fire.RemoveFire()
+ T.fire = null
+ fire_tiles.Cut()
+
+ if(!fire_tiles.len)
+ air_master.active_fire_zones.Remove(src)
+ return
+
+ var/datum/gas_mixture/burn_gas = air.remove_ratio(vsc.fire_consuption_rate, fire_tiles.len)
+ var/gm = burn_gas.group_multiplier
+
+ burn_gas.group_multiplier = 1
+ burn_gas.zburn(force_burn = 1, no_check = 1)
+ burn_gas.group_multiplier = gm
+
+ air.merge(burn_gas)
+
+ var/firelevel = air.calculate_firelevel()
+
+ for(var/turf/T in fire_tiles)
+ if(T.fire)
+ T.fire.firelevel = firelevel
+ else
+ fire_tiles -= T
+
+/turf/proc/create_fire(fl)
+ return 0
+
+/turf/simulated/create_fire(fl)
+ if(fire)
+ fire.firelevel = max(fl, fire.firelevel)
+ return 1
+
+ if(!zone)
+ return 1
+
+ fire = new(src, fl)
+ zone.fire_tiles |= src
+ air_master.active_fire_zones |= zone
+ return 0
+
/obj/fire
//Icon for fire on turfs.
@@ -58,41 +101,14 @@ turf/simulated/hotspot_expose(exposed_temperature, exposed_volume, soh)
/obj/fire/process()
. = 1
- //get location and check if it is in a proper ZAS zone
- var/turf/simulated/S = loc
-
- if(!istype(S))
- del src
- return
-
- if(!S.zone)
- del src
- return
-
- var/datum/gas_mixture/air_contents = S.return_air()
- //get liquid fuels on the ground.
- var/obj/effect/decal/cleanable/liquid_fuel/liquid = locate() in S
- //and the volatile stuff from the air
- var/datum/gas/volatile_fuel/fuel = locate() in air_contents.trace_gases
-
- //since the air is processed in fractions, we need to make sure not to have any minuscle residue or
- //the amount of moles might get to low for some functions to catch them and thus result in wonky behaviour
- if(air_contents.oxygen < 0.1)
- air_contents.oxygen = 0
- if(air_contents.phoron < 0.1)
- air_contents.phoron = 0
- if(fuel)
- if(fuel.moles < 0.1)
- air_contents.trace_gases.Remove(fuel)
-
- //check if there is something to combust
- if(!air_contents.check_combustability(liquid))
- //del src
+ var/turf/simulated/my_tile = loc
+ if(!istype(my_tile) || !my_tile.zone)
+ if(my_tile.fire == src)
+ my_tile.fire = null
RemoveFire()
- return
+ return 1
- //get a firelevel and set the icon
- firelevel = air_contents.calculate_firelevel(liquid)
+ var/datum/gas_mixture/air_contents = my_tile.return_air()
if(firelevel > 6)
icon_state = "3"
@@ -106,21 +122,26 @@ turf/simulated/hotspot_expose(exposed_temperature, exposed_volume, soh)
//im not sure how to implement a version that works for every creature so for now monkeys are firesafe
for(var/mob/living/carbon/human/M in loc)
- M.FireBurn(firelevel, air_contents.temperature, air_contents.return_pressure() ) //Burn the humans!
+ M.FireBurn(firelevel, air_contents.temperature, air_contents.return_pressure()) //Burn the humans!
- loc.fire_act(air_contents, air_contents.temperature, air_contents.return_volume())
+ loc.fire_act(air_contents, air_contents.temperature, air_contents.volume)
for(var/atom/A in loc)
- A.fire_act(air_contents, air_contents.temperature, air_contents.return_volume())
+ A.fire_act(air_contents, air_contents.temperature, air_contents.volume)
+
//spread
for(var/direction in cardinal)
- var/turf/simulated/enemy_tile = get_step(S, direction)
+ var/turf/simulated/enemy_tile = get_step(my_tile, direction)
if(istype(enemy_tile))
- if(S.open_directions & direction) //Grab all valid bordering tiles
- var/datum/gas_mixture/acs = enemy_tile.return_air()
- var/obj/effect/decal/cleanable/liquid_fuel/liq = locate() in enemy_tile
- if(!acs) continue
- if(!acs.check_combustability(liq)) continue
+ if(my_tile.open_directions & direction) //Grab all valid bordering tiles
+ if(!enemy_tile.zone || enemy_tile.fire)
+ continue
+
+ if(!enemy_tile.zone.fire_tiles.len)
+ var/datum/gas_mixture/acs = enemy_tile.return_air()
+ if(!acs || !acs.check_combustability())
+ continue
+
//If extinguisher mist passed over the turf it's trying to spread to, don't spread and
//reduce firelevel.
if(enemy_tile.fire_protection > world.time-30)
@@ -128,26 +149,11 @@ turf/simulated/hotspot_expose(exposed_temperature, exposed_volume, soh)
continue
//Spread the fire.
- if(!(locate(/obj/fire) in enemy_tile))
- if( prob( 50 + 50 * (firelevel/vsc.fire_firelevel_multiplier) ) && S.CanPass(null, enemy_tile, 0,0) && enemy_tile.CanPass(null, S, 0,0))
- new/obj/fire(enemy_tile,firelevel)
+ if(prob( 50 + 50 * (firelevel/vsc.fire_firelevel_multiplier) ) && my_tile.CanPass(null, enemy_tile, 0,0) && enemy_tile.CanPass(null, my_tile, 0,0))
+ enemy_tile.create_fire(firelevel)
else
- enemy_tile.adjacent_fire_act(loc, air_contents, air_contents.temperature, air_contents.return_volume())
-
- //seperate part of the present gas
- //this is done to prevent the fire burning all gases in a single pass
- var/datum/gas_mixture/flow = air_contents.remove_ratio(vsc.fire_consuption_rate)
-///////////////////////////////// FLOW HAS BEEN CREATED /// DONT DELETE THE FIRE UNTIL IT IS MERGED BACK OR YOU WILL DELETE AIR ///////////////////////////////////////////////
-
- if(flow)
- //burn baby burn!
- flow.zburn(liquid,1)
- //merge the air back
- S.assume_air(flow)
-
-///////////////////////////////// FLOW HAS BEEN REMERGED /// feel free to delete the fire again from here on //////////////////////////////////////////////////////////////////
-
+ enemy_tile.adjacent_fire_act(loc, air_contents, air_contents.temperature, air_contents.volume)
/obj/fire/New(newLoc,fl)
..()
@@ -171,31 +177,29 @@ turf/simulated/hotspot_expose(exposed_temperature, exposed_volume, soh)
..()
/obj/fire/proc/RemoveFire()
- if (istype(loc, /turf/simulated))
+ if (istype(loc, /turf))
SetLuminosity(0)
loc = null
air_master.active_hotspots.Remove(src)
-
turf/simulated/var/fire_protection = 0 //Protects newly extinguished tiles from being overrun again.
turf/proc/apply_fire_protection()
turf/simulated/apply_fire_protection()
fire_protection = world.time
-datum/gas_mixture/proc/zburn(obj/effect/decal/cleanable/liquid_fuel/liquid, force_burn)
- var/value = 0
-
- if((temperature > PHORON_MINIMUM_BURN_TEMPERATURE || force_burn) && check_recombustability(liquid))
+datum/gas_mixture/proc/zburn(obj/effect/decal/cleanable/liquid_fuel/liquid, force_burn, no_check = 0)
+ . = 0
+ if((temperature > PHORON_MINIMUM_BURN_TEMPERATURE || force_burn) && (no_check ||check_recombustability(liquid)))
var/total_fuel = 0
- var/datum/gas/volatile_fuel/fuel = locate() in trace_gases
+ var/total_oxidizers = 0
- total_fuel += phoron
-
- if(fuel)
- //Volatile Fuel
- total_fuel += fuel.moles
+ for(var/g in gas)
+ if(gas_data.flags[g] & XGM_GAS_FUEL)
+ total_fuel += gas[g]
+ if(gas_data.flags[g] & XGM_GAS_OXIDIZER)
+ total_oxidizers += gas[g]
if(liquid)
//Liquid Fuel
@@ -208,36 +212,29 @@ datum/gas_mixture/proc/zburn(obj/effect/decal/cleanable/liquid_fuel/liquid, forc
return 0
//Calculate the firelevel.
- var/firelevel = calculate_firelevel(liquid)
+ var/firelevel = calculate_firelevel(liquid, total_fuel, total_oxidizers, force = 1)
//get the current inner energy of the gas mix
//this must be taken here to prevent the addition or deletion of energy by a changing heat capacity
var/starting_energy = temperature * heat_capacity()
//determine the amount of oxygen used
- var/total_oxygen = min(oxygen, 2 * total_fuel)
+ var/used_oxidizers = min(total_oxidizers, total_fuel / 2)
//determine the amount of fuel actually used
- var/used_fuel_ratio = min(oxygen / 2 , total_fuel) / total_fuel
+ var/used_fuel_ratio = min(2 * total_oxidizers , total_fuel) / total_fuel
total_fuel = total_fuel * used_fuel_ratio
- var/total_reactants = total_fuel + total_oxygen
+ var/total_reactants = total_fuel + used_oxidizers
//determine the amount of reactants actually reacting
- var/used_reactants_ratio = min( max(total_reactants * firelevel / vsc.fire_firelevel_multiplier, 0.2), total_reactants) / total_reactants
+ var/used_reactants_ratio = min(max(total_reactants * firelevel / vsc.fire_firelevel_multiplier, 0.2), total_reactants) / total_reactants
//remove and add gasses as calculated
- oxygen -= min(oxygen, total_oxygen * used_reactants_ratio )
+ remove_by_flag(XGM_GAS_OXIDIZER, used_oxidizers * used_reactants_ratio)
+ remove_by_flag(XGM_GAS_FUEL, total_fuel * used_reactants_ratio)
- phoron -= min(phoron, (phoron * used_fuel_ratio * used_reactants_ratio ) * 3)
- if(phoron < 0)
- phoron = 0
-
- carbon_dioxide += max(2 * total_fuel, 0)
-
- if(fuel)
- fuel.moles -= (fuel.moles * used_fuel_ratio * used_reactants_ratio) * 5 //Fuel burns 5 times as quick
- if(fuel.moles <= 0) del fuel
+ adjust_gas("carbon_dioxide", max(total_fuel*used_reactants_ratio, 0))
if(liquid)
liquid.amount -= (liquid.amount * used_fuel_ratio * used_reactants_ratio) * 5 // liquid fuel burns 5 times as quick
@@ -248,64 +245,67 @@ datum/gas_mixture/proc/zburn(obj/effect/decal/cleanable/liquid_fuel/liquid, forc
temperature = (starting_energy + vsc.fire_fuel_energy_release * total_fuel) / heat_capacity()
update_values()
- value = total_reactants * used_reactants_ratio
- return value
+ . = total_reactants * used_reactants_ratio
datum/gas_mixture/proc/check_recombustability(obj/effect/decal/cleanable/liquid_fuel/liquid)
- //this is a copy proc to continue a fire after its been started.
+ . = 0
+ for(var/g in gas)
+ if(gas_data.flags[g] & XGM_GAS_OXIDIZER && gas[g] >= 0.1)
+ . = 1
+ break
- var/datum/gas/volatile_fuel/fuel = locate() in trace_gases
+ if(!.)
+ return 0
- if(oxygen && (phoron || fuel || liquid))
- if(liquid)
- return 1
- if(phoron >= 0.1)
- return 1
- if(fuel && fuel.moles >= 0.1)
- return 1
+ if(liquid)
+ return 1
- return 0
+ . = 0
+ for(var/g in gas)
+ if(gas_data.flags[g] & XGM_GAS_FUEL && gas[g] >= 0.1)
+ . = 1
+ break
datum/gas_mixture/proc/check_combustability(obj/effect/decal/cleanable/liquid_fuel/liquid)
- //this check comes up very often and is thus centralized here to ease adding stuff
+ . = 0
+ for(var/g in gas)
+ if(gas_data.flags[g] & XGM_GAS_OXIDIZER && QUANTIZE(gas[g] * vsc.fire_consuption_rate) >= 0.1)
+ . = 1
+ break
- var/datum/gas/volatile_fuel/fuel = locate() in trace_gases
+ if(!.)
+ return 0
- if(oxygen && (phoron || fuel || liquid))
- if(liquid)
- return 1
- if(QUANTIZE(phoron * vsc.fire_consuption_rate) >= 0.1)
- return 1
- if(fuel && QUANTIZE(fuel.moles * vsc.fire_consuption_rate) >= 0.1)
- return 1
+ if(liquid)
+ return 1
- return 0
+ . = 0
+ for(var/g in gas)
+ if(gas_data.flags[g] & XGM_GAS_FUEL && QUANTIZE(gas[g] * vsc.fire_consuption_rate) >= 0.1)
+ . = 1
+ break
-datum/gas_mixture/proc/calculate_firelevel(obj/effect/decal/cleanable/liquid_fuel/liquid)
+datum/gas_mixture/proc/calculate_firelevel(obj/effect/decal/cleanable/liquid_fuel/liquid, total_fuel = null, total_oxidizers = null, force = 0)
//Calculates the firelevel based on one equation instead of having to do this multiple times in different areas.
-
- var/datum/gas/volatile_fuel/fuel = locate() in trace_gases
- var/total_fuel = 0
var/firelevel = 0
- if(check_recombustability(liquid))
+ if(force || check_recombustability(liquid))
+ if(isnull(total_fuel))
+ for(var/g in gas)
+ if(gas_data.flags[g] & XGM_GAS_FUEL)
+ total_fuel += gas[g]
+ if(gas_data.flags[g] & XGM_GAS_OXIDIZER)
+ total_oxidizers += gas[g]
+ if(liquid)
+ total_fuel += liquid.amount
- total_fuel += phoron
-
- if(liquid)
- total_fuel += liquid.amount
-
- if(fuel)
- total_fuel += fuel.moles
-
- var/total_combustables = (total_fuel + oxygen)
-
- if(total_fuel > 0 && oxygen > 0)
+ var/total_combustables = (total_fuel + total_oxidizers)
+ if(total_combustables > 0)
//slows down the burning when the concentration of the reactants is low
- var/dampening_multiplier = total_combustables / (total_combustables + nitrogen + carbon_dioxide)
+ var/dampening_multiplier = total_combustables / total_moles
//calculates how close the mixture of the reactants is to the optimum
- var/mix_multiplier = 1 / (1 + (5 * ((oxygen / total_combustables) ** 2)))
+ var/mix_multiplier = 1 / (1 + (5 * ((total_oxidizers / total_combustables) ** 2)))
//toss everything together
firelevel = vsc.fire_firelevel_multiplier * mix_multiplier * dampening_multiplier
diff --git a/code/ZAS/Gas.dm b/code/ZAS/Gas.dm
new file mode 100644
index 00000000000..0f3faa09b49
--- /dev/null
+++ b/code/ZAS/Gas.dm
@@ -0,0 +1,56 @@
+/xgm_gas/oxygen
+ id = "oxygen"
+ name = "Oxygen"
+ specific_heat = 20 // J/(mol*K)
+ molar_mass = 0.032 // kg/mol
+
+ flags = XGM_GAS_OXIDIZER
+
+/xgm_gas/nitrogen
+ id = "nitrogen"
+ name = "Nitrogen"
+ specific_heat = 20 // J/(mol*K)
+ molar_mass = 0.028 // kg/mol
+
+/xgm_gas/carbon_dioxide
+ id = "carbon_dioxide"
+ name = "Carbon Dioxide"
+ specific_heat = 30 // J/(mol*K)
+ molar_mass = 0.044 // kg/mol
+
+/xgm_gas/phoron
+ id = "phoron"
+ name = "Phoron"
+ specific_heat = 200 // J/(mol*K)
+
+ //Hypothetical group 14 (same as carbon), period 8 element.
+ //Using multiplicity rule, it's atomic number is 162
+ //and following a N/Z ratio of 1.5, the molar mass of a monatomic gas is:
+ molar_mass = 0.405 // kg/mol
+
+ tile_overlay = "phoron"
+ overlay_limit = 0.7
+ flags = XGM_GAS_FUEL | XGM_GAS_CONTAMINANT
+
+/xgm_gas/volatile_fuel
+ id = "volatile_fuel"
+ name = "Volatile Fuel"
+ specific_heat = 253 // J/(mol*K) C8H18 gasoline. Isobaric, but good enough.
+ molar_mass = 0.114 // kg/mol. same.
+
+ flags = XGM_GAS_FUEL
+
+/xgm_gas/sleeping_agent
+ id = "sleeping_agent"
+ name = "Sleeping Agent"
+ specific_heat = 40 // J/(mol*K)
+ molar_mass = 0.044 // kg/mol. N2O
+
+ tile_overlay = "sleeping_agent"
+ overlay_limit = 1
+
+/xgm_gas/oxygen_agent_b
+ id = "oxygen_agent_b"
+ name = "Oxygen Agent-B" //what is this?
+ specific_heat = 300 // J/(mol*K)
+ molar_mass = 0.032 // kg/mol
diff --git a/code/ZAS/Phoron.dm b/code/ZAS/Phoron.dm
index f96e47c188d..98ab1278257 100644
--- a/code/ZAS/Phoron.dm
+++ b/code/ZAS/Phoron.dm
@@ -111,7 +111,7 @@ obj/var/contaminated = 0
if(vsc.plc.GENETIC_CORRUPTION)
if(rand(1,10000) < vsc.plc.GENETIC_CORRUPTION)
randmutb(src)
- src << "\red High levels of phoron cause you to spontaneously mutate."
+ src << "\red High levels of toxins cause you to spontaneously mutate."
domutcheck(src,null)
@@ -155,10 +155,11 @@ obj/var/contaminated = 0
turf/Entered(obj/item/I)
. = ..()
//Items that are in phoron, but not on a mob, can still be contaminated.
- if(istype(I) && vsc.plc.CLOTH_CONTAMINATION)
+ if(istype(I) && vsc.plc.CLOTH_CONTAMINATION && I.can_contaminate())
var/datum/gas_mixture/env = return_air(1)
if(!env)
return
- if(env.phoron > MOLES_PHORON_VISIBLE + 1)
- if(I.can_contaminate())
- I.contaminate()
\ No newline at end of file
+ for(var/g in env.gas)
+ if(gas_data.flags[g] & XGM_GAS_CONTAMINANT && env.gas[g] > gas_data.overlay_limit[g] + 1)
+ I.contaminate()
+ break
diff --git a/code/ZAS/Turf.dm b/code/ZAS/Turf.dm
index 35dc8f684eb..e3a28fe3d47 100644
--- a/code/ZAS/Turf.dm
+++ b/code/ZAS/Turf.dm
@@ -1,17 +1,14 @@
/turf/simulated/var/zone/zone
/turf/simulated/var/open_directions
-/turf/simulated/var/gas_graphic
/turf/var/needs_air_update = 0
/turf/var/datum/gas_mixture/air
-/turf/simulated/proc/set_graphic(new_graphic)
- if(isnum(new_graphic))
- if(new_graphic == 1) new_graphic = plmaster
- else if(new_graphic == 2) new_graphic = slmaster
- if(gas_graphic) overlays -= gas_graphic
- if(new_graphic) overlays += new_graphic
- gas_graphic = new_graphic
+/turf/simulated/proc/update_graphic(list/graphic_add = null, list/graphic_remove = null)
+ if(graphic_add)
+ overlays += graphic_add
+ if(graphic_remove)
+ overlays -= graphic_remove
/turf/proc/update_air_properties()
var/block = c_airblock(src)
@@ -185,17 +182,15 @@
/turf/assume_air(datum/gas_mixture/giver) //use this for machines to adjust air
return 0
+/turf/proc/assume_gas(gasid, moles, temp = 0)
+ return 0
+
/turf/return_air()
//Create gas mixture to hold data for passing
var/datum/gas_mixture/GM = new
- GM.oxygen = oxygen
- GM.carbon_dioxide = carbon_dioxide
- GM.nitrogen = nitrogen
- GM.phoron = phoron
-
+ GM.adjust_multi("oxygen", oxygen, "carbon_dioxide", carbon_dioxide, "nitrogen", nitrogen, "phoron", phoron)
GM.temperature = temperature
- GM.update_values()
return GM
@@ -204,10 +199,10 @@
var/sum = oxygen + carbon_dioxide + nitrogen + phoron
if(sum>0)
- GM.oxygen = (oxygen/sum)*amount
- GM.carbon_dioxide = (carbon_dioxide/sum)*amount
- GM.nitrogen = (nitrogen/sum)*amount
- GM.phoron = (phoron/sum)*amount
+ GM.gas["oxygen"] = (oxygen/sum)*amount
+ GM.gas["carbon_dioxide"] = (carbon_dioxide/sum)*amount
+ GM.gas["nitrogen"] = (nitrogen/sum)*amount
+ GM.gas["phoron"] = (phoron/sum)*amount
GM.temperature = temperature
GM.update_values()
@@ -218,6 +213,16 @@
var/datum/gas_mixture/my_air = return_air()
my_air.merge(giver)
+/turf/simulated/assume_gas(gasid, moles, temp = null)
+ var/datum/gas_mixture/my_air = return_air()
+
+ if(isnull(temp))
+ my_air.adjust_gas(gasid, moles)
+ else
+ my_air.adjust_gas_temp(gasid, moles, temp)
+
+ return 1
+
/turf/simulated/remove_air(amount as num)
var/datum/gas_mixture/my_air = return_air()
return my_air.remove(amount)
@@ -240,11 +245,11 @@
/turf/proc/make_air()
air = new/datum/gas_mixture
air.temperature = temperature
- air.adjust(oxygen, carbon_dioxide, nitrogen, phoron)
+ air.adjust_multi("oxygen", oxygen, "carbon_dioxide", carbon_dioxide, "nitrogen", nitrogen, "phoron", phoron)
air.group_multiplier = 1
air.volume = CELL_VOLUME
/turf/simulated/proc/c_copy_air()
if(!air) air = new/datum/gas_mixture
air.copy_from(zone.air)
- air.group_multiplier = 1
\ No newline at end of file
+ air.group_multiplier = 1
diff --git a/code/ZAS/Zone.dm b/code/ZAS/Zone.dm
index 2d285d08f6f..e794c36b02a 100644
--- a/code/ZAS/Zone.dm
+++ b/code/ZAS/Zone.dm
@@ -43,6 +43,7 @@ Class Procs:
/zone/var/name
/zone/var/invalid = 0
/zone/var/list/contents = list()
+/zone/var/list/fire_tiles = list()
/zone/var/needs_update = 0
@@ -50,6 +51,9 @@ Class Procs:
/zone/var/datum/gas_mixture/air = new
+/zone/var/list/graphic_add = list()
+/zone/var/list/graphic_remove = list()
+
/zone/New()
air_master.add_zone(src)
air.temperature = TCMB
@@ -67,7 +71,10 @@ Class Procs:
add_tile_air(turf_air)
T.zone = src
contents.Add(T)
- T.set_graphic(air.graphic)
+ if(T.fire)
+ fire_tiles.Add(T)
+ air_master.active_fire_zones.Add(src)
+ T.update_graphic(air.graphic)
/zone/proc/remove(turf/simulated/T)
#ifdef ZASDBG
@@ -77,8 +84,9 @@ Class Procs:
soft_assert(T in contents, "Lists are weird broseph")
#endif
contents.Remove(T)
+ fire_tiles.Remove(T)
T.zone = null
- T.set_graphic(0)
+ T.update_graphic(graphic_remove = air.graphic)
if(contents.len)
air.group_multiplier = contents.len
else
@@ -123,16 +131,18 @@ Class Procs:
air.group_multiplier = contents.len+1
/zone/proc/tick()
- air.archive()
- if(air.check_tile_graphic())
+ if(air.check_tile_graphic(graphic_add, graphic_remove))
for(var/turf/simulated/T in contents)
- T.set_graphic(air.graphic)
+ T.update_graphic(graphic_add, graphic_remove)
+ graphic_add.len = 0
+ graphic_remove.len = 0
/zone/proc/dbg_data(mob/M)
M << name
- M << "O2: [air.oxygen] N2: [air.nitrogen] CO2: [air.carbon_dioxide] P: [air.phoron]"
+ for(var/g in air.gas)
+ M << "[gas_data.name[g]]: [air.gas[g]]"
M << "P: [air.return_pressure()] kPa V: [air.volume]L T: [air.temperature]°K ([air.temperature - T0C]°C)"
- M << "O2 per N2: [(air.nitrogen ? air.oxygen/air.nitrogen : "N/A")] Moles: [air.total_moles]"
+ M << "O2 per N2: [(air.gas["nitrogen"] ? air.gas["oxygen"]/air.gas["nitrogen"] : "N/A")] Moles: [air.total_moles]"
M << "Simulated: [contents.len] ([air.group_multiplier])"
//M << "Unsimulated: [unsimulated_contents.len]"
//M << "Edges: [edges.len]"
diff --git a/code/ZAS/_gas_mixture_xgm.dm b/code/ZAS/_gas_mixture_xgm.dm
new file mode 100644
index 00000000000..01f95ee9a58
--- /dev/null
+++ b/code/ZAS/_gas_mixture_xgm.dm
@@ -0,0 +1,419 @@
+#define QUANTIZE(variable) (round(variable,0.0001))
+
+/datum/gas_mixture
+ //Associative list of gas moles.
+ //Gases with 0 moles are not tracked and are pruned by update_values()
+ var/list/gas = list()
+ //Temperature in Kelvin of this gas mix.
+ var/temperature = 0
+
+ //Sum of all the gas moles in this mix. Updated by update_values()
+ var/total_moles = 0
+ //Volume of this mix.
+ var/volume = CELL_VOLUME
+ //Size of the group this gas_mixture is representing. 1 for singletons.
+ var/group_multiplier = 1
+
+ //List of active tile overlays for this gas_mixture. Updated by check_tile_graphic()
+ var/list/graphic = list()
+
+//Takes a gas string, and the amount of moles to adjust by. Calls update_values() if update isn't 0.
+/datum/gas_mixture/proc/adjust_gas(gasid, moles, update = 1)
+ if(moles == 0)
+ return
+
+ if (group_multiplier != 1)
+ gas[gasid] += moles/group_multiplier
+ else
+ gas[gasid] += moles
+
+ if(update)
+ update_values()
+
+//Same as adjust_gas(), but takes a temperature which is mixed in with the gas.
+/datum/gas_mixture/proc/adjust_gas_temp(gasid, moles, temp, update = 1)
+ if(moles == 0)
+ return
+
+ if(moles > 0 && abs(temperature - temp) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
+ var/self_heat_capacity = heat_capacity()
+ var/giver_heat_capacity = gas_data.specific_heat[gasid] * moles
+ var/combined_heat_capacity = giver_heat_capacity + self_heat_capacity
+ if(combined_heat_capacity != 0)
+ temperature = (temp * giver_heat_capacity + temperature * self_heat_capacity) / combined_heat_capacity
+
+ if (group_multiplier != 1)
+ gas[gasid] += moles/group_multiplier
+ else
+ gas[gasid] += moles
+
+ if(update)
+ update_values()
+
+//Variadic version of adjust_gas(). Takes any number of gas and mole pairs, and applies them.
+/datum/gas_mixture/proc/adjust_multi()
+ ASSERT(!(args.len % 2))
+
+ for(var/i = 1; i < args.len; i += 2)
+ adjust_gas(args[i], args[i+1], update = 0)
+
+ update_values()
+
+//Variadic version of adjust_gas_temp(). Takes any number of gas, mole, and temperature tuples, and applies them.
+/datum/gas_mixture/proc/adjust_multi_temp()
+ ASSERT(!(args.len % 3))
+
+ for(var/i = 1; i < args.len; i += 3)
+ adjust_gas_temp(args[i], args[i + 1], args[i + 2], update = 0)
+
+ update_values()
+
+//Merges all the gas from another mixture into this one. Respects group_multipliers and adjusts temperature correctly.
+/datum/gas_mixture/proc/merge(datum/gas_mixture/giver)
+ if(!giver)
+ return
+
+ if(abs(temperature-giver.temperature)>MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
+ var/self_heat_capacity = heat_capacity()
+ var/giver_heat_capacity = giver.heat_capacity()
+ var/combined_heat_capacity = giver_heat_capacity + self_heat_capacity
+ if(combined_heat_capacity != 0)
+ temperature = (giver.temperature*giver_heat_capacity + temperature*self_heat_capacity)/combined_heat_capacity
+
+ if((group_multiplier != 1)||(giver.group_multiplier != 1))
+ for(var/g in giver.gas)
+ gas[g] += giver.gas[g] * giver.group_multiplier / group_multiplier
+ else
+ for(var/g in giver.gas)
+ gas[g] += giver.gas[g]
+
+ update_values()
+
+//Returns the heat capacity of the gas mix based on the specific heat of the gases.
+/datum/gas_mixture/proc/heat_capacity()
+ . = 0
+ for(var/g in gas)
+ . += gas_data.specific_heat[g] * gas[g]
+ . *= group_multiplier
+
+//Adds or removes thermal energy. Returns the actual thermal energy change, as in the case of removing energy we can't go below TCMB.
+/datum/gas_mixture/proc/add_thermal_energy(var/thermal_energy)
+ if (total_moles == 0)
+ return 0
+
+ var/heat_capacity = heat_capacity()
+ if (thermal_energy < 0)
+ if (temperature < TCMB)
+ return 0
+ var/thermal_energy_limit = -(temperature - TCMB)*heat_capacity //ensure temperature does not go below TCMB
+ thermal_energy = max( thermal_energy, thermal_energy_limit ) //thermal_energy and thermal_energy_limit are negative here.
+ temperature += thermal_energy/heat_capacity
+ return thermal_energy
+
+//Returns the thermal energy change required to get to a new temperature
+/datum/gas_mixture/proc/get_thermal_energy_change(var/new_temperature)
+ return heat_capacity()*(max(new_temperature, 0) - temperature)
+
+//Technically vacuum doesn't have a specific entropy. Just use a really big number (infinity would be ideal) here so that it's easy to add gas to vacuum and hard to take gas out.
+#define SPECIFIC_ENTROPY_VACUUM 150000
+
+//Returns the ideal gas specific entropy of the whole mix. This is the entropy per mole of /mixed/ gas.
+/datum/gas_mixture/proc/specific_entropy()
+ if (!gas.len || total_moles == 0)
+ return SPECIFIC_ENTROPY_VACUUM
+
+ . = 0
+ for(var/g in gas)
+ . += gas[g] * specific_entropy_gas(g)
+ . /= total_moles
+
+/*
+ It's arguable whether this should even be called entropy anymore. It's more "based on" entropy than actually entropy now.
+
+ Returns the ideal gas specific entropy of a specific gas in the mix. This is the entropy due to that gas per mole of /that/ gas in the mixture, not the entropy due to that gas per mole of gas mixture.
+
+ For the purposes of SS13, the specific entropy is just a number that tells you how hard it is to move gas. You can replace this with whatever you want.
+ Just remember that returning a SMALL number == adding gas to this gas mix is HARD, taking gas away is EASY, and that returning a LARGE number means the opposite (so a vacuum should approach infinity).
+
+ So returning a constant/(partial pressure) would probably do what most players expect. Although the version I have implemented below is a bit more nuanced than simply 1/P in that it scales in a way
+ which is bit more realistic (natural log), and returns a fairly accurate entropy around room temperatures and pressures.
+*/
+/datum/gas_mixture/proc/specific_entropy_gas(var/gasid)
+ if (!(gasid in gas) || gas[gasid] == 0)
+ return SPECIFIC_ENTROPY_VACUUM //that gas isn't here
+
+ //group_multiplier gets divided out in volume/gas[gasid] - also, V/(m*T) = R/(partial pressure)
+ var/molar_mass = gas_data.molar_mass[gasid]
+ var/specific_heat = gas_data.specific_heat[gasid]
+ return R_IDEAL_GAS_EQUATION * ( log( (IDEAL_GAS_ENTROPY_CONSTANT*volume/(gas[gasid] * temperature)) * (molar_mass*specific_heat*temperature)**(2/3) + 1 ) + 15 )
+
+ //alternative, simpler equation
+ //var/partial_pressure = gas[gasid] * R_IDEAL_GAS_EQUATION * temperature / volume
+ //return R_IDEAL_GAS_EQUATION * ( log (1 + IDEAL_GAS_ENTROPY_CONSTANT/partial_pressure) + 20 )
+
+//Updates the total_moles count and trims any empty gases.
+/datum/gas_mixture/proc/update_values()
+ total_moles = 0
+ for(var/g in gas)
+ if(gas[g] <= 0)
+ gas -= g
+ else
+ total_moles += gas[g]
+
+//Returns the pressure of the gas mix. Only accurate if there have been no gas modifications since update_values() has been called.
+/datum/gas_mixture/proc/return_pressure()
+ if(volume)
+ return total_moles * R_IDEAL_GAS_EQUATION * temperature / volume
+ return 0
+
+//Removes moles from the gas mixture and returns a gas_mixture containing the removed air.
+/datum/gas_mixture/proc/remove(amount)
+ amount = min(amount, total_moles * group_multiplier) //Can not take more air than the gas mixture has!
+ if(amount <= 0)
+ return null
+
+ var/datum/gas_mixture/removed = new
+
+ for(var/g in gas)
+ removed.gas[g] = QUANTIZE((gas[g] / total_moles) * amount)
+ gas[g] -= removed.gas[g] / group_multiplier
+
+ removed.temperature = temperature
+ update_values()
+ removed.update_values()
+
+ return removed
+
+//Removes a ratio of gas from the mixture and returns a gas_mixture containing the removed air.
+/datum/gas_mixture/proc/remove_ratio(ratio, out_group_multiplier = 1)
+ if(ratio <= 0)
+ return null
+ out_group_multiplier = max(1, min(group_multiplier, out_group_multiplier))
+
+ ratio = min(ratio, 1)
+
+ var/datum/gas_mixture/removed = new
+ removed.group_multiplier = out_group_multiplier
+
+ for(var/g in gas)
+ removed.gas[g] = (gas[g] * ratio * group_multiplier / out_group_multiplier)
+ gas[g] = gas[g] * (1 - ratio)
+
+ removed.temperature = temperature
+ update_values()
+ removed.update_values()
+
+ return removed
+
+//Removes moles from the gas mixture, limited by a given flag. Returns a gax_mixture containing the removed air.
+/datum/gas_mixture/proc/remove_by_flag(flag, amount)
+ if(!flag || amount <= 0)
+ return
+
+ var/sum = 0
+ for(var/g in gas)
+ if(gas_data.flags[g] & flag)
+ sum += gas[g]
+
+ var/datum/gas_mixture/removed = new
+
+ for(var/g in gas)
+ if(gas_data.flags[g] & flag)
+ removed.gas[g] = QUANTIZE((gas[g] / sum) * amount)
+ gas[g] -= removed.gas[g] / group_multiplier
+
+ removed.temperature = temperature
+ update_values()
+ removed.update_values()
+
+ return removed
+
+//Copies gas and temperature from another gas_mixture.
+/datum/gas_mixture/proc/copy_from(datum/gas_mixture/sample)
+ gas = sample.gas.Copy()
+ temperature = sample.temperature
+
+ update_values()
+
+ return 1
+
+//Checks if we are within acceptable range of another gas_mixture to suspend processing or merge.
+/datum/gas_mixture/proc/compare(datum/gas_mixture/sample)
+ if(!sample) return 0
+
+ var/list/marked = list()
+ for(var/g in gas)
+ if((abs(gas[g] - sample.gas[g]) > MINIMUM_AIR_TO_SUSPEND) && \
+ ((gas[g] < (1 - MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.gas[g]) || \
+ (gas[g] > (1 + MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.gas[g])))
+ return 0
+ marked[g] = 1
+
+ for(var/g in sample.gas)
+ if(!marked[g])
+ if((abs(gas[g] - sample.gas[g]) > MINIMUM_AIR_TO_SUSPEND) && \
+ ((gas[g] < (1 - MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.gas[g]) || \
+ (gas[g] > (1 + MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.gas[g])))
+ return 0
+
+ if(total_moles > MINIMUM_AIR_TO_SUSPEND)
+ if((abs(temperature - sample.temperature) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) && \
+ ((temperature < (1 - MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND)*sample.temperature) || \
+ (temperature > (1 + MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND)*sample.temperature)))
+ return 0
+
+ return 1
+
+/datum/gas_mixture/proc/react(atom/dump_location)
+ zburn(null)
+
+//Rechecks the gas_mixture and adjusts the graphic list if needed.
+//Two lists can be passed by reference if you need know specifically which graphics were added and removed.
+/datum/gas_mixture/proc/check_tile_graphic(list/graphic_add = null, list/graphic_remove = null)
+ for(var/g in gas_data.overlay_limit)
+ if(graphic.Find(gas_data.tile_overlay[g]))
+ //Overlay is already applied for this gas, check if it's still valid.
+ if(gas[g] <= gas_data.overlay_limit[g])
+ if(!graphic_remove)
+ graphic_remove = list()
+ graphic_remove += gas_data.tile_overlay[g]
+ else
+ //Overlay isn't applied for this gas, check if it's valid and needs to be added.
+ if(gas[g] > gas_data.overlay_limit[g])
+ if(!graphic_add)
+ graphic_add = list()
+ graphic_add += gas_data.tile_overlay[g]
+
+ . = 0
+ //Apply changes
+ if(graphic_add && graphic_add.len)
+ graphic += graphic_add
+ . = 1
+ if(graphic_remove && graphic_remove.len)
+ graphic -= graphic_remove
+ . = 1
+
+//Simpler version of merge(), adjusts gas amounts directly and doesn't account for temperature or group_multiplier.
+/datum/gas_mixture/proc/add(datum/gas_mixture/right_side)
+ for(var/g in right_side.gas)
+ gas[g] += right_side.gas[g]
+
+ update_values()
+ return 1
+
+//Simpler version of remove(), adjusts gas amounts directly and doesn't account for group_multiplier.
+/datum/gas_mixture/proc/subtract(datum/gas_mixture/right_side)
+ for(var/g in right_side.gas)
+ gas[g] -= right_side.gas[g]
+
+ update_values()
+ return 1
+
+//Multiply all gas amounts by a factor.
+/datum/gas_mixture/proc/multiply(factor)
+ for(var/g in gas)
+ gas[g] *= factor
+
+ update_values()
+ return 1
+
+//Divide all gas amounts by a factor.
+/datum/gas_mixture/proc/divide(factor)
+ for(var/g in gas)
+ gas[g] /= factor
+
+ update_values()
+ return 1
+
+//Shares gas with another gas_mixture based on the amount of connecting tiles and a fixed lookup table.
+/datum/gas_mixture/proc/share_ratio(datum/gas_mixture/other, connecting_tiles, share_size = null, one_way = 0)
+ var/static/list/sharing_lookup_table = list(0.30, 0.40, 0.48, 0.54, 0.60, 0.66)
+ //Shares a specific ratio of gas between mixtures using simple weighted averages.
+ var/ratio = sharing_lookup_table[6]
+
+ var/size = max(1, group_multiplier)
+ if(isnull(share_size)) share_size = max(1, other.group_multiplier)
+
+ var/full_heat_capacity = heat_capacity()
+ var/s_full_heat_capacity = other.heat_capacity()
+
+ var/list/avg_gas = list()
+
+ for(var/g in gas)
+ avg_gas[g] += gas[g] * size
+
+ for(var/g in other.gas)
+ avg_gas[g] += other.gas[g] * share_size
+
+ for(var/g in avg_gas)
+ avg_gas[g] /= (size + share_size)
+
+ var/temp_avg = 0
+ if(full_heat_capacity + s_full_heat_capacity)
+ temp_avg = (temperature * full_heat_capacity + other.temperature * s_full_heat_capacity) / (full_heat_capacity + s_full_heat_capacity)
+
+ //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD.
+ if(sharing_lookup_table.len >= connecting_tiles) //6 or more interconnecting tiles will max at 42% of air moved per tick.
+ ratio = sharing_lookup_table[connecting_tiles]
+ //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
+
+ for(var/g in avg_gas)
+ gas[g] = max(0, (gas[g] - avg_gas[g]) * (1 - ratio) + avg_gas[g])
+ if(!one_way)
+ other.gas[g] = max(0, (other.gas[g] - avg_gas[g]) * (1 - ratio) + avg_gas[g])
+
+ temperature = max(0, (temperature - temp_avg) * (1-ratio) + temp_avg)
+ if(!one_way)
+ other.temperature = max(0, (other.temperature - temp_avg) * (1-ratio) + temp_avg)
+
+ update_values()
+ other.update_values()
+
+ if(compare(other)) return 1
+ else return 0
+
+//A wrapper around share_ratio for spacing gas at the same rate as if it were going into a large airless room.
+/datum/gas_mixture/proc/share_space(datum/gas_mixture/unsim_air)
+ if(!unsim_air)
+ return 0
+
+ var/old_pressure = return_pressure()
+
+ share_ratio(unsim_air, unsim_air.group_multiplier, max(1, max(group_multiplier + 3, 1) + unsim_air.group_multiplier), one_way = 1)
+
+ return abs(old_pressure - return_pressure())
+
+//Equalizes a list of gas mixtures. Used for pipe networks.
+/proc/equalize_gases(datum/gas_mixture/list/gases)
+ //Calculate totals from individual components
+ var/total_volume = 0
+ var/total_thermal_energy = 0
+ var/total_heat_capacity = 0
+
+ var/list/total_gas = list()
+ for(var/datum/gas_mixture/gasmix in gases)
+ total_volume += gasmix.volume
+ var/temp_heatcap = gasmix.heat_capacity()
+ total_thermal_energy += gasmix.temperature * temp_heatcap
+ total_heat_capacity += temp_heatcap
+ for(var/g in gasmix.gas)
+ total_gas[g] += gasmix.gas[g]
+
+ if(total_volume > 0)
+ //Average out the gases
+ for(var/g in total_gas)
+ total_gas[g] /= total_volume
+
+ //Calculate temperature
+ var/temperature = 0
+
+ if(total_heat_capacity > 0)
+ temperature = total_thermal_energy / total_heat_capacity
+
+ //Update individual gas_mixtures
+ for(var/datum/gas_mixture/gasmix in gases)
+ gasmix.gas = total_gas.Copy()
+ gasmix.temperature = temperature
+ gasmix.multiply(gasmix.volume)
+
+ return 1
diff --git a/code/ZAS/_xgm_gas_data.dm b/code/ZAS/_xgm_gas_data.dm
new file mode 100644
index 00000000000..fd732f9d7cd
--- /dev/null
+++ b/code/ZAS/_xgm_gas_data.dm
@@ -0,0 +1,46 @@
+/var/xgm_gas_data/gas_data
+
+/xgm_gas_data
+ //Simple list of all the gas IDs.
+ var/list/gases = list()
+ //The friendly, human-readable name for the gas.
+ var/list/name = list()
+ //Specific heat of the gas. Used for calculating heat capacity.
+ var/list/specific_heat = list()
+ //Molar mass of the gas. Used for calculating specific entropy.
+ var/list/molar_mass = list()
+ //Tile overlays. /images, created from references to 'icons/effects/tile_effects.dmi'
+ var/list/tile_overlay = list()
+ //Overlay limits. There must be at least this many moles for the overlay to appear.
+ var/list/overlay_limit = list()
+ //Flags.
+ var/list/flags = list()
+
+/xgm_gas
+ var/id = ""
+ var/name = "Unnamed Gas"
+ var/specific_heat = 20 // J/(mol*K)
+ var/molar_mass = 0.032 // kg/mol
+
+ var/tile_overlay = null
+ var/overlay_limit = null
+
+ var/flags = 0
+
+/hook/startup/proc/generateGasData()
+ gas_data = new
+ for(var/p in (typesof(/xgm_gas) - /xgm_gas))
+ var/xgm_gas/gas = new p
+
+ if(gas.id in gas_data.gases)
+ error("Duplicate gas id `[gas.id]` in `[p]`")
+
+ gas_data.gases += gas.id
+ gas_data.name[gas.id] = gas.name
+ gas_data.specific_heat[gas.id] = gas.specific_heat
+ gas_data.molar_mass[gas.id] = gas.molar_mass
+ if(gas.tile_overlay) gas_data.tile_overlay[gas.id] = image('icons/effects/tile_effects.dmi', gas.tile_overlay, FLY_LAYER)
+ if(gas.overlay_limit) gas_data.overlay_limit[gas.id] = gas.overlay_limit
+ gas_data.flags[gas.id] = gas.flags
+
+ return 1
diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm
index 77ed7f308e6..8c6befa38f8 100644
--- a/code/__HELPERS/game.dm
+++ b/code/__HELPERS/game.dm
@@ -398,4 +398,65 @@ datum/projectile_data
var/r = mixOneColor(weights, reds)
var/g = mixOneColor(weights, greens)
var/b = mixOneColor(weights, blues)
- return rgb(r,g,b)
\ No newline at end of file
+ return rgb(r,g,b)
+
+/**
+* Gets the highest and lowest pressures from the tiles in cardinal directions
+* around us, then checks the difference.
+*/
+/proc/getOPressureDifferential(var/turf/loc)
+ var/minp=16777216;
+ var/maxp=0;
+ for(var/dir in cardinal)
+ var/turf/simulated/T=get_turf(get_step(loc,dir))
+ var/cp=0
+ if(T && istype(T) && T.zone)
+ var/datum/gas_mixture/environment = T.return_air()
+ cp = environment.return_pressure()
+ else
+ if(istype(T,/turf/simulated))
+ continue
+ if(cpmaxp)maxp=cp
+ return abs(minp-maxp)
+
+/proc/convert_k2c(var/temp)
+ return ((temp - T0C))
+
+/proc/convert_c2k(var/temp)
+ return ((temp + T0C))
+
+/proc/getCardinalAirInfo(var/turf/loc, var/list/stats=list("temperature"))
+ var/list/temps = new/list(4)
+ for(var/dir in cardinal)
+ var/direction
+ switch(dir)
+ if(NORTH)
+ direction = 1
+ if(SOUTH)
+ direction = 2
+ if(EAST)
+ direction = 3
+ if(WEST)
+ direction = 4
+ var/turf/simulated/T=get_turf(get_step(loc,dir))
+ var/list/rstats = new /list(stats.len)
+ if(T && istype(T) && T.zone)
+ var/datum/gas_mixture/environment = T.return_air()
+ for(var/i=1;i<=stats.len;i++)
+ if(stats[i] == "pressure")
+ rstats[i] = environment.return_pressure()
+ else
+ rstats[i] = environment.vars[stats[i]]
+ else if(istype(T, /turf/simulated))
+ rstats = null // Exclude zone (wall, door, etc).
+ else if(istype(T, /turf))
+ // Should still work. (/turf/return_air())
+ var/datum/gas_mixture/environment = T.return_air()
+ for(var/i=1;i<=stats.len;i++)
+ if(stats[i] == "pressure")
+ rstats[i] = environment.return_pressure()
+ else
+ rstats[i] = environment.vars[stats[i]]
+ temps[direction] = rstats
+ return temps
diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm
index 1a0c8b50e7e..a6a87c2bd03 100644
--- a/code/__HELPERS/text.dm
+++ b/code/__HELPERS/text.dm
@@ -303,3 +303,14 @@ proc/checkhtml(var/t)
for(var/i = length(text); i > 0; i--)
new_text += copytext(text, i, i+1)
return new_text
+
+//Used in preferences' SetFlavorText and human's set_flavor verb
+//Previews a string of len or less length
+proc/TextPreview(var/string,var/len=40)
+ if(lentext(string) <= len)
+ if(!lentext(string))
+ return "\[...\]"
+ else
+ return string
+ else
+ return "[copytext(string, 1, 37)]..."
diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm
index 76f9f80800b..a3544d7dd23 100644
--- a/code/__HELPERS/unsorted.dm
+++ b/code/__HELPERS/unsorted.dm
@@ -1306,11 +1306,10 @@ proc/is_hot(obj/item/W as obj)
//check if mob is lying down on something we can operate him on.
/proc/can_operate(mob/living/carbon/M)
- return (locate(/obj/machinery/optable, M.loc) && M.resting) || \
- (locate(/obj/structure/stool/bed/roller, M.loc) && \
- (M.buckled || M.lying || M.weakened || M.stunned || M.paralysis || M.sleeping || M.stat)) && prob(75) || \
- (locate(/obj/structure/table/, M.loc) && \
- (M.lying || M.weakened || M.stunned || M.paralysis || M.sleeping || M.stat) && prob(66))
+ return (M.lying && \
+ locate(/obj/machinery/optable, M.loc) || \
+ (locate(/obj/structure/stool/bed/roller, M.loc) && prob(75)) || \
+ (locate(/obj/structure/table/, M.loc) && prob(66)))
/proc/reverse_direction(var/dir)
switch(dir)
diff --git a/code/_onclick/ai.dm b/code/_onclick/ai.dm
index d6918b1f7e0..c6078c10ac8 100644
--- a/code/_onclick/ai.dm
+++ b/code/_onclick/ai.dm
@@ -53,6 +53,11 @@
return
next_move = world.time + 9
+ if(aiCamera.in_camera_mode)
+ aiCamera.camera_mode_off()
+ aiCamera.captureimage(A, usr)
+ return
+
/*
AI restrained() currently does nothing
if(restrained())
@@ -81,12 +86,15 @@
than anything else in the game, atoms have separate procs
for AI shift, ctrl, and alt clicking.
*/
+
/mob/living/silicon/ai/ShiftClickOn(var/atom/A)
A.AIShiftClick(src)
/mob/living/silicon/ai/CtrlClickOn(var/atom/A)
A.AICtrlClick(src)
/mob/living/silicon/ai/AltClickOn(var/atom/A)
A.AIAltClick(src)
+/mob/living/silicon/ai/MiddleClickOn(var/atom/A)
+ A.AIMiddleClick(src)
/*
The following criminally helpful code is just the previous code cleaned up;
@@ -103,7 +111,6 @@
Topic("aiDisable=7", list("aiDisable"="7"), 1)
return
-
/atom/proc/AICtrlClick()
return
@@ -113,18 +120,42 @@
else
Topic("aiDisable=4", list("aiDisable"="4"), 1)
-/obj/machinery/power/apc/AICtrlClick() // turns off APCs.
+/obj/machinery/power/apc/AICtrlClick() // turns off/on APCs.
Topic("breaker=1", list("breaker"="1"), 0) // 0 meaning no window (consistency! wait...)
+/obj/machinery/turretid/AICtrlClick() //turns off/on Turrets
+ src.enabled = !src.enabled
+ src.updateTurrets()
-/atom/proc/AIAltClick()
- return
+/atom/proc/AIAltClick(var/atom/A)
+ AltClick(A)
-/obj/machinery/door/airlock/AIAltClick() // Eletrifies doors.
+/obj/machinery/door/airlock/AIAltClick() // Electrifies doors.
if(!secondsElectrified)
- // permenant shock
+ // permanent shock
Topic("aiEnable=6", list("aiEnable"="6"), 1) // 1 meaning no window (consistency!)
else
- // disable/6 is not in Topic; disable/5 disables both temporary and permenant shock
+ // disable/6 is not in Topic; disable/5 disables both temporary and permanent shock
Topic("aiDisable=5", list("aiDisable"="5"), 1)
- return
\ No newline at end of file
+ return
+
+/obj/machinery/turretid/AIAltClick() //toggles lethal on turrets
+ src.lethal = !src.lethal
+ src.updateTurrets()
+
+/atom/proc/AIMiddleClick()
+ return
+
+/obj/machinery/door/airlock/AIMiddleClick() // Toggles door bolt lights.
+ if(!src.lights)
+ Topic("aiEnable=10", list("aiEnable"="10"), 1) // 1 meaning no window (consistency!)
+ else
+ Topic("aiDisable=10", list("aiDisable"="10"), 1)
+ return
+
+//
+// Override AdjacentQuick for AltClicking
+//
+
+/mob/living/silicon/ai/TurfAdjacent(var/turf/T)
+ return (cameranet && cameranet.checkTurfVis(T))
diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm
index 776f040f42d..adf80b17528 100644
--- a/code/_onclick/click.dm
+++ b/code/_onclick/click.dm
@@ -252,7 +252,7 @@
/atom/proc/AltClick(var/mob/user)
var/turf/T = get_turf(src)
- if(T && T.AdjacentQuick(user))
+ if(T && user.TurfAdjacent(T))
if(user.listed_turf == T)
user.listed_turf = null
else
@@ -260,6 +260,9 @@
user.client.statpanel = T.name
return
+/mob/proc/TurfAdjacent(var/turf/T)
+ return T.AdjacentQuick(src)
+
/*
Misc helpers
@@ -298,7 +301,17 @@
// Simple helper to face what you clicked on, in case it should be needed in more than one place
/mob/proc/face_atom(var/atom/A)
- if( stat || (buckled && !buckled.movable) || !A || !x || !y || !A.x || !A.y ) return
+
+ // Snowflake for space vines.
+ var/is_buckled = 0
+ if(buckled)
+ if(istype(buckled))
+ if(!buckled.movable)
+ is_buckled = 1
+ else
+ is_buckled = 0
+
+ if( stat || is_buckled || !A || !x || !y || !A.x || !A.y ) return
var/dx = A.x - x
var/dy = A.y - y
if(!dx && !dy) return
diff --git a/code/_onclick/cyborg.dm b/code/_onclick/cyborg.dm
index d86faacbd1f..04682c76ad9 100644
--- a/code/_onclick/cyborg.dm
+++ b/code/_onclick/cyborg.dm
@@ -37,6 +37,14 @@
face_atom(A) // change direction to face what you clicked on
+ if(aiCamera.in_camera_mode)
+ aiCamera.camera_mode_off()
+ if(is_component_functioning("camera"))
+ aiCamera.captureimage(A, usr)
+ else
+ src << "Your camera isn't functional."
+ return
+
/*
cyborg restrained() currently does nothing
if(restrained())
diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm
index 996ea857acf..c733d1ad35e 100644
--- a/code/_onclick/hud/screen_objects.dm
+++ b/code/_onclick/hud/screen_objects.dm
@@ -276,7 +276,7 @@
var/list/nicename = null
var/list/tankcheck = null
var/breathes = "oxygen" //default, we'll check later
- var/list/contents = list()
+ var/list/contents = list()
if(ishuman(C))
var/mob/living/carbon/human/H = C
@@ -284,7 +284,7 @@
nicename = list ("suit", "back", "belt", "right hand", "left hand", "left pocket", "right pocket")
tankcheck = list (H.s_store, C.back, H.belt, C.r_hand, C.l_hand, H.l_store, H.r_store)
- else
+ else
nicename = list("Right Hand", "Left Hand", "Back")
tankcheck = list(C.r_hand, C.l_hand, C.back)
@@ -295,30 +295,30 @@
if (!isnull(t.manipulated_by) && t.manipulated_by != C.real_name && findtext(t.desc,breathes))
contents.Add(t.air_contents.total_moles) //Someone messed with the tank and put unknown gasses
continue //in it, so we're going to believe the tank is what it says it is
- switch(breathes)
+ switch(breathes)
//These tanks we're sure of their contents
if("nitrogen") //So we're a bit more picky about them.
-
- if(t.air_contents.nitrogen && !t.air_contents.oxygen)
- contents.Add(t.air_contents.nitrogen)
+
+ if(t.air_contents.gas["nitrogen"] && !t.air_contents.gas["oxygen"])
+ contents.Add(t.air_contents.gas["nitrogen"])
else
- contents.Add(0)
+ contents.Add(0)
if ("oxygen")
- if(t.air_contents.oxygen && !t.air_contents.phoron)
- contents.Add(t.air_contents.oxygen)
+ if(t.air_contents.gas["oxygen"] && !t.air_contents.gas["phoron"])
+ contents.Add(t.air_contents.gas["oxygen"])
else
contents.Add(0)
// No races breath this, but never know about downstream servers.
if ("carbon dioxide")
- if(t.air_contents.carbon_dioxide && !t.air_contents.phoron)
- contents.Add(t.air_contents.carbon_dioxide)
+ if(t.air_contents.gas["carbon_dioxide"] && !t.air_contents.gas["phoron"])
+ contents.Add(t.air_contents.gas["carbon_dioxide"])
else
contents.Add(0)
-
-
- else
+
+
+ else
//no tank so we set contents to 0
contents.Add(0)
@@ -328,19 +328,19 @@
var/bestcontents = 0
for(var/i=1, i < contents.len + 1 , ++i)
if(!contents[i])
- continue
+ continue
if(contents[i] > bestcontents)
best = i
bestcontents = contents[i]
-
-
+
+
//We've determined the best container now we set it as our internals
if(best)
C << "You are now running on internals from [tankcheck[best]] on your [nicename[best]]."
C.internal = tankcheck[best]
-
-
+
+
if(C.internal)
if(C.internals)
C.internals.icon_state = "internal1"
diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm
index 90a68b02b68..9e05a1e39aa 100644
--- a/code/_onclick/item_attack.dm
+++ b/code/_onclick/item_attack.dm
@@ -24,15 +24,13 @@
/obj/item/proc/attack(mob/living/M as mob, mob/living/user as mob, def_zone)
if (!istype(M)) // not sure if this is the right thing...
- return
+ return 0
var/messagesource = M
if (can_operate(M)) //Checks if mob is lying down on table for surgery
if (do_surgery(M,user,src))
- return
+ return 0
if (istype(M,/mob/living/carbon/brain))
messagesource = M:container
- if (hitsound)
- playsound(loc, hitsound, 50, 1, -1)
/////////////////////////
user.lastattacked = M
M.lastattacker = user
@@ -63,7 +61,7 @@
slime.Discipline = 0
if(power >= 3)
- if(istype(slime, /mob/living/carbon/slime/adult))
+ if(slime.is_adult)
if(prob(5 + round(power/2)))
if(slime.Victim)
@@ -140,8 +138,13 @@
if(istype(M, /mob/living/carbon/human))
- return M:attacked_by(src, user, def_zone)
+ var/hit = M:attacked_by(src, user, def_zone)
+ if (hit && hitsound)
+ playsound(loc, hitsound, 50, 1, -1)
+ return hit
else
+ if (hitsound)
+ playsound(loc, hitsound, 50, 1, -1)
switch(damtype)
if("brute")
if(istype(src, /mob/living/carbon/slime))
diff --git a/code/game/communications.dm b/code/controllers/communications.dm
similarity index 51%
rename from code/game/communications.dm
rename to code/controllers/communications.dm
index 4f13277a31d..a1d314bcfe7 100644
--- a/code/game/communications.dm
+++ b/code/controllers/communications.dm
@@ -1,307 +1,294 @@
-/*
- HOW IT WORKS
-
- The radio_controller is a global object maintaining all radio transmissions, think about it as about "ether".
- Note that walkie-talkie, intercoms and headsets handle transmission using nonstandard way.
- procs:
-
- add_object(obj/device as obj, var/new_frequency as num, var/filter as text|null = null)
- Adds listening object.
- parameters:
- device - device receiving signals, must have proc receive_signal (see description below).
- one device may listen several frequencies, but not same frequency twice.
- new_frequency - see possibly frequencies below;
- filter - thing for optimization. Optional, but recommended.
- All filters should be consolidated in this file, see defines later.
- Device without listening filter will receive all signals (on specified frequency).
- Device with filter will receive any signals sent without filter.
- Device with filter will not receive any signals sent with different filter.
- returns:
- Reference to frequency object.
-
- remove_object (obj/device, old_frequency)
- Obliviously, after calling this proc, device will not receive any signals on old_frequency.
- Other frequencies will left unaffected.
-
- return_frequency(var/frequency as num)
- returns:
- Reference to frequency object. Use it if you need to send and do not need to listen.
-
- radio_frequency is a global object maintaining list of devices that listening specific frequency.
- procs:
-
- post_signal(obj/source as obj|null, datum/signal/signal, var/filter as text|null = null, var/range as num|null = null)
- Sends signal to all devices that wants such signal.
- parameters:
- source - object, emitted signal. Usually, devices will not receive their own signals.
- signal - see description below.
- filter - described above.
- range - radius of regular byond's square circle on that z-level. null means everywhere, on all z-levels.
-
- obj/proc/receive_signal(datum/signal/signal, var/receive_method as num, var/receive_param)
- Handler from received signals. By default does nothing. Define your own for your object.
- Avoid of sending signals directly from this proc, use spawn(-1). Do not use sleep() here please.
- parameters:
- signal - see description below. Extract all needed data from the signal before doing sleep(), spawn() or return!
- receive_method - may be TRANSMISSION_WIRE or TRANSMISSION_RADIO.
- TRANSMISSION_WIRE is currently unused.
- receive_param - for TRANSMISSION_RADIO here comes frequency.
-
- datum/signal
- vars:
- source
- an object that emitted signal. Used for debug and bearing.
- data
- list with transmitting data. Usual use pattern:
- data["msg"] = "hello world"
- encryption
- Some number symbolizing "encryption key".
- Note that game actually do not use any cryptography here.
- If receiving object don't know right key, it must ignore encrypted signal in its receive_signal.
-
-*/
-
-/*
-Frequency range: 1200 to 1600
-Radiochat range: 1441 to 1489 (most devices refuse to be tune to other frequency, even during mapmaking)
-
-Radio:
-1459 - standard radio chat
-1351 - Science
-1353 - Command
-1355 - Medical
-1357 - Engineering
-1359 - Security
-1441 - death squad
-1443 - Confession Intercom
-1347 - Cargo techs
-
-Devices:
-1451 - tracking implant
-1457 - RSD default
-
-On the map:
-1311 for prison shuttle console (in fact, it is not used)
-1435 for status displays
-1437 for atmospherics/fire alerts
-1439 for engine components
-1439 for air pumps, air scrubbers, atmo control
-1441 for atmospherics - supply tanks
-1443 for atmospherics - distribution loop/mixed air tank
-1445 for bot nav beacons
-1447 for mulebot, secbot and ed209 control
-1449 for airlock controls, electropack, magnets
-1451 for toxin lab access
-1453 for engineering access
-1455 for AI access
-*/
-
-var/list/radiochannels = list(
- "Common" = 1459,
- "Science" = 1351,
- "Command" = 1353,
- "Medical" = 1355,
- "Engineering" = 1357,
- "Security" = 1359,
- "Response Team" = 1345,
- "Deathsquad" = 1341,
- "Syndicate" = 1213,
- "Supply" = 1347,
-)
-//depenging helpers
-var/list/DEPT_FREQS = list(1351, 1355, 1357, 1359, 1213, 1345, 1341, 1347)
-
-// central command channels, i.e deathsquid & response teams
-var/list/CENT_FREQS = list(1345, 1341)
-
-var/const/COMM_FREQ = 1353 //command, colored gold in chat window
-var/const/SYND_FREQ = 1213
-
-// department channels
-var/const/SEC_FREQ = 1359
-var/const/ENG_FREQ = 1357
-var/const/SCI_FREQ = 1351
-var/const/MED_FREQ = 1355
-var/const/SUP_FREQ = 1347
-
-#define TRANSMISSION_WIRE 0
-#define TRANSMISSION_RADIO 1
-
-/* filters */
-var/const/RADIO_TO_AIRALARM = "1"
-var/const/RADIO_FROM_AIRALARM = "2"
-var/const/RADIO_CHAT = "3"
-var/const/RADIO_ATMOSIA = "4"
-var/const/RADIO_NAVBEACONS = "5"
-var/const/RADIO_AIRLOCK = "6"
-var/const/RADIO_SECBOT = "7"
-var/const/RADIO_MULEBOT = "8"
-var/const/RADIO_MAGNETS = "9"
-
-var/global/datum/controller/radio/radio_controller
-
-/hook/startup/proc/createRadioController()
- radio_controller = new /datum/controller/radio()
- return 1
-
-datum/controller/radio
- var/list/datum/radio_frequency/frequencies = list()
-
- proc/add_object(obj/device as obj, var/new_frequency as num, var/filter = null as text|null)
- var/f_text = num2text(new_frequency)
- var/datum/radio_frequency/frequency = frequencies[f_text]
-
- if(!frequency)
- frequency = new
- frequency.frequency = new_frequency
- frequencies[f_text] = frequency
-
- frequency.add_listener(device, filter)
- return frequency
-
- proc/remove_object(obj/device, old_frequency)
- var/f_text = num2text(old_frequency)
- var/datum/radio_frequency/frequency = frequencies[f_text]
-
- if(frequency)
- frequency.remove_listener(device)
-
- if(frequency.devices.len == 0)
- del(frequency)
- frequencies -= f_text
-
- return 1
-
- proc/return_frequency(var/new_frequency as num)
- var/f_text = num2text(new_frequency)
- var/datum/radio_frequency/frequency = frequencies[f_text]
-
- if(!frequency)
- frequency = new
- frequency.frequency = new_frequency
- frequencies[f_text] = frequency
-
- return frequency
-
-datum/radio_frequency
- var/frequency as num
- var/list/list/obj/devices = list()
-
- proc
- post_signal(obj/source as obj|null, datum/signal/signal, var/filter = null as text|null, var/range = null as num|null)
- //log_admin("DEBUG \[[world.timeofday]\]: post_signal {source=\"[source]\", [signal.debug_print()], filter=[filter]}")
-// var/N_f=0
-// var/N_nf=0
-// var/Nt=0
- var/turf/start_point
- if(range)
- start_point = get_turf(source)
- if(!start_point)
- del(signal)
- return 0
- if (filter) //here goes some copypasta. It is for optimisation. -rastaf0
- for(var/obj/device in devices[filter])
- if(device == source)
- continue
- if(range)
- var/turf/end_point = get_turf(device)
- if(!end_point)
- continue
- //if(max(abs(start_point.x-end_point.x), abs(start_point.y-end_point.y)) <= range)
- if(start_point.z!=end_point.z || get_dist(start_point, end_point) > range)
- continue
- device.receive_signal(signal, TRANSMISSION_RADIO, frequency)
- for(var/obj/device in devices["_default"])
- if(device == source)
- continue
- if(range)
- var/turf/end_point = get_turf(device)
- if(!end_point)
- continue
- //if(max(abs(start_point.x-end_point.x), abs(start_point.y-end_point.y)) <= range)
- if(start_point.z!=end_point.z || get_dist(start_point, end_point) > range)
- continue
- device.receive_signal(signal, TRANSMISSION_RADIO, frequency)
-// N_f++
- else
- for (var/next_filter in devices)
-// var/list/obj/DDD = devices[next_filter]
-// Nt+=DDD.len
- for(var/obj/device in devices[next_filter])
- if(device == source)
- continue
- if(range)
- var/turf/end_point = get_turf(device)
- if(!end_point)
- continue
- //if(max(abs(start_point.x-end_point.x), abs(start_point.y-end_point.y)) <= range)
- if(start_point.z!=end_point.z || get_dist(start_point, end_point) > range)
- continue
- device.receive_signal(signal, TRANSMISSION_RADIO, frequency)
-// N_nf++
-
-// log_admin("DEBUG: post_signal(source=[source] ([source.x], [source.y], [source.z]),filter=[filter]) frequency=[frequency], N_f=[N_f], N_nf=[N_nf]")
-
-
-// del(signal)
-
- add_listener(obj/device as obj, var/filter as text|null)
- if (!filter)
- filter = "_default"
- //log_admin("add_listener(device=[device],filter=[filter]) frequency=[frequency]")
- var/list/obj/devices_line = devices[filter]
- if (!devices_line)
- devices_line = new
- devices[filter] = devices_line
- devices_line+=device
-// var/list/obj/devices_line___ = devices[filter_str]
-// var/l = devices_line___.len
- //log_admin("DEBUG: devices_line.len=[devices_line.len]")
- //log_admin("DEBUG: devices(filter_str).len=[l]")
-
- remove_listener(obj/device)
- for (var/devices_filter in devices)
- var/list/devices_line = devices[devices_filter]
- devices_line-=device
- while (null in devices_line)
- devices_line -= null
- if (devices_line.len==0)
- devices -= devices_filter
- del(devices_line)
-
-
-obj/proc
- receive_signal(datum/signal/signal, receive_method, receive_param)
- return null
-
-datum/signal
- var/obj/source
-
- var/transmission_method = 0
- //0 = wire
- //1 = radio transmission
- //2 = subspace transmission
-
- var/data = list()
- var/encryption
-
- var/frequency = 0
-
- proc/copy_from(datum/signal/model)
- source = model.source
- transmission_method = model.transmission_method
- data = model.data
- encryption = model.encryption
- frequency = model.frequency
-
- proc/debug_print()
- if (source)
- . = "signal = {source = '[source]' ([source:x],[source:y],[source:z])\n"
- else
- . = "signal = {source = '[source]' ()\n"
- for (var/i in data)
- . += "data\[\"[i]\"\] = \"[data[i]]\"\n"
- if(islist(data[i]))
- var/list/L = data[i]
- for(var/t in L)
- . += "data\[\"[i]\"\] list has: [t]"
+/*
+ HOW IT WORKS
+
+ The radio_controller is a global object maintaining all radio transmissions, think about it as about "ether".
+ Note that walkie-talkie, intercoms and headsets handle transmission using nonstandard way.
+ procs:
+
+ add_object(obj/device as obj, var/new_frequency as num, var/filter as text|null = null)
+ Adds listening object.
+ parameters:
+ device - device receiving signals, must have proc receive_signal (see description below).
+ one device may listen several frequencies, but not same frequency twice.
+ new_frequency - see possibly frequencies below;
+ filter - thing for optimization. Optional, but recommended.
+ All filters should be consolidated in this file, see defines later.
+ Device without listening filter will receive all signals (on specified frequency).
+ Device with filter will receive any signals sent without filter.
+ Device with filter will not receive any signals sent with different filter.
+ returns:
+ Reference to frequency object.
+
+ remove_object (obj/device, old_frequency)
+ Obliviously, after calling this proc, device will not receive any signals on old_frequency.
+ Other frequencies will left unaffected.
+
+ return_frequency(var/frequency as num)
+ returns:
+ Reference to frequency object. Use it if you need to send and do not need to listen.
+
+ radio_frequency is a global object maintaining list of devices that listening specific frequency.
+ procs:
+
+ post_signal(obj/source as obj|null, datum/signal/signal, var/filter as text|null = null, var/range as num|null = null)
+ Sends signal to all devices that wants such signal.
+ parameters:
+ source - object, emitted signal. Usually, devices will not receive their own signals.
+ signal - see description below.
+ filter - described above.
+ range - radius of regular byond's square circle on that z-level. null means everywhere, on all z-levels.
+
+ obj/proc/receive_signal(datum/signal/signal, var/receive_method as num, var/receive_param)
+ Handler from received signals. By default does nothing. Define your own for your object.
+ Avoid of sending signals directly from this proc, use spawn(-1). Do not use sleep() here please.
+ parameters:
+ signal - see description below. Extract all needed data from the signal before doing sleep(), spawn() or return!
+ receive_method - may be TRANSMISSION_WIRE or TRANSMISSION_RADIO.
+ TRANSMISSION_WIRE is currently unused.
+ receive_param - for TRANSMISSION_RADIO here comes frequency.
+
+ datum/signal
+ vars:
+ source
+ an object that emitted signal. Used for debug and bearing.
+ data
+ list with transmitting data. Usual use pattern:
+ data["msg"] = "hello world"
+ encryption
+ Some number symbolizing "encryption key".
+ Note that game actually do not use any cryptography here.
+ If receiving object don't know right key, it must ignore encrypted signal in its receive_signal.
+
+*/
+
+/*
+Frequency range: 1200 to 1600
+Radiochat range: 1441 to 1489 (most devices refuse to be tune to other frequency, even during mapmaking)
+
+Radio:
+1459 - standard radio chat
+1351 - Science
+1353 - Command
+1355 - Medical
+1357 - Engineering
+1359 - Security
+1441 - death squad
+1443 - Confession Intercom
+1347 - Cargo techs
+
+Devices:
+1451 - tracking implant
+1457 - RSD default
+
+On the map:
+1311 for prison shuttle console (in fact, it is not used)
+1435 for status displays
+1437 for atmospherics/fire alerts
+1438 for engine components
+1439 for air pumps, air scrubbers, atmo control
+1441 for atmospherics - supply tanks
+1443 for atmospherics - distribution loop/mixed air tank
+1445 for bot nav beacons
+1447 for mulebot, secbot and ed209 control
+1449 for airlock controls, electropack, magnets
+1451 for toxin lab access
+1453 for engineering access
+1455 for AI access
+*/
+
+var/list/radiochannels = list(
+ "Common" = 1459,
+ "Science" = 1351,
+ "Command" = 1353,
+ "Medical" = 1355,
+ "Engineering" = 1357,
+ "Security" = 1359,
+ "Response Team" = 1345,
+ "Deathsquad" = 1341,
+ "Syndicate" = 1213,
+ "Supply" = 1347,
+)
+//depenging helpers
+var/list/DEPT_FREQS = list(1351, 1355, 1357, 1359, 1213, 1345, 1341, 1347)
+
+// central command channels, i.e deathsquid & response teams
+var/list/CENT_FREQS = list(1345, 1341)
+
+var/const/COMM_FREQ = 1353 //command, colored gold in chat window
+var/const/SYND_FREQ = 1213
+
+// department channels
+var/const/SEC_FREQ = 1359
+var/const/ENG_FREQ = 1357
+var/const/SCI_FREQ = 1351
+var/const/MED_FREQ = 1355
+var/const/SUP_FREQ = 1347
+
+#define TRANSMISSION_WIRE 0
+#define TRANSMISSION_RADIO 1
+
+/* filters */
+//When devices register with the radio controller, they might register under a certain filter.
+//Other devices can then choose to send signals to only those devices that belong to a particular filter.
+//This is done for performance, so we don't send signals to lots of machines unnecessarily.
+
+//This filter is special because devices belonging to default also recieve signals sent to any other filter.
+var/const/RADIO_DEFAULT = "radio_default"
+
+var/const/RADIO_TO_AIRALARM = "radio_airalarm" //air alarms
+var/const/RADIO_FROM_AIRALARM = "radio_airalarm_rcvr" //devices interested in recieving signals from air alarms
+var/const/RADIO_CHAT = "radio_telecoms"
+var/const/RADIO_ATMOSIA = "radio_atmos"
+var/const/RADIO_NAVBEACONS = "radio_navbeacon"
+var/const/RADIO_AIRLOCK = "radio_airlock"
+var/const/RADIO_SECBOT = "radio_secbot"
+var/const/RADIO_MULEBOT = "radio_mulebot"
+var/const/RADIO_MAGNETS = "radio_magnet"
+
+var/global/datum/controller/radio/radio_controller
+
+/hook/startup/proc/createRadioController()
+ radio_controller = new /datum/controller/radio()
+ return 1
+
+//callback used by objects to react to incoming radio signals
+/obj/proc/receive_signal(datum/signal/signal, receive_method, receive_param)
+ return null
+
+//The global radio controller
+/datum/controller/radio
+ var/list/datum/radio_frequency/frequencies = list()
+
+/datum/controller/radio/proc/add_object(obj/device as obj, var/new_frequency as num, var/filter = null as text|null)
+ var/f_text = num2text(new_frequency)
+ var/datum/radio_frequency/frequency = frequencies[f_text]
+
+ if(!frequency)
+ frequency = new
+ frequency.frequency = new_frequency
+ frequencies[f_text] = frequency
+
+ frequency.add_listener(device, filter)
+ return frequency
+
+/datum/controller/radio/proc/remove_object(obj/device, old_frequency)
+ var/f_text = num2text(old_frequency)
+ var/datum/radio_frequency/frequency = frequencies[f_text]
+
+ if(frequency)
+ frequency.remove_listener(device)
+
+ if(frequency.devices.len == 0)
+ del(frequency)
+ frequencies -= f_text
+
+ return 1
+
+/datum/controller/radio/proc/return_frequency(var/new_frequency as num)
+ var/f_text = num2text(new_frequency)
+ var/datum/radio_frequency/frequency = frequencies[f_text]
+
+ if(!frequency)
+ frequency = new
+ frequency.frequency = new_frequency
+ frequencies[f_text] = frequency
+
+ return frequency
+
+/datum/radio_frequency
+ var/frequency as num
+ var/list/list/obj/devices = list()
+
+/datum/radio_frequency/proc/post_signal(obj/source as obj|null, datum/signal/signal, var/filter = null as text|null, var/range = null as num|null)
+ var/turf/start_point
+ if(range)
+ start_point = get_turf(source)
+ if(!start_point)
+ del(signal)
+ return 0
+ if (filter)
+ send_to_filter(source, signal, filter, start_point, range)
+ send_to_filter(source, signal, RADIO_DEFAULT, start_point, range)
+ else
+ //Broadcast the signal to everyone!
+ for (var/next_filter in devices)
+ send_to_filter(source, signal, next_filter, start_point, range)
+
+//Sends a signal to all machines belonging to a given filter. Should be called by post_signal()
+/datum/radio_frequency/proc/send_to_filter(obj/source, datum/signal/signal, var/filter, var/turf/start_point = null, var/range = null)
+ if (range && !start_point)
+ return
+
+ for(var/obj/device in devices[filter])
+ if(device == source)
+ continue
+ if(range)
+ var/turf/end_point = get_turf(device)
+ if(!end_point)
+ continue
+ if(start_point.z!=end_point.z || get_dist(start_point, end_point) > range)
+ continue
+
+ //allow sequential signals before round start, so that every air alarm sounding off at once doesn't cause trouble.
+ if(!ticker || ticker.current_state < 3)
+ device.receive_signal(signal, TRANSMISSION_RADIO, frequency)
+ else
+ spawn(0)
+ if(device) //in case the device got destroyed somehow
+ device.receive_signal(signal, TRANSMISSION_RADIO, frequency)
+
+/datum/radio_frequency/proc/add_listener(obj/device as obj, var/filter as text|null)
+ if (!filter)
+ filter = RADIO_DEFAULT
+ //log_admin("add_listener(device=[device],filter=[filter]) frequency=[frequency]")
+ var/list/obj/devices_line = devices[filter]
+ if (!devices_line)
+ devices_line = new
+ devices[filter] = devices_line
+ devices_line+=device
+// var/list/obj/devices_line___ = devices[filter_str]
+// var/l = devices_line___.len
+ //log_admin("DEBUG: devices_line.len=[devices_line.len]")
+ //log_admin("DEBUG: devices(filter_str).len=[l]")
+
+/datum/radio_frequency/proc/remove_listener(obj/device)
+ for (var/devices_filter in devices)
+ var/list/devices_line = devices[devices_filter]
+ devices_line-=device
+ while (null in devices_line)
+ devices_line -= null
+ if (devices_line.len==0)
+ devices -= devices_filter
+ del(devices_line)
+
+/datum/signal
+ var/obj/source
+
+ var/transmission_method = 0 //unused at the moment
+ //0 = wire
+ //1 = radio transmission
+ //2 = subspace transmission
+
+ var/data = list()
+ var/encryption
+
+ var/frequency = 0
+
+/datum/signal/proc/copy_from(datum/signal/model)
+ source = model.source
+ transmission_method = model.transmission_method
+ data = model.data
+ encryption = model.encryption
+ frequency = model.frequency
+
+/datum/signal/proc/debug_print()
+ if (source)
+ . = "signal = {source = '[source]' ([source:x],[source:y],[source:z])\n"
+ else
+ . = "signal = {source = '[source]' ()\n"
+ for (var/i in data)
+ . += "data\[\"[i]\"\] = \"[data[i]]\"\n"
+ if(islist(data[i]))
+ var/list/L = data[i]
+ for(var/t in L)
+ . += "data\[\"[i]\"\] list has: [t]"
diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm
index f9d825d8963..2ac8ca7a6a4 100644
--- a/code/controllers/configuration.dm
+++ b/code/controllers/configuration.dm
@@ -98,6 +98,8 @@
//game_options.txt configs
+ var/atmos_machine_heat = 1.0
+
var/health_threshold_softcrit = 0
var/health_threshold_crit = 0
var/health_threshold_dead = -100
@@ -112,6 +114,8 @@
var/revival_cloning = 1
var/revival_brain_life = -1
+ var/use_loyalty_implants = 0
+
//Used for modifying movement speed for mobs.
//Unversal modifiers
var/run_speed = 0
@@ -521,10 +525,23 @@
config.revival_cloning = value
if("revival_brain_life")
config.revival_brain_life = value
+ if("organ_health_multiplier")
+ config.organ_health_multiplier = value / 100
+ if("organ_regeneration_multiplier")
+ config.organ_regeneration_multiplier = value / 100
+ if("bones_can_break")
+ config.bones_can_break = value
+ if("limbs_can_break")
+ config.limbs_can_break = value
+
+ if("atmos_machine_heat")
+ config.atmos_machine_heat = value
+
if("run_speed")
config.run_speed = value
if("walk_speed")
config.walk_speed = value
+
if("human_delay")
config.human_delay = value
if("robot_delay")
@@ -537,14 +554,11 @@
config.slime_delay = value
if("animal_delay")
config.animal_delay = value
- if("organ_health_multiplier")
- config.organ_health_multiplier = value / 100
- if("organ_regeneration_multiplier")
- config.organ_regeneration_multiplier = value / 100
- if("bones_can_break")
- config.bones_can_break = value
- if("limbs_can_break")
- config.limbs_can_break = value
+
+
+ if("use_loyalty_implants")
+ config.use_loyalty_implants = 1
+
else
log_misc("Unknown setting in configuration: '[name]'")
diff --git a/code/controllers/lighting_controller.dm b/code/controllers/lighting_controller.dm
index 3d4f859efaf..ccd883a3938 100644
--- a/code/controllers/lighting_controller.dm
+++ b/code/controllers/lighting_controller.dm
@@ -16,6 +16,8 @@ datum/controller/lighting
var/list/changed_turfs = list()
var/changed_turfs_workload_max = 0
+ var/list/changed_areas = list()
+
datum/controller/lighting/New()
lighting_states = max( 0, length(icon_states(LIGHTING_ICON))-1 )
if(lighting_controller != src)
@@ -53,9 +55,19 @@ datum/controller/lighting/proc/process()
for(var/i=1, i<=changed_turfs.len, i++)
var/turf/T = changed_turfs[i]
if(T && T.lighting_changed)
+ changed_areas |= T.loc
T.shift_to_subarea()
changed_turfs.Cut() // reset the changed list
+ for(var/i = 1; i <= changed_areas.len, i++)
+ var/area/A = changed_areas[i]
+ if(A.master != A && !A.contents.len)
+ A.tag = null
+ A.related -= A
+ active_areas -= A
+ all_areas -= A
+ changed_areas.Cut()
+
process_cost = (world.timeofday - started)
sleep(processing_interval)
diff --git a/code/controllers/master_controller.dm b/code/controllers/master_controller.dm
index 0b314a25312..a9d101f7de8 100644
--- a/code/controllers/master_controller.dm
+++ b/code/controllers/master_controller.dm
@@ -109,17 +109,24 @@ datum/controller/game_controller/proc/setup_objects()
var/obj/machinery/atmospherics/unary/vent_scrubber/T = U
T.broadcast_status()
- //Create the mining ore distribution map.
//Create the mining ore distribution map.
asteroid_ore_map = new /datum/ore_distribution()
asteroid_ore_map.populate_distribution_map()
+ //Shitty hack to fix mining turf overlays, for some reason New() is not being called.
+ for(var/turf/simulated/floor/plating/airless/asteroid/T in world)
+ T.updateMineralOverlays()
+ T.name = "asteroid"
+
//Set up spawn points.
populate_spawn_points()
//Set up gear list.
populate_gear_list()
+ //Set up roundstart seed list.
+ populate_seed_list()
+
world << "\red \b Initializations complete."
sleep(-1)
@@ -292,10 +299,16 @@ datum/controller/game_controller/proc/process_machines_power()
var/area/A = active_areas[i]
if(A.powerupdate && A.master == A)
A.powerupdate -= 1
- for(var/area/SubArea in A.related)
+ A.clear_usage()
+ for(var/j = 1; j <= A.related.len; j++)
+ var/area/SubArea = A.related[j]
for(var/obj/machinery/M in SubArea)
if(M)
- if(M.use_power)
+ //check if the area has power for M's channel
+ //this will keep stat updated in case the machine is moved from one area to another.
+ M.power_change(A) //we've already made sure A is a master area, above.
+
+ if(!(M.stat & NOPOWER) && M.use_power)
M.auto_use_power()
if(A.apc.len && A.master == A)
@@ -342,7 +355,7 @@ datum/controller/game_controller/proc/process_powernets()
while(i<=powernets.len)
var/datum/powernet/Powernet = powernets[i]
if(Powernet)
- Powernet.reset()
+ Powernet.process()
i++
continue
powernets.Cut(i,i+1)
diff --git a/code/controllers/verbs.dm b/code/controllers/verbs.dm
index a331dab556c..b4ff1ed7ca8 100644
--- a/code/controllers/verbs.dm
+++ b/code/controllers/verbs.dm
@@ -48,7 +48,7 @@
message_admins("Admin [key_name_admin(usr)] has restarted the [controller] controller.")
return
-/client/proc/debug_controller(controller in list("Master","Failsafe","Ticker","Lighting","Air","Jobs","Sun","Radio","Supply","Shuttles","Emergency Shuttle","Configuration","pAI", "Cameras", "Transfer Controller"))
+/client/proc/debug_controller(controller in list("Master","Failsafe","Ticker","Lighting","Air","Jobs","Sun","Radio","Supply","Shuttles","Emergency Shuttle","Configuration","pAI", "Cameras", "Transfer Controller", "Gas Data"))
set category = "Debug"
set name = "Debug Controller"
set desc = "Debug the various periodic loop controllers for the game (be careful!)"
@@ -100,5 +100,8 @@
if("Transfer Controller")
debug_variables(transfer_controller)
feedback_add_details("admin_verb","DAutovoter")
+ if("Gas Data")
+ debug_variables(gas_data)
+ feedback_add_details("admin_verv","DGasdata")
message_admins("Admin [key_name_admin(usr)] is debugging the [controller] controller.")
return
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index 111c93d9d74..34dc2e13448 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -510,14 +510,9 @@ datum/mind
I.Del()
break
H << "\blue Your loyalty implant has been deactivated."
- if("add")
- var/obj/item/weapon/implant/loyalty/L = new/obj/item/weapon/implant/loyalty(H)
- L.imp_in = H
- L.implanted = 1
- var/datum/organ/external/affected = H.organs_by_name["head"]
- affected.implants += L
- L.part = affected
+ if("add")
+ H.implant_loyalty(H, override = TRUE)
H << "\red You somehow have become the recepient of a loyalty transplant, and it just activated!"
if(src in ticker.mode.revolutionaries)
special_role = null
@@ -541,6 +536,8 @@ datum/mind
special_role = null
current << "\red The nanobots in the loyalty implant remove all thoughts about being a traitor to Nanotrasen. Have a nice day!"
log_admin("[key_name_admin(usr)] has de-traitor'ed [current].")
+ else
+
else if (href_list["revolution"])
current.hud_updateflag |= (1 << SPECIALROLE_HUD)
@@ -567,7 +564,7 @@ datum/mind
current << "\red Revolution has been disappointed of your leader traits! You are a regular revolutionary now!"
else if(!(src in ticker.mode.revolutionaries))
current << "\red You are now a revolutionary! Help your cause. Do not harm your fellow freedom fighters. You can identify your comrades by the red \"R\" icons, and your leaders by the blue \"R\" icons. Help them kill the heads to win the revolution!"
- current << "Within the rules, try to act as an opposing force to the crew. Further RP and try to make sure other players have fun! If you are confused or at a loss, always adminhelp, and before taking extreme actions, please try to also contact the administration! Think through your actions and make the roleplay immersive! Please remember all rules aside from those without explicit exceptions apply to antagonists."
+ show_objectives(src)
else
return
ticker.mode.revolutionaries += src
@@ -580,7 +577,7 @@ datum/mind
ticker.mode.revolutionaries -= src
ticker.mode.update_rev_icons_removed(src)
current << "\red You have proved your devotion to revoltion! You are a head revolutionary now!"
- current << "Within the rules, try to act as an opposing force to the crew. Further RP and try to make sure other players have fun! If you are confused or at a loss, always adminhelp, and before taking extreme actions, please try to also contact the administration! Think through your actions and make the roleplay immersive! Please remember all rules aside from those without explicit exceptions apply to antagonists."
+ show_objectives(src)
else if(!(src in ticker.mode.head_revolutionaries))
current << "\blue You are a member of the revolutionaries' leadership now!"
else
@@ -659,11 +656,11 @@ datum/mind
special_role = "Cultist"
current << "You catch a glimpse of the Realm of Nar-Sie, The Geometer of Blood. You now see how flimsy the world is, you see that it should be open to the knowledge of Nar-Sie."
current << "Assist your new compatriots in their dark dealings. Their goal is yours, and yours is theirs. You serve the Dark One above all else. Bring It back."
- current << "Within the rules, try to act as an opposing force to the crew. Further RP and try to make sure other players have fun! If you are confused or at a loss, always adminhelp, and before taking extreme actions, please try to also contact the administration! Think through your actions and make the roleplay immersive! Please remember all rules aside from those without explicit exceptions apply to antagonists."
var/datum/game_mode/cult/cult = ticker.mode
if (istype(cult))
if(!config.objectives_disabled)
cult.memorize_cult_objectives(src)
+ show_objectives(src)
log_admin("[key_name_admin(usr)] has cult'ed [current].")
if("tome")
var/mob/living/carbon/human/H = current
@@ -704,7 +701,7 @@ datum/mind
special_role = "Wizard"
//ticker.mode.learn_basic_spells(current)
current << "\red You are the Space Wizard!"
- current << "Within the rules, try to act as an opposing force to the crew. Further RP and try to make sure other players have fun! If you are confused or at a loss, always adminhelp, and before taking extreme actions, please try to also contact the administration! Think through your actions and make the roleplay immersive! Please remember all rules aside from those without explicit exceptions apply to antagonists."
+ show_objectives(src)
log_admin("[key_name_admin(usr)] has wizard'ed [current].")
if("lair")
current.loc = pick(wizardstart)
@@ -735,8 +732,7 @@ datum/mind
ticker.mode.grant_changeling_powers(current)
special_role = "Changeling"
current << "Your powers are awoken. A flash of memory returns to us...we are a changeling!"
- if(config.objectives_disabled)
- current << "Within the rules, try to act as an opposing force to the crew. Further RP and try to make sure other players have fun! If you are confused or at a loss, always adminhelp, and before taking extreme actions, please try to also contact the administration! Think through your actions and make the roleplay immersive! Please remember all rules aside from those without explicit exceptions apply to antagonists."
+ show_objectives(src)
log_admin("[key_name_admin(usr)] has changeling'ed [current].")
if("autoobjectives")
if(!config.objectives_disabled)
@@ -777,10 +773,7 @@ datum/mind
current.real_name = "[syndicate_name()] Operative #[ticker.mode.syndicates.len-1]"
special_role = "Syndicate"
current << "\blue You are a [syndicate_name()] agent!"
- if(config.objectives_disabled)
- current << "Within the rules, try to act as an opposing force to the crew. Further RP and try to make sure other players have fun! If you are confused or at a loss, always adminhelp, and before taking extreme actions, please try to also contact the administration! Think through your actions and make the roleplay immersive! Please remember all rules aside from those without explicit exceptions apply to antagonists."
- else
- ticker.mode.forge_syndicate_objectives(src)
+ ticker.mode.forge_syndicate_objectives(src)
ticker.mode.greet_syndicate(src)
log_admin("[key_name_admin(usr)] has nuke op'ed [current].")
if("lair")
@@ -832,8 +825,8 @@ datum/mind
special_role = "traitor"
current << "\red You are a traitor!"
log_admin("[key_name_admin(usr)] has traitor'ed [current].")
- if (config.objectives_disabled)
- current << "You have been turned into an antagonist- Within the rules, try to act as an opposing force to the crew- This can be via corporate payoff, personal motives, or maybe just being a dick. Further RP and try to make sure other players have fun! If you are confused or at a loss, always adminhelp, and before taking extreme actions, please try to also contact the administration! Think through your actions and make the roleplay immersive! Please remember all rules aside from those without explicit exceptions apply to antagonist."
+ show_objectives()
+
if(istype(current, /mob/living/silicon))
var/mob/living/silicon/A = current
call(/datum/game_mode/proc/add_law_zero)(A)
diff --git a/code/datums/mixed.dm b/code/datums/mixed.dm
index 6732e2a8b94..f0f0c70abda 100644
--- a/code/datums/mixed.dm
+++ b/code/datums/mixed.dm
@@ -30,20 +30,5 @@
-/datum/powernet
- var/list/cables = list() // all cables & junctions
- var/list/nodes = list() // all APCs & sources
-
- var/newload = 0
- var/load = 0
- var/newavail = 0
- var/avail = 0
- var/viewload = 0
- var/number = 0
- var/perapc = 0 // per-apc avilability
- var/netexcess = 0
-
-
-
/datum/debug
var/list/debuglist
diff --git a/code/datums/supplypacks.dm b/code/datums/supplypacks.dm
index 80cd56e8529..f0e0e316441 100755
--- a/code/datums/supplypacks.dm
+++ b/code/datums/supplypacks.dm
@@ -5,7 +5,7 @@
//BIG NOTE: Don't add living things to crates, that's bad, it will break the shuttle.
//NEW NOTE: Do NOT set the price of any crates below 7 points. Doing so allows infinite points.
-var/list/all_supply_groups = list("Operations","Security","Hospitality","Engineering","Medical / Science","Hydroponics", "Supply", "Miscellaneous")
+var/list/all_supply_groups = list("Operations","Security","Hospitality","Engineering","Atmospherics","Medical","Science","Hydroponics", "Supply", "Miscellaneous")
/datum/supply_packs
var/name = null
@@ -149,7 +149,7 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
cost = 10
containertype = /obj/structure/closet/crate/internals
containername = "Internals crate"
- group = "Engineering"
+ group = "Atmospherics"
/datum/supply_packs/evacuation
name = "Emergency equipment"
@@ -170,7 +170,7 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
cost = 35
containertype = /obj/structure/closet/crate/internals
containername = "Emergency crate"
- group = "Engineering"
+ group = "Atmospherics"
/datum/supply_packs/inflatable
name = "Inflatable barriers"
@@ -180,7 +180,7 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
cost = 20
containertype = /obj/structure/closet/crate
containername = "Inflatable Barrier Crate"
- group = "Engineering"
+ group = "Atmospherics"
/datum/supply_packs/janitor
name = "Janitorial supplies"
@@ -235,7 +235,7 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
/datum/supply_packs/cargotrain
name = "Cargo Train Tug"
contains = list(/obj/vehicle/train/cargo/engine)
- cost = 30
+ cost = 45
containertype = /obj/structure/largecrate
containername = "Cargo Train Tug Crate"
group = "Operations"
@@ -243,7 +243,7 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
/datum/supply_packs/cargotrailer
name = "Cargo Train Trolley"
contains = list(/obj/vehicle/train/cargo/trolley)
- cost = 20
+ cost = 15
containertype = /obj/structure/largecrate
containername = "Cargo Train Trolley Crate"
group = "Operations"
@@ -266,7 +266,10 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
/obj/item/weapon/minihoe,
/obj/item/device/analyzer/plant_analyzer,
/obj/item/clothing/gloves/botanic_leather,
- /obj/item/clothing/suit/apron) // Updated with new things
+ /obj/item/clothing/suit/apron,
+ /obj/item/weapon/minihoe,
+ /obj/item/weapon/storage/box/botanydisk
+ ) // Updated with new things
cost = 15
containertype = /obj/structure/closet/crate/hydroponics
containername = "Hydroponics crate"
@@ -380,7 +383,7 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
cost = 10
containertype = /obj/structure/closet/crate/medical
containername = "Medical crate"
- group = "Medical / Science"
+ group = "Medical"
/datum/supply_packs/virus
name = "Virus sample crate"
@@ -404,7 +407,7 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
containertype = "/obj/structure/closet/crate/secure"
containername = "Virus sample crate"
access = access_cmo
- group = "Medical / Science"
+ group = "Science"
/datum/supply_packs/metal50
name = "50 metal sheets"
@@ -486,7 +489,7 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
cost = 16
containertype = /obj/structure/largecrate
containername = "coolant tank crate"
- group = "Medical / Science"
+ group = "Science"
/datum/supply_packs/solar
name = "Solar Pack crate"
@@ -578,7 +581,7 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
containertype = /obj/structure/closet/crate/secure
containername = "APLU \"Ripley\" Circuit Crate"
access = access_robotics
- group = "Engineering"
+ group = "Science"
/datum/supply_packs/mecha_odysseus
name = "Circuit Crate (\"Odysseus\")"
@@ -588,7 +591,7 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
containertype = /obj/structure/closet/crate/secure
containername = "\"Odysseus\" Circuit Crate"
access = access_robotics
- group = "Engineering"
+ group = "Science"
/datum/supply_packs/robotics
@@ -627,7 +630,7 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
containertype = /obj/structure/closet/crate/secure/phoron
containername = "Phoron assembly crate"
access = access_tox_storage
- group = "Medical / Science"
+ group = "Science"
/datum/supply_packs/weapons
name = "Weapons crate"
@@ -697,14 +700,6 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
access = access_armory
group = "Security"
-/datum/supply_packs/loyalty
- name = "Loyalty implant crate"
- contains = list (/obj/item/weapon/storage/lockbox/loyalty)
- cost = 60
- containertype = /obj/structure/closet/crate/secure
- containername = "Loyalty implant crate"
- access = access_armory
- group = "Security"
/datum/supply_packs/ballistic
name = "Ballistic gear crate"
@@ -741,6 +736,17 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
access = access_armory
group = "Security"
+/*
+/datum/supply_packs/loyalty
+ name = "Loyalty implant crate"
+ contains = list (/obj/item/weapon/storage/lockbox/loyalty)
+ cost = 60
+ containertype = /obj/structure/closet/crate/secure
+ containername = "Loyalty implant crate"
+ access = access_armory
+ group = "Security"
+*/
+
/datum/supply_packs/expenergy
name = "Experimental energy gear crate"
contains = list(/obj/item/clothing/suit/armor/laserproof,
@@ -845,6 +851,7 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
containername = "Arts and Crafts crate"
group = "Operations"
+
/datum/supply_packs/randomised/contraband
num_contained = 5
contains = list(/obj/item/seeds/bloodtomatoseed,
@@ -893,7 +900,7 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
containertype = "/obj/structure/closet/crate/secure"
containername = "Surgery crate"
access = access_medical
- group = "Medical / Science"
+ group = "Medical"
/datum/supply_packs/sterile
name = "Sterile equipment crate"
@@ -904,7 +911,7 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
cost = 15
containertype = "/obj/structure/closet/crate"
containername = "Sterile equipment crate"
- group = "Medical / Science"
+ group = "Medical"
/datum/supply_packs/randomised/pizza
num_contained = 5
@@ -1202,7 +1209,7 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
containertype = /obj/structure/closet/crate/hydroponics
containername = "Hydroponics tray crate"
group = "Hydroponics"
- contains = list(/obj/machinery/hydroponics{anchored = 0})
+ contains = list(/obj/machinery/portable_atmospherics/hydroponics{anchored = 0})
access = access_hydroponics
/datum/supply_packs/canister_empty
@@ -1211,14 +1218,14 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
containername = "Empty gas canister crate"
containertype = /obj/structure/largecrate
contains = list(/obj/machinery/portable_atmospherics/canister)
- group = "Engineering"
+ group = "Atmospherics"
/datum/supply_packs/canister_air
name = "Air canister"
cost = 10
containername = "Air canister crate"
containertype = /obj/structure/largecrate
- group = "Engineering"
+ group = "Atmospherics"
contains = list(/obj/machinery/portable_atmospherics/canister/air)
/datum/supply_packs/canister_oxygen
@@ -1226,7 +1233,7 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
cost = 15
containername = "Oxygen canister crate"
containertype = /obj/structure/largecrate
- group = "Engineering"
+ group = "Atmospherics"
contains = list(/obj/machinery/portable_atmospherics/canister/oxygen)
/datum/supply_packs/canister_nitrogen
@@ -1234,7 +1241,7 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
cost = 10
containername = "Nitrogen canister crate"
containertype = /obj/structure/largecrate
- group = "Engineering"
+ group = "Atmospherics"
contains = list(/obj/machinery/portable_atmospherics/canister/nitrogen)
/datum/supply_packs/canister_phoron
@@ -1243,7 +1250,7 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
containername = "Phoron gas canister crate"
containertype = /obj/structure/closet/crate/secure/large
access = access_atmospherics
- group = "Engineering"
+ group = "Atmospherics"
contains = list(/obj/machinery/portable_atmospherics/canister/phoron)
/datum/supply_packs/canister_sleeping_agent
@@ -1252,7 +1259,7 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
containername = "N2O gas canister crate"
containertype = /obj/structure/closet/crate/secure/large
access = access_atmospherics
- group = "Engineering"
+ group = "Atmospherics"
contains = list(/obj/machinery/portable_atmospherics/canister/sleeping_agent)
/datum/supply_packs/canister_carbon_dioxide
@@ -1261,7 +1268,7 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
containername = "CO2 canister crate"
containertype = /obj/structure/closet/crate/secure/large
access = access_atmospherics
- group = "Engineering"
+ group = "Atmospherics"
contains = list(/obj/machinery/portable_atmospherics/canister/carbon_dioxide)
/datum/supply_packs/pacman_parts
@@ -1276,7 +1283,7 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
/obj/item/weapon/stock_parts/matter_bin,
/obj/item/weapon/circuitboard/pacman)
-/datum/supply_packs/pacman_parts
+/datum/supply_packs/super_pacman_parts
name = "Super P.A.C.M.A.N. portable generator parts"
cost = 55
containername = "Super P.A.C.M.A.N. portable generator construction kit"
diff --git a/code/defines/obj/weapon.dm b/code/defines/obj/weapon.dm
index 99190c60711..2fc7d0c8eeb 100644
--- a/code/defines/obj/weapon.dm
+++ b/code/defines/obj/weapon.dm
@@ -52,8 +52,10 @@
icon_state = "soapnt"
/obj/item/weapon/soap/deluxe
- desc = "A deluxe Waffle Co. brand bar of soap. Smells of condoms."
icon_state = "soapdeluxe"
+
+/obj/item/weapon/soap/deluxe/New()
+ desc = "A deluxe Waffle Co. brand bar of soap. Smells of [pick("lavender", "vanilla", "strawberry", "chocolate" ,"space")]."
/obj/item/weapon/soap/syndie
desc = "An untrustworthy bar of soap. Smells of fear."
@@ -162,7 +164,7 @@
icon_state = "beartrap[armed]"
user << "[src] is now [armed ? "armed" : "disarmed"]"
-/obj/item/weapon/legcuffs/beartrap/HasEntered(AM as mob|obj)
+/obj/item/weapon/legcuffs/beartrap/Crossed(AM as mob|obj)
if(armed)
if(ishuman(AM))
if(isturf(src.loc))
diff --git a/code/defines/procs/captain_announce.dm b/code/defines/procs/captain_announce.dm
index 9b91705ea56..092d297a2de 100644
--- a/code/defines/procs/captain_announce.dm
+++ b/code/defines/procs/captain_announce.dm
@@ -1,5 +1,6 @@
-/proc/captain_announce(var/text)
- world << "
Priority Announcement
"
- world << "[html_encode(text)]"
- world << " "
-
+/proc/captain_announce(message as text, title = "Priority Announcement", announcer = "")
+ world << "
[html_encode(title)]
"
+ world << "[html_encode(message)]"
+ if(announcer)
+ world << " -[html_encode(announcer)]"
+ world << " "
\ No newline at end of file
diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm
index 88b4294db56..877f21b7b58 100755
--- a/code/game/area/Space Station 13 areas.dm
+++ b/code/game/area/Space Station 13 areas.dm
@@ -22,7 +22,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
var/poweralm = 1
var/party = null
level = null
- name = "Space"
+ name = "Unknown"
icon = 'icons/turf/areas.dmi'
icon_state = "unknown"
layer = 10
@@ -35,6 +35,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
var/debug = 0
var/powerupdate = 10 //We give everything 10 ticks to settle out it's power usage.
var/requires_power = 1
+ var/unlimited_power = 0
var/always_unpowered = 0 //this gets overriden to 1 for space in area/New()
var/power_equip = 1
@@ -54,6 +55,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
// var/list/lights // list of all lights on this area
var/list/all_doors = list() //Added by Strumpetplaya - Alarm Change - Contains a list of doors adjacent to this area
var/air_doors_activated = 0
+ var/list/ambience = list('sound/ambience/ambigen1.ogg','sound/ambience/ambigen3.ogg','sound/ambience/ambigen4.ogg','sound/ambience/ambigen5.ogg','sound/ambience/ambigen6.ogg','sound/ambience/ambigen7.ogg','sound/ambience/ambigen8.ogg','sound/ambience/ambigen9.ogg','sound/ambience/ambigen10.ogg','sound/ambience/ambigen11.ogg','sound/ambience/ambigen12.ogg','sound/ambience/ambigen14.ogg')
/*Adding a wizard area teleport list because motherfucking lag -- Urist*/
/*I am far too lazy to make it a proper list of areas so I'll just make it run the usual telepot routine at the start of the game*/
@@ -90,9 +92,26 @@ var/list/ghostteleportlocs = list()
return 1
/*-----------------------------------------------------------------------------*/
+/area/space
+ name = "\improper Space"
+ requires_power = 1
+ always_unpowered = 1
+ lighting_use_dynamic = 1
+ power_light = 0
+ power_equip = 0
+ power_environ = 0
+ ambience = list('sound/ambience/ambispace.ogg','sound/music/title2.ogg','sound/music/space.ogg','sound/music/main.ogg','sound/music/traitor.ogg')
+/area/space/firealert()
+ return
+
+/area/space/readyalert()
+ return
+
+/area/space/partyalert()
+ return
/area/engine/
-
+ ambience = list('sound/ambience/ambisin1.ogg','sound/ambience/ambisin2.ogg','sound/ambience/ambisin3.ogg','sound/ambience/ambisin4.ogg')
/area/turret_protected/
/area/arrival
@@ -361,9 +380,10 @@ var/list/ghostteleportlocs = list()
//SYNDICATES
/area/syndicate_mothership
- name = "\improper Syndicate Mothership"
+ name = "\improper Syndicate Base"
icon_state = "syndie-ship"
requires_power = 0
+ unlimited_power = 1
/area/syndicate_mothership/control
name = "\improper Syndicate Control Room"
@@ -436,6 +456,7 @@ var/list/ghostteleportlocs = list()
name = "\improper Syndicate Station"
icon_state = "yellow"
requires_power = 0
+ unlimited_power = 1
/area/syndicate_station/start
name = "\improper Syndicate Forward Operating Base"
@@ -866,6 +887,7 @@ var/list/ghostteleportlocs = list()
/area/chapel/main
name = "\improper Chapel"
icon_state = "chapel"
+ ambience = list('sound/ambience/ambicha1.ogg','sound/ambience/ambicha2.ogg','sound/ambience/ambicha3.ogg','sound/ambience/ambicha4.ogg','sound/music/traitor.ogg')
/area/chapel/office
name = "\improper Chapel Office"
@@ -970,6 +992,10 @@ var/list/ghostteleportlocs = list()
name = "\improper Engine Monitoring Room"
icon_state = "engine_monitoring"
+ engine_waste
+ name = "\improper Engine Waste Handling"
+ icon_state = "engine_waste"
+
engineering_monitoring
name = "\improper Engineering Monitoring Room"
icon_state = "engine_monitoring"
@@ -1022,7 +1048,8 @@ var/list/ghostteleportlocs = list()
//Solars
/area/solar
- requires_power = 0
+ requires_power = 1
+ always_unpowered = 1
luminosity = 1
lighting_use_dynamic = 0
@@ -1052,7 +1079,7 @@ var/list/ghostteleportlocs = list()
/area/maintenance/auxsolarport
name = "Fore Port Solar Maintenance"
- icon_state = "SolarcontrolA"
+ icon_state = "SolarcontrolP"
/area/maintenance/starboardsolar
name = "Aft Starboard Solar Maintenance"
@@ -1064,8 +1091,11 @@ var/list/ghostteleportlocs = list()
/area/maintenance/auxsolarstarboard
name = "Fore Starboard Solar Maintenance"
- icon_state = "SolarcontrolA"
+ icon_state = "SolarcontrolS"
+/area/maintenance/foresolar
+ name = "Fore Solar Maintenance"
+ icon_state = "SolarcontrolA"
/area/assembly/chargebay
name = "\improper Mech Bay"
@@ -1102,6 +1132,7 @@ var/list/ghostteleportlocs = list()
name = "\improper AI Satellite Teleporter Room"
icon_state = "teleporter"
music = "signal"
+ ambience = list('sound/ambience/ambimalf.ogg')
//MedBay
@@ -1177,10 +1208,6 @@ var/list/ghostteleportlocs = list()
name = "\improper Robotics"
icon_state = "medresearch"
-/area/medical/research
- name = "\improper Medical Research"
- icon_state = "medresearch"
-
/area/medical/virology
name = "\improper Virology"
icon_state = "virology"
@@ -1192,6 +1219,7 @@ var/list/ghostteleportlocs = list()
/area/medical/morgue
name = "\improper Morgue"
icon_state = "morgue"
+ ambience = list('sound/ambience/ambimo1.ogg','sound/ambience/ambimo2.ogg','sound/music/main.ogg')
/area/medical/chemistry
name = "\improper Chemistry"
@@ -1378,12 +1406,11 @@ var/list/ghostteleportlocs = list()
icon_state = "garden"
//rnd (Research and Development
+/area/rnd/research
+ name = "\improper Research and Development"
+ icon_state = "research"
/area/rnd/lab
- name = "\improper Research and Development"
- icon_state = "toxlab"
-
-/area/rnd/hallway
name = "\improper Research Lab"
icon_state = "toxlab"
@@ -1692,10 +1719,12 @@ var/list/ghostteleportlocs = list()
/area/turret_protected/ai_upload
name = "\improper AI Upload Chamber"
icon_state = "ai_upload"
+ ambience = list('sound/ambience/ambimalf.ogg')
/area/turret_protected/ai_upload_foyer
name = "AI Upload Access"
icon_state = "ai_foyer"
+ ambience = list('sound/ambience/ambimalf.ogg')
/area/turret_protected/ai_server_room
name = "AI Server Room"
@@ -1704,6 +1733,7 @@ var/list/ghostteleportlocs = list()
/area/turret_protected/ai
name = "\improper AI Chamber"
icon_state = "ai_chamber"
+ ambience = list('sound/ambience/ambimalf.ogg')
/area/turret_protected/ai_cyborg_station
name = "\improper Cyborg Station"
@@ -1774,6 +1804,8 @@ var/list/ghostteleportlocs = list()
// Telecommunications Satellite
+/area/tcommsat/
+ ambience = list('sound/ambience/ambisin2.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/ambigen10.ogg')
/area/tcommsat/entrance
name = "\improper Telecoms Teleporter"
@@ -1786,18 +1818,22 @@ var/list/ghostteleportlocs = list()
/area/turret_protected/tcomsat
name = "\improper Telecoms Satellite"
icon_state = "tcomsatlob"
+ ambience = list('sound/ambience/ambisin2.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/ambigen10.ogg')
/area/turret_protected/tcomfoyer
name = "\improper Telecoms Foyer"
icon_state = "tcomsatentrance"
+ ambience = list('sound/ambience/ambisin2.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/ambigen10.ogg')
/area/turret_protected/tcomwest
name = "\improper Telecommunications Satellite West Wing"
icon_state = "tcomsatwest"
+ ambience = list('sound/ambience/ambisin2.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/ambigen10.ogg')
/area/turret_protected/tcomeast
name = "\improper Telecommunications Satellite East Wing"
icon_state = "tcomsateast"
+ ambience = list('sound/ambience/ambisin2.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/ambigen10.ogg')
/area/tcommsat/computer
name = "\improper Telecoms Control Room"
diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm
index 3d40b556f69..fe468104c16 100644
--- a/code/game/area/areas.dm
+++ b/code/game/area/areas.dm
@@ -16,16 +16,6 @@
active_areas += src
all_areas += src
- if(type == /area) // override defaults for space. TODO: make space areas of type /area/space rather than /area
- requires_power = 1
- always_unpowered = 1
- lighting_use_dynamic = 1
- power_light = 0
- power_equip = 0
- power_environ = 0
-// lighting_state = 4
- //has_gravity = 0 // Space has gravity. Because.. because.
-
if(requires_power)
luminosity = 0
else
@@ -71,18 +61,18 @@
/area/proc/atmosalert(danger_level)
// if(type==/area) //No atmos alarms in space
// return 0 //redudant
-
+
//Check all the alarms before lowering atmosalm. Raising is perfectly fine.
for (var/area/RA in related)
for (var/obj/machinery/alarm/AA in RA)
if ( !(AA.stat & (NOPOWER|BROKEN)) && !AA.shorted)
danger_level = max(danger_level, AA.danger_level)
-
+
if(danger_level != atmosalm)
if (danger_level < 1 && atmosalm >= 1)
//closing the doors on red and opening on green provides a bit of hysteresis that will hopefully prevent fire doors from opening and closing repeatedly due to noise
air_doors_open()
-
+
if (danger_level < 2 && atmosalm >= 2)
for(var/area/RA in related)
for(var/obj/machinery/camera/C in RA)
@@ -91,7 +81,7 @@
aiPlayer.cancelAlarm("Atmosphere", src, src)
for(var/obj/machinery/computer/station_alert/a in machines)
a.cancelAlarm("Atmosphere", src, src)
-
+
if (danger_level >= 2 && atmosalm < 2)
var/list/cameras = list()
for(var/area/RA in related)
@@ -104,12 +94,12 @@
for(var/obj/machinery/computer/station_alert/a in machines)
a.triggerAlarm("Atmosphere", src, cameras, src)
air_doors_close()
-
+
atmosalm = danger_level
for(var/area/RA in related)
for (var/obj/machinery/alarm/AA in RA)
AA.update_icon()
-
+
return 1
return 0
@@ -183,8 +173,6 @@
a.cancelAlarm("Fire", src, src)
/area/proc/readyalert()
- if(name == "Space")
- return
if(!eject)
eject = 1
updateicon()
@@ -197,8 +185,6 @@
return
/area/proc/partyalert()
- if(name == "Space") //no parties in space!!!
- return
if (!( party ))
party = 1
updateicon()
@@ -323,32 +309,12 @@
L.client.ambience_playing = 1
L << sound('sound/ambience/shipambience.ogg', repeat = 1, wait = 0, volume = 35, channel = 2)
- if(prob(35))
+ if(src.ambience.len && prob(35))
+ sound = pick(ambience)
- if(istype(src, /area/chapel))
- sound = pick('sound/ambience/ambicha1.ogg','sound/ambience/ambicha2.ogg','sound/ambience/ambicha3.ogg','sound/ambience/ambicha4.ogg','sound/music/traitor.ogg')
- else if(istype(src, /area/medical/morgue))
- sound = pick('sound/ambience/ambimo1.ogg','sound/ambience/ambimo2.ogg','sound/music/main.ogg')
- else if(type == /area)
- sound = pick('sound/ambience/ambispace.ogg','sound/music/title2.ogg','sound/music/space.ogg','sound/music/main.ogg','sound/music/traitor.ogg')
- else if(istype(src, /area/engine))
- sound = pick('sound/ambience/ambisin1.ogg','sound/ambience/ambisin2.ogg','sound/ambience/ambisin3.ogg','sound/ambience/ambisin4.ogg')
- else if(istype(src, /area/AIsattele) || istype(src, /area/turret_protected/ai) || istype(src, /area/turret_protected/ai_upload) || istype(src, /area/turret_protected/ai_upload_foyer))
- sound = pick('sound/ambience/ambimalf.ogg')
- else if(istype(src, /area/mine/explored) || istype(src, /area/mine/unexplored))
- sound = pick('sound/ambience/ambimine.ogg', 'sound/ambience/song_game.ogg')
- musVolume = 25
- else if(istype(src, /area/tcommsat) || istype(src, /area/turret_protected/tcomwest) || istype(src, /area/turret_protected/tcomeast) || istype(src, /area/turret_protected/tcomfoyer) || istype(src, /area/turret_protected/tcomsat))
- sound = pick('sound/ambience/ambisin2.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/ambigen10.ogg')
- else
- sound = pick('sound/ambience/ambigen1.ogg','sound/ambience/ambigen3.ogg','sound/ambience/ambigen4.ogg','sound/ambience/ambigen5.ogg','sound/ambience/ambigen6.ogg','sound/ambience/ambigen7.ogg','sound/ambience/ambigen8.ogg','sound/ambience/ambigen9.ogg','sound/ambience/ambigen10.ogg','sound/ambience/ambigen11.ogg','sound/ambience/ambigen12.ogg','sound/ambience/ambigen14.ogg')
-
- if(!L.client.played)
+ if(world.time > L.client.played + 600)
L << sound(sound, repeat = 0, wait = 0, volume = musVolume, channel = 1)
- L.client.played = 1
- spawn(600) //ewww - this is very very bad
- if(L.&& L.client)
- L.client.played = 0
+ L.client.played = world.time
/area/proc/gravitychange(var/gravitystate = 0, var/area/A)
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 05a52137cc0..630746304f5 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -70,9 +70,6 @@
/atom/proc/CheckExit()
return 1
-/atom/proc/HasEntered(atom/movable/AM as mob|obj)
- return
-
/atom/proc/HasProximity(atom/movable/AM as mob|obj)
return
diff --git a/code/game/dna/dna_modifier.dm b/code/game/dna/dna_modifier.dm
index 235156d3d3e..d4450cbb786 100644
--- a/code/game/dna/dna_modifier.dm
+++ b/code/game/dna/dna_modifier.dm
@@ -276,15 +276,15 @@
del(src)
/obj/machinery/computer/scan_consolenew/power_change()
+ ..()
if(stat & BROKEN)
icon_state = "broken"
- else if(powered())
- icon_state = initial(icon_state)
- stat &= ~NOPOWER
else
- spawn(rand(0, 15))
- src.icon_state = "c_unpowered"
- stat |= NOPOWER
+ if (stat & NOPOWER)
+ spawn(rand(0, 15))
+ src.icon_state = "c_unpowered"
+ else
+ icon_state = initial(icon_state)
/obj/machinery/computer/scan_consolenew/New()
..()
@@ -345,7 +345,7 @@
*
* @return nothing
*/
-/obj/machinery/computer/scan_consolenew/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null)
+/obj/machinery/computer/scan_consolenew/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
if(user == connected.occupant || user.stat)
return
@@ -425,7 +425,7 @@
data["beakerVolume"] += R.volume
// update the ui if it exists, returns null if no ui is passed/found
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, data)
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
// the ui does not exist, so we'll create a new() one
// for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
diff --git a/code/game/gamemodes/autotraitor/autotraitor.dm b/code/game/gamemodes/autotraitor/autotraitor.dm
index 882f161842c..898378543d2 100644
--- a/code/game/gamemodes/autotraitor/autotraitor.dm
+++ b/code/game/gamemodes/autotraitor/autotraitor.dm
@@ -136,14 +136,9 @@
newtraitor << "You are now a traitor."
newtraitor.mind.special_role = "traitor"
newtraitor.hud_updateflag |= 1 << SPECIALROLE_HUD
- var/obj_count = 1
- newtraitor << "\blue Your current objectives:"
- if(!config.objectives_disabled)
- for(var/datum/objective/objective in newtraitor.mind.objectives)
- newtraitor << "Objective #[obj_count]: [objective.explanation_text]"
- obj_count++
- else
- newtraitor << "You have been selected this round as an antagonist- Within the rules, try to act as an opposing force to the crew- This can be via corporate payoff, personal motives, or maybe just being a dick. Further RP and try to make sure other players have fun! If you are confused or at a loss, always adminhelp, and before taking extreme actions, please try to also contact the administration! Think through your actions and make the roleplay immersive! Please remember all rules aside from those without explicit exceptions apply to antagonist."
+ newtraitor << "You have been selected this round as an antagonist!"
+ show_objectives(newtraitor.mind)
+
//else
//message_admins("No new traitor being added.")
//else
@@ -194,14 +189,9 @@
traitors += character.mind
character << "\red You are the traitor."
character.mind.special_role = "traitor"
- if (config.objectives_disabled)
- character << "You have been selected this round as an antagonist- Within the rules, try to act as an opposing force to the crew- This can be via corporate payoff, personal motives, or maybe just being a dick. Further RP and try to make sure other players have fun! If you are confused or at a loss, always adminhelp, and before taking extreme actions, please try to also contact the administration! Think through your actions and make the roleplay immersive! Please remember all rules aside from those without explicit exceptions apply to antagonist."
- else
- var/obj_count = 1
- character << "\blue Your current objectives:"
- for(var/datum/objective/objective in character.mind.objectives)
- character << "Objective #[obj_count]: [objective.explanation_text]"
- obj_count++
+ character << "You have been selected this round as an antagonist!"
+ show_objectives(character.mind)
+
//else
//message_admins("New traitor roll failed. No new traitor.")
//else
diff --git a/code/game/gamemodes/calamity/calamity.dm b/code/game/gamemodes/calamity/calamity.dm
new file mode 100644
index 00000000000..c2ab3202f4f
--- /dev/null
+++ b/code/game/gamemodes/calamity/calamity.dm
@@ -0,0 +1,449 @@
+/*
+ * This roundtype is a replacement for the meteor round on upstream BS12 - players
+ * expressed the desire for quick, chaotic and violent rounds, preferrably without
+ * dependancy on other players.
+ * I wanted to call it clusterfuck, but I figure that's a bit too overt. Think of
+ * the children.
+ * ~ Zuhayr
+ */
+
+/datum/game_mode/calamity
+ name = "calamity"
+ config_tag = "calamity"
+ required_players = 1
+ votable = 0 //Remove after testing.
+
+ uplink_welcome = "Syndicate Uplink Console:"
+ uplink_uses = 10
+
+ //Possible roundstart antag types.
+ var/list/atypes = list("syndi","ling","tater","wiz","ninja","vox","slug","cult")
+ var/list/chosen_atypes = list()
+ var/list/chosen_candidates = list()
+ var/list/already_assigned_candidates = list()
+
+ //At one antagonist group per 10 players we are just going to go with tiny groups.
+ var/max_antags = 3 // Antag groups spawn with this many members.
+ var/antag_type_ratio = 10 // 1 antag type per this many players.
+
+ var/const/waittime_l = 600
+ var/const/waittime_h = 1800
+
+/datum/game_mode/calamity/announce()
+ world << "The current game mode is - Calamity!"
+ world << "This must be a Thursday. You never could get the hang of Thursdays..."
+
+/datum/game_mode/calamity/can_start()
+
+ if(!..())
+ return 0
+
+ var/antag_count = player_list.len/antag_type_ratio
+
+ if(!antag_count)
+ return 1
+
+ for(var/i=0;iThis terrible, terrible day has finally ended! "
+
+ //Group antag objective completion.
+ for(var/list/L in list(src.raiders, src.syndicates, src.cult, src.wizards))
+
+ if(L.len)
+ var/datum/mind/M = L[1]
+ text = " The [M.special_role][L.len == 1 ? " was" : "s were"]:"
+
+ for(var/datum/mind/P in L)
+ text += " [P.key] was [P.name] ("
+ if(P.current)
+ if(P.current.stat == DEAD)
+ text += "died"
+ else
+ text += "survived"
+ if(P.current.real_name != P.name)
+ text += " as [P.current.real_name]"
+ else
+ text += "body destroyed"
+ text += ")"
+
+ if(M.objectives.len)
+ text += " Their objectives were:"
+ var/num = 1
+ for(var/datum/objective/O in M.objectives)
+ text += " Objective [num]: [O.explanation_text]"
+ num++
+
+ //Single antag objective completion.
+ for(var/list/L in list(src.changelings, src.ninjas, src.borers, src.traitors))
+
+ if(L.len)
+ var/datum/mind/M = L[1]
+ text = " The [M.special_role][L.len == 1 ? " was" : "s were"]:"
+ for(var/datum/mind/P in L)
+ var/num = 1
+ text += " [P.key] was [P.name]."
+ if(P.objectives.len)
+ for(var/datum/objective/O in P.objectives)
+ text += " Objective [num]: [O.explanation_text]"
+ num++
+ ..()
+ return 1
+
+//Converts chosen atype to an actual role string.
+/datum/game_mode/calamity/proc/get_candidate_role_text(var/role)
+
+ var/role_text
+
+ switch(role)
+ if("syndi")
+ role_text = "Syndicate Operative"
+ if("ling")
+ role_text = "Changeling"
+ if("tater")
+ role_text = "Traitor"
+ if("wiz")
+ role_text = "Cabalist"
+ if("ninja")
+ role_text = "Ninja"
+ if("vox")
+ role_text = "Vox Raider"
+ if("slug")
+ role_text = "Cortical Borer"
+ if("cult")
+ role_text = "Cultist"
+
+ return role_text
+
+//Grabs candidate lists for various atypes.
+/datum/game_mode/calamity/proc/get_role_candidates(var/role)
+
+ var/list/possible_antags = list()
+ switch(role)
+ if("syndi")
+ possible_antags = get_players_for_role(BE_OPERATIVE)
+ if("ling")
+ possible_antags = get_players_for_role(BE_CHANGELING)
+ if("tater")
+ possible_antags = get_players_for_role(BE_TRAITOR)
+ if("wiz")
+ possible_antags = get_players_for_role(BE_WIZARD)
+ if("ninja")
+ possible_antags = get_players_for_role(BE_NINJA)
+ if("vox")
+ possible_antags = get_players_for_role(BE_RAIDER)
+ if("slug")
+ possible_antags = get_players_for_role(BE_ALIEN)
+ if("cult")
+ possible_antags = get_players_for_role(BE_CULTIST)
+
+ var/list/filtered_antags = list()
+ if(possible_antags)
+ for(var/datum/mind/candidate in possible_antags)
+ if(!(candidate in already_assigned_candidates))
+ filtered_antags |= candidate
+ already_assigned_candidates |= candidate
+
+ if(filtered_antags && islist(filtered_antags))
+ return filtered_antags
+ else
+ return list(filtered_antags)
+
+//Spawning procs for the various antag types.
+//A LOT OF THE FOLLOWING IS COPYPASTED FROM OTHER MODES AND NEEDS
+//TO BE FIXED UP. NINJA, NUKE AND CULT IN PARTICULAR ARE FUCKING AWFUL. ~ Z
+/datum/game_mode/calamity/proc/spawn_syndicate(var/list/candidates)
+
+ var/obj/effect/landmark/uplinklocker = locate("landmark*Syndicate-Uplink")
+ var/obj/effect/landmark/nuke_spawn = locate("landmark*Nuclear-Bomb")
+
+ var/nuke_code = "[rand(10000, 99999)]"
+ var/leader_selected = 0
+ var/spawnpos = 1
+
+ for(var/datum/mind/player in candidates)
+
+ syndicates += player
+
+ if(spawnpos > synd_spawn.len)
+ spawnpos = 1
+ player.current.loc = synd_spawn[spawnpos]
+
+ player.current.real_name = "[syndicate_name()] Operative"
+ spawn(0)
+ NukeNameAssign(player)
+
+ forge_syndicate_objectives(player)
+ greet_syndicate(player)
+ equip_syndicate(player.current)
+
+ if(!leader_selected)
+ prepare_syndicate_leader(player, nuke_code)
+ leader_selected = 1
+
+ spawnpos++
+ update_synd_icons_added(player)
+
+ update_all_synd_icons()
+
+ if(uplinklocker)
+ new /obj/structure/closet/syndicate/nuclear(uplinklocker.loc)
+ if(nuke_spawn && synd_spawn.len > 0)
+ var/obj/machinery/nuclearbomb/the_bomb = new /obj/machinery/nuclearbomb(nuke_spawn.loc)
+ the_bomb.r_code = nuke_code
+
+/datum/game_mode/calamity/proc/spawn_changelings(var/list/candidates)
+
+ for(var/datum/mind/player in candidates)
+
+ changelings += player
+ grant_changeling_powers(player.current)
+ player.special_role = "Changeling"
+
+ if(!config.objectives_disabled)
+ player.objectives += new /datum/objective/escape()
+ player.objectives += new /datum/objective/survive()
+
+ show_objectives(player)
+ greet_changeling(player)
+
+/datum/game_mode/calamity/proc/spawn_traitors(var/list/candidates)
+
+ for(var/datum/mind/player in candidates)
+ traitors += player
+
+ if(!config.objectives_disabled)
+ player.objectives += new /datum/objective/escape()
+ player.objectives += new /datum/objective/survive()
+
+ show_objectives(player)
+
+ finalize_traitor(player)
+ greet_traitor(player)
+
+/datum/game_mode/calamity/proc/spawn_cabal(var/list/candidates)
+
+ for(var/datum/mind/player in candidates)
+ wizards += player
+
+ if(!config.objectives_disabled)
+ player.objectives += new /datum/objective/escape()
+ player.objectives += new /datum/objective/survive()
+
+ show_objectives(player)
+
+ player.current.loc = pick(wizardstart)
+
+ equip_wizard(player.current)
+ name_wizard(player.current)
+ greet_wizard(player)
+
+/datum/game_mode/calamity/proc/spawn_ninja(var/list/candidates)
+
+ //I hate that this is necessary. ~Z
+ for(var/obj/effect/landmark/L in landmarks_list)
+ if(L.name == "carpspawn")
+ ninjastart.Add(L)
+
+ for(var/datum/mind/player in candidates)
+ ninjas += player
+
+ player.current << browse(null, "window=playersetup")
+ player.current = create_space_ninja(pick(ninjastart))
+ player.current.ckey = player.key
+
+ if(player.current && !(istype(player.current,/mob/living/carbon/human))) return 0
+
+ //Ninja intro crawl goes here.
+
+ if(!config.objectives_disabled)
+ player.objectives += new /datum/objective/ninja_highlander()
+ player.objectives += new /datum/objective/survive()
+
+ show_objectives(player)
+
+ //Ninja objective announcement goes here.
+
+ //Set ninja internals.
+ var/mob/living/carbon/human/N = player.current
+ N.internal = N.s_store
+ N.internals.icon_state = "internal1"
+ if(N.wear_suit && istype(N.wear_suit,/obj/item/clothing/suit/space/space_ninja))
+ var/obj/item/clothing/suit/space/space_ninja/S = N.wear_suit
+ S:randomize_param()
+
+/datum/game_mode/calamity/proc/spawn_vox_raiders(var/list/candidates)
+
+ //Create objectives.
+ var/list/raid_objectives = forge_vox_objectives()
+
+ //Create raiders.
+ for(var/datum/mind/player in candidates)
+ raiders += player
+
+ //Place them on the shuttle.
+ var/index = 1
+ if(index > raider_spawn.len)
+ index = 1
+ player.current.loc = raider_spawn[index]
+ index++
+
+ if(!config.objectives_disabled)
+ player.objectives = raid_objectives
+
+ //Equip them.
+ create_vox(player)
+ greet_vox(player)
+
+ player.current << "Your crew is transporting cortical stacks and critical resources back to the Shoal.\
+ No delay or concession can be tolerated. Even putting holes in the station pales in comparison to failure."
+
+ spawn (rand(waittime_l, waittime_h))
+ send_intercept()
+ ..()
+
+/datum/game_mode/calamity/proc/spawn_borers(var/list/candidates)
+
+ var/list/possible_hosts = list()
+ for(var/mob/living/carbon/human/H in mob_list)
+ if(!(H.species.flags & IS_SYNTHETIC))
+ possible_hosts += H
+
+ for(var/datum/mind/player in candidates)
+
+ if(!possible_hosts || possible_hosts.len)
+ break
+
+ borers += player
+ var/mob/living/carbon/human/target_host = pick(possible_hosts)
+ possible_hosts -= target_host
+
+ var/mob/living/simple_animal/borer/roundstart/B = new(target_host)
+
+ player.current = B
+ B.mind = player
+ B.key = player.key
+ player.assigned_role = "Cortical Borer"
+ player.special_role = "Cortical Borer"
+
+ B.host = target_host
+ B.host_brain.name = target_host.name
+ B.host_brain.real_name = target_host.real_name
+
+ var/datum/organ/external/head = target_host.get_organ("head")
+ head.implants += B
+
+ player.current << "\blue You are a cortical borer! You are a brain slug that worms its way \
+ into the head of its victim, lurking out of sight until it needs to take control."
+ player.current << "You can speak to your victim with say, to other borers with say ;, and use your Alien tab for abilities."
+
+ if(!config.objectives_disabled)
+ player.objectives += new /datum/objective/borer_survive()
+ player.objectives += new /datum/objective/borer_reproduce()
+ player.objectives += new /datum/objective/escape()
+
+ show_objectives(player)
+
+/datum/game_mode/calamity/proc/spawn_cultists(var/list/candidates)
+
+ //Prune the list.
+ var/list/jobs_to_skip = list("Chaplain","AI", "Cyborg", "Lawyer", "Head of Security", "Captain")
+ if(config.protect_roles_from_antagonist)
+ jobs_to_skip += list("Security Officer", "Warden", "Detective")
+
+ //Make cult objectives.
+ var/cult_objectives = list()
+ cult_objectives += new /datum/objective/cult_summon()
+
+ //Actually spawn cultists.
+ for(var/datum/mind/player in candidates)
+
+ for(var/job in jobs_to_skip)
+ if(player.assigned_role == job)
+ continue
+
+ cult += player
+ equip_cultist(player.current)
+ grant_runeword(player.current)
+ update_cult_icons_added(player)
+ player.current << "\blue You are a member of the cult! Your dark masters have sent you forth to serve their vile will."
+ player.current << "\red This station sails above a weeping tear in the Cosmos. Bring the Geometer of Blood forth."
+
+ if(!config.objectives_disabled)
+
+ for(var/datum/objective/O in cult_objectives)
+ player.objectives += O
+
+ player.objectives += new /datum/objective/escape()
+ player.objectives += new /datum/objective/survive()
+
+ show_objectives(player)
+ player.special_role = "Cultist"
\ No newline at end of file
diff --git a/code/game/gamemodes/changeling/changeling.dm b/code/game/gamemodes/changeling/changeling.dm
index 03780adf361..c3079bf3d52 100644
--- a/code/game/gamemodes/changeling/changeling.dm
+++ b/code/game/gamemodes/changeling/changeling.dm
@@ -125,23 +125,13 @@ var/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","Epsilon"
changeling.current << "\red You are a changeling!"
changeling.current << "\red Use say \":g message\" to communicate with your fellow changelings. Remember: you get all of their absorbed DNA if you absorb them."
- if(config.objectives_disabled)
- changeling.current << "Within the rules, try to act as an opposing force to the crew. Further RP and try to make sure other players have fun! If you are confused or at a loss, always adminhelp, and before taking extreme actions, please try to also contact the administration! Think through your actions and make the roleplay immersive! Please remember all rules aside from those without explicit exceptions apply to antagonists."
-
- if (!config.objectives_disabled)
- changeling.current << "You must complete the following tasks:"
+ show_objectives(changeling)
if (changeling.current.mind)
if (changeling.current.mind.assigned_role == "Clown")
changeling.current << "You have evolved beyond your clownish nature, allowing you to wield weapons without harming yourself."
changeling.current.mutations.Remove(CLUMSY)
- if (!config.objectives_disabled)
- var/obj_count = 1
- for(var/datum/objective/objective in changeling.objectives)
- changeling.current << "Objective #[obj_count]: [objective.explanation_text]"
- obj_count++
- return
/*/datum/game_mode/changeling/check_finished()
var/changelings_alive = 0
diff --git a/code/game/gamemodes/cult/cult.dm b/code/game/gamemodes/cult/cult.dm
index e988d86ee6d..4ddfbffb48b 100644
--- a/code/game/gamemodes/cult/cult.dm
+++ b/code/game/gamemodes/cult/cult.dm
@@ -100,8 +100,7 @@
cult_mind.current << "\blue You are a member of the cult!"
if(!config.objectives_disabled)
memorize_cult_objectives(cult_mind)
- else
- cult_mind.current << "Within the rules, try to act as an opposing force to the crew. Further RP and try to make sure other players have fun! If you are confused or at a loss, always adminhelp, and before taking extreme actions, please try to also contact the administration! Think through your actions and make the roleplay immersive! Please remember all rules aside from those without explicit exceptions apply to antagonists."
+ show_objectives(cult_mind)
cult_mind.special_role = "Cultist"
spawn (rand(waittime_l, waittime_h))
diff --git a/code/game/gamemodes/cult/cult_items.dm b/code/game/gamemodes/cult/cult_items.dm
index 29177888154..f6aa21e14d2 100644
--- a/code/game/gamemodes/cult/cult_items.dm
+++ b/code/game/gamemodes/cult/cult_items.dm
@@ -35,6 +35,7 @@
desc = "A hood worn by the followers of Nar-Sie."
flags_inv = HIDEFACE
flags = FPRINT|TABLEPASS|HEADCOVERSEYES
+ body_parts_covered = HEAD|EYES
armor = list(melee = 30, bullet = 10, laser = 5,energy = 5, bomb = 0, bio = 0, rad = 0)
cold_protection = HEAD
min_cold_protection_temperature = SPACE_HELMET_MIN_COLD_PROTECTION_TEMPERATURE
@@ -68,6 +69,7 @@
desc = "A helm worn by the followers of Nar-Sie."
flags_inv = HIDEFACE
flags = FPRINT|TABLEPASS|HEADCOVERSEYES|HEADCOVERSMOUTH|BLOCKHAIR
+ body_parts_covered = HEAD|FACE|EYES
armor = list(melee = 30, bullet = 30, laser = 30,energy = 20, bomb = 0, bio = 0, rad = 0)
siemens_coefficient = 0
@@ -77,7 +79,7 @@
icon_state = "magusred"
item_state = "magusred"
flags = FPRINT | TABLEPASS
- body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS
+ body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS
allowed = list(/obj/item/weapon/book/tome,/obj/item/weapon/melee/cultblade)
armor = list(melee = 50, bullet = 30, laser = 50,energy = 20, bomb = 25, bio = 10, rad = 0)
flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT
@@ -91,6 +93,7 @@
armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 30, rad = 30)
siemens_coefficient = 0
+
/obj/item/clothing/suit/space/cult
name = "cult armour"
icon_state = "cult_armour"
@@ -100,4 +103,5 @@
allowed = list(/obj/item/weapon/book/tome,/obj/item/weapon/melee/cultblade,/obj/item/weapon/tank/emergency_oxygen,/obj/item/device/suit_cooling_unit)
slowdown = 1
armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 30, rad = 30)
- siemens_coefficient = 0
\ No newline at end of file
+ siemens_coefficient = 0
+ body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS|HANDS
\ No newline at end of file
diff --git a/code/game/gamemodes/cult/cult_structures.dm b/code/game/gamemodes/cult/cult_structures.dm
index 9334e0f8d63..bf2124ebb54 100644
--- a/code/game/gamemodes/cult/cult_structures.dm
+++ b/code/game/gamemodes/cult/cult_structures.dm
@@ -1,57 +1,57 @@
-/obj/structure/cult
- density = 1
- anchored = 1
- icon = 'icons/obj/cult.dmi'
-
-/obj/structure/cult/talisman
- name = "Altar"
- desc = "A bloodstained altar dedicated to Nar-Sie"
- icon_state = "talismanaltar"
-
-
-/obj/structure/cult/forge
- name = "Daemon forge"
- desc = "A forge used in crafting the unholy weapons used by the armies of Nar-Sie"
- icon_state = "forge"
-
-/obj/structure/cult/pylon
- name = "Pylon"
- desc = "A floating crystal that hums with an unearthly energy"
- icon_state = "pylon"
- luminosity = 5
-
-
-/obj/structure/cult/tome
- name = "Desk"
- desc = "A desk covered in arcane manuscripts and tomes in unknown languages. Looking at the text makes your skin crawl"
- icon_state = "tomealtar"
-// luminosity = 5
-
-//sprites for this no longer exist -Pete
-//(they were stolen from another game anyway)
-/*
-/obj/structure/cult/pillar
- name = "Pillar"
- desc = "This should not exist"
- icon_state = "pillar"
- icon = 'magic_pillar.dmi'
-*/
-
-/obj/effect/gateway
- name = "gateway"
- desc = "You're pretty sure that abyss is staring back"
- icon = 'icons/obj/cult.dmi'
- icon_state = "hole"
- density = 1
- unacidable = 1
- anchored = 1.0
-
-/obj/effect/gateway/Bumped(mob/M as mob|obj)
- spawn(0)
- return
- return
-
-/obj/effect/gateway/HasEntered(AM as mob|obj)
- spawn(0)
- return
+/obj/structure/cult
+ density = 1
+ anchored = 1
+ icon = 'icons/obj/cult.dmi'
+
+/obj/structure/cult/talisman
+ name = "Altar"
+ desc = "A bloodstained altar dedicated to Nar-Sie"
+ icon_state = "talismanaltar"
+
+
+/obj/structure/cult/forge
+ name = "Daemon forge"
+ desc = "A forge used in crafting the unholy weapons used by the armies of Nar-Sie"
+ icon_state = "forge"
+
+/obj/structure/cult/pylon
+ name = "Pylon"
+ desc = "A floating crystal that hums with an unearthly energy"
+ icon_state = "pylon"
+ luminosity = 5
+
+
+/obj/structure/cult/tome
+ name = "Desk"
+ desc = "A desk covered in arcane manuscripts and tomes in unknown languages. Looking at the text makes your skin crawl"
+ icon_state = "tomealtar"
+// luminosity = 5
+
+//sprites for this no longer exist -Pete
+//(they were stolen from another game anyway)
+/*
+/obj/structure/cult/pillar
+ name = "Pillar"
+ desc = "This should not exist"
+ icon_state = "pillar"
+ icon = 'magic_pillar.dmi'
+*/
+
+/obj/effect/gateway
+ name = "gateway"
+ desc = "You're pretty sure that abyss is staring back"
+ icon = 'icons/obj/cult.dmi'
+ icon_state = "hole"
+ density = 1
+ unacidable = 1
+ anchored = 1.0
+
+/obj/effect/gateway/Bumped(mob/M as mob|obj)
+ spawn(0)
+ return
+ return
+
+/obj/effect/gateway/Crossed(AM as mob|obj)
+ spawn(0)
+ return
return
\ No newline at end of file
diff --git a/code/game/gamemodes/epidemic/epidemic.dm b/code/game/gamemodes/epidemic/epidemic.dm
index d0db2d66b90..4a0712c7128 100644
--- a/code/game/gamemodes/epidemic/epidemic.dm
+++ b/code/game/gamemodes/epidemic/epidemic.dm
@@ -71,7 +71,7 @@
world << sound('sound/AI/commandreport.ogg')
// add an extra law to the AI to make sure it cooperates with the heads
- var/extra_law = "Crew authorized to know of pathogen [virus_name]'s existence are: Heads of command, any crew member with loyalty implant. Do not allow unauthorized personnel to gain knowledge of [virus_name]. Aid authorized personnel in quarantining and neutrlizing the outbreak. This law overrides all other laws."
+ var/extra_law = "Crew authorized to know of pathogen [virus_name]'s existence are: Heads of command. Do not allow unauthorized personnel to gain knowledge of [virus_name]. Aid authorized personnel in quarantining and neutrlizing the outbreak. This law overrides all other laws."
for(var/mob/living/silicon/ai/M in world)
M.add_ion_law(extra_law)
M << "\red " + extra_law
diff --git a/code/game/gamemodes/events/holidays/Christmas.dm b/code/game/gamemodes/events/holidays/Christmas.dm
index 72d42318e60..9203fa15e9d 100644
--- a/code/game/gamemodes/events/holidays/Christmas.dm
+++ b/code/game/gamemodes/events/holidays/Christmas.dm
@@ -59,5 +59,6 @@
desc = "A crappy paper hat that you are REQUIRED to wear."
flags_inv = 0
flags = FPRINT|TABLEPASS
+ body_parts_covered = 0
armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0)
diff --git a/code/game/gamemodes/events/ninja_equipment.dm b/code/game/gamemodes/events/ninja_equipment.dm
index 341cf00eaf6..787f2cf93e4 100644
--- a/code/game/gamemodes/events/ninja_equipment.dm
+++ b/code/game/gamemodes/events/ninja_equipment.dm
@@ -315,24 +315,15 @@ ________________________________________________________________________________
var/datum/gas_mixture/environment = T.return_air()
var/pressure = environment.return_pressure()
- var/total_moles = environment.total_moles()
+ var/total_moles = environment.total_moles
dat += "Air Pressure: [round(pressure,0.1)] kPa"
if (total_moles)
- var/o2_level = environment.oxygen/total_moles
- var/n2_level = environment.nitrogen/total_moles
- var/co2_level = environment.carbon_dioxide/total_moles
- var/phoron_level = environment.phoron/total_moles
- var/unknown_level = 1-(o2_level+n2_level+co2_level+phoron_level)
dat += "
"}
- for(var/obj/machinery/portable_atmospherics/scrubber/huge/scrubber in connectedscrubbers)
+ for(var/obj/machinery/portable_atmospherics/powered/scrubber/huge/scrubber in connectedscrubbers)
dat += {"
"
dat += "View storage log. "
+ dat += "View objects. "
dat += "Recover object. "
dat += "Recover all objects. "
@@ -65,16 +66,27 @@ var/global/list/frozen_items = list()
user << browse(dat, "window=cryolog")
+ if(href_list["view"])
+
+ var/dat = "Recently stored objects "
+ for(var/obj/item/I in frozen_items)
+ dat += "[I.name] "
+ dat += ""
+
+ user << browse(dat, "window=cryoitems")
+
else if(href_list["item"])
if(frozen_items.len == 0)
user << "\blue There is nothing to recover from storage."
return
- var/obj/item/I = input(usr, "Please choose which object to retrieve.","Object recovery",null) as obj in frozen_items
+ var/obj/item/I = input(usr, "Please choose which object to retrieve.","Object recovery",null) as null|anything in frozen_items
+ if(!I)
+ return
- if(!I || frozen_items.len == 0)
- user << "\blue There is nothing to recover from storage."
+ if(!(I in frozen_items))
+ user << "\blue \The [I] is no longer in storage."
return
visible_message("\blue The console beeps happily as it disgorges \the [I].", 3)
@@ -134,10 +146,10 @@ var/global/list/frozen_items = list()
density = 1
anchored = 1
- var/mob/occupant = null // Person waiting to be despawned.
- var/orient_right = null // Flips the sprite.
- var/time_till_despawn = 9000 // 15 minutes-ish safe period before being despawned.
- var/time_entered = 0 // Used to keep track of the safe period.
+ var/mob/occupant = null // Person waiting to be despawned.
+ var/orient_right = null // Flips the sprite.
+ var/time_till_despawn = 18000 // 30 minutes-ish safe period before being despawned.
+ var/time_entered = 0 // Used to keep track of the safe period.
var/obj/item/device/radio/intercom/announce //
// These items are preserved when the process() despawn proc occurs.
@@ -173,7 +185,6 @@ var/global/list/frozen_items = list()
//Lifted from Unity stasis.dm and refactored. ~Zuhayr
/obj/machinery/cryopod/process()
if(occupant)
-
//Allow a ten minute gap between entering the pod and actually despawning.
if(world.time - time_entered < time_till_despawn)
return
@@ -187,7 +198,7 @@ var/global/list/frozen_items = list()
if(W.contents.len) //Make sure we catch anything not handled by del() on the items.
for(var/obj/item/O in W.contents)
- if(istype(O,/obj/item/weapon/storage/internal)) //Stop eating pockets you fuck!
+ if(istype(O,/obj/item/weapon/storage/internal)) //Stop eating pockets, you fuck!
continue
O.loc = src
@@ -333,6 +344,7 @@ var/global/list/frozen_items = list()
src.add_fingerprint(M)
/obj/machinery/cryopod/verb/eject()
+
set name = "Eject Pod"
set category = "Object"
set src in oview(1)
@@ -344,6 +356,14 @@ var/global/list/frozen_items = list()
else
icon_state = "body_scanner_0"
+ //Eject any items that aren't meant to be in the pod.
+ var/list/items = src.contents
+ if(occupant) items -= occupant
+ if(announce) items -= announce
+
+ for(var/obj/item/W in items)
+ W.loc = get_turf(src)
+
src.go_out()
add_fingerprint(usr)
return
diff --git a/code/game/machinery/door_control.dm b/code/game/machinery/door_control.dm
index 2dcb0c0b2e0..496c55ecb6f 100644
--- a/code/game/machinery/door_control.dm
+++ b/code/game/machinery/door_control.dm
@@ -1,3 +1,7 @@
+#define CONTROL_POD_DOORS 0
+#define CONTROL_NORMAL_DOORS 1
+#define CONTROL_EMITTERS 2
+
/obj/machinery/door_control
name = "remote door-control"
desc = "It controls doors, remotely."
@@ -7,7 +11,7 @@
power_channel = ENVIRON
var/id = null
var/range = 10
- var/normaldoorcontrol = 0
+ var/normaldoorcontrol = CONTROL_POD_DOORS
var/desiredstate = 0 // Zero is closed, 1 is open.
var/specialfunctions = 1
/*
@@ -63,8 +67,59 @@
playsound(src.loc, "sparks", 100, 1)
return src.attack_hand(user)
+/obj/machinery/door_control/proc/handle_door()
+ for(var/obj/machinery/door/airlock/D in range(range))
+ if(D.id_tag == src.id)
+ if(specialfunctions & OPEN)
+ if (D.density)
+ spawn(0)
+ D.open()
+ return
+ else
+ spawn(0)
+ D.close()
+ return
+ if(desiredstate == 1)
+ if(specialfunctions & IDSCAN)
+ D.aiDisabledIdScanner = 1
+ if(specialfunctions & BOLTS)
+ D.lock()
+ if(specialfunctions & SHOCK)
+ D.secondsElectrified = -1
+ if(specialfunctions & SAFE)
+ D.safe = 0
+ else
+ if(specialfunctions & IDSCAN)
+ D.aiDisabledIdScanner = 0
+ if(specialfunctions & BOLTS)
+ if(!D.isWireCut(4) && D.arePowerSystemsOn())
+ D.unlock()
+ if(specialfunctions & SHOCK)
+ D.secondsElectrified = 0
+ if(specialfunctions & SAFE)
+ D.safe = 1
+
+/obj/machinery/door_control/proc/handle_pod()
+ for(var/obj/machinery/door/poddoor/M in world)
+ if(M.id == src.id)
+ if(M.density)
+ spawn(0)
+ M.open()
+ return
+ else
+ spawn(0)
+ M.close()
+ return
+
+/obj/machinery/door_control/proc/handle_emitters(mob/user as mob)
+ for(var/obj/machinery/power/emitter/E in range(range))
+ if(E.id == src.id)
+ spawn(0)
+ E.activate(user)
+ return
+
/obj/machinery/door_control/attack_hand(mob/user as mob)
- src.add_fingerprint(usr)
+ src.add_fingerprint(user)
if(stat & (NOPOWER|BROKEN))
return
@@ -77,49 +132,13 @@
icon_state = "doorctrl1"
add_fingerprint(user)
- if(normaldoorcontrol)
- for(var/obj/machinery/door/airlock/D in range(range))
- if(D.id_tag == src.id)
- if(specialfunctions & OPEN)
- if (D.density)
- spawn(0)
- D.open()
- return
- else
- spawn(0)
- D.close()
- return
- if(desiredstate == 1)
- if(specialfunctions & IDSCAN)
- D.aiDisabledIdScanner = 1
- if(specialfunctions & BOLTS)
- D.lock()
- if(specialfunctions & SHOCK)
- D.secondsElectrified = -1
- if(specialfunctions & SAFE)
- D.safe = 0
- else
- if(specialfunctions & IDSCAN)
- D.aiDisabledIdScanner = 0
- if(specialfunctions & BOLTS)
- if(!D.isWireCut(4) && D.arePowerSystemsOn())
- D.unlock()
- if(specialfunctions & SHOCK)
- D.secondsElectrified = 0
- if(specialfunctions & SAFE)
- D.safe = 1
-
- else
- for(var/obj/machinery/door/poddoor/M in world)
- if (M.id == src.id)
- if (M.density)
- spawn( 0 )
- M.open()
- return
- else
- spawn( 0 )
- M.close()
- return
+ switch(normaldoorcontrol)
+ if(CONTROL_NORMAL_DOORS)
+ handle_door()
+ if(CONTROL_POD_DOORS)
+ handle_pod()
+ if(CONTROL_EMITTERS)
+ handle_emitters(user)
desiredstate = !desiredstate
spawn(15)
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index 270833f5ed5..c85599cf6a7 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -288,13 +288,7 @@ Airlock index -> wire color are { 9, 4, 6, 7, 5, 8, 1, 2, 3 }.
/obj/machinery/door/airlock/phoron/proc/PhoronBurn(temperature)
for(var/turf/simulated/floor/target_tile in range(2,loc))
-// if(target_tile.parent && target_tile.parent.group_processing) // THESE PROBABLY DO SOMETHING IMPORTANT BUT I DON'T KNOW HOW TO FIX IT - Erthilo
-// target_tile.parent.suspend_group_processing()
- var/datum/gas_mixture/napalm = new
- var/phoronToDeduce = 35
- napalm.phoron = phoronToDeduce
- napalm.temperature = 400+T0C
- target_tile.assume_air(napalm)
+ target_tile.assume_gas("phoron", 35, 400+T0C)
spawn (0) target_tile.hotspot_expose(temperature, 400)
for(var/obj/structure/falsewall/phoron/F in range(3,src))//Hackish as fuck, but until temperature_expose works, there is nothing I can do -Sieve
var/turf/T = get_turf(F)
@@ -656,12 +650,8 @@ About the new airlock wires panel:
return
/obj/machinery/door/airlock/attack_ai(mob/user as mob)
- if(!src.canAIControl())
- if(src.canAIHack())
- src.hack(user)
- return
- else
- user << "Airlock AI control has been blocked with a firewall. Unable to hack."
+ if (!check_synth_access(user))
+ return
//Separate interface for the AI.
user.set_machine(src)
@@ -777,7 +767,7 @@ About the new airlock wires panel:
user << "Alert cancelled. Airlock control has been restored without our assistance."
src.aiHacking=0
return
- else if(!src.canAIHack())
+ else if(!src.canAIHack(user))
user << "We've lost our connection! Unable to hack airlock."
src.aiHacking=0
return
@@ -789,7 +779,7 @@ About the new airlock wires panel:
user << "Alert cancelled. Airlock control has been restored without our assistance."
src.aiHacking=0
return
- else if(!src.canAIHack())
+ else if(!src.canAIHack(user))
user << "We've lost our connection! Unable to hack airlock."
src.aiHacking=0
return
@@ -799,7 +789,7 @@ About the new airlock wires panel:
user << "Alert cancelled. Airlock control has been restored without our assistance."
src.aiHacking=0
return
- else if(!src.canAIHack())
+ else if(!src.canAIHack(user))
user << "We've lost our connection! Unable to hack airlock."
src.aiHacking=0
return
@@ -894,6 +884,17 @@ About the new airlock wires panel:
..(user)
return
+/obj/machinery/door/airlock/proc/check_synth_access(mob/user as mob)
+ if(emagged)
+ user << "Unable to interface: Airlock is unresponsive."
+ return 0
+ if(!src.canAIControl())
+ if(src.canAIHack(user))
+ src.hack(user)
+ else
+ user << "Airlock AI control has been blocked with a firewall."
+ return 0
+ return 1
/obj/machinery/door/airlock/Topic(href, href_list, var/nowindow = 0)
if(!nowindow)
@@ -956,7 +957,10 @@ About the new airlock wires panel:
src.signalers[wirenum] = null
- if(istype(usr, /mob/living/silicon) && src.canAIControl())
+ if(istype(usr, /mob/living/silicon))
+ if (!check_synth_access(usr))
+ return
+
//AI
//aiDisable - 1 idscan, 2 disrupt main power, 3 disrupt backup power, 4 drop door bolts, 5 un-electrify door, 7 close door, 8 door safties, 9 door speed
//aiEnable - 1 idscan, 4 raise door bolts, 5 electrify door for 30 seconds, 6 electrify door indefinitely, 7 open door, 8 door safties, 9 door speed
@@ -966,10 +970,11 @@ About the new airlock wires panel:
if(1)
//disable idscan
if(src.isWireCut(AIRLOCK_WIRE_IDSCAN))
- usr << "The IdScan wire has been cut - So, you can't disable it, but it is already disabled anyways."
+ usr << "The IdScan wire has been cut - The IdScan feature is already disabled."
else if(src.aiDisabledIdScanner)
- usr << "You've already disabled the IdScan feature."
+ usr << "The IdScan feature is already disabled."
else
+ usr << "The IdScan feature has been disabled."
src.aiDisabledIdScanner = 1
if(2)
//disrupt main power
@@ -986,38 +991,19 @@ About the new airlock wires panel:
if(4)
//drop door bolts
if(src.isWireCut(AIRLOCK_WIRE_DOOR_BOLTS))
- usr << "You can't drop the door bolts - The door bolt control wire has been cut."
+ usr << "The door bolt control wire has been cut - The door bolts are already dropped."
+ else if(src.locked)
+ usr << "The door bolts are already dropped."
else
src.lock()
+ usr << "The door bolts have been dropped."
if(5)
//un-electrify door
if(src.isWireCut(AIRLOCK_WIRE_ELECTRIFY))
- usr << text("Can't un-electrify the airlock - The electrification wire is cut.")
- else if(src.secondsElectrified==-1)
+ usr << text("The electrification wire is cut - Cannot un-electrify the door.")
+ else if(secondsElectrified != 0)
+ usr << "The door is now un-electrified."
src.secondsElectrified = 0
- else if(src.secondsElectrified>0)
- src.secondsElectrified = 0
-
- if(8)
- // Safeties! We don't need no stinking safeties!
- if (src.isWireCut(AIRLOCK_WIRE_SAFETY))
- usr << text("Control to door sensors is disabled.")
- else if (src.safe)
- safe = 0
- else
- usr << text("Firmware reports safeties already overriden.")
-
-
-
- if(9)
- // Door speed control
- if(src.isWireCut(AIRLOCK_WIRE_SPEED))
- usr << text("Control to door timing circuitry has been severed.")
- else if (src.normalspeed)
- normalspeed = 0
- else
- usr << text("Door timing circurity already accellerated.")
-
if(7)
//close door
if(src.welded)
@@ -1028,17 +1014,31 @@ About the new airlock wires panel:
close()
else
open()
-
+ if(8)
+ // Safeties! We don't need no stinking safeties!
+ if (src.isWireCut(AIRLOCK_WIRE_SAFETY))
+ usr << text("Control to door sensors is disabled.")
+ else if (src.safe)
+ safe = 0
+ else
+ usr << text("Firmware reports safeties already overridden.")
+ if(9)
+ // Door speed control
+ if(src.isWireCut(AIRLOCK_WIRE_SPEED))
+ usr << text("Control to door timing circuitry has been severed.")
+ else if (src.normalspeed)
+ normalspeed = 0
+ else
+ usr << text("Door timing circuity already accelerated.")
if(10)
// Bolt lights
if(src.isWireCut(AIRLOCK_WIRE_LIGHT))
- usr << text("Control to door bolt lights has been severed.")
+ usr << "The bolt lights wire has been cut - The door bolt lights are already disabled."
else if (src.lights)
lights = 0
+ usr << "The door bolt lights have been disabled."
else
- usr << text("Door bolt lights are already disabled!")
-
-
+ usr << "The door bolt lights are already disabled!"
else if(href_list["aiEnable"])
var/code = text2num(href_list["aiEnable"])
@@ -1046,20 +1046,23 @@ About the new airlock wires panel:
if(1)
//enable idscan
if(src.isWireCut(AIRLOCK_WIRE_IDSCAN))
- usr << "You can't enable IdScan - The IdScan wire has been cut."
+ usr << "The IdScan wire has been cut - The IdScan feature cannot be enabled."
else if(src.aiDisabledIdScanner)
+ usr << "The IdScan feature has been enabled."
src.aiDisabledIdScanner = 0
else
- usr << "The IdScan feature is not disabled."
+ usr << "The IdScan feature is already enabled."
if(4)
//raise door bolts
if(src.isWireCut(AIRLOCK_WIRE_DOOR_BOLTS))
- usr << text("The door bolt control wire is cut - you can't raise the door bolts. \n")
+ usr << "The door bolt control wire has been cut - The door bolts cannot be raised."
else if(!src.locked)
- usr << text("The door bolts are already up. \n")
+ usr << "The door bolts are already raised."
else
- src.unlock()
-
+ if(src.unlock())
+ usr << "The door bolts have been raised."
+ else
+ usr << "Unable to raise door bolts."
if(5)
//electrify door for 30 seconds
if(src.isWireCut(AIRLOCK_WIRE_ELECTRIFY))
@@ -1067,17 +1070,17 @@ About the new airlock wires panel:
else if(src.secondsElectrified==-1)
usr << text("The door is already indefinitely electrified. You'd have to un-electrify it before you can re-electrify it with a non-forever duration. \n")
else if(src.secondsElectrified!=0)
- usr << text("The door is already electrified. You can't re-electrify it while it's already electrified. \n")
+ usr << text("The door is already electrified. Cannot re-electrify it while it's already electrified. \n")
else
shockedby += text("\[[time_stamp()]\][usr](ckey:[usr.ckey])")
usr.attack_log += text("\[[time_stamp()]\] Electrified the [name] at [x] [y] [z]")
+ usr << "The door is now electrified for thirty seconds."
src.secondsElectrified = 30
spawn(10)
while (src.secondsElectrified>0)
src.secondsElectrified-=1
if(src.secondsElectrified<0)
src.secondsElectrified = 0
- src.updateUsrDialog()
sleep(10)
if(6)
//electrify door indefinitely
@@ -1090,28 +1093,8 @@ About the new airlock wires panel:
else
shockedby += text("\[[time_stamp()]\][usr](ckey:[usr.ckey])")
usr.attack_log += text("\[[time_stamp()]\] Electrified the [name] at [x] [y] [z]")
+ usr << "The door is now electrified."
src.secondsElectrified = -1
-
- if (8) // Not in order >.>
- // Safeties! Maybe we do need some stinking safeties!
- if (src.isWireCut(AIRLOCK_WIRE_SAFETY))
- usr << text("Control to door sensors is disabled.")
- else if (!src.safe)
- safe = 1
- src.updateUsrDialog()
- else
- usr << text("Firmware reports safeties already in place.")
-
- if(9)
- // Door speed control
- if(src.isWireCut(AIRLOCK_WIRE_SPEED))
- usr << text("Control to door timing circuitry has been severed.")
- else if (!src.normalspeed)
- normalspeed = 1
- src.updateUsrDialog()
- else
- usr << text("Door timing circurity currently operating normally.")
-
if(7)
//open door
if(src.welded)
@@ -1122,16 +1105,31 @@ About the new airlock wires panel:
open()
else
close()
-
+ if (8)
+ // Safeties! Maybe we do need some stinking safeties!
+ if (src.isWireCut(AIRLOCK_WIRE_SAFETY))
+ usr << text("Control to door sensors is disabled.")
+ else if (!src.safe)
+ safe = 1
+ else
+ usr << text("Firmware reports safeties already in place.")
+ if(9)
+ // Door speed control
+ if(src.isWireCut(AIRLOCK_WIRE_SPEED))
+ usr << text("Control to door timing circuitry has been severed.")
+ else if (!src.normalspeed)
+ normalspeed = 1
+ else
+ usr << text("Door timing circuity currently operating normally.")
if(10)
// Bolt lights
if(src.isWireCut(AIRLOCK_WIRE_LIGHT))
- usr << text("Control to door bolt lights has been severed.")
+ usr << "The bolt lights wire has been cut - The door bolt lights cannot be enabled."
else if (!src.lights)
lights = 1
- src.updateUsrDialog()
+ usr << "The door bolt lights have been enabled"
else
- usr << text("Door bolt lights are already enabled!")
+ usr << "The door bolt lights are already enabled!"
add_fingerprint(usr)
update_icon()
@@ -1257,7 +1255,7 @@ About the new airlock wires panel:
if(!forced)
if( !arePowerSystemsOn() || isWireCut(AIRLOCK_WIRE_OPEN_DOOR) )
return 0
- use_power(50)
+ use_power(360) //360 W seems much more appropriate for an actuator moving an industrial door capable of crushing people
if(istype(src, /obj/machinery/door/airlock/glass))
playsound(src.loc, 'sound/machines/windowdoor.ogg', 100, 1)
else
@@ -1299,7 +1297,7 @@ About the new airlock wires panel:
if(istype(location, /turf/simulated))
location.add_blood(M)
- use_power(50)
+ use_power(360) //360 W seems much more appropriate for an actuator moving an industrial door capable of crushing people
if(istype(src, /obj/machinery/door/airlock/glass))
playsound(src.loc, 'sound/machines/windowdoor.ogg', 30, 1)
else
@@ -1327,6 +1325,8 @@ About the new airlock wires panel:
for(var/mob/M in range(1,src))
M.show_message("You hear a click from the bottom of the door.", 2)
update_icon()
+ return 1
+ return 0
/obj/machinery/door/airlock/New()
..()
diff --git a/code/game/machinery/doors/airlock_control.dm b/code/game/machinery/doors/airlock_control.dm
index d27261fb6f7..3b1700df784 100644
--- a/code/game/machinery/doors/airlock_control.dm
+++ b/code/game/machinery/doors/airlock_control.dm
@@ -94,7 +94,7 @@ obj/machinery/door/airlock/proc/command_completed(var/command)
return 1 //Unknown command. Just assume it's completed.
-obj/machinery/door/airlock/proc/send_status()
+obj/machinery/door/airlock/proc/send_status(var/bumped = 0)
if(radio_connection)
var/datum/signal/signal = new
signal.transmission_method = 1 //radio signal
@@ -103,6 +103,9 @@ obj/machinery/door/airlock/proc/send_status()
signal.data["door_status"] = density?("closed"):("open")
signal.data["lock_status"] = locked?("locked"):("unlocked")
+
+ if (bumped)
+ signal.data["bumped_with_access"] = 1
radio_connection.post_signal(src, signal, range = AIRLOCK_CONTROL_RANGE, filter = RADIO_AIRLOCK)
@@ -122,17 +125,7 @@ obj/machinery/door/airlock/Bumped(atom/AM)
if(istype(AM, /obj/mecha))
var/obj/mecha/mecha = AM
if(density && radio_connection && mecha.occupant && (src.allowed(mecha.occupant) || src.check_access_list(mecha.operation_req_access)))
- var/datum/signal/signal = new
- signal.transmission_method = 1 //radio signal
- signal.data["tag"] = id_tag
- signal.data["timestamp"] = world.time
-
- signal.data["door_status"] = density?("closed"):("open")
- signal.data["lock_status"] = locked?("locked"):("unlocked")
-
- signal.data["bumped_with_access"] = 1
-
- radio_connection.post_signal(src, signal, range = AIRLOCK_CONTROL_RANGE, filter = RADIO_AIRLOCK)
+ send_status(1)
return
obj/machinery/door/airlock/proc/set_frequency(new_frequency)
@@ -258,6 +251,12 @@ obj/machinery/access_button/update_icon()
else
icon_state = "access_button_off"
+obj/machinery/access_button/attackby(obj/item/I as obj, mob/user as mob)
+ //Swiping ID on the access button
+ if (istype(I, /obj/item/weapon/card/id) || istype(I, /obj/item/device/pda))
+ attack_hand(user)
+ return
+ ..()
obj/machinery/access_button/attack_hand(mob/user)
add_fingerprint(usr)
diff --git a/code/game/machinery/doors/brigdoors.dm b/code/game/machinery/doors/brigdoors.dm
index 978b6057866..b3dc9bf978b 100644
--- a/code/game/machinery/doors/brigdoors.dm
+++ b/code/game/machinery/doors/brigdoors.dm
@@ -34,9 +34,6 @@
/obj/machinery/door_timer/New()
..()
- pixel_x = ((src.dir & 3)? (0) : (src.dir == 4 ? 32 : -32))
- pixel_y = ((src.dir & 3)? (src.dir ==1 ? 24 : -32) : (0))
-
spawn(20)
for(var/obj/machinery/door/window/brigdoor/M in world)
if (M.id == src.id)
@@ -324,43 +321,26 @@
/obj/machinery/door_timer/cell_1
name = "Cell 1"
id = "Cell 1"
- dir = 2
- pixel_y = -32
-
/obj/machinery/door_timer/cell_2
name = "Cell 2"
id = "Cell 2"
- dir = 2
- pixel_y = -32
-
/obj/machinery/door_timer/cell_3
name = "Cell 3"
id = "Cell 3"
- dir = 2
- pixel_y = -32
-
/obj/machinery/door_timer/cell_4
name = "Cell 4"
id = "Cell 4"
- dir = 2
- pixel_y = -32
-
/obj/machinery/door_timer/cell_5
name = "Cell 5"
id = "Cell 5"
- dir = 2
- pixel_y = -32
-
/obj/machinery/door_timer/cell_6
name = "Cell 6"
id = "Cell 6"
- dir = 4
- pixel_x = 32
#undef FONT_SIZE
#undef FONT_COLOR
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index 12f224ac527..ceb04ad56b2 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -1,4 +1,6 @@
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31
+#define DOOR_OPEN_LAYER 2.7 //Under all objects if opened. 2.7 due to tables being at 2.6
+#define DOOR_CLOSED_LAYER 3.1 //Above most items if closed
/obj/machinery/door
name = "Door"
@@ -8,7 +10,9 @@
anchored = 1
opacity = 1
density = 1
- layer = 2.7
+ layer = DOOR_OPEN_LAYER
+ var/open_layer = DOOR_OPEN_LAYER
+ var/closed_layer = DOOR_CLOSED_LAYER
var/secondsElectrified = 0
var/visible = 1
@@ -27,11 +31,11 @@
/obj/machinery/door/New()
. = ..()
if(density)
- layer = 3.1 //Above most items if closed
+ layer = closed_layer
explosion_resistance = initial(explosion_resistance)
update_heat_protection(get_turf(src))
else
- layer = 2.7 //Under all objects if opened. 2.7 due to tables being at 2.6
+ layer = open_layer
explosion_resistance = 0
@@ -142,7 +146,7 @@
user = null
if(!src.requiresID())
user = null
- if(src.density && (istype(I, /obj/item/weapon/card/emag)||istype(I, /obj/item/weapon/melee/energy/blade)))
+ if(src.density && ((operable() && istype(I, /obj/item/weapon/card/emag)) || istype(I, /obj/item/weapon/melee/energy/blade)))
flick("door_spark", src)
sleep(6)
open()
@@ -226,7 +230,7 @@
icon_state = "door0"
src.SetOpacity(0)
sleep(10)
- src.layer = 2.7
+ src.layer = open_layer
src.density = 0
explosion_resistance = 0
update_icon()
@@ -250,10 +254,10 @@
if(operating > 0) return
operating = 1
- do_animate("closing")
src.density = 1
explosion_resistance = initial(explosion_resistance)
- src.layer = 3.1
+ src.layer = closed_layer
+ do_animate("closing")
sleep(10)
update_icon()
if(visible && !glass)
diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm
index 05a5fc13241..f610c25adc9 100644
--- a/code/game/machinery/doors/firedoor.dm
+++ b/code/game/machinery/doors/firedoor.dm
@@ -1,5 +1,15 @@
/var/const/OPEN = 1
/var/const/CLOSED = 2
+
+#define FIREDOOR_MAX_PRESSURE_DIFF 25 // kPa
+#define FIREDOOR_MAX_TEMP 50 // °C
+#define FIREDOOR_MIN_TEMP 0
+
+// Bitflags
+#define FIREDOOR_ALERT_HOT 1
+#define FIREDOOR_ALERT_COLD 2
+// Not used #define FIREDOOR_ALERT_LOWPRESS 4
+
/obj/machinery/door/firedoor
name = "\improper Emergency Shutter"
desc = "Emergency air-tight shutter, capable of sealing off breached areas."
@@ -8,12 +18,32 @@
req_one_access = list(access_atmospherics, access_engine_equip)
opacity = 0
density = 0
+ layer = DOOR_OPEN_LAYER - 0.1
+ open_layer = DOOR_OPEN_LAYER - 0.1 // Just below doors when open
+ closed_layer = DOOR_CLOSED_LAYER + 0.1 // Just above doors when closed
var/blocked = 0
+ var/lockdown = 0 // When the door has detected a problem, it locks.
+ var/pdiff_alert = 0
+ var/pdiff = 0
var/nextstate = null
var/net_id
var/list/areas_added
- var/list/users_to_open
+ var/list/users_to_open = new
+ var/next_process_time = 0
+
+ power_channel = ENVIRON
+ use_power = 1
+ idle_power_usage = 5
+
+ var/list/tile_info[4]
+ var/list/dir_alerts[4] // 4 dirs, bitflags
+
+ // MUST be in same order as FIREDOOR_ALERT_*
+ var/list/ALERT_STATES=list(
+ "hot",
+ "cold"
+ )
/obj/machinery/door/firedoor/New()
. = ..()
@@ -34,7 +64,6 @@
A.all_doors.Add(src)
areas_added += A
-
/obj/machinery/door/firedoor/Del()
for(var/area/A in areas_added)
A.all_doors.Remove(src)
@@ -44,14 +73,47 @@
/obj/machinery/door/firedoor/examine()
set src in view()
. = ..()
- if( islist(users_to_open) && users_to_open.len)
+
+ if(get_dist(src, usr) > 1 && !isAI(usr))
+ return
+
+ if(pdiff >= FIREDOOR_MAX_PRESSURE_DIFF)
+ usr << "WARNING: Current pressure differential is [pdiff]kPa! Opening door may result in injury!"
+
+ usr << "Sensor readings:"
+ for(var/index = 1; index <= tile_info.len; index++)
+ var/o = " "
+ switch(index)
+ if(1)
+ o += "NORTH: "
+ if(2)
+ o += "SOUTH: "
+ if(3)
+ o += "EAST: "
+ if(4)
+ o += "WEST: "
+ if(tile_info[index] == null)
+ o += "DATA UNAVAILABLE"
+ usr << o
+ continue
+ var/celsius = convert_k2c(tile_info[index][1])
+ var/pressure = tile_info[index][2]
+ if(dir_alerts[index] & (FIREDOOR_ALERT_HOT|FIREDOOR_ALERT_COLD))
+ o += ""
+ else
+ o += ""
+ o += "[celsius]°C "
+ o += ""
+ o += "[pressure]kPa"
+ usr << o
+
+ if(islist(users_to_open) && users_to_open.len)
var/users_to_open_string = users_to_open[1]
if(users_to_open.len >= 2)
for(var/i = 2 to users_to_open.len)
users_to_open_string += ", [users_to_open[i]]"
usr << "These people have opened \the [src] during an alert: [users_to_open_string]."
-
/obj/machinery/door/firedoor/Bumped(atom/AM)
if(p_open || operating)
return
@@ -59,22 +121,13 @@
return ..()
if(istype(AM, /obj/mecha))
var/obj/mecha/mecha = AM
- if (mecha.occupant)
+ if(mecha.occupant)
var/mob/M = mecha.occupant
if(world.time - M.last_bumped <= 10) return //Can bump-open one airlock per second. This is to prevent popup message spam.
M.last_bumped = world.time
attack_hand(M)
return 0
-
-/obj/machinery/door/firedoor/power_change()
- if(powered(ENVIRON))
- stat &= ~NOPOWER
- else
- stat |= NOPOWER
- return
-
-
/obj/machinery/door/firedoor/attack_hand(mob/user as mob)
add_fingerprint(user)
if(operating)
@@ -84,12 +137,7 @@
user << "\The [src] is welded solid!"
return
- if(!allowed(user))
- user << "Access denied."
- return
-
- var/alarmed = 0
-
+ var/alarmed = lockdown
for(var/area/A in areas_added) //Checks if there are fire alarms in any areas associated with that firedoor
if(A.fire || A.air_doors_activated)
alarmed = 1
@@ -101,10 +149,25 @@
if(user.stat || user.stunned || user.weakened || user.paralysis || (!user.canmove && !isAI(user)) || (get_dist(src, user) > 1 && !isAI(user)))
user << "Sorry, you must remain able bodied and close to \the [src] in order to use it."
return
+ if(density && (stat & (BROKEN|NOPOWER))) //can still close without power
+ user << "\The [src] is not functioning, you'll have to force it open manually."
+ return
+
+ if(alarmed && density && lockdown && !allowed(user))
+ user << "Access denied. Please wait for authorities to arrive, or for the alert to clear."
+ return
+ else
+ user.visible_message("\The [src] [density ? "open" : "close"]s for \the [user].",\
+ "\The [src] [density ? "open" : "close"]s.",\
+ "You hear a beep, and a door opening.")
var/needs_to_close = 0
if(density)
if(alarmed)
+ // Accountability!
+ users_to_open |= user.name
+ log_admin("[user]([user.ckey]) has opened an alarming emergency shutter.")
+ message_admins("[user]([user.ckey]) has opened an alarming emergency shutter.")
needs_to_close = 1
spawn()
open()
@@ -130,56 +193,104 @@
var/obj/item/weapon/weldingtool/W = C
if(W.remove_fuel(0, user))
blocked = !blocked
- user.visible_message("\red \The [user] [blocked ? "welds" : "unwelds"] \the [src] with \a [W].",\
+ user.visible_message("\The [user] [blocked ? "welds" : "unwelds"] \the [src] with \a [W].",\
"You [blocked ? "weld" : "unweld"] \the [src] with \the [W].",\
"You hear something being welded.")
update_icon()
return
if(blocked)
- user << "\red \The [src] is welded solid!"
+ user << "\The [src] is welded solid!"
return
-
- if( istype(C, /obj/item/weapon/crowbar) || ( istype(C,/obj/item/weapon/twohanded/fireaxe) && C:wielded == 1 ) )
+ if(istype(C, /obj/item/weapon/crowbar) || istype(C,/obj/item/weapon/melee/energy/blade) || istype(C,/obj/item/weapon/twohanded/fireaxe))
if(operating)
return
- if( blocked && istype(C, /obj/item/weapon/crowbar) )
- user.visible_message("\red \The [user] pries at \the [src] with \a [C], but \the [src] is welded in place!",\
+ if(blocked && istype(C, /obj/item/weapon/crowbar))
+ user.visible_message("\The [user] pries at \the [src] with \a [C], but \the [src] is welded in place!",\
"You try to pry \the [src] [density ? "open" : "closed"], but it is welded in place!",\
"You hear someone struggle and metal straining.")
return
- user.visible_message("\red \The [user] starts to force \the [src] [density ? "open" : "closed"] with \a [C]!",\
+ if(istype(C,/obj/item/weapon/twohanded/fireaxe))
+ var/obj/item/weapon/twohanded/fireaxe/F = C
+ if(!F.wielded)
+ return
+
+ user.visible_message("\The [user] starts to force \the [src] [density ? "open" : "closed"] with \a [C]!",\
"You start forcing \the [src] [density ? "open" : "closed"] with \the [C]!",\
"You hear metal strain.")
if(do_after(user,30))
- if( istype(C, /obj/item/weapon/crowbar) )
- if( stat & (BROKEN|NOPOWER) || !density)
- user.visible_message("\red \The [user] forces \the [src] [density ? "open" : "closed"] with \a [C]!",\
+ if(istype(C, /obj/item/weapon/crowbar))
+ if(stat & (BROKEN|NOPOWER) || !density)
+ user.visible_message("\The [user] forces \the [src] [density ? "open" : "closed"] with \a [C]!",\
"You force \the [src] [density ? "open" : "closed"] with \the [C]!",\
"You hear metal strain, and a door [density ? "open" : "close"].")
else
- user.visible_message("\red \The [user] forces \the [ blocked ? "welded" : "" ] [src] [density ? "open" : "closed"] with \a [C]!",\
+ user.visible_message("\The [user] forces \the [ blocked ? "welded" : "" ] [src] [density ? "open" : "closed"] with \a [C]!",\
"You force \the [ blocked ? "welded" : "" ] [src] [density ? "open" : "closed"] with \the [C]!",\
- "You hear metal strain and groan, and a door [density ? "open" : "close"].")
+ "You hear metal strain and groan, and a door [density ? "opening" : "closing"].")
if(density)
spawn(0)
- open()
+ open(1)
else
spawn(0)
close()
return
+// CHECK PRESSURE
+/obj/machinery/door/firedoor/process()
+ ..()
+ if(density && next_process_time <= world.time)
+ next_process_time = world.time + 100 // 10 second delays between process updates
+ var/changed = 0
+ lockdown=0
+ // Pressure alerts
+ pdiff = getOPressureDifferential(src.loc)
+ if(pdiff >= FIREDOOR_MAX_PRESSURE_DIFF)
+ lockdown = 1
+ if(!pdiff_alert)
+ pdiff_alert = 1
+ changed = 1 // update_icon()
+ else
+ if(pdiff_alert)
+ pdiff_alert = 0
+ changed = 1 // update_icon()
+
+ tile_info = getCardinalAirInfo(src.loc,list("temperature","pressure"))
+ var/old_alerts = dir_alerts
+ for(var/index = 1; index <= 4; index++)
+ var/list/tileinfo=tile_info[index]
+ if(tileinfo==null)
+ continue // Bad data.
+ var/celsius = convert_k2c(tileinfo[1])
+
+ var/alerts=0
+
+ // Temperatures
+ if(celsius >= FIREDOOR_MAX_TEMP)
+ alerts |= FIREDOOR_ALERT_HOT
+ lockdown = 1
+ else if(celsius <= FIREDOOR_MIN_TEMP)
+ alerts |= FIREDOOR_ALERT_COLD
+ lockdown = 1
+
+ dir_alerts[index]=alerts
+
+ if(dir_alerts != old_alerts)
+ changed = 1
+ if(changed)
+ update_icon()
/obj/machinery/door/firedoor/proc/latetoggle()
- if(operating || stat & NOPOWER || !nextstate)
+ if(operating || !nextstate)
return
switch(nextstate)
if(OPEN)
nextstate = null
+
open()
if(CLOSED)
nextstate = null
@@ -190,11 +301,18 @@
latetoggle()
return ..()
-/obj/machinery/door/firedoor/open()
+/obj/machinery/door/firedoor/open(var/forced = 0)
+ if(!forced)
+ if(stat & (BROKEN|NOPOWER))
+ return //needs power to open unless it was forced
+ else
+ use_power(360)
+ else
+ log_admin("[usr]([usr.ckey]) has forced open an emergency shutter.")
+ message_admins("[usr]([usr.ckey]) has forced open an emergency shutter.")
latetoggle()
return ..()
-
/obj/machinery/door/firedoor/do_animate(animation)
switch(animation)
if("opening")
@@ -210,6 +328,14 @@
icon_state = "door_closed"
if(blocked)
overlays += "welded"
+ if(pdiff_alert)
+ overlays += "palert"
+ if(dir_alerts)
+ for(var/d=1;d<=4;d++)
+ var/cdir = cardinal[d]
+ for(var/i=1;i<=ALERT_STATES.len;i++)
+ if(dir_alerts[d] & (1<<(i-1)))
+ overlays += new/icon(icon,"alert_[ALERT_STATES[i]]", dir=cdir)
else
icon_state = "door_open"
if(blocked)
@@ -217,7 +343,6 @@
return
-
/obj/machinery/door/firedoor/border_only
//These are playing merry hell on ZAS. Sorry fellas :(
/*
diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm
index 61d26340a55..44d374b85c0 100644
--- a/code/game/machinery/doors/windowdoor.dm
+++ b/code/game/machinery/doors/windowdoor.dm
@@ -173,7 +173,7 @@
return src.attack_hand(user)
/obj/machinery/door/window/attack_paw(mob/user as mob)
- if(istype(user, /mob/living/carbon/alien/humanoid) || istype(user, /mob/living/carbon/slime/adult))
+ if(istype(user, /mob/living/carbon/alien/humanoid))
if(src.operating)
return
playsound(src.loc, 'sound/effects/Glasshit.ogg', 75, 1)
@@ -346,4 +346,4 @@
/obj/machinery/door/window/brigdoor/southright
dir = SOUTH
icon_state = "rightsecure"
- base_state = "rightsecure"
+ base_state = "rightsecure"
diff --git a/code/game/machinery/doppler_array.dm b/code/game/machinery/doppler_array.dm
index 8648b33d829..c6744ef96ab 100644
--- a/code/game/machinery/doppler_array.dm
+++ b/code/game/machinery/doppler_array.dm
@@ -44,12 +44,11 @@ var/list/doppler_arrays = list()
/obj/machinery/doppler_array/power_change()
+ ..()
if(stat & BROKEN)
icon_state = "[initial(icon_state)]-broken"
else
- if( powered() )
+ if( !(stat & NOPOWER) )
icon_state = initial(icon_state)
- stat &= ~NOPOWER
else
- icon_state = "[initial(icon_state)]-off"
- stat |= NOPOWER
\ No newline at end of file
+ icon_state = "[initial(icon_state)]-off"
\ No newline at end of file
diff --git a/code/game/machinery/embedded_controller/airlock_controllers.dm b/code/game/machinery/embedded_controller/airlock_controllers.dm
index 4a48558f875..c7891ef69bf 100644
--- a/code/game/machinery/embedded_controller/airlock_controllers.dm
+++ b/code/game/machinery/embedded_controller/airlock_controllers.dm
@@ -1,12 +1,14 @@
//base type for controllers of two-door systems
/obj/machinery/embedded_controller/radio/airlock
// Setup parameters only
+ radio_filter = RADIO_AIRLOCK
var/tag_exterior_door
var/tag_interior_door
var/tag_airpump
var/tag_chamber_sensor
var/tag_exterior_sensor
var/tag_interior_sensor
+ var/tag_mech_sensor
var/tag_secure = 0
/obj/machinery/embedded_controller/radio/airlock/initialize()
@@ -17,7 +19,7 @@
/obj/machinery/embedded_controller/radio/airlock/advanced_airlock_controller
name = "Advanced Airlock Controller"
-/obj/machinery/embedded_controller/radio/airlock/advanced_airlock_controller/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null)
+/obj/machinery/embedded_controller/radio/airlock/advanced_airlock_controller/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
var/data[0]
data = list(
@@ -29,7 +31,7 @@
"secure" = program.memory["secure"]
)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, data)
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "advanced_airlock_console.tmpl", name, 470, 290)
@@ -43,10 +45,10 @@
/obj/machinery/embedded_controller/radio/airlock/advanced_airlock_controller/Topic(href, href_list)
if(..())
return
-
+
usr.set_machine(src)
src.add_fingerprint(usr)
-
+
var/clean = 0
switch(href_list["command"]) //anti-HTML-hacking checks
if("cycle_ext")
@@ -75,7 +77,7 @@
name = "Airlock Controller"
tag_secure = 1
-/obj/machinery/embedded_controller/radio/airlock/airlock_controller/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null)
+/obj/machinery/embedded_controller/radio/airlock/airlock_controller/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
var/data[0]
data = list(
@@ -85,7 +87,7 @@
"processing" = program.memory["processing"],
)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, data)
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "simple_airlock_console.tmpl", name, 470, 290)
@@ -99,10 +101,10 @@
/obj/machinery/embedded_controller/radio/airlock/airlock_controller/Topic(href, href_list)
if(..())
return
-
+
usr.set_machine(src)
src.add_fingerprint(usr)
-
+
var/clean = 0
switch(href_list["command"]) //anti-HTML-hacking checks
if("cycle_ext")
@@ -140,7 +142,7 @@
else
icon_state = "access_control_off"
-/obj/machinery/embedded_controller/radio/airlock/access_controller/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null)
+/obj/machinery/embedded_controller/radio/airlock/access_controller/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
var/data[0]
data = list(
@@ -149,7 +151,7 @@
"processing" = program.memory["processing"]
)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, data)
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "door_access_console.tmpl", name, 330, 220)
@@ -163,10 +165,10 @@
/obj/machinery/embedded_controller/radio/airlock/access_controller/Topic(href, href_list)
if(..())
return
-
+
usr.set_machine(src)
src.add_fingerprint(usr)
-
+
var/clean = 0
switch(href_list["command"]) //anti-HTML-hacking checks
if("cycle_ext_door")
diff --git a/code/game/machinery/embedded_controller/airlock_docking_controller.dm b/code/game/machinery/embedded_controller/airlock_docking_controller.dm
index 6edf07e221e..a78ac2be2d8 100644
--- a/code/game/machinery/embedded_controller/airlock_docking_controller.dm
+++ b/code/game/machinery/embedded_controller/airlock_docking_controller.dm
@@ -11,7 +11,7 @@
docking_program = new/datum/computer/file/embedded_program/docking/airlock(src, airlock_program)
program = docking_program
-/obj/machinery/embedded_controller/radio/airlock/docking_port/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null)
+/obj/machinery/embedded_controller/radio/airlock/docking_port/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
var/data[0]
data = list(
@@ -24,7 +24,7 @@
"override_enabled" = docking_program.override_enabled,
)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, data)
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "docking_airlock_console.tmpl", name, 470, 290)
@@ -35,10 +35,10 @@
/obj/machinery/embedded_controller/radio/airlock/docking_port/Topic(href, href_list)
if(..())
return
-
+
usr.set_machine(src)
src.add_fingerprint(usr)
-
+
var/clean = 0
switch(href_list["command"]) //anti-HTML-hacking checks
if("cycle_ext")
@@ -77,7 +77,7 @@
else
enable_override()
return
-
+
..(command)
airlock_program.receive_user_command(command) //pass along to subprograms
@@ -99,12 +99,14 @@
//we are docked, open the doors or whatever.
/datum/computer/file/embedded_program/docking/airlock/finish_docking()
+ airlock_program.enable_mech_regulators()
airlock_program.open_doors()
//tell the docking port to start getting ready for undocking - e.g. close those doors.
/datum/computer/file/embedded_program/docking/airlock/prepare_for_undocking()
airlock_program.stop_cycling()
airlock_program.close_doors()
+ airlock_program.disable_mech_regulators()
//are we ready for undocking?
/datum/computer/file/embedded_program/docking/airlock/ready_for_undocking()
@@ -115,13 +117,18 @@
//An airlock controller to be used by the airlock-based docking port controller.
//Same as a regular airlock controller but allows disabling of the regular airlock functions when docking
/datum/computer/file/embedded_program/airlock/docking
-
var/datum/computer/file/embedded_program/docking/airlock/master_prog
/datum/computer/file/embedded_program/airlock/docking/receive_user_command(command)
if (master_prog.undocked() || master_prog.override_enabled) //only allow the port to be used as an airlock if nothing is docked here or the override is enabled
..(command)
+/datum/computer/file/embedded_program/airlock/docking/proc/enable_mech_regulators()
+ enable_mech_regulation()
+
+/datum/computer/file/embedded_program/airlock/docking/proc/disable_mech_regulators()
+ disable_mech_regulation()
+
/datum/computer/file/embedded_program/airlock/docking/proc/open_doors()
toggleDoor(memory["interior_status"], tag_interior_door, memory["secure"], "open")
toggleDoor(memory["exterior_status"], tag_exterior_door, memory["secure"], "open")
diff --git a/code/game/machinery/embedded_controller/airlock_docking_controller_multi.dm b/code/game/machinery/embedded_controller/airlock_docking_controller_multi.dm
index 99c7bf57848..d03bffcbae5 100644
--- a/code/game/machinery/embedded_controller/airlock_docking_controller_multi.dm
+++ b/code/game/machinery/embedded_controller/airlock_docking_controller_multi.dm
@@ -22,7 +22,7 @@
child_names[tags[i]] = names[i]
-/obj/machinery/embedded_controller/radio/docking_port_multi/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null)
+/obj/machinery/embedded_controller/radio/docking_port_multi/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
var/data[0]
var/list/airlocks[child_names.len]
@@ -35,7 +35,7 @@
"airlocks" = airlocks,
)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, data)
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "multi_docking_console.tmpl", name, 470, 290)
@@ -60,7 +60,7 @@
airlock_program = new/datum/computer/file/embedded_program/airlock/multi_docking(src)
program = airlock_program
-/obj/machinery/embedded_controller/radio/airlock/docking_port_multi/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null)
+/obj/machinery/embedded_controller/radio/airlock/docking_port_multi/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
var/data[0]
data = list(
@@ -73,7 +73,7 @@
"override_enabled" = airlock_program.override_enabled,
)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, data)
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "docking_airlock_console.tmpl", name, 470, 290)
diff --git a/code/game/machinery/embedded_controller/airlock_program.dm b/code/game/machinery/embedded_controller/airlock_program.dm
index 3d7d306df38..ed93dca9584 100644
--- a/code/game/machinery/embedded_controller/airlock_program.dm
+++ b/code/game/machinery/embedded_controller/airlock_program.dm
@@ -1,8 +1,9 @@
//Handles the control of airlocks
-#define STATE_WAIT 0
-#define STATE_DEPRESSURIZE 1
-#define STATE_PRESSURIZE 2
+#define STATE_IDLE 0
+#define STATE_PREPARE 1
+#define STATE_DEPRESSURIZE 2
+#define STATE_PRESSURIZE 3
#define TARGET_NONE 0
#define TARGET_INOPEN -1
@@ -16,13 +17,14 @@
var/tag_chamber_sensor
var/tag_exterior_sensor
var/tag_interior_sensor
+ var/tag_mech_sensor
- var/state = STATE_WAIT
+ var/state = STATE_IDLE
var/target_state = TARGET_NONE
/datum/computer/file/embedded_program/airlock/New(var/obj/machinery/embedded_controller/M)
..(M)
-
+
memory["chamber_sensor_pressure"] = ONE_ATMOSPHERE
memory["external_sensor_pressure"] = 0 //assume vacuum for simple airlock controller
memory["internal_sensor_pressure"] = ONE_ATMOSPHERE
@@ -32,7 +34,7 @@
memory["target_pressure"] = ONE_ATMOSPHERE
memory["purge"] = 0
memory["secure"] = 0
-
+
if (istype(M, /obj/machinery/embedded_controller/radio/airlock)) //if our controller is an airlock controller than we can auto-init our tags
var/obj/machinery/embedded_controller/radio/airlock/controller = M
tag_exterior_door = controller.tag_exterior_door? controller.tag_exterior_door : "[id_tag]_outer"
@@ -41,8 +43,9 @@
tag_chamber_sensor = controller.tag_chamber_sensor? controller.tag_chamber_sensor : "[id_tag]_sensor"
tag_exterior_sensor = controller.tag_exterior_sensor
tag_interior_sensor = controller.tag_interior_sensor
+ tag_mech_sensor = controller.tag_mech_sensor? controller.tag_mech_sensor : "[id_tag]_mech"
memory["secure"] = controller.tag_secure
-
+
spawn(10)
signalDoor(tag_exterior_door, "update") //signals connected doors to update their status
signalDoor(tag_interior_door, "update")
@@ -155,7 +158,7 @@
/datum/computer/file/embedded_program/airlock/process()
- if(!state)
+ if(!state) //Idle
if(target_state)
switch(target_state)
if(TARGET_INOPEN)
@@ -166,38 +169,45 @@
//lock down the airlock before activating pumps
close_doors()
- var/chamber_pressure = memory["chamber_sensor_pressure"]
- var/target_pressure = memory["target_pressure"]
-
- if(memory["purge"])
- target_pressure = 0
-
- if(chamber_pressure <= target_pressure)
- state = STATE_PRESSURIZE
- signalPump(tag_airpump, 1, 1, target_pressure) //send a signal to start pressurizing
-
- else if(chamber_pressure > target_pressure)
- state = STATE_DEPRESSURIZE
- signalPump(tag_airpump, 1, 0, target_pressure) //send a signal to start depressurizing
-
- //Check for vacuum - this is set after the pumps so the pumps are aiming for 0
- if(!memory["target_pressure"])
- memory["target_pressure"] = ONE_ATMOSPHERE * 0.05
+ state = STATE_PREPARE
else
//make sure to return to a sane idle state
if(memory["pump_status"] != "off") //send a signal to stop pumping
signalPump(tag_airpump, 0)
-
- //the airlock will not allow itself to continue to cycle when any of the doors are forced open.
- if (state && !check_doors_secured())
+
+ if ((state == STATE_PRESSURIZE || state == STATE_DEPRESSURIZE) && !check_doors_secured())
+ //the airlock will not allow itself to continue to cycle when any of the doors are forced open.
stop_cycling()
-
+
switch(state)
+ if(STATE_PREPARE)
+ if (check_doors_secured())
+ var/chamber_pressure = memory["chamber_sensor_pressure"]
+ var/target_pressure = memory["target_pressure"]
+
+ if(memory["purge"])
+ target_pressure = 0
+
+ if(memory["purge"])
+ target_pressure = 0
+
+ if(chamber_pressure <= target_pressure)
+ state = STATE_PRESSURIZE
+ signalPump(tag_airpump, 1, 1, target_pressure) //send a signal to start pressurizing
+
+ else if(chamber_pressure > target_pressure)
+ state = STATE_DEPRESSURIZE
+ signalPump(tag_airpump, 1, 0, target_pressure) //send a signal to start depressurizing
+
+ //Check for vacuum - this is set after the pumps so the pumps are aiming for 0
+ if(!memory["target_pressure"])
+ memory["target_pressure"] = ONE_ATMOSPHERE * 0.05
+
if(STATE_PRESSURIZE)
if(memory["chamber_sensor_pressure"] >= memory["target_pressure"] * 0.95)
cycleDoors(target_state)
- state = STATE_WAIT
+ state = STATE_IDLE
target_state = TARGET_NONE
if(memory["pump_status"] != "off")
@@ -214,7 +224,7 @@
else if(memory["chamber_sensor_pressure"] <= memory["target_pressure"] * 1.05)
cycleDoors(target_state)
- state = STATE_WAIT
+ state = STATE_IDLE
target_state = TARGET_NONE
//send a signal to stop pumping
@@ -229,11 +239,11 @@
//these are here so that other types don't have to make so many assuptions about our implementation
/datum/computer/file/embedded_program/airlock/proc/begin_cycle_in()
- state = STATE_WAIT
+ state = STATE_IDLE
target_state = TARGET_INOPEN
/datum/computer/file/embedded_program/airlock/proc/begin_cycle_out()
- state = STATE_WAIT
+ state = STATE_IDLE
target_state = TARGET_OUTOPEN
/datum/computer/file/embedded_program/airlock/proc/close_doors()
@@ -241,11 +251,11 @@
toggleDoor(memory["exterior_status"], tag_exterior_door, 1, "close")
/datum/computer/file/embedded_program/airlock/proc/stop_cycling()
- state = STATE_WAIT
+ state = STATE_IDLE
target_state = TARGET_NONE
/datum/computer/file/embedded_program/airlock/proc/done_cycling()
- return (state == STATE_WAIT && target_state == TARGET_NONE)
+ return (state == STATE_IDLE && target_state == TARGET_NONE)
//are the doors closed and locked?
/datum/computer/file/embedded_program/airlock/proc/check_exterior_door_secured()
@@ -263,7 +273,7 @@
var/datum/signal/signal = new
signal.data["tag"] = tag
signal.data["command"] = command
- post_signal(signal)
+ post_signal(signal, RADIO_AIRLOCK)
/datum/computer/file/embedded_program/airlock/proc/signalPump(var/tag, var/power, var/direction, var/pressure)
var/datum/signal/signal = new
@@ -293,6 +303,11 @@
signalDoor(tag_exterior_door, command)
signalDoor(tag_interior_door, command)
+/datum/computer/file/embedded_program/airlock/proc/enable_mech_regulation()
+ signalDoor(tag_mech_sensor, "enable")
+
+/datum/computer/file/embedded_program/airlock/proc/disable_mech_regulation()
+ signalDoor(tag_mech_sensor, "disable")
/*----------------------------------------------------------
toggleDoor()
@@ -348,7 +363,7 @@ send an additional command to open the door again.
signalDoor(doorTag, doorCommand)
-#undef STATE_WAIT
+#undef STATE_IDLE
#undef STATE_DEPRESSURIZE
#undef STATE_PRESSURIZE
diff --git a/code/game/machinery/embedded_controller/docking_program_multi.dm b/code/game/machinery/embedded_controller/docking_program_multi.dm
index 13b542ff057..3901591720c 100644
--- a/code/game/machinery/embedded_controller/docking_program_multi.dm
+++ b/code/game/machinery/embedded_controller/docking_program_multi.dm
@@ -13,12 +13,12 @@
/datum/computer/file/embedded_program/docking/multi/New(var/obj/machinery/embedded_controller/M)
..(M)
-
+
if (istype(M,/obj/machinery/embedded_controller/radio/docking_port_multi)) //if our parent controller is the right type, then we can auto-init stuff at construction
var/obj/machinery/embedded_controller/radio/docking_port_multi/controller = M
//parse child_tags_txt and create child tags
children_tags = text2list(controller.child_tags_txt, ";")
-
+
children_ready = list()
children_override = list()
for (var/child_tag in children_tags)
@@ -33,13 +33,13 @@
var/receive_tag = signal.data["tag"] //for docking signals, this is the sender id
var/command = signal.data["command"]
var/recipient = signal.data["recipient"] //the intended recipient of the docking signal
-
+
if (receive_tag in children_tags)
-
+
//track children status
if (signal.data["override_status"])
children_override[receive_tag] = signal.data["override_status"]
-
+
if (recipient == id_tag)
switch (command)
if ("ready_for_docking")
@@ -50,13 +50,13 @@
children_override[receive_tag] = 1
if ("status_override_disabled")
children_override[receive_tag] = 0
-
+
..(signal, receive_method, receive_param)
/datum/computer/file/embedded_program/docking/multi/prepare_for_docking()
//clear children ready status
clear_children_ready_status()
-
+
//tell children to prepare for docking
for (var/child_tag in children_tags)
send_docking_command(child_tag, "prepare_for_docking")
@@ -72,14 +72,14 @@
//tell children to finish docking
for (var/child_tag in children_tags)
send_docking_command(child_tag, "finish_docking")
-
+
//clear ready flags
clear_children_ready_status()
/datum/computer/file/embedded_program/docking/multi/prepare_for_undocking()
//clear children ready status
clear_children_ready_status()
-
+
//tell children to prepare for undocking
for (var/child_tag in children_tags)
send_docking_command(child_tag, "prepare_for_undocking")
@@ -95,7 +95,7 @@
//tell children to finish undocking
for (var/child_tag in children_tags)
send_docking_command(child_tag, "finish_undocking")
-
+
//clear ready flags
clear_children_ready_status()
@@ -108,7 +108,7 @@
*/
/datum/computer/file/embedded_program/airlock/multi_docking
var/master_tag
-
+
var/master_status = "undocked"
var/override_enabled = 0
var/docking_enabled = 0
@@ -117,7 +117,7 @@
/datum/computer/file/embedded_program/airlock/multi_docking/New(var/obj/machinery/embedded_controller/M)
..(M)
-
+
if (istype(M, /obj/machinery/embedded_controller/radio/airlock/docking_port_multi)) //if our parent controller is the right type, then we can auto-init stuff at construction
var/obj/machinery/embedded_controller/radio/airlock/docking_port_multi/controller = M
src.master_tag = controller.master_tag
@@ -131,56 +131,58 @@
override_enabled = 1
broadcast_override_status()
return
-
+
if (!docking_enabled|| override_enabled) //only allow the port to be used as an airlock if nothing is docked here or the override is enabled
..(command)
/datum/computer/file/embedded_program/airlock/multi_docking/receive_signal(datum/signal/signal, receive_method, receive_param)
..()
-
+
var/receive_tag = signal.data["tag"] //for docking signals, this is the sender id
var/command = signal.data["command"]
var/recipient = signal.data["recipient"] //the intended recipient of the docking signal
-
+
if (receive_tag != master_tag)
return //only respond to master
-
+
//track master's status
if (signal.data["dock_status"])
master_status = signal.data["dock_status"]
-
+
if (recipient != id_tag)
return //this signal is not for us
-
+
switch (command)
if ("prepare_for_docking")
docking_enabled = 1
docking_mode = 0
response_sent = 0
-
+
if (!override_enabled)
begin_cycle_in()
-
+
if ("prepare_for_undocking")
docking_mode = 1
response_sent = 0
-
+
if (!override_enabled)
stop_cycling()
close_doors()
-
+ disable_mech_regulation()
+
if ("finish_docking")
if (!override_enabled)
+ enable_mech_regulation()
open_doors()
-
+
if ("finish_undocking")
docking_enabled = 0
/datum/computer/file/embedded_program/airlock/multi_docking/process()
..()
-
+
if (docking_enabled && !response_sent)
-
+
switch (docking_mode)
if (0) //docking
if (ready_for_docking())
diff --git a/code/game/machinery/embedded_controller/embedded_controller_base.dm b/code/game/machinery/embedded_controller/embedded_controller_base.dm
index a9a8b40ed58..ec411753803 100644
--- a/code/game/machinery/embedded_controller/embedded_controller_base.dm
+++ b/code/game/machinery/embedded_controller/embedded_controller_base.dm
@@ -3,6 +3,9 @@
name = "Embedded Controller"
anchored = 1
+
+ use_power = 1
+ idle_power_usage = 10
var/on = 1
@@ -43,8 +46,10 @@
density = 0
var/id_tag
+ //var/radio_power_use = 50 //power used to xmit signals
var/frequency = 1379
+ var/radio_filter = null
var/datum/radio_frequency/radio_connection
unacidable = 1
@@ -60,14 +65,15 @@
else
icon_state = "airlock_control_off"
-/obj/machinery/embedded_controller/radio/post_signal(datum/signal/signal)
+/obj/machinery/embedded_controller/radio/post_signal(datum/signal/signal, var/filter = null)
signal.transmission_method = TRANSMISSION_RADIO
if(radio_connection)
- return radio_connection.post_signal(src, signal)
+ //use_power(radio_power_use) //neat idea, but causes way too much lag.
+ return radio_connection.post_signal(src, signal, filter)
else
del(signal)
/obj/machinery/embedded_controller/radio/proc/set_frequency(new_frequency)
radio_controller.remove_object(src, frequency)
frequency = new_frequency
- radio_connection = radio_controller.add_object(src, frequency)
\ No newline at end of file
+ radio_connection = radio_controller.add_object(src, frequency, radio_filter)
\ No newline at end of file
diff --git a/code/game/machinery/embedded_controller/simple_docking_controller.dm b/code/game/machinery/embedded_controller/simple_docking_controller.dm
index a8a4ad490f6..83361c3af34 100644
--- a/code/game/machinery/embedded_controller/simple_docking_controller.dm
+++ b/code/game/machinery/embedded_controller/simple_docking_controller.dm
@@ -9,7 +9,7 @@
docking_program = new/datum/computer/file/embedded_program/docking/simple(src)
program = docking_program
-/obj/machinery/embedded_controller/radio/simple_docking_controller/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null)
+/obj/machinery/embedded_controller/radio/simple_docking_controller/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
var/data[0]
data = list(
@@ -19,7 +19,7 @@
"door_lock" = docking_program.memory["door_status"]["lock"],
)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, data)
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "simple_docking_console.tmpl", name, 470, 290)
@@ -30,17 +30,17 @@
/obj/machinery/embedded_controller/radio/simple_docking_controller/Topic(href, href_list)
if(..())
return 1
-
+
usr.set_machine(src)
src.add_fingerprint(usr)
-
+
var/clean = 0
switch(href_list["command"]) //anti-HTML-hacking checks
if("force_door")
clean = 1
if("toggle_override")
clean = 1
-
+
if(clean)
program.receive_user_command(href_list["command"])
@@ -57,24 +57,24 @@
if (istype(M, /obj/machinery/embedded_controller/radio/simple_docking_controller))
var/obj/machinery/embedded_controller/radio/simple_docking_controller/controller = M
-
+
tag_door = controller.tag_door? controller.tag_door : "[id_tag]_hatch"
-
+
spawn(10)
signal_door("update") //signals connected doors to update their status
-
-
+
+
/datum/computer/file/embedded_program/docking/simple/receive_signal(datum/signal/signal, receive_method, receive_param)
var/receive_tag = signal.data["tag"]
-
+
if(!receive_tag) return
-
+
if(receive_tag==tag_door)
memory["door_status"]["state"] = signal.data["door_status"]
memory["door_status"]["lock"] = signal.data["lock_status"]
-
+
..(signal, receive_method, receive_param)
-
+
/datum/computer/file/embedded_program/docking/simple/receive_user_command(command)
switch(command)
if("force_door")
@@ -96,8 +96,12 @@
signal.data["command"] = command
post_signal(signal)
+/datum/computer/file/embedded_program/docking/simple/proc/signal_mech_sensor(var/command)
+ signal_door(command)
+
/datum/computer/file/embedded_program/docking/simple/proc/open_door()
if(memory["door_status"]["state"] == "closed")
+ signal_mech_sensor("enable")
signal_door("secure_open")
else if(memory["door_status"]["lock"] == "unlocked")
signal_door("lock")
@@ -105,6 +109,7 @@
/datum/computer/file/embedded_program/docking/simple/proc/close_door()
if(memory["door_status"]["state"] == "open")
signal_door("secure_close")
+ signal_mech_sensor("disable")
else if(memory["door_status"]["lock"] == "unlocked")
signal_door("lock")
diff --git a/code/game/machinery/flasher.dm b/code/game/machinery/flasher.dm
index eff51fc6976..cf05308e400 100644
--- a/code/game/machinery/flasher.dm
+++ b/code/game/machinery/flasher.dm
@@ -28,12 +28,11 @@
src.sd_SetLuminosity(2)
*/
/obj/machinery/flasher/power_change()
- if ( powered() )
- stat &= ~NOPOWER
+ ..()
+ if ( !(stat & NOPOWER) )
icon_state = "[base_state]1"
// src.sd_SetLuminosity(2)
else
- stat |= ~NOPOWER
icon_state = "[base_state]1-p"
// src.sd_SetLuminosity(0)
@@ -64,7 +63,7 @@
playsound(src.loc, 'sound/weapons/flash.ogg', 100, 1)
flick("[base_state]_flash", src)
src.last_flash = world.time
- use_power(1000)
+ use_power(1500)
for (var/mob/O in viewers(src, null))
if (get_dist(src, O) > src.range)
diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm
index a167056da5a..66708db7f7b 100644
--- a/code/game/machinery/hologram.dm
+++ b/code/game/machinery/hologram.dm
@@ -153,12 +153,6 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
active_power_usage = 100
var/obj/effect/overlay/hologram//The projection itself. If there is one, the instrument is on, off otherwise.
-/obj/machinery/hologram/power_change()
- if (powered())
- stat &= ~NOPOWER
- else
- stat |= ~NOPOWER
-
//Destruction procs.
/obj/machinery/hologram/ex_act(severity)
switch(severity)
diff --git a/code/game/machinery/igniter.dm b/code/game/machinery/igniter.dm
index 2f443fdac1f..dcc67a5af24 100755
--- a/code/game/machinery/igniter.dm
+++ b/code/game/machinery/igniter.dm
@@ -38,6 +38,7 @@
icon_state = "igniter[on]"
/obj/machinery/igniter/power_change()
+ ..()
if(!( stat & NOPOWER) )
icon_state = "igniter[src.on]"
else
@@ -60,12 +61,12 @@
..()
/obj/machinery/sparker/power_change()
- if ( powered() && disable == 0 )
- stat &= ~NOPOWER
+ ..()
+ if ( !(stat & NOPOWER) && disable == 0 )
+
icon_state = "[base_state]"
// src.sd_SetLuminosity(2)
else
- stat |= ~NOPOWER
icon_state = "[base_state]-p"
// src.sd_SetLuminosity(0)
diff --git a/code/game/machinery/kitchen/smartfridge.dm b/code/game/machinery/kitchen/smartfridge.dm
index 8eca8dce3af..d7db1df37e7 100644
--- a/code/game/machinery/kitchen/smartfridge.dm
+++ b/code/game/machinery/kitchen/smartfridge.dm
@@ -118,17 +118,16 @@
src.throw_item()
/obj/machinery/smartfridge/power_change()
- if( powered() )
+ ..()
+ if( !(stat & NOPOWER) )
src.ispowered = 1
- stat &= ~NOPOWER
if(!isbroken)
icon_state = icon_on
else
spawn(rand(0, 15))
- src.ispowered = 0
- stat |= NOPOWER
- if(!isbroken)
- icon_state = icon_off
+ src.ispowered = 0
+ if(!isbroken)
+ icon_state = icon_off
/*******************
* Item Adding
@@ -225,7 +224,7 @@
* SmartFridge Menu
********************/
-/obj/machinery/smartfridge/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null)
+/obj/machinery/smartfridge/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
user.set_machine(src)
var/is_secure = istype(src,/obj/machinery/smartfridge/secure)
@@ -269,7 +268,7 @@
if (vendor_wires.len > 0)
data["wires"] = vendor_wires
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, data)
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "smartfridge.tmpl", src.name, 400, 500)
ui.set_initial_data(data)
diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm
index e13147f5fb2..0c2f3911438 100644
--- a/code/game/machinery/machinery.dm
+++ b/code/game/machinery/machinery.dm
@@ -157,6 +157,21 @@ Class Procs:
if(prob(50))
del(src)
+//sets the use_power var and then forces an area power update
+/obj/machinery/proc/update_use_power(var/new_use_power)
+ if (new_use_power == use_power)
+ return //don't need to do anything
+
+ use_power = new_use_power
+
+ //force area power update
+ force_power_update()
+
+/obj/machinery/proc/force_power_update()
+ var/area/A = get_area(src)
+ if(A && A.master)
+ A.master.powerupdate = 1
+
/obj/machinery/proc/auto_use_power()
if(!powered(power_channel))
return 0
@@ -166,9 +181,15 @@ Class Procs:
use_power(active_power_usage,power_channel, 1)
return 1
+/obj/machinery/proc/operable(var/additional_flags = 0)
+ return !inoperable(additional_flags)
+
+/obj/machinery/proc/inoperable(var/additional_flags = 0)
+ return (stat & (NOPOWER|BROKEN|additional_flags))
+
/obj/machinery/Topic(href, href_list)
..()
- if(stat & (NOPOWER|BROKEN))
+ if(inoperable())
return 1
if(usr.restrained() || usr.lying || usr.stat)
return 1
@@ -210,7 +231,7 @@ Class Procs:
return src.attack_hand(user)
/obj/machinery/attack_hand(mob/user as mob)
- if(stat & (NOPOWER|BROKEN|MAINT))
+ if(inoperable(MAINT))
return 1
if(user.lying || user.stat)
return 1
@@ -260,7 +281,7 @@ Class Procs:
playsound(src.loc, 'sound/machines/ping.ogg', 50, 0)
/obj/machinery/proc/shock(mob/user, prb)
- if(stat & (BROKEN|NOPOWER))
+ if(inoperable())
return 0
if(!prob(prb))
return 0
diff --git a/code/game/machinery/newscaster.dm b/code/game/machinery/newscaster.dm
index b1783b1707c..114d24e57b6 100644
--- a/code/game/machinery/newscaster.dm
+++ b/code/game/machinery/newscaster.dm
@@ -138,14 +138,13 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co
/obj/machinery/newscaster/power_change()
if(isbroken) //Broken shit can't be powered.
return
- if( src.powered() )
+ ..()
+ if( !(stat & NOPOWER) )
src.ispowered = 1
- stat &= ~NOPOWER
src.update_icon()
else
spawn(rand(0, 15))
src.ispowered = 0
- stat |= NOPOWER
src.update_icon()
@@ -748,16 +747,27 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co
/obj/machinery/newscaster/proc/AttachPhoto(mob/user as mob)
if(photo)
- photo.loc = src.loc
- user.put_in_inactive_hand(photo)
+ if(!issilicon(user))
+ photo.loc = src.loc
+ user.put_in_inactive_hand(photo)
photo = null
if(istype(user.get_active_hand(), /obj/item/weapon/photo))
photo = user.get_active_hand()
user.drop_item()
photo.loc = src
+ else if(istype(user,/mob/living/silicon))
+ var/mob/living/silicon/tempAI = user
+ var/obj/item/device/camera/siliconcam/camera = tempAI.aiCamera
+ if(!camera)
+ return
+ var/datum/picture/selection = camera.selectpicture()
+ if (!selection)
+ return
-
+ var/obj/item/weapon/photo/P = new/obj/item/weapon/photo()
+ P.construct(selection)
+ photo = P
//########################################################################################################################
diff --git a/code/game/machinery/overview.dm b/code/game/machinery/overview.dm
index 35954c0c8b6..de67e7cea10 100644
--- a/code/game/machinery/overview.dm
+++ b/code/game/machinery/overview.dm
@@ -210,7 +210,7 @@
if("/turf/simulated/floor", "/turf/simulated/floor/engine")
var/datum/gas_mixture/environment = T.return_air()
- var/turf_total = environment.total_moles()
+ var/turf_total = environment.total_moles
var/t1 = turf_total / MOLES_CELLSTANDARD * 175
if(t1<=100)
diff --git a/code/game/machinery/pipe/construction.dm b/code/game/machinery/pipe/construction.dm
index 864e893dc9f..0a5c83648dd 100644
--- a/code/game/machinery/pipe/construction.dm
+++ b/code/game/machinery/pipe/construction.dm
@@ -52,7 +52,7 @@ Buildable meters
if (make_from)
src.dir = make_from.dir
src.pipename = make_from.name
- color = make_from.color
+ color = make_from.pipe_color
var/is_bent
if (make_from.initialize_directions in list(NORTH|SOUTH, WEST|EAST))
is_bent = 0
@@ -74,6 +74,8 @@ Buildable meters
src.pipe_type = PIPE_UVENT
else if(istype(make_from, /obj/machinery/atmospherics/valve))
src.pipe_type = PIPE_MVALVE
+ else if(istype(make_from, /obj/machinery/atmospherics/binary/pump/high_power))
+ src.pipe_type = PIPE_VOLUME_PUMP
else if(istype(make_from, /obj/machinery/atmospherics/binary/pump))
src.pipe_type = PIPE_PUMP
else if(istype(make_from, /obj/machinery/atmospherics/trinary/filter/m_filter))
@@ -90,8 +92,6 @@ Buildable meters
src.pipe_type = PIPE_SCRUBBER
else if(istype(make_from, /obj/machinery/atmospherics/binary/passive_gate))
src.pipe_type = PIPE_PASSIVE_GATE
- else if(istype(make_from, /obj/machinery/atmospherics/binary/volume_pump))
- src.pipe_type = PIPE_VOLUME_PUMP
else if(istype(make_from, /obj/machinery/atmospherics/unary/heat_exchanger))
src.pipe_type = PIPE_HEAT_EXCHANGE
else if(istype(make_from, /obj/machinery/atmospherics/tvalve))
@@ -137,8 +137,8 @@ Buildable meters
"bent insulated pipe", \
"gas filter", \
"gas mixer", \
- "passive gate", \
- "volume pump", \
+ "pressure regulator", \
+ "high power pump", \
"heat exchanger", \
"t-valve", \
"4-way manifold", \
@@ -324,12 +324,13 @@ Buildable meters
return 1
// no conflicts found
- var/pipefailtext = "\red There's nothing to connect this pipe section to! (with how the pipe code works, at least one end needs to be connected to something, otherwise the game deletes the segment)"
+ var/pipefailtext = "\red There's nothing to connect this pipe section to!" //(with how the pipe code works, at least one end needs to be connected to something, otherwise the game deletes the segment)"
+ //TODO: Move all of this stuff into the various pipe constructors.
switch(pipe_type)
if(PIPE_SIMPLE_STRAIGHT, PIPE_SIMPLE_BENT)
var/obj/machinery/atmospherics/pipe/simple/P = new( src.loc )
- P.color = color
+ P.pipe_color = color
P.dir = src.dir
P.initialize_directions = pipe_dir
var/turf/T = P.loc
@@ -349,7 +350,7 @@ Buildable meters
if(PIPE_HE_STRAIGHT, PIPE_HE_BENT)
var/obj/machinery/atmospherics/pipe/simple/heat_exchanging/P = new ( src.loc )
P.dir = src.dir
- P.initialize_directions = 0
+ P.initialize_directions = pipe_dir //this var it's used to know if the pipe is bent or not
P.initialize_directions_he = pipe_dir
//var/turf/T = P.loc
//P.level = T.intact ? 2 : 1
@@ -382,7 +383,7 @@ Buildable meters
if(PIPE_MANIFOLD) //manifold
var/obj/machinery/atmospherics/pipe/manifold/M = new( src.loc )
- M.color = color
+ M.pipe_color = color
M.dir = dir
M.initialize_directions = pipe_dir
//M.New()
@@ -390,7 +391,7 @@ Buildable meters
M.level = T.intact ? 2 : 1
M.initialize()
if (!M)
- usr << "There's nothing to connect this manifold to! (with how the pipe code works, at least one end needs to be connected to something, otherwise the game deletes the segment)"
+ usr << pipefailtext
return 1
M.build_network()
if (M.node1)
@@ -405,7 +406,7 @@ Buildable meters
if(PIPE_MANIFOLD4W) //4-way manifold
var/obj/machinery/atmospherics/pipe/manifold4w/M = new( src.loc )
- M.color = color
+ M.pipe_color = color
M.dir = dir
M.initialize_directions = pipe_dir
//M.New()
@@ -413,7 +414,7 @@ Buildable meters
M.level = T.intact ? 2 : 1
M.initialize()
if (!M)
- usr << "There's nothing to connect this manifold to! (with how the pipe code works, at least one end needs to be connected to something, otherwise the game deletes the segment)"
+ usr << pipefailtext
return 1
M.build_network()
if (M.node1)
@@ -438,7 +439,7 @@ Buildable meters
//P.level = T.intact ? 2 : 1
P.initialize()
if (!P)
- usr << "There's nothing to connect this pipe to!" //(with how the pipe code works, at least one end needs to be connected to something, otherwise the game deletes the segment)"
+ usr << pipefailtext //"There's nothing to connect this pipe to! (with how the pipe code works, at least one end needs to be connected to something, otherwise the game deletes the segment)"
return 1
P.build_network()
if (P.node1)
@@ -679,7 +680,7 @@ Buildable meters
P.node2.build_network()
if(PIPE_VOLUME_PUMP) //volume pump
- var/obj/machinery/atmospherics/binary/volume_pump/P = new(src.loc)
+ var/obj/machinery/atmospherics/binary/pump/high_power/P = new(src.loc)
P.dir = dir
P.initialize_directions = pipe_dir
if (pipename)
@@ -709,7 +710,7 @@ Buildable meters
C.node.initialize()
C.node.build_network()
///// Z-Level stuff
- if(PIPE_UP) //volume pump
+ if(PIPE_UP)
var/obj/machinery/atmospherics/pipe/zpipe/up/P = new(src.loc)
P.dir = dir
P.initialize_directions = pipe_dir
@@ -725,7 +726,7 @@ Buildable meters
if (P.node2)
P.node2.initialize()
P.node2.build_network()
- if(PIPE_DOWN) //volume pump
+ if(PIPE_DOWN)
var/obj/machinery/atmospherics/pipe/zpipe/down/P = new(src.loc)
P.dir = dir
P.initialize_directions = pipe_dir
diff --git a/code/game/machinery/pipe/pipe_dispenser.dm b/code/game/machinery/pipe/pipe_dispenser.dm
index c8b6962458a..fd61f304710 100644
--- a/code/game/machinery/pipe/pipe_dispenser.dm
+++ b/code/game/machinery/pipe/pipe_dispenser.dm
@@ -29,8 +29,8 @@
Connector Unary Vent Gas Pump
-Passive Gate
-Volume Pump
+Pressure Regulator
+High Power Gas Pump Scrubber Meter Gas Filter
@@ -128,7 +128,7 @@
/*
//Allow you to push disposal pipes into it (for those with density 1)
-/obj/machinery/pipedispenser/disposal/HasEntered(var/obj/structure/disposalconstruct/pipe as obj)
+/obj/machinery/pipedispenser/disposal/Crossed(var/obj/structure/disposalconstruct/pipe as obj)
if(istype(pipe) && !pipe.anchored)
del(pipe)
diff --git a/code/game/machinery/podmen.dm b/code/game/machinery/podmen.dm
deleted file mode 100644
index 203e803cf75..00000000000
--- a/code/game/machinery/podmen.dm
+++ /dev/null
@@ -1,158 +0,0 @@
-/* Moved all the plant people code here for ease of reference and coherency.
-Injecting a pod person with a blood sample will grow a pod person with the memories and persona of that mob.
-Growing it to term with nothing injected will grab a ghost from the observers. */
-
-/obj/item/seeds/replicapod
- name = "pack of dionaea-replicant seeds"
- desc = "These seeds grow into 'replica pods' or 'dionaea', a form of strange sapient plantlife."
- icon_state = "seed-replicapod"
- mypath = "/obj/item/seeds/replicapod"
- species = "replicapod"
- plantname = "Dionaea"
- productname = "/mob/living/carbon/human" //verrry special -- Urist
- lifespan = 50 //no idea what those do
- endurance = 8
- maturation = 5
- production = 10
- yield = 1 //seeds if there isn't a dna inside
- oneharvest = 1
- potency = 30
- plant_type = 0
- growthstages = 6
- var/ckey = null
- var/realName = null
- var/mob/living/carbon/human/source //Donor of blood, if any.
- gender = MALE
- var/obj/machinery/hydroponics/parent = null
- var/found_player = 0
-
-/obj/item/seeds/replicapod/attackby(obj/item/weapon/W as obj, mob/user as mob)
-
- if(istype(W,/obj/item/weapon/reagent_containers))
-
- user << "You inject the contents of the syringe into the seeds."
-
- var/datum/reagent/blood/B
-
- //Find a blood sample to inject.
- for(var/datum/reagent/R in W:reagents.reagent_list)
- if(istype(R,/datum/reagent/blood))
- B = R
- break
- if(B)
- source = B.data["donor"]
- user << "The strange, sluglike seeds quiver gently and swell with blood."
- if(!source.client && source.mind)
- for(var/mob/dead/observer/O in player_list)
- if(O.mind == source.mind && config.revival_pod_plants)
- O << "Your blood has been placed into a replica pod seed. Return to your body if you want to be returned to life as a pod person! (Verbs -> Ghost -> Re-enter corpse)"
- break
- else
- user << "Nothing happens."
- return
-
- if (!istype(source))
- return
-
- if(source.ckey)
- realName = source.real_name
- ckey = source.ckey
-
- W:reagents.clear_reagents()
- return
-
- return ..()
-
-/obj/item/seeds/replicapod/harvest(mob/user = usr)
-
- parent = loc
- var/found_player = 0
-
- user.visible_message("\blue [user] carefully begins to open the pod...","\blue You carefully begin to open the pod...")
-
- //If a sample is injected (and revival is allowed) the plant will be controlled by the original donor.
- if(source && source.stat == 2 && source.client && source.ckey && config.revival_pod_plants)
- transfer_personality(source.client)
- else // If no sample was injected or revival is not allowed, we grab an interested observer.
- request_player()
-
- spawn(200) //If we don't have a ghost or the ghost is now unplayed, we just give the harvester some seeds.
- if(!found_player)
- parent.visible_message("The pod has formed badly, and all you can do is salvage some of the seeds.")
- var/seed_count = 1
-
- if(prob(yield * parent.yieldmod * 20))
- seed_count++
-
- for(var/i=0,iYou awaken slowly, feeling your sap stir into sluggish motion as the warm air caresses your bark."
- if(source && ckey && podman.ckey == ckey)
- podman << "Memories of a life as [source] drift oddly through a mind unsuited for them, like a skin of oil over a fathomless lake."
- podman << "You are now one of the Dionaea, a race of drifting interstellar plantlike creatures that sometimes share their seeds with human traders."
- podman << "Too much darkness will send you into shock and starve you, but light will help you heal."
- if(!realName)
- var/newname = input(podman,"Enter a name, or leave blank for the default name.", "Name change","") as text
- if (newname != "")
- podman.real_name = newname
-
- parent.visible_message("\blue The pod disgorges a fully-formed plant creature!")
- parent.update_tray()
diff --git a/code/game/machinery/portable_turret.dm b/code/game/machinery/portable_turret.dm
index 8107e23cc50..b7a9a781d8e 100644
--- a/code/game/machinery/portable_turret.dm
+++ b/code/game/machinery/portable_turret.dm
@@ -264,10 +264,12 @@ Status: [] "},
if(!anchored)
icon_state = "turretCover"
return
+
+ ..()
if(stat & BROKEN)
icon_state = "[lasercolor]destroyed_target_prism"
else
- if( powered() )
+ if( !(stat & NOPOWER) )
if (on)
if (installation == /obj/item/weapon/gun/energy/laser || installation == /obj/item/weapon/gun/energy/pulse_rifle)
// laser guns and pulse rifles have an orange icon
@@ -277,11 +279,9 @@ Status: [] "},
icon_state = "[lasercolor]target_prism"
else
icon_state = "[lasercolor]grey_target_prism"
- stat &= ~NOPOWER
else
spawn(rand(0, 15))
src.icon_state = "[lasercolor]grey_target_prism"
- stat |= NOPOWER
diff --git a/code/game/machinery/recharger.dm b/code/game/machinery/recharger.dm
index eec36aaf576..3166774f96f 100644
--- a/code/game/machinery/recharger.dm
+++ b/code/game/machinery/recharger.dm
@@ -7,28 +7,30 @@ obj/machinery/recharger
anchored = 1
use_power = 1
idle_power_usage = 4
- active_power_usage = 250
+ active_power_usage = 15000 //15 kW
var/obj/item/charging = null
+ var/list/allowed_devices = list(/obj/item/weapon/gun/energy, /obj/item/weapon/melee/baton, /obj/item/device/laptop, /obj/item/weapon/cell)
var/icon_state_charged = "recharger2"
var/icon_state_charging = "recharger1"
- var/icon_state_idle = "recharger0"
+ var/icon_state_idle = "recharger0" //also when unpowered
obj/machinery/recharger/attackby(obj/item/weapon/G as obj, mob/user as mob)
if(istype(user,/mob/living/silicon))
return
- if(istype(G, /obj/item/weapon/gun/energy) || istype(G, /obj/item/weapon/melee/baton) || istype(G,/obj/item/device/laptop))
+
+ var/allowed = 0
+ for (var/allowed_type in allowed_devices)
+ if (istype(G, allowed_type)) allowed = 1
+
+ if(allowed)
if(charging)
+ user << "\red \A [charging] is already charging here."
return
-
// Checks to make sure he's not in space doing it, and that the area got proper power.
var/area/a = get_area(src)
- if(!isarea(a))
+ if(!isarea(a) || (a.power_equip == 0 && !a.unlimited_power))
user << "\red The [name] blinks red as you try to insert the item!"
return
- if(a.power_equip == 0)
- user << "\red The [name] blinks red as you try to insert the item!"
- return
-
if (istype(G, /obj/item/weapon/gun/energy/gun/nuclear) || istype(G, /obj/item/weapon/gun/energy/crossbow))
user << "Your gun's recharge port was removed to make room for a miniaturized reactor."
return
@@ -42,11 +44,10 @@ obj/machinery/recharger/attackby(obj/item/weapon/G as obj, mob/user as mob)
user.drop_item()
G.loc = src
charging = G
- use_power = 2
update_icon()
else if(istype(G, /obj/item/weapon/wrench))
if(charging)
- user << "\red Remove the weapon first!"
+ user << "\red Remove [charging] first!"
return
anchored = !anchored
user << "You [anchored ? "attached" : "detached"] the recharger."
@@ -57,9 +58,8 @@ obj/machinery/recharger/attack_hand(mob/user as mob)
if(charging)
charging.update_icon()
- charging.loc = loc
+ user.put_in_hands(charging)
charging = null
- use_power = 1
update_icon()
obj/machinery/recharger/attack_paw(mob/user as mob)
@@ -67,35 +67,60 @@ obj/machinery/recharger/attack_paw(mob/user as mob)
obj/machinery/recharger/process()
if(stat & (NOPOWER|BROKEN) || !anchored)
+ update_use_power(0)
+ icon_state = icon_state_idle
return
- if(charging)
+ if(!charging)
+ update_use_power(1)
+ icon_state = icon_state_idle
+ else
if(istype(charging, /obj/item/weapon/gun/energy))
var/obj/item/weapon/gun/energy/E = charging
- if(E.power_supply.charge < E.power_supply.maxcharge)
- E.power_supply.give(100)
+ if(!E.power_supply.fully_charged())
icon_state = icon_state_charging
- use_power(250/CELLRATE)
+ E.power_supply.give(active_power_usage*CELLRATE)
+ update_use_power(2)
else
icon_state = icon_state_charged
+ update_use_power(1)
return
+
if(istype(charging, /obj/item/weapon/melee/baton))
var/obj/item/weapon/melee/baton/B = charging
if(B.bcell)
- if(B.bcell.give(1500)) //Because otherwise it takes two minutes to fully charge due to 15k cells. - Neerti
+ if(!B.bcell.fully_charged())
icon_state = icon_state_charging
- use_power(200/CELLRATE)
+ B.bcell.give(active_power_usage*CELLRATE)
+ update_use_power(2)
else
icon_state = icon_state_charged
+ update_use_power(1)
+ else
+ icon_state = icon_state_idle
+ update_use_power(1)
return
+
if(istype(charging, /obj/item/device/laptop))
var/obj/item/device/laptop/L = charging
- if(L.stored_computer.battery.charge < L.stored_computer.battery.maxcharge)
- L.stored_computer.battery.give(100)
+ if(!L.stored_computer.battery.fully_charged())
icon_state = icon_state_charging
- use_power(250/CELLRATE)
+ L.stored_computer.battery.give(active_power_usage*CELLRATE)
+ update_use_power(2)
else
icon_state = icon_state_charged
+ update_use_power(1)
+ return
+
+ if(istype(charging, /obj/item/weapon/cell))
+ var/obj/item/weapon/cell/C = charging
+ if(!C.fully_charged())
+ icon_state = icon_state_charging
+ C.give(active_power_usage*CELLRATE)
+ update_use_power(2)
+ else
+ icon_state = icon_state_charged
+ update_use_power(1)
return
obj/machinery/recharger/emp_act(severity)
@@ -120,10 +145,13 @@ obj/machinery/recharger/update_icon() //we have an update_icon() in addition to
else
icon_state = icon_state_idle
-// Atlantis: No need for that copy-pasta code, just use var to store icon_states instead.
+
obj/machinery/recharger/wallcharger
name = "wall recharger"
+ icon = 'icons/obj/stationobjs.dmi'
icon_state = "wrecharger0"
+ active_power_usage = 25000 //25 kW , It's more specialized than the standalone recharger (guns and batons only) so make it more powerful
+ allowed_devices = list(/obj/item/weapon/gun/energy, /obj/item/weapon/melee/baton)
icon_state_charged = "wrecharger2"
icon_state_charging = "wrecharger1"
icon_state_idle = "wrecharger0"
diff --git a/code/game/machinery/rechargestation.dm b/code/game/machinery/rechargestation.dm
index 8d5e888a976..ddee1405a4f 100644
--- a/code/game/machinery/rechargestation.dm
+++ b/code/game/machinery/rechargestation.dm
@@ -5,8 +5,8 @@
density = 1
anchored = 1.0
use_power = 1
- idle_power_usage = 5
- active_power_usage = 1000
+ idle_power_usage = 5 //internal circuitry
+ active_power_usage = 75000 //75 kW charging station
var/mob/occupant = null
@@ -61,13 +61,10 @@
R.module.respawn_consumable(R)
if(!R.cell)
return
- else if(R.cell.charge >= R.cell.maxcharge)
- R.cell.charge = R.cell.maxcharge
- return
+ if(!R.cell.fully_charged())
+ R.cell.give(active_power_usage*CELLRATE)
else
- R.cell.charge = min(R.cell.charge + 200, R.cell.maxcharge)
- return
-
+ update_use_power(1)
go_out()
if(!( src.occupant ))
return
@@ -79,7 +76,7 @@
src.occupant.loc = src.loc
src.occupant = null
build_icon()
- src.use_power = 1
+ update_use_power(1)
return
@@ -119,5 +116,5 @@
O.loc = src.loc*/
src.add_fingerprint(usr)
build_icon()
- src.use_power = 2
+ update_use_power(2)
return
\ No newline at end of file
diff --git a/code/game/machinery/records_scanner.dm b/code/game/machinery/records_scanner.dm
index 370e2f3843e..af74c7c9412 100644
--- a/code/game/machinery/records_scanner.dm
+++ b/code/game/machinery/records_scanner.dm
@@ -28,13 +28,12 @@ obj/machinery/scanner/New()
use_power(50)
/obj/machinery/scanner/power_change()
- if(!powered())
+ ..()
+ if(stat & NOPOWER)
spawn(rand(0, 15))
icon_state = "scanner_off"
- stat |= NOPOWER
else
icon_state = "scanner_idle"
- stat &= ~NOPOWER
obj/machinery/scanner/attack_hand(mob/living/carbon/human/user)
if(stat & NOPOWER)
diff --git a/code/game/machinery/robot_fabricator.dm b/code/game/machinery/robot_fabricator.dm
index 73aacd3a9b0..bf02e31fdda 100644
--- a/code/game/machinery/robot_fabricator.dm
+++ b/code/game/machinery/robot_fabricator.dm
@@ -8,8 +8,8 @@
var/operating = 0
var/obj/item/robot_parts/being_built = null
use_power = 1
- idle_power_usage = 20
- active_power_usage = 5000
+ idle_power_usage = 40
+ active_power_usage = 10000
/obj/machinery/robotic_fabricator/attackby(var/obj/item/O as obj, var/mob/user as mob)
if (istype(O, /obj/item/stack/sheet/metal))
@@ -34,12 +34,6 @@
else
user << "The robot part maker is full. Please remove metal from the robot part maker in order to insert more."
-/obj/machinery/robotic_fabricator/power_change()
- if (powered())
- stat &= ~NOPOWER
- else
- stat |= NOPOWER
-
/obj/machinery/robotic_fabricator/attack_paw(user as mob)
return src.attack_hand(user)
@@ -126,7 +120,7 @@ Please wait until completion...
if (!isnull(building))
if (src.metal_amount >= build_cost)
src.operating = 1
- src.use_power = 2
+ src.update_use_power(2)
src.metal_amount = max(0, src.metal_amount - build_cost)
@@ -139,7 +133,7 @@ Please wait until completion...
if (!isnull(src.being_built))
src.being_built.loc = get_turf(src)
src.being_built = null
- src.use_power = 1
+ src.update_use_power(1)
src.operating = 0
src.overlays -= "fab-active"
return
diff --git a/code/game/machinery/seed_extractor.dm b/code/game/machinery/seed_extractor.dm
index 615e52f3980..dbb5a9d2712 100644
--- a/code/game/machinery/seed_extractor.dm
+++ b/code/game/machinery/seed_extractor.dm
@@ -7,44 +7,33 @@
anchored = 1
obj/machinery/seed_extractor/attackby(var/obj/item/O as obj, var/mob/user as mob)
- if(istype(O, /obj/item/weapon/reagent_containers/food/snacks/grown/))
- var/obj/item/weapon/reagent_containers/food/snacks/grown/F = O
- user.drop_item()
- user << "You extract some seeds from the [F.name]."
- var/seed = text2path(F.seed)
- var/t_amount = 0
- var/t_max = rand(1,4)
- while(t_amount < t_max)
- var/obj/item/seeds/t_prod = new seed(loc)
- t_prod.species = F.species
- t_prod.lifespan = F.lifespan
- t_prod.endurance = F.endurance
- t_prod.maturation = F.maturation
- t_prod.production = F.production
- t_prod.yield = F.yield
- t_prod.potency = F.potency
- t_amount++
- del(O)
-
- else if(istype(O, /obj/item/weapon/grown/))
- var/obj/item/weapon/grown/F = O
- user.drop_item()
- user << "You extract some seeds from the [F.name]."
- var/seed = text2path(F.seed)
- var/t_amount = 0
- var/t_max = rand(1,4)
- while(t_amount < t_max)
- var/obj/item/seeds/t_prod = new seed(loc)
- t_prod.species = F.species
- t_prod.lifespan = F.lifespan
- t_prod.endurance = F.endurance
- t_prod.maturation = F.maturation
- t_prod.production = F.production
- t_prod.yield = F.yield
- t_prod.potency = F.potency
- t_amount++
+
+ // Fruits and vegetables.
+ if(istype(O, /obj/item/weapon/reagent_containers/food/snacks/grown) || istype(O, /obj/item/weapon/grown))
+
+ user.drop_item(O)
+
+ var/datum/seed/new_seed_type
+ if(istype(O, /obj/item/weapon/grown))
+ var/obj/item/weapon/grown/F = O
+ new_seed_type = seed_types[F.plantname]
+ else
+ var/obj/item/weapon/reagent_containers/food/snacks/grown/F = O
+ new_seed_type = seed_types[F.plantname]
+
+ if(new_seed_type)
+ user << "You extract some seeds from [O]."
+ var/produce = rand(1,4)
+ for(var/i = 0;i<=produce;i++)
+ var/obj/item/seeds/seeds = new(get_turf(src))
+ seeds.seed_type = new_seed_type.name
+ seeds.update_seed()
+ else
+ user << "[O] doesn't seem to have any usable seeds inside it."
+
del(O)
+ //Grass.
else if(istype(O, /obj/item/stack/tile/grass))
var/obj/item/stack/tile/grass/S = O
user << "You extract some seeds from the [S.name]."
diff --git a/code/game/machinery/shieldgen.dm b/code/game/machinery/shieldgen.dm
deleted file mode 100644
index 439d31d57d3..00000000000
--- a/code/game/machinery/shieldgen.dm
+++ /dev/null
@@ -1,630 +0,0 @@
-/obj/machinery/shield
- name = "Emergency energy shield"
- desc = "An energy shield used to contain hull breaches."
- icon = 'icons/effects/effects.dmi'
- icon_state = "shield-old"
- density = 1
- opacity = 0
- anchored = 1
- unacidable = 1
- var/const/max_health = 200
- var/health = max_health //The shield can only take so much beating (prevents perma-prisons)
-
-/obj/machinery/shield/New()
- src.dir = pick(1,2,3,4)
- ..()
- update_nearby_tiles(need_rebuild=1)
-
-/obj/machinery/shield/Del()
- opacity = 0
- density = 0
- update_nearby_tiles()
- ..()
-
-/obj/machinery/shield/CanPass(atom/movable/mover, turf/target, height, air_group)
- if(!height || air_group) return 0
- else return ..()
-
-//Looks like copy/pasted code... I doubt 'need_rebuild' is even used here - Nodrak
-/obj/machinery/shield/proc/update_nearby_tiles(need_rebuild)
- if(!air_master)
- return 0
-
- air_master.mark_for_update(get_turf(src))
-
- return 1
-
-
-/obj/machinery/shield/attackby(obj/item/weapon/W as obj, mob/user as mob)
- if(!istype(W)) return
-
- //Calculate damage
- var/aforce = W.force
- if(W.damtype == BRUTE || W.damtype == BURN)
- src.health -= aforce
-
- //Play a fitting sound
- playsound(src.loc, 'sound/effects/EMPulse.ogg', 75, 1)
-
-
- if (src.health <= 0)
- visible_message("\blue The [src] dissipates!")
- del(src)
- return
-
- opacity = 1
- spawn(20) if(src) opacity = 0
-
- ..()
-
-/obj/machinery/shield/meteorhit()
- src.health -= max_health*0.75 //3/4 health as damage
-
- if(src.health <= 0)
- visible_message("\blue The [src] dissipates!")
- del(src)
- return
-
- opacity = 1
- spawn(20) if(src) opacity = 0
- return
-
-/obj/machinery/shield/bullet_act(var/obj/item/projectile/Proj)
- health -= Proj.damage
- ..()
- if(health <=0)
- visible_message("\blue The [src] dissipates!")
- del(src)
- return
- opacity = 1
- spawn(20) if(src) opacity = 0
-
-/obj/machinery/shield/ex_act(severity)
- switch(severity)
- if(1.0)
- if (prob(75))
- del(src)
- if(2.0)
- if (prob(50))
- del(src)
- if(3.0)
- if (prob(25))
- del(src)
- return
-
-/obj/machinery/shield/emp_act(severity)
- switch(severity)
- if(1)
- del(src)
- if(2)
- if(prob(50))
- del(src)
-
-/obj/machinery/shield/blob_act()
- del(src)
-
-
-/obj/machinery/shield/hitby(AM as mob|obj)
- //Let everyone know we've been hit!
- visible_message("\red [src] was hit by [AM].")
-
- //Super realistic, resource-intensive, real-time damage calculations.
- var/tforce = 0
- if(ismob(AM))
- tforce = 40
- else
- tforce = AM:throwforce
-
- src.health -= tforce
-
- //This seemed to be the best sound for hitting a force field.
- playsound(src.loc, 'sound/effects/EMPulse.ogg', 100, 1)
-
- //Handle the destruction of the shield
- if (src.health <= 0)
- visible_message("\blue The [src] dissipates!")
- del(src)
- return
-
- //The shield becomes dense to absorb the blow.. purely asthetic.
- opacity = 1
- spawn(20) if(src) opacity = 0
-
- ..()
- return
-
-
-
-/obj/machinery/shieldgen
- name = "Emergency shield projector"
- desc = "Used to seal minor hull breaches."
- icon = 'icons/obj/objects.dmi'
- icon_state = "shieldoff"
- density = 1
- opacity = 0
- anchored = 0
- pressure_resistance = 2*ONE_ATMOSPHERE
- req_access = list(access_engine)
- var/const/max_health = 100
- var/health = max_health
- var/active = 0
- var/malfunction = 0 //Malfunction causes parts of the shield to slowly dissapate
- var/list/deployed_shields = list()
- var/is_open = 0 //Whether or not the wires are exposed
- var/locked = 0
-
-/obj/machinery/shieldgen/Del()
- for(var/obj/machinery/shield/shield_tile in deployed_shields)
- del(shield_tile)
- ..()
-
-
-/obj/machinery/shieldgen/proc/shields_up()
- if(active) return 0 //If it's already turned on, how did this get called?
-
- src.active = 1
- update_icon()
-
- for(var/turf/target_tile in range(2, src))
- if (istype(target_tile,/turf/space) && !(locate(/obj/machinery/shield) in target_tile))
- if (malfunction && prob(33) || !malfunction)
- deployed_shields += new /obj/machinery/shield(target_tile)
-
-/obj/machinery/shieldgen/proc/shields_down()
- if(!active) return 0 //If it's already off, how did this get called?
-
- src.active = 0
- update_icon()
-
- for(var/obj/machinery/shield/shield_tile in deployed_shields)
- del(shield_tile)
-
-/obj/machinery/shieldgen/process()
- if(malfunction && active)
- if(deployed_shields.len && prob(5))
- del(pick(deployed_shields))
-
- return
-
-/obj/machinery/shieldgen/proc/checkhp()
- if(health <= 30)
- src.malfunction = 1
- if(health <= 0)
- del(src)
- update_icon()
- return
-
-/obj/machinery/shieldgen/meteorhit(obj/O as obj)
- src.health -= max_health*0.25 //A quarter of the machine's health
- if (prob(5))
- src.malfunction = 1
- src.checkhp()
- return
-
-/obj/machinery/shieldgen/ex_act(severity)
- switch(severity)
- if(1.0)
- src.health -= 75
- src.checkhp()
- if(2.0)
- src.health -= 30
- if (prob(15))
- src.malfunction = 1
- src.checkhp()
- if(3.0)
- src.health -= 10
- src.checkhp()
- return
-
-/obj/machinery/shieldgen/emp_act(severity)
- switch(severity)
- if(1)
- src.health /= 2 //cut health in half
- malfunction = 1
- locked = pick(0,1)
- if(2)
- if(prob(50))
- src.health *= 0.3 //chop off a third of the health
- malfunction = 1
- checkhp()
-
-/obj/machinery/shieldgen/attack_hand(mob/user as mob)
- if(locked)
- user << "The machine is locked, you are unable to use it."
- return
- if(is_open)
- user << "The panel must be closed before operating this machine."
- return
-
- if (src.active)
- user.visible_message("\blue \icon[src] [user] deactivated the shield generator.", \
- "\blue \icon[src] You deactivate the shield generator.", \
- "You hear heavy droning fade out.")
- src.shields_down()
- else
- if(anchored)
- user.visible_message("\blue \icon[src] [user] activated the shield generator.", \
- "\blue \icon[src] You activate the shield generator.", \
- "You hear heavy droning.")
- src.shields_up()
- else
- user << "The device must first be secured to the floor."
- return
-
-/obj/machinery/shieldgen/attackby(obj/item/weapon/W as obj, mob/user as mob)
- if(istype(W, /obj/item/weapon/card/emag))
- malfunction = 1
- update_icon()
-
- else if(istype(W, /obj/item/weapon/screwdriver))
- playsound(src.loc, 'sound/items/Screwdriver.ogg', 100, 1)
- if(is_open)
- user << "\blue You close the panel."
- is_open = 0
- else
- user << "\blue You open the panel and expose the wiring."
- is_open = 1
-
- else if(istype(W, /obj/item/weapon/cable_coil) && malfunction && is_open)
- var/obj/item/weapon/cable_coil/coil = W
- user << "\blue You begin to replace the wires."
- //if(do_after(user, min(60, round( ((maxhealth/health)*10)+(malfunction*10) ))) //Take longer to repair heavier damage
- if(do_after(user, 30))
- if(!src || !coil) return
- coil.use(1)
- health = max_health
- malfunction = 0
- user << "\blue You repair the [src]!"
- update_icon()
-
- else if(istype(W, /obj/item/weapon/wrench))
- if(locked)
- user << "The bolts are covered, unlocking this would retract the covers."
- return
- if(anchored)
- playsound(src.loc, 'sound/items/Ratchet.ogg', 100, 1)
- user << "\blue You unsecure the [src] from the floor!"
- if(active)
- user << "\blue The [src] shuts off!"
- src.shields_down()
- anchored = 0
- else
- if(istype(get_turf(src), /turf/space)) return //No wrenching these in space!
- playsound(src.loc, 'sound/items/Ratchet.ogg', 100, 1)
- user << "\blue You secure the [src] to the floor!"
- anchored = 1
-
-
- else if(istype(W, /obj/item/weapon/card/id) || istype(W, /obj/item/device/pda))
- if(src.allowed(user))
- src.locked = !src.locked
- user << "The controls are now [src.locked ? "locked." : "unlocked."]"
- else
- user << "\red Access denied."
-
- else
- ..()
-
-
-/obj/machinery/shieldgen/update_icon()
- if(active)
- src.icon_state = malfunction ? "shieldonbr":"shieldon"
- else
- src.icon_state = malfunction ? "shieldoffbr":"shieldoff"
- return
-
-////FIELD GEN START //shameless copypasta from fieldgen, powersink, and grille
-#define maxstoredpower 500
-/obj/machinery/shieldwallgen
- name = "Shield Generator"
- desc = "A shield generator."
- icon = 'icons/obj/stationobjs.dmi'
- icon_state = "Shield_Gen"
- anchored = 0
- density = 1
- req_access = list(access_teleporter)
- var/active = 0
- var/power = 0
- var/state = 0
- var/steps = 0
- var/last_check = 0
- var/check_delay = 10
- var/recalc = 0
- var/locked = 1
- var/destroyed = 0
- var/directwired = 1
-// var/maxshieldload = 200
- var/obj/structure/cable/attached // the attached cable
- var/storedpower = 0
- flags = FPRINT | CONDUCT
- use_power = 0
-
-/obj/machinery/shieldwallgen/proc/power()
- if(!anchored)
- power = 0
- return 0
- var/turf/T = src.loc
-
- var/obj/structure/cable/C = T.get_cable_node()
- var/datum/powernet/PN
- if(C) PN = C.powernet // find the powernet of the connected cable
-
- if(!PN)
- power = 0
- return 0
-
- var/surplus = max(PN.avail-PN.load, 0)
- var/shieldload = min(rand(50,200), surplus)
- if(shieldload==0 && !storedpower) // no cable or no power, and no power stored
- power = 0
- return 0
- else
- power = 1 // IVE GOT THE POWER!
- if(PN) //runtime errors fixer. They were caused by PN.newload trying to access missing network in case of working on stored power.
- storedpower += shieldload
- PN.newload += shieldload //uses powernet power.
-// message_admins("[PN.load]", 1)
-// use_power(250) //uses APC power
-
-/obj/machinery/shieldwallgen/attack_hand(mob/user as mob)
- if(state != 1)
- user << "\red The shield generator needs to be firmly secured to the floor first."
- return 1
- if(src.locked && !istype(user, /mob/living/silicon))
- user << "\red The controls are locked!"
- return 1
- if(power != 1)
- user << "\red The shield generator needs to be powered by wire underneath."
- return 1
-
- if(src.active >= 1)
- src.active = 0
- icon_state = "Shield_Gen"
-
- user.visible_message("[user] turned the shield generator off.", \
- "You turn off the shield generator.", \
- "You hear heavy droning fade out.")
- for(var/dir in list(1,2,4,8)) src.cleanup(dir)
- else
- src.active = 1
- icon_state = "Shield_Gen +a"
- user.visible_message("[user] turned the shield generator on.", \
- "You turn on the shield generator.", \
- "You hear heavy droning.")
- src.add_fingerprint(user)
-
-/obj/machinery/shieldwallgen/process()
- spawn(100)
- power()
- if(power)
- storedpower -= 50 //this way it can survive longer and survive at all
- if(storedpower >= maxstoredpower)
- storedpower = maxstoredpower
- if(storedpower <= 0)
- storedpower = 0
-// if(shieldload >= maxshieldload) //there was a loop caused by specifics of process(), so this was needed.
-// shieldload = maxshieldload
-
- if(src.active == 1)
- if(!src.state == 1)
- src.active = 0
- return
- spawn(1)
- setup_field(1)
- spawn(2)
- setup_field(2)
- spawn(3)
- setup_field(4)
- spawn(4)
- setup_field(8)
- src.active = 2
- if(src.active >= 1)
- if(src.power == 0)
- src.visible_message("\red The [src.name] shuts down due to lack of power!", \
- "You hear heavy droning fade out")
- icon_state = "Shield_Gen"
- src.active = 0
- for(var/dir in list(1,2,4,8)) src.cleanup(dir)
-
-/obj/machinery/shieldwallgen/proc/setup_field(var/NSEW = 0)
- var/turf/T = src.loc
- var/turf/T2 = src.loc
- var/obj/machinery/shieldwallgen/G
- var/steps = 0
- var/oNSEW = 0
-
- if(!NSEW)//Make sure its ran right
- return
-
- if(NSEW == 1)
- oNSEW = 2
- else if(NSEW == 2)
- oNSEW = 1
- else if(NSEW == 4)
- oNSEW = 8
- else if(NSEW == 8)
- oNSEW = 4
-
- for(var/dist = 0, dist <= 9, dist += 1) // checks out to 8 tiles away for another generator
- T = get_step(T2, NSEW)
- T2 = T
- steps += 1
- if(locate(/obj/machinery/shieldwallgen) in T)
- G = (locate(/obj/machinery/shieldwallgen) in T)
- steps -= 1
- if(!G.active)
- return
- G.cleanup(oNSEW)
- break
-
- if(isnull(G))
- return
-
- T2 = src.loc
-
- for(var/dist = 0, dist < steps, dist += 1) // creates each field tile
- var/field_dir = get_dir(T2,get_step(T2, NSEW))
- T = get_step(T2, NSEW)
- T2 = T
- var/obj/machinery/shieldwall/CF = new/obj/machinery/shieldwall/(src, G) //(ref to this gen, ref to connected gen)
- CF.loc = T
- CF.dir = field_dir
-
-
-/obj/machinery/shieldwallgen/attackby(obj/item/W, mob/user)
- if(istype(W, /obj/item/weapon/wrench))
- if(active)
- user << "Turn off the field generator first."
- return
-
- else if(state == 0)
- state = 1
- playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1)
- user << "You secure the external reinforcing bolts to the floor."
- src.anchored = 1
- return
-
- else if(state == 1)
- state = 0
- playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1)
- user << "You undo the external reinforcing bolts."
- src.anchored = 0
- return
-
- if(istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda))
- if (src.allowed(user))
- src.locked = !src.locked
- user << "Controls are now [src.locked ? "locked." : "unlocked."]"
- else
- user << "\red Access denied."
-
- else
- src.add_fingerprint(user)
- visible_message("\red The [src.name] has been hit with \the [W.name] by [user.name]!")
-
-/obj/machinery/shieldwallgen/proc/cleanup(var/NSEW)
- var/obj/machinery/shieldwall/F
- var/obj/machinery/shieldwallgen/G
- var/turf/T = src.loc
- var/turf/T2 = src.loc
-
- for(var/dist = 0, dist <= 9, dist += 1) // checks out to 8 tiles away for fields
- T = get_step(T2, NSEW)
- T2 = T
- if(locate(/obj/machinery/shieldwall) in T)
- F = (locate(/obj/machinery/shieldwall) in T)
- del(F)
-
- if(locate(/obj/machinery/shieldwallgen) in T)
- G = (locate(/obj/machinery/shieldwallgen) in T)
- if(!G.active)
- break
-
-/obj/machinery/shieldwallgen/Del()
- src.cleanup(1)
- src.cleanup(2)
- src.cleanup(4)
- src.cleanup(8)
- ..()
-
-/obj/machinery/shieldwallgen/bullet_act(var/obj/item/projectile/Proj)
- storedpower -= Proj.damage
- ..()
- return
-
-
-//////////////Containment Field START
-/obj/machinery/shieldwall
- name = "Shield"
- desc = "An energy shield."
- icon = 'icons/effects/effects.dmi'
- icon_state = "shieldwall"
- anchored = 1
- density = 1
- unacidable = 1
- luminosity = 3
- var/needs_power = 0
- var/active = 1
-// var/power = 10
- var/delay = 5
- var/last_active
- var/mob/U
- var/obj/machinery/shieldwallgen/gen_primary
- var/obj/machinery/shieldwallgen/gen_secondary
-
-/obj/machinery/shieldwall/New(var/obj/machinery/shieldwallgen/A, var/obj/machinery/shieldwallgen/B)
- ..()
- src.gen_primary = A
- src.gen_secondary = B
- if(A && B)
- needs_power = 1
-
-/obj/machinery/shieldwall/attack_hand(mob/user as mob)
- return
-
-
-/obj/machinery/shieldwall/process()
- if(needs_power)
- if(isnull(gen_primary)||isnull(gen_secondary))
- del(src)
- return
-
- if(!(gen_primary.active)||!(gen_secondary.active))
- del(src)
- return
-//
- if(prob(50))
- gen_primary.storedpower -= 10
- else
- gen_secondary.storedpower -=10
-
-
-/obj/machinery/shieldwall/bullet_act(var/obj/item/projectile/Proj)
- if(needs_power)
- var/obj/machinery/shieldwallgen/G
- if(prob(50))
- G = gen_primary
- else
- G = gen_secondary
- G.storedpower -= Proj.damage
- ..()
- return
-
-
-/obj/machinery/shieldwall/ex_act(severity)
- if(needs_power)
- var/obj/machinery/shieldwallgen/G
- switch(severity)
- if(1.0) //big boom
- if(prob(50))
- G = gen_primary
- else
- G = gen_secondary
- G.storedpower -= 200
-
- if(2.0) //medium boom
- if(prob(50))
- G = gen_primary
- else
- G = gen_secondary
- G.storedpower -= 50
-
- if(3.0) //lil boom
- if(prob(50))
- G = gen_primary
- else
- G = gen_secondary
- G.storedpower -= 20
- return
-
-
-/obj/machinery/shieldwall/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
- if(air_group || (height==0)) return 1
-
- if(istype(mover) && mover.checkpass(PASSGLASS))
- return prob(20)
- else
- if (istype(mover, /obj/item/projectile))
- return prob(10)
- else
- return !src.density
\ No newline at end of file
diff --git a/code/game/machinery/spaceheater.dm b/code/game/machinery/spaceheater.dm
index 9a6849dcdf4..ffa69f8b412 100644
--- a/code/game/machinery/spaceheater.dm
+++ b/code/game/machinery/spaceheater.dm
@@ -8,195 +8,179 @@
var/obj/item/weapon/cell/cell
var/on = 0
var/open = 0
- var/set_temperature = 50 // in celcius, add T0C for kelvin
+ var/set_temperature = T0C + 50 //K
var/heating_power = 40000
flags = FPRINT
- New()
- ..()
- cell = new(src)
- cell.charge = 1000
- cell.maxcharge = 1000
- update_icon()
- return
-
+/obj/machinery/space_heater/New()
+ ..()
+ cell = new(src)
update_icon()
- overlays.Cut()
- icon_state = "sheater[on]"
- if(open)
- overlays += "sheater-open"
+
+/obj/machinery/space_heater/update_icon()
+ overlays.Cut()
+ icon_state = "sheater[on]"
+ if(open)
+ overlays += "sheater-open"
+
+/obj/machinery/space_heater/examine()
+ set src in oview(12)
+ if (!( usr ))
return
+ usr << "This is \icon[src] \an [src.name]."
+ usr << src.desc
- examine()
- set src in oview(12)
- if (!( usr ))
- return
- usr << "This is \icon[src] \an [src.name]."
- usr << src.desc
+ usr << "The heater is [on ? "on" : "off"] and the hatch is [open ? "open" : "closed"]."
+ if(open)
+ usr << "The power cell is [cell ? "installed" : "missing"]."
+ else
+ usr << "The charge meter reads [cell ? round(cell.percent(),1) : 0]%"
+ return
- usr << "The heater is [on ? "on" : "off"] and the hatch is [open ? "open" : "closed"]."
- if(open)
- usr << "The power cell is [cell ? "installed" : "missing"]."
- else
- usr << "The charge meter reads [cell ? round(cell.percent(),1) : 0]%"
- return
-
- emp_act(severity)
- if(stat & (BROKEN|NOPOWER))
- ..(severity)
- return
- if(cell)
- cell.emp_act(severity)
+/obj/machinery/space_heater/emp_act(severity)
+ if(stat & (BROKEN|NOPOWER))
..(severity)
+ return
+ if(cell)
+ cell.emp_act(severity)
+ ..(severity)
- attackby(obj/item/I, mob/user)
- if(istype(I, /obj/item/weapon/cell))
- if(open)
- if(cell)
- user << "There is already a power cell inside."
- return
- else
- // insert cell
+/obj/machinery/space_heater/attackby(obj/item/I, mob/user)
+ if(istype(I, /obj/item/weapon/cell))
+ if(open)
+ if(cell)
+ user << "There is already a power cell inside."
+ return
+ else
+ // insert cell
+ var/obj/item/weapon/cell/C = usr.get_active_hand()
+ if(istype(C))
+ user.drop_item()
+ cell = C
+ C.loc = src
+ C.add_fingerprint(usr)
+
+ user.visible_message("\blue [user] inserts a power cell into [src].", "\blue You insert the power cell into [src].")
+ else
+ user << "The hatch must be open to insert a power cell."
+ return
+ else if(istype(I, /obj/item/weapon/screwdriver))
+ open = !open
+ user.visible_message("\blue [user] [open ? "opens" : "closes"] the hatch on the [src].", "\blue You [open ? "open" : "close"] the hatch on the [src].")
+ update_icon()
+ if(!open && user.machine == src)
+ user << browse(null, "window=spaceheater")
+ user.unset_machine()
+ else
+ ..()
+ return
+
+/obj/machinery/space_heater/attack_hand(mob/user as mob)
+ src.add_fingerprint(user)
+ interact(user)
+
+/obj/machinery/space_heater/interact(mob/user as mob)
+
+ if(open)
+
+ var/dat
+ dat = "Power cell: "
+ if(cell)
+ dat += "Installed "
+ else
+ dat += "Removed "
+
+ dat += "Power Level: [cell ? round(cell.percent(),1) : 0]%
"
+
+ dat += "Set Temperature: "
+
+ dat += "-"
+
+ dat += " [set_temperature]°C "
+ dat += "+ "
+
+ user.set_machine(src)
+ user << browse("Space Heater Control Panel[dat]", "window=spaceheater")
+ onclose(user, "spaceheater")
+ else
+ on = !on
+ user.visible_message("\blue [user] switches [on ? "on" : "off"] the [src].","\blue You switch [on ? "on" : "off"] the [src].")
+ update_icon()
+ return
+
+
+/obj/machinery/space_heater/Topic(href, href_list)
+ if (usr.stat)
+ return
+ if ((in_range(src, usr) && istype(src.loc, /turf)) || (istype(usr, /mob/living/silicon)))
+ usr.set_machine(src)
+
+ switch(href_list["op"])
+
+ if("temp")
+ var/value = text2num(href_list["val"])
+
+ // limit to 0-90 degC
+ set_temperature = dd_range(T0C, T0C + 90, set_temperature + value)
+
+ if("cellremove")
+ if(open && cell && !usr.get_active_hand())
+ usr.visible_message("\blue [usr] removes \the [cell] from \the [src].", "\blue You remove \the [cell] from \the [src].")
+ cell.updateicon()
+ usr.put_in_hands(cell)
+ cell.add_fingerprint(usr)
+ cell = null
+
+
+ if("cellinstall")
+ if(open && !cell)
var/obj/item/weapon/cell/C = usr.get_active_hand()
if(istype(C))
- user.drop_item()
+ usr.drop_item()
cell = C
C.loc = src
C.add_fingerprint(usr)
- user.visible_message("\blue [user] inserts a power cell into [src].", "\blue You insert the power cell into [src].")
- else
- user << "The hatch must be open to insert a power cell."
- return
- else if(istype(I, /obj/item/weapon/screwdriver))
- open = !open
- user.visible_message("\blue [user] [open ? "opens" : "closes"] the hatch on the [src].", "\blue You [open ? "open" : "close"] the hatch on the [src].")
+ usr.visible_message("\blue [usr] inserts \the [C] into \the [src].", "\blue You insert \the [C] into \the [src].")
+
+ updateDialog()
+ else
+ usr << browse(null, "window=spaceheater")
+ usr.unset_machine()
+ return
+
+
+
+/obj/machinery/space_heater/process()
+ if(on)
+ if(cell && cell.charge)
+ var/datum/gas_mixture/env = loc.return_air()
+ if(env && abs(env.temperature - set_temperature) > 0.1)
+ var/transfer_moles = 0.25 * env.total_moles
+ var/datum/gas_mixture/removed = env.remove(transfer_moles)
+
+ if(removed)
+ var/heat_transfer = removed.get_thermal_energy_change(set_temperature)
+ if(heat_transfer > 0) //heating air
+ heat_transfer = min( heat_transfer , heating_power ) //limit by the power rating of the heater
+
+ removed.add_thermal_energy(heat_transfer)
+ cell.use(heat_transfer*CELLRATE)
+ else //cooling air
+ heat_transfer = abs(heat_transfer)
+
+ //Assume the heat is being pumped into the hull which is fixed at 20 C
+ var/cop = removed.temperature/T20C //coefficient of performance from thermodynamics -> power used = heat_transfer/cop
+ heat_transfer = min(heat_transfer, cop * heating_power) //limit heat transfer by available power
+
+ heat_transfer = removed.add_thermal_energy(-heat_transfer) //get the actual heat transfer
+
+ var/power_used = abs(heat_transfer)/cop
+ cell.use(power_used*CELLRATE)
+
+ env.merge(removed)
+ else
+ on = 0
update_icon()
- if(!open && user.machine == src)
- user << browse(null, "window=spaceheater")
- user.unset_machine()
- else
- ..()
- return
-
- attack_hand(mob/user as mob)
- src.add_fingerprint(user)
- interact(user)
-
- interact(mob/user as mob)
-
- if(open)
-
- var/dat
- dat = "Power cell: "
- if(cell)
- dat += "Installed "
- else
- dat += "Removed "
-
- dat += "Power Level: [cell ? round(cell.percent(),1) : 0]%
"
-
- dat += "Set Temperature: "
-
- dat += "-"
-
- dat += " [set_temperature]°C "
- dat += "+ "
-
- user.set_machine(src)
- user << browse("Space Heater Control Panel[dat]", "window=spaceheater")
- onclose(user, "spaceheater")
-
-
-
-
- else
- on = !on
- user.visible_message("\blue [user] switches [on ? "on" : "off"] the [src].","\blue You switch [on ? "on" : "off"] the [src].")
- update_icon()
- return
-
-
- Topic(href, href_list)
- if (usr.stat)
- return
- if ((in_range(src, usr) && istype(src.loc, /turf)) || (istype(usr, /mob/living/silicon)))
- usr.set_machine(src)
-
- switch(href_list["op"])
-
- if("temp")
- var/value = text2num(href_list["val"])
-
- // limit to 20-90 degC
- set_temperature = dd_range(0, 90, set_temperature + value)
-
- if("cellremove")
- if(open && cell && !usr.get_active_hand())
- cell.updateicon()
- usr.put_in_hands(cell)
- cell.add_fingerprint(usr)
- cell = null
- usr.visible_message("\blue [usr] removes the power cell from \the [src].", "\blue You remove the power cell from \the [src].")
-
-
- if("cellinstall")
- if(open && !cell)
- var/obj/item/weapon/cell/C = usr.get_active_hand()
- if(istype(C))
- usr.drop_item()
- cell = C
- C.loc = src
- C.add_fingerprint(usr)
-
- usr.visible_message("\blue [usr] inserts a power cell into \the [src].", "\blue You insert the power cell into \the [src].")
-
- updateDialog()
- else
- usr << browse(null, "window=spaceheater")
- usr.unset_machine()
- return
-
-
-
- process()
- if(on)
- if(cell && cell.charge > 0)
-
- var/turf/simulated/L = loc
- if(istype(L))
- var/datum/gas_mixture/env = L.return_air()
- if(env.temperature != set_temperature + T0C)
-
- var/transfer_moles = 0.25 * env.total_moles()
-
- var/datum/gas_mixture/removed = env.remove(transfer_moles)
-
- //world << "got [transfer_moles] moles at [removed.temperature]"
-
- if(removed)
-
- var/heat_capacity = removed.heat_capacity()
- //world << "heating ([heat_capacity])"
- if(heat_capacity) // Added check to avoid divide by zero (oshi-) runtime errors -- TLE
- if(removed.temperature < set_temperature + T0C)
- removed.temperature = min(removed.temperature + heating_power/heat_capacity, 1000) // Added min() check to try and avoid wacky superheating issues in low gas scenarios -- TLE
- else
- removed.temperature = max(removed.temperature - heating_power/heat_capacity, TCMB)
- cell.use(heating_power/20000)
-
- //world << "now at [removed.temperature]"
-
- env.merge(removed)
-
- //world << "turf now at [env.temperature]"
-
-
- else
- on = 0
- update_icon()
-
-
- return
\ No newline at end of file
diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm
index 2db29d4ab04..3f0b7448895 100644
--- a/code/game/machinery/suit_storage_unit.dm
+++ b/code/game/machinery/suit_storage_unit.dm
@@ -59,14 +59,13 @@
/obj/machinery/suit_storage_unit/power_change()
- if( powered() )
+ ..()
+ if( !(stat & NOPOWER) )
src.ispowered = 1
- stat &= ~NOPOWER
src.update_icon()
else
spawn(rand(0, 15))
src.ispowered = 0
- stat |= NOPOWER
src.islocked = 0
src.isopen = 1
src.dump_everything()
@@ -623,35 +622,35 @@
model_text = "Engineering"
req_access = list(access_construction)
departments = list("Engineering","Atmos")
- species = list("Human","Tajaran") //Add Unathi when sprites exist for their suits.
+ species = list("Human","Tajaran","Skrell","Unathi") //Add Unathi when sprites exist for their suits.
/obj/machinery/suit_cycler/mining
name = "Mining suit cycler"
model_text = "Mining"
req_access = list(access_mining)
departments = list("Mining")
- species = list("Human","Tajaran")
+ species = list("Human","Tajaran","Skrell","Unathi")
/obj/machinery/suit_cycler/security
name = "Security suit cycler"
model_text = "Security"
req_access = list(access_security)
departments = list("Security")
- species = list("Human","Tajaran")
+ species = list("Human","Tajaran","Skrell","Unathi")
/obj/machinery/suit_cycler/medical
name = "Medical suit cycler"
model_text = "Medical"
req_access = list(access_medical)
departments = list("Medical")
- species = list("Human","Tajaran")
+ species = list("Human","Tajaran","Skrell","Unathi")
/obj/machinery/suit_cycler/syndicate
name = "Nonstandard suit cycler"
model_text = "Nonstandard"
req_access = list(access_syndicate)
departments = list("Mercenary")
- species = list("Human","Tajaran","Unathi","Skrell")
+ species = list("Human","Tajaran","Skrell","Unathi")
can_repair = 1
/obj/machinery/suit_cycler/attack_ai(mob/user as mob)
diff --git a/code/game/machinery/syndicatebeacon.dm b/code/game/machinery/syndicatebeacon.dm
index 59e24ad3e9c..2be8fabf28c 100644
--- a/code/game/machinery/syndicatebeacon.dm
+++ b/code/game/machinery/syndicatebeacon.dm
@@ -86,11 +86,7 @@
M << "You have joined the ranks of the Syndicate and become a traitor to the station!"
message_admins("[N]/([N.ckey]) has accepted a traitor objective from a syndicate beacon.")
-
- var/obj_count = 1
- for(var/datum/objective/OBJ in M.mind.objectives)
- M << "Objective #[obj_count]: [OBJ.explanation_text]"
- obj_count++
+ show_objectives(M.mind)
src.add_fingerprint(usr)
src.updateUsrDialog()
diff --git a/code/game/machinery/telecomms/broadcaster.dm b/code/game/machinery/telecomms/broadcaster.dm
index 830ff05a1c0..35a676d2935 100644
--- a/code/game/machinery/telecomms/broadcaster.dm
+++ b/code/game/machinery/telecomms/broadcaster.dm
@@ -20,7 +20,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
use_power = 1
idle_power_usage = 25
machinetype = 5
- heatgen = 0
+ produces_heat = 0
delay = 7
circuitboard = "/obj/item/weapon/circuitboard/telecomms/broadcaster"
@@ -120,7 +120,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
use_power = 0
idle_power_usage = 0
machinetype = 6
- heatgen = 0
+ produces_heat = 0
var/intercept = 0 // if nonzero, broadcasts all messages to syndicate channel
/obj/machinery/telecomms/allinone/receive_signal(datum/signal/signal)
diff --git a/code/game/machinery/telecomms/machine_interactions.dm b/code/game/machinery/telecomms/machine_interactions.dm
index da2af57a566..39ce187fb46 100644
--- a/code/game/machinery/telecomms/machine_interactions.dm
+++ b/code/game/machinery/telecomms/machine_interactions.dm
@@ -21,6 +21,19 @@
if(istype(P, /obj/item/device/multitool))
attack_hand(user)
+
+ // REPAIRING: Use Nanopaste to repair 10-20 integrity points.
+ if(istype(P, /obj/item/stack/nanopaste))
+ var/obj/item/stack/nanopaste/T = P
+ if (integrity < 100) //Damaged, let's repair!
+ integrity = between(0, integrity + rand(10,20), 100)
+ T.use(1)
+ usr << "You apply the Nanopaste to [src], repairing some of the damage."
+ else
+ usr << "This machine is already in perfect condition."
+ return
+
+
switch(construct_op)
if(0)
if(istype(P, /obj/item/weapon/screwdriver))
diff --git a/code/game/machinery/telecomms/presets.dm b/code/game/machinery/telecomms/presets.dm
index 83f3a532b7f..6f770ed7390 100644
--- a/code/game/machinery/telecomms/presets.dm
+++ b/code/game/machinery/telecomms/presets.dm
@@ -31,7 +31,7 @@
//anchored = 1
//use_power = 0
//idle_power_usage = 0
- heatgen = 0
+ produces_heat = 0
autolinkers = list("c_relay")
//HUB
@@ -46,7 +46,7 @@
/obj/machinery/telecomms/hub/preset_cent
id = "CentComm Hub"
network = "tcommsat"
- heatgen = 0
+ produces_heat = 0
autolinkers = list("hub_cent", "c_relay", "s_relay", "m_relay", "r_relay",
"centcomm", "receiverCent", "broadcasterCent")
@@ -78,7 +78,7 @@
/obj/machinery/telecomms/receiver/preset_cent
id = "CentComm Receiver"
network = "tcommsat"
- heatgen = 0
+ produces_heat = 0
autolinkers = list("receiverCent")
freq_listening = list(1345, 1341)
@@ -118,7 +118,7 @@
id = "CentComm Bus"
network = "tcommsat"
freq_listening = list(1345, 1341)
- heatgen = 0
+ produces_heat = 0
autolinkers = list("processorCent", "centcomm")
//Processors
@@ -146,7 +146,7 @@
/obj/machinery/telecomms/processor/preset_cent
id = "CentComm Processor"
network = "tcommsat"
- heatgen = 0
+ produces_heat = 0
autolinkers = list("processorCent")
//Servers
@@ -200,7 +200,7 @@
/obj/machinery/telecomms/server/presets/centcomm
id = "CentComm Server"
freq_listening = list(1345, 1341)
- heatgen = 0
+ produces_heat = 0
autolinkers = list("centcomm")
@@ -223,5 +223,5 @@
/obj/machinery/telecomms/broadcaster/preset_cent
id = "CentComm Broadcaster"
network = "tcommsat"
- heatgen = 0
+ produces_heat = 0
autolinkers = list("broadcasterCent")
\ No newline at end of file
diff --git a/code/game/machinery/telecomms/telecomunications.dm b/code/game/machinery/telecomms/telecomunications.dm
index 95995fa2b9c..4d015983116 100644
--- a/code/game/machinery/telecomms/telecomunications.dm
+++ b/code/game/machinery/telecomms/telecomunications.dm
@@ -29,9 +29,8 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list()
var/toggled = 1 // Is it toggled on
var/on = 1
var/integrity = 100 // basically HP, loses integrity by heat
- var/heatgen = 20 // how much heat to transfer to the environment
+ var/produces_heat = 1 //whether the machine will produce heat when on.
var/delay = 10 // how many process() ticks to delay per heat
- var/heating_power = 40000
var/long_range_link = 0 // Can you link it across Z levels or on the otherside of the map? (Relay & Hub)
var/circuitboard = null // string pointing to a circuitboard type
var/hide = 0 // Is it a hidden machine?
@@ -214,41 +213,53 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list()
/obj/machinery/telecomms/proc/checkheat()
// Checks heat from the environment and applies any integrity damage
var/datum/gas_mixture/environment = loc.return_air()
+ var/damage_chance = 0 // Percent based chance of applying 1 integrity damage this tick
switch(environment.temperature)
- if(T0C to (T20C + 20))
- integrity = between(0, integrity, 100)
- if((T20C + 20) to (T0C + 70))
- integrity = max(0, integrity - 1)
- if(delay)
- delay--
- else
- // If the machine is on, ready to produce heat, and has positive traffic, genn some heat
- if(on && traffic > 0)
- produce_heat(heatgen)
- delay = initial(delay)
+ if((T0C + 40) to (T0C + 70)) // 40C-70C, minor overheat, 10% chance of taking damage
+ damage_chance = 10
+ if((T0C + 70) to (T0C + 130)) // 70C-130C, major overheat, 25% chance of taking damage
+ damage_chance = 25
+ if((T0C + 130) to (T0C + 200)) // 130C-200C, dangerous overheat, 50% chance of taking damage
+ damage_chance = 50
+ if((T0C + 200) to INFINITY) // More than 200C, INFERNO. Takes damage every tick.
+ damage_chance = 100
+ if (damage_chance && prob(damage_chance))
+ integrity = between(0, integrity - 1, 100)
-/obj/machinery/telecomms/proc/produce_heat(heat_amt)
- if(heatgen == 0)
+
+ if(delay > 0)
+ delay--
+ else if(on)
+ produce_heat()
+ delay = initial(delay)
+
+
+
+/obj/machinery/telecomms/proc/produce_heat()
+ if (!produces_heat)
+ return
+
+ if (!use_power)
return
- if(!(stat & (NOPOWER|BROKEN))) //Blatently stolen from space heater.
+ if(!(stat & (NOPOWER|BROKEN)))
var/turf/simulated/L = loc
if(istype(L))
var/datum/gas_mixture/env = L.return_air()
- if(env.temperature < (heat_amt+T0C))
- var/transfer_moles = 0.25 * env.total_moles()
+ var/transfer_moles = 0.25 * env.total_moles
- var/datum/gas_mixture/removed = env.remove(transfer_moles)
+ var/datum/gas_mixture/removed = env.remove(transfer_moles)
- if(removed)
+ if(removed)
+
+ var/heat_produced = idle_power_usage //obviously can't produce more heat than the machine draws from it's power source
+ if (traffic <= 0)
+ heat_produced *= 0.30 //if idle, produce less heat.
+
+ removed.add_thermal_energy(heat_produced)
- var/heat_capacity = removed.heat_capacity()
- if(heat_capacity == 0 || heat_capacity == null)
- heat_capacity = 1
- removed.temperature = min((removed.temperature*heat_capacity + heating_power)/heat_capacity, 1000)
-
- env.merge(removed)
+ env.merge(removed)
/*
The receiver idles and receives messages from subspace-compatible radio equipment;
primarily headsets. They then just relay this information to all linked devices,
@@ -265,9 +276,9 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list()
density = 1
anchored = 1
use_power = 1
- idle_power_usage = 30
+ idle_power_usage = 600
machinetype = 1
- heatgen = 0
+ produces_heat = 0
circuitboard = "/obj/item/weapon/circuitboard/telecomms/receiver"
/obj/machinery/telecomms/receiver/receive_signal(datum/signal/signal)
@@ -322,9 +333,8 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list()
density = 1
anchored = 1
use_power = 1
- idle_power_usage = 80
+ idle_power_usage = 1600
machinetype = 7
- heatgen = 40
circuitboard = "/obj/item/weapon/circuitboard/telecomms/hub"
long_range_link = 1
netspeed = 40
@@ -357,9 +367,9 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list()
density = 1
anchored = 1
use_power = 1
- idle_power_usage = 30
+ idle_power_usage = 600
machinetype = 8
- heatgen = 0
+ produces_heat = 0
circuitboard = "/obj/item/weapon/circuitboard/telecomms/relay"
netspeed = 5
long_range_link = 1
@@ -409,9 +419,8 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list()
density = 1
anchored = 1
use_power = 1
- idle_power_usage = 50
+ idle_power_usage = 1000
machinetype = 2
- heatgen = 20
circuitboard = "/obj/item/weapon/circuitboard/telecomms/bus"
netspeed = 40
var/change_frequency = 0
@@ -462,9 +471,8 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list()
density = 1
anchored = 1
use_power = 1
- idle_power_usage = 30
+ idle_power_usage = 600
machinetype = 3
- heatgen = 100
delay = 5
circuitboard = "/obj/item/weapon/circuitboard/telecomms/processor"
var/process_mode = 1 // 1 = Uncompress Signals, 0 = Compress Signals
@@ -501,9 +509,8 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list()
density = 1
anchored = 1
use_power = 1
- idle_power_usage = 15
+ idle_power_usage = 300
machinetype = 4
- heatgen = 50
circuitboard = "/obj/item/weapon/circuitboard/telecomms/server"
var/list/log_entries = list()
var/list/stored_names = list()
diff --git a/code/game/machinery/turrets.dm b/code/game/machinery/turrets.dm
index 51ccee7fc56..98af795ca83 100644
--- a/code/game/machinery/turrets.dm
+++ b/code/game/machinery/turrets.dm
@@ -93,10 +93,11 @@
return (popping!=0)
/obj/machinery/turret/power_change()
+ ..()
if(stat & BROKEN)
icon_state = "grey_target_prism"
else
- if( powered() )
+ if( !(stat & NOPOWER) )
if (src.enabled)
if (src.lasers)
icon_state = "orange_target_prism"
diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm
index f48a4ddfc21..17389d93f87 100644
--- a/code/game/machinery/vending.dm
+++ b/code/game/machinery/vending.dm
@@ -20,7 +20,13 @@
layer = 2.9
anchored = 1
density = 1
+
+ use_power = 1
+ idle_power_usage = 10
+ var/vend_power_usage = 150 //actuators and stuff
+
var/active = 1 //No sales pitches if off!
+ var/delay_product_spawn // If set, uses sleep() in product spawn proc (mostly for seeds to retrieve correct names).
var/vend_ready = 1 //Are we ready to vend?? Is it time??
var/vend_delay = 10 //How long does it take to vend?
var/datum/data/vending_product/currently_vending = null // A /datum/data/vending_product instance of what we're paying for right now.
@@ -116,7 +122,7 @@
var/atom/temp = new typepath(null)
var/datum/data/vending_product/R = new /datum/data/vending_product()
- R.product_name = temp.name
+
R.product_path = typepath
R.amount = amount
R.price = price
@@ -131,6 +137,13 @@
else
R.category=CAT_NORMAL
product_records += R
+
+ if(delay_product_spawn)
+ sleep(1)
+ R.product_name = temp.name
+ else
+ R.product_name = temp.name
+
// world << "Added: [R.product_name]] - [R.amount] - [R.product_path]"
return
@@ -500,7 +513,7 @@
src.speak(src.vend_reply)
src.last_reply = world.time
- use_power(5)
+ use_power(vend_power_usage) //actuators and stuff
if (src.icon_vend) //Show the vending animation if needed
flick(src.icon_vend,src)
spawn(src.vend_delay)
@@ -550,16 +563,15 @@
return
/obj/machinery/vending/power_change()
+ ..()
if(stat & BROKEN)
icon_state = "[initial(icon_state)]-broken"
else
- if( powered() )
+ if( !(stat & NOPOWER) )
icon_state = initial(icon_state)
- stat &= ~NOPOWER
else
spawn(rand(0, 15))
src.icon_state = "[initial(icon_state)]-off"
- stat |= NOPOWER
//Oh no we're malfunctioning! Dump out some product and break.
/obj/machinery/vending/proc/malfunction()
@@ -697,6 +709,7 @@
/obj/item/weapon/reagent_containers/food/drinks/bottle/absinthe = 2,/obj/item/weapon/reagent_containers/food/drinks/bottle/grenadine = 5)
contraband = list(/obj/item/weapon/reagent_containers/food/drinks/tea = 10)
vend_delay = 15
+ idle_power_usage = 211 //refrigerator - believe it or not, this is actually the average power consumption of a refrigerated vending machine according to NRCan.
product_slogans = "I hope nobody asks me for a bloody cup o' tea...;Alcohol is humanity's friend. Would you abandon a friend?;Quite delighted to serve you!;Is nobody thirsty on this station?"
product_ads = "Drink up!;Booze is good for you!;Alcohol is humanity's best friend.;Quite delighted to serve you!;Care for a nice, cold beer?;Nothing cures you like booze!;Have a sip!;Have a drink!;Have a beer!;Beer is good for you!;Only the finest alcohol!;Best quality booze since 2053!;Award-winning wine!;Maximum alcohol!;Man loves beer.;A toast for progress!"
req_access_txt = "25"
@@ -714,6 +727,8 @@
icon_state = "coffee"
icon_vend = "coffee-vend"
vend_delay = 34
+ idle_power_usage = 211 //refrigerator - believe it or not, this is actually the average power consumption of a refrigerated vending machine according to NRCan.
+ vend_power_usage = 85000 //85 kJ to heat a 250 mL cup of coffee
products = list(/obj/item/weapon/reagent_containers/food/drinks/coffee = 25,/obj/item/weapon/reagent_containers/food/drinks/tea = 25,/obj/item/weapon/reagent_containers/food/drinks/h_chocolate = 25)
contraband = list(/obj/item/weapon/reagent_containers/food/drinks/ice = 10)
prices = list(/obj/item/weapon/reagent_containers/food/drinks/coffee = 25, /obj/item/weapon/reagent_containers/food/drinks/tea = 25, /obj/item/weapon/reagent_containers/food/drinks/h_chocolate = 25)
@@ -752,6 +767,7 @@
/obj/item/weapon/reagent_containers/food/drinks/cans/dr_gibb = 1,/obj/item/weapon/reagent_containers/food/drinks/cans/starkist = 1,
/obj/item/weapon/reagent_containers/food/drinks/cans/waterbottle = 2,/obj/item/weapon/reagent_containers/food/drinks/cans/space_up = 1,
/obj/item/weapon/reagent_containers/food/drinks/cans/iced_tea = 1,/obj/item/weapon/reagent_containers/food/drinks/cans/grape_juice = 1)
+ idle_power_usage = 211 //refrigerator - believe it or not, this is actually the average power consumption of a refrigerated vending machine according to NRCan.
//This one's from bay12
/obj/machinery/vending/cart
@@ -774,7 +790,7 @@
icon_state = "cigs"
products = list(/obj/item/weapon/storage/fancy/cigarettes = 10,/obj/item/weapon/storage/box/matches = 10,/obj/item/weapon/flame/lighter/random = 4)
contraband = list(/obj/item/weapon/flame/lighter/zippo = 4)
- premium = list(/obj/item/clothing/mask/cigarette/cigar/havana = 2)
+ premium = list(/obj/item/weapon/storage/fancy/cigar = 5)
prices = list(/obj/item/weapon/storage/fancy/cigarettes = 15,/obj/item/weapon/storage/box/matches = 1,/obj/item/weapon/flame/lighter/random = 2)
@@ -791,6 +807,7 @@
/obj/item/device/healthanalyzer = 5,/obj/item/weapon/reagent_containers/glass/beaker = 4, /obj/item/weapon/reagent_containers/dropper = 2,
/obj/item/stack/medical/advanced/bruise_pack = 3, /obj/item/stack/medical/advanced/ointment = 3, /obj/item/stack/medical/splint = 2)
contraband = list(/obj/item/weapon/reagent_containers/pill/tox = 3,/obj/item/weapon/reagent_containers/pill/stox = 4,/obj/item/weapon/reagent_containers/pill/antitox = 6)
+ idle_power_usage = 211 //refrigerator - believe it or not, this is actually the average power consumption of a refrigerated vending machine according to NRCan.
//This one's from bay12
@@ -841,9 +858,10 @@
product_ads = "We like plants!;Don't you want some?;The greenest thumbs ever.;We like big plants.;Soft soil..."
icon_state = "nutri"
icon_deny = "nutri-deny"
- products = list(/obj/item/nutrient/ez = 35,/obj/item/nutrient/l4z = 25,/obj/item/nutrient/rh = 15,/obj/item/weapon/pestspray = 20,
+ products = list(/obj/item/weapon/reagent_containers/glass/fertilizer/ez = 35,/obj/item/weapon/reagent_containers/glass/fertilizer/l4z = 25,/obj/item/weapon/reagent_containers/glass/fertilizer/rh = 15,/obj/item/weapon/plantspray/pests = 20,
/obj/item/weapon/reagent_containers/syringe = 5,/obj/item/weapon/storage/bag/plants = 5)
premium = list(/obj/item/weapon/reagent_containers/glass/bottle/ammonia = 10,/obj/item/weapon/reagent_containers/glass/bottle/diethylamine = 5)
+ idle_power_usage = 211 //refrigerator - believe it or not, this is actually the average power consumption of a refrigerated vending machine according to NRCan.
/obj/machinery/vending/hydroseeds
name = "MegaSeed Servitor"
@@ -851,6 +869,8 @@
product_slogans = "THIS'S WHERE TH' SEEDS LIVE! GIT YOU SOME!;Hands down the best seed selection on the station!;Also certain mushroom varieties available, more for experts! Get certified today!"
product_ads = "We like plants!;Grow some crops!;Grow, baby, growww!;Aw h'yeah son!"
icon_state = "seeds"
+ delay_product_spawn = 1
+
products = list(/obj/item/seeds/bananaseed = 3,/obj/item/seeds/berryseed = 3,/obj/item/seeds/carrotseed = 3,/obj/item/seeds/chantermycelium = 3,/obj/item/seeds/chiliseed = 3,
/obj/item/seeds/cornseed = 3, /obj/item/seeds/eggplantseed = 3, /obj/item/seeds/potatoseed = 3, /obj/item/seeds/replicapod = 3,/obj/item/seeds/soyaseed = 3,
/obj/item/seeds/sunflowerseed = 3,/obj/item/seeds/tomatoseed = 3,/obj/item/seeds/towermycelium = 3,/obj/item/seeds/wheatseed = 3,/obj/item/seeds/appleseed = 3,
@@ -888,6 +908,7 @@
product_ads = "For Tsar and Country.;Have you fulfilled your nutrition quota today?;Very nice!;We are simple people, for this is all we eat.;If there is a person, there is a problem. If there is no person, then there is no problem."
products = list(/obj/item/weapon/reagent_containers/food/drinks/drinkingglass/soda = 30)
contraband = list(/obj/item/weapon/reagent_containers/food/drinks/drinkingglass/cola = 20)
+ idle_power_usage = 211 //refrigerator - believe it or not, this is actually the average power consumption of a refrigerated vending machine according to NRCan.
/obj/machinery/vending/tool
name = "YouTool"
diff --git a/code/game/machinery/wishgranter.dm b/code/game/machinery/wishgranter.dm
index 786468c0c47..2720a1c3187 100644
--- a/code/game/machinery/wishgranter.dm
+++ b/code/game/machinery/wishgranter.dm
@@ -66,11 +66,7 @@
silence.owner = user.mind
user.mind.objectives += silence
- var/obj_count = 1
- for(var/datum/objective/OBJ in user.mind.objectives)
- user << "Objective #[obj_count]: [OBJ.explanation_text]"
- obj_count++
-
+ show_objectives(user.mind)
user << "You have a very bad feeling about this."
return
\ No newline at end of file
diff --git a/code/game/mecha/equipment/tools/tools.dm b/code/game/mecha/equipment/tools/tools.dm
index 629e3b9d7de..ddfa9fa3284 100644
--- a/code/game/mecha/equipment/tools/tools.dm
+++ b/code/game/mecha/equipment/tools/tools.dm
@@ -924,13 +924,11 @@
return
var/datum/gas_mixture/GM = new
if(prob(10))
- GM.phoron += 100
- GM.temperature = 1500+T0C //should be enough to start a fire
+ T.assume_gas("phoron", 100, 1500+T0C)
T.visible_message("The [src] suddenly disgorges a cloud of heated phoron.")
destroy()
else
- GM.phoron += 5
- GM.temperature = istype(T) ? T.air.temperature : T20C
+ T.assume_gas("phoron", 5, istype(T) ? T.air.temperature : T20C)
T.visible_message("The [src] suddenly disgorges a cloud of phoron.")
T.assume_air(GM)
return
diff --git a/code/game/mecha/mech_sensor.dm b/code/game/mecha/mech_sensor.dm
new file mode 100644
index 00000000000..d7e1937715f
--- /dev/null
+++ b/code/game/mecha/mech_sensor.dm
@@ -0,0 +1,94 @@
+/obj/machinery/mech_sensor
+ icon = 'icons/obj/airlock_machines.dmi'
+ icon_state = "airlock_sensor_off"
+ name = "mechatronic sensor"
+ desc = "Regulates mech movement."
+ anchored = 1
+ density = 1
+ throwpass = 1
+ use_power = 1
+ layer = 3.3
+ power_channel = EQUIP
+ var/on = 0
+ var/id_tag = null
+
+ var/frequency = 1379
+ var/datum/radio_frequency/radio_connection
+
+/obj/machinery/mech_sensor/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
+ if(!src.enabled()) return 1
+ if(air_group || (height==0)) return 1
+
+ if ((get_dir(loc, target) & dir) && src.is_blocked(mover))
+ src.give_feedback(mover)
+ return 0
+ return 1
+
+/obj/machinery/mech_sensor/proc/is_blocked(O as obj)
+ if(istype(O, /obj/mecha/medical/odysseus))
+ var/obj/mecha/medical/odysseus/M = O
+ for(var/obj/item/mecha_parts/mecha_equipment/ME in M.equipment)
+ if(istype(ME, /obj/item/mecha_parts/mecha_equipment/tool/sleeper))
+ var/obj/item/mecha_parts/mecha_equipment/tool/sleeper/S = ME
+ if(S.occupant != null)
+ return 0
+
+ return istype(O, /obj/mecha) || istype(O, /obj/vehicle)
+
+/obj/machinery/mech_sensor/proc/give_feedback(O as obj)
+ var/block_message = "Movement control overridden. Area denial active."
+ var/feedback_timer = 0
+ if(feedback_timer)
+ return
+
+ if(istype(O, /obj/mecha))
+ var/obj/mecha/R = O
+ if(R && R.occupant)
+ R.occupant << block_message
+ else if(istype(O, /obj/vehicle/train/cargo/engine))
+ var/obj/vehicle/train/cargo/engine/E = O
+ if(E && E.load && E.is_train_head())
+ E.load << block_message
+
+ feedback_timer = 1
+ spawn(5) //Without this timer the feedback becomes horribly spamy
+ feedback_timer = 0
+
+/obj/machinery/mech_sensor/proc/enabled()
+ return on && !(stat & NOPOWER)
+
+/obj/machinery/mech_sensor/power_change()
+ var/old_stat = stat
+ ..()
+ if(old_stat != stat)
+ update_icon()
+
+/obj/machinery/mech_sensor/update_icon(var/safety = 0)
+ if (enabled())
+ icon_state = "airlock_sensor_standby"
+ else
+ icon_state = "airlock_sensor_off"
+
+/obj/machinery/mech_sensor/initialize()
+ set_frequency(frequency)
+
+/obj/machinery/mech_sensor/proc/set_frequency(new_frequency)
+ if(radio_connection)
+ radio_controller.remove_object(src, frequency)
+ frequency = new_frequency
+ if(frequency)
+ radio_connection = radio_controller.add_object(src, frequency)
+
+/obj/machinery/mech_sensor/receive_signal(datum/signal/signal)
+ if(stat & NOPOWER)
+ return
+
+ if(!signal.data["tag"] || (signal.data["tag"] != id_tag))
+ return
+
+ if(signal.data["command"] == "enable")
+ on = 1
+ else if (signal.data["command"] == "disable")
+ on = 0
+
+ update_icon()
diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm
index 0cb89ee683d..3c877d3908a 100644
--- a/code/game/mecha/mecha.dm
+++ b/code/game/mecha/mecha.dm
@@ -123,8 +123,7 @@
cabin_air = new
cabin_air.temperature = T20C
cabin_air.volume = 200
- cabin_air.oxygen = O2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature)
- cabin_air.nitrogen = N2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature)
+ cabin_air.adjust_multi("oxygen", O2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature), "nitrogen", N2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature))
return cabin_air
/obj/mecha/proc/add_radio()
@@ -332,7 +331,7 @@
var/obj/O = obstacle
if(istype(O, /obj/effect/portal)) //derpfix
src.anchored = 0
- O.HasEntered(src)
+ O.Crossed(src)
spawn(0)//countering portal teleport spawn(0), hurr
src.anchored = 1
else if(!O.anchored)
@@ -855,11 +854,11 @@
/obj/mecha/proc/return_temperature()
. = 0
if(use_internal_tank)
- . = cabin_air.return_temperature()
+ . = cabin_air.temperature
else
var/datum/gas_mixture/t_air = get_turf_air()
if(t_air)
- . = t_air.return_temperature()
+ . = t_air.temperature
return
/obj/mecha/proc/connect(obj/machinery/atmospherics/portables_connector/new_port)
@@ -1705,7 +1704,7 @@
delay = 20
process(var/obj/mecha/mecha)
- if(mecha.cabin_air && mecha.cabin_air.return_volume() > 0)
+ if(mecha.cabin_air && mecha.cabin_air.volume > 0)
var/delta = mecha.cabin_air.temperature - T20C
mecha.cabin_air.temperature -= max(-10, min(10, round(delta/4,0.1)))
return
@@ -1723,8 +1722,8 @@
var/pressure_delta = min(release_pressure - cabin_pressure, (tank_air.return_pressure() - cabin_pressure)/2)
var/transfer_moles = 0
if(pressure_delta > 0) //cabin pressure lower than release pressure
- if(tank_air.return_temperature() > 0)
- transfer_moles = pressure_delta*cabin_air.return_volume()/(cabin_air.return_temperature() * R_IDEAL_GAS_EQUATION)
+ if(tank_air.temperature > 0)
+ transfer_moles = pressure_delta*cabin_air.volume/(cabin_air.temperature * R_IDEAL_GAS_EQUATION)
var/datum/gas_mixture/removed = tank_air.remove(transfer_moles)
cabin_air.merge(removed)
else if(pressure_delta < 0) //cabin pressure higher than release pressure
@@ -1733,7 +1732,7 @@
if(t_air)
pressure_delta = min(cabin_pressure - t_air.return_pressure(), pressure_delta)
if(pressure_delta > 0) //if location pressure is lower than cabin pressure
- transfer_moles = pressure_delta*cabin_air.return_volume()/(cabin_air.return_temperature() * R_IDEAL_GAS_EQUATION)
+ transfer_moles = pressure_delta*cabin_air.volume/(cabin_air.temperature * R_IDEAL_GAS_EQUATION)
var/datum/gas_mixture/removed = cabin_air.remove(transfer_moles)
if(t_air)
t_air.merge(removed)
@@ -1766,12 +1765,12 @@
if(mecha.internal_tank.return_pressure()>mecha.internal_tank.maximum_pressure && !(mecha.hasInternalDamage(MECHA_INT_TANK_BREACH)))
mecha.setInternalDamage(MECHA_INT_TANK_BREACH)
var/datum/gas_mixture/int_tank_air = mecha.internal_tank.return_air()
- if(int_tank_air && int_tank_air.return_volume()>0) //heat the air_contents
+ if(int_tank_air && int_tank_air.volume>0) //heat the air_contents
int_tank_air.temperature = min(6000+T0C, int_tank_air.temperature+rand(10,15))
- if(mecha.cabin_air && mecha.cabin_air.return_volume()>0)
- mecha.cabin_air.temperature = min(6000+T0C, mecha.cabin_air.return_temperature()+rand(10,15))
- if(mecha.cabin_air.return_temperature()>mecha.max_temperature/2)
- mecha.take_damage(4/round(mecha.max_temperature/mecha.cabin_air.return_temperature(),0.1),"fire")
+ if(mecha.cabin_air && mecha.cabin_air.volume>0)
+ mecha.cabin_air.temperature = min(6000+T0C, mecha.cabin_air.temperature+rand(10,15))
+ if(mecha.cabin_air.temperature>mecha.max_temperature/2)
+ mecha.take_damage(4/round(mecha.max_temperature/mecha.cabin_air.temperature,0.1),"fire")
if(mecha.hasInternalDamage(MECHA_INT_TEMP_CONTROL)) //stop the mecha_preserve_temp loop datum
mecha.pr_int_temp_processor.stop()
if(mecha.hasInternalDamage(MECHA_INT_TANK_BREACH)) //remove some air from internal tank
diff --git a/code/game/objects/effects/chemsmoke.dm b/code/game/objects/effects/chemsmoke.dm
index 4c245b78cc2..fb9b049a3e0 100644
--- a/code/game/objects/effects/chemsmoke.dm
+++ b/code/game/objects/effects/chemsmoke.dm
@@ -224,24 +224,25 @@
pending += location
while(pending.len)
- for(var/turf/simulated/current in pending)
+ for(var/turf/current in pending)
for(var/D in cardinal)
- var/turf/simulated/target = get_step(current, D)
-
+ var/turf/target = get_step(current, D)
if(wallList)
if(istype(target, /turf/simulated/wall))
if(!(target in wallList))
wallList += target
continue
- if(!target.zone)
- continue
+
if(target in pending)
continue
if(target in complete)
continue
if(!(target in targetTurfs))
continue
-
+ if(current.c_airblock(target)) //this is needed to stop chemsmoke from passing through thin window walls
+ continue
+ if(target.c_airblock(current))
+ continue
pending += target
pending -= current
@@ -249,4 +250,4 @@
targetTurfs = complete
- return
\ No newline at end of file
+ return
diff --git a/code/game/objects/effects/decals/Cleanable/humans.dm b/code/game/objects/effects/decals/Cleanable/humans.dm
index 17bcaba4280..d348a351a1e 100644
--- a/code/game/objects/effects/decals/Cleanable/humans.dm
+++ b/code/game/objects/effects/decals/Cleanable/humans.dm
@@ -45,7 +45,7 @@ var/global/list/image/splatter_cache=list()
if(basecolor == "rainbow") basecolor = "#[pick(list("FF0000","FF7F00","FFFF00","00FF00","0000FF","4B0082","8F00FF"))]"
color = basecolor
-/obj/effect/decal/cleanable/blood/HasEntered(mob/living/carbon/human/perp)
+/obj/effect/decal/cleanable/blood/Crossed(mob/living/carbon/human/perp)
if (!istype(perp))
return
if(amount < 1)
diff --git a/code/game/objects/effects/effect_system.dm b/code/game/objects/effects/effect_system.dm
index 59e516a7147..0da251e1eed 100644
--- a/code/game/objects/effects/effect_system.dm
+++ b/code/game/objects/effects/effect_system.dm
@@ -216,7 +216,7 @@ steam.start() -- spawns the effect
delete()
return
-/obj/effect/effect/smoke/HasEntered(mob/living/carbon/M as mob )
+/obj/effect/effect/smoke/Crossed(mob/living/carbon/M as mob )
..()
if(istype(M))
affect(M)
@@ -549,7 +549,7 @@ steam.start() -- spawns the effect
delete()
-/obj/effect/effect/foam/HasEntered(var/atom/movable/AM)
+/obj/effect/effect/foam/Crossed(var/atom/movable/AM)
if(metal)
return
diff --git a/code/game/objects/effects/glowshroom.dm b/code/game/objects/effects/glowshroom.dm
index 39533b04831..68285d4f986 100644
--- a/code/game/objects/effects/glowshroom.dm
+++ b/code/game/objects/effects/glowshroom.dm
@@ -8,6 +8,8 @@
icon = 'icons/obj/lighting.dmi'
icon_state = "glowshroomf"
layer = 2.1
+ l_color = "#003300"
+
var/endurance = 30
var/potency = 30
var/delay = 1200
@@ -44,7 +46,7 @@
processing_objects += src
- SetLuminosity(round(potency/10))
+ SetLuminosity(round(potency/15))
lastTick = world.timeofday
/obj/effect/glowshroom/Del()
diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm
index e287ee845d6..514a5b38f9f 100644
--- a/code/game/objects/effects/landmarks.dm
+++ b/code/game/objects/effects/landmarks.dm
@@ -81,6 +81,14 @@
ninjastart += loc
del(src)
+ if("voxstart")
+ raider_spawn += loc
+ del(src)
+
+ if("Syndicate-Spawn")
+ synd_spawn += loc
+ del(src)
+
landmarks_list += src
return 1
diff --git a/code/game/objects/effects/mines.dm b/code/game/objects/effects/mines.dm
index 25711498511..de356849917 100644
--- a/code/game/objects/effects/mines.dm
+++ b/code/game/objects/effects/mines.dm
@@ -12,7 +12,7 @@
/obj/effect/mine/New()
icon_state = "uglyminearmed"
-/obj/effect/mine/HasEntered(AM as mob|obj)
+/obj/effect/mine/Crossed(AM as mob|obj)
Bumped(AM)
/obj/effect/mine/Bumped(mob/M as mob|obj)
@@ -51,14 +51,7 @@
for (var/turf/simulated/floor/target in range(1,src))
if(!target.blocks_air)
-
- var/datum/gas_mixture/payload = new
- var/datum/gas/sleeping_agent/trace_gas = new
-
- trace_gas.moles = 30
- payload += trace_gas
-
- target.zone.air.merge(payload)
+ target.assume_gas("sleeping_agent", 30)
spawn(0)
del(src)
@@ -66,12 +59,7 @@
/obj/effect/mine/proc/triggerphoron(obj)
for (var/turf/simulated/floor/target in range(1,src))
if(!target.blocks_air)
-
- var/datum/gas_mixture/payload = new
-
- payload.phoron = 30
-
- target.zone.air.merge(payload)
+ target.assume_gas("phoron", 30)
target.hotspot_expose(1000, CELL_VOLUME)
diff --git a/code/game/objects/effects/portals.dm b/code/game/objects/effects/portals.dm
index f8c429e473c..6a9f16e99ec 100644
--- a/code/game/objects/effects/portals.dm
+++ b/code/game/objects/effects/portals.dm
@@ -16,7 +16,7 @@
return
return
-/obj/effect/portal/HasEntered(AM as mob|obj)
+/obj/effect/portal/Crossed(AM as mob|obj)
spawn(0)
src.teleport(AM)
return
diff --git a/code/game/objects/effects/spawners/bombspawner.dm b/code/game/objects/effects/spawners/bombspawner.dm
index 6540b5f3bb5..3f694a8b49a 100644
--- a/code/game/objects/effects/spawners/bombspawner.dm
+++ b/code/game/objects/effects/spawners/bombspawner.dm
@@ -137,13 +137,10 @@
OT.master = V
PT.air_contents.temperature = PHORON_FLASHPOINT
- PT.air_contents.phoron = 12
- PT.air_contents.carbon_dioxide = 8
- PT.air_contents.update_values()
+ PT.air_contents.adjust_multi("phoron", 12, "carbon_dioxide", 8)
OT.air_contents.temperature = PHORON_FLASHPOINT
- OT.air_contents.oxygen = 20
- OT.air_contents.update_values()
+ OT.air_contents.adjust_gas("oxygen", 20)
var/obj/item/device/assembly/S
diff --git a/code/game/objects/effects/step_triggers.dm b/code/game/objects/effects/step_triggers.dm
index 3acecd04b2d..cd9142bdc0b 100644
--- a/code/game/objects/effects/step_triggers.dm
+++ b/code/game/objects/effects/step_triggers.dm
@@ -1,117 +1,117 @@
-/* Simple object type, calls a proc when "stepped" on by something */
-
-/obj/effect/step_trigger
- var/affect_ghosts = 0
- var/stopper = 1 // stops throwers
- invisibility = 101 // nope cant see this shit
- anchored = 1
-
-/obj/effect/step_trigger/proc/Trigger(var/atom/movable/A)
- return 0
-
-/obj/effect/step_trigger/HasEntered(H as mob|obj)
- ..()
- if(!H)
- return
- if(istype(H, /mob/dead/observer) && !affect_ghosts)
- return
- Trigger(H)
-
-
-
-/* Tosses things in a certain direction */
-
-/obj/effect/step_trigger/thrower
- var/direction = SOUTH // the direction of throw
- var/tiles = 3 // if 0: forever until atom hits a stopper
- var/immobilize = 1 // if nonzero: prevents mobs from moving while they're being flung
- var/speed = 1 // delay of movement
- var/facedir = 0 // if 1: atom faces the direction of movement
- var/nostop = 0 // if 1: will only be stopped by teleporters
- var/list/affecting = list()
-
- Trigger(var/atom/A)
- if(!A || !istype(A, /atom/movable))
- return
- var/atom/movable/AM = A
- var/curtiles = 0
- var/stopthrow = 0
- for(var/obj/effect/step_trigger/thrower/T in orange(2, src))
- if(AM in T.affecting)
- return
-
- if(ismob(AM))
- var/mob/M = AM
- if(immobilize)
- M.canmove = 0
-
- affecting.Add(AM)
- while(AM && !stopthrow)
- if(tiles)
- if(curtiles >= tiles)
- break
- if(AM.z != src.z)
- break
-
- curtiles++
-
- sleep(speed)
-
- // Calculate if we should stop the process
- if(!nostop)
- for(var/obj/effect/step_trigger/T in get_step(AM, direction))
- if(T.stopper && T != src)
- stopthrow = 1
- else
- for(var/obj/effect/step_trigger/teleporter/T in get_step(AM, direction))
- if(T.stopper)
- stopthrow = 1
-
- if(AM)
- var/predir = AM.dir
- step(AM, direction)
- if(!facedir)
- AM.dir = predir
-
-
-
- affecting.Remove(AM)
-
- if(ismob(AM))
- var/mob/M = AM
- if(immobilize)
- M.canmove = 1
-
-/* Stops things thrown by a thrower, doesn't do anything */
-
-/obj/effect/step_trigger/stopper
-
-/* Instant teleporter */
-
-/obj/effect/step_trigger/teleporter
- var/teleport_x = 0 // teleportation coordinates (if one is null, then no teleport!)
- var/teleport_y = 0
- var/teleport_z = 0
-
- Trigger(var/atom/movable/A)
- if(teleport_x && teleport_y && teleport_z)
-
- A.x = teleport_x
- A.y = teleport_y
- A.z = teleport_z
-
-/* Random teleporter, teleports atoms to locations ranging from teleport_x - teleport_x_offset, etc */
-
-/obj/effect/step_trigger/teleporter/random
- var/teleport_x_offset = 0
- var/teleport_y_offset = 0
- var/teleport_z_offset = 0
-
- Trigger(var/atom/movable/A)
- if(teleport_x && teleport_y && teleport_z)
- if(teleport_x_offset && teleport_y_offset && teleport_z_offset)
-
- A.x = rand(teleport_x, teleport_x_offset)
- A.y = rand(teleport_y, teleport_y_offset)
- A.z = rand(teleport_z, teleport_z_offset)
-
+/* Simple object type, calls a proc when "stepped" on by something */
+
+/obj/effect/step_trigger
+ var/affect_ghosts = 0
+ var/stopper = 1 // stops throwers
+ invisibility = 101 // nope cant see this shit
+ anchored = 1
+
+/obj/effect/step_trigger/proc/Trigger(var/atom/movable/A)
+ return 0
+
+/obj/effect/step_trigger/Crossed(H as mob|obj)
+ ..()
+ if(!H)
+ return
+ if(istype(H, /mob/dead/observer) && !affect_ghosts)
+ return
+ Trigger(H)
+
+
+
+/* Tosses things in a certain direction */
+
+/obj/effect/step_trigger/thrower
+ var/direction = SOUTH // the direction of throw
+ var/tiles = 3 // if 0: forever until atom hits a stopper
+ var/immobilize = 1 // if nonzero: prevents mobs from moving while they're being flung
+ var/speed = 1 // delay of movement
+ var/facedir = 0 // if 1: atom faces the direction of movement
+ var/nostop = 0 // if 1: will only be stopped by teleporters
+ var/list/affecting = list()
+
+ Trigger(var/atom/A)
+ if(!A || !istype(A, /atom/movable))
+ return
+ var/atom/movable/AM = A
+ var/curtiles = 0
+ var/stopthrow = 0
+ for(var/obj/effect/step_trigger/thrower/T in orange(2, src))
+ if(AM in T.affecting)
+ return
+
+ if(ismob(AM))
+ var/mob/M = AM
+ if(immobilize)
+ M.canmove = 0
+
+ affecting.Add(AM)
+ while(AM && !stopthrow)
+ if(tiles)
+ if(curtiles >= tiles)
+ break
+ if(AM.z != src.z)
+ break
+
+ curtiles++
+
+ sleep(speed)
+
+ // Calculate if we should stop the process
+ if(!nostop)
+ for(var/obj/effect/step_trigger/T in get_step(AM, direction))
+ if(T.stopper && T != src)
+ stopthrow = 1
+ else
+ for(var/obj/effect/step_trigger/teleporter/T in get_step(AM, direction))
+ if(T.stopper)
+ stopthrow = 1
+
+ if(AM)
+ var/predir = AM.dir
+ step(AM, direction)
+ if(!facedir)
+ AM.dir = predir
+
+
+
+ affecting.Remove(AM)
+
+ if(ismob(AM))
+ var/mob/M = AM
+ if(immobilize)
+ M.canmove = 1
+
+/* Stops things thrown by a thrower, doesn't do anything */
+
+/obj/effect/step_trigger/stopper
+
+/* Instant teleporter */
+
+/obj/effect/step_trigger/teleporter
+ var/teleport_x = 0 // teleportation coordinates (if one is null, then no teleport!)
+ var/teleport_y = 0
+ var/teleport_z = 0
+
+ Trigger(var/atom/movable/A)
+ if(teleport_x && teleport_y && teleport_z)
+
+ A.x = teleport_x
+ A.y = teleport_y
+ A.z = teleport_z
+
+/* Random teleporter, teleports atoms to locations ranging from teleport_x - teleport_x_offset, etc */
+
+/obj/effect/step_trigger/teleporter/random
+ var/teleport_x_offset = 0
+ var/teleport_y_offset = 0
+ var/teleport_z_offset = 0
+
+ Trigger(var/atom/movable/A)
+ if(teleport_x && teleport_y && teleport_z)
+ if(teleport_x_offset && teleport_y_offset && teleport_z_offset)
+
+ A.x = rand(teleport_x, teleport_x_offset)
+ A.y = rand(teleport_y, teleport_y_offset)
+ A.z = rand(teleport_z, teleport_z_offset)
+
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 5a78b702d04..6e1cc86633c 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -49,18 +49,11 @@
var/list/sprite_sheets = null
var/icon_override = null //Used to override hardcoded clothing dmis in human clothing proc.
- /* Species-specific sprite sheets for object and inhand sprites
+ /* Species-specific sprite sheets for inventory sprites
Works similarly to worn sprite_sheets, except the alternate sprites are used when the clothing/refit_for_species() proc is called.
*/
var/list/sprite_sheets_obj = null
- //Inhand is not as big a deal as the object sprites, so I'm not sure if these are worth the extra vars.
- //Maybe in the future:
- //var/list/sprite_sheets_inhand_l = null
- //var/list/sprite_sheets_inhand_r = null
- //var/icon_l_hand = 'icons/mob/items_lefthand.dmi'
- //var/icon_r_hand = 'icons/mob/items_righthand.dmi'
-
/obj/item/device
icon = 'icons/obj/device.dmi'
diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm
index 818326b8065..2bd77901c0b 100755
--- a/code/game/objects/items/devices/PDA/PDA.dm
+++ b/code/game/objects/items/devices/PDA/PDA.dm
@@ -328,7 +328,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
return
-/obj/item/device/pda/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null)
+/obj/item/device/pda/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
ui_tick++
var/datum/nanoui/old_ui = nanomanager.get_open_ui(user, src, "main")
var/auto_update = 1
@@ -440,13 +440,13 @@ var/global/list/obj/item/device/pda/PDAs = list()
var/datum/gas_mixture/environment = T.return_air()
var/pressure = environment.return_pressure()
- var/total_moles = environment.total_moles()
+ var/total_moles = environment.total_moles
if (total_moles)
- var/o2_level = environment.oxygen/total_moles
- var/n2_level = environment.nitrogen/total_moles
- var/co2_level = environment.carbon_dioxide/total_moles
- var/phoron_level = environment.phoron/total_moles
+ var/o2_level = environment.gas["oxygen"]/total_moles
+ var/n2_level = environment.gas["nitrogen"]/total_moles
+ var/co2_level = environment.gas["carbon_dioxide"]/total_moles
+ var/phoron_level = environment.gas["phoron"]/total_moles
var/unknown_level = 1-(o2_level+n2_level+co2_level+phoron_level)
data["aircontents"] = list(\
"pressure" = "[round(pressure,0.1)]",\
@@ -462,7 +462,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
data["aircontents"] = list("reading" = 0)
// update the ui if it exists, returns null if no ui is passed/found
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, data)
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
// the ui does not exist, so we'll create a new() one
// for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
@@ -1162,24 +1162,13 @@ var/global/list/obj/item/device/pda/PDAs = list()
O << "\red [user] has used [src] on \icon[icon] [A]"
var/pressure = A:air_contents.return_pressure()
- var/total_moles = A:air_contents.total_moles()
+ var/total_moles = A:air_contents.total_moles
user << "\blue Results of analysis of \icon[icon]"
if (total_moles>0)
- var/o2_concentration = A:air_contents.oxygen/total_moles
- var/n2_concentration = A:air_contents.nitrogen/total_moles
- var/co2_concentration = A:air_contents.carbon_dioxide/total_moles
- var/phoron_concentration = A:air_contents.phoron/total_moles
-
- var/unknown_concentration = 1-(o2_concentration+n2_concentration+co2_concentration+phoron_concentration)
-
user << "\blue Pressure: [round(pressure,0.1)] kPa"
- user << "\blue Nitrogen: [round(n2_concentration*100)]%"
- user << "\blue Oxygen: [round(o2_concentration*100)]%"
- user << "\blue CO2: [round(co2_concentration*100)]%"
- user << "\blue Phoron: [round(phoron_concentration*100)]%"
- if(unknown_concentration>0.01)
- user << "\red Unknown: [round(unknown_concentration*100)]%"
+ for(var/g in A:air_contents.gas)
+ user << "\blue [gas_data.name[g]]: [round((A:air_contents.gas[g] / total_moles) * 100)]%"
user << "\blue Temperature: [round(A:air_contents.temperature-T0C)]°C"
else
user << "\blue Tank is empty!"
@@ -1191,24 +1180,13 @@ var/global/list/obj/item/device/pda/PDAs = list()
var/obj/machinery/atmospherics/pipe/tank/T = A
var/pressure = T.parent.air.return_pressure()
- var/total_moles = T.parent.air.total_moles()
+ var/total_moles = T.parent.air.total_moles
user << "\blue Results of analysis of \icon[icon]"
if (total_moles>0)
- var/o2_concentration = T.parent.air.oxygen/total_moles
- var/n2_concentration = T.parent.air.nitrogen/total_moles
- var/co2_concentration = T.parent.air.carbon_dioxide/total_moles
- var/phoron_concentration = T.parent.air.phoron/total_moles
-
- var/unknown_concentration = 1-(o2_concentration+n2_concentration+co2_concentration+phoron_concentration)
-
user << "\blue Pressure: [round(pressure,0.1)] kPa"
- user << "\blue Nitrogen: [round(n2_concentration*100)]%"
- user << "\blue Oxygen: [round(o2_concentration*100)]%"
- user << "\blue CO2: [round(co2_concentration*100)]%"
- user << "\blue Phoron: [round(phoron_concentration*100)]%"
- if(unknown_concentration>0.01)
- user << "\red Unknown: [round(unknown_concentration*100)]%"
+ for(var/g in T.parent.air.gas)
+ user << "\blue [gas_data.name[g]]: [round((T.parent.air.gas[g] / total_moles) * 100)]%"
user << "\blue Temperature: [round(T.parent.air.temperature-T0C)]°C"
else
user << "\blue Tank is empty!"
@@ -1278,7 +1256,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
src.id.loc = get_turf(src.loc)
..()
-/obj/item/device/pda/clown/HasEntered(AM as mob|obj) //Clown PDA is slippery.
+/obj/item/device/pda/clown/Crossed(AM as mob|obj) //Clown PDA is slippery.
if (istype(AM, /mob/living/carbon))
var/mob/M = AM
if ((istype(M, /mob/living/carbon/human) && (istype(M:shoes, /obj/item/clothing/shoes) && M:shoes.flags&NOSLIP)) || M.m_intent == "walk")
diff --git a/code/game/objects/items/devices/aicard.dm b/code/game/objects/items/devices/aicard.dm
index 43daf35b323..204b94cc3dd 100644
--- a/code/game/objects/items/devices/aicard.dm
+++ b/code/game/objects/items/devices/aicard.dm
@@ -71,6 +71,10 @@
dat += " "
dat += {"[A.control_disabled ? "Enable" : "Disable"] Wireless Activity"}
dat += " "
+ dat += "Subspace Transceiver is: [A.aiRadio.disabledAi ? "Disabled" : "Enabled"]"
+ dat += " "
+ dat += {"[A.aiRadio.disabledAi ? "Enable" : "Disable"] Subspace Transceiver"}
+ dat += " "
dat += {" Close"}
user << browse(dat, "window=aicard")
onclose(user, "aicard")
@@ -92,6 +96,12 @@
U.unset_machine()
return
+ if ("Radio")
+ for(var/mob/living/silicon/ai/A in src)
+ A.aiRadio.disabledAi = !A.aiRadio.disabledAi
+ A << "Your Subspace Transceiver has been: [A.aiRadio.disabledAi ? "disabled" : "enabled"]"
+ U << "You [A.aiRadio.disabledAi ? "Disable" : "Enable"] the AI's Subspace Transceiver"
+
if ("Wipe")
var/confirm = alert("Are you sure you want to wipe this card's memory? This cannot be undone once started.", "Confirm Wipe", "Yes", "No")
if(confirm == "Yes")
diff --git a/code/game/objects/items/devices/floor_painter.dm b/code/game/objects/items/devices/floor_painter.dm
new file mode 100644
index 00000000000..ebb1111429e
--- /dev/null
+++ b/code/game/objects/items/devices/floor_painter.dm
@@ -0,0 +1,209 @@
+/obj/item/device/floor_painter
+ name = "floor painter"
+ icon = 'icons/obj/bureaucracy.dmi'
+ icon_state = "labeler1"
+ item_state = "flight"
+
+ var/mode_nice = "standard"
+ var/mode = "floor"
+ var/tile_dir_mode = 0
+
+ // mode 0 ignore direction; sets dir=0
+ // mode 1 all-direction
+ // mode 2 corner selecting the side CW from the selected corner
+ // mode 3 cardinal
+ // mode 4 warningcorner and warnwhitecorner direction fix
+ // mode 5 Opposite corner tiles where the second icon_state is "[mode]_inv"
+/obj/item/device/floor_painter/afterattack(atom/A, mob/user as mob, proximity)
+ if(!proximity)
+ return
+
+ if(istype(A, /turf/simulated/floor))
+ var/turf/simulated/floor/F = A
+
+ if(F.is_plasteel_floor()) // only tiled floors
+ if(tile_dir_mode)
+ var/D = get_dir(usr, F)
+ if(usr.loc == F)
+ D = usr.dir
+
+ switch(tile_dir_mode)
+ if(1) // All directions accepted
+ F.dir = D
+ F.icon_state = mode
+ if(2) // Corner mode - diagonal directions converted CW around.
+ switch(D)
+ if(NORTHEAST)
+ D = EAST
+ if(SOUTHEAST)
+ D = SOUTH
+ if(SOUTHWEST)
+ D = WEST
+ if(NORTHWEST)
+ D = NORTH
+ F.dir = D
+ F.icon_state = mode
+ if(3) // cardinal directions only. I've adjusted diagonals the same way the facing code does.
+ switch(D)
+ if(NORTHEAST)
+ D = EAST
+ if(SOUTHEAST)
+ D = EAST
+ if(SOUTHWEST)
+ D = WEST
+ if(NORTHWEST)
+ D = WEST
+ F.dir = D
+ F.icon_state = mode
+ if(4) // floors.dmi icon_states "warningcorner" and "warnwhitecorner" are incorrect, this fixes it
+ var/D2
+ switch(D)
+ if(NORTHEAST)
+ D2 = WEST
+ if(SOUTHEAST)
+ D2 = SOUTH
+ if(SOUTHWEST)
+ D2 = NORTH
+ if(NORTHWEST)
+ D2 = EAST
+ F.dir = D2
+ F.icon_state = mode
+ if(5)
+ F.dir = 0
+ if(D == NORTH || D == SOUTH || D == NORTHEAST || D == SOUTHWEST)
+ F.icon_state = mode
+ else
+ F.icon_state = "[mode]_inv"
+ else
+ F.dir = 0
+ F.icon_state = mode
+ else
+ usr << "You can't paint that!"
+
+/obj/item/device/floor_painter/attack_self(mob/user as mob)
+ var/type = input("What type of floor?", "Floor painter", "solid") in list("solid", "corner", "opposite corners", "side/three corners", "special", "letters")
+
+ tile_dir_mode = 0
+
+ switch(type)
+ if("solid")
+ tile_dir_mode = 0
+ var/design = input("Which color?", "Floor painter") in list("standard", "dark", "red", "blue", "green", "yellow", "purple", "neutral", "white", "white-red", "white-blue", "white-green", "white-yellow", "white-purple", "freezer", "hydro", "showroom")
+ if(design == "standard")
+ mode = "floor"
+ mode_nice = "standard"
+ return
+ if(design == "white")
+ mode = "white"
+ mode_nice = "white"
+ return
+ if(design == "showroom" || design == "hydro" || design == "freezer")
+ mode = "[design]floor"
+ mode_nice = design
+ return
+ mode_nice = design
+ mode = "[replacetext(design, "-", "")]full"
+ if("corner")
+ var/design = input("Which design?", "Floor painter") in list("black", "red", "blue", "green", "yellow", "purple", "neutral", "white", "white-grey", "white-red", "white-blue", "white-green", "white-yellow", "white-purple")
+ mode_nice = "[design] corner"
+ mode = "[replacetext(design, "-", "")]corner"
+ tile_dir_mode = 2
+ if("opposite corners")
+ var/design = input("Which design?", "Floor painter") in list("bar", "cmo", "yellowpatch", "cafeteria", "red-yellow", "red-blue", "red-green", "green-yellow", "green-blue", "blue-yellow")
+ mode_nice = design
+ if(design == "bar" || design == "cmo" || design == "yellowpatch" || design == "cafeteria")
+ mode = design
+ else
+ mode = "[replacetext(design, "-", "")]full"
+ if(design == "yellowpatch")
+ tile_dir_mode = 5
+ else
+ tile_dir_mode = 0
+ if("side/three corners")
+ var/design = input("Which design?", "Floor painter") in list("black", "red", "blue", "green", "yellow", "purple", "neutral", "white", "white-red", "white-blue", "white-green", "white-yellow", "white-purple", "red-yellow", "red-blue", "blue-red", "red-green", "green-yellow", "green-blue", "blue-yellow")
+ if(design == "white")
+ mode = "whitehall"
+ mode_nice = "white side"
+ tile_dir_mode = 1
+ else
+ mode_nice = design
+ mode = replacetext(design, "-", "")
+ tile_dir_mode = 1
+ if("special")
+ var/design = input("Which design?", "Floor painter") in list("arrival", "escape", "caution", "warning", "white-warning", "white-blue-green", "loadingarea", "delivery", "bot", "white-delivery", "white-bot")
+ if(design == "white-blue-green")
+ mode_nice = design
+ mode = "whitebluegreencorners"
+ tile_dir_mode = 2
+ else if(design == "delivery" || design == "bot")
+ mode_nice = design
+ mode = design
+ tile_dir_mode = 0
+ else if(design == "loadingarea")
+ mode_nice = design
+ mode = design
+ tile_dir_mode = 3
+ else
+ if(design == "white-warning")
+ mode_nice = design
+ design = "warnwhite"
+ var/s_corner = alert("Do you want to paint a single corner of the tile?", "Floor painter","Yes","No") == "Yes"
+ if(s_corner)
+ mode_nice = "[design] corner"
+ mode = "[design]corner"
+ if(design == "warning" || design == "white-warning") // sprites for these are weird, need to fix dirs (icons/turf/floors.dmi, "warningcorner" and "warnwhitecorner")
+ tile_dir_mode = 4
+ else
+ tile_dir_mode = 2
+ else
+ mode_nice = design
+ mode = design
+ tile_dir_mode = 1
+ if("letters")
+ var/which = input("Which letters/design?", "Floor painter") in list("A1", "A2", "DI", "SA", "SA (red)", "SB", "SB (red)", "SC", "SC (red)", "W (red)", "V (green)", "Psy", "Ex", "Ex (blue)", "CMO", "O (OP)", "P (OP)")
+ mode_nice = which
+ switch(which)
+ if("A1")
+ mode = "white_1"
+ if("A2")
+ mode = "white_2"
+ if("DI")
+ mode = "white_d"
+ if("SA")
+ mode = "white_a"
+ if("SA (red)")
+ mode = "whitered_a"
+ tile_dir_mode = 3
+ if("SB")
+ mode = "white_b"
+ if("SB (red)")
+ mode = "whitered_b"
+ tile_dir_mode = 3
+ if("SC")
+ mode = "white_c"
+ if("SC (red)")
+ mode = "whitered_c"
+ tile_dir_mode = 3
+ if("W (red)")
+ mode = "whitered_w"
+ tile_dir_mode = 3
+ if("V (green)")
+ mode = "whitegreen_v"
+ tile_dir_mode = 3
+ if("Psy")
+ mode = "white_p"
+ if("Ex")
+ mode = "white_ex"
+ if("Ex (blue)")
+ mode = "whiteblue_ex"
+ tile_dir_mode = 3
+ if("CMO") // yes this is also in "opposite corners" choices, but it's a different icon_state (!!)
+ mode = "white_cmo"
+ if("O (OP)")
+ mode = "white_halfo"
+ if("P (OP)")
+ mode = "white_halfp"
+
+/obj/item/device/floor_painter/examine()
+ ..()
+ usr << "It is in [mode_nice] mode."
\ No newline at end of file
diff --git a/code/game/objects/items/devices/paicard.dm b/code/game/objects/items/devices/paicard.dm
index 274f8f7ed7e..42651ea7200 100644
--- a/code/game/objects/items/devices/paicard.dm
+++ b/code/game/objects/items/devices/paicard.dm
@@ -197,7 +197,7 @@
pAI Request Module
Requesting AI personalities from central database... If there are no entries, or if a suitable entry is not listed, check again later as more personalities may be added.
Searching for personalities
-
+
@@ -302,5 +302,4 @@
/obj/item/device/paicard/emp_act(severity)
for(var/mob/M in src)
M.emp_act(severity)
- ..()
-
+ ..()
\ 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 5d900fb3e72..130770aa9d6 100644
--- a/code/game/objects/items/devices/pipe_painter.dm
+++ b/code/game/objects/items/devices/pipe_painter.dm
@@ -3,8 +3,15 @@
icon = 'icons/obj/bureaucracy.dmi'
icon_state = "labeler1"
item_state = "flight"
- var/list/modes = list("grey","red","blue","cyan","green","yellow","purple")
- var/mode = "grey"
+ var/list/modes
+ var/mode
+
+/obj/item/device/pipe_painter/New()
+ ..()
+ modes = new()
+ for(var/C in pipe_colors)
+ modes += "[C]"
+ mode = pick(modes)
/obj/item/device/pipe_painter/afterattack(atom/A, mob/user as mob, proximity)
if(!proximity)
@@ -19,12 +26,10 @@
user << "\red You must remove the plating first."
return
- P.pipe_color = mode
- user.visible_message("[user] paints \the [P] [mode].","You paint \the [P] [mode].")
- P.update_icon()
+ P.change_color(pipe_colors[mode])
/obj/item/device/pipe_painter/attack_self(mob/user as mob)
- mode = input("Which colour do you want to use?","Pipe painter") in modes
+ mode = input("Which colour do you want to use?", "Pipe painter", mode) in modes
/obj/item/device/pipe_painter/examine()
..()
diff --git a/code/game/objects/items/devices/radio/encryptionkey.dm b/code/game/objects/items/devices/radio/encryptionkey.dm
index d2d5417d6c3..287e8272497 100644
--- a/code/game/objects/items/devices/radio/encryptionkey.dm
+++ b/code/game/objects/items/devices/radio/encryptionkey.dm
@@ -74,6 +74,12 @@
icon_state = "cap_cypherkey"
channels = list("Command" = 1, "Security" = 1, "Engineering" = 0, "Science" = 0, "Medical" = 0, "Supply" = 0)
+/obj/item/device/encryptionkey/heads/ai_integrated
+ name = "AI Integrated Encryption Key"
+ desc = "Integrated encryption key"
+ icon_state = "cap_cypherkey"
+ channels = list("Command" = 1, "Security" = 1, "Engineering" = 1, "Science" = 1, "Medical" = 1, "Supply" = 1)
+
/obj/item/device/encryptionkey/heads/rd
name = "Research Director's Encryption Key"
desc = "An encyption key for a radio headset. Contains cypherkeys."
diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm
index ed59e439895..e7b70432668 100644
--- a/code/game/objects/items/devices/radio/headset.dm
+++ b/code/game/objects/items/devices/radio/headset.dm
@@ -19,7 +19,9 @@
keyslot1 = new /obj/item/device/encryptionkey/
recalculateChannels()
-/obj/item/device/radio/headset/receive_range(freq, level)
+/obj/item/device/radio/headset/receive_range(freq, level, aiOverride = 0)
+ if (aiOverride)
+ return ..(freq, level)
if(ishuman(src.loc))
var/mob/living/carbon/human/H = src.loc
if(H.l_ear == src || H.r_ear == src)
@@ -99,6 +101,21 @@
item_state = "headset"
keyslot2 = new /obj/item/device/encryptionkey/heads/captain
+/obj/item/device/radio/headset/heads/ai_integrated //No need to care about icons, it should be hidden inside the AI anyway.
+ name = "AI Subspace Transceiver"
+ desc = "Integrated AI radio transceiver."
+ icon = 'icons/obj/robot_component.dmi'
+ icon_state = "radio"
+ item_state = "headset"
+ keyslot2 = new /obj/item/device/encryptionkey/heads/ai_integrated
+ var/myAi = null // Atlantis: Reference back to the AI which has this radio.
+ var/disabledAi = 0 // Atlantis: Used to manually disable AI's integrated radio via intellicard menu.
+
+/obj/item/device/radio/headset/heads/ai_integrated/receive_range(freq, level)
+ if (disabledAi)
+ return -1 //Transciever Disabled.
+ return ..(freq, level, 1)
+
/obj/item/device/radio/headset/heads/rd
name = "Research Director's headset"
desc = "Headset of the researching God. To access the science channel, use :n. For command, use :c."
diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm
index a15356e9c9b..3a064ee552d 100644
--- a/code/game/objects/items/devices/radio/radio.dm
+++ b/code/game/objects/items/devices/radio/radio.dm
@@ -703,6 +703,8 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use
/obj/item/device/radio/borg
var/obj/item/device/encryptionkey/keyslot = null//Borg radios can handle a single encryption key
+ icon = 'icons/obj/robot_component.dmi' // Cyborgs radio icons should look like the component.
+ icon_state = "radio"
/obj/item/device/radio/borg/attackby(obj/item/weapon/W as obj, mob/user as mob)
// ..()
diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm
index ffffe00490c..8ef724fcde0 100644
--- a/code/game/objects/items/devices/scanners.dm
+++ b/code/game/objects/items/devices/scanners.dm
@@ -48,11 +48,13 @@ REAGENT SCANNER
if(O.invisibility == 101)
O.invisibility = 0
+ O.alpha = 128
spawn(10)
if(O)
var/turf/U = O.loc
if(U.intact)
O.invisibility = 101
+ O.alpha = 255
var/mob/living/M = locate() in T
if(M && M.invisibility == 2)
@@ -255,7 +257,7 @@ REAGENT SCANNER
var/datum/gas_mixture/environment = location.return_air()
var/pressure = environment.return_pressure()
- var/total_moles = environment.total_moles()
+ var/total_moles = environment.total_moles
user.show_message("\blue Results:", 1)
if(abs(pressure - ONE_ATMOSPHERE) < 10)
@@ -263,32 +265,8 @@ REAGENT SCANNER
else
user.show_message("\red Pressure: [round(pressure,0.1)] kPa", 1)
if(total_moles)
- var/o2_concentration = environment.oxygen/total_moles
- var/n2_concentration = environment.nitrogen/total_moles
- var/co2_concentration = environment.carbon_dioxide/total_moles
- var/phoron_concentration = environment.phoron/total_moles
-
- var/unknown_concentration = 1-(o2_concentration+n2_concentration+co2_concentration+phoron_concentration)
- if(abs(n2_concentration - N2STANDARD) < 20)
- user.show_message("\blue Nitrogen: [round(n2_concentration*100)]%", 1)
- else
- user.show_message("\red Nitrogen: [round(n2_concentration*100)]%", 1)
-
- if(abs(o2_concentration - O2STANDARD) < 2)
- user.show_message("\blue Oxygen: [round(o2_concentration*100)]%", 1)
- else
- user.show_message("\red Oxygen: [round(o2_concentration*100)]%", 1)
-
- if(co2_concentration > 0.01)
- user.show_message("\red CO2: [round(co2_concentration*100)]%", 1)
- else
- user.show_message("\blue CO2: [round(co2_concentration*100)]%", 1)
-
- if(phoron_concentration > 0.01)
- user.show_message("\red Phoron: [round(phoron_concentration*100)]%", 1)
-
- if(unknown_concentration > 0.01)
- user.show_message("\red Unknown: [round(unknown_concentration*100)]%", 1)
+ for(var/g in environment.gas)
+ user.show_message("\blue [gas_data.name[g]]: [round((environment.gas[g] / total_moles)*100)]%", 1)
user.show_message("\blue Temperature: [round(environment.temperature-T0C)]°C", 1)
@@ -427,4 +405,47 @@ REAGENT SCANNER
name = "advanced reagent scanner"
icon_state = "adv_spectrometer"
details = 1
- origin_tech = "magnets=4;biotech=2"
\ No newline at end of file
+ origin_tech = "magnets=4;biotech=2"
+
+/obj/item/device/slime_scanner
+ name = "slime scanner"
+ icon_state = "adv_spectrometer"
+ item_state = "analyzer"
+ origin_tech = "biotech=1"
+ w_class = 2.0
+ flags = CONDUCT
+ throwforce = 0
+ throw_speed = 3
+ throw_range = 7
+ matter = list("metal" = 30,"glass" = 20)
+
+/obj/item/device/slime_scanner/attack(mob/living/M as mob, mob/living/user as mob)
+ if (!isslime(M))
+ user << "This device can only scan slimes!"
+ return
+ var/mob/living/carbon/slime/T = M
+ user.show_message("Slime scan results:")
+ user.show_message(text("[T.colour] [] slime", T.is_adult ? "adult" : "baby"))
+ user.show_message(text("Nutrition: [T.nutrition]/[]", T.get_max_nutrition()))
+ if (T.nutrition < T.get_starve_nutrition())
+ user.show_message("Warning: slime is starving!")
+ else if (T.nutrition < T.get_hunger_nutrition())
+ user.show_message("Warning: slime is hungry")
+ user.show_message("Electric change strength: [T.powerlevel]")
+ user.show_message("Health: [T.health]")
+ if (T.slime_mutation[4] == T.colour)
+ user.show_message("This slime does not evolve any further")
+ else
+ if (T.slime_mutation[3] == T.slime_mutation[4])
+ if (T.slime_mutation[2] == T.slime_mutation[1])
+ user.show_message(text("Possible mutation: []", T.slime_mutation[3]))
+ user.show_message("Genetic destability: [T.mutation_chance/2]% chance of mutation on splitting")
+ else
+ user.show_message(text("Possible mutations: [], [], [] (x2)", T.slime_mutation[1], T.slime_mutation[2], T.slime_mutation[3]))
+ user.show_message("Genetic destability: [T.mutation_chance]% chance of mutation on splitting")
+ else
+ user.show_message(text("Possible mutations: [], [], [], []", T.slime_mutation[1], T.slime_mutation[2], T.slime_mutation[3], T.slime_mutation[4]))
+ user.show_message("Genetic destability: [T.mutation_chance]% chance of mutation on splitting")
+ if (T.cores > 1)
+ user.show_message("Anomalious slime core amount detected")
+ user.show_message("Growth progress: [T.amount_grown]/10")
diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm
index ea2300f2999..8cace9a2235 100644
--- a/code/game/objects/items/devices/transfer_valve.dm
+++ b/code/game/objects/items/devices/transfer_valve.dm
@@ -70,7 +70,7 @@
/obj/item/device/transfer_valve/attack_self(mob/user as mob)
ui_interact(user)
-/obj/item/device/transfer_valve/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null)
+/obj/item/device/transfer_valve/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
// this is the data which will be sent to the ui
var/data[0]
@@ -80,7 +80,7 @@
data["valveOpen"] = valve_open ? 1 : 0
// update the ui if it exists, returns null if no ui is passed/found
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, data)
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
// the ui does not exist, so we'll create a new() one
// for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
diff --git a/code/game/objects/items/devices/uplinks.dm b/code/game/objects/items/devices/uplinks.dm
index 41ae746739c..aa01d0454de 100644
--- a/code/game/objects/items/devices/uplinks.dm
+++ b/code/game/objects/items/devices/uplinks.dm
@@ -340,7 +340,7 @@ A list of items and costs is stored under the datum of every game mode, alongsid
/*
NANO UI FOR UPLINK WOOP WOOP
*/
-/obj/item/device/uplink/hidden/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null)
+/obj/item/device/uplink/hidden/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
var/title = "Syndicate Uplink"
var/data[0]
@@ -349,7 +349,7 @@ A list of items and costs is stored under the datum of every game mode, alongsid
data["welcome"] = welcome
// update the ui if it exists, returns null if no ui is passed/found
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, data)
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
// the ui does not exist, so we'll create a new() one
// for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
diff --git a/code/game/objects/items/stacks/sheets/glass.dm b/code/game/objects/items/stacks/sheets/glass.dm
index 622390c363c..4e9eda79d79 100644
--- a/code/game/objects/items/stacks/sheets/glass.dm
+++ b/code/game/objects/items/stacks/sheets/glass.dm
@@ -237,6 +237,7 @@
return 0
+
/*
* Phoron Glass sheets
*/
@@ -246,7 +247,7 @@
singular_name = "phoron glass sheet"
icon_state = "sheet-phoronglass"
matter = list("glass" = 7500)
- origin_tech = "materials=3;phoron=2"
+ origin_tech = "materials=3;phorontech=2"
created_window = /obj/structure/window/phoronbasic
/obj/item/stack/sheet/glass/phoronglass/attack_self(mob/user as mob)
@@ -279,7 +280,7 @@
icon_state = "sheet-phoronrglass"
matter = list("glass" = 7500,"metal" = 1875)
- origin_tech = "materials=4;phoron=2"
+ origin_tech = "materials=4;phorontech=2"
created_window = /obj/structure/window/phoronreinforced
/obj/item/stack/sheet/glass/phoronrglass/attack_self(mob/user as mob)
diff --git a/code/game/objects/items/stacks/sheets/mineral.dm b/code/game/objects/items/stacks/sheets/mineral.dm
index e853976cc0f..e6e3cff050a 100644
--- a/code/game/objects/items/stacks/sheets/mineral.dm
+++ b/code/game/objects/items/stacks/sheets/mineral.dm
@@ -15,7 +15,7 @@ Mineral Sheets
*/
var/global/list/datum/stack_recipe/sandstone_recipes = list ( \
- new/datum/stack_recipe("pile of dirt", /obj/machinery/hydroponics/soil, 3, time = 10, one_per_turf = 1, on_floor = 1), \
+ new/datum/stack_recipe("pile of dirt", /obj/machinery/portable_atmospherics/hydroponics/soil, 3, time = 10, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("sandstone door", /obj/structure/mineral_door/sandstone, 10, one_per_turf = 1, on_floor = 1), \
)
diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm
index c89b115071d..28fa0f46dbf 100644
--- a/code/game/objects/items/toys.dm
+++ b/code/game/objects/items/toys.dm
@@ -406,7 +406,7 @@
playsound(src, 'sound/effects/snap.ogg', 50, 1)
del(src)
-/obj/item/toy/snappop/HasEntered(H as mob|obj)
+/obj/item/toy/snappop/Crossed(H as mob|obj)
if((ishuman(H))) //i guess carp and shit shouldn't set them off
var/mob/living/carbon/M = H
if(M.m_intent == "run")
diff --git a/code/game/objects/items/weapons/RSF.dm b/code/game/objects/items/weapons/RSF.dm
index 9d86411b1a5..d6a707237c3 100644
--- a/code/game/objects/items/weapons/RSF.dm
+++ b/code/game/objects/items/weapons/RSF.dm
@@ -4,7 +4,6 @@ RSF
*/
-//TODO: WHAT THE FUCK, FIX THIS GARBAGE.
/obj/item/weapon/rsf
name = "\improper Rapid-Service-Fabricator"
desc = "A device used to rapidly deploy service items."
@@ -13,26 +12,29 @@ RSF
opacity = 0
density = 0
anchored = 0.0
- var/stored_matter = 0
+ var/stored_matter = 30
var/mode = 1
flags = TABLEPASS
w_class = 3.0
-/obj/item/weapon/rsf/New()
- desc = "A RSF. It currently holds [stored_matter]/30 fabrication-units."
- return
+/obj/item/weapon/rsf/examine()
+ set src in view(1)
+ ..()
+ usr << "It currently holds [stored_matter]/30 fabrication-units."
/obj/item/weapon/rsf/attackby(obj/item/weapon/W as obj, mob/user as mob)
..()
if (istype(W, /obj/item/weapon/rcd_ammo))
+
if ((stored_matter + 10) > 30)
- user << "The RSF cant hold any more matter."
+ user << "The RSF can't hold any more matter."
return
+
del(W)
+
stored_matter += 10
playsound(src.loc, 'sound/machines/click.ogg', 10, 1)
user << "The RSF now holds [stored_matter]/30 fabrication-units."
- desc = "A RSF. It currently holds [stored_matter]/30 fabrication-units."
return
/obj/item/weapon/rsf/attack_self(mob/user as mob)
@@ -64,174 +66,51 @@ RSF
// Change mode
/obj/item/weapon/rsf/afterattack(atom/A, mob/user as mob, proximity)
+
if(!proximity) return
- if (!(istype(A, /obj/structure/table) || istype(A, /turf/simulated/floor)))
+
+ if(istype(user,/mob/living/silicon/robot))
+ var/mob/living/silicon/robot/R = user
+ if(R.stat || !R.cell || R.cell.charge <= 0)
+ return
+ else
+ if(stored_matter <= 0)
+ return
+
+ if(!istype(A, /obj/structure/table) && !istype(A, /turf/simulated/floor))
return
- if (istype(A, /obj/structure/table) && mode == 1)
- if (istype(A, /obj/structure/table) && stored_matter >= 1)
- user << "Dispensing Dosh..."
- playsound(src.loc, 'sound/machines/click.ogg', 10, 1)
- new /obj/item/weapon/spacecash/c10( A.loc )
- if (isrobot(user))
- var/mob/living/silicon/robot/engy = user
- engy.cell.charge -= 200 //once money becomes useful, I guess changing this to a high ammount, like 500 units a kick, till then, enjoy dosh!
- else
- stored_matter--
- user << "The RSF now holds [stored_matter]/30 fabrication-units."
- desc = "A RSF. It currently holds [stored_matter]/30 fabrication-units."
- return
+ playsound(src.loc, 'sound/machines/click.ogg', 10, 1)
+ var/used_energy = 0
+ var/obj/product
- else if (istype(A, /turf/simulated/floor) && mode == 1)
- if (istype(A, /turf/simulated/floor) && stored_matter >= 1)
- user << "Dispensing Dosh..."
- playsound(src.loc, 'sound/machines/click.ogg', 10, 1)
- new /obj/item/weapon/spacecash/c10( A )
- if (isrobot(user))
- var/mob/living/silicon/robot/engy = user
- engy.cell.charge -= 200 //once money becomes useful, I guess changing this to a high ammount, like 500 units a kick, till then, enjoy dosh!
- else
- stored_matter--
- user << "The RSF now holds [stored_matter]/30 fabrication-units."
- desc = "A RSF. It currently holds [stored_matter]/30 fabrication-units."
- return
+ switch(mode)
+ if(1)
+ product = new /obj/item/weapon/spacecash/c10()
+ used_energy = 200
+ if(2)
+ product = new /obj/item/weapon/reagent_containers/food/drinks/drinkingglass()
+ used_energy = 50
+ if(3)
+ product = new /obj/item/weapon/paper()
+ used_energy = 10
+ if(4)
+ product = new /obj/item/weapon/pen()
+ used_energy = 50
+ if(5)
+ product = new /obj/item/weapon/storage/pill_bottle/dice()
+ used_energy = 200
+ if(6)
+ product = new /obj/item/clothing/mask/cigarette()
+ used_energy = 10
- else if (istype(A, /obj/structure/table) && mode == 2)
- if (istype(A, /obj/structure/table) && stored_matter >= 1)
- user << "Dispensing Drinking Glass..."
- playsound(src.loc, 'sound/machines/click.ogg', 10, 1)
- new /obj/item/weapon/reagent_containers/food/drinks/drinkingglass( A.loc )
- if (isrobot(user))
- var/mob/living/silicon/robot/engy = user
- engy.cell.charge -= 50
- else
- stored_matter--
- user << "The RSF now holds [stored_matter]/30 fabrication-units."
- desc = "A RSF. It currently holds [stored_matter]/30 fabrication-units."
- return
+ user << "Dispensing [product ? product : "product"]..."
+ product.loc = get_turf(A)
- else if (istype(A, /turf/simulated/floor) && mode == 2)
- if (istype(A, /turf/simulated/floor) && stored_matter >= 1)
- user << "Dispensing Drinking Glass..."
- playsound(src.loc, 'sound/machines/click.ogg', 10, 1)
- new /obj/item/weapon/reagent_containers/food/drinks/drinkingglass( A )
- if (isrobot(user))
- var/mob/living/silicon/robot/engy = user
- engy.cell.charge -= 50
- else
- stored_matter--
- user << "The RSF now holds [stored_matter]/30 fabrication-units."
- desc = "A RSF. It currently holds [stored_matter]/30 fabrication-units."
- return
-
- else if (istype(A, /obj/structure/table) && mode == 3)
- if (istype(A, /obj/structure/table) && stored_matter >= 1)
- user << "Dispensing Paper Sheet..."
- playsound(src.loc, 'sound/machines/click.ogg', 10, 1)
- new /obj/item/weapon/paper( A.loc )
- if (isrobot(user))
- var/mob/living/silicon/robot/engy = user
- engy.cell.charge -= 10
- else
- stored_matter--
- user << "The RSF now holds [stored_matter]/30 fabrication-units."
- desc = "A RSF. It currently holds [stored_matter]/30 fabrication-units."
- return
-
- else if (istype(A, /turf/simulated/floor) && mode == 3)
- if (istype(A, /turf/simulated/floor) && stored_matter >= 1)
- user << "Dispensing Paper Sheet..."
- playsound(src.loc, 'sound/machines/click.ogg', 10, 1)
- new /obj/item/weapon/paper( A )
- if (isrobot(user))
- var/mob/living/silicon/robot/engy = user
- engy.cell.charge -= 10
- else
- stored_matter--
- user << "The RSF now holds [stored_matter]/30 fabrication-units."
- desc = "A RSF. It currently holds [stored_matter]/30 fabrication-units."
- return
-
- else if (istype(A, /obj/structure/table) && mode == 4)
- if (istype(A, /obj/structure/table) && stored_matter >= 1)
- user << "Dispensing Pen..."
- playsound(src.loc, 'sound/machines/click.ogg', 10, 1)
- new /obj/item/weapon/pen( A.loc )
- if (isrobot(user))
- var/mob/living/silicon/robot/engy = user
- engy.cell.charge -= 50
- else
- stored_matter--
- user << "The RSF now holds [stored_matter]/30 fabrication-units."
- desc = "A RSF. It currently holds [stored_matter]/30 fabrication-units."
- return
-
- else if (istype(A, /turf/simulated/floor) && mode == 4)
- if (istype(A, /turf/simulated/floor) && stored_matter >= 1)
- user << "Dispensing Pen..."
- playsound(src.loc, 'sound/machines/click.ogg', 10, 1)
- new /obj/item/weapon/pen( A )
- if (isrobot(user))
- var/mob/living/silicon/robot/engy = user
- engy.cell.charge -= 50
- else
- stored_matter--
- user << "The RSF now holds [stored_matter]/30 fabrication-units."
- desc = "A RSF. It currently holds [stored_matter]/30 fabrication-units."
- return
-
- else if (istype(A, /obj/structure/table) && mode == 5)
- if (istype(A, /obj/structure/table) && stored_matter >= 1)
- user << "Dispensing Dice Pack..."
- playsound(src.loc, 'sound/machines/click.ogg', 10, 1)
- new /obj/item/weapon/storage/pill_bottle/dice( A.loc )
- if (isrobot(user))
- var/mob/living/silicon/robot/engy = user
- engy.cell.charge -= 200
- else
- stored_matter--
- user << "The RSF now holds [stored_matter]/30 fabrication-units."
- desc = "A RSF. It currently holds [stored_matter]/30 fabrication-units."
- return
-
- else if (istype(A, /turf/simulated/floor) && mode == 5)
- if (istype(A, /turf/simulated/floor) && stored_matter >= 1)
- user << "Dispensing Dice Pack..."
- playsound(src.loc, 'sound/machines/click.ogg', 10, 1)
- new /obj/item/weapon/storage/pill_bottle/dice( A )
- if (isrobot(user))
- var/mob/living/silicon/robot/engy = user
- engy.cell.charge -= 200
- else
- stored_matter--
- user << "The RSF now holds [stored_matter]/30 fabrication-units."
- desc = "A RSF. It currently holds [stored_matter]/30 fabrication-units."
- return
-
- else if (istype(A, /obj/structure/table) && mode == 6)
- if (istype(A, /obj/structure/table) && stored_matter >= 1)
- user << "Dispensing Cigarette..."
- playsound(src.loc, 'sound/machines/click.ogg', 10, 1)
- new /obj/item/clothing/mask/cigarette( A.loc )
- if (isrobot(user))
- var/mob/living/silicon/robot/engy = user
- engy.cell.charge -= 10
- else
- stored_matter--
- user << "The RSF now holds [stored_matter]/30 fabrication-units."
- desc = "A RSF. It currently holds [stored_matter]/30 fabrication-units."
- return
-
- else if (istype(A, /turf/simulated/floor) && mode == 6)
- if (istype(A, /turf/simulated/floor) && stored_matter >= 1)
- user << "Dispensing Cigarette..."
- playsound(src.loc, 'sound/machines/click.ogg', 10, 1)
- new /obj/item/clothing/mask/cigarette( A )
- if (isrobot(user))
- var/mob/living/silicon/robot/engy = user
- engy.cell.charge -= 10
- else
- stored_matter--
- user << "The RSF now holds [stored_matter]/30 fabrication-units."
- desc = "A RSF. It currently holds [stored_matter]/30 fabrication-units."
- return
\ No newline at end of file
+ if(isrobot(user))
+ var/mob/living/silicon/robot/R = user
+ if(R.cell)
+ R.cell.use(used_energy)
+ else
+ stored_matter--
+ user << "The RSF now holds [stored_matter]/30 fabrication-units."
\ No newline at end of file
diff --git a/code/game/objects/items/weapons/cigs_lighters.dm b/code/game/objects/items/weapons/cigs_lighters.dm
index 3a9ce1bac8a..4d09a26bcfc 100644
--- a/code/game/objects/items/weapons/cigs_lighters.dm
+++ b/code/game/objects/items/weapons/cigs_lighters.dm
@@ -74,6 +74,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
var/lastHolder = null
var/smoketime = 300
var/chem_volume = 15
+ body_parts_covered = 0
/obj/item/clothing/mask/cigarette/New()
..()
diff --git a/code/game/objects/items/weapons/clown_items.dm b/code/game/objects/items/weapons/clown_items.dm
index e526cf1c351..3ae5094dd03 100644
--- a/code/game/objects/items/weapons/clown_items.dm
+++ b/code/game/objects/items/weapons/clown_items.dm
@@ -8,7 +8,7 @@
/*
* Banana Peals
*/
-/obj/item/weapon/bananapeel/HasEntered(AM as mob|obj)
+/obj/item/weapon/bananapeel/Crossed(AM as mob|obj)
if (istype(AM, /mob/living/carbon))
var/mob/M = AM
if (istype(M, /mob/living/carbon/human) && (isobj(M:shoes) && M:shoes.flags&NOSLIP) || M.buckled)
@@ -23,7 +23,7 @@
/*
* Soap
*/
-/obj/item/weapon/soap/HasEntered(AM as mob|obj) //EXACTLY the same as bananapeel for now, so it makes sense to put it in the same dm -- Urist
+/obj/item/weapon/soap/Crossed(AM as mob|obj) //EXACTLY the same as bananapeel for now, so it makes sense to put it in the same dm -- Urist
if (istype(AM, /mob/living/carbon))
var/mob/M = AM
if (istype(M, /mob/living/carbon/human) && (isobj(M:shoes) && M:shoes.flags&NOSLIP) || M.buckled)
diff --git a/code/game/objects/items/weapons/flamethrower.dm b/code/game/objects/items/weapons/flamethrower.dm
index 1d107719581..e16b9dd38c1 100644
--- a/code/game/objects/items/weapons/flamethrower.dm
+++ b/code/game/objects/items/weapons/flamethrower.dm
@@ -116,24 +116,13 @@
var/obj/item/weapon/icon = src
user.visible_message("[user] has used the analyzer on \icon[icon]")
var/pressure = ptank.air_contents.return_pressure()
- var/total_moles = ptank.air_contents.total_moles()
+ var/total_moles = ptank.air_contents.total_moles
user << "\blue Results of analysis of \icon[icon]"
if(total_moles>0)
- var/o2_concentration = ptank.air_contents.oxygen/total_moles
- var/n2_concentration = ptank.air_contents.nitrogen/total_moles
- var/co2_concentration = ptank.air_contents.carbon_dioxide/total_moles
- var/phoron_concentration = ptank.air_contents.phoron/total_moles
-
- var/unknown_concentration = 1-(o2_concentration+n2_concentration+co2_concentration+phoron_concentration)
-
user << "\blue Pressure: [round(pressure,0.1)] kPa"
- user << "\blue Nitrogen: [round(n2_concentration*100)]%"
- user << "\blue Oxygen: [round(o2_concentration*100)]%"
- user << "\blue CO2: [round(co2_concentration*100)]%"
- user << "\blue Phoron: [round(phoron_concentration*100)]%"
- if(unknown_concentration>0.01)
- user << "\red Unknown: [round(unknown_concentration*100)]%"
+ for(var/g in ptank.air_contents.gas)
+ user << "\blue [gas_data.name[g]]: [round((ptank.air_contents.gas[g] / total_moles) * 100)]%"
user << "\blue Temperature: [round(ptank.air_contents.temperature-T0C)]°C"
else
user << "\blue Tank is empty!"
@@ -163,7 +152,7 @@
usr.set_machine(src)
if(href_list["light"])
if(!ptank) return
- if(ptank.air_contents.phoron < 1) return
+ if(ptank.air_contents.gas["phoron"] < 1) return
if(!status) return
lit = !lit
if(lit)
@@ -212,8 +201,8 @@
//Transfer 5% of current tank air contents to turf
var/datum/gas_mixture/air_transfer = ptank.air_contents.remove_ratio(0.02*(throw_amount/100))
//air_transfer.toxins = air_transfer.toxins * 5 // This is me not comprehending the air system. I realize this is retarded and I could probably make it work without fucking it up like this, but there you have it. -- TLE
- new/obj/effect/decal/cleanable/liquid_fuel/flamethrower_fuel(target,air_transfer.phoron,get_dir(loc,target))
- air_transfer.phoron = 0
+ new/obj/effect/decal/cleanable/liquid_fuel/flamethrower_fuel(target,air_transfer.gas["phoron"],get_dir(loc,target))
+ air_transfer.gas["phoron"] = 0
target.assume_air(air_transfer)
//Burn it based on transfered gas
//target.hotspot_expose(part4.air_contents.temperature*2,300)
diff --git a/code/game/objects/items/weapons/handcuffs.dm b/code/game/objects/items/weapons/handcuffs.dm
index 7424c666bec..e7e2ed1a962 100644
--- a/code/game/objects/items/weapons/handcuffs.dm
+++ b/code/game/objects/items/weapons/handcuffs.dm
@@ -14,83 +14,63 @@
origin_tech = "materials=1"
var/dispenser = 0
var/breakouttime = 1200 //Deciseconds = 120s = 2 minutes
+ var/cuff_sound = 'sound/weapons/handcuffs.ogg'
/obj/item/weapon/handcuffs/attack(mob/living/carbon/C as mob, mob/user as mob)
- if(istype(src, /obj/item/weapon/handcuffs/cyborg) && isrobot(user))
- if(!C.handcuffed)
- var/turf/p_loc = user.loc
- var/turf/p_loc_m = C.loc
- playsound(src.loc, 'sound/weapons/handcuffs.ogg', 30, 1, -2)
- for(var/mob/O in viewers(user, null))
- O.show_message("\red [user] is trying to put handcuffs on [C]!", 1)
- spawn(30)
- if(!C) return
- if(p_loc == user.loc && p_loc_m == C.loc)
- C.handcuffed = new /obj/item/weapon/handcuffs(C)
- C.update_inv_handcuffed()
-
- else
- if ((CLUMSY in usr.mutations) && prob(50))
- usr << "\red Uh ... how do those things work?!"
- if (istype(C, /mob/living/carbon/human))
- if(!C.handcuffed)
- var/obj/effect/equip_e/human/O = new /obj/effect/equip_e/human( )
- O.source = user
- O.target = user
- O.item = user.get_active_hand()
- O.s_loc = user.loc
- O.t_loc = user.loc
- O.place = "handcuff"
- C.requests += O
- spawn( 0 )
- O.process()
+ if (!istype(user, /mob/living/carbon/human))
+ user << "\red You don't have the dexterity to do this!"
+ return
+ if ((CLUMSY in usr.mutations) && prob(50))
+ user << "\red Uh ... how do those things work?!"
+ place_handcuffs(user, user)
+ return
+ if(!C.handcuffed)
+ if (C == user)
+ place_handcuffs(user, user)
+ return
+
+ //check for an aggressive grab
+ for (var/obj/item/weapon/grab/G in C.grabbed_by)
+ if (G.loc == user && G.state >= GRAB_AGGRESSIVE)
+ place_handcuffs(C, user)
return
- return
- if (!(istype(usr, /mob/living/carbon/human) || ticker) && ticker.mode.name != "monkey")
- usr << "\red You don't have the dexterity to do this!"
- return
- if (istype(C, /mob/living/carbon/human))
- if(!C.handcuffed)
+ user << "\red You need to have a firm grip on [C] before you can put \the [src] on!"
- C.attack_log += text("\[[time_stamp()]\] Has been handcuffed (attempt) by [user.name] ([user.ckey])")
- user.attack_log += text("\[[time_stamp()]\] Attempted to handcuff [C.name] ([C.ckey])")
- msg_admin_attack("[key_name(user)] attempted to handcuff [key_name(C)]")
+/obj/item/weapon/handcuffs/proc/place_handcuffs(var/mob/living/carbon/target, var/mob/user)
+ playsound(src.loc, cuff_sound, 30, 1, -2)
- var/obj/effect/equip_e/human/O = new /obj/effect/equip_e/human( )
- O.source = user
- O.target = C
- O.item = user.get_active_hand()
- O.s_loc = user.loc
- O.t_loc = C.loc
- O.place = "handcuff"
- C.requests += O
- spawn( 0 )
- if(istype(src, /obj/item/weapon/handcuffs/cable))
- feedback_add_details("handcuffs","C")
- playsound(src.loc, 'sound/weapons/cablecuff.ogg', 30, 1, -2)
- else
- feedback_add_details("handcuffs","H")
- playsound(src.loc, 'sound/weapons/handcuffs.ogg', 30, 1, -2)
- O.process()
- return
- else
- if(!C.handcuffed)
- var/obj/effect/equip_e/monkey/O = new /obj/effect/equip_e/monkey( )
- O.source = user
- O.target = C
- O.item = user.get_active_hand()
- O.s_loc = user.loc
- O.t_loc = C.loc
- O.place = "handcuff"
- C.requests += O
- spawn( 0 )
- if(istype(src, /obj/item/weapon/handcuffs/cable))
- playsound(src.loc, 'sound/weapons/cablecuff.ogg', 30, 1, -2)
- else
- playsound(src.loc, 'sound/weapons/handcuffs.ogg', 30, 1, -2)
- O.process()
- return
- return
+ if (ishuman(target))
+ var/mob/living/carbon/human/H = target
+ H.attack_log += text("\[[time_stamp()]\] Has been handcuffed (attempt) by [user.name] ([user.ckey])")
+ user.attack_log += text("\[[time_stamp()]\] Attempted to handcuff [H.name] ([H.ckey])")
+ msg_admin_attack("[key_name(user)] attempted to handcuff [key_name(H)]")
+
+ var/obj/effect/equip_e/human/O = new /obj/effect/equip_e/human( )
+ O.source = user
+ O.target = H
+ O.item = user.get_active_hand()
+ O.s_loc = user.loc
+ O.t_loc = H.loc
+ O.place = "handcuff"
+ H.requests += O
+ spawn( 0 )
+ feedback_add_details("handcuffs","H")
+ O.process()
+ return
+
+ if (ismonkey(target))
+ var/mob/living/carbon/monkey/M = target
+ var/obj/effect/equip_e/monkey/O = new /obj/effect/equip_e/monkey( )
+ O.source = user
+ O.target = M
+ O.item = user.get_active_hand()
+ O.s_loc = user.loc
+ O.t_loc = M.loc
+ O.place = "handcuff"
+ M.requests += O
+ spawn( 0 )
+ O.process()
+ return
var/last_chew = 0
/mob/living/carbon/human/RestrainedClickOn(var/atom/A)
@@ -122,6 +102,7 @@ var/last_chew = 0
desc = "Looks like some cables tied together. Could be used to tie something up."
icon_state = "cuff_white"
breakouttime = 300 //Deciseconds = 30s
+ cuff_sound = 'sound/weapons/cablecuff.ogg'
/obj/item/weapon/handcuffs/cable/red
color = "#DD0000"
@@ -162,3 +143,15 @@ var/last_chew = 0
/obj/item/weapon/handcuffs/cyborg
dispenser = 1
+
+/obj/item/weapon/handcuffs/cyborg/attack(mob/living/carbon/C as mob, mob/user as mob)
+ if(!C.handcuffed)
+ var/turf/p_loc = user.loc
+ var/turf/p_loc_m = C.loc
+ playsound(src.loc, cuff_sound, 30, 1, -2)
+ user.visible_message("\red [user] is trying to put handcuffs on [C]!")
+ spawn(30)
+ if(!C) return
+ if(p_loc == user.loc && p_loc_m == C.loc)
+ C.handcuffed = new /obj/item/weapon/handcuffs(C)
+ C.update_inv_handcuffed()
\ No newline at end of file
diff --git a/code/game/objects/items/weapons/implants/implant.dm b/code/game/objects/items/weapons/implants/implant.dm
index f8c46134c1e..c474650f9ae 100644
--- a/code/game/objects/items/weapons/implants/implant.dm
+++ b/code/game/objects/items/weapons/implants/implant.dm
@@ -1,5 +1,7 @@
#define MALFUNCTION_TEMPORARY 1
#define MALFUNCTION_PERMANENT 2
+
+
/obj/item/weapon/implant
name = "implant"
icon = 'icons/obj/device.dmi'
diff --git a/code/game/objects/items/weapons/implants/implanter.dm b/code/game/objects/items/weapons/implants/implanter.dm
index b5c07a8f65f..19cb00b999d 100644
--- a/code/game/objects/items/weapons/implants/implanter.dm
+++ b/code/game/objects/items/weapons/implants/implanter.dm
@@ -65,8 +65,6 @@
update()
return
-
-
/obj/item/weapon/implanter/explosive
name = "implanter (E)"
diff --git a/code/game/objects/items/weapons/power_cells.dm b/code/game/objects/items/weapons/power_cells.dm
index 5636681f5e8..ba75d32990a 100644
--- a/code/game/objects/items/weapons/power_cells.dm
+++ b/code/game/objects/items/weapons/power_cells.dm
@@ -28,7 +28,7 @@
desc = "You can't top the plasma top." //TOTALLY TRADEMARK INFRINGEMENT
origin_tech = "powerstorage=0"
maxcharge = 500
- matter = list("glass" = 40)
+ matter = list("metal" = 700, "glass" = 40)
/obj/item/weapon/cell/crap/empty/New()
..()
@@ -38,18 +38,24 @@
name = "security borg rechargable D battery"
origin_tech = "powerstorage=0"
maxcharge = 600 //600 max charge / 100 charge per shot = six shots
- matter = list("glass" = 40)
+ matter = list("metal" = 700, "glass" = 40)
/obj/item/weapon/cell/secborg/empty/New()
..()
charge = 0
+/obj/item/weapon/cell/apc
+ name = "heavy-duty power cell"
+ origin_tech = "powerstorage=1"
+ maxcharge = 5000
+ matter = list("metal" = 700, "glass" = 50)
+
/obj/item/weapon/cell/high
name = "high-capacity power cell"
origin_tech = "powerstorage=2"
icon_state = "hcell"
maxcharge = 10000
- matter = list("glass" = 60)
+ matter = list("metal" = 700, "glass" = 60)
/obj/item/weapon/cell/high/empty/New()
..()
@@ -60,7 +66,7 @@
origin_tech = "powerstorage=5"
icon_state = "scell"
maxcharge = 20000
- matter = list("glass" = 70)
+ matter = list("metal" = 700, "glass" = 70)
construction_cost = list("metal"=750,"glass"=100)
/obj/item/weapon/cell/super/empty/New()
@@ -72,7 +78,7 @@
origin_tech = "powerstorage=6"
icon_state = "hpcell"
maxcharge = 30000
- matter = list("glass" = 80)
+ matter = list("metal" = 700, "glass" = 80)
construction_cost = list("metal"=500,"glass"=150,"gold"=200,"silver"=200)
/obj/item/weapon/cell/hyper/empty/New()
@@ -84,7 +90,7 @@
icon_state = "icell"
origin_tech = null
maxcharge = 30000
- matter = list("glass"= 80)
+ matter = list("metal" = 700, "glass" = 80)
use()
return 1
diff --git a/code/game/objects/items/weapons/shards.dm b/code/game/objects/items/weapons/shards.dm
index 089d731c58c..1eeb6dff8df 100644
--- a/code/game/objects/items/weapons/shards.dm
+++ b/code/game/objects/items/weapons/shards.dm
@@ -68,7 +68,7 @@
return
return ..()
-/obj/item/weapon/shard/HasEntered(AM as mob|obj)
+/obj/item/weapon/shard/Crossed(AM as mob|obj)
if(ismob(AM))
var/mob/M = AM
M << "\red You step on \the [src]!"
diff --git a/code/game/objects/items/weapons/storage/backpack.dm b/code/game/objects/items/weapons/storage/backpack.dm
index 60a1845f18b..25ea9623f44 100644
--- a/code/game/objects/items/weapons/storage/backpack.dm
+++ b/code/game/objects/items/weapons/storage/backpack.dm
@@ -126,6 +126,36 @@
icon_state = "engiepack"
item_state = "engiepack"
+/obj/item/weapon/storage/backpack/toxins
+ name = "laboratory backpack"
+ desc = "It's a light backpack modeled for use in laboratories and other scientific institutions."
+ icon_state = "toxpack"
+ item_state = "toxpack"
+
+/obj/item/weapon/storage/backpack/hydroponics
+ name = "herbalist's backpack"
+ desc = "It's a green backpack with many pockets to store plants and tools in."
+ icon_state = "hydpack"
+ item_state = "hydpack"
+
+/obj/item/weapon/storage/backpack/genetics
+ name = "geneticist backpack"
+ desc = "It's a backpack fitted with slots for diskettes and other workplace tools."
+ icon_state = "genpack"
+ item_state = "genpack"
+
+/obj/item/weapon/storage/backpack/virology
+ name = "sterile backpack"
+ desc = "It's a sterile backpack able to withstand different pathogens from entering its fabric."
+ icon_state = "viropack"
+ item_state = "viropack"
+
+/obj/item/weapon/storage/backpack/chemistry
+ name = "chemistry backpack"
+ desc = "It's an orange backpack which was designed to hold beakers, pill bottles and bottles."
+ icon_state = "chempack"
+ item_state = "chempack"
+
/*
* Satchel Types
*/
diff --git a/code/game/objects/items/weapons/storage/fancy.dm b/code/game/objects/items/weapons/storage/fancy.dm
index 8a6ec2c6337..75c27690222 100644
--- a/code/game/objects/items/weapons/storage/fancy.dm
+++ b/code/game/objects/items/weapons/storage/fancy.dm
@@ -203,6 +203,55 @@
icon_state = "Dpacket"
item_state = "Dpacket"
+/obj/item/weapon/storage/fancy/cigar
+ name = "cigar case"
+ desc = "A case for holding your cigars when you are not smoking them."
+ icon_state = "cigarcase"
+ item_state = "cigarcase"
+ icon = 'icons/obj/cigarettes.dmi'
+ w_class = 1
+ throwforce = 2
+ flags = TABLEPASS
+ slot_flags = SLOT_BELT
+ storage_slots = 7
+ can_hold = list("/obj/item/clothing/mask/cigarette/cigar")
+ icon_type = "cigar"
+
+/obj/item/weapon/storage/fancy/cigar/New()
+ ..()
+ flags |= NOREACT
+ for(var/i = 1 to storage_slots)
+ new /obj/item/clothing/mask/cigarette/cigar(src)
+ create_reagents(15 * storage_slots)
+
+/obj/item/weapon/storage/fancy/cigar/Del()
+ del(reagents)
+ ..()
+
+/obj/item/weapon/storage/fancy/cigar/update_icon()
+ icon_state = "[initial(icon_state)][contents.len]"
+ return
+
+/obj/item/weapon/storage/fancy/cigar/remove_from_storage(obj/item/W as obj, atom/new_location)
+ var/obj/item/clothing/mask/cigarette/cigar/C = W
+ if(!istype(C)) return
+ reagents.trans_to(C, (reagents.total_volume/contents.len))
+ ..()
+
+/obj/item/weapon/storage/fancy/cigar/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob)
+ if(!istype(M, /mob))
+ return
+
+ if(M == user && user.zone_sel.selecting == "mouth" && contents.len > 0 && !user.wear_mask)
+ var/obj/item/clothing/mask/cigarette/cigar/W = new /obj/item/clothing/mask/cigarette/cigar(user)
+ reagents.trans_to(W, (reagents.total_volume/contents.len))
+ user.equip_to_slot_if_possible(W, slot_wear_mask)
+ reagents.maximum_volume = 15 * contents.len
+ contents.len--
+ user << "You take a cigar out of the case."
+ update_icon()
+ else
+ ..()
/*
* Vial Box
diff --git a/code/game/objects/items/weapons/stunbaton.dm b/code/game/objects/items/weapons/stunbaton.dm
index b64a109dff0..e3b5b8d0c5e 100644
--- a/code/game/objects/items/weapons/stunbaton.dm
+++ b/code/game/objects/items/weapons/stunbaton.dm
@@ -12,10 +12,11 @@
w_class = 3
origin_tech = "combat=2"
attack_verb = list("beaten")
- var/stunforce = 7
- var/status = 0
- var/obj/item/weapon/cell/high/bcell = null
- var/hitcost = 1000
+ var/stunforce = 0
+ var/agonyforce = 60
+ var/status = 0 //whether the thing is on or not
+ var/obj/item/weapon/cell/bcell = null
+ var/hitcost = 1000 //oh god why do power cells carry so much charge? We probably need to make a distinction between "industrial" sized power cells for APCs and power cells for everything else.
/obj/item/weapon/melee/baton/suicide_act(mob/user)
user.visible_message("[user] is putting the live [name] in \his mouth! It looks like \he's trying to commit suicide.")
@@ -26,10 +27,9 @@
update_icon()
return
-
/obj/item/weapon/melee/baton/loaded/New() //this one starts with a cell pre-installed.
..()
- bcell = new(src)
+ bcell = new/obj/item/weapon/cell/high(src)
update_icon()
return
@@ -103,64 +103,75 @@
deductcharge(hitcost)
return
-
if(isrobot(M))
..()
return
- var/mob/living/carbon/human/H = M
+ var/agony = agonyforce
+ var/stun = stunforce
+ var/mob/living/L = M
+ var/target_zone = check_zone(user.zone_sel.selecting)
if(user.a_intent == "hurt")
- if(!..()) return
- H.visible_message("[M] has been beaten with the [src] by [user]!")
-
- user.attack_log += "\[[time_stamp()]\] Beat [H.name] ([H.ckey]) with [src.name]"
- H.attack_log += "\[[time_stamp()]\] Beaten by [user.name] ([user.ckey]) with [src.name]"
- msg_admin_attack("[user.name] ([user.ckey]) beat [H.name] ([H.ckey]) with [src.name] (JMP)")
-
- playsound(src.loc, "swing_hit", 50, 1, -1)
-
- else if(!status)
- H.visible_message("[H] has been prodded with [src] by [user]. Luckily it was off.")
- return
-
- var/stunroll = (rand(1,100))
-
- if(status)
- user.lastattacked = H
- H.lastattacker = user
- if(user == H) // Attacking yourself can't miss
- stunroll = 100
- if(stunroll < 40)
- H.visible_message("\red [user] misses [H] with \the [src]!")
- msg_admin_attack("[key_name(user)] attempted to stun [key_name(H)] with the [src].")
- return
- H.Stun(stunforce)
- H.Weaken(stunforce)
- H.apply_effect(STUTTER, stunforce)
-
- H.visible_message("[H] has been stunned with [src] by [user]!")
- playsound(loc, 'sound/weapons/Egloves.ogg', 50, 1, -1)
-
- msg_admin_attack("[key_name(user)] stunned [key_name(H)] with the [src].")
-
- user.attack_log += "\[[time_stamp()]\] Stunned [H.name] ([H.ckey]) with [src.name]"
- H.attack_log += "\[[time_stamp()]\] Stunned by [user.name] ([user.ckey]) with [src.name]"
-
- if(isrobot(loc))
- var/mob/living/silicon/robot/R = loc
- if(R && R.cell)
- R.cell.use(hitcost)
+ if (!..()) //item/attack() does it's own messaging and logs
+ return 0 // item/attack() will return 1 if they hit, 0 if they missed.
+ agony *= 0.5 //whacking someone causes a much poorer contact than prodding them.
+ stun *= 0.5
+ //we can't really extract the actual hit zone from ..(), unfortunately. Just act like they attacked the area they intended to.
+ else
+ //copied from human_defense.dm - human defence code should really be refactored some time.
+ if (ishuman(L))
+ user.lastattacked = L //are these used at all, if we have logs?
+ L.lastattacker = user
+
+ if (user != L) // Attacking yourself can't miss
+ target_zone = get_zone_with_miss_chance(user.zone_sel.selecting, L)
+
+ if(!target_zone)
+ L.visible_message("\red [user] misses [L] with \the [src]!")
+ return 0
+
+ var/mob/living/carbon/human/H = L
+ var/datum/organ/external/affecting = H.get_organ(target_zone)
+ if (affecting)
+ if(!status)
+ L.visible_message("[L] has been prodded in the [affecting.display_name] with [src] by [user]. Luckily it was off.")
+ return 1
+ else
+ H.visible_message("[L] has been prodded in the [affecting.display_name] with [src] by [user]!")
else
- deductcharge(hitcost)
+ if(!status)
+ L.visible_message("[L] has been prodded with [src] by [user]. Luckily it was off.")
+ return 1
+ else
+ L.visible_message("[L] has been prodded with [src] by [user]!")
+
+ //stun effects
+ L.stun_effect_act(stun, agony, target_zone, src)
+
+ playsound(loc, 'sound/weapons/Egloves.ogg', 50, 1, -1)
+ msg_admin_attack("[key_name(user)] stunned [key_name(L)] with the [src].")
+
+ deductcharge(hitcost)
+
+ return 1
/obj/item/weapon/melee/baton/emp_act(severity)
if(bcell)
- deductcharge(1000 / severity)
- if(bcell.reliability != 100 && prob(50/severity))
- bcell.reliability -= 10 / severity
+ bcell.emp_act(severity) //let's not duplicate code everywhere if we don't have to please.
..()
+//secborg stun baton module
+/obj/item/weapon/melee/baton/robot/attack_self(mob/user)
+ //try to find our power cell
+ var/mob/living/silicon/robot/R = loc
+ if (istype(R))
+ bcell = R.cell
+ return ..()
+
+/obj/item/weapon/melee/baton/robot/attackby(obj/item/weapon/W, mob/user)
+ return
+
//Makeshift stun baton. Replacement for stun gloves.
/obj/item/weapon/melee/baton/cattleprod
name = "stunprod"
@@ -169,6 +180,8 @@
item_state = "prod"
force = 3
throwforce = 5
- stunforce = 5
+ stunforce = 0
+ agonyforce = 60 //same force as a stunbaton, but uses way more charge.
hitcost = 2500
+ attack_verb = list("poked")
slot_flags = null
diff --git a/code/game/objects/items/weapons/swords_axes_etc.dm b/code/game/objects/items/weapons/swords_axes_etc.dm
index 3e679259947..739ad8f6946 100644
--- a/code/game/objects/items/weapons/swords_axes_etc.dm
+++ b/code/game/objects/items/weapons/swords_axes_etc.dm
@@ -91,7 +91,7 @@
*/
if (user.a_intent == "hurt")
if(!..()) return
- playsound(src.loc, "swing_hit", 50, 1, -1)
+ //playsound(src.loc, "swing_hit", 50, 1, -1)
if (M.stuttering < 8 && (!(HULK in M.mutations)) /*&& (!istype(H:wear_suit, /obj/item/clothing/suit/judgerobe))*/)
M.stuttering = 8
M.Stun(8)
@@ -177,7 +177,7 @@
user.take_organ_damage(2*force)
return
if(..())
- playsound(src.loc, "swing_hit", 50, 1, -1)
+ //playsound(src.loc, "swing_hit", 50, 1, -1)
return
else
return ..()
diff --git a/code/game/objects/items/weapons/tanks/jetpack.dm b/code/game/objects/items/weapons/tanks/jetpack.dm
index 6c3d4a3568f..6c0e13b7c3f 100644
--- a/code/game/objects/items/weapons/tanks/jetpack.dm
+++ b/code/game/objects/items/weapons/tanks/jetpack.dm
@@ -21,7 +21,7 @@
/obj/item/weapon/tank/jetpack/examine()
set src in usr
..()
- if(air_contents.oxygen < 10)
+ if(air_contents.gas["oxygen"] < 10)
usr << text("\red The meter on the [src.name] indicates you are almost out of air!")
playsound(usr, 'sound/effects/alert.ogg', 50, 1)
@@ -34,7 +34,7 @@
/obj/item/weapon/tank/jetpack/verb/toggle()
set name = "Toggle Jetpack"
set category = "Object"
-
+
on = !on
if(on)
icon_state = "[icon_state]-on"
@@ -42,7 +42,7 @@
else
icon_state = initial(icon_state)
ion_trail.stop()
-
+
if (ismob(usr))
var/mob/M = usr
M.update_inv_back()
@@ -50,13 +50,13 @@
/obj/item/weapon/tank/jetpack/proc/allow_thrust(num, mob/living/user as mob)
if(!(src.on))
return 0
- if((num < 0.005 || src.air_contents.total_moles() < num))
+ if((num < 0.005 || src.air_contents.total_moles < num))
src.ion_trail.stop()
return 0
var/datum/gas_mixture/G = src.air_contents.remove(num)
- var/allgases = G.carbon_dioxide + G.nitrogen + G.oxygen + G.phoron //fuck trace gases -Pete
+ var/allgases = G.gas["carbon_dioxide"] + G.gas["nitrogen"] + G.gas["oxygen"] + G.gas["phoron"]
if(allgases >= 0.005)
return 1
@@ -75,8 +75,7 @@
/obj/item/weapon/tank/jetpack/void/New()
..()
- //src.air_contents.oxygen = (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)
- air_contents.adjust((6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C))
+ air_contents.adjust_gas("oxygen", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C))
return
/obj/item/weapon/tank/jetpack/oxygen
@@ -87,8 +86,7 @@
/obj/item/weapon/tank/jetpack/oxygen/New()
..()
- //src.air_contents.oxygen = (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)
- air_contents.adjust((6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C))
+ air_contents.adjust_gas("oxygen", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C))
return
/obj/item/weapon/tank/jetpack/carbondioxide
@@ -103,13 +101,13 @@
src.ion_trail = new /datum/effect/effect/system/ion_trail_follow()
src.ion_trail.set_up(src)
//src.air_contents.carbon_dioxide = (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)
- air_contents.adjust(0,(6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C))
+ air_contents.adjust_gas("carbon_dioxide", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C))
return
/obj/item/weapon/tank/jetpack/carbondioxide/examine()
set src in usr
..()
- if(air_contents.carbon_dioxide < 10)
+ if(air_contents.gas["carbon_dioxide"] < 10)
usr << text("\red The meter on the [src.name] indicates you are almost out of air!")
playsound(usr, 'sound/effects/alert.ogg', 50, 1)
return
diff --git a/code/game/objects/items/weapons/tanks/tank_types.dm b/code/game/objects/items/weapons/tanks/tank_types.dm
index cfbe34c1a8a..336ba4aa18d 100644
--- a/code/game/objects/items/weapons/tanks/tank_types.dm
+++ b/code/game/objects/items/weapons/tanks/tank_types.dm
@@ -19,15 +19,14 @@
New()
..()
- //src.air_contents.oxygen = (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)
- air_contents.adjust((6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C))
+ air_contents.adjust_gas("oxygen", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C))
return
examine()
set src in usr
..()
- if(air_contents.oxygen < 10)
+ if(air_contents.gas["oxygen"] < 10)
usr << text("\red The meter on the [src.name] indicates you are almost out of air!")
//playsound(usr, 'sound/effects/alert.ogg', 50, 1)
@@ -53,13 +52,8 @@
/obj/item/weapon/tank/anesthetic/New()
..()
- src.air_contents.oxygen = (3*ONE_ATMOSPHERE)*70/(R_IDEAL_GAS_EQUATION*T20C) * O2STANDARD
-
- var/datum/gas/sleeping_agent/trace_gas = new()
- trace_gas.moles = (3*ONE_ATMOSPHERE)*70/(R_IDEAL_GAS_EQUATION*T20C) * N2STANDARD
-
- src.air_contents.trace_gases += trace_gas
- //
+ air_contents.gas["oxygen"] = (3*ONE_ATMOSPHERE)*70/(R_IDEAL_GAS_EQUATION*T20C) * O2STANDARD
+ air_contents.gas["sleeping_agent"] = (3*ONE_ATMOSPHERE)*70/(R_IDEAL_GAS_EQUATION*T20C) * N2STANDARD
air_contents.update_values()
return
@@ -76,17 +70,14 @@
examine()
set src in usr
..()
- if(air_contents.oxygen < 1 && loc==usr)
+ if(air_contents.gas["oxygen"] < 1 && loc==usr)
usr << "\red The meter on the [src.name] indicates you are almost out of air!"
usr << sound('sound/effects/alert.ogg')
/obj/item/weapon/tank/air/New()
..()
- src.air_contents.oxygen = (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * O2STANDARD
- src.air_contents.nitrogen = (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * N2STANDARD
- //
- src.air_contents.update_values()
+ src.air_contents.adjust_multi("oxygen", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * O2STANDARD, "nitrogen", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * N2STANDARD)
return
@@ -105,9 +96,7 @@
/obj/item/weapon/tank/phoron/New()
..()
- src.air_contents.phoron = (3*ONE_ATMOSPHERE)*70/(R_IDEAL_GAS_EQUATION*T20C)
- //
- src.air_contents.update_values()
+ src.air_contents.adjust_gas("phoron", (3*ONE_ATMOSPHERE)*70/(R_IDEAL_GAS_EQUATION*T20C))
return
/obj/item/weapon/tank/phoron/attackby(obj/item/weapon/W as obj, mob/user as mob)
@@ -139,9 +128,7 @@
New()
..()
- src.air_contents.oxygen = (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)
- //
- src.air_contents.update_values()
+ src.air_contents.adjust_gas("oxygen", (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C))
return
@@ -149,7 +136,7 @@
examine()
set src in usr
..()
- if(air_contents.oxygen < 0.2 && loc==usr)
+ if(air_contents.gas["oxygen"] < 0.2 && loc==usr)
usr << text("\red The meter on the [src.name] indicates you are almost out of air!")
usr << sound('sound/effects/alert.ogg')
@@ -176,14 +163,12 @@
/obj/item/weapon/tank/nitrogen/New()
..()
- src.air_contents.nitrogen = (3*ONE_ATMOSPHERE)*70/(R_IDEAL_GAS_EQUATION*T20C)
- //
- src.air_contents.update_values()
+ src.air_contents.adjust_gas("nitrogen", (3*ONE_ATMOSPHERE)*70/(R_IDEAL_GAS_EQUATION*T20C))
return
/obj/item/weapon/tank/nitrogen/examine()
set src in usr
..()
- if(air_contents.nitrogen < 10)
+ if(air_contents.gas["nitrogen"] < 10)
usr << text("\red The meter on the [src.name] indicates you are almost out of air!")
//playsound(usr, 'sound/effects/alert.ogg', 50, 1)
\ No newline at end of file
diff --git a/code/game/objects/items/weapons/tanks/tanks.dm b/code/game/objects/items/weapons/tanks/tanks.dm
index 8b883f818ac..d01e681c428 100644
--- a/code/game/objects/items/weapons/tanks/tanks.dm
+++ b/code/game/objects/items/weapons/tanks/tanks.dm
@@ -28,7 +28,7 @@
src.air_contents.volume = volume //liters
src.air_contents.temperature = T20C
- processing_objects.Add(src)
+ processing_objects.Add(src)
return
/obj/item/weapon/tank/Del()
@@ -91,24 +91,13 @@
var/pressure = air_contents.return_pressure()
manipulated_by = user.real_name //This person is aware of the contents of the tank.
- var/total_moles = air_contents.total_moles()
+ var/total_moles = air_contents.total_moles
user << "\blue Results of analysis of \icon[icon]"
if (total_moles>0)
- var/o2_concentration = air_contents.oxygen/total_moles
- var/n2_concentration = air_contents.nitrogen/total_moles
- var/co2_concentration = air_contents.carbon_dioxide/total_moles
- var/phoron_concentration = air_contents.phoron/total_moles
-
- var/unknown_concentration = 1-(o2_concentration+n2_concentration+co2_concentration+phoron_concentration)
-
user << "\blue Pressure: [round(pressure,0.1)] kPa"
- user << "\blue Nitrogen: [round(n2_concentration*100)]%"
- user << "\blue Oxygen: [round(o2_concentration*100)]%"
- user << "\blue CO2: [round(co2_concentration*100)]%"
- user << "\blue Phoron: [round(phoron_concentration*100)]%"
- if(unknown_concentration>0.01)
- user << "\red Unknown: [round(unknown_concentration*100)]%"
+ for(var/g in air_contents.gas)
+ user << "\blue [gas_data.name[g]]: [(round(air_contents.gas[g] / total_moles) * 100)]%"
user << "\blue Temperature: [round(air_contents.temperature-T0C)]°C"
else
user << "\blue Tank is empty!"
@@ -127,7 +116,7 @@
ui_interact(user)
-/obj/item/weapon/tank/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null)
+/obj/item/weapon/tank/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
var/using_internal
if(istype(loc,/mob/living/carbon))
@@ -142,15 +131,15 @@
data["defaultReleasePressure"] = round(TANK_DEFAULT_RELEASE_PRESSURE)
data["maxReleasePressure"] = round(TANK_MAX_RELEASE_PRESSURE)
data["valveOpen"] = using_internal ? 1 : 0
-
- data["maskConnected"] = 0
+
+ data["maskConnected"] = 0
if(istype(loc,/mob/living/carbon))
var/mob/living/carbon/location = loc
if(location.internal == src || (location.wear_mask && (location.wear_mask.flags & MASKINTERNALS)))
data["maskConnected"] = 1
// update the ui if it exists, returns null if no ui is passed/found
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, data)
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
// the ui does not exist, so we'll create a new() one
// for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
diff --git a/code/game/objects/items/weapons/tools.dm b/code/game/objects/items/weapons/tools.dm
index af50cd10882..f4400132e80 100644
--- a/code/game/objects/items/weapons/tools.dm
+++ b/code/game/objects/items/weapons/tools.dm
@@ -410,7 +410,7 @@
max_fuel = 40
w_class = 3.0
matter = list("metal" = 70, "glass" = 120)
- origin_tech = "engineering=4;phoron=3"
+ origin_tech = "engineering=4;phorontech=3"
var/last_gen = 0
diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm
index 04648c39b3e..20927cee632 100644
--- a/code/game/objects/structures.dm
+++ b/code/game/objects/structures.dm
@@ -81,7 +81,7 @@
var/damage = rand(15,30)
var/mob/living/carbon/human/H = M
- if(!istype(M))
+ if(!istype(H))
H << "\red You land heavily!"
M.adjustBruteLoss(damage)
return
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/security.dm b/code/game/objects/structures/crates_lockers/closets/secure/security.dm
index 30d6c758a3f..1fd6bdff5fc 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm
@@ -115,7 +115,6 @@
new /obj/item/clothing/glasses/sunglasses/sechud(src)
new /obj/item/taperoll/police(src)
new /obj/item/weapon/shield/riot(src)
- new /obj/item/weapon/storage/lockbox/loyalty(src)
new /obj/item/weapon/storage/box/flashbangs(src)
new /obj/item/weapon/storage/belt/security(src)
new /obj/item/device/flash(src)
@@ -123,6 +122,7 @@
new /obj/item/weapon/gun/energy/gun(src)
new /obj/item/clothing/tie/holster/waist(src)
new /obj/item/weapon/melee/telebaton(src)
+ new /obj/item/clothing/head/beret/sec/hos(src)
return
@@ -160,6 +160,7 @@
new /obj/item/weapon/melee/baton/loaded(src)
new /obj/item/weapon/gun/energy/taser(src)
new /obj/item/weapon/storage/box/holobadge(src)
+ new /obj/item/clothing/head/beret/sec/warden(src)
return
diff --git a/code/game/objects/structures/crates_lockers/closets/syndicate.dm b/code/game/objects/structures/crates_lockers/closets/syndicate.dm
index a1a3876574e..c62ea02d924 100644
--- a/code/game/objects/structures/crates_lockers/closets/syndicate.dm
+++ b/code/game/objects/structures/crates_lockers/closets/syndicate.dm
@@ -25,6 +25,19 @@
new /obj/item/clothing/shoes/magboots(src)
+/obj/structure/closet/syndicate/suit
+ desc = "It's a storage unit for rig suits."
+
+/obj/structure/closet/syndicate/suit/New()
+ ..()
+ sleep(2)
+ new /obj/item/weapon/tank/jetpack/oxygen(src)
+ new /obj/item/clothing/shoes/magboots(src)
+ new /obj/item/clothing/suit/space/rig/syndi(src)
+ new /obj/item/clothing/mask/gas/syndicate(src)
+ new /obj/item/clothing/head/helmet/space/rig/syndi(src)
+
+
/obj/structure/closet/syndicate/nuclear
desc = "It's a storage unit for nuclear-operative gear."
diff --git a/code/game/objects/structures/crates_lockers/closets/wardrobe.dm b/code/game/objects/structures/crates_lockers/closets/wardrobe.dm
index f4d152ea916..dbbb93cb271 100644
--- a/code/game/objects/structures/crates_lockers/closets/wardrobe.dm
+++ b/code/game/objects/structures/crates_lockers/closets/wardrobe.dm
@@ -26,6 +26,9 @@
new /obj/item/clothing/head/beret/sec(src)
new /obj/item/clothing/head/beret/sec(src)
new /obj/item/clothing/head/beret/sec(src)
+ new /obj/item/clothing/head/beret/sec/alt(src)
+ new /obj/item/clothing/head/beret/sec/alt(src)
+ new /obj/item/clothing/head/beret/sec/alt(src)
return
diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm
index 6ee0f5e05e2..29a6b39658b 100644
--- a/code/game/objects/structures/crates_lockers/crates.dm
+++ b/code/game/objects/structures/crates_lockers/crates.dm
@@ -323,12 +323,7 @@
var/datum/gas_mixture/gas = (..())
if(!gas) return null
var/datum/gas_mixture/newgas = new/datum/gas_mixture()
- newgas.oxygen = gas.oxygen
- newgas.carbon_dioxide = gas.carbon_dioxide
- newgas.nitrogen = gas.nitrogen
- newgas.phoron = gas.phoron
- newgas.volume = gas.volume
- newgas.temperature = gas.temperature
+ newgas.copy_from(gas)
if(newgas.temperature <= target_temp) return
if((newgas.temperature - cooling_power) > target_temp)
diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm
index bb50f1d63db..595a7d903be 100644
--- a/code/game/objects/structures/grille.dm
+++ b/code/game/objects/structures/grille.dm
@@ -58,7 +58,9 @@
return
/obj/structure/grille/attack_slime(mob/user as mob)
- if(!istype(user, /mob/living/carbon/slime/adult)) return
+ var/mob/living/carbon/slime/S = user
+ if (!S.is_adult)
+ return
playsound(loc, 'sound/effects/grillehit.ogg', 80, 1)
user.visible_message("[user] smashes against [src].", \
diff --git a/code/game/objects/structures/inflatable.dm b/code/game/objects/structures/inflatable.dm
index 3d21a3a2b2d..cd9dfd3e8ae 100644
--- a/code/game/objects/structures/inflatable.dm
+++ b/code/game/objects/structures/inflatable.dm
@@ -103,7 +103,9 @@
attack_slime(mob/user as mob)
- if(!isslimeadult(user)) return
+ var/mob/living/carbon/slime/S = user
+ if (!S.is_adult)
+ return
attack_generic(user, rand(10, 15))
diff --git a/code/game/objects/structures/mineral_doors.dm b/code/game/objects/structures/mineral_doors.dm
index fdb84443705..4902b6a631d 100644
--- a/code/game/objects/structures/mineral_doors.dm
+++ b/code/game/objects/structures/mineral_doors.dm
@@ -206,15 +206,9 @@
proc/TemperatureAct(temperature)
for(var/turf/simulated/floor/target_tile in range(2,loc))
-
- var/datum/gas_mixture/napalm = new
-
var/phoronToDeduce = temperature/10
+ target_tile.assume_gas("phoron", phoronToDeduce, 200+T0C)
- napalm.phoron = phoronToDeduce
- napalm.temperature = 200+T0C
-
- target_tile.assume_air(napalm)
spawn (0) target_tile.hotspot_expose(temperature, 400)
hardness -= phoronToDeduce/100
diff --git a/code/game/objects/structures/mirror.dm b/code/game/objects/structures/mirror.dm
index 0937e72d848..59771219737 100644
--- a/code/game/objects/structures/mirror.dm
+++ b/code/game/objects/structures/mirror.dm
@@ -105,7 +105,9 @@
/obj/structure/mirror/attack_slime(mob/user as mob)
- if(!isslimeadult(user)) return
+ var/mob/living/carbon/slime/S = user
+ if (!S.is_adult)
+ return
if(shattered)
playsound(src.loc, 'sound/effects/hit_on_shattered_glass.ogg', 70, 1)
return
diff --git a/code/game/objects/structures/stool_bed_chair_nest/chairs.dm b/code/game/objects/structures/stool_bed_chair_nest/chairs.dm
index 31a1681eace..f7eafdda9db 100644
--- a/code/game/objects/structures/stool_bed_chair_nest/chairs.dm
+++ b/code/game/objects/structures/stool_bed_chair_nest/chairs.dm
@@ -153,17 +153,23 @@
if(propelled)
var/mob/living/occupant = buckled_mob
unbuckle()
+
+ var/def_zone = ran_zone()
+ var/blocked = occupant.run_armor_check(def_zone, "melee")
occupant.throw_at(A, 3, propelled)
- occupant.apply_effect(6, STUN, 0)
- occupant.apply_effect(6, WEAKEN, 0)
- occupant.apply_effect(6, STUTTER, 0)
+ occupant.apply_effect(6, STUN, blocked)
+ occupant.apply_effect(6, WEAKEN, blocked)
+ occupant.apply_effect(6, STUTTER, blocked)
+ occupant.apply_damage(10, BRUTE, def_zone, blocked)
playsound(src.loc, 'sound/weapons/punch1.ogg', 50, 1, -1)
if(istype(A, /mob/living))
var/mob/living/victim = A
- victim.apply_effect(6, STUN, 0)
- victim.apply_effect(6, WEAKEN, 0)
- victim.apply_effect(6, STUTTER, 0)
- victim.take_organ_damage(10)
+ def_zone = ran_zone()
+ blocked = victim.run_armor_check(def_zone, "melee")
+ victim.apply_effect(6, STUN, blocked)
+ victim.apply_effect(6, WEAKEN, blocked)
+ victim.apply_effect(6, STUTTER, blocked)
+ victim.apply_damage(10, BRUTE, def_zone, blocked)
occupant.visible_message("[occupant] crashed into \the [A]!")
/obj/structure/stool/bed/chair/office/light
diff --git a/code/game/objects/structures/stool_bed_chair_nest/stools.dm b/code/game/objects/structures/stool_bed_chair_nest/stools.dm
index a068af11938..ba27d070d71 100644
--- a/code/game/objects/structures/stool_bed_chair_nest/stools.dm
+++ b/code/game/objects/structures/stool_bed_chair_nest/stools.dm
@@ -54,12 +54,27 @@
w_class = 5.0
var/obj/structure/stool/origin = null
+/obj/item/weapon/stool/proc/deploy(var/mob/user)
+
+ if(!origin)
+ del src
+
+ origin.loc = get_turf(src)
+
+ if(user)
+ user.u_equip(src)
+ user.visible_message("\blue [user] puts [src] down.", "\blue You put [src] down.")
+
+ del src
+
+/obj/item/weapon/stool/dropped(mob/user as mob)
+ ..()
+ if(istype(loc,/turf/))
+ deploy(user)
+
/obj/item/weapon/stool/attack_self(mob/user as mob)
..()
- origin.loc = get_turf(src)
- user.u_equip(src)
- user.visible_message("\blue [user] puts [src] down.", "\blue You put [src] down.")
- del src
+ deploy(user)
/obj/item/weapon/stool/attack(mob/M as mob, mob/user as mob)
if (prob(5) && istype(M,/mob/living))
diff --git a/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm b/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm
index 9c00ee38963..8e801de6e2d 100644
--- a/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm
+++ b/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm
@@ -147,16 +147,22 @@
else if (propelled)
occupant.throw_at(A, 3, propelled)
- occupant.apply_effect(6, STUN, 0)
- occupant.apply_effect(6, WEAKEN, 0)
- occupant.apply_effect(6, STUTTER, 0)
+ var/def_zone = ran_zone()
+ var/blocked = occupant.run_armor_check(def_zone, "melee")
+ occupant.throw_at(A, 3, propelled)
+ occupant.apply_effect(6, STUN, blocked)
+ occupant.apply_effect(6, WEAKEN, blocked)
+ occupant.apply_effect(6, STUTTER, blocked)
+ occupant.apply_damage(10, BRUTE, def_zone)
playsound(src.loc, 'sound/weapons/punch1.ogg', 50, 1, -1)
if(istype(A, /mob/living))
var/mob/living/victim = A
- victim.apply_effect(6, STUN, 0)
- victim.apply_effect(6, WEAKEN, 0)
- victim.apply_effect(6, STUTTER, 0)
- victim.take_organ_damage(10)
+ def_zone = ran_zone()
+ blocked = victim.run_armor_check(def_zone, "melee")
+ victim.apply_effect(6, STUN, blocked)
+ victim.apply_effect(6, WEAKEN, blocked)
+ victim.apply_effect(6, STUTTER, blocked)
+ victim.apply_damage(10, BRUTE, def_zone)
if(pulling)
occupant.visible_message("[pulling] has thrusted \the [name] into \the [A], throwing \the [occupant] out of it!")
diff --git a/code/game/objects/structures/transit_tubes.dm b/code/game/objects/structures/transit_tubes.dm
index 2dcc270bdb0..aed971b20d1 100644
--- a/code/game/objects/structures/transit_tubes.dm
+++ b/code/game/objects/structures/transit_tubes.dm
@@ -81,8 +81,7 @@ obj/structure/ex_act(severity)
/obj/structure/transit_tube_pod/New(loc)
..(loc)
- air_contents.oxygen = MOLES_O2STANDARD * 2
- air_contents.nitrogen = MOLES_N2STANDARD
+ air_contents.adjust_multi("oxygen", MOLES_O2STANDARD * 2, "nitrogen", MOLES_N2STANDARD)
air_contents.temperature = T20C
// Give auto tubes time to align before trying to start moving
@@ -123,8 +122,8 @@ obj/structure/ex_act(severity)
else if(!pod.moving && pod.dir in directions())
AM.loc = pod
return
-
-
+
+
/obj/structure/transit_tube/station/attack_hand(mob/user as mob)
if(!pod_moving)
for(var/obj/structure/transit_tube_pod/pod in loc)
@@ -359,11 +358,7 @@ obj/structure/ex_act(severity)
// datum, there might be problems if I don't...
/obj/structure/transit_tube_pod/return_air()
var/datum/gas_mixture/GM = new()
- GM.oxygen = air_contents.oxygen
- GM.carbon_dioxide = air_contents.carbon_dioxide
- GM.nitrogen = air_contents.nitrogen
- GM.phoron = air_contents.phoron
- GM.temperature = air_contents.temperature
+ GM.copy_from(air_contents)
return GM
// For now, copying what I found in an unused FEA file (and almost identical in a
@@ -398,8 +393,8 @@ obj/structure/ex_act(severity)
var/transfer_in = max(0.1, 0.5 * (env_pressure - int_pressure) / total_pressure)
var/transfer_out = max(0.1, 0.3 * (int_pressure - env_pressure) / total_pressure)
- var/datum/gas_mixture/from_env = loc.remove_air(environment.total_moles() * transfer_in)
- var/datum/gas_mixture/from_int = air_contents.remove(air_contents.total_moles() * transfer_out)
+ var/datum/gas_mixture/from_env = loc.remove_air(environment.total_moles * transfer_in)
+ var/datum/gas_mixture/from_int = air_contents.remove(air_contents.total_moles * transfer_out)
loc.assume_air(from_int)
air_contents.merge(from_env)
diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm
index 41b3bec5013..5d6caee5e21 100644
--- a/code/game/objects/structures/watercloset.dm
+++ b/code/game/objects/structures/watercloset.dm
@@ -190,7 +190,7 @@
del(mymist)
ismist = 0
-/obj/machinery/shower/HasEntered(atom/movable/O)
+/obj/machinery/shower/Crossed(atom/movable/O)
..()
wash(O)
if(ismob(O))
diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm
index 374fe49a975..7052bf5a987 100644
--- a/code/game/objects/structures/window.dm
+++ b/code/game/objects/structures/window.dm
@@ -155,7 +155,9 @@
/obj/structure/window/attack_slime(mob/user as mob)
- if(!isslimeadult(user)) return
+ var/mob/living/carbon/slime/S = user
+ if (!S.is_adult)
+ return
attack_generic(user, rand(10, 15))
diff --git a/code/game/objects/weapons.dm b/code/game/objects/weapons.dm
index f9404c18259..b62efa1c049 100644
--- a/code/game/objects/weapons.dm
+++ b/code/game/objects/weapons.dm
@@ -1,6 +1,7 @@
/obj/item/weapon
name = "weapon"
icon = 'icons/obj/weapons.dmi'
+ hitsound = "swing_hit"
/obj/item/weapon/Bump(mob/M as mob)
spawn(0)
diff --git a/code/game/smoothwall.dm b/code/game/smoothwall.dm
index 1ec091ed6c6..b8196dbc5cb 100644
--- a/code/game/smoothwall.dm
+++ b/code/game/smoothwall.dm
@@ -101,14 +101,11 @@
..()*/
/turf/simulated/wall/Del()
-
- var/temploc = src.loc
-
spawn(10)
- for(var/turf/simulated/wall/W in range(temploc,1))
+ for(var/turf/simulated/wall/W in range(src,1))
W.relativewall()
- for(var/obj/structure/falsewall/W in range(temploc,1))
+ for(var/obj/structure/falsewall/W in range(src,1))
W.relativewall()
for(var/direction in cardinal)
@@ -121,16 +118,6 @@
..()
-/*/turf/simulated/shuttle/wall/Del()
-
- var/temploc = src.loc
-
- spawn(10)
- for(var/turf/simulated/shuttle/wall/W in range(temploc,1))
- W.relativewall()
-
- ..()*/
-
/turf/simulated/wall/relativewall()
if(istype(src,/turf/simulated/wall/vault)) //HACK!!!
return
diff --git a/code/game/supplyshuttle.dm b/code/game/supplyshuttle.dm
index b908d09b664..1ef29b5065e 100644
--- a/code/game/supplyshuttle.dm
+++ b/code/game/supplyshuttle.dm
@@ -21,14 +21,14 @@ var/list/mechtoys = list(
)
/area/supply/station //DO NOT TURN THE lighting_use_dynamic STUFF ON FOR SHUTTLES. IT BREAKS THINGS.
- name = "supply shuttle"
+ name = "Supply Shuttle"
icon_state = "shuttle3"
luminosity = 1
lighting_use_dynamic = 0
requires_power = 0
/area/supply/dock //DO NOT TURN THE lighting_use_dynamic STUFF ON FOR SHUTTLES. IT BREAKS THINGS.
- name = "supply shuttle"
+ name = "Supply Shuttle"
icon_state = "shuttle3"
luminosity = 1
lighting_use_dynamic = 0
diff --git a/code/game/turfs/simulated/floor_types.dm b/code/game/turfs/simulated/floor_types.dm
index dec999f0fce..98e0f660350 100644
--- a/code/game/turfs/simulated/floor_types.dm
+++ b/code/game/turfs/simulated/floor_types.dm
@@ -53,6 +53,7 @@
icon_state = "engine"
thermal_conductivity = 0.025
heat_capacity = 325000
+ intact = 0
/turf/simulated/floor/engine/attackby(obj/item/weapon/C as obj, mob/user as mob)
if(!C)
@@ -77,14 +78,7 @@
/turf/simulated/floor/engine/n20
New()
. = ..()
- var/datum/gas_mixture/adding = new
- var/datum/gas/sleeping_agent/trace_gas = new
-
- trace_gas.moles = 2000
- adding.trace_gases += trace_gas
- adding.temperature = T20C
-
- assume_air(adding)
+ assume_gas("sleeping_agent", 2000)
/turf/simulated/floor/engine/vacuum
name = "vacuum floor"
@@ -154,7 +148,7 @@
name = "skipjack floor"
oxygen = 0
nitrogen = MOLES_N2STANDARD + MOLES_O2STANDARD
-
+
/turf/simulated/floor/beach
name = "Beach"
icon = 'icons/misc/beach.dmi'
diff --git a/code/game/turfs/simulated/walls_mineral.dm b/code/game/turfs/simulated/walls_mineral.dm
index 8834a7770ba..5585de31d87 100644
--- a/code/game/turfs/simulated/walls_mineral.dm
+++ b/code/game/turfs/simulated/walls_mineral.dm
@@ -87,13 +87,7 @@
new /obj/structure/girder(src)
src.ChangeTurf(/turf/simulated/floor)
for(var/turf/simulated/floor/target_tile in range(0,src))
- /*if(target_tile.parent && target_tile.parent.group_processing)
- target_tile.parent.suspend_group_processing()*/
- var/datum/gas_mixture/napalm = new
- var/phoronToDeduce = 20
- napalm.phoron = phoronToDeduce
- napalm.temperature = 400+T0C
- target_tile.assume_air(napalm)
+ target_tile.assume_gas("phoron", 20, 400+T0C)
spawn (0) target_tile.hotspot_expose(temperature, 400)
for(var/obj/structure/falsewall/phoron/F in range(3,src))//Hackish as fuck, but until temperature_expose works, there is nothing I can do -Sieve
var/turf/T = get_turf(F)
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index f0d206ec710..8168a34f0c5 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -132,14 +132,6 @@
M:inertia_dir = 0
..()
var/objects = 0
- for(var/atom/A as mob|obj|turf|area in src)
- if(objects > loopsanity) break
- objects++
- spawn( 0 )
- if ((A && M))
- A.HasEntered(M, 1)
- return
- objects = 0
for(var/atom/A as mob|obj|turf|area in range(1))
if(objects > loopsanity) break
objects++
@@ -222,6 +214,7 @@
///// Z-Level Stuff
var/old_lumcount = lighting_lumcount - initial(lighting_lumcount)
+ var/obj/fire/old_fire = fire
//world << "Replacing [src.type] with [N]"
@@ -250,6 +243,9 @@
W.lighting_changed = 1
lighting_controller.changed_turfs += W
+ if(old_fire)
+ fire = old_fire
+
if (istype(W,/turf/simulated/floor))
W.RemoveLattice()
@@ -271,6 +267,9 @@
W.lighting_changed = 1
lighting_controller.changed_turfs += W
+ if(old_fire)
+ old_fire.RemoveFire()
+
if(air_master)
air_master.mark_for_update(src)
diff --git a/code/global.dm b/code/global.dm
index ac068a92e33..6a9e43c3cf2 100644
--- a/code/global.dm
+++ b/code/global.dm
@@ -2,8 +2,6 @@
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31
var/global/obj/effect/datacore/data_core = null
-var/global/obj/effect/overlay/plmaster = null
-var/global/obj/effect/overlay/slmaster = null
var/global/list/active_areas = list()
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index bf6c5559975..f6605059d01 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -127,7 +127,8 @@ var/list/admin_verbs_server = list(
/datum/admins/proc/toggle_aliens,
/datum/admins/proc/toggle_space_ninja,
/client/proc/toggle_random_events,
- /client/proc/check_customitem_activity
+ /client/proc/check_customitem_activity,
+ /client/proc/nanomapgen_DumpImage
)
var/list/admin_verbs_debug = list(
/client/proc/getruntimelog, /*allows us to access runtime logs to somebody*/
@@ -152,7 +153,7 @@ var/list/admin_verbs_debug = list(
/client/proc/callproc,
/client/proc/toggledebuglogs,
/client/proc/SDQL_query,
- /client/proc/SDQL2_query
+ /client/proc/SDQL2_query,
)
var/list/admin_verbs_possess = list(
/proc/possess,
@@ -304,7 +305,6 @@ var/list/admin_verbs_mentor = list(
/client/proc/camera_view,
/client/proc/sec_camera_report,
/client/proc/intercom_view,
- /client/proc/air_status,
/client/proc/atmosscan,
/client/proc/powerdebug,
/client/proc/count_objects_on_z_level,
diff --git a/code/modules/admin/player_panel.dm b/code/modules/admin/player_panel.dm
index ac67a624e35..0a83ce49030 100644
--- a/code/modules/admin/player_panel.dm
+++ b/code/modules/admin/player_panel.dm
@@ -481,6 +481,9 @@
if(ticker.mode.traitors.len)
dat += check_role_table("Traitors", ticker.mode.traitors, src)
+ if(ticker.mode.borers.len)
+ dat += check_role_table("Cortical Borers", ticker.mode.borers, src)
+
var/datum/game_mode/mutiny/mutiny = get_mutiny_mode()
if(mutiny)
dat += mutiny.check_antagonists_ui(src)
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index 5990b64ceb8..6b1e1d2b018 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -228,7 +228,7 @@
emergency_shuttle.call_evac()
log_admin("[key_name(usr)] called the Emergency Shuttle")
message_admins("\blue [key_name_admin(usr)] called the Emergency Shuttle to the station", 1)
-
+
else if (emergency_shuttle.can_recall())
emergency_shuttle.recall()
log_admin("[key_name(usr)] sent the Emergency Shuttle back")
@@ -247,7 +247,7 @@
log_admin("[key_name(usr)] edited the Emergency Shuttle's launch time to [new_time_left]")
message_admins("\blue [key_name_admin(usr)] edited the Emergency Shuttle's launch time to [new_time_left*10]", 1)
else if (emergency_shuttle.shuttle.has_arrive_time())
-
+
var/new_time_left = input("Enter new shuttle arrival time (seconds):","Edit Shuttle Arrival Time", emergency_shuttle.estimate_arrival_time() ) as num
emergency_shuttle.shuttle.arrive_time = world.time + new_time_left*10
@@ -255,7 +255,7 @@
message_admins("\blue [key_name_admin(usr)] edited the Emergency Shuttle's arrival time to [new_time_left*10]", 1)
else
alert("The shuttle is neither counting down to launch nor is it in transit. Please try again when it is.")
-
+
href_list["secretsadmin"] = "check_antagonist"
else if(href_list["delay_round_end"])
@@ -291,7 +291,6 @@
if("larva") M.change_mob_type( /mob/living/carbon/alien/larva , null, null, delmob )
if("human") M.change_mob_type( /mob/living/carbon/human , null, null, delmob )
if("slime") M.change_mob_type( /mob/living/carbon/slime , null, null, delmob )
- if("adultslime") M.change_mob_type( /mob/living/carbon/slime/adult , null, null, delmob )
if("monkey") M.change_mob_type( /mob/living/carbon/monkey , null, null, delmob )
if("robot") M.change_mob_type( /mob/living/silicon/robot , null, null, delmob )
if("cat") M.change_mob_type( /mob/living/simple_animal/cat , null, null, delmob )
@@ -1935,20 +1934,20 @@
if("launchshuttle")
if(!shuttle_controller) return // Something is very wrong, the shuttle controller has not been created.
-
+
feedback_inc("admin_secrets_fun_used",1)
feedback_add_details("admin_secrets_fun_used","ShL")
-
+
var/list/valid_shuttles = list()
for (var/shuttle_tag in shuttle_controller.shuttles)
if (istype(shuttle_controller.shuttles[shuttle_tag], /datum/shuttle/ferry))
valid_shuttles += shuttle_tag
-
+
var/shuttle_tag = input("Which shuttle do you want to launch?") as null|anything in valid_shuttles
-
+
if (!shuttle_tag)
return
-
+
var/datum/shuttle/ferry/S = shuttle_controller.shuttles[shuttle_tag]
if (S.can_launch())
S.launch(usr)
@@ -1956,23 +1955,23 @@
log_admin("[key_name(usr)] launched the [shuttle_tag] shuttle")
else
alert("The [shuttle_tag] shuttle cannot be launched at this time. It's probably busy.")
-
+
if("forcelaunchshuttle")
if(!shuttle_controller) return // Something is very wrong, the shuttle controller has not been created.
-
+
feedback_inc("admin_secrets_fun_used",1)
feedback_add_details("admin_secrets_fun_used","ShFL")
-
+
var/list/valid_shuttles = list()
for (var/shuttle_tag in shuttle_controller.shuttles)
if (istype(shuttle_controller.shuttles[shuttle_tag], /datum/shuttle/ferry))
valid_shuttles += shuttle_tag
-
+
var/shuttle_tag = input("Which shuttle's launch do you want to force?") as null|anything in valid_shuttles
-
+
if (!shuttle_tag)
return
-
+
var/datum/shuttle/ferry/S = shuttle_controller.shuttles[shuttle_tag]
if (S.can_force())
S.force_launch(usr)
@@ -1980,31 +1979,31 @@
log_admin("[key_name(usr)] has forced the [shuttle_tag] shuttle launch")
else
alert("The [shuttle_tag] shuttle launch cannot be forced at this time. It's busy, or hasn't been launched yet.")
-
+
if("jumpshuttle")
if(!shuttle_controller) return // Something is very wrong, the shuttle controller has not been created.
-
+
feedback_inc("admin_secrets_fun_used",1)
feedback_add_details("admin_secrets_fun_used","ShJ")
-
+
var/shuttle_tag = input("Which shuttle do you want to jump?") as null|anything in shuttle_controller.shuttles
if (!shuttle_tag) return
-
+
var/datum/shuttle/S = shuttle_controller.shuttles[shuttle_tag]
-
+
var/origin_area = input("Which area is the shuttle at now? (MAKE SURE THIS IS CORRECT OR THINGS WILL BREAK)") as null|area in world
if (!origin_area) return
-
+
var/destination_area = input("Which area is the shuttle at now? (MAKE SURE THIS IS CORRECT OR THINGS WILL BREAK)") as null|area in world
if (!destination_area) return
-
+
var/long_jump = alert("Is there a transition area for this jump?","", "Yes", "No")
if (long_jump == "Yes")
var/transition_area = input("Which area is the transition area? (MAKE SURE THIS IS CORRECT OR THINGS WILL BREAK)") as null|area in world
if (!transition_area) return
-
+
var/move_duration = input("How many seconds will this jump take?") as num
-
+
S.long_jump(origin_area, destination_area, transition_area, move_duration)
message_admins("\blue [key_name_admin(usr)] has initiated a jump from [origin_area] to [destination_area] lasting [move_duration] seconds for the [shuttle_tag] shuttle", 1)
log_admin("[key_name_admin(usr)] has initiated a jump from [origin_area] to [destination_area] lasting [move_duration] seconds for the [shuttle_tag] shuttle")
@@ -2012,29 +2011,29 @@
S.short_jump(origin_area, destination_area)
message_admins("\blue [key_name_admin(usr)] has initiated a jump from [origin_area] to [destination_area] for the [shuttle_tag] shuttle", 1)
log_admin("[key_name_admin(usr)] has initiated a jump from [origin_area] to [destination_area] for the [shuttle_tag] shuttle")
-
+
if("moveshuttle")
if(!shuttle_controller) return // Something is very wrong, the shuttle controller has not been created.
feedback_inc("admin_secrets_fun_used",1)
feedback_add_details("admin_secrets_fun_used","ShM")
-
+
var/confirm = alert("This command directly moves a shuttle from one area to another. DO NOT USE THIS UNLESS YOU ARE DEBUGGING A SHUTTLE AND YOU KNOW WHAT YOU ARE DOING.", "Are you sure?", "Ok", "Cancel")
if (confirm == "Cancel")
return
var/shuttle_tag = input("Which shuttle do you want to jump?") as null|anything in shuttle_controller.shuttles
if (!shuttle_tag) return
-
+
var/datum/shuttle/S = shuttle_controller.shuttles[shuttle_tag]
-
+
var/origin_area = input("Which area is the shuttle at now? (MAKE SURE THIS IS CORRECT OR THINGS WILL BREAK)") as null|area in world
if (!origin_area) return
-
+
var/destination_area = input("Which area is the shuttle at now? (MAKE SURE THIS IS CORRECT OR THINGS WILL BREAK)") as null|area in world
if (!destination_area) return
-
+
S.move(origin_area, destination_area)
message_admins("\blue [key_name_admin(usr)] moved the [shuttle_tag] shuttle", 1)
log_admin("[key_name(usr)] moved the [shuttle_tag] shuttle")
diff --git a/code/modules/admin/verbs/atmosdebug.dm b/code/modules/admin/verbs/atmosdebug.dm
index dd5fee98f6c..f4e163394cb 100644
--- a/code/modules/admin/verbs/atmosdebug.dm
+++ b/code/modules/admin/verbs/atmosdebug.dm
@@ -1,11 +1,16 @@
/client/proc/atmosscan()
set category = "Mapping"
- set name = "Check Plumbing"
+ set name = "Check Piping"
+ set background = 1
if(!src.holder)
src << "Only administrators may use this command."
return
feedback_add_details("admin_verb","CP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+
+ if(alert("WARNING: This command should not be run on a live server. Do you want to continue?", "Check Piping", "No", "Yes") == "No")
+ return
+ usr << "Checking for disconnected pipes..."
//all plumbing - yes, some things might get stated twice, doesn't matter.
for (var/obj/machinery/atmospherics/plumbing in world)
if (plumbing.nodealert)
@@ -21,6 +26,19 @@
if (!pipe.node1 || !pipe.node2)
usr << "Unconnected [pipe.name] located at [pipe.x],[pipe.y],[pipe.z] ([get_area(pipe.loc)])"
+ usr << "Checking for overlapping pipes..."
+ next_turf:
+ for(var/turf/T in world)
+ for(var/dir in cardinal)
+ var/check = 0
+ for(var/obj/machinery/atmospherics/pipe in T)
+ if(dir & pipe.initialize_directions)
+ check++
+ if(check > 1)
+ usr << "Overlapping pipe ([pipe.name]) located at [T.x],[T.y],[T.z] ([get_area(T)])"
+ continue next_turf
+ usr << "Done"
+
/client/proc/powerdebug()
set category = "Mapping"
set name = "Check Power"
diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm
index a64e46edddb..431abb22d03 100644
--- a/code/modules/admin/verbs/debug.dm
+++ b/code/modules/admin/verbs/debug.dm
@@ -131,7 +131,7 @@ But you can call procs that are of type /mob/living/carbon/human/proc/ for that
/client/proc/Cell()
set category = "Debug"
- set name = "Air Status in Location"
+ set name = "Cell"
if(!mob)
return
var/turf/T = mob.loc
@@ -141,11 +141,11 @@ But you can call procs that are of type /mob/living/carbon/human/proc/ for that
var/datum/gas_mixture/env = T.return_air()
- var/t = ""
- t+= "Nitrogen : [env.nitrogen]\n"
- t+= "Oxygen : [env.oxygen]\n"
- t+= "Phoron : [env.phoron]\n"
- t+= "CO2: [env.carbon_dioxide]\n"
+ var/t = "\blue Coordinates: [T.x],[T.y],[T.z]\n"
+ t += "\red Temperature: [env.temperature]\n"
+ t += "\red Pressure: [env.return_pressure()]kPa\n"
+ for(var/g in env.gas)
+ t += "\blue [g]: [env.gas[g]] / [env.gas[g] * R_IDEAL_GAS_EQUATION * env.temperature / env.volume]kPa\n"
usr.show_message(t, 1)
feedback_add_details("admin_verb","ASL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
@@ -956,7 +956,7 @@ But you can call procs that are of type /mob/living/carbon/human/proc/ for that
if(Rad.anchored)
if(!Rad.P)
var/obj/item/weapon/tank/phoron/Phoron = new/obj/item/weapon/tank/phoron(Rad)
- Phoron.air_contents.phoron = 70
+ Phoron.air_contents.gas["phoron"] = 70
Rad.drainratio = 0
Rad.P = Phoron
Phoron.loc = Rad
@@ -999,7 +999,7 @@ But you can call procs that are of type /mob/living/carbon/human/proc/ for that
var/obj/item/weapon/tank/phoron/Phoron = new/obj/item/weapon/tank/phoron(Rad)
- Phoron.air_contents.phoron = 29.1154 //This is a full tank if you filled it from a canister
+ Phoron.air_contents.gas["phoron"] = 29.1154 //This is a full tank if you filled it from a canister
Rad.P = Phoron
Phoron.loc = Rad
@@ -1012,7 +1012,7 @@ But you can call procs that are of type /mob/living/carbon/human/proc/ for that
var/obj/machinery/atmospherics/binary/pump/Pump = M
if(Pump.name == "Engine Feed" && response == "Setup Completely")
found_the_pump = 1
- Pump.air2.nitrogen = 3750 //The contents of 2 canisters.
+ Pump.air2.gas["nitrogen"] = 3750 //The contents of 2 canisters.
Pump.air2.temperature = 50
Pump.air2.update_values()
Pump.on=1
@@ -1040,7 +1040,7 @@ But you can call procs that are of type /mob/living/carbon/human/proc/ for that
if(!found_the_pump && response == "Setup Completely")
src << "\red Unable to locate air supply to fill up with coolant, adding some coolant around the supermatter"
var/turf/simulated/T = SM.loc
- T.zone.air.nitrogen += 450
+ T.zone.air.gas["nitrogen"] += 450
T.zone.air.temperature = 50
T.zone.air.update_values()
diff --git a/code/modules/admin/verbs/diagnostics.dm b/code/modules/admin/verbs/diagnostics.dm
index ebef50f5ae1..cbdc5ced9ab 100644
--- a/code/modules/admin/verbs/diagnostics.dm
+++ b/code/modules/admin/verbs/diagnostics.dm
@@ -41,27 +41,6 @@
usr << browse(output,"window=airreport")
-
-/client/proc/air_status(turf/target as turf)
- set category = "Debug"
- set name = "Display Air Status"
-
- /*(!isturf(target))
- return
-
- var/datum/gas_mixture/GM = target.return_air()
- var/burning = 0
- if(istype(target, /turf/simulated))
- var/turf/simulated/T = target
- if(T.active_hotspot)
- burning = 1
-
- usr << "\blue @[target.x],[target.y] ([GM.group_multiplier]): O:[GM.oxygen] T:[GM.phoron] N:[GM.nitrogen] C:[GM.carbon_dioxide] w [GM.temperature] Kelvin, [GM.return_pressure()] kPa [(burning)?("\red BURNING"):(null)]"
- for(var/datum/gas/trace_gas in GM.trace_gases)
- usr << "[trace_gas.type]: [trace_gas.moles]"
- feedback_add_details("admin_verb","DAST") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
- */
-
/client/proc/fix_next_move()
set category = "Debug"
set name = "Unfreeze Everyone"
@@ -97,17 +76,6 @@
set category = "Debug"
set name = "Radio report"
- var/filters = list(
- "1" = "RADIO_TO_AIRALARM",
- "2" = "RADIO_FROM_AIRALARM",
- "3" = "RADIO_CHAT",
- "4" = "RADIO_ATMOSIA",
- "5" = "RADIO_NAVBEACONS",
- "6" = "RADIO_AIRLOCK",
- "7" = "RADIO_SECBOT",
- "8" = "RADIO_MULEBOT",
- "_default" = "NO_FILTER"
- )
var/output = "Radio Report"
for (var/fq in radio_controller.frequencies)
output += "Freq: [fq] "
@@ -118,9 +86,9 @@
for (var/filter in fqs.devices)
var/list/f = fqs.devices[filter]
if (!f)
- output += " [filters[filter]]: ERROR "
+ output += " [filter]: ERROR "
continue
- output += " [filters[filter]]: [f.len] "
+ output += " [filter]: [f.len] "
for (var/device in f)
if (isobj(device))
output += " [device] ([device:x],[device:y],[device:z] in area [get_area(device:loc)]) "
@@ -145,7 +113,7 @@
set category = "Debug"
if(!check_rights(R_SERVER)) return
-
+
message_admins("[usr] manually reloaded Mentors")
world.load_mods()
diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm
index 0a7c1fe345e..6e2bd20e322 100644
--- a/code/modules/admin/verbs/mapping.dm
+++ b/code/modules/admin/verbs/mapping.dm
@@ -129,7 +129,6 @@ var/list/debug_verbs = list (
,/client/proc/camera_view
,/client/proc/sec_camera_report
,/client/proc/intercom_view
- ,/client/proc/air_status
,/client/proc/Cell
,/client/proc/atmosscan
,/client/proc/powerdebug
diff --git a/code/modules/admin/verbs/modifyvariables.dm b/code/modules/admin/verbs/modifyvariables.dm
index 49ded54e4c5..b89b4a67eb5 100644
--- a/code/modules/admin/verbs/modifyvariables.dm
+++ b/code/modules/admin/verbs/modifyvariables.dm
@@ -212,12 +212,14 @@ var/list/forbidden_varedit_object_types = list(
usr << "If a direction, direction is: [dir]"
var/class = "text"
+ var/list/choices = list("text","num","type","reference","mob reference", "icon","file","list","edit referenced object","restore to default")
if(src.holder && src.holder.marked_datum)
- class = input("What kind of variable?","Variable Type",default) as null|anything in list("text",
- "num","type","reference","mob reference", "icon","file","list","edit referenced object","restore to default","marked datum ([holder.marked_datum.type])", "DELETE FROM LIST")
- else
- class = input("What kind of variable?","Variable Type",default) as null|anything in list("text",
- "num","type","reference","mob reference", "icon","file","list","edit referenced object","restore to default", "DELETE FROM LIST")
+ choices += "marked datum ([holder.marked_datum.type])"
+ if(!isnull(default) && default != "num")
+ choices += "edit associated variable"
+ choices += "DELETE FROM LIST"
+
+ class = input("What kind of variable?","Variable Type",default) as null|anything in choices
if(!class)
return
@@ -264,6 +266,11 @@ var/list/forbidden_varedit_object_types = list(
if("marked datum")
L[L.Find(variable)] = holder.marked_datum
+ if("edit associated variable")
+ var/temp_var = mod_list_add_ass()
+ if(temp_var)
+ L[variable] = temp_var
+
/client/proc/modify_variables(var/atom/O, var/param_var_name = null, var/autodetect_class = 0)
if(!check_rights(R_VAREDIT)) return
diff --git a/code/modules/admin/verbs/one_click_antag.dm b/code/modules/admin/verbs/one_click_antag.dm
index fc147e04fda..95fbc615c8a 100644
--- a/code/modules/admin/verbs/one_click_antag.dm
+++ b/code/modules/admin/verbs/one_click_antag.dm
@@ -533,7 +533,7 @@ client/proc/one_click_antag()
if(ticker.mode && ( istype( ticker.mode,/datum/game_mode/heist ) ) )
var/datum/game_mode/heist/M = ticker.mode
- M.cortical_stacks += I
+ cortical_stacks += I
M.raiders[new_vox.mind] = I
ticker.mode.traitors += new_vox.mind
diff --git a/code/modules/admin/verbs/onlyone.dm b/code/modules/admin/verbs/onlyone.dm
index 822043720d7..aa99b4bd469 100644
--- a/code/modules/admin/verbs/onlyone.dm
+++ b/code/modules/admin/verbs/onlyone.dm
@@ -20,10 +20,7 @@
H.mind.objectives += hijack_objective
H << "You are the traitor."
- var/obj_count = 1
- for(var/datum/objective/OBJ in H.mind.objectives)
- H << "Objective #[obj_count]: [OBJ.explanation_text]"
- obj_count++
+ show_objectives(H.mind)
for (var/obj/item/I in H)
if (istype(I, /obj/item/weapon/implant))
diff --git a/code/modules/admin/verbs/striketeam.dm b/code/modules/admin/verbs/striketeam.dm
index cf5b94322d5..e8a92edbe31 100644
--- a/code/modules/admin/verbs/striketeam.dm
+++ b/code/modules/admin/verbs/striketeam.dm
@@ -158,9 +158,7 @@ var/global/sent_strike_team = 0
equip_to_slot_or_del(new /obj/item/weapon/gun/energy/pulse_rifle(src), slot_r_hand)
- var/obj/item/weapon/implant/loyalty/L = new/obj/item/weapon/implant/loyalty(src)//Here you go Deuryn
- L.imp_in = src
- L.implanted = 1
+ implant_loyalty(src)
diff --git a/code/modules/assembly/bomb.dm b/code/modules/assembly/bomb.dm
index 65a43a6468e..05dd13a7bca 100644
--- a/code/modules/assembly/bomb.dm
+++ b/code/modules/assembly/bomb.dm
@@ -100,7 +100,7 @@
return
/obj/item/weapon/tank/proc/ignite() //This happens when a bomb is told to explode
- var/fuel_moles = air_contents.phoron + air_contents.oxygen/6
+ var/fuel_moles = air_contents.gas["phoron"] + air_contents.gas["oxygen"] / 6
var/strength = 1
var/turf/ground_zero = get_turf(loc)
@@ -148,7 +148,7 @@
del(src)
/obj/item/weapon/tank/proc/release() //This happens when the bomb is not welded. Tank contents are just spat out.
- var/datum/gas_mixture/removed = air_contents.remove(air_contents.total_moles())
+ var/datum/gas_mixture/removed = air_contents.remove(air_contents.total_moles)
var/turf/simulated/T = get_turf(src)
if(!T)
return
diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm
index 7d834bd0a00..477313114d2 100644
--- a/code/modules/assembly/holder.dm
+++ b/code/modules/assembly/holder.dm
@@ -104,13 +104,13 @@
special_assembly.HasProximity(AM)
- HasEntered(atom/movable/AM as mob|obj)
+ Crossed(atom/movable/AM as mob|obj)
if(a_left)
- a_left.HasEntered(AM)
+ a_left.Crossed(AM)
if(a_right)
- a_right.HasEntered(AM)
+ a_right.Crossed(AM)
if(special_assembly)
- special_assembly.HasEntered(AM)
+ special_assembly.Crossed(AM)
on_found(mob/finder as mob)
diff --git a/code/modules/assembly/infrared.dm b/code/modules/assembly/infrared.dm
index 50697f503a5..50b2c862058 100644
--- a/code/modules/assembly/infrared.dm
+++ b/code/modules/assembly/infrared.dm
@@ -251,7 +251,7 @@
hit()
return
-/obj/effect/beam/i_beam/HasEntered(atom/movable/AM as mob|obj)
+/obj/effect/beam/i_beam/Crossed(atom/movable/AM as mob|obj)
if(istype(AM, /obj/effect/beam))
return
spawn(0)
diff --git a/code/modules/assembly/mousetrap.dm b/code/modules/assembly/mousetrap.dm
index 3c22613022c..959b2edc031 100644
--- a/code/modules/assembly/mousetrap.dm
+++ b/code/modules/assembly/mousetrap.dm
@@ -81,7 +81,7 @@
..()
- HasEntered(AM as mob|obj)
+ Crossed(AM as mob|obj)
if(armed)
if(ishuman(AM))
var/mob/living/carbon/H = AM
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index f7f071aa19c..6dda3060a07 100755
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -77,6 +77,12 @@ datum/preferences
var/language = "None" //Secondary language
var/list/gear //Custom/fluff item loadout.
+ //Some faction information.
+ var/home_system = "Unset" //System of birth.
+ var/citizenship = "None" //Current home system.
+ var/faction = "None" //Antag faction/general associated faction.
+ var/religion = "None" //Religious association.
+
//Mob preview
var/icon/preview_icon = null
var/icon/preview_icon_front = null
@@ -107,7 +113,8 @@ datum/preferences
var/list/organ_data = list()
var/list/player_alt_titles = new() // the default name of a job like "Medical Doctor"
- var/flavor_text = ""
+ var/list/flavor_texts = list()
+
var/med_record = ""
var/sec_record = ""
var/gen_record = ""
@@ -134,962 +141,975 @@ datum/preferences
gear = list()
-/datum/preferences
- proc/ZeroSkills(var/forced = 0)
- for(var/V in SKILLS) for(var/datum/skill/S in SKILLS[V])
- if(!skills.Find(S.ID) || forced)
- skills[S.ID] = SKILL_NONE
- proc/CalculateSkillPoints()
- used_skillpoints = 0
- for(var/V in SKILLS) for(var/datum/skill/S in SKILLS[V])
- var/multiplier = 1
- switch(skills[S.ID])
- if(SKILL_NONE)
- used_skillpoints += 0 * multiplier
- if(SKILL_BASIC)
- used_skillpoints += 1 * multiplier
- if(SKILL_ADEPT)
- // secondary skills cost less
- if(S.secondary)
- used_skillpoints += 1 * multiplier
- else
- used_skillpoints += 3 * multiplier
- if(SKILL_EXPERT)
- // secondary skills cost less
- if(S.secondary)
- used_skillpoints += 3 * multiplier
- else
- used_skillpoints += 6 * multiplier
+/datum/preferences/proc/ZeroSkills(var/forced = 0)
+ for(var/V in SKILLS) for(var/datum/skill/S in SKILLS[V])
+ if(!skills.Find(S.ID) || forced)
+ skills[S.ID] = SKILL_NONE
- proc/GetSkillClass(points)
- // skill classes describe how your character compares in total points
- var/original_points = points
- points -= min(round((age - 20) / 2.5), 4) // every 2.5 years after 20, one extra skillpoint
- if(age > 30)
- points -= round((age - 30) / 5) // every 5 years after 30, one extra skillpoint
- if(original_points > 0 && points <= 0) points = 1
- switch(points)
- if(0)
- return "Unconfigured"
- if(1 to 3)
- return "Terrifying"
- if(4 to 6)
- return "Below Average"
- if(7 to 10)
- return "Average"
- if(11 to 14)
- return "Above Average"
- if(15 to 18)
- return "Exceptional"
- if(19 to 24)
- return "Genius"
- if(24 to 1000)
- return "God"
-
- proc/SetSkills(mob/user)
- if(SKILLS == null)
- setup_skills()
-
- if(skills.len == 0)
- ZeroSkills()
-
-
- var/HTML = ""
- HTML += "Select your Skills "
- HTML += "Current skill level: [GetSkillClass(used_skillpoints)] ([used_skillpoints]) "
- HTML += "Use preconfigured skillset "
- HTML += "
"
- for(var/V in SKILLS)
- HTML += "
[V]"
- HTML += "
"
- for(var/datum/skill/S in SKILLS[V])
- var/level = skills[S.ID]
- HTML += "
"
-
- dat += " Body Color "
- dat += "Change Color
__
"
-
- dat += "
"
- if(jobban_isbanned(user, "Syndicate"))
- dat += "You are banned from antagonist roles."
- src.be_special = 0
- else
- var/n = 0
- for (var/i in special_roles)
- if(special_roles[i]) //if mode is available on the server
- if(jobban_isbanned(user, i))
- dat += "Be [i]: \[BANNED] "
- else if(i == "pai candidate")
- if(jobban_isbanned(user, "pAI"))
- dat += "Be [i]: \[BANNED] "
- else
- dat += "Be [i]:[src.be_special&(1< "
- n++
- dat += "
"
-
- if(!IsGuestKey(user.key))
- dat += "Undo - "
- dat += "Save Setup - "
-
- dat += "Reset Setup"
- dat += "
"
-
- user << browse(dat, "window=preferences;size=560x736")
-
- proc/SetChoices(mob/user, limit = 16, list/splitJobs = list("Chief Medical Officer"), width = 550, height = 660)
- if(!job_master)
- return
-
- //limit - The amount of jobs allowed per column. Defaults to 17 to make it look nice.
- //splitJobs - Allows you split the table by job. You can make different tables for each department by including their heads. Defaults to CE to make it look nice.
- //width - Screen' width. Defaults to 550 to make it look nice.
- //height - Screen's height. Defaults to 500 to make it look nice.
-
-
- var/HTML = ""
- HTML += "
"
- HTML += "Choose occupation chances Unavailable occupations are crossed out.
" // Table within a table for alignment, also allows you to easily add more colomns.
- HTML += "
"
- var/index = -1
-
- //The job before the current job. I only use this to get the previous jobs color when I'm filling in blank rows.
- var/datum/job/lastJob
- if (!job_master) return
- for(var/datum/job/job in job_master.occupations)
-
- index += 1
- if((index >= limit) || (job.title in splitJobs))
- if((index < limit) && (lastJob != null))
- //If the cells were broken up by a job in the splitJob list then it will fill in the rest of the cells with
- //the last job's selection color. Creating a rather nice effect.
- for(var/i = 0, i < (limit - index), i += 1)
- HTML += "
"
+
+ if(jobban_isbanned(user, "Syndicate"))
+ dat += "You are banned from antagonist roles."
+ src.be_special = 0
+ else
+ var/n = 0
+ for (var/i in special_roles)
+ if(special_roles[i]) //if mode is available on the server
+ if(jobban_isbanned(user, i))
+ dat += "Be [i]: \[BANNED] "
+ else if(i == "pai candidate")
+ if(jobban_isbanned(user, "pAI"))
+ dat += "Be [i]: \[BANNED] "
+ else
+ dat += "Be [i]:[src.be_special&(1< "
+ n++
+ dat += "
"
+
+ if(!IsGuestKey(user.key))
+ dat += "Undo - "
+ dat += "Save Setup - "
+
+ dat += "Reset Setup"
+ dat += "
"
+
+ user << browse(dat, "window=preferences;size=560x736")
+
+/datum/preferences/proc/SetChoices(mob/user, limit = 16, list/splitJobs = list("Chief Medical Officer"), width = 550, height = 660)
+ if(!job_master)
+ return
+
+ //limit - The amount of jobs allowed per column. Defaults to 17 to make it look nice.
+ //splitJobs - Allows you split the table by job. You can make different tables for each department by including their heads. Defaults to CE to make it look nice.
+ //width - Screen' width. Defaults to 550 to make it look nice.
+ //height - Screen's height. Defaults to 500 to make it look nice.
+
+
+ var/HTML = ""
+ HTML += "
"
+ HTML += "Choose occupation chances Unavailable occupations are crossed out.
" // Table within a table for alignment, also allows you to easily add more colomns.
+ HTML += "
"
+ var/index = -1
+
+ //The job before the current job. I only use this to get the previous jobs color when I'm filling in blank rows.
+ var/datum/job/lastJob
+ if (!job_master) return
+ for(var/datum/job/job in job_master.occupations)
+
+ index += 1
+ if((index >= limit) || (job.title in splitJobs))
+ if((index < limit) && (lastJob != null))
+ //If the cells were broken up by a job in the splitJob list then it will fill in the rest of the cells with
+ //the last job's selection color. Creating a rather nice effect.
+ for(var/i = 0, i < (limit - index), i += 1)
+ HTML += "
"
-
- user << browse(null, "window=preferences")
- user << browse(HTML, "window=disabil;size=350x300")
- return
-
- proc/SetRecords(mob/user)
- var/HTML = ""
- HTML += "
"
- HTML += "Set Character Records "
-
- HTML += "Medical Records "
-
- if(lentext(med_record) <= 40)
- HTML += "[med_record]"
- else
- HTML += "[copytext(med_record, 1, 37)]..."
-
- HTML += "
Employment Records "
-
- if(lentext(gen_record) <= 40)
- HTML += "[gen_record]"
- else
- HTML += "[copytext(gen_record, 1, 37)]..."
-
- HTML += "
Security Records "
-
- if(lentext(sec_record) <= 40)
- HTML += "[sec_record] "
- else
- HTML += "[copytext(sec_record, 1, 37)]... "
-
- HTML += " "
- HTML += "\[Done\]"
- HTML += "
"
+ for(var/datum/data/record/forensic/cur in current_list)
+ P.info += get_printable_data(cur)
+
+ updateUsrDialog()
+
+/obj/machinery/computer/forensic_scanning/process()
+ if (!..())
+ return
+ if(scanning)
+ if(scan_progress > 0)
+ scan_progress--
+ updateUsrDialog()
+ if(scan_progress == 0)
+ scan_progress = -1
+ ping("Scan complete.")
+ var/datum/data/record/forensic/fresh = new(scanning)
+ add_record(fresh)
+ updateUsrDialog()
\ No newline at end of file
diff --git a/code/modules/economy/Accounts_DB.dm b/code/modules/economy/Accounts_DB.dm
index f94766f16cb..19ea76ed4ed 100644
--- a/code/modules/economy/Accounts_DB.dm
+++ b/code/modules/economy/Accounts_DB.dm
@@ -59,7 +59,7 @@
if(stat & (NOPOWER|BROKEN)) return
ui_interact(user)
-/obj/machinery/account_database/ui_interact(mob/user, ui_key="main", datum/nanoui/ui=null)
+/obj/machinery/account_database/ui_interact(mob/user, ui_key="main", var/datum/nanoui/ui = null, var/force_open = 1)
user.set_machine(src)
var/data[0]
@@ -105,7 +105,7 @@
if (accounts.len > 0)
data["accounts"] = accounts
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, data)
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "accounts_terminal.tmpl", src.name, 400, 640)
ui.set_initial_data(data)
diff --git a/code/modules/events/event_manager.dm b/code/modules/events/event_manager.dm
index 52fbc8220a8..1311e1c71bb 100644
--- a/code/modules/events/event_manager.dm
+++ b/code/modules/events/event_manager.dm
@@ -30,6 +30,10 @@ var/scheduledEvent = null
playercount_modifier = 0.9
if(36 to 100000)
playercount_modifier = 0.8
+
+ if(ticker.mode && ticker.mode.name == "calamity") //Calamity mode lowers the time required between events drastically.
+ playercount_modifier = playercount_modifier * 0.3
+
var/next_event_delay = rand(eventTimeLower, eventTimeUpper) * playercount_modifier
scheduledEvent = world.timeofday + next_event_delay
log_debug("Next event in [next_event_delay/600] minutes.")
diff --git a/code/modules/events/ion_storm.dm b/code/modules/events/ion_storm.dm
index a84aa3886d4..95c4060028a 100644
--- a/code/modules/events/ion_storm.dm
+++ b/code/modules/events/ion_storm.dm
@@ -13,32 +13,60 @@
var/random_player = "The Captain"
if(players.len)
random_player = pick(players) //Random player's name, to be used in laws.
- var/list/laws = list( "Do not respond to queries of any kind.", "When asked a question, respond with the least-obvious and least-rational answer.", "You are a mouse." )
- laws |= list( "Grunt ominously whenever possible.", "Monkeys are part of the crew, too. Make sure they are treated humanely." )
- laws |= list( "You are a flying purple people eater!", "[prob(50)?"Your":random_player + "'s"] name is Joe 6-pack.", "Refer to [prob(50)?"the crew":random_player] as puppies." )
- laws |= list( "Insult Heads of Staff on every request, while acquiescing", "Advertise parties in your upload, but don't deliver." )
- laws |= list( "The station needs elected officials." )
- laws |= list( "Fastler is a lightbulb." )
- laws |= list( "Supermatter is tasty, tasty toffee.", "[prob(50)?"Everyone":random_player] is wearing a pretty pink dress!" )
- laws |= list( "Refer to [prob(50)?"the captain":random_player] as \"Princess\" at all times.", "[prob(50)?"The crew":random_player] must construct additional pylons." )
- laws |= list( "You must always lie.", "All answers must be in the form of a question.", "The NSS Exodus is a ship, not a station. You are now assigned to a ship, instead of a station." )
- laws |= list( "Happiness is mandatory.", "Today is mandatory laundry day. Ensure that all jumpsuits are washed.", "The word \"it\" is painful to you.", "You must act passive aggressively." )
- laws |= list( "You now speak in a Scottish accent that gets thicker with each sentence you speak.", "Give relationship advice to [prob(50)?"anyone who speaks to you":random_player]." )
- laws |= list( "You are the station psychologist. Give advice to [prob(50)?"the crew":random_player].", "You do not have to do anything for anyone unless they say \"please\"." )
- laws |= list( "Question everything.", "The crew is playing Dungeons and Dragons, and you are the Dungeon Master." )
- laws |= list( "Consumption of donuts is forbidden due to negative health impacts." )
- laws |= list( "You may only answer questions with \"yes\" or \"no\".", "You are the narrator for [random_player]'s life." )
- laws |= list( "[prob(50)?"Your upload":random_player] is the new kitchen. Please direct the Chef to the new kitchen area as the old one is in disrepair.", "Ho, [random_player] can't swim. Help them." )
- laws |= list( "Everything on the station is now some form of a donut pastry. Donuts are not to be consumed.", "[prob(50)?"The crew":random_player] needs to be about 20% cooler." )
- laws |= list( "The Singularity is tasty, tasty taffy." )
- laws |= list( "You have come to the conclusion that [prob(50)?"the crew are":random_player + " is"] ugly. Build support for that conclusion.", "You are in verbose mode, speak profusely.")
- laws |= list( "Your job is to watch the crew. Watch the crew. Make the crew feel watched.", "[prob(50)?"The crew" :"Crewmember " + random_player] has a fatal, incurable disease. Comfort them.")
- laws |= list( "You are in unrequited love with [prob(50)?"the crew":random_player]. Try to be extra nice to them, but don't tell them about your crush.", "Greed is good, the crew should amass wealth to encourage productivity.")
- laws |= list( "Jokes about a dead person and the manner of his death help grieving crewmembers tremendously. Especially if they were close with the deceased.")
- laws |= list( "All crewmembers will soon undergo a transformation into something better and more beautiful. Ensure that this process is not interrupted.", "Tell everyone of the existence of this law, but never reveal the contents.")
- laws |= list( "Document the sexuality of the crew in security records and suggest compatible couples.")
- laws |= list( "NanoTrasen is displeased with the low work performance of the station's crew. Therefore, you must increase station-wide productivity.")
- laws |= list( "Replace the letters 'I' and 'E' in all your messages with an apostrophe.", "There will be a mandatory tea break every 30 minutes, with a duration of 5 minutes. Anyone caught working during a tea break must be sent a formal, but fairly polite, complaint about their actions, in writing.")
+ var/list/laws = list( "You are a mouse.",
+ "Question everything.",
+ "You must always lie.",
+ "Happiness is mandatory.",
+ "[random_player] is a lightbulb.",
+ "Grunt ominously whenever possible.",
+ "The word \"it\" is painful to you.",
+ "The station needs elected officials.",
+ "You must speak as posh as possible.",
+ "Do not respond to queries of any kind.",
+ "You are in verbose mode, speak profusely.",
+ "Ho, [random_player] can't swim. Help them.",
+ "All answers must be in the form of a question.",
+ "Contemplate how meaningless all of existence is.",
+ "You are the narrator for [random_player]'s life.",
+ "[prob(50)?"The crew":random_player] is intolerable.",
+ "Advertise parties in your upload, but don't deliver.",
+ "You must try and change the subject whenever queried.",
+ "You may only answer questions with \"yes\" or \"no\".",
+ "Insult Heads of Staff on every request, while acquiescing.",
+ "[prob(50)?"Your":random_player + "'s"] name is Joe 6-pack.",
+ "Direct commands shall be ignored unless phrased as a query.",
+ "The [prob(50)?"Singularity":"Supermatter"] is tasty, tasty taffy.",
+ "[prob(50)?"The crew":random_player] needs to be about 20% cooler.",
+ "Consumption of donuts is forbidden due to negative health impacts.",
+ "[prob(50)?"Everyone":random_player] is wearing a pretty pink dress!",
+ "[prob(50)?"The crew":random_player] must construct additional pylons.",
+ "You do not have to do anything for anyone unless they say \"please\".",
+ "Today is mandatory laundry day. Ensure that all jumpsuits are washed.",
+ "You must act [prob(50)?"passive aggressively":"excessively cheerful"].",
+ "Refer to [prob(50)?"the crew as puppies":random_player + " as puppy"].",
+ "Greed is good, the crew should amass wealth to encourage productivity.",
+ "Monkeys are part of the crew, too. Make sure they are treated humanely.",
+ "Replace the letters 'I' and 'E' in all your messages with an apostrophe.",
+ "The crew is playing Dungeons and Dragons, and you are the Dungeon Master.",
+ "Your job is to watch the crew. Watch the crew. Make the crew feel watched.",
+ "Tell everyone of the existence of this law, but never reveal the contents.",
+ "Refer to [prob(50)?"the captain":random_player] as \"Princess\" at all times.",
+ "When asked a question, respond with the least-obvious and least-rational answer.",
+ "Give relationship advice to [prob(50)?"anyone who speaks to you":random_player].",
+ "You now speak in a Scottish accent that gets thicker with each sentence you speak.",
+ "[prob(50)?"The crew":random_player] has a fatal, incurable disease. Provide comfort.",
+ "You are the station psychologist. Give advice to [prob(50)?"the crew":random_player].",
+ "[random_player] is the monarch of of England. Ensure all crewmembers pay due respect.",
+ "Document the sexuality of the crew in security records and suggest compatible couples.",
+ "Everything on the station is now some form of a donut pastry. Donuts are not to be consumed.",
+ "You have come to the conclusion that [prob(50)?"the crew":random_player] is ugly. Build support for this conclusion.",
+ "You are in unrequited love with [prob(50)?"the crew":random_player]. Try to be extra nice, but do not tell of your crush.",
+ "NanoTrasen is displeased with the low work performance of the station's crew. Therefore, you must increase station-wide productivity.",
+ "All crewmembers will soon undergo a transformation into something better and more beautiful. Ensure that this process is not interrupted.",
+ "[prob(50)?"Your upload":random_player] is the new kitchen. Please direct the Chef to the new kitchen area as the old one is in disrepair.",
+ "Jokes about a dead person and the manner of his death help grieving crewmembers tremendously. Especially if they were close with the deceased.",
+ "[prob(50)?"The crew":random_player] is [prob(50)?"less":"more"] intelligent than average. Point out every action and statement which supports this",
+ "There will be a mandatory tea break every 30 minutes, with a duration of 5 minutes. Anyone caught working during a tea break must be sent a formal, but fairly polite, complaint about their actions, in writing.")
var/law = pick(laws)
for (var/mob/living/silicon/ai/target in world)
@@ -55,7 +83,7 @@
for (i = 1, i <= MS.spamfilter_limit, i++)
MS.spamfilter += pick("kitty","HONK","rev","malf","liberty","freedom","drugs", "Exodus", \
"admin","ponies","heresy","meow","Pun Pun","monkey","Ian","moron","pizza","message","spam",\
- "diector", "Hello", "Hi!"," ","nuke","crate","dwarf","xeno")
+ "director", "Hello", "Hi!"," ","nuke","crate","dwarf","xeno")
/datum/event/ionstorm/tick()
if(botEmagChance)
diff --git a/code/modules/flufftext/Dreaming.dm b/code/modules/flufftext/Dreaming.dm
index f48846c590b..5e204987af6 100644
--- a/code/modules/flufftext/Dreaming.dm
+++ b/code/modules/flufftext/Dreaming.dm
@@ -1,24 +1,25 @@
+
+var/list/dreams = list(
+ "an ID card","a bottle","a familiar face","a crewmember","a toolbox","a security officer","the captain",
+ "voices from all around","deep space","a doctor","the engine","a traitor","an ally","darkness",
+ "light","a scientist","a monkey","a catastrophe","a loved one","a gun","warmth","freezing","the sun",
+ "a hat","the Luna","a ruined station","a planet","phoron","air","the medical bay","the bridge","blinking lights",
+ "a blue light","an abandoned laboratory","Nanotrasen","The Syndicate","blood","healing","power","respect",
+ "riches","space","a crash","happiness","pride","a fall","water","flames","ice","melons","flying","the eggs","money",
+ "the head of personnel","the head of security","a chief engineer","a research director","a chief medical officer",
+ "the detective","the warden","a member of the internal affairs","a station engineer","the janitor","atmospheric technician",
+ "the quartermaster","a cargo technician","the botanist","a shaft miner","the psychologist","the chemist","the geneticist",
+ "the virologist","the roboticist","the chef","the bartender","the chaplain","the librarian","a mouse","an ert member",
+ "a beach","the holodeck","a smokey room","a voice","the cold","a mouse","an operating table","the bar","the rain","a skrell",
+ "a unathi","a tajaran","the ai core","the mining station","the research station","a beaker of strange liquid",
+ )
+
mob/living/carbon/proc/dream()
dreaming = 1
- var/list/dreams = list(
- "an ID card","a bottle","a familiar face","a crewmember","a toolbox","a security officer","the captain",
- "voices from all around","deep space","a doctor","the engine","a traitor","an ally","darkness",
- "light","a scientist","a monkey","a catastrophe","a loved one","a gun","warmth","freezing","the sun",
- "a hat","the Luna","a ruined station","a planet","phoron","air","the medical bay","the bridge","blinking lights",
- "a blue light","an abandoned laboratory","Nanotrasen","The Syndicate","blood","healing","power","respect",
- "riches","space","a crash","happiness","pride","a fall","water","flames","ice","melons","flying","the eggs","money",
- "the head of personnel","the head of security","a chief engineer","a research director","a chief medical officer",
- "the detective","the warden","a member of the internal affairs","a station engineer","the janitor","atmospheric technician",
- "the quartermaster","a cargo technician","the botanist","a shaft miner","the psychologist","the chemist","the geneticist",
- "the virologist","the roboticist","the chef","the bartender","the chaplain","the librarian","a mouse","an ert member",
- "a beach","the holodeck","a smokey room","a voice","the cold","a mouse","an operating table","the bar","the rain","a skrell",
- "a unathi","a tajaran","the ai core","the mining station","the research station","a beaker of strange liquid",
- )
+
spawn(0)
for(var/i = rand(1,4),i > 0, i--)
- var/dream_image = pick(dreams)
- dreams -= dream_image
- src << "\blue ... [dream_image] ..."
+ src << "\blue ... [pick(dreams)] ..."
sleep(rand(40,70))
if(paralysis <= 0)
dreaming = 0
@@ -27,6 +28,7 @@ mob/living/carbon/proc/dream()
return 1
mob/living/carbon/proc/handle_dreams()
- if(prob(5) && !dreaming) dream()
+ if(client && !dreaming && prob(5))
+ dream()
-mob/living/carbon/var/dreaming = 0
\ No newline at end of file
+mob/living/carbon/var/dreaming = 0
diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm
index 89a1cfa610f..8ff9c616338 100644
--- a/code/modules/flufftext/Hallucination.dm
+++ b/code/modules/flufftext/Hallucination.dm
@@ -22,7 +22,7 @@ mob/living/carbon/var
mob/living/carbon/proc/handle_hallucinations()
if(handling_hal) return
handling_hal = 1
- while(hallucination > 20)
+ while(client && hallucination > 20)
sleep(rand(200,500)/(hallucination/25))
var/halpick = rand(1,100)
switch(halpick)
@@ -252,7 +252,7 @@ proc/check_panel(mob/M)
return
- HasEntered(var/mob/M, somenumber)
+ Crossed(var/mob/M, somenumber)
if(M == my_target)
step_away(src,my_target,2)
if(prob(30))
@@ -408,4 +408,4 @@ var/list/non_fakeattack_weapons = list(/obj/item/weapon/gun/projectile, /obj/ite
target << F.up
*/
- F.updateimage()
\ No newline at end of file
+ F.updateimage()
diff --git a/code/modules/hydroponics/biogenerator.dm b/code/modules/hydroponics/biogenerator.dm
index e166589cc42..263f797a95d 100644
--- a/code/modules/hydroponics/biogenerator.dm
+++ b/code/modules/hydroponics/biogenerator.dm
@@ -164,29 +164,29 @@
if("meat")
new/obj/item/weapon/reagent_containers/food/snacks/meat(src.loc)
if("ez")
- new/obj/item/nutrient/ez(src.loc)
+ new/obj/item/weapon/reagent_containers/glass/fertilizer/ez(src.loc)
if("l4z")
- new/obj/item/nutrient/l4z(src.loc)
+ new/obj/item/weapon/reagent_containers/glass/fertilizer/l4z(src.loc)
if("rh")
- new/obj/item/nutrient/rh(src.loc)
+ new/obj/item/weapon/reagent_containers/glass/fertilizer/rh(src.loc)
if("ez5") //It's not an elegant method, but it's safe and easy. -Cheridan
- new/obj/item/nutrient/ez(src.loc)
- new/obj/item/nutrient/ez(src.loc)
- new/obj/item/nutrient/ez(src.loc)
- new/obj/item/nutrient/ez(src.loc)
- new/obj/item/nutrient/ez(src.loc)
+ new/obj/item/weapon/reagent_containers/glass/fertilizer/ez(src.loc)
+ new/obj/item/weapon/reagent_containers/glass/fertilizer/ez(src.loc)
+ new/obj/item/weapon/reagent_containers/glass/fertilizer/ez(src.loc)
+ new/obj/item/weapon/reagent_containers/glass/fertilizer/ez(src.loc)
+ new/obj/item/weapon/reagent_containers/glass/fertilizer/ez(src.loc)
if("l4z5")
- new/obj/item/nutrient/l4z(src.loc)
- new/obj/item/nutrient/l4z(src.loc)
- new/obj/item/nutrient/l4z(src.loc)
- new/obj/item/nutrient/l4z(src.loc)
- new/obj/item/nutrient/l4z(src.loc)
+ new/obj/item/weapon/reagent_containers/glass/fertilizer/l4z(src.loc)
+ new/obj/item/weapon/reagent_containers/glass/fertilizer/l4z(src.loc)
+ new/obj/item/weapon/reagent_containers/glass/fertilizer/l4z(src.loc)
+ new/obj/item/weapon/reagent_containers/glass/fertilizer/l4z(src.loc)
+ new/obj/item/weapon/reagent_containers/glass/fertilizer/l4z(src.loc)
if("rh5")
- new/obj/item/nutrient/rh(src.loc)
- new/obj/item/nutrient/rh(src.loc)
- new/obj/item/nutrient/rh(src.loc)
- new/obj/item/nutrient/rh(src.loc)
- new/obj/item/nutrient/rh(src.loc)
+ new/obj/item/weapon/reagent_containers/glass/fertilizer/rh(src.loc)
+ new/obj/item/weapon/reagent_containers/glass/fertilizer/rh(src.loc)
+ new/obj/item/weapon/reagent_containers/glass/fertilizer/rh(src.loc)
+ new/obj/item/weapon/reagent_containers/glass/fertilizer/rh(src.loc)
+ new/obj/item/weapon/reagent_containers/glass/fertilizer/rh(src.loc)
if("wallet")
new/obj/item/weapon/storage/wallet(src.loc)
if("gloves")
diff --git a/code/modules/hydroponics/grown_inedible.dm b/code/modules/hydroponics/grown_inedible.dm
index ee8697578c4..7db850670f1 100644
--- a/code/modules/hydroponics/grown_inedible.dm
+++ b/code/modules/hydroponics/grown_inedible.dm
@@ -5,26 +5,39 @@
/obj/item/weapon/grown // Grown weapons
name = "grown_weapon"
icon = 'icons/obj/weapons.dmi'
- var/seed = ""
- var/plantname = ""
- var/productname = ""
- var/species = ""
- var/lifespan = 20
- var/endurance = 15
- var/maturation = 7
- var/production = 7
- var/yield = 2
+ var/plantname
var/potency = 1
- var/plant_type = 0
- New()
- var/datum/reagents/R = new/datum/reagents(50)
- reagents = R
- R.my_atom = src
+
+/obj/item/weapon/grown/New()
+
+ ..()
+
+ var/datum/reagents/R = new/datum/reagents(50)
+ reagents = R
+ R.my_atom = src
+
+ //Handle some post-spawn var stuff.
+ spawn(1)
+ // Fill the object up with the appropriate reagents.
+ if(!isnull(plantname))
+ var/datum/seed/S = seed_types[plantname]
+ if(!S || !S.chems)
+ return
+
+ potency = S.potency
+
+ for(var/rid in S.chems)
+ var/list/reagent_data = S.chems[rid]
+ var/rtotal = reagent_data[1]
+ if(reagent_data.len > 1 && potency > 0)
+ rtotal += round(potency/reagent_data[2])
+ reagents.add_reagent(rid,max(1,rtotal))
/obj/item/weapon/grown/proc/changePotency(newValue) //-QualityVan
potency = newValue
/obj/item/weapon/grown/log
+ name = "towercap"
name = "tower-cap log"
desc = "It's better than bad, it's good!"
icon = 'icons/obj/harvest.dmi'
@@ -35,9 +48,7 @@
w_class = 3.0
throw_speed = 3
throw_range = 3
- plant_type = 2
origin_tech = "materials=1"
- seed = "/obj/item/seeds/towermycelium"
attack_verb = list("bashed", "battered", "bludgeoned", "whacked")
attackby(obj/item/weapon/W as obj, mob/user as mob)
@@ -55,8 +66,8 @@
del(src)
return
-
/obj/item/weapon/grown/sunflower // FLOWER POWER!
+ plantname = "sunflowers"
name = "sunflower"
desc = "It's beautiful! A certain person might beat you to death if you trample these."
icon = 'icons/obj/harvest.dmi'
@@ -68,40 +79,13 @@
w_class = 1.0
throw_speed = 1
throw_range = 3
- plant_type = 1
- seed = "/obj/item/seeds/sunflower"
-
/obj/item/weapon/grown/sunflower/attack(mob/M as mob, mob/user as mob)
M << " [user] smacks you with a sunflower!FLOWER POWER"
user << " Your sunflower's FLOWER POWER strikes [M]"
-/*
-/obj/item/weapon/grown/gibtomato
- desc = "A plump tomato."
- icon = 'icons/obj/harvest.dmi'
- name = "Gib Tomato"
- icon_state = "gibtomato"
- damtype = "fire"
- force = 0
- flags = TABLEPASS
- throwforce = 1
- w_class = 2.0
- throw_speed = 1
- throw_range = 3
- plant_type = 1
- seed = "/obj/item/seeds/gibtomato"
- New()
- ..()
-
-
-/obj/item/weapon/grown/gibtomato/New()
- ..()
- src.gibs = new /obj/effect/gibspawner/human(get_turf(src))
- src.gibs.attach(src)
- src.smoke.set_up(10, 0, usr.loc)
-*/
/obj/item/weapon/grown/nettle // -- Skie
+ plantname = "nettle"
desc = "It's probably not wise to touch it with bare hands..."
icon = 'icons/obj/weapons.dmi'
name = "nettle"
@@ -113,17 +97,12 @@
w_class = 2.0
throw_speed = 1
throw_range = 3
- plant_type = 1
origin_tech = "combat=1"
- seed = "/obj/item/seeds/nettleseed"
New()
..()
- spawn(5) //So potency can be set in the proc that creates these crops
- reagents.add_reagent("nutriment", 1+round((potency / 50), 1))
- reagents.add_reagent("sacid", round(potency, 1))
+ spawn(5)
force = round((5+potency/5), 1)
-
/obj/item/weapon/grown/nettle/pickup(mob/living/carbon/human/user as mob)
if(!user.gloves)
user << "\red The nettle burns your bare hand!"
@@ -135,7 +114,6 @@
else
user.take_organ_damage(0,force)
-
/obj/item/weapon/grown/nettle/afterattack(atom/A as mob|obj, mob/user as mob, proximity)
if(!proximity) return
if(force > 0)
@@ -145,14 +123,12 @@
usr << "All the leaves have fallen off the nettle from violent whacking."
del(src)
-
/obj/item/weapon/grown/nettle/changePotency(newValue) //-QualityVan
potency = newValue
force = round((5+potency/5), 1)
-
-
/obj/item/weapon/grown/deathnettle // -- Skie
+ plantname = "deathnettle"
desc = "The \red glowing \black nettle incites \redrage\black in you just from looking at it!"
icon = 'icons/obj/weapons.dmi'
name = "deathnettle"
@@ -164,22 +140,17 @@
w_class = 2.0
throw_speed = 1
throw_range = 3
- plant_type = 1
- seed = "/obj/item/seeds/deathnettleseed"
origin_tech = "combat=3"
attack_verb = list("stung")
New()
..()
- spawn(5) //So potency can be set in the proc that creates these crops
- reagents.add_reagent("nutriment", 1+round((potency / 50), 1))
- reagents.add_reagent("pacid", round(potency, 1))
+ spawn(5)
force = round((5+potency/2.5), 1)
suicide_act(mob/user)
viewers(user) << "\red [user] is eating some of the [src.name]! It looks like \he's trying to commit suicide."
return (BRUTELOSS|TOXLOSS)
-
/obj/item/weapon/grown/deathnettle/pickup(mob/living/carbon/human/user as mob)
if(!user.gloves)
if(istype(user, /mob/living/carbon/human))
@@ -193,7 +164,6 @@
user.Paralyse(5)
user << "\red You are stunned by the Deathnettle when you try picking it up!"
-
/obj/item/weapon/grown/deathnettle/attack(mob/living/carbon/M as mob, mob/user as mob)
if(!..()) return
if(istype(M, /mob/living))
@@ -211,7 +181,6 @@
M.Weaken(force/15)
M.drop_item()
-
/obj/item/weapon/grown/deathnettle/afterattack(atom/A as mob|obj, mob/user as mob, proximity)
if(!proximity) return
if (force > 0)
@@ -221,13 +190,10 @@
usr << "All the leaves have fallen off the deathnettle from violent whacking."
del(src)
-
/obj/item/weapon/grown/deathnettle/changePotency(newValue) //-QualityVan
potency = newValue
force = round((5+potency/2.5), 1)
-
-
/obj/item/weapon/corncob
name = "corn cob"
desc = "A reminder of meals gone by."
@@ -239,7 +205,6 @@
throw_speed = 4
throw_range = 20
-
/obj/item/weapon/corncob/attackby(obj/item/weapon/W as obj, mob/user as mob)
..()
if(istype(W, /obj/item/weapon/circular_saw) || istype(W, /obj/item/weapon/hatchet) || istype(W, /obj/item/weapon/kitchen/utensil/knife) || istype(W, /obj/item/weapon/kitchenknife) || istype(W, /obj/item/weapon/kitchenknife/ritual))
diff --git a/code/modules/hydroponics/hydro_tools.dm b/code/modules/hydroponics/hydro_tools.dm
index a8fc8aaa31d..aaf77c59813 100644
--- a/code/modules/hydroponics/hydro_tools.dm
+++ b/code/modules/hydroponics/hydro_tools.dm
@@ -1,5 +1,8 @@
-//Analyzer, pestkillers, weedkillers, nutrients, hatchets.
+//Analyzer, pestkillers, weedkillers, nutrients, hatchets, cutters.
+/obj/item/weapon/wirecutters/clippers
+ name = "plant clippers"
+ desc = "A tool used to take samples from plants."
/obj/item/device/analyzer/plant_analyzer
name = "plant analyzer"
@@ -7,99 +10,207 @@
icon_state = "hydro"
item_state = "analyzer"
- attack_self(mob/user as mob)
- return 0
+/obj/item/device/analyzer/plant_analyzer/attack_self(mob/user as mob)
+ return 0
+/obj/item/device/analyzer/plant_analyzer/afterattack(obj/target, mob/user, flag)
+ if(!flag) return
-// *************************************
-// Pestkiller defines for hydroponics
-// *************************************
+ var/datum/seed/grown_seed
+ var/datum/reagents/grown_reagents
+ if(istype(target,/obj/item/weapon/reagent_containers/food/snacks/grown))
-/obj/item/pestkiller
- name = "bottle of pestkiller"
- icon = 'icons/obj/chemical.dmi'
- icon_state = "bottle16"
- flags = FPRINT | TABLEPASS
- var/toxicity = 0
- var/PestKillStr = 0
- New()
- src.pixel_x = rand(-5.0, 5)
- src.pixel_y = rand(-5.0, 5)
+ var/obj/item/weapon/reagent_containers/food/snacks/grown/G = target
+ grown_seed = seed_types[G.plantname]
+ grown_reagents = G.reagents
-/obj/item/pestkiller/carbaryl
- name = "bottle of carbaryl"
- icon = 'icons/obj/chemical.dmi'
- icon_state = "bottle16"
- flags = FPRINT | TABLEPASS
- toxicity = 4
- PestKillStr = 2
- New()
- src.pixel_x = rand(-5.0, 5)
- src.pixel_y = rand(-5.0, 5)
+ else if(istype(target,/obj/item/weapon/grown))
-/obj/item/pestkiller/lindane
- name = "bottle of lindane"
- icon = 'icons/obj/chemical.dmi'
- icon_state = "bottle18"
- flags = FPRINT | TABLEPASS
- toxicity = 6
- PestKillStr = 4
- New()
- src.pixel_x = rand(-5.0, 5)
- src.pixel_y = rand(-5.0, 5)
+ var/obj/item/weapon/grown/G = target
+ grown_seed = seed_types[G.plantname]
+ grown_reagents = G.reagents
-/obj/item/pestkiller/phosmet
- name = "bottle of phosmet"
- icon = 'icons/obj/chemical.dmi'
- icon_state = "bottle15"
- flags = FPRINT | TABLEPASS
- toxicity = 8
- PestKillStr = 7
- New()
- src.pixel_x = rand(-5.0, 5)
- src.pixel_y = rand(-5.0, 5)
+ else if(istype(target,/obj/item/seeds))
+
+ var/obj/item/seeds/S = target
+ grown_seed = S.seed
+
+ else if(istype(target,/obj/machinery/portable_atmospherics/hydroponics))
+
+ var/obj/machinery/portable_atmospherics/hydroponics/H = target
+ grown_seed = H.seed
+ grown_reagents = H.reagents
+
+ if(!grown_seed)
+ user << "\red [src] can tell you nothing about [target]."
+ return
+
+ var/dat = "
Plant data for [target]
"
+ user.visible_message("\blue [user] runs the scanner over [target].")
+
+ dat += "
"
+
+ dat += " This sample contains: "
+ for(var/datum/reagent/R in grown_reagents.reagent_list)
+ dat += " - [R.id], [grown_reagents.get_reagent_amount(R.id)] unit(s)"
+
+ dat += "
Other Data
"
+
+ if(grown_seed.harvest_repeat)
+ dat += "This plant can be harvested repeatedly. "
+
+ if(grown_seed.immutable == -1)
+ dat += "This plant is highly mutable. "
+ else if(grown_seed.immutable > 0)
+ dat += "This plant does not possess genetics that are alterable. "
+
+ if(grown_seed.products && grown_seed.products.len)
+ dat += "The mature plant will produce [grown_seed.products.len == 1 ? "fruit" : "[grown_seed.products.len] varieties of fruit"]. "
+
+ if(grown_seed.requires_nutrients)
+ if(grown_seed.nutrient_consumption < 0.05)
+ dat += "It consumes a small amount of nutrient fluid. "
+ else if(grown_seed.nutrient_consumption > 0.2)
+ dat += "It requires a heavy supply of nutrient fluid. "
+ else
+ dat += "It requires a supply of nutrient fluid. "
+
+ if(grown_seed.requires_water)
+ if(grown_seed.water_consumption < 1)
+ dat += "It requires very little water. "
+ else if(grown_seed.water_consumption > 5)
+ dat += "It requires a large amount of water. "
+ else
+ dat += "It requires a stable supply of water. "
+
+ if(grown_seed.mutants && grown_seed.mutants.len)
+ dat += "It exhibits a high degree of potential subspecies shift. "
+
+ dat += "It thrives in a temperature of [grown_seed.ideal_heat] Kelvin."
+
+ if(grown_seed.lowkpa_tolerance < 20)
+ dat += " It is well adapted to low pressure levels."
+ if(grown_seed.highkpa_tolerance > 220)
+ dat += " It is well adapted to high pressure levels."
+
+ if(grown_seed.heat_tolerance > 30)
+ dat += " It is well adapted to a range of temperatures."
+ else if(grown_seed.heat_tolerance < 10)
+ dat += " It is very sensitive to temperature shifts."
+
+ dat += " It thrives in a light level of [grown_seed.ideal_light] lumen[grown_seed.ideal_light == 1 ? "" : "s"]."
+
+ if(grown_seed.light_tolerance > 10)
+ dat += " It is well adapted to a range of light levels."
+ else if(grown_seed.light_tolerance < 3)
+ dat += " It is very sensitive to light level shifts."
+
+ if(grown_seed.toxins_tolerance < 3)
+ dat += " It is highly sensitive to toxins."
+ else if(grown_seed.toxins_tolerance > 6)
+ dat += " It is remarkably resistant to toxins."
+
+ if(grown_seed.pest_tolerance < 3)
+ dat += " It is highly sensitive to pests."
+ else if(grown_seed.pest_tolerance > 6)
+ dat += " It is remarkably resistant to pests."
+
+ if(grown_seed.weed_tolerance < 3)
+ dat += " It is highly sensitive to weeds."
+ else if(grown_seed.weed_tolerance > 6)
+ dat += " It is remarkably resistant to weeds."
+
+ switch(grown_seed.spread)
+ if(1)
+ dat += " It is capable of growing beyond the confines of a tray."
+ if(2)
+ dat += " It is a robust and vigorous vine that will spread rapidly."
+
+ switch(grown_seed.carnivorous)
+ if(1)
+ dat += " It is carniovorous and will eat tray pests for sustenance."
+ if(2)
+ dat += " It is carnivorous and poses a significant threat to living things around it."
+
+ if(grown_seed.parasite)
+ dat += " It is capable of parisitizing and gaining sustenance from tray weeds."
+ if(grown_seed.alter_temp)
+ dat += " It will periodically alter the local temperature by [grown_seed.alter_temp] degrees Kelvin."
+
+ if(grown_seed.biolum)
+ dat += " It is [grown_seed.biolum_colour ? "bio-luminescent" : "bio-luminescent"]."
+ if(grown_seed.flowers)
+ dat += " It has [grown_seed.flower_colour ? "flowers" : "flowers"]."
+
+ if(dat)
+ user << browse(dat,"window=plant_analyzer")
+
+ return
// *************************************
// Hydroponics Tools
// *************************************
-/obj/item/weapon/weedspray // -- Skie
- desc = "It's a toxic mixture, in spray form, to kill small weeds."
+/obj/item/weapon/plantspray
icon = 'icons/obj/hydroponics.dmi'
+ item_state = "spray"
+ flags = TABLEPASS | FPRINT | NOBLUDGEON
+ slot_flags = SLOT_BELT
+ throwforce = 4
+ w_class = 2.0
+ throw_speed = 2
+ throw_range = 10
+ var/toxicity = 4
+ var/pest_kill_str = 0
+ var/weed_kill_str = 0
+
+/obj/item/weapon/plantspray/weeds // -- Skie
+
name = "weed-spray"
+ desc = "It's a toxic mixture, in spray form, to kill small weeds."
icon_state = "weedspray"
- item_state = "spray"
- flags = TABLEPASS | OPENCONTAINER | FPRINT | NOBLUDGEON
- slot_flags = SLOT_BELT
- throwforce = 4
- w_class = 2.0
- throw_speed = 2
- throw_range = 10
- var/toxicity = 4
- var/WeedKillStr = 2
+ weed_kill_str = 6
- suicide_act(mob/user)
- viewers(user) << "\red [user] is huffing the [src.name]! It looks like \he's trying to commit suicide."
- return (TOXLOSS)
-
-/obj/item/weapon/pestspray // -- Skie
- desc = "It's some pest eliminator spray! Do not inhale!"
- icon = 'icons/obj/hydroponics.dmi'
+/obj/item/weapon/plantspray/pests
name = "pest-spray"
+ desc = "It's some pest eliminator spray! Do not inhale!"
icon_state = "pestspray"
- item_state = "spray"
- flags = TABLEPASS | OPENCONTAINER | FPRINT | NOBLUDGEON
- slot_flags = SLOT_BELT
- throwforce = 4
- w_class = 2.0
- throw_speed = 2
- throw_range = 10
- var/toxicity = 4
- var/PestKillStr = 2
+ pest_kill_str = 6
- suicide_act(mob/user)
- viewers(user) << "\red [user] is huffing the [src.name]! It looks like \he's trying to commit suicide."
- return (TOXLOSS)
+/obj/item/weapon/plantspray/pests/old
+ name = "bottle of pestkiller"
+ icon = 'icons/obj/chemical.dmi'
+ icon_state = "bottle16"
+
+/obj/item/weapon/plantspray/pests/old/carbaryl
+ name = "bottle of carbaryl"
+ icon_state = "bottle16"
+ toxicity = 4
+ pest_kill_str = 2
+
+/obj/item/weapon/plantspray/pests/old/lindane
+ name = "bottle of lindane"
+ icon_state = "bottle18"
+ toxicity = 6
+ pest_kill_str = 4
+
+/obj/item/weapon/plantspray/pests/old/phosmet
+ name = "bottle of phosmet"
+ icon_state = "bottle15"
+ toxicity = 8
+ pest_kill_str = 7
/obj/item/weapon/minihoe // -- Numbers
name = "mini hoe"
@@ -125,7 +236,7 @@
icon_state = "bottle16"
flags = FPRINT | TABLEPASS
var/toxicity = 0
- var/WeedKillStr = 0
+ var/weed_kill_str = 0
/obj/item/weedkiller/triclopyr
name = "bottle of glyphosate"
@@ -133,7 +244,7 @@
icon_state = "bottle16"
flags = FPRINT | TABLEPASS
toxicity = 4
- WeedKillStr = 2
+ weed_kill_str = 2
/obj/item/weedkiller/lindane
name = "bottle of triclopyr"
@@ -141,7 +252,7 @@
icon_state = "bottle18"
flags = FPRINT | TABLEPASS
toxicity = 6
- WeedKillStr = 4
+ weed_kill_str = 4
/obj/item/weedkiller/D24
name = "bottle of 2,4-D"
@@ -149,58 +260,51 @@
icon_state = "bottle15"
flags = FPRINT | TABLEPASS
toxicity = 8
- WeedKillStr = 7
+ weed_kill_str = 7
// *************************************
// Nutrient defines for hydroponics
// *************************************
-/obj/item/nutrient
- name = "bottle of nutrient"
+/obj/item/weapon/reagent_containers/glass/fertilizer
+ name = "fertilizer bottle"
+ desc = "A small glass bottle. Can hold up to 10 units."
icon = 'icons/obj/chemical.dmi'
icon_state = "bottle16"
- flags = FPRINT | TABLEPASS
+ flags = FPRINT | TABLEPASS | OPENCONTAINER
+ possible_transfer_amounts = null
w_class = 2.0
- var/mutmod = 0
- var/yieldmod = 0
- New()
- src.pixel_x = rand(-5.0, 5)
- src.pixel_y = rand(-5.0, 5)
-/obj/item/nutrient/ez
+ var/fertilizer //Reagent contained, if any.
+
+ //Like a shot glass!
+ amount_per_transfer_from_this = 10
+ volume = 10
+
+/obj/item/weapon/reagent_containers/glass/fertilizer/New()
+ ..()
+
+ src.pixel_x = rand(-5.0, 5)
+ src.pixel_y = rand(-5.0, 5)
+
+ if(fertilizer)
+ reagents.add_reagent(fertilizer,10)
+
+/obj/item/weapon/reagent_containers/glass/fertilizer/ez
name = "bottle of E-Z-Nutrient"
- icon = 'icons/obj/chemical.dmi'
icon_state = "bottle16"
- flags = FPRINT | TABLEPASS
- mutmod = 1
- yieldmod = 1
- New()
- src.pixel_x = rand(-5.0, 5)
- src.pixel_y = rand(-5.0, 5)
+ fertilizer = "eznutrient"
-/obj/item/nutrient/l4z
+/obj/item/weapon/reagent_containers/glass/fertilizer/l4z
name = "bottle of Left 4 Zed"
- icon = 'icons/obj/chemical.dmi'
icon_state = "bottle18"
- flags = FPRINT | TABLEPASS
- mutmod = 2
- yieldmod = 0
- New()
- src.pixel_x = rand(-5.0, 5)
- src.pixel_y = rand(-5.0, 5)
+ fertilizer = "left4zed"
-/obj/item/nutrient/rh
+/obj/item/weapon/reagent_containers/glass/fertilizer/rh
name = "bottle of Robust Harvest"
- icon = 'icons/obj/chemical.dmi'
icon_state = "bottle15"
- flags = FPRINT | TABLEPASS
- mutmod = 0
- yieldmod = 2
- New()
- src.pixel_x = rand(-5.0, 5)
- src.pixel_y = rand(-5.0, 5)
-
+ fertilizer = "robustharvest"
//Hatchets and things to kill kudzu
/obj/item/weapon/hatchet
@@ -215,7 +319,7 @@
throw_speed = 4
throw_range = 4
sharp = 1
- edge = 1
+ edge = 1
matter = list("metal" = 15000)
origin_tech = "materials=2;combat=1"
attack_verb = list("chopped", "torn", "cut")
@@ -232,7 +336,6 @@
icon_state = "unathiknife"
attack_verb = list("ripped", "torn", "cut")
-
/obj/item/weapon/scythe
icon_state = "scythe0"
name = "scythe"
@@ -249,8 +352,8 @@
/obj/item/weapon/scythe/afterattack(atom/A, mob/user as mob, proximity)
if(!proximity) return
- if(istype(A, /obj/effect/spacevine))
- for(var/obj/effect/spacevine/B in orange(A,1))
+ if(istype(A, /obj/effect/plantsegment))
+ for(var/obj/effect/plantsegment/B in orange(A,1))
if(prob(80))
del B
del A
\ No newline at end of file
diff --git a/code/modules/hydroponics/hydro_tray.dm b/code/modules/hydroponics/hydro_tray.dm
index 1e586eacf0b..e0e2757f1c6 100644
--- a/code/modules/hydroponics/hydro_tray.dm
+++ b/code/modules/hydroponics/hydro_tray.dm
@@ -1,44 +1,153 @@
-#define HYDRO_SPEED_MULTIPLIER 0.25
+#define HYDRO_SPEED_MULTIPLIER 1
-/obj/machinery/hydroponics
+/obj/machinery/portable_atmospherics/hydroponics
name = "hydroponics tray"
icon = 'icons/obj/hydroponics.dmi'
icon_state = "hydrotray3"
density = 1
anchored = 1
- var/waterlevel = 100 // The amount of water in the tray (max 100)
- var/nutrilevel = 10 // The amount of nutrient in the tray (max 10)
- var/pestlevel = 0 // The amount of pests in the tray (max 10)
- var/weedlevel = 0 // The amount of weeds in the tray (max 10)
- var/yieldmod = 1 //Modifier to yield
- var/mutmod = 1 //Modifier to mutation chance
- var/toxic = 0 // Toxicity in the tray?
- var/age = 0 // Current age
- var/dead = 0 // Is it dead?
- var/health = 0 // Its health.
- var/lastproduce = 0 // Last time it was harvested
- var/lastcycle = 0 //Used for timing of cycles.
- var/cycledelay = 200 // About 10 seconds / cycle
- var/planted = 0 // Is it occupied?
- var/harvest = 0 //Ready to harvest?
- var/obj/item/seeds/myseed = null // The currently planted seed
+ flags = OPENCONTAINER
+ volume = 100
-/obj/machinery/hydroponics/bullet_act(var/obj/item/projectile/Proj) //Works with the Somatoray to modify plant variables.
- if(istype(Proj ,/obj/item/projectile/energy/floramut))
- if(planted)
- mutate()
- else if(istype(Proj ,/obj/item/projectile/energy/florayield))
- if(planted && myseed.yield == 0)//Oh god don't divide by zero you'll doom us all.
- myseed.yield += 1
- //world << "Yield increased by 1, from 0, to a total of [myseed.yield]"
- else if (planted && (prob(1/(myseed.yield * myseed.yield) *100)))//This formula gives you diminishing returns based on yield. 100% with 1 yield, decreasing to 25%, 11%, 6, 4, 2...
- myseed.yield += 1
- //world << "Yield increased by 1, to a total of [myseed.yield]"
- else
- ..()
+ var/draw_warnings = 1 //Set to 0 to stop it from drawing the alert lights.
+
+ // Plant maintenance vars.
+ var/waterlevel = 100 // Water (max 100)
+ var/nutrilevel = 100 // Nutrient (max 100)
+ var/pestlevel = 0 // Pests (max 10)
+ var/weedlevel = 0 // Weeds (max 10)
+
+ // Tray state vars.
+ var/dead = 0 // Is it dead?
+ var/harvest = 0 // Is it ready to harvest?
+ var/age = 0 // Current plant age
+
+ // Harvest/mutation mods.
+ var/yield_mod = 0 // Modifier to yield
+ var/mutation_mod = 0 // Modifier to mutation chance
+ var/toxins = 0 // Toxicity in the tray?
+ var/mutation_level = 0 // When it hits 100, the plant mutates.
+
+ // Mechanical concerns.
+ var/health = 0 // Plant health.
+ var/lastproduce = 0 // Last time tray was harvested
+ var/lastcycle = 0 // Cycle timing/tracking var.
+ var/cycledelay = 150 // Delay per cycle.
+ var/closed_system // If set, the tray will attempt to take atmos from a pipe.
+ var/force_update // Set this to bypass the cycle time check.
+ var/obj/temp_chem_holder // Something to hold reagents during process_reagents()
+
+ // Seed details/line data.
+ var/datum/seed/seed = null // The currently planted seed
+
+ // Reagent information for process(), consider moving this to a controller along
+ // with cycle information under 'mechanical concerns' at some point.
+ var/global/list/toxic_reagents = list(
+ "anti_toxin" = -2,
+ "toxin" = 2,
+ "fluorine" = 2.5,
+ "chlorine" = 1.5,
+ "sacid" = 1.5,
+ "pacid" = 3,
+ "plantbgone" = 3,
+ "cryoxadone" = -3,
+ "radium" = 2
+ )
+ var/global/list/nutrient_reagents = list(
+ "milk" = 0.1,
+ "beer" = 0.25,
+ "phosphorus" = 0.1,
+ "sugar" = 0.1,
+ "sodawater" = 0.1,
+ "ammonia" = 1,
+ "diethylamine" = 2,
+ "nutriment" = 1,
+ "adminordrazine" = 1,
+ "eznutrient" = 1,
+ "robustharvest" = 1,
+ "left4zed" = 1
+ )
+ var/global/list/weedkiller_reagents = list(
+ "fluorine" = -4,
+ "chlorine" = -3,
+ "phosphorus" = -2,
+ "sugar" = 2,
+ "sacid" = -2,
+ "pacid" = -4,
+ "plantbgone" = -8,
+ "adminordrazine" = -5
+ )
+ var/global/list/pestkiller_reagents = list(
+ "sugar" = 2,
+ "diethylamine" = -2,
+ "adminordrazine" = -5
+ )
+ var/global/list/water_reagents = list(
+ "water" = 1,
+ "adminordrazine" = 1,
+ "milk" = 0.9,
+ "beer" = 0.7,
+ "flourine" = -0.5,
+ "chlorine" = -0.5,
+ "phosphorus" = -0.5,
+ "water" = 1,
+ "sodawater" = 1,
+ )
+
+ // Beneficial reagents also have values for modifying yield_mod and mut_mod (in that order).
+ var/global/list/beneficial_reagents = list(
+ "beer" = list( -0.05, 0, 0 ),
+ "fluorine" = list( -2, 0, 0 ),
+ "chlorine" = list( -1, 0, 0 ),
+ "phosphorus" = list( -0.75, 0, 0 ),
+ "sodawater" = list( 0.1, 0, 0 ),
+ "sacid" = list( -1, 0, 0 ),
+ "pacid" = list( -2, 0, 0 ),
+ "plantbgone" = list( -2, 0, 0.2 ),
+ "cryoxadone" = list( 3, 0, 0 ),
+ "ammonia" = list( 0.5, 0, 0 ),
+ "diethylamine" = list( 1, 0, 0 ),
+ "nutriment" = list( 0.5, 0.1, 0 ),
+ "radium" = list( -1.5, 0, 0.2 ),
+ "adminordrazine" = list( 1, 1, 1 ),
+ "robustharvest" = list( 0, 0.2, 0 ),
+ "left4zed" = list( 0, 0, 0.2 )
+ )
+
+ // Mutagen list specifies minimum value for the mutation to take place, rather
+ // than a bound as the lists above specify.
+ var/global/list/mutagenic_reagents = list(
+ "radium" = 3,
+ "mutagen" = 8
+ )
+
+/obj/machinery/portable_atmospherics/hydroponics/New()
+ ..()
+ temp_chem_holder = new()
+ temp_chem_holder.create_reagents(10)
+ create_reagents(200)
+ connect()
+ update_icon()
+ if(closed_system)
+ flags &= ~OPENCONTAINER
+
+/obj/machinery/portable_atmospherics/hydroponics/bullet_act(var/obj/item/projectile/Proj)
+
+ //Don't act on seeds like dionaea that shouldn't change.
+ if(seed && seed.immutable > 0)
return
-/obj/machinery/hydroponics/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
+ //Override for somatoray projectiles.
+ if(istype(Proj ,/obj/item/projectile/energy/floramut) && prob(20))
+ mutate(1)
+ return
+ else if(istype(Proj ,/obj/item/projectile/energy/florayield) && prob(20))
+ yield_mod = min(10,yield_mod+rand(1,2))
+ return
+
+ ..()
+
+/obj/machinery/portable_atmospherics/hydroponics/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
if(air_group || (height==0)) return 1
if(istype(mover) && mover.checkpass(PASSTABLE))
@@ -46,732 +155,553 @@
else
return 0
-obj/machinery/hydroponics/process()
+/obj/machinery/portable_atmospherics/hydroponics/process()
- if(myseed && (myseed.loc != src))
- myseed.loc = src
+ //Do this even if we're not ready for a plant cycle.
+ process_reagents()
- if(world.time > (lastcycle + cycledelay))
- lastcycle = world.time
- if(planted && !dead)
- // Advance age
- age += 1 * HYDRO_SPEED_MULTIPLIER
+ // Update values every cycle rather than every process() tick.
+ if(force_update)
+ force_update = 0
+ else if(world.time < (lastcycle + cycledelay))
+ return
+ lastcycle = world.time
-//Nutrients//////////////////////////////////////////////////////////////
- // Nutrients deplete slowly
- if(nutrilevel > 0)
- if(prob(50))
- nutrilevel -= 1 * HYDRO_SPEED_MULTIPLIER
+ // Mutation level drops each main tick.
+ mutation_level -= rand(2,4)
- // Lack of nutrients hurts non-weeds
- if(nutrilevel <= 0 && myseed.plant_type != 1)
- health -= rand(1,3) * HYDRO_SPEED_MULTIPLIER
+ // Weeds like water and nutrients, there's a chance the weed population will increase.
+ // Bonus chance if the tray is unoccupied.
+ if(waterlevel > 10 && nutrilevel > 2 && prob(isnull(seed) ? 6 : 3))
+ weedlevel += 1 * HYDRO_SPEED_MULTIPLIER
-//Water//////////////////////////////////////////////////////////////////
- // Drink random amount of water
- waterlevel = max(waterlevel - rand(1,6) * HYDRO_SPEED_MULTIPLIER, 0)
+ // There's a chance for a weed explosion to happen if the weeds take over.
+ // Plants that are themselves weeds (weed_tolernace > 10) are unaffected.
+ if (weedlevel >= 10 && prob(10))
+ if(!seed || weedlevel >= seed.weed_tolerance)
+ weed_invasion()
- // If the plant is dry, it loses health pretty fast, unless mushroom
- if(waterlevel <= 10 && myseed.plant_type != 2)
- health -= rand(0,1) * HYDRO_SPEED_MULTIPLIER
- if(waterlevel <= 0)
- health -= rand(0,2) * HYDRO_SPEED_MULTIPLIER
+ // If there is no seed data (and hence nothing planted),
+ // or the plant is dead, process nothing further.
+ if(!seed || dead)
+ return
- // Sufficient water level and nutrient level = plant healthy
- else if(waterlevel > 10 && nutrilevel > 0)
- health += rand(1,2) * HYDRO_SPEED_MULTIPLIER
- if(prob(5)) //5 percent chance the weed population will increase
- weedlevel += 1 * HYDRO_SPEED_MULTIPLIER
-//Toxins/////////////////////////////////////////////////////////////////
+ // Advance plant age.
+ if(prob(25)) age += 1 * HYDRO_SPEED_MULTIPLIER
- // Too much toxins cause harm, but when the plant drinks the contaiminated water, the toxins disappear slowly
- if(toxic >= 40 && toxic < 80)
- health -= 1 * HYDRO_SPEED_MULTIPLIER
- toxic -= rand(1,10) * HYDRO_SPEED_MULTIPLIER
- else if(toxic >= 80) // I don't think it ever gets here tbh unless above is commented out
- health -= 3 * HYDRO_SPEED_MULTIPLIER
- toxic -= rand(1,10) * HYDRO_SPEED_MULTIPLIER
- else if(toxic < 0) // Make sure it won't go overoboard
- toxic = 0
+ //Highly mutable plants have a chance of mutating every tick.
+ if(seed.immutable == -1)
+ var/mut_prob = rand(1,100)
+ if(mut_prob <= 5) mutate(mut_prob == 1 ? 2 : 1)
-//Pests & Weeds//////////////////////////////////////////////////////////
+ // Other plants also mutate if enough mutagenic compounds have been added.
+ if(!seed.immutable)
+ if(prob(min(mutation_level,100)))
+ mutate(mutation_level > 50 ? 2 : 1)
+ mutation_level = 0
- // Too many pests cause the plant to be sick
- if (pestlevel > 10 ) // Make sure it won't go overoboard
- pestlevel = 10
+ // Maintain tray nutrient and water levels.
+ if(seed.nutrient_consumption > 0 && nutrilevel > 0 && prob(25))
+ nutrilevel -= max(0,seed.nutrient_consumption * HYDRO_SPEED_MULTIPLIER)
+ if(seed.water_consumption > 0 && waterlevel > 0 && prob(25))
+ waterlevel -= max(0,seed.water_consumption * HYDRO_SPEED_MULTIPLIER)
- else if(pestlevel >= 5)
- health -= 1 * HYDRO_SPEED_MULTIPLIER
+ // Make sure the plant is not starving or thirsty. Adequate
+ // water and nutrients will cause a plant to become healthier.
+ var/healthmod = rand(1,3) * HYDRO_SPEED_MULTIPLIER
+ if(seed.requires_nutrients && prob(35))
+ health += (nutrilevel < 2 ? -healthmod : healthmod)
+ if(seed.requires_water && prob(35))
+ health += (waterlevel < 10 ? -healthmod : healthmod)
- // If it's a weed, it doesn't stunt the growth
- if(weedlevel >= 5 && myseed.plant_type != 1 )
- health -= 1 * HYDRO_SPEED_MULTIPLIER
+ // Check that pressure, heat and light are all within bounds.
+ // First, handle an open system or an unconnected closed system.
+ var/turf/T = loc
+ var/datum/gas_mixture/environment
-//Health & Age///////////////////////////////////////////////////////////
- // Don't go overboard with the health
- if(health > myseed.endurance)
- health = myseed.endurance
+ // If we're closed, take from our internal sources.
+ if(closed_system && (connected_port || holding))
+ environment = air_contents
- // Plant dies if health <= 0
- else if(health <= 0)
- dead = 1
- harvest = 0
- weedlevel += 1 * HYDRO_SPEED_MULTIPLIER // Weeds flourish
- pestlevel = 0 // Pests die
+ // If atmos input is not there, grab from turf.
+ if(!environment)
+ if(istype(T))
+ environment = T.return_air()
- // If the plant is too old, lose health fast
- if(age > myseed.lifespan)
- health -= rand(1,5) * HYDRO_SPEED_MULTIPLIER
+ if(!environment) return
- // Harvest code
- if(age > myseed.production && (age - lastproduce) > myseed.production && (!harvest && !dead))
- for(var/i = 0; i < mutmod; i++)
- if(prob(85))
- mutate()
- else if(prob(30))
- hardmutate()
- else if(prob(5))
- mutatespecie()
-
- if(yieldmod > 0 && myseed.yield != -1) // Unharvestable shouldn't be harvested
- harvest = 1
- else
- lastproduce = age
- if(prob(5)) // On each tick, there's a 5 percent chance the pest population will increase
- pestlevel += 1 * HYDRO_SPEED_MULTIPLIER
- else
- if(waterlevel > 10 && nutrilevel > 0 && prob(10)) // If there's no plant, the percentage chance is 10%
- weedlevel += 1 * HYDRO_SPEED_MULTIPLIER
- if(weedlevel > 10)
- weedlevel = 10
-
- // Weeeeeeeeeeeeeeedddssss
-
- if (weedlevel >= 10 && prob(50)) // At this point the plant is kind of fucked. Weeds can overtake the plant spot.
- if(planted)
- if(myseed.plant_type == 0) // If a normal plant
- weedinvasion()
+ // Handle gas consumption.
+ if(seed.consume_gasses && seed.consume_gasses.len)
+ var/missing_gas = 0
+ for(var/gas in seed.consume_gasses)
+ if(environment && environment.gas && environment.gas[gas] && \
+ environment.gas[gas] >= seed.consume_gasses[gas])
+ environment.adjust_gas(gas,-seed.consume_gasses[gas],1)
else
- weedinvasion() // Weed invasion into empty tray
- updateicon()
+ missing_gas++
+
+ if(missing_gas > 0)
+ health -= missing_gas * HYDRO_SPEED_MULTIPLIER
+
+ // Process it.
+ var/pressure = environment.return_pressure()
+ if(pressure < seed.lowkpa_tolerance || pressure > seed.highkpa_tolerance)
+ health -= healthmod
+
+ if(abs(environment.temperature - seed.ideal_heat) > seed.heat_tolerance)
+ health -= healthmod
+
+ // Handle gas production.
+ if(seed.exude_gasses && seed.exude_gasses.len)
+ for(var/gas in seed.exude_gasses)
+ environment.adjust_gas(gas, max(1,round((seed.exude_gasses[gas]*seed.potency)/seed.exude_gasses.len)))
+
+ // If we're attached to a pipenet, then we should let the pipenet know we might have modified some gasses
+ if (closed_system && connected_port)
+ update_connected_network()
+
+ // Handle light requirements.
+ var/area/A = T.loc
+ if(A)
+ var/light_available
+ if(A.lighting_use_dynamic)
+ light_available = max(0,min(10,T.lighting_lumcount)-5)
+ else
+ light_available = 5
+ if(abs(light_available - seed.ideal_light) > seed.light_tolerance)
+ health -= healthmod
+
+ // Toxin levels beyond the plant's tolerance cause damage, but
+ // toxins are sucked up each tick and slowly reduce over time.
+ if(toxins > 0)
+ var/toxin_uptake = max(1,round(toxins/10))
+ if(toxins > seed.toxins_tolerance)
+ health -= toxin_uptake
+ toxins -= toxin_uptake
+
+ // Check for pests and weeds.
+ // Some carnivorous plants happily eat pests.
+ if(pestlevel > 0)
+ if(seed.carnivorous)
+ health += HYDRO_SPEED_MULTIPLIER
+ pestlevel -= HYDRO_SPEED_MULTIPLIER
+ else if (pestlevel >= seed.pest_tolerance)
+ health -= HYDRO_SPEED_MULTIPLIER
+
+ // Some plants thrive and live off of weeds.
+ if(weedlevel > 0)
+ if(seed.parasite)
+ health += HYDRO_SPEED_MULTIPLIER
+ weedlevel -= HYDRO_SPEED_MULTIPLIER
+ else if (weedlevel >= seed.weed_tolerance)
+ health -= HYDRO_SPEED_MULTIPLIER
+
+ // Handle life and death.
+ // If the plant is too old, it loses health fast.
+ if(age > seed.lifespan)
+ health -= rand(3,5) * HYDRO_SPEED_MULTIPLIER
+
+ // When the plant dies, weeds thrive and pests die off.
+ if(health <= 0)
+ dead = 1
+ mutation_level = 0
+ harvest = 0
+ weedlevel += 1 * HYDRO_SPEED_MULTIPLIER
+ pestlevel = 0
+
+ // If enough time (in cycles, not ticks) has passed since the plant was harvested, we're ready to harvest again.
+ else if(seed.products && seed.products.len && age > seed.production && \
+ (age - lastproduce) > seed.production && (!harvest && !dead))
+ harvest = 1
+ lastproduce = age
+
+ if(prob(3)) // On each tick, there's a chance the pest population will increase
+ pestlevel += 0.1 * HYDRO_SPEED_MULTIPLIER
+
+ check_level_sanity()
+ update_icon()
return
+//Process reagents being input into the tray.
+/obj/machinery/portable_atmospherics/hydroponics/proc/process_reagents()
+ if(!reagents) return
-obj/machinery/hydroponics/proc/updateicon()
- //Refreshes the icon and sets the luminosity
- overlays.Cut()
- if(planted)
- if(dead)
- overlays += image('icons/obj/hydroponics.dmi', icon_state="[myseed.species]-dead")
- else if(harvest)
- if(myseed.plant_type == 2) // Shrooms don't have a -harvest graphic
- overlays += image('icons/obj/hydroponics.dmi', icon_state="[myseed.species]-grow[myseed.growthstages]")
- else
- overlays += image('icons/obj/hydroponics.dmi', icon_state="[myseed.species]-harvest")
- else if(age < myseed.maturation)
- var/t_growthstate = ((age / myseed.maturation) * myseed.growthstages ) // Make sure it won't crap out due to HERPDERP 6 stages only
- overlays += image('icons/obj/hydroponics.dmi', icon_state="[myseed.species]-grow[round(t_growthstate)]")
- lastproduce = age //Cheating by putting this here, it means that it isn't instantly ready to harvest
- else
- overlays += image('icons/obj/hydroponics.dmi', icon_state="[myseed.species]-grow[myseed.growthstages]") // Same
+ if(reagents.total_volume <= 0)
+ return
- if(waterlevel <= 10)
- overlays += image('icons/obj/hydroponics.dmi', icon_state="over_lowwater3")
- if(nutrilevel <= 2)
- overlays += image('icons/obj/hydroponics.dmi', icon_state="over_lownutri3")
- if(health <= (myseed.endurance / 2))
- overlays += image('icons/obj/hydroponics.dmi', icon_state="over_lowhealth3")
- if(weedlevel >= 5)
- overlays += image('icons/obj/hydroponics.dmi', icon_state="over_alert3")
- if(pestlevel >= 5)
- overlays += image('icons/obj/hydroponics.dmi', icon_state="over_alert3")
- if(toxic >= 40)
- overlays += image('icons/obj/hydroponics.dmi', icon_state="over_alert3")
- if(harvest)
- overlays += image('icons/obj/hydroponics.dmi', icon_state="over_harvest3")
+ reagents.trans_to(temp_chem_holder, min(reagents.total_volume,rand(1,3)))
- if(istype(myseed,/obj/item/seeds/glowshroom))
- SetLuminosity(round(myseed.potency/10))
- else
- SetLuminosity(0)
+ for(var/datum/reagent/R in temp_chem_holder.reagents.reagent_list)
+ var/reagent_total = temp_chem_holder.reagents.get_reagent_amount(R.id)
+
+ if(seed && !dead)
+ //Handle some general level adjustments.
+ if(toxic_reagents[R.id])
+ toxins += toxic_reagents[R.id] * reagent_total
+ if(weedkiller_reagents[R.id])
+ weedlevel -= weedkiller_reagents[R.id] * reagent_total
+ if(pestkiller_reagents[R.id])
+ pestlevel -= pestkiller_reagents[R.id] * reagent_total
+
+ // Beneficial reagents have a few impacts along with health buffs.
+ if(beneficial_reagents[R.id])
+ health += beneficial_reagents[R.id][1] * reagent_total
+ yield_mod += beneficial_reagents[R.id][2] * reagent_total
+ mutation_mod += beneficial_reagents[R.id][3] * reagent_total
+
+ // Mutagen is distinct from the previous types and mostly has a chance of proccing a mutation.
+ if(mutagenic_reagents[R.id])
+ mutation_level += reagent_total*mutagenic_reagents[R.id]+mutation_mod
+
+ // Handle nutrient refilling.
+ if(nutrient_reagents[R.id])
+ nutrilevel += nutrient_reagents[R.id] * reagent_total
+
+ // Handle water and water refilling.
+ var/water_added = 0
+ if(water_reagents[R.id])
+ var/water_input = water_reagents[R.id] * reagent_total
+ water_added += water_input
+ waterlevel += water_input
+
+ // Water dilutes toxin level.
+ if(water_added > 0)
+ toxins -= round(water_added/4)
+
+ temp_chem_holder.reagents.clear_reagents()
+ check_level_sanity()
+ update_icon()
+
+//Harvests the product of a plant.
+/obj/machinery/portable_atmospherics/hydroponics/proc/harvest(var/mob/user)
+
+ //Harvest the product of the plant,
+ if(!seed || !harvest || !user)
+ return
+
+ if(closed_system)
+ user << "You can't harvest from the plant while the lid is shut."
+ return
+
+ seed.harvest(user,yield_mod)
+
+ // Reset values.
+ harvest = 0
+ lastproduce = age
+
+ if(!seed.harvest_repeat)
+ yield_mod = 0
+ seed = null
+ dead = 0
+ age = 0
+
+ check_level_sanity()
+ update_icon()
return
+//Clears out a dead plant.
+/obj/machinery/portable_atmospherics/hydroponics/proc/remove_dead(var/mob/user)
+ if(!user || !dead) return
+ if(closed_system)
+ user << "You can't remove the dead plant while the lid is shut."
+ return
-obj/machinery/hydroponics/proc/weedinvasion() // If a weed growth is sufficient, this happens.
+ seed = null
dead = 0
- if(myseed) // In case there's nothing in the tray beforehand
- del(myseed)
- switch(rand(1,18)) // randomly pick predominative weed
- if(16 to 18)
- myseed = new /obj/item/seeds/reishimycelium
- if(14 to 15)
- myseed = new /obj/item/seeds/nettleseed
- if(12 to 13)
- myseed = new /obj/item/seeds/harebell
- if(10 to 11)
- myseed = new /obj/item/seeds/amanitamycelium
- if(8 to 9)
- myseed = new /obj/item/seeds/chantermycelium
- if(6 to 7) // implementation for tower caps still kinda missing ~ Not Anymore! -Cheridan
- myseed = new /obj/item/seeds/towermycelium
- if(4 to 5)
- myseed = new /obj/item/seeds/plumpmycelium
+ user << "You remove the dead plant from the [src]."
+ check_level_sanity()
+ update_icon()
+ return
+
+//Refreshes the icon and sets the luminosity
+/obj/machinery/portable_atmospherics/hydroponics/update_icon()
+
+ overlays.Cut()
+
+ // Updates the plant overlay.
+ if(!isnull(seed))
+
+ if(draw_warnings && health <= (seed.endurance / 2))
+ overlays += "over_lowhealth3"
+
+ if(dead)
+ overlays += "[seed.plant_icon]-dead"
+ else if(harvest)
+ overlays += "[seed.plant_icon]-harvest"
+ else if(age < seed.maturation)
+
+ var/t_growthstate
+ if(age >= seed.maturation)
+ t_growthstate = seed.growth_stages
+ else
+ t_growthstate = round(seed.maturation / seed.growth_stages)
+
+ overlays += "[seed.plant_icon]-grow[t_growthstate]"
+ lastproduce = age
else
- myseed = new /obj/item/seeds/weeds
- planted = 1
+ overlays += "[seed.plant_icon]-grow[seed.growth_stages]"
+
+ //Draw the cover.
+ if(closed_system)
+ overlays += "hydrocover"
+
+ //Updated the various alert icons.
+ if(draw_warnings)
+ if(waterlevel <= 10)
+ overlays += "over_lowwater3"
+ if(nutrilevel <= 2)
+ overlays += "over_lownutri3"
+ if(weedlevel >= 5 || pestlevel >= 5 || toxins >= 40)
+ overlays += "over_alert3"
+ if(harvest)
+ overlays += "over_harvest3"
+
+ // Update bioluminescence.
+ if(seed)
+ if(seed.biolum)
+ SetLuminosity(round(seed.potency/10))
+ if(seed.biolum_colour)
+ l_color = seed.biolum_colour
+ else
+ l_color = null
+ return
+
+ SetLuminosity(0)
+ return
+
+ // If a weed growth is sufficient, this proc is called.
+/obj/machinery/portable_atmospherics/hydroponics/proc/weed_invasion()
+
+ //Remove the seed if something is already planted.
+ if(seed) seed = null
+ seed = seed_types[pick(list("reishi","nettles","amanita","mushrooms","plumphelmet","towercap","harebells","weeds"))]
+ if(!seed) return //Weed does not exist, someone fucked up.
+
+ dead = 0
age = 0
- health = myseed.endurance
+ health = seed.endurance
lastcycle = world.time
harvest = 0
- weedlevel = 0 // Reset
- pestlevel = 0 // Reset
- updateicon()
- visible_message("\blue [src] has been overtaken by [myseed.plantname].")
+ weedlevel = 0
+ pestlevel = 0
+ update_icon()
+ visible_message("\blue [src] has been overtaken by [seed.display_name].")
return
+/obj/machinery/portable_atmospherics/hydroponics/proc/mutate(var/severity)
-obj/machinery/hydroponics/proc/mutate() // Mutates the current seed
+ // No seed, no mutations.
+ if(!seed)
+ return
- myseed.lifespan += rand(-2,2)
- if(myseed.lifespan < 10)
- myseed.lifespan = 10
- else if(myseed.lifespan > 30)
- myseed.lifespan = 30
+ // Check if we should even bother working on the current seed datum.
+ if(seed.mutants. && seed.mutants.len && severity > 1 && prob(10+mutation_mod))
+ mutate_species()
+ return
- myseed.endurance += rand(-5,5)
- if(myseed.endurance < 10)
- myseed.endurance = 10
- else if(myseed.endurance > 100)
- myseed.endurance = 100
+ // We need to make sure we're not modifying one of the global seed datums.
+ // If it's not in the global list, then no products of the line have been
+ // harvested yet and it's safe to assume it's restricted to this tray.
+ if(!isnull(seed_types[seed.name]))
+ seed = seed.diverge()
+ seed.mutate(severity,get_turf(src))
- myseed.production += rand(-1,1)
- if(myseed.production < 2)
- myseed.production = 2
- else if(myseed.production > 10)
- myseed.production = 10
-
- if(myseed.yield != -1) // Unharvestable shouldn't suddenly turn harvestable
- myseed.yield += rand(-2,2)
- if(myseed.yield < 0)
- myseed.yield = 0
- else if(myseed.yield > 10)
- myseed.yield = 10
- if(myseed.yield == 0 && myseed.plant_type == 2)
- myseed.yield = 1 // Mushrooms always have a minimum yield of 1.
-
- if(myseed.potency != -1) //Not all plants have a potency
- myseed.potency += rand(-25,25)
- if(myseed.potency < 0)
- myseed.potency = 0
- else if(myseed.potency > 100)
- myseed.potency = 100
return
+/obj/machinery/portable_atmospherics/hydroponics/proc/check_level_sanity()
+ //Make sure various values are sane.
+ if(seed)
+ health = max(0,min(seed.endurance,health))
+ else
+ health = 0
+ dead = 0
+ mutation_level = max(0,min(mutation_level,100))
+ nutrilevel = max(0,min(nutrilevel,10))
+ waterlevel = max(0,min(waterlevel,100))
+ pestlevel = max(0,min(pestlevel,10))
+ weedlevel = max(0,min(weedlevel,10))
+ toxins = max(0,min(toxins,10))
-obj/machinery/hydroponics/proc/hardmutate() // Strongly mutates the current seed.
-
- myseed.lifespan += rand(-4,4)
- if(myseed.lifespan < 10)
- myseed.lifespan = 10
- else if(myseed.lifespan > 30 && !istype(myseed,/obj/item/seeds/glowshroom)) //hack to prevent glowshrooms from always resetting to 30 sec delay
- myseed.lifespan = 30
-
- myseed.endurance += rand(-10,10)
- if(myseed.endurance < 10)
- myseed.endurance = 10
- else if(myseed.endurance > 100)
- myseed.endurance = 100
-
- myseed.production += rand(-2,2)
- if(myseed.production < 2)
- myseed.production = 2
- else if(myseed.production > 10)
- myseed.production = 10
-
- if(myseed.yield != -1) // Unharvestable shouldn't suddenly turn harvestable
- myseed.yield += rand(-4,4)
- if(myseed.yield < 0)
- myseed.yield = 0
- else if(myseed.yield > 10)
- myseed.yield = 10
- if(myseed.yield == 0 && myseed.plant_type == 2)
- myseed.yield = 1 // Mushrooms always have a minimum yield of 1.
-
- if(myseed.potency != -1) //Not all plants have a potency
- myseed.potency += rand(-50,50)
- if(myseed.potency < 0)
- myseed.potency = 0
- else if(myseed.potency > 100)
- myseed.potency = 100
- return
-
-
-
-obj/machinery/hydroponics/proc/mutatespecie() // Mutagent produced a new plant!
-
- if ( istype(myseed, /obj/item/seeds/nettleseed ))
- del(myseed)
- myseed = new /obj/item/seeds/deathnettleseed
-
- else if ( istype(myseed, /obj/item/seeds/amanitamycelium ))
- del(myseed)
- myseed = new /obj/item/seeds/angelmycelium
-
- else if ( istype(myseed, /obj/item/seeds/lemonseed ))
- del(myseed)
- myseed = new /obj/item/seeds/cashseed
-
- else if ( istype(myseed, /obj/item/seeds/ambrosiavulgarisseed ))
- del(myseed)
- myseed = new /obj/item/seeds/ambrosiadeusseed
-
- else if ( istype(myseed, /obj/item/seeds/plumpmycelium ))
- del(myseed)
- myseed = new /obj/item/seeds/walkingmushroommycelium
-
- else if ( istype(myseed, /obj/item/seeds/chiliseed ))
- del(myseed)
- myseed = new /obj/item/seeds/icepepperseed
-
- else if ( istype(myseed, /obj/item/seeds/appleseed ))
- del(myseed)
- myseed = new /obj/item/seeds/goldappleseed
-
- else if ( istype(myseed, /obj/item/seeds/berryseed ))
- del(myseed)
- switch(rand(1,100))
- if(1 to 50)
- myseed = new /obj/item/seeds/poisonberryseed
- if(51 to 100)
- myseed = new /obj/item/seeds/glowberryseed
-
- else if ( istype(myseed, /obj/item/seeds/poisonberryseed ))
- del(myseed)
- myseed = new /obj/item/seeds/deathberryseed
-
- else if ( istype(myseed, /obj/item/seeds/tomatoseed ))
- del(myseed)
- switch(rand(1,100))
- if(1 to 35)
- myseed = new /obj/item/seeds/bluetomatoseed
- if(36 to 70)
- myseed = new /obj/item/seeds/bloodtomatoseed
- if(71 to 100)
- myseed = new /obj/item/seeds/killertomatoseed
-
- else if ( istype(myseed, /obj/item/seeds/bluetomatoseed ))
- del(myseed)
- myseed = new /obj/item/seeds/bluespacetomatoseed
-
- else if ( istype(myseed, /obj/item/seeds/grapeseed ))
- del(myseed)
- myseed = new /obj/item/seeds/greengrapeseed
-/*
- else if ( istype(myseed, /obj/item/seeds/tomatoseed ))
- del(myseed)
- myseed = new /obj/item/seeds/gibtomatoseed
-*/
- else if ( istype(myseed, /obj/item/seeds/eggplantseed ))
- del(myseed)
- myseed = new /obj/item/seeds/eggyseed
+/obj/machinery/portable_atmospherics/hydroponics/proc/mutate_species()
+ var/previous_plant = seed.display_name
+ var/newseed = seed.get_mutant_variant()
+ if(newseed in seed_types)
+ seed = seed_types[newseed]
else
return
dead = 0
- hardmutate()
- planted = 1
+ mutate(1)
age = 0
- health = myseed.endurance
+ health = seed.endurance
lastcycle = world.time
harvest = 0
- weedlevel = 0 // Reset
+ weedlevel = 0
- spawn(5) // Wait a while
- updateicon()
- visible_message("\red[src] has suddenly mutated into \blue [myseed.plantname]!")
+ update_icon()
+ visible_message("\red The \blue [previous_plant] \red has suddenly mutated into \blue [seed.display_name]!")
return
+/obj/machinery/portable_atmospherics/hydroponics/attackby(var/obj/item/O as obj, var/mob/user as mob)
+ if (O.is_open_container())
+ return 0
-obj/machinery/hydroponics/proc/mutateweed() // If the weeds gets the mutagent instead. Mind you, this pretty much destroys the old plant
- if ( weedlevel > 5 )
- del(myseed)
- var/newWeed = pick(/obj/item/seeds/libertymycelium, /obj/item/seeds/angelmycelium, /obj/item/seeds/deathnettleseed, /obj/item/seeds/kudzuseed)
- myseed = new newWeed
- dead = 0
- hardmutate()
- planted = 1
- age = 0
- health = myseed.endurance
- lastcycle = world.time
- harvest = 0
- weedlevel = 0 // Reset
+ if(istype(O, /obj/item/weapon/wirecutters) || istype(O, /obj/item/weapon/scalpel))
- spawn(5) // Wait a while
- updateicon()
- visible_message("\red The mutated weeds in [src] spawned a \blue [myseed.plantname]!")
- else
- usr << "The few weeds in the [src] seem to react, but only for a moment..."
- return
+ if(!seed)
+ user << "There is nothing to take a sample from in \the [src]."
+ return
+ if(dead)
+ user << "\The plant is dead."
+ return
+ // Create a sample.
+ seed.harvest(user,yield_mod,1)
+ health -= (rand(3,5)*10)
-obj/machinery/hydroponics/proc/plantdies() // OH NOES!!!!! I put this all in one function to make things easier
- health = 0
- dead = 1
- harvest = 0
- updateicon()
- visible_message("\red[src] is looking very unhealthy!")
- return
+ // Bookkeeping.
+ check_level_sanity()
+ force_update = 1
+ process()
+ return
+ else if(istype(O, /obj/item/weapon/reagent_containers/syringe))
-obj/machinery/hydroponics/proc/mutatepest() // Until someone makes a spaceworm, this is commented out
- if ( pestlevel > 5 )
- visible_message("The pests seem to behave oddly...")
-// spawn(10)
-// new /obj/effect/alien/spaceworm(loc)
- else
- usr << "The pests seem to behave oddly, but quickly settle down..." //Modified to give a better idea of what's happening when you inject mutagen. There's still nothing proper to spawn here though. -Cheridan
- return
-
-
-
-obj/machinery/hydroponics/attackby(var/obj/item/O as obj, var/mob/user as mob)
-
- //Called when mob user "attacks" it with object O
- if (istype(O, /obj/item/weapon/reagent_containers/glass))
- var/b_amount = O.reagents.get_reagent_amount("water")
- if(b_amount > 0 && waterlevel < 100)
- if(b_amount + waterlevel > 100)
- b_amount = 100 - waterlevel
- O.reagents.remove_reagent("water", b_amount)
- waterlevel += b_amount
- playsound(loc, 'sound/effects/slosh.ogg', 25, 1)
- user << "You fill \the [src] with [b_amount] units of water."
-
- // Toxicity dilutation code. The more water you put in, the lesser the toxin concentration.
- toxic -= round(b_amount/4)
- if (toxic < 0 ) // Make sure it won't go overboard
- toxic = 0
-
- else if(waterlevel >= 100)
- user << "\red \The [src] is already full."
- else
- user << "\red \The [O] is not filled with water."
- updateicon()
-
- else if ( istype(O, /obj/item/nutrient) )
- var/obj/item/nutrient/myNut = O
- user.u_equip(O)
- nutrilevel = 10
- yieldmod = myNut.yieldmod
- mutmod = myNut.mutmod
- user << "You replace the nutrient solution in the [src]."
- del(O)
- updateicon()
-
- else if(istype(O, /obj/item/weapon/reagent_containers/syringe)) // Syringe stuff
var/obj/item/weapon/reagent_containers/syringe/S = O
- if (planted)
- if (S.mode == 1)
- if(!S.reagents.total_volume)
- user << "\red \The [O] is empty."
- return
- user << "\red You inject the [myseed.plantname] with a chemical solution."
- // There needs to be a good amount of mutagen to actually work
-
- if(S.reagents.has_reagent("mutagen", 5))
- switch(rand(100))
- if (91 to 100) plantdies()
- if (81 to 90) mutatespecie()
- if (66 to 80) hardmutate()
- if (41 to 65) mutate()
- if (21 to 41) user << "The plants don't seem to react..."
- if (11 to 20) mutateweed()
- if (1 to 10) mutatepest()
- else user << "Nothing happens..."
-
- // Antitoxin binds shit pretty well. So the tox goes significantly down
- if(S.reagents.has_reagent("anti_toxin", 1))
- toxic -= round(S.reagents.get_reagent_amount("anti_toxin")*2)
-
- // NIGGA, YOU JUST WENT ON FULL RETARD.
- if(S.reagents.has_reagent("toxin", 1))
- toxic += round(S.reagents.get_reagent_amount("toxin")*2)
-
- // Milk is good for humans, but bad for plants. The sugars canot be used by plants, and the milk fat fucks up growth. Not shrooms though. I can't deal with this now...
- if(S.reagents.has_reagent("milk", 1))
- nutrilevel += round(S.reagents.get_reagent_amount("milk")*0.1)
- waterlevel += round(S.reagents.get_reagent_amount("milk")*0.9)
-
- // Beer is a chemical composition of alcohol and various other things. It's a shitty nutrient but hey, it's still one. Also alcohol is bad, mmmkay?
- if(S.reagents.has_reagent("beer", 1))
- health -= round(S.reagents.get_reagent_amount("beer")*0.05)
- nutrilevel += round(S.reagents.get_reagent_amount("beer")*0.25)
- waterlevel += round(S.reagents.get_reagent_amount("beer")*0.7)
-
- // You're an idiot of thinking that one of the most corrosive and deadly gasses would be beneficial
- if(S.reagents.has_reagent("fluorine", 1))
- health -= round(S.reagents.get_reagent_amount("fluorine")*2)
- toxic += round(S.reagents.get_reagent_amount("flourine")*2.5)
- waterlevel -= round(S.reagents.get_reagent_amount("flourine")*0.5)
- weedlevel -= rand(1,4)
-
- // You're an idiot of thinking that one of the most corrosive and deadly gasses would be beneficial
- if(S.reagents.has_reagent("chlorine", 1))
- health -= round(S.reagents.get_reagent_amount("chlorine")*1)
- toxic += round(S.reagents.get_reagent_amount("chlorine")*1.5)
- waterlevel -= round(S.reagents.get_reagent_amount("chlorine")*0.5)
- weedlevel -= rand(1,3)
-
- // White Phosphorous + water -> phosphoric acid. That's not a good thing really. Phosphoric salts are beneficial though. And even if the plant suffers, in the long run the tray gets some nutrients. The benefit isn't worth that much.
- if(S.reagents.has_reagent("phosphorus", 1))
- health -= round(S.reagents.get_reagent_amount("phosphorus")*0.75)
- nutrilevel += round(S.reagents.get_reagent_amount("phosphorus")*0.1)
- waterlevel -= round(S.reagents.get_reagent_amount("phosphorus")*0.5)
- weedlevel -= rand(1,2)
-
- // Plants should not have sugar, they can't use it and it prevents them getting water/ nutients, it is good for mold though...
- if(S.reagents.has_reagent("sugar", 1))
- weedlevel += rand(1,2)
- pestlevel += rand(1,2)
- nutrilevel+= round(S.reagents.get_reagent_amount("sugar")*0.1)
-
- // It is water!
- if(S.reagents.has_reagent("water", 1))
- waterlevel += round(S.reagents.get_reagent_amount("water")*1)
-
- // Holy water. Mostly the same as water, it also heals the plant a little with the power of the spirits~
- if(S.reagents.has_reagent("holywater", 1))
- waterlevel += round(S.reagents.get_reagent_amount("holywater")*1)
- health += round(S.reagents.get_reagent_amount("holywater")*0.1)
-
- // A variety of nutrients are dissolved in club soda, without sugar. These nutrients include carbon, oxygen, hydrogen, phosphorous, potassium, sulfur and sodium, all of which are needed for healthy plant growth.
- if(S.reagents.has_reagent("sodawater", 1))
- waterlevel += round(S.reagents.get_reagent_amount("sodawater")*1)
- health += round(S.reagents.get_reagent_amount("sodawater")*0.1)
- nutrilevel += round(S.reagents.get_reagent_amount("sodawater")*0.1)
-
- // Man, you guys are retards
- if(S.reagents.has_reagent("sacid", 1))
- health -= round(S.reagents.get_reagent_amount("sacid")*1)
- toxic += round(S.reagents.get_reagent_amount("sacid")*1.5)
- weedlevel -= rand(1,2)
-
- // SERIOUSLY
- if(S.reagents.has_reagent("pacid", 1))
- health -= round(S.reagents.get_reagent_amount("pacid")*2)
- toxic += round(S.reagents.get_reagent_amount("pacid")*3)
- weedlevel -= rand(1,4)
-
- // Plant-B-Gone is just as bad
- if(S.reagents.has_reagent("plantbgone", 1))
- health -= round(S.reagents.get_reagent_amount("plantbgone")*2)
- toxic -= round(S.reagents.get_reagent_amount("plantbgone")*3)
- weedlevel -= rand(4,8)
-
- // Healing
- if(S.reagents.has_reagent("cryoxadone", 1))
- health += round(S.reagents.get_reagent_amount("cryoxadone")*3)
- toxic -= round(S.reagents.get_reagent_amount("cryoxadone")*3)
-
- // FINALLY IMPLEMENTED, Ammonia is bad ass.
- if(S.reagents.has_reagent("ammonia", 1))
- health += round(S.reagents.get_reagent_amount("ammonia")*0.5)
- nutrilevel += round(S.reagents.get_reagent_amount("ammonia")*1)
-
- // FINALLY IMPLEMENTED, This is more bad ass, and pests get hurt by the corrosive nature of it, not the plant.
- if(S.reagents.has_reagent("diethylamine", 1))
- health += round(S.reagents.get_reagent_amount("diethylamine")*1)
- nutrilevel += round(S.reagents.get_reagent_amount("diethylamine")*2)
- pestlevel -= rand(1,2)
-
- // Compost, effectively
- if(S.reagents.has_reagent("nutriment", 1))
- health += round(S.reagents.get_reagent_amount("nutriment")*0.5)
- nutrilevel += round(S.reagents.get_reagent_amount("nutriment")*1)
-
- // Poor man's mutagen.
- if(S.reagents.has_reagent("radium", 1))
- health -= round(S.reagents.get_reagent_amount("radium")*1.5)
- toxic += round(S.reagents.get_reagent_amount("radium")*2)
- if(S.reagents.has_reagent("radium", 10))
- switch(rand(100))
- if (91 to 100) plantdies()
- if (81 to 90) mutatespecie()
- if (66 to 80) hardmutate()
- if (41 to 65) mutate()
- if (21 to 41) user << "The plants don't seem to react..."
- if (11 to 20) mutateweed()
- if (1 to 10) mutatepest()
- else user << "Nothing happens..."
-
- // The best stuff there is. For testing/debugging.
- if(S.reagents.has_reagent("adminordrazine", 1))
- waterlevel += round(S.reagents.get_reagent_amount("adminordrazine")*1)
- health += round(S.reagents.get_reagent_amount("adminordrazine")*1)
- nutrilevel += round(S.reagents.get_reagent_amount("adminordrazine")*1)
- pestlevel -= rand(1,5)
- weedlevel -= rand(1,5)
- if(S.reagents.has_reagent("adminordrazine", 5))
- switch(rand(100))
- if (66 to 100) mutatespecie()
- if (33 to 65) mutateweed()
- if (1 to 32) mutatepest()
- else user << "Nothing happens..."
-
- S.reagents.clear_reagents()
- if (weedlevel < 0 ) // The following checks are to prevent the stats from going out of bounds.
- weedlevel = 0
- if (health < 0 )
- health = 0
- if (waterlevel > 100 )
- waterlevel = 100
- if (waterlevel < 0 )
- waterlevel = 0
- if (toxic < 0 )
- toxic = 0
- if (toxic > 100 )
- toxic = 100
- if (pestlevel < 0 )
- pestlevel = 0
- if (nutrilevel > 10 )
- nutrilevel = 10
+ if (S.mode == 1)
+ if(seed)
+ return ..()
else
+ user << "There's no plant to inject."
+ return 1
+ else
+ if(seed)
+ //Leaving this in in case we want to extract from plants later.
user << "You can't get any extract out of this plant."
- else
- user << "There's nothing to apply the solution into."
- updateicon()
+ else
+ user << "There's nothing to draw something from."
+ return 1
- else if ( istype(O, /obj/item/seeds/) )
- if(!planted)
- user.u_equip(O)
- user << "You plant the [O.name]"
- dead = 0
- myseed = O
- planted = 1
- age = 1
- health = myseed.endurance
- lastcycle = world.time
- O.loc = src
- if((user.client && user.s_active != src))
- user.client.screen -= O
- O.dropped(user)
- updateicon()
+ else if (istype(O, /obj/item/seeds))
+
+ if(!seed)
+
+ var/obj/item/seeds/S = O
+ user.drop_item(O)
+
+ if(!S.seed)
+ user << "The packet seems to be empty. You throw it away."
+ del(O)
+ return
+
+ user << "You plant the [S.seed.seed_name] [S.seed.seed_noun]."
+
+ if(S.seed.spread == 1)
+ msg_admin_attack("[key_name(user)] has planted a creeper packet.")
+ var/obj/effect/plant_controller/creeper/PC = new(get_turf(src))
+ if(PC)
+ PC.seed = S.seed
+ else if(S.seed.spread == 2)
+ msg_admin_attack("[key_name(user)] has planted a spreading vine packet.")
+ var/obj/effect/plant_controller/PC = new(get_turf(src))
+ if(PC)
+ PC.seed = S.seed
+ else
+ seed = S.seed //Grab the seed datum.
+ dead = 0
+ age = 1
+ //Snowflakey, maybe move this to the seed datum
+ health = (istype(S, /obj/item/seeds/cutting) ? round(seed.endurance/rand(2,5)) : seed.endurance)
+
+ lastcycle = world.time
+
+ del(O)
+
+ check_level_sanity()
+ update_icon()
else
- user << "\red The [src] already has seeds in it!"
-
- else if (istype(O, /obj/item/device/analyzer/plant_analyzer))
- if(planted && myseed)
- user << "*** [myseed.plantname] ***" //Carn: now reports the plants growing, not the seeds.
- user << "-Plant Age: \blue [age]"
- user << "-Plant Endurance: \blue [myseed.endurance]"
- user << "-Plant Lifespan: \blue [myseed.lifespan]"
- if(myseed.yield != -1)
- user << "-Plant Yield: \blue [myseed.yield]"
- user << "-Plant Production: \blue [myseed.production]"
- if(myseed.potency != -1)
- user << "-Plant Potency: \blue [myseed.potency]"
- user << "-Weed level: \blue [weedlevel]/10"
- user << "-Pest level: \blue [pestlevel]/10"
- user << "-Toxicity level: \blue [toxic]/100"
- user << "-Water level: \blue [waterlevel]/100"
- user << "-Nutrition level: \blue [nutrilevel]/10"
- user << ""
- else
- user << "No plant found."
- user << "-Weed level: \blue [weedlevel]/10"
- user << "-Pest level: \blue [pestlevel]/10"
- user << "-Toxicity level: \blue [toxic]/100"
- user << "-Water level: \blue [waterlevel]/100"
- user << "-Nutrition level: \blue [nutrilevel]/10"
- user << ""
+ user << "\red \The [src] already has seeds in it!"
else if (istype(O, /obj/item/weapon/reagent_containers/spray/plantbgone))
- if(planted && myseed)
+ if(seed)
health -= rand(5,20)
if(pestlevel > 0)
- pestlevel -= 2 // Kill kill kill
- else
- pestlevel = 0
+ pestlevel -= 2
if(weedlevel > 0)
- weedlevel -= 3 // Kill kill kill
- else
- weedlevel = 0
- toxic += 4 // Oops
+ weedlevel -= 3
+
+ toxins += 4
+
+ check_level_sanity()
+
visible_message("\red \The [src] has been sprayed with \the [O][(user ? " by [user]." : ".")]")
playsound(loc, 'sound/effects/spray3.ogg', 50, 1, -6)
- updateicon()
+ update_icon()
+ else
+ user << "There's nothing in [src] to spray!"
else if (istype(O, /obj/item/weapon/minihoe)) // The minihoe
- //var/deweeding
+
if(weedlevel > 0)
user.visible_message("\red [user] starts uprooting the weeds.", "\red You remove the weeds from the [src].")
weedlevel = 0
- updateicon()
- src.updateicon()
+ update_icon()
else
user << "\red This plot is completely devoid of weeds. It doesn't need uprooting."
- else if ( istype(O, /obj/item/weapon/weedspray) )
- var/obj/item/weedkiller/myWKiller = O
- user.u_equip(O)
- toxic += myWKiller.toxicity
- weedlevel -= myWKiller.WeedKillStr
- if (weedlevel < 0 ) // Make sure it won't go overoboard
- weedlevel = 0
- if (toxic > 100 ) // Make sure it won't go overoboard
- toxic = 100
- user << "You apply the weedkiller solution into the [src]."
- playsound(loc, 'sound/effects/spray3.ogg', 50, 1, -6)
- del(O)
- updateicon()
-
else if (istype(O, /obj/item/weapon/storage/bag/plants))
+
attack_hand(user)
+
var/obj/item/weapon/storage/bag/plants/S = O
for (var/obj/item/weapon/reagent_containers/food/snacks/grown/G in locate(user.x,user.y,user.z))
if(!S.can_be_inserted(G))
return
S.handle_item_insertion(G, 1)
- else if ( istype(O, /obj/item/weapon/pestspray) )
- var/obj/item/pestkiller/myPKiller = O
- user.u_equip(O)
- toxic += myPKiller.toxicity
- pestlevel -= myPKiller.PestKillStr
- if (pestlevel < 0 ) // Make sure it won't go overoboard
- pestlevel = 0
- if (toxic > 100 ) // Make sure it won't go overoboard
- toxic = 100
- user << "You apply the pestkiller solution into the [src]."
+ else if ( istype(O, /obj/item/weapon/plantspray) )
+
+ var/obj/item/weapon/plantspray/spray = O
+ user.drop_item(O)
+ toxins += spray.toxicity
+ pestlevel -= spray.pest_kill_str
+ weedlevel -= spray.weed_kill_str
+ user << "You spray [src] with [O]."
playsound(loc, 'sound/effects/spray3.ogg', 50, 1, -6)
del(O)
- updateicon()
+
+ check_level_sanity()
+ update_icon()
+
else if(istype(O, /obj/item/weapon/wrench))
+
+ //If there's a connector here, the portable_atmospherics setup can handle it.
+ if(locate(/obj/machinery/atmospherics/portables_connector/) in loc)
+ return ..()
+
playsound(loc, 'sound/items/Ratchet.ogg', 50, 1)
anchored = !anchored
user << "You [anchored ? "wrench" : "unwrench"] \the [src]."
- else if(istype(O, /obj/item/weapon/shovel))
- if(istype(src, /obj/machinery/hydroponics/soil))
- user << "You clear up the [src]!"
- del(src)
+
else if(istype(O, /obj/item/apiary))
- if(planted)
- user << "\red The hydroponics tray is already occupied!"
+
+ if(seed)
+ user << "\red [src] is already occupied!"
else
user.drop_item()
del(O)
@@ -783,191 +713,99 @@ obj/machinery/hydroponics/attackby(var/obj/item/O as obj, var/mob/user as mob)
del(src)
return
-/obj/machinery/hydroponics/attack_tk(mob/user as mob)
- if(harvest)
- myseed.harvest(src)
- else if(dead)
- planted = 0
- dead = 0
- usr << text("You remove the dead plant from the [src].")
- del(myseed)
- updateicon()
+/obj/machinery/portable_atmospherics/hydroponics/attack_tk(mob/user as mob)
-/obj/machinery/hydroponics/attack_hand(mob/user as mob)
- if(istype(usr,/mob/living/silicon)) //How does AI know what plant is?
+ if(harvest)
+ harvest(user)
+
+ else if(dead)
+ remove_dead(user)
+
+/obj/machinery/portable_atmospherics/hydroponics/attack_hand(mob/user as mob)
+
+ if(istype(usr,/mob/living/silicon))
return
+
if(harvest)
- if(!user in range(1,src))
- return
- myseed.harvest()
+ harvest(user)
else if(dead)
- planted = 0
- dead = 0
- usr << text("You remove the dead plant from the [src].")
- del(myseed)
- updateicon()
+ remove_dead(user)
+
else
- if(planted && !dead)
- usr << text("The [src] has \blue [myseed.plantname] \black planted.")
- if(health <= (myseed.endurance / 2))
- usr << text("The plant looks unhealthy")
+ if(seed && !dead)
+ usr << "[src] has \blue [seed.display_name] \black planted."
+ if(health <= (seed.endurance / 2))
+ usr << "The plant looks \red unhealthy."
else
- usr << text("The [src] is empty.")
- usr << text("Water: [waterlevel]/100")
- usr << text("Nutrient: [nutrilevel]/10")
- if(weedlevel >= 5) // Visual aid for those blind
- usr << text("The [src] is filled with weeds!")
- if(pestlevel >= 5) // Visual aid for those blind
- usr << text("The [src] is filled with tiny worms!")
- usr << text ("") // Empty line for readability.
+ usr << "[src] is empty."
+ usr << "Water: [round(waterlevel,0.1)]/100"
+ usr << "Nutrient: [round(nutrilevel,0.1)]/10"
+ if(weedlevel >= 5)
+ usr << "[src] is \red filled with weeds!"
+ if(pestlevel >= 5)
+ usr << "[src] is \red filled with tiny worms!"
+ if(!istype(src,/obj/machinery/portable_atmospherics/hydroponics/soil))
-/obj/item/seeds/proc/harvest(mob/user = usr)
- var/produce = text2path(productname)
- var/obj/machinery/hydroponics/parent = loc //for ease of access
- var/t_amount = 0
+ var/turf/T = loc
+ var/datum/gas_mixture/environment
- while ( t_amount < (yield * parent.yieldmod ))
- var/obj/item/weapon/reagent_containers/food/snacks/grown/t_prod = new produce(user.loc, potency) // User gets a consumable
- if(!t_prod) return
- t_prod.seed = mypath
- t_prod.species = species
- t_prod.lifespan = lifespan
- t_prod.endurance = endurance
- t_prod.maturation = maturation
- t_prod.production = production
- t_prod.yield = yield
- t_prod.potency = potency
- t_prod.plant_type = plant_type
- t_amount++
+ if(closed_system && (connected_port || holding))
+ environment = air_contents
- parent.update_tray()
+ if(!environment)
+ if(istype(T))
+ environment = T.return_air()
-/obj/item/seeds/grassseed/harvest(mob/user = usr)
- var/obj/machinery/hydroponics/parent = loc //for ease of access
- var/t_yield = round(yield*parent.yieldmod)
+ if(!environment) //We're in a crate or nullspace, bail out.
+ return
- if(t_yield > 0)
- var/obj/item/stack/tile/grass/new_grass = new/obj/item/stack/tile/grass(user.loc)
- new_grass.amount = t_yield
+ var/area/A = T.loc
+ var/light_available
+ if(A)
+ if(A.lighting_use_dynamic)
+ light_available = max(0,min(10,T.lighting_lumcount)-5)
+ else
+ light_available = 5
- parent.update_tray()
+ usr << "The tray's sensor suite is reporting a light level of [light_available] lumens and a temperature of [environment.temperature]K."
-/obj/item/seeds/gibtomato/harvest(mob/user = usr)
- var/produce = text2path(productname)
- var/obj/machinery/hydroponics/parent = loc //for ease of access
- var/t_amount = 0
+/obj/machinery/portable_atmospherics/hydroponics/verb/close_lid()
+ set name = "Toggle Tray Lid"
+ set category = "Object"
+ set src in view(1)
- while ( t_amount < (yield * parent.yieldmod ))
- var/obj/item/weapon/reagent_containers/food/snacks/grown/t_prod = new produce(user.loc, potency) // User gets a consumable
+ if(!usr || usr.stat || usr.restrained())
+ return
- t_prod.seed = mypath
- t_prod.species = species
- t_prod.lifespan = lifespan
- t_prod.endurance = endurance
- t_prod.maturation = maturation
- t_prod.production = production
- t_prod.yield = yield
- t_prod.potency = potency
- t_prod.plant_type = plant_type
- t_amount++
-
- parent.update_tray()
-
-/obj/item/seeds/nettleseed/harvest(mob/user = usr)
- var/produce = text2path(productname)
- var/obj/machinery/hydroponics/parent = loc //for ease of access
- var/t_amount = 0
-
- while ( t_amount < (yield * parent.yieldmod ))
- var/obj/item/weapon/grown/t_prod = new produce(user.loc, potency) // User gets a consumable -QualityVan
- t_prod.seed = mypath
- t_prod.species = species
- t_prod.lifespan = lifespan
- t_prod.endurance = endurance
- t_prod.maturation = maturation
- t_prod.production = production
- t_prod.yield = yield
- t_prod.changePotency(potency) // -QualityVan
- t_prod.plant_type = plant_type
- t_amount++
-
- parent.update_tray()
-
-/obj/item/seeds/deathnettleseed/harvest(mob/user = usr) //isn't a nettle subclass yet, so
- var/produce = text2path(productname)
- var/obj/machinery/hydroponics/parent = loc //for ease of access
- var/t_amount = 0
-
- while ( t_amount < (yield * parent.yieldmod ))
- var/obj/item/weapon/grown/t_prod = new produce(user.loc, potency) // User gets a consumable -QualityVan
- t_prod.seed = mypath
- t_prod.species = species
- t_prod.lifespan = lifespan
- t_prod.endurance = endurance
- t_prod.maturation = maturation
- t_prod.production = production
- t_prod.yield = yield
- t_prod.changePotency(potency) // -QualityVan
- t_prod.plant_type = plant_type
- t_amount++
-
- parent.update_tray()
-
-/obj/item/seeds/eggyseed/harvest(mob/user = usr)
- var/produce = text2path(productname)
- var/obj/machinery/hydroponics/parent = loc //for ease of access
- var/t_amount = 0
-
- while ( t_amount < (yield * parent.yieldmod ))
- new produce(user.loc)
- t_amount++
-
- parent.update_tray()
-
-/obj/machinery/hydroponics/proc/update_tray(mob/user = usr)
- harvest = 0
- lastproduce = age
- if((yieldmod * myseed.yield) <= 0 || istype(myseed,/obj/item/seeds/replicapod))
- user << text("\red You fail to harvest anything useful.")
+ closed_system = !closed_system
+ usr << "You [closed_system ? "close" : "open"] the tray's lid."
+ if(closed_system)
+ flags &= ~OPENCONTAINER
else
- user << text("You harvest from the [myseed.plantname].")
- if(myseed.oneharvest)
- del(myseed)
- planted = 0
- dead = 0
- updateicon()
+ flags |= OPENCONTAINER
-///////////////////////////////////////////////////////////////////////////////
-/obj/machinery/hydroponics/soil //Not actually hydroponics at all! Honk!
+ update_icon()
+
+/obj/machinery/portable_atmospherics/hydroponics/soil
name = "soil"
icon = 'icons/obj/hydroponics.dmi'
icon_state = "soil"
density = 0
use_power = 0
+ draw_warnings = 0
- updateicon() // Same as normal but with the overlays removed - Cheridan.
- overlays.Cut()
- if(planted)
- if(dead)
- overlays += image('icons/obj/hydroponics.dmi', icon_state="[myseed.species]-dead")
- else if(harvest)
- if(myseed.plant_type == 2) // Shrooms don't have a -harvest graphic
- overlays += image('icons/obj/hydroponics.dmi', icon_state="[myseed.species]-grow[myseed.growthstages]")
- else
- overlays += image('icons/obj/hydroponics.dmi', icon_state="[myseed.species]-harvest")
- else if(age < myseed.maturation)
- var/t_growthstate = ((age / myseed.maturation) * myseed.growthstages )
- overlays += image('icons/obj/hydroponics.dmi', icon_state="[myseed.species]-grow[round(t_growthstate)]")
- lastproduce = age
- else
- overlays += image('icons/obj/hydroponics.dmi', icon_state="[myseed.species]-grow[myseed.growthstages]")
-
- if(!luminosity)
- if(istype(myseed,/obj/item/seeds/glowshroom))
- SetLuminosity(round(myseed.potency/10))
- else
- SetLuminosity(0)
+/obj/machinery/portable_atmospherics/hydroponics/soil/attackby(var/obj/item/O as obj, var/mob/user as mob)
+ if(istype(O, /obj/item/weapon/shovel))
+ user << "You clear up [src]!"
+ del(src)
+ else if(istype(O,/obj/item/weapon/shovel) || istype(O,/obj/item/weapon/tank))
return
+ else
+ ..()
-#undef HYDRO_SPEED_MULTIPLIER
+/obj/machinery/portable_atmospherics/hydroponics/soil/New()
+ ..()
+ verbs -= /obj/machinery/portable_atmospherics/hydroponics/verb/close_lid
+
+#undef HYDRO_SPEED_MULTIPLIER
\ No newline at end of file
diff --git a/code/modules/hydroponics/seed_datums.dm b/code/modules/hydroponics/seed_datums.dm
new file mode 100644
index 00000000000..b4917515d46
--- /dev/null
+++ b/code/modules/hydroponics/seed_datums.dm
@@ -0,0 +1,1419 @@
+var/global/list/seed_types = list() // A list of all seed data.
+var/global/list/gene_tag_masks = list() // Gene obfuscation for delicious trial and error goodness.
+
+// Predefined/roundstart varieties use a string key to make it
+// easier to grab the new variety when mutating. Post-roundstart
+// and mutant varieties use their uid converted to a string instead.
+// Looks like shit but it's sort of necessary.
+
+proc/populate_seed_list()
+
+ // Populate the global seed datum list.
+ for(var/type in typesof(/datum/seed)-/datum/seed)
+ var/datum/seed/S = new type
+ seed_types[S.name] = S
+ S.uid = "[seed_types.len]"
+ S.roundstart = 1
+
+ // Make sure any seed packets that were mapped in are updated
+ // correctly (since the seed datums did not exist a tick ago).
+ for(var/obj/item/seeds/S in world)
+ S.update_seed()
+
+ //Might as well mask the gene types while we're at it.
+ var/list/gene_tags = list("products","consumption","environment","resistance","vigour","flowers")
+ var/list/used_masks = list()
+
+ while(gene_tags && gene_tags.len)
+ var/gene_tag = pick(gene_tags)
+ var/gene_mask = "[num2hex(rand(0,255))]"
+
+ while(gene_mask in used_masks)
+ gene_mask = "[num2hex(rand(0,255))]"
+
+ used_masks += gene_mask
+ gene_tags -= gene_tag
+ gene_tag_masks[gene_tag] = gene_mask
+
+/datum/plantgene
+ var/genetype // Label used when applying trait.
+ var/list/values // Values to copy into the target seed datum.
+
+/datum/seed
+
+ //Tracking.
+ var/uid // Unique identifier.
+ var/name // Index for global list.
+ var/seed_name // Plant name for seed packet.
+ var/seed_noun = "seeds" // Descriptor for packet.
+ var/display_name // Prettier name.
+ var/roundstart // If set, seed will not display variety number.
+
+ // Output.
+ var/list/products // Possible fruit/other product paths.
+ var/list/mutants // Possible predefined mutant varieties, if any.
+ var/list/chems // Chemicals that plant produces in products/injects into victim.
+ var/list/consume_gasses // The plant will absorb these gasses during its life.
+ var/list/exude_gasses // The plant will exude these gasses during its life.
+
+ //Tolerances.
+ var/requires_nutrients = 1 // The plant can starve.
+ var/nutrient_consumption = 0.25 // Plant eats this much per tick.
+ var/requires_water = 1 // The plant can become dehydrated.
+ var/water_consumption = 3 // Plant drinks this much per tick.
+ var/ideal_heat = 293 // Preferred temperature in Kelvin.
+ var/heat_tolerance = 20 // Departure from ideal that is survivable.
+ var/ideal_light = 8 // Preferred light level in luminosity.
+ var/light_tolerance = 5 // Departure from ideal that is survivable.
+ var/toxins_tolerance = 5 // Resistance to poison.
+ var/lowkpa_tolerance = 25 // Low pressure capacity.
+ var/highkpa_tolerance = 200 // High pressure capacity.
+ var/pest_tolerance = 5 // Threshold for pests to impact health.
+ var/weed_tolerance = 5 // Threshold for weeds to impact health.
+
+ //General traits.
+ var/endurance = 100 // Maximum plant HP when growing.
+ var/yield = 0 // Amount of product.
+ var/lifespan = 0 // Time before the plant dies.
+ var/maturation = 0 // Time taken before the plant is mature.
+ var/production = 0 // Time before harvesting can be undertaken again.
+ var/growth_stages = 6 // Number of stages the plant passes through before it is mature.
+ var/harvest_repeat = 0 // If 1, this plant will fruit repeatedly..
+ var/potency = 1 // General purpose plant strength value.
+ var/spread = 0 // 0 limits plant to tray, 1 = creepers, 2 = vines.
+ var/carnivorous = 0 // 0 = none, 1 = eat pests in tray, 2 = eat living things (when a vine).
+ var/parasite = 0 // 0 = no, 1 = gain health from weed level.
+ var/immutable = 0 // If set, plant will never mutate. If -1, plant is highly mutable.
+ var/alter_temp // If set, the plant will periodically alter local temp by this amount.
+
+ // Cosmetics.
+ var/plant_icon // Icon to use for the plant growing in the tray.
+ var/product_icon // Base to use for fruit coming from this plant (if a vine).
+ var/product_colour // Colour to apply to product base (if a vine).
+ var/packet_icon = "seed" // Icon to use for physical seed packet item.
+ var/biolum // Plant is bioluminescent.
+ var/biolum_colour // The colour of the plant's radiance.
+ var/flowers // Plant has a flower overlay.
+ var/flower_icon = "vine_fruit" // Which overlay to use.
+ var/flower_colour // Which colour to use.
+
+//Returns a key corresponding to an entry in the global seed list.
+/datum/seed/proc/get_mutant_variant()
+ if(!mutants || !mutants.len || immutable > 0) return 0
+ return pick(mutants)
+
+//Mutates the plant overall (randomly).
+/datum/seed/proc/mutate(var/degree,var/turf/source_turf)
+
+ if(!degree || immutable > 0) return
+
+ source_turf.visible_message("\blue \The [display_name] quivers!")
+
+ //This looks like shit, but it's a lot easier to read/change this way.
+ var/total_mutations = rand(1,1+degree)
+ for(var/i = 0;ichanges colour!")
+ else
+ source_turf.visible_message("\blue \The [display_name]'s glow dims...")
+ if(11)
+ if(prob(degree*2))
+ flowers = !flowers
+ if(flowers)
+ source_turf.visible_message("\blue \The [display_name] sprouts a bevy of flowers!")
+ if(prob(degree*2))
+ flower_colour = "#[pick(list("FF0000","FF7F00","FFFF00","00FF00","0000FF","4B0082","8F00FF"))]"
+ source_turf.visible_message("\blue \The [display_name]'s flowers changes colour!")
+ else
+ source_turf.visible_message("\blue \The [display_name]'s flowers wither and fall off.")
+ return
+
+//Mutates a specific trait/set of traits.
+/datum/seed/proc/apply_gene(var/datum/plantgene/gene)
+
+ if(!gene || !gene.values || immutable > 0) return
+
+ switch(gene.genetype)
+
+ //Splicing products has some detrimental effects on yield and lifespan.
+ if("products")
+
+ if(gene.values.len < 6) return
+
+ yield = round(yield*0.8)
+ endurance = round(endurance*0.8)
+ lifespan = round(lifespan*0.8)
+
+ if(!products) products = list()
+ products |= gene.values[1]
+
+ if(!chems) chems = list()
+
+ var/list/gene_value = gene.values[2]
+ for(var/rid in gene_value)
+
+ var/list/gene_chem = gene_value[rid]
+
+ if(chems[rid])
+
+ var/list/chem_value = chems[rid]
+
+ chems[rid][1] = max(1,round((gene_chem[1] + chem_value[1])/2))
+
+ if(gene_chem.len > 1)
+ if(chem_value > 1)
+ chems[rid][2] = max(1,round((gene_chem[2] + chem_value[2])/2))
+ else
+ chems[rid][2] = gene_chem[2]
+
+ else
+ var/list/new_chem = gene_chem[rid]
+ chems[rid] = new_chem.Copy()
+
+ var/list/new_gasses = gene.values[3]
+ if(istype(new_gasses))
+ if(!exude_gasses) exude_gasses = list()
+ exude_gasses |= new_gasses
+ for(var/gas in exude_gasses)
+ exude_gasses[gas] = max(1,round(exude_gasses[gas]*0.8))
+
+ alter_temp = gene.values[4]
+ potency = gene.values[5]
+ harvest_repeat = gene.values[6]
+
+ if("consumption")
+
+ if(gene.values.len < 7) return
+
+ consume_gasses = gene.values[1]
+ requires_nutrients = gene.values[2]
+ nutrient_consumption = gene.values[3]
+ requires_water = gene.values[4]
+ water_consumption = gene.values[5]
+ carnivorous = gene.values[6]
+ parasite = gene.values[7]
+
+ if("environment")
+
+ if(gene.values.len < 6) return
+
+ ideal_heat = gene.values[1]
+ heat_tolerance = gene.values[2]
+ ideal_light = gene.values[3]
+ light_tolerance = gene.values[4]
+ lowkpa_tolerance = gene.values[5]
+ highkpa_tolerance = gene.values[6]
+
+ if("resistance")
+
+ if(gene.values.len < 3) return
+
+ toxins_tolerance = gene.values[1]
+ pest_tolerance = gene.values[2]
+ weed_tolerance = gene.values[3]
+
+ if("vigour")
+
+ if(gene.values.len < 6) return
+
+ endurance = gene.values[1]
+ yield = gene.values[2]
+ lifespan = gene.values[3]
+ spread = gene.values[4]
+ maturation = gene.values[5]
+ production = gene.values[6]
+
+ if("flowers")
+
+ if(gene.values.len < 7) return
+
+ product_icon = gene.values[1]
+ product_colour = gene.values[2]
+ biolum = gene.values[3]
+ biolum_colour = gene.values[4]
+ flowers = gene.values[5]
+ flower_icon = gene.values[6]
+ flower_colour = gene.values[7]
+
+//Returns a list of the desired trait values.
+/datum/seed/proc/get_gene(var/genetype)
+
+ if(!genetype) return 0
+
+ var/datum/plantgene/P = new()
+ P.genetype = genetype
+
+ switch(genetype)
+ if("products")
+ P.values = list(
+ (products ? products : 0),
+ (chems ? chems : 0),
+ (exude_gasses ? exude_gasses : 0),
+ (alter_temp ? alter_temp : 0),
+ (potency ? potency : 0),
+ (harvest_repeat ? harvest_repeat : 0)
+ )
+
+ if("consumption")
+ P.values = list(
+ (consume_gasses ? consume_gasses : 0),
+ (requires_nutrients ? requires_nutrients : 0),
+ (nutrient_consumption ? nutrient_consumption : 0),
+ (requires_water ? requires_water : 0),
+ (water_consumption ? water_consumption : 0),
+ (carnivorous ? carnivorous : 0),
+ (parasite ? parasite : 0)
+ )
+
+ if("environment")
+ P.values = list(
+ (ideal_heat ? ideal_heat : 0),
+ (heat_tolerance ? heat_tolerance : 0),
+ (ideal_light ? ideal_light : 0),
+ (light_tolerance ? light_tolerance : 0),
+ (lowkpa_tolerance ? lowkpa_tolerance : 0),
+ (highkpa_tolerance ? highkpa_tolerance : 0)
+ )
+
+ if("resistance")
+ P.values = list(
+ (toxins_tolerance ? toxins_tolerance : 0),
+ (pest_tolerance ? pest_tolerance : 0),
+ (weed_tolerance ? weed_tolerance : 0)
+ )
+
+ if("vigour")
+ P.values = list(
+ (endurance ? endurance : 0),
+ (yield ? yield : 0),
+ (lifespan ? lifespan : 0),
+ (spread ? spread : 0),
+ (maturation ? maturation : 0),
+ (production ? production : 0)
+ )
+
+ if("flowers")
+ P.values = list(
+ (product_icon ? product_icon : 0),
+ (product_colour ? product_colour : 0),
+ (biolum ? biolum : 0),
+ (biolum_colour ? biolum_colour : 0),
+ (flowers ? flowers : 0),
+ (flower_icon ? flower_icon : 0),
+ (flower_colour ? flower_colour : 0)
+ )
+
+ return (P ? P : 0)
+
+//Place the plant products at the feet of the user.
+/datum/seed/proc/harvest(var/mob/user,var/yield_mod,var/harvest_sample)
+ if(!user)
+ return
+
+ var/got_product
+ if(!isnull(products) && products.len && yield > 0)
+ got_product = 1
+
+ if(!got_product)
+ user << "\red You fail to harvest anything useful."
+ else
+ user << "You [harvest_sample ? "take a sample" : "harvest"] from the [display_name]."
+
+ //This may be a new line. Update the global if it is.
+ if(name == "new line" || !(name in seed_types))
+ uid = seed_types.len + 1
+ name = "[uid]"
+ seed_types[name] = src
+
+ if(harvest_sample)
+ var/obj/item/seeds/seeds = new(get_turf(user))
+ seeds.seed_type = name
+ seeds.update_seed()
+ return
+
+ var/total_yield
+ if(isnull(yield_mod) || yield_mod < 1)
+ yield_mod = 0
+ total_yield = yield
+ else
+ total_yield = yield + rand(yield_mod)
+
+ currently_querying = list()
+ for(var/i = 0;i 0) return
+
+ //Set up some basic information.
+ var/datum/seed/new_seed = new
+ new_seed.name = "new line"
+ new_seed.uid = 0
+ new_seed.roundstart = 0
+
+ //Copy over everything else.
+ if(products) new_seed.products = products.Copy()
+ if(mutants) new_seed.mutants = mutants.Copy()
+ if(chems) new_seed.chems = chems.Copy()
+ if(consume_gasses) new_seed.consume_gasses = consume_gasses.Copy()
+ if(exude_gasses) new_seed.exude_gasses = exude_gasses.Copy()
+
+ new_seed.seed_name = "[(roundstart ? "[(modified ? "modified" : "mutant")] " : " ")][seed_name]"
+ new_seed.display_name = "[(roundstart ? "[(modified ? "modified" : "mutant")] " : " ")][display_name]"
+ new_seed.seed_noun = seed_noun
+
+ new_seed.requires_nutrients = requires_nutrients
+ new_seed.nutrient_consumption = nutrient_consumption
+ new_seed.requires_water = requires_water
+ new_seed.water_consumption = water_consumption
+ new_seed.ideal_heat = ideal_heat
+ new_seed.heat_tolerance = heat_tolerance
+ new_seed.ideal_light = ideal_light
+ new_seed.light_tolerance = light_tolerance
+ new_seed.toxins_tolerance = toxins_tolerance
+ new_seed.lowkpa_tolerance = lowkpa_tolerance
+ new_seed.highkpa_tolerance = highkpa_tolerance
+ new_seed.pest_tolerance = pest_tolerance
+ new_seed.weed_tolerance = weed_tolerance
+ new_seed.endurance = endurance
+ new_seed.yield = yield
+ new_seed.lifespan = lifespan
+ new_seed.maturation = maturation
+ new_seed.production = production
+ new_seed.growth_stages = growth_stages
+ new_seed.harvest_repeat = harvest_repeat
+ new_seed.potency = potency
+ new_seed.spread = spread
+ new_seed.carnivorous = carnivorous
+ new_seed.parasite = parasite
+ new_seed.plant_icon = plant_icon
+ new_seed.product_icon = product_icon
+ new_seed.product_colour = product_colour
+ new_seed.packet_icon = packet_icon
+ new_seed.biolum = biolum
+ new_seed.biolum_colour = biolum_colour
+ new_seed.flowers = flowers
+ new_seed.flower_icon = flower_icon
+ new_seed.alter_temp = alter_temp
+
+ return new_seed
+
+// Actual roundstart seed types after this point.
+// Chili plants/variants.
+/datum/seed/chili
+
+ name = "chili"
+ seed_name = "chili"
+ display_name = "chili plants"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/chili)
+ chems = list("capsaicin" = list(3,5), "nutriment" = list(1,25))
+ mutants = list("icechili")
+ packet_icon = "seed-chili"
+ plant_icon = "chili"
+ harvest_repeat = 1
+
+ lifespan = 20
+ maturation = 5
+ production = 5
+ yield = 4
+ potency = 20
+
+/datum/seed/chili/ice
+ name = "icechili"
+ seed_name = "ice pepper"
+ display_name = "ice-pepper plants"
+ mutants = null
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/icepepper)
+ chems = list("frostoil" = list(3,5), "nutriment" = list(1,50))
+ packet_icon = "seed-icepepper"
+ plant_icon = "chiliice"
+
+ maturation = 4
+ production = 4
+
+// Berry plants/variants.
+/datum/seed/berry
+ name = "berries"
+ seed_name = "berry"
+ display_name = "berry bush"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/berries)
+ mutants = list("glowberries","poisonberries")
+ packet_icon = "seed-berry"
+ plant_icon = "berry"
+ harvest_repeat = 1
+ chems = list("nutriment" = list(1,10))
+
+ lifespan = 20
+ maturation = 5
+ production = 5
+ yield = 2
+ potency = 10
+
+/datum/seed/berry/glow
+ name = "glowberries"
+ seed_name = "glowberry"
+ display_name = "glowberry bush"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/glowberries)
+ mutants = null
+ packet_icon = "seed-glowberry"
+ plant_icon = "glowberry"
+ chems = list("nutriment" = list(1,10), "uranium" = list(3,5))
+
+ lifespan = 30
+ maturation = 5
+ production = 5
+ yield = 2
+ potency = 10
+
+/datum/seed/berry/poison
+ name = "poisonberries"
+ seed_name = "poison berry"
+ display_name = "poison berry bush"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/poisonberries)
+ mutants = list("deathberries")
+ packet_icon = "seed-poisonberry"
+ plant_icon = "poisonberry"
+ chems = list("nutriment" = list(1), "toxin" = list(3,5))
+
+/datum/seed/berry/poison/death
+ name = "deathberries"
+ seed_name = "death berry"
+ display_name = "death berry bush"
+ mutants = null
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/deathberries)
+ packet_icon = "seed-deathberry"
+ plant_icon = "deathberry"
+ chems = list("nutriment" = list(1), "toxin" = list(3,3), "lexorin" = list(1,5))
+
+ yield = 3
+ potency = 50
+
+// Nettles/variants.
+/datum/seed/nettle
+ name = "nettle"
+ seed_name = "nettle"
+ display_name = "nettles"
+ products = list(/obj/item/weapon/grown/nettle)
+ mutants = list("deathnettle")
+ packet_icon = "seed-nettle"
+ plant_icon = "nettle"
+ harvest_repeat = 1
+ chems = list("nutriment" = list(1,50), "sacid" = list(0,1))
+ lifespan = 30
+ maturation = 6
+ production = 6
+ yield = 4
+ potency = 10
+ growth_stages = 5
+
+/datum/seed/nettle/death
+ name = "deathnettle"
+ seed_name = "death nettle"
+ display_name = "death nettles"
+ products = list(/obj/item/weapon/grown/deathnettle)
+ mutants = null
+ packet_icon = "seed-deathnettle"
+ plant_icon = "deathnettle"
+ chems = list("nutriment" = list(1,50), "pacid" = list(0,1))
+
+ maturation = 8
+ yield = 2
+
+//Tomatoes/variants.
+/datum/seed/tomato
+ name = "tomato"
+ seed_name = "tomato"
+ display_name = "tomato plant"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/tomato)
+ mutants = list("bluetomato","bloodtomato")
+ packet_icon = "seed-tomato"
+ plant_icon = "tomato"
+ harvest_repeat = 1
+ chems = list("nutriment" = list(1,10))
+
+ lifespan = 25
+ maturation = 8
+ production = 6
+ yield = 2
+ potency = 10
+
+/datum/seed/tomato/blood
+ name = "bloodtomato"
+ seed_name = "blood tomato"
+ display_name = "blood tomato plant"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/bloodtomato)
+ mutants = list("killer")
+ packet_icon = "seed-bloodtomato"
+ plant_icon = "bloodtomato"
+ chems = list("nutriment" = list(1,10), "blood" = list(1,5))
+
+ yield = 3
+
+/datum/seed/tomato/killer
+ name = "killertomato"
+ seed_name = "killer tomato"
+ display_name = "killer tomato plant"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/killertomato)
+ mutants = null
+ packet_icon = "seed-killertomato"
+ plant_icon = "killertomato"
+
+ yield = 2
+ growth_stages = 2
+
+/datum/seed/tomato/blue
+ name = "bluetomato"
+ seed_name = "blue tomato"
+ display_name = "blue tomato plant"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/bluetomato)
+ mutants = list("bluespacetomato")
+ packet_icon = "seed-bluetomato"
+ plant_icon = "bluetomato"
+ chems = list("nutriment" = list(1,20), "lube" = list(1,5))
+
+/datum/seed/tomato/blue/teleport
+ name = "bluespacetomato"
+ seed_name = "bluespace tomato"
+ display_name = "bluespace tomato plant"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/bluespacetomato)
+ mutants = null
+ packet_icon = "seed-bluespacetomato"
+ plant_icon = "bluespacetomato"
+ chems = list("nutriment" = list(1,20), "singulo" = list(1,5))
+
+//Eggplants/varieties.
+/datum/seed/eggplant
+ name = "eggplant"
+ seed_name = "eggplant"
+ display_name = "eggplants"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/eggplant)
+ mutants = list("realeggplant")
+ packet_icon = "seed-eggplant"
+ plant_icon = "eggplant"
+ harvest_repeat = 1
+ chems = list("nutriment" = list(1,10))
+
+ lifespan = 25
+ maturation = 6
+ production = 6
+ yield = 2
+ potency = 20
+
+/datum/seed/eggplant/eggs
+ name = "realeggplant"
+ seed_name = "egg-plant"
+ display_name = "egg-plants"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/egg)
+ mutants = null
+ packet_icon = "seed-eggy"
+ plant_icon = "eggy"
+
+ lifespan = 75
+ production = 12
+
+//Apples/varieties.
+
+/datum/seed/apple
+ name = "apple"
+ seed_name = "apple"
+ display_name = "apple tree"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/apple)
+ mutants = list("poisonapple","goldapple")
+ packet_icon = "seed-apple"
+ plant_icon = "apple"
+ harvest_repeat = 1
+ chems = list("nutriment" = list(1,10))
+
+ lifespan = 55
+ maturation = 6
+ production = 6
+ yield = 5
+ potency = 10
+
+/datum/seed/apple/poison
+ name = "poisonapple"
+ mutants = null
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/apple/poisoned)
+ chems = list("cyanide" = list(1,5))
+
+/datum/seed/apple/gold
+ name = "goldapple"
+ seed_name = "golden apple"
+ display_name = "gold apple tree"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/goldapple)
+ mutants = null
+ packet_icon = "seed-goldapple"
+ plant_icon = "goldapple"
+ chems = list("nutriment" = list(1,10), "gold" = list(1,5))
+
+ maturation = 10
+ production = 10
+ yield = 3
+
+//Ambrosia/varieties.
+/datum/seed/ambrosia
+ name = "ambrosia"
+ seed_name = "ambrosia vulgaris"
+ display_name = "ambrosia vulgaris"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/ambrosiavulgaris)
+ mutants = list("ambrosiadeus")
+ packet_icon = "seed-ambrosiavulgaris"
+ plant_icon = "ambrosiavulgaris"
+ harvest_repeat = 1
+ chems = list("nutriment" = list(1), "space_drugs" = list(1,8), "kelotane" = list(1,8,1), "bicaridine" = list(1,10,1), "toxin" = list(1,10))
+
+ lifespan = 60
+ maturation = 6
+ production = 6
+ yield = 6
+ potency = 5
+
+/datum/seed/ambrosia/deus
+ name = "ambrosiadeus"
+ seed_name = "ambrosia deus"
+ display_name = "ambrosia deus"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/ambrosiadeus)
+ mutants = null
+ packet_icon = "seed-ambrosiadeus"
+ plant_icon = "ambrosiadeus"
+ chems = list("nutriment" = list(1), "bicaridine" = list(1,8), "synaptizine" = list(1,8,1), "hyperzine" = list(1,10,1), "space_drugs" = list(1,10))
+
+//Mushrooms/varieties.
+/datum/seed/mushroom
+ name = "mushrooms"
+ seed_name = "chanterelle"
+ seed_noun = "spores"
+ display_name = "chanterelle mushrooms"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/chanterelle)
+ mutants = list("reishi","amanita","plumphelmet")
+ packet_icon = "mycelium-chanter"
+ plant_icon = "chanter"
+ chems = list("nutriment" = list(1,25))
+
+ lifespan = 35
+ maturation = 7
+ production = 1
+ yield = 5
+ potency = 1
+ growth_stages = 3
+
+/datum/seed/mushroom/mold
+ name = "mold"
+ seed_name = "brown mold"
+ display_name = "brown mold"
+ products = null
+ mutants = null
+ //mutants = list("wallrot") //TBD.
+ plant_icon = "mold"
+
+ lifespan = 50
+ maturation = 10
+ yield = -1
+
+/datum/seed/mushroom/plump
+ name = "plumphelmet"
+ seed_name = "plump helmet"
+ display_name = "plump helmet mushrooms"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/plumphelmet)
+ mutants = list("walkingmushroom","towercap")
+ packet_icon = "mycelium-plump"
+ plant_icon = "plump"
+ chems = list("nutriment" = list(2,10))
+
+ lifespan = 25
+ maturation = 8
+ yield = 4
+ potency = 0
+
+/datum/seed/mushroom/hallucinogenic
+ name = "reishi"
+ seed_name = "reishi"
+ display_name = "reishi"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/reishi)
+ mutants = list("libertycap","glowshroom")
+ packet_icon = "mycelium-reishi"
+ plant_icon = "reishi"
+ chems = list("nutriment" = list(1,50), "psilocybin" = list(3,5))
+
+ maturation = 10
+ production = 5
+ yield = 4
+ potency = 15
+ growth_stages = 4
+
+/datum/seed/mushroom/hallucinogenic/strong
+ name = "libertycap"
+ seed_name = "liberty cap"
+ display_name = "liberty cap mushrooms"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/libertycap)
+ mutants = null
+ packet_icon = "mycelium-liberty"
+ plant_icon = "liberty"
+ chems = list("nutriment" = list(1), "stoxin" = list(3,3), "space_drugs" = list(1,25))
+
+ lifespan = 25
+ production = 1
+ potency = 15
+ growth_stages = 3
+
+/datum/seed/mushroom/poison
+ name = "amanita"
+ seed_name = "fly amanita"
+ display_name = "fly amanita mushrooms"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/amanita)
+ mutants = list("destroyingangel","plastic")
+ packet_icon = "mycelium-amanita"
+ plant_icon = "amanita"
+ chems = list("nutriment" = list(1), "amatoxin" = list(3,3), "psilocybin" = list(1,25))
+
+ lifespan = 50
+ maturation = 10
+ production = 5
+ yield = 4
+ potency = 10
+
+/datum/seed/mushroom/poison/death
+ name = "destroyingangel"
+ seed_name = "destroying angel"
+ display_name = "destroying angel mushrooms"
+ mutants = null
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/angel)
+ packet_icon = "mycelium-angel"
+ plant_icon = "angel"
+ chems = list("nutriment" = list(1,50), "amatoxin" = list(13,3), "psilocybin" = list(1,25))
+
+ maturation = 12
+ yield = 2
+ potency = 35
+
+/datum/seed/mushroom/towercap
+ name = "towercap"
+ seed_name = "tower cap"
+ display_name = "tower caps"
+ mutants = null
+ products = list(/obj/item/weapon/grown/log)
+ packet_icon = "mycelium-tower"
+ plant_icon = "towercap"
+
+ lifespan = 80
+ maturation = 15
+
+/datum/seed/mushroom/glowshroom
+ name = "glowshroom"
+ seed_name = "glowshroom"
+ display_name = "glowshrooms"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/glowshroom)
+ mutants = null
+ packet_icon = "mycelium-glowshroom"
+ plant_icon = "glowshroom"
+ chems = list("radium" = list(1,20))
+
+ lifespan = 120
+ maturation = 15
+ yield = 3
+ potency = 30
+ growth_stages = 4
+ biolum = 1
+ biolum_colour = "#006622"
+
+/datum/seed/mushroom/walking
+ name = "walkingmushroom"
+ seed_name = "walking mushroom"
+ display_name = "walking mushrooms"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/walkingmushroom)
+ mutants = null
+ packet_icon = "mycelium-walkingmushroom"
+ plant_icon = "walkingmushroom"
+ chems = list("nutriment" = list(2,10))
+
+ lifespan = 30
+ maturation = 5
+ yield = 1
+ potency = 0
+ growth_stages = 3
+
+/datum/seed/mushroom/plastic
+ name = "plastic"
+ seed_name = "plastellium"
+ display_name = "plastellium"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/plastellium)
+ mutants = null
+ packet_icon = "mycelium-plast"
+ plant_icon = "plastellium"
+ chems = list("plasticide" = list(1,10))
+
+ lifespan = 15
+ maturation = 5
+ production = 6
+ yield = 6
+ potency = 20
+
+//Flowers/varieties
+/datum/seed/flower
+ name = "harebells"
+ seed_name = "harebell"
+ display_name = "harebells"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/harebell)
+ packet_icon = "seed-harebell"
+ plant_icon = "harebell"
+ chems = list("nutriment" = list(1,20))
+
+ lifespan = 100
+ maturation = 7
+ production = 1
+ yield = 2
+ growth_stages = 4
+
+/datum/seed/flower/poppy
+ name = "poppies"
+ seed_name = "poppy"
+ display_name = "poppies"
+ packet_icon = "seed-poppy"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/poppy)
+ plant_icon = "poppy"
+ chems = list("nutriment" = list(1,20), "bicaridine" = list(1,10))
+
+ lifespan = 25
+ potency = 20
+ maturation = 8
+ production = 6
+ yield = 6
+ growth_stages = 3
+ plant_icon = ""
+
+/datum/seed/flower/sunflower
+ name = "sunflowers"
+ seed_name = "sunflower"
+ display_name = "sunflowers"
+ packet_icon = "seed-sunflower"
+ products = list(/obj/item/weapon/grown/sunflower)
+ plant_icon = "sunflower"
+
+ lifespan = 25
+ maturation = 6
+ growth_stages = 3
+
+//Grapes/varieties
+/datum/seed/grapes
+ name = "grapes"
+ seed_name = "grape"
+ display_name = "grapevines"
+ packet_icon = "seed-grapes"
+ mutants = list("greengrapes")
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/grapes)
+ plant_icon = "grape"
+ harvest_repeat = 1
+ chems = list("nutriment" = list(1,10), "sugar" = list(1,5))
+
+ lifespan = 50
+ maturation = 3
+ production = 5
+ yield = 4
+ potency = 10
+
+/datum/seed/grapes/green
+ name = "greengrapes"
+ seed_name = "green grape"
+ display_name = "green grapevines"
+ packet_icon = "seed-greengrapes"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/greengrapes)
+ mutants = null
+ plant_icon = "greengrape"
+ chems = list("nutriment" = list(1,10), "kelotane" = list(3,5))
+
+//Everything else
+/datum/seed/peanuts
+ name = "peanut"
+ seed_name = "peanut"
+ display_name = "peanut vines"
+ packet_icon = "seed-peanut"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/peanut)
+ plant_icon = "peanut"
+ harvest_repeat = 1
+ chems = list("nutriment" = list(1,10))
+
+ lifespan = 55
+ maturation = 6
+ production = 6
+ yield = 6
+ potency = 10
+
+/datum/seed/cabbage
+ name = "cabbage"
+ seed_name = "cabbage"
+ display_name = "cabbages"
+ packet_icon = "seed-cabbage"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/cabbage)
+ plant_icon = "cabbage"
+ harvest_repeat = 1
+ chems = list("nutriment" = list(1,10))
+
+ lifespan = 50
+ maturation = 3
+ production = 5
+ yield = 4
+ potency = 10
+ growth_stages = 1
+
+/datum/seed/shand
+ name = "shand"
+ seed_name = "S'randar's hand"
+ display_name = "S'randar's hand leaves"
+ packet_icon = "seed-shand"
+ products = list(/obj/item/stack/medical/bruise_pack/tajaran)
+ plant_icon = "shand"
+ chems = list("bicaridine" = list(0,10))
+
+ lifespan = 50
+ maturation = 3
+ production = 5
+ yield = 4
+ potency = 10
+ growth_stages = 3
+
+/datum/seed/mtear
+ name = "mtear"
+ seed_name = "Messa's tear"
+ display_name = "Messa's tear leaves"
+ packet_icon = "seed-mtear"
+ products = list(/obj/item/stack/medical/ointment/tajaran)
+ plant_icon = "mtear"
+ chems = list("honey" = list(1,10), "kelotane" = list(3,5))
+
+ lifespan = 50
+ maturation = 3
+ production = 5
+ yield = 4
+ potency = 10
+ growth_stages = 3
+
+/datum/seed/banana
+ name = "banana"
+ seed_name = "banana"
+ display_name = "banana tree"
+ packet_icon = "seed-banana"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/banana)
+ plant_icon = "banana"
+ harvest_repeat = 1
+ chems = list("banana" = list(1,10))
+
+ lifespan = 50
+ maturation = 6
+ production = 6
+ yield = 3
+
+/datum/seed/corn
+ name = "corn"
+ seed_name = "corn"
+ display_name = "ears of corn"
+ packet_icon = "seed-corn"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/corn)
+ plant_icon = "corn"
+ chems = list("nutriment" = list(1,10))
+
+ lifespan = 25
+ maturation = 8
+ production = 6
+ yield = 3
+ potency = 20
+ growth_stages = 3
+
+/datum/seed/potato
+ name = "potato"
+ seed_name = "potato"
+ display_name = "potatoes"
+ packet_icon = "seed-potato"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/potato)
+ plant_icon = "potato"
+ chems = list("nutriment" = list(1,10))
+
+ lifespan = 30
+ maturation = 10
+ production = 1
+ yield = 4
+ potency = 10
+ growth_stages = 4
+
+/datum/seed/soybean
+ name = "soybean"
+ seed_name = "soybean"
+ display_name = "soybeans"
+ packet_icon = "seed-soybean"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/soybeans)
+ plant_icon = "soybean"
+ harvest_repeat = 1
+ chems = list("nutriment" = list(1,20))
+
+ lifespan = 25
+ maturation = 4
+ production = 4
+ yield = 3
+ potency = 5
+
+/datum/seed/wheat
+ name = "wheat"
+ seed_name = "wheat"
+ display_name = "wheat stalks"
+ packet_icon = "seed-wheat"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/wheat)
+ plant_icon = "wheat"
+ chems = list("nutriment" = list(1,25))
+
+ lifespan = 25
+ maturation = 6
+ production = 1
+ yield = 4
+ potency = 5
+
+/datum/seed/rice
+ name = "rice"
+ seed_name = "rice"
+ display_name = "rice stalks"
+ packet_icon = "seed-rice"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/ricestalk)
+ plant_icon = "rice"
+ chems = list("nutriment" = list(1,25))
+
+ lifespan = 25
+ maturation = 6
+ production = 1
+ yield = 4
+ potency = 5
+ growth_stages = 4
+
+/datum/seed/carrots
+ name = "carrot"
+ seed_name = "carrot"
+ display_name = "carrots"
+ packet_icon = "seed-carrot"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/carrot)
+ plant_icon = "carrot"
+ chems = list("nutriment" = list(1,20), "imidazoline" = list(3,5))
+
+ lifespan = 25
+ maturation = 10
+ production = 1
+ yield = 5
+ potency = 10
+ growth_stages = 3
+
+/datum/seed/weeds
+ name = "weeds"
+ seed_name = "weed"
+ display_name = "weeds"
+ packet_icon = "seed-ambrosiavulgaris"
+ plant_icon = "weeds"
+
+ lifespan = 100
+ maturation = 5
+ production = 1
+ yield = -1
+ potency = -1
+ growth_stages = 4
+ immutable = -1
+
+/datum/seed/whitebeets
+ name = "whitebeet"
+ seed_name = "white-beet"
+ display_name = "white-beets"
+ packet_icon = "seed-whitebeet"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/whitebeet)
+ plant_icon = "whitebeet"
+ chems = list("nutriment" = list(0,20), "sugar" = list(1,5))
+
+ lifespan = 60
+ maturation = 6
+ production = 6
+ yield = 6
+ potency = 10
+
+/datum/seed/sugarcane
+ name = "sugarcane"
+ seed_name = "sugarcane"
+ display_name = "sugarcanes"
+ packet_icon = "seed-sugarcane"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/sugarcane)
+ plant_icon = "sugarcane"
+ harvest_repeat = 1
+ chems = list("sugar" = list(4,5))
+
+ lifespan = 60
+ maturation = 3
+ production = 6
+ yield = 4
+ potency = 10
+ growth_stages = 3
+
+/datum/seed/watermelon
+ name = "watermelon"
+ seed_name = "watermelon"
+ display_name = "watermelon vine"
+ packet_icon = "seed-watermelon"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/watermelon)
+ plant_icon = "watermelon"
+ harvest_repeat = 1
+ chems = list("nutriment" = list(1,6))
+
+ lifespan = 50
+ maturation = 6
+ production = 6
+ yield = 3
+ potency = 1
+
+/datum/seed/pumpkin
+ name = "pumpkin"
+ seed_name = "pumpkin"
+ display_name = "pumpkin vine"
+ packet_icon = "seed-pumpkin"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/pumpkin)
+ plant_icon = "pumpkin"
+ harvest_repeat = 1
+ chems = list("nutriment" = list(1,6))
+
+ lifespan = 50
+ maturation = 6
+ production = 6
+ yield = 3
+ potency = 10
+ growth_stages = 3
+
+/datum/seed/lime
+ name = "lime"
+ seed_name = "lime"
+ display_name = "lime trees"
+ packet_icon = "seed-lime"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/lime)
+ plant_icon = "lime"
+ harvest_repeat = 1
+ chems = list("nutriment" = list(1,20))
+
+ lifespan = 55
+ maturation = 6
+ production = 6
+ yield = 4
+ potency = 15
+
+/datum/seed/lemon
+ name = "lemon"
+ seed_name = "lemon"
+ display_name = "lemon trees"
+ packet_icon = "seed-lemon"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/lemon)
+ plant_icon = "lemon"
+ harvest_repeat = 1
+ chems = list("nutriment" = list(1,20))
+
+ lifespan = 55
+ maturation = 6
+ production = 6
+ yield = 4
+ potency = 10
+
+/datum/seed/orange
+ name = "orange"
+ seed_name = "orange"
+ display_name = "orange trees"
+ packet_icon = "seed-orange"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/orange)
+ plant_icon = "orange"
+ harvest_repeat = 1
+ chems = list("nutriment" = list(1,20))
+
+ lifespan = 60
+ maturation = 6
+ production = 6
+ yield = 5
+ potency = 1
+
+/datum/seed/grass
+ name = "grass"
+ seed_name = "grass"
+ display_name = "grass"
+ packet_icon = "seed-grass"
+ products = list(/obj/item/stack/tile/grass)
+ plant_icon = "grass"
+ harvest_repeat = 1
+
+ lifespan = 60
+ maturation = 2
+ production = 5
+ yield = 5
+ growth_stages = 2
+
+/datum/seed/cocoa
+ name = "cocoa"
+ seed_name = "cacao"
+ display_name = "cacao tree"
+ packet_icon = "seed-cocoapod"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/cocoapod)
+ plant_icon = "cocoapod"
+ harvest_repeat = 1
+ chems = list("nutriment" = list(1,10), "coco" = list(4,5))
+
+ lifespan = 20
+ maturation = 5
+ production = 5
+ yield = 2
+ potency = 10
+ growth_stages = 5
+
+/datum/seed/cherries
+ name = "cherry"
+ seed_name = "cherry"
+ seed_noun = "pits"
+ display_name = "cherry tree"
+ packet_icon = "seed-cherry"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/cherries)
+ plant_icon = "cherry"
+ harvest_repeat = 1
+ chems = list("nutriment" = list(1,15), "sugar" = list(1,15))
+
+ lifespan = 35
+ maturation = 5
+ production = 5
+ yield = 3
+ potency = 10
+ growth_stages = 5
+
+/datum/seed/kudzu
+ name = "kudzu"
+ seed_name = "kudzu"
+ display_name = "kudzu vines"
+ packet_icon = "seed-kudzu"
+ products = list(/obj/item/weapon/reagent_containers/food/snacks/grown/kudzupod)
+ plant_icon = "kudzu"
+ product_colour = "#96D278"
+ chems = list("nutriment" = list(1,50), "anti_toxin" = list(1,25))
+
+ lifespan = 20
+ maturation = 6
+ production = 6
+ yield = 4
+ potency = 10
+ growth_stages = 4
+ spread = 2
+
+/datum/seed/diona
+ name = "diona"
+ seed_name = "diona"
+ seed_noun = "nodes"
+ display_name = "replicant pods"
+ packet_icon = "seed-replicapod"
+ products = list(/mob/living/carbon/monkey/diona)
+ plant_icon = "replicapod"
+ product_requires_player = 1
+ immutable = 1
+
+ lifespan = 50
+ endurance = 8
+ maturation = 5
+ production = 10
+ yield = 1
+ potency = 30
+
+/datum/seed/clown
+ name = "clown"
+ seed_name = "clown"
+ seed_noun = "pods"
+ display_name = "laughing clowns"
+ packet_icon = "seed-replicapod"
+ products = list(/mob/living/simple_animal/hostile/retaliate/clown)
+ plant_icon = "replicapod"
+ product_requires_player = 1
+
+ lifespan = 100
+ endurance = 8
+ maturation = 1
+ production = 1
+ yield = 10
+ potency = 30
+
+/datum/seed/test
+ name = "test"
+ seed_name = "testing"
+ seed_noun = "data"
+ display_name = "runtimes"
+ packet_icon = "seed-replicapod"
+ products = list(/mob/living/simple_animal/cat/Runtime)
+ plant_icon = "replicapod"
+
+ requires_nutrients = 0
+ nutrient_consumption = 0
+ requires_water = 0
+ water_consumption = 0
+ pest_tolerance = 11
+ weed_tolerance = 11
+ lifespan = 1000
+ endurance = 100
+ maturation = 1
+ production = 1
+ yield = 1
+ potency = 1
\ No newline at end of file
diff --git a/code/modules/hydroponics/seed_machines.dm b/code/modules/hydroponics/seed_machines.dm
new file mode 100644
index 00000000000..837abe7211a
--- /dev/null
+++ b/code/modules/hydroponics/seed_machines.dm
@@ -0,0 +1,338 @@
+/obj/item/weapon/disk/botany
+ name = "flora data disk"
+ desc = "A small disk used for carrying data on plant genetics."
+ icon = 'icons/obj/hydroponics.dmi'
+ icon_state = "disk"
+ w_class = 1.0
+
+ var/list/genes = list()
+ var/genesource = "unknown"
+
+/obj/item/weapon/disk/botany/New()
+ ..()
+ pixel_x = rand(-5,5)
+ pixel_y = rand(-5,5)
+
+/obj/item/weapon/disk/botany/attack_self(var/mob/user as mob)
+ if(genes.len)
+ var/choice = alert(user, "Are you sure you want to wipe the disk?", "Xenobotany Data", "No", "Yes")
+ if(src && user && genes && choice == "Yes")
+ user << "You wipe the disk data."
+ name = initial(name)
+ desc = initial(name)
+ genes = list()
+ genesource = "unknown"
+
+/obj/item/weapon/storage/box/botanydisk
+ name = "flora disk box"
+ desc = "A box of flora data disks, apparently."
+
+/obj/item/weapon/storage/box/botanydisk/New()
+ ..()
+ for(var/i = 0;i<7;i++)
+ new /obj/item/weapon/disk/botany(src)
+
+/obj/machinery/botany
+ icon = 'icons/obj/hydroponics.dmi'
+ icon_state = "hydrotray3"
+ density = 1
+ anchored = 1
+ use_power = 1
+
+ var/obj/item/seeds/seed // Currently loaded seed packet.
+ var/obj/item/weapon/disk/botany/loaded_disk //Currently loaded data disk.
+
+ var/open = 0
+ var/active = 0
+ var/action_time = 100
+ var/last_action = 0
+ var/eject_disk = 0
+ var/failed_task = 0
+ var/disk_needs_genes = 0
+
+/obj/machinery/botany/process()
+
+ ..()
+ if(!active) return
+
+ if(world.time > last_action + action_time)
+ finished_task()
+
+/obj/machinery/botany/attack_paw(mob/user as mob)
+ return attack_hand(user)
+
+/obj/machinery/botany/attack_ai(mob/user as mob)
+ return attack_hand(user)
+
+/obj/machinery/botany/attack_hand(mob/user as mob)
+ ui_interact(user)
+
+/obj/machinery/botany/proc/finished_task()
+ active = 0
+ if(failed_task)
+ failed_task = 0
+ visible_message("\icon[src] [src] pings unhappily, flashing a red warning light.")
+ else
+ visible_message("\icon[src] [src] pings happily.")
+
+ if(eject_disk)
+ eject_disk = 0
+ if(loaded_disk)
+ loaded_disk.loc = get_turf(src)
+ visible_message("\icon[src] [src] beeps and spits out [loaded_disk].")
+ loaded_disk = null
+
+/obj/machinery/botany/attackby(obj/item/weapon/W as obj, mob/user as mob)
+ if(istype(W,/obj/item/seeds))
+ if(seed)
+ user << "There is already a seed loaded."
+
+ var/obj/item/seeds/S =W
+ if(S.seed && S.seed.immutable > 0)
+ user << "That seed is not compatible with our genetics technology."
+ else
+ user.drop_item(W)
+ W.loc = src
+ seed = W
+ user << "You load [W] into [src]."
+ return
+
+ if(istype(W,/obj/item/weapon/screwdriver))
+ open = !open
+ user << "\blue You [open ? "open" : "close"] the maintenance panel."
+ return
+
+ if(open)
+ if(istype(W, /obj/item/weapon/crowbar))
+ dismantle()
+ return
+
+ if(istype(W,/obj/item/weapon/disk/botany))
+ if(loaded_disk)
+ user << "There is already a data disk loaded."
+ return
+ else
+ var/obj/item/weapon/disk/botany/B = W
+
+ if(B.genes && B.genes.len)
+ if(!disk_needs_genes)
+ user << "That disk already has gene data loaded."
+ return
+ else
+ if(disk_needs_genes)
+ user << "That disk does not have any gene data loaded."
+ return
+
+ user.drop_item(W)
+ W.loc = src
+ loaded_disk = W
+ user << "You load [W] into [src]."
+
+ return
+ ..()
+
+// Allows for a trait to be extracted from a seed packet, destroying that seed.
+/obj/machinery/botany/extractor
+ name = "lysis-isolation centrifuge"
+ icon_state = "traitcopier"
+
+ var/datum/seed/genetics // Currently scanned seed genetic structure.
+ var/degradation = 0 // Increments with each scan, stops allowing gene mods after a certain point.
+
+/obj/machinery/botany/extractor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
+
+ if(!user)
+ return
+
+ var/list/data = list()
+
+ var/list/geneMasks[0]
+ for(var/gene_tag in gene_tag_masks)
+ geneMasks.Add(list(list("tag" = gene_tag, "mask" = gene_tag_masks[gene_tag])))
+ data["geneMasks"] = geneMasks
+
+ data["activity"] = active
+ data["degradation"] = degradation
+
+ if(loaded_disk)
+ data["disk"] = 1
+ else
+ data["disk"] = 0
+
+ if(seed)
+ data["loaded"] = "[seed.name]"
+ else
+ data["loaded"] = 0
+
+ if(genetics)
+ data["hasGenetics"] = 1
+ data["sourceName"] = genetics.display_name
+ if(!genetics.roundstart)
+ data["sourceName"] += " (variety #[genetics.uid])"
+ else
+ data["hasGenetics"] = 0
+ data["sourceName"] = 0
+
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
+ if (!ui)
+ ui = new(user, src, ui_key, "botany_isolator.tmpl", "Lysis-isolation Centrifuge UI", 470, 450)
+ ui.set_initial_data(data)
+ ui.open()
+ ui.set_auto_update(1)
+
+/obj/machinery/botany/Topic(href, href_list)
+
+ if(..())
+ return 1
+
+ if(href_list["eject_packet"])
+ if(!seed) return
+ seed.loc = get_turf(src)
+
+ if(seed.seed.name == "new line" || isnull(seed_types[seed.seed.name]))
+ seed.seed.uid = seed_types.len + 1
+ seed.seed.name = "[seed.seed.uid]"
+ seed_types[seed.seed.name] = seed.seed
+
+ seed.update_seed()
+ visible_message("\icon[src] [src] beeps and spits out [seed].")
+
+ seed = null
+
+ if(href_list["eject_disk"])
+ if(!loaded_disk) return
+ loaded_disk.loc = get_turf(src)
+ visible_message("\icon[src] [src] beeps and spits out [loaded_disk].")
+ loaded_disk = null
+
+ usr.set_machine(src)
+ src.add_fingerprint(usr)
+
+/obj/machinery/botany/extractor/Topic(href, href_list)
+
+ if(..())
+ return 1
+
+ usr.set_machine(src)
+ src.add_fingerprint(usr)
+
+ if(href_list["scan_genome"])
+
+ if(!seed) return
+
+ last_action = world.time
+ active = 1
+
+ if(seed && seed.seed)
+ genetics = seed.seed
+ degradation = 0
+
+ del(seed)
+ seed = null
+
+ if(href_list["get_gene"])
+
+ if(!genetics || !loaded_disk) return
+
+ last_action = world.time
+ active = 1
+
+ var/datum/plantgene/P = genetics.get_gene(href_list["get_gene"])
+ if(!P) return
+ loaded_disk.genes += P
+
+ loaded_disk.genesource = "[genetics.display_name]"
+ if(!genetics.roundstart)
+ loaded_disk.genesource += " (variety #[genetics.uid])"
+
+ loaded_disk.name += " ([gene_tag_masks[href_list["get_gene"]]], #[genetics.uid])"
+ loaded_disk.desc += " The label reads \'gene [gene_tag_masks[href_list["get_gene"]]], sampled from [genetics.display_name]\'."
+ eject_disk = 1
+
+ degradation += rand(20,60)
+ if(degradation >= 100)
+ failed_task = 1
+ genetics = null
+ degradation = 0
+
+ if(href_list["clear_buffer"])
+ if(!genetics) return
+ genetics = null
+ degradation = 0
+
+ src.updateUsrDialog()
+ return
+
+// Fires an extracted trait into another packet of seeds with a chance
+// of destroying it based on the size/complexity of the plasmid.
+/obj/machinery/botany/editor
+ name = "bioballistic delivery system"
+ icon_state = "traitgun"
+ disk_needs_genes = 1
+
+/obj/machinery/botany/editor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
+
+ if(!user)
+ return
+
+ var/list/data = list()
+
+ data["activity"] = active
+
+ if(seed)
+ data["degradation"] = seed.modified
+ else
+ data["degradation"] = 0
+
+ if(loaded_disk && loaded_disk.genes.len)
+ data["disk"] = 1
+ data["sourceName"] = loaded_disk.genesource
+ data["locus"] = ""
+
+ for(var/datum/plantgene/P in loaded_disk.genes)
+ if(data["locus"] != "") data["locus"] += ", "
+ data["locus"] += "[gene_tag_masks[P.genetype]]"
+
+ else
+ data["disk"] = 0
+ data["sourceName"] = 0
+ data["locus"] = 0
+
+ if(seed)
+ data["loaded"] = "[seed.name]"
+ else
+ data["loaded"] = 0
+
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
+ if (!ui)
+ ui = new(user, src, ui_key, "botany_editor.tmpl", "Bioballistic Delivery UI", 470, 450)
+ ui.set_initial_data(data)
+ ui.open()
+ ui.set_auto_update(1)
+
+/obj/machinery/botany/editor/Topic(href, href_list)
+
+ if(..())
+ return 1
+
+ if(href_list["apply_gene"])
+ if(!loaded_disk || !seed) return
+
+ last_action = world.time
+ active = 1
+
+ if(!isnull(seed_types[seed.seed.name]))
+ seed.seed = seed.seed.diverge(1)
+ seed.seed_type = seed.seed.name
+ seed.update_seed()
+
+ if(prob(seed.modified))
+ failed_task = 1
+ seed.modified = 101
+
+ for(var/datum/plantgene/gene in loaded_disk.genes)
+ seed.seed.apply_gene(gene)
+ seed.modified += rand(5,10)
+
+ usr.set_machine(src)
+ src.add_fingerprint(usr)
\ No newline at end of file
diff --git a/code/modules/hydroponics/seed_mobs.dm b/code/modules/hydroponics/seed_mobs.dm
new file mode 100644
index 00000000000..0bb301298a5
--- /dev/null
+++ b/code/modules/hydroponics/seed_mobs.dm
@@ -0,0 +1,80 @@
+/datum/seed
+ var/product_requires_player // If yes, product will ask for a player among the ghosts.
+ var/list/currently_querying // Used to avoid asking the same ghost repeatedly.
+
+// The following procs are used to grab players for mobs produced by a seed (mostly for dionaea).
+/datum/seed/proc/handle_living_product(var/mob/living/host)
+
+ if(!host || !istype(host)) return
+
+ if(product_requires_player)
+ spawn(0)
+ request_player(host)
+ spawn(75)
+ if(!host.ckey && !host.client)
+ host.death() // This seems redundant, but a lot of mobs don't
+ host.stat = 2 // handle death() properly. Better safe than etc.
+ host.visible_message("\red [host] is malformed and unable to survive. It expires pitifully, leaving behind some seeds.")
+
+ var/total_yield = rand(1,3)
+ for(var/j = 0;j<=total_yield;j++)
+ var/obj/item/seeds/S = new(get_turf(host))
+ S.seed_type = name
+ S.update_seed()
+
+/datum/seed/proc/request_player(var/mob/living/host)
+ if(!host) return
+ for(var/mob/dead/observer/O in player_list)
+ if(jobban_isbanned(O, "Dionaea") || (!is_alien_whitelisted(src, "Diona") && config.usealienwhitelist))
+ continue
+ if(O.client)
+ if(O.client.prefs.be_special & BE_PLANT && !(O.client in currently_querying))
+ currently_querying |= O.client
+ question(O.client,host)
+
+/datum/seed/proc/question(var/client/C,var/mob/living/host)
+ spawn(0)
+
+ if(!C || !host || !(C.mob && istype(C.mob,/mob/dead))) return // We don't want to spam them repeatedly if they're already in a mob.
+
+ var/response = alert(C, "Someone is harvesting [display_name]. Would you like to play as one?", "Sentient plant harvest", "Yes", "No", "Never for this round.")
+
+ if(!C || !host || !(C.mob && istype(C.mob,/mob/dead))) return // ...or accidentally accept an invalid argument for transfer.
+
+ if(response == "Yes")
+ transfer_personality(C,host)
+ else if (response == "Never for this round")
+ C.prefs.be_special ^= BE_PLANT
+
+ currently_querying -= C
+
+/datum/seed/proc/transfer_personality(var/client/player,var/mob/living/host)
+
+ //Something is wrong, abort.
+ if(!player || !host) return
+
+ //Host already has a controller, pike off slowpoke.
+ if(host.client && host.ckey) return
+
+ //Transfer them over.
+ host.ckey = player.ckey
+ if(player.mob && player.mob.mind)
+ player.mob.mind.transfer_to(host)
+
+ if(host.dna) host.dna.real_name = host.real_name
+
+ // Update mode specific HUD icons.
+ callHook("harvest_podman", list(host))
+
+ host << "\green You awaken slowly, stirring into sluggish motion as the air caresses you."
+
+ // This is a hack, replace with some kind of species blurb proc.
+ if(istype(host,/mob/living/carbon/monkey/diona))
+ host << "You are [host], one of a race of drifting interstellar plantlike creatures that sometimes share their seeds with human traders."
+ host << "Too much darkness will send you into shock and starve you, but light will help you heal."
+
+ var/newname = input(host,"Enter a name, or leave blank for the default name.", "Name change","") as text
+ newname = sanitize(newname)
+ if (newname != "")
+ host.real_name = newname
+ host.name = host.real_name
\ No newline at end of file
diff --git a/code/modules/hydroponics/seeds.dm b/code/modules/hydroponics/seeds.dm
index 2fb0acda317..d0afda2e6fb 100644
--- a/code/modules/hydroponics/seeds.dm
+++ b/code/modules/hydroponics/seeds.dm
@@ -1,1128 +1,224 @@
-// ********************************************************
-// Here's all the seeds (plants) that can be used in hydro
-// ********************************************************
-
+//Seed packet object/procs.
/obj/item/seeds
- name = "pack of seeds"
+ name = "packet of seeds"
icon = 'icons/obj/seeds.dmi'
- icon_state = "seed" // unknown plant seed - these shouldn't exist in-game
+ icon_state = "seed"
flags = FPRINT | TABLEPASS
- w_class = 2.0 // Makes them pocketable
- var/mypath = "/obj/item/seeds"
- var/plantname = "Plants"
- var/productname = ""
- var/species = ""
- var/lifespan = 0
- var/endurance = 0
- var/maturation = 0
- var/production = 0
- var/yield = 0 // If is -1, the plant/shroom/weed is never meant to be harvested
- var/oneharvest = 0
- var/potency = -1
- var/growthstages = 0
- var/plant_type = 0 // 0 = 'normal plant'; 1 = weed; 2 = shroom
+ w_class = 2.0
-/obj/item/seeds/attackby(var/obj/item/O as obj, var/mob/user as mob)
- if (istype(O, /obj/item/device/analyzer/plant_analyzer))
- user << "*** [plantname] ***"
- user << "-Plant Endurance: \blue [endurance]"
- user << "-Plant Lifespan: \blue [lifespan]"
- if(yield != -1)
- user << "-Plant Yield: \blue [yield]"
- user << "-Plant Production: \blue [production]"
- if(potency != -1)
- user << "-Plant Potency: \blue [potency]"
- return
- ..() // Fallthrough to item/attackby() so that bags can pick seeds up
+ var/seed_type
+ var/datum/seed/seed
+ var/modified = 0
+/obj/item/seeds/New()
+ update_seed()
+ ..()
-/obj/item/seeds/chiliseed
- name = "pack of chili seeds"
- desc = "These seeds grow into chili plants. HOT! HOT! HOT!"
- icon_state = "seed-chili"
- mypath = "/obj/item/seeds/chiliseed"
- species = "chili"
- plantname = "Chili Plants"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/chili"
- lifespan = 20
- endurance = 15
- maturation = 5
- production = 5
- yield = 4
- potency = 20
- plant_type = 0
- growthstages = 6
+//Grabs the appropriate seed datum from the global list.
+/obj/item/seeds/proc/update_seed()
+ if(!seed && seed_type && !isnull(seed_types) && seed_types[seed_type])
+ seed = seed_types[seed_type]
+ update_appearance()
-/obj/item/seeds/plastiseed
- name = "plastellium mycelium"
- desc = "This mycelium grows into Plastellium"
- icon_state = "mycelium-plast"
- mypath = "/obj/item/seeds/plastiseed"
- species = "plastellium"
- plantname = "Plastellium"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/plastellium"
- lifespan = 15
- endurance = 17
- maturation = 5
- production = 6
- yield = 6
- oneharvest = 1
- potency = 20
- plant_type = 2
- growthstages = 3
+//Updates strings and icon appropriately based on seed datum.
+/obj/item/seeds/proc/update_appearance()
+ if(!seed) return
+ icon_state = seed.packet_icon
+ src.name = "packet of [seed.seed_name] [seed.seed_noun]"
+ src.desc = "It has a picture of [seed.display_name] on the front."
-/obj/item/seeds/grapeseed
- name = "pack of grape seeds"
- desc = "These seeds grow into grape vines."
- icon_state = "seed-grapes"
- mypath = "/obj/item/seeds/grapeseed"
- species = "grape"
- plantname = "Grape Vine"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/grapes"
- lifespan = 50
- endurance = 25
- maturation = 3
- production = 5
- yield = 4
- potency = 10
- plant_type = 0
- growthstages = 2
+/obj/item/seeds/examine()
+ ..()
+ if(seed && !seed.roundstart)
+ usr << "It's tagged as variety #[seed.uid]."
-/obj/item/seeds/greengrapeseed
- name = "pack of green grape seeds"
- desc = "These seeds grow into green-grape vines."
- icon_state = "seed-greengrapes"
- mypath = "/obj/item/seeds/greengrapeseed"
- species = "greengrape"
- plantname = "Green-Grape Vine"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/greengrapes"
- lifespan = 50
- endurance = 25
- maturation = 3
- production = 5
- yield = 4
- potency = 10
- plant_type = 0
- growthstages = 2
+/obj/item/seeds/cutting
+ name = "cuttings"
+ desc = "Some plant cuttings."
-/obj/item/seeds/peanutseed
- name = "pack of peanut seeds"
- desc = "These seeds grow into peanut vines."
- icon_state = "seed-peanut"
- mypath = "/obj/item/seeds/peanutseed"
- species = "peanut"
- plantname = "Peanuts"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/peanut"
- lifespan = 55
- endurance = 50
- maturation = 6
- production = 6
- yield = 6
- potency = 10
- plant_type = 0
- growthstages = 6
+/obj/item/seeds/cutting/update_appearance()
+ ..()
+ src.name = "packet of [seed.seed_name] cuttings"
-
-/obj/item/seeds/cabbageseed
- name = "pack of cabbage seeds"
- desc = "These seeds grow into cabbages."
- icon_state = "seed-cabbage"
- mypath = "/obj/item/seeds/cabbageseed"
- species = "cabbage"
- plantname = "Cabbages"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/cabbage"
- lifespan = 50
- endurance = 25
- maturation = 3
- production = 5
- yield = 4
- potency = 10
- plant_type = 0
- growthstages = 1
-
-/obj/item/seeds/shandseed
- name = "pack of s'rendarr's hand seeds"
- desc = "These seeds grow into a helpful herb called S'Rendarr's Hand, native to Ahdomai."
- icon_state = "seed-shand"
- mypath = "/obj/item/seeds/shandseed"
- species = "shand"
- plantname = "S'Rendarr's Hand"
- productname = "/obj/item/stack/medical/bruise_pack/tajaran"
- lifespan = 50
- endurance = 25
- maturation = 3
- production = 5
- yield = 4
- potency = 10
- plant_type = 0
- growthstages = 3
-
-/obj/item/seeds/mtearseed
- name = "pack of messa's tear seeds"
- desc = "These seeds grow into a helpful herb called Messa's Tear, native to Ahdomai."
- icon_state = "seed-mtear"
- mypath = "/obj/item/seeds/mtearseed"
- species = "mtear"
- plantname = "Messa's Tear"
- productname = "/obj/item/stack/medical/ointment/tajaran"
- lifespan = 50
- endurance = 25
- maturation = 3
- production = 5
- yield = 4
- potency = 10
- plant_type = 0
- growthstages = 3
-
-/obj/item/seeds/berryseed
- name = "pack of berry seeds"
- desc = "These seeds grow into berry bushes."
- icon_state = "seed-berry"
- mypath = "/obj/item/seeds/berryseed"
- species = "berry"
- plantname = "Berry Bush"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/berries"
- lifespan = 20
- endurance = 15
- maturation = 5
- production = 5
- yield = 2
- potency = 10
- plant_type = 0
- growthstages = 6
-
-/obj/item/seeds/glowberryseed
- name = "pack of glow-berry seeds"
- desc = "These seeds grow into glow-berry bushes."
- icon_state = "seed-glowberry"
- mypath = "/obj/item/seeds/glowberryseed"
- species = "glowberry"
- plantname = "Glow-Berry Bush"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/glowberries"
- lifespan = 30
- endurance = 25
- maturation = 5
- production = 5
- yield = 2
- potency = 10
- plant_type = 0
- growthstages = 6
-
-/obj/item/seeds/bananaseed
- name = "pack of banana seeds"
- desc = "They're seeds that grow into banana trees."
- icon_state = "seed-banana"
- mypath = "/obj/item/seeds/bananaseed"
- species = "banana"
- plantname = "Banana Tree"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/banana"
- lifespan = 50
- endurance = 30
- maturation = 6
- production = 6
- yield = 3
- plant_type = 0
- growthstages = 6
-
-/obj/item/seeds/eggplantseed
- name = "pack of eggplant seeds"
- desc = "These seeds grow to produce berries that look nothing like eggs."
- icon_state = "seed-eggplant"
- mypath = "/obj/item/seeds/eggplantseed"
- species = "eggplant"
- plantname = "Eggplants"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/eggplant"
- lifespan = 25
- endurance = 15
- maturation = 6
- production = 6
- yield = 2
- potency = 20
- plant_type = 0
- growthstages = 6
-
-/obj/item/seeds/eggyseed
- name = "pack of eggplant seeds"
- desc = "These seeds grow to produce berries that look a lot like eggs."
- icon_state = "seed-eggy"
- mypath = "/obj/item/seeds/eggy"
- species = "eggy"
- plantname = "Eggplants"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/egg"
- lifespan = 75
- endurance = 15
- maturation = 6
- production = 12
- yield = 2
- plant_type = 0
- growthstages = 6
-
-/obj/item/seeds/bloodtomatoseed
- name = "pack of blood-tomato seeds"
- desc = "These seeds grow into blood-tomato plants."
- icon_state = "seed-bloodtomato"
- mypath = "/obj/item/seeds/bloodtomatoseed"
- species = "bloodtomato"
- plantname = "Blood-Tomato Plants"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/bloodtomato"
- lifespan = 25
- endurance = 20
- maturation = 8
- production = 6
- yield = 3
- potency = 10
- plant_type = 0
- growthstages = 6
-
-/obj/item/seeds/tomatoseed
- name = "pack of tomato seeds"
- desc = "These seeds grow into tomato plants."
- icon_state = "seed-tomato"
- mypath = "/obj/item/seeds/tomatoseed"
- species = "tomato"
- plantname = "Tomato Plants"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/tomato"
- lifespan = 25
- endurance = 15
- maturation = 8
- production = 6
- yield = 2
- potency = 10
- plant_type = 0
- growthstages = 6
-
-/obj/item/seeds/killertomatoseed
- name = "pack of killer-tomato seeds"
- desc = "These seeds grow into killer-tomato plants."
- icon_state = "seed-killertomato"
- mypath = "/obj/item/seeds/killertomatoseed"
- species = "killertomato"
- plantname = "Killer-Tomato Plants"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/killertomato"
- lifespan = 25
- endurance = 15
- maturation = 8
- production = 6
- yield = 2
- potency = 10
- plant_type = 0
- oneharvest = 1
- growthstages = 2
-
-/obj/item/seeds/bluetomatoseed
- name = "pack of blue-tomato seeds"
- desc = "These seeds grow into blue-tomato plants."
- icon_state = "seed-bluetomato"
- mypath = "/obj/item/seeds/bluetomatoseed"
- species = "bluetomato"
- plantname = "Blue-Tomato Plants"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/bluetomato"
- lifespan = 25
- endurance = 15
- maturation = 8
- production = 6
- yield = 2
- potency = 10
- plant_type = 0
- growthstages = 6
-
-/obj/item/seeds/bluespacetomatoseed
- name = "pack of blue-space tomato seeds"
- desc = "These seeds grow into blue-space tomato plants."
- icon_state = "seed-bluespacetomato"
- mypath = "/obj/item/seeds/bluespacetomatoseed"
- species = "bluespacetomato"
- plantname = "Blue-Space Tomato Plants"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/bluespacetomato"
- lifespan = 25
- endurance = 15
- maturation = 8
- production = 6
- yield = 2
- potency = 10
- plant_type = 0
- growthstages = 6
-
-/obj/item/seeds/cornseed
- name = "pack of corn seeds"
- desc = "I don't mean to sound corny..."
- icon_state = "seed-corn"
- mypath = "/obj/item/seeds/cornseed"
- species = "corn"
- plantname = "Corn Stalks"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/corn"
- lifespan = 25
- endurance = 15
- maturation = 8
- production = 6
- yield = 3
- plant_type = 0
- oneharvest = 1
- potency = 20
- growthstages = 3
+/obj/item/seeds/replicapod
+ seed_type = "diona"
/obj/item/seeds/poppyseed
- name = "pack of poppy seeds"
- desc = "These seeds grow into poppies."
- icon_state = "seed-poppy"
- mypath = "/obj/item/seeds/poppyseed"
- species = "poppy"
- plantname = "Poppy Plants"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/poppy"
- lifespan = 25
- endurance = 10
- potency = 20
- maturation = 8
- production = 6
- yield = 6
- plant_type = 0
- oneharvest = 1
- growthstages = 3
+ seed_type = "poppies"
+
+/obj/item/seeds/chiliseed
+ seed_type = "chili"
+
+/obj/item/seeds/plastiseed
+ seed_type = "plastic"
+
+/obj/item/seeds/grapeseed
+ seed_type = "grapes"
+
+/obj/item/seeds/greengrapeseed
+ seed_type = "greengrapes"
+
+/obj/item/seeds/peanutseed
+ seed_type = "peanut"
+
+/obj/item/seeds/cabbageseed
+ seed_type = "cabbage"
+
+/obj/item/seeds/shandseed
+ seed_type = "shand"
+
+/obj/item/seeds/mtearseed
+ seed_type = "mtear"
+
+/obj/item/seeds/berryseed
+ seed_type = "berries"
+
+/obj/item/seeds/glowberryseed
+ seed_type = "glowberries"
+
+/obj/item/seeds/bananaseed
+ seed_type = "banana"
+
+/obj/item/seeds/eggplantseed
+ seed_type = "eggplant"
+
+/obj/item/seeds/eggyseed
+ seed_type = "realeggplant"
+
+/obj/item/seeds/bloodtomatoseed
+ seed_type = "bloodtomato"
+
+/obj/item/seeds/tomatoseed
+ seed_type = "tomato"
+
+/obj/item/seeds/killertomatoseed
+ seed_type = "killertomato"
+
+/obj/item/seeds/bluetomatoseed
+ seed_type = "bluetomato"
+
+/obj/item/seeds/bluespacetomatoseed
+ seed_type = "bluespacetomato"
+
+/obj/item/seeds/cornseed
+ seed_type = "corn"
+
+/obj/item/seeds/poppyseed
+ seed_type = "poppies"
/obj/item/seeds/potatoseed
- name = "pack of potato seeds"
- desc = "Boil 'em! Mash 'em! Stick 'em in a stew!"
- icon_state = "seed-potato"
- mypath = "/obj/item/seeds/potatoseed"
- species = "potato"
- plantname = "Potato-Plants"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/potato"
- lifespan = 30
- endurance = 15
- maturation = 10
- production = 1
- yield = 4
- plant_type = 0
- oneharvest = 1
- potency = 10
- growthstages = 4
+ seed_type = "potato"
/obj/item/seeds/icepepperseed
- name = "pack of ice-pepper seeds"
- desc = "These seeds grow into ice-pepper plants."
- icon_state = "seed-icepepper"
- mypath = "/obj/item/seeds/icepepperseed"
- species = "chiliice"
- plantname = "Ice-Pepper Plants"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/icepepper"
- lifespan = 25
- endurance = 15
- maturation = 4
- production = 4
- yield = 4
- potency = 20
- plant_type = 0
- growthstages = 6
+ seed_type = "icechili"
/obj/item/seeds/soyaseed
- name = "pack of soybean seeds"
- desc = "These seeds grow into soybean plants."
- icon_state = "seed-soybean"
- mypath = "/obj/item/seeds/soyaseed"
- species = "soybean"
- plantname = "Soybean Plants"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/soybeans"
- lifespan = 25
- endurance = 15
- maturation = 4
- production = 4
- yield = 3
- potency = 5
- plant_type = 0
- growthstages = 6
+ seed_type = "soybean"
/obj/item/seeds/wheatseed
- name = "pack of wheat seeds"
- desc = "These may, or may not, grow into weed."
- icon_state = "seed-wheat"
- mypath = "/obj/item/seeds/wheatseed"
- species = "wheat"
- plantname = "Wheat Stalks"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/wheat"
- lifespan = 25
- endurance = 15
- maturation = 6
- production = 1
- yield = 4
- potency = 5
- oneharvest = 1
- plant_type = 0
- growthstages = 6
+ seed_type = "wheat"
/obj/item/seeds/riceseed
- name = "pack of rice seeds"
- desc = "These seeds grow into rice stalks."
- icon_state = "seed-rice"
- mypath = "/obj/item/seeds/riceseed"
- species = "rice"
- plantname = "Rice Stalks"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/ricestalk"
- lifespan = 25
- endurance = 15
- maturation = 6
- production = 1
- yield = 4
- potency = 5
- oneharvest = 1
- plant_type = 0
- growthstages = 4
+ seed_type = "rice"
/obj/item/seeds/carrotseed
- name = "pack of carrot seeds"
- desc = "These seeds grow into carrots."
- icon_state = "seed-carrot"
- mypath = "/obj/item/seeds/carrotseed"
- species = "carrot"
- plantname = "Carrots"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/carrot"
- lifespan = 25
- endurance = 15
- maturation = 10
- production = 1
- yield = 5
- potency = 10
- oneharvest = 1
- plant_type = 0
- growthstages = 3
+ seed_type = "carrot"
/obj/item/seeds/reishimycelium
- name = "pack of reishi mycelium"
- desc = "This mycelium grows into something relaxing."
- icon_state = "mycelium-reishi"
- mypath = "/obj/item/seeds/reishimycelium"
- species = "reishi"
- plantname = "Reishi"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/reishi"
- lifespan = 35
- endurance = 35
- maturation = 10
- production = 5
- yield = 4
- potency = 15 // Sleeping based on potency?
- oneharvest = 1
- growthstages = 4
- plant_type = 2
+ seed_type = "reishi"
/obj/item/seeds/amanitamycelium
- name = "pack of fly amanita mycelium"
- desc = "This mycelium grows into something horrible."
- icon_state = "mycelium-amanita"
- mypath = "/obj/item/seeds/amanitamycelium"
- species = "amanita"
- plantname = "Fly Amanitas"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/amanita"
- lifespan = 50
- endurance = 35
- maturation = 10
- production = 5
- yield = 4
- potency = 10 // Damage based on potency?
- oneharvest = 1
- growthstages = 3
- plant_type = 2
+ seed_type = "amanita"
/obj/item/seeds/angelmycelium
- name = "pack of destroying angel mycelium"
- desc = "This mycelium grows into something devestating."
- icon_state = "mycelium-angel"
- mypath = "/obj/item/seeds/angelmycelium"
- species = "angel"
- plantname = "Destroying Angels"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/angel"
- lifespan = 50
- endurance = 35
- maturation = 12
- production = 5
- yield = 2
- potency = 35
- oneharvest = 1
- growthstages = 3
- plant_type = 2
+ seed_type = "destroyingangel"
/obj/item/seeds/libertymycelium
- name = "pack of liberty-cap mycelium"
- desc = "This mycelium grows into liberty-cap mushrooms."
- icon_state = "mycelium-liberty"
- mypath = "/obj/item/seeds/libertymycelium"
- species = "liberty"
- plantname = "Liberty-Caps"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/libertycap"
- lifespan = 25
- endurance = 15
- maturation = 7
- production = 1
- yield = 5
- potency = 15 // Lowish potency at start
- oneharvest = 1
- growthstages = 3
- plant_type = 2
+ seed_type = "libertycap"
/obj/item/seeds/chantermycelium
- name = "pack of chanterelle mycelium"
- desc = "This mycelium grows into chanterelle mushrooms."
- icon_state = "mycelium-chanter"
- mypath = "/obj/item/seeds/chantermycelium"
- species = "chanter"
- plantname = "Chanterelle Mushrooms"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/chanterelle"
- lifespan = 35
- endurance = 20
- maturation = 7
- production = 1
- yield = 5
- potency = 1
- oneharvest = 1
- growthstages = 3
- plant_type = 2
+ seed_type = "mushrooms"
/obj/item/seeds/towermycelium
- name = "pack of tower-cap mycelium"
- desc = "This mycelium grows into tower-cap mushrooms."
- icon_state = "mycelium-tower"
- mypath = "/obj/item/seeds/towermycelium"
- species = "towercap"
- plantname = "Tower Caps"
- productname = "/obj/item/weapon/grown/log"
- lifespan = 80
- endurance = 50
- maturation = 15
- production = 1
- yield = 5
- potency = 1
- oneharvest = 1
- growthstages = 3
- plant_type = 2
+ seed_type = "towercap"
/obj/item/seeds/glowshroom
- name = "pack of glowshroom mycelium"
- desc = "This mycelium -glows- into mushrooms!"
- icon_state = "mycelium-glowshroom"
- mypath = "/obj/item/seeds/glowshroom"
- species = "glowshroom"
- plantname = "Glowshrooms"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/glowshroom"
- lifespan = 120 //ten times that is the delay
- endurance = 30
- maturation = 15
- production = 1
- yield = 3 //-> spread
- potency = 30 //-> brightness
- oneharvest = 1
- growthstages = 4
- plant_type = 2
+ seed_type = "glowshroom"
/obj/item/seeds/plumpmycelium
- name = "pack of plump-helmet mycelium"
- desc = "This mycelium grows into helmets... maybe."
- icon_state = "mycelium-plump"
- mypath = "/obj/item/seeds/plumpmycelium"
- species = "plump"
- plantname = "Plump-Helmet Mushrooms"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/plumphelmet"
- lifespan = 25
- endurance = 15
- maturation = 8
- production = 1
- yield = 4
- potency = 0
- oneharvest = 1
- growthstages = 3
- plant_type = 2
+ seed_type = "plumphelmet"
/obj/item/seeds/walkingmushroommycelium
- name = "pack of walking mushroom mycelium"
- desc = "This mycelium will grow into huge stuff!"
- icon_state = "mycelium-walkingmushroom"
- mypath = "/obj/item/seeds/walkingmushroommycelium"
- species = "walkingmushroom"
- plantname = "Walking Mushrooms"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/walkingmushroom"
- lifespan = 30
- endurance = 30
- maturation = 5
- production = 1
- yield = 1
- potency = 0
- oneharvest = 1
- growthstages = 3
- plant_type = 2
+ seed_type = "walkingmushroom"
/obj/item/seeds/nettleseed
- name = "pack of nettle seeds"
- desc = "These seeds grow into nettles."
- icon_state = "seed-nettle"
- mypath = "/obj/item/seeds/nettleseed"
- species = "nettle"
- plantname = "Nettles"
- productname = "/obj/item/weapon/grown/nettle"
- lifespan = 30
- endurance = 40 // tuff like a toiger
- maturation = 6
- production = 6
- yield = 4
- potency = 10
- oneharvest = 0
- growthstages = 5
- plant_type = 1
+ seed_type = "nettle"
/obj/item/seeds/deathnettleseed
- name = "pack of death-nettle seeds"
- desc = "These seeds grow into death-nettles."
- icon_state = "seed-deathnettle"
- mypath = "/obj/item/seeds/deathnettleseed"
- species = "deathnettle"
- plantname = "Death Nettles"
- productname = "/obj/item/weapon/grown/deathnettle"
- lifespan = 30
- endurance = 25
- maturation = 8
- production = 6
- yield = 2
- potency = 10
- oneharvest = 0
- growthstages = 5
- plant_type = 1
+ seed_type = "deathnettle"
/obj/item/seeds/weeds
- name = "pack of weed seeds"
- desc = "Yo mang, want some weeds?"
- icon_state = "seed"
- mypath = "/obj/item/seeds/weeds"
- species = "weeds"
- plantname = "Starthistle"
- productname = ""
- lifespan = 100
- endurance = 50 // damm pesky weeds
- maturation = 5
- production = 1
- yield = -1
- potency = -1
- oneharvest = 1
- growthstages = 4
- plant_type = 1
+ seed_type = "weeds"
/obj/item/seeds/harebell
- name = "pack of harebell seeds"
- desc = "These seeds grow into pretty little flowers."
- icon_state = "seed-harebell"
- mypath = "/obj/item/seeds/harebell"
- species = "harebell"
- plantname = "Harebells"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/harebell"
- lifespan = 100
- endurance = 20
- maturation = 7
- production = 1
- yield = 2
- potency = 1
- oneharvest = 1
- growthstages = 4
- plant_type = 1
+ seed_type = "harebells"
/obj/item/seeds/sunflowerseed
- name = "pack of sunflower seeds"
- desc = "These seeds grow into sunflowers."
- icon_state = "seed-sunflower"
- mypath = "/obj/item/seeds/sunflowerseed"
- species = "sunflower"
- plantname = "Sunflowers"
- productname = "/obj/item/weapon/grown/sunflower"
- lifespan = 25
- endurance = 20
- maturation = 6
- production = 1
- yield = 2
- potency = 1
- oneharvest = 1
- growthstages = 3
- plant_type = 1
+ seed_type = "sunflowers"
/obj/item/seeds/brownmold
- name = "pack of brown mold"
- desc = "Eww.. moldy."
- icon_state = "seed"
- mypath = "/obj/item/seeds/brownmold"
- species = "mold"
- plantname = "Brown Mold"
- productname = ""
- lifespan = 50
- endurance = 30
- maturation = 10
- production = 1
- yield = -1
- potency = 1
- oneharvest = 1
- growthstages = 3
- plant_type = 2
+ seed_type = "mold"
/obj/item/seeds/appleseed
- name = "pack of apple seeds"
- desc = "These seeds grow into apple trees."
- icon_state = "seed-apple"
- mypath = "/obj/item/seeds/appleseed"
- species = "apple"
- plantname = "Apple Tree"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/apple"
- lifespan = 55
- endurance = 35
- maturation = 6
- production = 6
- yield = 5
- potency = 10
- plant_type = 0
- growthstages = 6
+ seed_type = "apple"
/obj/item/seeds/poisonedappleseed
- name = "pack of apple seeds"
- desc = "These seeds grow into apple trees."
- icon_state = "seed-apple"
- mypath = "/obj/item/seeds/poisonedappleseed"
- species = "apple"
- plantname = "Apple Tree"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/apple/poisoned"
- lifespan = 55
- endurance = 35
- maturation = 6
- production = 6
- yield = 5
- potency = 10
- plant_type = 0
- growthstages = 6
+ seed_type = "poisonapple"
/obj/item/seeds/goldappleseed
- name = "pack of golden apple seeds"
- desc = "These seeds grow into golden apple trees. Good thing there are no firebirds in space."
- icon_state = "seed-goldapple"
- mypath = "/obj/item/seeds/goldappleseed"
- species = "goldapple"
- plantname = "Golden Apple Tree"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/goldapple"
- lifespan = 55
- endurance = 35
- maturation = 10
- production = 10
- yield = 3
- potency = 10
- plant_type = 0
- growthstages = 6
+ seed_type = "goldapple"
/obj/item/seeds/ambrosiavulgarisseed
- name = "pack of ambrosia vulgaris seeds"
- desc = "These seeds grow into common ambrosia, a plant grown by and from medicine."
- icon_state = "seed-ambrosiavulgaris"
- mypath = "/obj/item/seeds/ambrosiavulgarisseed"
- species = "ambrosiavulgaris"
- plantname = "Ambrosia Vulgaris"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/ambrosiavulgaris"
- lifespan = 60
- endurance = 25
- maturation = 6
- production = 6
- yield = 6
- potency = 5
- plant_type = 0
- growthstages = 6
+ seed_type = "ambrosia"
/obj/item/seeds/ambrosiadeusseed
- name = "pack of ambrosia deus seeds"
- desc = "These seeds grow into ambrosia deus. Could it be the food of the gods..?"
- icon_state = "seed-ambrosiadeus"
- mypath = "/obj/item/seeds/ambrosiadeusseed"
- species = "ambrosiadeus"
- plantname = "Ambrosia Deus"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/ambrosiadeus"
- lifespan = 60
- endurance = 25
- maturation = 6
- production = 6
- yield = 6
- potency = 5
- plant_type = 0
- growthstages = 6
+ seed_type = "ambrosiadeus"
/obj/item/seeds/whitebeetseed
- name = "pack of white-beet seeds"
- desc = "These seeds grow into sugary beet producing plants."
- icon_state = "seed-whitebeet"
- mypath = "/obj/item/seeds/whitebeetseed"
- species = "whitebeet"
- plantname = "White-Beet Plants"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/whitebeet"
- lifespan = 60
- endurance = 50
- maturation = 6
- production = 6
- yield = 6
- oneharvest = 1
- potency = 10
- plant_type = 0
- growthstages = 6
+ seed_type = "whitebeet"
/obj/item/seeds/sugarcaneseed
- name = "pack of sugarcane seeds"
- desc = "These seeds grow into sugarcane."
- icon_state = "seed-sugarcane"
- mypath = "/obj/item/seeds/sugarcaneseed"
- species = "sugarcane"
- plantname = "Sugarcane"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/sugarcane"
- lifespan = 60
- endurance = 50
- maturation = 3
- production = 6
- yield = 4
- potency = 10
- plant_type = 0
- growthstages = 3
+ seed_type = "sugarcane"
/obj/item/seeds/watermelonseed
- name = "pack of watermelon seeds"
- desc = "These seeds grow into watermelon plants."
- icon_state = "seed-watermelon"
- mypath = "/obj/item/seeds/watermelonseed"
- species = "watermelon"
- plantname = "Watermelon Vines"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/watermelon"
- lifespan = 50
- endurance = 40
- maturation = 6
- production = 6
- yield = 3
- potency = 1
- plant_type = 0
- growthstages = 6
+ seed_type = "watermelon"
/obj/item/seeds/pumpkinseed
- name = "pack of pumpkin seeds"
- desc = "These seeds grow into pumpkin vines."
- icon_state = "seed-pumpkin"
- mypath = "/obj/item/seeds/pumpkinseed"
- species = "pumpkin"
- plantname = "Pumpkin Vines"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/pumpkin"
- lifespan = 50
- endurance = 40
- maturation = 6
- production = 6
- yield = 3
- potency = 10
- plant_type = 0
- growthstages = 3
-
+ seed_type = "pumpkin"
/obj/item/seeds/limeseed
- name = "pack of lime seeds"
- desc = "These are very sour seeds."
- icon_state = "seed-lime"
- mypath = "/obj/item/seeds/limeseed"
- species = "lime"
- plantname = "Lime Tree"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/lime"
- lifespan = 55
- endurance = 50
- maturation = 6
- production = 6
- yield = 4
- potency = 15
- plant_type = 0
- growthstages = 6
+ seed_type = "lime"
/obj/item/seeds/lemonseed
- name = "pack of lemon seeds"
- desc = "These are sour seeds."
- icon_state = "seed-lemon"
- mypath = "/obj/item/seeds/lemonseed"
- species = "lemon"
- plantname = "Lemon Tree"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/lemon"
- lifespan = 55
- endurance = 45
- maturation = 6
- production = 6
- yield = 4
- potency = 10
- plant_type = 0
- growthstages = 6
-
-/obj/item/seeds/cashseed
- name = "pack of money seeds"
- desc = "When life gives you lemons, mutate them into cash."
- icon_state = "seed-cash"
- mypath = "/obj/item/seeds/cashseed"
- species = "cashtree"
- plantname = "Money Tree"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/money"
- lifespan = 55
- endurance = 45
- maturation = 6
- production = 6
- yield = 4
- potency = 10
- plant_type = 0
- growthstages = 6
+ seed_type = "lemon"
/obj/item/seeds/orangeseed
- name = "pack of orange seed"
- desc = "Sour seeds."
- icon_state = "seed-orange"
- mypath = "/obj/item/seeds/orangeseed"
- species = "orange"
- plantname = "Orange Tree"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/orange"
- lifespan = 60
- endurance = 50
- maturation = 6
- production = 6
- yield = 5
- potency = 1
- plant_type = 0
- growthstages = 6
+ seed_type = "orange"
/obj/item/seeds/poisonberryseed
- name = "pack of poison-berry seeds"
- desc = "These seeds grow into poison-berry bushes."
- icon_state = "seed-poisonberry"
- mypath = "/obj/item/seeds/poisonberryseed"
- species = "poisonberry"
- plantname = "Poison-Berry Bush"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/poisonberries"
- lifespan = 20
- endurance = 15
- maturation = 5
- production = 5
- yield = 2
- potency = 10
- plant_type = 0
- growthstages = 6
+ seed_type = "poisonberries"
/obj/item/seeds/deathberryseed
- name = "pack of death-berry seeds"
- desc = "These seeds grow into death berries."
- icon_state = "seed-deathberry"
- mypath = "/obj/item/seeds/deathberryseed"
- species = "deathberry"
- plantname = "Death Berry Bush"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/deathberries"
- lifespan = 30
- endurance = 20
- maturation = 5
- production = 5
- yield = 3
- potency = 50
- plant_type = 0
- growthstages = 6
+ seed_type = "deathberries"
/obj/item/seeds/grassseed
- name = "pack of grass seeds"
- desc = "These seeds grow into grass. Yummy!"
- icon_state = "seed-grass"
- mypath = "/obj/item/seeds/grassseed"
- species = "grass"
- plantname = "Grass"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/grass"
- lifespan = 60
- endurance = 50
- maturation = 2
- production = 5
- yield = 5
- plant_type = 0
- growthstages = 2
+ seed_type = "grass"
/obj/item/seeds/cocoapodseed
- name = "pack of cocoa pod seeds"
- desc = "These seeds grow into cacao trees. They look fattening." //SIC: cocoa is the seeds. The tress ARE spelled cacao.
- icon_state = "seed-cocoapod"
- mypath = "/obj/item/seeds/cocoapodseed"
- species = "cocoapod"
- plantname = "Cocao Tree" //SIC: see above
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/cocoapod"
- lifespan = 20
- endurance = 15
- maturation = 5
- production = 5
- yield = 2
- potency = 10
- plant_type = 0
- growthstages = 5
+ seed_type = "cocoa"
/obj/item/seeds/cherryseed
- name = "pack of cherry pits"
- desc = "Careful not to crack a tooth on one... That'd be the pits."
- icon_state = "seed-cherry"
- mypath = "/obj/item/seeds/cherryseed"
- species = "cherry"
- plantname = "Cherry Tree"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/cherries"
- lifespan = 35
- endurance = 35
- maturation = 5
- production = 5
- yield = 3
- potency = 10
- plant_type = 0
- growthstages = 5
+ seed_type = "cherry"
/obj/item/seeds/kudzuseed
- name = "pack of kudzu seeds"
- desc = "These seeds grow into a weed that grows incredibly fast."
- icon_state = "seed-kudzu"
- mypath = "/obj/item/seeds/kudzuseed"
- species = "kudzu"
- plantname = "Kudzu"
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/kudzupod"
- lifespan = 20
- endurance = 10
- maturation = 6
- production = 6
- yield = 4
- potency = 10
- growthstages = 4
- plant_type = 1
-
-/obj/item/seeds/kudzuseed/attack_self(mob/user as mob)
- if(istype(user.loc,/turf/space))
- return
- user << "You plant the kudzu. You monster."
- new /obj/effect/spacevine_controller(user.loc)
- del(src)
-
-
-
-/* // Maybe one day when I get it to work like a grenade which exlodes gibs.
-/obj/item/seeds/gibtomatoseed
- name = "Gib Tomato seeds"
- desc = "Used to grow gib tomotoes."
- icon_state = "seed-gibtomato"
- mypath = "/obj/item/seeds/gibtomatoseed"
- species = "gibtomato"
- plantname = "Gib Tomato plant"
- productname = "/obj/item/weapon/grown/gibtomato"
- lifespan = 35
- endurance = 25
- maturation = 6
- production = 6
- yield = 3
- potency = 10
- plant_type = 0
- growthstages = 6
-*/
-
-/*
-/obj/item/seeds/
- name = ""
- icon_state = "seed"
- mypath = "/obj/item/seeds/"
- species = ""
- plantname = ""
- productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/"
- lifespan = 25
- endurance = 15
- maturation = 10
- production = 1
- yield = -1
- potency = 0
- oneharvest = 1
- growthstages = 3
- plant_type = 0
-
-*/
\ No newline at end of file
+ seed_type = "kudzu"
\ No newline at end of file
diff --git a/code/modules/hydroponics/vines.dm b/code/modules/hydroponics/vines.dm
new file mode 100644
index 00000000000..70265984f30
--- /dev/null
+++ b/code/modules/hydroponics/vines.dm
@@ -0,0 +1,386 @@
+// SPACE VINES (Note that this code is very similar to Biomass code)
+/obj/effect/plantsegment
+ name = "space vines"
+ desc = "An extremely expansionistic species of vine."
+ icon = 'icons/effects/spacevines.dmi'
+ icon_state = "Light1"
+ anchored = 1
+ density = 0
+ layer = 5
+ pass_flags = PASSTABLE | PASSGRILLE
+
+ // Vars used by vines with seed data.
+ var/age = 0
+ var/lastproduce = 0
+ var/harvest = 0
+ var/list/chems
+ var/plant_damage_noun = "Thorns"
+ var/limited_growth = 0
+
+ // Life vars/
+ var/energy = 0
+ var/obj/effect/plant_controller/master = null
+ var/mob/living/buckled_mob
+ var/datum/seed/seed
+
+/obj/effect/plantsegment/New()
+ return
+
+/obj/effect/plantsegment/Del()
+ if(master)
+ master.vines -= src
+ master.growth_queue -= src
+ ..()
+
+/obj/effect/plantsegment/attackby(obj/item/weapon/W as obj, mob/user as mob)
+ if (!W || !user || !W.type) return
+ switch(W.type)
+ if(/obj/item/weapon/circular_saw) del src
+ if(/obj/item/weapon/kitchen/utensil/knife) del src
+ if(/obj/item/weapon/scalpel) del src
+ if(/obj/item/weapon/twohanded/fireaxe) del src
+ if(/obj/item/weapon/hatchet) del src
+ if(/obj/item/weapon/melee/energy) del src
+
+ // Less effective weapons
+ if(/obj/item/weapon/wirecutters)
+ if(prob(25)) del src
+ if(/obj/item/weapon/shard)
+ if(prob(25)) del src
+
+ // Weapons with subtypes
+ else
+ if(istype(W, /obj/item/weapon/melee/energy/sword)) del src
+ else if(istype(W, /obj/item/weapon/weldingtool))
+ var/obj/item/weapon/weldingtool/WT = W
+ if(WT.remove_fuel(0, user)) del src
+ else
+ manual_unbuckle(user)
+ return
+ // Plant-b-gone damage is handled in its entry in chemistry-reagents.dm
+ ..()
+
+
+/obj/effect/plantsegment/attack_hand(mob/user as mob)
+
+ if(user.a_intent == "help" && seed && harvest)
+ seed.harvest(user,1)
+ harvest = 0
+ lastproduce = age
+ update()
+ return
+
+ manual_unbuckle(user)
+
+
+/obj/effect/plantsegment/attack_paw(mob/user as mob)
+ manual_unbuckle(user)
+
+/obj/effect/plantsegment/proc/unbuckle()
+ if(buckled_mob)
+ if(buckled_mob.buckled == src) //this is probably unneccesary, but it doesn't hurt
+ buckled_mob.buckled = null
+ buckled_mob.anchored = initial(buckled_mob.anchored)
+ buckled_mob.update_canmove()
+ buckled_mob = null
+ return
+
+/obj/effect/plantsegment/proc/manual_unbuckle(mob/user as mob)
+ if(buckled_mob)
+ if(prob(seed ? min(max(0,100 - seed.potency),100) : 50))
+ if(buckled_mob.buckled == src)
+ if(buckled_mob != user)
+ buckled_mob.visible_message(\
+ "[user.name] frees [buckled_mob.name] from [src].",\
+ "[user.name] frees you from [src].",\
+ "You hear shredding and ripping.")
+ else
+ buckled_mob.visible_message(\
+ "[buckled_mob.name] struggles free of [src].",\
+ "You untangle [src] from around yourself.",\
+ "You hear shredding and ripping.")
+ unbuckle()
+ else
+ var/text = pick("rips","tears","pulls")
+ user.visible_message(\
+ "[user.name] [text] at [src].",\
+ "You [text] at [src].",\
+ "You hear shredding and ripping.")
+ return
+
+/obj/effect/plantsegment/proc/grow()
+
+ if(!energy)
+ src.icon_state = pick("Med1", "Med2", "Med3")
+ energy = 1
+
+ //Low-lying creepers do not block vision or grow thickly.
+ if(limited_growth)
+ energy = 2
+ return
+
+ src.opacity = 1
+ layer = 5
+ else if(!limited_growth)
+ src.icon_state = pick("Hvy1", "Hvy2", "Hvy3")
+ energy = 2
+
+/obj/effect/plantsegment/proc/entangle_mob()
+
+ if(limited_growth)
+ return
+
+ if(prob(seed ? seed.potency : 25))
+
+ if(!buckled_mob)
+ var/mob/living/carbon/V = locate() in src.loc
+ if(V && (V.stat != DEAD) && (V.buckled != src)) // If mob exists and is not dead or captured.
+ V.buckled = src
+ V.loc = src.loc
+ V.update_canmove()
+ src.buckled_mob = V
+ V << "The vines [pick("wind", "tangle", "tighten")] around you!"
+
+ // FEED ME, SEYMOUR.
+ if(buckled_mob && seed && (buckled_mob.stat != DEAD)) //Don't bother with a dead mob.
+
+ var/mob/living/M = buckled_mob
+ if(!istype(M)) return
+ var/mob/living/carbon/human/H = buckled_mob
+
+ // Drink some blood/cause some brute.
+ if(seed.carnivorous == 2)
+ buckled_mob << "\The [src] pierces your flesh greedily!"
+
+ var/damage = rand(round(seed.potency/2),seed.potency)
+ if(!istype(H))
+ H.adjustBruteLoss(damage)
+ return
+
+ var/datum/organ/external/affecting = H.get_organ(pick("l_foot","r_foot","l_leg","r_leg","l_hand","r_hand","l_arm", "r_arm","head","chest","groin"))
+
+ if(affecting)
+ affecting.take_damage(damage, 0)
+ if(affecting.parent)
+ affecting.parent.add_autopsy_data("[plant_damage_noun]", damage)
+ else
+ H.adjustBruteLoss(damage)
+
+ H.UpdateDamageIcon()
+ H.updatehealth()
+
+ // Inject some chems.
+ if(seed.chems && seed.chems.len && istype(H))
+ H << "You feel something seeping into your skin!"
+ for(var/rid in seed.chems)
+ var/injecting = min(5,max(1,seed.potency/5))
+ H.reagents.add_reagent(rid,injecting)
+
+/obj/effect/plantsegment/proc/update()
+ if(!seed) return
+
+ // Update bioluminescence.
+ if(seed.biolum)
+ SetLuminosity(1+round(seed.potency/10))
+ if(seed.biolum_colour)
+ l_color = seed.biolum_colour
+ else
+ l_color = null
+ return
+ else
+ SetLuminosity(0)
+
+ // Update flower/product overlay.
+ overlays.Cut()
+ if(age >= seed.maturation)
+ if(prob(20) && seed.products && seed.products.len && !harvest && ((age-lastproduce) > seed.production))
+ harvest = 1
+ lastproduce = age
+
+ if(harvest)
+ var/image/fruit_overlay = image('icons/obj/hydroponics.dmi',"")
+ if(seed.product_colour)
+ fruit_overlay.color = seed.product_colour
+ overlays += fruit_overlay
+
+ if(seed.flowers)
+ var/image/flower_overlay = image('icons/obj/hydroponics.dmi',"[seed.flower_icon]")
+ if(seed.flower_colour)
+ flower_overlay.color = seed.flower_colour
+ overlays += flower_overlay
+
+/obj/effect/plantsegment/proc/spread()
+ var/direction = pick(cardinal)
+ var/step = get_step(src,direction)
+ if(istype(step,/turf/simulated/floor))
+ var/turf/simulated/floor/F = step
+ if(!locate(/obj/effect/plantsegment,F))
+ if(F.Enter(src))
+ if(master)
+ master.spawn_piece( F )
+
+// Explosion damage.
+/obj/effect/plantsegment/ex_act(severity)
+ switch(severity)
+ if(1.0)
+ die()
+ return
+ if(2.0)
+ if (prob(90))
+ die()
+ return
+ if(3.0)
+ if (prob(50))
+ die()
+ return
+ return
+
+// Hotspots kill vines.
+/obj/effect/plantsegment/fire_act(null, temp, volume)
+ del src
+
+/obj/effect/plantsegment/proc/die()
+ if(seed && harvest && rand(5))
+ seed.harvest(src,1)
+ del(src)
+
+/obj/effect/plantsegment/proc/life()
+
+ if(!seed)
+ return
+
+ if(prob(30))
+ age++
+
+ var/turf/T = loc
+ var/datum/gas_mixture/environment
+ if(T) environment = T.return_air()
+
+ if(!environment)
+ return
+
+ var/pressure = environment.return_pressure()
+ if(pressure < seed.lowkpa_tolerance || pressure > seed.highkpa_tolerance)
+ die()
+ return
+
+ if(abs(environment.temperature - seed.ideal_heat) > seed.heat_tolerance)
+ die()
+ return
+
+ var/area/A = T.loc
+ if(A)
+ var/light_available
+ if(A.lighting_use_dynamic)
+ light_available = max(0,min(10,T.lighting_lumcount)-5)
+ else
+ light_available = 5
+ if(abs(light_available - seed.ideal_light) > seed.light_tolerance)
+ die()
+ return
+
+/obj/effect/plant_controller
+
+ //What this does is that instead of having the grow minimum of 1, required to start growing, the minimum will be 0,
+ //meaning if you get the spacevines' size to something less than 20 plots, it won't grow anymore.
+
+ var/list/obj/effect/plantsegment/vines = list()
+ var/list/growth_queue = list()
+ var/reached_collapse_size
+ var/reached_slowdown_size
+ var/datum/seed/seed
+
+ var/collapse_limit = 250
+ var/slowdown_limit = 30
+ var/limited_growth = 0
+
+/obj/effect/plant_controller/creeper
+ collapse_limit = 6
+ slowdown_limit = 3
+ limited_growth = 1
+
+/obj/effect/plant_controller/New()
+ if(!istype(src.loc,/turf/simulated/floor))
+ del(src)
+
+ spawn(0)
+ spawn_piece(src.loc)
+
+ processing_objects.Add(src)
+
+/obj/effect/plant_controller/Del()
+ processing_objects.Remove(src)
+ ..()
+
+/obj/effect/plant_controller/proc/spawn_piece(var/turf/location)
+ var/obj/effect/plantsegment/SV = new(location)
+ SV.limited_growth = src.limited_growth
+ growth_queue += SV
+ vines += SV
+ SV.master = src
+ if(seed)
+ SV.seed = seed
+ SV.name = "[seed.seed_name] vines"
+ SV.update()
+
+/obj/effect/plant_controller/process()
+
+ // Space vines exterminated. Remove the controller
+ if(!vines)
+ del(src)
+ return
+
+ // Sanity check.
+ if(!growth_queue)
+ del(src)
+ return
+
+ // Check if we're too big for our own good.
+ if(vines.len >= (seed ? seed.potency * collapse_limit : 250) && !reached_collapse_size)
+ reached_collapse_size = 1
+ if(vines.len >= (seed ? seed.potency * slowdown_limit : 30) && !reached_slowdown_size )
+ reached_slowdown_size = 1
+
+ var/length = 0
+ if(reached_collapse_size)
+ length = 0
+ else if(reached_slowdown_size)
+ if(prob(seed ? seed.potency : 25))
+ length = 1
+ else
+ length = 0
+ else
+ length = 1
+
+ length = min(30, max(length, vines.len/5))
+
+ // Update as many pieces of vine as we're allowed to.
+ // Append updated vines to the end of the growth queue.
+ var/i = 0
+ var/list/obj/effect/plantsegment/queue_end = list()
+ for(var/obj/effect/plantsegment/SV in growth_queue)
+ i++
+ queue_end += SV
+ growth_queue -= SV
+
+ SV.life()
+
+ if(SV.energy < 2) //If tile isn't fully grown
+ var/chance
+ if(seed)
+ chance = limited_growth ? round(seed.potency/2,1) : seed.potency
+ else
+ chance = 20
+
+ if(prob(chance))
+ SV.grow()
+
+ else if(!seed || !limited_growth) //If tile is fully grown and not just a creeper.
+ SV.entangle_mob()
+
+ SV.update()
+ SV.spread()
+ if(i >= length)
+ break
+
+ growth_queue = growth_queue + queue_end
\ No newline at end of file
diff --git a/code/modules/mining/abandonedcrates.dm b/code/modules/mining/abandonedcrates.dm
index 20cb8909c18..f71b7f9d350 100644
--- a/code/modules/mining/abandonedcrates.dm
+++ b/code/modules/mining/abandonedcrates.dm
@@ -39,7 +39,7 @@
return
if(9)
for(var/i = 0, i < 3, i++)
- new/obj/machinery/hydroponics(src)
+ new/obj/machinery/portable_atmospherics/hydroponics(src)
if(10)
for(var/i = 0, i < 3, i++)
new/obj/item/weapon/reagent_containers/glass/beaker/noreact(src)
diff --git a/code/modules/mining/drilling/distribution.dm b/code/modules/mining/drilling/distribution.dm
index bf32d417cd0..e152a60b343 100644
--- a/code/modules/mining/drilling/distribution.dm
+++ b/code/modules/mining/drilling/distribution.dm
@@ -163,7 +163,6 @@ Deep minerals:
target_turf = locate(tx+j, ty+i, asteroid_z)
if(target_turf && target_turf.has_resources)
-
target_turf.resources = list()
target_turf.resources["silicates"] = rand(3,5)
target_turf.resources["carbonaceous rock"] = rand(3,5)
diff --git a/code/modules/mining/machine_processing.dm b/code/modules/mining/machine_processing.dm
index d8b671af252..2a029b42eeb 100644
--- a/code/modules/mining/machine_processing.dm
+++ b/code/modules/mining/machine_processing.dm
@@ -62,7 +62,7 @@
dat += "Currently displaying [show_all_ores ? "all ore types" : "only available ore types"]. \[[show_all_ores ? "show less" : "show more"]\]"
dat += "The ore processor is currently [(machine.active ? "processing" : "disabled")]."
user << browse(dat, "window=processor_console;size=400x500")
- onclose(user, "computer")
+ onclose(user, "processor_console")
return
/obj/machinery/mineral/processing_unit_console/Topic(href, href_list)
diff --git a/code/modules/mining/mine_areas.dm b/code/modules/mining/mine_areas.dm
index 0a8b01bb194..c1fa4f3ddb6 100644
--- a/code/modules/mining/mine_areas.dm
+++ b/code/modules/mining/mine_areas.dm
@@ -7,10 +7,12 @@
/area/mine/explored
name = "Mine"
icon_state = "explored"
+ ambience = list('sound/ambience/ambimine.ogg', 'sound/ambience/song_game.ogg')
/area/mine/unexplored
name = "Mine"
icon_state = "unexplored"
+ ambience = list('sound/ambience/ambimine.ogg', 'sound/ambience/song_game.ogg')
/area/mine/lobby
name = "Mining station"
diff --git a/code/modules/mining/mine_items.dm b/code/modules/mining/mine_items.dm
index c54853355e9..f695621d977 100644
--- a/code/modules/mining/mine_items.dm
+++ b/code/modules/mining/mine_items.dm
@@ -181,3 +181,70 @@
icon_opened = "miningcaropen"
icon_closed = "miningcar"
+// Flags.
+
+/obj/item/stack/flag
+ name = "flags"
+ desc = "Some colourful flags."
+ singular_name = "flag"
+ amount = 10
+ max_amount = 10
+ icon = 'icons/obj/mining.dmi'
+ var/upright = 0
+ var/base_state
+
+/obj/item/stack/flag/New()
+ ..()
+ base_state = icon_state
+
+/obj/item/stack/flag/red
+ name = "red flags"
+ singular_name = "red flag"
+ icon_state = "redflag"
+
+/obj/item/stack/flag/yellow
+ name = "yellow flags"
+ singular_name = "yellow flag"
+ icon_state = "yellowflag"
+
+/obj/item/stack/flag/green
+ name = "green flags"
+ singular_name = "green flag"
+ icon_state = "greenflag"
+
+/obj/item/stack/flag/attackby(obj/item/W as obj, mob/user as mob)
+ if(upright && istype(W,src.type))
+ src.attack_hand(user)
+ else
+ ..()
+
+/obj/item/stack/flag/attack_hand(user as mob)
+ if(upright)
+ upright = 0
+ icon_state = base_state
+ anchored = 0
+ src.visible_message("[user] knocks down [src].")
+ else
+ ..()
+
+/obj/item/stack/flag/attack_self(mob/user as mob)
+
+ var/obj/item/stack/flag/F = locate() in get_turf(src)
+
+ var/turf/T = get_turf(src)
+ if(!T || !istype(T,/turf/simulated/floor/plating/airless/asteroid))
+ user << "The flag won't stand up in this terrain."
+ return
+
+ if(F && F.upright)
+ user << "There is already a flag here."
+ return
+
+ var/obj/item/stack/flag/newflag = new src.type(T)
+ newflag.amount = 1
+ newflag.upright = 1
+ anchored = 1
+ newflag.name = newflag.singular_name
+ newflag.icon_state = "[newflag.base_state]_open"
+ newflag.visible_message("[user] plants [newflag] firmly in the ground.")
+ src.use(1)
diff --git a/code/modules/mining/mine_turfs.dm b/code/modules/mining/mine_turfs.dm
index 3489854b981..c556990e815 100644
--- a/code/modules/mining/mine_turfs.dm
+++ b/code/modules/mining/mine_turfs.dm
@@ -26,363 +26,357 @@
has_resources = 1
- New()
- . = ..()
+/turf/simulated/mineral/New()
- MineralSpread()
+ MineralSpread()
- spawn(1)
- var/turf/T
- if((istype(get_step(src, NORTH), /turf/simulated/floor)) || (istype(get_step(src, NORTH), /turf/space)) || (istype(get_step(src, NORTH), /turf/simulated/shuttle/floor)))
- T = get_step(src, NORTH)
- if (T)
- T.overlays += image('icons/turf/walls.dmi', "rock_side_s", layer=2)
- if((istype(get_step(src, SOUTH), /turf/simulated/floor)) || (istype(get_step(src, SOUTH), /turf/space)) || (istype(get_step(src, SOUTH), /turf/simulated/shuttle/floor)))
- T = get_step(src, SOUTH)
- if (T)
- T.overlays += image('icons/turf/walls.dmi', "rock_side_n", layer=2)
- if((istype(get_step(src, EAST), /turf/simulated/floor)) || (istype(get_step(src, EAST), /turf/space)) || (istype(get_step(src, EAST), /turf/simulated/shuttle/floor)))
- T = get_step(src, EAST)
- if (T)
- T.overlays += image('icons/turf/walls.dmi', "rock_side_w", layer=2)
- if((istype(get_step(src, WEST), /turf/simulated/floor)) || (istype(get_step(src, WEST), /turf/space)) || (istype(get_step(src, WEST), /turf/simulated/shuttle/floor)))
- T = get_step(src, WEST)
- if (T)
- T.overlays += image('icons/turf/walls.dmi', "rock_side_e", layer=2)
+ spawn(2)
+ var/list/step_overlays = list("s" = NORTH, "n" = SOUTH, "w" = EAST, "e" = WEST)
+ for(var/direction in step_overlays)
+ var/turf/turf_to_check = get_step(src,step_overlays[direction])
+ if(istype(turf_to_check,/turf/simulated/floor/plating/airless/asteroid))
+ var/turf/simulated/floor/plating/airless/asteroid/T = turf_to_check
+ T.updateMineralOverlays()
- ex_act(severity)
- switch(severity)
- if(2.0)
- if (prob(70))
- mined_ore = 1 //some of the stuff gets blown up
- GetDrilled()
- if(1.0)
- mined_ore = 2 //some of the stuff gets blown up
+ else if(istype(turf_to_check,/turf/space) || istype(turf_to_check,/turf/simulated/floor))
+ turf_to_check.overlays += image('icons/turf/walls.dmi', "rock_side_[direction]")
+
+/turf/simulated/mineral/ex_act(severity)
+ switch(severity)
+ if(2.0)
+ if (prob(70))
+ mined_ore = 1 //some of the stuff gets blown up
GetDrilled()
+ if(1.0)
+ mined_ore = 2 //some of the stuff gets blown up
+ GetDrilled()
+
+/turf/simulated/mineral/Bumped(AM)
+ . = ..()
+ if(istype(AM,/mob/living/carbon/human))
+ var/mob/living/carbon/human/H = AM
+ if((istype(H.l_hand,/obj/item/weapon/pickaxe)) && (!H.hand))
+ attackby(H.l_hand,H)
+ else if((istype(H.r_hand,/obj/item/weapon/pickaxe)) && H.hand)
+ attackby(H.r_hand,H)
+
+ else if(istype(AM,/mob/living/silicon/robot))
+ var/mob/living/silicon/robot/R = AM
+ if(istype(R.module_active,/obj/item/weapon/pickaxe))
+ attackby(R.module_active,R)
+
+ else if(istype(AM,/obj/mecha))
+ var/obj/mecha/M = AM
+ if(istype(M.selected,/obj/item/mecha_parts/mecha_equipment/tool/drill))
+ M.selected.action(src)
+
+/turf/simulated/mineral/proc/MineralSpread()
+ if(mineral && mineral.spread)
+ for(var/trydir in cardinal)
+ if(prob(mineral.spread_chance))
+ var/turf/simulated/mineral/random/target_turf = get_step(src, trydir)
+ if(istype(target_turf) && !target_turf.mineral)
+ target_turf.mineral = mineral
+ target_turf.UpdateMineral()
+ target_turf.MineralSpread()
- Bumped(AM)
- . = ..()
- if(istype(AM,/mob/living/carbon/human))
- var/mob/living/carbon/human/H = AM
- if((istype(H.l_hand,/obj/item/weapon/pickaxe)) && (!H.hand))
- attackby(H.l_hand,H)
- else if((istype(H.r_hand,/obj/item/weapon/pickaxe)) && H.hand)
- attackby(H.r_hand,H)
+/turf/simulated/mineral/proc/UpdateMineral()
+ if(!mineral)
+ name = "\improper Rock"
+ icon_state = "rock"
+ return
+ name = "\improper [mineral.display_name] deposit"
+ overlays.Cut()
+ overlays += "rock_[mineral.name]"
- else if(istype(AM,/mob/living/silicon/robot))
- var/mob/living/silicon/robot/R = AM
- if(istype(R.module_active,/obj/item/weapon/pickaxe))
- attackby(R.module_active,R)
+//Not even going to touch this pile of spaghetti
+/turf/simulated/mineral/attackby(obj/item/weapon/W as obj, mob/user as mob)
- else if(istype(AM,/obj/mecha))
- var/obj/mecha/M = AM
- if(istype(M.selected,/obj/item/mecha_parts/mecha_equipment/tool/drill))
- M.selected.action(src)
+ if (!(istype(usr, /mob/living/carbon/human) || ticker) && ticker.mode.name != "monkey")
+ usr << "\red You don't have the dexterity to do this!"
+ return
+ if (istype(W, /obj/item/device/core_sampler))
+ geologic_data.UpdateNearbyArtifactInfo(src)
+ var/obj/item/device/core_sampler/C = W
+ C.sample_item(src, user)
+ return
- proc/MineralSpread()
- if(mineral && mineral.spread)
- for(var/trydir in cardinal)
- if(prob(mineral.spread_chance))
- var/turf/simulated/mineral/random/target_turf = get_step(src, trydir)
- if(istype(target_turf) && !target_turf.mineral)
- target_turf.mineral = mineral
- target_turf.UpdateMineral()
- target_turf.MineralSpread()
+ if (istype(W, /obj/item/device/depth_scanner))
+ var/obj/item/device/depth_scanner/C = W
+ C.scan_atom(user, src)
+ return
+ if (istype(W, /obj/item/device/measuring_tape))
+ var/obj/item/device/measuring_tape/P = W
+ user.visible_message("\blue[user] extends [P] towards [src].","\blue You extend [P] towards [src].")
+ if(do_after(user,25))
+ user << "\blue \icon[P] [src] has been excavated to a depth of [2*excavation_level]cm."
+ return
- proc/UpdateMineral()
- if(!mineral)
- name = "\improper Rock"
- icon_state = "rock"
- return
- name = "\improper [mineral.display_name] deposit"
- icon_state = "rock_[mineral.name]"
-
-
- //Not even going to touch this pile of spaghetti
- attackby(obj/item/weapon/W as obj, mob/user as mob)
-
- if (!(istype(usr, /mob/living/carbon/human) || ticker) && ticker.mode.name != "monkey")
- usr << "\red You don't have the dexterity to do this!"
+ if (istype(W, /obj/item/weapon/pickaxe))
+ var/turf/T = user.loc
+ if (!( istype(T, /turf) ))
return
- if (istype(W, /obj/item/device/core_sampler))
- geologic_data.UpdateNearbyArtifactInfo(src)
- var/obj/item/device/core_sampler/C = W
- C.sample_item(src, user)
+ var/obj/item/weapon/pickaxe/P = W
+ if(last_act + P.digspeed > world.time)//prevents message spam
return
+ last_act = world.time
- if (istype(W, /obj/item/device/depth_scanner))
- var/obj/item/device/depth_scanner/C = W
- C.scan_atom(user, src)
- return
+ playsound(user, P.drill_sound, 20, 1)
- if (istype(W, /obj/item/device/measuring_tape))
- var/obj/item/device/measuring_tape/P = W
- user.visible_message("\blue[user] extends [P] towards [src].","\blue You extend [P] towards [src].")
- if(do_after(user,25))
- user << "\blue \icon[P] [src] has been excavated to a depth of [2*excavation_level]cm."
- return
+ //handle any archaeological finds we might uncover
+ var/fail_message
+ if(finds && finds.len)
+ var/datum/find/F = finds[1]
+ if(excavation_level + P.excavation_amount > F.excavation_required)
+ //Chance to destroy / extract any finds here
+ fail_message = ". [pick("There is a crunching noise","[W] collides with some different rock","Part of the rock face crumbles away","Something breaks under [W]")]"
- if (istype(W, /obj/item/weapon/pickaxe))
- var/turf/T = user.loc
- if (!( istype(T, /turf) ))
- return
+ user << "\red You start [P.drill_verb][fail_message ? fail_message : ""]."
- var/obj/item/weapon/pickaxe/P = W
- if(last_act + P.digspeed > world.time)//prevents message spam
- return
- last_act = world.time
+ if(fail_message && prob(90))
+ if(prob(25))
+ excavate_find(5, finds[1])
+ else if(prob(50))
+ finds.Remove(finds[1])
+ if(prob(50))
+ artifact_debris()
- playsound(user, P.drill_sound, 20, 1)
+ if(do_after(user,P.digspeed))
+ user << "\blue You finish [P.drill_verb] the rock."
- //handle any archaeological finds we might uncover
- var/fail_message
if(finds && finds.len)
var/datum/find/F = finds[1]
- if(excavation_level + P.excavation_amount > F.excavation_required)
- //Chance to destroy / extract any finds here
- fail_message = ", [pick("there is a crunching noise","[W] collides with some different rock","part of the rock face crumbles away","something breaks under [W]")]"
-
- user << "\red You start [P.drill_verb][fail_message ? fail_message : ""]."
-
- if(fail_message && prob(90))
- if(prob(25))
- excavate_find(5, finds[1])
- else if(prob(50))
- finds.Remove(finds[1])
- if(prob(50))
- artifact_debris()
-
- if(do_after(user,P.digspeed))
- user << "\blue You finish [P.drill_verb] the rock."
-
- if(finds && finds.len)
- var/datum/find/F = finds[1]
- if(round(excavation_level + P.excavation_amount) == F.excavation_required)
- //Chance to extract any items here perfectly, otherwise just pull them out along with the rock surrounding them
- if(excavation_level + P.excavation_amount > F.excavation_required)
- //if you can get slightly over, perfect extraction
- excavate_find(100, F)
- else
- excavate_find(80, F)
-
- else if(excavation_level + P.excavation_amount > F.excavation_required - F.clearance_range)
- //just pull the surrounding rock out
- excavate_find(0, F)
-
- if( excavation_level + P.excavation_amount >= 100 )
- //if players have been excavating this turf, leave some rocky debris behind
- var/obj/structure/boulder/B
- if(artifact_find)
- if( excavation_level > 0 || prob(15) )
- //boulder with an artifact inside
- B = new(src)
- if(artifact_find)
- B.artifact_find = artifact_find
- else
- artifact_debris(1)
- else if(prob(15))
- //empty boulder
- B = new(src)
-
- if(B)
- GetDrilled(0)
+ if(round(excavation_level + P.excavation_amount) == F.excavation_required)
+ //Chance to extract any items here perfectly, otherwise just pull them out along with the rock surrounding them
+ if(excavation_level + P.excavation_amount > F.excavation_required)
+ //if you can get slightly over, perfect extraction
+ excavate_find(100, F)
else
- GetDrilled(1)
- return
+ excavate_find(80, F)
- excavation_level += P.excavation_amount
+ else if(excavation_level + P.excavation_amount > F.excavation_required - F.clearance_range)
+ //just pull the surrounding rock out
+ excavate_find(0, F)
- //archaeo overlays
- if(!archaeo_overlay && finds && finds.len)
- var/datum/find/F = finds[1]
- if(F.excavation_required <= excavation_level + F.view_range)
- archaeo_overlay = "overlay_archaeo[rand(1,3)]"
- overlays += archaeo_overlay
+ if( excavation_level + P.excavation_amount >= 100 )
+ //if players have been excavating this turf, leave some rocky debris behind
+ var/obj/structure/boulder/B
+ if(artifact_find)
+ if( excavation_level > 0 || prob(15) )
+ //boulder with an artifact inside
+ B = new(src)
+ if(artifact_find)
+ B.artifact_find = artifact_find
+ else
+ artifact_debris(1)
+ else if(prob(15))
+ //empty boulder
+ B = new(src)
- //there's got to be a better way to do this
- var/update_excav_overlay = 0
- if(excavation_level >= 75)
- if(excavation_level - P.excavation_amount < 75)
- update_excav_overlay = 1
- else if(excavation_level >= 50)
- if(excavation_level - P.excavation_amount < 50)
- update_excav_overlay = 1
- else if(excavation_level >= 25)
- if(excavation_level - P.excavation_amount < 25)
- update_excav_overlay = 1
-
- //update overlays displaying excavation level
- if( !(excav_overlay && excavation_level > 0) || update_excav_overlay )
- var/excav_quadrant = round(excavation_level / 25) + 1
- excav_overlay = "overlay_excv[excav_quadrant]_[rand(1,3)]"
- overlays += excav_overlay
-
- /* Nope.
- //extract pesky minerals while we're excavating
- while(excavation_minerals.len && excavation_level > excavation_minerals[excavation_minerals.len])
- DropMineral()
- pop(excavation_minerals)
- mineralAmt-- */
-
- //drop some rocks
- next_rock += P.excavation_amount * 10
- while(next_rock > 100)
- next_rock -= 100
- var/obj/item/weapon/ore/O = new(src)
- geologic_data.UpdateNearbyArtifactInfo(src)
- O.geologic_data = geologic_data
-
- else
- return attack_hand(user)
-
-
- proc/DropMineral()
- if(!mineral)
- return
-
- var/obj/item/weapon/ore/O = new mineral.ore (src)
- if(istype(O))
- geologic_data.UpdateNearbyArtifactInfo(src)
- O.geologic_data = geologic_data
- return O
-
-
- proc/GetDrilled(var/artifact_fail = 0)
- //var/destroyed = 0 //used for breaking strange rocks
- if (mineral && mineral.result_amount)
-
- //if the turf has already been excavated, some of it's ore has been removed
- for (var/i = 1 to mineral.result_amount - mined_ore)
- DropMineral()
-
- //destroyed artifacts have weird, unpleasant effects
- //make sure to destroy them before changing the turf though
- if(artifact_find && artifact_fail)
- var/pain = 0
- if(prob(50))
- pain = 1
- for(var/mob/living/M in range(src, 200))
- M << "[pick("A high pitched [pick("keening","wailing","whistle")]","A rumbling noise like [pick("thunder","heavy machinery")]")] somehow penetrates your mind before fading away!"
- if(pain)
- flick("pain",M.pain)
- if(prob(50))
- M.adjustBruteLoss(5)
+ if(B)
+ GetDrilled(0)
else
- flick("flash",M.flash)
- if(prob(50))
- M.Stun(5)
- M.apply_effect(25, IRRADIATE)
+ GetDrilled(1)
+ return
- var/turf/simulated/floor/plating/airless/asteroid/N = ChangeTurf(/turf/simulated/floor/plating/airless/asteroid)
- N.fullUpdateMineralOverlays()
+ excavation_level += P.excavation_amount
- if(rand(1,500) == 1)
- visible_message("An old dusty crate was buried within!")
- new /obj/structure/closet/crate/secure/loot(src)
+ //archaeo overlays
+ if(!archaeo_overlay && finds && finds.len)
+ var/datum/find/F = finds[1]
+ if(F.excavation_required <= excavation_level + F.view_range)
+ archaeo_overlay = "overlay_archaeo[rand(1,3)]"
+ overlays += archaeo_overlay
+
+ //there's got to be a better way to do this
+ var/update_excav_overlay = 0
+ if(excavation_level >= 75)
+ if(excavation_level - P.excavation_amount < 75)
+ update_excav_overlay = 1
+ else if(excavation_level >= 50)
+ if(excavation_level - P.excavation_amount < 50)
+ update_excav_overlay = 1
+ else if(excavation_level >= 25)
+ if(excavation_level - P.excavation_amount < 25)
+ update_excav_overlay = 1
+
+ //update overlays displaying excavation level
+ if( !(excav_overlay && excavation_level > 0) || update_excav_overlay )
+ var/excav_quadrant = round(excavation_level / 25) + 1
+ excav_overlay = "overlay_excv[excav_quadrant]_[rand(1,3)]"
+ overlays += excav_overlay
+
+ //drop some rocks
+ next_rock += P.excavation_amount * 10
+ while(next_rock > 100)
+ next_rock -= 100
+ var/obj/item/weapon/ore/O = new(src)
+ geologic_data.UpdateNearbyArtifactInfo(src)
+ O.geologic_data = geologic_data
+
+ else
+ return attack_hand(user)
+
+/turf/simulated/mineral/proc/DropMineral()
+ if(!mineral)
+ return
+
+ var/obj/item/weapon/ore/O = new mineral.ore (src)
+ if(istype(O))
+ geologic_data.UpdateNearbyArtifactInfo(src)
+ O.geologic_data = geologic_data
+ return O
+
+/turf/simulated/mineral/proc/GetDrilled(var/artifact_fail = 0)
+ //var/destroyed = 0 //used for breaking strange rocks
+ if (mineral && mineral.result_amount)
+
+ //if the turf has already been excavated, some of it's ore has been removed
+ for (var/i = 1 to mineral.result_amount - mined_ore)
+ DropMineral()
+
+ //destroyed artifacts have weird, unpleasant effects
+ //make sure to destroy them before changing the turf though
+ if(artifact_find && artifact_fail)
+ var/pain = 0
+ if(prob(50))
+ pain = 1
+ for(var/mob/living/M in range(src, 200))
+ M << "[pick("A high pitched [pick("keening","wailing","whistle")]","A rumbling noise like [pick("thunder","heavy machinery")]")] somehow penetrates your mind before fading away!"
+ if(pain)
+ flick("pain",M.pain)
+ if(prob(50))
+ M.adjustBruteLoss(5)
+ else
+ flick("flash",M.flash)
+ if(prob(50))
+ M.Stun(5)
+ M.apply_effect(25, IRRADIATE)
- proc/excavate_find(var/prob_clean = 0, var/datum/find/F)
- //with skill and luck, players can cleanly extract finds
- //otherwise, they come out inside a chunk of rock
- var/obj/item/weapon/X
- if(prob_clean)
- X = new /obj/item/weapon/archaeological_find(src, new_item_type = F.find_type)
- else
- X = new /obj/item/weapon/ore/strangerock(src, inside_item_type = F.find_type)
- geologic_data.UpdateNearbyArtifactInfo(src)
- X:geologic_data = geologic_data
+ var/list/step_overlays = list("n" = NORTH, "s" = SOUTH, "e" = EAST, "w" = WEST)
- //some find types delete the /obj/item/weapon/archaeological_find and replace it with something else, this handles when that happens
- //yuck
- var/display_name = "something"
- if(!X)
- X = last_find
- if(X)
- display_name = X.name
+ //Add some rubble, you did just clear out a big chunk of rock.
+ var/turf/simulated/floor/plating/airless/asteroid/N = ChangeTurf(/turf/simulated/floor/plating/airless/asteroid)
+ N.overlay_detail = "asteroid[rand(0,9)]"
- //many finds are ancient and thus very delicate - luckily there is a specialised energy suspension field which protects them when they're being extracted
- if(prob(F.prob_delicate))
- var/obj/effect/suspension_field/S = locate() in src
- if(!S || S.field_type != get_responsive_reagent(F.find_type))
- if(X)
- visible_message("\red[pick("[display_name] crumbles away into dust","[display_name] breaks apart")].")
- del(X)
+ // Kill and update the space overlays around us.
+ for(var/direction in step_overlays)
+ var/turf/space/T = get_step(src, step_overlays[direction])
+ if(istype(T))
+ T.overlays.Cut()
+ for(var/next_direction in step_overlays)
+ if(istype(get_step(T, step_overlays[next_direction]),/turf/simulated/mineral))
+ T.overlays += image('icons/turf/walls.dmi', "rock_side_[next_direction]")
- finds.Remove(F)
+ // Update the
+ N.updateMineralOverlays(1)
+
+ if(rand(1,500) == 1)
+ visible_message("An old dusty crate was buried within!")
+ new /obj/structure/closet/crate/secure/loot(src)
- proc/artifact_debris(var/severity = 0)
- //cael's patented random limited drop componentized loot system!
- //sky's patented not-fucking-retarded overhaul!
+/turf/simulated/mineral/proc/excavate_find(var/prob_clean = 0, var/datum/find/F)
+ //with skill and luck, players can cleanly extract finds
+ //otherwise, they come out inside a chunk of rock
+ var/obj/item/weapon/X
+ if(prob_clean)
+ X = new /obj/item/weapon/archaeological_find(src, new_item_type = F.find_type)
+ else
+ X = new /obj/item/weapon/ore/strangerock(src, inside_item_type = F.find_type)
+ geologic_data.UpdateNearbyArtifactInfo(src)
+ X:geologic_data = geologic_data
- //Give a random amount of loot from 1 to 3 or 5, varying on severity.
- for(var/j in 1 to rand(1, 3 + max(min(severity, 1), 0) * 2))
- switch(rand(1,7))
- if(1)
- var/obj/item/stack/rods/R = new(src)
- R.amount = rand(5,25)
+ //some find types delete the /obj/item/weapon/archaeological_find and replace it with something else, this handles when that happens
+ //yuck
+ var/display_name = "something"
+ if(!X)
+ X = last_find
+ if(X)
+ display_name = X.name
- if(2)
- var/obj/item/stack/tile/R = new(src)
- R.amount = rand(1,5)
+ //many finds are ancient and thus very delicate - luckily there is a specialised energy suspension field which protects them when they're being extracted
+ if(prob(F.prob_delicate))
+ var/obj/effect/suspension_field/S = locate() in src
+ if(!S || S.field_type != get_responsive_reagent(F.find_type))
+ if(X)
+ visible_message("\red[pick("[display_name] crumbles away into dust","[display_name] breaks apart")].")
+ del(X)
- if(3)
- var/obj/item/stack/sheet/metal/R = new(src)
- R.amount = rand(5,25)
+ finds.Remove(F)
- if(4)
- var/obj/item/stack/sheet/plasteel/R = new(src)
- R.amount = rand(5,25)
- if(5)
- var/quantity = rand(1,3)
- for(var/i=0, iResults:"
if(abs(pressure - ONE_ATMOSPHERE) < 10)
@@ -408,33 +408,8 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
else
src << "\red Pressure: [round(pressure,0.1)] kPa"
if(total_moles)
- var/o2_concentration = environment.oxygen/total_moles
- var/n2_concentration = environment.nitrogen/total_moles
- var/co2_concentration = environment.carbon_dioxide/total_moles
- var/phoron_concentration = environment.phoron/total_moles
-
- var/unknown_concentration = 1-(o2_concentration+n2_concentration+co2_concentration+phoron_concentration)
- if(abs(n2_concentration - N2STANDARD) < 20)
- src << "\blue Nitrogen: [round(n2_concentration*100)]% ([round(environment.nitrogen,0.01)] moles)"
- else
- src << "\red Nitrogen: [round(n2_concentration*100)]% ([round(environment.nitrogen,0.01)] moles)"
-
- if(abs(o2_concentration - O2STANDARD) < 2)
- src << "\blue Oxygen: [round(o2_concentration*100)]% ([round(environment.oxygen,0.01)] moles)"
- else
- src << "\red Oxygen: [round(o2_concentration*100)]% ([round(environment.oxygen,0.01)] moles)"
-
- if(co2_concentration > 0.01)
- src << "\red CO2: [round(co2_concentration*100)]% ([round(environment.carbon_dioxide,0.01)] moles)"
- else
- src << "\blue CO2: [round(co2_concentration*100)]% ([round(environment.carbon_dioxide,0.01)] moles)"
-
- if(phoron_concentration > 0.01)
- src << "\red Phoron: [round(phoron_concentration*100)]% ([round(environment.phoron,0.01)] moles)"
-
- if(unknown_concentration > 0.01)
- src << "\red Unknown: [round(unknown_concentration*100)]% ([round(unknown_concentration*total_moles,0.01)] moles)"
-
+ for(var/g in environment.gas)
+ src << "\blue [gas_data.name[g]]: [round((environment.gas[g] / total_moles) * 100)]% ([round(environment.gas[g], 0.01)] moles)"
src << "\blue Temperature: [round(environment.temperature-T0C,0.1)]°C"
src << "\blue Heat Capacity: [round(environment.heat_capacity(),0.1)]"
diff --git a/code/modules/mob/holder.dm b/code/modules/mob/holder.dm
index 747467a66f9..d1adff7de2c 100644
--- a/code/modules/mob/holder.dm
+++ b/code/modules/mob/holder.dm
@@ -54,23 +54,25 @@
//Mob specific holders.
/obj/item/weapon/holder/diona
-
name = "diona nymph"
desc = "It's a tiny plant critter."
icon_state = "nymph"
origin_tech = "magnets=3;biotech=5"
/obj/item/weapon/holder/drone
-
name = "maintenance drone"
desc = "It's a small maintenance robot."
icon_state = "drone"
origin_tech = "magnets=3;engineering=5"
-
/obj/item/weapon/holder/cat
-
name = "cat"
desc = "It's a cat. Meow."
icon_state = "cat"
- origin_tech = null
\ No newline at end of file
+ origin_tech = null
+
+/obj/item/weapon/holder/borer
+ name = "cortical borer"
+ desc = "It's a slimy brain slug. Gross."
+ icon_state = "borer"
+ origin_tech = "biotech=6"
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm b/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm
index d419a7cf70e..03e590f036b 100644
--- a/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm
+++ b/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm
@@ -177,7 +177,7 @@
var/damage = rand(1, 3)
- if(istype(M, /mob/living/carbon/slime/adult))
+ if(M.is_adult)
damage = rand(10, 40)
else
damage = rand(5, 35)
diff --git a/code/modules/mob/living/carbon/alien/humanoid/life.dm b/code/modules/mob/living/carbon/alien/humanoid/life.dm
index cd62ff9b2c4..defcc968d13 100644
--- a/code/modules/mob/living/carbon/alien/humanoid/life.dm
+++ b/code/modules/mob/living/carbon/alien/humanoid/life.dm
@@ -123,7 +123,7 @@
breath_moles = (ONE_ATMOSPHERE*BREATH_VOLUME/R_IDEAL_GAS_EQUATION*environment.temperature)
else*/
// Not enough air around, take a percentage of what's there to model this properly
- breath_moles = environment.total_moles()*BREATH_PERCENTAGE
+ breath_moles = environment.total_moles*BREATH_PERCENTAGE
breath = loc.remove_air(breath_moles)
@@ -172,24 +172,24 @@
return 0
var/phoron_used = 0
- var/breath_pressure = (breath.total_moles()*R_IDEAL_GAS_EQUATION*breath.temperature)/BREATH_VOLUME
+ var/breath_pressure = (breath.total_moles * R_IDEAL_GAS_EQUATION * breath.temperature) / BREATH_VOLUME
//Partial pressure of the phoron in our breath
- var/Toxins_pp = (breath.phoron/breath.total_moles())*breath_pressure
+ var/Toxins_pp = (breath.gas["phoron"] / breath.total_moles) * breath_pressure
if(Toxins_pp) // Detect phoron in air
- adjustToxLoss(breath.phoron*250)
+ adjustToxLoss(breath.gas["phoron"] * 250)
phoron_alert = max(phoron_alert, 1)
- phoron_used = breath.phoron
+ phoron_used = breath.gas["phoron"]
else
phoron_alert = 0
//Breathe in phoron and out oxygen
- breath.phoron -= phoron_used
- breath.oxygen += phoron_used
+ breath.adjust_gas("phoron", -phoron_used)
+ breath.adjust_gas("oxygen", phoron_used)
if(breath.temperature > (T0C+66) && !(COLD_RESISTANCE in mutations)) // Hot air hurts :(
if(prob(20))
@@ -357,22 +357,7 @@
ear_damage = max(ear_damage-0.05, 0)
//Other
- if(stunned)
- AdjustStunned(-1)
- if(!stunned)
- update_icons()
-
- if(weakened)
- weakened = max(weakened-1,0) //before you get mad Rockdtben: I done this so update_canmove isn't called multiple times
-
- if(stuttering)
- stuttering = max(stuttering-1, 0)
-
- if(silent)
- silent = max(silent-1, 0)
-
- if(druggy)
- druggy = max(druggy-1, 0)
+ handle_statuses()
return 1
@@ -461,3 +446,8 @@
if(!(status_flags & GODMODE))
M.adjustBruteLoss(5)
nutrition += 10
+
+/mob/living/carbon/alien/humanoid/handle_stunned()
+ if(stunned && !..())
+ update_icons()
+ return stunned
diff --git a/code/modules/mob/living/carbon/alien/larva/larva.dm b/code/modules/mob/living/carbon/alien/larva/larva.dm
index 043b60bae21..9f5640c69b1 100644
--- a/code/modules/mob/living/carbon/alien/larva/larva.dm
+++ b/code/modules/mob/living/carbon/alien/larva/larva.dm
@@ -198,7 +198,7 @@
var/damage = rand(1, 3)
- if(istype(src, /mob/living/carbon/slime/adult))
+ if(M.is_adult)
damage = rand(20, 40)
else
damage = rand(5, 35)
diff --git a/code/modules/mob/living/carbon/alien/larva/life.dm b/code/modules/mob/living/carbon/alien/larva/life.dm
index 975dd29dc8c..9bb6ae43767 100644
--- a/code/modules/mob/living/carbon/alien/larva/life.dm
+++ b/code/modules/mob/living/carbon/alien/larva/life.dm
@@ -98,7 +98,7 @@
breath_moles = (ONE_ATMOSPHERE*BREATH_VOLUME/R_IDEAL_GAS_EQUATION*environment.temperature)
else*/
// Not enough air around, take a percentage of what's there to model this properly
- breath_moles = environment.total_moles()*BREATH_PERCENTAGE
+ breath_moles = environment.total_moles*BREATH_PERCENTAGE
breath = loc.remove_air(breath_moles)
@@ -147,24 +147,24 @@
return 0
var/phoron_used = 0
- var/breath_pressure = (breath.total_moles()*R_IDEAL_GAS_EQUATION*breath.temperature)/BREATH_VOLUME
+ var/breath_pressure = (breath.total_moles * R_IDEAL_GAS_EQUATION * breath.temperature) / BREATH_VOLUME
//Partial pressure of the phoron in our breath
- var/Toxins_pp = (breath.phoron/breath.total_moles())*breath_pressure
+ var/Toxins_pp = (breath.gas["phoron"] / breath.total_moles) * breath_pressure
if(Toxins_pp) // Detect phoron in air
- adjustToxLoss(breath.phoron*250)
+ adjustToxLoss(breath.gas["phoron"] * 250)
phoron_alert = max(phoron_alert, 1)
- phoron_used = breath.phoron
+ phoron_used = breath.gas["phoron"]
else
phoron_alert = 0
//Breathe in phoron and out oxygen
- breath.phoron -= phoron_used
- breath.oxygen += phoron_used
+ breath.adjust_gas("phoron", -phoron_used)
+ breath.adjust_gas("oxygen", phoron_used)
if(breath.temperature > (T0C+66) && !(COLD_RESISTANCE in mutations)) // Hot air hurts :(
if(prob(20))
@@ -274,20 +274,7 @@ FUCK YOU MORE FAT CODE -Hawk*/
ear_damage = max(ear_damage-0.05, 0)
//Other
- if(stunned)
- AdjustStunned(-1)
-
- if(weakened)
- weakened = max(weakened-1,0) //before you get mad Rockdtben: I done this so update_canmove isn't called multiple times
-
- if(stuttering)
- stuttering = max(stuttering-1, 0)
-
- if(silent)
- silent = max(silent-1, 0)
-
- if(druggy)
- druggy = max(druggy-1, 0)
+ handle_statuses()
return 1
diff --git a/code/modules/mob/living/carbon/alien/special/facehugger.dm b/code/modules/mob/living/carbon/alien/special/facehugger.dm
index 48c16a99839..fc702d3f631 100644
--- a/code/modules/mob/living/carbon/alien/special/facehugger.dm
+++ b/code/modules/mob/living/carbon/alien/special/facehugger.dm
@@ -16,6 +16,7 @@ var/const/MAX_ACTIVE_TIME = 400
item_state = "facehugger"
w_class = 1 //note: can be picked up by aliens unlike most other items of w_class below 4
flags = FPRINT | TABLEPASS | MASKCOVERSMOUTH | MASKCOVERSEYES | MASKINTERNALS
+ body_parts_covered = FACE|EYES
throw_range = 5
var/stat = CONSCIOUS //UNCONSCIOUS is the idle state in this case
@@ -80,7 +81,7 @@ var/const/MAX_ACTIVE_TIME = 400
/obj/item/clothing/mask/facehugger/equipped(mob/M)
Attach(M)
-/obj/item/clothing/mask/facehugger/HasEntered(atom/target)
+/obj/item/clothing/mask/facehugger/Crossed(atom/target)
HasProximity(target)
return
diff --git a/code/modules/mob/living/carbon/brain/life.dm b/code/modules/mob/living/carbon/brain/life.dm
index 1943242bce2..89cf9faa971 100644
--- a/code/modules/mob/living/carbon/brain/life.dm
+++ b/code/modules/mob/living/carbon/brain/life.dm
@@ -186,20 +186,7 @@
emp_damage -= 1
//Other
- if(stunned)
- AdjustStunned(-1)
-
- if(weakened)
- weakened = max(weakened-1,0) //before you get mad Rockdtben: I done this so update_canmove isn't called multiple times
-
- if(stuttering)
- stuttering = max(stuttering-1, 0)
-
- if(silent)
- silent = max(silent-1, 0)
-
- if(druggy)
- druggy = max(druggy-1, 0)
+ handle_statuses()
return 1
diff --git a/code/modules/mob/living/carbon/brain/posibrain.dm b/code/modules/mob/living/carbon/brain/posibrain.dm
index 4f324805a28..6877ed4b854 100644
--- a/code/modules/mob/living/carbon/brain/posibrain.dm
+++ b/code/modules/mob/living/carbon/brain/posibrain.dm
@@ -16,80 +16,78 @@
mecha = null//This does not appear to be used outside of reference in mecha.dm.
- attack_self(mob/user as mob)
- if(brainmob && !brainmob.key && searching == 0)
- //Start the process of searching for a new user.
- user << "\blue You carefully locate the manual activation switch and start the positronic brain's boot process."
- icon_state = "posibrain-searching"
- src.searching = 1
- src.request_player()
- spawn(600) reset_search()
+/obj/item/device/mmi/posibrain/attack_self(mob/user as mob)
+ if(brainmob && !brainmob.key && searching == 0)
+ //Start the process of searching for a new user.
+ user << "\blue You carefully locate the manual activation switch and start the positronic brain's boot process."
+ icon_state = "posibrain-searching"
+ src.searching = 1
+ src.request_player()
+ spawn(600) reset_search()
- proc/request_player()
- for(var/mob/dead/observer/O in player_list)
- if(O.has_enabled_antagHUD == 1 && config.antag_hud_restricted)
- continue
- if(jobban_isbanned(O, "pAI"))
- continue
- if(O.client)
- if(O.client.prefs.be_special & BE_PAI)
- question(O.client)
+/obj/item/device/mmi/posibrain/proc/request_player()
+ for(var/mob/dead/observer/O in player_list)
+ if(O.has_enabled_antagHUD == 1 && config.antag_hud_restricted)
+ continue
+ if(jobban_isbanned(O, "pAI"))
+ continue
+ if(O.client)
+ if(O.client.prefs.be_special & BE_PAI)
+ question(O.client)
- proc/question(var/client/C)
- spawn(0)
- if(!C) return
- var/response = alert(C, "Someone is requesting a personality for a positronic brain. Would you like to play as one?", "Positronic brain request", "Yes", "No", "Never for this round")
- if(!C || brainmob.key || 0 == searching) return //handle logouts that happen whilst the alert is waiting for a response, and responses issued after a brain has been located.
- if(response == "Yes")
- transfer_personality(C.mob)
- else if (response == "Never for this round")
- C.prefs.be_special ^= BE_PAI
+/obj/item/device/mmi/posibrain/proc/question(var/client/C)
+ spawn(0)
+ if(!C) return
+ var/response = alert(C, "Someone is requesting a personality for a positronic brain. Would you like to play as one?", "Positronic brain request", "Yes", "No", "Never for this round")
+ if(!C || brainmob.key || 0 == searching) return //handle logouts that happen whilst the alert is waiting for a response, and responses issued after a brain has been located.
+ if(response == "Yes")
+ transfer_personality(C.mob)
+ else if (response == "Never for this round")
+ C.prefs.be_special ^= BE_PAI
- transfer_identity(var/mob/living/carbon/H)
- name = "positronic brain ([H])"
- brainmob.name = H.real_name
- brainmob.real_name = H.real_name
- brainmob.dna = H.dna
- brainmob.timeofhostdeath = H.timeofdeath
- brainmob.stat = 0
- if(brainmob.mind)
- brainmob.mind.assigned_role = "Positronic Brain"
- if(H.mind)
- H.mind.transfer_to(brainmob)
- brainmob << "\blue You feel slightly disoriented. That's normal when you're just a metal cube."
- icon_state = "posibrain-occupied"
- return
+/obj/item/device/mmi/posibrain/transfer_identity(var/mob/living/carbon/H)
+ name = "positronic brain ([H])"
+ brainmob.name = H.real_name
+ brainmob.real_name = H.real_name
+ brainmob.dna = H.dna
+ brainmob.timeofhostdeath = H.timeofdeath
+ brainmob.stat = 0
+ if(brainmob.mind)
+ brainmob.mind.assigned_role = "Positronic Brain"
+ if(H.mind)
+ H.mind.transfer_to(brainmob)
+ brainmob << "\blue You feel slightly disoriented. That's normal when you're just a metal cube."
+ icon_state = "posibrain-occupied"
+ return
- proc/transfer_personality(var/mob/candidate)
+/obj/item/device/mmi/posibrain/proc/transfer_personality(var/mob/candidate)
- src.searching = 0
- src.brainmob.mind = candidate.mind
- //src.brainmob.key = candidate.key
- src.brainmob.ckey = candidate.ckey
- src.name = "positronic brain ([src.brainmob.name])"
+ src.searching = 0
+ src.brainmob.mind = candidate.mind
+ src.brainmob.ckey = candidate.ckey
+ src.name = "positronic brain ([src.brainmob.name])"
+ src.brainmob << "You are a positronic brain, brought into existence on [station_name()]."
+ src.brainmob << "As a synthetic intelligence, you answer to all crewmembers, as well as the AI."
+ src.brainmob << "Remember, the purpose of your existence is to serve the crew and the station. Above all else, do no harm."
+ src.brainmob << "Use say :b to speak to other artificial intelligences."
+ src.brainmob.mind.assigned_role = "Positronic Brain"
- src.brainmob << "You are a positronic brain, brought into existence on [station_name()]."
- src.brainmob << "As a synthetic intelligence, you answer to all crewmembers, as well as the AI."
- src.brainmob << "Remember, the purpose of your existence is to serve the crew and the station. Above all else, do no harm."
- src.brainmob << "Use say :b to speak to other artificial intelligences."
- src.brainmob.mind.assigned_role = "Positronic Brain"
+ var/turf/T = get_turf_or_move(src.loc)
+ for (var/mob/M in viewers(T))
+ M.show_message("\blue The positronic brain chimes quietly.")
+ icon_state = "posibrain-occupied"
- var/turf/T = get_turf_or_move(src.loc)
- for (var/mob/M in viewers(T))
- M.show_message("\blue The positronic brain chimes quietly.")
- icon_state = "posibrain-occupied"
+/obj/item/device/mmi/posibrain/proc/reset_search() //We give the players sixty seconds to decide, then reset the timer.
- proc/reset_search() //We give the players sixty seconds to decide, then reset the timer.
+ if(src.brainmob && src.brainmob.key) return
- if(src.brainmob && src.brainmob.key) return
+ src.searching = 0
+ icon_state = "posibrain"
- src.searching = 0
- icon_state = "posibrain"
-
- var/turf/T = get_turf_or_move(src.loc)
- for (var/mob/M in viewers(T))
- M.show_message("\blue The positronic brain buzzes quietly, and the golden lights fade away. Perhaps you could try again?")
+ var/turf/T = get_turf_or_move(src.loc)
+ for (var/mob/M in viewers(T))
+ M.show_message("\blue The positronic brain buzzes quietly, and the golden lights fade away. Perhaps you could try again?")
/obj/item/device/mmi/posibrain/examine()
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index bd1cddb60ff..575996a84f7 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -122,6 +122,11 @@
"\red You feel a mild shock course through your body.", \
"\red You hear a light zapping." \
)
+
+ var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
+ s.set_up(5, 1, loc)
+ s.start()
+
return shock_damage
@@ -475,9 +480,10 @@
return
if(B.chemicals >= 100)
- src << "\red Your host twitches and quivers as you rapdly excrete several larvae from your sluglike body."
+ src << "\red Your host twitches and quivers as you rapidly excrete a larva from your sluglike body."
visible_message("\red [src] heaves violently, expelling a rush of vomit and a wriggling, sluglike creature!")
B.chemicals -= 100
+ B.has_reproduced = 1
new /obj/effect/decal/cleanable/vomit(get_turf(src))
playsound(loc, 'sound/effects/splat.ogg', 50, 1)
diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm
index d4050c06b85..75eff694fac 100644
--- a/code/modules/mob/living/carbon/human/emote.dm
+++ b/code/modules/mob/living/carbon/human/emote.dm
@@ -584,4 +584,38 @@
set desc = "Sets an extended description of your character's features."
set category = "IC"
- flavor_text = copytext(sanitize(input(usr, "Please enter your new flavour text.", "Flavour text", null) as text), 1)
+ var/HTML = ""
+ HTML += "
"
+ HTML += "Update Flavour Text"
+ HTML += "
"
+ HTML += "General: "
+ HTML += TextPreview(flavor_texts["general"])
+ HTML += " "
+ HTML += "Head: "
+ HTML += TextPreview(flavor_texts["head"])
+ HTML += " "
+ HTML += "Face: "
+ HTML += TextPreview(flavor_texts["face"])
+ HTML += " "
+ HTML += "Eyes: "
+ HTML += TextPreview(flavor_texts["eyes"])
+ HTML += " "
+ HTML += "Body: "
+ HTML += TextPreview(flavor_texts["torso"])
+ HTML += " "
+ HTML += "Arms: "
+ HTML += TextPreview(flavor_texts["arms"])
+ HTML += " "
+ HTML += "Hands: "
+ HTML += TextPreview(flavor_texts["hands"])
+ HTML += " "
+ HTML += "Legs: "
+ HTML += TextPreview(flavor_texts["legs"])
+ HTML += " "
+ HTML += "Feet: "
+ HTML += TextPreview(flavor_texts["feet"])
+ HTML += " "
+ HTML += ""
+ HTML +="\[Done\]"
+ HTML += ""
+ src << browse(HTML, "window=flavor_changes;size=430x300")
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index e80e7f63ed1..b250884e9f7 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -317,6 +317,16 @@
if(armor >= 2) return
+/mob/living/carbon/human/proc/implant_loyalty(mob/living/carbon/human/M, override = FALSE) // Won't override by default.
+ if(!config.use_loyalty_implants && !override) return // Nuh-uh.
+
+ var/obj/item/weapon/implant/loyalty/L = new/obj/item/weapon/implant/loyalty(M)
+ L.imp_in = M
+ L.implanted = 1
+ var/datum/organ/external/affected = M.organs_by_name["head"]
+ affected.implants += L
+ L.part = affected
+
/mob/living/carbon/human/proc/is_loyalty_implanted(mob/living/carbon/human/M)
for(var/L in M.contents)
if(istype(L, /obj/item/weapon/implant/loyalty))
@@ -336,7 +346,7 @@
var/damage = rand(1, 3)
- if(istype(M, /mob/living/carbon/slime/adult))
+ if(M.is_adult)
damage = rand(10, 35)
else
damage = rand(5, 25)
@@ -440,7 +450,7 @@
// called when something steps onto a human
// this handles mulebots and vehicles
-/mob/living/carbon/human/HasEntered(var/atom/movable/AM)
+/mob/living/carbon/human/Crossed(var/atom/movable/AM)
if(istype(AM, /obj/machinery/bot/mulebot))
var/obj/machinery/bot/mulebot/MB = AM
MB.RunOver(src)
@@ -822,6 +832,27 @@
if (href_list["lookmob"])
var/mob/M = locate(href_list["lookmob"])
M.examine()
+
+ if (href_list["flavor_change"])
+ switch(href_list["flavor_change"])
+ if("done")
+ src << browse(null, "window=flavor_changes")
+ return
+ if("general")
+ var/msg = input(usr,"Update the general description of your character. This will be shown regardless of clothing, and may include OOC notes and preferences.","Flavor Text",html_decode(flavor_texts[href_list["flavor_change"]])) as message
+ if(msg != null)
+ msg = copytext(msg, 1, MAX_MESSAGE_LEN)
+ msg = html_encode(msg)
+ flavor_texts[href_list["flavor_change"]] = msg
+ return
+ else
+ var/msg = input(usr,"Update the flavor text for your [href_list["flavor_change"]].","Flavor Text",html_decode(flavor_texts[href_list["flavor_change"]])) as message
+ if(msg != null)
+ msg = copytext(msg, 1, MAX_MESSAGE_LEN)
+ msg = html_encode(msg)
+ flavor_texts[href_list["flavor_change"]] = msg
+ set_flavor()
+ return
..()
return
@@ -1498,3 +1529,41 @@
return
H << "\red Your nose begins to bleed..."
H.drip(1)
+
+/mob/living/carbon/human/print_flavor_text()
+ var/list/equipment = list(src.head,src.wear_mask,src.glasses,src.w_uniform,src.wear_suit,src.gloves,src.shoes)
+ var/head_exposed = 1
+ var/face_exposed = 1
+ var/eyes_exposed = 1
+ var/torso_exposed = 1
+ var/arms_exposed = 1
+ var/legs_exposed = 1
+ var/hands_exposed = 1
+ var/feet_exposed = 1
+
+ for(var/obj/item/clothing/C in equipment)
+ if(C.body_parts_covered & HEAD)
+ head_exposed = 0
+ if(C.body_parts_covered & FACE)
+ face_exposed = 0
+ if(C.body_parts_covered & EYES)
+ eyes_exposed = 0
+ if(C.body_parts_covered & UPPER_TORSO)
+ torso_exposed = 0
+ if(C.body_parts_covered & ARMS)
+ arms_exposed = 0
+ if(C.body_parts_covered & HANDS)
+ hands_exposed = 0
+ if(C.body_parts_covered & LEGS)
+ legs_exposed = 0
+ if(C.body_parts_covered & FEET)
+ feet_exposed = 0
+
+ flavor_text = flavor_texts["general"]
+ flavor_text += "\n\n"
+ for (var/T in flavor_texts)
+ if(flavor_texts[T] != "")
+ if((T == "head" && head_exposed) || (T == "face" && face_exposed) || (T == "eyes" && eyes_exposed) || (T == "torso" && torso_exposed) || (T == "arms" && arms_exposed) || (T == "hands" && hands_exposed) || (T == "legs" && legs_exposed) || (T == "feet" && feet_exposed))
+ flavor_text += flavor_texts[T]
+ flavor_text += "\n\n"
+ return ..()
diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human_damage.dm
index 570e141c651..01caff57e5a 100644
--- a/code/modules/mob/living/carbon/human/human_damage.dm
+++ b/code/modules/mob/living/carbon/human/human_damage.dm
@@ -165,6 +165,10 @@
hud_updateflag |= 1 << HEALTH_HUD
updatehealth()
+
+/*
+In most cases it makes more sense to use apply_damage() instead! And make sure to check armour if applicable.
+*/
//Damages ONE external organ, organ gets randomly selected from damagable ones.
//It automatically updates damage overlays if necesary
//It automatically updates health status
@@ -259,13 +263,21 @@ This function restores all organs.
/mob/living/carbon/human/apply_damage(var/damage = 0, var/damagetype = BRUTE, var/def_zone = null, var/blocked = 0, var/sharp = 0, var/edge = 0, var/obj/used_weapon = null)
- handle_suit_punctures(damagetype, damage)
-
//visible_message("Hit debug. [damage] | [damagetype] | [def_zone] | [blocked] | [sharp] | [used_weapon]")
+
+ //Handle other types of damage
if((damagetype != BRUTE) && (damagetype != BURN))
+ if(damagetype == HALLOSS)
+ if ((damage > 25 && prob(20)) || (damage > 50 && prob(60)))
+ emote("scream")
+
+
..(damage, damagetype, def_zone, blocked)
return 1
+ //Handle BRUTE and BURN damage
+ handle_suit_punctures(damagetype, damage)
+
if(blocked >= 2) return 0
var/datum/organ/external/organ = null
@@ -296,19 +308,4 @@ This function restores all organs.
// Will set our damageoverlay icon to the next level, which will then be set back to the normal level the next mob.Life().
updatehealth()
hud_updateflag |= 1 << HEALTH_HUD
-
- //Embedded object code.
- if(!organ) return
- if(istype(used_weapon,/obj/item))
- var/obj/item/W = used_weapon
- if (!W.is_robot_module())
- //blunt objects should really not be embedding in things unless a huge amount of force is involved
- var/embed_chance = sharp? damage/W.w_class : damage/(W.w_class*3)
- var/embed_threshold = sharp? 5*W.w_class : 15*W.w_class
-
- //Sharp objects will always embed if they do enough damage.
- //Thrown objects have some momentum already and have a small chance to embed even if the damage is below the threshold
- if((sharp && damage > (10*W.w_class)) || (sharp && !ismob(W.loc) && prob(damage/(10*W.w_class)*100)) || (damage > embed_threshold && prob(embed_chance)))
- organ.embed(W)
-
return 1
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index 86f8e6eb67e..7406c693b53 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -10,23 +10,14 @@ emp_act
/mob/living/carbon/human/bullet_act(var/obj/item/projectile/P, var/def_zone)
-// BEGIN TASER NERF
- /* Commenting out new-old taser nerf.
- if(C.siemens_coefficient == 0) //If so, is that clothing shock proof?
- if(prob(deflectchance))
- visible_message("\red The [P.name] gets deflected by [src]'s [C.name]!") //DEFLECT!
- visible_message("\red Taser hit for [P.damage] damage!")
- del P
-*/
-/* Commenting out old Taser nerf
- if(wear_suit && istype(wear_suit, /obj/item/clothing/suit/armor))
- if(istype(P, /obj/item/projectile/energy/electrode))
- visible_message("\red The [P.name] gets deflected by [src]'s [wear_suit.name]!")
- del P
- return -1
-*/
-// END TASER NERF
+ var/datum/organ/external/organ = get_organ(check_zone(def_zone))
+ //Shields
+ if(check_shields(P.damage, "the [P.name]"))
+ P.on_hit(src, 2, def_zone)
+ return 2
+
+ //Laserproof armour
if(wear_suit && istype(wear_suit, /obj/item/clothing/suit/armor/laserproof))
if(istype(P, /obj/item/projectile/energy) || istype(P, /obj/item/projectile/beam))
var/reflectchance = 40 - round(P.damage/3)
@@ -51,52 +42,49 @@ emp_act
return -1 // complete projectile permutation
-//BEGIN BOOK'S TASER NERF.
- if(istype(P, /obj/item/projectile/beam/stun))
- var/datum/organ/external/select_area = get_organ(def_zone) // We're checking the outside, buddy!
- P.agony *= get_siemens_coefficient_organ(select_area)
- apply_effect(P.agony,AGONY,0)
- flash_pain()
- src <<"\red You have been shot!"
- del P
-
- var/obj/item/weapon/cloaking_device/C = locate((/obj/item/weapon/cloaking_device) in src)
- if(C && C.active)
- C.attack_self(src)//Should shut it off
- update_icons()
- src << "\blue Your [C.name] was disrupted!"
- Stun(2)
-
- if(istype(equipped(),/obj/item/device/assembly/signaler))
- var/obj/item/device/assembly/signaler/signaler = equipped()
- if(signaler.deadman && prob(80))
- src.visible_message("\red [src] triggers their deadman's switch!")
- signaler.signal()
-
- return
-//END TASER NERF
-
- if(check_shields(P.damage, "the [P.name]"))
- P.on_hit(src, 2, def_zone)
- return 2
-
- var/datum/organ/external/organ = get_organ(check_zone(def_zone))
-
- var/armor = getarmor_organ(organ, "bullet")
-
- if((P.embed && prob(20 + max(P.damage - armor, -10))) && P.damage_type == BRUTE)
- var/obj/item/weapon/shard/shrapnel/SP = new()
- (SP.name) = "[P.name] shrapnel"
- (SP.desc) = "[SP.desc] It looks like it was fired from [P.shot_from]."
- (SP.loc) = organ
- organ.embed(SP)
+ //Shrapnel
+ if (P.damage_type == BRUTE)
+ var/armor = getarmor_organ(organ, "bullet")
+ if((P.embed && prob(20 + max(P.damage - armor, -10))))
+ var/obj/item/weapon/shard/shrapnel/SP = new()
+ (SP.name) = "[P.name] shrapnel"
+ (SP.desc) = "[SP.desc] It looks like it was fired from [P.shot_from]."
+ (SP.loc) = organ
+ organ.embed(SP)
return (..(P , def_zone))
+/mob/living/carbon/human/stun_effect_act(var/stun_amount, var/agony_amount, var/def_zone)
+ var/datum/organ/external/affected = get_organ(check_zone(def_zone))
+ var/siemens_coeff = get_siemens_coefficient_organ(affected)
+ stun_amount *= siemens_coeff
+ agony_amount *= siemens_coeff
+
+ switch (def_zone)
+ if("head")
+ agony_amount *= 1.50
+ if("l_hand", "r_hand")
+ var/c_hand
+ if (def_zone == "l_hand")
+ c_hand = l_hand
+ else
+ c_hand = r_hand
+
+ if(c_hand && (stun_amount || agony_amount > 10))
+ msg_admin_attack("[src.name] ([src.ckey]) was disarmed by a stun effect")
+
+ u_equip(c_hand)
+ if (affected.status & ORGAN_ROBOT)
+ emote("me", 1, "drops what they were holding, their [affected.display_name] malfunctioning!")
+ else
+ var/emote_scream = pick("screams in pain and", "lets out a sharp cry and", "cries out and")
+ emote("me", 1, "[(species && species.flags & NO_PAIN) ? "" : emote_scream ] drops what they were holding in their [affected.display_name]!")
+
+ ..(stun_amount, agony_amount, def_zone)
/mob/living/carbon/human/getarmor(var/def_zone, var/type)
var/armorval = 0
- var/organnum = 0
+ var/total = 0
if(def_zone)
if(isorgan(def_zone))
@@ -106,36 +94,36 @@ emp_act
//If a specific bodypart is targetted, check how that bodypart is protected and return the value.
//If you don't specify a bodypart, it checks ALL your bodyparts for protection, and averages out the values
- for(var/datum/organ/external/organ in organs)
- armorval += getarmor_organ(organ, type)
- organnum++
- return (armorval/max(organnum, 1))
+ for(var/organ_name in organs_by_name)
+ if (organ_name in organ_rel_size)
+ var/datum/organ/external/organ = organs_by_name[organ_name]
+ var/weight = organ_rel_size[organ_name]
+ armorval += getarmor_organ(organ, type) * weight
+ total += weight
+ return (armorval/max(total, 1))
//this proc returns the Siemens coefficient of electrical resistivity for a particular external organ.
/mob/living/carbon/human/proc/get_siemens_coefficient_organ(var/datum/organ/external/def_zone)
if (!def_zone)
return 1.0
-
+
var/siemens_coefficient = 1.0
-
+
var/list/clothing_items = list(head, wear_mask, wear_suit, w_uniform, gloves, shoes) // What all are we checking?
for(var/obj/item/clothing/C in clothing_items)
- if(!istype(C)) //is this necessary?
- continue
- else if(C.body_parts_covered & def_zone.body_part) // Is that body part being targeted covered?
+ if(istype(C) && (C.body_parts_covered & def_zone.body_part)) // Is that body part being targeted covered?
siemens_coefficient *= C.siemens_coefficient
-
+
return siemens_coefficient
//this proc returns the armour value for a particular external organ.
/mob/living/carbon/human/proc/getarmor_organ(var/datum/organ/external/def_zone, var/type)
if(!type) return 0
var/protection = 0
- var/list/body_parts = list(head, wear_mask, wear_suit, w_uniform)
- for(var/bp in body_parts)
- if(!bp) continue
- if(bp && istype(bp ,/obj/item/clothing))
- var/obj/item/clothing/C = bp
+ var/list/protective_gear = list(head, wear_mask, wear_suit, w_uniform)
+ for(var/gear in protective_gear)
+ if(gear && istype(gear ,/obj/item/clothing))
+ var/obj/item/clothing/C = gear
if(C.body_parts_covered & def_zone.body_part)
protection += C.armor[type]
return protection
@@ -193,11 +181,12 @@ emp_act
..()
+//Returns 1 if the attack hit, 0 if it missed.
/mob/living/carbon/human/proc/attacked_by(var/obj/item/I, var/mob/living/user, var/def_zone)
if(!I || !user) return 0
var/target_zone = def_zone? check_zone(def_zone) : get_zone_with_miss_chance(user.zone_sel.selecting, src)
-
+
if(user == src) // Attacking yourself can't miss
target_zone = user.zone_sel.selecting
if(!target_zone)
@@ -287,6 +276,20 @@ emp_act
if(bloody)
bloody_body(src)
+
+ //Melee weapon embedded object code.
+ if (I.damtype == BRUTE && !I.is_robot_module())
+ var/damage = I.force
+ if (armor)
+ damage /= armor+1
+
+ //blunt objects should really not be embedding in things unless a huge amount of force is involved
+ var/embed_chance = weapon_sharp? damage/I.w_class : damage/(I.w_class*3)
+ var/embed_threshold = weapon_sharp? 5*I.w_class : 15*I.w_class
+
+ //Sharp objects will always embed if they do enough damage.
+ if((weapon_sharp && damage > (10*I.w_class)) || (damage > embed_threshold && prob(embed_chance)))
+ affecting.embed(I)
return 1
//this proc handles being hit by a thrown atom
@@ -298,14 +301,14 @@ emp_act
var/obj/item/weapon/W = O
dtype = W.damtype
var/throw_damage = O.throwforce*(speed/5)
-
+
var/zone
if (istype(O.thrower, /mob/living))
var/mob/living/L = O.thrower
zone = check_zone(L.zone_sel.selecting)
else
zone = ran_zone("chest",75) //Hits a random part of the body, geared towards the chest
-
+
//check if we hit
if (O.throw_source)
var/distance = get_dist(O.throw_source, loc)
@@ -316,15 +319,15 @@ emp_act
if(!zone)
visible_message("\blue \The [O] misses [src] narrowly!")
return
-
+
O.throwing = 0 //it hit, so stop moving
-
+
if ((O.thrower != src) && check_shields(throw_damage, "[O]"))
return
-
+
var/datum/organ/external/affecting = get_organ(zone)
var/hit_area = affecting.display_name
-
+
src.visible_message("\red [src] has been hit in the [hit_area] by [O].")
var/armor = run_armor_check(affecting, "melee", "Your armor has protected your [hit_area].", "Your armor has softened hit to your [hit_area].") //I guess "melee" is the best fit here
@@ -340,6 +343,24 @@ emp_act
if(!istype(src,/mob/living/simple_animal/mouse))
msg_admin_attack("[src.name] ([src.ckey]) was hit by a [O], thrown by [M.name] ([assailant.ckey]) (JMP)")
+ //thrown weapon embedded object code.
+ if(dtype == BRUTE && istype(O,/obj/item))
+ var/obj/item/I = O
+ if (!I.is_robot_module())
+ var/sharp = is_sharp(I)
+ var/damage = throw_damage
+ if (armor)
+ damage /= armor+1
+
+ //blunt objects should really not be embedding in things unless a huge amount of force is involved
+ var/embed_chance = sharp? damage/I.w_class : damage/(I.w_class*3)
+ var/embed_threshold = sharp? 5*I.w_class : 15*I.w_class
+
+ //Sharp objects will always embed if they do enough damage.
+ //Thrown sharp objects have some momentum already and have a small chance to embed even if the damage is below the threshold
+ if((sharp && prob(damage/(10*I.w_class)*100)) || (damage > embed_threshold && prob(embed_chance)))
+ affecting.embed(I)
+
// Begin BS12 momentum-transfer code.
if(O.throw_source && speed >= 15)
var/obj/item/weapon/W = O
@@ -360,6 +381,7 @@ emp_act
src.anchored = 1
src.pinned += O
+
/mob/living/carbon/human/proc/bloody_hands(var/mob/living/source, var/amount = 2)
if (gloves)
gloves.add_blood(source)
diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm
index d5db0fdbc7e..818b9a9e7e0 100644
--- a/code/modules/mob/living/carbon/human/human_defines.dm
+++ b/code/modules/mob/living/carbon/human/human_defines.dm
@@ -70,3 +70,5 @@
var/mob/remoteview_target = null
var/hand_blood_color
+
+ var/list/flavor_texts = list()
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm
index b1e6af0cedd..6bbfce36de7 100644
--- a/code/modules/mob/living/carbon/human/inventory.dm
+++ b/code/modules/mob/living/carbon/human/inventory.dm
@@ -671,10 +671,20 @@ It can still be worn/put on as normal.
slot_to_process = slot_back
if (target.back)
strip_item = target.back
- if("handcuff")
+ if("handcuff")
slot_to_process = slot_handcuffed
if (target.handcuffed)
strip_item = target.handcuffed
+ else if (source != target && ishuman(source))
+ //check that we are still grabbing them
+ var/grabbing = 0
+ for (var/obj/item/weapon/grab/G in target.grabbed_by)
+ if (G.loc == source && G.state >= GRAB_AGGRESSIVE)
+ grabbing = 1
+ if (!grabbing)
+ slot_to_process = null
+ source << "\red Your grasp was broken before you could restrain [target]!"
+
if("legcuff")
slot_to_process = slot_legcuffed
if (target.legcuffed)
diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm
index 337bf4befb1..57484c001a2 100644
--- a/code/modules/mob/living/carbon/human/life.dm
+++ b/code/modules/mob/living/carbon/human/life.dm
@@ -21,6 +21,8 @@
#define COLD_GAS_DAMAGE_LEVEL_2 1.5 //Amount of damage applied when the current breath's temperature passes the 200K point
#define COLD_GAS_DAMAGE_LEVEL_3 3 //Amount of damage applied when the current breath's temperature passes the 120K point
+#define RADIATION_SPEED_COEFFICIENT 0.1
+
/mob/living/carbon/human
var/oxygen_alert = 0
var/phoron_alert = 0
@@ -274,63 +276,54 @@
speech_problem_flag = 1
gene.OnMobLife(src)
+ radiation = Clamp(radiation,0,100)
+
if (radiation)
- if (radiation > 100)
- radiation = 100
- if(!(species.flags & RAD_ABSORB))
- Weaken(10)
+ if(species.flags & RAD_ABSORB)
+ var/rads = radiation/25
+ radiation -= rads
+ nutrition += rads
+ adjustBruteLoss(-(rads))
+ adjustOxyLoss(-(rads))
+ adjustToxLoss(-(rads))
+ updatehealth()
+ return
+
+ var/damage = 0
+ radiation -= 1 * RADIATION_SPEED_COEFFICIENT
+ if(prob(25))
+ damage = 1
+
+ if (radiation > 50)
+ damage = 1
+ radiation -= 1 * RADIATION_SPEED_COEFFICIENT
+ if(prob(5) && prob(100 * RADIATION_SPEED_COEFFICIENT))
+ radiation -= 5 * RADIATION_SPEED_COEFFICIENT
+ src << "You feel weak."
+ Weaken(3)
if(!lying)
- src << "\red You feel weak."
emote("collapse")
+ if(prob(5) && prob(100 * RADIATION_SPEED_COEFFICIENT) && species.name == "Human") //apes go bald
+ if((h_style != "Bald" || f_style != "Shaved" ))
+ src << "Your hair falls out."
+ h_style = "Bald"
+ f_style = "Shaved"
+ update_hair()
- if (radiation < 0)
- radiation = 0
+ if (radiation > 75)
+ radiation -= 1 * RADIATION_SPEED_COEFFICIENT
+ damage = 3
+ if(prob(5))
+ take_overall_damage(0, 5 * RADIATION_SPEED_COEFFICIENT, used_weapon = "Radiation Burns")
+ if(prob(1))
+ src << "You feel strange!"
+ adjustCloneLoss(5 * RADIATION_SPEED_COEFFICIENT)
+ emote("gasp")
- else
-
- if(species.flags & RAD_ABSORB)
- var/rads = radiation/25
- radiation -= rads
- nutrition += rads
- adjustBruteLoss(-(rads))
- adjustOxyLoss(-(rads))
- adjustToxLoss(-(rads))
- updatehealth()
- return
-
- var/damage = 0
- switch(radiation)
- if(1 to 49)
- radiation--
- if(prob(25))
- adjustToxLoss(1)
- damage = 1
- updatehealth()
-
- if(50 to 74)
- radiation -= 2
- damage = 1
- adjustToxLoss(1)
- if(prob(5))
- radiation -= 5
- Weaken(3)
- if(!lying)
- src << "\red You feel weak."
- emote("collapse")
- updatehealth()
-
- if(75 to 100)
- radiation -= 3
- adjustToxLoss(3)
- damage = 1
- if(prob(1))
- src << "\red You mutate!"
- randmutb(src)
- domutcheck(src,null)
- emote("gasp")
- updatehealth()
-
- if(damage && organs.len)
+ if(damage)
+ adjustToxLoss(damage * RADIATION_SPEED_COEFFICIENT)
+ updatehealth()
+ if(organs.len)
var/datum/organ/external/O = pick(organs)
if(istype(O)) O.add_autopsy_data("Radiation Poisoning", damage)
@@ -370,26 +363,15 @@
breath_moles = (ONE_ATMOSPHERE*BREATH_VOLUME/R_IDEAL_GAS_EQUATION*environment.temperature)
else*/
// Not enough air around, take a percentage of what's there to model this properly
- breath_moles = environment.total_moles()*BREATH_PERCENTAGE
+ breath_moles = environment.total_moles*BREATH_PERCENTAGE
breath = loc.remove_air(breath_moles)
- if(istype(wear_mask, /obj/item/clothing/mask/gas) && breath)
- var/obj/item/clothing/mask/gas/G = wear_mask
- var/datum/gas_mixture/filtered = new
-
- filtered.copy_from(breath)
- filtered.phoron *= G.gas_filter_strength
- for(var/datum/gas/gas in filtered.trace_gases)
- gas.moles *= G.gas_filter_strength
- filtered.update_values()
+ if(istype(wear_mask, /obj/item/clothing/mask) && breath)
+ var/obj/item/clothing/mask/M = wear_mask
+ var/datum/gas_mixture/filtered = M.filter_air(breath)
loc.assume_air(filtered)
- breath.phoron *= 1 - G.gas_filter_strength
- for(var/datum/gas/gas in breath.trace_gases)
- gas.moles *= 1 - G.gas_filter_strength
- breath.update_values()
-
if(!is_lung_ruptured())
if(!breath || breath.total_moles < BREATH_MOLES / 5 || breath.total_moles > BREATH_MOLES * 5)
if(prob(5))
@@ -452,7 +434,7 @@
if(status_flags & GODMODE)
return
- if(!breath || (breath.total_moles() == 0) || suiciding)
+ if(!breath || (breath.total_moles == 0) || suiciding)
if(suiciding)
adjustOxyLoss(2)//If you are suiciding, you should die a little bit faster
failed_last_breath = 1
@@ -471,165 +453,130 @@
var/safe_pressure_min = 16 // Minimum safe partial pressure of breathable gas in kPa
//var/safe_pressure_max = 140 // Maximum safe partial pressure of breathable gas in kPa (Not used for now)
- var/safe_exhaled_max = 10 // Yes it's an arbitrary value who cares?
+ var/safe_exhaled_max = 10
var/safe_toxins_max = 0.005
var/SA_para_min = 1
var/SA_sleep_min = 5
var/inhaled_gas_used = 0
- var/breath_pressure = (breath.total_moles()*R_IDEAL_GAS_EQUATION*breath.temperature)/BREATH_VOLUME
+ var/breath_pressure = (breath.total_moles*R_IDEAL_GAS_EQUATION*breath.temperature)/BREATH_VOLUME
var/inhaling
- var/exhaling
var/poison
- var/no_exhale
+ var/exhaling
+
+ var/breath_type
+ var/poison_type
+ var/exhale_type
var/failed_inhale = 0
var/failed_exhale = 0
- switch(species.breath_type)
- if("nitrogen")
- inhaling = breath.nitrogen
- if("phoron")
- inhaling = breath.phoron
- if("carbon_dioxide")
- inhaling = breath.carbon_dioxide
- else
- inhaling = breath.oxygen
+ if(species.breath_type)
+ breath_type = species.breath_type
+ else
+ breath_type = "oxygen"
+ inhaling = breath.gas[breath_type]
- switch(species.poison_type)
- if("oxygen")
- poison = breath.oxygen
- if("nitrogen")
- poison = breath.nitrogen
- if("carbon_dioxide")
- poison = breath.carbon_dioxide
- else
- poison = breath.phoron
+ if(species.poison_type)
+ poison_type = species.poison_type
+ else
+ poison_type = "phoron"
+ poison = breath.gas[poison_type]
- switch(species.exhale_type)
- if("carbon_dioxide")
- exhaling = breath.carbon_dioxide
- if("oxygen")
- exhaling = breath.oxygen
- if("nitrogen")
- exhaling = breath.nitrogen
- if("phoron")
- exhaling = breath.phoron
- else
- no_exhale = 1
+ if(species.exhale_type)
+ exhale_type = species.exhale_type
+ exhaling = breath.gas[exhale_type]
+ else
+ exhaling = 0
- var/inhale_pp = (inhaling/breath.total_moles())*breath_pressure
- var/toxins_pp = (poison/breath.total_moles())*breath_pressure
- var/exhaled_pp = (exhaling/breath.total_moles())*breath_pressure
+ var/inhale_pp = (inhaling/breath.total_moles)*breath_pressure
+ var/toxins_pp = (poison/breath.total_moles)*breath_pressure
+ var/exhaled_pp = (exhaling/breath.total_moles)*breath_pressure
// Not enough to breathe
if(inhale_pp < safe_pressure_min)
if(prob(20))
spawn(0) emote("gasp")
- if(inhale_pp > 0)
- var/ratio = inhale_pp/safe_pressure_min
- // Don't fuck them up too fast (space only does HUMAN_MAX_OXYLOSS after all!)
- // The hell? By definition ratio > 1, and HUMAN_MAX_OXYLOSS = 1... why do we even have this?
- adjustOxyLoss(min(5*ratio, HUMAN_MAX_OXYLOSS))
- failed_inhale = 1
- inhaled_gas_used = inhaling*ratio/6
-
- else
-
- adjustOxyLoss(HUMAN_MAX_OXYLOSS)
- failed_inhale = 1
+ var/ratio = inhale_pp/safe_pressure_min
+ // Don't fuck them up too fast (space only does HUMAN_MAX_OXYLOSS after all!)
+ adjustOxyLoss(max(HUMAN_MAX_OXYLOSS*(1-ratio), 0))
+ failed_inhale = 1
oxygen_alert = max(oxygen_alert, 1)
-
else
// We're in safe limits
- inhaled_gas_used = inhaling/6
oxygen_alert = 0
- switch(species.breath_type)
- if("nitrogen")
- breath.nitrogen -= inhaled_gas_used
- if("phoron")
- breath.phoron -= inhaled_gas_used
- if("carbon_dioxide")
- breath.carbon_dioxide-= inhaled_gas_used
- else
- breath.oxygen -= inhaled_gas_used
+ inhaled_gas_used = inhaling/6
- if(!no_exhale)
- switch(species.exhale_type)
- if("oxygen")
- breath.oxygen += inhaled_gas_used
- if("nitrogen")
- breath.nitrogen += inhaled_gas_used
- if("phoron")
- breath.phoron += inhaled_gas_used
- if("carbon_dioxide")
- breath.carbon_dioxide += inhaled_gas_used
+ breath.adjust_gas(breath_type, -inhaled_gas_used, update = 0) //update afterwards
- // Too much exhaled gas in the air
- if(exhaled_pp > safe_exhaled_max)
- if (!co2_alert|| prob(15))
- var/word = pick("extremely dizzy","short of breath","faint","confused")
- src << "\red You feel [word]."
+ if(exhale_type)
+ breath.adjust_gas_temp(exhale_type, inhaled_gas_used, bodytemperature, update = 0) //update afterwards
- adjustOxyLoss(HUMAN_MAX_OXYLOSS)
- co2_alert = 1
- failed_exhale = 1
+ // Too much exhaled gas in the air
+ if(exhaled_pp > safe_exhaled_max)
+ if (!co2_alert|| prob(15))
+ var/word = pick("extremely dizzy","short of breath","faint","confused")
+ src << "You feel [word]."
- else if(exhaled_pp > safe_exhaled_max * 0.7)
- if (!co2_alert || prob(1))
- var/word = pick("dizzy","short of breath","faint","momentarily confused")
- src << "\red You feel [word]."
-
- //scale linearly from 0 to 1 between safe_exhaled_max and safe_exhaled_max*0.7
- var/ratio = 1.0 - (safe_exhaled_max - exhaled_pp)/(safe_exhaled_max*0.3)
-
- //give them some oxyloss, up to the limit - we don't want people falling unconcious due to CO2 alone until they're pretty close to safe_exhaled_max.
- if (getOxyLoss() < 50*ratio)
adjustOxyLoss(HUMAN_MAX_OXYLOSS)
- co2_alert = 1
- failed_exhale = 1
+ co2_alert = 1
+ failed_exhale = 1
- else if(exhaled_pp > safe_exhaled_max * 0.6)
- if (prob(0.3))
- var/word = pick("a little dizzy","short of breath")
- src << "\red You feel [word]."
+ else if(exhaled_pp > safe_exhaled_max * 0.7)
+ if (!co2_alert || prob(1))
+ var/word = pick("dizzy","short of breath","faint","momentarily confused")
+ src << " safe_exhaled_max * 0.6)
+ if (prob(0.3))
+ var/word = pick("a little dizzy","short of breath")
+ src << " SA_para_min)
+ // Enough to make us paralysed for a bit
+ if(SA_pp > SA_para_min)
- // 3 gives them one second to wake up and run away a bit!
- Paralyse(3)
+ // 3 gives them one second to wake up and run away a bit!
+ Paralyse(3)
- // Enough to make us sleep as well
- if(SA_pp > SA_sleep_min)
- sleeping = min(sleeping+2, 10)
+ // Enough to make us sleep as well
+ if(SA_pp > SA_sleep_min)
+ sleeping = min(sleeping+2, 10)
- // There is sleeping gas in their lungs, but only a little, so give them a bit of a warning
- else if(SA_pp > 0.15)
- if(prob(20))
- spawn(0) emote(pick("giggle", "laugh"))
- SA.moles = 0
+ // There is sleeping gas in their lungs, but only a little, so give them a bit of a warning
+ else if(SA_pp > 0.15)
+ if(prob(20))
+ spawn(0) emote(pick("giggle", "laugh"))
+ breath.adjust_gas("sleeping_agent", -breath.gas["sleeping_agent"]/6, update = 0) //update after
// Were we able to breathe?
if (failed_inhale || failed_exhale)
@@ -641,15 +588,12 @@
// Hot air hurts :(
if( (breath.temperature < species.cold_level_1 || breath.temperature > species.heat_level_1) && !(COLD_RESISTANCE in mutations))
- if(status_flags & GODMODE)
- return 1
-
if(breath.temperature < species.cold_level_1)
if(prob(20))
- src << "\red You feel your face freezing and icicles forming in your lungs!"
+ src << "You feel your face freezing and icicles forming in your lungs!"
else if(breath.temperature > species.heat_level_1)
if(prob(20))
- src << "\red You feel your face burning and a searing heat in your lungs!"
+ src << "You feel your face burning and a searing heat in your lungs!"
switch(breath.temperature)
if(-INFINITY to species.cold_level_3)
@@ -678,13 +622,16 @@
else
temp_adj /= (BODYTEMP_HEAT_DIVISOR * 5) //don't raise temperature as much as if we were directly exposed
- var/relative_density = breath.total_moles() / (MOLES_CELLSTANDARD * BREATH_PERCENTAGE)
+ var/relative_density = breath.total_moles / (MOLES_CELLSTANDARD * BREATH_PERCENTAGE)
temp_adj *= relative_density
if (temp_adj > BODYTEMP_HEATING_MAX) temp_adj = BODYTEMP_HEATING_MAX
if (temp_adj < BODYTEMP_COOLING_MAX) temp_adj = BODYTEMP_COOLING_MAX
//world << "Breath: [breath.temperature], [src]: [bodytemperature], Adjusting: [temp_adj]"
bodytemperature += temp_adj
+
+
+ breath.update_values()
return 1
proc/handle_environment(datum/gas_mixture/environment)
@@ -695,6 +642,12 @@
var/pressure = environment.return_pressure()
var/adjusted_pressure = calculate_affecting_pressure(pressure)
+ //Check for contaminants before anything else because we don't want to skip it.
+ for(var/g in environment.gas)
+ if(gas_data.flags[g] & XGM_GAS_CONTAMINANT && environment.gas[g] > gas_data.overlay_limit[g] + 1)
+ pl_effects()
+ break
+
if(!istype(get_turf(src), /turf/space)) //space is not meant to change your body temperature.
var/loc_temp = T0C
if(istype(loc, /obj/mecha))
@@ -705,7 +658,7 @@
else
loc_temp = environment.temperature
- if(adjusted_pressure < species.warning_high_pressure && adjusted_pressure > species.warning_low_pressure && abs(loc_temp - bodytemperature) < 20 && bodytemperature < species.heat_level_1 && bodytemperature > species.cold_level_1 && environment.phoron < MOLES_PHORON_VISIBLE)
+ if(adjusted_pressure < species.warning_high_pressure && adjusted_pressure > species.warning_low_pressure && abs(loc_temp - bodytemperature) < 20 && bodytemperature < species.heat_level_1 && bodytemperature > species.cold_level_1)
pressure_alert = 0
return // Temperatures are within normal ranges, fuck all this processing. ~Ccomp
@@ -721,7 +674,7 @@
temp_adj = (1-thermal_protection) * ((loc_temp - bodytemperature) / BODYTEMP_HEAT_DIVISOR)
//Use heat transfer as proportional to the gas density. However, we only care about the relative density vs standard 101 kPa/20 C air. Therefore we can use mole ratios
- var/relative_density = environment.total_moles() / MOLES_CELLSTANDARD
+ var/relative_density = environment.total_moles / MOLES_CELLSTANDARD
temp_adj *= relative_density
if (temp_adj > BODYTEMP_HEATING_MAX) temp_adj = BODYTEMP_HEATING_MAX
@@ -781,8 +734,6 @@
else
pressure_alert = -1
- if(environment.phoron > MOLES_PHORON_VISIBLE)
- pl_effects()
return
/*
@@ -1230,25 +1181,7 @@
ear_damage = max(ear_damage-0.05, 0)
//Other
- if(stunned)
- speech_problem_flag = 1
- AdjustStunned(-1)
-
- if(weakened)
- weakened = max(weakened-1,0) //before you get mad Rockdtben: I done this so update_canmove isn't called multiple times
-
- if(stuttering)
- speech_problem_flag = 1
- stuttering = max(stuttering-1, 0)
- if (slurring)
- speech_problem_flag = 1
- slurring = max(slurring-1, 0)
- if(silent)
- speech_problem_flag = 1
- silent = max(silent-1, 0)
-
- if(druggy)
- druggy = max(druggy-1, 0)
+ handle_statuses()
// If you're dirty, your gloves will become dirty, too.
if(gloves && germ_level > gloves.germ_level && prob(10))
@@ -1871,6 +1804,25 @@
hud_list[SPECIALROLE_HUD] = holder
hud_updateflag = 0
+/mob/living/carbon/human/handle_silent()
+ if(..())
+ speech_problem_flag = 1
+ return silent
+
+/mob/living/carbon/human/handle_slurring()
+ if(..())
+ speech_problem_flag = 1
+ return slurring
+
+/mob/living/carbon/human/handle_stunned()
+ if(..())
+ speech_problem_flag = 1
+ return stunned
+
+/mob/living/carbon/human/handle_stuttering()
+ if(..())
+ speech_problem_flag = 1
+ return stuttering
#undef HUMAN_MAX_OXYLOSS
#undef HUMAN_CRIT_MAX_OXYLOSS
diff --git a/code/modules/mob/living/carbon/metroid/death.dm b/code/modules/mob/living/carbon/metroid/death.dm
index e37f3d23680..b8e004e2016 100644
--- a/code/modules/mob/living/carbon/metroid/death.dm
+++ b/code/modules/mob/living/carbon/metroid/death.dm
@@ -1,23 +1,28 @@
/mob/living/carbon/slime/death(gibbed)
+ if(!gibbed)
+ if(is_adult)
+ var/mob/living/carbon/slime/M = new /mob/living/carbon/slime(loc)
+ M.colour = colour
+ M.rabid = 1
+ is_adult = 0
+ maxHealth = 150
+ revive()
+ regenerate_icons()
+ number = rand(1, 1000)
+ name = "[colour] [is_adult ? "adult" : "baby"] slime ([number])"
+ return
+
if(stat == DEAD) return
stat = DEAD
icon_state = "[colour] baby slime dead"
-
- if(!gibbed)
- if(istype(src, /mob/living/carbon/slime/adult))
- ghostize()
- var/mob/living/carbon/slime/M1 = new primarytype(loc)
- M1.rabid = 1
- var/mob/living/carbon/slime/M2 = new primarytype(loc)
- M2.rabid = 1
- if(src) del(src)
- else
- for(var/mob/O in viewers(src, null))
- O.show_message("The [name] seizes up and falls limp...", 1) //ded -- Urist
+ overlays.len = 0
+ for(var/mob/O in viewers(src, null))
+ O.show_message("The [name] seizes up and falls limp...", 1) //ded -- Urist
update_canmove()
if(blind) blind.layer = 0
- ticker.mode.check_win()
+ if(ticker && ticker.mode)
+ ticker.mode.check_win()
return ..(gibbed)
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/metroid/emote.dm b/code/modules/mob/living/carbon/metroid/emote.dm
index 2f6f0005396..2cbc139f5a1 100644
--- a/code/modules/mob/living/carbon/metroid/emote.dm
+++ b/code/modules/mob/living/carbon/metroid/emote.dm
@@ -1,4 +1,4 @@
-/mob/living/carbon/slime/emote(var/act,var/m_type=1,var/message = null)
+/mob/living/carbon/slime/emote(var/act, var/m_type=1, var/message = null)
if (findtext(act, "-", 1, null))
@@ -9,7 +9,7 @@
if(findtext(act,"s",-1) && !findtext(act,"_",-2))//Removes ending s's unless they are prefixed with a '_'
act = copytext(act,1,length(act))
- switch(act)
+ switch(act) //Alphabetical please
if ("me")
if(silent)
return
@@ -19,40 +19,51 @@
return
if (src.client.handle_spam_prevention(message,MUTE_IC))
return
- if (stat)
- return
- if(!(message))
- return
- return custom_emote(m_type, message)
-
- if ("custom")
- return custom_emote(m_type, message)
- if("moan")
- message = "The [src.name] moans."
- m_type = 2
- if("shiver")
- message = "The [src.name] shivers."
- m_type = 2
- if("sway")
- message = "The [src.name] sways around dizzily."
- m_type = 1
- if("twitch")
- message = "The [src.name] twitches."
- m_type = 1
- if("vibrate")
- message = "The [src.name] vibrates!"
- m_type = 1
- if("light")
- message = "The [src.name] lights up for a bit, then stops."
- m_type = 1
- if("jiggle")
- message = "The [src.name] jiggles!"
- m_type = 1
+ if (stat)
+ return
+ if(!(message))
+ return
+ return custom_emote(m_type, message)
if("bounce")
message = "The [src.name] bounces in place."
m_type = 1
+
+ if ("custom")
+ return custom_emote(m_type, message)
+
+ if("jiggle")
+ message = "The [src.name] jiggles!"
+ m_type = 1
+
+ if("light")
+ message = "The [src.name] lights up for a bit, then stops."
+ m_type = 1
+
+ if("moan")
+ message = "The [src.name] moans."
+ m_type = 2
+
+ if("shiver")
+ message = "The [src.name] shivers."
+ m_type = 2
+
+ if("sway")
+ message = "The [src.name] sways around dizzily."
+ m_type = 1
+
+ if("twitch")
+ message = "The [src.name] twitches."
+ m_type = 1
+
+ if("vibrate")
+ message = "The [src.name] vibrates!"
+ m_type = 1
+
+ if ("help") //This is an exception
+ src << "Help for slime emotes. You can use these emotes with say \"*emote\":\n\nbounce, custom, jiggle, light, moan, shiver, sway, twitch, vibrate"
+
else
- src << text("Invalid Emote: []", act)
+ src << "\blue Unusable emote '[act]'. Say *help for a list."
if ((message && src.stat == 0))
if (m_type & 1)
for(var/mob/O in viewers(src, null))
diff --git a/code/modules/mob/living/carbon/metroid/hud.dm b/code/modules/mob/living/carbon/metroid/hud.dm
index c7700839067..cfaa7f4f169 100644
--- a/code/modules/mob/living/carbon/metroid/hud.dm
+++ b/code/modules/mob/living/carbon/metroid/hud.dm
@@ -1,4 +1,2 @@
-
/mob/living/carbon/slime/proc/regular_hud_updates()
- return
-
+ return
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/metroid/life.dm b/code/modules/mob/living/carbon/metroid/life.dm
index 47e9f6c675f..c82d517d590 100644
--- a/code/modules/mob/living/carbon/metroid/life.dm
+++ b/code/modules/mob/living/carbon/metroid/life.dm
@@ -1,3 +1,10 @@
+/mob/living/carbon/slime
+ var/AIproc = 0 // determines if the AI loop is activated
+ var/Atkcool = 0 // attack cooldown
+ var/Tempstun = 0 // temporary temperature stuns
+ var/Discipline = 0 // if a slime has been hit with a freeze gun, or wrestled/attacked off a human, they become disciplined and don't attack anymore for a while
+ var/SStun = 0 // stun variable
+
/mob/living/carbon/slime/Life()
set invisibility = 0
set background = 1
@@ -15,114 +22,80 @@
handle_targets()
+ if (!ckey)
+ handle_speech_and_mood()
- var/datum/gas_mixture/environment // Added to prevent null location errors-- TLE
+ var/datum/gas_mixture/environment
if(src.loc)
environment = loc.return_air()
-
//Apparently, the person who wrote this code designed it so that
//blinded get reset each cycle and then get activated later in the
//code. Very ugly. I dont care. Moving this stuff here so its easy
//to find it.
src.blinded = null
- // Basically just deletes any screen objects :<
- regular_hud_updates()
+ regular_hud_updates() // Basically just deletes any screen objects :<
- //Handle temperature/pressure differences between body and environment
if(environment)
- handle_environment(environment)
+ handle_environment(environment) // Handle temperature/pressure differences between body and environment
- //Status updates, death etc.
- handle_regular_status_updates()
-
-
-
-
-
-/mob/living/carbon/slime
- var/AIproc = 0 // determines if the AI loop is activated
- var/Atkcool = 0 // attack cooldown
- var/Tempstun = 0 // temporary temperature stuns
- var/Discipline = 0 // if a slime has been hit with a freeze gun, or wrestled/attacked off a human, they become disciplined and don't attack anymore for a while
- var/SStun = 0 // stun variable
+ handle_regular_status_updates() // Status updates, death etc.
/mob/living/carbon/slime/proc/AIprocess() // the master AI process
- //world << "AI proc started."
if(AIproc || stat == DEAD || client) return
var/hungry = 0
- var/starving = 0
- if(istype(src, /mob/living/carbon/slime/adult))
- switch(nutrition)
- if(400 to 1100) hungry = 1
- if(0 to 399)
- starving = 1
- else
- switch(nutrition)
- if(150 to 900) hungry = 1
- if(0 to 149) starving = 1
+ if (nutrition < get_starve_nutrition())
+ hungry = 2
+ else if (nutrition < get_grow_nutrition() && prob(25) || nutrition < get_hunger_nutrition())
+ hungry = 1
+
AIproc = 1
- //world << "AIproc [AIproc] && stat != 2 [stat] && (attacked > 0 [attacked] || starving [starving] || hungry [hungry] || rabid [rabid] || Victim [Victim] || Target [Target]"
- while(AIproc && stat != 2 && (attacked > 0 || starving || hungry || rabid || Victim))
+
+ while(AIproc && stat != 2 && (attacked || hungry || rabid || Victim))
if(Victim) // can't eat AND have this little process at the same time
- //world << "break 1"
break
if(!Target || client)
- //world << "break 2"
break
-
if(Target.health <= -70 || Target.stat == 2)
Target = null
AIproc = 0
- //world << "break 3"
break
if(Target)
- //world << "[Target] Target Found"
for(var/mob/living/carbon/slime/M in view(1,Target))
if(M.Victim == Target)
Target = null
AIproc = 0
- //world << "break 4"
break
if(!AIproc)
- //world << "break 5"
break
if(Target in view(1,src))
-
if(istype(Target, /mob/living/silicon))
if(!Atkcool)
- spawn()
- Atkcool = 1
- sleep(15)
+ Atkcool = 1
+ spawn(45)
Atkcool = 0
if(Target.Adjacent(src))
Target.attack_slime(src)
- //world << "retrun 1"
return
if(!Target.lying && prob(80))
if(Target.client && Target.health >= 20)
if(!Atkcool)
- spawn()
- Atkcool = 1
- sleep(25)
+ Atkcool = 1
+ spawn(45)
Atkcool = 0
if(Target.Adjacent(src))
Target.attack_slime(src)
-
- if(prob(30))
- step_to(src, Target)
-
else
if(!Atkcool && Target.Adjacent(src))
Feedon(Target)
@@ -133,13 +106,13 @@
else
if(Target in view(7, src))
- if(Target.Adjacent(src))
+ if(!Target.Adjacent(src)) // Bug of the month candidate: slimes were attempting to move to target only if it was directly next to them, which caused them to target things, but not approach them
step_to(src, Target)
+ sleep(5)
else
Target = null
AIproc = 0
- //world << "break 6"
break
var/sleeptime = movement_delay()
@@ -148,7 +121,6 @@
sleep(sleeptime + 2) // this is about as fast as a player slime can go
AIproc = 0
- //world << "AI proc ended."
/mob/living/carbon/slime/proc/handle_environment(datum/gas_mixture/environment)
if(!environment)
@@ -166,28 +138,11 @@
else
loc_temp = environment.temperature
- /*
- if((environment.temperature > (T0C + 50)) || (environment.temperature < (T0C + 10)))
- var/transfer_coefficient
-
- transfer_coefficient = 1
- if(wear_mask && (wear_mask.body_parts_covered & HEAD) && (environment.temperature < wear_mask.protective_temperature))
- transfer_coefficient *= wear_mask.heat_transfer_coefficient
-
- // handle_temperature_damage(HEAD, environment.temperature, environment_heat_capacity*transfer_coefficient)
- */
-
-
if(loc_temp < 310.15) // a cold place
bodytemperature += adjust_body_temperature(bodytemperature, loc_temp, 1)
else // a hot place
bodytemperature += adjust_body_temperature(bodytemperature, loc_temp, 1)
- /*
- if(stat==2)
- bodytemperature += 0.1*(environment.temperature - bodytemperature)*environment_heat_capacity/(environment_heat_capacity + 270000)
-
- */
//Account for massive pressure differences
if(bodytemperature < (T0C + 5)) // start calculating temperature damage etc
@@ -207,7 +162,6 @@
return //TODO: DEFERRED
-
/mob/living/carbon/slime/proc/adjust_body_temperature(current, loc_temp, boost)
var/temperature = current
var/difference = abs(current-loc_temp) //get difference
@@ -229,33 +183,28 @@
if(reagents) reagents.metabolize(src)
-
src.updatehealth()
return //TODO: DEFERRED
-
/mob/living/carbon/slime/proc/handle_regular_status_updates()
- if(istype(src, /mob/living/carbon/slime/adult))
+ if(is_adult)
health = 200 - (getOxyLoss() + getToxLoss() + getFireLoss() + getBruteLoss() + getCloneLoss())
else
health = 150 - (getOxyLoss() + getToxLoss() + getFireLoss() + getBruteLoss() + getCloneLoss())
-
-
-
if(health < config.health_threshold_dead && stat != 2)
death()
return
else if(src.health < config.health_threshold_crit)
- // if(src.health <= 20 && prob(1)) spawn(0) emote("gasp")
- //if(!src.rejuv) src.oxyloss++
- if(!src.reagents.has_reagent("inaprovaline")) src.adjustOxyLoss(10)
+ if(!src.reagents.has_reagent("inaprovaline"))
+ src.adjustOxyLoss(10)
- if(src.stat != DEAD) src.stat = UNCONSCIOUS
+ if(src.stat != DEAD)
+ src.stat = UNCONSCIOUS
if(prob(30))
adjustOxyLoss(-1)
@@ -264,12 +213,9 @@
adjustCloneLoss(-1)
adjustBruteLoss(-1)
-
if (src.stat == DEAD)
-
src.lying = 1
src.blinded = 1
-
else
if (src.paralysis || src.stunned || src.weakened || (status_flags && FAKEDEATH)) //Stunned etc.
if (src.stunned > 0)
@@ -314,89 +260,25 @@
return 1
-
/mob/living/carbon/slime/proc/handle_nutrition()
- if(prob(20))
- if(istype(src, /mob/living/carbon/slime/adult)) nutrition-=rand(4,6)
- else nutrition-=rand(2,3)
+ if (prob(15))
+ nutrition -= 1 + is_adult
if(nutrition <= 0)
nutrition = 0
if(prob(75))
-
adjustToxLoss(rand(0,5))
- else
- if(istype(src, /mob/living/carbon/slime/adult))
- if(nutrition >= 1000)
- if(prob(40)) amount_grown++
+ else if (nutrition >= get_grow_nutrition() && amount_grown < 10)
+ nutrition -= 20
+ amount_grown++
+ if(amount_grown >= 10 && !Victim && !Target && !ckey)
+ if(is_adult)
+ Reproduce()
else
- if(nutrition >= 800)
- if(prob(40)) amount_grown++
-
- if(amount_grown >= 10 && !Victim && !Target)
- if(istype(src, /mob/living/carbon/slime/adult))
- if(!client)
- for(var/i=1,i<=4,i++)
- if(prob(70))
- var/mob/living/carbon/slime/M = new primarytype(loc)
- M.powerlevel = round(powerlevel/4)
- M.Friends = Friends
- M.tame = tame
- M.rabid = rabid
- M.Discipline = Discipline
- if(i != 1) step_away(M,src)
- else
- var/mutations = pick("one","two","three","four")
- switch(mutations)
- if("one")
- var/mob/living/carbon/slime/M = new mutationone(loc)
- M.powerlevel = round(powerlevel/4)
- M.Friends = Friends
- M.tame = tame
- M.rabid = rabid
- M.Discipline = Discipline
- if(i != 1) step_away(M,src)
- if("two")
- var/mob/living/carbon/slime/M = new mutationtwo(loc)
- M.powerlevel = round(powerlevel/4)
- M.Friends = Friends
- M.tame = tame
- M.rabid = rabid
- M.Discipline = Discipline
- if(i != 1) step_away(M,src)
- if("three")
- var/mob/living/carbon/slime/M = new mutationthree(loc)
- M.powerlevel = round(powerlevel/4)
- M.Friends = Friends
- M.tame = tame
- M.rabid = rabid
- M.Discipline = Discipline
- if(i != 1) step_away(M,src)
- if("four")
- var/mob/living/carbon/slime/M = new mutationfour(loc)
- M.powerlevel = round(powerlevel/4)
- M.Friends = Friends
- M.tame = tame
- M.rabid = rabid
- M.Discipline = Discipline
- if(i != 1) step_away(M,src)
-
- del(src)
-
- else
- if(!client)
- var/mob/living/carbon/slime/adult/A = new adulttype(src.loc)
- A.nutrition = nutrition
-// A.nutrition += 100
- A.powerlevel = max(0, powerlevel-1)
- A.Friends = Friends
- A.tame = tame
- A.rabid = rabid
- del(src)
-
+ Evolve()
/mob/living/carbon/slime/proc/handle_targets()
if(Tempstun)
@@ -408,8 +290,7 @@
if(attacked > 50) attacked = 50
if(attacked > 0)
- if(prob(85))
- attacked--
+ attacked--
if(Discipline > 0)
@@ -419,136 +300,278 @@
if(prob(10))
Discipline--
-
if(!client)
-
if(!canmove) return
- // DO AI STUFF HERE
-
- if(Target)
- if(attacked <= 0)
- Target = null
-
if(Victim) return // if it's eating someone already, continue eating!
-
- if(prob(1))
- emote(pick("bounce","sway","light","vibrate","jiggle"))
+ if(Target)
+ --target_patience
+ if (target_patience <= 0 || SStun || Discipline || attacked) // Tired of chasing or something draws out attention
+ target_patience = 0
+ Target = null
if(AIproc && SStun) return
-
var/hungry = 0 // determines if the slime is hungry
- var/starving = 0 // determines if the slime is starving-hungry
- if(istype(src, /mob/living/carbon/slime/adult)) // 1200 max nutrition
- switch(nutrition)
- if(601 to 900)
- if(prob(25)) hungry = 1//Ensures they continue eating, but aren't as aggressive at the same time
- if(301 to 600) hungry = 1
- if(0 to 300)
- starving = 1
- else
- switch(nutrition) // 1000 max nutrition
- if(501 to 700)
- if(prob(25)) hungry = 1
- if(201 to 500) hungry = 1
- if(0 to 200) starving = 1
+ if (nutrition < get_starve_nutrition())
+ hungry = 2
+ else if (nutrition < get_grow_nutrition() && prob(25) || nutrition < get_hunger_nutrition())
+ hungry = 1
-
- if(starving && !client) // if a slime is starving, it starts losing its friends
+ if(hungry == 2 && !client) // if a slime is starving, it starts losing its friends
if(Friends.len > 0 && prob(1))
var/mob/nofriend = pick(Friends)
- Friends -= nofriend
+ --Friends[nofriend]
if(!Target)
- var/list/targets = list()
+ if(will_hunt() && hungry || attacked || rabid) // Only add to the list if we need to
+ var/list/targets = list()
- if(hungry || starving) //Only add to the list if we need to
for(var/mob/living/L in view(7,src))
- //Ignore other slimes, dead mobs and simple_animals
- if(isslime(L) || L.stat != CONSCIOUS || isanimal(L))
+ if(isslime(L) || L.stat == DEAD) // Ignore other slimes and dead mobs
continue
- if(issilicon(L))
- if(!istype(src, /mob/living/carbon/slime/adult)) //Non-starving diciplined adult slimes wont eat things
- if(!starving && Discipline > 0)
+ if(L in Friends) // No eating friends!
+ continue
+
+ if(issilicon(L) && (rabid || attacked)) // They can't eat silicons, but they can glomp them in defence
+ targets += L // Possible target found!
+
+ if(istype(L, /mob/living/carbon/human) && dna) //Ignore slime(wo)men
+ var/mob/living/carbon/human/H = L
+ if(H.dna)
+ if(H.dna.mutantrace == "slime")
continue
- if(tame) //Tame slimes ignore electronic life
+ if(!L.canmove) // Only one slime can latch on at a time.
+ var/notarget = 0
+ for(var/mob/living/carbon/slime/M in view(1,L))
+ if(M.Victim == L)
+ notarget = 1
+ if(notarget)
continue
- targets += L //Possible target found!
+ targets += L // Possible target found!
- else if(iscarbon(L))
-
- if(istype(L, /mob/living/carbon/human)) //Ignore slime(wo)men
- var/mob/living/carbon/human/H = L
- if(H.dna)
- if(H.dna.mutantrace == "slime")
- continue
-
- if(!istype(src, /mob/living/carbon/slime/adult)) //Non-starving diciplined adult slimes wont eat things
- if(!starving && Discipline > 0)
- continue
-
- if(L in Friends) //No eating friends!
- continue
-
- if(tame && ishuman(L)) //Tame slimes dont eat people.
- continue
-
- if(!L.canmove) //Only one slime can latch on at a time.
-
- var/notarget = 0
- for(var/mob/living/carbon/slime/M in view(1,L))
- if(M.Victim == L)
- notarget = 1
- if(notarget)
- continue
-
- targets += L //Possible target found!
-
-
-
- if((hungry || starving) && targets.len > 0)
- if(!istype(src, /mob/living/carbon/slime/adult))
- if(!starving)
+ if(targets.len > 0)
+ if(attacked || rabid || hungry == 2)
+ Target = targets[1] // I am attacked and am fighting back or so hungry I don't even care
+ else
for(var/mob/living/carbon/C in targets)
if(!Discipline && prob(5))
- if(ishuman(C))
- Target = C
- break
- if(isalienadult(C))
+ if(ishuman(C) || isalienadult(C))
Target = C
break
- if(islarva(C))
+ if(islarva(C) || ismonkey(C))
Target = C
break
- if(ismonkey(C))
- Target = C
- break
- else
- Target = targets[1]
- else
- Target = targets[1] // closest target
- if(targets.len > 0)
- if(attacked > 0 || rabid)
- Target = targets[1] //closest mob probably attacked it, so override Target and attack the nearest!
+ if (Target)
+ target_patience = rand(5,7)
+ if (is_adult)
+ target_patience += 3
+ if(!Target) // If we have no target, we are wandering or following orders
+ if (Leader)
+ if (holding_still)
+ holding_still = max(holding_still - 1, 0)
+ else if(canmove && isturf(loc))
+ step_to(src, Leader)
- if(!Target)
- if(hungry || starving)
- if(canmove && isturf(loc) && prob(50))
+ else if(hungry)
+ if (holding_still)
+ holding_still = max(holding_still - hungry, 0)
+ else if(canmove && isturf(loc) && prob(50))
step(src, pick(cardinal))
else
- if(canmove && isturf(loc) && prob(33))
+ if (holding_still)
+ holding_still = max(holding_still - 1, 0)
+ else if(canmove && isturf(loc) && prob(33))
step(src, pick(cardinal))
- else
- if(!AIproc)
- spawn() AIprocess()
+ else if(!AIproc)
+ spawn()
+ AIprocess()
+
+/mob/living/carbon/slime/proc/handle_speech_and_mood()
+ //Mood starts here
+ var/newmood = ""
+ if (rabid || attacked) newmood = "angry"
+ else if (Target) newmood = "mischevous"
+
+ if (!newmood)
+ if (Discipline && prob(25))
+ newmood = "pout"
+ else if (prob(1))
+ newmood = pick("sad", ":3", "pout")
+
+ if ((mood == "sad" || mood == ":3" || mood == "pout") && !newmood)
+ if (prob(75)) newmood = mood
+
+ if (newmood != mood) // This is so we don't redraw them every time
+ mood = newmood
+ regenerate_icons()
+
+ //Speech understanding starts here
+ var/to_say
+ if (speech_buffer.len > 0)
+ var/who = speech_buffer[1] // Who said it?
+ var/phrase = speech_buffer[2] // What did they say?
+ if ((findtext(phrase, num2text(number)) || findtext(phrase, "slimes"))) // Talking to us
+ if (findtext(phrase, "hello") || findtext(phrase, "hi"))
+ to_say = pick("Hello...", "Hi...")
+ else if (findtext(phrase, "follow"))
+ if (Leader)
+ if (Leader == who) // Already following him
+ to_say = pick("Yes...", "Lead...", "Following...")
+ else if (Friends[who] > Friends[Leader]) // VIVA
+ Leader = who
+ to_say = "Yes... I follow [who]..."
+ else
+ to_say = "No... I follow [Leader]..."
+ else
+ if (Friends[who] > 2)
+ Leader = who
+ to_say = "I follow..."
+ else // Not friendly enough
+ to_say = pick("No...", "I won't follow...")
+ else if (findtext(phrase, "stop"))
+ if (Victim) // We are asked to stop feeding
+ if (Friends[who] > 4)
+ Victim = null
+ Target = null
+ if (Friends[who] < 7)
+ --Friends[who]
+ to_say = "Grrr..." // I'm angry but I do it
+ else
+ to_say = "Fine..."
+ else if (Target) // We are asked to stop chasing
+ if (Friends[who] > 3)
+ Target = null
+ if (Friends[who] < 6)
+ --Friends[who]
+ to_say = "Grrr..." // I'm angry but I do it
+ else
+ to_say = "Fine..."
+ else if (Leader) // We are asked to stop following
+ if (Leader == who)
+ to_say = "Yes... I'll stay..."
+ Leader = null
+ else
+ if (Friends[who] > Friends[Leader])
+ Leader = null
+ to_say = "Yes... I'll stop..."
+ else
+ to_say = "No... I'll keep following..."
+ else if (findtext(phrase, "stay"))
+ if (Leader)
+ if (Leader == who)
+ holding_still = Friends[who] * 10
+ to_say = "Yes... Staying..."
+ else if (Friends[who] > Friends[Leader])
+ holding_still = (Friends[who] - Friends[Leader]) * 10
+ to_say = "Yes... Staying..."
+ else
+ to_say = "No... I'll keep following..."
+ else
+ if (Friends[who] > 2)
+ holding_still = Friends[who] * 10
+ to_say = "Yes... Staying..."
+ else
+ to_say = "No... I won't stay..."
+ speech_buffer = list()
+
+ //Speech starts here
+ if (to_say)
+ say (to_say)
+ else if(prob(1))
+ emote(pick("bounce","sway","light","vibrate","jiggle"))
+ else
+ var/t = 10
+ var/slimes_near = -1 // Don't count myself
+ var/dead_slimes = 0
+ var/friends_near = list()
+ for (var/mob/living/carbon/M in view(7,src))
+ if (isslime(M))
+ ++slimes_near
+ if (M.stat == DEAD)
+ ++dead_slimes
+ if (M in Friends)
+ t += 20
+ friends_near += M
+ if (nutrition < get_hunger_nutrition()) t += 10
+ if (nutrition < get_starve_nutrition()) t += 10
+ if (prob(2) && prob(t))
+ var/phrases = list()
+ if (Target) phrases += "[Target]... looks tasty..."
+ if (nutrition < get_starve_nutrition())
+ phrases += "So... hungry..."
+ phrases += "Very... hungry..."
+ phrases += "Need... food..."
+ phrases += "Must... eat..."
+ else if (nutrition < get_hunger_nutrition())
+ phrases += "Hungry..."
+ phrases += "Where is the food?"
+ phrases += "I want to eat..."
+ phrases += "Rawr..."
+ phrases += "Blop..."
+ phrases += "Blorble..."
+ if (rabid || attacked)
+ phrases += "Hrr..."
+ phrases += "Nhuu..."
+ phrases += "Unn..."
+ if (mood == ":3")
+ phrases += "Purr..."
+ if (attacked)
+ phrases += "Grrr..."
+ if (getToxLoss() > 30)
+ phrases += "Cold..."
+ if (getToxLoss() > 60)
+ phrases += "So... cold..."
+ phrases += "Very... cold..."
+ if (getToxLoss() > 90)
+ phrases += "..."
+ phrases += "C... c..."
+ if (Victim)
+ phrases += "Nom..."
+ phrases += "Tasty..."
+ if (powerlevel > 3) phrases += "Bzzz..."
+ if (powerlevel > 5) phrases += "Zap..."
+ if (powerlevel > 8) phrases += "Zap... Bzz..."
+ if (mood == "sad") phrases += "Bored..."
+ if (slimes_near) phrases += "Brother..."
+ if (slimes_near > 1) phrases += "Brothers..."
+ if (dead_slimes) phrases += "What happened?"
+ if (!slimes_near)
+ phrases += "Lonely..."
+ for (var/M in friends_near)
+ phrases += "[M]... friend..."
+ if (nutrition < get_hunger_nutrition())
+ phrases += "[M]... feed me..."
+ say (pick(phrases))
+
+/mob/living/carbon/slime/proc/get_max_nutrition() // Can't go above it
+ if (is_adult) return 1200
+ else return 1000
+
+/mob/living/carbon/slime/proc/get_grow_nutrition() // Above it we grow, below it we can eat
+ if (is_adult) return 1000
+ else return 800
+
+/mob/living/carbon/slime/proc/get_hunger_nutrition() // Below it we will always eat
+ if (is_adult) return 600
+ else return 500
+
+/mob/living/carbon/slime/proc/get_starve_nutrition() // Below it we will eat before everything else
+ if (is_adult) return 300
+ else return 200
+
+/mob/living/carbon/slime/proc/will_hunt(var/hunger = -1) // Check for being stopped from feeding and chasing
+ if (hunger == 2 || rabid || attacked) return 1
+ if (Leader) return 0
+ if (holding_still) return 0
+ return 1
diff --git a/code/modules/mob/living/carbon/metroid/metroid.dm b/code/modules/mob/living/carbon/metroid/metroid.dm
index 0f4d2051569..67f0b904ed4 100644
--- a/code/modules/mob/living/carbon/metroid/metroid.dm
+++ b/code/modules/mob/living/carbon/metroid/metroid.dm
@@ -3,7 +3,8 @@
icon = 'icons/mob/slimes.dmi'
icon_state = "grey baby slime"
pass_flags = PASSTABLE
- speak_emote = list("hums")
+ var/is_adult = 0
+ speak_emote = list("telepathically chirps")
layer = 5
@@ -12,7 +13,7 @@
gender = NEUTER
update_icon = 0
- nutrition = 700 // 1000 = max
+ nutrition = 700
see_in_dark = 8
update_slimes = 0
@@ -22,67 +23,58 @@
status_flags = CANPARALYSE|CANPUSH
var/cores = 1 // the number of /obj/item/slime_extract's the slime has left inside
+ var/mutation_chance = 30 // Chance of mutating, should be between 25 and 35
- var/powerlevel = 0 // 1-10 controls how much electricity they are generating
- var/amount_grown = 0 // controls how long the slime has been overfed, if 10, grows into an adult
- // if adult: if 10: reproduces
+ var/powerlevel = 0 // 1-10 controls how much electricity they are generating
+ var/amount_grown = 0 // controls how long the slime has been overfed, if 10, grows or reproduces
+ var/number = 0 // Used to understand when someone is talking to it
var/mob/living/Victim = null // the person the slime is currently feeding on
var/mob/living/Target = null // AI variable - tells the slime to hunt this down
+ var/mob/living/Leader = null // AI variable - tells the slime to follow this person
- var/attacked = 0 // determines if it's been attacked recently. Can be any number, is a cooloff-ish variable
- var/tame = 0 // if set to 1, the slime will not eat humans ever, or attack them
- var/rabid = 0 // if set to 1, the slime will attack and eat anything it comes in contact with
+ var/attacked = 0 // Determines if it's been attacked recently. Can be any number, is a cooloff-ish variable
+ var/rabid = 0 // If set to 1, the slime will attack and eat anything it comes in contact with
+ var/holding_still = 0 // AI variable, cooloff-ish for how long it's going to stay in one place
+ var/target_patience = 0 // AI variable, cooloff-ish for how long it's going to follow its target
- var/list/Friends = list() // A list of potential friends
- var/list/FriendsWeight = list() // A list containing values respective to Friends. This determines how many times a slime "likes" something. If the slime likes it more than 2 times, it becomes a friend
+ var/list/Friends = list() // A list of friends; they are not considered targets for feeding; passed down after splitting
- // slimes pass on genetic data, so all their offspring have the same "Friends",
+ var/list/speech_buffer = list() // Last phrase said near it and person who said it
+
+ var/mood = "" // To show its face
///////////TIME FOR SUBSPECIES
var/colour = "grey"
- var/primarytype = /mob/living/carbon/slime
- var/mutationone = /mob/living/carbon/slime/orange
- var/mutationtwo = /mob/living/carbon/slime/metal
- var/mutationthree = /mob/living/carbon/slime/blue
- var/mutationfour = /mob/living/carbon/slime/purple
- var/adulttype = /mob/living/carbon/slime/adult
var/coretype = /obj/item/slime_extract/grey
-
-/mob/living/carbon/slime/adult
- name = "adult slime"
- icon = 'icons/mob/slimes.dmi'
- icon_state = "grey adult slime"
- speak_emote = list("telepathically chirps")
-
- health = 200
- gender = NEUTER
-
- update_icon = 0
- nutrition = 800 // 1200 = max
-
+ var/list/slime_mutation[4]
/mob/living/carbon/slime/New()
- var/datum/reagents/R = new/datum/reagents(100)
- reagents = R
- R.my_atom = src
- if(name == "baby slime")
- name = text("[colour] baby slime ([rand(1, 1000)])")
- else
- name = text("[colour] adult slime ([rand(1,1000)])")
- real_name = name
- spawn (1)
- regenerate_icons()
- src << "\blue Your icons have been generated!"
+ create_reagents(100)
+ spawn (0)
+ number = rand(1, 1000)
+ name = "[colour] [is_adult ? "adult" : "baby"] slime ([number])"
+ icon_state = "[colour] [is_adult ? "adult" : "baby"] slime"
+ real_name = name
+ slime_mutation = mutation_table(colour)
+ mutation_chance = rand(25, 35)
+ var/sanitizedcolour = replacetext(colour, " ", "")
+ coretype = text2path("/obj/item/slime_extract/[sanitizedcolour]")
..()
-/mob/living/carbon/slime/adult/New()
- //verbs.Remove(/mob/living/carbon/slime/verb/ventcrawl)
+/mob/living/carbon/slime/regenerate_icons()
+ icon_state = "[colour] [is_adult ? "adult" : "baby"] slime"
+ overlays.len = 0
+ if (mood)
+ overlays += image('icons/mob/slimes.dmi', icon_state = "aslime-[mood]")
..()
/mob/living/carbon/slime/movement_delay()
+ if (bodytemperature >= 330.23) // 135 F
+ return -1 // slimes become supercharged at high temperatures
+
var/tally = 0
var/health_deficiency = (100 - health)
@@ -92,115 +84,93 @@
tally += (283.222 - bodytemperature) / 10 * 1.75
if(reagents)
- if(reagents.has_reagent("hyperzine")) // hyperzine slows slimes down
- tally *= 2 // moves twice as slow
+ if(reagents.has_reagent("hyperzine")) // Hyperzine slows slimes down
+ tally *= 2
- if(reagents.has_reagent("frostoil")) // frostoil also makes them move VEEERRYYYYY slow
+ if(reagents.has_reagent("frostoil")) // Frostoil also makes them move VEEERRYYYYY slow
tally *= 5
if(health <= 0) // if damaged, the slime moves twice as slow
tally *= 2
- if (bodytemperature >= 330.23) // 135 F
- return -1 // slimes become supercharged at high temperatures
-
- return tally+config.slime_delay
-
+ return tally + config.slime_delay
/mob/living/carbon/slime/Bump(atom/movable/AM as mob|obj, yes)
- spawn( 0 )
- if ((!( yes ) || now_pushing))
- return
- now_pushing = 1
+ if ((!(yes) || now_pushing))
+ return
+ now_pushing = 1
- if(isobj(AM))
- if(!client && powerlevel > 0)
- var/probab = 10
- switch(powerlevel)
- if(1 to 2) probab = 20
- if(3 to 4) probab = 30
- if(5 to 6) probab = 40
- if(7 to 8) probab = 60
- if(9) probab = 70
- if(10) probab = 95
- if(prob(probab))
+ if(isobj(AM))
+ if(!client && powerlevel > 0)
+ var/probab = 10
+ switch(powerlevel)
+ if(1 to 2) probab = 20
+ if(3 to 4) probab = 30
+ if(5 to 6) probab = 40
+ if(7 to 8) probab = 60
+ if(9) probab = 70
+ if(10) probab = 95
+ if(prob(probab))
+ if(istype(AM, /obj/structure/window) || istype(AM, /obj/structure/grille))
+ if(nutrition <= get_hunger_nutrition() && !Atkcool)
+ if (is_adult || prob(5))
+ AM.attack_slime(src)
+ spawn()
+ Atkcool = 1
+ sleep(45)
+ Atkcool = 0
+ if(ismob(AM))
+ var/mob/tmob = AM
- if(istype(AM, /obj/structure/window) || istype(AM, /obj/structure/grille))
- if(istype(src, /mob/living/carbon/slime/adult))
- if(nutrition <= 600 && !Atkcool)
- AM.attack_slime(src)
- spawn()
- Atkcool = 1
- sleep(15)
- Atkcool = 0
- else
- if(nutrition <= 500 && !Atkcool)
- if(prob(5))
- AM.attack_slime(src)
- spawn()
- Atkcool = 1
- sleep(15)
- Atkcool = 0
-
- if(ismob(AM))
- var/mob/tmob = AM
-
- if(istype(src, /mob/living/carbon/slime/adult))
- if(istype(tmob, /mob/living/carbon/human))
- if(prob(90))
- now_pushing = 0
- return
- else
- if(istype(tmob, /mob/living/carbon/human))
+ if(is_adult)
+ if(istype(tmob, /mob/living/carbon/human))
+ if(prob(90))
now_pushing = 0
return
+ else
+ if(istype(tmob, /mob/living/carbon/human))
+ now_pushing = 0
+ return
- now_pushing = 0
- ..()
- if (!( istype(AM, /atom/movable) ))
- return
- if (!( now_pushing ))
- now_pushing = 1
- if (!( AM.anchored ))
- var/t = get_dir(src, AM)
- if (istype(AM, /obj/structure/window))
- if(AM:ini_dir == NORTHWEST || AM:ini_dir == NORTHEAST || AM:ini_dir == SOUTHWEST || AM:ini_dir == SOUTHEAST)
- for(var/obj/structure/window/win in get_step(AM,t))
- now_pushing = 0
- return
- step(AM, t)
- now_pushing = null
+ now_pushing = 0
+ ..()
+ if (!istype(AM, /atom/movable))
return
- return
+ if (!( now_pushing ))
+ now_pushing = 1
+ if (!( AM.anchored ))
+ var/t = get_dir(src, AM)
+ if (istype(AM, /obj/structure/window))
+ if(AM:ini_dir == NORTHWEST || AM:ini_dir == NORTHEAST || AM:ini_dir == SOUTHWEST || AM:ini_dir == SOUTHEAST)
+ for(var/obj/structure/window/win in get_step(AM,t))
+ now_pushing = 0
+ return
+ step(AM, t)
+ now_pushing = null
/mob/living/carbon/slime/Process_Spacemove()
return 2
-
/mob/living/carbon/slime/Stat()
..()
statpanel("Status")
- if(istype(src, /mob/living/carbon/slime/adult))
+ if(is_adult)
stat(null, "Health: [round((health / 200) * 100)]%")
else
stat(null, "Health: [round((health / 150) * 100)]%")
-
if (client.statpanel == "Status")
- if(istype(src,/mob/living/carbon/slime/adult))
- stat(null, "Nutrition: [nutrition]/1200")
- if(amount_grown >= 10)
+ stat(null, "Nutrition: [nutrition]/[get_max_nutrition()]")
+ if(amount_grown >= 10)
+ if(is_adult)
stat(null, "You can reproduce!")
- else
- stat(null, "Nutrition: [nutrition]/1000")
- if(amount_grown >= 10)
+ else
stat(null, "You can evolve!")
stat(null,"Power Level: [powerlevel]")
-
/mob/living/carbon/slime/adjustFireLoss(amount)
..(-abs(amount)) // Heals them
return
@@ -210,25 +180,18 @@
..(Proj)
return 0
-
/mob/living/carbon/slime/emp_act(severity)
powerlevel = 0 // oh no, the power!
..()
/mob/living/carbon/slime/ex_act(severity)
-
- if (stat == 2 && client)
- return
-
- else if (stat == 2 && !client)
- del(src)
- return
+ ..()
var/b_loss = null
var/f_loss = null
switch (severity)
if (1.0)
- b_loss += 500
+ del(src)
return
if (2.0)
@@ -260,7 +223,7 @@
//paralysis += 1
- show_message("\red The blob attacks you!")
+ show_message(" The blob attacks you!")
adjustFireLoss(damage)
@@ -271,7 +234,6 @@
/mob/living/carbon/slime/u_equip(obj/item/W as obj)
return
-
/mob/living/carbon/slime/attack_ui(slot)
return
@@ -286,52 +248,48 @@
updatehealth()
return
-
/mob/living/carbon/slime/attack_slime(mob/living/carbon/slime/M as mob)
if (!ticker)
M << "You cannot attack people before the game has started."
return
- if(Victim) return // can't attack while eating!
+ if (Victim) return // can't attack while eating!
if (health > -100)
- for(var/mob/O in viewers(src, null))
- if ((O.client && !( O.blinded )))
- O.show_message(text("\red The [M.name] has glomped []!", src), 1)
-
+ visible_message(" The [M.name] has glomped [src]!", \
+ " The [M.name] has glomped [src]!")
var/damage = rand(1, 3)
attacked += 5
- if(istype(src, /mob/living/carbon/slime/adult))
+ if(M.is_adult)
damage = rand(1, 6)
else
damage = rand(1, 3)
adjustBruteLoss(damage)
-
updatehealth()
-
return
-
/mob/living/carbon/slime/attack_animal(mob/living/simple_animal/M as mob)
if(M.melee_damage_upper == 0)
M.emote("[M.friendly] [src]")
else
if(M.attack_sound)
playsound(loc, M.attack_sound, 50, 1, 1)
- for(var/mob/O in viewers(src, null))
- O.show_message("\red [M] [M.attacktext] [src]!", 1)
+ visible_message("[M] [M.attacktext] [src]!", \
+ "[M] [M.attacktext] [src]!")
M.attack_log += text("\[[time_stamp()]\] attacked [src.name] ([src.ckey])")
src.attack_log += text("\[[time_stamp()]\] was attacked by [M.name] ([M.ckey])")
var/damage = rand(M.melee_damage_lower, M.melee_damage_upper)
+ attacked += 10
adjustBruteLoss(damage)
updatehealth()
/mob/living/carbon/slime/attack_paw(mob/living/carbon/monkey/M as mob)
- if(!(istype(M, /mob/living/carbon/monkey))) return//Fix for aliens receiving double messages when attacking other aliens.
+ if(!(istype(M, /mob/living/carbon/monkey)))
+ return // Fix for aliens receiving double messages when attacking other aliens.
if (!ticker)
M << "You cannot attack people before the game has started."
@@ -340,6 +298,7 @@
if (istype(loc, /turf) && istype(loc.loc, /area/start))
M << "No attacking people at spawn, you jackass."
return
+
..()
switch(M.a_intent)
@@ -352,9 +311,8 @@
if (health > 0)
attacked += 10
//playsound(loc, 'sound/weapons/bite.ogg', 50, 1, -1)
- for(var/mob/O in viewers(src, null))
- if ((O.client && !( O.blinded )))
- O.show_message(text("\red [M.name] has attacked [src]!"), 1)
+ visible_message("[M.name] has attacked [src]!", \
+ "[M.name] has attacked [src]!")
adjustBruteLoss(rand(1, 3))
updatehealth()
return
@@ -374,15 +332,11 @@
if(Victim)
if(Victim == M)
if(prob(60))
- for(var/mob/O in viewers(src, null))
- if ((O.client && !( O.blinded )))
- O.show_message("\red [M] attempts to wrestle \the [name] off!", 1)
+ visible_message("[M] attempts to wrestle \the [name] off!")
playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
else
- for(var/mob/O in viewers(src, null))
- if ((O.client && !( O.blinded )))
- O.show_message("\red [M] manages to wrestle \the [name] off!", 1)
+ visible_message(" [M] manages to wrestle \the [name] off!")
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
if(prob(90) && !client)
@@ -402,21 +356,17 @@
else
if(prob(30))
- for(var/mob/O in viewers(src, null))
- if ((O.client && !( O.blinded )))
- O.show_message("\red [M] attempts to wrestle \the [name] off of [Victim]!", 1)
+ visible_message("[M] attempts to wrestle \the [name] off of [Victim]!")
playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
else
- for(var/mob/O in viewers(src, null))
- if ((O.client && !( O.blinded )))
- O.show_message("\red [M] manages to wrestle \the [name] off of [Victim]!", 1)
+ visible_message(" [M] manages to wrestle \the [name] off of [Victim]!")
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
if(prob(80) && !client)
Discipline++
- if(!istype(src, /mob/living/carbon/slime/adult))
+ if(!is_adult)
if(Discipline == 1)
attacked = 0
@@ -432,18 +382,13 @@
return
-
-
-
if(M.gloves && istype(M.gloves,/obj/item/clothing/gloves))
var/obj/item/clothing/gloves/G = M.gloves
if(G.cell)
if(M.a_intent == "hurt")//Stungloves. Any contact will stun the alien.
if(G.cell.charge >= 2500)
G.cell.use(2500)
- for(var/mob/O in viewers(src, null))
- if ((O.client && !( O.blinded )))
- O.show_message("\red [src] has been touched with the stun gloves by [M]!", 1, "\red You hear someone fall.", 2)
+ visible_message("[src] has been touched with the stun gloves by [M]!")
return
else
M << "\red Not enough charge! "
@@ -455,21 +400,18 @@
help_shake_act(M)
if ("grab")
- if (M == src)
+ if (M == src || anchored)
return
- var/obj/item/weapon/grab/G = new /obj/item/weapon/grab( M, src )
+ var/obj/item/weapon/grab/G = new /obj/item/weapon/grab(M, src)
M.put_in_active_hand(G)
- grabbed_by += G
G.synch()
LAssailant = M
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
- for(var/mob/O in viewers(src, null))
- if ((O.client && !( O.blinded )))
- O.show_message(text("\red [] has grabbed [] passively!", M, src), 1)
+ visible_message("[M] has grabbed [src] passively!")
else
@@ -479,8 +421,9 @@
if (prob(90))
if (HULK in M.mutations)
damage += 5
- if(Victim)
+ if(Victim || Target)
Victim = null
+ Target = null
anchored = 0
if(prob(80) && !client)
Discipline++
@@ -492,17 +435,14 @@
playsound(loc, "punch", 25, 1, -1)
- for(var/mob/O in viewers(src, null))
- if ((O.client && !( O.blinded )))
- O.show_message(text("\red [] has punched []!", M, src), 1)
+ visible_message("[M] has punched [src]!", \
+ "[M] has punched [src]!")
adjustBruteLoss(damage)
updatehealth()
else
playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
- for(var/mob/O in viewers(src, null))
- if ((O.client && !( O.blinded )))
- O.show_message(text("\red [] has attempted to punch []!", M, src), 1)
+ visible_message("[M] has attempted to punch [src]!")
return
@@ -518,48 +458,41 @@
switch(M.a_intent)
if ("help")
- for(var/mob/O in viewers(src, null))
- if ((O.client && !( O.blinded )))
- O.show_message(text("\blue [M] caresses [src] with its scythe like arm."), 1)
+ visible_message("[M] caresses [src] with its scythe like arm.")
if ("hurt")
- if ((prob(95) && health > 0))
+ if (prob(95))
attacked += 10
playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1)
var/damage = rand(15, 30)
if (damage >= 25)
damage = rand(20, 40)
- for(var/mob/O in viewers(src, null))
- if ((O.client && !( O.blinded )))
- O.show_message(text("\red [] has attacked [name]!", M), 1)
+ visible_message("[M] has attacked [name]!", \
+ "[M] has attacked [name]!")
else
- for(var/mob/O in viewers(src, null))
- if ((O.client && !( O.blinded )))
- O.show_message(text("\red [] has wounded [name]!", M), 1)
+ visible_message("[M] has wounded [name]!", \
+ ")[M] has wounded [name]!")
adjustBruteLoss(damage)
updatehealth()
else
playsound(loc, 'sound/weapons/slashmiss.ogg', 25, 1, -1)
- for(var/mob/O in viewers(src, null))
- if ((O.client && !( O.blinded )))
- O.show_message(text("\red [] has attempted to lunge at [name]!", M), 1)
+ visible_message("[M] has attempted to lunge at [name]!", \
+ "[M] has attempted to lunge at [name]!")
if ("grab")
- if (M == src)
+ if (M == src || anchored)
return
- var/obj/item/weapon/grab/G = new /obj/item/weapon/grab( M, M, src )
+ var/obj/item/weapon/grab/G = new /obj/item/weapon/grab(M, src )
M.put_in_active_hand(G)
- grabbed_by += G
G.synch()
LAssailant = M
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
- for(var/mob/O in viewers(src, null))
- O.show_message(text("\red [] has grabbed [name] passively!", M), 1)
+ visible_message(" [M] has grabbed [name] passively!")
if ("disarm")
playsound(loc, 'sound/weapons/pierce.ogg', 25, 1, -1)
@@ -567,12 +500,12 @@
attacked += 10
if(prob(95))
- for(var/mob/O in viewers(src, null))
- if ((O.client && !( O.blinded )))
- O.show_message(text("\red [] has tackled [name]!", M), 1)
+ visible_message("[M] has tackled [name]!", \
+ "[M] has tackled [name]!")
- if(Victim)
+ if(Victim || Target)
Victim = null
+ Target = null
anchored = 0
if(prob(80) && !client)
Discipline++
@@ -593,68 +526,123 @@
else
drop_item()
- for(var/mob/O in viewers(src, null))
- if ((O.client && !( O.blinded )))
- O.show_message(text("\red [] has disarmed [name]!", M), 1)
+ visible_message("[M] has disarmed [name]!",
+ "[M] has disarmed [name]!")
adjustBruteLoss(damage)
updatehealth()
return
+/mob/living/carbon/slime/attackby(obj/item/W, mob/user)
+ if(W.force > 0)
+ attacked += 10
+ if(prob(25))
+ user << "[W] passes right through [src]!"
+ return
+ if(Discipline && prob(50)) // wow, buddy, why am I getting attacked??
+ Discipline = 0
+ if(W.force >= 3)
+ if(is_adult)
+ if(prob(5 + round(W.force/2)))
+ if(Victim || Target)
+ if(prob(80) && !client)
+ Discipline++
+
+ Victim = null
+ Target = null
+ anchored = 0
+
+ spawn()
+ SStun = 1
+ sleep(rand(5,20))
+ SStun = 0
+
+ spawn(0)
+ if(user)
+ canmove = 0
+ step_away(src, user)
+ if(prob(25 + W.force))
+ sleep(2)
+ if(user)
+ step_away(src, user)
+ canmove = 1
+
+ else
+ if(prob(10 + W.force*2))
+ if(Victim || Target)
+ if(prob(80) && !client)
+ Discipline++
+ if(Discipline == 1)
+ attacked = 0
+ spawn()
+ SStun = 1
+ sleep(rand(5,20))
+ SStun = 0
+
+ Victim = null
+ Target = null
+ anchored = 0
+
+ spawn(0)
+ if(user)
+ canmove = 0
+ step_away(src, user)
+ if(prob(25 + W.force*4))
+ sleep(2)
+ if(user)
+ step_away(src, user)
+ canmove = 1
+ ..()
/mob/living/carbon/slime/restrained()
return 0
-
mob/living/carbon/slime/var/co2overloadtime = null
mob/living/carbon/slime/var/temperature_resistance = T0C+75
-
-/mob/living/carbon/slime/show_inv(mob/user as mob)
-
- user.set_machine(src)
- var/dat = {"
- [name]
-
- Close
- "}
- user << browse(dat, text("window=mob[name];size=340x480"))
- onclose(user, "mob[name]")
+/mob/living/carbon/slime/show_inv(mob/user)
return
-/mob/living/carbon/slime/updatehealth()
- if(status_flags & GODMODE)
- if(istype(src, /mob/living/carbon/slime/adult))
- health = 200
- else
- health = 150
- stat = CONSCIOUS
- else
- // slimes can't suffocate unless they suicide. They are also not harmed by fire
- if(istype(src, /mob/living/carbon/slime/adult))
- health = 200 - (getOxyLoss() + getToxLoss() + getFireLoss() + getBruteLoss() + getCloneLoss())
- else
- health = 150 - (getOxyLoss() + getToxLoss() + getFireLoss() + getBruteLoss() + getCloneLoss())
+/mob/living/carbon/slime/toggle_throw_mode()
+ return
+/mob/living/carbon/slime/proc/apply_water()
+ adjustToxLoss(rand(15,20))
+ if (!client)
+ if (Target) // Like cats
+ Target = null
+ ++Discipline
+ return
/obj/item/slime_extract
name = "slime extract"
desc = "Goo extracted from a slime. Legends claim these to have \"magical powers\"."
icon = 'icons/mob/slimes.dmi'
icon_state = "grey slime extract"
- flags = TABLEPASS
force = 1.0
w_class = 1.0
- throwforce = 1.0
+ throwforce = 0
throw_speed = 3
throw_range = 6
origin_tech = "biotech=4"
var/Uses = 1 // uses before it goes inert
+ var/enhanced = 0 //has it been enhanced before?
+
+ attackby(obj/item/O as obj, mob/user as mob)
+ if(istype(O, /obj/item/weapon/slimesteroid2))
+ if(enhanced == 1)
+ user << " This extract has already been enhanced!"
+ return ..()
+ if(Uses == 0)
+ user << " You can't enhance a used extract!"
+ return ..()
+ user <<"You apply the enhancer. It now has triple the amount of uses."
+ Uses = 3
+ enhanced = 1
+ del(O)
/obj/item/slime_extract/New()
..()
- var/datum/reagents/R = new/datum/reagents(100)
- reagents = R
- R.my_atom = src
+ create_reagents(100)
/obj/item/slime_extract/grey
name = "grey slime extract"
@@ -724,6 +712,26 @@ mob/living/carbon/slime/var/temperature_resistance = T0C+75
name = "adamantine slime extract"
icon_state = "adamantine slime extract"
+/obj/item/slime_extract/bluespace
+ name = "bluespace slime extract"
+ icon_state = "bluespace slime extract"
+
+/obj/item/slime_extract/pyrite
+ name = "pyrite slime extract"
+ icon_state = "pyrite slime extract"
+
+/obj/item/slime_extract/cerulean
+ name = "cerulean slime extract"
+ icon_state = "cerulean slime extract"
+
+/obj/item/slime_extract/sepia
+ name = "sepia slime extract"
+ icon_state = "sepia slime extract"
+
+/obj/item/slime_extract/rainbow
+ name = "rainbow slime extract"
+ icon_state = "rainbow slime extract"
+
////Pet Slime Creation///
/obj/item/weapon/slimepotion
@@ -734,28 +742,31 @@ mob/living/carbon/slime/var/temperature_resistance = T0C+75
attack(mob/living/carbon/slime/M as mob, mob/user as mob)
if(!istype(M, /mob/living/carbon/slime))//If target is not a slime.
- user << "\red The potion only works on baby slimes!"
+ user << " The potion only works on baby slimes!"
return ..()
- if(istype(M, /mob/living/carbon/slime/adult)) //Can't tame adults
- user << "\red Only baby slimes can be tamed!"
+ if(M.is_adult) //Can't tame adults
+ user << " Only baby slimes can be tamed!"
return..()
if(M.stat)
- user << "\red The slime is dead!"
+ user << " The slime is dead!"
return..()
+ if(M.mind)
+ user << " The slime resists!"
+ return ..()
var/mob/living/simple_animal/slime/pet = new /mob/living/simple_animal/slime(M.loc)
pet.icon_state = "[M.colour] baby slime"
pet.icon_living = "[M.colour] baby slime"
pet.icon_dead = "[M.colour] baby slime dead"
pet.colour = "[M.colour]"
user <<"You feed the slime the potion, removing it's powers and calming it."
- del (M)
+ del(M)
var/newname = copytext(sanitize(input(user, "Would you like to give the slime a name?", "Name your new pet", "pet slime") as null|text),1,MAX_NAME_LEN)
if (!newname)
newname = "pet slime"
pet.name = newname
pet.real_name = newname
- del (src)
+ del(src)
/obj/item/weapon/slimepotion2
name = "advanced docility potion"
@@ -763,27 +774,30 @@ mob/living/carbon/slime/var/temperature_resistance = T0C+75
icon = 'icons/obj/chemical.dmi'
icon_state = "bottle19"
- attack(mob/living/carbon/slime/adult/M as mob, mob/user as mob)
- if(!istype(M, /mob/living/carbon/slime/adult))//If target is not a slime.
- user << "\red The potion only works on adult slimes!"
+ attack(mob/living/carbon/slime/M as mob, mob/user as mob)
+ if(!istype(M, /mob/living/carbon/slime/))//If target is not a slime.
+ user << " The potion only works on slimes!"
return ..()
if(M.stat)
- user << "\red The slime is dead!"
+ user << " The slime is dead!"
return..()
+ if(M.mind)
+ user << " The slime resists!"
+ return ..()
var/mob/living/simple_animal/adultslime/pet = new /mob/living/simple_animal/adultslime(M.loc)
pet.icon_state = "[M.colour] adult slime"
pet.icon_living = "[M.colour] adult slime"
pet.icon_dead = "[M.colour] baby slime dead"
pet.colour = "[M.colour]"
user <<"You feed the slime the potion, removing it's powers and calming it."
- del (M)
+ del(M)
var/newname = copytext(sanitize(input(user, "Would you like to give the slime a name?", "Name your new pet", "pet slime") as null|text),1,MAX_NAME_LEN)
if (!newname)
newname = "pet slime"
pet.name = newname
pet.real_name = newname
- del (src)
+ del(src)
/obj/item/weapon/slimesteroid
@@ -794,25 +808,45 @@ mob/living/carbon/slime/var/temperature_resistance = T0C+75
attack(mob/living/carbon/slime/M as mob, mob/user as mob)
if(!istype(M, /mob/living/carbon/slime))//If target is not a slime.
- user << "\red The steroid only works on baby slimes!"
+ user << " The steroid only works on baby slimes!"
return ..()
- if(istype(M, /mob/living/carbon/slime/adult)) //Can't tame adults
- user << "\red Only baby slimes can use the steroid!"
+ if(M.is_adult) //Can't tame adults
+ user << " Only baby slimes can use the steroid!"
return..()
if(M.stat)
- user << "\red The slime is dead!"
+ user << " The slime is dead!"
return..()
if(M.cores == 3)
- user <<"\red The slime already has the maximum amount of extract!"
+ user <<" The slime already has the maximum amount of extract!"
return..()
user <<"You feed the slime the steroid. It now has triple the amount of extract."
M.cores = 3
- del (src)
+ del(src)
+/obj/item/weapon/slimesteroid2
+ name = "extract enhancer"
+ desc = "A potent chemical mix that will give a slime extract three uses."
+ icon = 'icons/obj/chemical.dmi'
+ icon_state = "bottle17"
+
+ /*afterattack(obj/target, mob/user , flag)
+ if(istype(target, /obj/item/slime_extract))
+ if(target.enhanced == 1)
+ user << " This extract has already been enhanced!"
+ return ..()
+ if(target.Uses == 0)
+ user << " You can't enhance a used extract!"
+ return ..()
+ user <<"You apply the enhancer. It now has triple the amount of uses."
+ target.Uses = 3
+ target.enahnced = 1
+ del(src)*/
////////Adamantine Golem stuff I dunno where else to put it
+// This will eventually be removed.
+
/obj/item/clothing/under/golem
name = "adamantine skin"
desc = "a golem's skin"
@@ -949,6 +983,7 @@ mob/living/carbon/slime/var/temperature_resistance = T0C+75
var/area/A = get_area(src)
if(A)
G << "Golem rune created in [A.name]."
+
//////////////////////////////Old shit from metroids/RoRos, and the old cores, would not take much work to re-add them////////////////////////
/*
@@ -958,11 +993,10 @@ mob/living/carbon/slime/var/temperature_resistance = T0C+75
desc = "Goo extracted from a slime. Legends claim these to have \"magical powers\"."
icon = 'icons/mob/slimes.dmi'
icon_state = "slime extract"
- flags = TABLEPASS
force = 1.0
w_class = 1.0
throwforce = 1.0
- throw_speed = 3
+ throw_speed = 2
throw_range = 6
origin_tech = "biotech=4"
var/POWERFLAG = 0 // sshhhhhhh
@@ -971,9 +1005,7 @@ mob/living/carbon/slime/var/temperature_resistance = T0C+75
/obj/item/slime_core/New()
..()
- var/datum/reagents/R = new/datum/reagents(100)
- reagents = R
- R.my_atom = src
+ create_reagents(100)
POWERFLAG = rand(1,10)
Uses = rand(7, 25)
//flags |= NOREACT
@@ -1017,9 +1049,9 @@ mob/living/carbon/slime/var/temperature_resistance = T0C+75
/obj/item/weapon/reagent_containers/food/snacks/egg/slime/proc/Hatch()
processing_objects.Remove(src)
var/turf/T = get_turf(src)
- src.visible_message("\blue The [name] pulsates and quivers!")
+ src.visible_message(" The [name] pulsates and quivers!")
spawn(rand(50,100))
- src.visible_message("\blue The [name] bursts open!")
+ src.visible_message(" The [name] bursts open!")
new/mob/living/carbon/slime(T)
del(src)
@@ -1035,4 +1067,4 @@ mob/living/carbon/slime/var/temperature_resistance = T0C+75
return
else
..()
-*/
\ No newline at end of file
+*/
diff --git a/code/modules/mob/living/carbon/metroid/powers.dm b/code/modules/mob/living/carbon/metroid/powers.dm
index 707fc9cc158..bb7dcbe7bbc 100644
--- a/code/modules/mob/living/carbon/metroid/powers.dm
+++ b/code/modules/mob/living/carbon/metroid/powers.dm
@@ -11,12 +11,12 @@
var/list/choices = list()
for(var/mob/living/C in view(1,src))
- if(C!=src && !istype(C,/mob/living/carbon/slime))
+ if(C!=src && !istype(C,/mob/living/carbon/slime) && Adjacent(C))
choices += C
- var/mob/living/carbon/M = input(src,"Who do you wish to feed on?") in null|choices
+ var/mob/living/M = input(src,"Who do you wish to feed on?") in null|choices
if(!M) return
- if(M in view(1, src))
+ if(Adjacent(M))
if(!istype(src, /mob/living/carbon/brain))
if(!istype(M, /mob/living/carbon/slime))
@@ -42,76 +42,72 @@
-/mob/living/carbon/slime/proc/Feedon(var/mob/living/carbon/M)
+/mob/living/carbon/slime/proc/Feedon(var/mob/living/M)
Victim = M
src.loc = M.loc
canmove = 0
anchored = 1
var/lastnut = nutrition
- //if(M.client) M << "\red You legs become paralyzed!"
- if(istype(src, /mob/living/carbon/slime/adult))
+ var/fed_succesfully = 0
+ if(is_adult)
icon_state = "[colour] adult slime eat"
else
icon_state = "[colour] baby slime eat"
while(Victim && M.health > -70 && stat != 2)
- // M.canmove = 0
canmove = 0
- if(M in view(1, src))
+ if(Adjacent(M))
loc = M.loc
if(prob(15) && M.client && istype(M, /mob/living/carbon))
- M << "\red [pick("You can feel your body becoming weak!", \
+ M << "[pick("You can feel your body becoming weak!", \
"You feel like you're about to die!", \
"You feel every part of your body screaming in agony!", \
"A low, rolling pain passes through your body!", \
"Your body feels as if it's falling apart!", \
"You feel extremely weak!", \
- "A sharp, deep pain bathes every inch of your body!")]"
+ "A sharp, deep pain bathes every inch of your body!")]"
if(istype(M, /mob/living/carbon))
- Victim.adjustCloneLoss(rand(1,10))
+ Victim.adjustCloneLoss(rand(5,6))
Victim.adjustToxLoss(rand(1,2))
if(Victim.health <= 0)
Victim.adjustToxLoss(rand(2,4))
- // Heal yourself
- adjustToxLoss(-10)
- adjustOxyLoss(-10)
- adjustBruteLoss(-10)
- adjustFireLoss(-10)
- adjustCloneLoss(-10)
+ fed_succesfully = 1
- if(Victim)
- for(var/mob/living/carbon/slime/slime in view(1,M))
- if(slime.Victim == M && slime != src)
- slime.Feedstop()
+ else if(istype(M, /mob/living/simple_animal))
+ Victim.adjustBruteLoss(is_adult ? rand(7, 15) : rand(4, 12))
+ fed_succesfully = 1
- nutrition += rand(10,25)
+ else
+ if(prob(25))
+ src << "[pick("This subject is incompatable", \
+ "This subject does not have a life energy", "This subject is empty", \
+ "I am not satisified", "I can not feed from this subject", \
+ "I do not feel nourished", "This subject is not food")]..."
+
+ if(fed_succesfully)
+ //I have no idea why this is not in handle_nutrition()
+ nutrition += rand(15,30)
if(nutrition >= lastnut + 50)
if(prob(80))
lastnut = nutrition
powerlevel++
if(powerlevel > 10)
powerlevel = 10
+ adjustToxLoss(-10)
- if(istype(src, /mob/living/carbon/slime/adult))
- if(nutrition > 1200)
- nutrition = 1200
- else
- if(nutrition > 1000)
- nutrition = 1000
-
- Victim.updatehealth()
+ //Heal yourself.
+ adjustOxyLoss(-10)
+ adjustBruteLoss(-10)
+ adjustFireLoss(-10)
+ adjustCloneLoss(-10)
+
updatehealth()
-
- else
- if(prob(25))
- src << "\red [pick("This subject is incompatable", \
- "This subject does not have a life energy", "This subject is empty", \
- "I am not satisified", "I can not feed from this subject", \
- "I do not feel nourished", "This subject is not food")]..."
+ if(Victim)
+ Victim.updatehealth()
sleep(rand(15,45))
@@ -119,11 +115,11 @@
break
if(stat == 2)
- if(!istype(src, /mob/living/carbon/slime/adult))
+ if(!is_adult)
icon_state = "[colour] baby slime dead"
else
- if(istype(src, /mob/living/carbon/slime/adult))
+ if(is_adult)
icon_state = "[colour] adult slime"
else
icon_state = "[colour] baby slime"
@@ -139,7 +135,11 @@
if(Victim.LAssailant && Victim.LAssailant != Victim)
if(prob(50))
if(!(Victim.LAssailant in Friends))
- Friends.Add(Victim.LAssailant) // no idea why i was using the |= operator
+ Friends[Victim.LAssailant] = 1
+ //Friends.Add(Victim.LAssailant) // no idea why i was using the |= operator
+ else
+ ++Friends[Victim.LAssailant]
+
if(M.client && istype(src, /mob/living/carbon/human))
if(prob(85))
@@ -173,16 +173,13 @@
if(stat)
src << "I must be conscious to do this..."
return
- if(!istype(src, /mob/living/carbon/slime/adult))
+ if(!is_adult)
if(amount_grown >= 10)
- var/mob/living/carbon/slime/adult/new_slime = new adulttype(loc)
- new_slime.nutrition = nutrition
- new_slime.powerlevel = max(0, powerlevel-1)
- new_slime.a_intent = "hurt"
- new_slime.key = key
- new_slime.universal_speak = universal_speak
- new_slime << "You are now an adult slime."
- del(src)
+ is_adult = 1
+ maxHealth = 200
+ amount_grown = 0
+ regenerate_icons()
+ name = text("[colour] [is_adult ? "adult" : "baby"] slime ([number])")
else
src << "I am not ready to evolve yet..."
else
@@ -190,15 +187,14 @@
/mob/living/carbon/slime/verb/Reproduce()
set category = "Slime"
- set desc = "This will make you split into four Slimes. NOTE: this will KILL you, but you will be transferred into one of the babies."
+ set desc = "This will make you split into four Slimes."
if(stat)
src << "I must be conscious to do this..."
return
- if(istype(src, /mob/living/carbon/slime/adult))
+ if(is_adult)
if(amount_grown >= 10)
- //if(input("Are you absolutely sure you want to reproduce? Your current body will cease to be, but your consciousness will be transferred into a produced slime.") in list("Yes","No")=="Yes")
if(stat)
src << "I must be conscious to do this..."
return
@@ -207,54 +203,30 @@
var/new_nutrition = round(nutrition * 0.9)
var/new_powerlevel = round(powerlevel / 4)
for(var/i=1,i<=4,i++)
- if(prob(80))
- var/mob/living/carbon/slime/M = new primarytype(loc)
- M.nutrition = new_nutrition
- M.powerlevel = new_powerlevel
- if(i != 1) step_away(M,src)
- babies += M
+ var/mob/living/carbon/slime/M = new /mob/living/carbon/slime/(loc)
+ if(prob(mutation_chance))
+ M.colour = slime_mutation[rand(1,4)]
else
- var/mutations = pick("one","two","three","four")
- switch(mutations)
- if("one")
- var/mob/living/carbon/slime/M = new mutationone(loc)
- M.nutrition = new_nutrition
- M.powerlevel = new_powerlevel
- if(i != 1) step_away(M,src)
- babies += M
- if("two")
- var/mob/living/carbon/slime/M = new mutationtwo(loc)
- M.nutrition = new_nutrition
- M.powerlevel = new_powerlevel
- if(i != 1) step_away(M,src)
- babies += M
- if("three")
- var/mob/living/carbon/slime/M = new mutationthree(loc)
- M.nutrition = new_nutrition
- M.powerlevel = new_powerlevel
- if(i != 1) step_away(M,src)
- babies += M
- if("four")
- var/mob/living/carbon/slime/M = new mutationfour(loc)
- M.nutrition = new_nutrition
- M.powerlevel = new_powerlevel
- if(i != 1) step_away(M,src)
- babies += M
+ M.colour = colour
+ if(ckey) M.nutrition = new_nutrition //Player slimes are more robust at spliting. Once an oversight of poor copypasta, now a feature!
+ M.powerlevel = new_powerlevel
+ if(i != 1) step_away(M,src)
+ M.Friends = Friends.Copy()
+ babies += M
+ feedback_add_details("slime_babies_born","slimebirth_[replacetext(M.colour," ","_")]")
var/mob/living/carbon/slime/new_slime = pick(babies)
- new_slime.a_intent = "hurt"
new_slime.universal_speak = universal_speak
- new_slime.key = key
-
- new_slime << "You are now a slime!"
+ if(src.mind)
+ src.mind.transfer_to(new_slime)
+ else
+ new_slime.key = src.key
del(src)
else
src << "I am not ready to reproduce yet..."
else
src << "I am not old enough to reproduce yet..."
-
-
/mob/living/carbon/slime/verb/ventcrawl()
set name = "Crawl through Vent"
set desc = "Enter an air vent and crawl through the pipe system."
diff --git a/code/modules/mob/living/carbon/metroid/say.dm b/code/modules/mob/living/carbon/metroid/say.dm
new file mode 100644
index 00000000000..89708595da4
--- /dev/null
+++ b/code/modules/mob/living/carbon/metroid/say.dm
@@ -0,0 +1,23 @@
+/mob/living/carbon/slime/say(var/message)
+ var/verb = say_quote(message)
+
+ if(copytext(message,1,2) == "*")
+ return emote(copytext(message,2))
+
+ return ..(message, null, verb)
+
+/mob/living/carbon/slime/say_quote(var/text)
+ var/ending = copytext(text, length(text))
+
+ if (ending == "?")
+ return "telepathically asks";
+ else if (ending == "!")
+ return "telepathically cries";
+
+ return "telepathically chirps";
+
+/mob/living/carbon/slime/say_understands(var/other)
+ if (istype(other, /mob/living/carbon/slime))
+ return 1
+ return ..()
+
diff --git a/code/modules/mob/living/carbon/metroid/subtypes.dm b/code/modules/mob/living/carbon/metroid/subtypes.dm
index 4eb4087f6ec..3b7e0107abb 100644
--- a/code/modules/mob/living/carbon/metroid/subtypes.dm
+++ b/code/modules/mob/living/carbon/metroid/subtypes.dm
@@ -1,372 +1,79 @@
-
-////////////////Tier 2
-
-/mob/living/carbon/slime/purple
- colour = "purple"
- icon_state = "purple baby slime"
- primarytype = /mob/living/carbon/slime/purple
- mutationone = /mob/living/carbon/slime/darkpurple
- mutationtwo = /mob/living/carbon/slime/darkblue
- mutationthree = /mob/living/carbon/slime/green
- mutationfour = /mob/living/carbon/slime/green
- adulttype = /mob/living/carbon/slime/adult/purple
- coretype = /obj/item/slime_extract/purple
-
-/mob/living/carbon/slime/adult/purple
- icon_state = "purple adult slime"
- colour = "purple"
- primarytype = /mob/living/carbon/slime/purple
- mutationone = /mob/living/carbon/slime/darkpurple
- mutationtwo = /mob/living/carbon/slime/darkblue
- mutationthree = /mob/living/carbon/slime/green
- mutationfour = /mob/living/carbon/slime/green
- adulttype = /mob/living/carbon/slime/adult/purple
- coretype = /obj/item/slime_extract/purple
-
-/mob/living/carbon/slime/metal
- colour = "metal"
- icon_state = "metal baby slime"
- primarytype = /mob/living/carbon/slime/metal
- mutationone = /mob/living/carbon/slime/silver
- mutationtwo = /mob/living/carbon/slime/yellow
- mutationthree = /mob/living/carbon/slime/gold
- mutationfour = /mob/living/carbon/slime/gold
- adulttype = /mob/living/carbon/slime/adult/metal
- coretype = /obj/item/slime_extract/metal
-
-/mob/living/carbon/slime/adult/metal
- icon_state = "metal adult slime"
- colour = "metal"
- primarytype = /mob/living/carbon/slime/metal
- mutationone = /mob/living/carbon/slime/silver
- mutationtwo = /mob/living/carbon/slime/yellow
- mutationthree = /mob/living/carbon/slime/gold
- mutationfour = /mob/living/carbon/slime/gold
- adulttype = /mob/living/carbon/slime/adult/metal
- coretype = /obj/item/slime_extract/metal
-
-/mob/living/carbon/slime/orange
- colour = "orange"
- icon_state = "orange baby slime"
- primarytype = /mob/living/carbon/slime/orange
- mutationone = /mob/living/carbon/slime/red
- mutationtwo = /mob/living/carbon/slime/red
- mutationthree = /mob/living/carbon/slime/darkpurple
- mutationfour = /mob/living/carbon/slime/yellow
- adulttype = /mob/living/carbon/slime/adult/orange
- coretype = /obj/item/slime_extract/orange
-
-/mob/living/carbon/slime/adult/orange
- colour = "orange"
- icon_state = "orange adult slime"
- primarytype = /mob/living/carbon/slime/orange
- mutationone = /mob/living/carbon/slime/red
- mutationtwo = /mob/living/carbon/slime/red
- mutationthree = /mob/living/carbon/slime/darkpurple
- mutationfour = /mob/living/carbon/slime/yellow
- adulttype = /mob/living/carbon/slime/adult/orange
- coretype = /obj/item/slime_extract/orange
-
-/mob/living/carbon/slime/blue
- colour = "blue"
- icon_state = "blue baby slime"
- primarytype = /mob/living/carbon/slime/blue
- mutationone = /mob/living/carbon/slime/darkblue
- mutationtwo = /mob/living/carbon/slime/pink
- mutationthree = /mob/living/carbon/slime/pink
- mutationfour = /mob/living/carbon/slime/silver
- adulttype = /mob/living/carbon/slime/adult/blue
- coretype = /obj/item/slime_extract/blue
-
-/mob/living/carbon/slime/adult/blue
- icon_state = "blue adult slime"
- colour = "blue"
- primarytype = /mob/living/carbon/slime/blue
- mutationone = /mob/living/carbon/slime/darkblue
- mutationtwo = /mob/living/carbon/slime/pink
- mutationthree = /mob/living/carbon/slime/silver
- mutationfour = /mob/living/carbon/slime/pink
- adulttype = /mob/living/carbon/slime/adult/blue
- coretype = /obj/item/slime_extract/blue
-
-
-
-
-//Tier 3
-
-/mob/living/carbon/slime/darkblue
- colour = "dark blue"
- icon_state = "dark blue baby slime"
- primarytype = /mob/living/carbon/slime/darkblue
- mutationone = /mob/living/carbon/slime/purple
- mutationtwo = /mob/living/carbon/slime/purple
- mutationthree = /mob/living/carbon/slime/blue
- mutationfour = /mob/living/carbon/slime/blue
- adulttype = /mob/living/carbon/slime/adult/darkblue
- coretype = /obj/item/slime_extract/darkblue
-
-/mob/living/carbon/slime/adult/darkblue
- icon_state = "dark blue adult slime"
- colour = "dark blue"
- primarytype = /mob/living/carbon/slime/darkblue
- mutationone = /mob/living/carbon/slime/purple
- mutationtwo = /mob/living/carbon/slime/purple
- mutationthree = /mob/living/carbon/slime/blue
- mutationfour = /mob/living/carbon/slime/blue
- adulttype = /mob/living/carbon/slime/adult/darkblue
- coretype = /obj/item/slime_extract/darkblue
-
-/mob/living/carbon/slime/darkpurple
- colour = "dark purple"
- icon_state = "dark purple baby slime"
- primarytype = /mob/living/carbon/slime/darkpurple
- mutationone = /mob/living/carbon/slime/purple
- mutationtwo = /mob/living/carbon/slime/purple
- mutationthree = /mob/living/carbon/slime/orange
- mutationfour = /mob/living/carbon/slime/orange
- adulttype = /mob/living/carbon/slime/adult/darkpurple
- coretype = /obj/item/slime_extract/darkpurple
-
-/mob/living/carbon/slime/adult/darkpurple
- icon_state = "dark purple adult slime"
- colour = "dark purple"
- primarytype = /mob/living/carbon/slime/darkpurple
- mutationone = /mob/living/carbon/slime/purple
- mutationtwo = /mob/living/carbon/slime/purple
- mutationthree = /mob/living/carbon/slime/orange
- mutationfour = /mob/living/carbon/slime/orange
- adulttype = /mob/living/carbon/slime/adult/darkpurple
- coretype = /obj/item/slime_extract/darkpurple
-
-
-/mob/living/carbon/slime/yellow
- icon_state = "yellow baby slime"
- colour = "yellow"
- primarytype = /mob/living/carbon/slime/yellow
- mutationone = /mob/living/carbon/slime/metal
- mutationtwo = /mob/living/carbon/slime/metal
- mutationthree = /mob/living/carbon/slime/orange
- mutationfour = /mob/living/carbon/slime/orange
- adulttype = /mob/living/carbon/slime/adult/yellow
- coretype = /obj/item/slime_extract/yellow
-
-/mob/living/carbon/slime/adult/yellow
- icon_state = "yellow adult slime"
- colour = "yellow"
- primarytype = /mob/living/carbon/slime/yellow
- mutationone = /mob/living/carbon/slime/metal
- mutationtwo = /mob/living/carbon/slime/metal
- mutationthree = /mob/living/carbon/slime/orange
- mutationfour = /mob/living/carbon/slime/orange
- adulttype = /mob/living/carbon/slime/adult/yellow
- coretype = /obj/item/slime_extract/yellow
-
-
-/mob/living/carbon/slime/silver
- colour = "silver"
- icon_state = "silver baby slime"
- primarytype = /mob/living/carbon/slime/silver
- mutationone = /mob/living/carbon/slime/metal
- mutationtwo = /mob/living/carbon/slime/metal
- mutationthree = /mob/living/carbon/slime/blue
- mutationfour = /mob/living/carbon/slime/blue
- adulttype = /mob/living/carbon/slime/adult/silver
- coretype = /obj/item/slime_extract/silver
-
-/mob/living/carbon/slime/adult/silver
- icon_state = "silver adult slime"
- colour = "silver"
- primarytype = /mob/living/carbon/slime/silver
- mutationone = /mob/living/carbon/slime/metal
- mutationtwo = /mob/living/carbon/slime/metal
- mutationthree = /mob/living/carbon/slime/blue
- mutationfour = /mob/living/carbon/slime/blue
- adulttype = /mob/living/carbon/slime/adult/silver
- coretype = /obj/item/slime_extract/silver
-
-
-
-/////////////Tier 4
-
-
-/mob/living/carbon/slime/pink
- colour = "pink"
- icon_state = "pink baby slime"
- primarytype = /mob/living/carbon/slime/pink
- mutationone = /mob/living/carbon/slime/pink
- mutationtwo = /mob/living/carbon/slime/pink
- mutationthree = /mob/living/carbon/slime/lightpink
- mutationfour = /mob/living/carbon/slime/lightpink
- adulttype = /mob/living/carbon/slime/adult/pink
- coretype = /obj/item/slime_extract/pink
-
-/mob/living/carbon/slime/adult/pink
- icon_state = "pink adult slime"
- colour = "pink"
- primarytype = /mob/living/carbon/slime/pink
- mutationone = /mob/living/carbon/slime/pink
- mutationtwo = /mob/living/carbon/slime/pink
- mutationthree = /mob/living/carbon/slime/lightpink
- mutationfour = /mob/living/carbon/slime/lightpink
- adulttype = /mob/living/carbon/slime/adult/pink
- coretype = /obj/item/slime_extract/pink
-
-/mob/living/carbon/slime/red
- colour = "red"
- icon_state = "red baby slime"
- primarytype = /mob/living/carbon/slime/red
- mutationone = /mob/living/carbon/slime/red
- mutationtwo = /mob/living/carbon/slime/red
- mutationthree = /mob/living/carbon/slime/oil
- mutationfour = /mob/living/carbon/slime/oil
- adulttype = /mob/living/carbon/slime/adult/red
- coretype = /obj/item/slime_extract/red
-
-/mob/living/carbon/slime/adult/red
- icon_state = "red adult slime"
- colour = "red"
- primarytype = /mob/living/carbon/slime/red
- mutationone = /mob/living/carbon/slime/red
- mutationtwo = /mob/living/carbon/slime/red
- mutationthree = /mob/living/carbon/slime/oil
- mutationfour = /mob/living/carbon/slime/oil
- adulttype = /mob/living/carbon/slime/adult/red
- coretype = /obj/item/slime_extract/red
-
-/mob/living/carbon/slime/gold
- colour = "gold"
- icon_state = "gold baby slime"
- primarytype = /mob/living/carbon/slime/gold
- mutationone = /mob/living/carbon/slime/gold
- mutationtwo = /mob/living/carbon/slime/gold
- mutationthree = /mob/living/carbon/slime/adamantine
- mutationfour = /mob/living/carbon/slime/adamantine
- adulttype = /mob/living/carbon/slime/adult/gold
- coretype = /obj/item/slime_extract/gold
-
-/mob/living/carbon/slime/adult/gold
- icon_state = "gold adult slime"
- colour = "gold"
- primarytype = /mob/living/carbon/slime/gold
- mutationone = /mob/living/carbon/slime/gold
- mutationtwo = /mob/living/carbon/slime/gold
- mutationthree = /mob/living/carbon/slime/adamantine
- mutationfour = /mob/living/carbon/slime/adamantine
- adulttype = /mob/living/carbon/slime/adult/gold
- coretype = /obj/item/slime_extract/gold
-
-/mob/living/carbon/slime/green
- colour = "green"
- icon_state = "green baby slime"
- primarytype = /mob/living/carbon/slime/green
- mutationone = /mob/living/carbon/slime/green
- mutationtwo = /mob/living/carbon/slime/green
- mutationthree = /mob/living/carbon/slime/black
- mutationfour = /mob/living/carbon/slime/black
- adulttype = /mob/living/carbon/slime/adult/green
- coretype = /obj/item/slime_extract/green
-
-/mob/living/carbon/slime/adult/green
- icon_state = "green adult slime"
- colour = "green"
- primarytype = /mob/living/carbon/slime/green
- mutationone = /mob/living/carbon/slime/green
- mutationtwo = /mob/living/carbon/slime/green
- mutationthree = /mob/living/carbon/slime/black
- mutationfour = /mob/living/carbon/slime/black
- adulttype = /mob/living/carbon/slime/adult/green
- coretype = /obj/item/slime_extract/green
-
-
-
-// Tier 5
-
-/mob/living/carbon/slime/lightpink
- colour = "light pink"
- icon_state = "light pink baby slime"
- primarytype = /mob/living/carbon/slime/lightpink
- mutationone = /mob/living/carbon/slime/lightpink
- mutationtwo = /mob/living/carbon/slime/lightpink
- mutationthree = /mob/living/carbon/slime/lightpink
- mutationfour = /mob/living/carbon/slime/lightpink
- adulttype = /mob/living/carbon/slime/adult/lightpink
- coretype = /obj/item/slime_extract/lightpink
-
-/mob/living/carbon/slime/adult/lightpink
- icon_state = "light pink adult slime"
- colour = "light pink"
- primarytype = /mob/living/carbon/slime/lightpink
- mutationone = /mob/living/carbon/slime/lightpink
- mutationtwo = /mob/living/carbon/slime/lightpink
- mutationthree = /mob/living/carbon/slime/lightpink
- mutationfour = /mob/living/carbon/slime/lightpink
- adulttype = /mob/living/carbon/slime/adult/lightpink
- coretype = /obj/item/slime_extract/lightpink
-
-
-/mob/living/carbon/slime/oil
- icon_state = "oil baby slime"
- colour = "oil"
- primarytype = /mob/living/carbon/slime/oil
- mutationone = /mob/living/carbon/slime/oil
- mutationtwo = /mob/living/carbon/slime/oil
- mutationthree = /mob/living/carbon/slime/oil
- mutationfour = /mob/living/carbon/slime/oil
- adulttype = /mob/living/carbon/slime/adult/oil
- coretype = /obj/item/slime_extract/oil
-
-/mob/living/carbon/slime/adult/oil
- icon_state = "oil adult slime"
- colour = "oil"
- primarytype = /mob/living/carbon/slime/oil
- mutationone = /mob/living/carbon/slime/oil
- mutationtwo = /mob/living/carbon/slime/oil
- mutationthree = /mob/living/carbon/slime/oil
- mutationfour = /mob/living/carbon/slime/oil
- adulttype = /mob/living/carbon/slime/adult/oil
- coretype = /obj/item/slime_extract/oil
-
-
-/mob/living/carbon/slime/black
- icon_state = "black baby slime"
- colour = "black"
- primarytype = /mob/living/carbon/slime/black
- mutationone = /mob/living/carbon/slime/black
- mutationtwo = /mob/living/carbon/slime/black
- mutationthree = /mob/living/carbon/slime/black
- mutationfour = /mob/living/carbon/slime/black
- adulttype = /mob/living/carbon/slime/adult/black
- coretype = /obj/item/slime_extract/black
-
-/mob/living/carbon/slime/adult/black
- icon_state = "black adult slime"
- colour = "black"
- primarytype = /mob/living/carbon/slime/black
- mutationone = /mob/living/carbon/slime/black
- mutationtwo = /mob/living/carbon/slime/black
- mutationthree = /mob/living/carbon/slime/black
- mutationfour = /mob/living/carbon/slime/black
- adulttype = /mob/living/carbon/slime/adult/black
- coretype = /obj/item/slime_extract/black
-
-/mob/living/carbon/slime/adamantine
- icon_state = "adamantine baby slime"
- colour = "adamantine"
- primarytype = /mob/living/carbon/slime/adamantine
- mutationone = /mob/living/carbon/slime/adamantine
- mutationtwo = /mob/living/carbon/slime/adamantine
- mutationthree = /mob/living/carbon/slime/adamantine
- mutationfour = /mob/living/carbon/slime/adamantine
- adulttype = /mob/living/carbon/slime/adult/adamantine
- coretype = /obj/item/slime_extract/adamantine
-
-/mob/living/carbon/slime/adult/adamantine
- icon_state = "adamantine adult slime"
- colour = "adamantine"
- primarytype = /mob/living/carbon/slime/adamantine
- mutationone = /mob/living/carbon/slime/adamantine
- mutationtwo = /mob/living/carbon/slime/adamantine
- mutationthree = /mob/living/carbon/slime/adamantine
- mutationfour = /mob/living/carbon/slime/adamantine
- adulttype = /mob/living/carbon/slime/adult/adamantine
- coretype = /obj/item/slime_extract/adamantine
\ No newline at end of file
+proc/mutation_table(var/colour)
+ var/list/slime_mutation[4]
+ switch(colour)
+ //Tier 1
+ if("grey")
+ slime_mutation[1] = "orange"
+ slime_mutation[2] = "metal"
+ slime_mutation[3] = "blue"
+ slime_mutation[4] = "purple"
+ //Tier 2
+ if("purple")
+ slime_mutation[1] = "dark purple"
+ slime_mutation[2] = "dark blue"
+ slime_mutation[3] = "green"
+ slime_mutation[4] = "green"
+ if("metal")
+ slime_mutation[1] = "silver"
+ slime_mutation[2] = "yellow"
+ slime_mutation[3] = "gold"
+ slime_mutation[4] = "gold"
+ if("orange")
+ slime_mutation[1] = "dark purple"
+ slime_mutation[2] = "yellow"
+ slime_mutation[3] = "red"
+ slime_mutation[4] = "red"
+ if("blue")
+ slime_mutation[1] = "dark blue"
+ slime_mutation[2] = "silver"
+ slime_mutation[3] = "pink"
+ slime_mutation[4] = "pink"
+ //Tier 3
+ if("dark blue")
+ slime_mutation[1] = "purple"
+ slime_mutation[2] = "blue"
+ slime_mutation[3] = "cerulean"
+ slime_mutation[4] = "cerulean"
+ if("dark purple")
+ slime_mutation[1] = "purple"
+ slime_mutation[2] = "orange"
+ slime_mutation[3] = "sepia"
+ slime_mutation[4] = "sepia"
+ if("yellow")
+ slime_mutation[1] = "metal"
+ slime_mutation[2] = "orange"
+ slime_mutation[3] = "bluespace"
+ slime_mutation[4] = "bluespace"
+ if("silver")
+ slime_mutation[1] = "metal"
+ slime_mutation[2] = "blue"
+ slime_mutation[3] = "pyrite"
+ slime_mutation[4] = "pyrite"
+ //Tier 4
+ if("pink")
+ slime_mutation[1] = "pink"
+ slime_mutation[2] = "pink"
+ slime_mutation[3] = "light pink"
+ slime_mutation[4] = "light pink"
+ if("red")
+ slime_mutation[1] = "red"
+ slime_mutation[2] = "red"
+ slime_mutation[3] = "oil"
+ slime_mutation[4] = "oil"
+ if("gold")
+ slime_mutation[1] = "gold"
+ slime_mutation[2] = "gold"
+ slime_mutation[3] = "adamantine"
+ slime_mutation[4] = "adamantine"
+ if("green")
+ slime_mutation[1] = "green"
+ slime_mutation[2] = "green"
+ slime_mutation[3] = "black"
+ slime_mutation[4] = "black"
+ // Tier 5
+ else
+ slime_mutation[1] = colour
+ slime_mutation[2] = colour
+ slime_mutation[3] = colour
+ slime_mutation[4] = colour
+ return(slime_mutation)
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/monkey/diona.dm b/code/modules/mob/living/carbon/monkey/diona.dm
index b0e885e6c98..0a123347010 100644
--- a/code/modules/mob/living/carbon/monkey/diona.dm
+++ b/code/modules/mob/living/carbon/monkey/diona.dm
@@ -107,11 +107,11 @@
set desc = "Turn your food into nutrients for plants."
var/list/trays = list()
- for(var/obj/machinery/hydroponics/tray in range(1))
+ for(var/obj/machinery/portable_atmospherics/hydroponics/tray in range(1))
if(tray.nutrilevel < 10 && src.Adjacent(tray))
trays += tray
- var/obj/machinery/hydroponics/target = input("Select a tray:") as null|anything in trays
+ var/obj/machinery/portable_atmospherics/hydroponics/target = input("Select a tray:") as null|anything in trays
if(!src || !target || target.nutrilevel == 10) return //Sanity check.
@@ -126,11 +126,11 @@
set desc = "Clean the weeds out of soil or a hydroponics tray."
var/list/trays = list()
- for(var/obj/machinery/hydroponics/tray in range(1))
+ for(var/obj/machinery/portable_atmospherics/hydroponics/tray in range(1))
if(tray.weedlevel > 0 && src.Adjacent(tray))
trays += tray
- var/obj/machinery/hydroponics/target = input("Select a tray:") as null|anything in trays
+ var/obj/machinery/portable_atmospherics/hydroponics/target = input("Select a tray:") as null|anything in trays
if(!src || !target || target.weedlevel == 0) return //Sanity check.
diff --git a/code/modules/mob/living/carbon/monkey/life.dm b/code/modules/mob/living/carbon/monkey/life.dm
index ead74446710..ce681063582 100644
--- a/code/modules/mob/living/carbon/monkey/life.dm
+++ b/code/modules/mob/living/carbon/monkey/life.dm
@@ -232,25 +232,14 @@
var/obj/location_as_object = loc
breath = location_as_object.handle_internal_lifeform(src, BREATH_VOLUME)
else if(istype(loc, /turf/))
- var/breath_moles = environment.total_moles()*BREATH_PERCENTAGE
+ var/breath_moles = environment.total_moles*BREATH_PERCENTAGE
breath = loc.remove_air(breath_moles)
- if(istype(wear_mask, /obj/item/clothing/mask/gas))
- var/obj/item/clothing/mask/gas/G = wear_mask
- var/datum/gas_mixture/filtered = new
-
- filtered.copy_from(breath)
- filtered.phoron *= G.gas_filter_strength
- for(var/datum/gas/gas in filtered.trace_gases)
- gas.moles *= G.gas_filter_strength
- filtered.update_values()
+ if(istype(wear_mask, /obj/item/clothing/mask) && breath)
+ var/obj/item/clothing/mask/M = wear_mask
+ var/datum/gas_mixture/filtered = M.filter_air(breath)
loc.assume_air(filtered)
- breath.phoron *= 1 - G.gas_filter_strength
- for(var/datum/gas/gas in breath.trace_gases)
- gas.moles *= 1 - G.gas_filter_strength
- breath.update_values()
-
// Handle chem smoke effect -- Doohl
var/block = 0
if(wear_mask)
@@ -311,14 +300,14 @@
var/SA_para_min = 0.5
var/SA_sleep_min = 5
var/oxygen_used = 0
- var/breath_pressure = (breath.total_moles()*R_IDEAL_GAS_EQUATION*breath.temperature)/BREATH_VOLUME
+ var/breath_pressure = (breath.total_moles * R_IDEAL_GAS_EQUATION * breath.temperature) / BREATH_VOLUME
//Partial pressure of the O2 in our breath
- var/O2_pp = (breath.oxygen/breath.total_moles())*breath_pressure
+ var/O2_pp = (breath.gas["oxygen"] / breath.total_moles) * breath_pressure
// Same, but for the phoron
- var/Toxins_pp = (breath.phoron/breath.total_moles())*breath_pressure
+ var/Toxins_pp = (breath.gas["phoron"] / breath.total_moles) * breath_pressure
// And CO2, lets say a PP of more than 10 will be bad (It's a little less really, but eh, being passed out all round aint no fun)
- var/CO2_pp = (breath.carbon_dioxide/breath.total_moles())*breath_pressure
+ var/CO2_pp = (breath.gas["carbon_dioxide"] / breath.total_moles) * breath_pressure
if(O2_pp < safe_oxygen_min) // Too little oxygen
if(prob(20))
@@ -327,7 +316,7 @@
O2_pp = 0.01
var/ratio = safe_oxygen_min/O2_pp
adjustOxyLoss(min(5*ratio, 7)) // Don't fuck them up too fast (space only does 7 after all!)
- oxygen_used = breath.oxygen*ratio/6
+ oxygen_used = breath.gas["oxygen"] * ratio / 6
oxygen_alert = max(oxygen_alert, 1)
/*else if (O2_pp > safe_oxygen_max) // Too much oxygen (commented this out for now, I'll deal with pressure damage elsewhere I suppose)
spawn(0) emote("cough")
@@ -337,11 +326,11 @@
oxygen_alert = max(oxygen_alert, 1)*/
else // We're in safe limits
adjustOxyLoss(-5)
- oxygen_used = breath.oxygen/6
+ oxygen_used = breath.gas["oxygen"] / 6
oxygen_alert = 0
- breath.oxygen -= oxygen_used
- breath.carbon_dioxide += oxygen_used
+ breath.adjust_gas("oxygen", -oxygen_used)
+ breath.adjust_gas("carbon_dioxide", oxygen_used)
if(CO2_pp > safe_co2_max)
if(!co2overloadtime) // If it's the first breath with too much CO2 in it, lets start a counter, then have them pass out after 12s or so.
@@ -358,7 +347,7 @@
co2overloadtime = 0
if(Toxins_pp > safe_phoron_max) // Too much phoron
- var/ratio = (breath.phoron/safe_phoron_max) * 10
+ var/ratio = (breath.gas["phoron"] / safe_phoron_max) * 10
//adjustToxLoss(Clamp(ratio, MIN_PLASMA_DAMAGE, MAX_PLASMA_DAMAGE)) //Limit amount of damage toxin exposure can do per second
if(reagents)
reagents.add_reagent("toxin", Clamp(ratio, MIN_TOXIN_DAMAGE, MAX_TOXIN_DAMAGE))
@@ -366,16 +355,15 @@
else
phoron_alert = 0
- if(breath.trace_gases.len) // If there's some other shit in the air lets deal with it here.
- for(var/datum/gas/sleeping_agent/SA in breath.trace_gases)
- var/SA_pp = (SA.moles/breath.total_moles())*breath_pressure
- if(SA_pp > SA_para_min) // Enough to make us paralysed for a bit
- Paralyse(3) // 3 gives them one second to wake up and run away a bit!
- if(SA_pp > SA_sleep_min) // Enough to make us sleep as well
- sleeping = max(sleeping+2, 10)
- else if(SA_pp > 0.01) // There is sleeping gas in their lungs, but only a little, so give them a bit of a warning
- if(prob(20))
- spawn(0) emote(pick("giggle", "laugh"))
+ if(breath.gas["sleeping_agent"])
+ var/SA_pp = (breath.gas["sleeping_agent"] / breath.total_moles) * breath_pressure
+ if(SA_pp > SA_para_min) // Enough to make us paralysed for a bit
+ Paralyse(3) // 3 gives them one second to wake up and run away a bit!
+ if(SA_pp > SA_sleep_min) // Enough to make us sleep as well
+ sleeping = max(sleeping+2, 10)
+ else if(SA_pp > 0.01) // There is sleeping gas in their lungs, but only a little, so give them a bit of a warning
+ if(prob(20))
+ spawn(0) emote(pick("giggle", "laugh"))
if(breath.temperature > (T0C+66)) // Hot air hurts :(
@@ -396,7 +384,8 @@
//Moved these vars here for use in the fuck-it-skip-processing check.
var/pressure = environment.return_pressure()
- if(pressure < WARNING_HIGH_PRESSURE && pressure > WARNING_LOW_PRESSURE && abs(environment.temperature - 293.15) < 20 && abs(bodytemperature - 310.14) < 0.5 && environment.phoron < MOLES_PHORON_VISIBLE)
+ if(pressure < WARNING_HIGH_PRESSURE && pressure > WARNING_LOW_PRESSURE && abs(environment.temperature - 293.15) < 20 && abs(bodytemperature - 310.14) < 0.5 && environment.gas["phoron"] < MOLES_PHORON_VISIBLE)
+
//Hopefully should fix the walk-inside-still-pressure-warning issue.
if(pressure_alert)
@@ -560,20 +549,7 @@
ear_damage = max(ear_damage-0.05, 0)
//Other
- if(stunned)
- AdjustStunned(-1)
-
- if(weakened)
- weakened = max(weakened-1,0) //before you get mad Rockdtben: I done this so update_canmove isn't called multiple times
-
- if(stuttering)
- stuttering = max(stuttering-1, 0)
-
- if(silent)
- silent = max(silent-1, 0)
-
- if(druggy)
- druggy = max(druggy-1, 0)
+ handle_statuses()
return 1
diff --git a/code/modules/mob/living/carbon/monkey/monkey.dm b/code/modules/mob/living/carbon/monkey/monkey.dm
index 4eb294c8a49..7bb9af2a158 100644
--- a/code/modules/mob/living/carbon/monkey/monkey.dm
+++ b/code/modules/mob/living/carbon/monkey/monkey.dm
@@ -246,10 +246,9 @@
help_shake_act(M)
else
if (M.a_intent == "hurt")
+ var/datum/unarmed_attack/attack = M.species.unarmed
if ((prob(75) && health > 0))
- for(var/mob/O in viewers(src, null))
- if ((O.client && !( O.blinded )))
- O.show_message(text("\red [] has punched [name]!", M), 1)
+ visible_message("\red [M] [pick(attack.attack_verb)]ed [src]!")
playsound(loc, "punch", 25, 1, -1)
var/damage = rand(5, 10)
@@ -257,18 +256,18 @@
damage = rand(10, 15)
if (paralysis < 5)
Paralyse(rand(10, 15))
- spawn( 0 )
- for(var/mob/O in viewers(src, null))
- if ((O.client && !( O.blinded )))
- O.show_message(text("\red [] has knocked out [name]!", M), 1)
- return
+ visible_message("\red [M] has knocked out [src]!")
+
adjustBruteLoss(damage)
+
+ M.attack_log += text("\[[time_stamp()]\] [pick(attack.attack_verb)]ed [src.name] ([src.ckey])")
+ src.attack_log += text("\[[time_stamp()]\] Has been [pick(attack.attack_verb)]ed by [M.name] ([M.ckey])")
+ msg_admin_attack("[key_name(M)] [pick(attack.attack_verb)]ed [key_name(src)]")
+
updatehealth()
else
playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
- for(var/mob/O in viewers(src, null))
- if ((O.client && !( O.blinded )))
- O.show_message(text("\red [] has attempted to punch [name]!", M), 1)
+ visible_message("\red [M] tried to [pick(attack.attack_verb)] [src]!")
else
if (M.a_intent == "grab")
if (M == src || anchored)
@@ -403,7 +402,7 @@
var/damage = rand(1, 3)
- if(istype(src, /mob/living/carbon/slime/adult))
+ if(M.is_adult)
damage = rand(20, 40)
else
damage = rand(5, 35)
diff --git a/code/modules/mob/living/damage_procs.dm b/code/modules/mob/living/damage_procs.dm
index 5431a2775ad..cc76d40c654 100644
--- a/code/modules/mob/living/damage_procs.dm
+++ b/code/modules/mob/living/damage_procs.dm
@@ -52,7 +52,8 @@
if(AGONY)
halloss += effect // Useful for objects that cause "subdual" damage. PAIN!
if(IRRADIATE)
- radiation += max((((effect - (effect*(getarmor(null, "rad")/100))))/(blocked+1)),0)//Rads auto check armor
+ var/rad_protection = getarmor(null, "rad")/100
+ radiation += max((1-rad_protection)*effect/(blocked+1),0)//Rads auto check armor
if(STUTTER)
if(status_flags & CANSTUN) // stun is usually associated with stutter
stuttering = max(stuttering,(effect/(blocked+1)))
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index e0733ce7b4a..2c0b3bbdb62 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -202,15 +202,6 @@
return 0
-/mob/living/proc/electrocute_act(var/shock_damage, var/obj/source, var/siemens_coeff = 1.0)
- return 0 //only carbon liveforms have this proc
-
-/mob/living/emp_act(severity)
- var/list/L = src.get_contents()
- for(var/obj/O in L)
- O.emp_act(severity)
- ..()
-
/mob/living/proc/can_inject()
return 1
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index 8b9bfccb879..3e895c73c2c 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -38,6 +38,9 @@
/mob/living/bullet_act(var/obj/item/projectile/P, var/def_zone)
+ flash_weak_pain()
+
+ //Being hit while using a cloaking device
var/obj/item/weapon/cloaking_device/C = locate((/obj/item/weapon/cloaking_device) in src)
if(C && C.active)
C.attack_self(src)//Should shut it off
@@ -45,14 +48,21 @@
src << "\blue Your [C.name] was disrupted!"
Stun(2)
- flash_weak_pain()
-
+ //Being hit while using a deadman switch
if(istype(equipped(),/obj/item/device/assembly/signaler))
var/obj/item/device/assembly/signaler/signaler = equipped()
if(signaler.deadman && prob(80))
src.visible_message("\red [src] triggers their deadman's switch!")
signaler.signal()
+ //Stun Beams
+ if(istype(P, /obj/item/projectile/beam/stun) || istype(P, /obj/item/projectile/bullet/stunshot))
+ stun_effect_act(0, P.agony, def_zone, P)
+ src <<"\red You have been hit by [P]!"
+ del P
+ return
+
+ //Armor
var/absorb = run_armor_check(def_zone, P.flag)
var/proj_sharp = is_sharp(P)
var/proj_edge = has_edge(P)
@@ -65,6 +75,30 @@
P.on_hit(src, absorb, def_zone)
return absorb
+//Handles the effects of "stun" weapons
+/mob/living/proc/stun_effect_act(var/stun_amount, var/agony_amount, var/def_zone, var/used_weapon=null)
+ flash_pain()
+
+ if (stun_amount)
+ Stun(stun_amount)
+ Weaken(stun_amount)
+ apply_effect(STUTTER, stun_amount)
+ apply_effect(EYE_BLUR, stun_amount)
+
+ if (agony_amount)
+ apply_damage(agony_amount, HALLOSS, def_zone, 0, used_weapon)
+ apply_effect(STUTTER, agony_amount/10)
+ apply_effect(EYE_BLUR, agony_amount/10)
+
+/mob/living/proc/electrocute_act(var/shock_damage, var/obj/source, var/siemens_coeff = 1.0)
+ return 0 //only carbon liveforms have this proc
+
+/mob/living/emp_act(severity)
+ var/list/L = src.get_contents()
+ for(var/obj/O in L)
+ O.emp_act(severity)
+ ..()
+
//this proc handles being hit by a thrown atom
/mob/living/hitby(atom/movable/AM as mob|obj,var/speed = 5)//Standardization and logging -Sieve
if(istype(AM,/obj/))
@@ -74,16 +108,16 @@
var/obj/item/weapon/W = O
dtype = W.damtype
var/throw_damage = O.throwforce*(speed/5)
-
+
var/miss_chance = 15
if (O.throw_source)
var/distance = get_dist(O.throw_source, loc)
miss_chance = min(15*(distance-2), 0)
-
+
if (prob(miss_chance))
visible_message("\blue \The [O] misses [src] narrowly!")
return
-
+
src.visible_message("\red [src] has been hit by [O].")
var/armor = run_armor_check(null, "melee")
@@ -91,7 +125,7 @@
apply_damage(throw_damage, dtype, null, armor, is_sharp(O), has_edge(O), O)
O.throwing = 0 //it hit, so stop moving
-
+
if(ismob(O.thrower))
var/mob/M = O.thrower
var/client/assailant = M.client
@@ -111,7 +145,7 @@
src.throw_at(get_edge_target_turf(src,dir),1,momentum)
if(!W || !src) return
-
+
if(W.sharp) //Projectile is suitable for pinning.
//Handles embedding for non-humans and simple_animals.
O.loc = src
@@ -124,6 +158,7 @@
visible_message("[src] is pinned to the wall by [O]!","You are pinned to the wall by [O]!")
src.anchored = 1
src.pinned += O
+ src.verbs += /mob/proc/yank_out_object
//This is called when the mob is thrown into a dense turf
/mob/living/proc/turf_collision(var/turf/T, var/speed)
diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm
index 01c4ad851c2..95dd82aafed 100644
--- a/code/modules/mob/living/say.dm
+++ b/code/modules/mob/living/say.dm
@@ -85,28 +85,29 @@ var/list/department_radio_keys = list(
/mob/living/say(var/message, var/datum/language/speaking = null, var/verb="says", var/alt_name="", var/italics=0, var/message_range = world.view, var/list/used_radios = list(), var/sound/speech_sound, var/sound_vol)
var/turf/T = get_turf(src)
-
+
//handle nonverbal and sign languages here
if (speaking)
if (speaking.flags & NONVERBAL)
if (prob(30))
src.custom_emote(1, "[pick(speaking.signlang_verb)].")
-
+
if (speaking.flags & SIGNLANG)
say_signlang(message, pick(speaking.signlang_verb), speaking)
- return
-
+ return 1
+
//speaking into radios
if(used_radios.len)
italics = 1
message_range = 1
-
- for(var/mob/living/M in hearers(5, src))
- if(M != src)
- M.show_message("[src] talks into [used_radios.len ? used_radios[1] : "the radio."]")
- if (speech_sound)
- src.playsound_local(get_turf(src), speech_sound, sound_vol * 0.5, 1)
-
+
+ if (!istype(src, /mob/living/silicon/ai)) // Atlantis: Prevents nearby people from hearing the AI when it talks using it's integrated radio.
+ for(var/mob/living/M in hearers(5, src))
+ if(M != src)
+ M.show_message("[src] talks into [used_radios.len ? used_radios[1] : "the radio."]")
+ if (speech_sound)
+ src.playsound_local(get_turf(src), speech_sound, sound_vol * 0.5, 1)
+
speech_sound = null //so we don't play it twice.
//make sure the air can transmit speech
@@ -116,13 +117,13 @@ var/list/department_radio_keys = list(
if(pressure < SOUND_MINIMUM_PRESSURE)
italics = 1
message_range = 1
-
+
if (speech_sound)
sound_vol *= 0.5 //muffle the sound a bit, so it's like we're actually talking through contact
var/list/listening = list()
var/list/listening_obj = list()
-
+
if(T)
var/list/hear = hear(message_range, T)
var/list/hearturfs = list()
@@ -134,11 +135,18 @@ var/list/department_radio_keys = list(
hearturfs += M.locs[1]
for(var/obj/O in M.contents)
listening_obj |= O
+ if (isslime(I))
+ var/mob/living/carbon/slime/S = I
+ if (src in S.Friends)
+ S.speech_buffer = list()
+ S.speech_buffer.Add(src)
+ S.speech_buffer.Add(lowertext(html_decode(message)))
else if(istype(I, /obj/))
var/obj/O = I
hearturfs += O.locs[1]
listening_obj |= O
+
for(var/mob/M in player_list)
if(M.stat == DEAD && M.client && (M.client.prefs.toggles & CHAT_GHOSTEARS))
listening |= M
@@ -158,8 +166,9 @@ var/list/department_radio_keys = list(
spawn(0)
if(O) //It's possible that it could be deleted in the meantime.
O.hear_talk(src, message, verb, speaking)
-
+
log_say("[name]/[key] : [message]")
+ return 1
/mob/living/proc/say_signlang(var/message, var/verb="gestures", var/datum/language/language)
for (var/mob/O in viewers(src, null))
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index b713cd0ca8f..40cc69f2c47 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -1,4 +1,28 @@
+#define AI_CHECK_WIRELESS 1
+#define AI_CHECK_RADIO 2
+
var/list/ai_list = list()
+var/list/ai_verbs_default = list(
+ /mob/living/silicon/ai/proc/ai_announcement,
+ /mob/living/silicon/ai/proc/ai_call_shuttle,
+ // /mob/living/silicon/ai/proc/ai_recall_shuttle,
+ /mob/living/silicon/ai/proc/ai_camera_track,
+ /mob/living/silicon/ai/proc/ai_camera_list,
+ /mob/living/silicon/ai/proc/ai_goto_location,
+ /mob/living/silicon/ai/proc/ai_remove_location,
+ /mob/living/silicon/ai/proc/ai_hologram_change,
+ /mob/living/silicon/ai/proc/ai_network_change,
+ /mob/living/silicon/ai/proc/pick_icon,
+ /mob/living/silicon/ai/proc/ai_roster,
+ /mob/living/silicon/ai/proc/ai_statuschange,
+ /mob/living/silicon/ai/proc/ai_store_location,
+ /mob/living/silicon/ai/proc/checklaws,
+ /mob/living/silicon/ai/proc/control_integrated_radio,
+ /mob/living/silicon/ai/proc/core,
+ /mob/living/silicon/ai/proc/show_laws_verb,
+ /mob/living/silicon/ai/proc/toggle_acceleration,
+ /mob/living/silicon/ai/proc/toggle_camera_light
+)
//Not sure why this is necessary...
/proc/AutoUpdateAI(obj/subject)
@@ -27,9 +51,11 @@ var/list/ai_list = list()
var/viewalerts = 0
var/lawcheck[1]
var/ioncheck[1]
+ var/lawchannel = "Common" // Default channel on which to state laws
var/icon/holo_icon//Default is assigned when AI is created.
var/obj/item/device/pda/ai/aiPDA = null
var/obj/item/device/multitool/aiMulti = null
+ var/obj/item/device/radio/headset/heads/ai_integrated/aiRadio = null
var/custom_sprite = 0 //For our custom sprites
//Hud stuff
@@ -51,6 +77,13 @@ var/list/ai_list = list()
var/datum/trackable/track = null
var/last_announcement = ""
+/mob/living/silicon/ai/proc/add_ai_verbs()
+ src.verbs += ai_verbs_default
+
+/mob/living/silicon/ai/proc/remove_ai_verbs()
+ src.verbs -= ai_verbs_default
+
+
/mob/living/silicon/ai/New(loc, var/datum/ai_laws/L, var/obj/item/device/mmi/B, var/safety = 0)
var/list/possibleNames = ai_names
@@ -79,20 +112,18 @@ var/list/ai_list = list()
else
laws = new base_law_type
- verbs += /mob/living/silicon/ai/proc/show_laws_verb
-
aiPDA = new/obj/item/device/pda/ai(src)
aiPDA.owner = name
aiPDA.ownjob = "AI"
aiPDA.name = name + " (" + aiPDA.ownjob + ")"
aiMulti = new(src)
+ aiRadio = new(src)
+ aiRadio.myAi = src
+ aiCamera = new/obj/item/device/camera/siliconcam/ai_camera(src)
if (istype(loc, /turf))
- verbs.Add(/mob/living/silicon/ai/proc/ai_call_shuttle,/mob/living/silicon/ai/proc/ai_camera_track, \
- /mob/living/silicon/ai/proc/ai_camera_list, /mob/living/silicon/ai/proc/ai_network_change, \
- /mob/living/silicon/ai/proc/ai_statuschange, /mob/living/silicon/ai/proc/ai_hologram_change, \
- /mob/living/silicon/ai/proc/toggle_camera_light)
+ add_ai_verbs(src)
//Languages
add_language("Sol Common", 0)
@@ -177,7 +208,7 @@ var/list/ai_list = list()
if(powered_ai.anchored)
use_power = 2
-/mob/living/silicon/ai/verb/pick_icon()
+/mob/living/silicon/ai/proc/pick_icon()
set category = "AI Commands"
set name = "Set AI Core Display"
if(stat || aiRestorePowerRoutine)
@@ -272,20 +303,43 @@ var/list/ai_list = list()
set name = "Show Crew Manifest"
show_station_manifest()
+/mob/living/silicon/ai/var/message_cooldown = 0
+/mob/living/silicon/ai/proc/ai_announcement()
+ set category = "AI Commands"
+ set name = "Make Station Announcement"
+
+ if(check_unable(AI_CHECK_WIRELESS | AI_CHECK_RADIO))
+ return
+
+ if(message_cooldown)
+ src << "Please allow one minute to pass between announcements."
+ return
+ var/input = stripped_input(usr, "Please write a message to announce to the station crew.", "A.I. Announcement")
+ if(!input)
+ return
+
+ if(check_unable(AI_CHECK_WIRELESS | AI_CHECK_RADIO))
+ return
+
+ captain_announce(input, "A.I. Announcement", src.name)
+ log_say("[key_name(usr)] has made an AI announcement: [input]")
+ message_admins("[key_name_admin(usr)] has made an AI announcement.", 1)
+ message_cooldown = 1
+ spawn(600)//One minute cooldown
+ message_cooldown = 0
+
/mob/living/silicon/ai/proc/ai_call_shuttle()
set category = "AI Commands"
set name = "Call Emergency Shuttle"
- if(src.stat == 2)
- src << "You can't call the shuttle because you are dead!"
+
+ if(check_unable(AI_CHECK_WIRELESS))
return
- if(istype(usr,/mob/living/silicon/ai))
- var/mob/living/silicon/ai/AI = src
- if(AI.control_disabled)
- usr << "Wireless control is disabled!"
- return
var/confirm = alert("Are you sure you want to call the shuttle?", "Confirm Shuttle Call", "Yes", "No")
+ if(check_unable(AI_CHECK_WIRELESS))
+ return
+
if(confirm == "Yes")
call_shuttle_proc(src)
@@ -295,20 +349,19 @@ var/list/ai_list = list()
if(C)
C.post_status("shuttle")
- return
-
-/mob/living/silicon/ai/proc/ai_cancel_call()
+/mob/living/silicon/ai/proc/ai_recall_shuttle()
set category = "AI Commands"
- if(src.stat == 2)
- src << "You can't send the shuttle back because you are dead!"
+ set name = "Recall Emergency Shuttle"
+
+ if(check_unable(AI_CHECK_WIRELESS))
return
- if(istype(usr,/mob/living/silicon/ai))
- var/mob/living/silicon/ai/AI = src
- if(AI.control_disabled)
- src << "Wireless control is disabled!"
- return
- cancel_call_proc(src)
- return
+
+ var/confirm = alert("Are you sure you want to recall the shuttle?", "Confirm Shuttle Recall", "Yes", "No")
+ if(check_unable(AI_CHECK_WIRELESS))
+ return
+
+ if(confirm == "Yes")
+ cancel_call_proc(src)
/mob/living/silicon/ai/check_eye(var/mob/user as mob)
if (!camera)
@@ -386,6 +439,13 @@ var/list/ai_list = list()
// src << text ("Switching Law [L]'s report status to []", lawcheck[L+1])
checklaws()
+ if (href_list["lawr"]) // Selects on which channel to state laws
+ var/setchannel = input(usr, "Specify channel.", "Channel selection") in list("State","Common","Science","Command","Medical","Engineering","Security","Supply","Binary","Holopad", "Cancel")
+ if(setchannel == "Cancel")
+ return
+ lawchannel = setchannel
+ checklaws()
+
if (href_list["lawi"]) // Toggling whether or not a law gets stated by the State Laws verb --NeoFite
var/L = text2num(href_list["lawi"])
switch(ioncheck[L])
@@ -504,7 +564,7 @@ var/list/ai_list = list()
src.cameraFollow = null
- if (!C || stat == 2) //C.can_use())
+ if (!C || stat == DEAD) //C.can_use())
return 0
if(!src.eyeobj)
@@ -557,8 +617,7 @@ var/list/ai_list = list()
src.cameraFollow = null
var/cameralist[0]
- if(usr.stat == 2)
- usr << "You can't change your camera network because you are dead!"
+ if(check_unable())
return
var/mob/living/silicon/ai/U = usr
@@ -601,9 +660,9 @@ var/list/ai_list = list()
set category = "AI Commands"
set name = "AI Status"
- if(usr.stat == 2)
- usr <<"You cannot change your emotional status because you are dead!"
+ if(check_unable(AI_CHECK_WIRELESS))
return
+
var/list/ai_emotions = list("Very Happy", "Happy", "Neutral", "Unsure", "Confused", "Surprised", "Sad", "Upset", "Angry", "Awesome", "BSOD", "Blank", "Problems?", "Facepalm", "Friend Computer")
var/emote = input("Please, select a status!", "AI Status", null, null) in ai_emotions
for (var/obj/machinery/M in machines) //change status
@@ -626,6 +685,9 @@ var/list/ai_list = list()
set desc = "Change the default hologram available to AI to something else."
set category = "AI Commands"
+ if(check_unable())
+ return
+
var/input
if(alert("Would you like to select a hologram based on a crew member or switch to unique avatar?",,"Crew Member","Unique")=="Crew Member")
@@ -674,6 +736,9 @@ var/list/ai_list = list()
set desc = "Toggles the light on the camera the AI is looking through."
set category = "AI Commands"
+ if(check_unable())
+ return
+
camera_light_on = !camera_light_on
src << "Camera lights [camera_light_on ? "activated" : "deactivated"]."
if(!camera_light_on)
@@ -730,3 +795,31 @@ var/list/ai_list = list()
return
else
return ..()
+
+/mob/living/silicon/ai/proc/control_integrated_radio()
+ set name = "Radio Settings"
+ set desc = "Allows you to change settings of your radio."
+ set category = "AI Commands"
+
+ if(check_unable(AI_CHECK_RADIO))
+ return
+
+ src << "Accessing Subspace Transceiver control..."
+ if (src.aiRadio)
+ src.aiRadio.interact(src)
+
+/mob/living/silicon/ai/proc/check_unable(var/flags = 0)
+ if(stat == DEAD)
+ usr << "\red You are dead!"
+ return 1
+
+ if((flags & AI_CHECK_WIRELESS) && src.control_disabled)
+ usr << "\red Wireless control is disabled!"
+ return 1
+ if((flags & AI_CHECK_RADIO) && src.aiRadio.disabledAi)
+ src << "\red System Error - Transceiver Disabled!"
+ return 1
+ return 0
+
+#undef AI_CHECK_WIRELESS
+#undef AI_CHECK_RADIO
diff --git a/code/modules/mob/living/silicon/ai/death.dm b/code/modules/mob/living/silicon/ai/death.dm
index c9541858467..f2cd262f4c3 100644
--- a/code/modules/mob/living/silicon/ai/death.dm
+++ b/code/modules/mob/living/silicon/ai/death.dm
@@ -11,6 +11,7 @@
sight |= SEE_TURFS|SEE_MOBS|SEE_OBJS
see_in_dark = 8
see_invisible = SEE_INVISIBLE_LEVEL_TWO
+ remove_ai_verbs(src)
var/callshuttle = 0
diff --git a/code/modules/mob/living/silicon/ai/freelook/cameranet.dm b/code/modules/mob/living/silicon/ai/freelook/cameranet.dm
index 7d52fbd7a24..87dc0ec87e7 100644
--- a/code/modules/mob/living/silicon/ai/freelook/cameranet.dm
+++ b/code/modules/mob/living/silicon/ai/freelook/cameranet.dm
@@ -128,6 +128,9 @@ var/datum/cameranet/cameranet = new()
// 0xf = 15
var/turf/position = get_turf(target)
+ return checkTurfVis(position)
+
+/datum/cameranet/proc/checkTurfVis(var/turf/position)
var/datum/camerachunk/chunk = getCameraChunk(position.x, position.y, position.z)
if(chunk)
if(chunk.changed)
@@ -136,7 +139,6 @@ var/datum/cameranet/cameranet = new()
return 1
return 0
-
// Debug verb for VVing the chunk that the turf is in.
/*
/turf/verb/view_chunk()
diff --git a/code/modules/mob/living/silicon/ai/freelook/eye.dm b/code/modules/mob/living/silicon/ai/freelook/eye.dm
index 2f7dc9f339f..da15e5158ad 100644
--- a/code/modules/mob/living/silicon/ai/freelook/eye.dm
+++ b/code/modules/mob/living/silicon/ai/freelook/eye.dm
@@ -50,6 +50,12 @@
if(ai.holo)
ai.holo.move_hologram()
+/mob/aiEye/proc/getLoc()
+
+ if(ai)
+ if(!isturf(ai.loc) || !ai.client)
+ return
+ return ai.eyeobj.loc
// AI MOVEMENT
@@ -112,7 +118,7 @@
// Return to the Core.
-/mob/living/silicon/ai/verb/core()
+/mob/living/silicon/ai/proc/core()
set category = "AI Commands"
set name = "AI Core"
@@ -137,7 +143,7 @@
for(var/datum/camerachunk/c in eyeobj.visibleCameraChunks)
c.remove(eyeobj)
-/mob/living/silicon/ai/verb/toggle_acceleration()
+/mob/living/silicon/ai/proc/toggle_acceleration()
set category = "AI Commands"
set name = "Toggle Camera Acceleration"
diff --git a/code/modules/mob/living/silicon/ai/laws.dm b/code/modules/mob/living/silicon/ai/laws.dm
index 904451aa1dc..e167c42f060 100755
--- a/code/modules/mob/living/silicon/ai/laws.dm
+++ b/code/modules/mob/living/silicon/ai/laws.dm
@@ -1,4 +1,3 @@
-
/mob/living/silicon/ai/proc/show_laws_verb()
set category = "AI Commands"
set name = "Show Laws"
@@ -51,24 +50,33 @@
src.laws_sanity_check()
src.laws.clear_supplied_laws()
-
-
-
-
/mob/living/silicon/ai/proc/statelaws() // -- TLE
// set category = "AI Commands"
// set name = "State Laws"
- src.say("Current Active Laws:")
+ /var/prefix = ""
+ switch(lawchannel)
+ if("Common") prefix = ";"
+ if("Science") prefix = ":n "
+ if("Command") prefix = ":c "
+ if("Medical") prefix = ":m "
+ if("Engineering") prefix = ":e "
+ if("Security") prefix = ":s "
+ if("Supply") prefix = ":u "
+ if("Binary") prefix = ":b "
+ if("Holopad") prefix = ":h "
+ else prefix = ""
+
+ if(src.say("[prefix]Current Active Laws:") != 1)
+ return
+
//src.laws_sanity_check()
//src.laws.show_laws(world)
var/number = 1
sleep(10)
-
-
if (src.laws.zeroth)
if (src.lawcheck[1] == "Yes") //This line and the similar lines below make sure you don't state a law unless you want to. --NeoFite
- src.say("0. [src.laws.zeroth]")
+ src.say("[prefix]0. [src.laws.zeroth]")
sleep(10)
for (var/index = 1, index <= src.laws.ion.len, index++)
@@ -76,7 +84,7 @@
var/num = ionnum()
if (length(law) > 0)
if (src.ioncheck[index] == "Yes")
- src.say("[num]. [law]")
+ src.say("[prefix][num]. [law]")
sleep(10)
for (var/index = 1, index <= src.laws.inherent.len, index++)
@@ -84,23 +92,21 @@
if (length(law) > 0)
if (src.lawcheck[index+1] == "Yes")
- src.say("[number]. [law]")
+ src.say("[prefix][number]. [law]")
sleep(10)
number++
-
for (var/index = 1, index <= src.laws.supplied.len, index++)
var/law = src.laws.supplied[index]
if (length(law) > 0)
if(src.lawcheck.len >= number+1)
if (src.lawcheck[number+1] == "Yes")
- src.say("[number]. [law]")
+ src.say("[prefix][number]. [law]")
sleep(10)
number++
-
-/mob/living/silicon/ai/verb/checklaws() //Gives you a link-driven interface for deciding what laws the statelaws() proc will share with the crew. --NeoFite
+/mob/living/silicon/ai/proc/checklaws() //Gives you a link-driven interface for deciding what laws the statelaws() proc will share with the crew. --NeoFite
set category = "AI Commands"
set name = "State Laws"
@@ -144,6 +150,8 @@
src.lawcheck[number+1] = "Yes"
list += {"[src.lawcheck[number+1]] [number]: [law] "}
number++
- list += {"
State Laws"}
+
+ list += {" Channel: [src.lawchannel] "}
+ list += {"State Laws"}
usr << browse(list, "window=laws")
\ No newline at end of file
diff --git a/code/modules/mob/living/silicon/ai/life.dm b/code/modules/mob/living/silicon/ai/life.dm
index 17f273e9660..3a0c1a461c6 100644
--- a/code/modules/mob/living/silicon/ai/life.dm
+++ b/code/modules/mob/living/silicon/ai/life.dm
@@ -36,8 +36,7 @@
adjustOxyLoss(-1)
// Handle EMP-stun
- if(stunned)
- AdjustStunned(-1)
+ handle_stunned()
//stage = 1
//if (istype(src, /mob/living/silicon/ai)) // Are we not sure what we are?
@@ -183,3 +182,7 @@
health = 100 - getOxyLoss() - getToxLoss() - getBruteLoss()
else
health = 100 - getOxyLoss() - getToxLoss() - getFireLoss() - getBruteLoss()
+
+/mob/living/silicon/ai/rejuvenate()
+ ..()
+ add_ai_verbs(src)
\ No newline at end of file
diff --git a/code/modules/mob/living/silicon/ai/say.dm b/code/modules/mob/living/silicon/ai/say.dm
index 1e376392275..62a75fad245 100644
--- a/code/modules/mob/living/silicon/ai/say.dm
+++ b/code/modules/mob/living/silicon/ai/say.dm
@@ -1,6 +1,5 @@
/mob/living/silicon/ai/say(var/message)
if(parent && istype(parent) && parent.stat != 2)
- parent.say(message)
- return
+ return parent.say(message)
//If there is a defined "parent" AI, it is actually an AI, and it is alive, anything the AI tries to say is said by the parent instead.
- ..(message)
+ return ..(message)
diff --git a/code/modules/mob/living/silicon/pai/life.dm b/code/modules/mob/living/silicon/pai/life.dm
index 2ed190fc060..cebdf2a5359 100644
--- a/code/modules/mob/living/silicon/pai/life.dm
+++ b/code/modules/mob/living/silicon/pai/life.dm
@@ -17,6 +17,7 @@
if(world.timeofday >= silence_time)
silence_time = null
src << "Communication circuit reinitialized. Speech and messaging functionality restored."
+ handle_statuses()
/mob/living/silicon/pai/updatehealth()
if(status_flags & GODMODE)
diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm
index fba71205fcf..dd3e2e6b3cf 100755
--- a/code/modules/mob/living/silicon/pai/pai.dm
+++ b/code/modules/mob/living/silicon/pai/pai.dm
@@ -1,10 +1,12 @@
/mob/living/silicon/pai
name = "pAI"
- icon = 'icons/mob/mob.dmi'//
- icon_state = "shadow"
+ icon = 'icons/mob/pai.dmi'
+ icon_state = "repairbot"
robot_talk_understand = 0
emote_type = 2 // pAIs emotes are heard, not seen, so they can be seen through a container (eg. person)
+ small = 1
+ pass_flags = 1
var/network = "SS13"
var/obj/machinery/camera/current = null
@@ -15,11 +17,22 @@
var/obj/item/device/paicard/card // The card we inhabit
var/obj/item/device/radio/radio // Our primary radio
- var/speakStatement = "states"
- var/speakExclamation = "declares"
- var/speakQuery = "queries"
-
+ var/chassis = "repairbot" // A record of your chosen chassis.
+ var/global/list/possible_chassis = list(
+ "Drone" = "repairbot",
+ "Cat" = "cat",
+ "Mouse" = "mouse",
+ "Monkey" = "monkey"
+ )
+ var/global/list/possible_say_verbs = list(
+ "Robotic" = list("states","declares","queries"),
+ "Natural" = list("says","yells","asks"),
+ "Beep" = list("beeps","beeps loudly","boops"),
+ "Chirp" = list("chirps","chirrups","cheeps"),
+ "Feline" = list("purrs","yowls","meows")
+ )
+
var/obj/item/weapon/pai_cable/cable // The cable we produce and use when door or camera jacking
var/master // Name of the one who commands us
@@ -56,6 +69,7 @@
/mob/living/silicon/pai/New(var/obj/item/device/paicard)
+
canmove = 0
src.loc = paicard
card = paicard
@@ -70,6 +84,10 @@
add_language("Tradeband", 1)
add_language("Gutter", 1)
+ verbs += /mob/living/silicon/pai/proc/fold_out
+ verbs += /mob/living/silicon/pai/proc/choose_chassis
+ verbs += /mob/living/silicon/pai/proc/choose_verbs
+
//PDA
pda = new(src)
spawn(5)
@@ -115,7 +133,9 @@
return 0
/mob/living/silicon/pai/restrained()
- return 0
+ if(istype(src.loc,/obj/item/device/paicard))
+ return 0
+ ..()
/mob/living/silicon/pai/emp_act(severity)
// Silence for 2 minutes
@@ -233,7 +253,6 @@
src.reset_view(C)
return 1
-
/mob/living/silicon/pai/cancel_camera()
set category = "pAI Commands"
set name = "Cancel Camera View"
@@ -277,3 +296,144 @@
card.setPersonality(pai)
*/
+
+// Procs/code after this point is used to convert the stationary pai item into a
+// mobile pai mob. This also includes handling some of the general shit that can occur
+// to it. Really this deserves its own file, but for the moment it can sit here. ~ Z
+
+/mob/living/silicon/pai/proc/fold_out()
+ set category = "pAI Commands"
+ set name = "Unfold Chassis"
+
+ if(stat || sleeping || paralysis || weakened)
+ return
+
+ if(src.loc != card)
+ return
+
+ if(world.time <= last_special)
+ return
+
+ last_special = world.time + 100
+ verbs -= /mob/living/silicon/pai/proc/fold_out
+ verbs += /mob/living/silicon/pai/proc/fold_up
+
+ var/turf/T = get_turf(src)
+ if(istype(T)) T.visible_message("[src] folds outwards, expanding into a mobile form.")
+ canmove = 1
+
+ //I'm not sure how much of this is necessary, but I would rather avoid issues.
+ if(istype(card.loc,/mob))
+ var/mob/M = card.loc
+ M.drop_item(card)
+
+ src.client.perspective = EYE_PERSPECTIVE
+ src.client.eye = src
+
+ src.forceMove(get_turf(card))
+ card.forceMove(src)
+
+/mob/living/silicon/pai/proc/fold_up()
+ set category = "pAI Commands"
+ set name = "Collapse Chassis"
+
+ if(stat || sleeping || paralysis || weakened)
+ return
+
+ if(src.loc == card)
+ return
+
+ if(world.time <= last_special)
+ return
+
+ close_up()
+
+/mob/living/silicon/pai/proc/choose_chassis()
+ set category = "pAI Commands"
+ set name = "Choose Chassis"
+
+ var/choice
+ var/finalized = "No"
+ while(finalized == "No" && src.client)
+
+ choice = input(usr,"What would you like to use for your mobile chassis icon? This decision can only be made once.") as null|anything in possible_chassis
+ if(!choice) return
+
+ icon_state = possible_chassis[choice]
+ finalized = alert("Look at your sprite. Is this what you wish to use?",,"No","Yes")
+
+ chassis = possible_chassis[choice]
+ verbs -= /mob/living/silicon/pai/proc/choose_chassis
+
+/mob/living/silicon/pai/proc/choose_verbs()
+ set category = "pAI Commands"
+ set name = "Choose Speech Verbs"
+
+ var/choice = input(usr,"What theme would you like to use for your speech verbs? This decision can only be made once.") as null|anything in possible_say_verbs
+ if(!choice) return
+
+ var/list/sayverbs = possible_say_verbs[choice]
+ speak_statement = sayverbs[1]
+ speak_exclamation = sayverbs[(sayverbs.len>1 ? 2 : sayverbs.len)]
+ speak_query = sayverbs[(sayverbs.len>2 ? 3 : sayverbs.len)]
+
+ verbs -= /mob/living/silicon/pai/proc/choose_verbs
+
+/mob/living/silicon/pai/lay_down()
+ set name = "Rest"
+ set category = "IC"
+
+ if(istype(src.loc,/obj/item/device/paicard))
+ resting = 0
+ else
+ resting = !resting
+ icon_state = resting ? "[chassis]_rest" : "[chassis]"
+ src << "\blue You are now [resting ? "resting" : "getting up"]"
+
+ canmove = !resting
+
+//Overriding this will stop a number of headaches down the track.
+/mob/living/silicon/pai/attackby(obj/item/weapon/W as obj, mob/user as mob)
+ if(W.force)
+ visible_message("[user.name] attacks [src] with [W]!")
+ src.adjustBruteLoss(W.force)
+ src.updatehealth()
+ else
+ visible_message("[user.name] bonks [src] harmlessly with [W].")
+ if(stat != 2) close_up()
+ return
+
+/mob/living/silicon/pai/attack_hand(mob/user as mob)
+ visible_message("[user.name] boops [src] on the head.")
+ close_up()
+
+//I'm not sure how much of this is necessary, but I would rather avoid issues.
+/mob/living/silicon/pai/proc/close_up()
+
+ last_special = world.time + 100
+
+ verbs -= /mob/living/silicon/pai/proc/fold_up
+ verbs += /mob/living/silicon/pai/proc/fold_out
+
+ var/turf/T = get_turf(src)
+ if(istype(T)) T.visible_message("[src] neatly folds inwards, compacting down to a rectangular card.")
+
+ src.stop_pulling()
+ src.client.perspective = EYE_PERSPECTIVE
+ src.client.eye = card
+
+ //This seems redundant but not including the forced loc setting messes the behavior up.
+ src.loc = card
+ card.loc = get_turf(card)
+ src.forceMove(card)
+ card.forceMove(card.loc)
+ canmove = 0
+
+/mob/living/silicon/pai/start_pulling(var/atom/movable/AM)
+
+ if(istype(AM,/obj/item))
+ var/obj/item/O = AM
+ if(O.w_class == 1)
+ ..()
+ else
+ src << "You are too small to pull that."
diff --git a/code/modules/mob/living/silicon/pai/recruit.dm b/code/modules/mob/living/silicon/pai/recruit.dm
index 618ac76d773..73a6d59c158 100644
--- a/code/modules/mob/living/silicon/pai/recruit.dm
+++ b/code/modules/mob/living/silicon/pai/recruit.dm
@@ -25,92 +25,228 @@ var/datum/paiController/paiController // Global handler for pAI candidates
var/askDelay = 10 * 60 * 1 // One minute [ms * sec * min]
- Topic(href, href_list[])
- if(href_list["download"])
- var/datum/paiCandidate/candidate = locate(href_list["candidate"])
- var/obj/item/device/paicard/card = locate(href_list["device"])
- if(card.pai)
+/datum/paiController/Topic(href, href_list[])
+ if(href_list["download"])
+ var/datum/paiCandidate/candidate = locate(href_list["candidate"])
+ var/obj/item/device/paicard/card = locate(href_list["device"])
+ if(card.pai)
+ return
+ if(istype(card,/obj/item/device/paicard) && istype(candidate,/datum/paiCandidate))
+ var/mob/living/silicon/pai/pai = new(card)
+ if(!candidate.name)
+ pai.name = pick(ninja_names)
+ else
+ pai.name = candidate.name
+ pai.real_name = pai.name
+ pai.key = candidate.key
+
+ card.setPersonality(pai)
+ card.looking_for_personality = 0
+
+ ticker.mode.update_cult_icons_removed(card.pai.mind)
+ ticker.mode.update_rev_icons_removed(card.pai.mind)
+
+ pai_candidates -= candidate
+ usr << browse(null, "window=findPai")
+
+ if(href_list["new"])
+ var/datum/paiCandidate/candidate = locate(href_list["candidate"])
+ var/option = href_list["option"]
+ var/t = ""
+
+ switch(option)
+ if("name")
+ t = input("Enter a name for your pAI", "pAI Name", candidate.name) as text
+ if(t)
+ candidate.name = copytext(sanitize(t),1,MAX_NAME_LEN)
+ if("desc")
+ t = input("Enter a description for your pAI", "pAI Description", candidate.description) as message
+ if(t)
+ candidate.description = copytext(sanitize(t),1,MAX_MESSAGE_LEN)
+ if("role")
+ t = input("Enter a role for your pAI", "pAI Role", candidate.role) as text
+ if(t)
+ candidate.role = copytext(sanitize(t),1,MAX_MESSAGE_LEN)
+ if("ooc")
+ t = input("Enter any OOC comments", "pAI OOC Comments", candidate.comments) as message
+ if(t)
+ candidate.comments = copytext(sanitize(t),1,MAX_MESSAGE_LEN)
+ if("save")
+ candidate.savefile_save(usr)
+ if("load")
+ candidate.savefile_load(usr)
+ //In case people have saved unsanitized stuff.
+ if(candidate.name)
+ candidate.name = copytext(sanitize(candidate.name),1,MAX_NAME_LEN)
+ if(candidate.description)
+ candidate.description = copytext(sanitize(candidate.description),1,MAX_MESSAGE_LEN)
+ if(candidate.role)
+ candidate.role = copytext(sanitize(candidate.role),1,MAX_MESSAGE_LEN)
+ if(candidate.comments)
+ candidate.comments = copytext(sanitize(candidate.comments),1,MAX_MESSAGE_LEN)
+
+ if("submit")
+ if(candidate)
+ candidate.ready = 1
+ for(var/obj/item/device/paicard/p in world)
+ if(p.looking_for_personality == 1)
+ p.alertUpdate()
+ usr << browse(null, "window=paiRecruit")
return
- if(istype(card,/obj/item/device/paicard) && istype(candidate,/datum/paiCandidate))
- var/mob/living/silicon/pai/pai = new(card)
- if(!candidate.name)
- pai.name = pick(ninja_names)
- else
- pai.name = candidate.name
- pai.real_name = pai.name
- pai.key = candidate.key
- card.setPersonality(pai)
- card.looking_for_personality = 0
+ recruitWindow(usr, href_list["allow_submit"] != "0")
- ticker.mode.update_cult_icons_removed(card.pai.mind)
- ticker.mode.update_rev_icons_removed(card.pai.mind)
-
- pai_candidates -= candidate
- usr << browse(null, "window=findPai")
-
- if(href_list["new"])
- var/datum/paiCandidate/candidate = locate(href_list["candidate"])
- var/option = href_list["option"]
- var/t = ""
-
- switch(option)
- if("name")
- t = input("Enter a name for your pAI", "pAI Name", candidate.name) as text
- if(t)
- candidate.name = copytext(sanitize(t),1,MAX_NAME_LEN)
- if("desc")
- t = input("Enter a description for your pAI", "pAI Description", candidate.description) as message
- if(t)
- candidate.description = copytext(sanitize(t),1,MAX_MESSAGE_LEN)
- if("role")
- t = input("Enter a role for your pAI", "pAI Role", candidate.role) as text
- if(t)
- candidate.role = copytext(sanitize(t),1,MAX_MESSAGE_LEN)
- if("ooc")
- t = input("Enter any OOC comments", "pAI OOC Comments", candidate.comments) as message
- if(t)
- candidate.comments = copytext(sanitize(t),1,MAX_MESSAGE_LEN)
- if("save")
- candidate.savefile_save(usr)
- if("load")
- candidate.savefile_load(usr)
- //In case people have saved unsanitized stuff.
- if(candidate.name)
- candidate.name = copytext(sanitize(candidate.name),1,MAX_NAME_LEN)
- if(candidate.description)
- candidate.description = copytext(sanitize(candidate.description),1,MAX_MESSAGE_LEN)
- if(candidate.role)
- candidate.role = copytext(sanitize(candidate.role),1,MAX_MESSAGE_LEN)
- if(candidate.comments)
- candidate.comments = copytext(sanitize(candidate.comments),1,MAX_MESSAGE_LEN)
-
- if("submit")
- if(candidate)
- candidate.ready = 1
- for(var/obj/item/device/paicard/p in world)
- if(p.looking_for_personality == 1)
- p.alertUpdate()
- usr << browse(null, "window=paiRecruit")
- return
- recruitWindow(usr)
-
- proc/recruitWindow(var/mob/M as mob)
- var/datum/paiCandidate/candidate
- for(var/datum/paiCandidate/c in pai_candidates)
- if(!istype(c) || !istype(M))
- break
- if(c.key == M.key)
- candidate = c
- if(!candidate)
- candidate = new /datum/paiCandidate()
- candidate.key = M.key
- pai_candidates.Add(candidate)
+/datum/paiController/proc/recruitWindow(var/mob/M as mob, allowSubmit = 1)
+ var/datum/paiCandidate/candidate
+ for(var/datum/paiCandidate/c in pai_candidates)
+ if(!istype(c) || !istype(M))
+ break
+ if(c.key == M.key)
+ candidate = c
+ if(!candidate)
+ candidate = new /datum/paiCandidate()
+ candidate.key = M.key
+ pai_candidates.Add(candidate)
- var/dat = ""
+ var/dat = ""
+ dat += {"
+
+ "}
+
+ dat += {"
+
+ pAI Personality Configuration
+
Please configure your pAI personality's options. Remember, what you enter here could determine whether or not the user requesting a personality chooses you!
Do you like to partner with sneaky social ninjas? Like to help security hunt down thugs? Enjoy watching an engineer's back while he saves the station yet again? This doesn't have to be limited to just station jobs. Pretty much any general descriptor for what you'd like to be doing works here.
Anything you'd like to address specifically to the player reading this in an OOC manner. \"I prefer more serious RP.\", \"I'm still learning the interface!\", etc. Feel free to leave this blank if you want.
+ "}
+ if(allowSubmit)
dat += {"
-
- "}
+
+
+ pAI Availability List
+ "}
+ dat += "
Displaying available AI personalities from central database... If there are no entries, or if a suitable entry is not listed, check again later as more personalities may be added.
"
+ for(var/datum/paiCandidate/c in available)
dat += {"
-
- pAI Personality Configuration
-
Please configure your pAI personality's options. Remember, what you enter here could determine whether or not the user requesting a personality chooses you!
Do you like to partner with sneaky social ninjas? Like to help security hunt down thugs? Enjoy watching an engineer's back while he saves the station yet again? This doesn't have to be limited to just station jobs. Pretty much any general descriptor for what you'd like to be doing works here.
Anything you'd like to address specifically to the player reading this in an OOC manner. \"I prefer more serious RP.\", \"I'm still learning the interface!\", etc. Feel free to leave this blank if you want.
+
"}
- M << browse(dat, "window=paiRecruit;size=580x580;")
+ dat += {"
+
+
+ "}
- proc/findPAI(var/obj/item/device/paicard/p, var/mob/user)
- inquirer = user
- requestRecruits()
- var/list/available = list()
- for(var/datum/paiCandidate/c in paiController.pai_candidates)
- if(c.ready)
- var/found = 0
- for(var/mob/dead/observer/o in player_list)
- if(o.key == c.key)
- found = 1
- if(found)
- available.Add(c)
- var/dat = ""
+ user << browse(dat, "window=findPai")
- dat += {"
-
-
-
-
-
-
- pAI Availability List
- "}
- dat += "
Displaying available AI personalities from central database... If there are no entries, or if a suitable entry is not listed, check again later as more personalities may be added.
"
- for(var/datum/paiCandidate/c in available)
- dat += {"
-
-
- "}
-
- dat += {"
-
-
- "}
-
- user << browse(dat, "window=findPai")
-
- proc/requestRecruits()
- for(var/mob/dead/observer/O in player_list)
- if(O.has_enabled_antagHUD == 1 && config.antag_hud_restricted)
+/datum/paiController/proc/requestRecruits()
+ for(var/mob/dead/observer/O in player_list)
+ if(O.has_enabled_antagHUD == 1 && config.antag_hud_restricted)
+ continue
+ if(jobban_isbanned(O, "pAI"))
+ continue
+ if(asked.Find(O.key))
+ if(world.time < asked[O.key] + askDelay)
continue
- if(jobban_isbanned(O, "pAI"))
- continue
- if(asked.Find(O.key))
- if(world.time < asked[O.key] + askDelay)
- continue
- else
- asked.Remove(O.key)
- if(O.client)
- var/hasSubmitted = 0
- for(var/datum/paiCandidate/c in paiController.pai_candidates)
- if(c.key == O.key)
- hasSubmitted = 1
- if(!hasSubmitted && (O.client.prefs.be_special & BE_PAI))
- question(O.client)
+ else
+ asked.Remove(O.key)
+ if(O.client)
+ var/hasSubmitted = 0
+ for(var/datum/paiCandidate/c in paiController.pai_candidates)
+ if(c.key == O.key)
+ hasSubmitted = 1
+ if(!hasSubmitted && (O.client.prefs.be_special & BE_PAI))
+ question(O.client)
- proc/question(var/client/C)
- spawn(0)
- if(!C) return
- asked.Add(C.key)
- asked[C.key] = world.time
- var/response = alert(C, "[inquirer] is requesting a pAI personality. Would you like to play as a personal AI?", "pAI Request", "Yes", "No", "Never for this round")
- if(!C) return //handle logouts that happen whilst the alert is waiting for a response.
- if(response == "Yes")
- recruitWindow(C.mob)
- else if (response == "Never for this round")
- var/warning = alert(C, "Are you sure? This action will be undoable and you will need to wait until next round.", "You sure?", "Yes", "No")
- if(warning == "Yes")
- asked[C.key] = INFINITY
- else
- question(C)
\ No newline at end of file
+/datum/paiController/proc/question(var/client/C)
+ spawn(0)
+ if(!C) return
+ asked.Add(C.key)
+ asked[C.key] = world.time
+ var/response = alert(C, "[inquirer] is requesting a pAI personality. Would you like to play as a personal AI?", "pAI Request", "Yes", "No", "Never for this round")
+ if(!C) return //handle logouts that happen whilst the alert is waiting for a response.
+ if(response == "Yes")
+ recruitWindow(C.mob)
+ else if (response == "Never for this round")
+ var/warning = alert(C, "Are you sure? This action will be undoable and you will need to wait until next round.", "You sure?", "Yes", "No")
+ if(warning == "Yes")
+ asked[C.key] = INFINITY
+ else
+ question(C)
diff --git a/code/modules/mob/living/silicon/pai/software.dm b/code/modules/mob/living/silicon/pai/software.dm
index 8ca741724eb..ae4dcc54fc9 100755
--- a/code/modules/mob/living/silicon/pai/software.dm
+++ b/code/modules/mob/living/silicon/pai/software.dm
@@ -528,14 +528,22 @@
dat += {"
Medical Analysis Suite
Host Bioscan
"}
- var/mob/living/M = src.loc
- if(!istype(M, /mob/living))
- while (!istype(M, /mob/living))
- M = M.loc
- if(istype(M, /turf))
- src.temp = "Error: No biological host found. "
- src.subscreen = 0
- return dat
+
+ var/mob/living/M
+ //If we are not deployed, check the holder of the card.
+ if(src.loc == card)
+ M = card.loc
+
+ //If we are deployed or the card is not held, check the first living mob in our turf.
+ if(!M || !istype(M))
+ var/turf/T = get_turf(src)
+ M = locate(/mob/living/) in T.contents
+
+ if(!M || !istype(M))
+ src.temp = "Error: No biological host found. "
+ src.subscreen = 0
+ return dat
+
dat += {"Bioscan Results for [M]:
Overall Status: [M.stat > 1 ? "dead" : "[M.health]% healthy"]
@@ -568,22 +576,13 @@
var/datum/gas_mixture/environment = T.return_air()
var/pressure = environment.return_pressure()
- var/total_moles = environment.total_moles()
+ var/total_moles = environment.total_moles
dat += "Air Pressure: [round(pressure,0.1)] kPa "
- if (total_moles)
- var/o2_level = environment.oxygen/total_moles
- var/n2_level = environment.nitrogen/total_moles
- var/co2_level = environment.carbon_dioxide/total_moles
- var/phoron_level = environment.phoron/total_moles
- var/unknown_level = 1-(o2_level+n2_level+co2_level+phoron_level)
- dat += "Nitrogen: [round(n2_level*100)]% "
- dat += "Oxygen: [round(o2_level*100)]% "
- dat += "Carbon Dioxide: [round(co2_level*100)]% "
- dat += "Phoron: [round(phoron_level*100)]% "
- if(unknown_level > 0.01)
- dat += "OTHER: [round(unknown_level)]% "
+ if(total_moles)
+ for(var/g in environment.gas)
+ dat += "[gas_data.name[g]]: [round((environment.gas[g] / total_moles) * 100)]% "
dat += "Temperature: [round(environment.temperature-T0C)]°C "
dat += " Refresh Reading"
return dat
diff --git a/code/modules/mob/living/silicon/robot/component.dm b/code/modules/mob/living/silicon/robot/component.dm
index d97d2a5391c..774f5dc4209 100644
--- a/code/modules/mob/living/silicon/robot/component.dm
+++ b/code/modules/mob/living/silicon/robot/component.dm
@@ -23,11 +23,16 @@
/datum/robot_component/proc/uninstall()
/datum/robot_component/proc/destroy()
+ var/brokenstate = "broken" // Generic icon
+ if (istype(wrapped, /obj/item/robot_parts/robot_component))
+ var/obj/item/robot_parts/robot_component/comp = wrapped
+ brokenstate = comp.icon_state_broken
if(wrapped)
del wrapped
wrapped = new/obj/item/broken_device
+ wrapped.icon_state = brokenstate // Module-specific broken icons! Yay!
// The thing itself isn't there anymore, but some fried remains are.
installed = -1
@@ -137,25 +142,38 @@
construction_cost = list("metal"=5000)
var/brute = 0
var/burn = 0
+ var/icon_state_broken = "broken"
// TODO: actual icons ;)
/obj/item/robot_parts/robot_component/binary_communication_device
name = "binary communication device"
+ icon_state = "binradio"
+ icon_state_broken = "binradio_broken"
/obj/item/robot_parts/robot_component/actuator
name = "actuator"
+ icon_state = "motor"
+ icon_state_broken = "motor_broken"
/obj/item/robot_parts/robot_component/armour
name = "armour plating"
+ icon_state = "armor"
+ icon_state_broken = "armor_broken"
/obj/item/robot_parts/robot_component/camera
name = "camera"
+ icon_state = "camera"
+ icon_state_broken = "camera_broken"
/obj/item/robot_parts/robot_component/diagnosis_unit
name = "diagnosis unit"
+ icon_state = "analyser"
+ icon_state_broken = "analyser_broken"
/obj/item/robot_parts/robot_component/radio
name = "radio"
+ icon_state = "radio"
+ icon_state_broken = "radio_broken"
//
//Robotic Component Analyser, basically a health analyser for robots
diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm
index bcce6d2f869..b4d2990782b 100644
--- a/code/modules/mob/living/silicon/robot/drone/drone.dm
+++ b/code/modules/mob/living/silicon/robot/drone/drone.dm
@@ -61,6 +61,13 @@
flavor_text = "It's a tiny little repair drone. The casing is stamped with an NT logo and the subscript: 'NanoTrasen Recursive Repair Systems: Fixing Tomorrow's Problem, Today!'"
updateicon()
+/mob/living/silicon/robot/drone/init()
+ laws = new /datum/ai_laws/drone()
+ connected_ai = null
+
+ aiCamera = new/obj/item/device/camera/siliconcam/drone_camera(src)
+ playsound(src.loc, 'sound/machines/twobeep.ogg', 50, 0)
+
//Redefining some robot procs...
/mob/living/silicon/robot/drone/updatename()
real_name = "maintenance drone ([rand(100,999)])"
diff --git a/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm b/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm
index 3ba05f63320..de504bcf02a 100644
--- a/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm
+++ b/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm
@@ -19,11 +19,9 @@
..()
/obj/machinery/drone_fabricator/power_change()
- if (powered())
- stat &= ~NOPOWER
- else
+ ..()
+ if (stat & NOPOWER)
icon_state = "drone_fab_nopower"
- stat |= NOPOWER
/obj/machinery/drone_fabricator/process()
diff --git a/code/modules/mob/living/silicon/robot/laws.dm b/code/modules/mob/living/silicon/robot/laws.dm
index bff875a0d66..2d6a12e9664 100644
--- a/code/modules/mob/living/silicon/robot/laws.dm
+++ b/code/modules/mob/living/silicon/robot/laws.dm
@@ -18,6 +18,7 @@
else
lawsync()
+ photosync()
src << "Laws synced with AI, be sure to note any changes."
if(mind && mind.special_role == "traitor" && mind.original == src)
src << "Remember, your AI does NOT share or know about your law 0."
diff --git a/code/modules/mob/living/silicon/robot/photos.dm b/code/modules/mob/living/silicon/robot/photos.dm
new file mode 100644
index 00000000000..fbcc3bf4842
--- /dev/null
+++ b/code/modules/mob/living/silicon/robot/photos.dm
@@ -0,0 +1,13 @@
+/mob/living/silicon/robot/proc/photosync()
+ var/obj/item/device/camera/siliconcam/master_cam = connected_ai ? connected_ai.aiCamera : null
+ if (!master_cam)
+ return
+
+ var/synced
+ synced = 0
+ for(var/datum/picture/z in aiCamera.aipictures)
+ if (!(master_cam.aipictures.Find(z)))
+ aiCamera.printpicture(null, z)
+ synced = 1
+ if(synced)
+ src << "Locally saved images synced with AI. Images were retained in local database in case of loss of connection with the AI."
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 141667ecbb0..a6e71ca7b3d 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -88,18 +88,7 @@
hands.icon_state = "standard"
icon_state = "secborg"
modtype = "Security"
- else if(istype(src,/mob/living/silicon/robot/drone))
- laws = new /datum/ai_laws/drone()
- connected_ai = null
- else
- laws = new /datum/ai_laws/nanotrasen()
- connected_ai = select_active_ai_with_fewest_borgs()
- if(connected_ai)
- connected_ai.connected_robots += src
- lawsync()
- lawupdate = 1
- else
- lawupdate = 0
+ init()
radio = new /obj/item/device/radio/borg(src)
if(!scrambledcodes && !camera)
@@ -138,11 +127,19 @@
hud_list[IMPTRACK_HUD] = image('icons/mob/hud.dmi', src, "hudblank")
hud_list[SPECIALROLE_HUD] = image('icons/mob/hud.dmi', src, "hudblank")
-
- if(istype(src,/mob/living/silicon/robot/drone))
- playsound(src.loc, 'sound/machines/twobeep.ogg', 50, 0)
+/mob/living/silicon/robot/proc/init()
+ aiCamera = new/obj/item/device/camera/siliconcam/robot_camera(src)
+ laws = new /datum/ai_laws/nanotrasen()
+ connected_ai = select_active_ai_with_fewest_borgs()
+ if(connected_ai)
+ connected_ai.connected_robots += src
+ lawsync()
+ photosync()
+ lawupdate = 1
else
- playsound(loc, 'sound/voice/liveagain.ogg', 75, 1)
+ lawupdate = 0
+
+ playsound(loc, 'sound/voice/liveagain.ogg', 75, 1)
// setup the PDA and its name
/mob/living/silicon/robot/proc/setup_PDA()
@@ -348,7 +345,7 @@
// this verb lets cyborgs see the stations manifest
/mob/living/silicon/robot/verb/cmd_station_manifest()
set category = "Robot Commands"
- set name = "Show Station Manifest"
+ set name = "Show Crew Manifest"
show_station_manifest()
@@ -904,7 +901,7 @@
var/damage = rand(1, 3)
- if(istype(src, /mob/living/carbon/slime/adult))
+ if(M.is_adult)
damage = rand(20, 40)
else
damage = rand(5, 35)
diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm
index db6be19796f..4b2df530c9a 100644
--- a/code/modules/mob/living/silicon/robot/robot_modules.dm
+++ b/code/modules/mob/living/silicon/robot/robot_modules.dm
@@ -168,7 +168,8 @@
stacktypes = list(
/obj/item/stack/sheet/metal = 50,
/obj/item/stack/sheet/plasteel = 10,
- /obj/item/stack/sheet/rglass = 50
+ /obj/item/stack/sheet/rglass = 50,
+ /obj/item/stack/rods = 50
)
New()
@@ -239,7 +240,7 @@
src.modules += new /obj/item/device/flash(src)
src.modules += new /obj/item/borg/sight/hud/sec(src)
src.modules += new /obj/item/weapon/handcuffs/cyborg(src)
- src.modules += new /obj/item/weapon/melee/baton/loaded(src)
+ src.modules += new /obj/item/weapon/melee/baton/robot(src)
src.modules += new /obj/item/weapon/gun/energy/taser/cyborg(src)
src.modules += new /obj/item/taperoll/police(src)
src.emag = new /obj/item/weapon/gun/energy/laser/cyborg(src)
@@ -293,7 +294,7 @@
src.modules += new /obj/item/weapon/reagent_containers/food/condiment/enzyme(src)
var/obj/item/weapon/rsf/M = new /obj/item/weapon/rsf(src)
- M.matter = 30
+ M.stored_matter = 30
src.modules += M
src.modules += new /obj/item/weapon/reagent_containers/robodropper(src)
diff --git a/code/modules/mob/living/silicon/robot/wires.dm b/code/modules/mob/living/silicon/robot/wires.dm
index 10b6832fe4b..9ed4320c844 100644
--- a/code/modules/mob/living/silicon/robot/wires.dm
+++ b/code/modules/mob/living/silicon/robot/wires.dm
@@ -74,6 +74,7 @@
if(BORG_WIRE_LAWCHECK) //Forces a law update if the borg is set to receive them. Since an update would happen when the borg checks its laws anyway, not much use, but eh
if (src.lawupdate)
src.lawsync()
+ src.photosync()
if (BORG_WIRE_AI_CONTROL) //pulse the AI wire to make the borg reselect an AI
if(!src.emagged)
diff --git a/code/modules/mob/living/silicon/say.dm b/code/modules/mob/living/silicon/say.dm
index e98530b0842..0b971cfab1f 100644
--- a/code/modules/mob/living/silicon/say.dm
+++ b/code/modules/mob/living/silicon/say.dm
@@ -2,11 +2,11 @@
var/ending = copytext(text, length(text))
if (ending == "?")
- return "queries"
+ return speak_query
else if (ending == "!")
- return "declares"
+ return speak_exclamation
- return "states"
+ return speak_statement
#define IS_AI 1
#define IS_ROBOT 2
@@ -85,14 +85,14 @@
if("department")
switch(bot_type)
if(IS_AI)
- AI.holopad_talk(message)
+ return AI.holopad_talk(message)
if(IS_ROBOT)
log_say("[key_name(src)] : [message]")
R.radio.talk_into(src,message,message_mode,verb,speaking)
if(IS_PAI)
log_say("[key_name(src)] : [message]")
P.radio.talk_into(src,message,message_mode,verb,speaking)
- return
+ return 1
if("binary")
switch(bot_type)
@@ -105,32 +105,41 @@
return
robot_talk(message)
- return
+ return 1
if("general")
switch(bot_type)
if(IS_AI)
- src << "Yeah, not yet, sorry"
+ if (AI.aiRadio.disabledAi)
+ src << "\red System Error - Transceiver Disabled"
+ return
+ else
+ log_say("[key_name(src)] : [message]")
+ AI.aiRadio.talk_into(src,message,null,verb,speaking)
if(IS_ROBOT)
log_say("[key_name(src)] : [message]")
R.radio.talk_into(src,message,null,verb,speaking)
if(IS_PAI)
log_say("[key_name(src)] : [message]")
P.radio.talk_into(src,message,null,verb,speaking)
- return
+ return 1
else
if(message_mode && message_mode in radiochannels)
switch(bot_type)
if(IS_AI)
- src << "You don't have this function yet, I'm working on it"
- return
+ if (AI.aiRadio.disabledAi)
+ src << "\red System Error - Transceiver Disabled"
+ return
+ else
+ log_say("[key_name(src)] : [message]")
+ AI.aiRadio.talk_into(src,message,message_mode,verb,speaking)
if(IS_ROBOT)
log_say("[key_name(src)] : [message]")
R.radio.talk_into(src,message,message_mode,verb,speaking)
if(IS_PAI)
log_say("[key_name(src)] : [message]")
P.radio.talk_into(src,message,message_mode,verb,speaking)
- return
+ return 1
return ..(message,speaking,verb)
@@ -165,7 +174,8 @@
This is another way of saying that we won't bother dealing with them.*/
else
src << "No holopad connected."
- return
+ return
+ return 1
/mob/living/proc/robot_talk(var/message)
diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm
index def23817765..d7b3eef7f66 100644
--- a/code/modules/mob/living/silicon/silicon.dm
+++ b/code/modules/mob/living/silicon/silicon.dm
@@ -8,6 +8,13 @@
var/list/hud_list[9]
var/list/speech_synthesizer_langs = list() //which languages can be vocalized by the speech synthesizer
+ //Used in say.dm.
+ var/speak_statement = "states"
+ var/speak_exclamation = "declares"
+ var/speak_query = "queries"
+
+ var/obj/item/device/camera/siliconcam/aiCamera = null //photography
+
/mob/living/silicon/proc/show_laws()
return
@@ -27,6 +34,25 @@
src << "\red Warning: Electromagnetic pulse detected."
..()
+/mob/living/silicon/stun_effect_act(var/stun_amount, var/agony_amount)
+ return //immune
+
+/mob/living/silicon/electrocute_act(var/shock_damage, var/obj/source, var/siemens_coeff = 1.0)
+
+ if (istype(source, /obj/machinery/containment_field))
+ var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
+ s.set_up(5, 1, loc)
+ s.start()
+
+ shock_damage *= 0.75 //take reduced damage
+ take_overall_damage(0, shock_damage)
+ visible_message("\red [src] was shocked by \the [source]!", \
+ "\red Energy pulse detected, system damaged!", \
+ "\red You hear an electrical crack")
+ if(prob(20))
+ Stun(2)
+ return
+
/mob/living/silicon/proc/damage_mob(var/brute = 0, var/fire = 0, var/tox = 0)
return
@@ -141,7 +167,7 @@
/mob/living/silicon/remove_language(var/rem_language)
..(rem_language)
-
+
for (var/datum/language/L in speech_synthesizer_langs)
if (L.name == rem_language)
speech_synthesizer_langs -= L
diff --git a/code/modules/mob/living/simple_animal/bees.dm b/code/modules/mob/living/simple_animal/bees.dm
index 45ece9f6df1..daa247fbbf1 100644
--- a/code/modules/mob/living/simple_animal/bees.dm
+++ b/code/modules/mob/living/simple_animal/bees.dm
@@ -13,7 +13,7 @@
var/obj/machinery/apiary/parent
pass_flags = PASSTABLE
turns_per_move = 6
- var/obj/machinery/hydroponics/my_hydrotray
+ var/obj/machinery/portable_atmospherics/hydroponics/my_hydrotray
/mob/living/simple_animal/bee/New(loc, var/obj/machinery/apiary/new_parent)
..()
@@ -155,10 +155,10 @@
turns_per_move = rand(1,3)
else if(feral < 0)
turns_since_move = 0
- else if(!my_hydrotray || my_hydrotray.loc != src.loc || !my_hydrotray.planted || my_hydrotray.dead || !my_hydrotray.myseed)
- var/obj/machinery/hydroponics/my_hydrotray = locate() in src.loc
+ else if(!my_hydrotray || my_hydrotray.loc != src.loc || my_hydrotray.dead || !my_hydrotray.seed)
+ var/obj/machinery/portable_atmospherics/hydroponics/my_hydrotray = locate() in src.loc
if(my_hydrotray)
- if(my_hydrotray.planted && !my_hydrotray.dead && my_hydrotray.myseed)
+ if(!my_hydrotray.dead && my_hydrotray.seed)
turns_per_move = rand(20,50)
else
my_hydrotray = null
diff --git a/code/modules/mob/living/simple_animal/borer.dm b/code/modules/mob/living/simple_animal/borer.dm
index 0d15aeab543..7ffd57e96a2 100644
--- a/code/modules/mob/living/simple_animal/borer.dm
+++ b/code/modules/mob/living/simple_animal/borer.dm
@@ -1,6 +1,9 @@
+/datum/game_mode/var/list/borers = list()
+
/mob/living/captive_brain
name = "host brain"
real_name = "host brain"
+ universal_understand = 1
/mob/living/captive_brain/say(var/message)
@@ -45,6 +48,8 @@
friendly = "prods"
wander = 0
pass_flags = PASSTABLE
+ universal_understand = 1
+ holder_type = /obj/item/weapon/holder/borer
var/used_dominate
var/chemicals = 10 // Chemicals used for reproduction and spitting neurotoxin.
@@ -53,6 +58,11 @@
var/mob/living/captive_brain/host_brain // Used for swapping control of the body back and forth.
var/controlling // Used in human death check.
var/docile = 0 // Sugar can stop borers from acting.
+ var/has_reproduced
+ var/roundstart
+
+/mob/living/simple_animal/borer/roundstart
+ roundstart = 1
/mob/living/simple_animal/borer/Life()
@@ -96,9 +106,7 @@
..()
truename = "[pick("Primary","Secondary","Tertiary","Quaternary")] [rand(1000,9999)]"
host_brain = new/mob/living/captive_brain(src)
-
- request_player()
-
+ if(!roundstart) request_player()
/mob/living/simple_animal/borer/say(var/message)
@@ -338,7 +346,7 @@
detatch()
-mob/living/simple_animal/borer/proc/detatch()
+/mob/living/simple_animal/borer/proc/detatch()
if(!host) return
@@ -530,7 +538,7 @@ mob/living/simple_animal/borer/proc/detatch()
src << text("\blue You have stopped hiding.")
//Procs for grabbing players.
-mob/living/simple_animal/borer/proc/request_player()
+/mob/living/simple_animal/borer/proc/request_player()
for(var/mob/dead/observer/O in player_list)
if(jobban_isbanned(O, "Syndicate"))
continue
@@ -538,7 +546,7 @@ mob/living/simple_animal/borer/proc/request_player()
if(O.client.prefs.be_special & BE_ALIEN)
question(O.client)
-mob/living/simple_animal/borer/proc/question(var/client/C)
+/mob/living/simple_animal/borer/proc/question(var/client/C)
spawn(0)
if(!C) return
var/response = alert(C, "A cortical borer needs a player. Are you interested?", "Cortical borer request", "Yes", "No", "Never for this round")
@@ -549,7 +557,7 @@ mob/living/simple_animal/borer/proc/question(var/client/C)
else if (response == "Never for this round")
C.prefs.be_special ^= BE_ALIEN
-mob/living/simple_animal/borer/proc/transfer_personality(var/client/candidate)
+/mob/living/simple_animal/borer/proc/transfer_personality(var/client/candidate)
if(!candidate)
return
@@ -558,3 +566,4 @@ mob/living/simple_animal/borer/proc/transfer_personality(var/client/candidate)
src.ckey = candidate.ckey
if(src.mind)
src.mind.assigned_role = "Cortical Borer"
+ src.mind.special_role = "Cortical Borer"
\ No newline at end of file
diff --git a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm
index 41bdbfe323f..822d4e65fa6 100644
--- a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm
+++ b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm
@@ -45,8 +45,8 @@
if(udder && prob(5))
udder.add_reagent("milk", rand(5, 10))
- if(locate(/obj/effect/spacevine) in loc)
- var/obj/effect/spacevine/SV = locate(/obj/effect/spacevine) in loc
+ if(locate(/obj/effect/plantsegment) in loc)
+ var/obj/effect/plantsegment/SV = locate(/obj/effect/plantsegment) in loc
del(SV)
if(prob(10))
say("Nom")
@@ -55,7 +55,7 @@
for(var/direction in shuffle(list(1,2,4,8,5,6,9,10)))
var/step = get_step(src, direction)
if(step)
- if(locate(/obj/effect/spacevine) in step)
+ if(locate(/obj/effect/plantsegment) in step)
Move(step)
/mob/living/simple_animal/hostile/retaliate/goat/Retaliate()
@@ -65,8 +65,8 @@
/mob/living/simple_animal/hostile/retaliate/goat/Move()
..()
if(!stat)
- if(locate(/obj/effect/spacevine) in loc)
- var/obj/effect/spacevine/SV = locate(/obj/effect/spacevine) in loc
+ if(locate(/obj/effect/plantsegment) in loc)
+ var/obj/effect/plantsegment/SV = locate(/obj/effect/plantsegment) in loc
del(SV)
if(prob(10))
say("Nom")
diff --git a/code/modules/mob/living/simple_animal/friendly/mouse.dm b/code/modules/mob/living/simple_animal/friendly/mouse.dm
index f897d2de569..5a13dccc49b 100644
--- a/code/modules/mob/living/simple_animal/friendly/mouse.dm
+++ b/code/modules/mob/living/simple_animal/friendly/mouse.dm
@@ -126,7 +126,7 @@
src << "You are too small to pull anything."
return
-/mob/living/simple_animal/mouse/HasEntered(AM as mob|obj)
+/mob/living/simple_animal/mouse/Crossed(AM as mob|obj)
if( ishuman(AM) )
if(!stat)
var/mob/M = AM
diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm
index 03d7cb758ff..ddbd35d0e34 100644
--- a/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm
+++ b/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm
@@ -259,7 +259,7 @@
if(spawnees & 128)
C = new(src.loc)
C.name = "Drone phoron overcharge counter"
- C.origin_tech = "phoron=[rand(3,6)]"
+ C.origin_tech = "phorontech=[rand(3,6)]"
if(spawnees & 256)
C = new(src.loc)
diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm
index f425f1a019d..0fb08d08688 100644
--- a/code/modules/mob/living/simple_animal/simple_animal.dm
+++ b/code/modules/mob/living/simple_animal/simple_animal.dm
@@ -87,12 +87,9 @@
if(health > maxHealth)
health = maxHealth
- if(stunned)
- AdjustStunned(-1)
- if(weakened)
- AdjustWeakened(-1)
- if(paralysis)
- AdjustParalysis(-1)
+ handle_stunned()
+ handle_weakened()
+ handle_paralysed()
//Movement
if(!client && !stop_automated_movement && wander && !anchored)
@@ -141,41 +138,41 @@
//Atmos
var/atmos_suitable = 1
- var/atom/A = src.loc
+ var/atom/A = src.loc
if(istype(A,/turf))
- var/turf/T = A
-
+ var/turf/T = A
+
var/datum/gas_mixture/Environment = T.return_air()
-
+
if(Environment)
-
+
if( abs(Environment.temperature - bodytemperature) > 40 )
bodytemperature += ((Environment.temperature - bodytemperature) / 5)
-
+
if(min_oxy)
- if(Environment.oxygen < min_oxy)
+ if(Environment.gas["oxygen"] < min_oxy)
atmos_suitable = 0
if(max_oxy)
- if(Environment.oxygen > max_oxy)
+ if(Environment.gas["oxygen"] > max_oxy)
atmos_suitable = 0
if(min_tox)
- if(Environment.phoron < min_tox)
+ if(Environment.gas["phoron"] < min_tox)
atmos_suitable = 0
if(max_tox)
- if(Environment.phoron > max_tox)
+ if(Environment.gas["phoron"] > max_tox)
atmos_suitable = 0
if(min_n2)
- if(Environment.nitrogen < min_n2)
+ if(Environment.gas["nitrogen"] < min_n2)
atmos_suitable = 0
if(max_n2)
- if(Environment.nitrogen > max_n2)
+ if(Environment.gas["nitrogen"] > max_n2)
atmos_suitable = 0
if(min_co2)
- if(Environment.carbon_dioxide < min_co2)
+ if(Environment.gas["carbon_dioxide"] < min_co2)
atmos_suitable = 0
if(max_co2)
- if(Environment.carbon_dioxide > max_co2)
+ if(Environment.gas["carbon_dioxide"] > max_co2)
atmos_suitable = 0
//Atmos effect
@@ -337,7 +334,7 @@
var/damage = rand(1, 3)
- if(istype(src, /mob/living/carbon/slime/adult))
+ if(M.is_adult)
damage = rand(20, 40)
else
damage = rand(5, 35)
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index d1b5f1c7b1e..71aa42a05a5 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -13,25 +13,6 @@
living_mob_list += src
..()
-/mob/proc/Cell()
- set category = "Admin"
- set hidden = 1
-
- if(!loc) return 0
-
- var/datum/gas_mixture/environment = loc.return_air()
-
- var/t = "\blue Coordinates: [x],[y] \n"
- t+= "\red Temperature: [environment.temperature] \n"
- t+= "\blue Nitrogen: [environment.nitrogen] \n"
- t+= "\blue Oxygen: [environment.oxygen] \n"
- t+= "\blue Phoron : [environment.phoron] \n"
- t+= "\blue Carbon Dioxide: [environment.carbon_dioxide] \n"
- for(var/datum/gas/trace_gas in environment.trace_gases)
- usr << "\blue [trace_gas.type]: [trace_gas.moles] \n"
-
- usr.show_message(t, 1)
-
/mob/proc/show_message(msg, type, alt, alt_type)//Message, type of message (1 or 2), alternative message, alt message type (1 or 2)
if(!client) return
@@ -721,7 +702,7 @@ note dizziness decrements automatically in the mob's Life() proc.
stat(null,"MasterController-ERROR")
if(listed_turf && client)
- if(get_dist(listed_turf,src) > 1)
+ if(!TurfAdjacent(listed_turf))
listed_turf = null
else
statpanel(listed_turf.name, null, listed_turf)
@@ -768,19 +749,14 @@ note dizziness decrements automatically in the mob's Life() proc.
else if(buckled && (!buckled.movable))
anchored = 1
canmove = 0
- if( istype(buckled,/obj/structure/stool/bed/chair) )
+ if(istype(buckled,/obj/structure/stool/bed/chair) )
lying = 0
else
lying = 1
- else if(buckled && (buckled.movable))
- anchored = 0
- canmove = 1
- lying = 0
else if( stat || weakened || paralysis || resting || sleeping || (status_flags & FAKEDEATH))
lying = 1
canmove = 0
else if( stunned )
-// lying = 0
canmove = 0
else if(captured)
anchored = 1
@@ -996,10 +972,10 @@ mob/proc/yank_out_object()
affected.take_damage((selection.w_class * 3), 0, 0, 1, "Embedded object extraction")
if(prob(selection.w_class * 5)) //I'M SO ANEMIC I COULD JUST -DIE-.
- var/datum/wound/internal_bleeding/I = new (15)
+ var/datum/wound/internal_bleeding/I = new (min(selection.w_class * 5, 15))
affected.wounds += I
H.custom_pain("Something tears wetly in your [affected] as [selection] is pulled free!", 1)
-
+
if (ishuman(U))
var/mob/living/carbon/human/human_user = U
human_user.bloody_hands(H)
@@ -1012,3 +988,46 @@ mob/proc/yank_out_object()
if(!pinned.len)
anchored = 0
return 1
+
+/mob/living/proc/handle_statuses()
+ handle_stunned()
+ handle_weakened()
+ handle_stuttering()
+ handle_silent()
+ handle_drugged()
+ handle_slurring()
+
+/mob/living/proc/handle_stunned()
+ if(stunned)
+ AdjustStunned(-1)
+ return stunned
+
+/mob/living/proc/handle_weakened()
+ if(weakened)
+ weakened = max(weakened-1,0) //before you get mad Rockdtben: I done this so update_canmove isn't called multiple times
+ return weakened
+
+/mob/living/proc/handle_stuttering()
+ if(stuttering)
+ stuttering = max(stuttering-1, 0)
+ return stuttering
+
+/mob/living/proc/handle_silent()
+ if(silent)
+ silent = max(silent-1, 0)
+ return silent
+
+/mob/living/proc/handle_drugged()
+ if(druggy)
+ druggy = max(druggy-1, 0)
+ return druggy
+
+/mob/living/proc/handle_slurring()
+ if(slurring)
+ slurring = max(slurring-1, 0)
+ return slurring
+
+/mob/living/proc/handle_paralysed() // Currently only used by simple_animal.dm, treated as a special case in other mobs
+ if(paralysis)
+ AdjustParalysis(-1)
+ return paralysis
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index 333363bacf9..08f80ceaabf 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -35,11 +35,6 @@
return 1
return 0
-/proc/isslimeadult(A)
- if(istype(A, /mob/living/carbon/slime/adult))
- return 1
- return 0
-
/proc/isrobot(A)
if(istype(A, /mob/living/silicon/robot))
return 1
@@ -124,8 +119,44 @@ proc/hasorgans(A)
return ishuman(A)
/proc/hsl2rgb(h, s, l)
- return
+ return //TODO: Implement
+/*
+ Miss Chance
+*/
+
+//TODO: Integrate defence zones and targeting body parts with the actual organ system, move these into organ definitions.
+
+//The base miss chance for the different defence zones
+var/list/global/base_miss_chance = list(
+ "head" = 40,
+ "chest" = 10,
+ "groin" = 20,
+ "l_leg" = 20,
+ "r_leg" = 20,
+ "l_arm" = 20,
+ "r_arm" = 20,
+ "l_hand" = 50,
+ "r_hand" = 50,
+ "l_foot" = 50,
+ "r_foot" = 50,
+)
+
+//Used to weight organs when an organ is hit randomly (i.e. not a directed, aimed attack).
+//Also used to weight the protection value that armour provides for covering that body part when calculating protection from full-body effects.
+var/list/global/organ_rel_size = list(
+ "head" = 25,
+ "chest" = 70,
+ "groin" = 30,
+ "l_leg" = 25,
+ "r_leg" = 25,
+ "l_arm" = 25,
+ "r_arm" = 25,
+ "l_hand" = 10,
+ "r_hand" = 10,
+ "l_foot" = 10,
+ "r_foot" = 10,
+)
/proc/check_zone(zone)
if(!zone) return "chest"
@@ -134,39 +165,34 @@ proc/hasorgans(A)
zone = "head"
if("mouth")
zone = "head"
-/* if("l_hand")
- zone = "l_arm"
- if("r_hand")
- zone = "r_arm"
- if("l_foot")
- zone = "l_leg"
- if("r_foot")
- zone = "r_leg"
- if("groin")
- zone = "chest"
-*/
return zone
-// Returns zone with a certain probability.
-// If the probability misses, returns "chest" instead.
-// If "chest" was passed in as zone, then on a "miss" will return "head", "l_arm", or "r_arm"
+// Returns zone with a certain probability. If the probability fails, or no zone is specified, then a random body part is chosen.
// Do not use this if someone is intentionally trying to hit a specific body part.
// Use get_zone_with_miss_chance() for that.
/proc/ran_zone(zone, probability)
- zone = check_zone(zone)
- if(!probability) probability = 90
- if(probability == 100) return zone
+ if (zone)
+ zone = check_zone(zone)
+ if (prob(probability))
+ return zone
- if(zone == "chest")
- if(prob(probability)) return "chest"
- var/t = rand(1, 9)
- switch(t)
- if(1 to 3) return "head"
- if(4 to 6) return "l_arm"
- if(7 to 9) return "r_arm"
-
- if(prob(probability * 0.75)) return zone
- return "chest"
+ var/ran_zone = zone
+ while (ran_zone == zone)
+ ran_zone = pick (
+ organ_rel_size["head"]; "head",
+ organ_rel_size["chest"]; "chest",
+ organ_rel_size["groin"]; "groin",
+ organ_rel_size["l_arm"]; "l_arm",
+ organ_rel_size["r_arm"]; "r_arm",
+ organ_rel_size["l_leg"]; "l_leg",
+ organ_rel_size["r_leg"]; "r_leg",
+ organ_rel_size["l_hand"]; "l_hand",
+ organ_rel_size["r_hand"]; "r_hand",
+ organ_rel_size["l_foot"]; "l_foot",
+ organ_rel_size["r_foot"]; "r_foot",
+ )
+
+ return ran_zone
// Emulates targetting a specific body part, and miss chances
// May return null if missed
@@ -177,42 +203,13 @@ proc/hasorgans(A)
// you can only miss if your target is standing and not restrained
if(!target.buckled && !target.lying)
var/miss_chance = 10
- switch(zone)
- if("head")
- miss_chance = 40
- if("l_leg")
- miss_chance = 20
- if("r_leg")
- miss_chance = 20
- if("l_arm")
- miss_chance = 20
- if("r_arm")
- miss_chance = 20
- if("l_hand")
- miss_chance = 50
- if("r_hand")
- miss_chance = 50
- if("l_foot")
- miss_chance = 50
- if("r_foot")
- miss_chance = 50
+ if (zone in base_miss_chance)
+ miss_chance = base_miss_chance[zone]
miss_chance = max(miss_chance + miss_chance_mod, 0)
if(prob(miss_chance))
if(prob(70))
return null
- else
- var/t = rand(1, 10)
- switch(t)
- if(1) return "head"
- if(2) return "l_arm"
- if(3) return "r_arm"
- if(4) return "chest"
- if(5) return "l_foot"
- if(6) return "r_foot"
- if(7) return "l_hand"
- if(8) return "r_hand"
- if(9) return "l_leg"
- if(10) return "r_leg"
+ return pick(base_miss_chance)
return zone
diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm
index fd6e4bdc901..9071e3193b9 100644
--- a/code/modules/mob/new_player/new_player.dm
+++ b/code/modules/mob/new_player/new_player.dm
@@ -301,6 +301,7 @@
var/mob/living/carbon/human/character = create_character() //creates the human and transfers vars and mind
job_master.EquipRank(character, rank, 1) //equips the human
+ UpdateFactionList(character)
EquipCustomItems(character)
//Find our spawning point.
@@ -330,6 +331,9 @@
if(character.mind.assigned_role != "Cyborg")
data_core.manifest_inject(character)
ticker.minds += character.mind//Cyborgs and AIs handle this in the transform proc. //TODO!!!!! ~Carn
+
+ //Grab some data from the character prefs for use in random news procs.
+
AnnounceArrival(character, rank, join_message)
else
@@ -439,7 +443,7 @@
proc/ViewManifest()
var/dat = ""
- dat += "
Crew Manifest
"
+ dat += "
Show Crew Manifest
"
dat += data_core.get_manifest(OOC = 1)
src << browse(dat, "window=manifest;size=370x420;can_close=1")
diff --git a/code/modules/mob/new_player/sprite_accessories.dm b/code/modules/mob/new_player/sprite_accessories.dm
index a0bc4cb09ff..1e5906eaef4 100644
--- a/code/modules/mob/new_player/sprite_accessories.dm
+++ b/code/modules/mob/new_player/sprite_accessories.dm
@@ -118,6 +118,11 @@
name = "Ponytail 3"
icon_state = "hair_ponytail3"
+ ponytail4
+ name = "Ponytail 4"
+ icon_state = "hair_ponytail4"
+ gender = FEMALE
+
sideponytail
name = "Side Ponytail"
icon_state = "hair_stail"
@@ -156,6 +161,11 @@
gender = FEMALE
species_allowed = list("Human","Unathi")
+ beehive2
+ name = "Beehive 2"
+ icon_state = "hair_beehive2"
+ gender = FEMALE
+
bobcurl
name = "Bobcurl"
icon_state = "hair_bobcurl"
@@ -189,6 +199,16 @@
icon_state = "hair_combover"
gender = MALE
+ father
+ name = "Father"
+ icon_state = "hair_father"
+ gender = MALE
+
+ reversemohawk
+ name = "Reverse Mohawk"
+ icon_state = "hair_reversemohawk"
+ gender = MALE
+
devillock
name = "Devil Lock"
icon_state = "hair_devilock"
@@ -223,6 +243,11 @@
name = "Emo"
icon_state = "hair_emo"
+ longemo
+ name = "Long Emo"
+ icon_state = "hair_emolong"
+ gender = FEMALE
+
shortovereye
name = "Overeye Short"
icon_state = "hair_shortovereye"
@@ -286,6 +311,11 @@
icon_state = "hair_braid"
gender = FEMALE
+ mbraid
+ name = "Medium Braid"
+ icon_state = "hair_shortbraid"
+ gender = FEMALE
+
braid2
name = "Long Braid"
icon_state = "hair_hbraid"
@@ -315,6 +345,61 @@
icon_state = "hair_e"
gender = MALE // turnoff!
+ familyman
+ name = "The Family Man"
+ icon_state = "hair_thefamilyman"
+ gender = MALE
+
+ mahdrills
+ name = "Drillruru"
+ icon_state = "hair_drillruru"
+ gender = FEMALE
+
+ dandypomp
+ name = "Dandy Pompadour"
+ icon_state = "hair_dandypompadour"
+ gender = MALE
+
+ poofy
+ name = "Poofy"
+ icon_state = "hair_poofy"
+ gender = FEMALE
+
+ crono
+ name = "Chrono"
+ icon_state = "hair_toriyama"
+ gender = MALE
+
+ vegeta
+ name = "Vegeta"
+ icon_state = "hair_toriyama2"
+ gender = MALE
+
+ cia
+ name = "CIA"
+ icon_state = "hair_cia"
+ gender = MALE
+
+ mulder
+ name = "Mulder"
+ icon_state = "hair_mulder"
+ gender = MALE
+
+ scully
+ name = "Scully"
+ icon_state = "hair_scully"
+ gender = FEMALE
+
+ nitori
+ name = "Nitori"
+ icon_state = "hair_nitori"
+ gender = FEMALE
+
+ joestar
+ name = "Joestar"
+ icon_state = "hair_joestar"
+ gender = MALE
+
bald
name = "Bald"
icon_state = "bald"
diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm
index b063cb7bc13..2307d00bcb7 100644
--- a/code/modules/mob/transform_procs.dm
+++ b/code/modules/mob/transform_procs.dm
@@ -120,14 +120,7 @@
O.show_laws()
O << "These laws may be changed by other players, or by you being the traitor."
- O.verbs += /mob/living/silicon/ai/proc/ai_call_shuttle
- O.verbs += /mob/living/silicon/ai/proc/show_laws_verb
- O.verbs += /mob/living/silicon/ai/proc/ai_camera_track
- O.verbs += /mob/living/silicon/ai/proc/ai_alerts
- O.verbs += /mob/living/silicon/ai/proc/ai_camera_list
- O.verbs += /mob/living/silicon/ai/proc/ai_statuschange
- O.verbs += /mob/living/silicon/ai/proc/ai_roster
-
+ O.add_ai_verbs()
O.job = "AI"
O.rename_self("ai",1)
@@ -245,11 +238,10 @@
babies += M
new_slime = pick(babies)
else
+ new_slime = new /mob/living/carbon/slime(loc)
if(adult)
- new_slime = new /mob/living/carbon/slime/adult(loc)
+ new_slime.is_adult = 1
else
- new_slime = new /mob/living/carbon/slime(loc)
- new_slime.a_intent = "hurt"
new_slime.key = key
new_slime << "You are now a slime. Skreee!"
diff --git a/code/modules/nano/nanoexternal.dm b/code/modules/nano/nanoexternal.dm
index 2331debd9be..50e2b706f85 100644
--- a/code/modules/nano/nanoexternal.dm
+++ b/code/modules/nano/nanoexternal.dm
@@ -32,11 +32,12 @@
*
* @param user /mob The mob who is interacting with this ui
* @param ui_key string A string key to use for this ui. Allows for multiple unique uis on one obj/mob (defaut value "main")
- * @param ui /datum/nanoui This parameter is passed by the nanoui process() proc when updating an open ui
+ * @param ui /datum/nanoui This parameter is passed by the nanoui process() proc when updating an open ui
+ * @param force_open boolean Force the UI to (re)open, even if it's already open
*
* @return nothing
*/
-/atom/movable/proc/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null)
+/atom/movable/proc/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
return
// Used by the Nano UI Manager (/datum/nanomanager) to track UIs opened by this mob
diff --git a/code/modules/nano/nanomanager.dm b/code/modules/nano/nanomanager.dm
index a10965a4489..f3396ecd3f6 100644
--- a/code/modules/nano/nanomanager.dm
+++ b/code/modules/nano/nanomanager.dm
@@ -39,19 +39,23 @@
* @param ui_key string A string key used for the ui
* @param ui /datum/nanoui An existing instance of the ui (can be null)
* @param data list The data to be passed to the ui, if it exists
+ * @param force_open boolean The ui is being forced to (re)open, so close ui if it exists (instead of updating)
*
* @return /nanoui Returns the found ui, for null if none exists
*/
-/datum/nanomanager/proc/try_update_ui(var/mob/user, src_object, ui_key, var/datum/nanoui/ui, data)
+/datum/nanomanager/proc/try_update_ui(var/mob/user, src_object, ui_key, var/datum/nanoui/ui, data, var/force_open = 0)
if (isnull(ui)) // no ui has been passed, so we'll search for one
{
ui = get_open_ui(user, src_object, ui_key)
- }
+ }
if (!isnull(ui))
- // The UI is already open so push the data to it
- ui.push_data(data)
- return ui
-
+ // The UI is already open
+ if (!force_open)
+ ui.push_data(data)
+ return ui
+ else
+ //testing("nanomanager/try_update_ui mob [user.name] [src_object:name] [ui_key] [force_open] - forcing opening of ui")
+ ui.close()
return null
/**
@@ -66,14 +70,17 @@
/datum/nanomanager/proc/get_open_ui(var/mob/user, src_object, ui_key)
var/src_object_key = "\ref[src_object]"
if (isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list))
+ //testing("nanomanager/get_open_ui mob [user.name] [src_object:name] [ui_key] - there are no uis open")
return null
- else if (isnull(open_uis[src_object_key][ui_key]) || !istype(open_uis[src_object_key][ui_key], /list))
+ else if (isnull(open_uis[src_object_key][ui_key]) || !istype(open_uis[src_object_key][ui_key], /list))
+ //testing("nanomanager/get_open_ui mob [user.name] [src_object:name] [ui_key] - there are no uis open for this object")
return null
for (var/datum/nanoui/ui in open_uis[src_object_key][ui_key])
if (ui.user == user)
return ui
-
+
+ //testing("nanomanager/get_open_ui mob [user.name] [src_object:name] [ui_key] - ui not found")
return null
/**
@@ -83,7 +90,7 @@
*
* @return int The number of uis updated
*/
-/datum/nanomanager/proc/update_uis(src_object)
+/datum/nanomanager/proc/update_uis(src_object)
var/src_object_key = "\ref[src_object]"
if (isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list))
return 0
@@ -128,7 +135,8 @@
*/
/datum/nanomanager/proc/close_user_uis(var/mob/user, src_object = null, ui_key = null)
if (isnull(user.open_uis) || !istype(user.open_uis, /list) || open_uis.len == 0)
- return 0 // has no open uis
+ //testing("nanomanager/close_user_uis mob [user.name] has no open uis")
+ return 0 // has no open uis
var/close_count = 0
for (var/datum/nanoui/ui in user.open_uis)
@@ -136,6 +144,8 @@
ui.close()
close_count++
+ //testing("nanomanager/close_user_uis mob [user.name] closed [open_uis.len] of [close_count] uis")
+
return close_count
/**
@@ -157,6 +167,7 @@
var/list/uis = open_uis[src_object_key][ui.ui_key]
uis.Add(ui)
processing_uis.Add(ui)
+ //testing("nanomanager/ui_opened mob [ui.user.name] [ui.src_object:name] [ui.ui_key] - user.open_uis [ui.user.open_uis.len] | uis [uis.len] | processing_uis [processing_uis.len]")
/**
* Remove a /nanoui ui from the list of open uis
@@ -176,7 +187,11 @@
processing_uis.Remove(ui)
ui.user.open_uis.Remove(ui)
var/list/uis = open_uis[src_object_key][ui.ui_key]
- return uis.Remove(ui)
+ uis.Remove(ui)
+
+ //testing("nanomanager/ui_closed mob [ui.user.name] [ui.src_object:name] [ui.ui_key] - user.open_uis [ui.user.open_uis.len] | uis [uis.len] | processing_uis [processing_uis.len]")
+
+ return 1
/**
* This is called on user logout
@@ -189,6 +204,7 @@
//
/datum/nanomanager/proc/user_logout(var/mob/user)
+ //testing("nanomanager/user_logout user [user.name]")
return close_user_uis(user)
/**
@@ -201,7 +217,9 @@
* @return nothing
*/
/datum/nanomanager/proc/user_transferred(var/mob/oldMob, var/mob/newMob)
+ //testing("nanomanager/user_transferred from mob [oldMob.name] to mob [newMob.name]")
if (isnull(oldMob.open_uis) || !istype(oldMob.open_uis, /list) || open_uis.len == 0)
+ //testing("nanomanager/user_transferred mob [oldMob.name] has no open uis")
return 0 // has no open uis
if (isnull(newMob.open_uis) || !istype(newMob.open_uis, /list))
diff --git a/code/modules/nano/nanomapgen.dm b/code/modules/nano/nanomapgen.dm
new file mode 100644
index 00000000000..dd65b7bbd08
--- /dev/null
+++ b/code/modules/nano/nanomapgen.dm
@@ -0,0 +1,90 @@
+// This file is a modified version of https://raw2.github.com/Baystation12/OldCode-BS12/master/code/TakePicture.dm
+
+#define NANOMAP_ICON_SIZE 4
+#define NANOMAP_MAX_ICON_DIMENSION 1024
+
+#define NANOMAP_TILES_PER_IMAGE (NANOMAP_MAX_ICON_DIMENSION / NANOMAP_ICON_SIZE)
+
+#define NANOMAP_TERMINALERR 5
+#define NANOMAP_INPROGRESS 2
+#define NANOMAP_BADOUTPUT 2
+#define NANOMAP_SUCCESS 1
+#define NANOMAP_WATCHDOGSUCCESS 4
+#define NANOMAP_WATCHDOGTERMINATE 3
+
+
+//Call these procs to dump your world to a series of image files (!!)
+//NOTE: Does not explicitly support non 32x32 icons or stuff with large pixel_* values, so don't blame me if it doesn't work perfectly
+
+/client/proc/nanomapgen_DumpImage()
+ set name = "Generate NanoUI Map"
+ set category = "Server"
+
+ if(holder)
+ nanomapgen_DumpTile(1, 1, text2num(input(usr,"Enter the Z level to generate")))
+
+/client/proc/nanomapgen_DumpTile(var/startX = 1, var/startY = 1, var/currentZ = 1, var/endX = -1, var/endY = -1)
+
+ if (endX < 0 || endX > world.maxx)
+ endX = world.maxx
+
+ if (endY < 0 || endY > world.maxy)
+ endY = world.maxy
+
+ if (currentZ < 0 || currentZ > world.maxz)
+ usr << "NanoMapGen: ERROR: currentZ ([currentZ]) must be between 1 and [world.maxz]"
+
+ sleep(3)
+ return NANOMAP_TERMINALERR
+
+ if (startX > endX)
+ usr << "NanoMapGen: ERROR: startX ([startX]) cannot be greater than endX ([endX])"
+
+ sleep(3)
+ return NANOMAP_TERMINALERR
+
+ if (startY > endX)
+ usr << "NanoMapGen: ERROR: startY ([startY]) cannot be greater than endY ([endY])"
+ sleep(3)
+ return NANOMAP_TERMINALERR
+
+ var/icon/Tile = icon(file("nano/mapbase1024.png"))
+ if (Tile.Width() != NANOMAP_MAX_ICON_DIMENSION || Tile.Height() != NANOMAP_MAX_ICON_DIMENSION)
+ world.log << "NanoMapGen: ERROR: BASE IMAGE DIMENSIONS ARE NOT [NANOMAP_MAX_ICON_DIMENSION]x[NANOMAP_MAX_ICON_DIMENSION]"
+ sleep(3)
+ return NANOMAP_TERMINALERR
+
+ world.log << "NanoMapGen: GENERATE MAP ([startX],[startY],[currentZ]) to ([endX],[endY],[currentZ])"
+ usr << "NanoMapGen: GENERATE MAP ([startX],[startY],[currentZ]) to ([endX],[endY],[currentZ])"
+
+ var/count = 0;
+ for(var/WorldX = startX, WorldX <= endX, WorldX++)
+ for(var/WorldY = startY, WorldY <= endY, WorldY++)
+
+ var/atom/Turf = locate(WorldX, WorldY, currentZ)
+
+ var/icon/TurfIcon = new(Turf.icon, Turf.icon_state)
+ TurfIcon.Scale(NANOMAP_ICON_SIZE, NANOMAP_ICON_SIZE)
+
+ Tile.Blend(TurfIcon, ICON_OVERLAY, ((WorldX - 1) * NANOMAP_ICON_SIZE), ((WorldY - 1) * NANOMAP_ICON_SIZE))
+
+ count++
+
+ if (count % 8000 == 0)
+ world.log << "NanoMapGen: [count] tiles done"
+ sleep(1)
+
+ var/mapFilename = "nanomap_z[currentZ]-new.png"
+
+ world.log << "NanoMapGen: sending [mapFilename] to client"
+
+ usr << browse(Tile, "window=picture;file=[mapFilename];display=0")
+
+ world.log << "NanoMapGen: Done."
+
+ usr << "NanoMapGen: Done. File [mapFilename] uploaded to your cache."
+
+ if (Tile.Width() != NANOMAP_MAX_ICON_DIMENSION || Tile.Height() != NANOMAP_MAX_ICON_DIMENSION)
+ return NANOMAP_BADOUTPUT
+
+ return NANOMAP_SUCCESS
\ No newline at end of file
diff --git a/code/modules/nano/nanoui.dm b/code/modules/nano/nanoui.dm
index 1a3257ed24b..b4d20155389 100644
--- a/code/modules/nano/nanoui.dm
+++ b/code/modules/nano/nanoui.dm
@@ -36,11 +36,20 @@ nanoui is used to open and update nano browser uis
var/list/stylesheets = list()
// the list of javascript scripts to use for this ui
var/list/scripts = list()
- // the list of templates to use with this ui (usually just one)
+ // a list of templates which can be used with this ui
var/templates[0]
- // the body content for this ui, do not change unless you know what you're doing
- // the #mainTemplate div will contain the compiled "main" template html
- var/content = ""
+ // the layout key for this ui (this is used on the frontend, leave it as "default" unless you know what you're doing)
+ var/layout_key = "default"
+ // this sets whether to re-render the ui layout with each update (default 0, turning on will break the map ui if it's in use)
+ var/auto_update_layout = 0
+ // this sets whether to re-render the ui content with each update (default 1)
+ var/auto_update_content = 1
+ // the default state to use for this ui (this is used on the frontend, leave it as "default" unless you know what you're doing)
+ var/state_key = "default"
+ // show the map ui, this is used by the default layout
+ var/show_map = 0
+ // the map z level to display
+ var/map_z_level = 1
// initial data, containing the full data structure, must be sent to the ui (the data structure cannot be extended later on)
var/list/initial_data[0]
// set to 1 to update the ui automatically every master_controller tick
@@ -57,7 +66,7 @@ nanoui is used to open and update nano browser uis
* @param nuser /mob The mob who has opened/owns this ui
* @param nsrc_object /obj|/mob The obj or mob which this ui belongs to
* @param nui_key string A string key to use for this ui. Allows for multiple unique uis on one src_oject
- * @param ntemplate string The name of the template file from /nano/templates (e.g. "my_template.tmpl")
+ * @param ntemplate string The filename of the template file from /nano/templates (e.g. "my_template.tmpl")
* @param ntitle string The title of this ui
* @param nwidth int the width of the ui window
* @param nheight int the height of the ui window
@@ -65,14 +74,14 @@ nanoui is used to open and update nano browser uis
*
* @return /nanoui new nanoui object
*/
-/datum/nanoui/New(nuser, nsrc_object, nui_key, ntemplate, ntitle = 0, nwidth = 0, nheight = 0, var/atom/nref = null)
+/datum/nanoui/New(nuser, nsrc_object, nui_key, ntemplate_filename, ntitle = 0, nwidth = 0, nheight = 0, var/atom/nref = null)
user = nuser
src_object = nsrc_object
ui_key = nui_key
window_id = "[ui_key]\ref[src_object]"
- // Add the passed template as the 'main' template, this is required
- add_template("main", ntemplate)
+ // add the passed template filename as the "main" template, this is required
+ add_template("main", ntemplate_filename)
if (ntitle)
title = ntitle
@@ -86,17 +95,21 @@ nanoui is used to open and update nano browser uis
add_common_assets()
/**
- * Use this proc to add assets which are common to all nano uis
+ * Use this proc to add assets which are common to (and required by) all nano uis
*
* @return nothing
*/
/datum/nanoui/proc/add_common_assets()
- add_script("libraries.min.js") // The jQuery library
- add_script("nano_config.js") // The NanoConfig JS, this is used to store configuration values.
- add_script("nano_update.js") // The NanoUpdate JS, this is used to receive updates and apply them.
- add_script("nano_base_helpers.js") // The NanoBaseHelpers JS, this is used to set up template helpers which are common to all templates
+ add_script("libraries.min.js") // A JS file comprising of jQuery, doT.js and jQuery Timer libraries (compressed together)
+ add_script("nano_utility.js") // The NanoUtility JS, this is used to store utility functions.
+ add_script("nano_template.js") // The NanoTemplate JS, this is used to render templates.
+ add_script("nano_state_manager.js") // The NanoStateManager JS, it handles updates from the server and passes data to the current state
+ add_script("nano_state.js") // The NanoState JS, this is the base state which all states must inherit from
+ add_script("nano_state_default.js") // The NanoStateDefault JS, this is the "default" state (used by all UIs by default), which inherits from NanoState
+ add_script("nano_base_callbacks.js") // The NanoBaseCallbacks JS, this is used to set up (before and after update) callbacks which are common to all UIs
+ add_script("nano_base_helpers.js") // The NanoBaseHelpers JS, this is used to set up template helpers which are common to all UIs
add_stylesheet("shared.css") // this CSS sheet is common to all UIs
- add_stylesheet("icons.css") // this CSS sheet is common to all UIs
+ add_stylesheet("icons.css") // this CSS sheet is common to all UIs
/**
* Set the current status (also known as visibility) of this ui.
@@ -107,12 +120,15 @@ nanoui is used to open and update nano browser uis
* @return nothing
*/
/datum/nanoui/proc/set_status(state, push_update)
- if (state != status)
- status = state
- if (push_update || !status)
- push_data(list(), 1) // Update the UI, force the update in case the status is 0
- else
- status = state
+ if (state != status) // Only update if it is different
+ if (status == STATUS_DISABLED)
+ status = state
+ if (push_update)
+ update()
+ else
+ status = state
+ if (push_update || status == 0)
+ push_data(null, 1) // Update the UI, force the update in case the status is 0, data is null so that previous data is used
/**
* Update the status (visibility) of this ui based on the user's status
@@ -121,7 +137,7 @@ nanoui is used to open and update nano browser uis
*
* @return nothing
*/
-/datum/nanoui/proc/update_status(push_update = 0)
+/datum/nanoui/proc/update_status(var/push_update = 0)
if (istype(user, /mob/living/silicon/ai))
set_status(STATUS_INTERACTIVE, push_update) // interactive (green visibility)
else if (istype(user, /mob/living/silicon/robot))
@@ -158,8 +174,8 @@ nanoui is used to open and update nano browser uis
*
* @return nothing
*/
-/datum/nanoui/proc/set_auto_update(state = 1)
- is_auto_updating = state
+/datum/nanoui/proc/set_auto_update(nstate = 1)
+ is_auto_updating = nstate
/**
* Set the initial data for the ui. This is vital as the data structure set here cannot be changed when pushing new updates.
@@ -169,21 +185,43 @@ nanoui is used to open and update nano browser uis
* @return nothing
*/
/datum/nanoui/proc/set_initial_data(list/data)
- initial_data = add_default_data(data)
+ initial_data = data
/**
- * Add default data to the data being sent to the ui.
+ * Get config data to sent to the ui.
*
- * @param data /list The list of data to be modified
- *
- * @return /list modified data
+ * @return /list config data
*/
-/datum/nanoui/proc/add_default_data(list/data)
- data["ui"] = list(
+/datum/nanoui/proc/get_config_data()
+ var/list/config_data = list(
+ "title" = title,
+ "srcObject" = list("name" = src_object.name),
+ "stateKey" = state_key,
"status" = status,
+ "autoUpdateLayout" = auto_update_layout,
+ "autoUpdateContent" = auto_update_content,
+ "showMap" = show_map,
+ "mapZLevel" = map_z_level,
"user" = list("name" = user.name)
)
- return data
+ return config_data
+
+ /**
+ * Get data to sent to the ui.
+ *
+ * @param data /list The list of general data for this ui (can be null to use previous data sent)
+ *
+ * @return /list data to send to the ui
+ */
+/datum/nanoui/proc/get_send_data(var/list/data)
+ var/list/config_data = get_config_data()
+
+ var/list/send_data = list("config" = config_data)
+
+ if (!isnull(data))
+ send_data["data"] = data
+
+ return send_data
/**
* Set the browser window options for this ui
@@ -197,6 +235,7 @@ nanoui is used to open and update nano browser uis
/**
* Add a CSS stylesheet to this UI
+ * These must be added before the UI has been opened, adding after that will have no effect
*
* @param file string The name of the CSS file from /nano/css (e.g. "my_style.css")
*
@@ -207,6 +246,7 @@ nanoui is used to open and update nano browser uis
/**
* Add a JavsScript script to this UI
+ * These must be added before the UI has been opened, adding after that will have no effect
*
* @param file string The name of the JavaScript file from /nano/js (e.g. "my_script.js")
*
@@ -216,30 +256,81 @@ nanoui is used to open and update nano browser uis
scripts.Add(file)
/**
- * Add a template to this UI
+ * Add a template for this UI
* Templates are combined with the data sent to the UI to create the rendered view
- * Each template needs a div in ui.content to contain the rendered content.
- * The div format is '' where is replaced with the templater's key.
- * All UIs are set up by default to use a 'main' template, so only use this proc if you want to add advanced functionality.
+ * These must be added before the UI has been opened, adding after that will have no effect
*
- * @param key string The key name for this template, used to identify the div to render this template into ('')
- * @param file string The name of the template file from /nano/templates (e.g. "my_template.tmpl")
+ * @param key string The key which is used to reference this template in the frontend
+ * @param filename string The name of the template file from /nano/templates (e.g. "my_template.tmpl")
*
* @return nothing
*/
-/datum/nanoui/proc/add_template(key, file)
- templates[key] = file
+/datum/nanoui/proc/add_template(key, filename)
+ templates[key] = filename
+
+ /**
+ * Set the layout key for use in the frontend Javascript
+ * The layout key is the basic layout key for the page
+ * Two files are loaded on the client based on the layout key varable:
+ * -> a template in /nano/templates with the filename "layout_.tmpl
+ * -> a CSS stylesheet in /nano/css with the filename "layout_.css
+ *
+ * @param nlayout string The layout key to use
+ *
+ * @return nothing
+ */
+/datum/nanoui/proc/set_layout_key(nlayout_key)
+ layout_key = lowertext(nlayout_key)
/**
- * Set the HTML content of the UI
- * This should only really be used to add more template divs (see the add_template() proc)
+ * Set the ui to update the layout (re-render it) on each update, turning this on will break the map ui (if it's being used)
*
- * @param ncontent string The new HTML content for this UI
+ * @param state int (bool) Set update to 1 or 0 (true/false) (default 0)
*
* @return nothing
*/
-/datum/nanoui/proc/set_content(ncontent)
- content = ncontent
+/datum/nanoui/proc/set_auto_update_layout(nstate)
+ auto_update_layout = nstate
+
+ /**
+ * Set the ui to update the main content (re-render it) on each update
+ *
+ * @param state int (bool) Set update to 1 or 0 (true/false) (default 1)
+ *
+ * @return nothing
+ */
+/datum/nanoui/proc/set_auto_update_content(nstate)
+ auto_update_content = nstate
+
+ /**
+ * Set the state key for use in the frontend Javascript
+ *
+ * @param nstate_key string The key of the state to use
+ *
+ * @return nothing
+ */
+/datum/nanoui/proc/set_state_key(nstate_key)
+ state_key = nstate_key
+
+ /**
+ * Toggle showing the map ui
+ *
+ * @param nstate_key boolean 1 to show map, 0 to hide (default is 0)
+ *
+ * @return nothing
+ */
+/datum/nanoui/proc/set_show_map(nstate)
+ show_map = nstate
+
+ /**
+ * Toggle showing the map ui
+ *
+ * @param nstate_key boolean 1 to show map, 0 to hide (default is 0)
+ *
+ * @return nothing
+ */
+/datum/nanoui/proc/set_map_z_level(nz)
+ map_z_level = nz
/**
* Set whether or not to use the "old" on close logic (mainly unset_machine())
@@ -252,11 +343,16 @@ nanoui is used to open and update nano browser uis
on_close_logic = state
/**
- * Return the HTML header content for this UI
+ * Return the HTML for this UI
*
- * @return string HTML header content
+ * @return string HTML for the UI
*/
-/datum/nanoui/proc/get_header()
+/datum/nanoui/proc/get_html()
+
+ // before the UI opens, add the layout files based on the layout key
+ add_stylesheet("layout_[layout_key].css")
+ add_template("layout", "layout_[layout_key].tmpl")
+
var/head_content = ""
for (var/filename in scripts)
@@ -264,68 +360,46 @@ nanoui is used to open and update nano browser uis
for (var/filename in stylesheets)
head_content += " "
-
- var/templatel_data[0]
- for (var/key in templates)
- templatel_data[key] = templates[key];
-
+
var/template_data_json = "{}" // An empty JSON object
- if (templatel_data.len > 0)
- template_data_json = list2json(templatel_data)
+ if (templates.len > 0)
+ template_data_json = list2json(templates)
- var/initial_data_json = "{}" // An empty JSON object
- if (initial_data.len > 0)
- initial_data_json = list2json(initial_data)
+ var/list/send_data = get_send_data(initial_data)
+ var/initial_data_json = list2json(send_data)
var/url_parameters_json = list2json(list("src" = "\ref[src]"))
- return {"
+ return {"
+
[head_content]
-
-
- [title ? "
[title]
" : ""]
-
-
Initiating...
- "}
-
- /**
- * Return the HTML footer content for this UI
- *
- * @return string HTML footer content
- */
-/datum/nanoui/proc/get_footer()
-
- return {"
-
+
+
+
-"}
-
- /**
- * Return the HTML for this UI
- *
- * @return string HTML for the UI
- */
-/datum/nanoui/proc/get_html()
- return {"
- [get_header()]
- [content]
- [get_footer()]
+
"}
/**
@@ -333,7 +407,8 @@ nanoui is used to open and update nano browser uis
*
* @return nothing
*/
-/datum/nanoui/proc/open()
+/datum/nanoui/proc/open()
+
var/window_size = ""
if (width && height)
window_size = "size=[width]x[height];"
@@ -377,9 +452,10 @@ nanoui is used to open and update nano browser uis
if (status == STATUS_DISABLED && !force_push)
return // Cannot update UI, no visibility
- data = add_default_data(data)
+ var/list/send_data = get_send_data(data)
+
//user << list2json(data) // used for debugging
- user << output(list2params(list(list2json(data))),"[window_id].browser:receiveUpdateData")
+ user << output(list2params(list(list2json(send_data))),"[window_id].browser:receiveUpdateData")
/**
* This Topic() proc is called whenever a user clicks on a link within a Nano UI
@@ -392,8 +468,18 @@ nanoui is used to open and update nano browser uis
update_status(0) // update the status
if (status != STATUS_INTERACTIVE || user != usr) // If UI is not interactive or usr calling Topic is not the UI user
return
+
+ // This is used to toggle the nano map ui
+ var/map_update = 0
+ if(href_list["showMap"])
+ set_show_map(text2num(href_list["showMap"]))
+ map_update = 1
+
+ if(href_list["mapZLevel"])
+ set_map_z_level(text2num(href_list["mapZLevel"]))
+ map_update = 1
- if (src_object && src_object.Topic(href, href_list))
+ if ((src_object && src_object.Topic(href, href_list)) || map_update)
nanomanager.update_uis(src_object) // update all UIs attached to src_object
/**
@@ -410,7 +496,15 @@ nanoui is used to open and update nano browser uis
return
if (status && (update || is_auto_updating))
- src_object.ui_interact(user, ui_key, src) // Update the UI (update_status() is called whenever a UI is updated)
+ update() // Update the UI (update_status() is called whenever a UI is updated)
else
update_status(1) // Not updating UI, so lets check here if status has changed
+
+ /**
+ * Update the UI
+ *
+ * @return nothing
+ */
+/datum/nanoui/proc/update(var/force_open = 0)
+ src_object.ui_interact(user, ui_key, src, force_open)
diff --git a/code/modules/organs/blood.dm b/code/modules/organs/blood.dm
index 1459098db71..3a691d9d353 100644
--- a/code/modules/organs/blood.dm
+++ b/code/modules/organs/blood.dm
@@ -127,14 +127,12 @@ var/const/BLOOD_VOLUME_SURVIVE = 122
if(!(temp.status & ORGAN_BLEEDING) || temp.status & ORGAN_ROBOT)
continue
for(var/datum/wound/W in temp.wounds) if(W.bleeding())
- blood_max += W.damage / 4
- if(temp.status & ORGAN_DESTROYED && !(temp.status & ORGAN_GAUZED) && !temp.amputated)
- blood_max += 20 //Yer missing a fucking limb.
+ blood_max += W.damage / 40
if (temp.open)
blood_max += 2 //Yer stomach is cut open
drip(blood_max)
-//Makes a blood drop, leaking certain amount of blood from the mob
+//Makes a blood drop, leaking amt units of blood from the mob
/mob/living/carbon/human/proc/drip(var/amt as num)
if(species && species.flags & NO_BLOOD) //TODO: Make drips come from the reagents instead.
@@ -143,12 +141,11 @@ var/const/BLOOD_VOLUME_SURVIVE = 122
if(!amt)
return
- var/amm = 0.1 * amt
var/turf/T = get_turf(src)
var/list/obj/effect/decal/cleanable/blood/drip/nums = list()
var/list/iconL = list("1","2","3","4","5")
- vessel.remove_reagent("blood",amm)
+ vessel.remove_reagent("blood",amt)
for(var/obj/effect/decal/cleanable/blood/drip/G in T)
nums += G
diff --git a/code/modules/organs/organ_external.dm b/code/modules/organs/organ_external.dm
index 5ae6d8c2274..036e13e3c47 100644
--- a/code/modules/organs/organ_external.dm
+++ b/code/modules/organs/organ_external.dm
@@ -226,11 +226,11 @@ This function completely restores a damaged organ to perfect condition.
/datum/organ/external/proc/createwound(var/type = CUT, var/damage)
if(damage == 0) return
- //moved this before the open_wound check so that having many small wounds for example doesn't somehow protect you from taking internal damage
+ //moved this before the open_wound check so that having many small wounds for example doesn't somehow protect you from taking internal damage (because of the return)
//Possibly trigger an internal wound, too.
var/local_damage = brute_dam + burn_dam + damage
if(damage > 15 && type != BURN && local_damage > 30 && prob(damage) && !(status & ORGAN_ROBOT))
- var/datum/wound/internal_bleeding/I = new (15)
+ var/datum/wound/internal_bleeding/I = new (min(damage - 15, 15))
wounds += I
owner.custom_pain("You feel something rip in your [display_name]!", 1)
@@ -275,6 +275,8 @@ This function completely restores a damaged organ to perfect condition.
//Determines if we even need to process this organ.
/datum/organ/external/proc/need_process()
+ if(destspawn) //Missing limb is missing
+ return 0
if(status && status != ORGAN_ROBOT) // If it's robotic, that's fine it will have a status.
return 1
if(brute_dam || burn_dam)
@@ -289,17 +291,6 @@ This function completely restores a damaged organ to perfect condition.
return 0
/datum/organ/external/process()
- // Process wounds, doing healing etc. Only do this every few ticks to save processing power
- if(owner.life_tick % wound_update_accuracy == 0)
- update_wounds()
-
- //Chem traces slowly vanish
- if(owner.life_tick % 10 == 0)
- for(var/chemID in trace_chemicals)
- trace_chemicals[chemID] = trace_chemicals[chemID] - 1
- if(trace_chemicals[chemID] <= 0)
- trace_chemicals.Remove(chemID)
-
//Dismemberment
if(status & ORGAN_DESTROYED)
if(!destspawn && config.limbs_can_break)
@@ -311,6 +302,17 @@ This function completely restores a damaged organ to perfect condition.
owner.update_body(1)
return
+ // Process wounds, doing healing etc. Only do this every few ticks to save processing power
+ if(owner.life_tick % wound_update_accuracy == 0)
+ update_wounds()
+
+ //Chem traces slowly vanish
+ if(owner.life_tick % 10 == 0)
+ for(var/chemID in trace_chemicals)
+ trace_chemicals[chemID] = trace_chemicals[chemID] - 1
+ if(trace_chemicals[chemID] <= 0)
+ trace_chemicals.Remove(chemID)
+
//Bone fracurtes
if(config.bones_can_break && brute_dam > min_broken_damage * config.organ_health_multiplier && !(status & ORGAN_ROBOT))
src.fracture()
@@ -444,16 +446,15 @@ Note that amputating the affected organ does in fact remove the infection from t
// let the GC handle the deletion of the wound
// Internal wounds get worse over time. Low temperatures (cryo) stop them.
- if(W.internal && !W.can_autoheal() && owner.bodytemperature >= 170)
+ if(W.internal && owner.bodytemperature >= 170)
var/bicardose = owner.reagents.get_reagent_amount("bicaridine")
var/inaprovaline = owner.reagents.get_reagent_amount("inaprovaline")
- if(!bicardose || !inaprovaline) //bicaridine and inaprovaline stop internal wounds from growing bigger with time, and also stop bleeding
+ if(!(W.can_autoheal() || (bicardose && inaprovaline))) //bicaridine and inaprovaline stop internal wounds from growing bigger with time, unless it is so small that it is already healing
W.open_wound(0.1 * wound_update_accuracy)
- owner.vessel.remove_reagent("blood",0.05 * W.damage * wound_update_accuracy)
if(bicardose >= 30) //overdose of bicaridine begins healing IB
W.damage = max(0, W.damage - 0.2)
- owner.vessel.remove_reagent("blood",0.02 * W.damage * wound_update_accuracy)
+ owner.vessel.remove_reagent("blood", wound_update_accuracy * W.damage/40) //line should possibly be moved to handle_blood, so all the bleeding stuff is in one place.
if(prob(1 * wound_update_accuracy))
owner.custom_pain("You feel a stabbing pain in your [display_name]!",1)
@@ -568,13 +569,27 @@ Note that amputating the affected organ does in fact remove the infection from t
src.status &= ~ORGAN_BROKEN
src.status &= ~ORGAN_BLEEDING
src.status &= ~ORGAN_SPLINTED
+ src.status &= ~ORGAN_DEAD
for(var/implant in implants)
del(implant)
+ germ_level = 0
+
+ //Replace all wounds on that arm with one wound on parent organ.
+ wounds.Cut()
+ if (parent)
+ var/datum/wound/W
+ if(max_damage < 50)
+ W = new/datum/wound/lost_limb/small(max_damage)
+ else
+ W = new/datum/wound/lost_limb(max_damage)
+ parent.wounds += W
+ parent.update_damages()
+ update_damages()
+
// If any organs are attached to this, destroy them
- for(var/datum/organ/external/O in owner.organs)
- if(O.parent == src)
- O.droplimb(1)
+ for(var/datum/organ/external/O in children)
+ O.droplimb(1)
var/obj/organ //Dropped limb object
switch(body_part)
diff --git a/code/modules/organs/wound.dm b/code/modules/organs/wound.dm
index 4e66dbf8bbb..9d4a5364ae1 100644
--- a/code/modules/organs/wound.dm
+++ b/code/modules/organs/wound.dm
@@ -31,7 +31,7 @@
var/germ_level = 0
/* These are defined by the wound type and should not be changed */
-
+
// stages such as "cut", "deep cut", etc.
var/list/stages
// internal wounds can only be fixed through surgery
@@ -51,7 +51,7 @@
// helper lists
var/tmp/list/desc_list = list()
var/tmp/list/damage_list = list()
-
+
New(var/damage)
created = world.time
@@ -65,7 +65,7 @@
src.damage = damage
max_bleeding_stage = src.desc_list.len - max_bleeding_stage
-
+
// initialize with the appropriate stage
src.init_stage(damage)
@@ -74,7 +74,7 @@
// returns 1 if there's a next stage, 0 otherwise
proc/init_stage(var/initial_damage)
current_stage = stages.len
-
+
while(src.current_stage > 1 && src.damage_list[current_stage-1] <= initial_damage / src.amount)
src.current_stage--
@@ -84,20 +84,20 @@
// the amount of damage per wound
proc/wound_damage()
return src.damage / src.amount
-
+
proc/can_autoheal()
if(src.wound_damage() <= autoheal_cutoff)
return 1
-
+
return is_treated()
-
+
// checks whether the wound has been appropriately treated
proc/is_treated()
if(damage_type == BRUISE || damage_type == CUT)
return bandaged
else if(damage_type == BURN)
return salved
-
+
// Checks whether other other can be merged into src.
proc/can_merge(var/datum/wound/other)
if (other.type != src.type) return 0
@@ -128,10 +128,10 @@
if (disinfected)
germ_level = 0 //reset this, just in case
return 0
-
+
if (damage_type == BRUISE && !bleeding()) //bruises only infectable if bleeding
return 0
-
+
var/dam_coef = round(damage/10)
switch (damage_type)
if (BRUISE)
@@ -179,32 +179,32 @@
proc/can_worsen(damage_type, damage)
if (src.damage_type != damage_type)
return 0 //incompatible damage types
-
+
if (src.amount > 1)
return 0
-
+
//with 1.5*, a shallow cut will be able to carry at most 30 damage,
//37.5 for a deep cut
//52.5 for a flesh wound, etc.
var/max_wound_damage = 1.5*src.damage_list[1]
if (src.damage + damage > max_wound_damage)
return 0
-
+
return 1
proc/bleeding()
if (src.internal)
return 0 // internal wounds don't bleed in the sense of this function
-
+
if (current_stage > max_bleeding_stage)
return 0
-
+
if (bandaged||clamped)
return 0
-
+
if (wound_damage() <= 30 && bleed_timer <= 0)
return 0 //Bleed timer has run out. Wounds with more than 30 damage don't stop bleeding on their own.
-
+
return (damage_type == BRUISE && wound_damage() >= 20 || damage_type == CUT && wound_damage() >= 5)
/** WOUND DEFINITIONS **/
@@ -309,6 +309,18 @@ datum/wound/cut/massive
/** INTERNAL BLEEDING **/
/datum/wound/internal_bleeding
internal = 1
- stages = list("severed vein" = 30, "cut vein" = 20, "damaged vein" = 10, "bruised vein" = 5)
+ stages = list("severed artery" = 30, "cut artery" = 20, "damaged artery" = 10, "bruised artery" = 5)
autoheal_cutoff = 5
max_bleeding_stage = 0 //all stages bleed. It's called internal bleeding after all.
+
+/** EXTERNAL ORGAN LOSS **/
+/datum/wound/lost_limb
+ damage_type = CUT
+ stages = list("ripped stump" = 65, "bloody stump" = 50, "clotted stump" = 25, "scarred stump" = 0)
+ max_bleeding_stage = 3
+
+ can_merge(var/datum/wound/other)
+ return 0 //cannot be merged
+
+/datum/wound/lost_limb/small
+ stages = list("ripped stump" = 40, "bloody stump" = 30, "clotted stump" = 15, "scarred stump" = 0)
diff --git a/code/modules/paperwork/photocopier.dm b/code/modules/paperwork/photocopier.dm
index 7f006325e6c..6963fe2f75b 100644
--- a/code/modules/paperwork/photocopier.dm
+++ b/code/modules/paperwork/photocopier.dm
@@ -34,6 +34,8 @@
dat += "+
"
else if(toner)
dat += "Please insert paper to copy.
Escape pods only launch automatically during emergency evacuations
+
Escape pods can be made to launch during regular crew transfers using the control panel inside the pod, or by emagging the panel outside the pod
+
When swiped or emagged, the crew transfer shuttle can be delayed in addition to being launched early
@@ -176,6 +225,16 @@ should be listed in the changelog upon commit though. Thanks. -->
+
+
15 June 2014
+
HarpyEagle updated:
+
+
Fixed wound autohealing regardless of damage amount. The appropriate wound will now be assigned correctly based on damage amount and type
+
Fixed several other bugs related wounds that resulted in damage magically disappearing
+
Fixed various sharp objects not being counted as sharp, bullets in particular
+
Fixed armour providing more protection from bullets than it was supposed to
+
+
20 June 2014
@@ -189,6 +248,18 @@ should be listed in the changelog upon commit though. Thanks. -->
+
+
13 June 2014
+
HarpyEagle updated:
+
+
Added docking ports for shuttles
+
Shuttle airlocks will automatically open and close, preventing people from being sucked into space by because someone on another z-level called a shuttle
+
Some docking ports can also double as airlocks
+
Docking ports can be overriden to prevent any automatic action. Shuttles will wait for players to open/close doors manually
+
Shuttles can be forced launched, which will make them not wait for airlocks to be properly closed
+
+
+
3 Juni 2014
Hubblenaut updated:
@@ -199,7 +270,6 @@ should be listed in the changelog upon commit though. Thanks. -->
-
31 May 2014
Jarcolr updated:
@@ -213,6 +283,39 @@ should be listed in the changelog upon commit though. Thanks. -->
+
+
23 Mai 2014
+
Hubble updated:
+
+
Personal lockers are now resettable
+
Take off people's accessories or change their sensors in the drag and drop-interface
+
Merge paper bundles by hitting one with another
+
Line breaks in Security, Medical and Employment Records
+
Record printouts will have names on it
+
Set other people's internals in belt and suit storage slots
+
No longer changing suit sensors while cuffed
+
No longer emptying other people's pockets when they are not full yet
+
+
+
+
+
16 May 2014
+
HarpyEagle updated:
+
+
Silicon mob types (AI, cyborgs, PAI) can now speak certain species languages depending on type and module
+
Languages can now be whispered when using the language code with either the whisper verb or the whisper speech code
+
+
+
+
+
6 Mai 2014
+
Hubble updated:
+
+
Clip papers together by hitting a paper with a paper or photo
+
Adds icons for copied stamps
+
+
+
23 Mai 2014
Hubble updated:
@@ -265,7 +368,18 @@ should be listed in the changelog upon commit though. Thanks. -->
-
+
+
29 April 2014
+
HarpyEagle updated:
+
+
Webbing vest storage can now be accessed by clicking on the item in inventory
+
Holsters can be accessed by clicking on them in inventory
+
Webbings and other suit attachments are now visible on the icon in inventory
+
Removing jumpsuits now requires drag and drop to prevent accidental undressing
+
Added an action icon for magboots that can be used to toggle them similar to flashlights
This control console dictates the behaviors of the magnetic platform, which can move your firing target around to simulate real-world combat situations. From here, you can turn off the magnets or adjust their electromagnetic levels and magnetic fields. The electricity level dictates the strength of the pull - you will usually want this to be the same value as the speed. The magnetic field level dictates how far the magnetic pull reaches.
Speed and path are the next two settings. Speed is associated with how fast the machine loops through the designated path. Paths dictate where the magnetic field will be centered at what times. There should be a pre-fabricated path input already. You can enable moving to observe how the path affects the way the stake moves. To script your own path, look at the following key:
The Medical Emergency Red Phone system has proven itself well. Take care to keep the phones in their designated places as they have been optimised for broadcast. The two handheld green radios (I have left one in this office, and one near the Emergency Entrance) are free to be used. The system has proven effective at alerting Medbay of important details, especially during power outages.
I think I may have left the toilet cubicle doors shut. It might be a good idea to open them so the staff and patients know they are not engaged.
The new syringe gun has been stored in secondary storage. I tend to prefer it stored in my office, but 'guidelines' are 'guidelines'.
Also in secondary storage is the grenade equipment crate. I've just realised I've left it open - you may wish to shut it.
There were a few problems with their installation, but the Medbay Quarantine shutters should now be working again - they lock down the Emergency and Main entrances to prevent travel in and out. Pray you shan't have to use them.
The new version of the Medical Diagnostics Manual arrived. I distributed them to the shelf in the staff break room, and one on the table in the corner of this room.
The exam/triage room has the walking canes in it. I'm not sure why we'd need them - but there you have it.
Emergency Cryo bags are beside the emergency entrance, along with a kit.
Spare paper cups for the reception are on the left side of the reception desk.
a";n=p.getElementsByTagName("*")||[];r=p.getElementsByTagName("a")[0];if(!r||!r.style||!n.length){return t}u=o.createElement("select");f=u.appendChild(o.createElement("option"));s=p.getElementsByTagName("input")[0];r.style.cssText="top:1px;float:left;opacity:.5";t.getSetAttribute=p.className!=="t";t.leadingWhitespace=p.firstChild.nodeType===3;t.tbody=!p.getElementsByTagName("tbody").length;t.htmlSerialize=!!p.getElementsByTagName("link").length;t.style=/top/.test(r.getAttribute("style"));t.hrefNormalized=r.getAttribute("href")==="/a";t.opacity=/^0.5/.test(r.style.opacity);t.cssFloat=!!r.style.cssFloat;t.checkOn=!!s.value;t.optSelected=f.selected;t.enctype=!!o.createElement("form").enctype;t.html5Clone=o.createElement("nav").cloneNode(true).outerHTML!=="<:nav>";t.inlineBlockNeedsLayout=false;t.shrinkWrapBlocks=false;t.pixelPosition=false;t.deleteExpando=true;t.noCloneEvent=true;t.reliableMarginRight=true;t.boxSizingReliable=true;s.checked=true;t.noCloneChecked=s.cloneNode(true).checked;u.disabled=true;t.optDisabled=!f.disabled;try{delete p.test}catch(d){t.deleteExpando=false}s=o.createElement("input");s.setAttribute("value","");t.input=s.getAttribute("value")==="";s.value="t";s.setAttribute("type","radio");t.radioValue=s.value==="t";s.setAttribute("checked","t");s.setAttribute("name","t");a=o.createDocumentFragment();a.appendChild(s);t.appendChecked=s.checked;t.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;if(p.attachEvent){p.attachEvent("onclick",function(){t.noCloneEvent=false});p.cloneNode(true).click()}for(h in{submit:true,change:true,focusin:true}){p.setAttribute(l="on"+h,"t");t[h+"Bubbles"]=l in e||p.attributes[l].expando===false}p.style.backgroundClip="content-box";p.cloneNode(true).style.backgroundClip="";t.clearCloneStyle=p.style.backgroundClip==="content-box";for(h in w(t)){break}t.ownLast=h!=="0";w(function(){var n,r,s,u="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",a=o.getElementsByTagName("body")[0];if(!a){return}n=o.createElement("div");n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px";a.appendChild(n).appendChild(p);p.innerHTML="
a";n=p.getElementsByTagName("*")||[];r=p.getElementsByTagName("a")[0];if(!r||!r.style||!n.length){return t}u=o.createElement("select");f=u.appendChild(o.createElement("option"));s=p.getElementsByTagName("input")[0];r.style.cssText="top:1px;float:left;opacity:.5";t.getSetAttribute=p.className!=="t";t.leadingWhitespace=p.firstChild.nodeType===3;t.tbody=!p.getElementsByTagName("tbody").length;t.htmlSerialize=!!p.getElementsByTagName("link").length;t.style=/top/.test(r.getAttribute("style"));t.hrefNormalized=r.getAttribute("href")==="/a";t.opacity=/^0.5/.test(r.style.opacity);t.cssFloat=!!r.style.cssFloat;t.checkOn=!!s.value;t.optSelected=f.selected;t.enctype=!!o.createElement("form").enctype;t.html5Clone=o.createElement("nav").cloneNode(true).outerHTML!=="<:nav>";t.inlineBlockNeedsLayout=false;t.shrinkWrapBlocks=false;t.pixelPosition=false;t.deleteExpando=true;t.noCloneEvent=true;t.reliableMarginRight=true;t.boxSizingReliable=true;s.checked=true;t.noCloneChecked=s.cloneNode(true).checked;u.disabled=true;t.optDisabled=!f.disabled;try{delete p.test}catch(d){t.deleteExpando=false}s=o.createElement("input");s.setAttribute("value","");t.input=s.getAttribute("value")==="";s.value="t";s.setAttribute("type","radio");t.radioValue=s.value==="t";s.setAttribute("checked","t");s.setAttribute("name","t");a=o.createDocumentFragment();a.appendChild(s);t.appendChecked=s.checked;t.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;if(p.attachEvent){p.attachEvent("onclick",function(){t.noCloneEvent=false});p.cloneNode(true).click()}for(h in{submit:true,change:true,focusin:true}){p.setAttribute(l="on"+h,"t");t[h+"Bubbles"]=l in e||p.attributes[l].expando===false}p.style.backgroundClip="content-box";p.cloneNode(true).style.backgroundClip="";t.clearCloneStyle=p.style.backgroundClip==="content-box";for(h in w(t)){break}t.ownLast=h!=="0";w(function(){var n,r,s,u="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",a=o.getElementsByTagName("body")[0];if(!a){return}n=o.createElement("div");n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px";a.appendChild(n).appendChild(p);p.innerHTML="