diff --git a/Scopes Files/signal_modifier.dm b/Scopes Files/signal_modifier.dm index 593755a0..91169a3b 100644 --- a/Scopes Files/signal_modifier.dm +++ b/Scopes Files/signal_modifier.dm @@ -16,8 +16,7 @@ throw_range = 15 throw_speed = 3 desc = "If you have this, it is a mistake." //here to - m_amt = 50 - g_amt = 20 + matter = list("metal" = 20,"glass" = 50) origin_tech = "magnets=1;engineering=1" var/id_setting = null var/signal_setting = null diff --git a/aurora.dme b/aurora.dme index b784347e..e2732b89 100644 --- a/aurora.dme +++ b/aurora.dme @@ -17,6 +17,7 @@ #define FILE_DIR "html" #define FILE_DIR "icons" #define FILE_DIR "icons/48x48" +#define FILE_DIR "icons/atmos" #define FILE_DIR "icons/effects" #define FILE_DIR "icons/mecha" #define FILE_DIR "icons/misc" @@ -45,8 +46,6 @@ #define FILE_DIR "icons/Testing" #define FILE_DIR "icons/turf" #define FILE_DIR "icons/vending_icons" -#define FILE_DIR "Mocheeze" -#define FILE_DIR "Mocheeze/Scraps" #define FILE_DIR "nano" #define FILE_DIR "nano/images" #define FILE_DIR "Sammacs Folder" @@ -131,6 +130,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" @@ -144,8 +145,13 @@ #include "code\ATMOSPHERICS\components\binary_devices\circulator.dm" #include "code\ATMOSPHERICS\components\binary_devices\dp_vent_pump.dm" #include "code\ATMOSPHERICS\components\binary_devices\passive_gate.dm" +#include "code\ATMOSPHERICS\components\binary_devices\pipeturbine.dm" #include "code\ATMOSPHERICS\components\binary_devices\pump.dm" #include "code\ATMOSPHERICS\components\binary_devices\volume_pump.dm" +#include "code\ATMOSPHERICS\components\omni_devices\_omni_extras.dm" +#include "code\ATMOSPHERICS\components\omni_devices\filter.dm" +#include "code\ATMOSPHERICS\components\omni_devices\mixer.dm" +#include "code\ATMOSPHERICS\components\omni_devices\omni_base.dm" #include "code\ATMOSPHERICS\components\trinary_devices\filter.dm" #include "code\ATMOSPHERICS\components\trinary_devices\mixer.dm" #include "code\ATMOSPHERICS\components\trinary_devices\trinary_base.dm" @@ -160,8 +166,10 @@ #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\delta_level.dm" +#include "code\controllers\emergency_shuttle_controller.dm" #include "code\controllers\failsafe.dm" #include "code\controllers\hooks-defs.dm" #include "code\controllers\hooks.dm" @@ -253,7 +261,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" @@ -346,10 +353,10 @@ #include "code\game\jobs\job\silicon.dm" #include "code\game\machinery\adv_med.dm" #include "code\game\machinery\ai_slipper.dm" -#include "code\game\machinery\airlock_control.dm" #include "code\game\machinery\alarm.dm" #include "code\game\machinery\atmo_control.dm" #include "code\game\machinery\autolathe.dm" +#include "code\game\machinery\autolathe_datums.dm" #include "code\game\machinery\Beacon.dm" #include "code\game\machinery\bees_apiary.dm" #include "code\game\machinery\bees_items.dm" @@ -365,7 +372,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\hydroponics.dm" @@ -403,7 +409,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" @@ -448,6 +453,7 @@ #include "code\game\machinery\computer\syndicate_specops_shuttle.dm" #include "code\game\machinery\computer\vox_shuttle.dm" #include "code\game\machinery\doors\airlock.dm" +#include "code\game\machinery\doors\airlock_control.dm" #include "code\game\machinery\doors\airlock_electronics.dm" #include "code\game\machinery\doors\alarmlock.dm" #include "code\game\machinery\doors\brigdoors.dm" @@ -460,8 +466,14 @@ #include "code\game\machinery\doors\unpowered.dm" #include "code\game\machinery\doors\windowdoor.dm" #include "code\game\machinery\embedded_controller\airlock_controllers.dm" +#include "code\game\machinery\embedded_controller\airlock_docking_controller.dm" +#include "code\game\machinery\embedded_controller\airlock_docking_controller_multi.dm" #include "code\game\machinery\embedded_controller\airlock_program.dm" +#include "code\game\machinery\embedded_controller\docking_program.dm" +#include "code\game\machinery\embedded_controller\docking_program_multi.dm" #include "code\game\machinery\embedded_controller\embedded_controller_base.dm" +#include "code\game\machinery\embedded_controller\embedded_program_base.dm" +#include "code\game\machinery\embedded_controller\simple_docking_controller.dm" #include "code\game\machinery\kitchen\deep_fryer.dm" #include "code\game\machinery\kitchen\gibber.dm" #include "code\game\machinery\kitchen\juicer.dm" @@ -827,8 +839,10 @@ #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" #include "code\modules\client\preferences_toggles.dm" #include "code\modules\clothing\clothing.dm" #include "code\modules\clothing\glasses\glasses.dm" @@ -1059,6 +1073,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" @@ -1110,6 +1125,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" @@ -1188,8 +1204,10 @@ #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" #include "code\modules\power\cable.dm" #include "code\modules\power\cable_heavyduty.dm" #include "code\modules\power\cable_logic.dm" @@ -1201,6 +1219,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" @@ -1384,6 +1405,16 @@ #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\shuttles\antagonist.dm" +#include "code\modules\shuttles\departmental.dm" +#include "code\modules\shuttles\escape_pods.dm" +#include "code\modules\shuttles\shuttle.dm" +#include "code\modules\shuttles\shuttle_console.dm" +#include "code\modules\shuttles\shuttle_emergency.dm" +#include "code\modules\shuttles\shuttle_ferry.dm" +#include "code\modules\shuttles\shuttle_specops.dm" +#include "code\modules\shuttles\shuttle_supply.dm" +#include "code\modules\shuttles\shuttles_multi.dm" #include "code\modules\supermatter\supermatter.dm" #include "code\modules\surgery\appendix.dm" #include "code\modules\surgery\bones.dm" @@ -1464,7 +1495,8 @@ #include "code\WorkInProgress\SkyMarshal\officer_stuff.dm" #include "code\WorkInProgress\SkyMarshal\Ultralight_procs.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" @@ -1474,6 +1506,7 @@ #include "code\ZAS\Debug.dm" #include "code\ZAS\Diagnostic.dm" #include "code\ZAS\Fire.dm" +#include "code\ZAS\Gas.dm" #include "code\ZAS\Plasma.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 00000000..a7585bf7 --- /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 00000000..8e3a2b0d --- /dev/null +++ b/code/ATMOSPHERICS/_atmospherics_helpers.dm @@ -0,0 +1,410 @@ +/* + 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 + +/client/proc/atmos_toggle_debug(var/obj/machinery/atmospherics/M in view()) + set name = "Toggle Debug Messages" + set category = "Debug" + M.debug = !M.debug + usr << "[M]: Debug messages toggled [M.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 + + //var/source_moles_initial = source.total_moles + + if (isnull(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 + + 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 (isnull(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() + + 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 (isnull(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) + + 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 (isnull(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() + + 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_clean.merge(removed) + + 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 (isnull(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] + 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 6cf8a229..8fa6b6cf 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 7fbf7609..6c050fd3 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 6be4147f..75512f04 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 6739a897..37004ada 100644 --- a/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm +++ b/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm @@ -1,188 +1,267 @@ -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 +/obj/machinery/atmospherics/binary/passive_gate/New() + ..() + air1.volume = ATMOS_DEFAULT_VOLUME_PUMP * 2.5 + air2.volume = ATMOS_DEFAULT_VOLUME_PUMP * 2.5 + +/obj/machinery/atmospherics/binary/passive_gate/update_icon() + icon_state = (on && flowing)? "on" : "off" + +/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) + +/obj/machinery/atmospherics/binary/passive_gate/hide(var/i) + update_underlays() + +/obj/machinery/atmospherics/binary/passive_gate/process() + ..() + if(!on) + last_flow_rate = 0 + return 0 + + 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 + + //-1 if pump_gas() did not move any gas, >= 0 otherwise + var/returnval = -1 + 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 + returnval = pump_gas(src, air1, air2, transfer_moles, available_power=0) //available_power=0 means we only move gas if it would flow naturally + + if (returnval < 0) + flowing = 0 + last_flow_rate = 0 + else + if(network1) + network1.update = 1 + + if(network2) + network2.update = 1 + + if (!last_flow_rate) + flowing = 0 + 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(!on) - return 0 - - var/output_starting_pressure = air2.return_pressure() - var/input_starting_pressure = air1.return_pressure() - - 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 - - //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 - - 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) - - if(network1) - network1.update = 1 - - if(network2) - network2.update = 1 - //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/pipeturbine.dm b/code/ATMOSPHERICS/components/binary_devices/pipeturbine.dm new file mode 100644 index 00000000..7f0984ef --- /dev/null +++ b/code/ATMOSPHERICS/components/binary_devices/pipeturbine.dm @@ -0,0 +1,289 @@ +#define ADIABATIC_EXPONENT 0.667 //Actually adiabatic exponent - 1. + +/obj/machinery/atmospherics/pipeturbine + name = "turbine" + desc = "A gas turbine. Converting pressure into energy since 1884." + icon = 'icons/obj/pipeturbine.dmi' + icon_state = "turbine" + anchored = 0 + density = 1 + + var/efficiency = 0.4 + var/kin_energy = 0 + var/datum/gas_mixture/air_in = new + var/datum/gas_mixture/air_out = new + var/volume_ratio = 0.2 + var/kin_loss = 0.001 + + var/dP = 0 + + var/obj/machinery/atmospherics/node1 + var/obj/machinery/atmospherics/node2 + + var/datum/pipe_network/network1 + var/datum/pipe_network/network2 + + New() + ..() + air_in.volume = 200 + air_out.volume = 800 + volume_ratio = air_in.volume / (air_in.volume + air_out.volume) + switch(dir) + if(NORTH) + initialize_directions = EAST|WEST + if(SOUTH) + initialize_directions = EAST|WEST + if(EAST) + initialize_directions = NORTH|SOUTH + if(WEST) + initialize_directions = NORTH|SOUTH + + Del() + loc = null + + if(node1) + node1.disconnect(src) + del(network1) + if(node2) + node2.disconnect(src) + del(network2) + + node1 = null + node2 = null + + ..() + + process() + ..() + if(anchored && !(stat&BROKEN)) + kin_energy *= 1 - kin_loss + dP = max(air_in.return_pressure() - air_out.return_pressure(), 0) + if(dP > 10) + kin_energy += 1/ADIABATIC_EXPONENT * dP * air_in.volume * (1 - volume_ratio**ADIABATIC_EXPONENT) * efficiency + air_in.temperature *= volume_ratio**ADIABATIC_EXPONENT + + var/datum/gas_mixture/air_all = new + air_all.volume = air_in.volume + air_out.volume + air_all.merge(air_in.remove_ratio(1)) + air_all.merge(air_out.remove_ratio(1)) + + air_in.merge(air_all.remove(volume_ratio)) + air_out.merge(air_all) + + update_icon() + + if (network1) + network1.update = 1 + if (network2) + network2.update = 1 + + update_icon() + overlays.Cut() + if (dP > 10) + overlays += image('icons/obj/pipeturbine.dmi', "moto-turb") + if (kin_energy > 100000) + overlays += image('icons/obj/pipeturbine.dmi', "low-turb") + if (kin_energy > 500000) + overlays += image('icons/obj/pipeturbine.dmi', "med-turb") + if (kin_energy > 1000000) + overlays += image('icons/obj/pipeturbine.dmi', "hi-turb") + + attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W, /obj/item/weapon/wrench)) + anchored = !anchored + user << "\blue You [anchored ? "secure" : "unsecure"] the bolts holding [src] to the floor." + + if(anchored) + if(dir & (NORTH|SOUTH)) + initialize_directions = EAST|WEST + else if(dir & (EAST|WEST)) + initialize_directions = NORTH|SOUTH + + initialize() + build_network() + if (node1) + node1.initialize() + node1.build_network() + if (node2) + node2.initialize() + node2.build_network() + else + if(node1) + node1.disconnect(src) + del(network1) + if(node2) + node2.disconnect(src) + del(network2) + + node1 = null + node2 = null + + else + ..() + + verb/rotate_clockwise() + set category = "Object" + set name = "Rotate Circulator (Clockwise)" + set src in view(1) + + if (usr.stat || usr.restrained() || anchored) + return + + src.dir = turn(src.dir, -90) + + + verb/rotate_anticlockwise() + set category = "Object" + set name = "Rotate Circulator (Counterclockwise)" + set src in view(1) + + if (usr.stat || usr.restrained() || anchored) + return + + src.dir = turn(src.dir, 90) + +//Goddamn copypaste from binary base class because atmospherics machinery API is not damn flexible + network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) + if(reference == node1) + network1 = new_network + + else if(reference == node2) + network2 = new_network + + if(new_network.normal_members.Find(src)) + return 0 + + new_network.normal_members += src + + return null + + initialize() + if(node1 && node2) return + + var/node2_connect = turn(dir, -90) + var/node1_connect = turn(dir, 90) + + for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect)) + if(target.initialize_directions & get_dir(target,src)) + node1 = target + break + + for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect)) + if(target.initialize_directions & get_dir(target,src)) + node2 = target + break + + build_network() + if(!network1 && node1) + network1 = new /datum/pipe_network() + network1.normal_members += src + network1.build_network(node1, src) + + if(!network2 && node2) + network2 = new /datum/pipe_network() + network2.normal_members += src + network2.build_network(node2, src) + + + return_network(obj/machinery/atmospherics/reference) + build_network() + + if(reference==node1) + return network1 + + if(reference==node2) + return network2 + + return null + + reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network) + if(network1 == old_network) + network1 = new_network + if(network2 == old_network) + network2 = new_network + + return 1 + + return_network_air(datum/pipe_network/reference) + var/list/results = list() + + if(network1 == reference) + results += air_in + if(network2 == reference) + results += air_out + + return results + + disconnect(obj/machinery/atmospherics/reference) + if(reference==node1) + del(network1) + node1 = null + + else if(reference==node2) + del(network2) + node2 = null + + return null + + +/obj/machinery/power/turbinemotor + name = "motor" + desc = "Electrogenerator. Converts rotation into power." + icon = 'icons/obj/pipeturbine.dmi' + icon_state = "motor" + anchored = 0 + density = 1 + + var/kin_to_el_ratio = 0.1 //How much kinetic energy will be taken from turbine and converted into electricity + var/obj/machinery/atmospherics/pipeturbine/turbine + + New() + ..() + spawn(1) + updateConnection() + + proc/updateConnection() + turbine = null + if(src.loc && anchored) + turbine = locate(/obj/machinery/atmospherics/pipeturbine) in get_step(src,dir) + if (turbine.stat & (BROKEN) || !turbine.anchored || turn(turbine.dir,180) != dir) + turbine = null + + process() + updateConnection() + if(!turbine || !anchored || stat & (BROKEN)) + return + + var/power_generated = kin_to_el_ratio * turbine.kin_energy + turbine.kin_energy -= power_generated + add_avail(power_generated) + + + attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W, /obj/item/weapon/wrench)) + anchored = !anchored + turbine = null + user << "\blue You [anchored ? "secure" : "unsecure"] the bolts holding [src] to the floor." + updateConnection() + else + ..() + + verb/rotate_clock() + set category = "Object" + set name = "Rotate Motor Clockwise" + set src in view(1) + + if (usr.stat || usr.restrained() || anchored) + return + + src.dir = turn(src.dir, -90) + + verb/rotate_anticlock() + set category = "Object" + set name = "Rotate Motor Counterclockwise" + set src in view(1) + + if (usr.stat || usr.restrained() || anchored) + return + + src.dir = turn(src.dir, 90) \ No newline at end of file diff --git a/code/ATMOSPHERICS/components/binary_devices/pump.dm b/code/ATMOSPHERICS/components/binary_devices/pump.dm index 11143d73..97456fef 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 87c795de..4ec5d465 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 new file mode 100644 index 00000000..7620548b --- /dev/null +++ b/code/ATMOSPHERICS/components/omni_devices/_omni_extras.dm @@ -0,0 +1,109 @@ +//-------------------------------------------- +// Omni device port types +//-------------------------------------------- +#define ATM_NONE 0 +#define ATM_INPUT 1 +#define ATM_OUTPUT 2 + +#define ATM_O2 3 +#define ATM_N2 4 +#define ATM_CO2 5 +#define ATM_P 6 //Phoron +#define ATM_N2O 7 + +//-------------------------------------------- +// Omni port datum +// +// Used by omni devices to manage connections +// to other atmospheric objects. +//-------------------------------------------- +/datum/omni_port + var/obj/machinery/atmospherics/omni/master + var/dir + var/update = 1 + var/mode = 0 + var/concentration = 0 + var/con_lock = 0 + var/transfer_moles = 0 + var/datum/gas_mixture/air + var/obj/machinery/atmospherics/node + var/datum/pipe_network/network + +/datum/omni_port/New(var/obj/machinery/atmospherics/omni/M, var/direction = NORTH) + ..() + dir = direction + if(istype(M)) + master = M + air = new + air.volume = 200 + +/datum/omni_port/proc/connect() + if(node) + return + master.initialize() + master.build_network() + if(node) + node.initialize() + node.build_network() + +/datum/omni_port/proc/disconnect() + if(node) + node.disconnect(master) + master.disconnect(node) + + +//-------------------------------------------- +// Need to find somewhere else for these +//-------------------------------------------- + +//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 +/proc/dir_name(var/dir, var/capitalize = 0) + var/string = null + switch(dir) + if(NORTH) + string = "North" + if(SOUTH) + string = "South" + if(EAST) + string = "East" + if(WEST) + string = "West" + + if(!capitalize && string) + string = lowertext(string) + + return string + +//returns a direction flag based on the string passed to it +// case insensitive +/proc/dir_flag(var/dir) + dir = lowertext(dir) + switch(dir) + if("north") + return NORTH + if("south") + return SOUTH + if("east") + return EAST + if("west") + return WEST + else + 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 "toxins" + 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 new file mode 100644 index 00000000..05bebd5e --- /dev/null +++ b/code/ATMOSPHERICS/components/omni_devices/filter.dm @@ -0,0 +1,267 @@ +//-------------------------------------------- +// Gas filter - omni variant +//-------------------------------------------- +/obj/machinery/atmospherics/omni/filter + name = "omni gas filter" + icon_state = "map_filter" + + 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 + output = null + filters.Cut() + ..() + +/obj/machinery/atmospherics/omni/filter/sort_ports() + for(var/datum/omni_port/P in ports) + if(P.update) + if(output == P) + output = null + if(input == P) + input = null + if(filters.Find(P)) + filters -= P + + P.air.volume = 200 + switch(P.mode) + if(ATM_INPUT) + input = P + if(ATM_OUTPUT) + output = P + if(ATM_O2 to ATM_N2O) + filters += P + +/obj/machinery/atmospherics/omni/filter/error_check() + if(!input || !output || !filters) + return 1 + 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(!..()) + return 0 + + 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* + + //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(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 + +/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, force_open) + + if (!ui) + ui = new(user, src, ui_key, "omni_filter.tmpl", "Omni Filter Control", 330, 330) + ui.set_initial_data(data) + + ui.open() + +/obj/machinery/atmospherics/omni/filter/proc/build_uidata() + var/list/data = new() + + data["power"] = on + data["config"] = configuring + + var/portData[0] + for(var/datum/omni_port/P in ports) + if(!configuring && P.mode == 0) + continue + + var/input = 0 + var/output = 0 + var/filter = 1 + var/f_type = null + switch(P.mode) + if(ATM_INPUT) + input = 1 + filter = 0 + if(ATM_OUTPUT) + output = 1 + filter = 0 + if(ATM_O2 to ATM_N2O) + f_type = mode_send_switch(P.mode) + + portData[++portData.len] = list("dir" = dir_name(P.dir, capitalize = 1), \ + "input" = input, \ + "output" = output, \ + "filter" = filter, \ + "f_type" = f_type) + + if(portData.len) + data["ports"] = portData + if(output) + 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 + +/obj/machinery/atmospherics/omni/filter/proc/mode_send_switch(var/mode = ATM_NONE) + switch(mode) + if(ATM_O2) + return "Oxygen" + if(ATM_N2) + return "Nitrogen" + if(ATM_CO2) + return "Carbon Dioxide" + if(ATM_P) + return "Toxins" //*cough* Plasma *cough* + if(ATM_N2O) + return "Nitrous Oxide" + else + return null + +/obj/machinery/atmospherics/omni/filter/Topic(href, href_list) + if(..()) return + switch(href_list["command"]) + if("power") + if(!configuring) + on = !on + else + on = 0 + if("configure") + configuring = !configuring + if(configuring) + on = 0 + + //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_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") + var/new_filter = input(usr,"Select filter mode:","Change filter",href_list["mode"]) in list("None", "Oxygen", "Nitrogen", "Carbon Dioxide", "Toxins", "Nitrous Oxide") + switch_filter(dir_flag(href_list["dir"]), mode_return_switch(new_filter)) + + update_icon() + nanomanager.update_uis(src) + return + +/obj/machinery/atmospherics/omni/filter/proc/mode_return_switch(var/mode) + switch(mode) + if("Oxygen") + return ATM_O2 + if("Nitrogen") + return ATM_N2 + if("Carbon Dioxide") + return ATM_CO2 + if("Toxins") + return ATM_P + if("Nitrous Oxide") + return ATM_N2O + if("in") + return ATM_INPUT + if("out") + return ATM_OUTPUT + if("None") + return ATM_NONE + else + return null + +/obj/machinery/atmospherics/omni/filter/proc/switch_filter(var/dir, var/mode) + //check they aren't trying to disable the input or output ~this can only happen if they hack the cached tmpl file + for(var/datum/omni_port/P in ports) + if(P.dir == dir) + if(P.mode == ATM_INPUT || P.mode == ATM_OUTPUT) + return + + switch_mode(dir, mode) + +/obj/machinery/atmospherics/omni/filter/proc/switch_mode(var/port, var/mode) + if(mode == null || !port) + return + var/datum/omni_port/target_port = null + var/list/other_ports = new() + + for(var/datum/omni_port/P in ports) + if(P.dir == port) + target_port = P + else + other_ports += P + + var/previous_mode = null + if(target_port) + previous_mode = target_port.mode + target_port.mode = mode + if(target_port.mode != previous_mode) + handle_port_change(target_port) + rebuild_filtering_list() + else + return + else + return + + for(var/datum/omni_port/P in other_ports) + if(P.mode == mode) + var/old_mode = P.mode + 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) + initialize_directions &= ~P.dir + P.disconnect() + else + initialize_directions |= P.dir + P.connect() + P.update = 1 \ No newline at end of file diff --git a/code/ATMOSPHERICS/components/omni_devices/mixer.dm b/code/ATMOSPHERICS/components/omni_devices/mixer.dm new file mode 100644 index 00000000..b59c0281 --- /dev/null +++ b/code/ATMOSPHERICS/components/omni_devices/mixer.dm @@ -0,0 +1,299 @@ +//-------------------------------------------- +// Gas mixer - omni variant +//-------------------------------------------- +/obj/machinery/atmospherics/omni/mixer + 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_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() + ..() + if(mapper_set()) + var/con = 0 + for(var/datum/omni_port/P in ports) + switch(P.dir) + if(NORTH) + if(tag_north_con && tag_north == 1) + P.concentration = tag_north_con + con += max(0, tag_north_con) + if(SOUTH) + if(tag_south_con && tag_south == 1) + P.concentration = tag_south_con + con += max(0, tag_south_con) + if(EAST) + if(tag_east_con && tag_east == 1) + P.concentration = tag_east_con + con += max(0, tag_east_con) + if(WEST) + 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 + + rebuild_mixing_inputs() + +/obj/machinery/atmospherics/omni/mixer/Del() + inputs.Cut() + output = null + ..() + +/obj/machinery/atmospherics/omni/mixer/sort_ports() + for(var/datum/omni_port/P in ports) + if(P.update) + if(output == P) + output = null + if(inputs.Find(P)) + inputs -= P + + switch(P.mode) + if(ATM_INPUT) + inputs += P + if(ATM_OUTPUT) + output = P + + if(!mapper_set()) + for(var/datum/omni_port/P in inputs) + P.concentration = 1 / max(1, inputs.len) + + if(output) + output.air.volume = ATMOS_DEFAULT_VOLUME_MIXER * 0.75 * inputs.len + output.concentration = 1 + +/obj/machinery/atmospherics/omni/mixer/proc/mapper_set() + return (tag_north_con || tag_south_con || tag_east_con || tag_west_con) + +/obj/machinery/atmospherics/omni/mixer/error_check() + if(!output || !inputs) + return 1 + 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(!..()) + return 0 + + //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) + + 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) + if(P.concentration && P.network) + P.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, 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, force_open) + + if (!ui) + ui = new(user, src, ui_key, "omni_mixer.tmpl", "Omni Mixer Control", 360, 330) + ui.set_initial_data(data) + + ui.open() + +/obj/machinery/atmospherics/omni/mixer/proc/build_uidata() + var/list/data = new() + + data["power"] = on + data["config"] = configuring + + var/portData[0] + for(var/datum/omni_port/P in ports) + if(!configuring && P.mode == 0) + continue + + var/input = 0 + var/output = 0 + switch(P.mode) + if(ATM_INPUT) + input = 1 + if(ATM_OUTPUT) + output = 1 + + portData[++portData.len] = list("dir" = dir_name(P.dir, capitalize = 1), \ + "concentration" = P.concentration, \ + "input" = input, \ + "output" = output, \ + "con_lock" = P.con_lock) + + if(portData.len) + data["ports"] = portData + if(output) + 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 + +/obj/machinery/atmospherics/omni/mixer/Topic(href, href_list) + if(..()) return + + switch(href_list["command"]) + if("power") + if(!configuring) + on = !on + else + on = 0 + if("configure") + configuring = !configuring + if(configuring) + on = 0 + + //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_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") + change_concentration(dir_flag(href_list["dir"])) + if("switch_conlock") + con_lock(dir_flag(href_list["dir"])) + + update_icon() + nanomanager.update_uis(src) + return + +/obj/machinery/atmospherics/omni/mixer/proc/switch_mode(var/port = NORTH, var/mode = ATM_NONE) + if(mode != ATM_INPUT && mode != ATM_OUTPUT) + switch(mode) + if("in") + mode = ATM_INPUT + if("out") + mode = ATM_OUTPUT + else + mode = ATM_NONE + + for(var/datum/omni_port/P in ports) + var/old_mode = P.mode + if(P.dir == port) + switch(mode) + if(ATM_INPUT) + if(P.mode == ATM_OUTPUT) + return + P.mode = mode + if(ATM_OUTPUT) + P.mode = mode + if(ATM_NONE) + if(P.mode == ATM_OUTPUT) + return + if(P.mode == ATM_INPUT && inputs.len > 2) + P.mode = mode + else if(P.mode == ATM_OUTPUT && mode == ATM_OUTPUT) + P.mode = ATM_INPUT + if(P.mode != old_mode) + switch(P.mode) + if(ATM_NONE) + initialize_directions &= ~P.dir + P.disconnect() + else + initialize_directions |= P.dir + P.connect() + P.update = 1 + + update_ports() + rebuild_mixing_inputs() + +/obj/machinery/atmospherics/omni/mixer/proc/change_concentration(var/port = NORTH) + tag_north_con = null + tag_south_con = null + tag_east_con = null + tag_west_con = null + + var/old_con = 0 + var/non_locked = 0 + var/remain_con = 1 + + for(var/datum/omni_port/P in inputs) + if(P.dir == port) + old_con = P.concentration + else if(!P.con_lock) + non_locked++ + else + remain_con -= P.concentration + + //return if no adjustable ports + if(non_locked < 1) + return + + var/new_con = (input(usr,"Enter a new concentration (0-[round(remain_con * 100, 0.5)])%","Concentration control", min(remain_con, old_con)*100) as num) / 100 + + //cap it between 0 and the max remaining concentration + new_con = between(0, new_con, remain_con) + + //new_con = min(remain_con, new_con) + + //clamp remaining concentration so we don't go into negatives + remain_con = max(0, remain_con - new_con) + + //distribute remaining concentration between unlocked ports evenly + remain_con /= max(1, non_locked) + + for(var/datum/omni_port/P in inputs) + if(P.dir == port) + 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) + if(P.dir == port) + P.con_lock = !P.con_lock \ No newline at end of file diff --git a/code/ATMOSPHERICS/components/omni_devices/omni_base.dm b/code/ATMOSPHERICS/components/omni_devices/omni_base.dm new file mode 100644 index 00000000..a89b48e0 --- /dev/null +++ b/code/ATMOSPHERICS/components/omni_devices/omni_base.dm @@ -0,0 +1,299 @@ +//-------------------------------------------- +// Base omni device +//-------------------------------------------- +/obj/machinery/atmospherics/omni + name = "omni device" + 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 //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 + var/tag_east = ATM_NONE + var/tag_west = ATM_NONE + + var/overlays_on[5] + var/overlays_off[5] + var/overlays_error[2] + var/underlays_current[4] + + var/list/ports = new() + +/obj/machinery/atmospherics/omni/New() + ..() + icon_state = "base" + + ports = new() + for(var/d in cardinal) + var/datum/omni_port/new_port = new(src, d) + switch(d) + if(NORTH) + new_port.mode = tag_north + if(SOUTH) + new_port.mode = tag_south + if(EAST) + new_port.mode = tag_east + if(WEST) + new_port.mode = tag_west + if(new_port.mode > 0) + initialize_directions |= d + ports += new_port + + build_icons() + +/obj/machinery/atmospherics/omni/update_icon() + if(stat & NOPOWER) + overlays = overlays_off + else if(error_check()) + overlays = overlays_error + else + overlays = on ? (overlays_on) : (overlays_off) + + underlays = underlays_current + + return + +/obj/machinery/atmospherics/omni/proc/error_check() + return + +/obj/machinery/atmospherics/omni/process() + 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 0 + return 1 + +/obj/machinery/atmospherics/omni/power_change() + var/old_stat = stat + ..() + if(old_stat != stat) + update_icon() + +/obj/machinery/atmospherics/omni/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + if(!istype(W, /obj/item/weapon/wrench)) + return ..() + + var/int_pressure = 0 + for(var/datum/omni_port/P in ports) + int_pressure += P.air.return_pressure() + var/datum/gas_mixture/env_air = loc.return_air() + if ((int_pressure - env_air.return_pressure()) > 2*ONE_ATMOSPHERE) + user << "You cannot unwrench [src], it is too exerted due to internal pressure." + add_fingerprint(user) + return 1 + user << "\blue You begin to unfasten \the [src]..." + playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1) + if(do_after(user, 40)) + user.visible_message( \ + "[user] unfastens \the [src].", \ + "\blue You have unfastened \the [src].", \ + "You hear a ratchet.") + new /obj/item/pipe(loc, make_from=src) + del(src) + +/obj/machinery/atmospherics/omni/attack_hand(user as mob) + if(..()) + return + + src.add_fingerprint(usr) + ui_interact(user) + return + +/obj/machinery/atmospherics/omni/proc/build_icons() + if(!check_icon_cache()) + return + + var/core_icon = null + if(istype(src, /obj/machinery/atmospherics/omni/mixer)) + core_icon = "mixer" + else if(istype(src, /obj/machinery/atmospherics/omni/filter)) + core_icon = "filter" + else + return + + //directional icons are layers 1-4, with the core icon on layer 5 + if(core_icon) + 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] = 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 + switch(P.dir) + if(NORTH) + ref_layer = 1 + if(SOUTH) + ref_layer = 2 + if(EAST) + ref_layer = 3 + if(WEST) + ref_layer = 4 + + if(!ref_layer) + continue + + var/list/port_icons = select_port_icons(P) + if(port_icons) + if(P.node) + underlays_current[ref_layer] = port_icons["pipe_icon"] + else + underlays_current[ref_layer] = null + 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 + overlays_on[ref_layer] = null + + update_icon() + +/obj/machinery/atmospherics/omni/proc/select_port_icons(var/datum/omni_port/P) + if(!istype(P)) + return + + if(P.mode > 0) + var/ic_dir = dir_name(P.dir) + var/ic_on = ic_dir + var/ic_off = ic_dir + switch(P.mode) + if(ATM_INPUT) + ic_on += "_in_glow" + ic_off += "_in" + if(ATM_OUTPUT) + ic_on += "_out_glow" + ic_off += "_out" + if(ATM_O2 to ATM_N2O) + ic_on += "_filter" + ic_off += "_out" + + 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() + for(var/datum/omni_port/P in ports) + P.update = 0 + +/obj/machinery/atmospherics/omni/proc/sort_ports() + return + + +// Housekeeping and pipe network stuff below + +/obj/machinery/atmospherics/omni/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) + for(var/datum/omni_port/P in ports) + if(reference == P.node) + P.network = new_network + break + + if(new_network.normal_members.Find(src)) + return 0 + + new_network.normal_members += src + + return null + +/obj/machinery/atmospherics/omni/Del() + loc = null + + for(var/datum/omni_port/P in ports) + if(P.node) + P.node.disconnect(src) + del(P.network) + P.node = null + + ..() + +/obj/machinery/atmospherics/omni/initialize() + for(var/datum/omni_port/P in ports) + if(P.node || P.mode == 0) + continue + for(var/obj/machinery/atmospherics/target in get_step(src, P.dir)) + if(target.initialize_directions & get_dir(target,src)) + P.node = target + break + + for(var/datum/omni_port/P in ports) + P.update = 1 + + update_ports() + +/obj/machinery/atmospherics/omni/build_network() + for(var/datum/omni_port/P in ports) + if(!P.network && P.node) + P.network = new /datum/pipe_network() + P.network.normal_members += src + P.network.build_network(P.node, src) + +/obj/machinery/atmospherics/omni/return_network(obj/machinery/atmospherics/reference) + build_network() + + for(var/datum/omni_port/P in ports) + if(reference == P.node) + return P.network + + return null + +/obj/machinery/atmospherics/omni/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network) + for(var/datum/omni_port/P in ports) + if(P.network == old_network) + P.network = new_network + + return 1 + +/obj/machinery/atmospherics/omni/return_network_air(datum/pipe_network/reference) + var/list/results = list() + + for(var/datum/omni_port/P in ports) + if(P.network == reference) + results += P.air + + return results + +/obj/machinery/atmospherics/omni/disconnect(obj/machinery/atmospherics/reference) + for(var/datum/omni_port/P in ports) + if(reference == P.node) + del(P.network) + P.node = null + P.update = 1 + break + + update_ports() + + return null \ No newline at end of file diff --git a/code/ATMOSPHERICS/components/portables_connector.dm b/code/ATMOSPHERICS/components/portables_connector.dm index c5002fe1..090980a7 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 5ea2c0eb..76c36eca 100644 --- a/code/ATMOSPHERICS/components/trinary_devices/filter.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/filter.dm @@ -1,124 +1,115 @@ -obj/machinery/atmospherics/trinary/filter - icon = 'icons/obj/atmospherics/filter.dmi' - icon_state = "intact_off" - density = 1 +/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: Toxins: Toxins, Oxygen Agent B + 1: Oxygen: Oxygen ONLY + 2: Nitrogen: Nitrogen ONLY + 3: Carbon Dioxide: Carbon Dioxide ONLY + 4: Sleeping Agent (N2O) + */ + var/filter_type = -1 + var/list/filtered_out = list() - var/filter_type = 0 -/* -Filter types: --1: Nothing - 0: Plasma: Plasma Toxin, Oxygen Agent B - 1: Oxygen: Oxygen ONLY - 2: Nitrogen: Nitrogen ONLY - 3: Carbon Dioxide: Carbon Dioxide ONLY - 4: Sleeping Agent (N2O) -*/ 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() + ..() + switch(filter_type) + if(0) //removing hydrocarbons + filtered_out = list("toxins", "oxygen_agent_b") + if(1) //removing O2 + filtered_out = list("oxygen") + if(2) //removing N2 + filtered_out = list("nitrogen") + if(3) //removing CO2 + filtered_out = list("carbon_dioxide") + if(4)//removing N2O + filtered_out = list("sleeping_agent") + + 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.toxins = removed.toxins - removed.toxins = 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 +119,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 @@ -170,7 +164,7 @@ obj/machinery/atmospherics/trinary/filter/attack_hand(user as mob) // -- TLE var/current_filter_type switch(filter_type) if(0) - current_filter_type = "Plasma" + current_filter_type = "Toxins" if(1) current_filter_type = "Oxygen" if(2) @@ -188,14 +182,16 @@ obj/machinery/atmospherics/trinary/filter/attack_hand(user as mob) // -- TLE Power: [on?"On":"Off"]
Filtering: [current_filter_type]

Set Filter Type:

- Plasma
+ Toxins
Oxygen
Nitrogen
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 +207,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 += "toxins" + 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 +245,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 +263,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 +288,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 54a43075..cb76dc77 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" - density = 1 +/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 8c56dcfd..1ec311c1 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 83f592c3..603feb02 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 38da8398..0427fc0a 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 dfbecb65..d3c6cca8 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 1052191c..62be8b87 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,139 +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 +/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 - attackby(obj/item/weapon/C as obj, mob/user as mob) - add_fingerprint(user) - if(istype(C, /obj/item/device/signaltool)) - var/obj/item/device/signaltool/ST = C - id = ST.change_ID(id) - set_frequency(ST.change_freq(frequency)) - return \ No newline at end of file + 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 4cf446c9..59d147a2 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 786161c6..33aed69a 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 743680f1..6113a2a2 100644 --- a/code/ATMOSPHERICS/components/unary/vent_pump.dm +++ b/code/ATMOSPHERICS/components/unary/vent_pump.dm @@ -1,11 +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 @@ -14,14 +27,21 @@ var/on = 0 var/pump_direction = 1 //0 = siphoning, 1 = releasing - var/external_pressure_bound = ONE_ATMOSPHERE - var/internal_pressure_bound = 0 + var/last_power_draw = 0 - var/pressure_checks = 1 + var/external_pressure_bound = EXTERNAL_PRESSURE_BOUND + var/internal_pressure_bound = INTERNAL_PRESSURE_BOUND + + var/pressure_checks = PRESSURE_CHECKS //1: Do not pass external_pressure_bound //2: Do not pass internal_pressure_bound //3: Do not pass either + // Used when handling incoming radio signals requesting default settings + var/external_pressure_bound_default = EXTERNAL_PRESSURE_BOUND + var/internal_pressure_bound_default = INTERNAL_PRESSURE_BOUND + var/pressure_checks_default = PRESSURE_CHECKS + var/welded = 0 // Added for aliens -- TLE var/frequency = 1439 @@ -30,305 +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"] != 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"] != 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"] != 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) - internal_pressure_bound = between( - 0, - internal_pressure_bound + text2num(signal.data["adjust_internal_pressure"]), - ONE_ATMOSPHERE*50 - ) + 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["adjust_external_pressure"] != null) - external_pressure_bound = between( - 0, - external_pressure_bound + text2num(signal.data["adjust_external_pressure"]), - ONE_ATMOSPHERE*50 - ) + if(signal.data["adjust_external_pressure"] != null) - if(signal.data["init"] != null) - name = signal.data["init"] - return - if(signal.data["status"] != null) - spawn(2) - broadcast_status() - return //do not update_icon + external_pressure_bound = between( + 0, + external_pressure_bound + text2num(signal.data["adjust_external_pressure"]), + ONE_ATMOSPHERE*50 + ) - //log_admin("DEBUG \[[world.timeofday]\]: vent_pump/receive_signal: unknown command \"[signal.data["command"]]\"\n[signal.debug_print()]") + 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 ac7adddc..b11986da 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("carbon_dioxide") - 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_toxins" = ("toxins" 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"]) != ("toxins" in scrubbing_gas)) + toggle += "toxins" + else if(signal.data["toggle_tox_scrub"]) + toggle += "toxins" + + 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_toxins" = 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.toxins>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.toxins = removed.toxins - removed.toxins = 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 a4e5bfb6..2e521880 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 6b488ea7..23e6f742 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.toxins = 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.toxins += gas.toxins - 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.toxins = air_transient.toxins*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_toxins = 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_toxins += gas.toxins - 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.toxins = total_toxins*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 eb512893..22443092 100644 --- a/code/ATMOSPHERICS/datum_pipeline.dm +++ b/code/ATMOSPHERICS/datum_pipeline.dm @@ -1,3 +1,4 @@ + datum/pipeline var/datum/gas_mixture/air @@ -35,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.toxins = air.toxins*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 @@ -209,5 +197,17 @@ datum/pipeline (partial_heat_capacity*target.heat_capacity/(partial_heat_capacity+target.heat_capacity)) air.temperature -= heat/total_heat_capacity + if(network) + network.update = 1 + + //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 63c7e234..543c5cee 100644 --- a/code/ATMOSPHERICS/he_pipes.dm +++ b/code/ATMOSPHERICS/he_pipes.dm @@ -1,9 +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 //surface area in m^2 minimum_temperature_difference = 20 thermal_conductivity = OPEN_HEAT_TRANSFER_COEFFICIENT @@ -34,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 @@ -49,17 +55,17 @@ obj/machinery/atmospherics/pipe/simple/heat_exchanging 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) - + 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 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/pipes.dm b/code/ATMOSPHERICS/pipes.dm index d52f5fcd..71c0d54f 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,35 +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() - if(node1&&node2) - 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 = "intact[invisibility ? "-f" : "" ]" +/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() - //var/node1_direction = get_dir(src, node1) - //var/node2_direction = get_dir(src, node2) +/obj/machinery/atmospherics/pipe/simple/update_icon(var/safety = 0) + if(!check_icon_cache()) + return - //dir = node1_direction|node2_direction + alpha = 255 + 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) - del(src) //TODO: silent deleting looks weird - 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 @@ -265,13 +282,16 @@ obj/machinery/atmospherics/pipe/simple/initialize() node2 = target break + if(!node1 && !node2) + 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) @@ -286,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" @@ -347,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 @@ -365,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 @@ -377,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) @@ -423,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) @@ -443,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) @@ -511,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 @@ -582,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) @@ -630,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) @@ -655,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) @@ -707,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' @@ -773,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) @@ -785,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) @@ -814,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 @@ -828,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/toxins - icon = 'icons/obj/atmospherics/orange_pipe_tank.dmi' - name = "Pressure Tank (Plasma)" - - New() - air_temporary = new - air_temporary.volume = volume - air_temporary.temperature = T20C - - air_temporary.toxins = (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 + Plasma)" - - 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)) @@ -980,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/plasma_concentration = parent.air.toxins/total_moles - - var/unknown_concentration = 1-(o2_concentration+n2_concentration+co2_concentration+plasma_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 Plasma: [round(plasma_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/toxins + name = "Pressure Tank (Plasma)" + icon_state = "phoron_map" + +/obj/machinery/atmospherics/pipe/tank/toxins/New() + air_temporary = new + air_temporary.volume = volume + air_temporary.temperature = T20C + + air_temporary.adjust_gas("toxins", (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" @@ -1038,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 @@ -1056,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" @@ -1082,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)) @@ -1092,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) @@ -1102,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/Rust/core_field.dm b/code/WorkInProgress/Cael_Aislinn/Rust/core_field.dm index 09ac351d..242779fd 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 plasma 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_plasma.toxins < transfer_ratio * 1000) + if(held_plasma.gas["toxins"] < 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/plasma_captured = new /datum/gas_mixture() // - plasma_captured.toxins = round(gas_covered.toxins * transfer_ratio) - //world << "\blue[plasma_captured.toxins] moles of plasma captured" + plasma_captured.gas["toxins"] = round(gas_covered.gas["toxins"] * transfer_ratio) + //world << "\blue[phoron_captured.toxins] moles of phoron captured" plasma_captured.temperature = gas_covered.temperature plasma_captured.update_values() // - gas_covered.toxins -= plasma_captured.toxins - gas_covered.update_values() + gas_covered.adjust_gas("toxins", -plasma_captured.gas["toxins"]) // held_plasma.merge(plasma_captured) // @@ -171,15 +170,15 @@ Deuterium-tritium fusion: 4.5 x 10^7 K //change held plasma temp according to energy levels //SPECIFIC_HEAT_TOXIN - if(mega_energy > 0 && held_plasma.toxins) - var/heat_capacity = held_plasma.heat_capacity()//200 * number of plasma moles + if(mega_energy > 0 && held_plasma.gas["toxins"]) + var/heat_capacity = held_plasma.heat_capacity()//200 * number of phoron moles if(heat_capacity > 0.0003) //formerly MINIMUM_HEAT_CAPACITY held_plasma.temperature = (heat_capacity + mega_energy * 35000)/heat_capacity //if there is too much plasma in the field, lose some /*if( held_plasma.toxins > (MOLES_CELLSTANDARD * 7) * (50 / field_strength) ) LosePlasma()*/ - if(held_plasma.toxins > 1) + if(held_plasma.gas["toxins"] > 1) //lose a random amount of plasma 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 plasma" @@ -187,16 +186,16 @@ Deuterium-tritium fusion: 4.5 x 10^7 K var/datum/gas_mixture/plasma_lost = new plasma_lost.temperature = held_plasma.temperature // - plasma_lost.toxins = held_plasma.toxins * loss_ratio + plasma_lost.gas["toxins"] = held_plasma.gas["toxins"] * loss_ratio //plasma_lost.update_values() - held_plasma.toxins -= held_plasma.toxins * loss_ratio + held_plasma.gas["toxins"] -= held_plasma.gas["toxins"] * loss_ratio //held_plasma.update_values() // environment.merge(plasma_lost) radiation += loss_ratio * mega_energy * 0.1 mega_energy -= loss_ratio * mega_energy * 0.1 else - held_plasma.toxins = 0 + held_plasma.gas["toxins"] = null //held_plasma.update_values() //handle some reactants formatting diff --git a/code/WorkInProgress/Chinsky/ashtray.dm b/code/WorkInProgress/Chinsky/ashtray.dm index 58251711..386c4cd7 100644 --- a/code/WorkInProgress/Chinsky/ashtray.dm +++ b/code/WorkInProgress/Chinsky/ashtray.dm @@ -82,8 +82,7 @@ icon_broken = "ashtray_bork_bl" max_butts = 14 health = 24.0 - g_amt = 30 - m_amt = 30 + matter = list("metal" = 20,"glass" = 30) empty_desc = "Cheap plastic ashtray." throwforce = 3.0 die() @@ -103,7 +102,7 @@ icon_broken = "ashtray_bork_br" max_butts = 10 health = 72.0 - m_amt = 80 + matter = list("metal" = 80) empty_desc = "Massive bronze ashtray." throwforce = 10.0 @@ -124,7 +123,7 @@ icon_broken = "ashtray_bork_gl" max_butts = 12 health = 12.0 - g_amt = 60 + matter = list("glass" = 60) empty_desc = "Glass ashtray. Looks fragile." throwforce = 6.0 diff --git a/code/WorkInProgress/Mini/atmos_control.dm b/code/WorkInProgress/Mini/atmos_control.dm index adf81eef..2e828c62 100644 --- a/code/WorkInProgress/Mini/atmos_control.dm +++ b/code/WorkInProgress/Mini/atmos_control.dm @@ -11,7 +11,7 @@ circuit = "/obj/item/weapon/circuitboard/atmoscontrol" var/obj/machinery/alarm/current var/overridden = 0 //not set yet, can't think of a good way to do it - req_access = list(access_ce) + req_access = list(access_atmospherics) /obj/machinery/computer/atmoscontrol/attack_ai(var/mob/user as mob) @@ -156,12 +156,14 @@ src.updateUsrDialog() return - if(href_list["atmos_unlock"]) - switch(href_list["atmos_unlock"]) - if("0") - current.air_doors_close(1) - if("1") - current.air_doors_open(1) + //commenting this out because it causes compile errors + //I tried fixing it but wasn't sucessful. + //if(href_list["atmos_unlock"]) + // switch(href_list["atmos_unlock"]) + // if("0") + // current.alarm_area.air_doors_close() + // if("1") + // current.alarm_area.air_doors_open() if(href_list["atmos_alarm"]) if (current.alarm_area.atmosalert(2)) diff --git a/code/ZAS/Airflow.dm b/code/ZAS/Airflow.dm index 67ace07f..5d63512c 100644 --- a/code/ZAS/Airflow.dm +++ b/code/ZAS/Airflow.dm @@ -103,7 +103,7 @@ obj/item/check_airflow_movable(n) density = 1 od = 1 while(airflow_speed > 0) - if(airflow_speed <= 0) return + if(airflow_speed <= 0) break airflow_speed = min(airflow_speed,15) airflow_speed -= vsc.airflow_speed_decay if(airflow_speed > 7) @@ -120,9 +120,9 @@ obj/item/check_airflow_movable(n) if ((!( src.airflow_dest ) || src.loc == src.airflow_dest)) src.airflow_dest = locate(min(max(src.x + xo, 1), world.maxx), min(max(src.y + yo, 1), world.maxy), src.z) if ((src.x == 1 || src.x == world.maxx || src.y == 1 || src.y == world.maxy)) - return + break if(!istype(loc, /turf)) - return + break step_towards(src, src.airflow_dest) if(ismob(src) && src:client) src:client:move_delay = world.time + vsc.airflow_mob_slowdown diff --git a/code/ZAS/ConnectionGroup.dm b/code/ZAS/ConnectionGroup.dm index c31dbf9a..7fab5e94 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_plasma = A.toxins * 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_plasma = B.toxins * 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) - plasma_avg = (full_plasma + s_full_plasma) / (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.toxins = max(0, (A.toxins - plasma_avg) * (1-ratio) + plasma_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.toxins = max(0, (B.toxins - plasma_avg) * (1-ratio) + plasma_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_plasma = 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_plasma = avg_unsim.toxins - 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_plasma += T.toxins - 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_plasma *= 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_plasma) - - 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_plasma = A.toxins * 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) - plasma_avg = (full_plasma + unsim_plasma*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.toxins = max(0, (A.toxins - plasma_avg) * (1 - ratio) + plasma_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 83b4bf3f..70e5f6ae 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 eb5d7278..b667c36c 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.toxins]" + 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/FEA_gas_mixture.dm b/code/ZAS/FEA_gas_mixture.dm deleted file mode 100644 index 9a829633..00000000 --- a/code/ZAS/FEA_gas_mixture.dm +++ /dev/null @@ -1,1090 +0,0 @@ -/* -What are the archived variables for? - Calculations are done using the archived variables with the results merged into the regular variables. - This prevents race conditions that arise based on the order of tile processing. -*/ - -#define SPECIFIC_HEAT_TOXIN 200 -#define SPECIFIC_HEAT_AIR 20 -#define SPECIFIC_HEAT_CDO 30 -#define HEAT_CAPACITY_CALCULATION(oxygen,carbon_dioxide,nitrogen,toxins) \ - max(0, carbon_dioxide * SPECIFIC_HEAT_CDO + (oxygen + nitrogen) * SPECIFIC_HEAT_AIR + toxins * SPECIFIC_HEAT_TOXIN) - -#define MINIMUM_HEAT_CAPACITY 0.0003 -#define QUANTIZE(variable) (round(variable,0.0001)) -#define TRANSFER_FRACTION 5 //What fraction (1/#) of the air difference to try and transfer - -/hook/startup/proc/createGasOverlays() - plmaster = new /obj/effect/overlay() - plmaster.icon = 'icons/effects/tile_effects.dmi' - plmaster.icon_state = "plasma" - plmaster.layer = FLY_LAYER - plmaster.mouse_opacity = 0 - - slmaster = new /obj/effect/overlay() - slmaster.icon = 'icons/effects/tile_effects.dmi' - slmaster.icon_state = "sleeping_agent" - slmaster.layer = FLY_LAYER - slmaster.mouse_opacity = 0 - return 1 - -/datum/gas/sleeping_agent/specific_heat = 40 //These are used for the "Trace Gases" stuff, but is buggy. - -/datum/gas/oxygen_agent_b/specific_heat = 300 - -/datum/gas/volatile_fuel/specific_heat = 30 - -/datum/gas - var/moles = 0 - - var/specific_heat = 0 - - var/moles_archived = 0 - -/datum/gas_mixture/ - var/oxygen = 0 //Holds the "moles" of each of the four gases. - var/carbon_dioxide = 0 - var/nitrogen = 0 - var/toxins = 0 - - var/total_moles = 0 //Updated when a reaction occurs. - - var/volume = CELL_VOLUME - - var/temperature = 0 //in Kelvin, use calculate_temperature() to modify - - var/group_multiplier = 1 - //Size of the group this gas_mixture is representing. - //=1 for singletons - - var/graphic - - var/list/datum/gas/trace_gases = list() //Seemed to be a good idea that was abandoned - - var/tmp/oxygen_archived //These are variables for use with the archived data - var/tmp/carbon_dioxide_archived - var/tmp/nitrogen_archived - var/tmp/toxins_archived - - var/tmp/temperature_archived - - var/tmp/graphic_archived = 0 - var/tmp/fuel_burnt = 0 - - var/reacting = 0 - -//FOR THE LOVE OF GOD PLEASE USE THIS PROC -//Call it with negative numbers to remove gases. - -/datum/gas_mixture/proc/adjust(o2 = 0, co2 = 0, n2 = 0, tx = 0, list/datum/gas/traces = list()) - //Purpose: Adjusting the gases within a airmix - //Called by: Nothing, yet! - //Inputs: The values of the gases to adjust - //Outputs: null - - oxygen = max(0, oxygen + o2) - carbon_dioxide = max(0, carbon_dioxide + co2) - nitrogen = max(0, nitrogen + n2) - toxins = max(0, toxins + tx) - - //handle trace gasses - for(var/datum/gas/G in traces) - var/datum/gas/T = locate(G.type) in trace_gases - if(T) - T.moles = max(G.moles + T.moles, 0) - else if(G.moles > 0) - trace_gases |= G - update_values() - return - - //tg seems to like using these a lot -/datum/gas_mixture/proc/return_temperature() - return temperature - - -/datum/gas_mixture/proc/return_volume() - return max(0, volume) - - -/datum/gas_mixture/proc/thermal_energy() - return temperature*heat_capacity() - -/////////////////////////////// -//PV=nRT - related procedures// -/////////////////////////////// - -/datum/gas_mixture/proc/heat_capacity() - //Purpose: Returning the heat capacity of the gas mix - //Called by: UNKNOWN - //Inputs: None - //Outputs: Heat capacity - - var/heat_capacity = HEAT_CAPACITY_CALCULATION(oxygen,carbon_dioxide,nitrogen,toxins) - - if(trace_gases.len) - for(var/datum/gas/trace_gas in trace_gases) - heat_capacity += trace_gas.moles*trace_gas.specific_heat - - return max(MINIMUM_HEAT_CAPACITY,heat_capacity) - -/datum/gas_mixture/proc/heat_capacity_archived() - //Purpose: Returning the archived heat capacity of the gas mix - //Called by: UNKNOWN - //Inputs: None - //Outputs: Archived heat capacity - - var/heat_capacity_archived = HEAT_CAPACITY_CALCULATION(oxygen_archived,carbon_dioxide_archived,nitrogen_archived,toxins_archived) - - if(trace_gases.len) - for(var/datum/gas/trace_gas in trace_gases) - heat_capacity_archived += trace_gas.moles_archived*trace_gas.specific_heat - - return max(MINIMUM_HEAT_CAPACITY,heat_capacity_archived) - -/datum/gas_mixture/proc/total_moles() - return total_moles - /*var/moles = oxygen + carbon_dioxide + nitrogen + toxins - - if(trace_gases.len) - for(var/datum/gas/trace_gas in trace_gases) - moles += trace_gas.moles - return moles*/ - -/datum/gas_mixture/proc/return_pressure() - //Purpose: Calculating Current Pressure - //Called by: - //Inputs: None - //Outputs: Gas pressure. - - if(volume>0) - return total_moles()*R_IDEAL_GAS_EQUATION*temperature/volume - return 0 - -// proc/return_temperature() - //Purpose: - //Inputs: - //Outputs: - -// return temperature - -// proc/return_volume() - //Purpose: - //Inputs: - //Outputs: - -// return max(0, volume) - -// proc/thermal_energy() - //Purpose: - //Inputs: - //Outputs: - -// return temperature*heat_capacity() - -/datum/gas_mixture/proc/update_values() - //Purpose: Calculating and storing values which were normally called CONSTANTLY - //Called by: Anything that changes values within a gas mix. - //Inputs: None - //Outputs: None - - total_moles = oxygen + carbon_dioxide + nitrogen + toxins - - if(trace_gases.len) - for(var/datum/gas/trace_gas in trace_gases) - total_moles += trace_gas.moles - - return - -//////////////////////////////////////////// -//Procedures used for very specific events// -//////////////////////////////////////////// - -/datum/gas_mixture/proc/check_tile_graphic() - //Purpose: Calculating the graphic for a tile - //Called by: Turfs updating - //Inputs: None - //Outputs: 1 if graphic changed, 0 if unchanged - - graphic = 0 - if(toxins > MOLES_PLASMA_VISIBLE) - graphic = 1 - else if(length(trace_gases)) - var/datum/gas/sleeping_agent = locate(/datum/gas/sleeping_agent) in trace_gases - if(sleeping_agent && (sleeping_agent.moles > 1)) - graphic = 2 - else - graphic = 0 - - return graphic != graphic_archived - -/datum/gas_mixture/proc/react(atom/dump_location) - //Purpose: Calculating if it is possible for a fire to occur in the airmix - //Called by: Air mixes updating? - //Inputs: None - //Outputs: If a fire occured - - //set to 1 if a notable reaction occured (used by pipe_network) - - zburn(null) - - return reacting - -/* -/datum/gas_mixture/proc/fire() - //Purpose: Calculating any fire reactions. - //Called by: react() (See above) - //Inputs: None - //Outputs: How much fuel burned - - return zburn(null) - - var/energy_released = 0 - var/old_heat_capacity = heat_capacity() - - var/datum/gas/volatile_fuel/fuel_store = locate(/datum/gas/volatile_fuel) in trace_gases - if(fuel_store) //General volatile gas burn - var/burned_fuel = 0 - - if(oxygen < fuel_store.moles) - burned_fuel = oxygen - fuel_store.moles -= burned_fuel - oxygen = 0 - else - burned_fuel = fuel_store.moles - oxygen -= fuel_store.moles - del(fuel_store) - - energy_released += FIRE_CARBON_ENERGY_RELEASED * burned_fuel - carbon_dioxide += burned_fuel - fuel_burnt += burned_fuel - - //Handle plasma burning - if(toxins > MINIMUM_HEAT_CAPACITY) - var/plasma_burn_rate = 0 - var/oxygen_burn_rate = 0 - //more plasma released at higher temperatures - var/temperature_scale - if(temperature > PLASMA_UPPER_TEMPERATURE) - temperature_scale = 1 - else - temperature_scale = (temperature-PLASMA_MINIMUM_BURN_TEMPERATURE)/(PLASMA_UPPER_TEMPERATURE-PLASMA_MINIMUM_BURN_TEMPERATURE) - if(temperature_scale > 0) - oxygen_burn_rate = 1.4 - temperature_scale - if(oxygen > toxins*PLASMA_OXYGEN_FULLBURN) - plasma_burn_rate = (toxins*temperature_scale)/4 - else - plasma_burn_rate = (temperature_scale*(oxygen/PLASMA_OXYGEN_FULLBURN))/4 - if(plasma_burn_rate > MINIMUM_HEAT_CAPACITY) - toxins -= plasma_burn_rate - oxygen -= plasma_burn_rate*oxygen_burn_rate - carbon_dioxide += plasma_burn_rate - - energy_released += FIRE_PLASMA_ENERGY_RELEASED * (plasma_burn_rate) - - fuel_burnt += (plasma_burn_rate)*(1+oxygen_burn_rate) - - if(energy_released > 0) - var/new_heat_capacity = heat_capacity() - if(new_heat_capacity > MINIMUM_HEAT_CAPACITY) - temperature = (temperature*old_heat_capacity + energy_released)/new_heat_capacity - update_values() - - return fuel_burnt*/ - -////////////////////////////////////////////// -//Procs for general gas spread calculations.// -////////////////////////////////////////////// - - -/datum/gas_mixture/proc/archive() - //Purpose: Archives the current gas values - //Called by: UNKNOWN - //Inputs: None - //Outputs: 1 - - oxygen_archived = oxygen - carbon_dioxide_archived = carbon_dioxide - nitrogen_archived = nitrogen - toxins_archived = toxins - - if(trace_gases.len) - for(var/datum/gas/trace_gas in trace_gases) - trace_gas.moles_archived = trace_gas.moles - - temperature_archived = temperature - - graphic_archived = graphic - - return 1 - -/datum/gas_mixture/proc/check_then_merge(datum/gas_mixture/giver) - //Purpose: Similar to merge(...) but first checks to see if the amount of air assumed is small enough - // that group processing is still accurate for source (aborts if not) - //Called by: airgroups/machinery expelling air, ? - //Inputs: The gas to try and merge - //Outputs: 1 on successful merge. 0 otherwise. - - if(!giver) - return 0 - if(((giver.oxygen > MINIMUM_AIR_TO_SUSPEND) && (giver.oxygen >= oxygen*MINIMUM_AIR_RATIO_TO_SUSPEND)) \ - || ((giver.carbon_dioxide > MINIMUM_AIR_TO_SUSPEND) && (giver.carbon_dioxide >= carbon_dioxide*MINIMUM_AIR_RATIO_TO_SUSPEND)) \ - || ((giver.nitrogen > MINIMUM_AIR_TO_SUSPEND) && (giver.nitrogen >= nitrogen*MINIMUM_AIR_RATIO_TO_SUSPEND)) \ - || ((giver.toxins > MINIMUM_AIR_TO_SUSPEND) && (giver.toxins >= toxins*MINIMUM_AIR_RATIO_TO_SUSPEND))) - return 0 - if(abs(giver.temperature - temperature) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) - return 0 - - if(giver.trace_gases.len) - for(var/datum/gas/trace_gas in giver.trace_gases) - var/datum/gas/corresponding = locate(trace_gas.type) in trace_gases - if((trace_gas.moles > MINIMUM_AIR_TO_SUSPEND) && (!corresponding || (trace_gas.moles >= corresponding.moles*MINIMUM_AIR_RATIO_TO_SUSPEND))) - return 0 - - return merge(giver) - -/datum/gas_mixture/proc/merge(datum/gas_mixture/giver) - //Purpose: Merges all air from giver into self. Deletes giver. - //Called by: Machinery expelling air, check_then_merge, ? - //Inputs: The gas to merge. - //Outputs: 1 - - if(!giver) - return 0 - - if(abs(temperature-giver.temperature)>MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER) - var/self_heat_capacity = heat_capacity()*group_multiplier - var/giver_heat_capacity = giver.heat_capacity()*giver.group_multiplier - 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)) - oxygen += giver.oxygen*giver.group_multiplier/group_multiplier - carbon_dioxide += giver.carbon_dioxide*giver.group_multiplier/group_multiplier - nitrogen += giver.nitrogen*giver.group_multiplier/group_multiplier - toxins += giver.toxins*giver.group_multiplier/group_multiplier - else - oxygen += giver.oxygen - carbon_dioxide += giver.carbon_dioxide - nitrogen += giver.nitrogen - toxins += giver.toxins - - if(giver.trace_gases.len) - for(var/datum/gas/trace_gas in giver.trace_gases) - var/datum/gas/corresponding = locate(trace_gas.type) in trace_gases - if(!corresponding) - corresponding = new trace_gas.type() - trace_gases += corresponding - corresponding.moles += trace_gas.moles*giver.group_multiplier/group_multiplier - update_values() - - // Let the garbage collector handle it, faster according to /tg/ testers - //del(giver) - return 1 - -/datum/gas_mixture/proc/remove(amount) - //Purpose: Removes a certain number of moles from the air. - //Called by: ? - //Inputs: How many moles to remove. - //Outputs: Removed air. - - var/sum = total_moles() - amount = min(amount,sum) //Can not take more air than tile has! - if(amount <= 0) - return null - - var/datum/gas_mixture/removed = new - - - removed.oxygen = QUANTIZE((oxygen/sum)*amount) - removed.nitrogen = QUANTIZE((nitrogen/sum)*amount) - removed.carbon_dioxide = QUANTIZE((carbon_dioxide/sum)*amount) - removed.toxins = QUANTIZE(((toxins/sum)*amount)) - - oxygen -= removed.oxygen/group_multiplier - nitrogen -= removed.nitrogen/group_multiplier - carbon_dioxide -= removed.carbon_dioxide/group_multiplier - toxins -= removed.toxins/group_multiplier - - if(trace_gases.len) - for(var/datum/gas/trace_gas in trace_gases) - var/datum/gas/corresponding = new trace_gas.type() - removed.trace_gases += corresponding - - corresponding.moles = ((trace_gas.moles/sum)*amount) - trace_gas.moles -= (corresponding.moles/group_multiplier) - - removed.temperature = temperature - update_values() - removed.update_values() - - return removed - -/datum/gas_mixture/proc/remove_ratio(ratio) - //Purpose: Removes a certain ratio of the air. - //Called by: ? - //Inputs: Percentage to remove. - //Outputs: Removed air. - - if(ratio <= 0) - return null - - ratio = min(ratio, 1) - - var/datum/gas_mixture/removed = new - - removed.oxygen = QUANTIZE(oxygen*ratio) - removed.nitrogen = QUANTIZE(nitrogen*ratio) - removed.carbon_dioxide = QUANTIZE(carbon_dioxide*ratio) - removed.toxins = QUANTIZE(toxins*ratio) - - oxygen -= removed.oxygen/group_multiplier - nitrogen -= removed.nitrogen/group_multiplier - carbon_dioxide -= removed.carbon_dioxide/group_multiplier - toxins -= removed.toxins/group_multiplier - - if(trace_gases.len) - for(var/datum/gas/trace_gas in trace_gases) - var/datum/gas/corresponding = new trace_gas.type() - removed.trace_gases += corresponding - - corresponding.moles = trace_gas.moles*ratio - trace_gas.moles -= corresponding.moles/group_multiplier - - removed.temperature = temperature - update_values() - removed.update_values() - - return removed - -/datum/gas_mixture/proc/check_then_remove(amount) - //Purpose: Similar to remove(...) but first checks to see if the amount of air removed is small enough - // that group processing is still accurate for source (aborts if not) - //Called by: ? - //Inputs: Number of moles to remove - //Outputs: Removed air or 0 if it can remove air or not. - - amount = min(amount,total_moles()) //Can not take more air than tile has! - - if((amount > MINIMUM_AIR_RATIO_TO_SUSPEND) && (amount > total_moles()*MINIMUM_AIR_RATIO_TO_SUSPEND)) - return 0 - - return remove(amount) - -/datum/gas_mixture/proc/copy_from(datum/gas_mixture/sample) - //Purpose: Duplicates the sample air mixture. - //Called by: airgroups splitting, ? - //Inputs: Gas to copy - //Outputs: 1 - - oxygen = sample.oxygen - carbon_dioxide = sample.carbon_dioxide - nitrogen = sample.nitrogen - toxins = sample.toxins - total_moles = sample.total_moles() - - trace_gases.len=null - if(sample.trace_gases.len > 0) - for(var/datum/gas/trace_gas in sample.trace_gases) - var/datum/gas/corresponding = new trace_gas.type() - trace_gases += corresponding - - corresponding.moles = trace_gas.moles - - temperature = sample.temperature - - return 1 - -/datum/gas_mixture/proc/check_gas_mixture(datum/gas_mixture/sharer) - //Purpose: Telling if one or both airgroups needs to disable group processing. - //Called by: Airgroups sharing air, checking if group processing needs disabled. - //Inputs: Gas to compare from other airgroup - //Outputs: 0 if the self-check failed (local airgroup breaks?) - // then -1 if sharer-check failed (sharing airgroup breaks?) - // then 1 if both checks pass (share succesful?) - if(!istype(sharer)) - return - - var/delta_oxygen = QUANTIZE(oxygen_archived - sharer.oxygen_archived)/TRANSFER_FRACTION - var/delta_carbon_dioxide = QUANTIZE(carbon_dioxide_archived - sharer.carbon_dioxide_archived)/TRANSFER_FRACTION - var/delta_nitrogen = QUANTIZE(nitrogen_archived - sharer.nitrogen_archived)/TRANSFER_FRACTION - var/delta_toxins = QUANTIZE(toxins_archived - sharer.toxins_archived)/TRANSFER_FRACTION - - var/delta_temperature = (temperature_archived - sharer.temperature_archived) - - if(((abs(delta_oxygen) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_oxygen) >= oxygen_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \ - || ((abs(delta_carbon_dioxide) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_carbon_dioxide) >= carbon_dioxide_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \ - || ((abs(delta_nitrogen) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_nitrogen) >= nitrogen_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \ - || ((abs(delta_toxins) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_toxins) >= toxins_archived*MINIMUM_AIR_RATIO_TO_SUSPEND))) - return 0 - - if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) - return 0 - - if(sharer.trace_gases.len) - for(var/datum/gas/trace_gas in sharer.trace_gases) - if(trace_gas.moles_archived > MINIMUM_AIR_TO_SUSPEND*4) - var/datum/gas/corresponding = locate(trace_gas.type) in trace_gases - if(corresponding) - if(trace_gas.moles_archived >= corresponding.moles_archived*MINIMUM_AIR_RATIO_TO_SUSPEND*4) - return 0 - else - return 0 - - if(trace_gases.len) - for(var/datum/gas/trace_gas in trace_gases) - if(trace_gas.moles_archived > MINIMUM_AIR_TO_SUSPEND*4) - if(!locate(trace_gas.type) in sharer.trace_gases) - return 0 - - if(((abs(delta_oxygen) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_oxygen) >= sharer.oxygen_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \ - || ((abs(delta_carbon_dioxide) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_carbon_dioxide) >= sharer.carbon_dioxide_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \ - || ((abs(delta_nitrogen) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_nitrogen) >= sharer.nitrogen_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \ - || ((abs(delta_toxins) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_toxins) >= sharer.toxins_archived*MINIMUM_AIR_RATIO_TO_SUSPEND))) - return -1 - - if(trace_gases.len) - for(var/datum/gas/trace_gas in trace_gases) - if(trace_gas.moles_archived > MINIMUM_AIR_TO_SUSPEND*4) - var/datum/gas/corresponding = locate(trace_gas.type) in sharer.trace_gases - if(corresponding) - if(trace_gas.moles_archived >= corresponding.moles_archived*MINIMUM_AIR_RATIO_TO_SUSPEND*4) - return -1 - else - return -1 - - return 1 - -/datum/gas_mixture/proc/check_turf(turf/model) - //Purpose: Used to compare the gases in an unsimulated turf with the gas in a simulated one. - //Called by: Sharing air (mimicing) with adjacent unsimulated turfs - //Inputs: Unsimulated turf - //Outputs: 1 if safe to mimic, 0 if needs to break airgroup. - - var/delta_oxygen = (oxygen_archived - model.oxygen)/TRANSFER_FRACTION - var/delta_carbon_dioxide = (carbon_dioxide_archived - model.carbon_dioxide)/TRANSFER_FRACTION - var/delta_nitrogen = (nitrogen_archived - model.nitrogen)/TRANSFER_FRACTION - var/delta_toxins = (toxins_archived - model.toxins)/TRANSFER_FRACTION - - var/delta_temperature = (temperature_archived - model.temperature) - - if(((abs(delta_oxygen) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_oxygen) >= oxygen_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \ - || ((abs(delta_carbon_dioxide) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_carbon_dioxide) >= carbon_dioxide_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \ - || ((abs(delta_nitrogen) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_nitrogen) >= nitrogen_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \ - || ((abs(delta_toxins) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_toxins) >= toxins_archived*MINIMUM_AIR_RATIO_TO_SUSPEND))) - return 0 - if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) - return 0 - - if(trace_gases.len) - for(var/datum/gas/trace_gas in trace_gases) - if(trace_gas.moles_archived > MINIMUM_AIR_TO_SUSPEND*4) - return 0 - - return 1 - -/datum/gas_mixture/proc/share(datum/gas_mixture/sharer) - //Purpose: Used to transfer gas from a more pressurised tile to a less presurised tile - // (Two directional, if the other tile is more pressurised, air travels to current tile) - //Called by: Sharing air with adjacent simulated turfs - //Inputs: Air datum to share with - //Outputs: Amount of gas exchanged (Negative if lost air, positive if gained.) - - - if(!istype(sharer)) - return - - var/delta_oxygen = QUANTIZE(oxygen_archived - sharer.oxygen_archived)/TRANSFER_FRACTION - var/delta_carbon_dioxide = QUANTIZE(carbon_dioxide_archived - sharer.carbon_dioxide_archived)/TRANSFER_FRACTION - var/delta_nitrogen = QUANTIZE(nitrogen_archived - sharer.nitrogen_archived)/TRANSFER_FRACTION - var/delta_toxins = QUANTIZE(toxins_archived - sharer.toxins_archived)/TRANSFER_FRACTION - - var/delta_temperature = (temperature_archived - sharer.temperature_archived) - - var/old_self_heat_capacity = 0 - var/old_sharer_heat_capacity = 0 - - var/heat_self_to_sharer = 0 - var/heat_capacity_self_to_sharer = 0 - var/heat_sharer_to_self = 0 - var/heat_capacity_sharer_to_self = 0 - - if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER) - - var/delta_air = delta_oxygen+delta_nitrogen - if(delta_air) - var/air_heat_capacity = SPECIFIC_HEAT_AIR*delta_air - if(delta_air > 0) - heat_self_to_sharer += air_heat_capacity*temperature_archived - heat_capacity_self_to_sharer += air_heat_capacity - else - heat_sharer_to_self -= air_heat_capacity*sharer.temperature_archived - heat_capacity_sharer_to_self -= air_heat_capacity - - if(delta_carbon_dioxide) - var/carbon_dioxide_heat_capacity = SPECIFIC_HEAT_CDO*delta_carbon_dioxide - if(delta_carbon_dioxide > 0) - heat_self_to_sharer += carbon_dioxide_heat_capacity*temperature_archived - heat_capacity_self_to_sharer += carbon_dioxide_heat_capacity - else - heat_sharer_to_self -= carbon_dioxide_heat_capacity*sharer.temperature_archived - heat_capacity_sharer_to_self -= carbon_dioxide_heat_capacity - - if(delta_toxins) - var/toxins_heat_capacity = SPECIFIC_HEAT_TOXIN*delta_toxins - if(delta_toxins > 0) - heat_self_to_sharer += toxins_heat_capacity*temperature_archived - heat_capacity_self_to_sharer += toxins_heat_capacity - else - heat_sharer_to_self -= toxins_heat_capacity*sharer.temperature_archived - heat_capacity_sharer_to_self -= toxins_heat_capacity - - old_self_heat_capacity = heat_capacity()*group_multiplier - old_sharer_heat_capacity = sharer.heat_capacity()*sharer.group_multiplier - - oxygen -= delta_oxygen/group_multiplier - sharer.oxygen += delta_oxygen/sharer.group_multiplier - - carbon_dioxide -= delta_carbon_dioxide/group_multiplier - sharer.carbon_dioxide += delta_carbon_dioxide/sharer.group_multiplier - - nitrogen -= delta_nitrogen/group_multiplier - sharer.nitrogen += delta_nitrogen/sharer.group_multiplier - - toxins -= delta_toxins/group_multiplier - sharer.toxins += delta_toxins/sharer.group_multiplier - - var/moved_moles = (delta_oxygen + delta_carbon_dioxide + delta_nitrogen + delta_toxins) - - var/list/trace_types_considered = list() - - if(trace_gases.len) - for(var/datum/gas/trace_gas in trace_gases) - - var/datum/gas/corresponding = locate(trace_gas.type) in sharer.trace_gases - var/delta = 0 - - if(corresponding) - delta = QUANTIZE(trace_gas.moles_archived - corresponding.moles_archived)/TRANSFER_FRACTION - else - corresponding = new trace_gas.type() - sharer.trace_gases += corresponding - - delta = trace_gas.moles_archived/TRANSFER_FRACTION - - trace_gas.moles -= delta/group_multiplier - corresponding.moles += delta/sharer.group_multiplier - - if(delta) - var/individual_heat_capacity = trace_gas.specific_heat*delta - if(delta > 0) - heat_self_to_sharer += individual_heat_capacity*temperature_archived - heat_capacity_self_to_sharer += individual_heat_capacity - else - heat_sharer_to_self -= individual_heat_capacity*sharer.temperature_archived - heat_capacity_sharer_to_self -= individual_heat_capacity - - moved_moles += delta - - trace_types_considered += trace_gas.type - - - if(sharer.trace_gases.len) - for(var/datum/gas/trace_gas in sharer.trace_gases) - if(trace_gas.type in trace_types_considered) continue - else - var/datum/gas/corresponding - var/delta = 0 - - corresponding = new trace_gas.type() - trace_gases += corresponding - - delta = trace_gas.moles_archived/TRANSFER_FRACTION - - trace_gas.moles -= delta/sharer.group_multiplier - corresponding.moles += delta/group_multiplier - - //Guaranteed transfer from sharer to self - var/individual_heat_capacity = trace_gas.specific_heat*delta - heat_sharer_to_self += individual_heat_capacity*sharer.temperature_archived - heat_capacity_sharer_to_self += individual_heat_capacity - - moved_moles += -delta - update_values() - sharer.update_values() - - if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER) - var/new_self_heat_capacity = old_self_heat_capacity + heat_capacity_sharer_to_self - heat_capacity_self_to_sharer - var/new_sharer_heat_capacity = old_sharer_heat_capacity + heat_capacity_self_to_sharer - heat_capacity_sharer_to_self - - if(new_self_heat_capacity > MINIMUM_HEAT_CAPACITY) - temperature = (old_self_heat_capacity*temperature - heat_capacity_self_to_sharer*temperature_archived + heat_capacity_sharer_to_self*sharer.temperature_archived)/new_self_heat_capacity - - if(new_sharer_heat_capacity > MINIMUM_HEAT_CAPACITY) - sharer.temperature = (old_sharer_heat_capacity*sharer.temperature-heat_capacity_sharer_to_self*sharer.temperature_archived + heat_capacity_self_to_sharer*temperature_archived)/new_sharer_heat_capacity - - if(abs(old_sharer_heat_capacity) > MINIMUM_HEAT_CAPACITY) - if(abs(new_sharer_heat_capacity/old_sharer_heat_capacity - 1) < 0.10) // <10% change in sharer heat capacity - temperature_share(sharer, OPEN_HEAT_TRANSFER_COEFFICIENT) - - if((delta_temperature > MINIMUM_TEMPERATURE_TO_MOVE) || abs(moved_moles) > MINIMUM_MOLES_DELTA_TO_MOVE) - var/delta_pressure = temperature_archived*(total_moles() + moved_moles) - sharer.temperature_archived*(sharer.total_moles() - moved_moles) - return delta_pressure*R_IDEAL_GAS_EQUATION/volume - - else - return 0 - -/datum/gas_mixture/proc/mimic(turf/model, border_multiplier) - //Purpose: Used transfer gas from a more pressurised tile to a less presurised unsimulated tile. - //Called by: "sharing" from unsimulated to simulated turfs. - //Inputs: Unsimulated turf, Multiplier for gas transfer (optional) - //Outputs: Amount of gas exchanged - - var/delta_oxygen = QUANTIZE(oxygen_archived - model.oxygen)/TRANSFER_FRACTION - var/delta_carbon_dioxide = QUANTIZE(carbon_dioxide_archived - model.carbon_dioxide)/TRANSFER_FRACTION - var/delta_nitrogen = QUANTIZE(nitrogen_archived - model.nitrogen)/TRANSFER_FRACTION - var/delta_toxins = QUANTIZE(toxins_archived - model.toxins)/TRANSFER_FRACTION - - var/delta_temperature = (temperature_archived - model.temperature) - - var/heat_transferred = 0 - var/old_self_heat_capacity = 0 - var/heat_capacity_transferred = 0 - - if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER) - - var/delta_air = delta_oxygen+delta_nitrogen - if(delta_air) - var/air_heat_capacity = SPECIFIC_HEAT_AIR*delta_air - heat_transferred -= air_heat_capacity*model.temperature - heat_capacity_transferred -= air_heat_capacity - - if(delta_carbon_dioxide) - var/carbon_dioxide_heat_capacity = SPECIFIC_HEAT_CDO*delta_carbon_dioxide - heat_transferred -= carbon_dioxide_heat_capacity*model.temperature - heat_capacity_transferred -= carbon_dioxide_heat_capacity - - if(delta_toxins) - var/toxins_heat_capacity = SPECIFIC_HEAT_TOXIN*delta_toxins - heat_transferred -= toxins_heat_capacity*model.temperature - heat_capacity_transferred -= toxins_heat_capacity - - old_self_heat_capacity = heat_capacity()*group_multiplier - - if(border_multiplier) - oxygen -= delta_oxygen*border_multiplier/group_multiplier - carbon_dioxide -= delta_carbon_dioxide*border_multiplier/group_multiplier - nitrogen -= delta_nitrogen*border_multiplier/group_multiplier - toxins -= delta_toxins*border_multiplier/group_multiplier - else - oxygen -= delta_oxygen/group_multiplier - carbon_dioxide -= delta_carbon_dioxide/group_multiplier - nitrogen -= delta_nitrogen/group_multiplier - toxins -= delta_toxins/group_multiplier - - var/moved_moles = (delta_oxygen + delta_carbon_dioxide + delta_nitrogen + delta_toxins) - - if(trace_gases.len) - for(var/datum/gas/trace_gas in trace_gases) - var/delta = 0 - - delta = trace_gas.moles_archived/TRANSFER_FRACTION - - if(border_multiplier) - trace_gas.moles -= delta*border_multiplier/group_multiplier - else - trace_gas.moles -= delta/group_multiplier - - var/heat_cap_transferred = delta*trace_gas.specific_heat - heat_transferred += heat_cap_transferred*temperature_archived - heat_capacity_transferred += heat_cap_transferred - moved_moles += delta - update_values() - - if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER) - var/new_self_heat_capacity = old_self_heat_capacity - heat_capacity_transferred - if(new_self_heat_capacity > MINIMUM_HEAT_CAPACITY) - if(border_multiplier) - temperature = (old_self_heat_capacity*temperature - heat_capacity_transferred*border_multiplier*temperature_archived)/new_self_heat_capacity - else - temperature = (old_self_heat_capacity*temperature - heat_capacity_transferred*border_multiplier*temperature_archived)/new_self_heat_capacity - - temperature_mimic(model, model.thermal_conductivity, border_multiplier) - - if((delta_temperature > MINIMUM_TEMPERATURE_TO_MOVE) || abs(moved_moles) > MINIMUM_MOLES_DELTA_TO_MOVE) - var/delta_pressure = temperature_archived*(total_moles() + moved_moles) - model.temperature*(model.oxygen+model.carbon_dioxide+model.nitrogen+model.toxins) - return delta_pressure*R_IDEAL_GAS_EQUATION/volume - else - return 0 - -/datum/gas_mixture/proc/check_both_then_temperature_share(datum/gas_mixture/sharer, conduction_coefficient) - var/delta_temperature = (temperature_archived - sharer.temperature_archived) - - var/self_heat_capacity = heat_capacity_archived() - var/sharer_heat_capacity = sharer.heat_capacity_archived() - - var/self_temperature_delta = 0 - var/sharer_temperature_delta = 0 - - if((sharer_heat_capacity > MINIMUM_HEAT_CAPACITY) && (self_heat_capacity > MINIMUM_HEAT_CAPACITY)) - var/heat = conduction_coefficient*delta_temperature* \ - (self_heat_capacity*sharer_heat_capacity/(self_heat_capacity+sharer_heat_capacity)) - - self_temperature_delta = -heat/(self_heat_capacity*group_multiplier) - sharer_temperature_delta = heat/(sharer_heat_capacity*sharer.group_multiplier) - else - return 1 - - if((abs(self_temperature_delta) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) \ - && (abs(self_temperature_delta) > MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND*temperature_archived)) - return 0 - - if((abs(sharer_temperature_delta) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) \ - && (abs(sharer_temperature_delta) > MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND*sharer.temperature_archived)) - return -1 - - temperature += self_temperature_delta - sharer.temperature += sharer_temperature_delta - - return 1 - //Logic integrated from: temperature_share(sharer, conduction_coefficient) for efficiency - -/datum/gas_mixture/proc/check_me_then_temperature_share(datum/gas_mixture/sharer, conduction_coefficient) - var/delta_temperature = (temperature_archived - sharer.temperature_archived) - - var/self_heat_capacity = heat_capacity_archived() - var/sharer_heat_capacity = sharer.heat_capacity_archived() - - var/self_temperature_delta = 0 - var/sharer_temperature_delta = 0 - - if((sharer_heat_capacity > MINIMUM_HEAT_CAPACITY) && (self_heat_capacity > MINIMUM_HEAT_CAPACITY)) - var/heat = conduction_coefficient*delta_temperature* \ - (self_heat_capacity*sharer_heat_capacity/(self_heat_capacity+sharer_heat_capacity)) - - self_temperature_delta = -heat/(self_heat_capacity*group_multiplier) - sharer_temperature_delta = heat/(sharer_heat_capacity*sharer.group_multiplier) - else - return 1 - - if((abs(self_temperature_delta) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) \ - && (abs(self_temperature_delta) > MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND*temperature_archived)) - return 0 - - temperature += self_temperature_delta - sharer.temperature += sharer_temperature_delta - - return 1 - //Logic integrated from: temperature_share(sharer, conduction_coefficient) for efficiency - -/datum/gas_mixture/proc/check_me_then_temperature_turf_share(turf/simulated/sharer, conduction_coefficient) - var/delta_temperature = (temperature_archived - sharer.temperature) - - var/self_temperature_delta = 0 - var/sharer_temperature_delta = 0 - - if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER) - var/self_heat_capacity = heat_capacity_archived() - - if((sharer.heat_capacity > MINIMUM_HEAT_CAPACITY) && (self_heat_capacity > MINIMUM_HEAT_CAPACITY)) - var/heat = conduction_coefficient*delta_temperature* \ - (self_heat_capacity*sharer.heat_capacity/(self_heat_capacity+sharer.heat_capacity)) - - self_temperature_delta = -heat/(self_heat_capacity*group_multiplier) - sharer_temperature_delta = heat/sharer.heat_capacity - else - return 1 - - if((abs(self_temperature_delta) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) \ - && (abs(self_temperature_delta) > MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND*temperature_archived)) - return 0 - - temperature += self_temperature_delta - sharer.temperature += sharer_temperature_delta - - return 1 - //Logic integrated from: temperature_turf_share(sharer, conduction_coefficient) for efficiency - -/datum/gas_mixture/proc/check_me_then_temperature_mimic(turf/model, conduction_coefficient) - var/delta_temperature = (temperature_archived - model.temperature) - var/self_temperature_delta = 0 - - if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER) - var/self_heat_capacity = heat_capacity_archived() - - if((model.heat_capacity > MINIMUM_HEAT_CAPACITY) && (self_heat_capacity > MINIMUM_HEAT_CAPACITY)) - var/heat = conduction_coefficient*delta_temperature* \ - (self_heat_capacity*model.heat_capacity/(self_heat_capacity+model.heat_capacity)) - - self_temperature_delta = -heat/(self_heat_capacity*group_multiplier) - - if((abs(self_temperature_delta) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) \ - && (abs(self_temperature_delta) > MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND*temperature_archived)) - return 0 - - temperature += self_temperature_delta - - return 1 - //Logic integrated from: temperature_mimic(model, conduction_coefficient) for efficiency - -/datum/gas_mixture/proc/temperature_share(datum/gas_mixture/sharer, conduction_coefficient) - var/delta_temperature = (temperature_archived - sharer.temperature_archived) - if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER) - var/self_heat_capacity = heat_capacity_archived() - var/sharer_heat_capacity = sharer.heat_capacity_archived() - if(!group_multiplier) - message_admins("Error! The gas mixture (ref \ref[src]) has no group multiplier!") - return - - if((sharer_heat_capacity > MINIMUM_HEAT_CAPACITY) && (self_heat_capacity > MINIMUM_HEAT_CAPACITY)) - var/heat = conduction_coefficient*delta_temperature* \ - (self_heat_capacity*sharer_heat_capacity/(self_heat_capacity+sharer_heat_capacity)) - - temperature -= heat/(self_heat_capacity*group_multiplier) - sharer.temperature += heat/(sharer_heat_capacity*sharer.group_multiplier) - -/datum/gas_mixture/proc/temperature_mimic(turf/model, conduction_coefficient, border_multiplier) - var/delta_temperature = (temperature - model.temperature) - if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER) - var/self_heat_capacity = heat_capacity()//_archived() - if(!group_multiplier) - message_admins("Error! The gas mixture (ref \ref[src]) has no group multiplier!") - return - - if((model.heat_capacity > MINIMUM_HEAT_CAPACITY) && (self_heat_capacity > MINIMUM_HEAT_CAPACITY)) - var/heat = conduction_coefficient*delta_temperature* \ - (self_heat_capacity*model.heat_capacity/(self_heat_capacity+model.heat_capacity)) - - if(border_multiplier) - temperature -= heat*border_multiplier/(self_heat_capacity*group_multiplier) - else - temperature -= heat/(self_heat_capacity*group_multiplier) - -/datum/gas_mixture/proc/temperature_turf_share(turf/simulated/sharer, conduction_coefficient) - var/delta_temperature = (temperature_archived - sharer.temperature) - if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER) - var/self_heat_capacity = heat_capacity() - - if((sharer.heat_capacity > MINIMUM_HEAT_CAPACITY) && (self_heat_capacity > MINIMUM_HEAT_CAPACITY)) - var/heat = conduction_coefficient*delta_temperature* \ - (self_heat_capacity*sharer.heat_capacity/(self_heat_capacity+sharer.heat_capacity)) - - temperature -= heat/(self_heat_capacity*group_multiplier) - sharer.temperature += heat/sharer.heat_capacity - -/datum/gas_mixture/proc/compare(datum/gas_mixture/sample) - //Purpose: Compares sample to self to see if within acceptable ranges that group processing may be enabled - //Called by: Airgroups trying to rebuild - //Inputs: Gas mix to compare - //Outputs: 1 if can rebuild, 0 if not. - if(!sample) return 0 - - - if((abs(oxygen-sample.oxygen) > MINIMUM_AIR_TO_SUSPEND) && \ - ((oxygen < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.oxygen) || (oxygen > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.oxygen))) - return 0 - if((abs(nitrogen-sample.nitrogen) > MINIMUM_AIR_TO_SUSPEND) && \ - ((nitrogen < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.nitrogen) || (nitrogen > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.nitrogen))) - return 0 - if((abs(carbon_dioxide-sample.carbon_dioxide) > MINIMUM_AIR_TO_SUSPEND) && \ - ((carbon_dioxide < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.carbon_dioxide) || (carbon_dioxide > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.carbon_dioxide))) - return 0 - if((abs(toxins-sample.toxins) > MINIMUM_AIR_TO_SUSPEND) && \ - ((toxins < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.toxins) || (toxins > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.toxins))) - 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))) - //world << "temp fail [temperature] & [sample.temperature]" - return 0 - var/check_moles - if(sample.trace_gases.len) - for(var/datum/gas/trace_gas in sample.trace_gases) - var/datum/gas/corresponding = locate(trace_gas.type) in trace_gases - if(corresponding) - check_moles = corresponding.moles - else - check_moles = 0 - - if((abs(trace_gas.moles - check_moles) > MINIMUM_AIR_TO_SUSPEND) && \ - ((check_moles < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*trace_gas.moles) || (check_moles > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*trace_gas.moles))) - return 0 - - if(trace_gases.len) - for(var/datum/gas/trace_gas in trace_gases) - var/datum/gas/corresponding = locate(trace_gas.type) in trace_gases - if(corresponding) - check_moles = corresponding.moles - else - check_moles = 0 - - if((abs(trace_gas.moles - check_moles) > MINIMUM_AIR_TO_SUSPEND) && \ - ((trace_gas.moles < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*check_moles) || (trace_gas.moles > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*check_moles))) - return 0 - - return 1 - -/datum/gas_mixture/proc/add(datum/gas_mixture/right_side) - oxygen += right_side.oxygen - carbon_dioxide += right_side.carbon_dioxide - nitrogen += right_side.nitrogen - toxins += right_side.toxins - - if(trace_gases.len || right_side.trace_gases.len) - for(var/datum/gas/trace_gas in right_side.trace_gases) - var/datum/gas/corresponding = locate(trace_gas.type) in trace_gases - if(!corresponding) - corresponding = new trace_gas.type() - trace_gases += corresponding - corresponding.moles += trace_gas.moles - - update_values() - return 1 - -/datum/gas_mixture/proc/subtract(datum/gas_mixture/right_side) - //Purpose: Subtracts right_side from air_mixture. Used to help turfs mingle - //Called by: Pipelines ending in a break (or something) - //Inputs: Gas mix to remove - //Outputs: 1 - - oxygen = max(oxygen - right_side.oxygen) - carbon_dioxide = max(carbon_dioxide - right_side.carbon_dioxide) - nitrogen = max(nitrogen - right_side.nitrogen) - toxins = max(toxins - right_side.toxins) - - if(trace_gases.len || right_side.trace_gases.len) - for(var/datum/gas/trace_gas in right_side.trace_gases) - var/datum/gas/corresponding = locate(trace_gas.type) in trace_gases - if(corresponding) - corresponding.moles = max(0, corresponding.moles - trace_gas.moles) - - update_values() - return 1 - -/datum/gas_mixture/proc/multiply(factor) - oxygen *= factor - carbon_dioxide *= factor - nitrogen *= factor - toxins *= factor - - if(trace_gases && trace_gases.len) - for(var/datum/gas/trace_gas in trace_gases) - trace_gas.moles *= factor - - update_values() - return 1 - -/datum/gas_mixture/proc/divide(factor) - oxygen /= factor - carbon_dioxide /= factor - nitrogen /= factor - toxins /= factor - - if(trace_gases && trace_gases.len) - for(var/datum/gas/trace_gas in trace_gases) - trace_gas.moles /= factor - - update_values() - return 1 diff --git a/code/ZAS/FEA_system.dm b/code/ZAS/FEA_system.dm deleted file mode 100644 index 5a386d59..00000000 --- a/code/ZAS/FEA_system.dm +++ /dev/null @@ -1,356 +0,0 @@ -/* -Overview: - The air_master global variable is the workhorse for the system. - -Why are you archiving data before modifying it? - The general concept with archiving data and having each tile keep track of when they were last updated is to keep everything symmetric - and totally independent of the order they are read in an update cycle. - This prevents abnormalities like air/fire spreading rapidly in one direction and super slowly in the other. - -Why not just archive everything and then calculate? - Efficiency. While a for-loop that goes through all tils and groups to archive their information before doing any calculations seems simple, it is - slightly less efficient than the archive-before-modify/read method. - -Why is there a cycle check for calculating data as well? - This ensures that every connection between group-tile, tile-tile, and group-group is only evaluated once per loop. - - - - -Important variables: - air_master.groups_to_rebuild (list) - A list of air groups that have had their geometry occluded and thus may need to be split in half. - A set of adjacent groups put in here will join together if validly connected. - This is done before air system calculations for a cycle. - air_master.tiles_to_update (list) - Turfs that are in this list have their border data updated before the next air calculations for a cycle. - Place turfs in this list rather than call the proc directly to prevent race conditions - - turf/simulated.archive() and datum/air_group.archive() - This stores all data for. - If you modify, make sure to update the archived_cycle to prevent race conditions and maintain symmetry - - atom/CanPass(atom/movable/mover, turf/target, height, air_group) - returns 1 for allow pass and 0 for deny pass - Turfs automatically call this for all objects/mobs in its turf. - This is called both as source.CanPass(target, height, air_group) - and target.CanPass(source, height, air_group) - - Cases for the parameters - 1. This is called with args (mover, location, height>0, air_group=0) for normal objects. - 2. This is called with args (null, location, height=0, air_group=0) for flowing air. - 3. This is called with args (null, location, height=?, air_group=1) for determining group boundaries. - - Cases 2 and 3 would be different for doors or other objects open and close fairly often. - (Case 3 would return 0 always while Case 2 would return 0 only when the door is open) - This prevents the necessity of re-evaluating group geometry every time a door opens/closes. - - -Important Procedures - air_master.process() - This first processes the air_master update/rebuild lists then processes all groups and tiles for air calculations - -*/ - -var/tick_multiplier = 2 - -atom/proc/CanPass(atom/movable/mover, turf/target, height=1.5, air_group = 0) - //Purpose: Determines if the object (or airflow) can pass this atom. - //Called by: Movement, airflow. - //Inputs: The moving atom (optional), target turf, "height" and air group - //Outputs: Boolean if can pass. - - return (!density || !height || air_group) - -/turf/CanPass(atom/movable/mover, turf/target, height=1.5,air_group=0) - if(!target) return 0 - - if(istype(mover)) // turf/Enter(...) will perform more advanced checks - return !density - - else // Now, doing more detailed checks for air movement and air group formation - if(target.blocks_air||blocks_air) - return 0 - - for(var/obj/obstacle in src) - if(!obstacle.CanPass(mover, target, height, air_group)) - return 0 - if(target != src) - for(var/obj/obstacle in target) - if(!obstacle.CanPass(mover, src, height, air_group)) - return 0 - - return 1 - - -var/datum/controller/air_system/air_master - -/datum/controller/air_system - //Geometry lists - var/list/turfs_with_connections = list() - var/list/active_hotspots = list() - - //Special functions lists - var/reconsidering_zones = FALSE - var/list/tiles_to_reconsider_zones = list() - var/list/tiles_to_reconsider_alternate - - //Geometry updates lists - var/updating_tiles = FALSE - var/list/tiles_to_update = list() - var/list/tiles_to_update_alternate - - var/checking_connections = FALSE - var/list/connections_to_check = list() - var/list/connections_to_check_alternate - - var/list/potential_intrazone_connections = list() - - //Zone lists - var/list/active_zones = list() - var/list/zones_needing_rebuilt = list() - var/list/zones = list() - - - var/current_cycle = 0 - var/update_delay = 5 //How long between check should it try to process atmos again. - var/failed_ticks = 0 //How many ticks have runtimed? - - var/tick_progress = 0 - - -/datum/controller/air_system/proc/Setup() - //Purpose: Call this at the start to setup air groups geometry - // (Warning: Very processor intensive but only must be done once per round) - //Called by: Gameticker/Master controller - //Inputs: None. - //Outputs: None. - - set background = 1 - world << "\red \b Processing Geometry..." - sleep(-1) - - var/start_time = world.timeofday - - var/simulated_turf_count = 0 - - for(var/turf/simulated/S in world) - simulated_turf_count++ - if(!S.zone && !S.blocks_air) - if(S.CanPass(null, S, 0, 0)) - new/zone(S) - - for(var/turf/simulated/S in world) - S.update_air_properties() - - world << {"Geometry initialized in [round(0.1*(world.timeofday-start_time),0.1)] seconds. -Total Simulated Turfs: [simulated_turf_count] -Total Zones: [zones.len] -Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_count]"} - -// spawn Start() - - -/datum/controller/air_system/proc/Start() - //Purpose: This is kicked off by the master controller, and controls the processing of all atmosphere. - //Called by: Master controller - //Inputs: None. - //Outputs: None. - - - set background = 1 - - while(1) - if(!air_processing_killed) - var/success = Tick() //Changed so that a runtime does not crash the ticker. - if(!success) //Runtimed. - failed_ticks++ - if(failed_ticks > 20) - world << "ERROR IN ATMOS TICKER. Killing air simulation!" - air_processing_killed = 1 - sleep(max(5,update_delay*tick_multiplier)) - - -/datum/controller/air_system/proc/Tick() - . = 1 //Set the default return value, for runtime detection. - - current_cycle++ - - //If there are tiles to update, do so. - tick_progress = "updating turf properties" - if(tiles_to_update.len) - updating_tiles = TRUE - - for(var/turf/simulated/T in tiles_to_update) - if(. && T && !T.update_air_properties()) - //If a runtime occured, make sure we can sense it. - . = 0 - - updating_tiles = FALSE - - if(.) - if(tiles_to_update_alternate) - tiles_to_update = tiles_to_update_alternate - tiles_to_update_alternate = null - else - tiles_to_update = list() - - else if(tiles_to_update_alternate) - tiles_to_update |= tiles_to_update_alternate - tiles_to_update_alternate = null - - //Rebuild zones. - if(.) - tick_progress = "rebuilding zones" - if(zones_needing_rebuilt.len) - for(var/zone/zone in zones_needing_rebuilt) - zone.Rebuild() - - zones_needing_rebuilt = list() - - //Check sanity on connection objects. - if(.) - tick_progress = "checking/creating connections" - if(connections_to_check.len) - checking_connections = TRUE - - for(var/connection/C in connections_to_check) - C.Cleanup() - - for(var/turf/simulated/turf_1 in potential_intrazone_connections) - for(var/turf/simulated/turf_2 in potential_intrazone_connections[turf_1]) - - if(!turf_1.zone || !turf_2.zone) - continue - - if(turf_1.zone == turf_2.zone) - continue - - var/should_skip = FALSE - if(turf_1 in air_master.turfs_with_connections) - - for(var/connection/C in turfs_with_connections[turf_1]) - if(C.B == turf_2 || C.A == turf_2) - should_skip = TRUE - break - if(should_skip) - continue - - new /connection(turf_1, turf_2) - - checking_connections = FALSE - - potential_intrazone_connections = list() - - if(connections_to_check_alternate) - connections_to_check = connections_to_check_alternate - connections_to_check_alternate = null - else - connections_to_check = list() - - //Process zones. - if(.) - tick_progress = "processing zones" - for(var/zone/Z in active_zones) - if(Z.last_update < current_cycle) - var/output = Z.process() - if(Z) - Z.last_update = current_cycle - if(. && Z && !output) - . = 0 - - //Ensure tiles still have zones. - if(.) - tick_progress = "reconsidering zones on turfs" - if(tiles_to_reconsider_zones.len) - reconsidering_zones = TRUE - - for(var/turf/simulated/T in tiles_to_reconsider_zones) - if(!T.zone) - new /zone(T) - - reconsidering_zones = FALSE - - if(tiles_to_reconsider_alternate) - tiles_to_reconsider_zones = tiles_to_reconsider_alternate - tiles_to_reconsider_alternate = null - else - tiles_to_reconsider_zones = list() - - //Process fires. - if(.) - tick_progress = "processing fire" - for(var/obj/fire/F in active_hotspots) - if(. && F && !F.process()) - . = 0 - - if(.) - tick_progress = "success" - - -/datum/controller/air_system/proc/AddTurfToUpdate(turf/simulated/outdated_turf) - var/list/tiles_to_check = list() - - if(istype(outdated_turf)) - tiles_to_check |= outdated_turf - - if(istype(outdated_turf, /turf)) - for(var/direction in cardinal) - var/turf/simulated/adjacent_turf = get_step(outdated_turf, direction) - if(istype(adjacent_turf)) - tiles_to_check |= adjacent_turf - - if(updating_tiles) - if(!tiles_to_update_alternate) - tiles_to_update_alternate = tiles_to_check - else - tiles_to_update_alternate |= tiles_to_check - else - tiles_to_update |= tiles_to_check - - -/datum/controller/air_system/proc/AddConnectionToCheck(connection/connection) - if(checking_connections) - if(istype(connection, /list)) - if(!connections_to_check_alternate) - connections_to_check_alternate = connection - - else if(!connections_to_check_alternate) - connections_to_check_alternate = list() - - connections_to_check_alternate |= connection - - else - connections_to_check |= connection - - -/datum/controller/air_system/proc/ReconsiderTileZone(var/turf/simulated/zoneless_turf) - if(zoneless_turf.zone) - return - - if(reconsidering_zones) - if(!tiles_to_reconsider_alternate) - tiles_to_reconsider_alternate = list() - - tiles_to_reconsider_alternate |= zoneless_turf - - else - tiles_to_reconsider_zones |= zoneless_turf - - -/datum/controller/air_system/proc/AddIntrazoneConnection(var/turf/simulated/A, var/turf/simulated/B) - if(!istype(A) || !istype(B)) - return - - if(A in potential_intrazone_connections) - if(B in potential_intrazone_connections[A]) - return - - if (B in potential_intrazone_connections) - if(A in potential_intrazone_connections[B]) - return - - potential_intrazone_connections[B] += A - - else - potential_intrazone_connections[B] = list(A) \ No newline at end of file diff --git a/code/ZAS/Fire.dm b/code/ZAS/Fire.dm index fbf2338e..56ba7196 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. @@ -50,7 +93,7 @@ turf/simulated/hotspot_expose(exposed_temperature, exposed_volume, soh) icon = 'icons/effects/fire.dmi' icon_state = "1" - + l_color = "#ED9200" layer = TURF_LAYER var/firelevel = 10000 //Calculated by gas_mixture.calculate_firelevel() @@ -58,38 +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 - - if(!S.zone) - del src - - 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.toxins < 0.1) - air_contents.toxins = 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 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" @@ -103,20 +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.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) - if(S.open_directions & direction) //Grab all valid bordering tiles + var/turf/simulated/enemy_tile = get_step(my_tile, direction) - var/turf/simulated/enemy_tile = get_step(S, direction) + if(istype(enemy_tile)) + 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(istype(enemy_tile)) - 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 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) @@ -124,23 +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) - - //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 ////////////////////////////////////////////////////////////////// + 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.volume) /obj/fire/New(newLoc,fl) ..() @@ -164,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 > PLASMA_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 > PLASMA_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 += toxins - - 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 @@ -201,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) - toxins -= min(toxins, (toxins * used_fuel_ratio * used_reactants_ratio ) * 3) - if(toxins < 0) - toxins = 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 @@ -241,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 && (toxins || fuel || liquid)) - if(liquid) - return 1 - if(toxins >= 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 && (toxins || fuel || liquid)) - if(liquid) - return 1 - if(QUANTIZE(toxins * 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 += toxins - - 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/Functions.dm b/code/ZAS/Functions.dm deleted file mode 100644 index 3037e13f..00000000 --- a/code/ZAS/Functions.dm +++ /dev/null @@ -1,172 +0,0 @@ -//Global Functions -//Contents: FloodFill, ZMerge, ZConnect - -//Floods outward from an initial turf to fill everywhere it's zone would reach. -proc/FloodFill(turf/simulated/start) - - if(!istype(start)) - return list() - - //The list of tiles waiting to be evaulated. - var/list/open = list(start) - //The list of tiles which have been evaulated. - var/list/closed = list() - - //Loop through the turfs in the open list in order to find which adjacent turfs should be added to the zone. - while(open.len) - var/turf/simulated/T = pick(open) - - //sanity! - if(!istype(T)) - open -= T - continue - - //Check all cardinal directions - for(var/d in cardinal) - var/turf/simulated/O = get_step(T,d) - - //Ensure the turf is of proper type, that it is not in either list, and that air can reach it. - if(istype(O) && !(O in open) && !(O in closed) && O.ZCanPass(T)) - - //Handle connections from a tile with a door. - if(T.HasDoor()) - //If they both have doors, then they are not able to connect period. - if(O.HasDoor()) - continue - - //Connect first to north and west - if(d == NORTH || d == WEST) - open += O - - //If that fails, and north/west cannot be connected to, see if west or south can be connected instead. - else - var/turf/simulated/W = get_step(O, WEST) - var/turf/simulated/N = get_step(O, NORTH) - - if( !O.ZCanPass(N) && !O.ZCanPass(W) ) - //If it cannot connect either to the north or west, connect it! - open += O - - //If no doors are involved, add it immediately. - else if(!O.HasDoor()) - open += O - - //Handle connecting to a tile with a door. - else - if(d == SOUTH || d == EAST) - //doors prefer connecting to zones to the north or west - closed += O - - else - //see if we need to force an attempted connection - //(there are no potentially viable zones to the north/west of the door) - var/turf/simulated/W = get_step(O, WEST) - var/turf/simulated/N = get_step(O, NORTH) - - if( !O.ZCanPass(N) && !O.ZCanPass(W) ) - //If it cannot connect either to the north or west, connect it! - closed += O - - //This tile is now evaluated, and can be moved to the list of evaluated tiles. - open -= T - closed += T - - return closed - - -//Procedure to merge two zones together. -proc/ZMerge(zone/A,zone/B) - - //Sanity~ - if(!istype(A) || !istype(B)) - return - - var/new_contents = A.contents + B.contents - - //Set all the zone vars. - for(var/turf/simulated/T in B.contents) - T.zone = A - - if(istype(A.air) && istype(B.air)) - //Merges two zones so that they are one. - var/a_size = A.air.group_multiplier - var/b_size = B.air.group_multiplier - var/c_size = a_size + b_size - - //Set air multipliers to one so air represents gas per tile. - A.air.group_multiplier = 1 - B.air.group_multiplier = 1 - - //Remove some air proportional to the size of this zone. - A.air.remove_ratio(a_size/c_size) - B.air.remove_ratio(b_size/c_size) - - //Merge the gases and set the multiplier to the sum of the old ones. - A.air.merge(B.air) - A.air.group_multiplier = c_size - - //I hate when the air datum somehow disappears. - // Try to make it sorta work anyways. Fakit - else if(istype(B.air)) - A.air = B.air - A.air.group_multiplier = A.contents.len - - else if(istype(A.air)) - A.air.group_multiplier = A.contents.len - - //Doublefakit. - else - A.air = new - - //Check for connections to merge into the new zone. - for(var/connection/C in B.connections) - //The Cleanup proc will delete the connection if the zones are the same. - // It will also set the zone variables correctly. - C.Cleanup() - - //Add space tiles. - if(A.unsimulated_tiles && B.unsimulated_tiles) - A.unsimulated_tiles |= B.unsimulated_tiles - else if (B.unsimulated_tiles) - A.unsimulated_tiles = B.unsimulated_tiles - - //Add contents. - A.contents = new_contents - - //Remove the "B" zone, finally. - B.SoftDelete() - - -//Connects two zones by forming a connection object representing turfs A and B. -proc/ZConnect(turf/simulated/A,turf/simulated/B) - - //Make sure that if it's space, it gets added to unsimulated_tiles instead. - if(!istype(B)) - if(A.zone) - A.zone.AddTurf(B) - return - if(!istype(A)) - if(B.zone) - B.zone.AddTurf(A) - return - - if(!istype(A) || !istype(B)) - return - - //Make some preliminary checks to see if the connection is valid. - if(!A.zone || !B.zone) return - if(A.zone == B.zone) - air_master.AddIntrazoneConnection(A,B) - return - - if(A.CanPass(null, B, 1.5, 1) && A.zone.air.compare(B.zone.air)) - return ZMerge(A.zone,B.zone) - - //Ensure the connection isn't already made. - if(A in air_master.turfs_with_connections) - for(var/connection/C in air_master.turfs_with_connections[A]) - if(C.B == B || C.A == B) - return - - //Make the connection. - new /connection(A,B) diff --git a/code/ZAS/Gas.dm b/code/ZAS/Gas.dm new file mode 100644 index 00000000..b4a562eb --- /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/Plasma.dm b/code/ZAS/Plasma.dm index 716ea216..7db835d1 100644 --- a/code/ZAS/Plasma.dm +++ b/code/ZAS/Plasma.dm @@ -118,7 +118,7 @@ obj/var/contaminated = 0 /mob/living/carbon/human/proc/burn_eyes() //The proc that handles eye burning. if(prob(20)) src << "\red Your eyes burn!" - var/datum/organ/internal/eyes/E = internal_organs["eyes"] + var/datum/organ/internal/eyes/E = internal_organs_by_name["eyes"] E.damage += 2.5 eye_blurry = min(eye_blurry+1.5,50) if (prob(max(0,E.damage - 15) + 1) &&!eye_blind) @@ -142,6 +142,7 @@ obj/var/contaminated = 0 if(wear_suit.flags & PLASMAGUARD) return 1 else if(wear_suit.flags_inv & HIDEJUMPSUIT) return 1 + //should check HIDETAIL as well, but for the moment tails are not a part that can be damaged separately return 0 /mob/living/carbon/human/proc/suit_contamination() @@ -153,11 +154,12 @@ obj/var/contaminated = 0 turf/Entered(obj/item/I) . = ..() - //Items that are in plasma, but not on a mob, can still be contaminated. - if(istype(I) && vsc.plc.CLOTH_CONTAMINATION) + //Items that are in phoron, but not on a mob, can still be contaminated. + if(istype(I) && vsc.plc.CLOTH_CONTAMINATION && I.can_contaminate()) var/datum/gas_mixture/env = return_air(1) if(!env) return - if(env.toxins > MOLES_PLASMA_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 e989d654..c9a68dab 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 && graphic_add.len) + overlays += graphic_add + if(graphic_remove && graphic_remove.len) + overlays -= graphic_remove /turf/proc/update_air_properties() var/block = c_airblock(src) @@ -26,6 +23,10 @@ #endif var/turf/unsim = get_step(src, d) + + if(!unsim) + continue + block = unsim.c_airblock(src) if(block & AIR_BLOCKED) @@ -75,6 +76,10 @@ #endif var/turf/unsim = get_step(src, d) + + if(!unsim) //edge of map + continue + var/block = unsim.c_airblock(src) if(block & AIR_BLOCKED) @@ -108,6 +113,8 @@ if(istype(unsim, /turf/simulated)) var/turf/simulated/sim = unsim + sim.open_directions |= reverse_dir[d] + if(air_master.has_valid_zone(sim)) //Might have assigned a zone, since this happens for each direction. @@ -175,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.toxins = toxins - + GM.adjust_multi("oxygen", oxygen, "carbon_dioxide", carbon_dioxide, "nitrogen", nitrogen, "toxins", toxins) GM.temperature = temperature - GM.update_values() return GM @@ -194,10 +199,10 @@ var/sum = oxygen + carbon_dioxide + nitrogen + toxins if(sum>0) - GM.oxygen = (oxygen/sum)*amount - GM.carbon_dioxide = (carbon_dioxide/sum)*amount - GM.nitrogen = (nitrogen/sum)*amount - GM.toxins = (toxins/sum)*amount + GM.gas["oxygen"] = (oxygen/sum)*amount + GM.gas["carbon_dioxide"] = (carbon_dioxide/sum)*amount + GM.gas["nitrogen"] = (nitrogen/sum)*amount + GM.gas["toxins"] = (toxins/sum)*amount GM.temperature = temperature GM.update_values() @@ -208,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) @@ -230,11 +245,11 @@ /turf/proc/make_air() air = new/datum/gas_mixture air.temperature = temperature - air.adjust(oxygen, carbon_dioxide, nitrogen, toxins) + air.adjust_multi("oxygen", oxygen, "carbon_dioxide", carbon_dioxide, "nitrogen", nitrogen, "toxins", toxins) 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/Variable Settings.dm b/code/ZAS/Variable Settings.dm index 92841a02..8aded57b 100644 --- a/code/ZAS/Variable Settings.dm +++ b/code/ZAS/Variable Settings.dm @@ -290,6 +290,35 @@ var/global/vs_control/vsc = new airflow_mob_slowdown = 3 connection_insulation = 0 + if("ZAS/Phoron - Initial") + fire_consuption_rate = initial(fire_consuption_rate) + fire_firelevel_multiplier = initial(fire_firelevel_multiplier) + fire_fuel_energy_release = initial(fire_fuel_energy_release) + IgnitionLevel = initial(IgnitionLevel) + airflow_lightest_pressure = initial(airflow_lightest_pressure) + airflow_light_pressure = initial(airflow_light_pressure) + airflow_medium_pressure = initial(airflow_medium_pressure) + airflow_heavy_pressure = initial(airflow_heavy_pressure) + airflow_dense_pressure = initial(airflow_dense_pressure) + airflow_stun_pressure = initial(airflow_stun_pressure) + airflow_stun_cooldown = initial(airflow_stun_cooldown) + airflow_stun = initial(airflow_stun) + airflow_damage = initial(airflow_damage) + airflow_speed_decay = initial(airflow_speed_decay) + airflow_delay = initial(airflow_delay) + airflow_mob_slowdown = initial(airflow_mob_slowdown) + connection_insulation = initial(connection_insulation) + connection_temperature_delta = initial(connection_temperature_delta) + + plc.PLASMA_DMG = initial(plc.PLASMA_DMG) + plc.CLOTH_CONTAMINATION = initial(plc.CLOTH_CONTAMINATION) + plc.PLASMAGUARD_ONLY = initial(plc.PLASMAGUARD_ONLY) + plc.GENETIC_CORRUPTION = initial(plc.GENETIC_CORRUPTION) + plc.SKIN_BURNS = initial(plc.SKIN_BURNS) + plc.EYE_BURNS = initial(plc.EYE_BURNS) + plc.CONTAMINATION_LOSS = initial(plc.CONTAMINATION_LOSS) + plc.PLASMA_HALLUCINATION = initial(plc.PLASMA_HALLUCINATION) + plc.N2O_HALLUCINATION = initial(plc.N2O_HALLUCINATION) world << "\blue [key_name(user)] changed the global plasma/ZAS settings to \"[def]\"" diff --git a/code/ZAS/ZAS_Turfs.dm b/code/ZAS/ZAS_Turfs.dm deleted file mode 100644 index 43ea2887..00000000 --- a/code/ZAS/ZAS_Turfs.dm +++ /dev/null @@ -1,344 +0,0 @@ -/atom/var/pressure_resistance = ONE_ATMOSPHERE - -/turf/var/zone/zone - -/turf/assume_air(datum/gas_mixture/giver) //use this for machines to adjust air - del(giver) - 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.toxins = toxins - - GM.temperature = temperature - GM.update_values() - - return GM - -/turf/remove_air(amount as num) - var/datum/gas_mixture/GM = new - - var/sum = oxygen + carbon_dioxide + nitrogen + toxins - if(sum>0) - GM.oxygen = (oxygen/sum)*amount - GM.carbon_dioxide = (carbon_dioxide/sum)*amount - GM.nitrogen = (nitrogen/sum)*amount - GM.toxins = (toxins/sum)*amount - - GM.temperature = temperature - GM.update_values() - - return GM - -/turf/simulated/var/current_graphic = null - -/turf/simulated/var/tmp/datum/gas_mixture/air - -/turf/simulated/var/tmp/air_check_directions = 0 //Do not modify this, just add turf to air_master.tiles_to_update - -/turf/simulated/var/tmp/unsim_check_directions = 0 //See above. - -/turf/simulated/var/tmp/obj/fire/active_hotspot - -/turf/simulated/proc/update_visuals() - overlays = null - - var/siding_icon_state = return_siding_icon_state() - if(siding_icon_state) - overlays += image('icons/turf/floors.dmi',siding_icon_state) - var/datum/gas_mixture/model = return_air() - switch(model.graphic) - if(1) - overlays.Add(plmaster) //TODO: Make invisible plasma an option - if(2) - overlays.Add(slmaster) - -/turf/simulated/New() - if(!blocks_air) - air = new - - air.oxygen = oxygen - air.carbon_dioxide = carbon_dioxide - air.nitrogen = nitrogen - air.toxins = toxins - - air.temperature = temperature - air.update_values() - - if(air_master) - air_master.tiles_to_update.Add(src) - - else - if(air_master) - for(var/direction in cardinal) - var/turf/simulated/floor/target = get_step(src,direction) - if(istype(target)) - air_master.tiles_to_update |= target - - . = ..() - -/turf/simulated/Del() - if(active_hotspot) - del(active_hotspot) - if(blocks_air) - for(var/direction in list(NORTH, SOUTH, EAST, WEST)) - var/turf/simulated/tile = get_step(src,direction) - if(istype(tile) && !tile.blocks_air) - air_master.tiles_to_update.Add(tile) - ..() - -/turf/simulated/assume_air(datum/gas_mixture/giver) - if(!giver) return 0 - if(zone) - zone.assume_air(giver) - return 1 - else - return ..() - -/turf/simulated/return_air() - if(zone) - return zone.air - else if(air) - return air - - else - return ..() - -/turf/simulated/remove_air(amount as num) - if(zone) - return zone.remove_air(amount) - - else if(air) - var/datum/gas_mixture/removed = null - removed = air.remove(amount) - - if(air.check_tile_graphic()) - update_visuals(air) - return removed - - else - return ..() - -/turf/simulated/proc/update_air_properties() - var/air_directions_archived = air_check_directions - air_check_directions = 0 - - var/unsim_directions_archived = unsim_check_directions - unsim_check_directions = 0 - - for(var/direction in cardinal) - var/turf/check_turf = get_step(src, direction) - if(ZAirPass(check_turf)) - if(istype(check_turf, /turf/simulated)) - air_check_directions |= direction - else if(istype(check_turf, /turf/space) || istype(check_turf, /turf/unsimulated)) - unsim_check_directions |= direction - - if(!zone && !blocks_air) //No zone, but not a wall. - for(var/direction in DoorDirections) //Check door directions first. - if(air_check_directions & direction) - var/turf/simulated/T = get_step(src, direction) - if(!istype(T)) - continue - if(T.zone) - T.zone.AddTurf(src) - break - if(!zone) //Still no zone - for(var/direction in CounterDoorDirections) //Check the others second. - if(air_check_directions & direction) - var/turf/simulated/T = get_step(src,direction) - if(!istype(T)) - continue - if(T.zone) - T.zone.AddTurf(src) - break - if(!zone) //No zone found, new zone! - new/zone(src) - if(!zone) //Still no zone, the floodfill determined it is not part of a larger zone. Force a zone on it. - new/zone(list(src)) - - //Check pass sanity of the connections. - if(src in air_master.turfs_with_connections) - air_master.AddConnectionToCheck(air_master.turfs_with_connections[src]) - - if(zone && !air_master.zones_needing_rebuilt.Find(zone)) - for(var/direction in cardinal) - var/turf/T = get_step(src,direction) - if(!istype(T)) - continue - - //I can connect to air in this direction - if(air_check_directions & direction || unsim_check_directions & direction) - - //If either block air, we must look to see if the adjacent turfs need rebuilt. - if(!CanPass(null, T, 0, 0)) - - //Target blocks air - if(!T.CanPass(null, T, 0, 0)) - var/turf/NT = get_step(T, direction) - - //If that turf is in my zone still, rebuild. - if(istype(NT,/turf/simulated) && NT in zone.contents) - air_master.zones_needing_rebuilt.Add(zone) - - //If that is an unsimulated tile in my zone, see if we need to rebuild or just remove. - else if(istype(NT) && NT in zone.unsimulated_tiles) - var/consider_rebuild = 0 - for(var/d in cardinal) - var/turf/UT = get_step(NT,d) - if(istype(UT, /turf/simulated) && UT.zone == zone && UT.CanPass(null, NT, 0, 0)) //If we find a neighboring tile that is in the same zone, check if we need to rebuild - consider_rebuild = 1 - break - if(consider_rebuild) - air_master.zones_needing_rebuilt.Add(zone) //Gotta check if we need to rebuild, dammit - else - zone.RemoveTurf(NT) //Not adjacent to anything, and unsimulated. Goodbye~ - - //To make a closed connection through closed door. - ZConnect(T, src) - - //If I block air. - else if(T.zone && !air_master.zones_needing_rebuilt.Find(T.zone)) - var/turf/NT = get_step(src, reverse_direction(direction)) - - //If I am splitting a zone, rebuild. - if(istype(NT,/turf/simulated) && (NT in T.zone.contents || (NT.zone && T in NT.zone.contents))) - air_master.zones_needing_rebuilt.Add(T.zone) - - //If NT is unsimulated, parse if I should remove it or rebuild. - else if(istype(NT) && NT in T.zone.unsimulated_tiles) - var/consider_rebuild = 0 - for(var/d in cardinal) - var/turf/UT = get_step(NT,d) - if(istype(UT, /turf/simulated) && UT.zone == T.zone && UT.CanPass(null, NT, 0, 0)) //If we find a neighboring tile that is in the same zone, check if we need to rebuild - consider_rebuild = 1 - break - - //Needs rebuilt. - if(consider_rebuild) - air_master.zones_needing_rebuilt.Add(T.zone) - - //Not adjacent to anything, and unsimulated. Goodbye~ - else - T.zone.RemoveTurf(NT) - - else - //Produce connection through open door. - ZConnect(src,T) - - //Something like a wall was built, changing the geometry. - else if(air_directions_archived & direction || unsim_directions_archived & direction) - var/turf/NT = get_step(T, direction) - - //If the tile is in our own zone, and we cannot connect to it, better rebuild. - if(istype(NT,/turf/simulated) && NT in zone.contents) - air_master.zones_needing_rebuilt.Add(zone) - - //Parse if we need to remove the tile, or rebuild the zone. - else if(istype(NT) && NT in zone.unsimulated_tiles) - var/consider_rebuild = 0 - - //Loop through all neighboring turfs to see if we should remove the turf or just rebuild. - for(var/d in cardinal) - var/turf/UT = get_step(NT,d) - - //If we find a neighboring tile that is in the same zone, rebuild - if(istype(UT, /turf/simulated) && UT.zone == zone && UT.CanPass(null, NT, 0, 0)) - consider_rebuild = 1 - break - - //The unsimulated turf is adjacent to another one of our zone's turfs, - // better rebuild to be sure we didn't get cut in twain - if(consider_rebuild) - air_master.zones_needing_rebuilt.Add(NT.zone) - - //Not adjacent to anything, and unsimulated. Goodbye~ - else - zone.RemoveTurf(NT) - - return 1 - -/turf/proc/HasDoor(turf/O) - //Checks for the presence of doors, used for zone spreading and connection. - //A positive numerical argument checks only for closed doors. - //Another turf as an argument checks for windoors between here and there. - for(var/obj/machinery/door/D in src) - if(isnum(O) && O) - if(!D.density) continue - if(istype(D,/obj/machinery/door/window)) - if(!istype(O)) - continue - if(D.dir == get_dir(D,O)) - return 1 - else - return 1 - -/turf/proc/ZCanPass(turf/simulated/T, var/include_space = 0) - //Fairly standard pass checks for turfs, objects and directional windows. Also stops at the edge of space. - if(!istype(T)) - return 0 - - if(!istype(T) && !include_space) - return 0 - else - if(T.blocks_air||blocks_air) - return 0 - - for(var/obj/obstacle in src) - if(istype(obstacle, /obj/machinery/door) && !(obstacle:air_properties_vary_with_direction)) - continue - if(!obstacle.CanPass(null, T, 1.5, 1)) - return 0 - - for(var/obj/obstacle in T) - if(istype(obstacle, /obj/machinery/door) && !(obstacle:air_properties_vary_with_direction)) - continue - if(!obstacle.CanPass(null, src, 1.5, 1)) - return 0 - - return 1 - -/turf/proc/ZAirPass(turf/T) - //Fairly standard pass checks for turfs, objects and directional windows. - if(!istype(T)) - return 0 - - if(T.blocks_air||blocks_air) - return 0 - - for(var/obj/obstacle in src) - if(istype(obstacle, /obj/machinery/door) && !(obstacle:air_properties_vary_with_direction)) - continue - if(!obstacle.CanPass(null, T, 0, 0)) - return 0 - - for(var/obj/obstacle in T) - if(istype(obstacle, /obj/machinery/door) && !(obstacle:air_properties_vary_with_direction)) - continue - if(!obstacle.CanPass(null, src, 0, 0)) - return 0 - - return 1 - -/*UNUSED -/turf/proc/check_connections() - //Checks for new connections that can be made. - for(var/d in cardinal) - var/turf/simulated/T = get_step(src,d) - if(istype(T) && ( !T.zone || !T.CanPass(0,src,0,0) ) ) - continue - if(T.zone != zone) - ZConnect(src,T) - -/turf/proc/check_for_space() - //Checks for space around the turf. - for(var/d in cardinal) - var/turf/T = get_step(src,d) - if(istype(T,/turf/space) && T.CanPass(0,src,0,0)) - zone.AddSpace(T) - */ \ No newline at end of file diff --git a/code/ZAS/ZAS_Zones.dm b/code/ZAS/ZAS_Zones.dm deleted file mode 100644 index c7387254..00000000 --- a/code/ZAS/ZAS_Zones.dm +++ /dev/null @@ -1,873 +0,0 @@ -var/list/DoorDirections = list(NORTH,WEST) //Which directions doors turfs can connect to zones -var/list/CounterDoorDirections = list(SOUTH,EAST) //Which directions doors turfs can connect to zones - -/zone - var/dbg_output = 0 //Enables debug output. - - var/datum/gas_mixture/air //The air contents of the zone. - var/datum/gas_mixture/archived_air - - var/list/contents //All the tiles that are contained in this zone. - var/list/unsimulated_tiles // Any space tiles in this list will cause air to flow out. - - var/datum/gas_mixture/air_unsim //Overall average of the air in connected unsimualted tiles. - var/unsim_air_needs_update = 0 //Set to 1 on geometry changes, marks air_unsim as needing update. - - var/list/connections //connection objects which refer to connections with other zones, e.g. through a door. - var/list/direct_connections //connections which directly connect two zones. - - var/list/connected_zones //Parallels connections, but lists zones to which this one is connected and the number - //of points they're connected at. - var/list/closed_connection_zones //Same as connected_zones, but for zones where the door or whatever is closed. - - var/last_update = 0 - var/last_rebuilt = 0 - var/status = ZONE_ACTIVE - var/interactions_with_neighbors = 0 - var/interactions_with_unsim = 0 - var/progress = "nothing" - -//CREATION AND DELETION -/zone/New(turf/start) - . = ..() - //Get the turfs that are part of the zone using a floodfill method - if(istype(start,/list)) - contents = start - else - contents = FloodFill(start) - - //Change all the zone vars of the turfs, check for space to be added to unsimulated_tiles. - for(var/turf/T in contents) - if(T.zone && T.zone != src) - T.zone.RemoveTurf(T) - T.zone = src - if(!istype(T,/turf/simulated)) - AddTurf(T) - - //Generate the gas_mixture for use in txhis zone by using the average of the gases - //defined at startup. - //Changed to try and find the source of the error. - air = new - air.group_multiplier = contents.len - for(var/turf/simulated/T in contents) - if(!T.air) - continue - air.oxygen += T.air.oxygen / air.group_multiplier - air.nitrogen += T.air.nitrogen / air.group_multiplier - air.carbon_dioxide += T.air.carbon_dioxide / air.group_multiplier - air.toxins += T.air.toxins / air.group_multiplier - air.temperature += T.air.temperature / air.group_multiplier - for(var/datum/gas/trace in T.air.trace_gases) - var/datum/gas/corresponding_gas = locate(trace.type) in air.trace_gases - if(!corresponding_gas) - corresponding_gas = new trace.type() - air.trace_gases.Add(corresponding_gas) - corresponding_gas.moles += trace.moles - air.update_values() - - //Add this zone to the global list. - if(air_master) - air_master.zones.Add(src) - air_master.active_zones.Add(src) - - -//DO NOT USE. Use the SoftDelete proc. -/zone/Del() - //Ensuring the zone list doesn't get clogged with null values. - for(var/turf/simulated/T in contents) - RemoveTurf(T) - air_master.ReconsiderTileZone(T) - - if(air_master) - air_master.AddConnectionToCheck(connections) - - air = null - - . = ..() - - -//Handles deletion via garbage collection. -/zone/proc/SoftDelete() - air = null - - if(air_master) - air_master.zones.Remove(src) - air_master.active_zones.Remove(src) - air_master.zones_needing_rebuilt.Remove(src) - air_master.AddConnectionToCheck(connections) - - connections = null - for(var/connection/C in direct_connections) - if(C.A.zone == src) - C.A.zone = null - if(C.B.zone == src) - C.B.zone = null - if(C.zone_A == src) - C.zone_A = null - if(C.zone_B == src) - C.zone_B = null - direct_connections = null - - //Ensuring the zone list doesn't get clogged with null values. - for(var/turf/simulated/T in contents) - RemoveTurf(T) - air_master.ReconsiderTileZone(T) - - contents.Cut() - - //Removing zone connections and scheduling connection cleanup - for(var/zone/Z in connected_zones) - Z.connected_zones.Remove(src) - if(!Z.connected_zones.len) - Z.connected_zones = null - - if(Z.closed_connection_zones) - Z.closed_connection_zones.Remove(src) - if(!Z.closed_connection_zones.len) - Z.closed_connection_zones = null - - connected_zones = null - closed_connection_zones = null - - return 1 - - -//ZONE MANAGEMENT FUNCTIONS -/zone/proc/AddTurf(turf/T) - //Adds the turf to contents, increases the size of the zone, and sets the zone var. - if(istype(T, /turf/simulated)) - if(T in contents) - return - if(T.zone) - T.zone.RemoveTurf(T) - contents += T - if(air) - air.group_multiplier++ - - T.zone = src - - else - if(!unsimulated_tiles) - unsimulated_tiles = list() - else if(T in unsimulated_tiles) - return - unsimulated_tiles += T - contents -= T - - unsim_air_needs_update = 1 - -/zone/proc/RemoveTurf(turf/T) - //Same, but in reverse. - if(istype(T, /turf/simulated)) - if(!(T in contents)) - return - contents -= T - if(air) - air.group_multiplier-- - - if(T.zone == src) - T.zone = null - - if(!contents.len) - SoftDelete() - - else if(unsimulated_tiles) - unsimulated_tiles -= T - if(!unsimulated_tiles.len) - unsimulated_tiles = null - - unsim_air_needs_update = 1 - -//Updates the air_unsim var -/zone/proc/UpdateUnsimAvg() - if(!unsimulated_tiles || !unsimulated_tiles.len) //if we don't have any unsimulated tiles, we can't do much. - return - - if(!unsim_air_needs_update && air_unsim) //if air_unsim doesn't exist, we need to create it even if we don't need an update. - return - - //Tempfix. - if(!air) - return - - unsim_air_needs_update = 0 - - if(!air_unsim) - air_unsim = new /datum/gas_mixture - - air_unsim.oxygen = 0 - air_unsim.nitrogen = 0 - air_unsim.carbon_dioxide = 0 - air_unsim.toxins = 0 - air_unsim.temperature = 0 - - var/correction_ratio = max(1, max(max(1, air.group_multiplier) + 3, 1) + unsimulated_tiles.len) / unsimulated_tiles.len - - for(var/turf/T in unsimulated_tiles) - if(!istype(T, /turf/simulated)) - air_unsim.oxygen += T.oxygen - air_unsim.carbon_dioxide += T.carbon_dioxide - air_unsim.nitrogen += T.nitrogen - air_unsim.toxins += T.toxins - air_unsim.temperature += T.temperature/unsimulated_tiles.len - - //These values require adjustment in order to properly represent a room of the specified size. - air_unsim.oxygen *= correction_ratio - air_unsim.carbon_dioxide *= correction_ratio - air_unsim.nitrogen *= correction_ratio - air_unsim.toxins *= correction_ratio - - air_unsim.group_multiplier = unsimulated_tiles.len - - air_unsim.update_values() - return - - ////////////// - //PROCESSING// -////////////// - -#define QUANTIZE(variable) (round(variable,0.0001)) - -/zone/proc/process() - . = 1 - - progress = "problem with: SoftDelete()" - - //Deletes zone if empty. - if(!contents.len) - return SoftDelete() - - progress = "problem with: Rebuild()" - - if(!contents.len) //If we got soft deleted. - return - - progress = "problem with: air regeneration" - - //Sometimes explosions will cause the air to be deleted for some reason. - if(!air) - air = new() - air.oxygen = MOLES_O2STANDARD - air.nitrogen = MOLES_N2STANDARD - air.temperature = T0C - air.total_moles() - world.log << "Air object lost in zone. Regenerating." - - - progress = "problem with: ShareSpace()" - - if(unsim_air_needs_update) - unsim_air_needs_update = 0 - UpdateUnsimAvg() - - if(unsimulated_tiles) - if(locate(/turf/simulated) in unsimulated_tiles) - for(var/turf/simulated/T in unsimulated_tiles) - unsimulated_tiles -= T - - if(unsimulated_tiles.len) - var/moved_air = ShareSpace(air, air_unsim) - - if(!air.compare(air_unsim)) - interactions_with_unsim++ - - if(moved_air > vsc.airflow_lightest_pressure) - AirflowSpace(src) - else - unsimulated_tiles = null - - //Check the graphic. - progress = "problem with: modifying turf graphics" - - air.graphic = 0 - if(air.toxins > MOLES_PLASMA_VISIBLE) - air.graphic = 1 - else if(air.trace_gases.len) - var/datum/gas/sleeping_agent = locate(/datum/gas/sleeping_agent) in air.trace_gases - if(sleeping_agent && (sleeping_agent.moles > 1)) - air.graphic = 2 - - progress = "problem with an inbuilt byond function: some conditional checks" - - //Only run through the individual turfs if there's reason to. - if(air.graphic != air.graphic_archived || air.temperature > PLASMA_FLASHPOINT) - - progress = "problem with: turf/simulated/update_visuals()" - - for(var/turf/simulated/S in contents) - //Update overlays. - if(air.graphic != air.graphic_archived) - if(S.HasDoor(1)) - S.update_visuals() - else - S.update_visuals(air) - - progress = "problem with: item or turf temperature_expose()" - - //Expose stuff to extreme heat. - if(air.temperature > PLASMA_FLASHPOINT) - for(var/atom/movable/item in S) - item.temperature_expose(air, air.temperature, CELL_VOLUME) - S.hotspot_expose(air.temperature, CELL_VOLUME) - - progress = "problem with: calculating air graphic" - - //Archive graphic so we can know if it's different. - air.graphic_archived = air.graphic - - progress = "problem with: calculating air temp" - - //Ensure temperature does not reach absolute zero. - air.temperature = max(TCMB,air.temperature) - - progress = "problem with an inbuilt byond function: length(connections)" - - //Handle connections to other zones. - if(length(connections)) - - progress = "problem with: ZMerge(), a couple of misc procs" - - if(length(direct_connections)) - for(var/connection/C in direct_connections) - - //Do merging if conditions are met. Specifically, if there's a non-door connection - //to somewhere with space, the zones are merged regardless of equilibrium, to speed - //up spacing in areas with double-plated windows. - if(C.A.zone && C.B.zone) - if(C.A.zone.air.compare(C.B.zone.air) || unsimulated_tiles) - ZMerge(C.A.zone,C.B.zone) - - progress = "problem with: ShareRatio(), Airflow(), a couple of misc procs" - - //Share some - for(var/zone/Z in connected_zones) - //If that zone has already processed, skip it. - if(Z.last_update > last_update) - continue - - //Handle adjacent zones that are sleeping - if(Z.status == ZONE_SLEEPING) - if(air.compare(Z.air)) - continue - - else - Z.SetStatus(ZONE_ACTIVE) - - if(air && Z.air) - //Ensure we're not doing pointless calculations on equilibrium zones. - if(!air.compare(Z.air)) - if(abs(Z.air.return_pressure() - air.return_pressure()) > vsc.airflow_lightest_pressure) - Airflow(src,Z) - var/unsimulated_boost = 0 - if(unsimulated_tiles) - unsimulated_boost += unsimulated_tiles.len - if(Z.unsimulated_tiles) - unsimulated_boost += Z.unsimulated_tiles.len - unsimulated_boost = max(0, min(3, unsimulated_boost)) - ShareRatio( air , Z.air , connected_zones[Z] + unsimulated_boost) - - Z.interactions_with_neighbors++ - interactions_with_neighbors++ - - if(!vsc.connection_insulation) - for(var/zone/Z in closed_connection_zones) - //If that zone has already processed, skip it. - if(Z.last_update > last_update || !Z.air) - continue - - var/handle_temperature = abs(air.temperature - Z.air.temperature) > vsc.connection_temperature_delta - - if(Z.status == ZONE_SLEEPING) - if (handle_temperature) - Z.SetStatus(ZONE_ACTIVE) - else - continue - - if(air && Z.air) - if( handle_temperature ) - ShareHeat(air, Z.air, closed_connection_zones[Z]) - - Z.interactions_with_neighbors++ - interactions_with_neighbors++ - - if(!interactions_with_neighbors && !interactions_with_unsim) - SetStatus(ZONE_SLEEPING) - - interactions_with_neighbors = 0 - interactions_with_unsim = 0 - - progress = "all components completed successfully, the problem is not here" - - -/zone/proc/SetStatus(var/new_status) - if(status == ZONE_SLEEPING && new_status == ZONE_ACTIVE) - air_master.active_zones.Add(src) - status = ZONE_ACTIVE - - else if(status == ZONE_ACTIVE && new_status == ZONE_SLEEPING) - air_master.active_zones.Remove(src) - status = ZONE_SLEEPING - - if(unsimulated_tiles && unsimulated_tiles.len) - UpdateUnsimAvg() - air.copy_from(air_unsim) - - if(!archived_air) - archived_air = new - archived_air.copy_from(air) - - -/zone/proc/CheckStatus() - return status - - -/zone/proc/ActivateIfNeeded() - if(status == ZONE_ACTIVE) return - - var/difference = 0 - - if(unsimulated_tiles && unsimulated_tiles.len) - UpdateUnsimAvg() - if(!air.compare(air_unsim)) - difference = 1 - - if(!difference) - for(var/zone/Z in connected_zones) //Check adjacent zones for air difference. - if(!air.compare(Z.air)) - difference = 1 - break - - if(difference) //We have a difference, activate the zone. - SetStatus(ZONE_ACTIVE) - - return - - -/zone/proc/assume_air(var/datum/gas_mixture/giver) - if(status == ZONE_ACTIVE) - return air.merge(giver) - - else - if(unsimulated_tiles && unsimulated_tiles.len) - UpdateUnsimAvg() - var/datum/gas_mixture/compare_air = new - compare_air.copy_from(giver) - compare_air.add(air_unsim) - compare_air.divide(air.group_multiplier) - - if(air_unsim.compare(compare_air)) - return 0 - - var/result = air.merge(giver) - - if(!archived_air.compare(air)) - SetStatus(ZONE_ACTIVE) - return result - - -/zone/proc/remove_air(var/amount) - if(status == ZONE_ACTIVE) - return air.remove(amount) - - else - var/result = air.remove(amount) - - if(!archived_air.compare(air)) - SetStatus(ZONE_ACTIVE) - - return result - - //////////////// - //Air Movement// -//////////////// - -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_plasma = A.toxins * 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_plasma = B.toxins * 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) - plasma_avg = (full_plasma + s_full_plasma) / (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.toxins = max(0, (A.toxins - plasma_avg) * (1-ratio) + plasma_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.toxins = max(0, (B.toxins - plasma_avg) * (1-ratio) + plasma_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_plasma = 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_plasma = avg_unsim.toxins - unsim_temperature = avg_unsim.temperature - share_size = max(1, max(size + 3, 1) + avg_unsim.group_multiplier) - tileslen = avg_unsim.group_multiplier - - 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_plasma += T.toxins - 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_plasma *= 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_plasma) - - 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_plasma = A.toxins * size - - full_heat_capacity = A.heat_capacity() * size - - oxy_avg = (full_oxy + unsim_oxygen) / (size + share_size) - nit_avg = (full_nitro + unsim_nitrogen) / (size + share_size) - co2_avg = (full_co2 + unsim_co2) / (size + share_size) - plasma_avg = (full_plasma + unsim_plasma) / (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] - - 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.toxins = max(0, (A.toxins - plasma_avg) * (1 - ratio) + plasma_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() - - 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 - var/maximum_energy_delta = max(0, min(A.temperature * A.heat_capacity() * A.group_multiplier, B.temperature * B.heat_capacity() * B.group_multiplier)) - if(maximum_energy_delta > abs(energy_delta)) - if(energy_delta < 0) - maximum_energy_delta *= -1 - energy_delta = maximum_energy_delta - - A.temperature -= energy_delta / (A.heat_capacity() * A.group_multiplier) - B.temperature += energy_delta / (B.heat_capacity() * B.group_multiplier) - - /* This was bad an I feel bad. - //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 - - full_heat_capacity = A.heat_capacity() - - s_full_heat_capacity = B.heat_capacity() - - 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 - - //We need to adjust it to account for the insulation settings. - ratio *= 1 - vsc.connection_insulation - - A.temperature = max(0, (A.temperature - temp_avg) * (1- (ratio / max(1,A.group_multiplier)) ) + temp_avg ) - B.temperature = max(0, (B.temperature - temp_avg) * (1- (ratio / max(1,B.group_multiplier)) ) + temp_avg ) - */ - - /////////////////// - //Zone Rebuilding// -/////////////////// -//Used for updating zone geometry when a zone is cut into two parts. - -zone/proc/Rebuild() - if(last_rebuilt == air_master.current_cycle) - return - - last_rebuilt = air_master.current_cycle - - var/list/new_zone_contents = IsolateContents() - if(new_zone_contents.len == 1) - return - - var/list/current_contents - var/list/new_zones = list() - - contents = new_zone_contents[1] - air.group_multiplier = contents.len - - for(var/identifier in 2 to new_zone_contents.len) - current_contents = new_zone_contents[identifier] - var/zone/new_zone = new (current_contents) - new_zone.air.copy_from(air) - new_zones += new_zone - - for(var/connection/connection in connections) - connection.Cleanup() - - var/turf/simulated/adjacent - - for(var/turf/unsimulated in unsimulated_tiles) - for(var/direction in cardinal) - adjacent = get_step(unsimulated, direction) - - if(istype(adjacent) && adjacent.CanPass(null, unsimulated, 0, 0)) - for(var/zone/zone in new_zones) - if(adjacent in zone) - zone.AddTurf(unsimulated) - - -//Implements a two-pass connected component labeling algorithm to determine if the zone is, in fact, split. - -/zone/proc/IsolateContents() - var/list/current_adjacents = list() - var/adjacent_id - var/lowest_id - - var/list/identical_ids = list() - var/list/turfs = contents.Copy() - var/current_identifier = 1 - - for(var/turf/simulated/current in turfs) - lowest_id = null - current_adjacents = list() - - for(var/direction in cardinal) - var/turf/simulated/adjacent = get_step(current, direction) - if(!current.ZCanPass(adjacent)) - continue - if(adjacent in turfs) - current_adjacents += adjacent - adjacent_id = turfs[adjacent] - - if(adjacent_id && (!lowest_id || adjacent_id < lowest_id)) - lowest_id = adjacent_id - - if(!lowest_id) - lowest_id = current_identifier++ - identical_ids += lowest_id - - for(var/turf/simulated/adjacent in current_adjacents) - adjacent_id = turfs[adjacent] - if(adjacent_id != lowest_id) - if(adjacent_id) - identical_ids[adjacent_id] = lowest_id - turfs[adjacent] = lowest_id - turfs[current] = lowest_id - - var/list/final_arrangement = list() - - for(var/turf/simulated/current in turfs) - current_identifier = identical_ids[turfs[current]] - - if( current_identifier > final_arrangement.len ) - final_arrangement.len = current_identifier - final_arrangement[current_identifier] = list(current) - - else - //Sanity check. - if(!islist(final_arrangement[current_identifier])) - final_arrangement[current_identifier] = list() - final_arrangement[current_identifier] += current - - //lazy but fast - final_arrangement.Remove(null) - - return final_arrangement - - -/* - if(!RequiresRebuild()) - return - - //Choose a random turf and regenerate the zone from it. - var/list/new_contents - var/list/new_unsimulated - - var/list/turfs_needing_zones = list() - - var/list/zones_to_check_connections = list(src) - - if(!locate(/turf/simulated/floor) in contents) - for(var/turf/simulated/turf in contents) - air_master.ReconsiderTileZone(turf) - return SoftDelete() - - var/turfs_to_ignore = list() - if(direct_connections) - for(var/connection/connection in direct_connections) - if(connection.A.zone != src) - turfs_to_ignore += A - else if(connection.B.zone != src) - turfs_to_ignore += B - - new_unsimulated = ( unsimulated_tiles ? unsimulated_tiles : list() ) - - //Now, we have allocated the new turfs into proper lists, and we can start actually rebuilding. - - //If something isn't carried over, it will need a new zone. - for(var/turf/T in contents) - if(!(T in new_contents)) - RemoveTurf(T) - turfs_needing_zones += T - - //Handle addition of new turfs - for(var/turf/S in new_contents) - if(!istype(S, /turf/simulated)) - new_unsimulated |= S - new_contents.Remove(S) - - //If something new is added, we need to deal with it seperately. - else if(!(S in contents) && istype(S, /turf/simulated)) - if(!(S.zone in zones_to_check_connections)) - zones_to_check_connections += S.zone - - S.zone.RemoveTurf(S) - AddTurf(S) - - //Handle the addition of new unsimulated tiles. - unsimulated_tiles = null - - if(new_unsimulated.len) - for(var/turf/S in new_unsimulated) - if(istype(S, /turf/simulated)) - continue - for(var/direction in cardinal) - var/turf/simulated/T = get_step(S,direction) - if(istype(T) && T.zone && S.CanPass(null, T, 0, 0)) - T.zone.AddTurf(S) - - //Finally, handle the orphaned turfs - - for(var/turf/simulated/T in turfs_needing_zones) - if(!T.zone) - zones_to_check_connections += new /zone(T) - - for(var/zone/zone in zones_to_check_connections) - for(var/connection/C in zone.connections) - C.Cleanup()*/ - diff --git a/code/ZAS/Zone.dm b/code/ZAS/Zone.dm index 349d0e05..1f2cbee8 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 @@ -110,6 +118,7 @@ Class Procs: if(invalid) return //Short circuit for explosions where rebuild is called many times over. c_invalidate() for(var/turf/simulated/T in contents) + T.update_graphic(graphic_remove = air.graphic) //we need to remove the overlays so they're not doubled when the zone is rebuilt //T.dbg(invalid_zone) T.needs_air_update = 0 //Reset the marker so that it will be added to the list. air_master.mark_for_update(T) @@ -123,16 +132,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.toxins]" + 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 00000000..8f30ca40 --- /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 00000000..78ff0b98 --- /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 687ef35b..05d8e81a 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -56,7 +56,7 @@ var/dx = abs(Ax - Bx) //sides of right-angled triangle var/dy = abs(Ay - By) if(dx>=dy) return (k1*dx) + (k2*dy) //No sqrt or powers :) - else return (k1*dx) + (k2*dy) + else return (k2*dx) + (k1*dy) #undef k1 #undef k2 @@ -90,6 +90,14 @@ //turfs += centerturf return atoms +/proc/trange(rad = 0, turf/centre = null) //alternative to range (ONLY processes turfs and thus less intensive) + if(!centre) + return + + var/turf/x1y1 = locate(((centre.x-rad)<1 ? 1 : centre.x-rad),((centre.y-rad)<1 ? 1 : centre.y-rad),centre.z) + var/turf/x2y2 = locate(((centre.x+rad)>world.maxx ? world.maxx : centre.x+rad),((centre.y+rad)>world.maxy ? world.maxy : centre.y+rad),centre.z) + return block(x1y1,x2y2) + /proc/get_dist_euclidian(atom/Loc1 as turf|mob|obj,atom/Loc2 as turf|mob|obj) var/dx = Loc1.x - Loc2.x var/dy = Loc1.y - Loc2.y @@ -367,10 +375,96 @@ datum/projectile_data return new /datum/projectile_data(src_x, src_y, time, distance, power_x, power_y, dest_x, dest_y) +/proc/GetRedPart(const/hexa) + return hex2num(copytext(hexa,2,4)) -/proc/flick_overlay(image/I, list/show_to, duration) - for(var/client/C in show_to) - C.images += I - sleep(duration) - for(var/client/C in show_to) - C.images -= I \ No newline at end of file +/proc/GetGreenPart(const/hexa) + return hex2num(copytext(hexa,4,6)) + +/proc/GetBluePart(const/hexa) + return hex2num(copytext(hexa,6,8)) + +/proc/GetHexColors(const/hexa) + return list( + GetRedPart(hexa), + GetGreenPart(hexa), + GetBluePart(hexa) + ) + +/proc/MixColors(const/list/colors) + var/list/reds = list() + var/list/blues = list() + var/list/greens = list() + var/list/weights = list() + + for (var/i = 0, ++i <= colors.len) + reds.Add(GetRedPart(colors[i])) + blues.Add(GetBluePart(colors[i])) + greens.Add(GetGreenPart(colors[i])) + weights.Add(1) + + var/r = mixOneColor(weights, reds) + var/g = mixOneColor(weights, greens) + var/b = mixOneColor(weights, blues) + 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/global_lists.dm b/code/__HELPERS/global_lists.dm index b6fd017b..3bc648ad 100644 --- a/code/__HELPERS/global_lists.dm +++ b/code/__HELPERS/global_lists.dm @@ -22,6 +22,7 @@ var/global/list/joblist = list() //list of all jobstypes, minus borg and AI //Languages/species/whitelist. var/global/list/all_species[0] var/global/list/all_languages[0] +var/global/list/language_keys[0] //table of say codes for all languages var/global/list/whitelisted_species = list("Human") // Posters @@ -99,6 +100,12 @@ var/global/list/backbaglist = list("Nothing", "Backpack", "Satchel", "Satchel Al for(var/T in paths) var/datum/language/L = new T all_languages[L.name] = L + + for (var/language_name in all_languages) + var/datum/language/L = all_languages[language_name] + language_keys[":[lowertext(L.key)]"] = L + language_keys[".[lowertext(L.key)]"] = L + language_keys["#[lowertext(L.key)]"] = L var/rkey = 0 paths = typesof(/datum/species)-/datum/species @@ -123,4 +130,4 @@ var/global/list/backbaglist = list("Nothing", "Backpack", "Satchel", "Satchel Al for(var/t in L) . += " has: [t]\n" world << . -*/ \ No newline at end of file +*/ diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm index e5144fd5..de66011f 100644 --- a/code/__HELPERS/icons.dm +++ b/code/__HELPERS/icons.dm @@ -634,20 +634,60 @@ as a single icon. Useful for when you want to manipulate an icon via the above a The _flatIcons list is a cache for generated icon files. */ -proc - getFlatIcon(atom/A, dir) // 1 = use cache, 2 = override cache, 0 = ignore cache +proc // Creates a single icon from a given /atom or /image. Only the first argument is required. + getFlatIcon(image/A, defdir=2, deficon=null, defstate="", defblend=BLEND_DEFAULT) + // We start with a blank canvas, otherwise some icon procs crash silently + var/icon/flat = icon('icons/effects/effects.dmi', "icon_state"="nothing") // Final flattened icon + if(!A) + return flat + if(A.alpha <= 0) + return flat + var/noIcon = FALSE + + var/curicon + if(A.icon) + curicon = A.icon + else + curicon = deficon + + if(!curicon) + noIcon = TRUE // Do not render this object. + + var/curstate + if(A.icon_state) + curstate = A.icon_state + else + curstate = defstate + + if(!noIcon && !(curstate in icon_states(curicon))) + if("" in icon_states(curicon)) + curstate = "" + else + noIcon = TRUE // Do not render this object. + + var/curdir + if(A.dir != 2) + curdir = A.dir + else + curdir = defdir + + var/curblend + if(A.blend_mode == BLEND_DEFAULT) + curblend = defblend + else + curblend = A.blend_mode + // Layers will be a sorted list of icons/overlays, based on the order in which they are displayed var/list/layers = list() - - // Add the atom's icon itself - if(A.icon) - // Make a copy without pixel_x/y settings - var/image/copy = image(icon=A.icon,icon_state=A.icon_state,layer=A.layer,dir=A.dir) + var/image/copy + // Add the atom's icon itself, without pixel_x/y offsets. + if(!noIcon) + copy = image(icon=curicon, icon_state=curstate, layer=A.layer, dir=curdir) + copy.color = A.color + copy.alpha = A.alpha + copy.blend_mode = curblend layers[copy] = A.layer - // dir defaults to A's dir - if(!dir) dir = A.dir - // Loop through the underlays, then overlays, sorting them into the layers list var/list/process = A.underlays // Current list being processed var/pSet=0 // Which list is being processed: 0 = underlays, 1 = overlays @@ -662,7 +702,7 @@ proc if(!current) continue currentLayer = current:layer if(currentLayer<0) // Special case for FLY_LAYER - if(currentLayer <= -1000) return 0 + if(currentLayer <= -1000) return flat if(pSet == 0) // Underlay currentLayer = A.layer+currentLayer/1000 else // Overlay @@ -688,8 +728,6 @@ proc else // All done break - // We start with a blank canvas, otherwise some icon procs crash silently - var/icon/flat = icon('icons/effects/effects.dmi', "icon_state"="nothing") // Final flattened icon var/icon/add // Icon of overlay being added // Current dimensions of flattened icon @@ -699,24 +737,33 @@ proc for(var/I in layers) - if(I:icon) - if(I:icon_state) - // Has icon and state set - add = icon(I:icon, I:icon_state) - else - if(A.icon_state in icon_states(I:icon)) - // Inherits icon_state from atom - add = icon(I:icon, A.icon_state) - else - // Uses default state ("") - add = icon(I:icon) - else if(I:icon_state) - // Inherits icon from atom - add = icon(A.icon, I:icon_state) - else - // Unknown + if(I:alpha == 0) continue + if(I == copy) // 'I' is an /image based on the object being flattened. + curblend = BLEND_OVERLAY + add = icon(I:icon, I:icon_state, I:dir) + // This checks for a silent failure mode of the icon routine. If the requested dir + // doesn't exist in this icon state it returns a 32x32 icon with 0 alpha. + if (I:dir != SOUTH && add.Width() == 32 && add.Height() == 32) + // Check every pixel for blank (computationally expensive, but the process is limited + // by the amount of film on the station, only happens when we hit something that's + // turned, and bails at the very first pixel it sees. + var/blankpixel; + for(var/y;y<=32;y++) + for(var/x;x<32;x++) + blankpixel = isnull(add.GetPixel(x,y)) + if(!blankpixel) + break + if(!blankpixel) + break + // If we ALWAYS returned a null (which happens when GetPixel encounters something with alpha 0) + if (blankpixel) + // Pull the default direction. + add = icon(I:icon, I:icon_state) + else // 'I' is an appearance object. + add = getFlatIcon(new/image(I), curdir, curicon, curstate, curblend) + // Find the new dimensions of the flat icon to fit the added overlay addX1 = min(flatX1, I:pixel_x+1) addX2 = max(flatX2, I:pixel_x+add.Width()) @@ -730,9 +777,14 @@ proc flatY1=addY1;flatY2=addY2 // Blend the overlay into the flattened icon - flat.Blend(add,ICON_OVERLAY,I:pixel_x+2-flatX1,I:pixel_y+2-flatY1) + flat.Blend(add, blendMode2iconMode(curblend), I:pixel_x + 2 - flatX1, I:pixel_y + 2 - flatY1) - return flat + if(A.color) + flat.Blend(A.color, ICON_MULTIPLY) + if(A.alpha < 255) + flat.Blend(rgb(255, 255, 255, A.alpha), ICON_MULTIPLY) + + return icon(flat, "", SOUTH) getIconMask(atom/A)//By yours truly. Creates a dynamic mask for a mob/whatever. /N var/icon/alpha_mask = new(A.icon,A.icon_state)//So we want the default icon and icon state of A. @@ -783,4 +835,23 @@ proc/adjust_brightness(var/color, var/value) RGB[1] = Clamp(RGB[1]+value,0,255) RGB[2] = Clamp(RGB[2]+value,0,255) RGB[3] = Clamp(RGB[3]+value,0,255) - return rgb(RGB[1],RGB[2],RGB[3]) \ No newline at end of file + return rgb(RGB[1],RGB[2],RGB[3]) + +proc/sort_atoms_by_layer(var/list/atoms) + // Comb sort icons based on levels + var/list/result = atoms.Copy() + var/gap = result.len + var/swapped = 1 + while (gap > 1 || swapped) + swapped = 0 + if(gap > 1) + gap = round(gap / 1.3) // 1.3 is the emperic comb sort coefficient + if(gap < 1) + gap = 1 + for(var/i = 1; gap + i <= result.len; i++) + var/atom/l = result[i] //Fucking hate + var/atom/r = result[gap+i] //how lists work here + if(l.layer > r.layer) //no "result[i].layer" for me + result.Swap(i, gap + i) + swapped = 1 + return result diff --git a/code/__HELPERS/lists.dm b/code/__HELPERS/lists.dm index e869196f..2de9613e 100644 --- a/code/__HELPERS/lists.dm +++ b/code/__HELPERS/lists.dm @@ -297,6 +297,31 @@ proc/listclearnulls(list/list) return (result + R.Copy(Ri, 0)) +// List of lists, sorts by element[key] - for things like crew monitoring computer sorting records by name. +/proc/sortByKey(var/list/L, var/key) + if(L.len < 2) + return L + var/middle = L.len / 2 + 1 + return mergeKeyedLists(sortByKey(L.Copy(0, middle), key), sortByKey(L.Copy(middle), key), key) + +/proc/mergeKeyedLists(var/list/L, var/list/R, var/key) + var/Li=1 + var/Ri=1 + var/list/result = new() + while(Li <= L.len && Ri <= R.len) + if(sorttext(L[Li][key], R[Ri][key]) < 1) + // Works around list += list2 merging lists; it's not pretty but it works + result += "temp item" + result[result.len] = R[Ri++] + else + result += "temp item" + result[result.len] = L[Li++] + + if(Li <= L.len) + return (result + L.Copy(Li, 0)) + return (result + R.Copy(Ri, 0)) + + //Mergesort: any value in a list, preserves key=value structure /proc/sortAssoc(var/list/L) if(L.len < 2) diff --git a/code/__HELPERS/logging.dm b/code/__HELPERS/logging.dm index 5ffdd022..9a53f3db 100644 --- a/code/__HELPERS/logging.dm +++ b/code/__HELPERS/logging.dm @@ -11,6 +11,7 @@ /proc/error(msg) world.log << "## ERROR: [msg][log_end]" +#define WARNING(MSG) warning("[MSG] in [__FILE__] at line [__LINE__] src: [src] usr: [usr].") //print a warning message to world.log /proc/warning(msg) world.log << "## WARNING: [msg][log_end]" diff --git a/code/__HELPERS/maths.dm b/code/__HELPERS/maths.dm index 091beda9..ad450b18 100644 --- a/code/__HELPERS/maths.dm +++ b/code/__HELPERS/maths.dm @@ -3,6 +3,11 @@ var/const/E = 2.71828183 var/const/Sqrt2 = 1.41421356 +// List of square roots for the numbers 1-100. +var/list/sqrtTable = list(1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10) /proc/Atan2(x, y) if(!x && !y) return 0 diff --git a/code/__HELPERS/names.dm b/code/__HELPERS/names.dm index 6ca01ec8..d3ce3787 100644 --- a/code/__HELPERS/names.dm +++ b/code/__HELPERS/names.dm @@ -43,6 +43,9 @@ var/religion_name = null return capitalize(name) +/proc/system_name() + return "Nyx" + /proc/station_name() if (station_name) return station_name @@ -308,4 +311,4 @@ var/syndicate_code_response//Code response for traitors. if(5) return -*/ \ No newline at end of file +*/ diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index d4e184fa..18acc2cf 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -15,9 +15,8 @@ // Run all strings to be used in an SQL query through this proc first to properly escape out injection attempts. /proc/sanitizeSQL(var/t as text) - var/sanitized_text = replacetext(t, "'", "\\'") - sanitized_text = replacetext(sanitized_text, "\"", "\\\"") - return sanitized_text + var/sqltext = dbcon.Quote(t); + return copytext(sqltext, 2, lentext(sqltext));//Quote() adds quotes around input, we already do that /* * Text sanitization @@ -304,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/time.dm b/code/__HELPERS/time.dm index e10ae27a..40accbf3 100644 --- a/code/__HELPERS/time.dm +++ b/code/__HELPERS/time.dm @@ -1,3 +1,9 @@ +#define SECOND *10 +#define SECONDS *10 + +#define MINUTE *600 +#define MINUTES *600 + //Returns the world time in english proc/worldtime2text(time = world.time) return "[round(time / 36000)+12]:[(time / 600 % 60) < 10 ? add_zero(time / 600 % 60, 1) : time / 600 % 60]" diff --git a/code/__HELPERS/type2type.dm b/code/__HELPERS/type2type.dm index d6977cb1..37bdf305 100644 --- a/code/__HELPERS/type2type.dm +++ b/code/__HELPERS/type2type.dm @@ -298,6 +298,13 @@ proc/tg_list2text(list/list, glue=",") /proc/angle2text(var/degree) return dir2text(angle2dir(degree)) +//Converts a blend_mode constant to one acceptable to icon.Blend() +/proc/blendMode2iconMode(blend_mode) + switch(blend_mode) + if(BLEND_MULTIPLY) return ICON_MULTIPLY + if(BLEND_ADD) return ICON_ADD + if(BLEND_SUBTRACT) return ICON_SUBTRACT + else return ICON_OVERLAY //Converts a rights bitfield into a string /proc/rights2text(rights,seperator="") diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index 6f4ab5a1..b1a391cb 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -550,15 +550,6 @@ Turf and target are seperate in case you want to teleport some distance from a t return key_name(whom, 1, include_name) -//Will return the location of the turf an atom is ultimatly sitting on -//isn't this just a null-reference-vulnerable copy of /proc/get_turf()? -/proc/get_turf_loc(var/atom/movable/M) //gets the location of the turf that the atom is on, or what the atom is in is on, etc - //in case they're in a closet or sleeper or something - var/atom/loc = M.loc - while(!istype(loc, /turf/)) - loc = loc.loc - return loc - // returns the turf located at the map edge in the specified direction relative to A // used for mass driver /proc/get_edge_target_turf(var/atom/A, var/direction) @@ -865,11 +856,19 @@ proc/anim(turf/location as turf,target as mob|obj,a_icon,a_icon_state as text,fl var/old_icon_state1 = T.icon_state var/old_icon1 = T.icon - var/turf/X = new T.type(B) + var/turf/X = B.ChangeTurf(T.type) X.dir = old_dir1 X.icon_state = old_icon_state1 X.icon = old_icon1 //Shuttle floors are in shuttle.dmi while the defaults are floors.dmi + var/turf/simulated/ST = T + if(istype(ST) && ST.zone) + var/turf/simulated/SX = X + if(!SX.air) + SX.make_air() + SX.air.copy_from(ST.zone.air) + ST.zone.remove(ST) + /* Quick visual fix for some weird shuttle corner artefacts when on transit space tiles */ if(direction && findtext(X.icon_state, "swall_s")) @@ -918,16 +917,7 @@ proc/anim(turf/location as turf,target as mob|obj,a_icon,a_icon_state as text,fl toupdate += X if(turftoleave) - var/turf/ttl = new turftoleave(T) - -// var/area/AR2 = ttl.loc - -// if(AR2.lighting_use_dynamic) //TODO: rewrite this code so it's not messed by lighting ~Carn -// ttl.opacity = !ttl.opacity -// ttl.sd_SetOpacity(!ttl.opacity) - - fromupdate += ttl - + fromupdate += T.ChangeTurf(turftoleave) else T.ChangeTurf(/turf/space) @@ -1169,8 +1159,9 @@ proc/get_mob_with_client_list() else if (zone == "r_foot") return "right foot" else return zone - -/proc/get_turf(turf/location) +//gets the turf the atom is located in (or itself, if it is a turf). +//returns null if the atom is not in a turf. +/proc/get_turf(atom/location) while(location) if(isturf(location)) return location @@ -1328,11 +1319,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) @@ -1399,3 +1389,8 @@ var/list/WALLITEMS = list( /proc/format_text(text) return replacetext(replacetext(text,"\proper ",""),"\improper ","") + +/proc/topic_link(var/datum/D, var/arglist, var/content) + if(istype(arglist,/list)) + arglist = list2params(arglist) + return "[content]" diff --git a/code/_onclick/ai.dm b/code/_onclick/ai.dm index d6918b1f..c6078c10 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 147d7909..adf80b17 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -80,6 +80,9 @@ throw_item(A) return + if(!istype(A,/obj/item/weapon/gun) && !isturf(A) && !istype(A,/obj/screen)) + last_target_click = world.time + var/obj/item/W = get_active_hand() if(W == A) @@ -249,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 @@ -257,6 +260,9 @@ user.client.statpanel = T.name return +/mob/proc/TurfAdjacent(var/turf/T) + return T.AdjacentQuick(src) + /* Misc helpers @@ -295,14 +301,29 @@ // 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 || !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 + var/direction if(abs(dx) < abs(dy)) - if(dy > 0) usr.dir = NORTH - else usr.dir = SOUTH + if(dy > 0) direction = NORTH + else direction = SOUTH else - if(dx > 0) usr.dir = EAST - else usr.dir = WEST \ No newline at end of file + if(dx > 0) direction = EAST + else direction = WEST + usr.dir = direction + if(buckled && buckled.movable) + buckled.dir = direction + buckled.handle_rotation() \ No newline at end of file diff --git a/code/_onclick/cyborg.dm b/code/_onclick/cyborg.dm index d86faacb..04682c76 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/human.dm b/code/_onclick/hud/human.dm index ca053f86..bf397445 100644 --- a/code/_onclick/hud/human.dm +++ b/code/_onclick/hud/human.dm @@ -13,6 +13,8 @@ using.icon = ui_style using.icon_state = "intent_"+mymob.a_intent using.screen_loc = ui_acti + using.color = ui_color + using.alpha = ui_alpha using.layer = 20 src.adding += using action_intent = using @@ -27,6 +29,7 @@ using.name = "help" using.icon = ico using.screen_loc = ui_acti + using.alpha = ui_alpha using.layer = 21 src.adding += using help_intent = using @@ -38,6 +41,7 @@ using.name = "disarm" using.icon = ico using.screen_loc = ui_acti + using.alpha = ui_alpha using.layer = 21 src.adding += using disarm_intent = using @@ -49,6 +53,7 @@ using.name = "grab" using.icon = ico using.screen_loc = ui_acti + using.alpha = ui_alpha using.layer = 21 src.adding += using grab_intent = using @@ -60,6 +65,7 @@ using.name = "harm" using.icon = ico using.screen_loc = ui_acti + using.alpha = ui_alpha using.layer = 21 src.adding += using hurt_intent = using @@ -440,23 +446,24 @@ //Handle the gun settings buttons mymob.gun_setting_icon = new /obj/screen/gun/mode(null) + //mymob.gun_setting_icon.color = ui_color + mymob.gun_setting_icon.alpha = ui_alpha + + mymob.item_use_icon = new /obj/screen/gun/item(null) + //mymob.item_use_icon.color = ui_color + mymob.item_use_icon.alpha = ui_alpha + + mymob.gun_move_icon = new /obj/screen/gun/move(null) + //mymob.gun_move_icon.color = ui_color + mymob.gun_move_icon.alpha = ui_alpha + + mymob.gun_run_icon = new /obj/screen/gun/run(null) + //mymob.gun_run_icon.color = ui_color + mymob.gun_run_icon.alpha = ui_alpha + if (mymob.client) if (mymob.client.gun_mode) // If in aim mode, correct the sprite mymob.gun_setting_icon.dir = 2 - for(var/obj/item/weapon/gun/G in mymob) // If targeting someone, display other buttons - if (G.target) - mymob.item_use_icon = new /obj/screen/gun/item(null) - if (mymob.client.target_can_click) - mymob.item_use_icon.dir = 1 - src.adding += mymob.item_use_icon - mymob.gun_move_icon = new /obj/screen/gun/move(null) - if (mymob.client.target_can_move) - mymob.gun_move_icon.dir = 1 - mymob.gun_run_icon = new /obj/screen/gun/run(null) - if (mymob.client.target_can_run) - mymob.gun_run_icon.dir = 1 - src.adding += mymob.gun_run_icon - src.adding += mymob.gun_move_icon mymob.client.screen = null @@ -495,7 +502,7 @@ for(var/obj/item/I in src) if(I.icon_action_button) var/obj/screen/item_action/A = new(hud_used) - + //A.icon = 'icons/mob/screen1_action.dmi' //A.icon_state = I.icon_action_button A.icon = ui_style2icon(client.prefs.UI_style) diff --git a/code/_onclick/hud/monkey.dm b/code/_onclick/hud/monkey.dm index 41e8e3bd..7ca8216b 100644 --- a/code/_onclick/hud/monkey.dm +++ b/code/_onclick/hud/monkey.dm @@ -109,7 +109,7 @@ src.l_hand_hud_object = inv_box src.adding += inv_box - using = new /obj/screen() + using = new /obj/screen/inventory() using.name = "hand" using.dir = SOUTH using.icon = ui_style @@ -118,7 +118,7 @@ using.layer = 19 src.adding += using - using = new /obj/screen() + using = new /obj/screen/inventory() using.name = "hand" using.dir = SOUTH using.icon = ui_style diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm index ddc78f4a..02929525 100644 --- a/code/_onclick/hud/screen_objects.dm +++ b/code/_onclick/hud/screen_objects.dm @@ -91,7 +91,7 @@ if(master) var/obj/item/I = usr.get_active_hand() if(I) - master.attackby(I, usr) + usr.ClickOn(master) usr.next_move = world.time+2 return 1 @@ -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.toxins) - contents.Add(t.air_contents.oxygen) + if(t.air_contents.gas["oxygen"] && !t.air_contents.gas["toxins"]) + 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.toxins) - contents.Add(t.air_contents.carbon_dioxide) + if(t.air_contents.gas["carbon_dioxide"] && !t.air_contents.gas["toxins"]) + 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 e7fdac2a..3037458a 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/_onclick/telekinesis.dm b/code/_onclick/telekinesis.dm index 0abb637c..3f74cbd0 100644 --- a/code/_onclick/telekinesis.dm +++ b/code/_onclick/telekinesis.dm @@ -141,7 +141,7 @@ var/const/tk_maxrange = 15 else apply_focus_overlay() - focus.throw_at(target, 10, 1) + focus.throw_at(target, 10, 1, user) last_throw = world.time return diff --git a/code/controllers/_DynamicAreaLighting_TG.dm b/code/controllers/_DynamicAreaLighting_TG.dm index 43a19920..9a09aa00 100644 --- a/code/controllers/_DynamicAreaLighting_TG.dm +++ b/code/controllers/_DynamicAreaLighting_TG.dm @@ -5,9 +5,10 @@ ShadowDarke's respective lighting libraries. Credits, where due, to them. Like sd_DAL (what we used to use), it changes the shading overlays of areas by splitting each type of area into sub-areas - by using the var/tag variable and moving turfs into the contents list of the correct sub-area. + by using the var/tag variable and moving turfs into the contents list of the correct sub-area. This method is + much less costly than using overlays or objects. - Unlike sd_DAL however it uses a queueing system. Everytime we call a change to opacity or luminosity + Unlike sd_DAL however it uses a queueing system. Everytime we call a change to opacity or luminosity (through SetOpacity() or SetLuminosity()) we are simply updating variables and scheduling certain lights/turfs for an update. Actual updates are handled periodically by the lighting_controller. This carries additional overheads, however it means that each thing is changed only once per lighting_controller.processing_interval ticks. Allowing for greater control @@ -15,50 +16,46 @@ setting lighting_controller.processing = 0 at say, the start of a large explosion, waiting for it to finish, and then turning it back on with lighting_controller.processing = 1. - Unlike our old system there is a hardcoded maximum luminosity. This is to discourage coders using large luminosity values - for dynamic lighting, as the cost of lighting grows rapidly at large luminosity levels (especially when changing opacity - at runtime) + Unlike our old system there are hardcoded maximum luminositys (different for certain atoms). + This is to cap the cost of creating lighting effects. + (without this, an atom with luminosity of 20 would have to update 41^2 turfs!) :s Also, in order for the queueing system to work, each light remembers the effect it casts on each turf. This is going to - have larger memory requirements than our previous system but hopefully it's worth the hassle for the greater control we - gain. Besides, there are far far worse uses of needless lists in the game, it'd be worth pruning some of them to offset - costs. + have larger memory requirements than our previous system but it's easily worth the hassle for the greater control we + gain. It also reduces cost of removing lighting effects by a lot! Known Issues/TODO: - admin-spawned turfs will have broken lumcounts. Not willing to fix it at this moment - mob luminosity will be lower than expected when one of multiple light sources is dropped after exceeding the maximum luminosity Shuttles still do not have support for dynamic lighting (I hope to fix this at some point) - No directional lighting support. Fairly easy to add this and the code is ready. + No directional lighting support. (prototype looked ugly) */ -#define LIGHTING_MAX_LUMINOSITY 12 //Hard maximum luminosity to prevet lag which could be caused by coders making mini-suns -#define LIGHTING_MAX_LUMINOSITY_MOB 7 //Mobs get their own max because 60-odd human suns running around would be pretty silly -#define LIGHTING_LAYER 10 //Drawing layer for lighting overlays -#define LIGHTING_ICON 'icons/effects/ss13_dark_alpha7.dmi' //Icon used for lighting shading effects +#define LIGHTING_CIRCULAR 1 //comment this out to use old square lighting effects. +#define LIGHTING_LAYER 10 //Drawing layer for lighting overlays +#define LIGHTING_ICON 'icons/effects/ss13_dark_alpha6.dmi' //Icon used for lighting shading effects datum/light_source var/atom/owner var/changed = 1 - var/mobile = 1 var/list/effect = list() - var/__x = 0 //x coordinate at last update var/__y = 0 //y coordinate at last update + var/__z = 0 //z coordinate at last update + + var/_l_color //do not use directly, only used as reference for updating + var/col_r + var/col_g + var/col_b New(atom/A) if(!istype(A)) CRASH("The first argument to the light object's constructor must be the atom that is the light source. Expected atom, received '[A]' instead.") - ..() owner = A - - if(istype(owner, /atom/movable)) mobile = 1 //apparantly this is faster than type-checking - else mobile = 0 //Perhaps removing support for luminous turfs would be a good idea. - + readrgb(owner.l_color) __x = owner.x __y = owner.y - + __z = owner.z // the lighting object maintains a list of all light sources lighting_controller.lights += src @@ -69,12 +66,16 @@ datum/light_source remove_effect() return 1 //causes it to be removed from our list of lights. The garbage collector will then destroy it. - if(mobile) - // check to see if we've moved since last update - if(owner.x != __x || owner.y != __y) - __x = owner.x - __y = owner.y - changed = 1 + // check to see if we've moved since last update + if(owner.x != __x || owner.y != __y || owner.z != __z) + __x = owner.x + __y = owner.y + __z = owner.z + changed = 1 + + if (owner.l_color != _l_color) + readrgb(owner.l_color) + changed = 1 if(changed) changed = 0 @@ -85,197 +86,267 @@ datum/light_source proc/remove_effect() // before we apply the effect we remove the light's current effect. - if(effect.len) - for(var/turf in effect) // negate the effect of this light source - var/turf/T = turf - T.update_lumcount(-effect[T]) - effect.Cut() // clear the effect list + for(var/turf/T in effect) // negate the effect of this light source + T.update_lumcount(-effect[T], col_r, col_g, col_b, 1) + effect.Cut() // clear the effect list proc/add_effect() // only do this if the light is turned on and is on the map if(owner.loc && owner.luminosity > 0) - effect = new_effect() // identify the effects of this light source - for(var/turf in effect) - var/turf/T = turf - T.update_lumcount(effect[T]) // apply the effect + readrgb(owner.l_color) + effect = list() + for(var/turf/T in view(owner.get_light_range(),owner)) + var/delta_lumen = lum(T) + if(delta_lumen > 0) + effect[T] = delta_lumen + T.update_lumcount(delta_lumen, col_r, col_g, col_b, 0) + return 0 else owner.light = null return 1 //cause the light to be removed from the lights list and garbage collected once it's no //longer referenced by the queue - proc/new_effect() - . = list() - - for(var/turf/T in view(owner.luminosity, owner)) -// var/area/A = T.loc -// if(!A) continue - var/change_in_lumcount = lum(T) - if(change_in_lumcount > 0) - .[T] = change_in_lumcount - - return . - - proc/lum(turf/A) - return owner.luminosity - max(abs(A.x-__x),abs(A.y-__y)) -// var/dist = cheap_hypotenuse(A.x,A.y,__x,__y) //fetches the pythagorean distance between A and the light -// if(owner.luminosity < dist) //if the turf is outside the radius the light doesn't illuminate it -// return 0 -// return round(owner.luminosity - (dist/2),0.1) + if (owner.trueLuminosity < 1) + return 0 + var/dist + if(!A) + dist = 0 + else +#ifdef LIGHTING_CIRCULAR + dist = cheap_hypotenuse(A.x, A.y, __x, __y) +#else + dist = max(abs(A.x - __x), abs(A.y - __y)) +#endif + if (owner.trueLuminosity > 100) // This will never happen... right? + return sqrt(owner.trueLuminosity) - dist + else + return sqrtTable[owner.trueLuminosity] - dist + + proc/readrgb(col) + _l_color = col + if(col) + col_r = GetRedPart(col) + col_g = GetGreenPart(col) + col_b = GetBluePart(col) + else + col_r = null atom var/datum/light_source/light + var/trueLuminosity = 0 // Typically 'luminosity' squared. The builtin luminosity must remain linear. + // We may read it, but NEVER set it directly. + var/l_color //Turfs with opacity when they are constructed will trigger nearby lights to update -//Turfs atoms with luminosity when they are constructed will create a light_source automatically -//TODO: lag reduction +//Turfs and atoms with luminosity when they are constructed will create a light_source automatically turf/New() ..() - if(opacity) - UpdateAffectingLights() if(luminosity) - world.log << "[type] has luminosity at New()" - if(light) world.log << "## WARNING: [type] - Don't set lights up manually during New(), We do it automatically." + if(light) WARNING("[type] - Don't set lights up manually during New(), We do it automatically.") + trueLuminosity = luminosity * luminosity light = new(src) //Movable atoms with opacity when they are constructed will trigger nearby lights to update //Movable atoms with luminosity when they are constructed will create a light_source automatically -//TODO: lag reduction atom/movable/New() ..() if(opacity) - UpdateAffectingLights() + if(isturf(loc)) + if(loc:lighting_lumcount > 1) + UpdateAffectingLights() if(luminosity) - if(light) world.log << "## WARNING: [type] - Don't set lights up manually during New(), We do it automatically." + if(light) WARNING("[type] - Don't set lights up manually during New(), We do it automatically.") + trueLuminosity = luminosity * luminosity light = new(src) -//Turfs with opacity will trigger nearby lights to update at next lighting process. -//TODO: is this really necessary? Removing it could help reduce lag during singulo-mayhem somewhat -turf/Del() - if(opacity) - UpdateAffectingLights() - ..() - //Objects with opacity will trigger nearby lights to update at next lighting process. atom/movable/Del() if(opacity) - UpdateAffectingLights() + if(isturf(loc)) + if(loc:lighting_lumcount > 1) + UpdateAffectingLights() + ..() -//Sets our luminosity. Enforces a hardcoded maximum luminosity by default. This maximum can be overridden but it is extremely -//unwise to do so. +//Sets our luminosity. //If we have no light it will create one. -//If we are setting luminosity to 0 the light will be cleaned up and delted once all its queues are complete -//if we have a light already it is merely updated -atom/proc/SetLuminosity(new_luminosity, max_luminosity = LIGHTING_MAX_LUMINOSITY) +//If we are setting luminosity to 0 the light will be cleaned up by the controller and garbage collected once all its +//queues are complete. +//if we have a light already it is merely updated, rather than making a new one. +atom/proc/SetLuminosity(new_luminosity, trueLum = FALSE) if(new_luminosity < 0) new_luminosity = 0 -// world.log << "## WARNING: [type] - luminosity cannot be negative" - else if(max_luminosity < new_luminosity) - new_luminosity = max_luminosity -// if(luminosity != new_luminosity) -// world.log << "## WARNING: [type] - LIGHT_MAX_LUMINOSITY exceeded" + if(!trueLum) + new_luminosity *= new_luminosity + if(light) + if(trueLuminosity != new_luminosity) //non-luminous lights are removed from the lights list in add_effect() + light.changed = 1 + else + if(new_luminosity) + light = new(src) + trueLuminosity = new_luminosity + if (trueLuminosity < 1) + luminosity = 0 + else if (trueLuminosity <= 100) + luminosity = sqrtTable[trueLuminosity] + else + luminosity = sqrt(trueLuminosity) - if(isturf(loc)) - if(light) - if(luminosity != new_luminosity) //TODO: remove lights from the light list when they're not luminous? DONE in add_effect - light.changed = 1 - else - if(new_luminosity) - light = new(src) +atom/proc/AddLuminosity(delta_luminosity) + if(delta_luminosity > 0) + SetLuminosity(trueLuminosity + delta_luminosity*delta_luminosity, TRUE) + else if(delta_luminosity < 0) + SetLuminosity(trueLuminosity - delta_luminosity*delta_luminosity, TRUE) - luminosity = new_luminosity +area/SetLuminosity(new_luminosity) //we don't want dynamic lighting for areas + luminosity = !!new_luminosity + trueLuminosity = luminosity -//Snowflake code to prevent mobs becoming suns (lag-prevention) -mob/SetLuminosity(new_luminosity) - ..(new_luminosity,LIGHTING_MAX_LUMINOSITY_MOB) //change our opacity (defaults to toggle), and then update all lights that affect us. -atom/proc/SetOpacity(var/new_opacity) - if(new_opacity == null) new_opacity = !opacity - else if(opacity == new_opacity) return - opacity = new_opacity +atom/proc/SetOpacity(new_opacity) + if(new_opacity == null) + new_opacity = !opacity //default = toggle opacity + else if(opacity == new_opacity) + return 0 //opacity hasn't changed! don't bother doing anything + opacity = new_opacity //update opacity, the below procs now call light updates. + return 1 - UpdateAffectingLights() +turf/SetOpacity(new_opacity) + if(..()==1) //only bother if opacity changed + if(lighting_lumcount) //only bother with an update if our turf is currently affected by a light + UpdateAffectingLights() -//set the changed status of all lights which could have possibly lit this atom. -//We don't need to worry about lights which lit us but moved away, since they will have change status set already -atom/proc/UpdateAffectingLights() - var/turf/T = src - if(!isturf(T)) - T = loc - if(!isturf(T)) return - for(var/atom in range(LIGHTING_MAX_LUMINOSITY,T)) //TODO: this will probably not work very well :( - var/atom/A = atom - if(A.light && A.luminosity) - A.light.changed = 1 //force it to update at next process() +/atom/movable/SetOpacity(new_opacity) + if(..()==1) //only bother if opacity changed + if(isturf(loc)) //only bother with an update if we're on a turf + var/turf/T = loc + if(T.lighting_lumcount) //only bother with an update if our turf is currently affected by a light + UpdateAffectingLights() -// for(var/light in lighting_controller.lights) //TODO: this will probably laaaaaag -// var/datum/light_source/L = light -// if(L.changed) continue -// if(!L.owner) continue -// if(!L.owner.luminosity) continue -// if(src in L.effect) -// L.changed = 1 turf var/lighting_lumcount = 0 var/lighting_changed = 0 + var/color_lighting_lumcount = 0 + + var/lumcount_r = 0 + var/lumcount_g = 0 + var/lumcount_b = 0 + var/light_col_sources = 0 turf/space lighting_lumcount = 4 //starlight -turf/proc/update_lumcount(amount) +turf/proc/update_lumcount(amount, col_r, col_g, col_b, removing = 0) lighting_lumcount += amount -// if(lighting_lumcount < 0 || lighting_lumcount > 100) -// world.log << "## WARNING: [type] ([src]) lighting_lumcount = [lighting_lumcount]" + + if(!isnull(col_r)) //col_r is the "key" var, if it's null so will the rest + if(removing) + light_col_sources-- + lumcount_r -= col_r + lumcount_g -= col_g + lumcount_b -= col_b + else + light_col_sources++ + lumcount_r += col_r + lumcount_g += col_g + lumcount_b += col_b + + if(light_col_sources) + var/r_avg = max(0, min(255, round(lumcount_r / light_col_sources, 16) + 15)) + var/g_avg = max(0, min(255, round(lumcount_g / light_col_sources, 16) + 15)) + var/b_avg = max(0, min(255, round(lumcount_b / light_col_sources, 16) + 15)) + l_color = rgb(r_avg, g_avg, b_avg) + else + l_color = null + + color_lighting_lumcount = max(color_lighting_lumcount + amount, 0) // Minimum of 0. + if(!lighting_changed) lighting_controller.changed_turfs += src lighting_changed = 1 +turf/proc/lighting_tag(const/level) + var/area/A = loc + return A.tagbase + "sd_L[level]" + +turf/proc/build_lighting_area(const/tag, const/level, const/color_light) + var/area/Area = loc + var/area/A = new Area.type() // create area if it wasn't found + // replicate vars + for(var/V in Area.vars) + switch(V) + if ("contents","lighting_overlay", "color_overlay", "overlays") + continue + else + if(issaved(Area.vars[V])) A.vars[V] = Area.vars[V] + + A.tag = tag + A.lighting_subarea = 1 + A.lighting_space = 0 // in case it was copied from a space subarea + + if (l_color != A.l_color) + A.l_color = l_color + //color_light = min(max(round(color_lighting_lumcount, 1), 0), lighting_controller.lighting_states) + //world << "[color_light] [color_lighting_lumcount]" + + A.SetLightLevel(level, color_light) + Area.related += A + return A + turf/proc/shift_to_subarea() lighting_changed = 0 var/area/Area = loc if(!istype(Area) || !Area.lighting_use_dynamic) return - // change the turf's area depending on its brightness - // restrict light to valid levels - var/light = min(max(round(lighting_lumcount,1),0),lighting_controller.lighting_states) + var/level = min(max(round(lighting_lumcount,1),0),lighting_controller.lighting_states) + var/new_tag = lighting_tag(level) - var/find = findtextEx(Area.tag, "sd_L") - var/new_tag = copytext(Area.tag, 1, find) - new_tag += "sd_L[light]" + // pomf - If we have a lighting color that is not null, apply the new tag to seperate the areas. + if (l_color) + // pomf - We append the (rounded!) color lighting lumcount so we can have colored lights. + new_tag += "[l_color][min(max(round(color_lighting_lumcount,1),0),lighting_controller.lighting_states)]" if(Area.tag!=new_tag) //skip if already in this area - var/area/A = locate(new_tag) // find an appropriate area + var/color_light = min(max(round(color_lighting_lumcount,1),0),lighting_controller.lighting_states) - if(!A) - - A = new Area.type() // create area if it wasn't found - // replicate vars - for(var/V in Area.vars) - switch(V) - if("contents","lighting_overlay","overlays") continue - else - if(issaved(Area.vars[V])) A.vars[V] = Area.vars[V] - - A.tag = new_tag - A.lighting_subarea = 1 - A.SetLightLevel(light) - - Area.related += A + if (!A) + A = build_lighting_area(new_tag, level, color_light) + else if (l_color != A.l_color) + A.l_color = l_color + //color_light = min(max(round(color_lighting_lumcount, 1), 0), lighting_controller.lighting_states) + A.SetLightLevel(level, color_light) A.contents += src // move the turf into the area +// Dedicated lighting sublevel for space turfs +// helps us depower things in space, remove space fire alarms, +// and evens out space lighting +turf/space/lighting_tag(var/level) + var/area/A = loc + return A.tagbase + "sd_L_space" +turf/space/build_lighting_area(var/tag,var/level) + var/area/A = ..(tag,4) + A.lighting_space = 1 + A.SetLightLevel(4) + A.icon_state = null + return A + + area var/lighting_use_dynamic = 1 //Turn this flag off to prevent sd_DynamicAreaLighting from affecting this area var/image/lighting_overlay //tracks the darkness image of the area for easy removal var/lighting_subarea = 0 //tracks whether we're a lighting sub-area + var/lighting_space = 0 // true for space-only lighting subareas + var/tagbase + var/image/color_overlay //Tracks the color image. - proc/SetLightLevel(light) + proc/SetLightLevel(light, color_light = 0) if(!src) return if(light <= 0) light = 0 @@ -291,17 +362,97 @@ area else lighting_overlay = image(LIGHTING_ICON,,num2text(light),LIGHTING_LAYER) - overlays += lighting_overlay + if (color_overlay) + overlays.Remove(color_overlay) + color_overlay.icon_state = "5" + else + if (l_color) + color_overlay = image('icons/effects/effects.dmi', ,"5", 10.1) + //color_overlay = image('icons/effects/effects.dmi', ,"white", 10.1) + + if (istype(color_overlay)) + color_overlay.color = l_color + + + switch (color_light) + if (6) + color_overlay.icon_state = "5" + //color_overlay.alpha = 180 + if (5) + color_overlay.icon_state = "4" + //color_overlay.alpha = 150 + if (4) + color_overlay.icon_state = "3" + //color_overlay.alpha = 120 + if (3) + color_overlay.icon_state = "2" + //color_overlay.alpha = 90 + if (2) + color_overlay.icon_state = "1" + //color_overlay.alpha = 60 + if (1) + color_overlay.icon_state = "1" + color_overlay.alpha = 200 + //color_overlay.alpha = 30 + if (-INFINITY to 0) + //world << "Zero or below, [color_light]." + color_overlay.alpha = 0 + else + //world << "Setting the alpha to max... color_light [color_light]." + color_overlay.alpha = 180 + + color_overlay.blend_mode = BLEND_ADD + if (color_overlay.color) + overlays.Add(color_overlay) + + if (isnull(color_overlay)) + overlays.Add(lighting_overlay) + else if (light < 6) + overlays.Add(lighting_overlay) + + proc/SetDynamicLighting() + + src.lighting_use_dynamic = 1 + for(var/turf/T in src.contents) + T.update_lumcount(0) proc/InitializeLighting() //TODO: could probably improve this bit ~Carn - if(!tag) tag = "[type]" + tagbase = "[type]" + if(!tag) tag = tagbase if(!lighting_use_dynamic) if(!lighting_subarea) // see if this is a lighting subarea already //show the dark overlay so areas, not yet in a lighting subarea, won't be bright as day and look silly. SetLightLevel(4) +//#undef LIGHTING_LAYER +#undef LIGHTING_CIRCULAR +//#undef LIGHTING_ICON -#undef LIGHTING_MAX_LUMINOSITY -#undef LIGHTING_MAX_LUMINOSITY_MOB -#undef LIGHTING_LAYER -//#undef LIGHTING_ICON \ No newline at end of file +#define LIGHTING_MAX_LUMINOSITY_STATIC 8 //Maximum luminosity to reduce lag. +#define LIGHTING_MAX_LUMINOSITY_MOBILE 5 //Moving objects have a lower max luminosity since these update more often. (lag reduction) +#define LIGHTING_MAX_LUMINOSITY_TURF 1 //turfs have a severely shortened range to protect from inevitable floor-lighttile spam. + +//set the changed status of all lights which could have possibly lit this atom. +//We don't need to worry about lights which lit us but moved away, since they will have change status set already +//This proc can cause lots of lights to be updated. :( +atom/proc/UpdateAffectingLights() + for(var/atom/A in oview(LIGHTING_MAX_LUMINOSITY_STATIC-1,src)) + if(A.light) + A.light.changed = 1 //force it to update at next process() + +//caps luminosity effects max-range based on what type the light's owner is. +atom/proc/get_light_range() + return min(luminosity, LIGHTING_MAX_LUMINOSITY_STATIC) + +atom/movable/get_light_range() + return min(luminosity, LIGHTING_MAX_LUMINOSITY_MOBILE) + +obj/machinery/light/get_light_range() + return min(luminosity, LIGHTING_MAX_LUMINOSITY_STATIC) + +turf/get_light_range() + return min(luminosity, LIGHTING_MAX_LUMINOSITY_TURF) + +#undef LIGHTING_MAX_LUMINOSITY_STATIC +#undef LIGHTING_MAX_LUMINOSITY_MOBILE +#undef LIGHTING_MAX_LUMINOSITY_TURF \ No newline at end of file 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 1da0e44b..0b6459a8 100644 --- a/code/game/communications.dm +++ b/code/controllers/communications.dm @@ -40,7 +40,7 @@ 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. + Avoid of sending signals directly from this proc, use spawn(-1). DO NOT use sleep() here or call procs that sleep please. If you must, use spawn() 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. @@ -84,7 +84,7 @@ 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 +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 @@ -128,15 +128,22 @@ var/const/SUP_FREQ = 1347 #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" +//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 @@ -144,140 +151,114 @@ var/global/datum/controller/radio/radio_controller radio_controller = new /datum/controller/radio() return 1 -datum/controller/radio +//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() - 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] +/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 + if(!frequency) + frequency = new + frequency.frequency = new_frequency + frequencies[f_text] = frequency - frequency.add_listener(device, filter) - return 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] +/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) + frequency.remove_listener(device) - if(frequency.devices.len == 0) - del(frequency) - frequencies -= f_text + if(frequency.devices.len == 0) + del(frequency) + frequencies -= f_text - return 1 + return 1 - proc/return_frequency(var/new_frequency as num) - var/f_text = num2text(new_frequency) - var/datum/radio_frequency/frequency = frequencies[f_text] +/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 + if(!frequency) + frequency = new + frequency.frequency = new_frequency + frequencies[f_text] = frequency - return frequency + return frequency -datum/radio_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++ +/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) -// 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]") +//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 + + device.receive_signal(signal, TRANSMISSION_RADIO, frequency) - -// 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 +/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]") + //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) +/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) - -obj/proc - receive_signal(datum/signal/signal, receive_method, receive_param) - return null - -datum/signal +/datum/signal var/obj/source - var/transmission_method = 0 + var/transmission_method = 0 //unused at the moment //0 = wire //1 = radio transmission //2 = subspace transmission @@ -287,21 +268,21 @@ datum/signal 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 +/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 - 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]" +/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/emergency_shuttle_controller.dm b/code/controllers/emergency_shuttle_controller.dm new file mode 100644 index 00000000..6c6043f7 --- /dev/null +++ b/code/controllers/emergency_shuttle_controller.dm @@ -0,0 +1,261 @@ +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 + +// Controls the emergency shuttle + +var/global/datum/emergency_shuttle_controller/emergency_shuttle + +/datum/emergency_shuttle_controller + var/datum/shuttle/ferry/emergency/shuttle + var/list/escape_pods + + var/launch_time //the time at which the shuttle will be launched + var/auto_recall = 0 //if set, the shuttle will be auto-recalled + var/auto_recall_time //the time at which the shuttle will be auto-recalled + var/evac = 0 //1 = emergency evacuation, 0 = crew transfer + var/wait_for_launch = 0 //if the shuttle is waiting to launch + var/autopilot = 1 //set to 0 to disable the shuttle automatically launching + + var/deny_shuttle = 0 //allows admins to prevent the shuttle from being called + var/departed = 0 //if the shuttle has left the station at least once + + +/datum/emergency_shuttle_controller/proc/process() + if (wait_for_launch) + if (auto_recall && world.time >= auto_recall_time) + recall() + if (world.time >= launch_time) //time to launch the shuttle + stop_launch_countdown() + + if (!shuttle.location) //leaving from the station + //launch the pods! + for (var/datum/shuttle/ferry/escape_pod/pod in escape_pods) + if (!pod.arming_controller || pod.arming_controller.armed) + pod.launch(src) + + if (autopilot) + shuttle.launch(src) + +//called when the shuttle has arrived. +/datum/emergency_shuttle_controller/proc/shuttle_arrived() + if (!shuttle.location) //at station + if (autopilot) + set_launch_countdown(SHUTTLE_LEAVETIME) //get ready to return + + if (evac) + captain_announce("The Emergency Shuttle has docked with the station. You have approximately [round(estimate_launch_time()/60,1)] minutes to board the Emergency Shuttle.") + world << sound('sound/AI/shuttledock.ogg') + else + captain_announce("The scheduled Crew Transfer Shuttle has docked with the station. It will depart in approximately [round(emergency_shuttle.estimate_launch_time()/60,1)] minutes.") + + //arm the escape pods + if (evac) + for (var/datum/shuttle/ferry/escape_pod/pod in escape_pods) + if (pod.arming_controller) + pod.arming_controller.arm() + +//begins the launch countdown and sets the amount of time left until launch +/datum/emergency_shuttle_controller/proc/set_launch_countdown(var/seconds) + wait_for_launch = 1 + launch_time = world.time + seconds*10 + +/datum/emergency_shuttle_controller/proc/stop_launch_countdown() + wait_for_launch = 0 + +//calls the shuttle for an emergency evacuation +/datum/emergency_shuttle_controller/proc/call_evac() + if(!can_call()) return + + //set the launch timer + autopilot = 1 + set_launch_countdown(get_shuttle_prep_time()) + auto_recall_time = rand(world.time + 300, launch_time - 300) + + //reset the shuttle transit time if we need to + shuttle.move_time = SHUTTLE_TRANSIT_DURATION + + evac = 1 + captain_announce("An emergency evacuation shuttle has been called. It will arrive in approximately [round(estimate_arrival_time()/60)] minutes.") + world << sound('sound/AI/shuttlecalled.ogg') + for(var/area/A in world) + if(istype(A, /area/hallway)) + A.readyalert() + +//calls the shuttle for a routine crew transfer +/datum/emergency_shuttle_controller/proc/call_transfer() + if(!can_call()) return + + //set the launch timer + autopilot = 1 + set_launch_countdown(get_shuttle_prep_time()) + auto_recall_time = rand(world.time + 300, launch_time - 300) + + //reset the shuttle transit time if we need to + shuttle.move_time = SHUTTLE_TRANSIT_DURATION + + captain_announce("A crew transfer has been scheduled. The shuttle has been called. It will arrive in approximately [round(estimate_arrival_time()/60)] minutes.") + +//recalls the shuttle +/datum/emergency_shuttle_controller/proc/recall() + if (!can_recall()) return + + wait_for_launch = 0 + shuttle.cancel_launch(src) + + if (evac) + captain_announce("The emergency shuttle has been recalled.") + world << sound('sound/AI/shuttlerecalled.ogg') + + for(var/area/A in world) + if(istype(A, /area/hallway)) + A.readyreset() + evac = 0 + else + captain_announce("The scheduled crew transfer has been cancelled.") + +/datum/emergency_shuttle_controller/proc/can_call() + if (deny_shuttle) + return 0 + if (shuttle.moving_status != SHUTTLE_IDLE || !shuttle.location) //must be idle at centcom + return 0 + if (wait_for_launch) //already launching + return 0 + return 1 + +//this only returns 0 if it would absolutely make no sense to recall +//e.g. the shuttle is already at the station or wasn't called to begin with +//other reasons for the shuttle not being recallable should be handled elsewhere +/datum/emergency_shuttle_controller/proc/can_recall() + if (shuttle.moving_status == SHUTTLE_INTRANSIT) //if the shuttle is already in transit then it's too late + return 0 + if (!shuttle.location) //already at the station. + return 0 + if (!wait_for_launch) //we weren't going anywhere, anyways... + return 0 + return 1 + +/datum/emergency_shuttle_controller/proc/get_shuttle_prep_time() + + return SHUTTLE_PREPTIME + + +/* + These procs are not really used by the controller itself, but are for other parts of the + game whose logic depends on the emergency shuttle. +*/ + +//returns 1 if the shuttle is docked at the station and waiting to leave +/datum/emergency_shuttle_controller/proc/waiting_to_leave() + if (shuttle.location) + return 0 //not at station + return (wait_for_launch || shuttle.moving_status != SHUTTLE_INTRANSIT) + +//so we don't have emergency_shuttle.shuttle.location everywhere +/datum/emergency_shuttle_controller/proc/location() + if (!shuttle) + return 1 //if we dont have a shuttle datum, just act like it's at centcom + return shuttle.location + +//returns the time left until the shuttle arrives at it's destination, in seconds +/datum/emergency_shuttle_controller/proc/estimate_arrival_time() + var/eta + if (shuttle.has_arrive_time()) + //we are in transition and can get an accurate ETA + eta = shuttle.arrive_time + else + //otherwise we need to estimate the arrival time using the scheduled launch time + eta = launch_time + shuttle.move_time*10 + shuttle.warmup_time*10 + return (eta - world.time)/10 + +//returns the time left until the shuttle launches, in seconds +/datum/emergency_shuttle_controller/proc/estimate_launch_time() + return (launch_time - world.time)/10 + +/datum/emergency_shuttle_controller/proc/has_eta() + return (wait_for_launch || shuttle.moving_status != SHUTTLE_IDLE) + +//returns 1 if the shuttle has gone to the station and come back at least once, +//used for game completion checking purposes +/datum/emergency_shuttle_controller/proc/returned() + return (departed && shuttle.moving_status == SHUTTLE_IDLE && shuttle.location) //we've gone to the station at least once, no longer in transit and are idle back at centcom + +//returns 1 if the shuttle is not idle at centcom +/datum/emergency_shuttle_controller/proc/online() + if (!shuttle.location) //not at centcom + return 1 + if (wait_for_launch || shuttle.moving_status != SHUTTLE_IDLE) + return 1 + return 0 + +//returns 1 if the shuttle is currently in transit (or just leaving) to the station +/datum/emergency_shuttle_controller/proc/going_to_station() + return (!shuttle.direction && shuttle.moving_status != SHUTTLE_IDLE) + +//returns 1 if the shuttle is currently in transit (or just leaving) to centcom +/datum/emergency_shuttle_controller/proc/going_to_centcom() + return (shuttle.direction && shuttle.moving_status != SHUTTLE_IDLE) + + +/datum/emergency_shuttle_controller/proc/get_status_panel_eta() + if (online()) + if (shuttle.has_arrive_time()) + var/timeleft = emergency_shuttle.estimate_arrival_time() + return "ETA-[(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]" + + if (waiting_to_leave()) + if (shuttle.moving_status == SHUTTLE_WARMUP) + return "Departing..." + + var/timeleft = emergency_shuttle.estimate_launch_time() + return "ETD-[(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]" + + return "" +/* + Some slapped-together star effects for maximum spess immershuns. Basically consists of a + spawner, an ender, and bgstar. Spawners create bgstars, bgstars shoot off into a direction + until they reach a starender. +*/ + +/obj/effect/bgstar + name = "star" + var/speed = 10 + var/direction = SOUTH + layer = 2 // TURF_LAYER + +/obj/effect/bgstar/New() + ..() + pixel_x += rand(-2,30) + pixel_y += rand(-2,30) + var/starnum = pick("1", "1", "1", "2", "3", "4") + + icon_state = "star"+starnum + + speed = rand(2, 5) + +/obj/effect/bgstar/proc/startmove() + + while(src) + sleep(speed) + step(src, direction) + for(var/obj/effect/starender/E in loc) + del(src) + + +/obj/effect/starender + invisibility = 101 + +/obj/effect/starspawner + invisibility = 101 + var/spawndir = SOUTH + var/spawning = 0 + +/obj/effect/starspawner/West + spawndir = WEST + +/obj/effect/starspawner/proc/startspawn() + spawning = 1 + while(spawning) + sleep(rand(2, 30)) + var/obj/effect/bgstar/S = new/obj/effect/bgstar(locate(x,y,z)) + S.direction = spawndir + spawn() + S.startmove() \ No newline at end of file diff --git a/code/controllers/hooks-defs.dm b/code/controllers/hooks-defs.dm index cb51229c..1cd7c4aa 100644 --- a/code/controllers/hooks-defs.dm +++ b/code/controllers/hooks-defs.dm @@ -15,3 +15,73 @@ * Called in gameticker.dm when a round ends. */ /hook/roundend + +/** + * Death hook. + * Called in death.dm when someone dies. + * Parameters: var/mob/living/carbon/human, var/gibbed + */ +/hook/death + +/** + * Cloning hook. + * Called in cloning.dm when someone is brought back by the wonders of modern science. + * Parameters: var/mob/living/carbon/human + */ +/hook/clone + +/** + * Debrained hook. + * Called in brain_item.dm when someone gets debrained. + * Parameters: var/obj/item/brain + */ +/hook/debrain + +/** + * Borged hook. + * Called in robot_parts.dm when someone gets turned into a cyborg. + * Parameters: var/mob/living/silicon/robot + */ +/hook/borgify + +/** + * Podman hook. + * Called in podmen.dm when someone is brought back as a Diona. + * Parameters: var/mob/living/carbon/monkey/diona + */ +/hook/harvest_podman + +/** + * Payroll revoked hook. + * Called in Accounts_DB.dm when someone's payroll is stolen at the Accounts terminal. + * Parameters: var/datum/money_account + */ +/hook/revoke_payroll + +/** + * Account suspension hook. + * Called in Accounts_DB.dm when someone's account is suspended or unsuspended at the Accounts terminal. + * Parameters: var/datum/money_account + */ +/hook/change_account_status + +/** + * Employee reassignment hook. + * Called in card.dm when someone's card is reassigned at the HoP's desk. + * Parameters: var/obj/item/weapon/card/id + */ +/hook/reassign_employee + +/** + * Employee terminated hook. + * Called in card.dm when someone's card is terminated at the HoP's desk. + * Parameters: var/obj/item/weapon/card/id + */ +/hook/terminate_employee + +/** + * Crate sold hook. + * Called in supplyshuttle.dm when a crate is sold on the shuttle. + * Parameters: var/obj/structure/closet/crate/sold, var/area/shuttle + */ +/hook/sell_crate diff --git a/code/controllers/hooks.dm b/code/controllers/hooks.dm index 5045da75..48f1199e 100644 --- a/code/controllers/hooks.dm +++ b/code/controllers/hooks.dm @@ -23,7 +23,7 @@ * @param hook Identifier of the hook to call. * @returns 1 if all hooked code runs successfully, 0 otherwise. */ -/proc/callHook(hook) +/proc/callHook(hook, list/args=null) var/hook_path = text2path("/hook/[hook]") if(!hook_path) error("Invalid hook '/hook/[hook]' called.") @@ -32,7 +32,7 @@ var/caller = new hook_path var/status = 1 for(var/P in typesof("[hook_path]/proc")) - if(!call(caller, P)()) + if(!call(caller, P)(arglist(args))) error("Hook '[P]' failed or runtimed.") status = 0 diff --git a/code/controllers/lighting_controller.dm b/code/controllers/lighting_controller.dm index e9bbfae9..e077af53 100644 --- a/code/controllers/lighting_controller.dm +++ b/code/controllers/lighting_controller.dm @@ -16,6 +16,7 @@ datum/controller/lighting var/list/changed_turfs = list() var/changed_turfs_workload_max = 0 + datum/controller/lighting/New() lighting_states = max( 0, length(icon_states(LIGHTING_ICON))-1 ) if(lighting_controller != src) diff --git a/code/controllers/master_controller.dm b/code/controllers/master_controller.dm index 4b2afd6b..0c8a82f4 100644 --- a/code/controllers/master_controller.dm +++ b/code/controllers/master_controller.dm @@ -33,6 +33,10 @@ datum/controller/game_controller var/mob/list/expensive_mobs = list() var/rebuild_active_areas = 0 + var/list/shuttle_list // For debugging and VV +// var/datum/ore_distribution/asteroid_ore_map // For debugging and VV. + + datum/controller/game_controller/New() //There can be only one master_controller. Out with the old and in with the new. if(master_controller != src) @@ -50,7 +54,8 @@ datum/controller/game_controller/New() if(!syndicate_code_phrase) syndicate_code_phrase = generate_code_phrase() if(!syndicate_code_response) syndicate_code_response = generate_code_phrase() - if(!emergency_shuttle) emergency_shuttle = new /datum/shuttle_controller/emergency_shuttle() + if(!emergency_shuttle) emergency_shuttle = new /datum/emergency_shuttle_controller() + if(!shuttle_controller) shuttle_controller = new /datum/shuttle_controller() if(!delta_level) delta_level = new /datum/delta_level() datum/controller/game_controller/proc/setup() @@ -77,9 +82,6 @@ datum/controller/game_controller/proc/setup() for(var/i=0, i= 600) - return - captain_announce("The emergency shuttle has been recalled.") - world << sound('sound/AI/shuttlerecalled.ogg') - setdirection(-1) - online = 1 - for(var/area/A in world) - if(istype(A, /area/hallway)) - A.readyreset() - return - else //makes it possible to send shuttle back. - captain_announce("The shuttle has been recalled.") - setdirection(-1) - online = 1 - alert = 0 // set alert back to 0 after an admin recall - return - - // returns the time (in seconds) before shuttle arrival - // note if direction = -1, gives a count-up to SHUTTLEARRIVETIME -datum/shuttle_controller/proc/timeleft() - if(online) - var/timeleft = round((endtime - world.timeofday)/10 ,1) - if(direction == 1 || direction == 2) - return timeleft - else - return SHUTTLEARRIVETIME-timeleft - else - return SHUTTLEARRIVETIME - - // sets the time left to a given delay (in seconds) -datum/shuttle_controller/proc/settimeleft(var/delay) - endtime = world.timeofday + delay * 10 - timelimit = delay - - // sets the shuttle direction - // 1 = towards SS13, -1 = back to centcom -datum/shuttle_controller/proc/setdirection(var/dirn) - if(direction == dirn) - return - direction = dirn - // if changing direction, flip the timeleft by SHUTTLEARRIVETIME - var/ticksleft = endtime - world.timeofday - endtime = world.timeofday + (SHUTTLEARRIVETIME*10 - ticksleft) - return - -datum/shuttle_controller/proc/process() - -datum/shuttle_controller/emergency_shuttle/process() - if(!online) - return - var/timeleft = timeleft() - if(timeleft > 1e5) // midnight rollover protection - timeleft = 0 - switch(location) - if(0) - - /* --- Shuttle is in transit to Central Command from SS13 --- */ - if(direction == 2) - if(timeleft>0) - return 0 - - /* --- Shuttle has arrived at Centrcal Command --- */ - else - // turn off the star spawners - /* - for(var/obj/effect/starspawner/S in world) - S.spawning = 0 - */ - - location = 2 - - //main shuttle - var/area/start_location = locate(/area/shuttle/escape/transit) - var/area/end_location = locate(/area/shuttle/escape/centcom) - - start_location.move_contents_to(end_location, null, NORTH) - - for(var/obj/machinery/door/unpowered/shuttle/D in end_location) - spawn(0) - D.locked = 0 - D.open() - - for(var/mob/M in end_location) - if(M.client) - spawn(0) - if(M.buckled) - shake_camera(M, 4, 1) // buckled, not a lot of shaking - else - shake_camera(M, 10, 2) // unbuckled, HOLY SHIT SHAKE THE ROOM - if(istype(M, /mob/living/carbon)) - if(!M.buckled) - M.Weaken(5) - - //pods - start_location = locate(/area/shuttle/escape_pod1/transit) - end_location = locate(/area/shuttle/escape_pod1/centcom) - start_location.move_contents_to(end_location, null, NORTH) - - for(var/obj/machinery/door/D in machines) - if( get_area(D) == end_location ) - spawn(0) - D.open() - - for(var/mob/M in end_location) - if(M.client) - spawn(0) - if(M.buckled) - shake_camera(M, 4, 1) // buckled, not a lot of shaking - else - shake_camera(M, 10, 2) // unbuckled, HOLY SHIT SHAKE THE ROOM - if(istype(M, /mob/living/carbon)) - if(!M.buckled) - M.Weaken(5) - - start_location = locate(/area/shuttle/escape_pod2/transit) - end_location = locate(/area/shuttle/escape_pod2/centcom) - start_location.move_contents_to(end_location, null, NORTH) - - for(var/obj/machinery/door/D in machines) - if( get_area(D) == end_location ) - spawn(0) - D.open() - - for(var/mob/M in end_location) - if(M.client) - spawn(0) - if(M.buckled) - shake_camera(M, 4, 1) // buckled, not a lot of shaking - else - shake_camera(M, 10, 2) // unbuckled, HOLY SHIT SHAKE THE ROOM - if(istype(M, /mob/living/carbon)) - if(!M.buckled) - M.Weaken(5) - - start_location = locate(/area/shuttle/escape_pod3/transit) - end_location = locate(/area/shuttle/escape_pod3/centcom) - start_location.move_contents_to(end_location, null, NORTH) - - for(var/obj/machinery/door/D in machines) - if( get_area(D) == end_location ) - spawn(0) - D.open() - - for(var/mob/M in end_location) - if(M.client) - spawn(0) - if(M.buckled) - shake_camera(M, 4, 1) // buckled, not a lot of shaking - else - shake_camera(M, 10, 2) // unbuckled, HOLY SHIT SHAKE THE ROOM - if(istype(M, /mob/living/carbon)) - if(!M.buckled) - M.Weaken(5) - - start_location = locate(/area/shuttle/escape_pod5/transit) - end_location = locate(/area/shuttle/escape_pod5/centcom) - start_location.move_contents_to(end_location, null, EAST) - - for(var/obj/machinery/door/D in machines) - if( get_area(D) == end_location ) - spawn(0) - D.open() - - for(var/mob/M in end_location) - if(M.client) - spawn(0) - if(M.buckled) - shake_camera(M, 4, 1) // buckled, not a lot of shaking - else - shake_camera(M, 10, 2) // unbuckled, HOLY SHIT SHAKE THE ROOM - if(istype(M, /mob/living/carbon)) - if(!M.buckled) - M.Weaken(5) - - online = 0 - - return 1 - - /* --- Shuttle has docked centcom after being recalled --- */ - if(timeleft>timelimit) - online = 0 - direction = 1 - endtime = null - - return 0 - - else if((fake_recall != 0) && (timeleft <= fake_recall)) - recall() - fake_recall = 0 - return 0 - - /* --- Shuttle has docked with the station - begin countdown to transit --- */ - else if(timeleft <= 0) - location = 1 - var/area/start_location = locate(/area/shuttle/escape/centcom) - var/area/end_location = locate(/area/shuttle/escape/station) - - var/list/dstturfs = list() - var/throwy = world.maxy - - for(var/turf/T in end_location) - dstturfs += T - if(T.y < throwy) - throwy = T.y - - // hey you, get out of the way! - for(var/turf/T in dstturfs) - // find the turf to move things to - var/turf/D = locate(T.x, throwy - 1, 1) - //var/turf/E = get_step(D, SOUTH) - for(var/atom/movable/AM as mob|obj in T) - AM.Move(D) - // NOTE: Commenting this out to avoid recreating mass driver glitch - /* - spawn(0) - AM.throw_at(E, 1, 1) - return - */ - - if(istype(T, /turf/simulated)) - del(T) - - for(var/mob/living/carbon/bug in end_location) // If someone somehow is still in the shuttle's docking area... - bug.gib() - - for(var/mob/living/simple_animal/pest in end_location) // And for the other kind of bug... - pest.gib() - - start_location.move_contents_to(end_location) - settimeleft(SHUTTLELEAVETIME) - //send2irc("Server", "The Emergency Shuttle has docked with the station.") - captain_announce("The Emergency Shuttle has docked with the station. You have [round(timeleft()/60,1)] minutes to board the Emergency Shuttle.") - world << sound('sound/AI/shuttledock.ogg') - - return 1 - - if(1) - - // Just before it leaves, close the damn doors! - if(timeleft == 2 || timeleft == 1) - var/area/start_location = locate(/area/shuttle/escape/station) - for(var/obj/machinery/door/unpowered/shuttle/D in start_location) - spawn(0) - D.close() - D.locked = 1 - - if(timeleft>0) - return 0 - - /* --- Shuttle leaves the station, enters transit --- */ - else - - // Turn on the star effects - - /* // kinda buggy atm, i'll fix this later - for(var/obj/effect/starspawner/S in world) - if(!S.spawning) - spawn() S.startspawn() - */ - - departed = 1 // It's going! - location = 0 // in deep space - direction = 2 // heading to centcom - - //main shuttle - var/area/start_location = locate(/area/shuttle/escape/station) - var/area/end_location = locate(/area/shuttle/escape/transit) - - settimeleft(SHUTTLETRANSITTIME) - start_location.move_contents_to(end_location, null, NORTH) - - // Close shuttle doors, lock - for(var/obj/machinery/door/unpowered/shuttle/D in end_location) - spawn(0) - D.close() - D.locked = 1 - - // Some aesthetic turbulance shaking - for(var/mob/M in end_location) - if(M.client) - spawn(0) - if(M.buckled) - shake_camera(M, 4, 1) // buckled, not a lot of shaking - else - shake_camera(M, 10, 2) // unbuckled, HOLY SHIT SHAKE THE ROOM - if(istype(M, /mob/living/carbon)) - if(!M.buckled) - M.Weaken(5) - - //pods - start_location = locate(/area/shuttle/escape_pod1/station) - end_location = locate(/area/shuttle/escape_pod1/transit) - start_location.move_contents_to(end_location, null, NORTH) - for(var/obj/machinery/door/D in end_location) - spawn(0) - D.close() - - for(var/mob/M in end_location) - if(M.client) - spawn(0) - if(M.buckled) - shake_camera(M, 4, 1) // buckled, not a lot of shaking - else - shake_camera(M, 10, 2) // unbuckled, HOLY SHIT SHAKE THE ROOM - if(istype(M, /mob/living/carbon)) - if(!M.buckled) - M.Weaken(5) - - start_location = locate(/area/shuttle/escape_pod2/station) - end_location = locate(/area/shuttle/escape_pod2/transit) - start_location.move_contents_to(end_location, null, NORTH) - for(var/obj/machinery/door/D in end_location) - spawn(0) - D.close() - - for(var/mob/M in end_location) - if(M.client) - spawn(0) - if(M.buckled) - shake_camera(M, 4, 1) // buckled, not a lot of shaking - else - shake_camera(M, 10, 2) // unbuckled, HOLY SHIT SHAKE THE ROOM - if(istype(M, /mob/living/carbon)) - if(!M.buckled) - M.Weaken(5) - - start_location = locate(/area/shuttle/escape_pod3/station) - end_location = locate(/area/shuttle/escape_pod3/transit) - start_location.move_contents_to(end_location, null, NORTH) - for(var/obj/machinery/door/D in end_location) - spawn(0) - D.close() - - for(var/mob/M in end_location) - if(M.client) - spawn(0) - if(M.buckled) - shake_camera(M, 4, 1) // buckled, not a lot of shaking - else - shake_camera(M, 10, 2) // unbuckled, HOLY SHIT SHAKE THE ROOM - if(istype(M, /mob/living/carbon)) - if(!M.buckled) - M.Weaken(5) - - start_location = locate(/area/shuttle/escape_pod5/station) - end_location = locate(/area/shuttle/escape_pod5/transit) - start_location.move_contents_to(end_location, null, EAST) - for(var/obj/machinery/door/D in end_location) - spawn(0) - D.close() - - for(var/mob/M in end_location) - if(M.client) - spawn(0) - if(M.buckled) - shake_camera(M, 4, 1) // buckled, not a lot of shaking - else - shake_camera(M, 10, 2) // unbuckled, HOLY SHIT SHAKE THE ROOM - if(istype(M, /mob/living/carbon)) - if(!M.buckled) - M.Weaken(5) - - captain_announce("The Emergency Shuttle has left the station. Estimate [round(timeleft()/60,1)] minutes until the shuttle docks at Central Command.") - - return 1 - - else - return 1 - - -/* - Some slapped-together star effects for maximum spess immershuns. Basically consists of a - spawner, an ender, and bgstar. Spawners create bgstars, bgstars shoot off into a direction - until they reach a starender. -*/ - -/obj/effect/bgstar - name = "star" - var/speed = 10 - var/direction = SOUTH - layer = 2 // TURF_LAYER - -/obj/effect/bgstar/New() - ..() - pixel_x += rand(-2,30) - pixel_y += rand(-2,30) - var/starnum = pick("1", "1", "1", "2", "3", "4") - - icon_state = "star"+starnum - - speed = rand(2, 5) - -/obj/effect/bgstar/proc/startmove() - - while(src) - sleep(speed) - step(src, direction) - for(var/obj/effect/starender/E in loc) - del(src) - - -/obj/effect/starender - invisibility = 101 - -/obj/effect/starspawner - invisibility = 101 - var/spawndir = SOUTH - var/spawning = 0 - -/obj/effect/starspawner/West - spawndir = WEST - -/obj/effect/starspawner/proc/startspawn() - spawning = 1 - while(spawning) - sleep(rand(2, 30)) - var/obj/effect/bgstar/S = new/obj/effect/bgstar(locate(x,y,z)) - S.direction = spawndir - spawn() - S.startmove() - +var/global/datum/shuttle_controller/shuttle_controller + + +/datum/shuttle_controller + var/list/shuttles //maps shuttle tags to shuttle datums, so that they can be looked up. + var/list/process_shuttles //simple list of shuttles, for processing + +/datum/shuttle_controller/proc/process() + //process ferry shuttles + for (var/datum/shuttle/ferry/shuttle in process_shuttles) + if (shuttle.process_state) + shuttle.process() + + +/datum/shuttle_controller/New() + shuttles = list() + process_shuttles = list() + + var/datum/shuttle/ferry/shuttle + + // Escape shuttle and pods + shuttle = new/datum/shuttle/ferry/emergency() + shuttle.location = 1 + shuttle.warmup_time = 10 + shuttle.area_offsite = locate(/area/shuttle/escape/centcom) + shuttle.area_station = locate(/area/shuttle/escape/station) + shuttle.area_transition = locate(/area/shuttle/escape/transit) + shuttle.docking_controller_tag = "escape_shuttle" + shuttle.dock_target_station = "escape_dock" + shuttle.dock_target_offsite = "centcom_dock" + shuttle.transit_direction = NORTH + shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN + //shuttle.docking_controller_tag = "supply_shuttle" + //shuttle.dock_target_station = "cargo_bay" + shuttles["Escape"] = shuttle + process_shuttles += shuttle + + shuttle = new/datum/shuttle/ferry/escape_pod() + shuttle.location = 0 + shuttle.warmup_time = 0 + shuttle.area_station = locate(/area/shuttle/escape_pod1/station) + shuttle.area_offsite = locate(/area/shuttle/escape_pod1/centcom) + shuttle.area_transition = locate(/area/shuttle/escape_pod1/transit) + shuttle.docking_controller_tag = "escape_pod_1" + shuttle.dock_target_station = "escape_pod_1_berth" + shuttle.dock_target_offsite = "escape_pod_1_recovery" + shuttle.transit_direction = NORTH + shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN + rand(-30, 60) //randomize this so it seems like the pods are being picked up one by one + process_shuttles += shuttle + shuttles["Escape Pod 1"] = shuttle + + shuttle = new/datum/shuttle/ferry/escape_pod() + shuttle.location = 0 + shuttle.warmup_time = 0 + shuttle.area_station = locate(/area/shuttle/escape_pod2/station) + shuttle.area_offsite = locate(/area/shuttle/escape_pod2/centcom) + shuttle.area_transition = locate(/area/shuttle/escape_pod2/transit) + shuttle.docking_controller_tag = "escape_pod_2" + shuttle.dock_target_station = "escape_pod_2_berth" + shuttle.dock_target_offsite = "escape_pod_2_recovery" + shuttle.transit_direction = NORTH + shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN + rand(-30, 60) //randomize this so it seems like the pods are being picked up one by one + process_shuttles += shuttle + shuttles["Escape Pod 2"] = shuttle + + shuttle = new/datum/shuttle/ferry/escape_pod() + shuttle.location = 0 + shuttle.warmup_time = 0 + shuttle.area_station = locate(/area/shuttle/escape_pod3/station) + shuttle.area_offsite = locate(/area/shuttle/escape_pod3/centcom) + shuttle.area_transition = locate(/area/shuttle/escape_pod3/transit) + shuttle.docking_controller_tag = "escape_pod_3" + shuttle.dock_target_station = "escape_pod_3_berth" + shuttle.dock_target_offsite = "escape_pod_3_recovery" + shuttle.transit_direction = EAST + shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN + rand(-30, 60) //randomize this so it seems like the pods are being picked up one by one + process_shuttles += shuttle + shuttles["Escape Pod 3"] = shuttle + + //There is no pod 4, apparently. + + shuttle = new/datum/shuttle/ferry/escape_pod() + shuttle.location = 0 + shuttle.warmup_time = 0 + shuttle.area_station = locate(/area/shuttle/escape_pod5/station) + shuttle.area_offsite = locate(/area/shuttle/escape_pod5/centcom) + shuttle.area_transition = locate(/area/shuttle/escape_pod5/transit) + shuttle.docking_controller_tag = "escape_pod_5" + shuttle.dock_target_station = "escape_pod_5_berth" + shuttle.dock_target_offsite = "escape_pod_5_recovery" + shuttle.transit_direction = EAST //should this be WEST? I have no idea. + shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN + rand(-30, 60) //randomize this so it seems like the pods are being picked up one by one + process_shuttles += shuttle + shuttles["Escape Pod 5"] = shuttle + + //give the emergency shuttle controller it's shuttles + emergency_shuttle.shuttle = shuttles["Escape"] + emergency_shuttle.escape_pods = list( + shuttles["Escape Pod 1"], + shuttles["Escape Pod 2"], + shuttles["Escape Pod 3"], + shuttles["Escape Pod 5"], + ) + + // Supply shuttle + shuttle = new/datum/shuttle/ferry/supply() + shuttle.location = 1 + shuttle.warmup_time = 10 + shuttle.area_offsite = locate(/area/supply/dock) + shuttle.area_station = locate(/area/supply/station) + shuttle.docking_controller_tag = "supply_shuttle" + shuttle.dock_target_station = "cargo_bay" + shuttles["Supply"] = shuttle + process_shuttles += shuttle + + supply_controller.shuttle = shuttle + + // Admin shuttles. + shuttle = new() + shuttle.location = 1 + shuttle.warmup_time = 10 + shuttle.area_offsite = locate(/area/shuttle/transport1/centcom) + shuttle.area_station = locate(/area/shuttle/transport1/station) + shuttle.docking_controller_tag = "centcom_shuttle" + shuttle.dock_target_station = "centcom_shuttle_dock_airlock" + shuttle.dock_target_offsite = "centcom_shuttle_bay" + shuttles["Centcom"] = shuttle + process_shuttles += shuttle + + shuttle = new() + shuttle.location = 1 + shuttle.warmup_time = 10 //want some warmup time so people can cancel. + shuttle.area_offsite = locate(/area/shuttle/administration/centcom) + shuttle.area_station = locate(/area/shuttle/administration/station) + shuttle.docking_controller_tag = "admin_shuttle" + shuttle.dock_target_station = "admin_shuttle_dock_airlock" + shuttle.dock_target_offsite = "admin_shuttle_bay" + shuttles["Administration"] = shuttle + process_shuttles += shuttle + + shuttle = new() + shuttle.area_offsite = locate(/area/shuttle/alien/base) + shuttle.area_station = locate(/area/shuttle/alien/mine) + shuttles["Alien"] = shuttle + //process_shuttles += shuttle //don't need to process this. It can only be moved using admin magic anyways. + + // Public shuttles + shuttle = new() + shuttle.location = 1 + shuttle.warmup_time = 10 + shuttle.area_offsite = locate(/area/shuttle/engineering/outpost) + shuttle.area_station = locate(/area/shuttle/engineering/station) + shuttle.docking_controller_tag = "engineering_shuttle" + shuttle.dock_target_station = "engineering_dock_airlock" + shuttle.dock_target_offsite = "engineering_station_airlock" + shuttles["Engineering"] = shuttle + process_shuttles += shuttle + + shuttle = new() + shuttle.warmup_time = 10 + shuttle.area_offsite = locate(/area/shuttle/mining/outpost) + shuttle.area_station = locate(/area/shuttle/mining/station) + shuttle.docking_controller_tag = "mining_shuttle" + shuttle.dock_target_station = "mining_dock_airlock" + shuttle.dock_target_offsite = "mining_outpost_airlock" + shuttles["Mining"] = shuttle + process_shuttles += shuttle + + shuttle = new() + shuttle.warmup_time = 10 + shuttle.area_offsite = locate(/area/shuttle/research/outpost) + shuttle.area_station = locate(/area/shuttle/research/station) + shuttle.docking_controller_tag = "research_shuttle" + shuttle.dock_target_station = "research_dock_airlock" + shuttle.dock_target_offsite = "research_outpost_dock" + shuttles["Research"] = shuttle + process_shuttles += shuttle + + // ERT Shuttle + var/datum/shuttle/ferry/multidock/specops/ERT = new() + ERT.location = 0 + ERT.warmup_time = 10 + ERT.area_offsite = locate(/area/shuttle/specops/station) //centcom is the home station, the Exodus is offsite + ERT.area_station = locate(/area/shuttle/specops/centcom) + ERT.docking_controller_tag = "specops_shuttle_port" + ERT.docking_controller_tag_station = "specops_shuttle_port" + ERT.docking_controller_tag_offsite = "specops_shuttle_fore" + ERT.dock_target_station = "specops_centcom_dock" + ERT.dock_target_offsite = "specops_dock_airlock" + shuttles["Special Operations"] = ERT + process_shuttles += ERT + + //Vox Shuttle. + var/datum/shuttle/multi_shuttle/VS = new/datum/shuttle/multi_shuttle() + VS.origin = locate(/area/shuttle/vox/station) + + VS.destinations = list( + "Fore Starboard Solars" = locate(/area/vox_station/northeast_solars), + "Fore Port Solars" = locate(/area/vox_station/northwest_solars), + "Aft Starboard Solars" = locate(/area/vox_station/southeast_solars), + "Aft Port Solars" = locate(/area/vox_station/southwest_solars), + "Mining asteroid" = locate(/area/vox_station/mining) + ) + + VS.announcer = "NSV Icarus" + VS.arrival_message = "Attention, Exodus, we just tracked a small target bypassing our defensive perimeter. Can't fire on it without hitting the station - you've got incoming visitors, like it or not." + VS.departure_message = "Your guests are pulling away, Exodus - moving too fast for us to draw a bead on them. Looks like they're heading out of the system at a rapid clip." + VS.interim = locate(/area/vox_station/transit) + + VS.warmup_time = 0 + shuttles["Vox Skipjack"] = VS + + //Nuke Ops shuttle. + var/datum/shuttle/multi_shuttle/MS = new/datum/shuttle/multi_shuttle() + MS.origin = locate(/area/syndicate_station/start) + + MS.destinations = list( + "Northwest of the station" = locate(/area/syndicate_station/northwest), + "North of the station" = locate(/area/syndicate_station/north), + "Northeast of the station" = locate(/area/syndicate_station/northeast), + "Southwest of the station" = locate(/area/syndicate_station/southwest), + "South of the station" = locate(/area/syndicate_station/south), + "Southeast of the station" = locate(/area/syndicate_station/southeast), + "Telecomms Satellite" = locate(/area/syndicate_station/commssat), + "Mining Asteroid" = locate(/area/syndicate_station/mining) + ) + + MS.announcer = "NSV Icarus" + MS.arrival_message = "Attention, Exodus, you have a large signature approaching the station - looks unarmed to surface scans. We're too far out to intercept - brace for visitors." + MS.departure_message = "Your visitors are on their way out of the system, Exodus, burning delta-v like it's nothing. Good riddance." + MS.interim = locate(/area/syndicate_station/transit) + + MS.warmup_time = 0 + shuttles["Syndicate"] = MS + + +//This is called by gameticker after all the machines and radio frequencies have been properly initialized +/datum/shuttle_controller/proc/setup_shuttle_docks() + var/datum/shuttle/shuttle + var/datum/shuttle/ferry/multidock/multidock + var/list/dock_controller_map = list() //so we only have to iterate once through each list + + //multidock shuttles + var/list/dock_controller_map_station = list() + var/list/dock_controller_map_offsite = list() + + for (var/shuttle_tag in shuttles) + shuttle = shuttles[shuttle_tag] + if (shuttle.docking_controller_tag) + dock_controller_map[shuttle.docking_controller_tag] = shuttle + if (istype(shuttle, /datum/shuttle/ferry/multidock)) + multidock = shuttle + dock_controller_map_station[multidock.docking_controller_tag_station] = multidock + dock_controller_map_offsite[multidock.docking_controller_tag_offsite] = multidock + + //escape pod arming controllers + var/datum/shuttle/ferry/escape_pod/pod + var/list/pod_controller_map = list() + for (var/datum/shuttle/ferry/escape_pod/P in emergency_shuttle.escape_pods) + if (P.dock_target_station) + pod_controller_map[P.dock_target_station] = P + + //search for the controllers, if we have one. + if (dock_controller_map.len) + for (var/obj/machinery/embedded_controller/radio/C in machines) //only radio controllers are supported at the moment + if (istype(C.program, /datum/computer/file/embedded_program/docking)) + if (C.id_tag in dock_controller_map) + shuttle = dock_controller_map[C.id_tag] + shuttle.docking_controller = C.program + dock_controller_map -= C.id_tag + + //escape pods + if(istype(C, /obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod) && istype(shuttle, /datum/shuttle/ferry/escape_pod)) + var/obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod/EPC = C + EPC.pod = shuttle + + if (C.id_tag in dock_controller_map_station) + multidock = dock_controller_map_station[C.id_tag] + if (istype(multidock)) + multidock.docking_controller_station = C.program + dock_controller_map_station -= C.id_tag + if (C.id_tag in dock_controller_map_offsite) + multidock = dock_controller_map_offsite[C.id_tag] + if (istype(multidock)) + multidock.docking_controller_offsite = C.program + dock_controller_map_offsite -= C.id_tag + + //escape pods + if (C.id_tag in pod_controller_map) + pod = pod_controller_map[C.id_tag] + if (istype(C.program, /datum/computer/file/embedded_program/docking/simple/escape_pod/)) + pod.arming_controller = C.program + + //sanity check + if (dock_controller_map.len || dock_controller_map_station.len || dock_controller_map_offsite.len) + var/dat = "" + for (var/dock_tag in dock_controller_map + dock_controller_map_station + dock_controller_map_offsite) + dat += "\"[dock_tag]\", " + world << "\red \b warning: shuttles with docking tags [dat] could not find their controllers!" + + //makes all shuttles docked to something at round start go into the docked state + for (var/shuttle_tag in shuttles) + shuttle = shuttles[shuttle_tag] + shuttle.dock() diff --git a/code/controllers/verbs.dm b/code/controllers/verbs.dm index a1d7ea25..b030b0b9 100644 --- a/code/controllers/verbs.dm +++ b/code/controllers/verbs.dm @@ -1,7 +1,28 @@ //TODO: rewrite and standardise all controller datums to the datum/controller type //TODO: allow all controllers to be deleted for clean restarts (see WIP master controller stuff) - MC done - lighting done -/client/proc/restart_controller(controller in list("Master","Failsafe","Lighting","Supply Shuttle")) +/*/client/proc/show_distribution_map() + set category = "Debug" + set name = "Show Distribution Map" + set desc = "Print the asteroid ore distribution map to the world." + + if(!holder) return + + if(master_controller && master_controller.asteroid_ore_map) + master_controller.asteroid_ore_map.print_distribution_map() + +/client/proc/remake_distribution_map() + set category = "Debug" + set name = "Remake Distribution Map" + set desc = "Rebuild the asteroid ore distribution map." + + if(!holder) return + + if(master_controller && master_controller.asteroid_ore_map) + master_controller.asteroid_ore_map = new /datum/ore_distribution() + master_controller.asteroid_ore_map.populate_distribution_map()*/ + +/client/proc/restart_controller(controller in list("Master","Failsafe","Lighting","Supply")) set category = "Debug" set name = "Restart Controller" set desc = "Restart one of the various periodic loop controllers for the game (be careful!)" @@ -21,15 +42,14 @@ new /datum/controller/lighting() lighting_controller.process() feedback_add_details("admin_verb","RLighting") - if("Supply Shuttle") - supply_shuttle.process() + if("Supply") + supply_controller.process() feedback_add_details("admin_verb","RSupply") message_admins("Admin [key_name_admin(usr)] has restarted the [controller] controller.") msg_scopes("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 Shuttle","Emergency Shuttle","Configuration","pAI", "Cameras", "Transfer Controller", "Delta Level")) +/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!)" @@ -60,9 +80,12 @@ if("Radio") debug_variables(radio_controller) feedback_add_details("admin_verb","DRadio") - if("Supply Shuttle") - debug_variables(supply_shuttle) + if("Supply") + debug_variables(supply_controller) feedback_add_details("admin_verb","DSupply") + if("Shuttles") + debug_variables(shuttle_controller) + feedback_add_details("admin_verb","DShuttles") if("Emergency Shuttle") debug_variables(emergency_shuttle) feedback_add_details("admin_verb","DEmergency") @@ -81,5 +104,8 @@ if("Delta Level") debug_variables(delta_level) feedback_add_details("admin_verb","DDeltaLevel") + 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/browser.dm b/code/datums/browser.dm index cee5acdd..09281bf1 100644 --- a/code/datums/browser.dm +++ b/code/datums/browser.dm @@ -13,6 +13,7 @@ var/body_elements var/head_content = "" var/content = "" + var/title_buttons = "" /datum/browser/New(nuser, nwindow_id, ntitle = 0, nwidth = 0, nheight = 0, var/atom/nref = null) @@ -29,9 +30,15 @@ ref = nref add_stylesheet("common", 'html/browser/common.css') // this CSS sheet is common to all UIs +/datum/browser/proc/set_title(ntitle) + title = format_text(ntitle) + /datum/browser/proc/add_head_content(nhead_content) head_content = nhead_content +/datum/browser/proc/set_title_buttons(ntitle_buttons) + title_buttons = ntitle_buttons + /datum/browser/proc/set_window_options(nwindow_options) window_options = nwindow_options @@ -75,7 +82,7 @@
- [title ? "
[title]
" : ""] + [title ? "
[title]
[title_buttons]
" : ""]
"} @@ -137,7 +144,7 @@ // Otherwise, the user mob's machine var will be reset directly. // /proc/onclose(mob/user, windowid, var/atom/ref=null) - if(!user.client) return + if(!user || !user.client) return var/param = "null" if(ref) param = "\ref[ref]" @@ -159,11 +166,10 @@ //world << "windowclose: [atomref]" if(atomref!="null") // if passed a real atomref var/hsrc = locate(atomref) // find the reffed atom - var/href = "close=1" if(hsrc) //world << "[src] Topic [href] [hsrc]" usr = src.mob - src.Topic(href, params2list(href), hsrc) // this will direct to the atom's + src.Topic("close=1", list("close"="1"), hsrc) // this will direct to the atom's return // Topic() proc via client.Topic() // no atomref specified (or not found) diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm index c039d862..bcf8f76a 100644 --- a/code/datums/datacore.dm +++ b/code/datums/datacore.dm @@ -119,7 +119,7 @@ L.fields["b_dna"] = H.dna.unique_enzymes L.fields["enzymes"] = H.dna.SE // Used in respawning L.fields["identity"] = H.dna.UI // " - L.fields["image"] = getFlatIcon(H,0) //This is god-awful + L.fields["image"] = getFlatIcon(H) //This is god-awful locked += L return @@ -146,7 +146,7 @@ proc/get_id_photo(var/mob/living/carbon/human/H) if(E.status & ORGAN_ROBOT) temp.MapColors(rgb(77,77,77), rgb(150,150,150), rgb(28,28,28), rgb(0,0,0)) preview_icon.Blend(temp, ICON_OVERLAY) - + //Tail if(H.species.tail && H.species.flags & HAS_TAIL) temp = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "[H.species.tail]_s") diff --git a/code/datums/helper_datums/teleport.dm b/code/datums/helper_datums/teleport.dm index ebe9e6b3..9092cfc1 100644 --- a/code/datums/helper_datums/teleport.dm +++ b/code/datums/helper_datums/teleport.dm @@ -108,12 +108,19 @@ playSpecials(curturf,effectin,soundin) + var/obj/structure/stool/bed/chair/C = null + if(isliving(teleatom)) + var/mob/living/L = teleatom + if(L.buckled) + C = L.buckled if(force_teleport) teleatom.forceMove(destturf) playSpecials(destturf,effectout,soundout) else if(teleatom.Move(destturf)) playSpecials(destturf,effectout,soundout) + if(C) + C.forceMove(destturf) destarea.Entered(teleatom) diff --git a/code/datums/mixed.dm b/code/datums/mixed.dm index 79896e38..1d8228a6 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 d84c7ba3..acb9f645 100644 --- a/code/datums/supplypacks.dm +++ b/code/datums/supplypacks.dm @@ -1207,7 +1207,7 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee /* *Skull's additions to satisfy the cravings of any atmospheric tech begin here. */ - +/* /datum/supply_packs/atmos_freezer name = "Gas Cooling Unit" contains = list(/obj/machinery/atmospherics/unary/cold_sink/freezer/cargo) @@ -1225,7 +1225,7 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee containername = "gas heating unit crate" access = access_atmospherics group = "Engineering" - +*/ ///datum/supply_packs/atmos_airtank // name = "Large Air Tank" // contains = list() //MAKE A LARGE RED AIRTANK THAT IS UNWRENCHED, AND CAN BE MANIPULATED AT SPAWN. This may cause issues, hueh. diff --git a/code/defines/obj.dm b/code/defines/obj.dm index 21f0a82a..a46c4813 100644 --- a/code/defines/obj.dm +++ b/code/defines/obj.dm @@ -303,9 +303,10 @@ var/global/list/PDA_Manifest = list() throw_speed = 1 throw_range = 20 flags = FPRINT | TABLEPASS | CONDUCT + afterattack(atom/target as mob|obj|turf|area, mob/user as mob) user.drop_item() - src.throw_at(target, throw_range, throw_speed) + src.throw_at(target, throw_range, throw_speed, user) /obj/effect/stop var/victim = null diff --git a/code/defines/obj/hydro.dm b/code/defines/obj/hydro.dm index a959345c..d267fdcc 100644 --- a/code/defines/obj/hydro.dm +++ b/code/defines/obj/hydro.dm @@ -1369,7 +1369,7 @@ force = 5.0 throwforce = 7.0 w_class = 2.0 - m_amt = 50 + matter = list("metal" = 50) attack_verb = list("slashed", "sliced", "cut", "clawed") // ************************************* diff --git a/code/defines/obj/weapon.dm b/code/defines/obj/weapon.dm index e8a72f1d..0ecd66e6 100644 --- a/code/defines/obj/weapon.dm +++ b/code/defines/obj/weapon.dm @@ -20,7 +20,7 @@ opacity = 0 density = 0 anchored = 0.0 - var/matter = 0 + var/stored_matter = 0 var/mode = 1 flags = TABLEPASS w_class = 3.0 @@ -105,7 +105,7 @@ force = 5.0 throwforce = 7.0 w_class = 2.0 - m_amt = 50 + matter = list("metal" = 50) attack_verb = list("bludgeoned", "whacked", "disciplined", "thrashed") /obj/item/weapon/disk @@ -221,7 +221,7 @@ icon = 'icons/obj/items.dmi' icon_state = "rack_parts" flags = FPRINT | TABLEPASS| CONDUCT - m_amt = 3750 + matter = list("metal" = 3750) /obj/item/weapon/shard name = "shard" @@ -234,7 +234,7 @@ force = 5.0 throwforce = 8.0 item_state = "shard-glass" - g_amt = 3750 + matter = list("metal" = 3750) attack_verb = list("stabbed", "slashed", "sliced", "cut") suicide_act(mob/user) @@ -262,7 +262,7 @@ item_state = "radio" throw_speed = 4 throw_range = 20 - m_amt = 100 + matter = list("metal" = 100 origin_tech = "magnets=2;syndicate=3"*/ /obj/item/weapon/shard/shrapnel @@ -304,7 +304,7 @@ w_class = 2.0 throw_speed = 4 throw_range = 20 - m_amt = 100 + matter = list("metal" = 100) origin_tech = "magnets=1" /obj/item/weapon/staff @@ -352,7 +352,7 @@ gender = PLURAL icon = 'icons/obj/items.dmi' icon_state = "table_parts" - m_amt = 3750 + matter = list("metal" = 3750) flags = FPRINT | TABLEPASS| CONDUCT attack_verb = list("slammed", "bashed", "battered", "bludgeoned", "thrashed", "whacked") @@ -361,7 +361,7 @@ desc = "Hard table parts. Well...harder..." icon = 'icons/obj/items.dmi' icon_state = "reinf_tableparts" - m_amt = 7500 + matter = list("metal" = 7500) flags = FPRINT | TABLEPASS| CONDUCT /obj/item/weapon/table_parts/wood @@ -378,7 +378,7 @@ var/amount = 1.0 var/laying = 0.0 var/old_lay = null - m_amt = 40 + matter = list("metal" = 40) attack_verb = list("whipped", "lashed", "disciplined", "tickled") suicide_act(mob/user) @@ -402,8 +402,14 @@ name = "power control module" icon_state = "power_mod" desc = "Heavy-duty switching circuits for power control." - m_amt = 50 - g_amt = 50 + matter = list("metal" = 50, "glass" = 50) + +/obj/item/weapon/module/power_control/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + if (istype(W, /obj/item/device/multitool)) + var/obj/item/weapon/circuitboard/ghettosmes/newcircuit = new/obj/item/weapon/circuitboard/ghettosmes(user.loc) + del(src) + user.put_in_hands(newcircuit) + /obj/item/weapon/module/id_auth name = "\improper ID authentication module" @@ -478,7 +484,7 @@ throwforce = 15.0 throw_speed = 4 throw_range = 4 - m_amt = 15000 + matter = list("metal" = 15000) origin_tech = "materials=2;combat=1" attack_verb = list("chopped", "torn", "cut") @@ -570,45 +576,42 @@ desc = "Used in the construction of computers and other devices with a interactive console." icon_state = "screen" origin_tech = "materials=1" - g_amt = 200 + matter = list("glass" = 200) /obj/item/weapon/stock_parts/capacitor name = "capacitor" desc = "A basic capacitor used in the construction of a variety of devices." icon_state = "capacitor" origin_tech = "powerstorage=1" - m_amt = 50 - g_amt = 50 + matter = list("metal" = 50, "glass" = 50) /obj/item/weapon/stock_parts/scanning_module name = "scanning module" desc = "A compact, high resolution scanning module used in the construction of certain devices." icon_state = "scan_module" origin_tech = "magnets=1" - m_amt = 50 - g_amt = 20 + matter = list("metal" = 50, "glass" = 20) /obj/item/weapon/stock_parts/manipulator name = "micro-manipulator" desc = "A tiny little manipulator used in the construction of certain devices." icon_state = "micro_mani" origin_tech = "materials=1;programming=1" - m_amt = 30 + matter = list("metal" = 30) /obj/item/weapon/stock_parts/micro_laser name = "micro-laser" desc = "A tiny laser used in certain devices." icon_state = "micro_laser" origin_tech = "magnets=1" - m_amt = 10 - g_amt = 20 + matter = list("metal" = 10, "glass" = 20) /obj/item/weapon/stock_parts/matter_bin name = "matter bin" desc = "A container for hold compressed matter awaiting re-construction." icon_state = "matter_bin" origin_tech = "materials=1" - m_amt = 80 + matter = list("metal" = 80) //Rank 2 @@ -617,8 +620,7 @@ desc = "An advanced capacitor used in the construction of a variety of devices." origin_tech = "powerstorage=3" rating = 2 - m_amt = 50 - g_amt = 50 + matter = list("metal" = 50, "glass" = 50) /obj/item/weapon/stock_parts/scanning_module/adv name = "advanced scanning module" @@ -626,8 +628,7 @@ icon_state = "scan_module" origin_tech = "magnets=3" rating = 2 - m_amt = 50 - g_amt = 20 + matter = list("metal" = 50, "glass" = 20) /obj/item/weapon/stock_parts/manipulator/nano name = "nano-manipulator" @@ -635,7 +636,7 @@ icon_state = "nano_mani" origin_tech = "materials=3,programming=2" rating = 2 - m_amt = 30 + matter = list("metal" = 30) /obj/item/weapon/stock_parts/micro_laser/high name = "high-power micro-laser" @@ -643,8 +644,7 @@ icon_state = "high_micro_laser" origin_tech = "magnets=3" rating = 2 - m_amt = 10 - g_amt = 20 + matter = list("metal" = 10, "glass" = 20) /obj/item/weapon/stock_parts/matter_bin/adv name = "advanced matter bin" @@ -652,7 +652,7 @@ icon_state = "advanced_matter_bin" origin_tech = "materials=3" rating = 2 - m_amt = 80 + matter = list("metal" = 80) //Rating 3 @@ -661,16 +661,14 @@ desc = "A super-high capacity capacitor used in the construction of a variety of devices." origin_tech = "powerstorage=5;materials=4" rating = 3 - m_amt = 50 - g_amt = 50 + matter = list("metal" = 50, "glass" = 50) /obj/item/weapon/stock_parts/scanning_module/phasic name = "phasic scanning module" desc = "A compact, high resolution phasic scanning module used in the construction of certain devices." origin_tech = "magnets=5" rating = 3 - m_amt = 50 - g_amt = 20 + matter = list("metal" = 50, "glass" = 20) /obj/item/weapon/stock_parts/manipulator/pico name = "pico-manipulator" @@ -678,7 +676,7 @@ icon_state = "pico_mani" origin_tech = "materials=5,programming=2" rating = 3 - m_amt = 30 + matter = list("metal" = 30) /obj/item/weapon/stock_parts/micro_laser/ultra name = "ultra-high-power micro-laser" @@ -686,8 +684,7 @@ desc = "A tiny laser used in certain devices." origin_tech = "magnets=5" rating = 3 - m_amt = 10 - g_amt = 20 + matter = list("metal" = 10, "glass" = 20) /obj/item/weapon/stock_parts/matter_bin/super name = "super matter bin" @@ -695,7 +692,7 @@ icon_state = "super_matter_bin" origin_tech = "materials=5" rating = 3 - m_amt = 80 + matter = list("metal" = 80) // Subspace stock parts @@ -704,54 +701,49 @@ icon_state = "subspace_ansible" desc = "A compact module capable of sensing extradimensional activity." origin_tech = "programming=3;magnets=5;materials=4;bluespace=2" - m_amt = 30 - g_amt = 10 + matter = list("metal" = 30, "glass" = 10) /obj/item/weapon/stock_parts/subspace/filter name = "hyperwave filter" icon_state = "hyperwave_filter" desc = "A tiny device capable of filtering and converting super-intense radiowaves." origin_tech = "programming=4;magnets=2" - m_amt = 30 - g_amt = 10 + matter = list("metal" = 30, "glass" = 10) /obj/item/weapon/stock_parts/subspace/amplifier name = "subspace amplifier" icon_state = "subspace_amplifier" desc = "A compact micro-machine capable of amplifying weak subspace transmissions." origin_tech = "programming=3;magnets=4;materials=4;bluespace=2" - m_amt = 30 - g_amt = 10 + matter = list("metal" = 30, "glass" = 10) /obj/item/weapon/stock_parts/subspace/treatment name = "subspace treatment disk" icon_state = "treatment_disk" desc = "A compact micro-machine capable of stretching out hyper-compressed radio waves." origin_tech = "programming=3;magnets=2;materials=5;bluespace=2" - m_amt = 30 - g_amt = 10 + matter = list("metal" = 30, "glass" = 10) /obj/item/weapon/stock_parts/subspace/analyzer name = "subspace wavelength analyzer" icon_state = "wavelength_analyzer" desc = "A sophisticated analyzer capable of analyzing cryptic subspace wavelengths." origin_tech = "programming=3;magnets=4;materials=4;bluespace=2" - m_amt = 30 - g_amt = 10 + matter = list("metal" = 30, "glass" = 10) /obj/item/weapon/stock_parts/subspace/crystal name = "ansible crystal" icon_state = "ansible_crystal" desc = "A crystal made from pure glass used to transmit laser databursts to subspace." origin_tech = "magnets=4;materials=4;bluespace=2" - g_amt = 50 + matter = list("glass" = 50) /obj/item/weapon/stock_parts/subspace/transmitter name = "subspace transmitter" icon_state = "subspace_transmitter" desc = "A large piece of equipment used to open a window into the subspace dimension." origin_tech = "magnets=5;materials=5;bluespace=3" - m_amt = 50 + matter = list("metal" = 50) /obj/item/weapon/ectoplasm name = "ectoplasm" diff --git a/code/defines/procs/captain_announce.dm b/code/defines/procs/captain_announce.dm index 94fed997..2c306d2f 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 68a7cf25..9269e20c 100644 --- 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" @@ -627,6 +648,14 @@ var/list/ghostteleportlocs = list() name = "Medbay Maintenance" icon_state = "asmaint" +/area/maintenance/engi_shuttle + name = "Engineering Shuttle Access" + icon_state = "asmaint" + +/area/maintenance/engi_engine + name = "Engine Maintenance" + icon_state = "asmaint" + /area/maintenance/asmaint2 name = "Science Maintenance" icon_state = "asmaint" @@ -791,6 +820,10 @@ var/list/ghostteleportlocs = list() name = "\improper Engineering Dormitories" icon_state = "Sleep" +/area/crew_quarters/sleep/engi_wash + name = "\improper Engineering Washroom" + icon_state = "toilet" + /area/crew_quarters/sleep/sec name = "\improper Security Dormitories" icon_state = "Sleep" @@ -858,6 +891,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" @@ -963,6 +997,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" @@ -1019,7 +1057,8 @@ var/list/ghostteleportlocs = list() //Solars /area/solar - requires_power = 0 + requires_power = 1 + always_unpowered = 1 luminosity = 1 lighting_use_dynamic = 0 @@ -1099,6 +1138,7 @@ var/list/ghostteleportlocs = list() name = "\improper AI Satellite Teleporter Room" icon_state = "teleporter" music = "signal" + ambience = list('sound/ambience/ambimalf.ogg') //MedBay @@ -1189,6 +1229,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" @@ -1616,10 +1657,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" @@ -1628,6 +1671,11 @@ 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" + icon_state = "ai_cyborg" /area/turret_protected/aisat name = "\improper AI Satellite" @@ -1694,6 +1742,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" @@ -1706,18 +1756,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 da6adcf2..328dc566 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -71,10 +71,28 @@ /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) - //updateicon() - //mouse_opacity = 0 - if (danger_level==2) + 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) + C.network.Remove("Atmosphere Alarms") + for(var/mob/living/silicon/aiPlayer in player_list) + 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) //updateicon() @@ -85,18 +103,39 @@ aiPlayer.triggerAlarm("Atmosphere", src, cameras, src) for(var/obj/machinery/computer/station_alert/a in machines) a.triggerAlarm("Atmosphere", src, cameras, src) - else if (atmosalm == 2) - for(var/area/RA in related) - for(var/obj/machinery/camera/C in RA) - C.network.Remove("Atmosphere Alarms") - for(var/mob/living/silicon/aiPlayer in player_list) - aiPlayer.cancelAlarm("Atmosphere", src, src) - for(var/obj/machinery/computer/station_alert/a in machines) - a.cancelAlarm("Atmosphere", src, 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 +/area/proc/air_doors_close() + if(!src.master.air_doors_activated) + src.master.air_doors_activated = 1 + for(var/obj/machinery/door/firedoor/E in src.master.all_doors) + if(!E:blocked) + if(E.operating) + E:nextstate = CLOSED + else if(!E.density) + spawn(0) + E.close() + +/area/proc/air_doors_open() + if(src.master.air_doors_activated) + src.master.air_doors_activated = 0 + for(var/obj/machinery/door/firedoor/E in src.master.all_doors) + if(!E:blocked) + if(E.operating) + E:nextstate = OPEN + else if(E.density) + spawn(0) + E.open() + + /area/proc/firealert() if(name == "Space") //no fire alarms in space return @@ -144,8 +183,6 @@ a.cancelAlarm("Fire", src, src) /area/proc/readyalert() - if(name == "Space") - return if(!eject) eject = 1 updateicon() @@ -158,8 +195,6 @@ return /area/proc/partyalert() - if(name == "Space") //no parties in space!!! - return if (!( party )) party = 1 updateicon() @@ -284,43 +319,23 @@ 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) -/area/proc/gravitychange(var/gravitystate = 0, var/area/A, var/round_start = 0) A.has_gravity = gravitystate for(var/area/SubA in A.related) SubA.has_gravity = gravitystate - if(!round_start) - if(gravitystate) - for(var/mob/living/carbon/human/M in SubA) - thunk(M) + if(gravitystate) + for(var/mob/living/carbon/human/M in SubA) + thunk(M) /area/proc/thunk(mob) if(istype(mob,/mob/living/carbon/human/)) // Only humans can wear magboots, so we give them a chance to. diff --git a/code/game/atoms.dm b/code/game/atoms.dm index ca6760c7..03e64bf2 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -23,27 +23,6 @@ //Detective Work, used for the duplicate data points kept in the scanners var/list/original_atom -/atom/proc/throw_impact(atom/hit_atom, var/speed) - if(istype(hit_atom,/mob/living)) - var/mob/living/M = hit_atom - M.hitby(src,speed) - - else if(isobj(hit_atom)) - var/obj/O = hit_atom - if(!O.anchored) - step(O, src.dir) - O.hitby(src,speed) - - else if(isturf(hit_atom)) - var/turf/T = hit_atom - if(T.density) - spawn(2) - step(src, turn(src.dir, 180)) - if(istype(src,/mob/living)) - var/mob/living/M = src - M.take_organ_damage(20) - - /atom/proc/assume_air(datum/gas_mixture/giver) return null @@ -238,6 +217,8 @@ its easier to just keep the beam vertical. return /atom/proc/hitby(atom/movable/AM as mob|obj) + if (density) + AM.throwing = 0 return /atom/proc/add_hiddenprint(mob/living/M as mob) diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index a9b11922..1ef754a1 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -7,6 +7,8 @@ var/l_move_time = 1 var/m_flag = 1 var/throwing = 0 + var/thrower + var/turf/throw_source = null var/throw_speed = 2 var/throw_range = 7 var/moved_recently = 0 @@ -15,8 +17,8 @@ /atom/movable/Move() var/atom/A = src.loc . = ..() - src.move_speed = world.timeofday - src.l_move_time - src.l_move_time = world.timeofday + src.move_speed = world.time - src.l_move_time + src.l_move_time = world.time src.m_flag = 1 if ((A != src.loc && A && A.z == src.z)) src.last_move = get_dir(A, src.loc) @@ -25,7 +27,6 @@ /atom/movable/Bump(var/atom/A as mob|obj|turf|area, yes) if(src.throwing) src.throw_impact(A) - src.throwing = 0 spawn( 0 ) if ((A && yes)) @@ -44,6 +45,29 @@ return 1 return 0 +//called when src is thrown into hit_atom +/atom/movable/proc/throw_impact(atom/hit_atom, var/speed) + if(istype(hit_atom,/mob/living)) + var/mob/living/M = hit_atom + M.hitby(src,speed) + + else if(isobj(hit_atom)) + var/obj/O = hit_atom + if(!O.anchored) + step(O, src.dir) + O.hitby(src,speed) + + else if(isturf(hit_atom)) + src.throwing = 0 + var/turf/T = hit_atom + if(T.density) + spawn(2) + step(src, turn(src.dir, 180)) + if(istype(src,/mob/living)) + var/mob/living/M = src + M.turf_collision(T, speed) + +//decided whether a movable atom being thrown can pass through the turf it is in. /atom/movable/proc/hit_check(var/speed) if(src.throwing) for(var/atom/A in get_turf(src)) @@ -51,18 +75,17 @@ if(istype(A,/mob/living)) if(A:lying) continue src.throw_impact(A,speed) - if(src.throwing == 1) - src.throwing = 0 if(isobj(A)) if(A.density && !A.throwpass) // **TODO: Better behaviour for windows which are dense, but shouldn't always stop movement src.throw_impact(A,speed) - src.throwing = 0 -/atom/movable/proc/throw_at(atom/target, range, speed) +/atom/movable/proc/throw_at(atom/target, range, speed, thrower) if(!target || !src) return 0 //use a modified version of Bresenham's algorithm to get from the atom's current position to that of the target src.throwing = 1 + src.thrower = thrower + src.throw_source = get_turf(src) //store the origin turf if(usr) if(HULK in usr.mutations) @@ -149,8 +172,10 @@ a = get_area(src.loc) //done throwing, either because it hit something or it finished moving - src.throwing = 0 if(isobj(src)) src.throw_impact(get_turf(src),speed) + src.throwing = 0 + src.thrower = null + src.throw_source = null //Overlays diff --git a/code/game/dna/dna2_domutcheck.dm b/code/game/dna/dna2_domutcheck.dm index 954c86f2..02495bbc 100644 --- a/code/game/dna/dna2_domutcheck.dm +++ b/code/game/dna/dna2_domutcheck.dm @@ -6,7 +6,7 @@ #define MUTCHK_FORCED 1 /proc/domutcheck(var/mob/living/M, var/connected=null, var/flags=0) for(var/datum/dna/gene/gene in dna_genes) - if(!M) + if(!M || !M.dna) return if(!gene.block) continue diff --git a/code/game/dna/dna_modifier.dm b/code/game/dna/dna_modifier.dm index ff8faf2a..b52682fc 100644 --- a/code/game/dna/dna_modifier.dm +++ b/code/game/dna/dna_modifier.dm @@ -17,6 +17,7 @@ var/ckey=null var/mind=null var/languages=null + var/list/flavor=null /datum/dna2/record/proc/GetData() var/list/ser=list("data" = null, "owner" = null, "label" = null, "type" = null, "ue" = 0) @@ -276,15 +277,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 +346,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 +426,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/dna/genes/monkey.dm b/code/game/dna/genes/monkey.dm index 444e56a5..69aba349 100644 --- a/code/game/dna/genes/monkey.dm +++ b/code/game/dna/genes/monkey.dm @@ -160,6 +160,7 @@ continue else O.real_name = randomname + O.dna.real_name = randomname i++ O.UpdateAppearance() O.take_overall_damage(M.getBruteLoss(), M.getFireLoss()) diff --git a/code/game/gamemodes/events/ninja_equipment.dm b/code/game/gamemodes/events/ninja_equipment.dm index b4264f24..2b701caf 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/plasma_level = environment.toxins/total_moles - var/unknown_level = 1-(o2_level+n2_level+co2_level+plasma_level) dat += "
    " - dat += "
  • Nitrogen: [round(n2_level*100)]%
  • " - dat += "
  • Oxygen: [round(o2_level*100)]%
  • " - dat += "
  • Carbon Dioxide: [round(co2_level*100)]%
  • " - dat += "
  • Plasma: [round(plasma_level*100)]%
  • " + for(var/g in environment.gas) + dat += "
  • [gas_data.name[g]]: [round((environment.gas[g] / total_moles) * 100)]%
  • " dat += "
" - if(unknown_level > 0.01) - dat += "OTHER: [round(unknown_level)]%
" dat += "Temperature: [round(environment.temperature-T0C)]°C" if(2) @@ -519,7 +510,7 @@ ________________________________________________________________________________ var/damage = min(cell.charge, rand(50,150))//Uses either the current energy left over or between 50 and 150. if(damage>1)//So they don't spam it when energy is a factor. spark_system.start()//SPARKS THERE SHALL BE SPARKS - U.electrocute_act(damage, src,0.1,1)//The last argument is a safety for the human proc that checks for gloves. + U.electrocute_act(damage, src, 0.1) if(cell.charge < damage) cell.use(cell.charge) else @@ -634,17 +625,21 @@ ________________________________________________________________________________ pai.attack_self(U) if("Eject pAI") - var/turf/T = get_turf(loc) - if(!U.get_active_hand()) - U.put_in_hands(pai) - pai.add_fingerprint(U) - pai = null - else - if(T) - pai.loc = T + if(pai) + if(pai.loc != src) pai = null else - U << "\red ERROR: \black Could not eject pAI card." + var/turf/T = get_turf(loc) + if(!U.get_active_hand()) + U.put_in_hands(pai) + pai.add_fingerprint(U) + pai = null + else + if(T) + pai.loc = T + pai = null + else + U << "\red ERROR: \black Could not eject pAI card." if("Override AI Laws") var/law_zero = A.laws.zeroth//Remembers law zero, if there is one. @@ -1040,8 +1035,7 @@ ________________________________________________________________________________ drain = rand(G.mindrain,G.maxdrain) var/drained = 0 if(PN&&do_after(U,10)) - drained = min(drain, PN.avail) - PN.newload += drained + drained = PN.draw_power(drain) if(drained < drain)//if no power on net, drain apcs for(var/obj/machinery/power/terminal/T in PN.nodes) if(istype(T.master, /obj/machinery/power/apc)) @@ -1092,8 +1086,7 @@ ________________________________________________________________________________ drain = (round((rand(G.mindrain,G.maxdrain))/2)) var/drained = 0 if(PN&&do_after(U,10)) - drained = min(drain, PN.avail) - PN.newload += drained + drained = PN.draw_power(drain) if(drained < drain)//if no power on net, drain apcs for(var/obj/machinery/power/terminal/T in PN.nodes) if(istype(T.master, /obj/machinery/power/apc)) @@ -1377,19 +1370,17 @@ It is possible to destroy the net by the occupant or someone else. playsound(M.loc, 'sound/effects/sparks2.ogg', 50, 1) anim(M.loc,M,'icons/mob/mob.dmi',,"phasein",,M.dir) del(src)//Wait for everything to finish, delete the net. Else it will stop everything once net is deleted, including the spawn(0). - else - M.loc = null - M << "\red You appear in a strange place!" + M << "\red You appear in a strange place!" - for(var/mob/O in viewers(src, 3)) - O.show_message(text("[] vanished!", M), 1, text("You hear sparks flying!"), 2) + for(var/mob/O in viewers(src, 3)) + O.show_message(text("[] vanished!", M), 1, text("You hear sparks flying!"), 2) - if(!isnull(master))//As long as they still exist. - master << "\blue SUCCESS: \black transport procedure of \the [affecting] complete." + if(!isnull(master))//As long as they still exist. + master << "\blue SUCCESS: \black transport procedure of \the [affecting] complete." - M.captured = 0 //Important. - M.anchored = initial(M.anchored) //Changes the mob's anchored status to the original one; this is not handled by the can_move proc. + M.captured = 0 //Important. + M.anchored = initial(M.anchored) //Changes the mob's anchored status to the original one; this is not handled by the can_move proc. else//And they are free. M << "\blue You are free of the net!" diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index 577bb9b1..acb858c8 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -131,10 +131,12 @@ Implants; /datum/game_mode/proc/check_finished() //to be called by ticker - if(emergency_shuttle.location==2 || station_was_nuked) + if(emergency_shuttle.returned() || station_was_nuked) return 1 return 0 +/datum/game_mode/proc/cleanup() //This is called when the round has ended but not the game, if any cleanup would be necessary in that case. + return /datum/game_mode/proc/declare_completion() var/clients = 0 @@ -486,3 +488,28 @@ proc/get_nt_opposed() dudes += man if(dudes.len == 0) return null return pick(dudes) + +//Announces objectives/generic antag text. +/proc/show_generic_antag_text(var/datum/mind/player) + if(player.current) + player.current << \ + "You are an antagonist! 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." + +/proc/show_objectives(var/datum/mind/player) + + if(!player || !player.current) return + + if(config.objectives_disabled) + show_generic_antag_text(player) + return + + var/obj_count = 1 + player.current << "\blue Your current objectives:" + for(var/datum/objective/objective in player.objectives) + player.current << "Objective #[obj_count]: [objective.explanation_text]" + obj_count++ \ No newline at end of file diff --git a/code/game/gamemodes/gameticker.dm b/code/game/gamemodes/gameticker.dm index 4bea30a5..1aac8be0 100644 --- a/code/game/gamemodes/gameticker.dm +++ b/code/game/gamemodes/gameticker.dm @@ -12,6 +12,7 @@ var/global/datum/controller/gameticker/ticker var/hide_mode = 0 var/datum/game_mode/mode = null + var/post_game = 0 var/event_time = null var/event = 0 @@ -36,6 +37,8 @@ var/global/datum/controller/gameticker/ticker var/triai = 0//Global holder for Triumvirate + var/round_end_announced = 0 // Spam Prevention. Announce round end only once. + /datum/controller/gameticker/proc/pregame() login_music = pick(\ /*'sound/music/halloween/skeletons.ogg',\ @@ -132,6 +135,8 @@ var/global/datum/controller/gameticker/ticker //here to initialize the random events nicely at round start setup_economy() + shuttle_controller.setup_shuttle_docks() + spawn(0)//Forking here so we dont have to wait for this to finish mode.post_setup() //Cleanup some stuff @@ -154,7 +159,7 @@ var/global/datum/controller/gameticker/ticker if(admins_number == 0) send2adminirc("Round has started with no admins online.") - supply_shuttle.process() //Start the supply shuttle regenerating points -- TLE + supply_controller.process() //Start the supply shuttle regenerating points -- TLE master_controller.process() //Start master_controller.process() lighting_controller.process() //Start processing DynamicAreaLighting updates @@ -297,6 +302,7 @@ var/global/datum/controller/gameticker/ticker captainless=0 if(player.mind.assigned_role != "MODE") job_master.EquipRank(player, player.mind.assigned_role, 0) + UpdateFactionList(player) EquipCustomItems(player) if(captainless) for(var/mob/M in player_list) @@ -312,10 +318,16 @@ var/global/datum/controller/gameticker/ticker emergency_shuttle.process() - delta_level.process() + var/game_finished = 0 + var/mode_finished = 0 + if (config.continous_rounds) + game_finished = (emergency_shuttle.returned() || mode.station_was_nuked) + mode_finished = (!post_game && mode.check_finished()) + else + game_finished = (mode.check_finished() || (emergency_shuttle.returned() && emergency_shuttle.evac == 1)) + mode_finished = game_finished - var/mode_finished = mode.check_finished() || (emergency_shuttle.location == 2 && emergency_shuttle.alert == 1) - if(!mode.explosion_in_progress && mode_finished) + if(!mode.explosion_in_progress && game_finished && (mode_finished || post_game)) current_state = GAME_STATE_FINISHED spawn @@ -346,6 +358,18 @@ var/global/datum/controller/gameticker/ticker else world << "\blue An admin has delayed the round end" + else if (mode_finished) + post_game = 1 + + mode.cleanup() + + //call a transfer shuttle vote + spawn(50) + if(!round_end_announced) // Spam Prevention. Now it should announce only once. + world << "\red The round has ended!" + round_end_announced = 1 + vote.autotransfer() + return 1 proc/getfactionbyname(var/name) diff --git a/code/game/gamemodes/malfunction/malfunction.dm b/code/game/gamemodes/malfunction/malfunction.dm index 54b9ede6..7fb5a79f 100644 --- a/code/game/gamemodes/malfunction/malfunction.dm +++ b/code/game/gamemodes/malfunction/malfunction.dm @@ -67,7 +67,7 @@ AI_mind.current.icon_state = "ai-malf2" */ if(emergency_shuttle) - emergency_shuttle.always_fake_recall = 1 + emergency_shuttle.auto_recall = 1 spawn (rand(waittime_l, waittime_h)) send_intercept() ..() @@ -136,7 +136,7 @@ if (is_malf_ai_dead()) if(config.continous_rounds) if(emergency_shuttle) - emergency_shuttle.always_fake_recall = 0 + emergency_shuttle.auto_recall = 0 malf_mode_declared = 0 else return 1 @@ -206,7 +206,7 @@ /datum/game_mode/malfunction/declare_completion() var/malf_dead = is_malf_ai_dead() - var/crew_evacuated = (emergency_shuttle.location==2) + var/crew_evacuated = (emergency_shuttle.returned()) if ( station_captured && station_was_nuked) feedback_set_details("round_end_result","win - AI win - nuke") diff --git a/code/game/gamemodes/newobjective.dm b/code/game/gamemodes/newobjective.dm index 9f26b049..7bf13de5 100644 --- a/code/game/gamemodes/newobjective.dm +++ b/code/game/gamemodes/newobjective.dm @@ -267,7 +267,7 @@ datum explanation_text = "Frame [target.current.real_name], the [target.assigned_role] for a crime and make sure they are arrested and brought back to the Centcom station alive. We'll handle the rest from there." check_completion() - if(emergency_shuttle.location<2) + if(!emergency_shuttle.returned()) return 0 if(target.current.stat == 2) return 0 @@ -313,7 +313,7 @@ datum explanation_text = "[target.current.real_name], the [target.assigned_role] is a [pick("relative of a","friend of a","") + pick("high ranking","important","well-liked")] Syndicate [pick("Leader","Officer","Agent","sympathiser")]. Make sure they get off the station safely, while minimizing intervention." check_completion() - if(emergency_shuttle.location<2) + if(!emergency_shuttle.returned()) return 0 if(target.current.stat == 2) @@ -477,7 +477,7 @@ datum explanation_text = "Hijack the emergency shuttle by escaping alone." check_completion() - if(emergency_shuttle.location<2) + if(!emergency_shuttle.returned()) return 0 if(!owner.current || owner.current.stat == 2) @@ -512,7 +512,7 @@ datum explanation_text = "Escape on the shuttle alive, without being arrested." check_completion() - if(emergency_shuttle.location<2) + if(!emergency_shuttle.returned()) return 0 if(!owner.current || owner.current.stat ==2) @@ -1224,7 +1224,7 @@ datum check_completion() if(!istype(owner.current, /mob/living/silicon)) return 0 - if(emergency_shuttle.location<2) + if(!emergency_shuttle.returned()) return 0 if(!owner.current) return 0 @@ -1447,7 +1447,7 @@ datum/objective/silence explanation_text = "Do not allow anyone to escape the station. Only allow the shuttle to be called when everyone is dead and your story is the only one left." check_completion() - if(emergency_shuttle.location<2) + if(!emergency_shuttle.returned()) return 0 var/area/shuttle = locate(/area/shuttle/escape/centcom) diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm index d9d13168..08fcc563 100644 --- a/code/game/gamemodes/nuclear/nuclear.dm +++ b/code/game/gamemodes/nuclear/nuclear.dm @@ -282,7 +282,7 @@ if(!is_type_in_list(disk_area, centcom_areas)) disk_rescued = 0 break - var/crew_evacuated = (emergency_shuttle.location==2) + var/crew_evacuated = (emergency_shuttle.returned()) //var/operatives_are_dead = is_operatives_are_dead() diff --git a/code/game/gamemodes/nuclear/pinpointer.dm b/code/game/gamemodes/nuclear/pinpointer.dm index 27d9986d..240e2fdf 100644 --- a/code/game/gamemodes/nuclear/pinpointer.dm +++ b/code/game/gamemodes/nuclear/pinpointer.dm @@ -8,7 +8,7 @@ item_state = "electronic" throw_speed = 4 throw_range = 20 - m_amt = 500 + matter = list("metal" = 500) var/obj/item/weapon/disk/nuclear/the_disk = null var/active = 0 diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm index 4104a2c0..b0fcd27a 100644 --- a/code/game/gamemodes/objective.dm +++ b/code/game/gamemodes/objective.dm @@ -285,7 +285,7 @@ datum/objective/hijack check_completion() if(!owner.current || owner.current.stat) return 0 - if(emergency_shuttle.location<2) + if(!emergency_shuttle.returned()) return 0 if(issilicon(owner.current)) return 0 @@ -307,7 +307,7 @@ datum/objective/block check_completion() if(!istype(owner.current, /mob/living/silicon)) return 0 - if(emergency_shuttle.location<2) + if(!emergency_shuttle.returned()) return 0 if(!owner.current) return 0 @@ -325,7 +325,7 @@ datum/objective/silence explanation_text = "Do not allow anyone to escape the station. Only allow the shuttle to be called when everyone is dead and your story is the only one left." check_completion() - if(emergency_shuttle.location<2) + if(!emergency_shuttle.returned()) return 0 for(var/mob/living/player in player_list) @@ -350,7 +350,7 @@ datum/objective/escape return 0 if(isbrain(owner.current)) return 0 - if(emergency_shuttle.location<2) + if(!emergency_shuttle.returned()) return 0 if(!owner.current || owner.current.stat ==2) return 0 @@ -557,7 +557,7 @@ datum/objective/steal for(var/obj/item/I in all_items) //Check for plasma tanks if(istype(I, steal_target)) - found_amount += (target_name=="28 moles of plasma (full tank)" ? (I:air_contents:toxins) : (I:amount)) + found_amount += (target_name=="28 moles of plasma (full tank)" ? (I:air_contents:gas["toxins"]) : (I:amount)) return found_amount>=target_amount if("50 coins (in bag)") @@ -721,7 +721,7 @@ datum/objective/absorb explanation_text = "Our knowledge must live on. Make sure at least 5 acolytes escape on the shuttle to spread their work on an another station." check_completion() - if(emergency_shuttle.location<2) + if(!emergency_shuttle.returned()) return 0 var/cultists_escaped = 0 diff --git a/code/game/gamemodes/revolution/revolution.dm b/code/game/gamemodes/revolution/revolution.dm index ea26f403..39b6c3c3 100644 --- a/code/game/gamemodes/revolution/revolution.dm +++ b/code/game/gamemodes/revolution/revolution.dm @@ -91,7 +91,7 @@ greet_revolutionary(rev_mind) modePlayer += head_revolutionaries if(emergency_shuttle) - emergency_shuttle.always_fake_recall = 1 + emergency_shuttle.auto_recall = 1 spawn (rand(waittime_l, waittime_h)) send_intercept() ..() @@ -175,7 +175,7 @@ if(config.continous_rounds) if(finished != 0) if(emergency_shuttle) - emergency_shuttle.always_fake_recall = 0 + emergency_shuttle.auto_recall = 0 return ..() if(finished != 0) return 1 diff --git a/code/game/jobs/job_controller.dm b/code/game/jobs/job_controller.dm index 8a92dcd5..6770c4c5 100644 --- a/code/game/jobs/job_controller.dm +++ b/code/game/jobs/job_controller.dm @@ -346,52 +346,46 @@ var/global/datum/controller/occupations/job_master proc/EquipRank(var/mob/living/carbon/human/H, var/rank, var/joined_late = 0) + if(!H) return 0 + var/datum/job/job = GetJob(rank) + var/list/spawn_in_storage = list() + if(job) + //Equip custom gear loadout. - switch(rank) - if("Cyborg","AI","Clown")//Because yeah - else - if(H.client.prefs.gear && H.client.prefs.gear.len) + if(H.client.prefs.gear && H.client.prefs.gear.len && job.title != "Cyborg" && job.title != "AI") - for(var/thing in H.client.prefs.gear) - var/datum/gear/G = gear_datums[thing] - if(G) - var/permitted - if(G.allowed_roles) - for(var/job_name in G.allowed_roles) - if(job.title == job_name) - permitted = 1 - else + for(var/thing in H.client.prefs.gear) + var/datum/gear/G = gear_datums[thing] + if(G) + var/permitted + if(G.allowed_roles) + for(var/job_name in G.allowed_roles) + if(job.title == job_name) permitted = 1 + else + permitted = 1 - if(G.whitelisted && !is_alien_whitelisted(H, G.whitelisted)) - permitted = 0 + if(G.whitelisted && !is_alien_whitelisted(H, G.whitelisted)) + permitted = 0 + + if(!permitted) + H << "\red Your current job or whitelist status does not permit you to spawn with [thing]!" + continue + + if(G.slot) + H.equip_to_slot_or_del(new G.path(H), G.slot) + H << "\blue Equipping you with [thing]!" + + else + spawn_in_storage += thing - if(!permitted) - H << "\red Your current job or whitelist status does not permit you to spawn with [thing]!" - continue - if(G.slot) - if(!H.equip_to_slot_or_del(new G.path(H), G.slot)) - spawn(6) - if(!H.equip_to_slot_or_del(new G.path(H.back), slot_in_backpack)) - new G.path(H.loc) - else - if(H.backbag) - spawn(6) //I can't beleive this worked: 4 works but I want to give a little time for lags - if(!H.equip_to_slot_or_del(new G.path(H.back), slot_in_backpack)) - new G.path(H.loc) - else - msg_scopes("G.path = [G.path] | This got called, see if [H.name] has it") // If I never see this in game then Yiss - new G.path(H.loc) -// spawn(1) -// new G.path(get_turf(H.loc)) //Equip job items. job.equip(H) else - msg_scopes("[H.mind.name] has rank: [rank] and this thing happened.") H << "Your job is [rank] and the game just can't handle it! Please report this bug to an administrator." H.job = rank @@ -407,7 +401,7 @@ var/global/datum/controller/occupations/job_master S = locate("start*[rank]") // use old stype if(istype(S, /obj/effect/landmark/start) && istype(S.loc, /turf)) H.loc = S.loc - // Moving wheelchair if they have one + // Moving wheelchair if they have one if(H.buckled && istype(H.buckled, /obj/structure/stool/bed/chair/wheelchair)) H.buckled.loc = H.loc H.buckled.dir = H.dir @@ -469,9 +463,45 @@ var/global/datum/controller/occupations/job_master new /obj/item/weapon/storage/box/survival(BPK) H.equip_to_slot_or_del(BPK, slot_back,1) + //Deferred item spawning. + if(spawn_in_storage && spawn_in_storage.len) + var/obj/item/weapon/storage/B + for(var/obj/item/weapon/storage/S in H.contents) + B = S + break + + if(!isnull(B)) + for(var/thing in spawn_in_storage) + H << "\blue Placing [thing] in your [B]!" + var/datum/gear/G = gear_datums[thing] + new G.path(B) + else + H << "\red Failed to locate a storage object on your mob, either you spawned with no arms and no backpack or this is a bug." + //TODO: Generalize this by-species - if(H.species && (H.species.name == "Tajaran" || H.species.name == "Unathi")) - H.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal(H),slot_shoes,1) + if(H.species) + if(H.species.name == "Tajaran" || H.species.name == "Unathi") + H.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal(H),slot_shoes,1) + else if(H.species.name == "Vox") + H.equip_to_slot_or_del(new /obj/item/clothing/mask/breath(H), slot_wear_mask) + if(!H.r_hand) + H.equip_to_slot_or_del(new /obj/item/weapon/tank/nitrogen(H), slot_r_hand) + H.internal = H.r_hand + else if (!H.l_hand) + H.equip_to_slot_or_del(new /obj/item/weapon/tank/nitrogen(H), slot_l_hand) + H.internal = H.l_hand + H.internals.icon_state = "internal1" + + if(istype(H)) //give humans wheelchairs, if they need them. + var/datum/organ/external/l_foot = H.get_organ("l_foot") + var/datum/organ/external/r_foot = H.get_organ("r_foot") + if((!l_foot || l_foot.status & ORGAN_DESTROYED) && (!r_foot || r_foot.status & ORGAN_DESTROYED)) + var/obj/structure/stool/bed/chair/wheelchair/W = new /obj/structure/stool/bed/chair/wheelchair(H.loc) + H.buckled = W + H.update_canmove() + W.dir = H.dir + W.buckled_mob = H + W.add_fingerprint(H) H << "You are the [alt_title ? alt_title : rank]." H << "As the [alt_title ? alt_title : rank] you answer directly to [job.supervisors]. Special circumstances may change this." diff --git a/code/game/machinery/OpTable.dm b/code/game/machinery/OpTable.dm index 3eafd335..c73cd13b 100644 --- a/code/game/machinery/OpTable.dm +++ b/code/game/machinery/OpTable.dm @@ -8,7 +8,6 @@ use_power = 1 idle_power_usage = 1 active_power_usage = 5 - moveable = 1 var/mob/living/carbon/human/victim = null var/strapped = 0.0 @@ -88,7 +87,7 @@ /obj/machinery/optable/proc/check_victim() if(locate(/mob/living/carbon/human, src.loc)) var/mob/living/carbon/human/M = locate(/mob/living/carbon/human, src.loc) - if(M.resting) + if(M.lying) src.victim = M icon_state = M.pulse ? "table2-active" : "table2-idle" return 1 @@ -124,22 +123,26 @@ set category = "Object" set src in oview(1) - if(usr.stat || !ishuman(usr) || usr.buckled || usr.restrained()) - return - - if(src.victim) - usr << "\blue The table is already occupied!" + if(usr.stat || !ishuman(usr) || usr.restrained() || !check_table(usr)) return take_victim(usr,usr) /obj/machinery/optable/attackby(obj/item/weapon/W as obj, mob/living/carbon/user as mob) if (istype(W, /obj/item/weapon/grab)) - if(iscarbon(W:affecting)) - take_victim(W:affecting,usr) + var/obj/item/weapon/grab/G = W + if(iscarbon(G.affecting) && check_table(G.affecting)) + take_victim(G.affecting,usr) del(W) return - user.drop_item() - if(W && W.loc) - W.loc = src.loc - return + +/obj/machinery/optable/proc/check_table(mob/living/carbon/patient as mob) + if(src.victim) + usr << "\blue The table is already occupied!" + return 0 + + if(patient.buckled) + usr << "\blue Unbuckle first!" + return 0 + + return 1 diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm index e68935d9..c4f018da 100644 --- a/code/game/machinery/Sleeper.dm +++ b/code/game/machinery/Sleeper.dm @@ -10,6 +10,9 @@ anchored = 1 //About time someone fixed this. density = 0 var/orient = "LEFT" // "RIGHT" changes the dir suffix to "-r" + + use_power = 1 + idle_power_usage = 40 /obj/machinery/sleep_console/process() @@ -53,9 +56,14 @@ /obj/machinery/sleep_console/attack_hand(mob/user as mob) if(..()) return - if (src.connected) + if(stat & (NOPOWER|BROKEN)) + return + var/dat = "" + if (!src.connected || (connected.stat & (NOPOWER|BROKEN))) + dat += "This console is not connected to a sleeper or the sleeper is non-functional." + else var/mob/living/occupant = src.connected.occupant - var/dat = "Occupant Statistics:
" + dat += "Occupant Statistics:
" if (occupant) var/t1 switch(occupant.stat) @@ -99,9 +107,9 @@ dat += "
Eject Patient" else dat += "The sleeper is empty." - dat += text("

Close", user) - user << browse(dat, "window=sleeper;size=400x500") - onclose(user, "sleeper") + dat += text("

Close", user) + user << browse(dat, "window=sleeper;size=400x500") + onclose(user, "sleeper") return /obj/machinery/sleep_console/Topic(href, href_list) @@ -134,9 +142,6 @@ return -/obj/machinery/sleep_console/power_change() - return - // no change - sleeper works without power (you just can't inject more) @@ -150,6 +155,7 @@ /obj/machinery/sleeper name = "Sleeper" + desc = "A fancy bed with built-in injectors, a dialysis machine, and a limited health scanner." icon = 'icons/obj/Cryogenic2.dmi' icon_state = "sleeper_0" density = 1 @@ -160,6 +166,10 @@ var/amounts = list(5, 10) var/obj/item/weapon/reagent_containers/glass/beaker = null var/filtering = 0 + + use_power = 1 + idle_power_usage = 15 + active_power_usage = 200 //builtin health analyzer, dialysis machine, injectors. New() ..() @@ -176,6 +186,9 @@ process() + if (stat & (NOPOWER|BROKEN)) + return + if(filtering > 0) if(beaker) if(beaker.reagents.total_volume < beaker.reagents.maximum_volume) @@ -233,13 +246,12 @@ M.client.perspective = EYE_PERSPECTIVE M.client.eye = src M.loc = src + update_use_power(2) src.occupant = M src.icon_state = "sleeper_1" if(orient == "RIGHT") icon_state = "sleeper_1-r" - M << "\blue You feel cool air surround you. You go numb as your senses turn inward." - src.add_fingerprint(user) del(G) return @@ -314,6 +326,7 @@ src.occupant.client.perspective = MOB_PERSPECTIVE src.occupant.loc = src.loc src.occupant = null + update_use_power(1) if(orient == "RIGHT") icon_state = "sleeper_0-r" return @@ -409,13 +422,12 @@ usr.client.perspective = EYE_PERSPECTIVE usr.client.eye = src usr.loc = src + update_use_power(2) src.occupant = usr src.icon_state = "sleeper_1" if(orient == "RIGHT") icon_state = "sleeper_1-r" - usr << "\blue You feel cool air surround you. You go numb as your senses turn inward." - for(var/obj/O in src) del(O) src.add_fingerprint(usr) diff --git a/code/game/machinery/adv_med.dm b/code/game/machinery/adv_med.dm index c400185a..9948ce14 100644 --- a/code/game/machinery/adv_med.dm +++ b/code/game/machinery/adv_med.dm @@ -9,6 +9,10 @@ icon_state = "body_scanner_0" density = 1 anchored = 1 + + use_power = 1 + idle_power_usage = 60 + active_power_usage = 10000 //10 kW. It's a big all-body scanner. /*/obj/machinery/bodyscanner/allow_drop() return 0*/ @@ -48,6 +52,7 @@ usr.client.eye = src usr.loc = src src.occupant = usr + update_use_power(2) src.icon_state = "body_scanner_1" for(var/obj/O in src) //O = null @@ -67,6 +72,7 @@ src.occupant.client.perspective = MOB_PERSPECTIVE src.occupant.loc = src.loc src.occupant = null + update_use_power(1) src.icon_state = "body_scanner_0" return @@ -85,6 +91,7 @@ M.client.eye = src M.loc = src src.occupant = M + update_use_power(2) src.icon_state = "body_scanner_1" for(var/obj/O in src) O.loc = src.loc @@ -152,15 +159,15 @@ del(src) /obj/machinery/body_scanconsole/power_change() + ..() if(stat & BROKEN) icon_state = "body_scannerconsole-p" - else if(powered()) - icon_state = initial(icon_state) - stat &= ~NOPOWER else - spawn(rand(0, 15)) - src.icon_state = "body_scannerconsole-p" - stat |= NOPOWER + if (stat & NOPOWER) + spawn(rand(0, 15)) + src.icon_state = "body_scannerconsole-p" + else + icon_state = initial(icon_state) /obj/machinery/body_scanconsole var/obj/machinery/bodyscanner/connected @@ -214,6 +221,12 @@ /obj/machinery/body_scanconsole/attack_hand(user as mob) if(..()) return + if(stat & (NOPOWER|BROKEN)) + return + if(!connected || (connected.stat & (NOPOWER|BROKEN))) + user << "\red This console is not connected to a functioning body scanner." + return + if(!ishuman(connected.occupant)) user << "\red This device can only scan compatible lifeforms." return @@ -325,8 +338,7 @@ else dat += "[e.display_name]--Not Found" dat += "" - for(var/organ_name in occupant.internal_organs) - var/datum/organ/internal/i = occupant.internal_organs[organ_name] + for(var/datum/organ/internal/i in occupant.internal_organs) var/mech = "" if(i.robotic == 1) mech = "Assisted:" diff --git a/code/game/machinery/ai_slipper.dm b/code/game/machinery/ai_slipper.dm index 4df9f2a5..3f2b1dc3 100644 --- a/code/game/machinery/ai_slipper.dm +++ b/code/game/machinery/ai_slipper.dm @@ -1,7 +1,7 @@ /obj/machinery/ai_slipper - name = "AI Liquid Dispenser" + name = "\improper AI Liquid Dispenser" icon = 'icons/obj/device.dmi' - icon_state = "motion3" + icon_state = "motion0" layer = 3 anchored = 1.0 var/uses = 20 @@ -13,15 +13,19 @@ var/cooldown_on = 0 req_access = list(access_ai_upload) +/obj/machinery/ai_slipper/New() + ..() + update_icon() + /obj/machinery/ai_slipper/power_change() - if(stat & BROKEN) - return + ..() + update_icon() + +/obj/machinery/ai_slipper/update_icon() + if (stat & NOPOWER || stat & BROKEN) + icon_state = "motion0" else - if( powered() ) - stat &= ~NOPOWER - else - icon_state = "motion0" - stat |= NOPOWER + icon_state = disabled ? "motion0" : "motion3" /obj/machinery/ai_slipper/proc/setState(var/enabled, var/uses) src.disabled = disabled @@ -90,7 +94,7 @@ return if (href_list["toggleOn"]) src.disabled = !src.disabled - icon_state = src.disabled? "motion0":"motion3" + update_icon() if (href_list["toggleUse"]) if(cooldown_on || disabled) return diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm index 6b7152f0..e5dfdb84 100644 --- a/code/game/machinery/alarm.dm +++ b/code/game/machinery/alarm.dm @@ -47,9 +47,6 @@ #define RCON_AUTO 2 #define RCON_YES 3 -//1000 joules equates to about 1 degree every 2 seconds for a single tile of air. -#define MAX_ENERGY_CHANGE 1000 - #define MAX_TEMPERATURE 90 #define MIN_TEMPERATURE -40 @@ -67,10 +64,11 @@ icon_state = "alarm0" anchored = 1 use_power = 1 - idle_power_usage = 4 - active_power_usage = 8 + idle_power_usage = 80 + active_power_usage = 1000 //For heating/cooling rooms. 1000 joules equates to about 1 degree every 2 seconds for a single tile of air. power_channel = ENVIRON req_one_access = list(access_atmospherics, access_engine_equip) + var/breach_detection = 1 // Whether to use automatic breach detection or not var/frequency = 1439 //var/skipprocess = 0 //Experimenting var/alarm_frequency = 1437 @@ -87,7 +85,6 @@ var/screen = AALARM_SCREEN_MAIN var/area_uid var/area/alarm_area - var/danger_level = 0 var/buildstage = 2 //2 is built, 1 is building, 0 is frame. var/target_temperature = T0C+20 @@ -97,6 +94,13 @@ var/list/TLV = list() + var/danger_level = 0 + var/pressure_dangerlevel = 0 + var/oxygen_dangerlevel = 0 + var/co2_dangerlevel = 0 + var/toxins_dangerlevel = 0 + var/temperature_dangerlevel = 0 + var/other_dangerlevel = 0 /obj/machinery/alarm/server/New() ..() @@ -143,7 +147,7 @@ // breathable air according to human/Life() TLV["oxygen"] = list(16, 19, 135, 140) // Partial pressure, kpa TLV["carbon dioxide"] = list(-1.0, -1.0, 5, 10) // Partial pressure, kpa - TLV["plasma"] = list(-1.0, -1.0, 0.2, 0.5) // Partial pressure, kpa + TLV["toxins"] = list(-1.0, -1.0, 0.2, 0.5) // Partial pressure, kpa TLV["other"] = list(-1.0, -1.0, 0.5, 1.0) // Partial pressure, kpa TLV["pressure"] = list(ONE_ATMOSPHERE*0.80,ONE_ATMOSPHERE*0.90,ONE_ATMOSPHERE*1.10,ONE_ATMOSPHERE*1.20) /* kpa */ TLV["temperature"] = list(T0C-26, T0C, T0C+40, T0C+66) // K @@ -154,7 +158,6 @@ if (!master_is_operating()) elect_master() - /obj/machinery/alarm/process() if((stat & (NOPOWER|BROKEN)) || shorted || buildstage != 2) return @@ -165,56 +168,24 @@ var/datum/gas_mixture/environment = location.return_air() //Handle temperature adjustment here. - if(environment.temperature < target_temperature - 2 || environment.temperature > target_temperature + 2 || regulating_temperature) - //If it goes too far, we should adjust ourselves back before stopping. - if(get_danger_level(target_temperature, TLV["temperature"])) - return - - if(!regulating_temperature) - regulating_temperature = 1 - visible_message("\The [src] clicks as it starts [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\ - "You hear a click and a faint electronic hum.") - - if(target_temperature > T0C + MAX_TEMPERATURE) - target_temperature = T0C + MAX_TEMPERATURE - - if(target_temperature < T0C + MIN_TEMPERATURE) - target_temperature = T0C + MIN_TEMPERATURE - - var/datum/gas_mixture/gas - gas = location.remove_air(0.25*environment.total_moles) - if(gas) - var/heat_capacity = gas.heat_capacity() - var/energy_used = min( abs( heat_capacity*(gas.temperature - target_temperature) ), MAX_ENERGY_CHANGE) - - //Use power. Assuming that each power unit represents 1 watts.... - use_power(energy_used, ENVIRON) - - //We need to cool ourselves. - if(environment.temperature > target_temperature) - gas.temperature -= energy_used/heat_capacity - else - gas.temperature += energy_used/heat_capacity - - environment.merge(gas) - - if(abs(environment.temperature - target_temperature) <= 0.5) - regulating_temperature = 0 - visible_message("\The [src] clicks quietly as it stops [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\ - "You hear a click as a faint electronic humming stops.") + handle_heating_cooling(environment) var/old_level = danger_level - danger_level = overall_danger_level() + var/old_pressurelevel = pressure_dangerlevel + danger_level = overall_danger_level(environment) if (old_level != danger_level) - refresh_danger_level() - update_icon() + apply_danger_level(danger_level) + + if (old_pressurelevel != pressure_dangerlevel) + if (breach_detected()) + mode = AALARM_MODE_OFF + apply_mode() if (mode==AALARM_MODE_CYCLE && environment.return_pressure() 2.0) + update_use_power(2) + regulating_temperature = 1 + visible_message("\The [src] clicks as it starts [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\ + "You hear a click and a faint electronic hum.") + else + //check for when we should stop adjusting temperature + if (get_danger_level(target_temperature, TLV["temperature"]) || abs(environment.temperature - target_temperature) <= 0.5) + update_use_power(1) + regulating_temperature = 0 + visible_message("\The [src] clicks quietly as it stops [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\ + "You hear a click as a faint electronic humming stops.") - var/datum/gas_mixture/environment = location.return_air() + if (regulating_temperature) + if(target_temperature > T0C + MAX_TEMPERATURE) + target_temperature = T0C + MAX_TEMPERATURE + if(target_temperature < T0C + MIN_TEMPERATURE) + target_temperature = T0C + MIN_TEMPERATURE + + var/datum/gas_mixture/gas + gas = environment.remove(0.25*environment.total_moles) + if(gas) + + if (gas.temperature <= target_temperature) //gas heating + var/energy_used = min( gas.get_thermal_energy_change(target_temperature) , active_power_usage) + + gas.add_thermal_energy(energy_used) + //use_power(energy_used, ENVIRON) //handle by update_use_power instead + else //gas cooling + var/heat_transfer = min(abs(gas.get_thermal_energy_change(target_temperature)), active_power_usage) + + //Assume the heat is being pumped into the hull which is fixed at 20 C + //none of this is really proper thermodynamics but whatever + + var/cop = gas.temperature/T20C //coefficient of performance -> power used = heat_transfer/cop + + heat_transfer = min(heat_transfer, cop * active_power_usage) //this ensures that we don't use more than active_power_usage amount of power + + heat_transfer = -gas.add_thermal_energy(-heat_transfer) //get the actual heat transfer + + //use_power(heat_transfer / cop, ENVIRON) //handle by update_use_power instead + + environment.merge(gas) + +/obj/machinery/alarm/proc/overall_danger_level(var/datum/gas_mixture/environment) var/partial_pressure = R_IDEAL_GAS_EQUATION*environment.temperature/environment.volume var/environment_pressure = environment.return_pressure() - var/other_moles = 0.0 - for(var/datum/gas/G in environment.trace_gases) - other_moles+=G.moles + //var/other_moles = 0.0 + ////for(var/datum/gas/G in environment.trace_gases) + // other_moles+=G.moles - var/pressure_dangerlevel = get_danger_level(environment_pressure, TLV["pressure"]) - var/oxygen_dangerlevel = get_danger_level(environment.oxygen*partial_pressure, TLV["oxygen"]) - var/co2_dangerlevel = get_danger_level(environment.carbon_dioxide*partial_pressure, TLV["carbon dioxide"]) - var/plasma_dangerlevel = get_danger_level(environment.toxins*partial_pressure, TLV["plasma"]) - var/temperature_dangerlevel = get_danger_level(environment.temperature, TLV["temperature"]) - var/other_dangerlevel = get_danger_level(other_moles*partial_pressure, TLV["other"]) + pressure_dangerlevel = get_danger_level(environment_pressure, TLV["pressure"]) + oxygen_dangerlevel = get_danger_level(environment.gas["oxygen"]*partial_pressure, TLV["oxygen"]) + co2_dangerlevel = get_danger_level(environment.gas["carbon_dioxide"]*partial_pressure, TLV["carbon dioxide"]) + toxins_dangerlevel = get_danger_level(environment.gas["toxins"]*partial_pressure, TLV["toxins"]) + temperature_dangerlevel = get_danger_level(environment.temperature, TLV["temperature"]) + //other_dangerlevel = get_danger_level(other_moles*partial_pressure, TLV["other"]) return max( pressure_dangerlevel, oxygen_dangerlevel, co2_dangerlevel, - plasma_dangerlevel, - other_dangerlevel, + toxins_dangerlevel, + //other_dangerlevel, temperature_dangerlevel ) +// Returns whether this air alarm thinks there is a breach, given the sensors that are available to it. +/obj/machinery/alarm/proc/breach_detected() + var/turf/simulated/location = loc + + if(!istype(location)) + return 0 + + if(breach_detection == 0) + return 0 + + var/datum/gas_mixture/environment = location.return_air() + var/environment_pressure = environment.return_pressure() + var/pressure_levels = TLV["pressure"] + + if (environment_pressure <= pressure_levels[1]) //low pressures + if (!(mode == AALARM_MODE_PANIC || mode == AALARM_MODE_CYCLE)) + return 1 + + return 0 + + /obj/machinery/alarm/proc/master_is_operating() return alarm_area.master_air_alarm && !(alarm_area.master_air_alarm.stat & (NOPOWER|BROKEN)) @@ -284,7 +319,12 @@ if((stat & (NOPOWER|BROKEN)) || shorted) icon_state = "alarmp" return - switch(max(danger_level, alarm_area.atmosalm)) + + var/icon_level = danger_level + if (alarm_area.atmosalm) + icon_level = max(icon_level, 1) //if there's an atmos alarm but everything is okay locally, no need to go past yellow + + switch(icon_level) if (0) icon_state = "alarm0" if (1) @@ -367,14 +407,18 @@ return 1 /obj/machinery/alarm/proc/apply_mode() - var/current_pressures = TLV["pressure"] - var/target_pressure = (current_pressures[2] + current_pressures[3])/2 + //propagate mode to other air alarms in the area + //TODO: make it so that players can choose between applying the new mode to the room they are in (related area) vs the entire alarm area + for (var/area/RA in alarm_area.related) + for (var/obj/machinery/alarm/AA in RA) + AA.mode = mode + switch(mode) if(AALARM_MODE_SCRUBBING) for(var/device_id in alarm_area.air_scrub_names) send_signal(device_id, list("power"= 1, "co2_scrub"= 1, "scrubbing"= 1, "panic_siphon"= 0) ) for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list("power"= 1, "checks"= 1, "set_external_pressure"= target_pressure) ) + send_signal(device_id, list("power"= 1, "checks"= "default", "set_external_pressure"= "default") ) if(AALARM_MODE_PANIC, AALARM_MODE_CYCLE) for(var/device_id in alarm_area.air_scrub_names) @@ -386,13 +430,13 @@ for(var/device_id in alarm_area.air_scrub_names) send_signal(device_id, list("power"= 1, "panic_siphon"= 1) ) for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list("power"= 1, "checks"= 1, "set_external_pressure"= target_pressure) ) + send_signal(device_id, list("power"= 1, "checks"= "default", "set_external_pressure"= "default") ) if(AALARM_MODE_FILL) for(var/device_id in alarm_area.air_scrub_names) send_signal(device_id, list("power"= 0) ) for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list("power"= 1, "checks"= 1, "set_external_pressure"= target_pressure) ) + send_signal(device_id, list("power"= 1, "checks"= "default", "set_external_pressure"= "default") ) if(AALARM_MODE_OFF) for(var/device_id in alarm_area.air_scrub_names) @@ -404,16 +448,6 @@ if (alarm_area.atmosalert(new_danger_level)) post_alert(new_danger_level) - for (var/area/A in alarm_area.related) - for (var/obj/machinery/alarm/AA in A) - if ( !(AA.stat & (NOPOWER|BROKEN)) && !AA.shorted && AA.danger_level != new_danger_level) - AA.update_icon() - - if(danger_level > 1) - air_doors_close(0) - else - air_doors_open(0) - update_icon() /obj/machinery/alarm/proc/post_alert(alert_level) @@ -436,71 +470,6 @@ frequency.post_signal(src, alert_signal) -/obj/machinery/alarm/proc/refresh_danger_level() - var/level = 0 - for (var/area/A in alarm_area.related) - for (var/obj/machinery/alarm/AA in A) - if ( !(AA.stat & (NOPOWER|BROKEN)) && !AA.shorted) - if (AA.danger_level > level) - level = AA.danger_level - apply_danger_level(level) - -/obj/machinery/alarm/proc/air_doors_close(manual) - var/area/A = get_area(src) - if(!A.master.air_doors_activated) - A.master.air_doors_activated = 1 - for(var/obj/machinery/door/E in A.master.all_doors) - if(istype(E,/obj/machinery/door/firedoor)) - if(!E:blocked) - if(E.operating) - E:nextstate = CLOSED - else if(!E.density) - spawn(0) - E.close() - continue - -/* if(istype(E, /obj/machinery/door/airlock)) - if((!E:arePowerSystemsOn()) || (E.stat & NOPOWER) || E:air_locked) continue - if(!E.density) - spawn(0) - E.close() - spawn(10) - if(E.density) - E:air_locked = E.req_access - E:req_access = list(ACCESS_ENGINE, ACCESS_ATMOSPHERICS) - E.update_icon() - else if(E.operating) - spawn(10) - E.close() - if(E.density) - E:air_locked = E.req_access - E:req_access = list(ACCESS_ENGINE, ACCESS_ATMOSPHERICS) - E.update_icon() - else if(!E:locked) //Don't lock already bolted doors. - E:air_locked = E.req_access - E:req_access = list(ACCESS_ENGINE, ACCESS_ATMOSPHERICS) - E.update_icon()*/ - -/obj/machinery/alarm/proc/air_doors_open(manual) - var/area/A = get_area(loc) - if(A.master.air_doors_activated) - A.master.air_doors_activated = 0 - for(var/obj/machinery/door/E in A.master.all_doors) - if(istype(E, /obj/machinery/door/firedoor)) - if(!E:blocked) - if(E.operating) - E:nextstate = OPEN - else if(E.density) - spawn(0) - E.open() - continue - -/* if(istype(E, /obj/machinery/door/airlock)) - if((!E:arePowerSystemsOn()) || (E.stat & NOPOWER)) continue - if(!isnull(E:air_locked)) //Don't mess with doors locked for other reasons. - E:req_access = E:air_locked - E:air_locked = null - E.update_icon()*/ /////////// //HACKING// @@ -688,7 +657,7 @@ /obj/machinery/alarm/proc/return_status() var/turf/location = get_turf(src) var/datum/gas_mixture/environment = location.return_air() - var/total = environment.oxygen + environment.carbon_dioxide + environment.toxins + environment.nitrogen + var/total = environment.total_moles var/output = "Air Status:
" if(total == 0) @@ -710,22 +679,22 @@ var/pressure_dangerlevel = get_danger_level(environment_pressure, current_settings) current_settings = TLV["oxygen"] - var/oxygen_dangerlevel = get_danger_level(environment.oxygen*partial_pressure, current_settings) - var/oxygen_percent = round(environment.oxygen / total * 100, 2) + var/oxygen_dangerlevel = get_danger_level(environment.gas["oxygen"]*partial_pressure, current_settings) + var/oxygen_percent = round(environment.gas["oxygen"] / total * 100, 2) current_settings = TLV["carbon dioxide"] - var/co2_dangerlevel = get_danger_level(environment.carbon_dioxide*partial_pressure, current_settings) - var/co2_percent = round(environment.carbon_dioxide / total * 100, 2) + var/co2_dangerlevel = get_danger_level(environment.gas["carbon_dioxide"]*partial_pressure, current_settings) + var/co2_percent = round(environment.gas["carbon_dioxide"] / total * 100, 2) - current_settings = TLV["plasma"] - var/plasma_dangerlevel = get_danger_level(environment.toxins*partial_pressure, current_settings) - var/plasma_percent = round(environment.toxins / total * 100, 2) + current_settings = TLV["phoron"] + var/phoron_dangerlevel = get_danger_level(environment.gas["phoron"]*partial_pressure, current_settings) + var/phoron_percent = round(environment.gas["phoron"] / total * 100, 2) - current_settings = TLV["other"] - var/other_moles = 0.0 - for(var/datum/gas/G in environment.trace_gases) - other_moles+=G.moles - var/other_dangerlevel = get_danger_level(other_moles*partial_pressure, current_settings) + //current_settings = TLV["other"] + //var/other_moles = 0.0 + //for(var/datum/gas/G in environment.trace_gases) + // other_moles+=G.moles + //var/other_dangerlevel = get_danger_level(other_moles*partial_pressure, current_settings) current_settings = TLV["temperature"] var/temperature_dangerlevel = get_danger_level(environment.temperature, current_settings) @@ -734,27 +703,32 @@ Pressure: [environment_pressure]kPa
Oxygen: [oxygen_percent]%
Carbon dioxide: [co2_percent]%
-Toxins: [plasma_percent]%
+Toxins: [phoron_percent]%
"} - if (other_dangerlevel==2) - output += "Notice: High Concentration of Unknown Particles Detected
" - else if (other_dangerlevel==1) - output += "Notice: Low Concentration of Unknown Particles Detected
" + //if (other_dangerlevel==2) + // output += "Notice: High Concentration of Unknown Particles Detected
" + //else if (other_dangerlevel==1) + // output += "Notice: Low Concentration of Unknown Particles Detected
" output += "Temperature: [environment.temperature]K ([round(environment.temperature - T0C, 0.1)]C)
" - //Overall status + //'Local Status' should report the LOCAL status, damnit. output += "Local Status: " - switch(max(pressure_dangerlevel,oxygen_dangerlevel,co2_dangerlevel,plasma_dangerlevel,other_dangerlevel,temperature_dangerlevel)) + switch(max(pressure_dangerlevel,oxygen_dangerlevel,co2_dangerlevel,phoron_dangerlevel,other_dangerlevel,temperature_dangerlevel)) if(2) - output += "DANGER: Internals Required" + output += "DANGER: Internals Required
" if(1) - output += "Caution" + output += "Caution
" if(0) - if(alarm_area.atmosalm) - output += {"Caution: Atmos alert in area"} - else - output += {"Optimal"} + output += "Optimal
" + + output += "Area Status: " + if(alarm_area.atmosalm) + output += "Atmos alert in area" + else if (alarm_area.fire) + output += "Fire alarm in area" + else + output += "No alerts" return output @@ -786,9 +760,9 @@ Toxins: [plasma_percent]%
switch(screen) if (AALARM_SCREEN_MAIN) if(alarm_area.atmosalm) - output += "Reset - Atmospheric Alarm
" + output += "Reset - Area Atmospheric Alarm
" else - output += "Activate - Atmospheric Alarm
" + output += "Activate - Area Atmospheric Alarm
" output += {" Scrubbers Control
@@ -869,7 +843,7 @@ siphoning Carbon Dioxide [data["filter_co2"]?"on":"off"]; Toxins -[data["filter_toxins"]?"on":"off"]; +[data["filter_phoron"]?"on":"off"]; Nitrous Oxide [data["filter_n2o"]?"on":"off"]
@@ -952,11 +926,11 @@ table tr:first-child th:first-child { border: none;} add_fingerprint(usr) usr.set_machine(src) - + // hrefs that can always be called -walter0o if(href_list["rcon"]) var/attempted_rcon_setting = text2num(href_list["rcon"]) - + switch(attempted_rcon_setting) if(RCON_NO) rcon_setting = RCON_NO @@ -966,7 +940,7 @@ table tr:first-child th:first-child { border: none;} rcon_setting = RCON_YES else return - + if(href_list["temperature"]) var/list/selected = TLV["temperature"] var/max_temperature = min(selected[3] - T0C, MAX_TEMPERATURE) @@ -979,7 +953,7 @@ table tr:first-child th:first-child { border: none;} // hrefs that need the AA unlocked -walter0o if(!locked || istype(usr, /mob/living/silicon)) - + if(href_list["command"]) var/device_id = href_list["id_tag"] switch(href_list["command"]) @@ -992,9 +966,9 @@ table tr:first-child th:first-child { border: none;} "n2o_scrub", "panic_siphon", "scrubbing") - + send_signal(device_id, list(href_list["command"] = text2num(href_list["val"]) ) ) - + if("set_threshold") var/env = href_list["env"] var/threshold = text2num(href_list["var"]) @@ -1042,36 +1016,36 @@ table tr:first-child th:first-child { border: none;} selected[2] = selected[4] if(selected[3] > selected[4]) selected[3] = selected[4] - + apply_mode() - + if(href_list["screen"]) screen = text2num(href_list["screen"]) - + if(href_list["atmos_unlock"]) switch(href_list["atmos_unlock"]) if("0") - air_doors_close(1) + alarm_area.air_doors_close() if("1") - air_doors_open(1) - + alarm_area.air_doors_open() + if(href_list["atmos_alarm"]) if (alarm_area.atmosalert(2)) apply_danger_level(2) update_icon() - + if(href_list["atmos_reset"]) if (alarm_area.atmosalert(0)) apply_danger_level(0) update_icon() - + if(href_list["mode"]) mode = text2num(href_list["mode"]) apply_mode() - + // hrefs that need the AA wires exposed, note that borgs should be in range here too -walter0o if(wiresexposed && Adjacent(usr)) - + if (href_list["AAlarmwires"]) var/t1 = text2num(href_list["AAlarmwires"]) if (!( istype(usr.equipped(), /obj/item/weapon/wirecutters) )) @@ -1086,7 +1060,7 @@ table tr:first-child th:first-child { border: none;} update_icon() buildstage = 1 return - + else if (href_list["pulse"]) var/t1 = text2num(href_list["pulse"]) if (!istype(usr.equipped(), /obj/item/device/multitool)) @@ -1182,10 +1156,7 @@ table tr:first-child th:first-child { border: none;} return ..() /obj/machinery/alarm/power_change() - if(powered(power_channel)) - stat &= ~NOPOWER - else - stat |= NOPOWER + ..() spawn(rand(0,15)) update_icon() @@ -1205,8 +1176,7 @@ Just a object used in constructing air alarms icon_state = "door_electronics" desc = "Looks like a circuit. Probably is." w_class = 2.0 - m_amt = 50 - g_amt = 50 + matter = list("metal" = 50, "glass" = 50) /* @@ -1236,7 +1206,7 @@ Code shamelessly copied from apc_frame if (!(ndir in cardinal)) return - var/turf/loc = get_turf_loc(usr) + var/turf/loc = get_turf(usr) var/area/A = loc.loc if (!istype(loc, /turf/simulated/floor)) usr << "\red Air Alarm cannot be placed on this spot." @@ -1296,7 +1266,7 @@ FIRE ALARM else icon_state = "fire0" -/obj/machinery/firealarm/temperature_expose(datum/gas_mixture/air, temperature, volume) +/obj/machinery/firealarm/fire_act(datum/gas_mixture/air, temperature, volume) if(src.detecting) if(temperature > T0C+200) src.alarm() // added check of detector status here @@ -1394,13 +1364,9 @@ FIRE ALARM return /obj/machinery/firealarm/power_change() - if(powered(ENVIRON)) - stat &= ~NOPOWER + ..() + spawn(rand(0,15)) update_icon() - else - spawn(rand(0,15)) - stat |= NOPOWER - update_icon() /obj/machinery/firealarm/attack_hand(mob/user as mob) if(user.stat || stat & (NOPOWER|BROKEN)) @@ -1533,8 +1499,7 @@ Just a object used in constructing fire alarms icon_state = "door_electronics" desc = "A circuit. It has a label on it, it says \"Can handle heat levels up to 40 degrees celsius!\"" w_class = 2.0 - m_amt = 50 - g_amt = 50 + matter = list("metal" = 50, "glass" = 50) /* @@ -1564,7 +1529,7 @@ Code shamelessly copied from apc_frame if (!(ndir in cardinal)) return - var/turf/loc = get_turf_loc(usr) + var/turf/loc = get_turf(usr) var/area/A = loc.loc if (!istype(loc, /turf/simulated/floor)) usr << "\red Fire Alarm cannot be placed on this spot." diff --git a/code/game/machinery/atmo_control.dm b/code/game/machinery/atmo_control.dm index e0415c72..7efd242b 100644 --- a/code/game/machinery/atmo_control.dm +++ b/code/game/machinery/atmo_control.dm @@ -1,4 +1,4 @@ -obj/machinery/air_sensor +/obj/machinery/air_sensor icon = 'icons/obj/stationobjs.dmi' icon_state = "gsensor1" name = "Gas Sensor" @@ -22,67 +22,58 @@ obj/machinery/air_sensor var/datum/radio_frequency/radio_connection - update_icon() - icon_state = "gsensor[on]" +/obj/machinery/air_sensor/update_icon() + icon_state = "gsensor[on]" - process() - if(on) - var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal - signal.data["tag"] = id_tag - signal.data["timestamp"] = world.time +/obj/machinery/air_sensor/process() + if(on) + var/datum/signal/signal = new + signal.transmission_method = 1 //radio signal + signal.data["tag"] = id_tag + signal.data["timestamp"] = world.time - var/datum/gas_mixture/air_sample = return_air() + var/datum/gas_mixture/air_sample = return_air() - if(output&1) - signal.data["pressure"] = num2text(round(air_sample.return_pressure(),0.1),) - if(output&2) - signal.data["temperature"] = round(air_sample.temperature,0.1) + if(output&1) + signal.data["pressure"] = num2text(round(air_sample.return_pressure(),0.1),) + if(output&2) + signal.data["temperature"] = round(air_sample.temperature,0.1) - if(output>4) - var/total_moles = air_sample.total_moles() - if(total_moles > 0) - if(output&4) - signal.data["oxygen"] = round(100*air_sample.oxygen/total_moles,0.1) - if(output&8) - signal.data["toxins"] = round(100*air_sample.toxins/total_moles,0.1) - if(output&16) - signal.data["nitrogen"] = round(100*air_sample.nitrogen/total_moles,0.1) - if(output&32) - signal.data["carbon_dioxide"] = round(100*air_sample.carbon_dioxide/total_moles,0.1) - else - signal.data["oxygen"] = 0 - signal.data["toxins"] = 0 - signal.data["nitrogen"] = 0 - signal.data["carbon_dioxide"] = 0 - signal.data["sigtype"]="status" - radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) + if(output>4) + var/total_moles = air_sample.total_moles + if(total_moles > 0) + if(output&4) + signal.data["oxygen"] = round(100*air_sample.gas["oxygen"]/total_moles,0.1) + if(output&8) + signal.data["toxins"] = round(100*air_sample.gas["toxins"]/total_moles,0.1) + if(output&16) + signal.data["nitrogen"] = round(100*air_sample.gas["nitrogen"]/total_moles,0.1) + if(output&32) + signal.data["carbon_dioxide"] = round(100*air_sample.gas["carbon_dioxide"]/total_moles,0.1) + else + signal.data["oxygen"] = 0 + signal.data["toxins"] = 0 + signal.data["nitrogen"] = 0 + signal.data["carbon_dioxide"] = 0 + signal.data["sigtype"]="status" + radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) - proc - set_frequency(new_frequency) - radio_controller.remove_object(src, frequency) - frequency = new_frequency - radio_connection = radio_controller.add_object(src, frequency, RADIO_ATMOSIA) +/obj/machinery/air_sensor/proc/set_frequency(new_frequency) + radio_controller.remove_object(src, frequency) + frequency = new_frequency + radio_connection = radio_controller.add_object(src, frequency, RADIO_ATMOSIA) - initialize() +/obj/machinery/air_sensor/initialize() + set_frequency(frequency) + +/obj/machinery/air_sensor/New() + ..() + + if(radio_controller) set_frequency(frequency) - New() - ..() - - if(radio_controller) - set_frequency(frequency) - - attackby(var/obj/item/I, mob/user) - if(istype(I, /obj/item/device/signaltool)) - var/obj/item/device/signaltool/ST = I - id_tag = ST.change_ID(id_tag) - set_frequency(ST.change_freq(frequency)) - return - - -obj/machinery/computer/general_air_control +/obj/machinery/computer/general_air_control icon = 'icons/obj/computer.dmi' icon_state = "tank" @@ -93,363 +84,428 @@ obj/machinery/computer/general_air_control var/list/sensor_information = list() var/datum/radio_frequency/radio_connection + circuit = /obj/item/weapon/circuitboard/air_management - attack_hand(mob/user) - if(..(user)) - return - user << browse(return_text(),"window=computer") - user.set_machine(src) - onclose(user, "computer") - - process() - ..() - src.updateUsrDialog() - - attackby(I as obj, user as mob) - if(istype(I, /obj/item/weapon/screwdriver)) - playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1) - if(do_after(user, 20)) - if (src.stat & BROKEN) - user << "\blue The broken glass falls out." - var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc ) - new /obj/item/weapon/shard( src.loc ) - var/obj/item/weapon/circuitboard/air_management/M = new /obj/item/weapon/circuitboard/air_management( A ) - for (var/obj/C in src) - C.loc = src.loc - M.frequency = src.frequency - A.circuit = M - A.state = 3 - A.icon_state = "3" - A.anchored = 1 - del(src) - else - user << "\blue You disconnect the monitor." - var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc ) - var/obj/item/weapon/circuitboard/air_management/M = new /obj/item/weapon/circuitboard/air_management( A ) - for (var/obj/C in src) - C.loc = src.loc - M.frequency = src.frequency - A.circuit = M - A.state = 4 - A.icon_state = "4" - A.anchored = 1 - del(src) - else - src.attack_hand(user) +/obj/machinery/computer/general_air_control/attack_hand(mob/user) + if(..(user)) return + user << browse(return_text(),"window=computer") + user.set_machine(src) + onclose(user, "computer") - receive_signal(datum/signal/signal) - if(!signal || signal.encryption) return +/obj/machinery/computer/general_air_control/process() + ..() + src.updateUsrDialog() - var/id_tag = signal.data["tag"] - if(!id_tag || !sensors.Find(id_tag)) return +/obj/machinery/computer/general_air_control/receive_signal(datum/signal/signal) + if(!signal || signal.encryption) return - sensor_information[id_tag] = signal.data + var/id_tag = signal.data["tag"] + if(!id_tag || !sensors.Find(id_tag)) return - proc/return_text() - var/sensor_data - if(sensors.len) - for(var/id_tag in sensors) - var/long_name = sensors[id_tag] - var/list/data = sensor_information[id_tag] - var/sensor_part = "[long_name]:
" + sensor_information[id_tag] = signal.data - if(data) - if(data["pressure"]) - sensor_part += " Pressure: [data["pressure"]] kPa
" - if(data["temperature"]) - sensor_part += " Temperature: [data["temperature"]] K
" - if(data["oxygen"]||data["toxins"]||data["nitrogen"]||data["carbon_dioxide"]) - sensor_part += " Gas Composition :" - if(data["oxygen"]) - sensor_part += "[data["oxygen"]]% O2; " - if(data["nitrogen"]) - sensor_part += "[data["nitrogen"]]% N; " - if(data["carbon_dioxide"]) - sensor_part += "[data["carbon_dioxide"]]% CO2; " - if(data["toxins"]) - sensor_part += "[data["toxins"]]% TX; " - sensor_part += "
" +/obj/machinery/computer/general_air_control/proc/return_text() + var/sensor_data + if(sensors.len) + for(var/id_tag in sensors) + var/long_name = sensors[id_tag] + var/list/data = sensor_information[id_tag] + var/sensor_part = "[long_name]:
" - else - sensor_part = "[long_name] can not be found!
" + if(data) + if(data["pressure"]) + sensor_part += " Pressure: [data["pressure"]] kPa
" + if(data["temperature"]) + sensor_part += " Temperature: [data["temperature"]] K
" + if(data["oxygen"]||data["toxins"]||data["nitrogen"]||data["carbon_dioxide"]) + sensor_part += " Gas Composition :" + if(data["oxygen"]) + sensor_part += "[data["oxygen"]]% O2; " + if(data["nitrogen"]) + sensor_part += "[data["nitrogen"]]% N; " + if(data["carbon_dioxide"]) + sensor_part += "[data["carbon_dioxide"]]% CO2; " + if(data["toxins"]) + sensor_part += "[data["toxins"]]% TX; " + sensor_part += "
" - sensor_data += sensor_part + else + sensor_part = "[long_name] can not be found!
" - else - sensor_data = "No sensors connected." + sensor_data += sensor_part - var/output = {"[name]
+ else + sensor_data = "No sensors connected." + + var/output = {"[name]
Sensor Data:

[sensor_data]"} - return output + return output - proc - set_frequency(new_frequency) - radio_controller.remove_object(src, frequency) - frequency = new_frequency - radio_connection = radio_controller.add_object(src, frequency, RADIO_ATMOSIA) +/obj/machinery/computer/general_air_control/proc/set_frequency(new_frequency) + radio_controller.remove_object(src, frequency) + frequency = new_frequency + radio_connection = radio_controller.add_object(src, frequency, RADIO_ATMOSIA) - initialize() - set_frequency(frequency) - - large_tank_control - icon = 'icons/obj/computer.dmi' - icon_state = "tank" - - var/input_tag - var/output_tag - - var/list/input_info - var/list/output_info - - var/pressure_setting = ONE_ATMOSPHERE * 45 +/obj/machinery/computer/general_air_control/initialize() + set_frequency(frequency) - return_text() - var/output = ..() - //if(signal.data) - // input_info = signal.data // Attempting to fix intake control -- TLE +/obj/machinery/computer/general_air_control/large_tank_control + icon = 'icons/obj/computer.dmi' + icon_state = "tank" - output += "Tank Control System
" - if(input_info) - var/power = (input_info["power"]) - var/volume_rate = input_info["volume_rate"] - output += {"Input: [power?("Injecting"):("On Hold")] Refresh
-Rate: [volume_rate] L/sec
"} - output += "Command: Toggle Power
" + frequency = 1441 + var/input_tag + var/output_tag - else - output += "ERROR: Can not find input port Search
" + var/list/input_info + var/list/output_info - output += "
" + var/input_flow_setting = 200 + var/pressure_setting = ONE_ATMOSPHERE * 45 + circuit = /obj/item/weapon/circuitboard/air_management/tank_control - if(output_info) - var/power = (output_info["power"]) - var/output_pressure = output_info["internal"] - output += {"Output: [power?("Open"):("On Hold")] Refresh
+ +/obj/machinery/computer/general_air_control/large_tank_control/return_text() + var/output = ..() + //if(signal.data) + // input_info = signal.data // Attempting to fix intake control -- TLE + + output += "Tank Control System

" + if(input_info) + var/power = (input_info["power"]) + var/volume_rate = round(input_info["volume_rate"], 0.1) + output += "Input: [power?("Injecting"):("On Hold")] Refresh
Flow Rate Limit: [volume_rate] L/s
" + output += "Command: Toggle Power Set Flow Rate
" + + else + output += "ERROR: Can not find input port Search
" + + output += "Flow Rate Limit: - - - - [round(input_flow_setting, 0.1)] L/s + + + +
" + + output += "
" + + if(output_info) + var/power = (output_info["power"]) + var/output_pressure = output_info["internal"] + output += {"Output: [power?("Open"):("On Hold")] Refresh
Max Output Pressure: [output_pressure] kPa
"} - output += "Command: Toggle Power Set Pressure
" + output += "Command: Toggle Power Set Pressure
" - else - output += "ERROR: Can not find output port Search
" + else + output += "ERROR: Can not find output port Search
" - output += "Max Output Pressure Set: - - - - [pressure_setting] kPa + + + +
" + output += "Max Output Pressure Set: - - - - [pressure_setting] kPa + + + +
" - return output + return output - receive_signal(datum/signal/signal) - if(!signal || signal.encryption) return +/obj/machinery/computer/general_air_control/large_tank_control/receive_signal(datum/signal/signal) + if(!signal || signal.encryption) return - var/id_tag = signal.data["tag"] + var/id_tag = signal.data["tag"] - if(input_tag == id_tag) - input_info = signal.data - else if(output_tag == id_tag) - output_info = signal.data - else - ..(signal) + if(input_tag == id_tag) + input_info = signal.data + else if(output_tag == id_tag) + output_info = signal.data + else + ..(signal) - Topic(href, href_list) - if(..()) - return +/obj/machinery/computer/general_air_control/large_tank_control/Topic(href, href_list) + if(..()) + return - if(href_list["adj_pressure"]) - var/change = text2num(href_list["adj_pressure"]) - pressure_setting = between(0, pressure_setting + change, 50*ONE_ATMOSPHERE) - spawn(1) - src.updateUsrDialog() - return + if(href_list["adj_pressure"]) + var/change = text2num(href_list["adj_pressure"]) + pressure_setting = between(0, pressure_setting + change, 50*ONE_ATMOSPHERE) + spawn(1) + src.updateUsrDialog() + return - if(!radio_connection) - return 0 - var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal - signal.source = src - if(href_list["in_refresh_status"]) - input_info = null - signal.data = list ("tag" = input_tag, "status") + if(href_list["adj_input_flow_rate"]) + var/change = text2num(href_list["adj_input_flow_rate"]) + input_flow_setting = between(0, input_flow_setting + change, ATMOS_DEFAULT_VOLUME_PUMP + 500) //default flow rate limit for air injectors + spawn(1) + src.updateUsrDialog() + return + + if(!radio_connection) + return 0 + var/datum/signal/signal = new + signal.transmission_method = 1 //radio signal + signal.source = src + if(href_list["in_refresh_status"]) + input_info = null + signal.data = list ("tag" = input_tag, "status" = 1) - if(href_list["in_toggle_injector"]) - input_info = null - signal.data = list ("tag" = input_tag, "power_toggle") + if(href_list["in_toggle_injector"]) + input_info = null + signal.data = list ("tag" = input_tag, "power_toggle" = 1) - if(href_list["out_refresh_status"]) - output_info = null - signal.data = list ("tag" = output_tag, "status") + if(href_list["in_set_flowrate"]) + input_info = null + signal.data = list ("tag" = input_tag, "set_volume_rate" = "[input_flow_setting]") - if(href_list["out_toggle_power"]) - output_info = null - signal.data = list ("tag" = output_tag, "power_toggle") + if(href_list["out_refresh_status"]) + output_info = null + signal.data = list ("tag" = output_tag, "status" = 1) - if(href_list["out_set_pressure"]) - output_info = null - signal.data = list ("tag" = output_tag, "set_internal_pressure" = "[pressure_setting]") + if(href_list["out_toggle_power"]) + output_info = null + signal.data = list ("tag" = output_tag, "power_toggle" = 1) - signal.data["sigtype"]="command" - radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) + if(href_list["out_set_pressure"]) + output_info = null + signal.data = list ("tag" = output_tag, "set_internal_pressure" = "[pressure_setting]") - spawn(5) - src.updateUsrDialog() + signal.data["sigtype"]="command" + radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) - fuel_injection - icon = 'icons/obj/computer.dmi' - icon_state = "atmos" + spawn(5) + src.updateUsrDialog() - var/device_tag - var/list/device_info +/obj/machinery/computer/general_air_control/supermatter_core + icon = 'icons/obj/computer.dmi' + icon_state = "tank" - var/automation = 0 + frequency = 1438 + var/input_tag + var/output_tag - var/cutoff_temperature = 2000 - var/on_temperature = 1200 + var/list/input_info + var/list/output_info - attackby(I as obj, user as mob) - if(istype(I, /obj/item/weapon/screwdriver)) - playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1) - if(do_after(user, 20)) - if (src.stat & BROKEN) - user << "\blue The broken glass falls out." - var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc ) - new /obj/item/weapon/shard( src.loc ) - var/obj/item/weapon/circuitboard/injector_control/M = new /obj/item/weapon/circuitboard/injector_control( A ) - for (var/obj/C in src) - C.loc = src.loc - M.frequency = src.frequency - A.circuit = M - A.state = 3 - A.icon_state = "3" - A.anchored = 1 - del(src) - else - user << "\blue You disconnect the monitor." - var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc ) - var/obj/item/weapon/circuitboard/injector_control/M = new /obj/item/weapon/circuitboard/injector_control( A ) - for (var/obj/C in src) - C.loc = src.loc - M.frequency = src.frequency - A.circuit = M - A.state = 4 - A.icon_state = "4" - A.anchored = 1 - del(src) - else - src.attack_hand(user) - return + var/input_flow_setting = 700 + var/pressure_setting = 100 + circuit = /obj/item/weapon/circuitboard/air_management/supermatter_core - process() - if(automation) - if(!radio_connection) - return 0 - var/injecting = 0 - for(var/id_tag in sensor_information) - var/list/data = sensor_information[id_tag] - if(data["temperature"]) - if(data["temperature"] >= cutoff_temperature) - injecting = 0 - break - if(data["temperature"] <= on_temperature) - injecting = 1 +/obj/machinery/computer/general_air_control/supermatter_core/return_text() + var/output = ..() + //if(signal.data) + // input_info = signal.data // Attempting to fix intake control -- TLE - var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal - signal.source = src + output += "Core Cooling Control System

" + if(input_info) + var/power = (input_info["power"]) + var/volume_rate = round(input_info["volume_rate"], 0.1) + output += "Coolant Input: [power?("Injecting"):("On Hold")] Refresh
Flow Rate Limit: [volume_rate] L/s
" + output += "Command: Toggle Power Set Flow Rate
" - signal.data = list( - "tag" = device_tag, - "power" = injecting, - "sigtype"="command" - ) + else + output += "ERROR: Can not find input port Search
" + + output += "Flow Rate Limit: - - - - [round(input_flow_setting, 0.1)] L/s + + + +
" - radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) + output += "
" - ..() + if(output_info) + var/power = (output_info["power"]) + var/pressure_limit = output_info["external"] + output += {"Core Outpump: [power?("Open"):("On Hold")] Refresh
+Min Core Pressure: [pressure_limit] kPa
"} + output += "Command: Toggle Power Set Pressure
" - return_text() - var/output = ..() + else + output += "ERROR: Can not find output port Search
" - output += "Fuel Injection System
" - if(device_info) - var/power = device_info["power"] - var/volume_rate = device_info["volume_rate"] - output += {"Status: [power?("Injecting"):("On Hold")] Refresh
+ output += "Min Core Pressure Set: - - - - [pressure_setting] kPa + + + +
" + + return output + +/obj/machinery/computer/general_air_control/supermatter_core/receive_signal(datum/signal/signal) + if(!signal || signal.encryption) return + + var/id_tag = signal.data["tag"] + + if(input_tag == id_tag) + input_info = signal.data + else if(output_tag == id_tag) + output_info = signal.data + else + ..(signal) + +/obj/machinery/computer/general_air_control/supermatter_core/Topic(href, href_list) + if(..()) + return + + if(href_list["adj_pressure"]) + var/change = text2num(href_list["adj_pressure"]) + pressure_setting = between(0, pressure_setting + change, 10*ONE_ATMOSPHERE) + spawn(1) + src.updateUsrDialog() + return + + if(href_list["adj_input_flow_rate"]) + var/change = text2num(href_list["adj_input_flow_rate"]) + input_flow_setting = between(0, input_flow_setting + change, ATMOS_DEFAULT_VOLUME_PUMP + 500) //default flow rate limit for air injectors + spawn(1) + src.updateUsrDialog() + return + + if(!radio_connection) + return 0 + var/datum/signal/signal = new + signal.transmission_method = 1 //radio signal + signal.source = src + if(href_list["in_refresh_status"]) + input_info = null + signal.data = list ("tag" = input_tag, "status" = 1) + + if(href_list["in_toggle_injector"]) + input_info = null + signal.data = list ("tag" = input_tag, "power_toggle" = 1) + + if(href_list["in_set_flowrate"]) + input_info = null + signal.data = list ("tag" = input_tag, "set_volume_rate" = "[input_flow_setting]") + + if(href_list["out_refresh_status"]) + output_info = null + signal.data = list ("tag" = output_tag, "status" = 1) + + if(href_list["out_toggle_power"]) + output_info = null + signal.data = list ("tag" = output_tag, "power_toggle" = 1) + + if(href_list["out_set_pressure"]) + output_info = null + signal.data = list ("tag" = output_tag, "set_external_pressure" = "[pressure_setting]", "checks" = 1) + + signal.data["sigtype"]="command" + radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) + + spawn(5) + src.updateUsrDialog() + +/obj/machinery/computer/general_air_control/fuel_injection + icon = 'icons/obj/computer.dmi' + icon_state = "atmos" + + var/device_tag + var/list/device_info + + var/automation = 0 + + var/cutoff_temperature = 2000 + var/on_temperature = 1200 + circuit = /obj/item/weapon/circuitboard/air_management/injector_control + +/obj/machinery/computer/general_air_control/fuel_injection/process() + if(automation) + if(!radio_connection) + return 0 + + var/injecting = 0 + for(var/id_tag in sensor_information) + var/list/data = sensor_information[id_tag] + if(data["temperature"]) + if(data["temperature"] >= cutoff_temperature) + injecting = 0 + break + if(data["temperature"] <= on_temperature) + injecting = 1 + + var/datum/signal/signal = new + signal.transmission_method = 1 //radio signal + signal.source = src + + signal.data = list( + "tag" = device_tag, + "power" = injecting, + "sigtype"="command" + ) + + radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) + + ..() + +/obj/machinery/computer/general_air_control/fuel_injection/return_text() + var/output = ..() + + output += "Fuel Injection System
" + if(device_info) + var/power = device_info["power"] + var/volume_rate = device_info["volume_rate"] + output += {"Status: [power?("Injecting"):("On Hold")] Refresh
Rate: [volume_rate] L/sec
"} - if(automation) - output += "Automated Fuel Injection: Engaged
" - output += "Injector Controls Locked Out
" - else - output += "Automated Fuel Injection: Disengaged
" - output += "Injector: Toggle Power Inject (1 Cycle)
" + if(automation) + output += "Automated Fuel Injection: Engaged
" + output += "Injector Controls Locked Out
" + else + output += "Automated Fuel Injection: Disengaged
" + output += "Injector: Toggle Power Inject (1 Cycle)
" - else - output += "ERROR: Can not find device Search
" + else + output += "ERROR: Can not find device Search
" - return output + return output - receive_signal(datum/signal/signal) - if(!signal || signal.encryption) return +/obj/machinery/computer/general_air_control/fuel_injection/receive_signal(datum/signal/signal) + if(!signal || signal.encryption) return - var/id_tag = signal.data["tag"] + var/id_tag = signal.data["tag"] - if(device_tag == id_tag) - device_info = signal.data - else - ..(signal) + if(device_tag == id_tag) + device_info = signal.data + else + ..(signal) - Topic(href, href_list) - if(..()) - return +/obj/machinery/computer/general_air_control/fuel_injection/Topic(href, href_list) + if(..()) + return - if(href_list["refresh_status"]) - device_info = null - if(!radio_connection) - return 0 + if(href_list["refresh_status"]) + device_info = null + if(!radio_connection) + return 0 - var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal - signal.source = src - signal.data = list( - "tag" = device_tag, - "status", - "sigtype"="command" - ) - radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) + var/datum/signal/signal = new + signal.transmission_method = 1 //radio signal + signal.source = src + signal.data = list( + "tag" = device_tag, + "status" = 1, + "sigtype"="command" + ) + radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) - if(href_list["toggle_automation"]) - automation = !automation + if(href_list["toggle_automation"]) + automation = !automation - if(href_list["toggle_injector"]) - device_info = null - if(!radio_connection) - return 0 + if(href_list["toggle_injector"]) + device_info = null + if(!radio_connection) + return 0 - var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal - signal.source = src - signal.data = list( - "tag" = device_tag, - "power_toggle", - "sigtype"="command" - ) + var/datum/signal/signal = new + signal.transmission_method = 1 //radio signal + signal.source = src + signal.data = list( + "tag" = device_tag, + "power_toggle" = 1, + "sigtype"="command" + ) - radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) + radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) - if(href_list["injection"]) - if(!radio_connection) - return 0 + if(href_list["injection"]) + if(!radio_connection) + return 0 - var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal - signal.source = src - signal.data = list( - "tag" = device_tag, - "inject", - "sigtype"="command" - ) + var/datum/signal/signal = new + signal.transmission_method = 1 //radio signal + signal.source = src + signal.data = list( + "tag" = device_tag, + "inject" = 1, + "sigtype"="command" + ) - radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) + radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) diff --git a/code/game/machinery/atmoalter/area_atmos_computer.dm b/code/game/machinery/atmoalter/area_atmos_computer.dm index ac7af074..20347eef 100644 --- a/code/game/machinery/atmoalter/area_atmos_computer.dm +++ b/code/game/machinery/atmoalter/area_atmos_computer.dm @@ -71,11 +71,19 @@ [status]
Scan "} - 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 += {" @@ -96,7 +104,7 @@ if(href_list["scan"]) scanscrubbers() else if(href_list["toggle"]) - var/obj/machinery/portable_atmospherics/scrubber/huge/scrubber = locate(href_list["scrub"]) + var/obj/machinery/portable_atmospherics/powered/scrubber/huge/scrubber = locate(href_list["scrub"]) if(!validscrubber(scrubber)) spawn(20) @@ -108,7 +116,7 @@ scrubber.on = text2num(href_list["toggle"]) scrubber.update_icon() - proc/validscrubber( var/obj/machinery/portable_atmospherics/scrubber/huge/scrubber as obj ) + proc/validscrubber( var/obj/machinery/portable_atmospherics/powered/scrubber/huge/scrubber as obj ) if(!isobj(scrubber) || get_dist(scrubber.loc, src.loc) > src.range || scrubber.loc.z != src.loc.z) return 0 @@ -118,7 +126,7 @@ connectedscrubbers = new() var/found = 0 - for(var/obj/machinery/portable_atmospherics/scrubber/huge/scrubber in range(range, src.loc)) + for(var/obj/machinery/portable_atmospherics/powered/scrubber/huge/scrubber in range(range, src.loc)) if(istype(scrubber)) found = 1 connectedscrubbers += scrubber @@ -132,7 +140,7 @@ /obj/machinery/computer/area_atmos/area zone = "This computer is working in a wired network limited to this area." - validscrubber( var/obj/machinery/portable_atmospherics/scrubber/huge/scrubber as obj ) + validscrubber( var/obj/machinery/portable_atmospherics/powered/scrubber/huge/scrubber as obj ) if(!isobj(scrubber)) return 0 @@ -166,7 +174,7 @@ var/area/A = T.loc if (A.master) A = A.master - for(var/obj/machinery/portable_atmospherics/scrubber/huge/scrubber in world ) + for(var/obj/machinery/portable_atmospherics/powered/scrubber/huge/scrubber in world ) var/turf/T2 = get_turf(scrubber) if(T2 && T2.loc) var/area/A2 = T2.loc diff --git a/code/game/machinery/atmoalter/canister.dm b/code/game/machinery/atmoalter/canister.dm index 088770da..08df37b7 100644 --- a/code/game/machinery/atmoalter/canister.dm +++ b/code/game/machinery/atmoalter/canister.dm @@ -49,6 +49,16 @@ icon_state = "grey" canister_color = "grey" can_label = 0 +/obj/machinery/portable_atmospherics/canister/air/airlock + filled = 0.05 +/obj/machinery/portable_atmospherics/canister/empty/oxygen + name = "Canister: \[O2\]" + icon_state = "blue" + canister_color = "blue" +/obj/machinery/portable_atmospherics/canister/empty/toxins + name = "Canister \[Toxin (Bio)\]" + icon_state = "orange" + canister_color = "orange" /obj/machinery/portable_atmospherics/canister/proc/check_change() var/old_flag = update_flag @@ -88,9 +98,9 @@ update_flag src.overlays = 0 src.icon_state = text("[]-1", src.canister_color) - if(icon_state != "[canister_color]") + if(icon_state != "[canister_color]") icon_state = "[canister_color]" - + if(check_change()) //Returns 1 if no change needed to icons. return @@ -227,10 +237,10 @@ update_flag return ..() - + nanomanager.update_uis(src) // Update all NanoUIs attached to src - - + + /obj/machinery/portable_atmospherics/canister/attack_ai(var/mob/user as mob) return src.attack_hand(user) @@ -241,7 +251,7 @@ update_flag /obj/machinery/portable_atmospherics/canister/attack_hand(var/mob/user as mob) return src.ui_interact(user) -/obj/machinery/portable_atmospherics/canister/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null) +/obj/machinery/portable_atmospherics/canister/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) if (src.destroyed) return @@ -255,13 +265,13 @@ update_flag data["minReleasePressure"] = round(ONE_ATMOSPHERE/10) data["maxReleasePressure"] = round(10*ONE_ATMOSPHERE) data["valveOpen"] = valve_open ? 1 : 0 - - data["hasHoldingTank"] = holding ? 1 : 0 + + data["hasHoldingTank"] = holding ? 1 : 0 if (holding) data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure())) // 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 @@ -299,6 +309,9 @@ update_flag if (href_list["remove_tank"]) if(holding) + if (valve_open) + valve_open = 0 + release_log += "Valve was closed by [usr] ([usr.ckey]), stopping the transfer into the [holding]
" if(istype(holding, /obj/item/weapon/tank)) holding.manipulated_by = usr.real_name holding.loc = loc @@ -327,18 +340,17 @@ update_flag src.canister_color = colors[label] src.icon_state = colors[label] src.name = "Canister: [label]" - + src.add_fingerprint(usr) update_icon() - + return 1 /obj/machinery/portable_atmospherics/canister/toxins/New() ..() - src.air_contents.toxins = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - air_contents.update_values() + src.air_contents.adjust_gas("toxins", (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature)) src.update_icon() return 1 @@ -347,8 +359,7 @@ update_flag ..() - src.air_contents.oxygen = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - air_contents.update_values() + src.air_contents.adjust_gas("oxygen", (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature)) src.update_icon() return 1 @@ -356,10 +367,7 @@ update_flag ..() - var/datum/gas/sleeping_agent/trace_gas = new - air_contents.trace_gases += trace_gas - trace_gas.moles = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - air_contents.update_values() + air_contents.adjust_gas("sleeping_agent", (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature)) src.update_icon() return 1 @@ -367,8 +375,7 @@ update_flag //Dirty way to fill room with gas. However it is a bit easier to do than creating some floor/engine/n2o -rastaf0 /obj/machinery/portable_atmospherics/canister/sleeping_agent/roomfiller/New() ..() - var/datum/gas/sleeping_agent/trace_gas = air_contents.trace_gases[1] - trace_gas.moles = 9*4000 + air_contents.gas["sleeping_agent"] = 9*4000 spawn(10) var/turf/simulated/location = src.loc if (istype(src.loc)) @@ -382,8 +389,7 @@ update_flag ..() - src.air_contents.nitrogen = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - air_contents.update_values() + src.air_contents.adjust_gas("nitrogen", (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature)) src.update_icon() return 1 @@ -391,19 +397,14 @@ update_flag /obj/machinery/portable_atmospherics/canister/carbon_dioxide/New() ..() - src.air_contents.carbon_dioxide = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - air_contents.update_values() - - src.update_icon() + src.air_contents.adjust_gas("carbon_dioxide", (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature)) return 1 /obj/machinery/portable_atmospherics/canister/air/New() ..() - src.air_contents.oxygen = (O2STANDARD*src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - src.air_contents.nitrogen = (N2STANDARD*src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - air_contents.update_values() + src.air_contents.adjust_multi("oxygen", (O2STANDARD*src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature), "nitrogen", (N2STANDARD*src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature)) src.update_icon() return 1 diff --git a/code/game/machinery/atmoalter/meter.dm b/code/game/machinery/atmoalter/meter.dm index 84516d27..19bdd700 100644 --- a/code/game/machinery/atmoalter/meter.dm +++ b/code/game/machinery/atmoalter/meter.dm @@ -9,8 +9,7 @@ var/frequency = 0 var/id use_power = 1 - idle_power_usage = 2 - active_power_usage = 5 + idle_power_usage = 15 /obj/machinery/meter/New() ..() @@ -30,8 +29,6 @@ icon_state = "meter0" return 0 - //use_power(5) - var/datum/gas_mixture/environment = target.return_air() if(!environment) icon_state = "meterX" @@ -68,41 +65,33 @@ ) radio_connection.post_signal(src, signal) -/obj/machinery/meter/proc/status() - var/t = "" - if (src.target) +/obj/machinery/meter/examine() + var/t = "A gas flow meter. " + + if(get_dist(usr, src) > 3 && !(istype(usr, /mob/living/silicon/ai) || istype(usr, /mob/dead))) + t += "\blue You are too far away to read it." + + else if(stat & (NOPOWER|BROKEN)) + t += "\red The display is off." + + else if(src.target) var/datum/gas_mixture/environment = target.return_air() if(environment) - t += "The pressure gauge reads [round(environment.return_pressure(), 0.01)] kPa; [round(environment.temperature,0.01)]°K ([round(environment.temperature-T0C,0.01)]°C)" + t += "The pressure gauge reads [round(environment.return_pressure(), 0.01)] kPa; [round(environment.temperature,0.01)]K ([round(environment.temperature-T0C,0.01)]°C)" else t += "The sensor error light is blinking." else t += "The connect error light is blinking." - return t - -/obj/machinery/meter/examine() - set src in view(3) - - var/t = "A gas flow meter. " - t += status() + usr << t - - /obj/machinery/meter/Click() - if(stat & (NOPOWER|BROKEN)) + if(istype(usr, /mob/living/silicon/ai)) // ghosts can call ..() for examine + examine() return 1 - - var/t = null - if (get_dist(usr, src) <= 3 || istype(usr, /mob/living/silicon/ai) || istype(usr, /mob/dead)) - t += status() - else - usr << "\blue You are too far away." - return 1 - - usr << t - return 1 + + return ..() /obj/machinery/meter/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) if (!istype(W, /obj/item/weapon/wrench)) diff --git a/code/game/machinery/atmoalter/portable_atmospherics.dm b/code/game/machinery/atmoalter/portable_atmospherics.dm index 0ce20b44..8b0f535c 100644 --- a/code/game/machinery/atmoalter/portable_atmospherics.dm +++ b/code/game/machinery/atmoalter/portable_atmospherics.dm @@ -11,76 +11,82 @@ var/maximum_pressure = 90*ONE_ATMOSPHERE - New() - ..() +/obj/machinery/portable_atmospherics/New() + ..() - air_contents.volume = volume - air_contents.temperature = T20C + air_contents.volume = volume + air_contents.temperature = T20C - return 1 + return 1 - initialize() - . = ..() - spawn() - var/obj/machinery/atmospherics/portables_connector/port = locate() in loc - if(port) - connect(port) - update_icon() - - process() - if(!connected_port) //only react when pipe_network will ont it do it for you - //Allow for reactions - air_contents.react() - else +/obj/machinery/portable_atmospherics/initialize() + . = ..() + spawn() + var/obj/machinery/atmospherics/portables_connector/port = locate() in loc + if(port) + connect(port) update_icon() - Del() - del(air_contents) +/obj/machinery/portable_atmospherics/process() + if(!connected_port) //only react when pipe_network will ont it do it for you + //Allow for reactions + air_contents.react() + else + update_icon() - ..() +/obj/machinery/portable_atmospherics/Del() + del(air_contents) - update_icon() - return null + ..() - proc +/obj/machinery/portable_atmospherics/update_icon() + return null - connect(obj/machinery/atmospherics/portables_connector/new_port) - //Make sure not already connected to something else - if(connected_port || !new_port || new_port.connected_device) - return 0 +/obj/machinery/portable_atmospherics/proc/connect(obj/machinery/atmospherics/portables_connector/new_port) + //Make sure not already connected to something else + if(connected_port || !new_port || new_port.connected_device) + return 0 - //Make sure are close enough for a valid connection - if(new_port.loc != loc) - return 0 + //Make sure are close enough for a valid connection + if(new_port.loc != loc) + return 0 - //Perform the connection - connected_port = new_port - connected_port.connected_device = src + //Perform the connection + connected_port = new_port + connected_port.connected_device = src - anchored = 1 //Prevent movement + anchored = 1 //Prevent movement - //Actually enforce the air sharing - var/datum/pipe_network/network = connected_port.return_network(src) - if(network && !network.gases.Find(air_contents)) - network.gases += air_contents - network.update = 1 + //Actually enforce the air sharing + var/datum/pipe_network/network = connected_port.return_network(src) + if(network && !network.gases.Find(air_contents)) + network.gases += air_contents + network.update = 1 - return 1 + return 1 - disconnect() - if(!connected_port) - return 0 +/obj/machinery/portable_atmospherics/proc/disconnect() + if(!connected_port) + return 0 - var/datum/pipe_network/network = connected_port.return_network(src) - if(network) - network.gases -= air_contents + var/datum/pipe_network/network = connected_port.return_network(src) + if(network) + network.gases -= air_contents - anchored = 0 + anchored = 0 - connected_port.connected_device = null - connected_port = null + connected_port.connected_device = null + connected_port = null - return 1 + return 1 + +/obj/machinery/portable_atmospherics/proc/update_connected_network() + if(!connected_port) + return + + var/datum/pipe_network/network = connected_port.return_network(src) + if (network) + network.update = 1 /obj/machinery/portable_atmospherics/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) var/obj/icon = src @@ -118,24 +124,13 @@ visible_message("\red [user] has used [W] on \icon[icon]") if(air_contents) var/pressure = air_contents.return_pressure() - 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/plasma_concentration = air_contents.toxins/total_moles - - var/unknown_concentration = 1-(o2_concentration+n2_concentration+co2_concentration+plasma_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 Plasma: [round(plasma_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!" @@ -144,3 +139,39 @@ return return + + + +/obj/machinery/portable_atmospherics/powered + var/power_rating + var/power_losses + var/last_power_draw = 0 + var/obj/item/weapon/cell/cell + +/obj/machinery/portable_atmospherics/powered/attackby(obj/item/I, mob/user) + if(istype(I, /obj/item/weapon/cell)) + if(cell) + user << "There is already a power cell installed." + return + + var/obj/item/weapon/cell/C = I + + user.drop_item() + C.add_fingerprint(user) + cell = C + C.loc = src + user.visible_message("\blue [user] opens the panel on [src] and inserts [C].", "\blue You open the panel on [src] and insert [C].") + return + + if(istype(I, /obj/item/weapon/screwdriver)) + if(!cell) + user << "\red There is no power cell installed." + return + + user.visible_message("\blue [user] opens the panel on [src] and removes [cell].", "\blue You open the panel on [src] and remove [cell].") + cell.add_fingerprint(user) + cell.loc = src.loc + cell = null + return + + ..() diff --git a/code/game/machinery/atmoalter/pump.dm b/code/game/machinery/atmoalter/pump.dm index 126056cf..f5f91681 100644 --- a/code/game/machinery/atmoalter/pump.dm +++ b/code/game/machinery/atmoalter/pump.dm @@ -1,5 +1,5 @@ -/obj/machinery/portable_atmospherics/pump - name = "Portable Air Pump" +/obj/machinery/portable_atmospherics/powered/pump + name = "portable air pump" icon = 'icons/obj/atmos.dmi' icon_state = "psiphon:0" @@ -10,11 +10,18 @@ var/target_pressure = 100 volume = 1000 + + power_rating = 7500 //7500 W ~ 10 HP + power_losses = 150 -/obj/machinery/portable_atmospherics/pump/update_icon() +/obj/machinery/portable_atmospherics/powered/pump/New() + ..() + cell = new/obj/item/weapon/cell(src) + +/obj/machinery/portable_atmospherics/powered/pump/update_icon() src.overlays = 0 - if(on) + if(on && cell && cell.charge) icon_state = "psiphon:1" else icon_state = "psiphon:0" @@ -27,7 +34,7 @@ return -/obj/machinery/portable_atmospherics/pump/emp_act(severity) +/obj/machinery/portable_atmospherics/powered/pump/emp_act(severity) if(stat & (BROKEN|NOPOWER)) ..(severity) return @@ -43,76 +50,81 @@ ..(severity) -/obj/machinery/portable_atmospherics/pump/process() +/obj/machinery/portable_atmospherics/powered/pump/process() ..() - if(on) + var/power_draw = -1 + + if(on && cell && cell.charge) var/datum/gas_mixture/environment if(holding) environment = holding.air_contents else environment = loc.return_air() + + var/pressure_delta + var/output_volume + var/air_temperature if(direction_out) - var/pressure_delta = target_pressure - environment.return_pressure() - //Can not have a pressure delta that would cause environment pressure > tank pressure - - var/transfer_moles = 0 - if(air_contents.temperature > 0) - transfer_moles = pressure_delta*environment.volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION) - - //Actually transfer the gas - var/datum/gas_mixture/removed = air_contents.remove(transfer_moles) - - if(holding) - environment.merge(removed) - else - loc.assume_air(removed) + pressure_delta = target_pressure - environment.return_pressure() + output_volume = environment.volume * environment.group_multiplier + air_temperature = environment.temperature? environment.temperature : air_contents.temperature else - var/pressure_delta = target_pressure - air_contents.return_pressure() - //Can not have a pressure delta that would cause environment pressure > tank pressure - - var/transfer_moles = 0 - if(environment.temperature > 0) - transfer_moles = pressure_delta*air_contents.volume/(environment.temperature * R_IDEAL_GAS_EQUATION) - - //Actually transfer the gas - var/datum/gas_mixture/removed - if(holding) - removed = environment.remove(transfer_moles) - else - removed = loc.remove_air(transfer_moles) - - air_contents.merge(removed) - //src.update_icon() - + pressure_delta = target_pressure - air_contents.return_pressure() + output_volume = air_contents.volume * air_contents.group_multiplier + air_temperature = air_contents.temperature? air_contents.temperature : environment.temperature + + var/transfer_moles = pressure_delta*output_volume/(air_temperature * R_IDEAL_GAS_EQUATION) + + if (pressure_delta > 0.01) + if (direction_out) + power_draw = pump_gas(src, air_contents, environment, transfer_moles, power_rating) + else + power_draw = pump_gas(src, environment, air_contents, transfer_moles, power_rating) + + if (power_draw < 0) + last_flow_rate = 0 + last_power_draw = 0 + else + power_draw = max(power_draw, power_losses) + cell.use(power_draw * CELLRATE) + last_power_draw = power_draw + + update_connected_network() + + //ran out of charge + if (!cell.charge) + update_icon() + src.updateDialog() - return -/obj/machinery/portable_atmospherics/pump/return_air() +/obj/machinery/portable_atmospherics/powered/pump/return_air() return air_contents -/obj/machinery/portable_atmospherics/pump/attack_ai(var/mob/user as mob) +/obj/machinery/portable_atmospherics/powered/pump/attack_ai(var/mob/user as mob) return src.attack_hand(user) -/obj/machinery/portable_atmospherics/pump/attack_paw(var/mob/user as mob) +/obj/machinery/portable_atmospherics/powered/pump/attack_paw(var/mob/user as mob) return src.attack_hand(user) -/obj/machinery/portable_atmospherics/pump/attack_hand(var/mob/user as mob) +/obj/machinery/portable_atmospherics/powered/pump/attack_hand(var/mob/user as mob) user.set_machine(src) var/holding_text if(holding) - holding_text = {"
Tank Pressure: [holding.air_contents.return_pressure()] KPa
-Remove Tank
+ holding_text = {"
Tank Pressure: [round(holding.air_contents.return_pressure(), 0.01)] kPa
+Remove Tank "} - var/output_text = {"[name]
-Pressure: [air_contents.return_pressure()] KPa
+ var/output_text = {"[capitalize(name)]
+Pressure: [round(air_contents.return_pressure(), 0.01)] kPa
+Flow Rate: [round(last_flow_rate, 0.1)] L/s
Port Status: [(connected_port)?("Connected"):("Disconnected")] -[holding_text] +[holding_text]

+Cell Charge: [cell? "[round(cell.percent())]%" : "N/A"] | Load: [round(last_power_draw)] W
Power Switch: [on?("On"):("Off")]
Pump Direction: [direction_out?("Out"):("In")]
-Target Pressure: - - - - [target_pressure] + + + +
+Target Pressure: - - - - [target_pressure] kPa+ + + +

Close
"} @@ -122,7 +134,7 @@ Target Pressure: - 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 - - 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 - - //Remix the resulting gases - air_contents.merge(filtered_out) - - if(holding) - environment.merge(removed) - else - loc.assume_air(removed) - //src.update_icon() + + var/transfer_moles = min(1, volume_rate/environment.volume)*environment.total_moles + + power_draw = scrub_gas(src, scrubbing_gas, environment, air_contents, transfer_moles, power_rating) + + if (power_draw < 0) + last_flow_rate = 0 + last_power_draw = 0 + else + power_draw = max(power_draw, power_losses) + cell.use(power_draw * CELLRATE) + last_power_draw = power_draw + + update_connected_network() + + //ran out of charge + if (!cell.charge) + update_icon() + + //src.update_icon() src.updateDialog() - return -/obj/machinery/portable_atmospherics/scrubber/return_air() +/obj/machinery/portable_atmospherics/powered/scrubber/return_air() return air_contents -/obj/machinery/portable_atmospherics/scrubber/attack_ai(var/mob/user as mob) +/obj/machinery/portable_atmospherics/powered/scrubber/attack_ai(var/mob/user as mob) return src.attack_hand(user) -/obj/machinery/portable_atmospherics/scrubber/attack_paw(var/mob/user as mob) +/obj/machinery/portable_atmospherics/powered/scrubber/attack_paw(var/mob/user as mob) return src.attack_hand(user) -/obj/machinery/portable_atmospherics/scrubber/attack_hand(var/mob/user as mob) +/obj/machinery/portable_atmospherics/powered/scrubber/attack_hand(var/mob/user as mob) user.set_machine(src) var/holding_text if(holding) - holding_text = {"
Tank Pressure: [holding.air_contents.return_pressure()] KPa
-
Remove Tank
+ holding_text = {"
Tank Pressure: [round(holding.air_contents.return_pressure(), 0.01)] kPa
+Remove Tank "} var/output_text = {"[name]
-Pressure: [air_contents.return_pressure()] KPa
+Pressure: [round(air_contents.return_pressure(), 0.01)] kPa
+Flow Rate: [round(last_flow_rate, 0.1)] L/s
Port Status: [(connected_port)?("Connected"):("Disconnected")] -[holding_text] +[holding_text]

+Cell Charge: [cell? "[round(cell.percent())]%" : "N/A"] | Load: [round(last_power_draw)] W
Power Switch: [on?("On"):("Off")]
-Power regulator: - - - - [volume_rate] + + + +
+Flow Rate Regulator: - - - - [volume_rate] L/s + + + +

Close
@@ -177,7 +115,7 @@ Power regulator: - - - [src.wires[wire] ? "Mend" : "Cut"] Pulse
") +/obj/machinery/autolathe/interact(mob/user as mob) - dat += text("The red light is [src.disabled ? "off" : "on"].
") - dat += text("The green light is [src.shocked ? "off" : "on"].
") - dat += text("The blue light is [src.hacked ? "off" : "on"].
") - user << browse("Autolathe Hacking[dat]","window=autolathe_hack") - onclose(user, "autolathe_hack") - - regular_win(mob/user as mob) - var/dat as text - dat = text("Metal Amount: [src.m_amount] cm3 (MAX: [max_m_amount])
\nGlass Amount: [src.g_amount] cm3 (MAX: [max_g_amount])
") - var/list/objs = list() - objs += src.L - if (src.hacked) - objs += src.LL - for(var/obj/t in objs) - var/title = "[t.name] ([t.m_amt] m /[t.g_amt] g)" - if (m_amount" - continue - dat += "[title]" - if (istype(t, /obj/item/stack)) - var/obj/item/stack/S = t - var/max_multiplier = min(S.max_amount, S.m_amt?round(m_amount/S.m_amt):INFINITY, S.g_amt?round(g_amount/S.g_amt):INFINITY) - if (max_multiplier>1) - dat += " |" - if (max_multiplier>10) - dat += " x[10]" - if (max_multiplier>25) - dat += " x[25]" - if (max_multiplier>1) - dat += " x[max_multiplier]" - dat += "
" - user << browse("Autolathe Control Panel[dat]", "window=autolathe_regular") - onclose(user, "autolathe_regular") - - - - interact(mob/user as mob) - if(istype(user, /mob/living/carbon/human)) - var/mob/living/carbon/human/M = user - if(M.h_style == "Floorlength Braid" || M.h_style == "Very Long Hair") - if(prob(10)) - M.apply_damage(30, BRUTE, "head") - M.apply_damage(45, HALLOSS) - M.visible_message("\red [user]'s hair catches in the [src]!", "\red Your hair gets caught in the [src]!") - M.say("*scream") - if(..()) - return - if (src.shocked) - src.shock(user,50) - if (src.opened) - wires_win(user,50) - return - if (src.disabled) - user << "\red You press the button, but nothing happens." - return - regular_win(user) + if(..() || disabled) return - attackby(var/obj/item/O as obj, var/mob/user as mob) - if (stat) - return 1 - if (busy) - user << "\red The autolathe is busy. Please wait for completion of previous operation." - return 1 - if (istype(O, /obj/item/weapon/screwdriver)) - if (!opened) - src.opened = 1 - src.icon_state = "autolathe_t" - user << "You open the maintenance hatch of [src]." - else - src.opened = 0 - src.icon_state = "autolathe" - user << "You close the maintenance hatch of [src]." - return 1 - if (opened) - if(istype(O, /obj/item/weapon/crowbar)) - playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1) - var/obj/machinery/constructable_frame/machine_frame/M = new /obj/machinery/constructable_frame/machine_frame(src.loc) - M.state = 2 - M.icon_state = "box_1" - for(var/obj/I in component_parts) - if(I.reliability != 100 && crit_fail) - I.crit_fail = 1 - I.loc = src.loc - if(m_amount >= 3750) - var/obj/item/stack/sheet/metal/G = new /obj/item/stack/sheet/metal(src.loc) - G.amount = round(m_amount / 3750) - if(g_amount >= 3750) - var/obj/item/stack/sheet/glass/G = new /obj/item/stack/sheet/glass(src.loc) - G.amount = round(g_amount / 3750) - del(src) - return 1 - else - user.set_machine(src) - interact(user) - return 1 + if (shocked) + shock(user,50) - if (src.m_amount + O.m_amt > max_m_amount) - user << "\red The autolathe is full. Please remove metal from the autolathe in order to insert more." - return 1 - if (src.g_amount + O.g_amt > max_g_amount) - user << "\red The autolathe is full. Please remove glass from the autolathe in order to insert more." - return 1 - if (O.m_amt == 0 && O.g_amt == 0) - user << "\red This object does not contain significant amounts of metal or glass, or cannot be accepted by the autolathe due to size or hazardous materials." - return 1 - /* - if (istype(O, /obj/item/weapon/grab) && src.hacked) - var/obj/item/weapon/grab/G = O - if (prob(25) && G.affecting) - G.affecting.gib() - m_amount += 50000 - return - */ + var/dat = "

Autolathe Control Panel


" - var/amount = 1 - var/obj/item/stack/stack - var/m_amt = O.m_amt - var/g_amt = O.g_amt - if (istype(O, /obj/item/stack)) - stack = O - amount = stack.amount - if (m_amt) - amount = min(amount, round((max_m_amount-src.m_amount)/m_amt)) - flick("autolathe_o",src)//plays metal insertion animation - if (g_amt) - amount = min(amount, round((max_g_amount-src.g_amount)/g_amt)) - flick("autolathe_r",src)//plays glass insertion animation - stack.use(amount) + dat += "
[scrubber.name]Turn On Turn Off + [scrubber.name]
+ Pressure: [round(scrubber.air_contents.return_pressure(), 0.01)] kPa
+ Flow Rate: [round(scrubber.last_flow_rate,0.1)] L/s
+
+ Turn On + Turn Off
+ Load: [round(scrubber.last_power_draw)] W +
" + var/material_top = "" + var/material_bottom = "" + + for(var/material in stored_material) + material_top += "" + material_bottom += "" + + dat += "[material_top][material_bottom]
[material][stored_material[material]]/[storage_capacity[material]]

" + dat += "

Printable Designs

Showing: [show_category].

" + + var/index = 0 + for(var/datum/autolathe/recipe/R in autolathe_recipes) + index++ + if(R.hidden && !hacked || (show_category != "All" && show_category != R.category)) + continue + + var/can_make = 1 + var/material_string = "" + var/multiplier_string = "" + var/max_sheets + + var/comma + if(!R.resources || !R.resources.len) + material_string = "No resources required." else - usr.before_take_item(O) - O.loc = src - icon_state = "autolathe" + + //Make sure it's buildable and list requires resources. + for(var/material in R.resources) + var/sheets = round(stored_material[material]/R.resources[material]) + if(isnull(max_sheets) || max_sheets > sheets) + max_sheets = sheets + + if(!isnull(stored_material[material]) && stored_material[material] < R.resources[material]) + can_make = 0 + if(!comma) + comma = 1 + else + material_string += ", " + material_string += "[R.resources[material]] [material]" + material_string += ".
" + + //Build list of multipliers for sheets. + if(R.is_stack) + if(max_sheets && max_sheets > 0) + multiplier_string += "
" + for(var/i = 5;i*" : ""][can_make ? "" : ""][R.name][can_make ? "" : ""][R.hidden ? "*" : ""][multiplier_string]" + + dat += "
[material_string]

" + + //Hacking. + if(opened) + dat += "

Maintenance Panel

" + for(var/wire in wires) + dat += "[wire] Wire: [wires[wire] ? "Mend" : "Cut"] Pulse
" + dat += "
" + dat += "The red light is [disabled ? "off" : "on"].
" + dat += "The green light is [shocked ? "off" : "on"].
" + dat += "The blue light is [hacked ? "off" : "on"].
" + + dat += "
" + + user << browse(dat, "window=autolathe") + onclose(user, "autolathe") + +/obj/machinery/autolathe/attackby(var/obj/item/O as obj, var/mob/user as mob) + + if (stat) + return + + if (busy) + user << "\red \The [src] is busy. Please wait for completion of previous operation." + return + + if(istype(O, /obj/item/weapon/screwdriver)) + opened = !opened + icon_state = (opened ? "autolathe_t": "autolathe") + user << "You [opened ? "open" : "close"] the maintenance hatch of [src]." + updateUsrDialog() + return + + if (opened) + //Don't eat multitools or wirecutters used on an open lathe. + if(istype(O, /obj/item/device/multitool) || istype(O, /obj/item/weapon/wirecutters)) + attack_hand(user) + return + + //Dismantle the frame. + if(istype(O, /obj/item/weapon/crowbar)) + dismantle() + return + + //Resources are being loaded. + var/obj/item/eating = O + if(!eating.matter) + user << "\The [eating] does not contain significant amounts of useful materials and cannot be accepted." + return + + var/filltype = 0 // Used to determine message. + var/total_used = 0 // Amount of material used. + var/mass_per_sheet = 0 // Amount of material constituting one sheet. + + for(var/material in eating.matter) + + if(isnull(stored_material[material]) || isnull(storage_capacity[material])) + continue + + if(stored_material[material] >= storage_capacity[material]) + continue + + var/total_material = eating.matter[material] + + //If it's a stack, we eat multiple sheets. + if(istype(eating,/obj/item/stack)) + var/obj/item/stack/stack = eating + total_material *= stack.amount + + if(stored_material[material] + total_material > storage_capacity[material]) + total_material = storage_capacity[material] - stored_material[material] + filltype = 1 + else + filltype = 2 + + stored_material[material] += total_material + total_used += total_material + mass_per_sheet += eating.matter[material] + + if(!filltype) + user << "\red \The [src] is full. Please remove material from the autolathe in order to insert more." + return + else if(filltype == 1) + user << "You fill \the [src] to capacity with \the [eating]." + else + user << "You fill \the [src] with \the [eating]." + + flick("autolathe_o",src) // Plays metal insertion animation. Work out a good way to work out a fitting animation. ~Z + + if(istype(eating,/obj/item/stack)) + var/obj/item/stack/stack = eating + stack.use(max(1,round(total_used/mass_per_sheet))) // Always use at least 1 to prevent infinite materials. + else + user.drop_item(O) + del(O) + + updateUsrDialog() + return + +/obj/machinery/autolathe/attack_paw(mob/user as mob) + return attack_hand(user) + +/obj/machinery/autolathe/attack_hand(mob/user as mob) + user.set_machine(src) + interact(user) + +/obj/machinery/autolathe/Topic(href, href_list) + + if(..()) + return + + usr.set_machine(src) + add_fingerprint(usr) + + if(busy) + usr << "\red The autolathe is busy. Please wait for completion of previous operation." + return + + if(href_list["change_category"]) + + var/choice = input("Which category do you wish to display?") as null|anything in autolathe_categories+"All" + if(!choice) return + show_category = choice + + if(href_list["make"] && autolathe_recipes) + + var/index = text2num(href_list["make"]) + var/multiplier = text2num(href_list["multiplier"]) + var/datum/autolathe/recipe/making + + if(index > 0 && index <= autolathe_recipes.len) + making = autolathe_recipes[index] + + //Exploit detection, not sure if necessary after rewrite. + if(!making || multiplier < 0 || multiplier > 100) + var/turf/exploit_loc = get_turf(usr) + message_admins("[key_name_admin(usr)] tried to exploit an autolathe to duplicate an item! ([exploit_loc ? "JMP" : "null"])", 0) + log_admin("EXPLOIT : [key_name(usr)] tried to exploit an autolathe to duplicate an item!") + return + busy = 1 - use_power(max(1000, (m_amt+g_amt)*amount/10)) - src.m_amount += m_amt * amount - src.g_amount += g_amt * amount - user << "You insert [amount] sheet[amount>1 ? "s" : ""] to the autolathe." - if (O && O.loc == src) - del(O) + //This needs some work. + use_power(max(2000, (making.power_use*multiplier))) + + //Check if we still have the materials. + for(var/material in making.resources) + if(!isnull(stored_material[material])) + if(stored_material[material] < (making.resources[material]*multiplier)) + return + + //Consume materials. + for(var/material in making.resources) + if(!isnull(stored_material[material])) + stored_material[material] = max(0,stored_material[material]-(making.resources[material]*multiplier)) + + //Fancy autolathe animation. + flick("autolathe_n",src) + + sleep(50) + busy = 0 - src.updateUsrDialog() - attack_paw(mob/user as mob) - return src.attack_hand(user) + //Sanity check. + if(!making || !src) return - attack_hand(mob/user as mob) - user.set_machine(src) - interact(user) + //Create the desired item. + var/obj/item/I = new making.path(get_step(loc, get_dir(src,usr))) + if(multiplier>1 && istype(I,/obj/item/stack)) + var/obj/item/stack/S = I + S.amount = multiplier + + if(href_list["act"]) + + var/temp_wire = href_list["wire"] + if(href_list["act"] == "pulse") + + if (!istype(usr.get_active_hand(), /obj/item/device/multitool)) + usr << "You need a multitool!" + return + + if(wires[temp_wire]) + usr << "You can't pulse a cut wire." + return + + if(hack_wire == temp_wire) + hacked = !hacked + + spawn(100) + hacked = !hacked + + if(disable_wire == temp_wire) + disabled = !disabled + shock(usr,50) + + spawn(100) + disabled = !disabled + + if(shock_wire == temp_wire) + shocked = !shocked + shock(usr,50) + + spawn(100) + shocked = !shocked + + else if(href_list["act"] == "wire") + + if (!istype(usr.get_active_hand(), /obj/item/weapon/wirecutters)) + usr << "You need wirecutters!" + return + + wires[temp_wire] = !wires[temp_wire] + + if(hack_wire == temp_wire) + hacked = !hacked + + if(disable_wire == temp_wire) + disabled = !disabled + shock(usr,50) + + if(shock_wire == temp_wire) + shocked = !shocked + shock(usr,50) + + updateUsrDialog() - Topic(href, href_list) - if(..()) - return - usr.set_machine(src) - src.add_fingerprint(usr) - if (!busy) - if(href_list["make"]) - var/turf/T = get_step(src.loc, get_dir(src,usr)) +/obj/machinery/autolathe/New() - // critical exploit fix start -walter0o - var/obj/item/template = null - var/attempting_to_build = locate(href_list["make"]) + ..() - if(!attempting_to_build) - return - - if(locate(attempting_to_build, src.L) || locate(attempting_to_build, src.LL)) // see if the requested object is in one of the construction lists, if so, it is legit -walter0o - template = attempting_to_build - - else // somebody is trying to exploit, alert admins -walter0o - - var/turf/LOC = get_turf(usr) - message_admins("[key_name_admin(usr)] tried to exploit an autolathe to duplicate [attempting_to_build] ! ([LOC ? "JMP" : "null"])", 0) - log_admin("EXPLOIT : [key_name(usr)] tried to exploit an autolathe to duplicate [attempting_to_build] !") - return - - // now check for legit multiplier, also only stacks should pass with one to prevent raw-materials-manipulation -walter0o - - var/multiplier = text2num(href_list["multiplier"]) - - if (!multiplier) multiplier = 1 - var/max_multiplier = 1 - - if(istype(template, /obj/item/stack)) // stacks are the only items which can have a multiplier higher than 1 -walter0o - var/obj/item/stack/S = template - max_multiplier = min(S.max_amount, S.m_amt?round(m_amount/S.m_amt):INFINITY, S.g_amt?round(g_amount/S.g_amt):INFINITY) // pasta from regular_win() to make sure the numbers match -walter0o - - if( (multiplier > max_multiplier) || (multiplier <= 0) ) // somebody is trying to exploit, alert admins-walter0o - - var/turf/LOC = get_turf(usr) - message_admins("[key_name_admin(usr)] tried to exploit an autolathe with multiplier set to [multiplier] on [template] ! ([LOC ? "JMP" : "null"])" , 0) - log_admin("EXPLOIT : [key_name(usr)] tried to exploit an autolathe with multiplier set to [multiplier] on [template] !") - return - - var/power = max(2000, (template.m_amt+template.g_amt)*multiplier/5) - if(src.m_amount >= template.m_amt*multiplier && src.g_amount >= template.g_amt*multiplier) - busy = 1 - use_power(power) - icon_state = "autolathe" - flick("autolathe_n",src) - spawn(16) - use_power(power) - spawn(16) - use_power(power) - spawn(16) - src.m_amount -= template.m_amt*multiplier - src.g_amount -= template.g_amt*multiplier - if(src.m_amount < 0) - src.m_amount = 0 - if(src.g_amount < 0) - src.g_amount = 0 - var/obj/new_item = new template.type(T) - if (multiplier>1) - var/obj/item/stack/S = new_item - S.amount = multiplier - busy = 0 - src.updateUsrDialog() - if(href_list["act"]) - var/temp_wire = href_list["wire"] - if(href_list["act"] == "pulse") - if (!istype(usr.get_active_hand(), /obj/item/device/multitool)) - usr << "You need a multitool!" - else - if(src.wires[temp_wire]) - usr << "You can't pulse a cut wire." - else - if(src.hack_wire == temp_wire) - src.hacked = !src.hacked - spawn(100) src.hacked = !src.hacked - if(src.disable_wire == temp_wire) - src.disabled = !src.disabled - src.shock(usr,50) - spawn(100) src.disabled = !src.disabled - if(src.shock_wire == temp_wire) - src.shocked = !src.shocked - src.shock(usr,50) - spawn(100) src.shocked = !src.shocked - if(href_list["act"] == "wire") - if (!istype(usr.get_active_hand(), /obj/item/weapon/wirecutters)) - usr << "You need wirecutters!" - else - wires[temp_wire] = !wires[temp_wire] - if(src.hack_wire == temp_wire) - src.hacked = !src.hacked - if(src.disable_wire == temp_wire) - src.disabled = !src.disabled - src.shock(usr,50) - if(src.shock_wire == temp_wire) - src.shocked = !src.shocked - src.shock(usr,50) - else - usr << "\red The autolathe is busy. Please wait for completion of previous operation." - src.updateUsrDialog() - return + //Create global autolathe recipe list if it hasn't been made already. + if(isnull(autolathe_recipes)) + autolathe_recipes = list() + autolathe_categories = list() + for(var/R in typesof(/datum/autolathe/recipe)-/datum/autolathe/recipe) + var/datum/autolathe/recipe/recipe = new R + autolathe_recipes += recipe + autolathe_categories |= recipe.category + var/obj/item/I = new recipe.path + if(I.matter && !recipe.resources) //This can be overidden in the datums. + recipe.resources = list() + for(var/material in I.matter) + if(!isnull(storage_capacity[material])) + recipe.resources[material] = round(I.matter[material]*1.25) // More expensive to produce than they are to recycle. + del(I) + //Create parts for lathe. + component_parts = list() + component_parts += new /obj/item/weapon/circuitboard/autolathe(src) + component_parts += new /obj/item/weapon/stock_parts/matter_bin(src) + component_parts += new /obj/item/weapon/stock_parts/matter_bin(src) + component_parts += new /obj/item/weapon/stock_parts/matter_bin(src) + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + component_parts += new /obj/item/weapon/stock_parts/console_screen(src) RefreshParts() - ..() - var/tot_rating = 0 - for(var/obj/item/weapon/stock_parts/matter_bin/MB in component_parts) - tot_rating += MB.rating - tot_rating *= 25000 - max_m_amount = tot_rating * 2 - max_g_amount = tot_rating - New() - ..() - component_parts = list() - component_parts += new /obj/item/weapon/circuitboard/autolathe(src) - component_parts += new /obj/item/weapon/stock_parts/matter_bin(src) - component_parts += new /obj/item/weapon/stock_parts/matter_bin(src) - component_parts += new /obj/item/weapon/stock_parts/matter_bin(src) - component_parts += new /obj/item/weapon/stock_parts/manipulator(src) - component_parts += new /obj/item/weapon/stock_parts/console_screen(src) - RefreshParts() + //Init wires. + wires = list( + "Light Red" = 0, + "Dark Red" = 0, + "Blue" = 0, + "Green" = 0, + "Yellow" = 0, + "Black" = 0, + "White" = 0, + "Gray" = 0, + "Orange" = 0, + "Pink" = 0 + ) - src.L = autolathe_recipes - src.LL = autolathe_recipes_hidden - src.wires["Light Red"] = 0 - src.wires["Dark Red"] = 0 - src.wires["Blue"] = 0 - src.wires["Green"] = 0 - src.wires["Yellow"] = 0 - src.wires["Black"] = 0 - src.wires["White"] = 0 - src.wires["Gray"] = 0 - src.wires["Orange"] = 0 - src.wires["Pink"] = 0 - var/list/w = list("Light Red","Dark Red","Blue","Green","Yellow","Black","White","Gray","Orange","Pink") - src.hack_wire = pick(w) - w -= src.hack_wire - src.shock_wire = pick(w) - w -= src.shock_wire - src.disable_wire = pick(w) - w -= src.disable_wire + //Randomize wires. + var/list/w = list("Light Red","Dark Red","Blue","Green","Yellow","Black","White","Gray","Orange","Pink") + hack_wire = pick(w) + w -= hack_wire + shock_wire = pick(w) + w -= shock_wire + disable_wire = pick(w) + w -= disable_wire - MouseDrop_T(mob/living/carbon/human/target as mob, mob/user as mob) - if (!istype(target) || target.buckled || get_dist(user, src) > 1 || get_dist(user, target) > 1 || user.stat || istype(user, /mob/living/silicon/ai)) - return - if(target == user) - if(target.h_style == "Floorlength Braid" || target.h_style == "Very Long Hair") - user.visible_message("\blue [user] looks like they're about to feed their own hair into the [src], but think better of it.", "\blue You grasp your hair and are about to feed it into the [src], but stop and come to your sense.") - return - src.add_fingerprint(user) - var/target_loc = target.loc - if(target != user && !user.restrained() && !user.stat && !user.weakened && !user.stunned && !user.paralysis) - if(target.h_style != "Cut Hair" || target.h_style != "Short Hair" || target.h_style != "Skinhead" || target.h_style != "Buzzcut" || target.h_style != "Crewcut" || target.h_style != "Bald" || target.h_style != "Balding Hair") - user.visible_message("\red [user] starts feeding [target]'s hair into the [src]!", "\red You start feeding [target]'s hair into the [src]!") - if(!do_after(usr, 50)) - return - if(target_loc != target.loc) - return - if(target != user && !user.restrained() && !user.stat && !user.weakened && !user.stunned && !user.paralysis) - user.visible_message("\red [user] feeds the [target]'s hair into the [src] and flicks it on!", "\red You turn the [src] on!") - target.apply_damage(30, BRUTE, "head") - target.apply_damage(25, HALLOSS) - target.say("*scream") +//Updates overall lathe storage size. +/obj/machinery/autolathe/RefreshParts() + ..() + var/tot_rating = 0 + for(var/obj/item/weapon/stock_parts/matter_bin/MB in component_parts) + tot_rating += MB.rating - user.attack_log += text("\[[time_stamp()]\] Has fed [target.name]'s ([target.ckey]) hair into a [src].") - target.attack_log += text("\[[time_stamp()]\] Has had their hair fed into [src] by [user.name] ([user.ckey])") - msg_admin_attack("[user] ([user.ckey]) fed [target]'s ([target.ckey]) in a [src]. (JMP)") - else - return - if(!do_after(usr, 35)) - return - if(target_loc != target.loc) - return - if(target != user && !user.restrained() && !user.stat && !user.weakened && !user.stunned && !user.paralysis) - user.visible_message("\red [user] starts tugging on [target]'s head as the [src] keeps running!", "\red You start tugging on [target]'s head!") - target.apply_damage(25, BRUTE, "head") - target.apply_damage(10, HALLOSS) - target.say("*scream") - spawn(10) - user.visible_message("\red [user] stops the [src] and leaves [target] resting as they are.", "\red You turn the [src] off and let go of [target].") \ No newline at end of file + storage_capacity["metal"] = tot_rating * 25000 + storage_capacity["glass"] = tot_rating * 12500 + +/obj/machinery/autolathe/dismantle() + ..() + var/list/sheets = list("metal" = /obj/item/stack/sheet/metal, "glass" = /obj/item/stack/sheet/glass) + + for(var/mat in stored_material) + var/T = sheets[mat] + var/obj/item/stack/sheet/S = new T + if(stored_material[mat] > S.perunit) + S.amount = round(stored_material[mat] / S.perunit) + S.loc = loc \ No newline at end of file diff --git a/code/game/machinery/autolathe_datums.dm b/code/game/machinery/autolathe_datums.dm new file mode 100644 index 00000000..06955ea2 --- /dev/null +++ b/code/game/machinery/autolathe_datums.dm @@ -0,0 +1,348 @@ +/var/global/list/autolathe_recipes +/var/global/list/autolathe_categories + +/datum/autolathe/recipe + var/name = "object" + var/path + var/list/resources + var/hidden + var/category + var/power_use = 0 + var/is_stack + +/datum/autolathe/recipe/bucket + name = "bucket" + path = /obj/item/weapon/reagent_containers/glass/bucket + category = "General" + +/datum/autolathe/recipe/flashlight + name = "flashlight" + path = /obj/item/device/flashlight + category = "General" + +/datum/autolathe/recipe/extinguisher + name = "extinguisher" + path = /obj/item/weapon/extinguisher + category = "General" + +/datum/autolathe/recipe/crowbar + name = "crowbar" + path = /obj/item/weapon/crowbar + category = "Tools" + +/datum/autolathe/recipe/multitool + name = "multitool" + path = /obj/item/device/multitool + category = "Tools" + +/datum/autolathe/recipe/t_scanner + name = "T-ray scanner" + path = /obj/item/device/t_scanner + category = "Tools" + +/datum/autolathe/recipe/weldertool + name = "welding tool" + path = /obj/item/weapon/weldingtool + category = "Tools" + +/datum/autolathe/recipe/screwdriver + name = "screwdriver" + path = /obj/item/weapon/screwdriver + category = "Tools" + +/datum/autolathe/recipe/wirecutters + name = "wirecutters" + path = /obj/item/weapon/wirecutters + category = "Tools" + +/datum/autolathe/recipe/wrench + name = "wrench" + path = /obj/item/weapon/wrench + category = "Tools" + +/datum/autolathe/recipe/radio_headset + name = "radio headset" + path = /obj/item/device/radio/headset + category = "General" + +/datum/autolathe/recipe/radio_bounced + name = "station bounced radio" + path = /obj/item/device/radio/off + category = "General" + +/datum/autolathe/recipe/weldermask + name = "welding mask" + path = /obj/item/clothing/head/welding + category = "General" + +/datum/autolathe/recipe/metal + name = "steel sheets" + path = /obj/item/stack/sheet/metal + category = "General" + is_stack = 1 + +/datum/autolathe/recipe/glass + name = "glass sheets" + path = /obj/item/stack/sheet/glass + category = "General" + is_stack = 1 + +/datum/autolathe/recipe/rglass + name = "reinforced glass sheets" + path = /obj/item/stack/sheet/rglass + category = "General" + is_stack = 1 + +/datum/autolathe/recipe/rods + name = "metal rods" + path = /obj/item/stack/rods + category = "General" + is_stack = 1 + +/datum/autolathe/recipe/knife + name = "kitchen knife" + path = /obj/item/weapon/kitchenknife + category = "General" + +/datum/autolathe/recipe/taperecorder + name = "tape recorder" + path = /obj/item/device/taperecorder + category = "General" + +/datum/autolathe/recipe/airlockmodule + name = "airlock electronics" + path = /obj/item/weapon/airlock_electronics + category = "Engineering" + +/datum/autolathe/recipe/airalarm + name = "air alarm electronics" + path = /obj/item/weapon/airalarm_electronics + category = "Engineering" + +/datum/autolathe/recipe/firealarm + name = "fire alarm electronics" + path = /obj/item/weapon/firealarm_electronics + category = "Engineering" + +/datum/autolathe/recipe/powermodule + name = "power control module" + path = /obj/item/weapon/module/power_control + category = "Engineering" + +/datum/autolathe/recipe/rcd_ammo + name = "matter cartridge" + path = /obj/item/weapon/rcd_ammo + category = "Engineering" + +/datum/autolathe/recipe/scalpel + name = "scalpel" + path = /obj/item/weapon/scalpel + category = "Medical" + +/datum/autolathe/recipe/circularsaw + name = "circular saw" + path = /obj/item/weapon/circular_saw + category = "Medical" + +/datum/autolathe/recipe/surgicaldrill + name = "surgical drill" + path = /obj/item/weapon/surgicaldrill + category = "Medical" + +/datum/autolathe/recipe/retractor + name = "retractor" + path = /obj/item/weapon/retractor + category = "Medical" + +/datum/autolathe/recipe/cautery + name = "cautery" + path = /obj/item/weapon/cautery + category = "Medical" + +/datum/autolathe/recipe/hemostat + name = "hemostat" + path = /obj/item/weapon/hemostat + category = "Medical" + +/datum/autolathe/recipe/beaker + name = "glass beaker" + path = /obj/item/weapon/reagent_containers/glass/beaker + category = "Medical" + +/datum/autolathe/recipe/beaker_large + name = "large glass beaker" + path = /obj/item/weapon/reagent_containers/glass/beaker/large + category = "Medical" + +/datum/autolathe/recipe/vial + name = "glass vial" + path = /obj/item/weapon/reagent_containers/glass/beaker/vial + category = "Medical" + +/datum/autolathe/recipe/pillbottle + name = "pill bottle" + path = /obj/item/weapon/storage/pill_bottle + category = "Medical" + +/datum/autolathe/recipe/syringe + name = "syringe" + path = /obj/item/weapon/reagent_containers/syringe + category = "Medical" + +/datum/autolathe/recipe/shotgun_blanks + name = "ammunition (shotgun, blanks)" + path = /obj/item/ammo_casing/shotgun/blank + category = "Arms and Ammunition" + +/datum/autolathe/recipe/shotgun_beanbag + name = "ammunition (shotgun, beanbag)" + path = /obj/item/ammo_casing/shotgun/beanbag + category = "Arms and Ammunition" + +/datum/autolathe/recipe/magazine_rubber + name = "ammunition (rubber)" + path = /obj/item/ammo_magazine/c45r + category = "Arms and Ammunition" + +/datum/autolathe/recipe/consolescreen + name = "console screen" + path = /obj/item/weapon/stock_parts/console_screen + category = "Devices and Components" + +/datum/autolathe/recipe/igniter + name = "igniter" + path = /obj/item/device/assembly/igniter + category = "Devices and Components" + +/datum/autolathe/recipe/signaler + name = "signaler" + path = /obj/item/device/assembly/signaler + category = "Devices and Components" + +/datum/autolathe/recipe/sensor_infra + name = "infrared sensor" + path = /obj/item/device/assembly/infra + category = "Devices and Components" + +/datum/autolathe/recipe/timer + name = "timer" + path = /obj/item/device/assembly/timer + category = "Devices and Components" + +/datum/autolathe/recipe/sensor_prox + name = "proximity sensor" + path = /obj/item/device/assembly/prox_sensor + category = "Devices and Components" + +/datum/autolathe/recipe/tube + name = "light tube" + path = /obj/item/weapon/light/tube + category = "General" + +/datum/autolathe/recipe/bulb + name = "light bulb" + path = /obj/item/weapon/light/bulb + category = "General" + +/datum/autolathe/recipe/ashtray_glass + name = "glass ashtray" + path = /obj/item/ashtray/glass + category = "General" + +/datum/autolathe/recipe/camera_assembly + name = "camera assembly" + path = /obj/item/weapon/camera_assembly + category = "Engineering" + +/datum/autolathe/recipe/flamethrower + name = "flamethrower" + path = /obj/item/weapon/flamethrower/full + hidden = 1 + category = "Arms and Ammunition" + +/datum/autolathe/recipe/magazine_revolver_1 + name = "ammunition (.357)" + path = /obj/item/ammo_magazine/a357 + hidden = 1 + category = "Arms and Ammunition" + +/datum/autolathe/recipe/magazine_revolver_2 + name = "ammunition (.45)" + path = /obj/item/ammo_magazine/c45m + hidden = 1 + category = "Arms and Ammunition" + +/datum/autolathe/recipe/shotgun + name = "ammunition (shell, shotgun)" + path = /obj/item/ammo_casing/shotgun + hidden = 1 + category = "Arms and Ammunition" + +/datum/autolathe/recipe/shotgun_dart + name = "ammunition (dart, shotgun)" + path = /obj/item/ammo_casing/shotgun/dart + hidden = 1 + category = "Arms and Ammunition" + +/datum/autolathe/recipe/rcd + name = "rapid construction device" + path = /obj/item/weapon/rcd + hidden = 1 + category = "Engineering" + +/datum/autolathe/recipe/electropack + name = "electropack" + path = /obj/item/device/radio/electropack + hidden = 1 + category = "Devices and Components" + +/datum/autolathe/recipe/welder_industrial + name = "industrial welding tool" + path = /obj/item/weapon/weldingtool/largetank + hidden = 1 + category = "Tools" + +/datum/autolathe/recipe/handcuffs + name = "handcuffs" + path = /obj/item/weapon/handcuffs + hidden = 1 + category = "General" + +/datum/autolathe/recipe/oxygentank + name = "oxygen tank" + path = /obj/item/weapon/tank/autolathe + category = "General" + +/datum/autolathe/recipe/bonesetter + name = "bonesetter" + path = /obj/item/weapon/bonesetter + category = "Medical" + +/datum/autolathe/recipe/sunglasses + name = "sunglasses" + path = /obj/item/clothing/glasses/sunglasses + hidden = 1 + category = "General" + +/datum/autolathe/recipe/lantern + name = "lantern" + path = /obj/item/device/flashlight/lantern + category = "General" + +/datum/autolathe/recipe/incendiaryshotgunmag + name = "incendiary shotgun ammunition - Magazine" + path = /obj/item/ammo_magazine/shotgun/incendiary + hidden = 1 + category = "Arms and Ammunition" + +/datum/autolathe/recipe/incendiaryshotgunammo + name = "incendiary shotgun ammunation - Ammo" + path = /obj/item/ammo_casing/shotgun/incendiary + hidden = 1 + category = "Arms and Ammunition" + +/datum/autolathe/recipe/tanktransfervalve + name = "tank transfer valve" + path = /obj/item/device/transfer_valve + hidden = 1 + category = "General" \ No newline at end of file diff --git a/code/game/machinery/bots/bots.dm b/code/game/machinery/bots/bots.dm index 68184517..ddbcc5f4 100644 --- a/code/game/machinery/bots/bots.dm +++ b/code/game/machinery/bots/bots.dm @@ -37,7 +37,7 @@ if(locked) locked = 0 emagged = 1 - user << "You bypass [src]'s controls." + user << "You short out [src]'s maintenance hatch lock." if(!locked && open) emagged = 2 diff --git a/code/game/machinery/bots/farmbot.dm b/code/game/machinery/bots/farmbot.dm index 228b5250..1c0bd087 100644 --- a/code/game/machinery/bots/farmbot.dm +++ b/code/game/machinery/bots/farmbot.dm @@ -394,7 +394,7 @@ if ( emagged ) // Warning, hungry humans detected: throw fertilizer at them spawn(0) fert.loc = src.loc - fert.throw_at(target, 16, 3) + fert.throw_at(target, 16, 3, src) src.visible_message("\red [src] launches [fert.name] at [target.name]!") flick("farmbot_broke", src) spawn (FARMBOT_EMAG_DELAY) diff --git a/code/game/machinery/bots/floorbot.dm b/code/game/machinery/bots/floorbot.dm index 59a96573..0c021eb3 100644 --- a/code/game/machinery/bots/floorbot.dm +++ b/code/game/machinery/bots/floorbot.dm @@ -79,7 +79,7 @@ var/dat dat += "Automatic Station Floor Repairer v1.0

" dat += "Status: [src.on ? "On" : "Off"]
" - dat += "Maintenance panel panel is [src.open ? "opened" : "closed"]
" + dat += "Maintenance panel is [src.open ? "opened" : "closed"]
" dat += "Tiles left: [src.amount]
" dat += "Behvaiour controls are [src.locked ? "locked" : "unlocked"]
" if(!src.locked || issilicon(user)) diff --git a/code/game/machinery/bots/medbot.dm b/code/game/machinery/bots/medbot.dm index f0c649ac..64e3ce9c 100644 --- a/code/game/machinery/bots/medbot.dm +++ b/code/game/machinery/bots/medbot.dm @@ -105,7 +105,7 @@ var/dat dat += "Automatic Medical Unit v1.0

" dat += "Status: [src.on ? "On" : "Off"]
" - dat += "Maintenance panel panel is [src.open ? "opened" : "closed"]
" + dat += "Maintenance panel is [src.open ? "opened" : "closed"]
" dat += "Beaker: " if (src.reagent_glass) dat += "Loaded \[[src.reagent_glass.reagents.total_volume]/[src.reagent_glass.reagents.maximum_volume]\]" diff --git a/code/game/machinery/bots/mulebot.dm b/code/game/machinery/bots/mulebot.dm index e05ff5ea..d0b0aa0d 100644 --- a/code/game/machinery/bots/mulebot.dm +++ b/code/game/machinery/bots/mulebot.dm @@ -789,7 +789,7 @@ return get_turf(src) -// called from mob/living/carbon/human/HasEntered() +// called from mob/living/carbon/human/Crossed() // when mulebot is in the same loc /obj/machinery/bot/mulebot/proc/RunOver(var/mob/living/carbon/human/H) src.visible_message("\red [src] drives over [H]!") diff --git a/code/game/machinery/bots/secbot.dm b/code/game/machinery/bots/secbot.dm index 3aa31527..587ca17e 100644 --- a/code/game/machinery/bots/secbot.dm +++ b/code/game/machinery/bots/secbot.dm @@ -111,7 +111,7 @@ Automatic Security Unit v1.3

Status: []
Behaviour controls are [src.locked ? "locked" : "unlocked"]
-Maintenance panel panel is [src.open ? "opened" : "closed"]"}, +Maintenance panel is [src.open ? "opened" : "closed"]"}, "[src.on ? "On" : "Off"]" ) @@ -662,13 +662,13 @@ Auto Patrol: []"}, threatcount -= 2 if(src.check_records) - for (var/datum/data/record/E in data_core.general) - var/perpname = perp.name - if(perp.wear_id) - var/obj/item/weapon/card/id/id = perp.wear_id.GetID() - if(id) - perpname = id.registered_name + var/perpname = perp.name + if(perp.wear_id) + var/obj/item/weapon/card/id/id = perp.wear_id.GetID() + if(id) + perpname = id.registered_name + for (var/datum/data/record/E in data_core.general) if(E.fields["name"] == perpname) for (var/datum/data/record/R in data_core.security) if((R.fields["id"] == E.fields["id"]) && (R.fields["criminal"] == "*Arrest*")) @@ -713,9 +713,7 @@ Auto Patrol: []"}, Sa.overlays += image('icons/obj/aibots.dmi', "hs_hole") Sa.created_name = src.name new /obj/item/device/assembly/prox_sensor(Tsec) - - var/obj/item/weapon/melee/baton/B = new /obj/item/weapon/melee/baton(Tsec) - B.charges = 0 + new /obj/item/weapon/melee/baton(Tsec) if(prob(50)) new /obj/item/robot_parts/l_arm(Tsec) diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index 9fe62563..e7a83dc9 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -70,13 +70,7 @@ cancelCameraAlarm() if(can_use()) cameranet.addCamera(src) - for(var/mob/O in mob_list) - if (istype(O.machine, /obj/machinery/computer/security)) - var/obj/machinery/computer/security/S = O.machine - if (S.current == src) - O.unset_machine() - O.reset_view(null) - O << "The screen bursts into static." + kick_viewers() ..() @@ -196,6 +190,10 @@ // now disconnect anyone using the camera //Apparently, this will disconnect anyone even if the camera was re-activated. //I guess that doesn't matter since they can't use it anyway? + kick_viewers() + +//This might be redundant, because of check_eye() +/obj/machinery/camera/proc/kick_viewers() for(var/mob/O in player_list) if (istype(O.machine, /obj/machinery/computer/security)) var/obj/machinery/computer/security/S = O.machine diff --git a/code/game/machinery/camera/camera_assembly.dm b/code/game/machinery/camera/camera_assembly.dm index d403d418..46b36088 100644 --- a/code/game/machinery/camera/camera_assembly.dm +++ b/code/game/machinery/camera/camera_assembly.dm @@ -6,11 +6,10 @@ w_class = 2 anchored = 0 - m_amt = 700 - g_amt = 300 + matter = list("metal" = 700,"glass" = 300) // Motion, EMP-Proof, X-Ray - var/list/obj/item/possible_upgrades = list(/obj/item/device/assembly/prox_sensor, /obj/item/stack/sheet/mineral/plasma, /obj/item/weapon/reagent_containers/food/snacks/grown/carrot) + var/list/obj/item/possible_upgrades = list(/obj/item/device/assembly/prox_sensor, /obj/item/stack/sheet/mineral/plasma, /obj/item/weapon/stock_parts/scanning_module) var/list/upgrades = list() var/state = 0 var/busy = 0 @@ -87,7 +86,8 @@ usr << "No network found please hang up and try your call again." return - var/temptag = "[get_area(src)] ([rand(1, 999)])" + var/area/camera_area = get_area(src) + var/temptag = "[sanitize(camera_area.name)] ([rand(1, 999)])" input = strip_html(input(usr, "How would you like to name the camera?", "Set Camera Name", temptag)) state = 4 @@ -124,7 +124,7 @@ // Upgrades! if(is_type_in_list(W, possible_upgrades) && !is_type_in_list(W, upgrades)) // Is a possible upgrade and isn't in the camera already. - user << "You attach the [W] into the assembly inner circuits." + user << "You attach \the [W] into the assembly inner circuits." upgrades += W user.drop_item(W) W.loc = src diff --git a/code/game/machinery/camera/motion.dm b/code/game/machinery/camera/motion.dm index d672af55..e7db077a 100644 --- a/code/game/machinery/camera/motion.dm +++ b/code/game/machinery/camera/motion.dm @@ -8,6 +8,8 @@ /obj/machinery/camera/process() // motion camera event loop + if (stat & (EMPED|NOPOWER)) + return if(!isMotion()) . = PROCESS_KILL return @@ -40,16 +42,20 @@ cancelAlarm() /obj/machinery/camera/proc/cancelAlarm() + if (!status || (stat & NOPOWER)) + return 0 if (detectTime == -1) for (var/mob/living/silicon/aiPlayer in player_list) - if (status) aiPlayer.cancelAlarm("Motion", get_area(src), src) + aiPlayer.cancelAlarm("Motion", get_area(src), src) detectTime = 0 return 1 /obj/machinery/camera/proc/triggerAlarm() + if (!status || (stat & NOPOWER)) + return 0 if (!detectTime) return 0 for (var/mob/living/silicon/aiPlayer in player_list) - if (status) aiPlayer.triggerAlarm("Motion", get_area(src), list(src), src) + aiPlayer.triggerAlarm("Motion", get_area(src), list(src), src) detectTime = -1 return 1 diff --git a/code/game/machinery/camera/presets.dm b/code/game/machinery/camera/presets.dm index bbe579d8..571eca02 100644 --- a/code/game/machinery/camera/presets.dm +++ b/code/game/machinery/camera/presets.dm @@ -57,8 +57,10 @@ return O /obj/machinery/camera/proc/isXRay() - var/O = locate(/obj/item/weapon/reagent_containers/food/snacks/grown/carrot) in assembly.upgrades - return O + var/obj/item/weapon/stock_parts/scanning_module/O = locate(/obj/item/weapon/stock_parts/scanning_module) in assembly.upgrades + if (O && O.rating >= 2) + return O + return null /obj/machinery/camera/proc/isMotion() var/O = locate(/obj/item/device/assembly/prox_sensor) in assembly.upgrades @@ -70,8 +72,18 @@ assembly.upgrades.Add(new /obj/item/stack/sheet/mineral/plasma(assembly)) /obj/machinery/camera/proc/upgradeXRay() - assembly.upgrades.Add(new /obj/item/weapon/reagent_containers/food/snacks/grown/carrot(assembly)) + assembly.upgrades.Add(new /obj/item/weapon/stock_parts/scanning_module/adv(assembly)) + setPowerUsage() // If you are upgrading Motion, and it isn't in the camera's New(), add it to the machines list. /obj/machinery/camera/proc/upgradeMotion() - assembly.upgrades.Add(new /obj/item/device/assembly/prox_sensor(assembly)) \ No newline at end of file + assembly.upgrades.Add(new /obj/item/device/assembly/prox_sensor(assembly)) + setPowerUsage() + +/obj/machinery/camera/proc/setPowerUsage() + var/mult = 1 + if (isXRay()) + mult++ + if (isMotion()) + mult++ + active_power_usage = mult*initial(active_power_usage) diff --git a/code/game/machinery/camera/tracking.dm b/code/game/machinery/camera/tracking.dm index 678f2201..4a14ff1d 100644 --- a/code/game/machinery/camera/tracking.dm +++ b/code/game/machinery/camera/tracking.dm @@ -1,3 +1,15 @@ +/mob/living/silicon/ai/var/max_locations = 5 +/mob/living/silicon/ai/var/stored_locations[0] + +/mob/living/silicon/ai/proc/InvalidTurf(turf/T as turf) + if(!T) + return 1 + if(T.z == 2) + return 1 + if(T.z > 6) + return 1 + return 0 + /mob/living/silicon/ai/proc/get_camera_list() if(src.stat == 2) @@ -38,6 +50,59 @@ return +/mob/living/silicon/ai/proc/ai_store_location(loc as text) + set category = "AI Commands" + set name = "Store Camera Location" + set desc = "Stores your current camera location by the given name" + + loc = copytext(sanitize(loc), 1, MAX_MESSAGE_LEN) + if(!loc) + src << "\red Must supply a location name" + return + + if(stored_locations.len >= max_locations) + src << "\red Cannot store additional locations. Remove one first" + return + + if(loc in stored_locations) + src << "\red There is already a stored location by this name" + return + + var/L = src.eyeobj.getLoc() + if (InvalidTurf(get_turf(L))) + src << "\red Unable to store this location" + return + + stored_locations[loc] = L + src << "Location '[loc]' stored" + +/mob/living/silicon/ai/proc/sorted_stored_locations() + return sortList(stored_locations) + +/mob/living/silicon/ai/proc/ai_goto_location(loc in sorted_stored_locations()) + set category = "AI Commands" + set name = "Goto Camera Location" + set desc = "Returns to the selected camera location" + + if (!(loc in stored_locations)) + src << "\red Location [loc] not found" + return + + var/L = stored_locations[loc] + src.eyeobj.setLoc(L) + +/mob/living/silicon/ai/proc/ai_remove_location(loc in sorted_stored_locations()) + set category = "AI Commands" + set name = "Delete Camera Location" + set desc = "Deletes the selected camera location" + + if (!(loc in stored_locations)) + src << "\red Location [loc] not found" + return + + stored_locations.Remove(loc) + src << "Location [loc] removed" + // Used to allow the AI is write in mob names/camera name from the CMD line. /datum/trackable var/list/names = list() @@ -55,12 +120,7 @@ for(var/mob/living/M in mob_list) // Easy checks first. // Don't detect mobs on Centcom. Since the wizard den is on Centcomm, we only need this. - var/turf/T = get_turf(M) - if(!T) - continue - if(T.z == 2) - continue - if(T.z > 6) + if(InvalidTurf(get_turf(M))) continue if(M == usr) continue diff --git a/code/game/machinery/camera/wires.dm b/code/game/machinery/camera/wires.dm index 1dcbc0db..c05a7f30 100644 --- a/code/game/machinery/camera/wires.dm +++ b/code/game/machinery/camera/wires.dm @@ -50,7 +50,6 @@ if(CAMERA_WIRE_POWER) deactivate(usr, 1) - //shock(usr) if(CAMERA_WIRE_LIGHT) light_disabled = 1 diff --git a/code/game/machinery/cell_charger.dm b/code/game/machinery/cell_charger.dm index 42f72bc6..2401837f 100644 --- a/code/game/machinery/cell_charger.dm +++ b/code/game/machinery/cell_charger.dm @@ -1,101 +1,103 @@ /obj/machinery/cell_charger - name = "cell charger" - desc = "It charges power cells." + name = "heavy-duty cell charger" + desc = "A much more powerful version of the standard recharger that is specially designed for charging power cells." icon = 'icons/obj/power.dmi' icon_state = "ccharger0" anchored = 1 use_power = 1 idle_power_usage = 5 - active_power_usage = 60 + active_power_usage = 40000 //40 kW. (this the power drawn when charging) power_channel = EQUIP var/obj/item/weapon/cell/charging = null var/chargelevel = -1 - var/efficiency = 0.875 //<1.0 means some power is lost in the charging process, >1.0 means free energy. - proc - updateicon() - icon_state = "ccharger[charging ? 1 : 0]" + +/obj/machinery/cell_charger/proc/updateicon() + icon_state = "ccharger[charging ? 1 : 0]" - if(charging && !(stat & (BROKEN|NOPOWER)) ) + if(charging && !(stat & (BROKEN|NOPOWER)) ) - var/newlevel = round(charging.percent() * 4.0 / 99) - //world << "nl: [newlevel]" + var/newlevel = round(charging.percent() * 4.0 / 99) + //world << "nl: [newlevel]" - if(chargelevel != newlevel) + if(chargelevel != newlevel) - overlays.Cut() - overlays += "ccharger-o[newlevel]" + overlays.Cut() + overlays += "ccharger-o[newlevel]" - chargelevel = newlevel - else - overlays.Cut() - examine() - set src in oview(5) - ..() - usr << "There's [charging ? "a" : "no"] cell in the charger." - if(charging) - usr << "Current charge: [charging.charge]" + chargelevel = newlevel + else + overlays.Cut() + +/obj/machinery/cell_charger/examine() + set src in oview(5) + ..() + usr << "There's [charging ? "a" : "no"] cell in the charger." + if(charging) + usr << "Current charge: [charging.charge]" - attackby(obj/item/weapon/W, mob/user) - if(stat & BROKEN) - return - - if(istype(W, /obj/item/weapon/cell) && anchored) - if(charging) - user << "\red There is already a cell in the charger." - return - else - var/area/a = loc.loc // Gets our locations location, like a dream within a dream - if(!isarea(a)) - return - if(a.power_equip == 0) // There's no APC in this area, don't try to cheat power! - user << "\red The [name] blinks red as you try to insert the cell!" - return - - user.drop_item() - W.loc = src - charging = W - user.visible_message("[user] inserts a cell into the charger.", "You insert a cell into the charger.") - chargelevel = -1 - updateicon() - else if(istype(W, /obj/item/weapon/wrench)) - if(charging) - user << "\red Remove the cell first!" - return - - anchored = !anchored - user << "You [anchored ? "attach" : "detach"] the cell charger [anchored ? "to" : "from"] the ground" - playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1) - - attack_hand(mob/user) - if(charging) - usr.put_in_hands(charging) - charging.add_fingerprint(user) - charging.updateicon() - - src.charging = null - user.visible_message("[user] removes the cell from the charger.", "You remove the cell from the charger.") - chargelevel = -1 - updateicon() - - attack_ai(mob/user) +/obj/machinery/cell_charger/attackby(obj/item/weapon/W, mob/user) + if(stat & BROKEN) return - emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - return + if(istype(W, /obj/item/weapon/cell) && anchored) if(charging) - charging.emp_act(severity) - ..(severity) - - - process() - //world << "ccpt [charging] [stat]" - if(!charging || (stat & (BROKEN|NOPOWER)) || !anchored) + user << "\red There is already a cell in the charger." return - - var/power_used = 100000 //for 200 units of charge. Yes, thats right, 100 kW. Is something wrong with CELLRATE? - - power_used = charging.give(power_used*CELLRATE*efficiency) - use_power(power_used) + else + var/area/a = loc.loc // Gets our locations location, like a dream within a dream + if(!isarea(a)) + return + if(a.power_equip == 0) // There's no APC in this area, don't try to cheat power! + user << "\red The [name] blinks red as you try to insert the cell!" + return + + user.drop_item() + W.loc = src + charging = W + user.visible_message("[user] inserts a cell into the charger.", "You insert a cell into the charger.") + chargelevel = -1 + updateicon() + else if(istype(W, /obj/item/weapon/wrench)) + if(charging) + user << "\red Remove the cell first!" + return + + anchored = !anchored + user << "You [anchored ? "attach" : "detach"] the cell charger [anchored ? "to" : "from"] the ground" + playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1) + +/obj/machinery/cell_charger/attack_hand(mob/user) + if(charging) + usr.put_in_hands(charging) + charging.add_fingerprint(user) + charging.updateicon() + + src.charging = null + user.visible_message("[user] removes the cell from the charger.", "You remove the cell from the charger.") + chargelevel = -1 + updateicon() + +/obj/machinery/cell_charger/attack_ai(mob/user) + return + +/obj/machinery/cell_charger/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + return + if(charging) + charging.emp_act(severity) + ..(severity) + + +/obj/machinery/cell_charger/process() + //world << "ccpt [charging] [stat]" + if((stat & (BROKEN|NOPOWER)) || !anchored) + update_use_power(0) + return + + if (charging && !charging.fully_charged()) + charging.give(active_power_usage*CELLRATE) + update_use_power(2) updateicon() + else + update_use_power(1) diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm index 523d73ae..18d919a1 100644 --- a/code/game/machinery/cloning.dm +++ b/code/game/machinery/cloning.dm @@ -183,6 +183,7 @@ H << "Consciousness slowly creeps over you as your body regenerates.
So this is what cloning feels like?
" // -- Mode/mind specific stuff goes here + callHook("clone", list(H)) switch(ticker.mode.name) if("revolution") @@ -214,6 +215,7 @@ for(var/datum/language/L in R.languages) H.add_language(L.name) + H.flavor_texts = R.flavor.Copy() H.suiciding = 0 src.attempting = 0 return 1 @@ -303,6 +305,21 @@ user.drop_item() del(W) return + else if (istype(W, /obj/item/weapon/wrench)) + if(src.locked && (src.anchored || src.occupant)) + user << "\red Can not do that while [src] is in use." + else + if(src.anchored) + src.anchored = 0 + connected.pod1 = null + connected = null + else + src.anchored = 1 + playsound(src.loc, 'sound/items/Ratchet.ogg', 100, 1) + if(anchored) + user.visible_message("[user] secures [src] to the floor.", "You secure [src] to the floor.") + else + user.visible_message("[user] unsecures [src] from the floor.", "You unsecure [src] from the floor.") else ..() diff --git a/code/game/machinery/computer/HolodeckControl.dm b/code/game/machinery/computer/HolodeckControl.dm index 9647d427..af0288e9 100644 --- a/code/game/machinery/computer/HolodeckControl.dm +++ b/code/game/machinery/computer/HolodeckControl.dm @@ -449,7 +449,7 @@ if(isrobot(user)) return - + ..() /obj/structure/table/holotable/wood diff --git a/code/game/machinery/computer/ai_core.dm b/code/game/machinery/computer/ai_core.dm index 0946e794..a9c8a9a7 100644 --- a/code/game/machinery/computer/ai_core.dm +++ b/code/game/machinery/computer/ai_core.dm @@ -245,6 +245,7 @@ That prevents a few funky behaviors. var/mob/living/silicon/ai/A = locate() in C//I love locate(). Best proc ever. if(A)//If AI exists on the card. Else nothing since both are empty. A.control_disabled = 0 + A.aiRadio.disabledAi = 0 A.loc = T.loc//To replace the terminal. C.icon_state = "aicard" C.name = "inteliCard" diff --git a/code/game/machinery/computer/aifixer.dm b/code/game/machinery/computer/aifixer.dm index d6cb0ede..e963aec6 100644 --- a/code/game/machinery/computer/aifixer.dm +++ b/code/game/machinery/computer/aifixer.dm @@ -3,7 +3,7 @@ icon = 'icons/obj/computer.dmi' icon_state = "ai-fixer" circuit = /obj/item/weapon/circuitboard/aifixer - req_access = list(access_captain, access_robotics, access_heads) + req_one_access = list(access_robotics, access_heads) var/mob/living/silicon/ai/occupant = null var/active = 0 @@ -17,7 +17,7 @@ user << "This terminal isn't functioning right now, get it working!" return I:transfer_ai("AIFIXER","AICARD",src,user) - + ..() return @@ -87,13 +87,14 @@ src.occupant.adjustToxLoss(-1) src.occupant.adjustBruteLoss(-1) src.occupant.updatehealth() - if (src.occupant.health >= 0 && src.occupant.stat == 2) - src.occupant.stat = 0 + if (src.occupant.health >= 0 && src.occupant.stat == DEAD) + src.occupant.stat = CONSCIOUS src.occupant.lying = 0 dead_mob_list -= src.occupant living_mob_list += src.occupant src.overlays -= image('icons/obj/computer.dmi', "ai-fixer-404") src.overlays += image('icons/obj/computer.dmi', "ai-fixer-full") + src.occupant.add_ai_verbs() src.updateUsrDialog() sleep(10) src.active = 0 diff --git a/code/game/machinery/computer/buildandrepair.dm b/code/game/machinery/computer/buildandrepair.dm index cafcae39..5020471d 100644 --- a/code/game/machinery/computer/buildandrepair.dm +++ b/code/game/machinery/computer/buildandrepair.dm @@ -19,211 +19,267 @@ icon_state = "id_mod" item_state = "electronic" origin_tech = "programming=2" - var/id = null - var/frequency = null var/build_path = null var/board_type = "computer" var/list/req_components = null - var/powernet = null - var/list/records = null var/frame_desc = null var/contain_parts = 1 +//Called when the circuitboard is used to contruct a new machine. +/obj/item/weapon/circuitboard/proc/construct(var/obj/machinery/M) + if (istype(M, build_path)) + return 1 + return 0 + +//Called when a computer is deconstructed to produce a circuitboard. +//Only used by computers, as other machines store their circuitboard instance. +/obj/item/weapon/circuitboard/proc/deconstruct(var/obj/machinery/M) + if (istype(M, build_path)) + return 1 + return 0 + +/* + Circuit Board Definitions +*/ /obj/item/weapon/circuitboard/message_monitor name = "Circuit board (Message Monitor)" - build_path = "/obj/machinery/computer/message_monitor" + build_path = /obj/machinery/computer/message_monitor origin_tech = "programming=3" + +//TODO: Move these into computer/camera.dm /obj/item/weapon/circuitboard/security - name = "Circuit board (Security)" - build_path = "/obj/machinery/computer/security" + name = "Circuit board (Security Camera Monitor)" + build_path = /obj/machinery/computer/security var/network = list("SS13") req_access = list(access_security) var/locked = 1 var/emagged = 0 + +/obj/item/weapon/circuitboard/security/construct(var/obj/machinery/computer/security/C) + if (..(C)) + C.network = network + +/obj/item/weapon/circuitboard/security/deconstruct(var/obj/machinery/computer/security/C) + if (..(C)) + network = C.network + +/obj/item/weapon/circuitboard/security/engineering + name = "Circuit board (Engineering Camera Monitor)" + build_path = /obj/machinery/computer/security/engineering + network = list("Engineering","Power Alarms","Atmosphere Alarms","Fire Alarms") + req_access = list() +/obj/item/weapon/circuitboard/security/mining + name = "Circuit board (Mining Camera Monitor)" + build_path = /obj/machinery/computer/security/mining + network = list("MINE") + req_access = list() /obj/item/weapon/circuitboard/aicore - name = "Circuit board (AI core)" + name = "Circuit board (AI Core)" origin_tech = "programming=4;biotech=2" board_type = "other" /obj/item/weapon/circuitboard/aiupload name = "Circuit board (AI Upload)" - build_path = "/obj/machinery/computer/aiupload" + build_path = /obj/machinery/computer/aiupload origin_tech = "programming=4" /obj/item/weapon/circuitboard/borgupload name = "Circuit board (Cyborg Upload)" - build_path = "/obj/machinery/computer/borgupload" + build_path = /obj/machinery/computer/borgupload origin_tech = "programming=4" /obj/item/weapon/circuitboard/med_data name = "Circuit board (Medical Records)" - build_path = "/obj/machinery/computer/med_data" + build_path = /obj/machinery/computer/med_data /obj/item/weapon/circuitboard/pandemic name = "Circuit board (PanD.E.M.I.C. 2200)" - build_path = "/obj/machinery/computer/pandemic" + build_path = /obj/machinery/computer/pandemic origin_tech = "programming=2;biotech=2" /obj/item/weapon/circuitboard/scan_consolenew name = "Circuit board (DNA Machine)" - build_path = "/obj/machinery/computer/scan_consolenew" + build_path = /obj/machinery/computer/scan_consolenew origin_tech = "programming=2;biotech=2" /obj/item/weapon/circuitboard/communications name = "Circuit board (Communications)" - build_path = "/obj/machinery/computer/communications" + build_path = /obj/machinery/computer/communications origin_tech = "programming=2;magnets=2" /obj/item/weapon/circuitboard/card name = "Circuit board (ID Computer)" - build_path = "/obj/machinery/computer/card" + build_path = /obj/machinery/computer/card /obj/item/weapon/circuitboard/card/centcom name = "Circuit board (CentCom ID Computer)" - build_path = "/obj/machinery/computer/card/centcom" + build_path = /obj/machinery/computer/card/centcom //obj/item/weapon/circuitboard/shield // name = "Circuit board (Shield Control)" // build_path = "/obj/machinery/computer/stationshield" /obj/item/weapon/circuitboard/teleporter name = "Circuit board (Teleporter)" - build_path = "/obj/machinery/computer/teleporter" + build_path = /obj/machinery/computer/teleporter origin_tech = "programming=2;bluespace=2" /obj/item/weapon/circuitboard/secure_data name = "Circuit board (Security Records)" - build_path = "/obj/machinery/computer/secure_data" + build_path = /obj/machinery/computer/secure_data /obj/item/weapon/circuitboard/skills name = "Circuit board (Employment Records)" - build_path = "/obj/machinery/computer/skills" + build_path = /obj/machinery/computer/skills /obj/item/weapon/circuitboard/stationalert name = "Circuit board (Station Alerts)" - build_path = "/obj/machinery/computer/station_alert" -/obj/item/weapon/circuitboard/atmospheresiphonswitch - name = "Circuit board (Atmosphere siphon control)" - build_path = "/obj/machinery/computer/atmosphere/siphonswitch" + build_path = /obj/machinery/computer/station_alert +///obj/item/weapon/circuitboard/atmospheresiphonswitch +// name = "Circuit board (Atmosphere Siphon Control)" +// build_path = /obj/machinery/computer/atmosphere/siphonswitch /obj/item/weapon/circuitboard/air_management - name = "Circuit board (Atmospheric monitor)" - build_path = "/obj/machinery/computer/general_air_control" -/obj/item/weapon/circuitboard/injector_control - name = "Circuit board (Injector control)" - build_path = "/obj/machinery/computer/general_air_control/fuel_injection" + name = "Circuit board (Atmospheric Monitor)" + build_path = /obj/machinery/computer/general_air_control + var/frequency = 1439 +/obj/item/weapon/circuitboard/air_management/tank_control + name = "Circuit board (Tank Control)" + build_path = /obj/machinery/computer/general_air_control/large_tank_control + frequency = 1441 +/obj/item/weapon/circuitboard/air_management/supermatter_core + name = "Circuit board (Core Control)" + build_path = /obj/machinery/computer/general_air_control/supermatter_core + frequency = 1438 +/obj/item/weapon/circuitboard/air_management/injector_control + name = "Circuit board (Injector Control)" + build_path = /obj/machinery/computer/general_air_control/fuel_injection + +/obj/item/weapon/circuitboard/air_management/construct(var/obj/machinery/computer/general_air_control/C) + if (..(C)) + C.frequency = frequency + +/obj/item/weapon/circuitboard/air_management/deconstruct(var/obj/machinery/computer/general_air_control/C) + if (..(C)) + frequency = C.frequency + /obj/item/weapon/circuitboard/atmos_alert name = "Circuit board (Atmospheric Alert)" - build_path = "/obj/machinery/computer/atmos_alert" + build_path = /obj/machinery/computer/atmos_alert /obj/item/weapon/circuitboard/pod name = "Circuit board (Massdriver control)" - build_path = "/obj/machinery/computer/pod" + build_path = /obj/machinery/computer/pod /obj/item/weapon/circuitboard/robotics name = "Circuit board (Robotics Control)" - build_path = "/obj/machinery/computer/robotics" + build_path = /obj/machinery/computer/robotics origin_tech = "programming=3" /obj/item/weapon/circuitboard/drone_control name = "Circuit board (Drone Control)" - build_path = "/obj/machinery/computer/drone_control" + build_path = /obj/machinery/computer/drone_control origin_tech = "programming=3" /obj/item/weapon/circuitboard/cloning name = "Circuit board (Cloning)" - build_path = "/obj/machinery/computer/cloning" + build_path = /obj/machinery/computer/cloning origin_tech = "programming=3;biotech=3" /obj/item/weapon/circuitboard/arcade name = "Circuit board (Arcade)" - build_path = "/obj/machinery/computer/arcade" + build_path = /obj/machinery/computer/arcade origin_tech = "programming=1" /obj/item/weapon/circuitboard/turbine_control name = "Circuit board (Turbine control)" - build_path = "/obj/machinery/computer/turbine_computer" + build_path = /obj/machinery/computer/turbine_computer /obj/item/weapon/circuitboard/solar_control - name = "Circuit board (Solar Control)" //name fixed 250810 - build_path = "/obj/machinery/power/solar_control" + name = "Circuit board (Solar Control)" + build_path = /obj/machinery/power/solar_control origin_tech = "programming=2;powerstorage=2" /obj/item/weapon/circuitboard/powermonitor - name = "Circuit board (Power Monitor)" //name fixed 250810 - build_path = "/obj/machinery/power/monitor" + name = "Circuit board (Power Monitor)" + build_path = /obj/machinery/power/monitor /obj/item/weapon/circuitboard/olddoor name = "Circuit board (DoorMex)" - build_path = "/obj/machinery/computer/pod/old" + build_path = /obj/machinery/computer/pod/old /obj/item/weapon/circuitboard/syndicatedoor name = "Circuit board (ProComp Executive)" - build_path = "/obj/machinery/computer/pod/old/syndicate" + build_path = /obj/machinery/computer/pod/old/syndicate /obj/item/weapon/circuitboard/swfdoor name = "Circuit board (Magix)" - build_path = "/obj/machinery/computer/pod/old/swf" + build_path = /obj/machinery/computer/pod/old/swf /obj/item/weapon/circuitboard/prisoner name = "Circuit board (Prisoner Management)" - build_path = "/obj/machinery/computer/prisoner" + build_path = /obj/machinery/computer/prisoner /obj/item/weapon/circuitboard/rdconsole name = "Circuit Board (RD Console)" - build_path = "/obj/machinery/computer/rdconsole/core" + build_path = /obj/machinery/computer/rdconsole/core /obj/item/weapon/circuitboard/mecha_control name = "Circuit Board (Exosuit Control Console)" - build_path = "/obj/machinery/computer/mecha" + build_path = /obj/machinery/computer/mecha /obj/item/weapon/circuitboard/rdservercontrol name = "Circuit Board (R&D Server Control)" - build_path = "/obj/machinery/computer/rdservercontrol" + build_path = /obj/machinery/computer/rdservercontrol /obj/item/weapon/circuitboard/crew name = "Circuit board (Crew monitoring computer)" - build_path = "/obj/machinery/computer/crew" + build_path = /obj/machinery/computer/crew origin_tech = "programming=3;biotech=2;magnets=2" /obj/item/weapon/circuitboard/mech_bay_power_console name = "Circuit board (Mech Bay Power Control Console)" - build_path = "/obj/machinery/computer/mech_bay_power_console" + build_path = /obj/machinery/computer/mech_bay_power_console origin_tech = "programming=2;powerstorage=3" /obj/item/weapon/circuitboard/ordercomp name = "Circuit board (Supply ordering console)" - build_path = "/obj/machinery/computer/ordercomp" + build_path = /obj/machinery/computer/ordercomp origin_tech = "programming=2" + /obj/item/weapon/circuitboard/supplycomp name = "Circuit board (Supply shuttle console)" - build_path = "/obj/machinery/computer/supplycomp" + build_path = /obj/machinery/computer/supplycomp origin_tech = "programming=3" var/contraband_enabled = 0 -/obj/item/weapon/circuitboard/research_shuttle - name = "Circuit board (Research Shuttle)" - build_path = "/obj/machinery/computer/research_shuttle" - origin_tech = "programming=2" + +/obj/item/weapon/circuitboard/supplycomp/construct(var/obj/machinery/computer/supplycomp/SC) + if (..(SC)) + SC.can_order_contraband = contraband_enabled + +/obj/item/weapon/circuitboard/supplycomp/deconstruct(var/obj/machinery/computer/supplycomp/SC) + if (..(SC)) + contraband_enabled = SC.can_order_contraband + /obj/item/weapon/circuitboard/operating name = "Circuit board (Operating Computer)" - build_path = "/obj/machinery/computer/operating" + build_path = /obj/machinery/computer/operating origin_tech = "programming=2;biotech=2" /obj/item/weapon/circuitboard/comm_monitor name = "Circuit board (Telecommunications Monitor)" - build_path = "/obj/machinery/computer/telecomms/monitor" + build_path = /obj/machinery/computer/telecomms/monitor origin_tech = "programming=3" /obj/item/weapon/circuitboard/comm_server name = "Circuit board (Telecommunications Server Monitor)" - build_path = "/obj/machinery/computer/telecomms/server" + build_path = /obj/machinery/computer/telecomms/server origin_tech = "programming=3" /obj/item/weapon/circuitboard/comm_traffic name = "Circuitboard (Telecommunications Traffic Control)" - build_path = "/obj/machinery/computer/telecomms/traffic" + build_path = /obj/machinery/computer/telecomms/traffic origin_tech = "programming=3" /obj/item/weapon/circuitboard/curefab name = "Circuit board (Cure fab)" - build_path = "/obj/machinery/computer/curer" + build_path = /obj/machinery/computer/curer /obj/item/weapon/circuitboard/splicer name = "Circuit board (Disease Splicer)" - build_path = "/obj/machinery/computer/diseasesplicer" - + build_path = /obj/machinery/computer/diseasesplicer /obj/item/weapon/circuitboard/mining_shuttle name = "Circuit board (Mining Shuttle)" - build_path = "/obj/machinery/computer/mining_shuttle" - origin_tech = "programming=2" -/obj/item/weapon/circuitboard/research_shuttle - name = "Circuit board (Research Shuttle)" - build_path = "/obj/machinery/computer/research_shuttle" + build_path = /obj/machinery/computer/shuttle_control/mining origin_tech = "programming=2" /obj/item/weapon/circuitboard/engineering_shuttle name = "Circuit board (Engineering Shuttle)" - build_path = "/obj/machinery/computer/engineering_shuttle" + build_path = /obj/machinery/computer/shuttle_control/engineering + origin_tech = "programming=2" +/obj/item/weapon/circuitboard/research_shuttle + name = "Circuit board (Research Shuttle)" + build_path = /obj/machinery/computer/shuttle_control/research origin_tech = "programming=2" /obj/item/weapon/circuitboard/HolodeckControl // Not going to let people get this, but it's just here for future name = "Circuit board (Holodeck Control)" - build_path = "/obj/machinery/computer/HolodeckControl" + build_path = /obj/machinery/computer/HolodeckControl origin_tech = "programming=4" /obj/item/weapon/circuitboard/aifixer name = "Circuit board (AI Integrity Restorer)" - build_path = "/obj/machinery/computer/aifixer" + build_path = /obj/machinery/computer/aifixer origin_tech = "programming=3;biotech=2" /obj/item/weapon/circuitboard/area_atmos name = "Circuit board (Area Air Control)" - build_path = "/obj/machinery/computer/area_atmos" + build_path = /obj/machinery/computer/area_atmos origin_tech = "programming=2" /obj/item/weapon/circuitboard/prison_shuttle name = "Circuit board (Prison Shuttle)" - build_path = "/obj/machinery/computer/prison_shuttle" + build_path = /obj/machinery/computer/prison_shuttle origin_tech = "programming=2" @@ -286,13 +342,13 @@ /obj/item/weapon/circuitboard/rdconsole/attackby(obj/item/I as obj, mob/user as mob) if(istype(I,/obj/item/weapon/screwdriver)) user.visible_message("\blue \the [user] adjusts the jumper on the [src]'s access protocol pins.", "\blue You adjust the jumper on the access protocol pins.") - if(src.build_path == "/obj/machinery/computer/rdconsole/core") + if(src.build_path == /obj/machinery/computer/rdconsole/core) src.name = "Circuit Board (RD Console - Robotics)" - src.build_path = "/obj/machinery/computer/rdconsole/robotics" + src.build_path = /obj/machinery/computer/rdconsole/robotics user << "\blue Access protocols set to robotics." else src.name = "Circuit Board (RD Console)" - src.build_path = "/obj/machinery/computer/rdconsole/core" + src.build_path = /obj/machinery/computer/rdconsole/core user << "\blue Access protocols set to default." return @@ -300,7 +356,6 @@ switch(state) if(0) if(istype(P, /obj/item/weapon/wrench)) - usr << "\blue You begin wrenching the frame into place." playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1) if(do_after(user, 20)) user << "\blue You wrench the frame into place." @@ -356,7 +411,6 @@ if(istype(P, /obj/item/weapon/cable_coil)) if(P:amount >= 5) playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) - usr << "\blue You begin adding cables to the frame." if(do_after(user, 20)) if(P) P:amount -= 5 @@ -376,7 +430,6 @@ if(istype(P, /obj/item/stack/sheet/glass)) if(P:amount >= 2) playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) - usr << "\blue You begin adding glass into the frame." if(do_after(user, 20)) if(P) P:use(2) @@ -394,16 +447,5 @@ playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1) user << "\blue You connect the monitor." var/B = new src.circuit.build_path ( src.loc ) - if(circuit.powernet) B:powernet = circuit.powernet - if(circuit.id) B:id = circuit.id - if(circuit.records) B:records = circuit.records - if(circuit.frequency) B:frequency = circuit.frequency - if(istype(circuit,/obj/item/weapon/circuitboard/supplycomp)) - var/obj/machinery/computer/supplycomp/SC = B - var/obj/item/weapon/circuitboard/supplycomp/C = circuit - SC.can_order_contraband = C.contraband_enabled - if(istype(circuit,/obj/item/weapon/circuitboard/security)) - var/obj/machinery/computer/security/C = B - var/obj/item/weapon/circuitboard/security/CB = circuit - C.network = CB.network + src.circuit.construct(B) del(src) \ No newline at end of file diff --git a/code/game/machinery/computer/camera.dm b/code/game/machinery/computer/camera.dm index 28c17805..e4f1e72e 100644 --- a/code/game/machinery/computer/camera.dm +++ b/code/game/machinery/computer/camera.dm @@ -9,6 +9,7 @@ var/last_pic = 1.0 var/list/network = list("SS13") var/mapping = 0//For the overview file, interesting bit of code. + circuit = /obj/item/weapon/circuitboard/security attack_ai(var/mob/user as mob) @@ -20,8 +21,10 @@ check_eye(var/mob/user as mob) - if ((get_dist(user, src) > 1 || !( user.canmove ) || user.blinded || !( current ) || !( current.status )) && (!istype(user, /mob/living/silicon))) + if (user.stat || ((get_dist(user, src) > 1 || !( user.canmove ) || user.blinded) && !istype(user, /mob/living/silicon))) //user can't see - not sure why canmove is here. return null + if ( !current || !current.can_use() ) //camera doesn't work + current = null user.reset_view(current) return 1 @@ -45,7 +48,7 @@ D["Cancel"] = "Cancel" for(var/obj/machinery/camera/C in L) if(can_access_camera(C)) - D[text("[][]", C.c_tag, (C.status ? null : " (Deactivated)"))] = C + D[text("[][]", C.c_tag, (C.can_use() ? null : " (Deactivated)"))] = C var/t = input(user, "Which camera should you change to?") as null|anything in D if(!t) @@ -71,52 +74,18 @@ return 0 proc/switch_to_camera(var/mob/user, var/obj/machinery/camera/C) - if ((get_dist(user, src) > 1 || user.machine != src || user.blinded || !( user.canmove ) || !( C.can_use() )) && (!istype(user, /mob/living/silicon/ai))) - if(!C.can_use() && !isAI(user)) - src.current = null - return 0 - else - if(isAI(user)) - var/mob/living/silicon/ai/A = user - A.eyeobj.setLoc(get_turf(C)) - A.client.eye = A.eyeobj - else - src.current = C - use_power(50) + //don't need to check if the camera works for AI because the AI jumps to the camera location and doesn't actually look through cameras. + if(isAI(user)) + var/mob/living/silicon/ai/A = user + A.eyeobj.setLoc(get_turf(C)) + A.client.eye = A.eyeobj return 1 - - attackby(I as obj, user as mob) - if(istype(I, /obj/item/weapon/screwdriver)) - playsound(loc, 'sound/items/Screwdriver.ogg', 50, 1) - if(do_after(user, 20)) - if (stat & BROKEN) - user << "\blue The broken glass falls out." - var/obj/structure/computerframe/CF = new /obj/structure/computerframe(loc) - new /obj/item/weapon/shard(loc) - var/obj/item/weapon/circuitboard/security/CB = new /obj/item/weapon/circuitboard/security(CF) - CB.network = network - for (var/obj/C in src) - C.loc = loc - CF.circuit = CB - CF.state = 3 - CF.icon_state = "3" - CF.anchored = 1 - del(src) - else - user << "\blue You disconnect the monitor." - var/obj/structure/computerframe/CF = new /obj/structure/computerframe( loc ) - var/obj/item/weapon/circuitboard/security/CB = new /obj/item/weapon/circuitboard/security(CF) - CB.network = network - for (var/obj/C in src) - C.loc = loc - CF.circuit = CB - CF.state = 4 - CF.icon_state = "4" - CF.anchored = 1 - del(src) - else - attack_hand(user) - return + + if (!C.can_use() || user.stat || (get_dist(user, src) > 1 || user.machine != src || user.blinded || !( user.canmove ) && !istype(user, /mob/living/silicon))) + return 0 + src.current = C + use_power(50) + return 1 //Camera control: moving. proc/jump_on_click(var/mob/user,var/A) @@ -171,6 +140,7 @@ icon_state = "telescreen" network = list("thunder") density = 0 + circuit = null /obj/machinery/computer/security/telescreen/update_icon() icon_state = initial(icon_state) @@ -183,11 +153,13 @@ desc = "Damn, why do they never have anything interesting on these things?" icon = 'icons/obj/status_display.dmi' icon_state = "entertainment" + circuit = null /obj/machinery/computer/security/wooden_tv name = "Security Cameras" desc = "An old TV hooked into the stations camera network." icon_state = "security_det" + circuit = null /obj/machinery/computer/security/mining @@ -195,15 +167,18 @@ desc = "Used to access the various cameras on the outpost." icon_state = "miningcameras" network = list("MINE") + circuit = /obj/item/weapon/circuitboard/security/mining /obj/machinery/computer/security/engineering name = "Engineering Cameras" desc = "Used to monitor fires and breaches." icon_state = "engineeringcameras" network = list("Engineering","Power Alarms","Atmosphere Alarms","Fire Alarms") + circuit = /obj/item/weapon/circuitboard/security/engineering /obj/machinery/computer/security/nuclear name = "Mission Monitor" desc = "Used to access the built-in cameras in helmets." icon_state = "syndicam" network = list("NUKE") + circuit = null diff --git a/code/game/machinery/computer/camera_monitor.dm b/code/game/machinery/computer/camera_monitor.dm index c8f3b7b3..11e8ddd4 100644 --- a/code/game/machinery/computer/camera_monitor.dm +++ b/code/game/machinery/computer/camera_monitor.dm @@ -40,7 +40,7 @@ D["Cancel"] = "Cancel" for (var/obj/machinery/camera/C in L) if ( C.network in src.networks ) - D[text("[]: [][]", C.network, C.c_tag, (C.status ? null : " (Deactivated)"))] = C + D[text("[]: [][]", C.network, C.c_tag, (C.can_use() ? null : " (Deactivated)"))] = C var/t = input(user, "Which camera should you change to?") as null|anything in D diff --git a/code/game/machinery/computer/card.dm b/code/game/machinery/computer/card.dm index 8c86629d..b40f511a 100644 --- a/code/game/machinery/computer/card.dm +++ b/code/game/machinery/computer/card.dm @@ -2,256 +2,147 @@ /obj/machinery/computer/card name = "Identification Computer" - desc = "You can use this to change ID's." + desc = "Terminal for programming NanoTrasen employee ID cards to access parts of the station." icon_state = "id" req_access = list(access_change_ids) circuit = "/obj/item/weapon/circuitboard/card" var/obj/item/weapon/card/id/scan = null var/obj/item/weapon/card/id/modify = null - var/authenticated = 0.0 var/mode = 0.0 var/printing = null + proc/is_centcom() + return istype(src, /obj/machinery/computer/card/centcom) -/obj/machinery/computer/card/attackby(O as obj, user as mob)//TODO:SANITY - if(istype(O, /obj/item/weapon/card/id)) - var/obj/item/weapon/card/id/idcard = O - if(access_change_ids in idcard.access) - if(!scan) - usr.drop_item() - idcard.loc = src - scan = idcard - else if(!modify) - usr.drop_item() - idcard.loc = src - modify = idcard - else - if(!modify) - usr.drop_item() - idcard.loc = src - modify = idcard + proc/is_authenticated() + return scan ? check_access(scan) : 0 + + proc/get_target_rank() + return modify && modify.assignment ? modify.assignment : "Unassigned" + + proc/format_jobs(list/jobs) + var/list/formatted = list() + for(var/job in jobs) + formatted.Add(list(list( + "display_name" = replacetext(job, " ", " "), + "target_rank" = get_target_rank(), + "job" = job))) + + return formatted + +/obj/machinery/computer/card/verb/eject_id() + set category = "Object" + set name = "Eject ID Card" + set src in oview(1) + + if(!usr || usr.stat || usr.lying) return + + if(scan) + usr << "You remove \the [scan] from \the [src]." + scan.loc = get_turf(src) + if(!usr.get_active_hand()) + usr.put_in_hands(scan) + scan = null + else if(modify) + usr << "You remove \the [modify] from \the [src]." + modify.loc = get_turf(src) + if(!usr.get_active_hand()) + usr.put_in_hands(modify) + modify = null else - ..() + usr << "There is nothing to remove from the console." + return +/obj/machinery/computer/card/attackby(obj/item/weapon/card/id/id_card, mob/user) + if(!istype(id_card)) + return ..() + + if(!scan && access_change_ids in id_card.access) + user.drop_item() + id_card.loc = src + scan = id_card + else if(!modify) + user.drop_item() + id_card.loc = src + modify = id_card + + nanomanager.update_uis(src) + attack_hand(user) /obj/machinery/computer/card/attack_ai(var/mob/user as mob) return attack_hand(user) - /obj/machinery/computer/card/attack_paw(var/mob/user as mob) return attack_hand(user) +/obj/machinery/computer/card/attack_hand(mob/user as mob) + if(..()) return + if(stat & (NOPOWER|BROKEN)) return + ui_interact(user) -/obj/machinery/computer/card/attack_hand(var/mob/user as mob) - if(..()) - return - +/obj/machinery/computer/card/ui_interact(mob/user, ui_key="main", var/datum/nanoui/ui = null, var/force_open = 1) user.set_machine(src) - var/dat - if (!( ticker )) - return - if (mode) // accessing crew manifest - dat += "

Crew Manifest

" - dat += "Entries cannot be modified from this terminal.

" - if(data_core) - dat += data_core.get_manifest(0) // make it monochrome - dat += "
" - dat += "Print
" - dat += "
" - dat += "Access ID modification console.
" + var/data[0] + data["src"] = "\ref[src]" + data["station_name"] = station_name() + data["mode"] = mode + data["printing"] = printing + data["manifest"] = data_core ? data_core.get_manifest(0) : null + data["target_name"] = modify ? modify.name : "-----" + data["target_owner"] = modify && modify.registered_name ? modify.registered_name : "-----" + data["target_rank"] = get_target_rank() + data["scan_name"] = scan ? scan.name : "-----" + data["authenticated"] = is_authenticated() + data["has_modify"] = !!modify + data["account_number"] = modify ? modify.associated_account_number : null + data["centcom_access"] = is_centcom() + data["all_centcom_access"] = null + data["regions"] = null - /*var/crew = "" - var/list/L = list() - for (var/datum/data/record/t in data_core.general) - var/R = t.fields["name"] + " - " + t.fields["rank"] - L += R - for(var/R in sortList(L)) - crew += "[R]
"*/ - //dat = "Crew Manifest:
Please use security record computer to modify entries.

[crew]Print

Access ID modification console.
" - else - var/header = "
Identification Card Modifier
" + data["engineering_jobs"] = format_jobs(engineering_positions) + data["medical_jobs"] = format_jobs(medical_positions) + data["science_jobs"] = format_jobs(science_positions) + data["security_jobs"] = format_jobs(security_positions) + data["civilian_jobs"] = format_jobs(civilian_positions) + data["centcom_jobs"] = format_jobs(get_all_centcom_jobs()) - var/target_name - var/target_owner - var/target_rank - if(modify) - target_name = modify.name - else - target_name = "--------" - if(modify && modify.registered_name) - target_owner = modify.registered_name - else - target_owner = "--------" - if(modify && modify.assignment) - target_rank = modify.assignment - else - target_rank = "Unassigned" + if (modify && is_centcom()) + var/list/all_centcom_access = list() + for(var/access in get_all_centcom_access()) + all_centcom_access.Add(list(list( + "desc" = replacetext(get_centcom_access_desc(access), " ", " "), + "ref" = access, + "allowed" = (access in modify.access) ? 1 : 0))) - var/scan_name - if(scan) - scan_name = scan.name - else - scan_name = "--------" + data["all_centcom_access"] = all_centcom_access + else if (modify) + var/list/regions = list() + for(var/i = 1; i <= 7; i++) + var/list/accesses = list() + for(var/access in get_region_accesses(i)) + if (get_access_desc(access)) + accesses.Add(list(list( + "desc" = replacetext(get_access_desc(access), " ", " "), + "ref" = access, + "allowed" = (access in modify.access) ? 1 : 0))) - if(!authenticated) - header += "
Please insert the cards into the slots
" - header += "Target: [target_name]
" - header += "Confirm Identity: [scan_name]
" - else - header += "

" - header += "Remove [target_name] || " - header += "Remove [scan_name]
" - header += "Access Crew Manifest || " - header += "Log Out
" + regions.Add(list(list( + "name" = get_region_accesses_name(i), + "accesses" = accesses))) - header += "
" - - var/jobs_all = "" - var/counter = 0 - jobs_all += "" - - jobs_all += ""//Captain in special because he is head of heads ~Intercross21 - jobs_all += "" - jobs_all += "" - - counter = 0 - jobs_all += ""//Red - for(var/job in security_positions) - counter++ - if(counter >= 6) - jobs_all += "" - counter = 0 - jobs_all += "" - - counter = 0 - jobs_all += ""//Orange - for(var/job in engineering_positions) - counter++ - if(counter >= 6) - jobs_all += "" - counter = 0 - jobs_all += "" - - counter = 0 - jobs_all += ""//Green - for(var/job in medical_positions) - counter++ - if(counter >= 6) - jobs_all += "" - counter = 0 - jobs_all += "" - - counter = 0 - jobs_all += ""//Purple - for(var/job in science_positions) - counter++ - if(counter >= 6) - jobs_all += "" - counter = 0 - jobs_all += "" - - counter = 0 - jobs_all += ""//Grey - for(var/job in civilian_positions) - counter++ - if(counter >= 6) - jobs_all += "" - counter = 0 - jobs_all += "" - - if(istype(src,/obj/machinery/computer/card/centcom)) - counter = 0 - jobs_all += ""//Brown - for(var/job in get_all_centcom_jobs()) - if(counter >= 6) - jobs_all += "" - counter = 0 - jobs_all += "" - - var/body - if (authenticated && modify) - var/carddesc = {""} - carddesc += "" - carddesc += "" - carddesc += "" - carddesc += "registered_name:" - carddesc += "" - carddesc += "" - - carddesc += "" - carddesc += "" - carddesc += "" - carddesc += "Stored account number:" - carddesc += "" - carddesc += "" - - carddesc += "Assignment: " - var/jobs = "[target_rank]" //CHECK THIS - var/accesses = "" - if(istype(src,/obj/machinery/computer/card/centcom)) - accesses += "
Central Command:
" - for(var/A in get_all_centcom_access()) - if(A in modify.access) - accesses += "[replacetext(get_centcom_access_desc(A), " ", " ")] " - else - accesses += "[replacetext(get_centcom_access_desc(A), " ", " ")] " - else - accesses += "
Access
" - accesses += "
Command
SpecialCaptainCustom
Security
[replacetext(job, " ", " ")]
Engineering
[replacetext(job, " ", " ")]
Medical
[replacetext(job, " ", " ")]
Science
[replacetext(job, " ", " ")]
Civilian
[replacetext(job, " ", " ")]
CentComm
[replacetext(job, " ", " ")]
" - accesses += "" - for(var/i = 1; i <= 7; i++) - accesses += "" - accesses += "" - for(var/i = 1; i <= 7; i++) - accesses += "" - accesses += "
[get_region_accesses_name(i)]:
" - for(var/A in get_region_accesses(i)) - if(A in modify.access) - accesses += "[replacetext(get_access_desc(A), " ", " ")] " - else - accesses += "[replacetext(get_access_desc(A), " ", " ")] " - accesses += "
" - accesses += "
" - body = "[carddesc]
[jobs]

[accesses]" //CHECK THIS - else - body = "{Log in}

" - body += "Access Crew Manifest" - dat = "[header][body]

" - user << browse(dat, "window=id_com;size=900x520") - onclose(user, "id_com") - return + data["regions"] = regions + ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "identification_computer.tmpl", src.name, 600, 700) + ui.set_initial_data(data) + ui.open() /obj/machinery/computer/card/Topic(href, href_list) if(..()) - return - usr.set_machine(src) + return 1 + switch(href_list["choice"]) if ("modify") if (modify) @@ -271,7 +162,6 @@ usr.drop_item() I.loc = src modify = I - authenticated = 0 if ("scan") if (scan) @@ -289,26 +179,19 @@ usr.drop_item() I.loc = src scan = I - authenticated = 0 - if ("auth") - if ((!( authenticated ) && (scan || (istype(usr, /mob/living/silicon))) && (modify || mode))) - if (check_access(scan)) - authenticated = 1 - else if ((!( authenticated ) && (istype(usr, /mob/living/silicon))) && (!modify)) - usr << "You can't modify an ID without an ID inserted to modify. Once one is in the modify slot on the computer, you can log in." - if ("logout") - authenticated = 0 + if("access") if(href_list["allowed"]) - if(authenticated) + if(is_authenticated()) var/access_type = text2num(href_list["access_target"]) var/access_allowed = text2num(href_list["allowed"]) - if(access_type in (istype(src,/obj/machinery/computer/card/centcom)?get_all_centcom_access() : get_all_accesses())) + if(access_type in (is_centcom() ? get_all_centcom_access() : get_all_accesses())) modify.access -= access_type - if(access_allowed == 1) + if(!access_allowed) modify.access += access_type + if ("assign") - if (authenticated) + if (is_authenticated() && modify) var/t1 = href_list["assign_target"] if(t1 == "Custom") var/temp_t = copytext(sanitize(input("Enter a custom job assignment.","Assignment")),1,45) @@ -316,69 +199,92 @@ if(temp_t && modify) modify.assignment = temp_t else - var/datum/job/jobdatum - for(var/jobtype in typesof(/datum/job)) - var/datum/job/J = new jobtype - if(ckey(J.title) == ckey(t1)) - jobdatum = J - break - if(!jobdatum) - usr << "\red No log exists for this job." - return + var/list/access = list() + if(is_centcom()) + access = get_centcom_access(t1) + else + var/datum/job/jobdatum + for(var/jobtype in typesof(/datum/job)) + var/datum/job/J = new jobtype + if(ckey(J.title) == ckey(t1)) + jobdatum = J + break + if(!jobdatum) + usr << "\red No log exists for this job: [t1]" + return + + access = jobdatum.get_access() + + modify.access = access + modify.assignment = t1 + modify.rank = t1 + + callHook("reassign_employee", list(modify)) - modify.access = ( istype(src,/obj/machinery/computer/card/centcom) ? get_centcom_access(t1) : jobdatum.get_access() ) - if (modify) - modify.assignment = t1 - modify.rank = t1 if ("reg") - if (authenticated) + if (is_authenticated()) var/t2 = modify - //var/t1 = input(usr, "What name?", "ID computer", null) as text - if ((authenticated && modify == t2 && (in_range(src, usr) || (istype(usr, /mob/living/silicon))) && istype(loc, /turf))) + if ((modify == t2 && (in_range(src, usr) || (istype(usr, /mob/living/silicon))) && istype(loc, /turf))) var/temp_name = reject_bad_name(href_list["reg"]) if(temp_name) modify.registered_name = temp_name else src.visible_message("[src] buzzes rudely.") + nanomanager.update_uis(src) + if ("account") - if (authenticated) + if (is_authenticated()) var/t2 = modify - //var/t1 = input(usr, "What name?", "ID computer", null) as text - if ((authenticated && modify == t2 && (in_range(src, usr) || (istype(usr, /mob/living/silicon))) && istype(loc, /turf))) + if ((modify == t2 && (in_range(src, usr) || (istype(usr, /mob/living/silicon))) && istype(loc, /turf))) var/account_num = text2num(href_list["account"]) modify.associated_account_number = account_num + nanomanager.update_uis(src) + if ("mode") mode = text2num(href_list["mode_target"]) + if ("print") - if (!( printing )) + if (!printing) printing = 1 - sleep(50) - var/obj/item/weapon/paper/P = new /obj/item/weapon/paper( loc ) - /*var/t1 = "Crew Manifest:
" - var/list/L = list() - for (var/datum/data/record/t in data_core.general) - var/R = t.fields["name"] + " - " + t.fields["rank"] - L += R - for(var/R in sortList(L)) - t1 += "[R]
"*/ + spawn(50) + printing = null + nanomanager.update_uis(src) - var/t1 = "

Crew Manifest

" - t1 += "
" - if(data_core) - t1 += data_core.get_manifest(0) // make it monochrome + var/obj/item/weapon/paper/P = new(loc) + if (mode) + P.name = text("crew manifest ([])", worldtime2text()) + P.info = {"

Crew Manifest

+
+ [data_core ? data_core.get_manifest(0) : ""] + "} + else if (modify) + P.name = "access report" + P.info = {"

Access Report

+ Prepared By: [scan.registered_name ? scan.registered_name : "Unknown"]
+ For: [modify.registered_name ? modify.registered_name : "Unregistered"]
+
+ Assignment: [modify.assignment]
+ Account Number: #[modify.associated_account_number]
+ Blood Type: [modify.blood_type]

+ Access:
+ "} + + for(var/A in modify.access) + P.info += " [get_access_desc(A)]" + + if ("terminate") + if (is_authenticated()) + modify.assignment = "Terminated" + modify.access = list() + + callHook("terminate_employee", list(modify)) - P.info = t1 - P.name = text("Crew Manifest ([])", worldtime2text()) - printing = null if (modify) modify.name = text("[modify.registered_name]'s ID Card ([modify.assignment])") - updateUsrDialog() - return - + return 1 /obj/machinery/computer/card/centcom name = "CentCom Identification Computer" circuit = "/obj/item/weapon/circuitboard/card/centcom" req_access = list(access_cent_captain) - diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm index 9ce8ccb4..70a0bb50 100644 --- a/code/game/machinery/computer/cloning.dm +++ b/code/game/machinery/computer/cloning.dm @@ -349,6 +349,9 @@ if (NOCLONE in subject.mutations) scantemp = "Error: Mental interface failure." return + if (subject.species && subject.species.flags & NO_SCAN) + scantemp = "Error: Mental interface failure." + return if (!isnull(find_record(subject.ckey))) scantemp = "Subject already in database." return @@ -362,6 +365,7 @@ R.name=R.dna.real_name R.types=DNA2_BUF_UI|DNA2_BUF_UE|DNA2_BUF_SE R.languages=subject.languages + R.flavor=subject.flavor_texts.Copy() //Add an implant if needed var/obj/item/weapon/implant/health/imp = locate(/obj/item/weapon/implant/health, subject) diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm index 4ddb45ad..b314ff4b 100644 --- a/code/game/machinery/computer/communications.dm +++ b/code/game/machinery/computer/communications.dm @@ -119,7 +119,7 @@ if("callshuttle2") if(src.authenticated) call_shuttle_proc(usr) - if(emergency_shuttle.online) + if(emergency_shuttle.online()) post_status("shuttle") src.state = STATE_DEFAULT if("cancelshuttle") @@ -169,10 +169,10 @@ post_status(href_list["statdisp"]) if("setmsg1") - stat_msg1 = input("Line 1", "Enter Message Text", stat_msg1) as text|null + stat_msg1 = reject_bad_text(trim(copytext(sanitize(input("Line 1", "Enter Message Text", stat_msg1) as text|null), 1, 40)), 40) src.updateDialog() if("setmsg2") - stat_msg2 = input("Line 2", "Enter Message Text", stat_msg2) as text|null + stat_msg2 = reject_bad_text(trim(copytext(sanitize(input("Line 2", "Enter Message Text", stat_msg2) as text|null), 1, 40)), 40) src.updateDialog() // OMG CENTCOMM LETTERHEAD @@ -284,8 +284,8 @@ user.set_machine(src) var/dat = "Communications Console" - if (emergency_shuttle.online && emergency_shuttle.location==0) - var/timeleft = emergency_shuttle.timeleft() + if (emergency_shuttle.has_eta()) + var/timeleft = emergency_shuttle.estimate_arrival_time() dat += "Emergency shuttle\n
\nETA: [timeleft / 60 % 60]:[add_zero(num2text(timeleft % 60), 2)]
" if (istype(user, /mob/living/silicon)) @@ -309,8 +309,8 @@ dat += "
\[ Restore Backup Routing Data \]" dat += "
\[ Change alert level \]" - if(emergency_shuttle.location==0) - if (emergency_shuttle.online) + if(emergency_shuttle.location()) + if (emergency_shuttle.online()) dat += "
\[ Cancel Shuttle Call \]" else dat += "
\[ Call Emergency Shuttle \]" @@ -377,7 +377,7 @@ var/dat = "" switch(src.aistate) if(STATE_DEFAULT) - if(emergency_shuttle.location==0 && !emergency_shuttle.online) + if(emergency_shuttle.location() && !emergency_shuttle.online()) dat += "
\[ Call Emergency Shuttle \]" dat += "
\[ Message List \]" dat += "
\[ Set Status Display \]" @@ -424,22 +424,26 @@ PS.allowedtocall = !(PS.allowedtocall) /proc/call_shuttle_proc(var/mob/user) - if ((!( ticker ) || emergency_shuttle.location)) + if ((!( ticker ) || !emergency_shuttle.location())) return if(sent_strike_team == 1) user << "Centcom will not allow the shuttle to be called. Consider all contracts terminated." return - if(world.time < 6000) // Ten minute grace period to let the game get going without lolmetagaming. -- TLE - user << "The emergency shuttle is refueling. Please wait another [round((6000-world.time)/600)] minutes before trying again." + if(emergency_shuttle.deny_shuttle) + user << "The emergency shuttle may not be sent at this time. Please try again later." return - if(emergency_shuttle.direction == -1) + if(world.time < 6000) // Ten minute grace period to let the game get going without lolmetagaming. -- TLE + user << "The emergency shuttle is refueling. Please wait another [round((6000-world.time)/60)] minutes before trying again." + return + + if(emergency_shuttle.going_to_centcom()) user << "The emergency shuttle may not be called while returning to CentCom." return - if(emergency_shuttle.online) + if(emergency_shuttle.online()) user << "The emergency shuttle is already on its way." return @@ -447,23 +451,22 @@ user << "Under directive 7-10, [station_name()] is quarantined until further notice." return - emergency_shuttle.incall() + emergency_shuttle.call_evac() log_game("[key_name(user)] has called the shuttle.") message_admins("[key_name_admin(user)] has called the shuttle.", 1) - captain_announce("The emergency shuttle has been called. It will arrive in [round(emergency_shuttle.timeleft()/60)] minutes.") - world << sound('sound/AI/shuttlecalled.ogg') + return /proc/init_shift_change(var/mob/user, var/force = 0) - if ((!( ticker ) || emergency_shuttle.location)) + if ((!( ticker ) || !emergency_shuttle.location())) return - if(emergency_shuttle.direction == -1) + if(emergency_shuttle.going_to_centcom()) user << "The shuttle may not be called while returning to CentCom." return - if(emergency_shuttle.online) + if(emergency_shuttle.online()) user << "The shuttle is already on its way." return @@ -478,32 +481,30 @@ return if(world.time < 54000) // 30 minute grace period to let the game get going - user << "The shuttle is refueling. Please wait another [round((54000-world.time)/600)] minutes before trying again."//may need to change "/600" + user << "The shuttle is refueling. Please wait another [round((54000-world.time)/60)] minutes before trying again." return if(ticker.mode.name == "revolution" || ticker.mode.name == "AI malfunction" || ticker.mode.name == "sandbox") //New version pretends to call the shuttle but cause the shuttle to return after a random duration. - emergency_shuttle.fake_recall = rand(300,500) + emergency_shuttle.auto_recall = 1 if(ticker.mode.name == "blob" || ticker.mode.name == "epidemic") user << "Under directive 7-10, [station_name()] is quarantined until further notice." return - emergency_shuttle.shuttlealert(1) - emergency_shuttle.incall() + emergency_shuttle.call_transfer() log_game("[key_name(user)] has called the shuttle.") message_admins("[key_name_admin(user)] has called the shuttle.", 1) - captain_announce("A crew transfer has been initiated. The shuttle has been called. It will arrive in [round(emergency_shuttle.timeleft()/60)] minutes.") return /proc/cancel_call_proc(var/mob/user) - if ((!( ticker ) || emergency_shuttle.location || emergency_shuttle.direction == 0 || emergency_shuttle.timeleft() < 300)) + if (!( ticker ) || !emergency_shuttle.can_recall()) return if((ticker.mode.name == "blob")||(ticker.mode.name == "meteor")) return - if(emergency_shuttle.direction != -1 && emergency_shuttle.online) //check that shuttle isn't already heading to centcomm + if(!emergency_shuttle.going_to_centcom()) //check that shuttle isn't already heading to centcomm emergency_shuttle.recall() log_game("[key_name(user)] has recalled the shuttle.") message_admins("[key_name_admin(user)] has recalled the shuttle.", 1) @@ -549,12 +550,9 @@ if(ticker.mode.name == "revolution" || ticker.mode.name == "AI malfunction" || sent_strike_team) return ..() - emergency_shuttle.incall(2) + emergency_shuttle.call_evac() log_game("All the AIs, comm consoles and boards are destroyed. Shuttle called.") message_admins("All the AIs, comm consoles and boards are destroyed. Shuttle called.", 1) - message_mods("All the AIs, comm consoles and boards are destroyed. Shuttle called.") - captain_announce("The emergency shuttle has been called. It will arrive in [round(emergency_shuttle.timeleft()/60)] minutes.") - world << sound('sound/AI/shuttlecalled.ogg') ..() @@ -575,10 +573,8 @@ if(ticker.mode.name == "revolution" || ticker.mode.name == "AI malfunction" || sent_strike_team) return ..() - emergency_shuttle.incall(2) + emergency_shuttle.call_evac() log_game("All the AIs, comm consoles and boards are destroyed. Shuttle called.") message_admins("All the AIs, comm consoles and boards are destroyed. Shuttle called.", 1) - captain_announce("The emergency shuttle has been called. It will arrive in [round(emergency_shuttle.timeleft()/60)] minutes.") - world << sound('sound/AI/shuttlecalled.ogg') ..() diff --git a/code/game/machinery/computer/computer.dm b/code/game/machinery/computer/computer.dm index b289bcaa..ff88f89c 100644 --- a/code/game/machinery/computer/computer.dm +++ b/code/game/machinery/computer/computer.dm @@ -6,7 +6,7 @@ use_power = 1 idle_power_usage = 300 active_power_usage = 300 - var/obj/item/weapon/circuitboard/circuit = null //if circuit==null, computer can't disassembly + var/circuit = null //The path to the circuit board type. If circuit==null, the computer can't be disassembled. var/processing = 0 /obj/machinery/computer/New() @@ -120,6 +120,7 @@ user << "\blue You disconnect the monitor." A.state = 4 A.icon_state = "4" + M.deconstruct(src) del(src) else src.attack_hand(user) diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm index 0074f1c0..33d53089 100644 --- a/code/game/machinery/computer/crew.dm +++ b/code/game/machinery/computer/crew.dm @@ -16,14 +16,14 @@ /obj/machinery/computer/crew/attack_ai(mob/user) attack_hand(user) - interact(user) + ui_interact(user) /obj/machinery/computer/crew/attack_hand(mob/user) add_fingerprint(user) if(stat & (BROKEN|NOPOWER)) return - interact(user) + ui_interact(user) /obj/machinery/computer/crew/update_icon() @@ -43,67 +43,86 @@ if(..()) return if (src.z > 6) usr << "\red Unable to establish a connection: \black You're too far away from the station!" - return + return 0 if( href_list["close"] ) - usr << browse(null, "window=crewcomp") + var/mob/user = usr + var/datum/nanoui/ui = nanomanager.get_open_ui(user, src, "main") usr.unset_machine() - return + ui.close() + return 0 if(href_list["update"]) src.updateDialog() - return - + return 1 /obj/machinery/computer/crew/interact(mob/user) + ui_interact(user) + +/obj/machinery/computer/crew/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) if(stat & (BROKEN|NOPOWER)) return - if(!istype(user, /mob/living/silicon) && get_dist(src, user) > 1) - user.unset_machine() - user << browse(null, "window=powcomp") - return user.set_machine(src) src.scan() - var/t = "Crew Monitoring
" - t += "
Refresh " - t += "Close
" - t += "" - var/list/logs = list() + + var/data[0] + var/list/crewmembers = list() + for(var/obj/item/clothing/under/C in src.tracked) - var/log = "" + + var/turf/pos = get_turf(C) + if((C) && (C.has_sensor) && (pos) && (pos.z == src.z) && C.sensor_mode) if(istype(C.loc, /mob/living/carbon/human)) var/mob/living/carbon/human/H = C.loc - var/dam1 = round(H.getOxyLoss(),1) - var/dam2 = round(H.getToxLoss(),1) - var/dam3 = round(H.getFireLoss(),1) - var/dam4 = round(H.getBruteLoss(),1) + var/list/crewmemberData = list() - var/life_status = "[H.stat > 1 ? "Deceased" : "Living"]" - var/damage_report = "([dam1]/[dam2]/[dam3]/[dam4])" + crewmemberData["sensor_type"] = C.sensor_mode + crewmemberData["dead"] = H.stat > 1 + crewmemberData["oxy"] = round(H.getOxyLoss(), 1) + crewmemberData["tox"] = round(H.getToxLoss(), 1) + crewmemberData["fire"] = round(H.getFireLoss(), 1) + crewmemberData["brute"] = round(H.getBruteLoss(), 1) + + crewmemberData["name"] = "Unknown" + crewmemberData["rank"] = "Unknown" + if(H.wear_id && istype(H.wear_id, /obj/item/weapon/card/id) ) + var/obj/item/weapon/card/id/I = H.wear_id + crewmemberData["name"] = I.name + crewmemberData["rank"] = I.rank + else if(H.wear_id && istype(H.wear_id, /obj/item/device/pda) ) + var/obj/item/device/pda/P = H.wear_id + crewmemberData["name"] = (P.id ? P.id.name : "Unknown") + crewmemberData["rank"] = (P.id ? P.id.rank : "Unknown") + + var/area/A = get_area(H) + crewmemberData["area"] = sanitize(A.name) + crewmemberData["x"] = pos.x + crewmemberData["y"] = pos.y - if(H.wear_id) - log += "" - else - log += "" + // Works around list += list2 merging lists; it's not pretty but it works + crewmembers += "temporary item" + crewmembers[crewmembers.len] = crewmemberData - switch(C.sensor_mode) - if(1) - log += "" - if(2) - log += "" - if(3) - var/area/player_area = get_area(H) - log += "" - logs += log - logs = sortList(logs) - for(var/log in logs) - t += log - t += "
NameVitalsPosition
[H.wear_id.name]
Unknown[life_status]Not Available
[life_status] [damage_report]Not Available
[life_status] [damage_report][player_area.name] ([pos.x], [pos.y])
" - t += "
" - user << browse(t, "window=crewcomp;size=900x600") - onclose(user, "crewcomp") + crewmembers = sortByKey(crewmembers, "name") + + data["crewmembers"] = crewmembers + + ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) + if(!ui) + ui = new(user, src, ui_key, "crew_monitor.tmpl", "Crew Monitoring Computer", 900, 800) + + // adding a template with the key "mapContent" enables the map ui functionality + ui.add_template("mapContent", "crew_monitor_map_content.tmpl") + // adding a template with the key "mapHeader" replaces the map header content + ui.add_template("mapHeader", "crew_monitor_map_header.tmpl") + + ui.set_initial_data(data) + ui.open() + + // should make the UI auto-update; doesn't seem to? + ui.set_auto_update(1) /obj/machinery/computer/crew/proc/scan() @@ -112,4 +131,4 @@ var/obj/item/clothing/under/C = H.w_uniform if (C.has_sensor) tracked |= C - return 1 \ No newline at end of file + return 1 diff --git a/code/game/machinery/computer/medical.dm b/code/game/machinery/computer/medical.dm index cf9f292f..77212d64 100644 --- a/code/game/machinery/computer/medical.dm +++ b/code/game/machinery/computer/medical.dm @@ -16,6 +16,23 @@ var/temp = null var/printing = null +/obj/machinery/computer/med_data/verb/eject_id() + set category = "Object" + set name = "Eject ID Card" + set src in oview(1) + + if(!usr || usr.stat || usr.lying) return + + if(scan) + usr << "You remove \the [scan] from \the [src]." + scan.loc = get_turf(src) + if(!usr.get_active_hand()) + usr.put_in_hands(scan) + scan = null + else + usr << "There is nothing to remove from the console." + return + /obj/machinery/computer/med_data/attackby(obj/item/O as obj, user as mob) if(istype(O, /obj/item/weapon/card/id) && !scan) usr.drop_item() @@ -236,7 +253,7 @@ switch(href_list["field"]) if("fingerprint") if (istype(src.active1, /datum/data/record)) - var/t1 = copytext(sanitize(input("Please input fingerprint hash:", "Med. records", src.active1.fields["fingerprint"], null) as text),1,MAX_MESSAGE_LEN) + var/t1 = copytext(trim(sanitize(input("Please input fingerprint hash:", "Med. records", src.active1.fields["fingerprint"], null) as text)),1,MAX_MESSAGE_LEN) if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || src.active1 != a1)) return src.active1.fields["fingerprint"] = t1 @@ -254,55 +271,55 @@ src.active1.fields["age"] = t1 if("mi_dis") if (istype(src.active2, /datum/data/record)) - var/t1 = copytext(sanitize(input("Please input minor disabilities list:", "Med. records", src.active2.fields["mi_dis"], null) as text),1,MAX_MESSAGE_LEN) + var/t1 = copytext(trim(sanitize(input("Please input minor disabilities list:", "Med. records", src.active2.fields["mi_dis"], null) as text)),1,MAX_MESSAGE_LEN) if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || src.active2 != a2)) return src.active2.fields["mi_dis"] = t1 if("mi_dis_d") if (istype(src.active2, /datum/data/record)) - var/t1 = copytext(sanitize(input("Please summarize minor dis.:", "Med. records", src.active2.fields["mi_dis_d"], null) as message),1,MAX_MESSAGE_LEN) + var/t1 = copytext(trim(sanitize(input("Please summarize minor dis.:", "Med. records", src.active2.fields["mi_dis_d"], null) as message)),1,MAX_MESSAGE_LEN) if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || src.active2 != a2)) return src.active2.fields["mi_dis_d"] = t1 if("ma_dis") if (istype(src.active2, /datum/data/record)) - var/t1 = copytext(sanitize(input("Please input major diabilities list:", "Med. records", src.active2.fields["ma_dis"], null) as text),1,MAX_MESSAGE_LEN) + var/t1 = copytext(trim(sanitize(input("Please input major diabilities list:", "Med. records", src.active2.fields["ma_dis"], null) as text)),1,MAX_MESSAGE_LEN) if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || src.active2 != a2)) return src.active2.fields["ma_dis"] = t1 if("ma_dis_d") if (istype(src.active2, /datum/data/record)) - var/t1 = copytext(sanitize(input("Please summarize major dis.:", "Med. records", src.active2.fields["ma_dis_d"], null) as message),1,MAX_MESSAGE_LEN) + var/t1 = copytext(trim(sanitize(input("Please summarize major dis.:", "Med. records", src.active2.fields["ma_dis_d"], null) as message)),1,MAX_MESSAGE_LEN) if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || src.active2 != a2)) return src.active2.fields["ma_dis_d"] = t1 if("alg") if (istype(src.active2, /datum/data/record)) - var/t1 = copytext(sanitize(input("Please state allergies:", "Med. records", src.active2.fields["alg"], null) as text),1,MAX_MESSAGE_LEN) + var/t1 = copytext(trim(sanitize(input("Please state allergies:", "Med. records", src.active2.fields["alg"], null) as text)),1,MAX_MESSAGE_LEN) if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || src.active2 != a2)) return src.active2.fields["alg"] = t1 if("alg_d") if (istype(src.active2, /datum/data/record)) - var/t1 = copytext(sanitize(input("Please summarize allergies:", "Med. records", src.active2.fields["alg_d"], null) as message),1,MAX_MESSAGE_LEN) + var/t1 = copytext(trim(sanitize(input("Please summarize allergies:", "Med. records", src.active2.fields["alg_d"], null) as message)),1,MAX_MESSAGE_LEN) if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || src.active2 != a2)) return src.active2.fields["alg_d"] = t1 if("cdi") if (istype(src.active2, /datum/data/record)) - var/t1 = copytext(sanitize(input("Please state diseases:", "Med. records", src.active2.fields["cdi"], null) as text),1,MAX_MESSAGE_LEN) + var/t1 = copytext(trim(sanitize(input("Please state diseases:", "Med. records", src.active2.fields["cdi"], null) as text)),1,MAX_MESSAGE_LEN) if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || src.active2 != a2)) return src.active2.fields["cdi"] = t1 if("cdi_d") if (istype(src.active2, /datum/data/record)) - var/t1 = copytext(sanitize(input("Please summarize diseases:", "Med. records", src.active2.fields["cdi_d"], null) as message),1,MAX_MESSAGE_LEN) + var/t1 = copytext(trim(sanitize(input("Please summarize diseases:", "Med. records", src.active2.fields["cdi_d"], null) as message)),1,MAX_MESSAGE_LEN) if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || src.active2 != a2)) return src.active2.fields["cdi_d"] = t1 if("notes") if (istype(src.active2, /datum/data/record)) - var/t1 = copytext(html_encode(input("Please summarize notes:", "Med. records", html_decode(src.active2.fields["notes"]), null) as message),1,MAX_MESSAGE_LEN) + var/t1 = copytext(html_encode(trim(input("Please summarize notes:", "Med. records", html_decode(src.active2.fields["notes"]), null) as message)),1,MAX_MESSAGE_LEN) if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || src.active2 != a2)) return src.active2.fields["notes"] = t1 @@ -316,22 +333,22 @@ if (istype(src.active2, /datum/data/record)) src.temp = text("Blood Type:
\n\tA- A+
\n\tB- B+
\n\tAB- AB+
\n\tO- O+
", src, src, src, src, src, src, src, src) if("b_dna") - if (istype(src.active1, /datum/data/record)) - var/t1 = copytext(sanitize(input("Please input DNA hash:", "Med. records", src.active1.fields["dna"], null) as text),1,MAX_MESSAGE_LEN) - if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || src.active1 != a1)) + if (istype(src.active2, /datum/data/record)) + var/t1 = copytext(trim(sanitize(input("Please input DNA hash:", "Med. records", src.active2.fields["b_dna"], null) as text)),1,MAX_MESSAGE_LEN) + if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || src.active2 != a2)) return - src.active1.fields["dna"] = t1 + src.active2.fields["b_dna"] = t1 if("vir_name") var/datum/data/record/v = locate(href_list["edit_vir"]) if (v) - var/t1 = copytext(sanitize(input("Please input pathogen name:", "VirusDB", v.fields["name"], null) as text),1,MAX_MESSAGE_LEN) + var/t1 = copytext(trim(sanitize(input("Please input pathogen name:", "VirusDB", v.fields["name"], null) as text)),1,MAX_MESSAGE_LEN) if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || src.active1 != a1)) return v.fields["name"] = t1 if("vir_desc") var/datum/data/record/v = locate(href_list["edit_vir"]) if (v) - var/t1 = copytext(sanitize(input("Please input information about pathogen:", "VirusDB", v.fields["description"], null) as message),1,MAX_MESSAGE_LEN) + var/t1 = copytext(trim(sanitize(input("Please input information about pathogen:", "VirusDB", v.fields["description"], null) as message)),1,MAX_MESSAGE_LEN) if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || src.active1 != a1)) return v.fields["description"] = t1 @@ -434,7 +451,7 @@ if (!( istype(src.active2, /datum/data/record) )) return var/a2 = src.active2 - var/t1 = copytext(sanitize(input("Add Comment:", "Med. records", null, null) as message),1,MAX_MESSAGE_LEN) + var/t1 = copytext(trim(sanitize(input("Add Comment:", "Med. records", null, null) as message)),1,MAX_MESSAGE_LEN) if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || src.active2 != a2)) return var/counter = 1 diff --git a/code/game/machinery/computer/message.dm b/code/game/machinery/computer/message.dm index 0cfc5fe3..508355e8 100644 --- a/code/game/machinery/computer/message.dm +++ b/code/game/machinery/computer/message.dm @@ -26,7 +26,7 @@ var/emag = 0 // When it is emagged. var/message = "System bootup complete. Please select an option." // The message that shows on the main menu. var/auth = 0 // Are they authenticated? - var/optioncount = 7 + var/optioncount = 8 // Custom Message Properties var/customsender = "System Administrator" var/obj/item/device/pda/customrecepient = null @@ -36,6 +36,7 @@ /obj/machinery/computer/message_monitor/attackby(obj/item/weapon/O as obj, mob/living/user as mob) if(stat & (NOPOWER|BROKEN)) + ..() return if(!istype(user)) return @@ -122,6 +123,7 @@ dat += "
[++i]. Clear Request Console Logs
" dat += "
[++i]. Set Custom Key
" dat += "
[++i]. Send Admin Message
" + dat += "
[++i]. Modify Spam Filter
" else for(var/n = ++i; n <= optioncount; n++) dat += "
[n]. ---------------
" @@ -240,6 +242,19 @@ [rc.rec_dpt][rc.message][rc.stamp][rc.id_auth][rc.priority]"} dat += "" + //Spam filter modification + if(5) + dat += "
Back - Refresh

" + var/index = 0 + for(var/token in src.linkedServer.spamfilter) + index++ + if(index > 3000) + break + dat += "
[index] \[[token]\]
" + dat += "
" + if (linkedServer.spamfilter.len < linkedServer.spamfilter_limit) + dat += "Add token
" + dat += "" message = defaultmsg @@ -485,6 +500,26 @@ //usr << href_list["select"] + if(href_list["spam"]) + if(src.linkedServer == null || (src.linkedServer.stat & (NOPOWER|BROKEN))) + message = noserver + else + if(auth) + src.screen = 5 + + if(href_list["addtoken"]) + if(src.linkedServer == null || (src.linkedServer.stat & (NOPOWER|BROKEN))) + message = noserver + else + src.linkedServer.spamfilter += input(usr,"Enter text you want to be filtered out","Token creation") as text|null + + if(href_list["deltoken"]) + if(src.linkedServer == null || (src.linkedServer.stat & (NOPOWER|BROKEN))) + message = noserver + else + var/tokennum = text2num(href_list["deltoken"]) + src.linkedServer.spamfilter.Cut(tokennum,tokennum+1) + if (href_list["back"]) src.screen = 0 diff --git a/code/game/machinery/computer/prisoner.dm b/code/game/machinery/computer/prisoner.dm index ca0512e2..cb3e0f8f 100644 --- a/code/game/machinery/computer/prisoner.dm +++ b/code/game/machinery/computer/prisoner.dm @@ -50,7 +50,7 @@ var/loc_display = "Unknown" var/mob/living/carbon/M = T.imp_in if(M.z == 1 && !istype(M.loc, /turf/space)) - var/turf/mob_loc = get_turf_loc(M) + var/turf/mob_loc = get_turf(M) loc_display = mob_loc.loc if(T.malfunction) loc_display = pick(teleportlocs) diff --git a/code/game/machinery/computer/robot.dm b/code/game/machinery/computer/robot.dm index ed7c05d4..1e4aa057 100644 --- a/code/game/machinery/computer/robot.dm +++ b/code/game/machinery/computer/robot.dm @@ -200,7 +200,10 @@ else if (href_list["magbot"]) if(src.allowed(usr)) var/mob/living/silicon/robot/R = locate(href_list["magbot"]) - if(R) + + // whatever weirdness this is supposed to be, but that is how the href gets added, so here it is again + if(istype(R) && istype(usr, /mob/living/silicon) && usr.mind.special_role && (usr.mind.original == usr) && !R.emagged) + var/choice = input("Are you certain you wish to hack [R.name]?") in list("Confirm", "Abort") if(choice == "Confirm") if(R && istype(R)) @@ -228,4 +231,4 @@ if(!R.scrambledcodes && !istype(R, /mob/living/silicon/robot/drone)) R.self_destruct() - return \ No newline at end of file + return diff --git a/code/game/machinery/computer/security.dm b/code/game/machinery/computer/security.dm index bfb5341b..3714a8e2 100644 --- a/code/game/machinery/computer/security.dm +++ b/code/game/machinery/computer/security.dm @@ -22,6 +22,22 @@ var/sortBy = "name" var/order = 1 // -1 = Descending - 1 = Ascending +/obj/machinery/computer/secure_data/verb/eject_id() + set category = "Object" + set name = "Eject ID Card" + set src in oview(1) + + if(!usr || usr.stat || usr.lying) return + + if(scan) + usr << "You remove \the [scan] from \the [src]." + scan.loc = get_turf(src) + if(!usr.get_active_hand()) + usr.put_in_hands(scan) + scan = null + else + usr << "There is nothing to remove from the console." + return /obj/machinery/computer/secure_data/attackby(obj/item/O as obj, user as mob) if(istype(O, /obj/item/weapon/card/id) && !scan) @@ -371,7 +387,7 @@ What a mess.*/ if (!( istype(active2, /datum/data/record) )) return var/a2 = active2 - var/t1 = copytext(sanitize(input("Add Comment:", "Secure. records", null, null) as message),1,MAX_MESSAGE_LEN) + var/t1 = copytext(trim(sanitize(input("Add Comment:", "Secure. records", null, null) as message)),1,MAX_MESSAGE_LEN) if ((!( t1 ) || !( authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || active2 != a2)) return var/counter = 1 @@ -434,19 +450,19 @@ What a mess.*/ switch(href_list["field"]) if("name") if (istype(active1, /datum/data/record)) - var/t1 = input("Please input name:", "Secure. records", active1.fields["name"], null) as text + var/t1 = reject_bad_name(input("Please input name:", "Secure. records", active1.fields["name"], null) as text) if ((!( t1 ) || !length(trim(t1)) || !( authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon)))) || active1 != a1) return active1.fields["name"] = t1 if("id") if (istype(active2, /datum/data/record)) - var/t1 = copytext(sanitize(input("Please input id:", "Secure. records", active1.fields["id"], null) as text),1,MAX_MESSAGE_LEN) + var/t1 = copytext(trim(sanitize(input("Please input id:", "Secure. records", active1.fields["id"], null) as text)),1,MAX_MESSAGE_LEN) if ((!( t1 ) || !( authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || active1 != a1)) return active1.fields["id"] = t1 if("fingerprint") if (istype(active1, /datum/data/record)) - var/t1 = copytext(sanitize(input("Please input fingerprint hash:", "Secure. records", active1.fields["fingerprint"], null) as text),1,MAX_MESSAGE_LEN) + var/t1 = copytext(trim(sanitize(input("Please input fingerprint hash:", "Secure. records", active1.fields["fingerprint"], null) as text)),1,MAX_MESSAGE_LEN) if ((!( t1 ) || !( authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || active1 != a1)) return active1.fields["fingerprint"] = t1 @@ -464,31 +480,31 @@ What a mess.*/ active1.fields["age"] = t1 if("mi_crim") if (istype(active2, /datum/data/record)) - var/t1 = copytext(sanitize(input("Please input minor disabilities list:", "Secure. records", active2.fields["mi_crim"], null) as text),1,MAX_MESSAGE_LEN) + var/t1 = copytext(trim(sanitize(input("Please input minor disabilities list:", "Secure. records", active2.fields["mi_crim"], null) as text)),1,MAX_MESSAGE_LEN) if ((!( t1 ) || !( authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || active2 != a2)) return active2.fields["mi_crim"] = t1 if("mi_crim_d") if (istype(active2, /datum/data/record)) - var/t1 = copytext(sanitize(input("Please summarize minor dis.:", "Secure. records", active2.fields["mi_crim_d"], null) as message),1,MAX_MESSAGE_LEN) + var/t1 = copytext(trim(sanitize(input("Please summarize minor dis.:", "Secure. records", active2.fields["mi_crim_d"], null) as message)),1,MAX_MESSAGE_LEN) if ((!( t1 ) || !( authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || active2 != a2)) return active2.fields["mi_crim_d"] = t1 if("ma_crim") if (istype(active2, /datum/data/record)) - var/t1 = copytext(sanitize(input("Please input major diabilities list:", "Secure. records", active2.fields["ma_crim"], null) as text),1,MAX_MESSAGE_LEN) + var/t1 = copytext(trim(sanitize(input("Please input major diabilities list:", "Secure. records", active2.fields["ma_crim"], null) as text)),1,MAX_MESSAGE_LEN) if ((!( t1 ) || !( authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || active2 != a2)) return active2.fields["ma_crim"] = t1 if("ma_crim_d") if (istype(active2, /datum/data/record)) - var/t1 = copytext(sanitize(input("Please summarize major dis.:", "Secure. records", active2.fields["ma_crim_d"], null) as message),1,MAX_MESSAGE_LEN) + var/t1 = copytext(trim(sanitize(input("Please summarize major dis.:", "Secure. records", active2.fields["ma_crim_d"], null) as message)),1,MAX_MESSAGE_LEN) if ((!( t1 ) || !( authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || active2 != a2)) return active2.fields["ma_crim_d"] = t1 if("notes") if (istype(active2, /datum/data/record)) - var/t1 = copytext(html_encode(input("Please summarize notes:", "Secure. records", html_decode(active2.fields["notes"]), null) as message),1,MAX_MESSAGE_LEN) + var/t1 = copytext(html_encode(trim(input("Please summarize notes:", "Secure. records", html_decode(active2.fields["notes"]), null) as message)),1,MAX_MESSAGE_LEN) if ((!( t1 ) || !( authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || active2 != a2)) return active2.fields["notes"] = t1 @@ -515,7 +531,7 @@ What a mess.*/ alert(usr, "You do not have the required rank to do this!") if("species") if (istype(active1, /datum/data/record)) - var/t1 = copytext(sanitize(input("Please enter race:", "General records", active1.fields["species"], null) as message),1,MAX_MESSAGE_LEN) + var/t1 = copytext(trim(sanitize(input("Please enter race:", "General records", active1.fields["species"], null) as message)),1,MAX_MESSAGE_LEN) if ((!( t1 ) || !( authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || active1 != a1)) return active1.fields["species"] = t1 diff --git a/code/game/machinery/computer/shuttle.dm b/code/game/machinery/computer/shuttle.dm index 41835d19..56c2ab0f 100644 --- a/code/game/machinery/computer/shuttle.dm +++ b/code/game/machinery/computer/shuttle.dm @@ -8,7 +8,7 @@ attackby(var/obj/item/weapon/card/W as obj, var/mob/user as mob) if(stat & (BROKEN|NOPOWER)) return - if ((!( istype(W, /obj/item/weapon/card) ) || !( ticker ) || emergency_shuttle.location != 1 || !( user ))) return + if ((!( istype(W, /obj/item/weapon/card) ) || !( ticker ) || emergency_shuttle.location() || !( user ))) return if (istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda)) if (istype(W, /obj/item/device/pda)) var/obj/item/device/pda/pda = W @@ -27,7 +27,7 @@ return 0 var/choice = alert(user, text("Would you like to (un)authorize a shortened launch time? [] authorization\s are still needed. Use abort to cancel all authorizations.", src.auth_need - src.authorized.len), "Shuttle Launch", "Authorize", "Repeal", "Abort") - if(emergency_shuttle.location != 1 && user.get_active_hand() != W) + if(emergency_shuttle.location() && user.get_active_hand() != W) return 0 switch(choice) if("Authorize") @@ -41,8 +41,7 @@ message_admins("[key_name_admin(user)] has launched the shuttle") log_game("[user.ckey] has launched the shuttle early") world << "\blue Alert: Shuttle launch time shortened to 10 seconds!" - emergency_shuttle.online = 1 - emergency_shuttle.settimeleft(10) + emergency_shuttle.set_launch_countdown(10) //src.authorized = null del(src.authorized) src.authorized = list( ) @@ -59,11 +58,11 @@ else if (istype(W, /obj/item/weapon/card/emag) && !emagged) var/choice = alert(user, "Would you like to launch the shuttle?","Shuttle control", "Launch", "Cancel") - if(!emagged && emergency_shuttle.location == 1 && user.get_active_hand() == W) + if(!emagged && !emergency_shuttle.location() && user.get_active_hand() == W) switch(choice) if("Launch") world << "\blue Alert: Shuttle launch time shortened to 10 seconds!" - emergency_shuttle.settimeleft( 10 ) + emergency_shuttle.set_launch_countdown(10) emagged = 1 if("Cancel") return diff --git a/code/game/machinery/computer/skills.dm b/code/game/machinery/computer/skills.dm index 27360e96..bc4d977d 100644 --- a/code/game/machinery/computer/skills.dm +++ b/code/game/machinery/computer/skills.dm @@ -325,19 +325,19 @@ What a mess.*/ switch(href_list["field"]) if("name") if (istype(active1, /datum/data/record)) - var/t1 = input("Please input name:", "Secure. records", active1.fields["name"], null) as text + var/t1 = reject_bad_name(input("Please input name:", "Secure. records", active1.fields["name"], null) as text) if ((!( t1 ) || !length(trim(t1)) || !( authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon)))) || active1 != a1) return active1.fields["name"] = t1 if("id") if (istype(active1, /datum/data/record)) - var/t1 = copytext(sanitize(input("Please input id:", "Secure. records", active1.fields["id"], null) as text),1,MAX_MESSAGE_LEN) + var/t1 = copytext(trim(sanitize(input("Please input id:", "Secure. records", active1.fields["id"], null) as text)),1,MAX_MESSAGE_LEN) if ((!( t1 ) || !( authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || active1 != a1)) return active1.fields["id"] = t1 if("fingerprint") if (istype(active1, /datum/data/record)) - var/t1 = copytext(sanitize(input("Please input fingerprint hash:", "Secure. records", active1.fields["fingerprint"], null) as text),1,MAX_MESSAGE_LEN) + var/t1 = copytext(trim(sanitize(input("Please input fingerprint hash:", "Secure. records", active1.fields["fingerprint"], null) as text)),1,MAX_MESSAGE_LEN) if ((!( t1 ) || !( authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || active1 != a1)) return active1.fields["fingerprint"] = t1 @@ -366,7 +366,7 @@ What a mess.*/ alert(usr, "You do not have the required rank to do this!") if("species") if (istype(active1, /datum/data/record)) - var/t1 = copytext(sanitize(input("Please enter race:", "General records", active1.fields["species"], null) as message),1,MAX_MESSAGE_LEN) + var/t1 = copytext(trim(sanitize(input("Please enter race:", "General records", active1.fields["species"], null) as message)),1,MAX_MESSAGE_LEN) if ((!( t1 ) || !( authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || active1 != a1)) return active1.fields["species"] = t1 diff --git a/code/game/machinery/constructable_frame.dm b/code/game/machinery/constructable_frame.dm index 751501cb..d8032de4 100644 --- a/code/game/machinery/constructable_frame.dm +++ b/code/game/machinery/constructable_frame.dm @@ -114,9 +114,8 @@ if(component_check) playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1) var/obj/machinery/new_machine = new src.circuit.build_path(src.loc) - for(var/obj/O in new_machine.component_parts) - del(O) - new_machine.component_parts = list() + new_machine.component_parts.Cut() + src.circuit.construct(new_machine) for(var/obj/O in src) if(circuit.contain_parts) // things like disposal don't want their parts in them O.loc = new_machine @@ -277,6 +276,52 @@ obj/item/weapon/circuitboard/rdserver "/obj/item/weapon/stock_parts/console_screen" = 1, "/obj/item/weapon/cable_coil" = 2,) +/obj/item/weapon/circuitboard/unary_atmos + board_type = "machine" + var/machine_dir = SOUTH + var/init_dirs = SOUTH + +/obj/item/weapon/circuitboard/unary_atmos/attackby(obj/item/I as obj, mob/user as mob) + if(istype(I,/obj/item/weapon/screwdriver)) + machine_dir = turn(machine_dir, 90) + init_dirs = machine_dir + user.visible_message("\blue \The [user] adjusts the jumper on the [src]'s port configuration pins.", "\blue You adjust the jumper on the port configuration pins. Now set to [dir2text(machine_dir)].") + return + +/obj/item/weapon/circuitboard/unary_atmos/examine() + ..() + usr << "The jumper is connecting the [dir2text(machine_dir)] pins." + +/obj/item/weapon/circuitboard/unary_atmos/construct(var/obj/machinery/atmospherics/unary/U) + //TODO: Move this stuff into the relevant constructor when pipe/construction.dm is cleaned up. + U.dir = src.machine_dir + U.initialize_directions = src.init_dirs + U.initialize() + U.build_network() + if (U.node) + U.node.initialize() + U.node.build_network() + +/obj/item/weapon/circuitboard/unary_atmos/heater + name = "Circuit Board (Gas Heating System)" + build_path = "/obj/machinery/atmospherics/unary/heater" + origin_tech = "powerstorage=2;engineering=1" + frame_desc = "Requires 5 Pieces of Cable, 1 Matter Bin, and 2 Capacitors." + req_components = list( + "/obj/item/weapon/cable_coil" = 5, + "/obj/item/weapon/stock_parts/matter_bin" = 1, + "/obj/item/weapon/stock_parts/capacitor" = 2) + +/obj/item/weapon/circuitboard/unary_atmos/cooler + name = "Circuit Board (Gas Cooling System)" + build_path = "/obj/machinery/atmospherics/unary/freezer" + origin_tech = "magnets=2;engineering=2" + frame_desc = "Requires 2 Pieces of Cable, 1 Matter Bin, 1 Micro Manipulator, and 2 Capacitors." + req_components = list( + "/obj/item/weapon/cable_coil" = 2, + "/obj/item/weapon/stock_parts/matter_bin" = 1, + "/obj/item/weapon/stock_parts/capacitor" = 2, + "/obj/item/weapon/stock_parts/manipulator" = 1) // Telecomms circuit boards: diff --git a/code/game/machinery/cryo.dm b/code/game/machinery/cryo.dm index d6c23031..67b5432f 100644 --- a/code/game/machinery/cryo.dm +++ b/code/game/machinery/cryo.dm @@ -1,3 +1,5 @@ +#define HEAT_CAPACITY_HUMAN 100 //249840 J/K, for a 72 kg person. + /obj/machinery/atmospherics/unary/cryo_cell name = "cryo cell" icon = 'icons/obj/cryogenics.dmi' @@ -7,6 +9,10 @@ layer = 2.8 var/on = 0 + use_power = 1 + idle_power_usage = 20 + active_power_usage = 200 + var/temperature_archived var/mob/living/carbon/occupant = null var/obj/item/weapon/reagent_containers/glass/beaker = null @@ -73,7 +79,7 @@ * * @return nothing */ -/obj/machinery/atmospherics/unary/cryo_cell/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null) +/obj/machinery/atmospherics/unary/cryo_cell/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) if(user == occupant || user.stat) return @@ -121,7 +127,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 @@ -194,7 +200,7 @@ icon_state = "cell-off" /obj/machinery/atmospherics/unary/cryo_cell/proc/process_occupant() - if(air_contents.total_moles() < 10) + if(air_contents.total_moles < 10) return if(occupant) if(occupant.stat == 2) @@ -205,7 +211,7 @@ if(occupant.bodytemperature < T0C) occupant.sleeping = max(5, (1/occupant.bodytemperature)*2000) occupant.Paralyse(max(5, (1/occupant.bodytemperature)*3000)) - if(air_contents.oxygen > 2) + if(air_contents.gas["oxygen"] > 2) if(occupant.getOxyLoss()) occupant.adjustOxyLoss(-1) else occupant.adjustOxyLoss(-1) @@ -224,7 +230,7 @@ beaker.reagents.reaction(occupant) /obj/machinery/atmospherics/unary/cryo_cell/proc/heat_gas_contents() - if(air_contents.total_moles() < 1) + if(air_contents.total_moles < 1) return var/air_heat_capacity = air_contents.heat_capacity() var/combined_heat_capacity = current_heat_capacity + air_heat_capacity @@ -233,7 +239,7 @@ air_contents.temperature = combined_energy/combined_heat_capacity /obj/machinery/atmospherics/unary/cryo_cell/proc/expel_gas() - if(air_contents.total_moles() < 1) + if(air_contents.total_moles < 1) return // var/datum/gas_mixture/expel_gas = new // var/remove_amount = air_contents.total_moles()/50 @@ -256,9 +262,14 @@ occupant.bodytemperature = 261 // Changed to 70 from 140 by Zuhayr due to reoccurance of bug. // occupant.metabslow = 0 occupant = null + current_heat_capacity = initial(current_heat_capacity) + update_use_power(1) update_icon() return /obj/machinery/atmospherics/unary/cryo_cell/proc/put_mob(mob/living/carbon/M as mob) + if (stat & (NOPOWER|BROKEN)) + usr << "\red The cryo cell is not functioning." + return if (!istype(M)) usr << "\red The cryo cell cannot handle such a lifeform!" return @@ -279,6 +290,8 @@ if(M.health > -100 && (M.health < 0 || M.sleeping)) M << "\blue You feel a cold liquid surround you. Your skin starts to freeze up." occupant = M + current_heat_capacity = HEAT_CAPACITY_HUMAN + update_use_power(2) // M.metabslow = 1 M.ExtinguishMob() add_fingerprint(usr) @@ -312,7 +325,7 @@ if(M.Victim == usr) usr << "You're too busy getting your life sucked out of you." return - if (usr.stat != 0 || stat & (NOPOWER|BROKEN)) + if (usr.stat != 0) return put_mob(usr) return diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index 88649b08..48321f9f 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -26,7 +26,7 @@ var/global/list/frozen_items = list() /obj/machinery/computer/cryopod/attack_ai() src.attack_hand() -obj/machinery/computer/cryopod/attack_hand(mob/user = usr) +/obj/machinery/computer/cryopod/attack_hand(mob/user = usr) if(stat & (NOPOWER|BROKEN)) return @@ -41,14 +41,14 @@ obj/machinery/computer/cryopod/attack_hand(mob/user = usr) dat += "

Cryogenic Oversight Control
" dat += "Welcome, [user.real_name].


" dat += "View storage log.
" + dat += "View objects.
" dat += "Recover object.
" dat += "Recover all objects.
" - dat += "Revive crew.

" user << browse(dat, "window=cryopod_console") onclose(user, "cryopod_console") -obj/machinery/computer/cryopod/Topic(href, href_list) +/obj/machinery/computer/cryopod/Topic(href, href_list) if(..()) return @@ -66,16 +66,27 @@ obj/machinery/computer/cryopod/Topic(href, href_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 null|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) @@ -95,9 +106,6 @@ obj/machinery/computer/cryopod/Topic(href, href_list) I.loc = get_turf(src) frozen_items -= I - else if(href_list["crew"]) - user << "\red Functionality unavailable at this time." - src.updateUsrDialog() return @@ -138,10 +146,10 @@ obj/machinery/computer/cryopod/Topic(href, href_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. @@ -156,7 +164,8 @@ obj/machinery/computer/cryopod/Topic(href, href_list) /obj/item/clothing/suit, /obj/item/clothing/shoes/magboots, /obj/item/blueprints, - /obj/item/clothing/head/helmet/space/ + /obj/item/clothing/head/helmet/space, + /obj/item/weapon/storage/internal ) /obj/machinery/cryopod/right @@ -176,7 +185,6 @@ obj/machinery/computer/cryopod/Topic(href, href_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 @@ -190,6 +198,8 @@ obj/machinery/computer/cryopod/Topic(href, href_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! + continue O.loc = src //Delete all items not on the preservation list. @@ -198,6 +208,7 @@ obj/machinery/computer/cryopod/Topic(href, href_list) items -= announce // or the autosay radio. for(var/obj/item/W in items) + var/preserve = null for(var/T in preserve_items) if(istype(W,T)) @@ -208,11 +219,14 @@ obj/machinery/computer/cryopod/Topic(href, href_list) del(W) else frozen_items += W + W.loc = null //Update any existing objectives involving this mob. for(var/datum/objective/O in all_objectives) - if(istype(O,/datum/objective/mutiny) && O.target == occupant.mind) //We don't want revs to get objectives that aren't for heads of staff. Letting them win or lose based on cryo is silly so we remove the objective. - del(O) //TODO: Update rev objectives on login by head (may happen already?) ~ Z + // We don't want revs to get objectives that aren't for heads of staff. Letting + // them win or lose based on cryo is silly so we remove the objective. + if(istype(O,/datum/objective/mutiny) && O.target == occupant.mind) + del(O) else if(O.target && istype(O.target,/datum/mind)) if(O.target == occupant.mind) if(O.owner && O.owner.current) @@ -322,14 +336,15 @@ obj/machinery/computer/cryopod/Topic(href, href_list) time_entered = world.time // Book keeping! - log_admin("[key_name_admin(M)] has entered a stasis pod.") + var/turf/location = get_turf(src) + log_admin("[key_name_admin(M)] has entered a stasis pod. (JMP)") message_admins("\blue [key_name_admin(M)] has entered a stasis pod.") - message_mods("\blue [key_name_admin(M)] has entered a stasis pod.") //Despawning occurs when process() is called with an occupant without a client. src.add_fingerprint(M) /obj/machinery/cryopod/verb/eject() + set name = "Eject Pod" set category = "Object" set src in oview(1) @@ -341,6 +356,14 @@ obj/machinery/computer/cryopod/Topic(href, href_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 203e481d..8a060d75 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 /* @@ -82,8 +86,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 @@ -96,51 +151,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.locked = 1 - D.update_icon() - 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.locked = 0 - D.update_icon() - 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 9dba5ddd..489481dd 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -32,11 +32,19 @@ //This generates the randomized airlock wire assignments for the game. /proc/RandomAirlockWires() + var/list/wire_assignments = CreateRandomAirlockWires() + + globalAirlockIndexToFlag = wire_assignments[2] + globalAirlockIndexToWireColor = wire_assignments[3] + globalAirlockWireColorToIndex = wire_assignments[4] + return wire_assignments[1] + +/proc/CreateRandomAirlockWires() //to make this not randomize the wires, just set index to 1 and increment it in the flag for loop (after doing everything else). var/list/wires = list(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) - airlockIndexToFlag = list(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) - airlockIndexToWireColor = list(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) - airlockWireColorToIndex = list(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + var/airlockIndexToFlag = list(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + var/airlockIndexToWireColor = list(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + var/airlockWireColorToIndex = list(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) var/flagIndex = 1 for (var/flag=1, flag<4096, flag+=flag) var/valid = 0 @@ -54,7 +62,7 @@ airlockWireColorToIndex[colorIndex] = flagIndex colorList -= colorIndex flagIndex+=1 - return wires + return list(wires, airlockIndexToFlag, airlockIndexToWireColor, airlockWireColorToIndex) /* Example: Airlock wires color -> flag are { 64, 128, 256, 2, 16, 4, 8, 32, 1 }. @@ -69,6 +77,7 @@ Airlock index -> wire color are { 9, 4, 6, 7, 5, 8, 1, 2, 3 }. icon_state = "door_closed" power_channel = ENVIRON + explosion_resistance = 15 var/aiControlDisabled = 0 //If 1, AI control is disabled until the AI hacks back in and disables the lock. If 2, the AI has bypassed the lock. If -1, the control is enabled but the AI had bypassed it earlier, so if it is disabled again the AI would have no trouble getting back in. var/hackProof = 0 // if 1, this door can't be hacked by the AI var/secondsMainPowerLost = 0 //The number of seconds until power is restored. @@ -93,6 +102,11 @@ Airlock index -> wire color are { 9, 4, 6, 7, 5, 8, 1, 2, 3 }. normalspeed = 1 var/obj/item/weapon/airlock_electronics/electronics = null var/hasShocked = 0 //Prevents multiple shocks from happening + var/secured_wires = 0 //for mapping use + var/list/airlockIndexToFlag + var/list/airlockWireColorToFlag + var/list/airlockIndexToWireColor + var/list/airlockWireColorToIndex /obj/machinery/door/airlock/command name = "Airlock" @@ -274,13 +288,7 @@ Airlock index -> wire color are { 9, 4, 6, 7, 5, 8, 1, 2, 3 }. /obj/machinery/door/airlock/plasma/proc/PlasmaBurn(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/toxinsToDeduce = 35 - napalm.toxins = toxinsToDeduce - napalm.temperature = 400+T0C - target_tile.assume_air(napalm) + target_tile.assume_gas("toxins", 35, 400+T0C) spawn (0) target_tile.hotspot_expose(temperature, 400) for(var/obj/structure/falsewall/plasma/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) @@ -293,11 +301,6 @@ Airlock index -> wire color are { 9, 4, 6, 7, 5, 8, 1, 2, 3 }. new/obj/structure/door_assembly( src.loc ) del (src) -/obj/machinery/door/airlock/clown - name = "Bananium Airlock" - icon = 'icons/obj/doors/Doorbananium.dmi' - mineral = "clown" - /obj/machinery/door/airlock/sandstone name = "Sandstone Airlock" icon = 'icons/obj/doors/Doorsand.dmi' @@ -366,26 +369,19 @@ About the new airlock wires panel: //Sending a pulse through this flashes the red light on the door (if the door has power). if((src.arePowerSystemsOn()) && (!(stat & NOPOWER))) do_animate("deny") - if(AIRLOCK_WIRE_MAIN_POWER1 || AIRLOCK_WIRE_MAIN_POWER2) + if(AIRLOCK_WIRE_MAIN_POWER1, AIRLOCK_WIRE_MAIN_POWER2) //Sending a pulse through either one causes a breaker to trip, disabling the door for 10 seconds if backup power is connected, or 1 minute if not (or until backup power comes back on, whichever is shorter). src.loseMainPower() if(AIRLOCK_WIRE_DOOR_BOLTS) //one wire for door bolts. Sending a pulse through this drops door bolts if they're not down (whether power's on or not), //raises them if they are down (only if power's on) if(!src.locked) - src.locked = 1 - for(var/mob/M in range(1,src)) - M << "You hear a click from the bottom of the door." - src.updateUsrDialog() + src.lock() else - if(src.arePowerSystemsOn()) //only can raise bolts if power's on - src.locked = 0 - for(var/mob/M in range(1,src)) - M << "You hear a click from the bottom of the door." - src.updateUsrDialog() - update_icon() + src.unlock() + src.updateUsrDialog() - if(AIRLOCK_WIRE_BACKUP_POWER1 || AIRLOCK_WIRE_BACKUP_POWER2) + if(AIRLOCK_WIRE_BACKUP_POWER1, AIRLOCK_WIRE_BACKUP_POWER2) //two wires for backup power. Sending a pulse through either one causes a breaker to trip, but this does not disable it unless main power is down too (in which case it is disabled for 1 minute or however long it takes main power to come back, whichever is shorter). src.loseBackupPower() if(AIRLOCK_WIRE_AI_CONTROL) @@ -440,18 +436,16 @@ About the new airlock wires panel: var/wireIndex = airlockWireColorToIndex[wireColor] wires &= ~wireFlag switch(wireIndex) - if(AIRLOCK_WIRE_MAIN_POWER1 || AIRLOCK_WIRE_MAIN_POWER2) + if(AIRLOCK_WIRE_MAIN_POWER1, AIRLOCK_WIRE_MAIN_POWER2) //Cutting either one disables the main door power, but unless backup power is also cut, the backup power re-powers the door in 10 seconds. While unpowered, the door may be crowbarred open, but bolts-raising will not work. Cutting these wires may electocute the user. src.loseMainPower() src.shock(usr, 50) src.updateUsrDialog() if(AIRLOCK_WIRE_DOOR_BOLTS) //Cutting this wire also drops the door bolts, and mending it does not raise them. (This is what happens now, except there are a lot more wires going to door bolts at present) - if(src.locked!=1) - src.locked = 1 - update_icon() + src.lock() src.updateUsrDialog() - if(AIRLOCK_WIRE_BACKUP_POWER1 || AIRLOCK_WIRE_BACKUP_POWER2) + if(AIRLOCK_WIRE_BACKUP_POWER1, AIRLOCK_WIRE_BACKUP_POWER2) //Cutting either one disables the backup door power (allowing it to be crowbarred open, but disabling bolts-raising), but may electocute the user. src.loseBackupPower() src.shock(usr, 50) @@ -487,12 +481,12 @@ About the new airlock wires panel: var/wireIndex = airlockWireColorToIndex[wireColor] //not used in this function wires |= wireFlag switch(wireIndex) - if(AIRLOCK_WIRE_MAIN_POWER1 || AIRLOCK_WIRE_MAIN_POWER2) + if(AIRLOCK_WIRE_MAIN_POWER1, AIRLOCK_WIRE_MAIN_POWER2) if((!src.isWireCut(AIRLOCK_WIRE_MAIN_POWER1)) && (!src.isWireCut(AIRLOCK_WIRE_MAIN_POWER2))) src.regainMainPower() src.shock(usr, 50) src.updateUsrDialog() - if(AIRLOCK_WIRE_BACKUP_POWER1 || AIRLOCK_WIRE_BACKUP_POWER2) + if(AIRLOCK_WIRE_BACKUP_POWER1, AIRLOCK_WIRE_BACKUP_POWER2) if((!src.isWireCut(AIRLOCK_WIRE_BACKUP_POWER1)) && (!src.isWireCut(AIRLOCK_WIRE_BACKUP_POWER2))) src.regainBackupPower() src.shock(usr, 50) @@ -538,23 +532,26 @@ About the new airlock wires panel: return ((src.wires & wireFlag) == 0) /obj/machinery/door/airlock/proc/canAIControl() - return ((src.aiControlDisabled!=1) && (!src.isAllPowerCut())); + return ((src.aiControlDisabled!=1) && (!src.isAllPowerLoss())); /obj/machinery/door/airlock/proc/canAIHack() - return ((src.aiControlDisabled==1) && (!hackProof) && (!src.isAllPowerCut())); + return ((src.aiControlDisabled==1) && (!hackProof) && (!src.isAllPowerLoss())); /obj/machinery/door/airlock/proc/arePowerSystemsOn() + if (stat & NOPOWER) + return 0 return (src.secondsMainPowerLost==0 || src.secondsBackupPowerLost==0) /obj/machinery/door/airlock/requiresID() return !(src.isWireCut(AIRLOCK_WIRE_IDSCAN) || aiDisabledIdScanner) -/obj/machinery/door/airlock/proc/isAllPowerCut() - var/retval=0 +/obj/machinery/door/airlock/proc/isAllPowerLoss() + if(stat & NOPOWER) + return 1 if(src.isWireCut(AIRLOCK_WIRE_MAIN_POWER1) || src.isWireCut(AIRLOCK_WIRE_MAIN_POWER2)) if(src.isWireCut(AIRLOCK_WIRE_BACKUP_POWER1) || src.isWireCut(AIRLOCK_WIRE_BACKUP_POWER2)) - retval=1 - return retval + return 1 + return 0 /obj/machinery/door/airlock/proc/regainMainPower() if(src.secondsMainPowerLost > 0) @@ -645,18 +642,16 @@ About the new airlock wires panel: else flick("door_closing", src) if("spark") - flick("door_spark", src) + if(density) + flick("door_spark", src) if("deny") - flick("door_deny", src) + if(density) + flick("door_deny", src) 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) @@ -702,7 +697,7 @@ About the new airlock wires panel: t1 += text("Backup Power Output wire is cut.
\n") if(src.isWireCut(AIRLOCK_WIRE_DOOR_BOLTS)) - t1 += text("Door bolt drop wire is cut.
\n") + t1 += text("Door bolt control wire is cut.
\n") else if(!src.locked) t1 += text("Door bolts are up. Drop them?
\n", src) else @@ -772,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 @@ -784,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 @@ -794,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 @@ -813,7 +808,7 @@ About the new airlock wires panel: if (src.isElectrified()) if (istype(mover, /obj/item)) var/obj/item/i = mover - if (i.m_amt) + if (i.matter["metal"]) var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread s.set_up(5, 1, src) s.start() @@ -878,7 +873,7 @@ About the new airlock wires panel: t1 += "Attach signaler" t1 += "
" - t1 += text("
\n[]
\n[]
\n[]
\n[]
\n[]
\n[]", (src.locked ? "The door bolts have fallen!" : "The door bolts look up."), (src.lights ? "The door bolt lights are on." : "The door bolt lights are off!"), ((src.arePowerSystemsOn() && !(stat & NOPOWER)) ? "The test light is on." : "The test light is off!"), (src.aiControlDisabled==0 ? "The 'AI control allowed' light is on." : "The 'AI control allowed' light is off."), (src.safe==0 ? "The 'Check Wiring' light is on." : "The 'Check Wiring' light is off."), (src.normalspeed==0 ? "The 'Check Timing Mechanism' light is on." : "The 'Check Timing Mechanism' light is off.")) + t1 += text("
\n[]
\n[]
\n[]
\n[]
\n[]
\n[]", (src.locked ? "The door bolts have fallen!" : "The door bolts look up."), (src.lights ? "The door bolt lights are on." : "The door bolt lights are off!"), ((src.arePowerSystemsOn()) ? "The test light is on." : "The test light is off!"), (src.aiControlDisabled==0 ? "The 'AI control allowed' light is on." : "The 'AI control allowed' light is off."), (src.safe==0 ? "The 'Check Wiring' light is on." : "The 'Check Wiring' light is off."), (src.normalspeed==0 ? "The 'Check Timing Mechanism' light is on." : "The 'Check Timing Mechanism' light is off.")) t1 += text("

Close

\n", src) @@ -889,6 +884,20 @@ About the new airlock wires panel: ..(user) return +/obj/machinery/door/airlock/proc/check_synth_access(mob/user as mob) + if(operating < 0) //emagged + user << "Unable to interface: Internal error." + return 0 + if(!src.canAIControl()) + if(src.canAIHack(user)) + src.hack(user) + else + if (src.isAllPowerLoss()) //don't really like how this gets checked a second time, but not sure how else to do it. + user << "Unable to interface: Connection timed out." + else + user << "Unable to interface: Connection refused." + return 0 + return 1 /obj/machinery/door/airlock/Topic(href, href_list, var/nowindow = 0) if(!nowindow) @@ -951,7 +960,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 @@ -961,10 +973,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 @@ -981,39 +994,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 dropping wire has been cut." - else if(src.locked!=1) - src.locked = 1 - update_icon() + 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) @@ -1024,17 +1017,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"]) @@ -1042,24 +1049,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 drop 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 - if(src.arePowerSystemsOn()) - src.locked = 0 - update_icon() + if(src.unlock()) + usr << "The door bolts have been raised." else - usr << text("Cannot raise door bolts due to power failure.
\n") - + usr << "Unable to raise door bolts." if(5) //electrify door for 30 seconds if(src.isWireCut(AIRLOCK_WIRE_ELECTRIFY)) @@ -1067,17 +1073,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 +1096,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 +1108,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() @@ -1184,14 +1185,17 @@ About the new airlock wires panel: beingcrowbarred = 1 //derp, Agouri else beingcrowbarred = 0 - if( beingcrowbarred && (operating == -1 || density && welded && operating != 1 && src.p_open && (!src.arePowerSystemsOn() || stat & NOPOWER) && !src.locked) ) + if( beingcrowbarred && src.p_open && (operating == -1 || (density && welded && operating != 1 && !src.arePowerSystemsOn() && !src.locked)) ) playsound(src.loc, 'sound/items/Crowbar.ogg', 100, 1) user.visible_message("[user] removes the electronics from the airlock assembly.", "You start to remove electronics from the airlock assembly.") if(do_after(user,40)) user << "\blue You removed the airlock electronics!" var/obj/structure/door_assembly/da = new assembly_type(src.loc) - da.anchored = 1 + if (istype(da, /obj/structure/door_assembly/multi_tile)) + da.dir = src.dir + + da.anchored = 1 if(mineral) da.glass = mineral //else if(glass) @@ -1221,7 +1225,7 @@ About the new airlock wires panel: del(src) return - else if(arePowerSystemsOn() && !(stat & NOPOWER)) + else if(arePowerSystemsOn()) user << "\blue The airlock's motors resist your efforts to force it." else if(locked) user << "\blue The airlock's bolts prevent it from being forced." @@ -1258,13 +1262,11 @@ About the new airlock wires panel: if( operating || welded || locked ) return 0 if(!forced) - if( !arePowerSystemsOn() || (stat & NOPOWER) || isWireCut(AIRLOCK_WIRE_OPEN_DOOR) ) + 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) - if(istype(src, /obj/machinery/door/airlock/clown)) - playsound(src.loc, 'sound/items/bikehorn.ogg', 30, 1) else playsound(src.loc, 'sound/machines/airlock.ogg', 30, 1) if(src.closeOther != null && istype(src.closeOther, /obj/machinery/door/airlock/) && !src.closeOther.density) @@ -1275,7 +1277,7 @@ About the new airlock wires panel: if(operating || welded || locked) return if(!forced) - if( !arePowerSystemsOn() || (stat & NOPOWER) || isWireCut(AIRLOCK_WIRE_DOOR_BOLTS) ) + if( !arePowerSystemsOn() || isWireCut(AIRLOCK_WIRE_DOOR_BOLTS) ) return if(safe) for(var/turf/turf in locs) @@ -1304,11 +1306,9 @@ 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) - if(istype(src, /obj/machinery/door/airlock/clown)) - playsound(src.loc, 'sound/items/bikehorn.ogg', 30, 1) else playsound(src.loc, 'sound/machines/airlock.ogg', 30, 1) for(var/turf/turf in locs) @@ -1318,8 +1318,37 @@ About the new airlock wires panel: ..() return +/obj/machinery/door/airlock/proc/lock(var/forced=0) + if (operating || src.locked) return + + src.locked = 1 + for(var/mob/M in range(1,src)) + M.show_message("You hear a click from the bottom of the door.", 2) + update_icon() + +/obj/machinery/door/airlock/proc/unlock(var/forced=0) + if (operating || !src.locked) return + + if (forced || (src.arePowerSystemsOn())) //only can raise bolts if power's on + src.locked = 0 + 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() ..() + + //wires + if (!secured_wires) + airlockWireColorToFlag = globalAirlockWireColorToFlag + airlockIndexToFlag = globalAirlockIndexToFlag + airlockIndexToWireColor = globalAirlockIndexToWireColor + airlockWireColorToIndex = globalAirlockWireColorToIndex + else + randomize_wires() + if(src.closeOtherId != null) spawn (5) for (var/obj/machinery/door/airlock/A in world) @@ -1327,9 +1356,15 @@ About the new airlock wires panel: src.closeOther = A break +/obj/machinery/door/airlock/proc/randomize_wires() + var/wire_assignments = CreateRandomAirlockWires() + airlockWireColorToFlag = wire_assignments[1] + airlockIndexToFlag = wire_assignments[2] + airlockIndexToWireColor = wire_assignments[3] + airlockWireColorToIndex = wire_assignments[4] /obj/machinery/door/airlock/proc/prison_open() - src.locked = 0 + src.unlock() src.open() - src.locked = 1 + src.lock() return \ No newline at end of file diff --git a/code/game/machinery/airlock_control.dm b/code/game/machinery/doors/airlock_control.dm similarity index 76% rename from code/game/machinery/airlock_control.dm rename to code/game/machinery/doors/airlock_control.dm index 0e2fb21d..7ad909c8 100644 --- a/code/game/machinery/airlock_control.dm +++ b/code/game/machinery/doors/airlock_control.dm @@ -1,4 +1,4 @@ -#define AIRLOCK_CONTROL_RANGE 5 +#define AIRLOCK_CONTROL_RANGE 22 // This code allows for airlocks to be controlled externally by setting an id_tag and comm frequency (disables ID access) obj/machinery/door/airlock @@ -6,51 +6,96 @@ obj/machinery/door/airlock var/frequency var/shockedby = list() var/datum/radio_frequency/radio_connection - explosion_resistance = 15 + var/cur_command = null //the command the door is currently attempting to complete +obj/machinery/door/airlock/proc/can_radio() + if(!arePowerSystemsOn()) + return 0 + return 1 + +obj/machinery/door/airlock/process() + ..() + if (arePowerSystemsOn()) + execute_current_command() obj/machinery/door/airlock/receive_signal(datum/signal/signal) + if (!arePowerSystemsOn()) return //no power + + if (!can_radio()) return //no radio + if(!signal || signal.encryption) return if(id_tag != signal.data["tag"] || !signal.data["command"]) return - switch(signal.data["command"]) + cur_command = signal.data["command"] + spawn() + execute_current_command() + +obj/machinery/door/airlock/proc/execute_current_command() + if(operating) + return //emagged or busy doing something else + + if (!cur_command) + return + + do_command(cur_command) + if (command_completed(cur_command)) + cur_command = null + +obj/machinery/door/airlock/proc/do_command(var/command) + switch(command) if("open") - open(1) + open() if("close") - close(1) + close() if("unlock") - locked = 0 - update_icon() + unlock() if("lock") - locked = 1 - update_icon() + lock() if("secure_open") - locked = 0 - update_icon() + unlock() sleep(2) - open(1) + open() - locked = 1 - update_icon() + lock() if("secure_close") - locked = 0 - close(1) + unlock() + close() - locked = 1 + lock() sleep(2) - update_icon() - + send_status() +obj/machinery/door/airlock/proc/command_completed(var/command) + switch(command) + if("open") + return (!density) -obj/machinery/door/airlock/proc/send_status() + if("close") + return density + + if("unlock") + return !locked + + if("lock") + return locked + + if("secure_open") + return (locked && !density) + + if("secure_close") + return (locked && density) + + return 1 //Unknown command. Just assume it's completed. + +obj/machinery/door/airlock/proc/send_status(var/bumped = 0) if(radio_connection) var/datum/signal/signal = new signal.transmission_method = 1 //radio signal @@ -59,6 +104,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) @@ -78,17 +126,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) @@ -133,13 +171,6 @@ obj/machinery/airlock_sensor var/alert = 0 var/previousPressure - attackby(C as obj, mob/user as mob) - if(istype(C, /obj/item/device/signaltool)) - var/obj/item/device/signaltool/ST = C - id_tag = ST.change_ID(id_tag) - set_frequency(ST.change_freq(frequency)) - return - obj/machinery/airlock_sensor/update_icon() if(on) if(alert) @@ -214,12 +245,6 @@ obj/machinery/access_button var/on = 1 - attackby(C as obj, mob/user as mob) - if(istype(C, /obj/item/device/signaltool)) - var/obj/item/device/signaltool/ST = C - master_tag = ST.change_ID(master_tag) - set_frequency(ST.change_freq(frequency)) - return obj/machinery/access_button/update_icon() if(on) @@ -227,6 +252,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/airlock_electronics.dm b/code/game/machinery/doors/airlock_electronics.dm index eab70dd4..fe9e8cf1 100644 --- a/code/game/machinery/doors/airlock_electronics.dm +++ b/code/game/machinery/doors/airlock_electronics.dm @@ -5,8 +5,8 @@ icon = 'icons/obj/doors/door_assembly.dmi' icon_state = "door_electronics" w_class = 2.0 //It should be tiny! -Agouri - m_amt = 50 - g_amt = 50 + + matter = list("metal" = 50,"glass" = 50) req_access = list(access_engine) @@ -109,3 +109,8 @@ if (!conf_access.len) conf_access = null + +/obj/item/weapon/airlock_electronics/secure + name = "secure airlock electronics" + desc = "designed to be somewhat more resistant to hacking than standard electronics." + origin_tech = "programming=3" diff --git a/code/game/machinery/doors/brigdoors.dm b/code/game/machinery/doors/brigdoors.dm index a63edd84..48c9a443 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) @@ -329,43 +326,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 e8f8eace..59ec1262 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 1d434429..d194cdf0 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.01 + open_layer = DOOR_OPEN_LAYER - 0.01 // Just below doors when open + closed_layer = DOOR_CLOSED_LAYER + 0.01 // 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/multi_tile.dm b/code/game/machinery/doors/multi_tile.dm index 986b25b7..474e3b66 100644 --- a/code/game/machinery/doors/multi_tile.dm +++ b/code/game/machinery/doors/multi_tile.dm @@ -7,4 +7,4 @@ icon = 'icons/obj/doors/Door2x1glass.dmi' opacity = 0 glass = 1 - assembly_type = "obj/structure/door_assembly/multi_tile" \ No newline at end of file + assembly_type = /obj/structure/door_assembly/multi_tile \ No newline at end of file diff --git a/code/game/machinery/doors/poddoor.dm b/code/game/machinery/doors/poddoor.dm index c9c61f39..910e885a 100644 --- a/code/game/machinery/doors/poddoor.dm +++ b/code/game/machinery/doors/poddoor.dm @@ -1,6 +1,6 @@ /obj/machinery/door/poddoor name = "Podlock" - desc = "Why it no open!!!" + desc = "That looks like it doesn't open easily." icon = 'icons/obj/doors/rapid_pdoor.dmi' icon_state = "pdoor1" var/id = 1.0 diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm index 204727f7..531773fc 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) diff --git a/code/game/machinery/doppler_array.dm b/code/game/machinery/doppler_array.dm index 73a784a7..19367b10 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 4ace6342..fb909dee 100644 --- a/code/game/machinery/embedded_controller/airlock_controllers.dm +++ b/code/game/machinery/embedded_controller/airlock_controllers.dm @@ -1,8 +1,26 @@ +//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_airlock_mech_sensor + var/tag_shuttle_mech_sensor + var/tag_secure = 0 + +/obj/machinery/embedded_controller/radio/airlock/initialize() + ..() + program = new/datum/computer/file/embedded_program/airlock(src) + //Advanced airlock controller for when you want a more versatile airlock controller - useful for turning simple access control rooms into airlocks -/obj/machinery/embedded_controller/radio/advanced_airlock_controller +/obj/machinery/embedded_controller/radio/airlock/advanced_airlock_controller name = "Advanced Airlock Controller" -/obj/machinery/embedded_controller/radio/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( @@ -14,7 +32,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) @@ -25,7 +43,13 @@ ui.set_auto_update(1) -/obj/machinery/embedded_controller/radio/advanced_airlock_controller/Topic(href, href_list) +/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") @@ -50,11 +74,11 @@ //Airlock controller for airlock control - most airlocks on the station use this -/obj/machinery/embedded_controller/radio/airlock_controller +/obj/machinery/embedded_controller/radio/airlock/airlock_controller name = "Airlock Controller" tag_secure = 1 -/obj/machinery/embedded_controller/radio/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( @@ -64,7 +88,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) @@ -75,7 +99,13 @@ ui.set_auto_update(1) -/obj/machinery/embedded_controller/radio/airlock_controller/Topic(href, href_list) +/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") @@ -96,7 +126,7 @@ //Access controller for door control - used in virology and the like -/obj/machinery/embedded_controller/radio/access_controller +/obj/machinery/embedded_controller/radio/airlock/access_controller icon = 'icons/obj/airlock_machines.dmi' icon_state = "access_control_standby" @@ -104,7 +134,7 @@ tag_secure = 1 -/obj/machinery/embedded_controller/radio/access_controller/update_icon() +/obj/machinery/embedded_controller/radio/airlock/access_controller/update_icon() if(on && program) if(program.memory["processing"]) icon_state = "access_control_process" @@ -113,7 +143,7 @@ else icon_state = "access_control_off" -/obj/machinery/embedded_controller/radio/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( @@ -122,7 +152,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) @@ -133,7 +163,13 @@ ui.set_auto_update(1) -/obj/machinery/embedded_controller/radio/access_controller/Topic(href, href_list) +/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 new file mode 100644 index 00000000..a78ac2be --- /dev/null +++ b/code/game/machinery/embedded_controller/airlock_docking_controller.dm @@ -0,0 +1,180 @@ +//a docking port based on an airlock +/obj/machinery/embedded_controller/radio/airlock/docking_port + name = "docking port controller" + var/datum/computer/file/embedded_program/airlock/docking/airlock_program + var/datum/computer/file/embedded_program/docking/airlock/docking_program + tag_secure = 1 + +/obj/machinery/embedded_controller/radio/airlock/docking_port/initialize() + ..() + airlock_program = new/datum/computer/file/embedded_program/airlock/docking(src) + 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, var/force_open = 1) + var/data[0] + + data = list( + "chamber_pressure" = round(airlock_program.memory["chamber_sensor_pressure"]), + "exterior_status" = airlock_program.memory["exterior_status"], + "interior_status" = airlock_program.memory["interior_status"], + "processing" = airlock_program.memory["processing"], + "docking_status" = docking_program.get_docking_status(), + "airlock_disabled" = !(docking_program.undocked() || docking_program.override_enabled), + "override_enabled" = docking_program.override_enabled, + ) + + 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) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +/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") + clean = 1 + if("cycle_int") + clean = 1 + if("force_ext") + clean = 1 + if("force_int") + clean = 1 + if("abort") + clean = 1 + if("toggle_override") + clean = 1 + + if(clean) + program.receive_user_command(href_list["command"]) + + return 1 + + + +//A docking controller for an airlock based docking port +/datum/computer/file/embedded_program/docking/airlock + var/datum/computer/file/embedded_program/airlock/docking/airlock_program + +/datum/computer/file/embedded_program/docking/airlock/New(var/obj/machinery/embedded_controller/M, var/datum/computer/file/embedded_program/airlock/docking/A) + ..(M) + airlock_program = A + airlock_program.master_prog = src + +/datum/computer/file/embedded_program/docking/airlock/receive_user_command(command) + if (command == "toggle_override") + if (override_enabled) + disable_override() + else + enable_override() + return + + ..(command) + airlock_program.receive_user_command(command) //pass along to subprograms + +/datum/computer/file/embedded_program/docking/airlock/process() + airlock_program.process() + ..() + +/datum/computer/file/embedded_program/docking/airlock/receive_signal(datum/signal/signal, receive_method, receive_param) + airlock_program.receive_signal(signal, receive_method, receive_param) //pass along to subprograms + ..(signal, receive_method, receive_param) + +//tell the docking port to start getting ready for docking - e.g. pressurize +/datum/computer/file/embedded_program/docking/airlock/prepare_for_docking() + airlock_program.begin_cycle_in() + +//are we ready for docking? +/datum/computer/file/embedded_program/docking/airlock/ready_for_docking() + return airlock_program.done_cycling() + +//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() + var/ext_closed = airlock_program.check_exterior_door_secured() + var/int_closed = airlock_program.check_interior_door_secured() + return (ext_closed || int_closed) + +//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") + +/datum/computer/file/embedded_program/airlock/docking/cycleDoors(var/target) + 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 + ..(target) + +/*** DEBUG VERBS *** + +/datum/computer/file/embedded_program/docking/proc/print_state() + world << "id_tag: [id_tag]" + world << "dock_state: [dock_state]" + world << "control_mode: [control_mode]" + world << "tag_target: [tag_target]" + world << "response_sent: [response_sent]" + +/datum/computer/file/embedded_program/docking/post_signal(datum/signal/signal, comm_line) + world << "Program [id_tag] sent a message!" + print_state() + world << "[id_tag] sent command \"[signal.data["command"]]\" to \"[signal.data["recipient"]]\"" + ..(signal) + +/obj/machinery/embedded_controller/radio/airlock/docking_port/verb/view_state() + set category = "Debug" + set src in view(1) + src.program:print_state() + +/obj/machinery/embedded_controller/radio/airlock/docking_port/verb/spoof_signal(var/command as text, var/sender as text) + set category = "Debug" + set src in view(1) + var/datum/signal/signal = new + signal.data["tag"] = sender + signal.data["command"] = command + signal.data["recipient"] = id_tag + + src.program:receive_signal(signal) + +/obj/machinery/embedded_controller/radio/airlock/docking_port/verb/debug_init_dock(var/target as text) + set category = "Debug" + set src in view(1) + src.program:initiate_docking(target) + +/obj/machinery/embedded_controller/radio/airlock/docking_port/verb/debug_init_undock() + set category = "Debug" + set src in view(1) + src.program:initiate_undocking() + +*/ \ No newline at end of file diff --git a/code/game/machinery/embedded_controller/airlock_docking_controller_multi.dm b/code/game/machinery/embedded_controller/airlock_docking_controller_multi.dm new file mode 100644 index 00000000..d03bffcb --- /dev/null +++ b/code/game/machinery/embedded_controller/airlock_docking_controller_multi.dm @@ -0,0 +1,153 @@ +//a controller for a docking port with multiple independent airlocks +//this is the master controller, that things will try to dock with. +/obj/machinery/embedded_controller/radio/docking_port_multi + name = "docking port controller" + + var/child_tags_txt + var/child_names_txt + var/list/child_names = list() + + var/datum/computer/file/embedded_program/docking/multi/docking_program + +/obj/machinery/embedded_controller/radio/docking_port_multi/initialize() + ..() + docking_program = new/datum/computer/file/embedded_program/docking/multi(src) + program = docking_program + + var/list/names = text2list(child_names_txt, ";") + var/list/tags = text2list(child_tags_txt, ";") + + if (names.len == tags.len) + for (var/i = 1; i <= tags.len; i++) + 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, var/force_open = 1) + var/data[0] + + var/list/airlocks[child_names.len] + var/i = 1 + for (var/child_tag in child_names) + airlocks[i++] = list("name"=child_names[child_tag], "override_enabled"=(docking_program.children_override[child_tag] == "enabled")) + + data = list( + "docking_status" = docking_program.get_docking_status(), + "airlocks" = airlocks, + ) + + 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) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/embedded_controller/radio/docking_port_multi/Topic(href, href_list) + return + + + +//a docking port based on an airlock +/obj/machinery/embedded_controller/radio/airlock/docking_port_multi + name = "docking port controller" + var/master_tag //for mapping + var/datum/computer/file/embedded_program/airlock/multi_docking/airlock_program + tag_secure = 1 + +/obj/machinery/embedded_controller/radio/airlock/docking_port_multi/initialize() + ..() + 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, var/force_open = 1) + var/data[0] + + data = list( + "chamber_pressure" = round(airlock_program.memory["chamber_sensor_pressure"]), + "exterior_status" = airlock_program.memory["exterior_status"], + "interior_status" = airlock_program.memory["interior_status"], + "processing" = airlock_program.memory["processing"], + "docking_status" = airlock_program.master_status, + "airlock_disabled" = (airlock_program.docking_enabled && !airlock_program.override_enabled), + "override_enabled" = airlock_program.override_enabled, + ) + + 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) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/embedded_controller/radio/airlock/docking_port_multi/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") + clean = 1 + if("cycle_int") + clean = 1 + if("force_ext") + clean = 1 + if("force_int") + clean = 1 + if("abort") + clean = 1 + if("toggle_override") + clean = 1 + + if(clean) + program.receive_user_command(href_list["command"]) + + return 1 + + + +/*** DEBUG VERBS *** + +/datum/computer/file/embedded_program/docking/multi/proc/print_state() + world << "id_tag: [id_tag]" + world << "dock_state: [dock_state]" + world << "control_mode: [control_mode]" + world << "tag_target: [tag_target]" + world << "response_sent: [response_sent]" + +/datum/computer/file/embedded_program/docking/multi/post_signal(datum/signal/signal, comm_line) + world << "Program [id_tag] sent a message!" + print_state() + world << "[id_tag] sent command \"[signal.data["command"]]\" to \"[signal.data["recipient"]]\"" + ..(signal) + +/obj/machinery/embedded_controller/radio/docking_port_multi/verb/view_state() + set category = "Debug" + set src in view(1) + src.program:print_state() + +/obj/machinery/embedded_controller/radio/docking_port_multi/verb/spoof_signal(var/command as text, var/sender as text) + set category = "Debug" + set src in view(1) + var/datum/signal/signal = new + signal.data["tag"] = sender + signal.data["command"] = command + signal.data["recipient"] = id_tag + + src.program:receive_signal(signal) + +/obj/machinery/embedded_controller/radio/docking_port_multi/verb/debug_init_dock(var/target as text) + set category = "Debug" + set src in view(1) + src.program:initiate_docking(target) + +/obj/machinery/embedded_controller/radio/docking_port_multi/verb/debug_init_undock() + set category = "Debug" + set src in view(1) + src.program:initiate_undocking() + +*/ diff --git a/code/game/machinery/embedded_controller/airlock_program.dm b/code/game/machinery/embedded_controller/airlock_program.dm index 8ce0256a..8cb3abb9 100644 --- a/code/game/machinery/embedded_controller/airlock_program.dm +++ b/code/game/machinery/embedded_controller/airlock_program.dm @@ -1,31 +1,31 @@ //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 #define TARGET_OUTOPEN -2 -/datum/computer/file/embedded_program - var/list/memory = list() - var/obj/machinery/embedded_controller/master - var/id_tag +/datum/computer/file/embedded_program/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_airlock_mech_sensor + var/tag_shuttle_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) -/datum/computer/file/embedded_program/New() - ..() memory["chamber_sensor_pressure"] = ONE_ATMOSPHERE memory["external_sensor_pressure"] = 0 //assume vacuum for simple airlock controller memory["internal_sensor_pressure"] = ONE_ATMOSPHERE @@ -36,10 +36,23 @@ 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" + tag_interior_door = controller.tag_interior_door? controller.tag_interior_door : "[id_tag]_inner" + tag_airpump = controller.tag_airpump? controller.tag_airpump : "[id_tag]_pump" + 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_airlock_mech_sensor = controller.tag_airlock_mech_sensor? controller.tag_airlock_mech_sensor : "[id_tag]_airlock_mech" + tag_shuttle_mech_sensor = controller.tag_shuttle_mech_sensor? controller.tag_shuttle_mech_sensor : "[id_tag]_shuttle_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") - -/datum/computer/file/embedded_program/proc/receive_signal(datum/signal/signal, receive_method, receive_param) +/datum/computer/file/embedded_program/airlock/receive_signal(datum/signal/signal, receive_method, receive_param) var/receive_tag = signal.data["tag"] if(!receive_tag) return @@ -68,7 +81,7 @@ memory["pump_status"] = "off" else if(receive_tag==id_tag) - if(istype(master, /obj/machinery/embedded_controller/radio/access_controller)) + if(istype(master, /obj/machinery/embedded_controller/radio/airlock/access_controller)) switch(signal.data["command"]) if("cycle_exterior") receive_user_command("cycle_ext_door") @@ -92,16 +105,14 @@ receive_user_command("cycle_int") -/datum/computer/file/embedded_program/proc/receive_user_command(command) +/datum/computer/file/embedded_program/airlock/receive_user_command(command) var/shutdown_pump = 0 switch(command) if("cycle_ext") - state = STATE_WAIT - target_state = TARGET_OUTOPEN + begin_cycle_out() if("cycle_int") - state = STATE_WAIT - target_state = TARGET_INOPEN + begin_cycle_in() if("cycle_ext_door") cycleDoors(TARGET_OUTOPEN) @@ -110,11 +121,15 @@ cycleDoors(TARGET_INOPEN) if("abort") + stop_cycling() + /* + //dont do this. If the airlock can't get enough air to pressurize the person inside is stuck state = STATE_PRESSURIZE target_state = TARGET_NONE memory["target_pressure"] = ONE_ATMOSPHERE signalPump(tag_airpump, 1, 1, memory["target_pressure"]) process() + */ if("force_ext") toggleDoor(memory["exterior_status"], tag_exterior_door, memory["secure"], "toggle") @@ -144,43 +159,57 @@ signalPump(tag_airpump, 0) //send a signal to stop pressurizing -/datum/computer/file/embedded_program/proc/process() - if(!state && target_state) - switch(target_state) - if(TARGET_INOPEN) - memory["target_pressure"] = memory["internal_sensor_pressure"] - if(TARGET_OUTOPEN) - memory["target_pressure"] = memory["external_sensor_pressure"] +/datum/computer/file/embedded_program/airlock/process() + if(!state) //Idle + if(target_state) + switch(target_state) + if(TARGET_INOPEN) + memory["target_pressure"] = memory["internal_sensor_pressure"] + if(TARGET_OUTOPEN) + memory["target_pressure"] = memory["external_sensor_pressure"] - //lock down the airlock before activating pumps - toggleDoor(memory["exterior_status"], tag_exterior_door, 1, "close") - toggleDoor(memory["interior_status"], tag_interior_door, 1, "close") + //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) + 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") @@ -197,7 +226,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 @@ -209,22 +238,46 @@ return 1 +//these are here so that other types don't have to make so many assuptions about our implementation -/datum/computer/file/embedded_program/proc/post_signal(datum/signal/signal, comm_line) - if(master) - master.post_signal(signal, comm_line) - else - del(signal) +/datum/computer/file/embedded_program/airlock/proc/begin_cycle_in() + state = STATE_IDLE + target_state = TARGET_INOPEN +/datum/computer/file/embedded_program/airlock/proc/begin_cycle_out() + state = STATE_IDLE + target_state = TARGET_OUTOPEN -/datum/computer/file/embedded_program/proc/signalDoor(var/tag, var/command) +/datum/computer/file/embedded_program/airlock/proc/close_doors() + toggleDoor(memory["interior_status"], tag_interior_door, 1, "close") + toggleDoor(memory["exterior_status"], tag_exterior_door, 1, "close") + +/datum/computer/file/embedded_program/airlock/proc/stop_cycling() + state = STATE_IDLE + target_state = TARGET_NONE + +/datum/computer/file/embedded_program/airlock/proc/done_cycling() + 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() + return (memory["exterior_status"]["state"] == "closed" && memory["exterior_status"]["lock"] == "locked") + +/datum/computer/file/embedded_program/airlock/proc/check_interior_door_secured() + return (memory["interior_status"]["state"] == "closed" && memory["interior_status"]["lock"] == "locked") + +/datum/computer/file/embedded_program/airlock/proc/check_doors_secured() + var/ext_closed = check_exterior_door_secured() + var/int_closed = check_interior_door_secured() + return (ext_closed && int_closed) + +/datum/computer/file/embedded_program/airlock/proc/signalDoor(var/tag, var/command) 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/proc/signalPump(var/tag, var/power, var/direction, var/pressure) +/datum/computer/file/embedded_program/airlock/proc/signalPump(var/tag, var/power, var/direction, var/pressure) var/datum/signal/signal = new signal.data = list( "tag" = tag, @@ -235,8 +288,8 @@ ) post_signal(signal) - -/datum/computer/file/embedded_program/proc/cycleDoors(var/target) +//this is called to set the appropriate door state at the end of a cycling process, or for the exterior buttons +/datum/computer/file/embedded_program/airlock/proc/cycleDoors(var/target) switch(target) if(TARGET_OUTOPEN) toggleDoor(memory["interior_status"], tag_interior_door, memory["secure"], "close") @@ -252,6 +305,19 @@ signalDoor(tag_exterior_door, command) signalDoor(tag_interior_door, command) +datum/computer/file/embedded_program/airlock/proc/signal_mech_sensor(var/command, var/sensor) + var/datum/signal/signal = new + signal.data["tag"] = sensor + signal.data["command"] = command + post_signal(signal) + +/datum/computer/file/embedded_program/airlock/proc/enable_mech_regulation() + signal_mech_sensor("enable", tag_shuttle_mech_sensor) + signal_mech_sensor("enable", tag_airlock_mech_sensor) + +/datum/computer/file/embedded_program/airlock/proc/disable_mech_regulation() + signal_mech_sensor("disable", tag_shuttle_mech_sensor) + signal_mech_sensor("disable", tag_airlock_mech_sensor) /*---------------------------------------------------------- toggleDoor() @@ -265,7 +331,7 @@ Only sends a command if it is needed, i.e. if the door is already open, passing an open command to this proc will not send an additional command to open the door again. ----------------------------------------------------------*/ -/datum/computer/file/embedded_program/proc/toggleDoor(var/list/doorStatus, var/doorTag, var/secure, var/command) +/datum/computer/file/embedded_program/airlock/proc/toggleDoor(var/list/doorStatus, var/doorTag, var/secure, var/command) var/doorCommand = null if(command == "toggle") @@ -307,7 +373,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.dm b/code/game/machinery/embedded_controller/docking_program.dm new file mode 100644 index 00000000..032676f0 --- /dev/null +++ b/code/game/machinery/embedded_controller/docking_program.dm @@ -0,0 +1,285 @@ + +#define STATE_UNDOCKED 0 +#define STATE_DOCKING 1 +#define STATE_UNDOCKING 2 +#define STATE_DOCKED 3 + +#define MODE_NONE 0 +#define MODE_SERVER 1 +#define MODE_CLIENT 2 //The one who initiated the docking, and who can initiate the undocking. The server cannot initiate undocking, and is the one responsible for deciding to accept a docking request and signals when docking and undocking is complete. (Think server == station, client == shuttle) + +#define MESSAGE_RESEND_TIME 5 //how long (in seconds) do we wait before resending a message + +/* + *** STATE TABLE *** + + MODE_CLIENT|STATE_UNDOCKED sent a request for docking and now waiting for a reply. + MODE_CLIENT|STATE_DOCKING server told us they are OK to dock, waiting for our docking port to be ready. + MODE_CLIENT|STATE_DOCKED idle - docked as client. + MODE_CLIENT|STATE_UNDOCKING we are either waiting for our docking port to be ready or for the server to give us the OK to finish undocking. + + MODE_SERVER|STATE_UNDOCKED should never happen. + MODE_SERVER|STATE_DOCKING someone requested docking, we are waiting for our docking port to be ready. + MODE_SERVER|STATE_DOCKED idle - docked as server + MODE_SERVER|STATE_UNDOCKING client requested undocking, we are waiting for our docking port to be ready. + + MODE_NONE|STATE_UNDOCKED idle - not docked. + MODE_NONE|anything else should never happen. + + *** Docking Signals *** + + Docking + Client sends request_dock + Server sends confirm_dock to say that yes, we will serve your request + When client is ready, sends confirm_dock + Server sends confirm_dock back to indicate that docking is complete + + Undocking + Client sends request_undock + When client is ready, sends confirm_undock + Server sends confirm_undock back to indicate that docking is complete + + Note that in both cases each side exchanges confirm_dock before the docking operation is considered done. + The client first sends a confirm message to indicate it is ready, and then finally the server will send it's + confirm message to indicate that the operation is complete. + + Note also that when docking, the server sends an additional confirm message. This is because before docking, + the server and client do not have a defined relationship. Before undocking, the server and client are already + related to each other, thus the extra confirm message is not needed. + + *** Override, what is it? *** + + The purpose of enabling the override is to prevent the docking program from automatically doing things with the docking port when docking or undocking. + Maybe the shuttle is full of plamsa/phoron for some reason, and you don't want the door to automatically open, or the airlock to cycle. + This means that the prepare_for_docking/undocking and finish_docking/undocking procs don't get called. + + The docking controller will still check the state of the docking port, and thus prevent the shuttle from launching unless they force the launch (handling forced + launches is not the docking controller's responsibility). In this case it is up to the players to manually get the docking port into a good state to undock + (which usually just means closing and locking the doors). + + In line with this, docking controllers should prevent players from manually doing things when the override is NOT enabled. +*/ + + +/datum/computer/file/embedded_program/docking + var/tag_target //the tag of the docking controller that we are trying to dock with + var/dock_state = STATE_UNDOCKED + var/control_mode = MODE_NONE + var/response_sent = 0 //so we don't spam confirmation messages + var/resend_counter = 0 //for periodically resending confirmation messages in case they are missed + + var/override_enabled = 0 //when enabled, do not open/close doors or cycle airlocks and wait for the player to do it manually + var/received_confirm = 0 //for undocking, whether the server has recieved a confirmation from the client + +/datum/computer/file/embedded_program/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 (recipient != id_tag) + return //this signal is not for us + + switch (command) + if ("confirm_dock") + if (control_mode == MODE_CLIENT && dock_state == STATE_UNDOCKED && receive_tag == tag_target) + dock_state = STATE_DOCKING + broadcast_docking_status() + if (!override_enabled) + prepare_for_docking() + + else if (control_mode == MODE_CLIENT && dock_state == STATE_DOCKING && receive_tag == tag_target) + dock_state = STATE_DOCKED + broadcast_docking_status() + if (!override_enabled) + finish_docking() //client done docking! + response_sent = 0 + else if (control_mode == MODE_SERVER && dock_state == STATE_DOCKING && receive_tag == tag_target) //client just sent us the confirmation back, we're done with the docking process + received_confirm = 1 + + if ("request_dock") + if (control_mode == MODE_NONE && dock_state == STATE_UNDOCKED) + control_mode = MODE_SERVER + + dock_state = STATE_DOCKING + broadcast_docking_status() + + tag_target = receive_tag + if (!override_enabled) + prepare_for_docking() + send_docking_command(tag_target, "confirm_dock") //acknowledge the request + + if ("confirm_undock") + if (control_mode == MODE_CLIENT && dock_state == STATE_UNDOCKING && receive_tag == tag_target) + if (!override_enabled) + finish_undocking() + reset() //client is done undocking! + else if (control_mode == MODE_SERVER && dock_state == STATE_UNDOCKING && receive_tag == tag_target) + received_confirm = 1 + + if ("request_undock") + if (control_mode == MODE_SERVER && dock_state == STATE_DOCKED && receive_tag == tag_target) + dock_state = STATE_UNDOCKING + broadcast_docking_status() + + if (!override_enabled) + prepare_for_undocking() + + if ("dock_error") + if (receive_tag == tag_target) + reset() + +/datum/computer/file/embedded_program/docking/process() + switch(dock_state) + if (STATE_DOCKING) //waiting for our docking port to be ready for docking + if (ready_for_docking()) + if (control_mode == MODE_CLIENT) + if (!response_sent) + send_docking_command(tag_target, "confirm_dock") //tell the server we're ready + response_sent = 1 + + else if (control_mode == MODE_SERVER && received_confirm) + send_docking_command(tag_target, "confirm_dock") //tell the client we are done docking. + + dock_state = STATE_DOCKED + broadcast_docking_status() + + if (!override_enabled) + finish_docking() //server done docking! + response_sent = 0 + received_confirm = 0 + + if (STATE_UNDOCKING) + if (ready_for_undocking()) + if (control_mode == MODE_CLIENT) + if (!response_sent) + send_docking_command(tag_target, "confirm_undock") //tell the server we are OK to undock. + response_sent = 1 + + else if (control_mode == MODE_SERVER && received_confirm) + send_docking_command(tag_target, "confirm_undock") //tell the client we are done undocking. + if (!override_enabled) + finish_undocking() + reset() //server is done undocking! + + if (response_sent || resend_counter > 0) + resend_counter++ + + if (resend_counter >= MESSAGE_RESEND_TIME || (dock_state != STATE_DOCKING && dock_state != STATE_UNDOCKING)) + response_sent = 0 + resend_counter = 0 + + //handle invalid states + if (control_mode == MODE_NONE && dock_state != STATE_UNDOCKED) + if (tag_target) + send_docking_command(tag_target, "dock_error") + reset() + if (control_mode == MODE_SERVER && dock_state == STATE_UNDOCKED) + control_mode = MODE_NONE + + +/datum/computer/file/embedded_program/docking/proc/initiate_docking(var/target) + if (dock_state != STATE_UNDOCKED || control_mode == MODE_SERVER) //must be undocked and not serving another request to begin a new docking handshake + return + + tag_target = target + control_mode = MODE_CLIENT + + send_docking_command(tag_target, "request_dock") + +/datum/computer/file/embedded_program/docking/proc/initiate_undocking() + if (dock_state != STATE_DOCKED || control_mode != MODE_CLIENT) //must be docked and must be client to start undocking + return + + dock_state = STATE_UNDOCKING + broadcast_docking_status() + + if (!override_enabled) + prepare_for_undocking() + + send_docking_command(tag_target, "request_undock") + +//tell the docking port to start getting ready for docking - e.g. pressurize +/datum/computer/file/embedded_program/docking/proc/prepare_for_docking() + return + +//are we ready for docking? +/datum/computer/file/embedded_program/docking/proc/ready_for_docking() + return 1 + +//we are docked, open the doors or whatever. +/datum/computer/file/embedded_program/docking/proc/finish_docking() + return + +//tell the docking port to start getting ready for undocking - e.g. close those doors. +/datum/computer/file/embedded_program/docking/proc/prepare_for_undocking() + return + +//we are docked, open the doors or whatever. +/datum/computer/file/embedded_program/docking/proc/finish_undocking() + return + +//are we ready for undocking? +/datum/computer/file/embedded_program/docking/proc/ready_for_undocking() + return 1 + +/datum/computer/file/embedded_program/docking/proc/enable_override() + override_enabled = 1 + +/datum/computer/file/embedded_program/docking/proc/disable_override() + override_enabled = 0 + +/datum/computer/file/embedded_program/docking/proc/reset() + dock_state = STATE_UNDOCKED + broadcast_docking_status() + + control_mode = MODE_NONE + tag_target = null + response_sent = 0 + received_confirm = 0 + +/datum/computer/file/embedded_program/docking/proc/force_undock() + //world << "[id_tag]: forcing undock" + if (tag_target) + send_docking_command(tag_target, "dock_error") + reset() + +/datum/computer/file/embedded_program/docking/proc/docked() + return (dock_state == STATE_DOCKED) + +/datum/computer/file/embedded_program/docking/proc/undocked() + return (dock_state == STATE_UNDOCKED) + +//returns 1 if we are saftely undocked (and the shuttle can leave) +/datum/computer/file/embedded_program/docking/proc/can_launch() + return undocked() + +/datum/computer/file/embedded_program/docking/proc/send_docking_command(var/recipient, var/command) + var/datum/signal/signal = new + signal.data["tag"] = id_tag + signal.data["command"] = command + signal.data["recipient"] = recipient + post_signal(signal) + +/datum/computer/file/embedded_program/docking/proc/broadcast_docking_status() + var/datum/signal/signal = new + signal.data["tag"] = id_tag + signal.data["dock_status"] = get_docking_status() + post_signal(signal) + +//this is mostly for NanoUI +/datum/computer/file/embedded_program/docking/proc/get_docking_status() + switch (dock_state) + if (STATE_UNDOCKED) return "undocked" + if (STATE_DOCKING) return "docking" + if (STATE_UNDOCKING) return "undocking" + if (STATE_DOCKED) return "docked" + + +#undef STATE_UNDOCKED +#undef STATE_DOCKING +#undef STATE_UNDOCKING +#undef STATE_DOCKED + +#undef MODE_NONE +#undef MODE_SERVER +#undef MODE_CLIENT \ No newline at end of file diff --git a/code/game/machinery/embedded_controller/docking_program_multi.dm b/code/game/machinery/embedded_controller/docking_program_multi.dm new file mode 100644 index 00000000..39015917 --- /dev/null +++ b/code/game/machinery/embedded_controller/docking_program_multi.dm @@ -0,0 +1,225 @@ +/* + These programs are for docking controllers that consist of multiple independent airlocks + Each airlock has an airlock controller running the child program, and there is one docking controller running the master program +*/ + +/* + the master program +*/ +/datum/computer/file/embedded_program/docking/multi + var/list/children_tags + var/list/children_ready + var/list/children_override + +/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) + children_ready[child_tag] = 0 + children_override[child_tag] = "disabled" + +/datum/computer/file/embedded_program/docking/multi/proc/clear_children_ready_status() + for (var/child_tag in children_tags) + children_ready[child_tag] = 0 + +/datum/computer/file/embedded_program/docking/multi/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 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") + children_ready[receive_tag] = 1 + if ("ready_for_undocking") + children_ready[receive_tag] = 1 + if ("status_override_enabled") + 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") + +/datum/computer/file/embedded_program/docking/multi/ready_for_docking() + //check children ready status + for (var/child_tag in children_tags) + if (!children_ready[child_tag]) + return 0 + return 1 + +/datum/computer/file/embedded_program/docking/multi/finish_docking() + //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") + +/datum/computer/file/embedded_program/docking/multi/ready_for_undocking() + //check children ready status + for (var/child_tag in children_tags) + if (!children_ready[child_tag]) + return 0 + return 1 + +/datum/computer/file/embedded_program/docking/multi/finish_undocking() + //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() + + + + +/* + the child program + technically should be "slave" but eh... I'm too politically correct. +*/ +/datum/computer/file/embedded_program/airlock/multi_docking + var/master_tag + + var/master_status = "undocked" + var/override_enabled = 0 + var/docking_enabled = 0 + var/docking_mode = 0 //0 = docking, 1 = undocking + var/response_sent = 0 + +/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 + +/datum/computer/file/embedded_program/airlock/multi_docking/receive_user_command(command) + if (command == "toggle_override") + if (override_enabled) + override_enabled = 0 + broadcast_override_status() + else + 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()) + send_signal_to_master("ready_for_docking") + response_sent = 1 + if (1) //undocking + if (ready_for_undocking()) + send_signal_to_master("ready_for_undocking") + response_sent = 1 + +//checks if we are ready for docking +/datum/computer/file/embedded_program/airlock/multi_docking/proc/ready_for_docking() + return done_cycling() + +//checks if we are ready for undocking +/datum/computer/file/embedded_program/airlock/multi_docking/proc/ready_for_undocking() + var/ext_closed = check_exterior_door_secured() + var/int_closed = check_interior_door_secured() + return (ext_closed || int_closed) + +/datum/computer/file/embedded_program/airlock/multi_docking/proc/open_doors() + toggleDoor(memory["interior_status"], tag_interior_door, memory["secure"], "open") + toggleDoor(memory["exterior_status"], tag_exterior_door, memory["secure"], "open") + +/datum/computer/file/embedded_program/airlock/multi_docking/cycleDoors(var/target) + 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 + ..(target) + +/datum/computer/file/embedded_program/airlock/multi_docking/proc/send_signal_to_master(var/command) + var/datum/signal/signal = new + signal.data["tag"] = id_tag + signal.data["command"] = command + signal.data["recipient"] = master_tag + post_signal(signal) + +/datum/computer/file/embedded_program/airlock/multi_docking/proc/broadcast_override_status() + var/datum/signal/signal = new + signal.data["tag"] = id_tag + signal.data["override_status"] = override_enabled? "enabled" : "disabled" + post_signal(signal) diff --git a/code/game/machinery/embedded_controller/embedded_controller_base.dm b/code/game/machinery/embedded_controller/embedded_controller_base.dm index 9a63be44..beb30973 100644 --- a/code/game/machinery/embedded_controller/embedded_controller_base.dm +++ b/code/game/machinery/embedded_controller/embedded_controller_base.dm @@ -1,8 +1,11 @@ /obj/machinery/embedded_controller - var/datum/computer/file/embedded_program/program + var/datum/computer/file/embedded_program/program //the currently executing program name = "Embedded Controller" anchored = 1 + + use_power = 1 + idle_power_usage = 10 var/on = 1 @@ -41,40 +44,17 @@ icon_state = "airlock_control_standby" power_channel = ENVIRON density = 0 - - // Setup parameters only + var/id_tag - 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_secure = 0 + //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 /obj/machinery/embedded_controller/radio/initialize() set_frequency(frequency) - var/datum/computer/file/embedded_program/new_prog = new - - new_prog.id_tag = id_tag - new_prog.tag_exterior_door = tag_exterior_door - new_prog.tag_interior_door = tag_interior_door - new_prog.tag_airpump = tag_airpump - new_prog.tag_chamber_sensor = tag_chamber_sensor - new_prog.tag_exterior_sensor = tag_exterior_sensor - new_prog.tag_interior_sensor = tag_interior_sensor - new_prog.memory["secure"] = tag_secure - - new_prog.master = src - program = new_prog - - spawn(10) - program.signalDoor(tag_exterior_door, "update") //signals connected doors to update their status - program.signalDoor(tag_interior_door, "update") /obj/machinery/embedded_controller/radio/update_icon() if(on && program) @@ -85,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/embedded_program_base.dm b/code/game/machinery/embedded_controller/embedded_program_base.dm new file mode 100644 index 00000000..611fcfc5 --- /dev/null +++ b/code/game/machinery/embedded_controller/embedded_program_base.dm @@ -0,0 +1,27 @@ + +/datum/computer/file/embedded_program + var/list/memory = list() + var/obj/machinery/embedded_controller/master + + var/id_tag + +/datum/computer/file/embedded_program/New(var/obj/machinery/embedded_controller/M) + master = M + if (istype(M, /obj/machinery/embedded_controller/radio)) + var/obj/machinery/embedded_controller/radio/R = M + id_tag = R.id_tag + +/datum/computer/file/embedded_program/proc/receive_user_command(command) + return + +/datum/computer/file/embedded_program/proc/receive_signal(datum/signal/signal, receive_method, receive_param) + return + +/datum/computer/file/embedded_program/proc/process() + return + +/datum/computer/file/embedded_program/proc/post_signal(datum/signal/signal, comm_line) + if(master) + master.post_signal(signal, comm_line) + else + del(signal) diff --git a/code/game/machinery/embedded_controller/simple_docking_controller.dm b/code/game/machinery/embedded_controller/simple_docking_controller.dm new file mode 100644 index 00000000..f1001ada --- /dev/null +++ b/code/game/machinery/embedded_controller/simple_docking_controller.dm @@ -0,0 +1,164 @@ +//a docking port that uses a single door +/obj/machinery/embedded_controller/radio/simple_docking_controller + name = "docking hatch controller" + var/tag_door + var/datum/computer/file/embedded_program/docking/simple/docking_program + +/obj/machinery/embedded_controller/radio/simple_docking_controller/initialize() + ..() + 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, var/force_open = 1) + var/data[0] + + data = list( + "docking_status" = docking_program.get_docking_status(), + "override_enabled" = docking_program.override_enabled, + "door_state" = docking_program.memory["door_status"]["state"], + "door_lock" = docking_program.memory["door_status"]["lock"], + ) + + 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) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +/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"]) + + return 0 + + +//A docking controller program for a simple door based docking port +/datum/computer/file/embedded_program/docking/simple + var/tag_door + +/datum/computer/file/embedded_program/docking/simple/New(var/obj/machinery/embedded_controller/M) + ..(M) + memory["door_status"] = list(state = "closed", lock = "locked") //assume closed and locked in case the doors dont report in + + 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") + if (override_enabled) + if(memory["door_status"]["state"] == "open") + close_door() + else + open_door() + if("toggle_override") + if (override_enabled) + disable_override() + else + enable_override() + + +/datum/computer/file/embedded_program/docking/simple/proc/signal_door(var/command) + var/datum/signal/signal = new + signal.data["tag"] = tag_door + signal.data["command"] = command + post_signal(signal) + +///datum/computer/file/embedded_program/docking/simple/proc/signal_mech_sensor(var/command) +// signal_door(command) +// return + +/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") + +/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") + +//tell the docking port to start getting ready for docking - e.g. pressurize +/datum/computer/file/embedded_program/docking/simple/prepare_for_docking() + return //don't need to do anything + +//are we ready for docking? +/datum/computer/file/embedded_program/docking/simple/ready_for_docking() + return 1 //don't need to do anything + +//we are docked, open the doors or whatever. +/datum/computer/file/embedded_program/docking/simple/finish_docking() + open_door() + +//tell the docking port to start getting ready for undocking - e.g. close those doors. +/datum/computer/file/embedded_program/docking/simple/prepare_for_undocking() + close_door() + +//are we ready for undocking? +/datum/computer/file/embedded_program/docking/simple/ready_for_undocking() + return (memory["door_status"]["state"] == "closed" && memory["door_status"]["lock"] == "locked") + +/*** DEBUG VERBS *** + +/obj/machinery/embedded_controller/radio/simple_docking_controller/verb/view_state() + set category = "Debug" + set src in view(1) + src.program:print_state() + +/obj/machinery/embedded_controller/radio/simple_docking_controller/verb/spoof_signal(var/command as text, var/sender as text) + set category = "Debug" + set src in view(1) + var/datum/signal/signal = new + signal.data["tag"] = sender + signal.data["command"] = command + signal.data["recipient"] = id_tag + + src.program:receive_signal(signal) + +/obj/machinery/embedded_controller/radio/simple_docking_controller/verb/debug_init_dock(var/target as text) + set category = "Debug" + set src in view(1) + src.program:initiate_docking(target) + +/obj/machinery/embedded_controller/radio/simple_docking_controller/verb/debug_init_undock() + set category = "Debug" + set src in view(1) + src.program:initiate_undocking() + +*/ \ No newline at end of file diff --git a/code/game/machinery/flasher.dm b/code/game/machinery/flasher.dm index baf2f1c1..2bf4f0df 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) @@ -68,7 +67,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) @@ -85,7 +84,7 @@ O.Weaken(strength) if (istype(O, /mob/living/carbon/human)) var/mob/living/carbon/human/H = O - var/datum/organ/internal/eyes/E = H.internal_organs["eyes"] + var/datum/organ/internal/eyes/E = H.internal_organs_by_name["eyes"] if ((E.damage > E.min_bruised_damage && prob(E.damage + 50))) flick("e_flash", O:flash) E.damage += rand(1, 5) diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm index 79ec51fd..3907bbbc 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/hologram.dm @@ -101,7 +101,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ hologram.SetLuminosity(2) //hologram lighting SetLuminosity(2) //pad lighting icon_state = "holopad1" - A.current = src + A.holo = src master = A//AI is the master. use_power = 2//Active power usage. return 1 @@ -109,8 +109,8 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ /obj/machinery/hologram/holopad/proc/clear_holo() // hologram.SetLuminosity(0)//Clear lighting. //handled by the lighting controller when its ower is deleted del(hologram)//Get rid of hologram. - if(master.current == src) - master.current = null + if(master.holo == src) + master.holo = null master = null//Null the master, since no-one is using it now. SetLuminosity(0) //pad lighting (hologram lighting will be handled automatically since its owner was deleted) icon_state = "holopad0" @@ -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 d98fc361..6ad157d1 100644 --- 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/gibber.dm b/code/game/machinery/kitchen/gibber.dm index 895f82dd..8df3668d 100644 --- a/code/game/machinery/kitchen/gibber.dm +++ b/code/game/machinery/kitchen/gibber.dm @@ -226,7 +226,7 @@ var/obj/item/meatslab = allmeat[i] var/turf/Tx = locate(src.x - i, src.y, src.z) meatslab.loc = src.loc - meatslab.throw_at(Tx,i,3) + meatslab.throw_at(Tx,i,3,src) if (!Tx.density) new /obj/effect/decal/cleanable/blood/gibs(Tx,i) src.operating = 0 diff --git a/code/game/machinery/kitchen/smartfridge.dm b/code/game/machinery/kitchen/smartfridge.dm index ad15d889..31441da6 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) @@ -403,7 +402,7 @@ if(!throw_item) return 0 spawn(0) - throw_item.throw_at(target,16,3) + throw_item.throw_at(target,16,3,src) src.visible_message("\red [src] launches [throw_item.name] at [target.name]!") return 1 diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm index 5b7a47e7..731cb229 100644 --- a/code/game/machinery/machinery.dm +++ b/code/game/machinery/machinery.dm @@ -106,12 +106,10 @@ Class Procs: var/active_power_usage = 0 var/power_channel = EQUIP //EQUIP,ENVIRON or LIGHT - var/list/component_parts = null //list of all the parts used to build it, if made from certain kinds of frames. + var/list/component_parts = list() //list of all the parts used to build it, if made from certain kinds of frames. var/uid var/manual = 0 - var/interact_offline = 0 var/global/gl_uid = 1 - var/moveable = 0 /obj/machinery/New() ..() @@ -159,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 @@ -168,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(!interact_offline && stat & (NOPOWER|BROKEN)) + if(inoperable()) return 1 if(usr.restrained() || usr.lying || usr.stat) return 1 @@ -212,7 +231,7 @@ Class Procs: return src.attack_hand(user) /obj/machinery/attack_hand(mob/user as mob) - if(!interact_offline && stat & (NOPOWER|BROKEN|MAINT)) + if(inoperable(MAINT)) return 1 if(user.lying || user.stat) return 1 @@ -261,27 +280,9 @@ Class Procs: state(text, "blue") playsound(src.loc, 'sound/machines/ping.ogg', 50, 0) -/obj/machinery/attackby(obj/item/W, mob/user) - if(istype(W, /obj/item/weapon/wrench)) - if(moveable == 1) - switch(anchored) - if(0) - playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1) - user.visible_message("[user.name] secures [src.name] to the floor.", "You secure [src.name] to the floor.", "You hear a ratchet") - spawn(10) - anchored = 1 - if(1) - playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1) - user.visible_message("[user.name] unsecures [src.name] reinforcing bolts from the floor.", "You unsecure [src.name] from the floor.", "You hear a ratchet") - spawn(10) - anchored = 0 - return - else - user << "The item is secured to the floor too firmly." - /obj/machinery/proc/shock(mob/user, prb) -// if(inoperable()) -// return 0 + if(inoperable()) + return 0 if(!prob(prb)) return 0 var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread @@ -292,3 +293,14 @@ Class Procs: else return 0 +/obj/machinery/proc/dismantle() + playsound(loc, 'sound/items/Crowbar.ogg', 50, 1) + var/obj/machinery/constructable_frame/machine_frame/M = new /obj/machinery/constructable_frame/machine_frame(loc) + M.state = 2 + M.icon_state = "box_1" + for(var/obj/I in component_parts) + if(I.reliability != 100 && crit_fail) + I.crit_fail = 1 + I.loc = loc + del(src) + return 1 diff --git a/code/game/machinery/newscaster.dm b/code/game/machinery/newscaster.dm index b1783b17..114d24e5 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 08219daf..fde6cfb5 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 1ca95c1b..be645268 100644 --- a/code/game/machinery/pipe/construction.dm +++ b/code/game/machinery/pipe/construction.dm @@ -50,7 +50,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 @@ -72,6 +72,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)) @@ -88,8 +90,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)) @@ -131,8 +131,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", \ @@ -314,12 +314,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 @@ -339,7 +340,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 @@ -372,7 +373,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() @@ -380,7 +381,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) @@ -395,7 +396,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() @@ -403,7 +404,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) @@ -428,7 +429,7 @@ Buildable meters //P.level = T.intact ? 2 : 1 P.initialize() if (!P) - usr << "There's nothing to connect this junction 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) @@ -669,7 +670,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) @@ -699,7 +700,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 @@ -715,7 +716,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 0db01901..b573d07f 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
@@ -126,7 +126,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/portable_turret.dm b/code/game/machinery/portable_turret.dm index 4f3cd656..87962906 100644 --- a/code/game/machinery/portable_turret.dm +++ b/code/game/machinery/portable_turret.dm @@ -270,10 +270,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 @@ -283,11 +285,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 @@ -401,8 +401,8 @@ Status: []
"}, check_records=pick(0,1) criminals=pick(0,1) auth_weapons=pick(0,1) - stun_all=pick(0,1) // stun_all is a pretty big deal, so it's least likely to get turned on //but think of the fun - if(prob(50)) emagged=1 + stun_all=pick(0,0,0,0,1) // stun_all is a pretty big deal, so it's least likely to get turned on + if(prob(5)) emagged=1 on=0 sleep(rand(60,600)) if(!on) diff --git a/code/game/machinery/recharger.dm b/code/game/machinery/recharger.dm index faef2948..5f757d72 100644 --- a/code/game/machinery/recharger.dm +++ b/code/game/machinery/recharger.dm @@ -7,51 +7,62 @@ obj/machinery/recharger anchored = 1 use_power = 1 idle_power_usage = 4 - active_power_usage = 250 - var/obj/item/weapon/charging = null + 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/weapon/cell) + var/icon_state_charged = "recharger2" + var/icon_state_charging = "recharger1" + 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)) - if(charging) - return + 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/rifle/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 if (istype(G, /obj/item/weapon/gun/energy/staff)) return +/* if(istype(G, /obj/item/device/laptop)) + var/obj/item/device/laptop/L = G + if(!L.stored_computer.battery) + user << "There's no battery in it!" + return*/ 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." playsound(loc, 'sound/items/Ratchet.ogg', 75, 1) obj/machinery/recharger/attack_hand(mob/user as mob) + if(istype(user,/mob/living/silicon)) + return + add_fingerprint(user) 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) @@ -59,26 +70,61 @@ 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) - icon_state = "recharger1" - use_power(250) + if(!E.power_supply.fully_charged()) + icon_state = icon_state_charging + E.power_supply.give(active_power_usage*CELLRATE) + update_use_power(2) else - icon_state = "recharger2" + 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.charges < initial(B.charges)) - B.charges++ - icon_state = "recharger1" - use_power(150) + if(B.bcell) + if(!B.bcell.fully_charged()) + icon_state = icon_state_charging + B.bcell.give(active_power_usage*CELLRATE) + update_use_power(2) + else + icon_state = icon_state_charged + update_use_power(1) else - icon_state = "recharger2" + 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.fully_charged()) + icon_state = icon_state_charging + 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) if(stat & (NOPOWER|BROKEN) || !anchored) @@ -92,45 +138,23 @@ obj/machinery/recharger/emp_act(severity) else if(istype(charging, /obj/item/weapon/melee/baton)) var/obj/item/weapon/melee/baton/B = charging - B.charges = 0 + if(B.bcell) + B.bcell.charge = 0 ..(severity) obj/machinery/recharger/update_icon() //we have an update_icon() in addition to the stuff in process to make it feel a tiny bit snappier. if(charging) - icon_state = "recharger1" + icon_state = icon_state_charging else - icon_state = "recharger0" + icon_state = icon_state_idle + obj/machinery/recharger/wallcharger name = "wall recharger" icon = 'icons/obj/stationobjs.dmi' icon_state = "wrecharger0" - -obj/machinery/recharger/wallcharger/process() - if(stat & (NOPOWER|BROKEN) || !anchored) - return - - if(charging) - 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) - icon_state = "wrecharger1" - use_power(250) - else - icon_state = "wrecharger2" - return - if(istype(charging, /obj/item/weapon/melee/baton)) - var/obj/item/weapon/melee/baton/B = charging - if(B.charges < initial(B.charges)) - B.charges++ - icon_state = "wrecharger1" - use_power(150) - else - icon_state = "wrecharger2" - -obj/machinery/recharger/wallcharger/update_icon() - if(charging) - icon_state = "wrecharger1" - else - icon_state = "wrecharger0" \ No newline at end of file + 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 e225751a..d6d161d5 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 370e2f38..af74c7c9 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 b921d8d9..7422ab0c 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)) @@ -21,7 +21,7 @@ if(!O:amount) return while(metal_amount < 150000 && O:amount) - src.metal_amount += O:m_amt /*O:height * O:width * O:length * 100000.0*/ + src.metal_amount += O.matter["metal"] /*O:height * O:width * O:length * 100000.0*/ O:amount-- count++ @@ -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/spaceheater.dm b/code/game/machinery/spaceheater.dm index ed8e83cc..f6a1455e 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]K ([set_temperature-T0C]°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/status_display.dm b/code/game/machinery/status_display.dm index 3d7ab712..055f884d 100644 --- a/code/game/machinery/status_display.dm +++ b/code/game/machinery/status_display.dm @@ -74,13 +74,19 @@ if(0) //blank remove_display() if(1) //emergency shuttle timer - if(emergency_shuttle.online) - var/line1 - var/line2 = get_shuttle_timer() - if(emergency_shuttle.location == 1) - line1 = "-ETD-" + if(emergency_shuttle.waiting_to_leave()) + var/line1 = "-ETD-" + var/line2 + if (emergency_shuttle.shuttle.is_launching()) + line2 = "Launch" else - line1 = "-ETA-" + line2 = get_shuttle_timer_departure() + if(length(line2) > CHARS_PER_LINE) + line2 = "Error!" + update_display(line1, line2) + else if(emergency_shuttle.has_eta()) + var/line1 = "-ETA-" + var/line2 = get_shuttle_timer_arrival() if(length(line2) > CHARS_PER_LINE) line2 = "Error!" update_display(line1, line2) @@ -110,13 +116,22 @@ update_display(line1, line2) if(4) // supply shuttle timer var/line1 = "SUPPLY" - var/line2 - if(supply_shuttle.moving) + var/line2 = "" + + var/datum/shuttle/ferry/supply/shuttle = supply_controller.shuttle + if (!shuttle) + line2 = "Error" + else if(shuttle.has_arrive_time()) line2 = get_supply_shuttle_timer() if(lentext(line2) > CHARS_PER_LINE) line2 = "Error" + else if (shuttle.is_launching()) + if (shuttle.at_station()) + line2 = "Launch" + else + line2 = "ETA" else - if(supply_shuttle.at_station) + if(shuttle.at_station()) line2 = "Docked" else line1 = "" @@ -155,15 +170,25 @@ if(maptext != new_text) maptext = new_text - proc/get_shuttle_timer() - var/timeleft = emergency_shuttle.timeleft() - if(timeleft) - return "[add_zero(num2text((timeleft / 60) % 60),2)]:[add_zero(num2text(timeleft % 60), 2)]" - return "" + proc/get_shuttle_timer_arrival() + var/timeleft = emergency_shuttle.estimate_arrival_time() + if(timeleft < 0) + return "" + return "[add_zero(num2text((timeleft / 60) % 60),2)]:[add_zero(num2text(timeleft % 60), 2)]" + + proc/get_shuttle_timer_departure() + var/timeleft = emergency_shuttle.estimate_launch_time() + if(timeleft < 0) + return "" + return "[add_zero(num2text((timeleft / 60) % 60),2)]:[add_zero(num2text(timeleft % 60), 2)]" proc/get_supply_shuttle_timer() - if(supply_shuttle.moving) - var/timeleft = round((supply_shuttle.eta_timeofday - world.timeofday) / 10,1) + var/datum/shuttle/ferry/supply/shuttle = supply_controller.shuttle + if (!shuttle) + return "Error" + + if(shuttle.has_arrive_time()) + var/timeleft = round((shuttle.arrive_time - world.time) / 10,1) if(timeleft < 0) return "Late" return "[add_zero(num2text((timeleft / 60) % 60),2)]:[add_zero(num2text(timeleft % 60), 2)]" @@ -249,6 +274,12 @@ set_picture("ai_confused") if("Sad") set_picture("ai_sad") + if("Surprised") + set_picture("ai_surprised") + if("Upset") + set_picture("ai_upset") + if("Angry") + set_picture("ai_angry") if("BSOD") set_picture("ai_bsod") if("Blank") diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm index 18cd1151..9761ed7c 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() diff --git a/code/game/machinery/syndicatebeacon.dm b/code/game/machinery/syndicatebeacon.dm index 82805efc..137f587d 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 830ff05a..35a676d2 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/logbrowser.dm b/code/game/machinery/telecomms/logbrowser.dm index 968b6cf1..82a80822 100644 --- a/code/game/machinery/telecomms/logbrowser.dm +++ b/code/game/machinery/telecomms/logbrowser.dm @@ -73,7 +73,9 @@ var/mob/M = new mobtype if(ishuman(M) || isbrain(M)) - race = "Human" + var/mob/living/carbon/human/H = M + race = "[H.species.name]" + else if(ismonkey(M)) race = "Monkey" diff --git a/code/game/machinery/telecomms/machine_interactions.dm b/code/game/machinery/telecomms/machine_interactions.dm index da2af57a..39ce187f 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 e0f725a5..3fd85970 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 ff955640..fba4f246 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/telecomms/traffic_control.dm b/code/game/machinery/telecomms/traffic_control.dm index 5c5a038b..226c4722 100644 --- a/code/game/machinery/telecomms/traffic_control.dm +++ b/code/game/machinery/telecomms/traffic_control.dm @@ -15,7 +15,7 @@ var/list/viewingcode = list() var/obj/machinery/telecomms/server/SelectedServer - var/network = "tcommsat" // the network to probe + var/network = "NULL" // the network to probe var/temp = "" // temporary feedback messages var/storedcode = "" // code stored diff --git a/code/game/machinery/teleporter.dm b/code/game/machinery/teleporter.dm index 916c7450..b247f179 100644 --- a/code/game/machinery/teleporter.dm +++ b/code/game/machinery/teleporter.dm @@ -3,6 +3,7 @@ desc = "Used to control a linked teleportation Hub and Station." icon_state = "teleport" circuit = "/obj/item/weapon/circuitboard/teleporter" + dir = 4 var/obj/item/locked = null var/id = null var/one_time_use = 0 //Used for one-time-use teleport cards (such as clown planet coordinates.) @@ -11,8 +12,24 @@ /obj/machinery/computer/teleporter/New() src.id = "[rand(1000, 9999)]" ..() + underlays.Cut() + underlays += image('icons/obj/stationobjs.dmi', icon_state = "telecomp-wires") return +/obj/machinery/computer/teleporter/initialize() + ..() + var/obj/machinery/teleport/station/station = locate(/obj/machinery/teleport/station, get_step(src, dir)) + var/obj/machinery/teleport/hub/hub + if(station) + hub = locate(/obj/machinery/teleport/hub, get_step(station, dir)) + + if(istype(station)) + station.com = hub + station.dir = dir + + if(istype(hub)) + hub.com = src + hub.dir = dir /obj/machinery/computer/teleporter/attackby(I as obj, mob/living/user as mob) if(istype(I, /obj/item/weapon/card/data/)) @@ -143,10 +160,17 @@ name = "teleporter hub" desc = "It's the hub of a teleporting machine." icon_state = "tele0" + dir = 4 var/accurate = 0 use_power = 1 idle_power_usage = 10 active_power_usage = 2000 + var/obj/machinery/computer/teleporter/com + +/obj/machinery/teleport/hub/New() + ..() + underlays.Cut() + underlays += image('icons/obj/stationobjs.dmi', icon_state = "tele-wires") /obj/machinery/teleport/hub/Bumped(M as mob|obj) spawn() @@ -156,8 +180,6 @@ return /obj/machinery/teleport/hub/proc/teleport(atom/movable/M as mob|obj) - var/atom/l = src.loc - var/obj/machinery/computer/teleporter/com = locate(/obj/machinery/computer/teleporter, locate(l.x - 2, l.y, l.z)) if (!com) return if (!com.locked) @@ -177,6 +199,8 @@ var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread s.set_up(5, 1, src) s.start() + accurate = 1 + spawn(3000) accurate = 0 //Accurate teleporting for 5 minutes for(var/mob/B in hearers(src, null)) B.show_message("\blue Test fire completed.") return @@ -271,11 +295,18 @@ name = "station" desc = "It's the station thingy of a teleport thingy." //seriously, wtf. icon_state = "controller" + dir = 4 var/active = 0 var/engaged = 0 use_power = 1 idle_power_usage = 10 active_power_usage = 2000 + var/obj/machinery/teleport/hub/com + +/obj/machinery/teleport/station/New() + ..() + overlays.Cut() + overlays += image('icons/obj/stationobjs.dmi', icon_state = "controller-wires") /obj/machinery/teleport/station/attackby(var/obj/item/weapon/W) src.attack_hand() @@ -296,8 +327,6 @@ if(stat & (BROKEN|NOPOWER)) return - var/atom/l = src.loc - var/atom/com = locate(/obj/machinery/teleport/hub, locate(l.x + 1, l.y, l.z)) if (com) com.icon_state = "tele1" use_power(5000) @@ -311,10 +340,9 @@ if(stat & (BROKEN|NOPOWER)) return - var/atom/l = src.loc - var/atom/com = locate(/obj/machinery/teleport/hub, locate(l.x + 1, l.y, l.z)) if (com) com.icon_state = "tele0" + com.accurate = 0 for(var/mob/O in hearers(src, null)) O.show_message("\blue Teleporter disengaged!", 2) src.add_fingerprint(usr) @@ -329,8 +357,6 @@ if(stat & (BROKEN|NOPOWER) || !istype(usr,/mob/living)) return - var/atom/l = src.loc - var/obj/machinery/teleport/hub/com = locate(/obj/machinery/teleport/hub, locate(l.x + 1, l.y, l.z)) if (com && !active) active = 1 for(var/mob/O in hearers(src, null)) @@ -348,7 +374,7 @@ ..() if(stat & NOPOWER) icon_state = "controller-p" - var/obj/machinery/teleport/hub/com = locate(/obj/machinery/teleport/hub, locate(x + 1, y, z)) + if(com) com.icon_state = "tele0" else diff --git a/code/game/machinery/turrets.dm b/code/game/machinery/turrets.dm index c9334924..5a93329d 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 381cc702..774500ea 100644 --- a/code/game/machinery/vending.dm +++ b/code/game/machinery/vending.dm @@ -1,9 +1,14 @@ +#define CAT_NORMAL 0 +#define CAT_HIDDEN 1 +#define CAT_COIN 2 + /datum/data/vending_product var/product_name = "generic" var/product_path = null var/amount = 0 var/price = 0 var/display_color = "blue" + var/category = CAT_NORMAL @@ -15,8 +20,13 @@ layer = 2.9 anchored = 1 density = 1 - moveable = 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. @@ -105,6 +115,10 @@ return /obj/machinery/vending/proc/build_inventory(var/list/productlist,hidden=0,req_coin=0) + + if(delay_product_spawn) + sleep(15) //Make ABSOLUTELY SURE the seed datum is properly populated. + for(var/typepath in productlist) var/amount = productlist[typepath] var/price = prices[typepath] @@ -112,18 +126,27 @@ 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 R.display_color = pick("red","blue","green") if(hidden) + R.category=CAT_HIDDEN hidden_records += R else if(req_coin) + R.category=CAT_COIN coin_records += R else + R.category=CAT_NORMAL product_records += R + + if(delay_product_spawn) + sleep(5) //sleep(1) did not seem to cut it, so here we are. + + R.product_name = temp.name + // world << "Added: [R.product_name]] - [R.amount] - [R.product_path]" return @@ -159,6 +182,20 @@ ewallet = W user << "\blue You insert the [W] into the [src]" + else if(istype(W, /obj/item/weapon/wrench)) + + if(do_after(user, 20)) + if(!src) return + playsound(src.loc, 'sound/items/Ratchet.ogg', 100, 1) + switch (anchored) + if (0) + anchored = 1 + user.visible_message("[user] tightens the bolts securing \the [src] to the floor.", "You tighten the bolts securing \the [src] to the floor.") + if (1) + user.visible_message("[user] unfastens the bolts securing \the [src] to the floor.", "You unfasten the bolts securing \the [src] to the floor.") + anchored = 0 + return + else if(src.panel_open) for(var/datum/data/vending_product/R in product_records) @@ -174,53 +211,63 @@ if (istype(I, /obj/item/weapon/card/id)) var/obj/item/weapon/card/id/C = I visible_message("[usr] swipes a card through [src].") - if(check_accounts) - if(vendor_account) - var/attempt_pin = input("Enter pin code", "Vendor transaction") as num - var/datum/money_account/D = attempt_account_access(C.associated_account_number, attempt_pin, 2) - if(D) - var/transaction_amount = currently_vending.price - if(transaction_amount <= D.money) - - //transfer the money - D.money -= transaction_amount - vendor_account.money += transaction_amount - - //create entries in the two account transaction logs - var/datum/transaction/T = new() - T.target_name = "[vendor_account.owner_name] (via [src.name])" - T.purpose = "Purchase of [currently_vending.product_name]" - if(transaction_amount > 0) - T.amount = "([transaction_amount])" - else - T.amount = "[transaction_amount]" - T.source_terminal = src.name - T.date = current_date_string - T.time = worldtime2text() - D.transaction_log.Add(T) - // - T = new() - T.target_name = D.owner_name - T.purpose = "Purchase of [currently_vending.product_name]" - T.amount = "[transaction_amount]" - T.source_terminal = src.name - T.date = current_date_string - T.time = worldtime2text() - vendor_account.transaction_log.Add(T) - - // Vend the item - src.vend(src.currently_vending, usr) - currently_vending = null + var/datum/money_account/CH = get_account(C.associated_account_number) + if (CH) // Only proceed if card contains proper account number. + if(!CH.suspended) + if(CH.security_level != 0) //If card requires pin authentication (ie seclevel 1 or 2) + if(vendor_account) + var/attempt_pin = input("Enter pin code", "Vendor transaction") as num + var/datum/money_account/D = attempt_account_access(C.associated_account_number, attempt_pin, 2) + transfer_and_vend(D) + else + usr << "\icon[src]Unable to access account. Check security settings and try again." else - usr << "\icon[src]You don't have that much money!" + //Just Vend it. + transfer_and_vend(CH) else - usr << "\icon[src]Unable to access account. Check security settings and try again." + usr << "\icon[src]Connected account has been suspended." else - //Just Vend it. + usr << "\icon[src]Error: Unable to access your account. Please contact technical support if problem persists." + +/obj/machinery/vending/proc/transfer_and_vend(var/datum/money_account/acc) + if(acc) + var/transaction_amount = currently_vending.price + if(transaction_amount <= acc.money) + + //transfer the money + acc.money -= transaction_amount + vendor_account.money += transaction_amount + + //create entries in the two account transaction logs + var/datum/transaction/T = new() + T.target_name = "[vendor_account.owner_name] (via [src.name])" + T.purpose = "Purchase of [currently_vending.product_name]" + if(transaction_amount > 0) + T.amount = "([transaction_amount])" + else + T.amount = "[transaction_amount]" + T.source_terminal = src.name + T.date = current_date_string + T.time = worldtime2text() + acc.transaction_log.Add(T) + // + T = new() + T.target_name = acc.owner_name + T.purpose = "Purchase of [currently_vending.product_name]" + T.amount = "[transaction_amount]" + T.source_terminal = src.name + T.date = current_date_string + T.time = worldtime2text() + vendor_account.transaction_log.Add(T) + + // Vend the item src.vend(src.currently_vending, usr) currently_vending = null + else + usr << "\icon[src]You don't have that much money!" else - usr << "\icon[src]Unable to access vendor account. Please record the machine ID and call CentComm Support." + usr << "\icon[src]Error: Unable to access your account. Please contact technical support if problem persists." + /obj/machinery/vending/attack_paw(mob/user as mob) return attack_hand(user) @@ -228,6 +275,31 @@ /obj/machinery/vending/attack_ai(mob/user as mob) return attack_hand(user) +/obj/machinery/vending/proc/GetProductIndex(var/datum/data/vending_product/P) + var/list/plist + switch(P.category) + if(CAT_NORMAL) + plist=product_records + if(CAT_HIDDEN) + plist=hidden_records + if(CAT_COIN) + plist=coin_records + else + warning("UNKNOWN CATEGORY [P.category] IN TYPE [P.product_path] INSIDE [type]!") + return plist.Find(P) + +/obj/machinery/vending/proc/GetProductByID(var/pid, var/category) + switch(category) + if(CAT_NORMAL) + return product_records[pid] + if(CAT_HIDDEN) + return hidden_records[pid] + if(CAT_COIN) + return coin_records[pid] + else + warning("UNKNOWN PRODUCT: PID: [pid], CAT: [category] INSIDE [type]!") + return null + /obj/machinery/vending/attack_hand(mob/user as mob) if(stat & (BROKEN|NOPOWER)) return @@ -259,13 +331,13 @@ if (src.product_records.len == 0) dat += "No product loaded!" else - var/list/display_records = src.product_records + var/list/display_records = list() + display_records += src.product_records + if(src.extended_inventory) - display_records = src.product_records + src.hidden_records + display_records += src.hidden_records if(src.coin) - display_records = src.product_records + src.coin_records - if(src.coin && src.extended_inventory) - display_records = src.product_records + src.hidden_records + src.coin_records + display_records += src.coin_records for (var/datum/data/vending_product/R in display_records) dat += "[R.product_name]:" @@ -273,7 +345,8 @@ if(R.price) dat += " (Price: [R.price])" if (R.amount > 0) - dat += " (Vend)" + var/idx=GetProductIndex(R) + dat += " (Vend)" else dat += " SOLD OUT" dat += "
" @@ -357,7 +430,10 @@ flick(src.icon_deny,src) return - var/datum/data/vending_product/R = locate(href_list["vend"]) + var/idx=text2num(href_list["vend"]) + var/cat=text2num(href_list["cat"]) + + var/datum/data/vending_product/R = GetProductByID(idx,cat) if (!R || !istype(R) || !R.product_path || R.amount <= 0) return @@ -440,7 +516,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) @@ -490,16 +566,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() @@ -539,7 +614,7 @@ if (!throw_item) return 0 spawn(0) - throw_item.throw_at(target, 16, 3) + throw_item.throw_at(target, 16, 3, src) src.visible_message("\red [src] launches [throw_item.name] at [target.name]!") return 1 @@ -641,6 +716,7 @@ /obj/item/weapon/reagent_containers/food/drinks/bottle/cremeyvette = 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" @@ -658,6 +734,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) @@ -696,6 +774,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 @@ -729,13 +808,13 @@ icon_deny = "med-deny" product_ads = "Go save some lives!;The best stuff for your medbay.;Only the finest tools.;Natural chemicals!;This stuff saves lives.;Don't you want some?;Ping!" req_access_txt = "5" - moveable = 0 products = list(/obj/item/weapon/reagent_containers/glass/bottle/antitoxin = 4,/obj/item/weapon/reagent_containers/glass/bottle/inaprovaline = 4, /obj/item/weapon/reagent_containers/glass/bottle/stoxin = 4,/obj/item/weapon/reagent_containers/glass/bottle/toxin = 4, /obj/item/weapon/reagent_containers/syringe/antiviral = 4,/obj/item/weapon/reagent_containers/syringe = 12, /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 @@ -753,7 +832,6 @@ icon_state = "wallmed" icon_deny = "wallmed-deny" req_access_txt = "5" - moveable = 0 density = 0 //It is wall-mounted, and thus, not dense. --Superxpdude products = list(/obj/item/stack/medical/bruise_pack = 2,/obj/item/stack/medical/ointment = 2,/obj/item/weapon/reagent_containers/hypospray/autoinjector = 4,/obj/item/device/healthanalyzer = 1) contraband = list(/obj/item/weapon/reagent_containers/syringe/antitoxin = 4,/obj/item/weapon/reagent_containers/syringe/antiviral = 4,/obj/item/weapon/reagent_containers/pill/tox = 1) @@ -764,7 +842,6 @@ icon_state = "wallmed" icon_deny = "wallmed-deny" req_access_txt = "5" - moveable = 0 density = 0 //It is wall-mounted, and thus, not dense. --Superxpdude products = list(/obj/item/weapon/reagent_containers/hypospray/autoinjector = 5,/obj/item/weapon/reagent_containers/syringe/antitoxin = 3,/obj/item/stack/medical/bruise_pack = 3, /obj/item/stack/medical/ointment =3,/obj/item/device/healthanalyzer = 3) @@ -777,7 +854,6 @@ icon_state = "sec" icon_deny = "sec-deny" req_access_txt = "1" - moveable = 0 products = list(/obj/item/weapon/handcuffs = 8,/obj/item/weapon/grenade/flashbang = 4,/obj/item/device/flash = 5, /obj/item/weapon/reagent_containers/food/snacks/donut/normal = 12,/obj/item/weapon/storage/box/evidence = 6) contraband = list(/obj/item/clothing/glasses/sunglasses = 2,/obj/item/weapon/storage/fancy/donut_box = 2) @@ -792,6 +868,7 @@ products = list(/obj/item/nutrient/ez = 35,/obj/item/nutrient/l4z = 25,/obj/item/nutrient/rh = 15,/obj/item/weapon/pestspray = 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" @@ -799,6 +876,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, @@ -817,7 +896,6 @@ product_slogans = "Sling spells the proper way with MagiVend!;Be your own Houdini! Use MagiVend!" vend_delay = 15 vend_reply = "Have an enchanted evening!" - moveable = 0 product_ads = "FJKLFJSD;AJKFLBJAKL;1234 LOONIES LOL!;>MFW;Kill them fuckers!;GET DAT FUKKEN DISK;HONK!;EI NATH;Destroy the station!;Admin conspiracies since forever!;Space-time bending hardware!" products = list(/obj/item/clothing/head/wizard = 1,/obj/item/clothing/suit/wizrobe = 1,/obj/item/clothing/head/wizard/red = 1,/obj/item/clothing/suit/wizrobe/red = 1,/obj/item/clothing/shoes/sandal = 1,/obj/item/weapon/staff = 2) contraband = list(/obj/item/weapon/reagent_containers/glass/bottle/wizarditis = 1) //No one can get to the machine to hack it anyways; for the lulz - Microwave @@ -837,13 +915,13 @@ 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" desc = "Tools for tools." icon_state = "tool" icon_deny = "tool-deny" - moveable = 0 //req_access_txt = "12" //Maintenance access products = list(/obj/item/weapon/cable_coil/random = 10,/obj/item/weapon/crowbar = 5,/obj/item/weapon/weldingtool = 3,/obj/item/weapon/wirecutters = 5, /obj/item/weapon/wrench = 5,/obj/item/device/analyzer = 5,/obj/item/device/t_scanner = 5,/obj/item/weapon/screwdriver = 5) @@ -855,7 +933,6 @@ desc = "Spare tool vending. What? Did you expect some witty description?" icon_state = "engivend" icon_deny = "engivend-deny" - moveable = 0 req_access_txt = "11" //Engineering Equipment access products = list(/obj/item/clothing/glasses/meson = 2,/obj/item/device/multitool = 4,/obj/item/weapon/airlock_electronics = 10,/obj/item/weapon/module/power_control = 10,/obj/item/weapon/airalarm_electronics = 10,/obj/item/weapon/cell/high = 10) contraband = list(/obj/item/weapon/cell/potato = 3) @@ -868,7 +945,6 @@ icon_state = "engi" icon_deny = "engi-deny" req_access_txt = "11" - moveable = 0 products = list(/obj/item/clothing/under/rank/chief_engineer = 4,/obj/item/clothing/under/rank/engineer = 4,/obj/item/clothing/shoes/orange = 4,/obj/item/clothing/head/hardhat = 4, /obj/item/weapon/storage/belt/utility = 4,/obj/item/clothing/glasses/meson = 4,/obj/item/clothing/gloves/yellow = 4, /obj/item/weapon/screwdriver = 12, /obj/item/weapon/crowbar = 12,/obj/item/weapon/wirecutters = 12,/obj/item/device/multitool = 12,/obj/item/weapon/wrench = 12,/obj/item/device/t_scanner = 12, @@ -886,7 +962,6 @@ icon_state = "robotics" icon_deny = "robotics-deny" req_access_txt = "29" - moveable = 0 products = list(/obj/item/clothing/suit/storage/labcoat = 4,/obj/item/clothing/under/rank/roboticist = 4,/obj/item/weapon/cable_coil = 4,/obj/item/device/flash = 4, /obj/item/weapon/cell/high = 12, /obj/item/device/assembly/prox_sensor = 3,/obj/item/device/assembly/signaler = 3,/obj/item/device/healthanalyzer = 3, /obj/item/weapon/scalpel = 2,/obj/item/weapon/circular_saw = 2,/obj/item/weapon/tank/anesthetic = 2,/obj/item/clothing/mask/breath/medical = 5, diff --git a/code/game/machinery/wishgranter.dm b/code/game/machinery/wishgranter.dm index 786468c0..2720a1c3 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 916749b5..84c8e845 100644 --- a/code/game/mecha/equipment/tools/tools.dm +++ b/code/game/mecha/equipment/tools/tools.dm @@ -465,7 +465,7 @@ return else if(target!=locked) if(locked in view(chassis)) - locked.throw_at(target, 14, 1.5) + locked.throw_at(target, 14, 1.5, chassis) locked = null send_byjax(chassis.occupant,"exosuit.browser","\ref[src]",src.get_equip_info()) set_ready_state(0) @@ -519,9 +519,8 @@ can_attach(obj/mecha/M as obj) if(..()) - if(!istype(M, /obj/mecha/combat/honker)) - if(!M.proc_res["dynattackby"]) - return 1 + if(!M.proc_res["dynattackby"]) + return 1 return 0 attach(obj/mecha/M as obj) @@ -570,9 +569,8 @@ can_attach(obj/mecha/M as obj) if(..()) - if(!istype(M, /obj/mecha/combat/honker)) - if(!M.proc_res["dynbulletdamage"] && !M.proc_res["dynhitby"]) - return 1 + if(!M.proc_res["dynbulletdamage"] && !M.proc_res["dynhitby"]) + return 1 return 0 attach(obj/mecha/M as obj) @@ -927,13 +925,11 @@ return var/datum/gas_mixture/GM = new if(prob(10)) - GM.toxins += 100 - GM.temperature = 1500+T0C //should be enough to start a fire + T.assume_gas("toxins", 100, 1500+T0C) T.visible_message("The [src] suddenly disgorges a cloud of heated plasma.") destroy() else - GM.toxins += 5 - GM.temperature = istype(T) ? T.air.temperature : T20C + T.assume_gas("toxins", 5, istype(T) ? T.air.temperature : T20C) T.visible_message("The [src] suddenly disgorges a cloud of plasma.") T.assume_air(GM) return diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm index 77238b1f..1dcf80ae 100644 --- a/code/game/mecha/equipment/weapons/weapons.dm +++ b/code/game/mecha/equipment/weapons/weapons.dm @@ -230,7 +230,7 @@ var/missile_range = 30 /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/Fire(atom/movable/AM, atom/target, turf/aimloc) - AM.throw_at(target,missile_range, missile_speed) + AM.throw_at(target,missile_range, missile_speed, chassis) /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/explosive name = "SRM-8 Missile Rack" @@ -356,7 +356,7 @@ w_class = 2.0 throw_speed = 2 throw_range = 5 - m_amt = 500 + matter = list("metal" = 500) origin_tech = "materials=1" var/dispenser = 0 var/breakouttime = 1200 //Deciseconds = 120s = 2 minutes diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index ee5d1927..1d38590b 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) @@ -469,6 +468,7 @@ return /obj/mecha/hitby(atom/movable/A as mob|obj) //wrapper + ..() src.log_message("Hit by [A].",1) call((proc_res["dynhitby"]||src), "dynhitby")(A) return @@ -854,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) @@ -1299,9 +1299,9 @@ Powercell charge: [isnull(cell_charge)?"No powercell installed":"[cell.percent()]%"]
Air source: [use_internal_tank?"Internal Airtank":"Environment"]
Airtank pressure: [tank_pressure]kPa
- Airtank temperature: [tank_temperature]°K|[tank_temperature - T0C]°C
+ Airtank temperature: [tank_temperature]K|[tank_temperature - T0C]°C
Cabin pressure: [cabin_pressure>WARNING_HIGH_PRESSURE ? "[cabin_pressure]": cabin_pressure]kPa
- Cabin temperature: [return_temperature()]°K|[return_temperature() - T0C]°C
+ Cabin temperature: [return_temperature()]K|[return_temperature() - T0C]°C
Lights: [lights?"on":"off"]
[src.dna?"DNA-locked:
[src.dna] \[Reset\]
":null] "} @@ -1704,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 @@ -1722,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 @@ -1732,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) @@ -1765,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/mines.dm b/code/game/objects/effects/mines.dm index 320c801e..bd8ac32f 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/triggerplasma(obj) for (var/turf/simulated/floor/target in range(1,src)) if(!target.blocks_air) - - var/datum/gas_mixture/payload = new - - payload.toxins = 30 - - target.zone.air.merge(payload) + target.assume_gas("toxins", 30) target.hotspot_expose(1000, CELL_VOLUME) diff --git a/code/game/objects/effects/spawners/bombspawner.dm b/code/game/objects/effects/spawners/bombspawner.dm index 7fe38eca..3719dd6c 100644 --- a/code/game/objects/effects/spawners/bombspawner.dm +++ b/code/game/objects/effects/spawners/bombspawner.dm @@ -137,14 +137,10 @@ OT.master = V PT.air_contents.temperature = PLASMA_FLASHPOINT - PT.air_contents.toxins = 12 - PT.air_contents.carbon_dioxide = 8 - - PT.air_contents.update_values() + PT.air_contents.adjust_multi("toxins", 12, "carbon_dioxide", 8) OT.air_contents.temperature = PLASMA_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/items/blueprints.dm b/code/game/objects/items/blueprints.dm index 5c5b1b6f..ff1a1fb1 100644 --- a/code/game/objects/items/blueprints.dm +++ b/code/game/objects/items/blueprints.dm @@ -74,7 +74,7 @@ move an amendment to the drawing.

/obj/item/blueprints/proc/get_area() - var/turf/T = get_turf_loc(usr) + var/turf/T = get_turf(usr) var/area/A = T.loc A = A.master return A @@ -101,7 +101,7 @@ move an amendment to the drawing.

/obj/item/blueprints/proc/create_area() //world << "DEBUG: create_area" - var/res = detect_room(get_turf_loc(usr)) + var/res = detect_room(get_turf(usr)) if(!istype(res,/list)) switch(res) if(ROOM_ERR_SPACE) diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm index 9538cd81..b04ca203 100644 --- a/code/game/objects/items/devices/PDA/PDA.dm +++ b/code/game/objects/items/devices/PDA/PDA.dm @@ -333,7 +333,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 @@ -445,13 +445,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/plasma_level = environment.toxins/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/plasma_level = environment.gas["toxins"]/total_moles var/unknown_level = 1-(o2_level+n2_level+co2_level+plasma_level) data["aircontents"] = list(\ "pressure" = "[round(pressure,0.1)]",\ @@ -467,7 +467,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 @@ -778,14 +778,18 @@ var/global/list/obj/item/device/pda/PDAs = list() //pAI FUNCTIONS=================================== if("pai") - switch(href_list["option"]) - if("1") // Configure pAI device - pai.attack_self(U) - if("2") // Eject pAI device - var/turf/T = get_turf_or_move(src.loc) - if(T) - pai.loc = T - pai = null + if(pai) + if(pai.loc != src) + pai = null + else + switch(href_list["option"]) + if("1") // Configure pAI device + pai.attack_self(U) + if("2") // Eject pAI device + var/turf/T = get_turf_or_move(src.loc) + if(T) + pai.loc = T + pai = null else mode = text2num(href_list["choice"]) @@ -924,7 +928,11 @@ var/global/list/obj/item/device/pda/PDAs = list() if(useTC != 2) // Does our recipient have a broadcaster on their level? U << "ERROR: Cannot reach recipient." return - useMS.send_pda_message("[P.owner]","[owner]","[t]") + var/send_result = useMS.send_pda_message("[P.owner]","[owner]","[t]") + if (send_result) + U << "ERROR: Messaging server rejected your message. Reason: contains '[send_result]'." + return + tnote.Add(list(list("sent" = 1, "owner" = "[P.owner]", "job" = "[P.ownjob]", "message" = "[t]", "target" = "\ref[P]"))) P.tnote.Add(list(list("sent" = 0, "owner" = "[owner]", "job" = "[ownjob]", "message" = "[t]", "target" = "\ref[src]"))) for(var/mob/M in player_list) @@ -1165,24 +1173,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/plasma_concentration = A:air_contents.toxins/total_moles - - var/unknown_concentration = 1-(o2_concentration+n2_concentration+co2_concentration+plasma_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 Plasma: [round(plasma_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!" @@ -1194,31 +1191,66 @@ 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/plasma_concentration = T.parent.air.toxins/total_moles - - var/unknown_concentration = 1-(o2_concentration+n2_concentration+co2_concentration+plasma_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 Plasma: [round(plasma_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!" if (!scanmode && istype(A, /obj/item/weapon/paper) && owner) - note = A:info - user << "\blue Paper scanned." //concept of scanning paper copyright brainoblivion 2009 + // JMO 20140705: Makes scanned document show up properly in the notes. Not pretty for formatted documents, + // as this will clobber the HTML, but at least it lets you scan a document. You can restore the original + // notes by editing the note again. (Was going to allow you to edit, but scanned documents are too long.) + var/raw_scan = (A:info) + var/formatted_scan = "" + // Scrub out the tags (replacing a few formatting ones along the way) + + // Find the beginning and end of the first tag. + var/tag_start = findtext(raw_scan,"<") + var/tag_stop = findtext(raw_scan,">") + + // Until we run out of complete tags... + while(tag_start&&tag_stop) + var/pre = copytext(raw_scan,1,tag_start) // Get the stuff that comes before the tag + var/tag = lowertext(copytext(raw_scan,tag_start+1,tag_stop)) // Get the tag so we can do intellegent replacement + var/tagend = findtext(tag," ") // Find the first space in the tag if there is one. + + // Anything that's before the tag can just be added as is. + formatted_scan = formatted_scan+pre + + // If we have a space after the tag (and presumably attributes) just crop that off. + if (tagend) + tag=copytext(tag,1,tagend) + + if (tag=="p"||tag=="/p"||tag=="br") // Check if it's I vertical space tag. + formatted_scan=formatted_scan+"
" // If so, add some padding in. + + raw_scan = copytext(raw_scan,tag_stop+1) // continue on with the stuff after the tag + + // Look for the next tag in what's left + tag_start = findtext(raw_scan,"<") + tag_stop = findtext(raw_scan,">") + + // Anything that is left in the page. just tack it on to the end as is + formatted_scan=formatted_scan+raw_scan + + // If there is something in there already, pad it out. + if (length(note)>0) + note = note + "

" + + // Store the scanned document to the notes + note = "Scanned Document. Edit to restore previous notes/delete scan.
----------
" + formatted_scan + "
" + // notehtml ISN'T set to allow user to get their old notes back. A better implementation would add a "scanned documents" + // feature to the PDA, which would better convey the availability of the feature, but this will work for now. + + // Inform the user + user << "\blue Paper scanned and OCRed to notekeeper." //concept of scanning paper copyright brainoblivion 2009 + /obj/item/device/pda/proc/explode() //This needs tuning. //Sure did. @@ -1235,7 +1267,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/PDA/cart.dm b/code/game/objects/items/devices/PDA/cart.dm index d0bc984a..d71bd2d7 100644 --- a/code/game/objects/items/devices/PDA/cart.dm +++ b/code/game/objects/items/devices/PDA/cart.dm @@ -245,12 +245,12 @@ /* Signaler (Mode: 40) */ - + if(istype(radio,/obj/item/radio/integrated/signal) && (mode==40)) var/obj/item/radio/integrated/signal/R = radio values["signal_freq"] = format_frequency(R.frequency) values["signal_code"] = R.code - + /* Station Display (Mode: 42) */ @@ -266,8 +266,8 @@ for(var/obj/machinery/power/monitor/pMon in world) if(!(pMon.stat & (NOPOWER|BROKEN)) ) pMonData[++pMonData.len] = list ("Name" = pMon.name, "ref" = "\ref[pMon]") - if(isnull(powmonitor)) powmonitor = pMon - + if(isnull(powmonitor)) powmonitor = pMon + values["powermonitors"] = pMonData values["poweravail"] = powmonitor.powernet.avail @@ -284,22 +284,22 @@ var/apcData[0] for(var/obj/machinery/power/apc/A in L) apcData[++apcData.len] = list("Name" = html_encode(A.area.name), "Equipment" = Status[A.equipment+1], "Lights" = Status[A.lighting+1], "Environment" = Status[A.environ+1], "CellPct" = A.cell ? round(A.cell.percent(),1) : -1, "CellStatus" = A.cell ? chg[A.charging+1] : 0) - + values["apcs"] = apcData - - - + + + /* General Records (Mode: 44 / 441 / 45 / 451) */ if(mode == 44 || mode == 441 || mode == 45 || mode ==451) if(istype(active1, /datum/data/record) && (active1 in data_core.general)) values["general"] = active1.fields values["general_exists"] = 1 - + else values["general_exists"] = 0 - + /* Medical Records (Mode: 44 / 441) */ @@ -316,7 +316,7 @@ else values["medical_exists"] = 0 - /* Security Records (Mode:45 / 451) */ + /* Security Records (Mode:45 / 451) */ if(mode == 45 || mode == 451) var/secData[0] @@ -348,15 +348,15 @@ if(SC.botlist && SC.botlist.len) for(var/obj/machinery/bot/B in SC.botlist) botsCount++ - if(B.loc) + if(B.loc) botsData[++botsData.len] = list("Name" = sanitize(B.name), "Location" = sanitize(B.loc.loc.name), "ref" = "\ref[B]") - + if(!botsData.len) botsData[++botsData.len] = list("Name" = "No bots found", "Location" = "Invalid", "ref"= null) beepskyData["bots"] = botsData beepskyData["count"] = botsCount - + else beepskyData["active"] = 0 botsData[++botsData.len] = list("Name" = "No bots found", "Location" = "Invalid", "ref"= null) @@ -379,10 +379,10 @@ var/area/loca = QC.botstatus["loca"] var/loca_name = sanitize(loca.name) muleData["botstatus"] = list("loca" = loca_name, "mode" = QC.botstatus["mode"],"home"=QC.botstatus["home"],"powr" = QC.botstatus["powr"],"retn" =QC.botstatus["retn"], "pick"=QC.botstatus["pick"], "load" = QC.botstatus["load"], "dest" = sanitize(QC.botstatus["dest"])) - + else muleData["botstatus"] = list("loca" = null, "mode" = -1,"home"=null,"powr" = null,"retn" =null, "pick"=null, "load" = null, "dest" = null) - + var/mulebotsCount=0 for(var/obj/machinery/bot/B in QC.botlist) @@ -397,7 +397,7 @@ muleData["count"] = mulebotsCount else - muleData["botstatus"] = list("loca" = null, "mode" = -1,"home"=null,"powr" = null,"retn" =null, "pick"=null, "load" = null, "dest" = null) + muleData["botstatus"] = list("loca" = null, "mode" = -1,"home"=null,"powr" = null,"retn" =null, "pick"=null, "load" = null, "dest" = null) muleData["active"] = 0 mulebotsData[++mulebotsData.len] = list("Name" = "No bots found", "Location" = "Invalid", "ref"= null) muleData["bots"] = mulebotsData @@ -411,43 +411,45 @@ if(mode==47) var/supplyData[0] - supplyData["shuttle_moving"] = supply_shuttle.moving - supplyData["shuttle_eta"] = supply_shuttle.eta - supplyData["shuttle_loc"] = supply_shuttle.at_station ? "Station" : "Dock" + var/datum/shuttle/ferry/supply/shuttle = supply_controller.shuttle + if (shuttle) + supplyData["shuttle_moving"] = shuttle.has_arrive_time() + supplyData["shuttle_eta"] = shuttle.eta_minutes() + supplyData["shuttle_loc"] = shuttle.at_station() ? "Station" : "Dock" var/supplyOrderCount = 0 var/supplyOrderData[0] - for(var/S in supply_shuttle.shoppinglist) + for(var/S in supply_controller.shoppinglist) var/datum/supply_order/SO = S - + supplyOrderData[++supplyOrderData.len] = list("Number" = SO.ordernum, "Name" = html_encode(SO.object.name), "ApprovedBy" = SO.orderedby, "Comment" = html_encode(SO.comment)) if(!supplyOrderData.len) supplyOrderData[++supplyOrderData.len] = list("Number" = null, "Name" = null, "OrderedBy"=null) - + supplyData["approved"] = supplyOrderData - supplyData["approved_count"] = supplyOrderCount + supplyData["approved_count"] = supplyOrderCount var/requestCount = 0 var/requestData[0] - for(var/S in supply_shuttle.requestlist) + for(var/S in supply_controller.requestlist) var/datum/supply_order/SO = S requestCount++ requestData[++requestData.len] = list("Number" = SO.ordernum, "Name" = html_encode(SO.object.name), "OrderedBy" = SO.orderedby, "Comment" = html_encode(SO.comment)) if(!requestData.len) requestData[++requestData.len] = list("Number" = null, "Name" = null, "orderedBy" = null, "Comment" = null) - + supplyData["requests"] = requestData supplyData["requests_count"] = requestCount values["supply"] = supplyData - + /* Janitor Supplies Locator (Mode: 49) */ if(mode==49) var/JaniData[0] var/turf/cl = get_turf(src) - + if(cl) JaniData["user_loc"] = list("x" = cl.x, "y" = cl.y) else @@ -460,14 +462,14 @@ continue var/direction = get_dir(src, M) MopData[++MopData.len] = list ("x" = ml.x, "y" = ml.y, "dir" = uppertext(dir2text(direction)), "status" = M.reagents.total_volume ? "Wet" : "Dry") - + if(!MopData.len) MopData[++MopData.len] = list("x" = 0, "y" = 0, dir=null, status = null) - + var/BucketData[0] for(var/obj/structure/mopbucket/B in world) - var/turf/bl = get_turf(B) + var/turf/bl = get_turf(B) if(bl) if(bl.z != cl.z) continue @@ -485,8 +487,8 @@ continue var/direction = get_dir(src,B) CbotData[++CbotData.len] = list("x" = bl.x, "y" = bl.y, "dir" = uppertext(dir2text(direction)), "status" = B.on ? "Online" : "Offline") - - + + if(!CbotData.len) CbotData[++CbotData.len] = list("x" = 0, "y" = 0, dir=null, status = null) var/CartData[0] @@ -499,7 +501,7 @@ CartData[++CartData.len] = list("x" = bl.x, "y" = bl.y, "dir" = uppertext(dir2text(direction)), "status" = B.reagents.total_volume/100) if(!CartData.len) CartData[++CartData.len] = list("x" = 0, "y" = 0, dir=null, status = null) - + @@ -510,7 +512,7 @@ values["janitor"] = JaniData return values - + @@ -523,8 +525,8 @@ usr << browse(null, "window=pda") return - - + + switch(href_list["choice"]) if("Medical Records") @@ -575,10 +577,10 @@ if("alert") post_status("alert", href_list["alert"]) if("setmsg1") - message1 = input("Line 1", "Enter Message Text", message1) as text|null + message1 = reject_bad_text(trim(copytext(sanitize(input("Line 1", "Enter Message Text", message1) as text|null), 1, 40)), 40) updateSelfDialog() if("setmsg2") - message2 = input("Line 2", "Enter Message Text", message2) as text|null + message2 = reject_bad_text(trim(copytext(sanitize(input("Line 2", "Enter Message Text", message2) as text|null), 1, 40)), 40) updateSelfDialog() else post_status(href_list["statdisp"]) diff --git a/code/game/objects/items/devices/debugger.dm b/code/game/objects/items/devices/debugger.dm index 669261c7..051a1972 100644 --- a/code/game/objects/items/devices/debugger.dm +++ b/code/game/objects/items/devices/debugger.dm @@ -16,8 +16,9 @@ throw_range = 15 throw_speed = 3 desc = "You can use this on airlocks or APCs to try to hack them without cutting wires." - m_amt = 50 - g_amt = 20 + + matter = list("metal" = 50,"glass" = 20) + origin_tech = "magnets=1;engineering=1" var/obj/machinery/telecomms/buffer // simple machine buffer for device linkage diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm index 1ce4162a..fcbe33ac 100644 --- a/code/game/objects/items/devices/flashlight.dm +++ b/code/game/objects/items/devices/flashlight.dm @@ -7,8 +7,7 @@ w_class = 2 flags = FPRINT | TABLEPASS | CONDUCT slot_flags = SLOT_BELT - m_amt = 50 - g_amt = 20 + matter = list("glass" = 20,"metal" = 50) icon_action_button = "action_flashlight" var/on = 0 var/brightness_on = 4 //luminosity when on @@ -137,8 +136,7 @@ brightness_on = 5 w_class = 4 flags = FPRINT | TABLEPASS | CONDUCT - m_amt = 0 - g_amt = 0 + matter = list("glass" = 0,"metal" = 0) on = 1 @@ -290,7 +288,7 @@ var/charge_tick = 0 return // This code... Gave me cancer... -Dalekfodder - + /obj/item/device/flashlight/slime gender = PLURAL name = "glowing slime extract" @@ -299,16 +297,15 @@ var/charge_tick = 0 icon_state = "floor1" //not a slime extract sprite but... something close enough! item_state = "slime" w_class = 1 - m_amt = 0 - g_amt = 0 + matter = list("glass" = 0,"metal" = 0) brightness_on = 6 on = 1 //Bio-luminesence has one setting, on. - + /obj/item/device/flashlight/slime/New() SetLuminosity(brightness_on) spawn(1) //Might be sloppy, but seems to be necessary to prevent further runtimes and make these work as intended... don't judge me! update_brightness() icon_state = initial(icon_state) - + /obj/item/device/flashlight/slime/attack_self(mob/user) return //Bio-luminescence does not toggle. \ No newline at end of file diff --git a/code/game/objects/items/devices/laserpointer.dm b/code/game/objects/items/devices/laserpointer.dm index 509f0533..c57a5bfe 100644 --- a/code/game/objects/items/devices/laserpointer.dm +++ b/code/game/objects/items/devices/laserpointer.dm @@ -7,8 +7,7 @@ var/pointer_icon_state flags = FPRINT | TABLEPASS | CONDUCT | USEDELAY slot_flags = SLOT_BELT - m_amt = 500 - g_amt = 500 + matter = list("glass" = 500,"metal" = 500) w_class = 2 //Increased to 2, because diodes are w_class 2. Conservation of matter. origin_tech = "combat=1" origin_tech = "magnets=2" diff --git a/code/game/objects/items/devices/multitool.dm b/code/game/objects/items/devices/multitool.dm index 1e5af941..782e2f69 100644 --- a/code/game/objects/items/devices/multitool.dm +++ b/code/game/objects/items/devices/multitool.dm @@ -15,7 +15,6 @@ throw_range = 15 throw_speed = 3 desc = "You can use this on airlocks or APCs to try to hack them without cutting wires." - m_amt = 50 - g_amt = 20 + matter = list("glass" = 20,"metal" = 50) origin_tech = "magnets=1;engineering=1" var/obj/machinery/telecomms/buffer // simple machine buffer for device linkage \ No newline at end of file diff --git a/code/game/objects/items/devices/powersink.dm b/code/game/objects/items/devices/powersink.dm index 9a282046..86c805b2 100644 --- a/code/game/objects/items/devices/powersink.dm +++ b/code/game/objects/items/devices/powersink.dm @@ -10,8 +10,7 @@ throwforce = 5 throw_speed = 1 throw_range = 2 - m_amt = 750 - w_amt = 750 + matter = list("glass" = 750,"metal" = 750) origin_tech = "powerstorage=3;syndicate=5" var/drain_rate = 600000 // amount of power to drain per tick var/power_drained = 0 // has drained this much power diff --git a/code/game/objects/items/devices/radio/electropack.dm b/code/game/objects/items/devices/radio/electropack.dm index 826201fb..4e5f5908 100644 --- a/code/game/objects/items/devices/radio/electropack.dm +++ b/code/game/objects/items/devices/radio/electropack.dm @@ -7,8 +7,7 @@ flags = FPRINT | CONDUCT | TABLEPASS slot_flags = SLOT_BACK w_class = 5.0 - g_amt = 2500 - m_amt = 10000 + matter = list("metal" = 10000,"glass" = 2500) var/code = 2 /obj/item/device/radio/electropack/attack_hand(mob/user as mob) diff --git a/code/game/objects/items/devices/radio/encryptionkey.dm b/code/game/objects/items/devices/radio/encryptionkey.dm index d2d5417d..287e8272 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 05195d58..1ab358e5 100644 --- a/code/game/objects/items/devices/radio/headset.dm +++ b/code/game/objects/items/devices/radio/headset.dm @@ -3,8 +3,7 @@ desc = "An updated, modular intercom that fits over the head. Takes encryption keys" icon_state = "headset" item_state = "headset" - g_amt = 0 - m_amt = 75 + matter = list("metal" = 75) subspace_transmission = 1 canhear_range = 0 // can't hear headsets from very far away @@ -20,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) @@ -100,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 d00825cf..cf63ddbc 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -30,8 +30,9 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use throw_speed = 2 throw_range = 9 w_class = 2 - g_amt = 25 - m_amt = 75 + + matter = list("glass" = 25,"metal" = 75) + var/const/WIRE_SIGNAL = 1 //sends a signal, like to set off a bomb or electrocute someone var/const/WIRE_RECEIVE = 2 var/const/WIRE_TRANSMIT = 4 @@ -702,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) // ..() @@ -761,10 +764,10 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use continue src.channels += ch_name src.channels[ch_name] += keyslot.channels[ch_name] - + if(keyslot.syndie) src.syndie = 1 - + for (var/ch_name in src.channels) if(!radio_controller) diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm index f7de7211..4283af4b 100644 --- a/code/game/objects/items/devices/scanners.dm +++ b/code/game/objects/items/devices/scanners.dm @@ -17,7 +17,9 @@ REAGENT SCANNER slot_flags = SLOT_BELT w_class = 2 item_state = "electronic" - m_amt = 150 + + matter = list("metal" = 150) + origin_tech = "magnets=1;engineering=1" /obj/item/device/t_scanner/attack_self(mob/user) @@ -46,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) @@ -71,7 +75,7 @@ REAGENT SCANNER w_class = 2.0 throw_speed = 5 throw_range = 10 - m_amt = 200 + matter = list("metal" = 200) origin_tech = "magnets=1;biotech=1" var/mode = 1; @@ -89,7 +93,18 @@ REAGENT SCANNER if (!(istype(usr, /mob/living/carbon/human) || ticker) && ticker.mode.name != "monkey") usr << "\red You don't have the dexterity to do this!" return - user.visible_message(" [user] has analyzed [M]'s vitals."," You have analyzed [M]'s vitals.") + user.visible_message(" [user] has analyzed [M]'s vitals."," You have analyzed [M]'s vitals.") + + if (!istype(M, /mob/living/carbon) || (ishuman(M) && (M:species.flags & IS_SYNTHETIC))) + //these sensors are designed for organic life + user.show_message("\blue Analyzing Results for ERROR:\n\t Overall Status: ERROR") + user.show_message("\t Key: Suffocation/Toxin/Burns/Brute", 1) + user.show_message("\t Damage Specifics: ? - ? - ? - ?") + user.show_message("\blue Body Temperature: [M.bodytemperature-T0C]°C ([M.bodytemperature*1.8-459.67]°F)", 1) + user.show_message("\red Warning: Blood Level ERROR: --% --cl.\blue Type: ERROR") + user.show_message("\blue Subject's pulse: -- bpm.") + return + var/fake_oxy = max(rand(1,40), M.getOxyLoss(), (300 - (M.getToxLoss() + M.getFireLoss() + M.getBruteLoss()))) var/OX = M.getOxyLoss() > 50 ? "[M.getOxyLoss()]" : M.getOxyLoss() var/TX = M.getToxLoss() > 50 ? "[M.getToxLoss()]" : M.getToxLoss() @@ -128,7 +143,20 @@ REAGENT SCANNER user.show_message("[OX] | [TX] | [BU] | [BR]") if (istype(M, /mob/living/carbon)) if(M:reagents.total_volume > 0) - user.show_message(text("\red Warning: Unknown substance detected in subject's blood.")) + var/unknown = 0 + var/reagentdata[0] + for(var/A in M.reagents.reagent_list) + var/datum/reagent/R = A + if(R.scannable) + reagentdata["[R.id]"] = "\t \blue [round(M.reagents.get_reagent_amount(R.id), 1)]u [R.name]" + else + unknown++ + if(reagentdata.len) + user.show_message("\blue Beneficial reagents detected in subject's blood:") + for(var/d in reagentdata) + user.show_message(reagentdata[d]) + if(unknown) + user.show_message(text("\red Warning: Unknown substance[(unknown>1)?"s":""] detected in subject's blood.")) if(M:virus2.len) var/mob/living/carbon/C = M for (var/ID in C.virus2) @@ -141,8 +169,8 @@ REAGENT SCANNER for(var/datum/disease/D in M.viruses) if(!D.hidden[SCANNER]) user.show_message(text("\red Warning: [D.form] Detected\nName: [D.name].\nType: [D.spread].\nStage: [D.stage]/[D.max_stages].\nPossible Cure: [D.cure]")) - if (M.reagents && M.reagents.get_reagent_amount("inaprovaline")) - user.show_message("\blue Bloodstream Analysis located [M.reagents:get_reagent_amount("inaprovaline")] units of rejuvenation chemicals.") +// if (M.reagents && M.reagents.get_reagent_amount("inaprovaline")) +// user.show_message("\blue Bloodstream Analysis located [M.reagents:get_reagent_amount("inaprovaline")] units of rejuvenation chemicals.") if (M.has_brain_worms()) user.show_message("\red Subject suffering from aberrant brain activity. Recommend further scanning.") else if (M.getBrainLoss() >= 100 || istype(M, /mob/living/carbon/human) && M:brain_op_stage == 4.0) @@ -159,7 +187,7 @@ REAGENT SCANNER if(e.status & ORGAN_BROKEN) if(((e.name == "l_arm") || (e.name == "r_arm") || (e.name == "l_leg") || (e.name == "r_leg")) && (!(e.status & ORGAN_SPLINTED))) user << "\red Unsecured fracture in subject [limb]. Splinting recommended for transport." - if(e.is_infected()) + if(e.has_infected_wound()) user << "\red Infected wound detected in subject [limb]. Disinfection recommended." for(var/name in H.organs_by_name) @@ -209,8 +237,7 @@ REAGENT SCANNER throwforce = 5 throw_speed = 4 throw_range = 20 - m_amt = 30 - g_amt = 20 + matter = list("glass" = 20,"metal" = 30) origin_tech = "magnets=1;engineering=1" /obj/item/device/analyzer/attack_self(mob/user as mob) @@ -228,7 +255,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) @@ -236,32 +263,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/plasma_concentration = environment.toxins/total_moles - - var/unknown_concentration = 1-(o2_concentration+n2_concentration+co2_concentration+plasma_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(plasma_concentration > 0.01) - user.show_message("\red Plasma: [round(plasma_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) @@ -279,8 +282,7 @@ REAGENT SCANNER throwforce = 5 throw_speed = 4 throw_range = 20 - m_amt = 30 - g_amt = 20 + matter = list("metal" = 20,"glass" = 50) origin_tech = "magnets=2;biotech=2" var/details = 0 var/recent_fail = 0 @@ -352,13 +354,14 @@ REAGENT SCANNER throwforce = 5 throw_speed = 4 throw_range = 20 - m_amt = 30 - g_amt = 20 + matter = list("metal" = 30,"glass" = 20) origin_tech = "magnets=2;biotech=2" var/details = 0 var/recent_fail = 0 -/obj/item/device/reagent_scanner/afterattack(obj/O, mob/user as mob) +/obj/item/device/reagent_scanner/afterattack(obj/O, mob/user as mob, proximity) + if(!proximity) + return if (user.stat) return if (!(istype(user, /mob/living/carbon/human) || ticker) && ticker.mode.name != "monkey") @@ -397,4 +400,48 @@ 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/taperecorder.dm b/code/game/objects/items/devices/taperecorder.dm index 4e9e90e8..40247347 100644 --- a/code/game/objects/items/devices/taperecorder.dm +++ b/code/game/objects/items/devices/taperecorder.dm @@ -4,8 +4,7 @@ icon_state = "taperecorderidle" item_state = "analyzer" w_class = 1.0 - m_amt = 60 - g_amt = 30 + matter = list("metal" = 60,"glass" = 50) var/emagged = 0.0 var/recording = 0.0 var/playing = 0.0 diff --git a/code/game/objects/items/shooting_range.dm b/code/game/objects/items/shooting_range.dm index 8d5ac993..c21692b7 100644 --- a/code/game/objects/items/shooting_range.dm +++ b/code/game/objects/items/shooting_range.dm @@ -61,7 +61,7 @@ user.put_in_hands(src) user << "You take the target out of the stake." else - src.loc = get_turf_loc(user) + src.loc = get_turf(user) user << "You take the target out of the stake." stake.pinned_target = null diff --git a/code/game/objects/items/stacks/rods.dm b/code/game/objects/items/stacks/rods.dm index 8ad23f4b..de18e574 100644 --- a/code/game/objects/items/stacks/rods.dm +++ b/code/game/objects/items/stacks/rods.dm @@ -9,7 +9,7 @@ throwforce = 15.0 throw_speed = 5 throw_range = 20 - m_amt = 1875 + matter = list("metal" = 1875) max_amount = 60 attack_verb = list("hit", "bludgeoned", "whacked") @@ -52,15 +52,19 @@ use(1) else return 1 - else + + else if(!in_use) if(amount < 2) user << "\blue You need at least two rods to do this." return usr << "\blue Assembling grille..." + in_use = 1 if (!do_after(usr, 10)) + in_use = 0 return var/obj/structure/grille/F = new /obj/structure/grille/ ( usr.loc ) usr << "\blue You assemble a grille" + in_use = 0 F.add_fingerprint(usr) use(2) return diff --git a/code/game/objects/items/stacks/sheets/glass.dm b/code/game/objects/items/stacks/sheets/glass.dm index 8227b042..9519d262 100644 --- a/code/game/objects/items/stacks/sheets/glass.dm +++ b/code/game/objects/items/stacks/sheets/glass.dm @@ -15,7 +15,7 @@ desc = "HOLY SHEET! That is a lot of glass." singular_name = "glass sheet" icon_state = "sheet-glass" - g_amt = 3750 + matter = list("glass" = 3750) origin_tech = "materials=1" var/created_window = /obj/structure/window/basic @@ -24,7 +24,7 @@ desc = "HOLY SHEET! That is a lot of glass." singular_name = "glass sheet" icon_state = "sheet-glass" - g_amt = 0 + matter = null created_window = /obj/structure/window/basic /obj/item/stack/sheet/glass/attack_self(mob/user as mob) @@ -123,8 +123,9 @@ desc = "Glass which seems to have rods or something stuck in them." singular_name = "reinforced glass sheet" icon_state = "sheet-rglass" - g_amt = 3750 - m_amt = 1875 + + matter = list("metal" = 1875,"glass" = 3750) + origin_tech = "materials=2" /obj/item/stack/sheet/rglass/cyborg @@ -132,8 +133,6 @@ desc = "Glass which seems to have rods or something stuck in them." singular_name = "reinforced glass sheet" icon_state = "sheet-rglass" - g_amt = 0 - m_amt = 0 /obj/item/stack/sheet/rglass/attack_self(mob/user as mob) construct_window(user) @@ -317,7 +316,7 @@ desc = "A very strong and very resistant sheet of a plasma-glass alloy." singular_name = "plasma glass sheet" icon_state = "sheet-plasmaglass" - g_amt = 7500 + matter = list("glass" = 7500) origin_tech = "materials=3;plasma=2" created_window = /obj/structure/window/plasmabasic @@ -349,8 +348,7 @@ desc = "Plasma glass which seems to have rods or something stuck in them." singular_name = "reinforced plasma glass sheet" icon_state = "sheet-plasmarglass" - g_amt = 7500 - m_amt = 1875 + matter = list("glass" = 7500,"metal" = 1875) origin_tech = "materials=4;plasma=2" created_window = /obj/structure/window/plasmareinforced diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm index 29c71e76..e90d6bf1 100644 --- a/code/game/objects/items/stacks/sheets/sheet_types.dm +++ b/code/game/objects/items/stacks/sheets/sheet_types.dm @@ -79,7 +79,7 @@ var/global/list/datum/stack_recipe/metal_recipes = list ( \ desc = "Sheets made out off metal. It has been dubbed Metal Sheets." singular_name = "metal sheet" icon_state = "sheet-metal" - m_amt = 3750 + matter = list("metal" = 3750) throwforce = 14.0 flags = FPRINT | TABLEPASS | CONDUCT origin_tech = "materials=1" @@ -89,7 +89,6 @@ var/global/list/datum/stack_recipe/metal_recipes = list ( \ desc = "Sheets made out off metal. It has been dubbed Metal Sheets." singular_name = "metal sheet" icon_state = "sheet-metal" - m_amt = 0 throwforce = 14.0 flags = FPRINT | TABLEPASS | CONDUCT @@ -115,7 +114,7 @@ var/global/list/datum/stack_recipe/plasteel_recipes = list ( \ desc = "This sheet is an alloy of iron and plasma." icon_state = "sheet-plasteel" item_state = "sheet-metal" - m_amt = 7500 + matter = list("metal" = 7500) throwforce = 15.0 flags = FPRINT | TABLEPASS | CONDUCT origin_tech = "materials=2" diff --git a/code/game/objects/items/stacks/tiles/plasteel.dm b/code/game/objects/items/stacks/tiles/plasteel.dm index c793d99b..f26df1e8 100644 --- a/code/game/objects/items/stacks/tiles/plasteel.dm +++ b/code/game/objects/items/stacks/tiles/plasteel.dm @@ -5,7 +5,7 @@ icon_state = "tile" w_class = 3.0 force = 6.0 - m_amt = 937.5 + matter = list("metal" = 937.5) throwforce = 15.0 throw_speed = 5 throw_range = 20 diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index e6e41270..de6ce1c8 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -126,8 +126,9 @@ flags = FPRINT | TABLEPASS | CONDUCT slot_flags = SLOT_BELT w_class = 3.0 - g_amt = 10 - m_amt = 10 + + matter = list("glass" = 10,"metal" = 10) + attack_verb = list("struck", "pistol whipped", "hit", "bashed") var/bullets = 7.0 @@ -182,8 +183,9 @@ icon_state = "357-7" flags = FPRINT | TABLEPASS| CONDUCT w_class = 1.0 - g_amt = 10 - m_amt = 10 + + matter = list("metal" = 10,"glass" = 10) + var/amount_left = 7.0 update_icon() diff --git a/code/game/objects/items/weapons/RCD.dm b/code/game/objects/items/weapons/RCD.dm index 97e5b06e..8f57b0a4 100644 --- a/code/game/objects/items/weapons/RCD.dm +++ b/code/game/objects/items/weapons/RCD.dm @@ -18,10 +18,10 @@ RCD throw_speed = 1 throw_range = 5 w_class = 3.0 - m_amt = 50000 + matter = list("metal" = 50000) origin_tech = "engineering=4;materials=2" var/datum/effect/effect/system/spark_spread/spark_system - var/matter = 0 + var/stored_matter = 0 var/working = 0 var/mode = 1 var/canRwall = 0 @@ -29,7 +29,7 @@ RCD New() - desc = "A RCD. It currently holds [matter]/30 matter-units." + desc = "A RCD. It currently holds [stored_matter]/30 matter-units." src.spark_system = new /datum/effect/effect/system/spark_spread spark_system.set_up(5, 0, src) spark_system.attach(src) @@ -39,15 +39,15 @@ RCD attackby(obj/item/weapon/W, mob/user) ..() if(istype(W, /obj/item/weapon/rcd_ammo)) - if((matter + 10) > 30) + if((stored_matter + 10) > 30) user << "The RCD cant hold any more matter-units." return user.drop_item() del(W) - matter += 10 + stored_matter += 10 playsound(src.loc, 'sound/machines/click.ogg', 50, 1) - user << "The RCD now holds [matter]/30 matter-units." - desc = "A RCD. It currently holds [matter]/30 matter-units." + user << "The RCD now holds [stored_matter]/30 matter-units." + desc = "A RCD. It currently holds [stored_matter]/30 matter-units." return @@ -163,14 +163,14 @@ RCD return 0 /obj/item/weapon/rcd/proc/useResource(var/amount, var/mob/user) - if(matter < amount) + if(stored_matter < amount) return 0 - matter -= amount - desc = "A RCD. It currently holds [matter]/30 matter-units." + stored_matter -= amount + desc = "A RCD. It currently holds [stored_matter]/30 matter-units." return 1 /obj/item/weapon/rcd/proc/checkResource(var/amount, var/mob/user) - return matter >= amount + return stored_matter >= amount /obj/item/weapon/rcd/borg/useResource(var/amount, var/mob/user) if(!isrobot(user)) return 0 @@ -196,5 +196,4 @@ RCD density = 0 anchored = 0.0 origin_tech = "materials=2" - m_amt = 30000 - g_amt = 15000 \ No newline at end of file + matter = list("metal" = 30000, "glass" = 15000) \ No newline at end of file diff --git a/code/game/objects/items/weapons/RSF.dm b/code/game/objects/items/weapons/RSF.dm index ae12e8a6..f412666f 100644 --- a/code/game/objects/items/weapons/RSF.dm +++ b/code/game/objects/items/weapons/RSF.dm @@ -3,6 +3,7 @@ CONTAINS: RSF */ + /obj/item/weapon/rsf name = "\improper Rapid-Service-Fabricator" desc = "A device used to rapidly deploy service items." @@ -11,26 +12,29 @@ RSF opacity = 0 density = 0 anchored = 0.0 - var/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 [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 ((matter + 10) > 30) - user << "The RSF cant hold any more matter." + + if ((stored_matter + 10) > 30) + user << "The RSF can't hold any more matter." return + del(W) - matter += 10 + + stored_matter += 10 playsound(src.loc, 'sound/machines/click.ogg', 10, 1) - user << "The RSF now holds [matter]/30 fabrication-units." - desc = "A RSF. It currently holds [matter]/30 fabrication-units." + user << "The RSF now holds [stored_matter]/30 fabrication-units." return /obj/item/weapon/rsf/attack_self(mob/user as mob) @@ -62,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) && 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 - matter-- - user << "The RSF now holds [matter]/30 fabrication-units." - desc = "A RSF. It currently holds [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) && 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 - matter-- - user << "The RSF now holds [matter]/30 fabrication-units." - desc = "A RSF. It currently holds [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) && 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 - matter-- - user << "The RSF now holds [matter]/30 fabrication-units." - desc = "A RSF. It currently holds [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) && 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 - matter-- - user << "The RSF now holds [matter]/30 fabrication-units." - desc = "A RSF. It currently holds [matter]/30 fabrication-units." - return - - else if (istype(A, /obj/structure/table) && mode == 3) - if (istype(A, /obj/structure/table) && 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 - matter-- - user << "The RSF now holds [matter]/30 fabrication-units." - desc = "A RSF. It currently holds [matter]/30 fabrication-units." - return - - else if (istype(A, /turf/simulated/floor) && mode == 3) - if (istype(A, /turf/simulated/floor) && 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 - matter-- - user << "The RSF now holds [matter]/30 fabrication-units." - desc = "A RSF. It currently holds [matter]/30 fabrication-units." - return - - else if (istype(A, /obj/structure/table) && mode == 4) - if (istype(A, /obj/structure/table) && 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 - matter-- - user << "The RSF now holds [matter]/30 fabrication-units." - desc = "A RSF. It currently holds [matter]/30 fabrication-units." - return - - else if (istype(A, /turf/simulated/floor) && mode == 4) - if (istype(A, /turf/simulated/floor) && 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 - matter-- - user << "The RSF now holds [matter]/30 fabrication-units." - desc = "A RSF. It currently holds [matter]/30 fabrication-units." - return - - else if (istype(A, /obj/structure/table) && mode == 5) - if (istype(A, /obj/structure/table) && 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 - matter-- - user << "The RSF now holds [matter]/30 fabrication-units." - desc = "A RSF. It currently holds [matter]/30 fabrication-units." - return - - else if (istype(A, /turf/simulated/floor) && mode == 5) - if (istype(A, /turf/simulated/floor) && 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 - matter-- - user << "The RSF now holds [matter]/30 fabrication-units." - desc = "A RSF. It currently holds [matter]/30 fabrication-units." - return - - else if (istype(A, /obj/structure/table) && mode == 6) - if (istype(A, /obj/structure/table) && 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 - matter-- - user << "The RSF now holds [matter]/30 fabrication-units." - desc = "A RSF. It currently holds [matter]/30 fabrication-units." - return - - else if (istype(A, /turf/simulated/floor) && mode == 6) - if (istype(A, /turf/simulated/floor) && 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 - matter-- - user << "The RSF now holds [matter]/30 fabrication-units." - desc = "A RSF. It currently holds [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/extinguisher.dm b/code/game/objects/items/weapons/extinguisher.dm index a8768aae..499492f9 100644 --- a/code/game/objects/items/weapons/extinguisher.dm +++ b/code/game/objects/items/weapons/extinguisher.dm @@ -11,7 +11,7 @@ throw_speed = 2 throw_range = 10 force = 10.0 - m_amt = 90 + matter = list("metal" = 90) attack_verb = list("slammed", "whacked", "bashed", "thunked", "battered", "bludgeoned", "thrashed") var/max_water = 50 var/last_use = 1.0 @@ -28,7 +28,6 @@ throwforce = 2 w_class = 2.0 force = 3.0 - m_amt = 0 max_water = 30 sprite_name = "miniFE" diff --git a/code/game/objects/items/weapons/flamethrower.dm b/code/game/objects/items/weapons/flamethrower.dm index 028edd69..b8dd1c51 100644 --- a/code/game/objects/items/weapons/flamethrower.dm +++ b/code/game/objects/items/weapons/flamethrower.dm @@ -10,7 +10,7 @@ throw_speed = 1 throw_range = 5 w_class = 3.0 - m_amt = 500 + matter = list("metal" = 500) origin_tech = "combat=1;plasmatech=1" var/status = 0 var/throw_amount = 100 @@ -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/plasma_concentration = ptank.air_contents.toxins/total_moles - - var/unknown_concentration = 1-(o2_concentration+n2_concentration+co2_concentration+plasma_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 Plasma: [round(plasma_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.toxins < 1) return + if(ptank.air_contents.gas["toxins"] < 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.toxins,get_dir(loc,target)) - air_transfer.toxins = 0 + new/obj/effect/decal/cleanable/liquid_fuel/flamethrower_fuel(target,air_transfer.gas["toxins"],get_dir(loc,target)) + air_transfer.gas["toxins"] = 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 6aed134a..69b36fe4 100644 --- a/code/game/objects/items/weapons/handcuffs.dm +++ b/code/game/objects/items/weapons/handcuffs.dm @@ -10,7 +10,7 @@ w_class = 2.0 throw_speed = 2 throw_range = 5 - m_amt = 500 + matter = list("metal" = 500) origin_tech = "materials=1" var/dispenser = 0 var/breakouttime = 1200 //Deciseconds = 120s = 2 minutes diff --git a/code/game/objects/items/weapons/kitchen.dm b/code/game/objects/items/weapons/kitchen.dm index 4e9d781b..ed53cd12 100644 --- a/code/game/objects/items/weapons/kitchen.dm +++ b/code/game/objects/items/weapons/kitchen.dm @@ -163,7 +163,7 @@ throwforce = 6.0 throw_speed = 3 throw_range = 6 - m_amt = 12000 + matter = list("metal" = 12000) origin_tech = "materials=1" attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") @@ -193,7 +193,7 @@ throwforce = 8.0 throw_speed = 3 throw_range = 6 - m_amt = 12000 + matter = list("metal" = 12000) origin_tech = "materials=1" attack_verb = list("cleaved", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") sharp = 1 @@ -265,7 +265,7 @@ throw_range = 5 w_class = 3.0 flags = FPRINT | TABLEPASS | CONDUCT - m_amt = 3000 + matter = list("metal" = 3000) /* // NOPE var/food_total= 0 var/burger_amt = 0 diff --git a/code/game/objects/items/weapons/paint.dm b/code/game/objects/items/weapons/paint.dm index da2e7b83..06361d00 100644 --- a/code/game/objects/items/weapons/paint.dm +++ b/code/game/objects/items/weapons/paint.dm @@ -8,8 +8,7 @@ var/global/list/cached_icons = list() icon = 'icons/obj/items.dmi' icon_state = "paint_neutral" item_state = "paintcan" - m_amt = 200 - g_amt = 0 + matter = list("metal" = 200) w_class = 3.0 amount_per_transfer_from_this = 10 possible_transfer_amounts = list(10,20,30,50,70) diff --git a/code/game/objects/items/weapons/power_cells.dm b/code/game/objects/items/weapons/power_cells.dm index a497452a..7281e322 100644 --- a/code/game/objects/items/weapons/power_cells.dm +++ b/code/game/objects/items/weapons/power_cells.dm @@ -13,8 +13,7 @@ w_class = 3.0 var/charge = 0 // note %age conveted to actual charge in New var/maxcharge = 1000 - m_amt = 700 - g_amt = 50 + matter = list("metal" = 700, "glass" = 50) var/rigged = 0 // true if rigged to explode var/minor_fault = 0 //If not 100% reliable, it will build up faults. var/construction_cost = list("metal"=750,"glass"=75) @@ -29,7 +28,7 @@ desc = "You can't top the plasma top." //TOTALLY TRADEMARK INFRINGEMENT origin_tech = "powerstorage=0" maxcharge = 500 - g_amt = 40 + matter = list("glass" = 40) /obj/item/weapon/cell/crap/empty/New() ..() @@ -39,18 +38,24 @@ name = "security borg rechargable D battery" origin_tech = "powerstorage=0" maxcharge = 600 //600 max charge / 100 charge per shot = six shots - g_amt = 40 + matter = list("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 - g_amt = 60 + matter = list("glass" = 60) /obj/item/weapon/cell/high/empty/New() ..() @@ -61,7 +66,7 @@ origin_tech = "powerstorage=5" icon_state = "scell" maxcharge = 20000 - g_amt = 70 + matter = list("glass" = 70) construction_cost = list("metal"=750,"glass"=100) /obj/item/weapon/cell/super/empty/New() @@ -73,7 +78,7 @@ origin_tech = "powerstorage=6" icon_state = "hpcell" maxcharge = 30000 - g_amt = 80 + matter = list("glass" = 80) construction_cost = list("metal"=500,"glass"=150,"gold"=200,"silver"=200) /obj/item/weapon/cell/hyper/empty/New() @@ -85,7 +90,7 @@ icon_state = "icell" origin_tech = null maxcharge = 30000 - g_amt = 80 + matter = list("glass" = 80) use() return 1 @@ -97,8 +102,7 @@ icon_state = "potato_cell" //"potato_battery" charge = 100 maxcharge = 300 - m_amt = 0 - g_amt = 0 + matter = list("metal" = 0, "glass" = 0) minor_fault = 1 @@ -110,5 +114,4 @@ icon_state = "yellow slime extract" //"potato_battery" maxcharge = 10000 maxcharge = 10000 - m_amt = 0 - g_amt = 0 + matter = null \ No newline at end of file diff --git a/code/game/objects/items/weapons/shields.dm b/code/game/objects/items/weapons/shields.dm index 9e5ccf48..67e0527b 100644 --- a/code/game/objects/items/weapons/shields.dm +++ b/code/game/objects/items/weapons/shields.dm @@ -13,8 +13,7 @@ throw_speed = 1 throw_range = 4 w_class = 4.0 - g_amt = 7500 - m_amt = 1000 + matter = list("metal" = 1000, "glass" = 7500) origin_tech = "materials=2" attack_verb = list("shoved", "bashed") var/cooldown = 0 //shield bash cooldown. based on world.time diff --git a/code/game/objects/items/weapons/storage/boxes.dm b/code/game/objects/items/weapons/storage/boxes.dm index 503f7b99..ee629779 100644 --- a/code/game/objects/items/weapons/storage/boxes.dm +++ b/code/game/objects/items/weapons/storage/boxes.dm @@ -529,7 +529,7 @@ force = 5.0 throwforce = 7.0 w_class = 2.0 - m_amt = 50 + matter = list("metal" = 50) attack_verb = list("bludgeoned", "whacked", "disciplined", "thrashed") can_hold = list( "/obj/item/weapon/canesword") diff --git a/code/game/objects/items/weapons/storage/firstaid.dm b/code/game/objects/items/weapons/storage/firstaid.dm index 773ed2e7..1d12a9c4 100644 --- a/code/game/objects/items/weapons/storage/firstaid.dm +++ b/code/game/objects/items/weapons/storage/firstaid.dm @@ -123,8 +123,7 @@ allow_quick_gather = 1 use_to_pickup = 1 storage_slots = 14 - m_amt = 100 - g_amt = 700 + matter = list("metal" = 100, "glass" = 700) use_sound = null /obj/item/weapon/storage/pill_bottle/verb/rename() diff --git a/code/game/objects/items/weapons/surgery_tools.dm b/code/game/objects/items/weapons/surgery_tools.dm index fa50951b..fcd94603 100644 --- a/code/game/objects/items/weapons/surgery_tools.dm +++ b/code/game/objects/items/weapons/surgery_tools.dm @@ -16,8 +16,7 @@ desc = "Retracts stuff." icon = 'icons/obj/surgery.dmi' icon_state = "retractor" - m_amt = 10000 - g_amt = 5000 + matter = list("metal" = 10000, "glass" = 5000) flags = FPRINT | TABLEPASS | CONDUCT w_class = 2.0 origin_tech = "materials=1;biotech=1" @@ -130,8 +129,7 @@ LOOK FOR SURGERY.DM*/ desc = "You think you have seen this before." icon = 'icons/obj/surgery.dmi' icon_state = "hemostat" - m_amt = 5000 - g_amt = 2500 + matter = list("metal" = 5000, "glass" = 2500) flags = FPRINT | TABLEPASS | CONDUCT w_class = 2.0 origin_tech = "materials=1;biotech=1" @@ -265,8 +263,7 @@ LOOK FOR SURGERY.DM*/ desc = "This stops bleeding." icon = 'icons/obj/surgery.dmi' icon_state = "cautery" - m_amt = 5000 - g_amt = 2500 + matter = list("metal" = 5000, "glass" = 2500) flags = FPRINT | TABLEPASS | CONDUCT w_class = 2.0 origin_tech = "materials=1;biotech=1" @@ -356,8 +353,7 @@ LOOK FOR SURGERY.DM*/ icon = 'icons/obj/surgery.dmi' icon_state = "drill" hitsound = 'sound/weapons/circsawhit.ogg' - m_amt = 15000 - g_amt = 10000 + matter = list("metal" = 15000, "glass" = 10000) flags = FPRINT | TABLEPASS | CONDUCT force = 15.0 w_class = 2.0 @@ -385,8 +381,7 @@ LOOK FOR SURGERY.DM*/ throwforce = 5.0 throw_speed = 3 throw_range = 5 - m_amt = 10000 - g_amt = 5000 + matter = list("metal" = 10000, "glass" = 5000) origin_tech = "materials=1;biotech=1" attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") @@ -666,8 +661,7 @@ LOOK FOR SURGERY.DM*/ throwforce = 9.0 throw_speed = 3 throw_range = 5 - m_amt = 20000 - g_amt = 10000 + matter = list("metal" = 20000, "glass" = 10000) origin_tech = "materials=1;biotech=1" attack_verb = list("attacked", "slashed", "sawed", "cut") sharp = 1 @@ -850,7 +844,7 @@ LOOK FOR SURGERY.DM*/ force = 8.0 throwforce = 9.0 throw_speed = 3 - m_amt = 1000 + matter = list("metal" = 1000) throw_range = 5 w_class = 2.0 attack_verb = list("attacked", "hit", "bludgeoned") diff --git a/code/game/objects/items/weapons/tanks/jetpack.dm b/code/game/objects/items/weapons/tanks/jetpack.dm index de2c136d..0d08aa10 100644 --- a/code/game/objects/items/weapons/tanks/jetpack.dm +++ b/code/game/objects/items/weapons/tanks/jetpack.dm @@ -13,63 +13,58 @@ var/volume_rate = 500 //Needed for borg jetpack transfer icon_action_button = "action_jetpack" - New() - ..() - src.ion_trail = new /datum/effect/effect/system/ion_trail_follow() - src.ion_trail.set_up(src) - return +/obj/item/weapon/tank/jetpack/New() + ..() + src.ion_trail = new /datum/effect/effect/system/ion_trail_follow() + src.ion_trail.set_up(src) +/obj/item/weapon/tank/jetpack/examine() + set src in usr + ..() + 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) - examine() - set src in usr - ..() - if(air_contents.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) - return +/obj/item/weapon/tank/jetpack/verb/toggle_rockets() + set name = "Toggle Jetpack Stabilization" + set category = "Object" + src.stabilization_on = !( src.stabilization_on ) + usr << "You toggle the stabilization [stabilization_on? "on":"off"]." +/obj/item/weapon/tank/jetpack/verb/toggle() + set name = "Toggle Jetpack" + set category = "Object" - verb/toggle_rockets() - set name = "Toggle Jetpack Stabilization" - set category = "Object" - src.stabilization_on = !( src.stabilization_on ) - usr << "You toggle the stabilization [stabilization_on? "on":"off"]." - return + on = !on + if(on) + icon_state = "[icon_state]-on" + ion_trail.start() + else + icon_state = initial(icon_state) + ion_trail.stop() + if (ismob(usr)) + var/mob/M = usr + M.update_inv_back() - verb/toggle() - set name = "Toggle Jetpack" - set category = "Object" - on = !on - if(on) - icon_state = "[icon_state]-on" -// item_state = "[item_state]-on" - ion_trail.start() - else - icon_state = initial(icon_state) -// item_state = initial(item_state) - ion_trail.stop() - return +/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)) + src.ion_trail.stop() + return 0 + var/datum/gas_mixture/G = src.air_contents.remove(num) - proc/allow_thrust(num, mob/living/user as mob) - if(!(src.on)) - return 0 - if((num < 0.005 || src.air_contents.total_moles() < num)) - src.ion_trail.stop() - return 0 + var/allgases = G.gas["carbon_dioxide"] + G.gas["nitrogen"] + G.gas["oxygen"] + G.gas["phoron"] + if(allgases >= 0.005) + return 1 - var/datum/gas_mixture/G = src.air_contents.remove(num) + del(G) + return - var/allgases = G.carbon_dioxide + G.nitrogen + G.oxygen + G.toxins //fuck trace gases -Pete - if(allgases >= 0.005) - return 1 - - del(G) - return - - ui_action_click() - toggle() +/obj/item/weapon/tank/jetpack/ui_action_click() + toggle() /obj/item/weapon/tank/jetpack/void @@ -78,11 +73,10 @@ icon_state = "jetpack-void" item_state = "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)) - return +/obj/item/weapon/tank/jetpack/void/New() + ..() + air_contents.adjust_gas("oxygen", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) + return /obj/item/weapon/tank/jetpack/oxygen name = "Jetpack (Oxygen)" @@ -90,11 +84,10 @@ icon_state = "jetpack" item_state = "jetpack" - 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)) - return +/obj/item/weapon/tank/jetpack/oxygen/New() + ..() + air_contents.adjust_gas("oxygen", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) + return /obj/item/weapon/tank/jetpack/oxygen/syndicate name = "syndicate jetpack (Oxygen)" @@ -109,18 +102,18 @@ icon_state = "jetpack-black" item_state = "jetpack-black" - New() - ..() - 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)) - return +/obj/item/weapon/tank/jetpack/carbondioxide/New() + ..() + 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_gas("carbon_dioxide", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) + return - examine() - set src in usr - ..() - if(air_contents.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 +/obj/item/weapon/tank/jetpack/carbondioxide/examine() + set src in usr + ..() + 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 dbaf6627..6c76ddb5 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/plasma/New() ..() - src.air_contents.toxins = (3*ONE_ATMOSPHERE)*70/(R_IDEAL_GAS_EQUATION*T20C) - // - src.air_contents.update_values() + src.air_contents.adjust_gas("toxins", (3*ONE_ATMOSPHERE)*70/(R_IDEAL_GAS_EQUATION*T20C)) return /obj/item/weapon/tank/plasma/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,15 +163,13 @@ /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) @@ -196,6 +181,6 @@ slot_flags = SLOT_BELT w_class = 2.0 force = 4.0 - m_amt = 1000 + matter = list("metal" = 1000) distribute_pressure = ONE_ATMOSPHERE*O2STANDARD volume = 2 \ 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 9c8dd739..19b76f2a 100644 --- a/code/game/objects/items/weapons/tanks/tanks.dm +++ b/code/game/objects/items/weapons/tanks/tanks.dm @@ -90,24 +90,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/plasma_concentration = air_contents.toxins/total_moles - - var/unknown_concentration = 1-(o2_concentration+n2_concentration+co2_concentration+plasma_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 Plasma: [round(plasma_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!" @@ -126,7 +115,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)) @@ -149,7 +138,7 @@ 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/teleportation.dm b/code/game/objects/items/weapons/teleportation.dm index 728f87d4..351f8547 100644 --- a/code/game/objects/items/weapons/teleportation.dm +++ b/code/game/objects/items/weapons/teleportation.dm @@ -21,7 +21,7 @@ item_state = "electronic" throw_speed = 4 throw_range = 20 - m_amt = 400 + matter = list("metal" = 400) origin_tech = "magnets=1" /obj/item/weapon/locator/attack_self(mob/user as mob) @@ -132,7 +132,7 @@ Frequency: w_class = 2.0 throw_speed = 3 throw_range = 5 - m_amt = 10000 + matter = list("metal" = 10000) origin_tech = "magnets=1;bluespace=3" /obj/item/weapon/hand_tele/attack_self(mob/user as mob) diff --git a/code/game/objects/items/weapons/tools.dm b/code/game/objects/items/weapons/tools.dm index 974655df..a9d4490c 100644 --- a/code/game/objects/items/weapons/tools.dm +++ b/code/game/objects/items/weapons/tools.dm @@ -24,7 +24,7 @@ force = 5.0 throwforce = 7.0 w_class = 2.0 - m_amt = 150 + matter = list("metal" = 150) origin_tech = "materials=1;engineering=1" attack_verb = list("bashed", "battered", "bludgeoned", "whacked") @@ -44,8 +44,7 @@ throwforce = 5.0 throw_speed = 3 throw_range = 5 - g_amt = 0 - m_amt = 75 + matter = list("metal" = 75) attack_verb = list("stabbed") suicide_act(mob/user) @@ -103,7 +102,7 @@ throw_speed = 2 throw_range = 9 w_class = 2.0 - m_amt = 80 + matter = list("metal" = 80) origin_tech = "materials=1;engineering=1" attack_verb = list("pinched", "nipped") sharp = 1 @@ -143,8 +142,7 @@ w_class = 2.0 //Cost to make in the autolathe - m_amt = 70 - g_amt = 30 + matter = list("metal" = 70, "glass" = 30) //R&D tech level origin_tech = "engineering=1" @@ -382,7 +380,7 @@ var/safety = user:eyecheck() if(istype(user, /mob/living/carbon/human)) var/mob/living/carbon/human/H = user - var/datum/organ/internal/eyes/E = H.internal_organs["eyes"] + var/datum/organ/internal/eyes/E = H.internal_organs_by_name["eyes"] if(H.species.flags & IS_SYNTHETIC) return switch(safety) @@ -421,24 +419,21 @@ /obj/item/weapon/weldingtool/largetank name = "Industrial Welding Tool" max_fuel = 40 - m_amt = 70 - g_amt = 60 + matter = list("metal" = 70, "glass" = 60) origin_tech = "engineering=2" /obj/item/weapon/weldingtool/hugetank name = "Upgraded Welding Tool" max_fuel = 80 w_class = 3.0 - m_amt = 70 - g_amt = 120 + matter = list("metal" = 70, "glass" = 120) origin_tech = "engineering=3" /obj/item/weapon/weldingtool/experimental name = "Experimental Welding Tool" max_fuel = 40 w_class = 3.0 - m_amt = 70 - g_amt = 120 + matter = list("metal" = 70, "glass" = 120) origin_tech = "engineering=4;plasma=3" var/last_gen = 0 @@ -465,7 +460,7 @@ throwforce = 7.0 item_state = "crowbar" w_class = 2.0 - m_amt = 50 + matter = list("metal" = 50) origin_tech = "engineering=1" attack_verb = list("attacked", "bashed", "battered", "bludgeoned", "whacked") @@ -499,4 +494,4 @@ user << "Nothing to fix!" else - return ..() \ No newline at end of file + return ..() diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index 1357510d..f9f3297d 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -1,8 +1,7 @@ /obj - //var/datum/module/mod //not used - var/m_amt = 0 // metal - var/g_amt = 0 // glass - var/w_amt = 0 // waster amounts + //Used to store information about the contents of the object. + var/list/matter + var/origin_tech = null //Used by R&D to determine what research bonuses it grants. var/reliability = 100 //Used by SOME devices to determine how reliable they are. var/crit_fail = 0 @@ -122,6 +121,7 @@ /obj/proc/hear_talk(mob/M as mob, text) + /* var/mob/mo = locate(/mob) in src if(mo) diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm index 74d7bf80..d40632f7 100644 --- a/code/game/objects/structures.dm +++ b/code/game/objects/structures.dm @@ -1,11 +1,12 @@ -obj/structure +/obj/structure icon = 'icons/obj/structures.dmi' + var/climbable -obj/structure/blob_act() +/obj/structure/blob_act() if(prob(50)) del(src) -obj/structure/ex_act(severity) +/obj/structure/ex_act(severity) switch(severity) if(1.0) del(src) @@ -17,12 +18,128 @@ obj/structure/ex_act(severity) if(3.0) return -obj/structure/meteorhit(obj/O as obj) +/obj/structure/meteorhit(obj/O as obj) del(src) +/obj/structure/New() + ..() + if(climbable) + verbs += /obj/structure/proc/climb_on +/obj/structure/proc/climb_on() + set name = "Climb structure" + set desc = "Climbs onto a structure." + set category = "Object" + set src in oview(1) + do_climb(usr) +/obj/structure/MouseDrop_T(mob/target, mob/user) + var/mob/living/H = user + if(!istype(H) || target != user) // No making other people climb onto tables. + return + do_climb(target) + +/obj/structure/proc/do_climb(var/mob/living/user) + + if (!can_touch(user) || !climbable) + return + + var/turf/T = src.loc + if(!T || !istype(T)) return + + for(var/obj/O in T.contents) + if(istype(O,/obj/structure)) + var/obj/structure/S = O + if(S.climbable) + continue + + if(O && O.density) + usr << "\red There's \a [O] in the way." + return + + usr.visible_message("[user] starts climbing onto \the [src]!") + + if(!do_after(user,50)) + return + + if (!can_touch(user) || !climbable) + return + + for(var/obj/O in T.contents) + if(istype(O,/obj/structure)) + var/obj/structure/S = O + if(S.climbable) + continue + + if(O && O.density) + usr << "\red There's \a [O] in the way." + return + + usr.loc = get_turf(src) + + if (get_turf(user) == get_turf(src)) + usr.visible_message("[user] climbs onto \the [src]!") + +/obj/structure/proc/structure_shaken() + + for(var/mob/living/M in get_turf(src)) + + if(M.lying) return //No spamming this on people. + + M.Weaken(5) + M << "\red You topple as \the [src] moves under you!" + + if(prob(25)) + + var/damage = rand(15,30) + var/mob/living/carbon/human/H = M + if(!istype(H)) + H << "\red You land heavily!" + M.adjustBruteLoss(damage) + return + + var/datum/organ/external/affecting + + switch(pick(list("ankle","wrist","head","knee","elbow"))) + if("ankle") + affecting = H.get_organ(pick("l_foot", "r_foot")) + if("knee") + affecting = H.get_organ(pick("l_leg", "r_leg")) + if("wrist") + affecting = H.get_organ(pick("l_hand", "r_hand")) + if("elbow") + affecting = H.get_organ(pick("l_arm", "r_arm")) + if("head") + affecting = H.get_organ("head") + + if(affecting) + M << "\red You land heavily on your [affecting.display_name]!" + affecting.take_damage(damage, 0) + if(affecting.parent) + affecting.parent.add_autopsy_data("Misadventure", damage) + else + H << "\red You land heavily!" + H.adjustBruteLoss(damage) + + H.UpdateDamageIcon() + H.updatehealth() + return + +/obj/structure/proc/can_touch(var/mob/user) + if (!user) + return 0 + if(!Adjacent(user)) + return 0 + if (user.restrained() || user.buckled) + user << "You need your hands and legs free for this." + return 0 + if (user.stat || user.paralysis || user.sleeping || user.lying || user.weakened) + return 0 + if (issilicon(user)) + user << "You need hands for this." + return 0 + return 1 \ No newline at end of file diff --git a/code/game/objects/structures/barsign.dm b/code/game/objects/structures/barsign.dm index 399fab67..4a9b2f7e 100644 --- a/code/game/objects/structures/barsign.dm +++ b/code/game/objects/structures/barsign.dm @@ -10,3 +10,15 @@ //on = 0 //brightness_on = 4 //uncomment these when the lighting fixes get in return + +/obj/structure/sign/double/barsign/attackby(obj/item/I, mob/user) + if(istype(I, /obj/item/weapon/card/id)) + var/obj/item/weapon/card/id/card = I + if(access_bar in card.GetAccess()) + var/sign_type = input(user, "What would you like to change the barsign to?") as null|anything in list("Pink Flamingo", "Magma Sea", "Limbo", "Rusty Axe", "Armok Bar", "Broken Drum", "Mead Bay", "The Damn Wall", "The Cavern", "Cindi Kate", "The Orchard", "The Saucy Clown", "The Clowns Head") + if(sign_type == null) + return + else + sign_type = replacetext(lowertext(sign_type), " ", "") // lowercase, strip spaces - along with choices for user options, avoids huge if-else-else + src.ChangeSign(sign_type) + user << "You change the barsign." \ No newline at end of file diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index 7ade7f82..d3ebf9d8 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -100,7 +100,7 @@ break if(istype (M, /mob/dead/observer)) continue - if(M.buckled) + if(M.buckled || M.pinned.len) continue if(M.client) @@ -120,8 +120,7 @@ return 1 /obj/structure/closet/proc/toggle(mob/user as mob) - . = src.opened ? src.close() : src.open() - if(!.) + if(!(src.opened ? src.close() : src.open())) user << "It won't budge!" return diff --git a/code/game/objects/structures/crates_lockers/closets/fireaxe.dm b/code/game/objects/structures/crates_lockers/closets/fireaxe.dm index 2090d76a..87eb2cb8 100644 --- a/code/game/objects/structures/crates_lockers/closets/fireaxe.dm +++ b/code/game/objects/structures/crates_lockers/closets/fireaxe.dm @@ -25,10 +25,10 @@ if(istype(O, /obj/item/device/multitool)) user << "\red Resetting circuitry..." playsound(user, 'sound/machines/lockreset.ogg', 50, 1) - sleep(50) // Sleeping time~ - src.locked = 0 - user << "\blue You disable the locking modules." - update_icon() + if(do_after(user, 20)) + src.locked = 0 + user << " You disable the locking modules." + update_icon() return else if(istype(O, /obj/item/weapon)) var/obj/item/weapon/W = O @@ -87,6 +87,9 @@ src.locked = 1 user << "\blue You re-enable the locking modules." playsound(user, 'sound/machines/lockenable.ogg', 50, 1) + if(do_after(user,20)) + src.locked = 1 + user << " You re-enable the locking modules." return else localopened = !localopened diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm index 1ac70cb7..8fa8b0d3 100644 --- a/code/game/objects/structures/crates_lockers/crates.dm +++ b/code/game/objects/structures/crates_lockers/crates.dm @@ -7,6 +7,7 @@ icon_state = "crate" icon_opened = "crateopen" icon_closed = "crate" + climbable = 1 // mouse_drag_pointer = MOUSE_ACTIVE_POINTER //??? var/rigged = 0 @@ -36,6 +37,9 @@ O.loc = get_turf(src) icon_state = icon_opened src.opened = 1 + + if(climbable) + structure_shaken() return 1 /obj/structure/closet/crate/close() @@ -319,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.toxins = gas.toxins - 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/door_assembly.dm b/code/game/objects/structures/door_assembly.dm index f9270d2b..73ce7bbf 100644 --- a/code/game/objects/structures/door_assembly.dm +++ b/code/game/objects/structures/door_assembly.dm @@ -268,9 +268,13 @@ obj/structure/door_assembly path = text2path("/obj/machinery/door/airlock[glass_type]") else path = text2path("/obj/machinery/door/airlock[airlock_type]") + var/obj/machinery/door/airlock/door = new path(src.loc) + door.assembly_type = type door.electronics = src.electronics + if (istype(electronics, /obj/item/weapon/airlock_electronics/secure)) + door.randomize_wires() if(src.electronics.one_access) door.req_access = null door.req_one_access = src.electronics.conf_access diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm index 5d46c857..55400d9c 100644 --- a/code/game/objects/structures/girders.dm +++ b/code/game/objects/structures/girders.dm @@ -25,8 +25,7 @@ if(do_after(user,40)) if(!src) return user << "\blue You dissasembled the girder!" - new /obj/item/stack/sheet/metal(get_turf(src)) - del(src) + dismantle() else if(!anchored) playsound(src.loc, 'sound/items/Ratchet.ogg', 100, 1) user << "\blue Now securing the girder" @@ -40,13 +39,11 @@ if(do_after(user,30)) if(!src) return user << "\blue You slice apart the girder!" - new /obj/item/stack/sheet/metal(get_turf(src)) - del(src) + dismantle() else if(istype(W, /obj/item/weapon/pickaxe/diamonddrill)) user << "\blue You drill through the girder!" - new /obj/item/stack/sheet/metal(get_turf(src)) - del(src) + dismantle() else if(istype(W, /obj/item/weapon/screwdriver) && state == 2 && istype(src,/obj/structure/girder/reinforced)) playsound(src.loc, 'sound/items/Screwdriver.ogg', 100, 1) @@ -166,6 +163,23 @@ else ..() + proc/dismantle() + new /obj/item/stack/sheet/metal(get_turf(src)) + del(src) + + attack_hand(mob/user as mob) + if (HULK in user.mutations) + visible_message("[user] smashes [src] apart!") + dismantle() + return + return ..() + + attack_animal(mob/living/simple_animal/user) + if(user.wall_smash) + visible_message("[user] smashes [src] apart!") + dismantle() + return + return ..() blob_act() if(prob(40)) diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm index b6dda25f..cde3078b 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 3d21a3a2..cd9dfd3e 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 cbd2e9b9..f87b0b5d 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/toxinsToDeduce = temperature/10 + target_tile.assume_gas("toxins", toxinsToDeduce, 200+T0C) - napalm.toxins = toxinsToDeduce - napalm.temperature = 200+T0C - - target_tile.assume_air(napalm) spawn (0) target_tile.hotspot_expose(temperature, 400) hardness -= toxinsToDeduce/100 diff --git a/code/game/objects/structures/mirror.dm b/code/game/objects/structures/mirror.dm index 835f36fd..21071711 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/bed.dm b/code/game/objects/structures/stool_bed_chair_nest/bed.dm index 0d831cb7..4f90e965 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/bed.dm +++ b/code/game/objects/structures/stool_bed_chair_nest/bed.dm @@ -12,10 +12,10 @@ desc = "This is used to lie in, sleep in or strap on." icon_state = "bed" var/mob/living/buckled_mob - var/movable = 0 + var/movable = 0 // For mobility checks /obj/structure/stool/bed/psych - name = "psych bed" + name = "psychiatrists couch" desc = "For prime comfort during psychiatric evaluations." icon_state = "psychbed" @@ -36,6 +36,9 @@ manual_unbuckle(user) return +/obj/structure/stool/bed/proc/handle_rotation() + return + /obj/structure/stool/bed/MouseDrop(atom/over_object) return @@ -44,15 +47,21 @@ buckle_mob(M, user) return +/obj/structure/stool/bed/proc/afterbuckle(mob/M as mob) // Called after somebody buckled / unbuckled + return + + /obj/structure/stool/bed/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() - if(!buckled_mob.mob_has_gravity(buckled_mob.loc)) - buckled_mob.float(1) + + var/M = buckled_mob buckled_mob = null + + afterbuckle(M) return /obj/structure/stool/bed/proc/manual_unbuckle(mob/user as mob) @@ -70,12 +79,14 @@ "You hear metal clanking") unbuckle() src.add_fingerprint(user) - return + return 1 + + return 0 /obj/structure/stool/bed/proc/buckle_mob(mob/M as mob, mob/user as mob) if (!ticker) user << "You can't buckle anyone in before the game starts." - if ( !ismob(M) || (get_dist(src, user) > 1) || (M.loc != src.loc) || user.restrained() || user.lying || user.stat || M.buckled || istype(user, /mob/living/silicon/pai) ) + if ( !ismob(M) || (get_dist(src, user) > 1) || (M.loc != src.loc) || user.restrained() || user.lying || user.stat || M.buckled || M.pinned.len || istype(user, /mob/living/silicon/pai) ) return if (istype(M, /mob/living/carbon/slime)) @@ -102,6 +113,7 @@ M.float(0) src.buckled_mob = M src.add_fingerprint(user) + afterbuckle(M) return /* @@ -113,6 +125,18 @@ icon_state = "down" anchored = 0 +/obj/structure/stool/bed/roller/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W,/obj/item/roller_holder)) + if(buckled_mob) + manual_unbuckle() + else + visible_message("[user] collapses \the [src.name].") + new/obj/item/roller(get_turf(src)) + spawn(0) + del(src) + return + ..() + /obj/item/roller name = "roller bed" desc = "A collapsed roller bed that can be carried around." @@ -120,11 +144,47 @@ icon_state = "folded" w_class = 4.0 // Can't be put in backpacks. Oh well. - attack_self(mob/user) +/obj/item/roller/attack_self(mob/user) var/obj/structure/stool/bed/roller/R = new /obj/structure/stool/bed/roller(user.loc) R.add_fingerprint(user) del(src) +/obj/item/roller/attackby(obj/item/weapon/W as obj, mob/user as mob) + + if(istype(W,/obj/item/roller_holder)) + var/obj/item/roller_holder/RH = W + if(!RH.held) + user << "\blue You collect the roller bed." + src.loc = RH + RH.held = src + return + + ..() + +/obj/item/roller_holder + name = "roller bed rack" + desc = "A rack for carrying a collapsed roller bed." + icon = 'icons/obj/rollerbed.dmi' + icon_state = "folded" + var/obj/item/roller/held + +/obj/item/roller_holder/New() + ..() + held = new /obj/item/roller(src) + +/obj/item/roller_holder/attack_self(mob/user as mob) + + if(!held) + user << "\blue The rack is empty." + return + + user << "\blue You deploy the roller bed." + var/obj/structure/stool/bed/roller/R = new /obj/structure/stool/bed/roller(user.loc) + R.add_fingerprint(user) + del(held) + held = null + + /obj/structure/stool/bed/roller/Move() ..() if(buckled_mob) 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 5b152839..03f28188 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/chairs.dm +++ b/code/game/objects/structures/stool_bed_chair_nest/chairs.dm @@ -39,7 +39,7 @@ rotate() return -/obj/structure/stool/bed/chair/proc/handle_rotation() //making this into a seperate proc so office chairs can call it on Move() +/obj/structure/stool/bed/chair/handle_rotation() //making this into a seperate proc so office chairs can call it on Move() if(src.dir == NORTH) src.layer = FLY_LAYER else @@ -95,24 +95,40 @@ /obj/structure/stool/bed/chair/comfy name = "comfy chair" desc = "It looks comfy." + icon_state = "comfychair" + color = rgb(255,255,255) + var/image/armrest = null + +/obj/structure/stool/bed/chair/comfy/New() + armrest = image("icons/obj/objects.dmi", "comfychair_armrest") + armrest.layer = MOB_LAYER + 0.1 + + return ..() + +/obj/structure/stool/bed/chair/comfy/afterbuckle() + if(buckled_mob) + overlays += armrest + else + overlays -= armrest /obj/structure/stool/bed/chair/comfy/brown - icon_state = "comfychair_brown" + color = rgb(255,113,0) /obj/structure/stool/bed/chair/comfy/beige - icon_state = "comfychair_beige" + color = rgb(255,253,195) /obj/structure/stool/bed/chair/comfy/teal - icon_state = "comfychair_teal" + color = rgb(0,255,255) /obj/structure/stool/bed/chair/office anchored = 0 + movable = 1 /obj/structure/stool/bed/chair/comfy/black - icon_state = "comfychair_black" + color = rgb(167,164,153) /obj/structure/stool/bed/chair/comfy/lime - icon_state = "comfychair_lime" + color = rgb(255,251,0) /obj/structure/stool/bed/chair/office/Move() ..() @@ -137,18 +153,24 @@ if(propelled) var/mob/living/occupant = buckled_mob unbuckle() - occupant.throw_at(A, 3, 2) - 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, 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) - occupant.visible_message("[occupant] clashed into \the [A]!") + 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 icon_state = "officechair_white" 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 03d4de26..2de807b4 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 9c00ee38..8e801de6 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/tables_racks.dm b/code/game/objects/structures/tables_racks.dm index 5a1d266f..3341cc28 100644 --- a/code/game/objects/structures/tables_racks.dm +++ b/code/game/objects/structures/tables_racks.dm @@ -19,9 +19,11 @@ anchored = 1.0 layer = 2.8 throwpass = 1 //You can throw objects over this, despite it's density.") + climbable = 1 + var/parts = /obj/item/weapon/table_parts var/flipped = 0 - var/health = 200 + var/health = 100 /obj/structure/table/proc/update_adjacent() for(var/direction in list(1,2,4,8,5,6,9,10)) @@ -274,7 +276,6 @@ /obj/structure/table/attack_alien(mob/user) visible_message("[user] slices [src] apart!") - destroy() /obj/structure/table/attack_animal(mob/living/simple_animal/user) if(user.wall_smash) @@ -298,6 +299,8 @@ return (check_cover(mover,target)) if(istype(mover) && mover.checkpass(PASSTABLE)) return 1 + if(locate(/obj/structure/table) in get_turf(mover)) + return 1 if (flipped) if (get_dir(loc, target) == dir) return !density @@ -311,14 +314,14 @@ if (get_dist(P.starting, loc) <= 1) //Tables won't help you if people are THIS close return 1 if (get_turf(P.original) == cover) - var/chance = 30 + var/chance = 20 if (ismob(P.original)) var/mob/M = P.original if (M.lying) - chance += 50 //Lying down lets you catch less bullets + chance += 20 //Lying down lets you catch less bullets if(flipped) if(get_dir(loc, from) == dir) //Flipped tables catch mroe bullets - chance += 50 + chance += 20 else return 1 //But only from one side if(prob(chance)) @@ -343,8 +346,9 @@ return 1 /obj/structure/table/MouseDrop_T(obj/O as obj, mob/user as mob) + if ((!( istype(O, /obj/item/weapon) ) || user.get_active_hand() != O)) - return + return ..() if(isrobot(user)) return user.drop_item() @@ -373,7 +377,7 @@ G.affecting.loc = src.loc G.affecting.Weaken(5) visible_message("\red [G.assailant] puts [G.affecting] on \the [src].") - del(W) + del(W) return if (istype(W, /obj/item/weapon/wrench)) @@ -414,55 +418,26 @@ return 0 return T.straight_table_check(direction) -/obj/structure/table/verb/can_touch(var/mob/user) - if (!user) - return 0 - if (user.stat) //zombie goasts go away - return 0 - if (issilicon(user)) - user << "You need hands for this." - return 0 - return 1 - - -/obj/structure/table/verb/do_climb() - set name = "Climb table" - set desc = "Climbs onto a table." - set category = "Object" - set src in oview(1) - - if (!can_touch(usr)) - return - - usr.visible_message("[usr] starts climbing onto \the [src]!") - - if(!do_after(usr,50)) - return - - usr.loc = get_turf(src) - for(var/mob/living/M in get_turf(src)) - M.Weaken(5) - if (get_turf(usr) == get_turf(src)) - usr.visible_message("[usr] climbs onto \the [src]!") - /obj/structure/table/verb/do_flip() set name = "Flip table" set desc = "Flips a non-reinforced table" set category = "Object" set src in oview(1) - if(ismouse(usr)) - return - if (!can_touch(usr)) + + if (!can_touch(usr) || ismouse(usr)) return + if(!flip(get_cardinal_dir(usr,src))) usr << "It won't budge." - else - usr.visible_message("[usr] flips \the [src]!") - for(var/mob/living/M in get_turf(src)) - M.Weaken(5) - M << "\red You topple as \the [src] moves under you!" return + usr.visible_message("[usr] flips \the [src]!") + + if(climbable) + structure_shaken() + + return + /obj/structure/table/proc/unflipping_check(var/direction) for(var/mob/M in oview(src,0)) return 0 @@ -545,7 +520,7 @@ desc = "Do not apply fire to this. Rumour says it burns easily." icon_state = "wood_table" parts = /obj/item/weapon/table_parts/wood - health = 100 + health = 50 /* * Reinforced tables */ @@ -553,7 +528,7 @@ name = "reinforced table" desc = "A version of the four legged table. It is stronger." icon_state = "reinf_table" - health = 400 + health = 200 var/status = 2 parts = /obj/item/weapon/table_parts/reinforced diff --git a/code/game/objects/structures/tank_dispenser.dm b/code/game/objects/structures/tank_dispenser.dm index 1d2a0c18..987aa0ea 100644 --- a/code/game/objects/structures/tank_dispenser.dm +++ b/code/game/objects/structures/tank_dispenser.dm @@ -50,6 +50,8 @@ oxytanks.Add(I) oxygentanks++ user << "You put [I] in [src]." + if(oxygentanks < 5) + update_icon() else user << "[src] is full." updateUsrDialog() @@ -61,6 +63,8 @@ platanks.Add(I) plasmatanks++ user << "You put [I] in [src]." + if(oxygentanks < 6) + update_icon() else user << "[src] is full." updateUsrDialog() diff --git a/code/game/objects/structures/target_stake.dm b/code/game/objects/structures/target_stake.dm index ff11c310..dc42bac3 100644 --- a/code/game/objects/structures/target_stake.dm +++ b/code/game/objects/structures/target_stake.dm @@ -49,7 +49,7 @@ user.put_in_hands(pinned_target) user << "You take the target out of the stake." else - pinned_target.loc = get_turf_loc(user) + pinned_target.loc = get_turf(user) user << "You take the target out of the stake." pinned_target = null diff --git a/code/game/objects/structures/transit_tubes.dm b/code/game/objects/structures/transit_tubes.dm index 29bbed2e..aed971b2 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.toxins = air_contents.toxins - 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 440c8556..adde9371 100644 --- a/code/game/objects/structures/watercloset.dm +++ b/code/game/objects/structures/watercloset.dm @@ -76,7 +76,7 @@ else user << "You need a tighter grip." - if(cistern) + if(cistern && !istype(user,/mob/living/silicon/robot)) //STOP PUTTING YOUR MODULES IN THE TOILET. if(I.w_class > 3) user << "\The [I] does not fit." return @@ -153,7 +153,7 @@ if(I.type == /obj/item/device/analyzer) user << "The water temperature seems to be [watertemp]." if(istype(I, /obj/item/weapon/wrench)) - user << "You begin to adjust the temperature valve with the [I]." + user << "You begin to adjust the temperature valve with \the [I]." if(do_after(user, 50)) switch(watertemp) if("normal") @@ -162,7 +162,8 @@ watertemp = "boiling" if("boiling") watertemp = "normal" - user.visible_message("[user] adjusts the shower with the [I].", "You adjust the shower with the [I].") + user.visible_message("[user] adjusts the shower with \the [I].", "You adjust the shower with \the [I].") + add_fingerprint(user) /obj/machinery/shower/update_icon() //this is terribly unreadable, but basically it makes the shower mist up overlays.Cut() //once it's been on for a while, in addition to handling the water overlay. @@ -189,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)) @@ -205,77 +206,76 @@ /obj/machinery/shower/proc/wash(atom/movable/O as obj|mob) if(!on) return - if(isliving(O)) - var/mob/living/L = O - L.ExtinguishMob() - L.fire_stacks = -20 //Douse ourselves with water to avoid fire more easily - L << "You've been drenched in water!" - if(iscarbon(O)) - var/mob/living/carbon/M = O - if(M.r_hand) - M.r_hand.clean_blood() - if(M.l_hand) - M.l_hand.clean_blood() - if(M.back) - if(M.back.clean_blood()) - M.update_inv_back(0) - if(ishuman(M)) - var/mob/living/carbon/human/H = M - var/washgloves = 1 - var/washshoes = 1 - var/washmask = 1 - var/washears = 1 - var/washglasses = 1 + if(iscarbon(O)) + var/mob/living/carbon/M = O + if(M.r_hand) + M.r_hand.clean_blood() + if(M.l_hand) + M.l_hand.clean_blood() + if(M.back) + if(M.back.clean_blood()) + M.update_inv_back(0) + if(ishuman(M)) + var/mob/living/carbon/human/H = M + var/washgloves = 1 + var/washshoes = 1 + var/washmask = 1 + var/washears = 1 + var/washglasses = 1 - if(H.wear_suit) - washgloves = !(H.wear_suit.flags_inv & HIDEGLOVES) - washshoes = !(H.wear_suit.flags_inv & HIDESHOES) + if(H.wear_suit) + washgloves = !(H.wear_suit.flags_inv & HIDEGLOVES) + washshoes = !(H.wear_suit.flags_inv & HIDESHOES) - if(H.head) - washmask = !(H.head.flags_inv & HIDEMASK) - washglasses = !(H.head.flags_inv & HIDEEYES) - washears = !(H.head.flags_inv & HIDEEARS) + if(H.head) + washmask = !(H.head.flags_inv & HIDEMASK) + washglasses = !(H.head.flags_inv & HIDEEYES) + washears = !(H.head.flags_inv & HIDEEARS) - if(H.wear_mask) - if (washears) - washears = !(H.wear_mask.flags_inv & HIDEEARS) - if (washglasses) - washglasses = !(H.wear_mask.flags_inv & HIDEEYES) - - if(H.head) - if(H.head.clean_blood()) - H.update_inv_head(0) - if(H.wear_suit) - if(H.wear_suit.clean_blood()) - H.update_inv_wear_suit(0) - else if(H.w_uniform) - if(H.w_uniform.clean_blood()) - H.update_inv_w_uniform(0) - if(H.gloves && washgloves) - if(H.gloves.clean_blood()) - H.update_inv_gloves(0) - if(H.shoes && washshoes) - if(H.shoes.clean_blood()) - H.update_inv_shoes(0) - if(H.wear_mask && washmask) - if(H.wear_mask.clean_blood()) - H.update_inv_wear_mask(0) - if(H.glasses && washglasses) - if(H.glasses.clean_blood()) - H.update_inv_glasses(0) -/* if(H.ears && washears) - if(H.ears.clean_blood()) - H.update_inv_ears(0)*/ - if(H.belt) - if(H.belt.clean_blood()) - H.update_inv_belt(0) - else - if(M.wear_mask) //if the mob is not human, it cleans the mask without asking for bitflags - if(M.wear_mask.clean_blood()) - M.update_inv_wear_mask(0) + if(H.wear_mask) + if (washears) + washears = !(H.wear_mask.flags_inv & HIDEEARS) + if (washglasses) + washglasses = !(H.wear_mask.flags_inv & HIDEEYES) + if(H.head) + if(H.head.clean_blood()) + H.update_inv_head(0) + if(H.wear_suit) + if(H.wear_suit.clean_blood()) + H.update_inv_wear_suit(0) + else if(H.w_uniform) + if(H.w_uniform.clean_blood()) + H.update_inv_w_uniform(0) + if(H.gloves && washgloves) + if(H.gloves.clean_blood()) + H.update_inv_gloves(0) + if(H.shoes && washshoes) + if(H.shoes.clean_blood()) + H.update_inv_shoes(0) + if(H.wear_mask && washmask) + if(H.wear_mask.clean_blood()) + H.update_inv_wear_mask(0) + if(H.glasses && washglasses) + if(H.glasses.clean_blood()) + H.update_inv_glasses(0) + if(H.l_ear && washears) + if(H.l_ear.clean_blood()) + H.update_inv_ears(0) + if(H.r_ear && washears) + if(H.r_ear.clean_blood()) + H.update_inv_ears(0) + if(H.belt) + if(H.belt.clean_blood()) + H.update_inv_belt(0) + H.clean_blood(washshoes) else - O.clean_blood() + if(M.wear_mask) //if the mob is not human, it cleans the mask without asking for bitflags + if(M.wear_mask.clean_blood()) + M.update_inv_wear_mask(0) + M.clean_blood() + else + O.clean_blood() if(isturf(loc)) var/turf/tile = loc @@ -323,47 +323,49 @@ anchored = 1 var/busy = 0 //Something's being washed at the moment -/obj/structure/sink/attack_hand(mob/M as mob) - if(isrobot(M) || isAI(M)) +/obj/structure/sink/attack_hand(mob/user as mob) + if (hasorgans(user)) + var/datum/organ/external/temp = user:organs_by_name["r_hand"] + if (user.hand) + temp = user:organs_by_name["l_hand"] + if(temp && !temp.is_usable()) + user << "You try to move your [temp.display_name], but cannot!" + return + + if(isrobot(user) || isAI(user)) + return + + if(!Adjacent(user)) return if(busy) - M << "\red Someone's already washing here." + user << "\red Someone's already washing here." return - var/turf/location = M.loc - if(!isturf(location)) return usr << "\blue You start washing your hands." busy = 1 sleep(40) busy = 0 - if(M.loc != location) return //Person has moved away from the sink - - M.clean_blood() - if(ishuman(M)) - M:update_inv_gloves() - if(ishuman(M)) - var/mob/living/carbon/human/B = M - if(B.gloves) - var/obj/item/clothing/gloves/C = B.gloves - C.gsr = 0 - else - B.gsr = 0 + if(!Adjacent(user)) return //Person has moved away from the sink + user.clean_blood() + if(ishuman(user)) + user:update_inv_gloves() for(var/mob/V in viewers(src, null)) - V.show_message("\blue [M] washes their hands using \the [src].") + V.show_message("\blue [user] washes their hands using \the [src].") + /obj/structure/sink/attackby(obj/item/O as obj, mob/user as mob) if(busy) user << "\red Someone's already washing here." return - if (istype(O, /obj/item/weapon/reagent_containers)) - var/obj/item/weapon/reagent_containers/RG = O + var/obj/item/weapon/reagent_containers/RG = O + if (istype(RG) && RG.is_open_container()) RG.reagents.add_reagent("water", min(RG.volume - RG.reagents.total_volume, RG.amount_per_transfer_from_this)) - user.visible_message("\blue [user] fills the [RG] using \the [src].","\blue You fill the [RG] using \the [src].") + user.visible_message("\blue [user] fills \the [RG] using \the [src].","\blue You fill \the [RG] using \the [src].") return else if (istype(O, /obj/item/weapon/melee/baton)) diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index 755641f4..5c982710 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -162,12 +162,15 @@ /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)) /obj/structure/window/attackby(obj/item/W as obj, mob/user as mob) if(!istype(W)) return//I really wish I did not need this + if(W.flags & NOBLUDGEON) return if (istype(W, /obj/item/weapon/grab) && get_dist(src,user)<2) var/obj/item/weapon/grab/G = W @@ -334,7 +337,7 @@ //checks if this window is full-tile one /obj/structure/window/proc/is_fulltile() - if(dir in list(5,6,9,10)) + if(dir & (dir - 1)) return 1 return 0 diff --git a/code/game/objects/weapons.dm b/code/game/objects/weapons.dm index f9404c18..b62efa1c 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 80e66c52..2ab44276 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/sound.dm b/code/game/sound.dm index 6e0ba0dd..36bce87a 100644 --- a/code/game/sound.dm +++ b/code/game/sound.dm @@ -9,7 +9,7 @@ var/list/hiss_sound = list('sound/voice/hiss1.ogg','sound/voice/hiss2.ogg','soun var/list/page_sound = list('sound/effects/pageturn1.ogg', 'sound/effects/pageturn2.ogg','sound/effects/pageturn3.ogg') //var/list/gun_sound = list('sound/weapons/Gunshot.ogg', 'sound/weapons/Gunshot2.ogg','sound/weapons/Gunshot3.ogg','sound/weapons/Gunshot4.ogg') -/proc/playsound(var/atom/source, soundin, vol as num, vary, extrarange as num, falloff) +/proc/playsound(var/atom/source, soundin, vol as num, vary, extrarange as num, falloff, var/is_global) soundin = get_sfx(soundin) // same sound for everyone @@ -25,15 +25,17 @@ var/list/page_sound = list('sound/effects/pageturn1.ogg', 'sound/effects/pagetur var/mob/M = P if(!M || !M.client) continue - if(get_dist(M, turf_source) <= world.view + extrarange) + + var/distance = get_dist(M, turf_source) + if(distance <= (world.view + extrarange) * 3) var/turf/T = get_turf(M) + if(T && T.z == turf_source.z) - M.playsound_local(turf_source, soundin, vol, vary, frequency, falloff) + M.playsound_local(turf_source, soundin, vol, vary, frequency, falloff, is_global) -var/const/FALLOFF_SOUNDS = 1 -var/const/SURROUND_CAP = 7 +var/const/FALLOFF_SOUNDS = 0.5 -/mob/proc/playsound_local(var/turf/turf_source, soundin, vol as num, vary, frequency, falloff) +/mob/proc/playsound_local(var/turf/turf_source, soundin, vol as num, vary, frequency, falloff, is_global) if(!src.client || ear_deaf > 0) return soundin = get_sfx(soundin) @@ -41,7 +43,7 @@ var/const/SURROUND_CAP = 7 S.wait = 0 //No queue S.channel = 0 //Any channel S.volume = vol - + S.environment = -1 if (vary) if(frequency) S.frequency = frequency @@ -51,17 +53,43 @@ var/const/SURROUND_CAP = 7 if(isturf(turf_source)) // 3D sounds, the technology is here! var/turf/T = get_turf(src) + + //sound volume falloff with distance + var/distance = get_dist(T, turf_source) + + S.volume -= max(distance - world.view, 0) * 2 //multiplicative falloff to add on top of natural audio falloff. + + //sound volume falloff with pressure + var/pressure_factor = 1.0 + + var/datum/gas_mixture/hearer_env = T.return_air() + var/datum/gas_mixture/source_env = turf_source.return_air() + + if (hearer_env && source_env) + var/pressure = min(hearer_env.return_pressure(), source_env.return_pressure()) + + if (pressure < ONE_ATMOSPHERE) + pressure_factor = max((pressure - SOUND_MINIMUM_PRESSURE)/(ONE_ATMOSPHERE - SOUND_MINIMUM_PRESSURE), 0) + else //in space + pressure_factor = 0 + + if (distance <= 1) + pressure_factor = max(pressure_factor, 0.15) //hearing through contact + + S.volume *= pressure_factor + + if (S.volume <= 0) + return //no volume means no sound + var/dx = turf_source.x - T.x // Hearing from the right/left - - S.x = round(max(-SURROUND_CAP, min(SURROUND_CAP, dx)), 1) - + S.x = dx var/dz = turf_source.y - T.y // Hearing from infront/behind - S.z = round(max(-SURROUND_CAP, min(SURROUND_CAP, dz)), 1) - + S.z = dz // The y value is for above your head, but there is no ceiling in 2d spessmens. S.y = 1 S.falloff = (falloff ? falloff : FALLOFF_SOUNDS) - + if(!is_global) + S.environment = 2 src << S /client/proc/playtitlemusic() diff --git a/code/game/supplyshuttle.dm b/code/game/supplyshuttle.dm index 4a2bcb39..94b61351 100644 --- a/code/game/supplyshuttle.dm +++ b/code/game/supplyshuttle.dm @@ -4,7 +4,7 @@ #define SUPPLY_STATION_AREATYPE "/area/supply/station" //Type of the supply shuttle area for station #define SUPPLY_DOCK_AREATYPE "/area/supply/dock" //Type of the supply shuttle area for dock -var/datum/controller/supply_shuttle/supply_shuttle = new() +var/datum/controller/supply/supply_controller = new() var/list/mechtoys = list( /obj/item/toy/prize/ripley, @@ -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 @@ -54,7 +54,10 @@ var/list/mechtoys = list( if (istype(A, /obj/structure/stool/bed) && B.buckled_mob)//if it's a bed/chair and someone is buckled, it will not pass return 0 - else if(istype(A, /mob/living)) // You Shall Not Pass! +/* if(istype(A, /obj/vehicle)) //no vehicles + return 0*/ + + if(istype(A, /mob/living)) // You Shall Not Pass! var/mob/living/M = A if(!M.lying && !istype(M, /mob/living/carbon/monkey) && !istype(M, /mob/living/carbon/slime) && !istype(M, /mob/living/simple_animal/mouse) && !istype(M, /mob/living/silicon/robot/drone)) //If your not laying down, or a small creature, no pass. return 0 @@ -125,7 +128,7 @@ var/list/mechtoys = list( var/orderedby = null var/comment = null -/datum/controller/supply_shuttle +/datum/controller/supply var/processing = 1 var/processing_interval = 300 var/iteration = 0 @@ -141,11 +144,8 @@ var/list/mechtoys = list( var/list/requestlist = list() var/list/supply_packs = list() //shuttle movement - var/at_station = 0 var/movetime = 1200 - var/moving = 0 - var/eta_timeofday - var/eta + var/datum/shuttle/ferry/supply/shuttle New() ordernum = rand(1,9000) @@ -163,52 +163,8 @@ var/list/mechtoys = list( iteration++ points += points_per_process - if(moving == 1) - var/ticksleft = (eta_timeofday - world.timeofday) - if(ticksleft > 0) - eta = round(ticksleft/600,1) - else - eta = 0 - send() - - sleep(processing_interval) - proc/send() - var/area/from - var/area/dest - var/area/the_shuttles_way - switch(at_station) - if(1) - from = locate(SUPPLY_STATION_AREATYPE) - dest = locate(SUPPLY_DOCK_AREATYPE) - the_shuttles_way = from - at_station = 0 - if(0) - from = locate(SUPPLY_DOCK_AREATYPE) - dest = locate(SUPPLY_STATION_AREATYPE) - the_shuttles_way = dest - at_station = 1 - moving = 0 - - //Do I really need to explain this loop? - for(var/mob/living/unlucky_person in the_shuttles_way) - unlucky_person.gib() - - from.move_contents_to(dest) - - //Check whether the shuttle is allowed to move - proc/can_move() - if(moving) return 0 - - var/area/shuttle = locate(/area/supply/station) - if(!shuttle) return 0 - - if(forbidden_atoms_check(shuttle)) - return 0 - - return 1 - //To stop things being sent to centcomm which should not be sent to centcomm. Recursively checks for these types. proc/forbidden_atoms_check(atom/A) if(istype(A,/mob/living)) @@ -227,20 +183,18 @@ var/list/mechtoys = list( //Sellin proc/sell() - var/shuttle_at - if(at_station) shuttle_at = SUPPLY_STATION_AREATYPE - else shuttle_at = SUPPLY_DOCK_AREATYPE - - var/area/shuttle = locate(shuttle_at) - if(!shuttle) return + var/area/area_shuttle = shuttle.get_location_area() + if(!area_shuttle) return var/plasma_count = 0 - for(var/atom/movable/MA in shuttle) + for(var/atom/movable/MA in area_shuttle) if(MA.anchored) continue // Must be in a crate! if(istype(MA,/obj/structure/closet/crate)) + callHook("sell_crate", list(MA, area_shuttle)) + points += points_per_crate var/find_slip = 1 @@ -267,16 +221,12 @@ var/list/mechtoys = list( proc/buy() if(!shoppinglist.len) return - var/shuttle_at - if(at_station) shuttle_at = SUPPLY_STATION_AREATYPE - else shuttle_at = SUPPLY_DOCK_AREATYPE - - var/area/shuttle = locate(shuttle_at) - if(!shuttle) return + var/area/area_shuttle = shuttle.get_location_area() + if(!area_shuttle) return var/list/clear_turfs = list() - for(var/turf/T in shuttle) + for(var/turf/T in area_shuttle) if(T.density || T.contents.len) continue clear_turfs += T @@ -298,7 +248,7 @@ var/list/mechtoys = list( slip.info = "

[command_name()] Shipping Manifest



" slip.info +="Order #[SO.ordernum]
" slip.info +="Destination: [station_name]
" - slip.info +="[supply_shuttle.shoppinglist.len] PACKAGES IN THIS SHIPMENT
" + slip.info +="[shoppinglist.len] PACKAGES IN THIS SHIPMENT
" slip.info +="CONTENTS:
    " //spawn the stuff, finish generating the manifest while you're at it @@ -327,7 +277,7 @@ var/list/mechtoys = list( slip.info += "CHECK CONTENTS AND STAMP BELOW THE LINE TO CONFIRM RECEIPT OF GOODS
    " if (SP.contraband) slip.loc = null //we are out of blanks for Form #44-D Ordering Illicit Drugs. - supply_shuttle.shoppinglist.Cut() + shoppinglist.Cut() return /obj/item/weapon/paper/manifest @@ -354,9 +304,11 @@ var/list/mechtoys = list( if(temp) dat = temp else - dat += {"
    Supply shuttle
    - Location: [supply_shuttle.moving ? "Moving to station ([supply_shuttle.eta] Mins.)":supply_shuttle.at_station ? "Station":"Dock"]
    -
    Supply points: [supply_shuttle.points]
    + var/datum/shuttle/ferry/supply/shuttle = supply_controller.shuttle + if (shuttle) + dat += {"
    Supply shuttle
    + Location: [shuttle.has_arrive_time() ? "Moving to station ([shuttle.eta_minutes()] Mins.)":shuttle.at_station() ? "Docked":"Away"]
    +
    Supply points: [supply_controller.points]

    \nRequest items

    View approved orders

    View requests

    @@ -378,18 +330,18 @@ var/list/mechtoys = list( //all_supply_groups //Request what? last_viewed_group = "categories" - temp = "Supply points: [supply_shuttle.points]
    " + temp = "Supply points: [supply_controller.points]
    " temp += "Main Menu


    " temp += "Select a category

    " for(var/supply_group_name in all_supply_groups ) temp += "[supply_group_name]
    " else last_viewed_group = href_list["order"] - temp = "Supply points: [supply_shuttle.points]
    " + temp = "Supply points: [supply_controller.points]
    " temp += "Back to all categories


    " temp += "Request from: [last_viewed_group]

    " - for(var/supply_name in supply_shuttle.supply_packs ) - var/datum/supply_packs/N = supply_shuttle.supply_packs[supply_name] + for(var/supply_name in supply_controller.supply_packs ) + var/datum/supply_packs/N = supply_controller.supply_packs[supply_name] if(N.hidden || N.contraband || N.group != last_viewed_group) continue //Have to send the type instead of a reference to temp += "[supply_name] Cost: [N.cost]
    " //the obj because it would get caught by the garbage @@ -400,7 +352,7 @@ var/list/mechtoys = list( return //Find the correct supply_pack datum - var/datum/supply_packs/P = supply_shuttle.supply_packs[href_list["doorder"]] + var/datum/supply_packs/P = supply_controller.supply_packs[href_list["doorder"]] if(!istype(P)) return var/timeout = world.time + 600 @@ -417,11 +369,11 @@ var/list/mechtoys = list( else if(issilicon(usr)) idname = usr.real_name - supply_shuttle.ordernum++ + supply_controller.ordernum++ var/obj/item/weapon/paper/reqform = new /obj/item/weapon/paper(loc) reqform.name = "Requisition Form - [P.name]" reqform.info += "

    [station_name] Supply Requisition Form


    " - reqform.info += "INDEX: #[supply_shuttle.ordernum]
    " + reqform.info += "INDEX: #[supply_controller.ordernum]
    " reqform.info += "REQUESTED BY: [idname]
    " reqform.info += "RANK: [idrank]
    " reqform.info += "REASON: [reason]
    " @@ -437,24 +389,24 @@ var/list/mechtoys = list( //make our supply_order datum var/datum/supply_order/O = new /datum/supply_order() - O.ordernum = supply_shuttle.ordernum + O.ordernum = supply_controller.ordernum O.object = P O.orderedby = idname - supply_shuttle.requestlist += O + supply_controller.requestlist += O temp = "Thanks for your request. The cargo team will process it as soon as possible.
    " temp += "
    Back Main Menu" else if (href_list["vieworders"]) temp = "Current approved orders:

    " - for(var/S in supply_shuttle.shoppinglist) + for(var/S in supply_controller.shoppinglist) var/datum/supply_order/SO = S temp += "[SO.object.name] approved by [SO.orderedby] [SO.comment ? "([SO.comment])":""]
    " temp += "
    OK" else if (href_list["viewrequests"]) temp = "Current requests:

    " - for(var/S in supply_shuttle.requestlist) + for(var/S in supply_controller.requestlist) var/datum/supply_order/SO = S temp += "#[SO.ordernum] - [SO.object.name] requested by [SO.orderedby]
    " temp += "
    OK" @@ -479,15 +431,48 @@ var/list/mechtoys = list( if (temp) dat = temp else - dat += {"
    Supply shuttle
    - \nLocation: [supply_shuttle.moving ? "Moving to station ([supply_shuttle.eta] Mins.)":supply_shuttle.at_station ? "Station":"Away"]
    -
    \nSupply points: [supply_shuttle.points]
    \n
    - [supply_shuttle.moving ? "\n*Must be away to order items*
    \n
    ":supply_shuttle.at_station ? "\n*Must be away to order items*
    \n
    ":"\nOrder items
    \n
    "] - [supply_shuttle.moving ? "\n*Shuttle already called*
    \n
    ":supply_shuttle.at_station ? "\nSend away
    \n
    ":"\nSend to station
    \n
    "] + var/datum/shuttle/ferry/supply/shuttle = supply_controller.shuttle + if (shuttle) + dat += "
    Supply shuttle
    " + dat += "\nLocation: " + if (shuttle.has_arrive_time()) + dat += "In transit ([shuttle.eta_minutes()] Mins.)
    " + else + if (shuttle.at_station()) + if (shuttle.docking_controller) + switch(shuttle.docking_controller.get_docking_status()) + if ("docked") dat += "Docked at station
    " + if ("undocked") dat += "Undocked from station
    " + if ("docking") dat += "Docking with station [shuttle.can_force()? "Force Launch" : ""]
    " + if ("undocking") dat += "Undocking from station [shuttle.can_force()? "Force Launch" : ""]
    " + else + dat += "Station
    " + + if (shuttle.can_launch()) + dat += "Send away" + else if (shuttle.can_cancel()) + dat += "Cancel launch" + else + dat += "*Shuttle is busy*" + dat += "
    \n
    " + else + dat += "Away
    " + if (shuttle.can_launch()) + dat += "Request supply shuttle" + else if (shuttle.can_cancel()) + dat += "Cancel request" + else + dat += "*Shuttle is busy*" + dat += "
    \n
    " + + + dat += {"
    \nSupply points: [supply_controller.points]
    \n
    + \nOrder items
    \n
    \nView requests
    \n
    \nView orders
    \n
    \nClose"} + user << browse(dat, "window=computer;size=575x450") onclose(user, "computer") return @@ -502,8 +487,12 @@ var/list/mechtoys = list( return /obj/machinery/computer/supplycomp/Topic(href, href_list) - if(!supply_shuttle) - world.log << "## ERROR: Eek. The supply_shuttle controller datum is missing somehow." + if(!supply_controller) + world.log << "## ERROR: Eek. The supply_controller controller datum is missing somehow." + return + var/datum/shuttle/ferry/supply/shuttle = supply_controller.shuttle + if (!shuttle) + world.log << "## ERROR: Eek. The supply/shuttle datum is missing somehow." return if(..()) return @@ -513,46 +502,48 @@ var/list/mechtoys = list( //Calling the shuttle if(href_list["send"]) - if(!supply_shuttle.can_move()) - temp = "For safety reasons the automated supply shuttle cannot transport live organisms, classified nuclear weaponry or homing beacons.

    OK" - - else if(supply_shuttle.at_station) - supply_shuttle.moving = -1 - supply_shuttle.sell() - supply_shuttle.send() - temp = "The supply shuttle has departed.

    OK" + if(shuttle.at_station()) + if (shuttle.forbidden_atoms_check()) + temp = "For safety reasons the automated supply shuttle cannot transport live organisms, classified nuclear weaponry or homing beacons.

    OK" + else + shuttle.launch(src) + temp = "Initiating launch sequence. \[Force Launch\]

    OK" else - supply_shuttle.moving = 1 - supply_shuttle.buy() - supply_shuttle.eta_timeofday = (world.timeofday + supply_shuttle.movetime) % 864000 - temp = "The supply shuttle has been called and will arrive in [round(supply_shuttle.movetime/600,1)] minutes.

    OK" + shuttle.launch(src) + temp = "The supply shuttle has been called and will arrive in approximately [round(supply_controller.movetime/600,1)] minutes.

    OK" post_signal("supply") + if (href_list["force_send"]) + shuttle.force_launch(src) + + if (href_list["cancel_send"]) + shuttle.cancel_launch(src) + else if (href_list["order"]) - if(supply_shuttle.moving) return + //if(!shuttle.idle()) return //this shouldn't be necessary it seems if(href_list["order"] == "categories") //all_supply_groups //Request what? last_viewed_group = "categories" - temp = "Supply points: [supply_shuttle.points]
    " + temp = "Supply points: [supply_controller.points]
    " temp += "Main Menu


    " temp += "Select a category

    " for(var/supply_group_name in all_supply_groups ) temp += "[supply_group_name]
    " else last_viewed_group = href_list["order"] - temp = "Supply points: [supply_shuttle.points]
    " + temp = "Supply points: [supply_controller.points]
    " temp += "Back to all categories


    " temp += "Request from: [last_viewed_group]

    " - for(var/supply_name in supply_shuttle.supply_packs ) - var/datum/supply_packs/N = supply_shuttle.supply_packs[supply_name] + for(var/supply_name in supply_controller.supply_packs ) + var/datum/supply_packs/N = supply_controller.supply_packs[supply_name] if((N.hidden && !hacked) || (N.contraband && !can_order_contraband) || N.group != last_viewed_group) continue //Have to send the type instead of a reference to temp += "[supply_name] Cost: [N.cost]
    " //the obj because it would get caught by the garbage - /*temp = "Supply points: [supply_shuttle.points]


    Request what?

    " + /*temp = "Supply points: [supply_controller.points]


    Request what?

    " - for(var/supply_name in supply_shuttle.supply_packs ) - var/datum/supply_packs/N = supply_shuttle.supply_packs[supply_name] + for(var/supply_name in supply_controller.supply_packs ) + var/datum/supply_packs/N = supply_controller.supply_packs[supply_name] if(N.hidden && !hacked) continue if(N.contraband && !can_order_contraband) continue temp += "[supply_name] Cost: [N.cost]
    " //the obj because it would get caught by the garbage @@ -565,7 +556,7 @@ var/list/mechtoys = list( return //Find the correct supply_pack datum - var/datum/supply_packs/P = supply_shuttle.supply_packs[href_list["doorder"]] + var/datum/supply_packs/P = supply_controller.supply_packs[href_list["doorder"]] if(!istype(P)) return var/timeout = world.time + 600 @@ -582,11 +573,11 @@ var/list/mechtoys = list( else if(issilicon(usr)) idname = usr.real_name - supply_shuttle.ordernum++ + supply_controller.ordernum++ var/obj/item/weapon/paper/reqform = new /obj/item/weapon/paper(loc) reqform.name = "Requisition Form - [P.name]" reqform.info += "

    [station_name] Supply Requisition Form


    " - reqform.info += "INDEX: #[supply_shuttle.ordernum]
    " + reqform.info += "INDEX: #[supply_controller.ordernum]
    " reqform.info += "REQUESTED BY: [idname]
    " reqform.info += "RANK: [idrank]
    " reqform.info += "REASON: [reason]
    " @@ -602,10 +593,10 @@ var/list/mechtoys = list( //make our supply_order datum var/datum/supply_order/O = new /datum/supply_order() - O.ordernum = supply_shuttle.ordernum + O.ordernum = supply_controller.ordernum O.object = P O.orderedby = idname - supply_shuttle.requestlist += O + supply_controller.requestlist += O temp = "Order request placed.
    " temp += "
    Back | Main Menu | Authorize Order" @@ -616,15 +607,15 @@ var/list/mechtoys = list( var/datum/supply_order/O var/datum/supply_packs/P temp = "Invalid Request" - for(var/i=1, i<=supply_shuttle.requestlist.len, i++) - var/datum/supply_order/SO = supply_shuttle.requestlist[i] + for(var/i=1, i<=supply_controller.requestlist.len, i++) + var/datum/supply_order/SO = supply_controller.requestlist[i] if(SO.ordernum == ordernum) O = SO P = O.object - if(supply_shuttle.points >= P.cost) - supply_shuttle.requestlist.Cut(i,i+1) - supply_shuttle.points -= P.cost - supply_shuttle.shoppinglist += O + if(supply_controller.points >= P.cost) + supply_controller.requestlist.Cut(i,i+1) + supply_controller.points -= P.cost + supply_controller.shoppinglist += O temp = "Thanks for your order.
    " temp += "
    Back Main Menu" else @@ -634,7 +625,7 @@ var/list/mechtoys = list( else if (href_list["vieworders"]) temp = "Current approved orders:

    " - for(var/S in supply_shuttle.shoppinglist) + for(var/S in supply_controller.shoppinglist) var/datum/supply_order/SO = S temp += "#[SO.ordernum] - [SO.object.name] approved by [SO.orderedby][SO.comment ? " ([SO.comment])":""]
    "// (Cancel)
    " temp += "
    OK" @@ -652,9 +643,9 @@ var/list/mechtoys = list( */ else if (href_list["viewrequests"]) temp = "Current requests:

    " - for(var/S in supply_shuttle.requestlist) + for(var/S in supply_controller.requestlist) var/datum/supply_order/SO = S - temp += "#[SO.ordernum] - [SO.object.name] requested by [SO.orderedby] [supply_shuttle.moving ? "":supply_shuttle.at_station ? "":"Approve Remove"]
    " + temp += "#[SO.ordernum] - [SO.object.name] requested by [SO.orderedby] Approve Remove
    " temp += "
    Clear list" temp += "
    OK" @@ -662,16 +653,16 @@ var/list/mechtoys = list( else if (href_list["rreq"]) var/ordernum = text2num(href_list["rreq"]) temp = "Invalid Request.
    " - for(var/i=1, i<=supply_shuttle.requestlist.len, i++) - var/datum/supply_order/SO = supply_shuttle.requestlist[i] + for(var/i=1, i<=supply_controller.requestlist.len, i++) + var/datum/supply_order/SO = supply_controller.requestlist[i] if(SO.ordernum == ordernum) - supply_shuttle.requestlist.Cut(i,i+1) + supply_controller.requestlist.Cut(i,i+1) temp = "Request removed.
    " break temp += "
    Back Main Menu" else if (href_list["clearreq"]) - supply_shuttle.requestlist.Cut() + supply_controller.requestlist.Cut() temp = "List cleared.
    " temp += "
    OK" diff --git a/code/game/turfs/simulated.dm b/code/game/turfs/simulated.dm index c4ed38d2..dadcb84b 100644 --- a/code/game/turfs/simulated.dm +++ b/code/game/turfs/simulated.dm @@ -73,6 +73,12 @@ bloodDNA = null + var/noslip = 0 + for (var/obj/structure/stool/bed/chair/C in loc) + if (C.buckled_mob == M) + noslip = 1 + if (noslip) + return // no slipping while sitting in a chair, plz switch (src.wet) if(1) if(istype(M, /mob/living/carbon/human)) // Added check since monkeys don't have shoes @@ -99,7 +105,7 @@ return if(2) //lube //can cause infinite loops - needs work - if(!istype(M, /mob/living/carbon/slime)) + if(!istype(M, /mob/living/carbon/slime) && !M.buckled) M.stop_pulling() step(M, M.dir) spawn(1) step(M, M.dir) diff --git a/code/game/turfs/simulated/floor.dm b/code/game/turfs/simulated/floor.dm index b483e514..58383bfd 100644 --- a/code/game/turfs/simulated/floor.dm +++ b/code/game/turfs/simulated/floor.dm @@ -75,6 +75,21 @@ var/list/wood_icons = list("wood","wood-broken") src.hotspot_expose(1000,CELL_VOLUME) return +/turf/simulated/floor/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) + if(!burnt && prob(5)) + burn_tile() + else if(prob(1) && !is_plating()) + make_plating() + burn_tile() + return + +/turf/simulated/floor/adjacent_fire_act(turf/simulated/floor/adj_turf, datum/gas_mixture/adj_air, adj_temp, adj_volume) + var/dir_to = get_dir(src, adj_turf) + + for(var/obj/structure/window/W in src) + if(W.dir == dir_to || W.is_fulltile()) //Same direction or diagonal (full tile) + W.fire_act(adj_air, adj_temp, adj_volume) + /turf/simulated/floor/blob_act() return diff --git a/code/game/turfs/simulated/floor_types.dm b/code/game/turfs/simulated/floor_types.dm index 5a331b40..cbad3000 100644 --- a/code/game/turfs/simulated/floor_types.dm +++ b/code/game/turfs/simulated/floor_types.dm @@ -53,6 +53,10 @@ icon_state = "engine" thermal_conductivity = 0.025 heat_capacity = 325000 + intact = 0 + +/turf/simulated/floor/engine/nitrogen + oxygen = 0 /turf/simulated/floor/engine/attackby(obj/item/weapon/C as obj, mob/user as mob) if(!C) @@ -77,14 +81,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" @@ -142,10 +139,19 @@ icon = 'icons/turf/floors.dmi' icon_state = "plating" +/turf/simulated/shuttle/plating/vox //Vox skipjack plating + oxygen = 0 + nitrogen = MOLES_N2STANDARD + MOLES_O2STANDARD + /turf/simulated/shuttle/floor4 // Added this floor tile so that I have a seperate turf to check in the shuttle -- Polymorph name = "Brig floor" // Also added it into the 2x3 brig area of the shuttle. icon_state = "floor4" +/turf/simulated/shuttle/floor4/vox //Vox skipjack floors + 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.dm b/code/game/turfs/simulated/walls.dm index b798f636..40cfb6ca 100644 --- a/code/game/turfs/simulated/walls.dm +++ b/code/game/turfs/simulated/walls.dm @@ -4,6 +4,15 @@ icon = 'icons/turf/walls.dmi' var/mineral = "metal" var/rotting = 0 + + var/damage = 0 + var/damage_cap = 100 //Wall will break down to girders if damage reaches this point + + var/damage_overlay + var/global/damage_overlays[8] + + var/max_temperature = 1800 //K, walls will take damage if they're next to a fire hotter than this + opacity = 1 density = 1 blocks_air = 1 @@ -13,6 +22,89 @@ var/walltype = "metal" +/turf/simulated/wall/Del() + for(var/obj/effect/E in src) if(E.name == "Wallrot") del E + ..() + +/turf/simulated/wall/ChangeTurf(var/newtype) + for(var/obj/effect/E in src) if(E.name == "Wallrot") del E + ..(newtype) + +//Appearance + +/turf/simulated/wall/examine() + . = ..() + + if(!damage) + usr << "It looks fully intact." + else + var/dam = damage / damage_cap + if(dam <= 0.3) + usr << "It looks slightly damaged." + else if(dam <= 0.6) + usr << "It looks moderately damaged." + else + usr << "It looks heavily damaged." + + if(rotting) + usr << "There is fungus growing on [src]." + +/turf/simulated/wall/proc/update_icon() + if(!damage_overlays[1]) //list hasn't been populated + generate_overlays() + + if(!damage) + overlays.Cut() + return + + var/overlay = round(damage / damage_cap * damage_overlays.len) + 1 + if(overlay > damage_overlays.len) + overlay = damage_overlays.len + + if(damage_overlay && overlay == damage_overlay) //No need to update. + return + + overlays.Cut() + overlays += damage_overlays[overlay] + damage_overlay = overlay + + return + +/turf/simulated/wall/proc/generate_overlays() + var/alpha_inc = 256 / damage_overlays.len + + for(var/i = 1; i <= damage_overlays.len; i++) + var/image/img = image(icon = 'icons/turf/walls.dmi', icon_state = "overlay_damage") + img.blend_mode = BLEND_MULTIPLY + img.alpha = (i * alpha_inc) - 1 + damage_overlays[i] = img + +//Damage + +/turf/simulated/wall/proc/take_damage(dam) + if(dam) + damage = max(0, damage + dam) + update_damage() + return + +/turf/simulated/wall/proc/update_damage() + var/cap = damage_cap + if(rotting) + cap = cap / 10 + + if(damage >= cap) + dismantle_wall() + else + update_icon() + + return + +/turf/simulated/wall/adjacent_fire_act(turf/simulated/floor/adj_turf, datum/gas_mixture/adj_air, adj_temp, adj_volume) + if(adj_temp > max_temperature) + take_damage(rand(10, 20) * (adj_temp / max_temperature)) + + return ..() + /turf/simulated/wall/proc/dismantle_wall(devastated=0, explode=0) if(istype(src,/turf/simulated/wall/r_wall)) if(!devastated) @@ -64,31 +156,76 @@ ChangeTurf(/turf/simulated/floor/plating) /turf/simulated/wall/ex_act(severity) - if(rotting) severity = 1.0 switch(severity) if(1.0) - //SN src = null src.ChangeTurf(/turf/space) return if(2.0) - if (prob(50)) - dismantle_wall(0,1) + if(prob(75)) + take_damage(rand(150, 250)) else dismantle_wall(1,1) if(3.0) - var/proba - if (istype(src, /turf/simulated/wall/r_wall)) - proba = 15 - else - proba = 40 - if (prob(proba)) - dismantle_wall(0,1) + take_damage(rand(0, 250)) else return /turf/simulated/wall/blob_act() - if(prob(50) || rotting) + take_damage(rand(75, 125)) + return + +// Wall-rot effect, a nasty fungus that destroys walls. +/turf/simulated/wall/proc/rot() + if(!rotting) + rotting = 1 + + var/number_rots = rand(2,3) + for(var/i=0, iThe thermite starts melting through the wall." + + spawn(100) + if(O) del(O) +// F.sd_LumReset() //TODO: ~Carn + return + +/turf/simulated/wall/meteorhit(obj/M as obj) + if (prob(15) && !rotting) dismantle_wall() + else if(prob(70) && !rotting) + ChangeTurf(/turf/simulated/floor/plating) + else + ReplaceWithLattice() + return 0 + +//Interactions /turf/simulated/wall/attack_paw(mob/user as mob) if ((HULK in user.mutations)) @@ -99,6 +236,7 @@ return else usr << text("\blue You punch the wall.") + take_damage(rand(25, 75)) return return src.attack_hand(user) @@ -116,6 +254,7 @@ return else M << text("\blue You smash against the wall.") + take_damage(rand(25, 75)) return M << "\blue You push the wall but nothing happens!" @@ -130,6 +269,7 @@ return else usr << text("\blue You punch the wall.") + take_damage(rand(25, 75)) return if(rotting) @@ -215,17 +355,32 @@ //DECONSTRUCTION if( istype(W, /obj/item/weapon/weldingtool) ) + + var/response = "Dismantle" + if(damage) + response = alert(user, "Would you like to repair or dismantle [src]?", "[src]", "Repair", "Dismantle") + var/obj/item/weapon/weldingtool/WT = W - if( WT.remove_fuel(0,user) ) - user << "You begin slicing through the outer plating." - playsound(src, 'sound/items/Welder.ogg', 100, 1) - sleep(100) - if( !istype(src, /turf/simulated/wall) || !user || !WT || !WT.isOn() || !T ) return + if(WT.remove_fuel(0,user)) + if(response == "Repair") + user << "You start repairing the damage to [src]." + playsound(src, 'sound/items/Welder.ogg', 100, 1) + if(do_after(user, max(5, damage / 5)) && WT && WT.isOn()) + user << "You finish repairing the damage to [src]." + take_damage(-damage) - if( user.loc == T && user.get_active_hand() == WT ) - user << "You remove the outer plating." - dismantle_wall() + else if(response == "Dismantle") + user << "You begin slicing through the outer plating." + playsound(src, 'sound/items/Welder.ogg', 100, 1) + + sleep(100) + if( !istype(src, /turf/simulated/wall) || !user || !WT || !WT.isOn() || !T ) return + + if( user.loc == T && user.get_active_hand() == WT ) + user << "You remove the outer plating." + dismantle_wall() + return else user << "You need more welding fuel to complete this task." return @@ -328,62 +483,3 @@ else return attack_hand(user) return - -// Wall-rot effect, a nasty fungus that destroys walls. -/turf/simulated/wall/proc/rot() - if(!rotting) - rotting = 1 - - var/number_rots = rand(2,3) - for(var/i=0, iThe thermite starts melting through the wall." - - spawn(100) - if(O) del(O) -// F.sd_LumReset() //TODO: ~Carn - return - -/turf/simulated/wall/meteorhit(obj/M as obj) - if (prob(15) && !rotting) - dismantle_wall() - else if(prob(70) && !rotting) - ChangeTurf(/turf/simulated/floor/plating) - else - ReplaceWithLattice() - return 0 - -/turf/simulated/wall/Del() - for(var/obj/effect/E in src) if(E.name == "Wallrot") del E - ..() - -/turf/simulated/wall/ChangeTurf(var/newtype) - for(var/obj/effect/E in src) if(E.name == "Wallrot") del E - ..(newtype) \ No newline at end of file diff --git a/code/game/turfs/simulated/walls_mineral.dm b/code/game/turfs/simulated/walls_mineral.dm index 05450d3a..ccd9dac6 100644 --- a/code/game/turfs/simulated/walls_mineral.dm +++ b/code/game/turfs/simulated/walls_mineral.dm @@ -94,13 +94,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/toxinsToDeduce = 20 - napalm.toxins = toxinsToDeduce - napalm.temperature = 400+T0C - target_tile.assume_air(napalm) + target_tile.assume_gas("toxins", 20, 400+T0C) spawn (0) target_tile.hotspot_expose(temperature, 400) for(var/obj/structure/falsewall/plasma/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/simulated/walls_reinforced.dm b/code/game/turfs/simulated/walls_reinforced.dm index b8da744a..c85dac3f 100644 --- a/code/game/turfs/simulated/walls_reinforced.dm +++ b/code/game/turfs/simulated/walls_reinforced.dm @@ -5,6 +5,9 @@ opacity = 1 density = 1 + damage_cap = 200 + max_temperature = 6000 + walltype = "rwall" var/d_state = 0 @@ -103,6 +106,19 @@ user << "This wall is too thick to slice through. You will need to find a different path." return + if(damage && istype(W, /obj/item/weapon/weldingtool)) + var/obj/item/weapon/weldingtool/WT = W + if(WT.remove_fuel(0,user)) + user << "You start repairing the damage to [src]." + playsound(src, 'sound/items/Welder.ogg', 100, 1) + if(do_after(user, max(5, damage / 5)) && WT && WT.isOn()) + user << "You finish repairing the damage to [src]." + take_damage(-damage) + return + else + user << "You need more welding fuel to complete this task." + return + var/turf/T = user.loc //get user's location for delay checks //DECONSTRUCTION diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 57e9aab9..34639d44 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -22,6 +22,10 @@ var/icon_old = null var/pathweight = 1 + //Mining resource generation stuff. + var/has_resources + var/list/resources + /turf/New() ..() for(var/atom/movable/AM as mob|obj in src) @@ -128,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++ @@ -145,6 +141,9 @@ return return +/turf/proc/adjacent_fire_act(turf/simulated/floor/source, temperature, volume) + return + /turf/proc/is_plating() return 0 /turf/proc/is_asteroid_floor() @@ -215,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]" @@ -243,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() @@ -264,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/game/verbs/ooc.dm b/code/game/verbs/ooc.dm index 1c23f9b9..a756ca4d 100644 --- a/code/game/verbs/ooc.dm +++ b/code/game/verbs/ooc.dm @@ -14,7 +14,7 @@ var/global/normal_ooc_colour = "#002eb8" src << "Guests may not use OOC." return - msg = copytext(sanitize(msg), 1, MAX_MESSAGE_LEN) + msg = trim(copytext(sanitize(msg), 1, MAX_MESSAGE_LEN)) if(!msg) return if(!(prefs.toggles & CHAT_OOC)) @@ -104,7 +104,7 @@ var/global/normal_ooc_colour = "#002eb8" src << "Guests may not use OOC." return - msg = copytext(sanitize(msg), 1, MAX_MESSAGE_LEN) + msg = trim(copytext(sanitize(msg), 1, MAX_MESSAGE_LEN)) if(!msg) return if(!(prefs.toggles & CHAT_LOOC)) diff --git a/code/global.dm b/code/global.dm index 6e696060..47816187 100644 --- a/code/global.dm +++ b/code/global.dm @@ -110,7 +110,6 @@ var/list/bombers = list( ) var/list/admin_log = list ( ) var/list/lastsignalers = list( ) //keeps last 100 signals here in format: "[src] used \ref[src] @ location [src.loc]: [freq]/[code]" var/list/lawchanges = list( ) //Stores who uploaded laws to which silicon-based lifeform, and what the law was -var/list/shuttles = list( ) var/list/reg_dna = list( ) // list/traitobj = list( ) @@ -127,7 +126,12 @@ var/airtunnel_bottom = 72 // default var/list/monkeystart = list() var/list/wizardstart = list() var/list/newplayer_start = list() + +//Spawnpoints. var/list/latejoin = list() +var/list/latejoin_gateway = list() +var/list/latejoin_cryo = list() + var/list/prisonwarp = list() //prisoners go to these var/list/holdingfacility = list() //captured people go here var/list/xeno_spawn = list()//Aliens spawn at these. @@ -143,6 +147,8 @@ var/list/ninjastart = list() // list/traitors = list() //traitor list var/list/cardinal = list( NORTH, SOUTH, EAST, WEST ) var/list/alldirs = list(NORTH, SOUTH, EAST, WEST, NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST) +// reverse_dir[dir] = reverse of dir +var/list/reverse_dir = list(2, 1, 3, 8, 10, 9, 11, 4, 6, 5, 7, 12, 14, 13, 15, 32, 34, 33, 35, 40, 42, 41, 43, 36, 38, 37, 39, 44, 46, 45, 47, 16, 18, 17, 19, 24, 26, 25, 27, 20, 22, 21, 23, 28, 30, 29, 31, 48, 50, 49, 51, 56, 58, 57, 59, 52, 54, 53, 55, 60, 62, 61, 63) var/datum/station_state/start_state = null var/datum/configuration/config = null @@ -177,10 +183,10 @@ var/datum/nanomanager/nanomanager = new() //airlockWireColorToIndex takes a number representing the wire color, e.g. the orange wire is always 1, the dark red wire is always 2, etc. It returns the index for whatever that wire does. //airlockIndexToWireColor does the opposite thing - it takes the index for what the wire does, for example AIRLOCK_WIRE_IDSCAN is 1, AIRLOCK_WIRE_POWER1 is 2, etc. It returns the wire color number. //airlockWireColorToFlag takes the wire color number and returns the flag for it (1, 2, 4, 8, 16, etc) -var/list/airlockWireColorToFlag = RandomAirlockWires() -var/list/airlockIndexToFlag -var/list/airlockIndexToWireColor -var/list/airlockWireColorToIndex +var/list/globalAirlockWireColorToFlag = RandomAirlockWires() +var/list/globalAirlockIndexToFlag +var/list/globalAirlockIndexToWireColor +var/list/globalAirlockWireColorToIndex var/list/APCWireColorToFlag = RandomAPCWires() var/list/APCIndexToFlag var/list/APCIndexToWireColor @@ -254,3 +260,6 @@ var/DBConnection/dbcon_old = new() //Tgstation database (Old database) - See the // Reference list for disposal sort junctions. Filled up by sorting junction's New() /var/list/tagger_locations = list() + +//added for Xenoarchaeology, might be useful for other stuff +var/global/list/alphabet_uppercase = list("A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z") diff --git a/code/modules/admin/player_panel.dm b/code/modules/admin/player_panel.dm index 13d06c1a..4dab57e7 100644 --- a/code/modules/admin/player_panel.dm +++ b/code/modules/admin/player_panel.dm @@ -322,20 +322,15 @@ /datum/admins/proc/player_panel_old() if (!usr.client.holder) return + var/dat = "Player Menu" - dat += "" - - if(check_rights(R_ADMIN|R_MOD, 0)) - dat += "" - else - dat += "" - + dat += "
    NameReal NameAssigned JobKeyOptionsPMTraitor?
    View Var's
    " //add to this if wanting to add back in IP checking //add if you want to know their ip to the lists below var/list/mobs = sortmobs() for(var/mob/M in mobs) - if(!M.ckey) continue + if(!M.ckey) continue dat += "" if(isAI(M)) @@ -370,16 +365,25 @@ "} - if(!check_rights(R_ADMIN|R_MOD, 0)) - dat += {""} + + + + if(usr.client) + var/client/C = usr.client + if(!check_rights(R_ADMIN|R_MOD, 0)) + dat += {""} + else + switch(is_special_character(M)) + if(0) + dat += {""} + if(1) + dat += {""} + if(2) + dat += {""} else - switch(is_special_character(M)) - if(0) - dat += {""} - if(1) - dat += {""} - if(2) - dat += {""} + dat += {""} + + dat += "
    NameReal NameAssigned JobKeyOptionsPMTraitor?
    IP:(IP: [M.lastKnownIP])
    [M.name]X PMVV N/A Traitor?Traitor?Traitor?Traitor?Traitor?Traitor? N/A
    " @@ -393,16 +397,21 @@ dat += "Current Game Mode: [ticker.mode.name]
    " dat += "Round Duration: [round(world.time / 36000)]:[add_zero(world.time / 600 % 60, 2)]:[world.time / 100 % 6][world.time / 100 % 10]
    " dat += "Emergency shuttle
    " - if (!emergency_shuttle.online) + if (!emergency_shuttle.online()) dat += "Call Shuttle
    " else - var/timeleft = emergency_shuttle.timeleft() - switch(emergency_shuttle.location) - if(0) - dat += "ETA: [(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]
    " - dat += "Send Back
    " - if(1) - dat += "ETA: [(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]
    " + if (emergency_shuttle.wait_for_launch) + var/timeleft = emergency_shuttle.estimate_launch_time() + dat += "ETL: [(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]
    " + + else if (emergency_shuttle.shuttle.has_arrive_time()) + var/timeleft = emergency_shuttle.estimate_arrival_time() + dat += "ETA: [(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]
    " + dat += "Send Back
    " + + if (emergency_shuttle.shuttle.moving_status == SHUTTLE_WARMUP) + dat += "Launching now..." + dat += "[ticker.delay_end ? "End Round Normally" : "Delay Round End"]
    " if(ticker.mode.syndicates.len) dat += "
    " @@ -448,7 +457,7 @@ if(M) dat += "" dat += "" - var/turf/mob_loc = get_turf_loc(M) + var/turf/mob_loc = get_turf(M) dat += "" else dat += "" diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index a4879a4d..cfa1663e 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -214,36 +214,48 @@ switch(href_list["call_shuttle"]) if("1") - if ((!( ticker ) || emergency_shuttle.location)) + if ((!( ticker ) || !emergency_shuttle.location())) return - emergency_shuttle.incall() - captain_announce("The emergency shuttle has been called. It will arrive in [round(emergency_shuttle.timeleft()/60)] minutes.") - log_admin("[key_name(usr)] called the Emergency Shuttle") - message_admins("\blue [key_name_admin(usr)] called the Emergency Shuttle to the station", 1) + if (emergency_shuttle.can_call()) + 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) if("2") - if ((!( ticker ) || emergency_shuttle.location || emergency_shuttle.direction == 0)) + if (!( ticker ) || !emergency_shuttle.location()) return - switch(emergency_shuttle.direction) - if(-1) - emergency_shuttle.incall() - captain_announce("The emergency shuttle has been called. It will arrive in [round(emergency_shuttle.timeleft()/60)] minutes.") - log_admin("[key_name(usr)] called the Emergency Shuttle") - message_admins("\blue [key_name_admin(usr)] called the Emergency Shuttle to the station", 1) - if(1) - emergency_shuttle.recall() - log_admin("[key_name(usr)] sent the Emergency Shuttle back") - message_admins("\blue [key_name_admin(usr)] sent the Emergency Shuttle back", 1) + if (emergency_shuttle.can_call()) + 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") + message_admins("\blue [key_name_admin(usr)] sent the Emergency Shuttle back", 1) href_list["secretsadmin"] = "check_antagonist" else if(href_list["edit_shuttle_time"]) if(!check_rights(R_SERVER)) return - emergency_shuttle.settimeleft( input("Enter new shuttle duration (seconds):","Edit Shuttle Timeleft", emergency_shuttle.timeleft() ) as num ) - log_admin("[key_name(usr)] edited the Emergency Shuttle's timeleft to [emergency_shuttle.timeleft()]") - captain_announce("The emergency shuttle has been called. It will arrive in [round(emergency_shuttle.timeleft()/60)] minutes.") - message_admins("\blue [key_name_admin(usr)] edited the Emergency Shuttle's timeleft to [emergency_shuttle.timeleft()]", 1) + if (emergency_shuttle.wait_for_launch) + var/new_time_left = input("Enter new shuttle launch countdown (seconds):","Edit Shuttle Launch Time", emergency_shuttle.estimate_launch_time() ) as num + + emergency_shuttle.launch_time = world.time + new_time_left*10 + + 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 + + log_admin("[key_name(usr)] edited the Emergency Shuttle's arrival time to [new_time_left]") + 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"]) @@ -1954,32 +1966,113 @@ ticker.mode.finalize_traitor(A.mind) message_admins("\blue [key_name_admin(usr)] used everyone is a traitor secret. Objective is [objective]", 1) log_admin("[key_name(usr)] used everyone is a traitor secret. Objective is [objective]") - if("moveminingshuttle") - if(mining_shuttle_moving) + + 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) + message_admins("\blue [key_name_admin(usr)] launched the [shuttle_tag] shuttle", 1) + 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) + message_admins("\blue [key_name_admin(usr)] has forced the [shuttle_tag] shuttle launch", 1) + 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") + else + 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") - move_mining_shuttle() - message_admins("\blue [key_name_admin(usr)] moved mining shuttle", 1) - log_admin("[key_name(usr)] moved the mining shuttle") - if("moveadminshuttle") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","ShA") - move_admin_shuttle() - message_admins("\blue [key_name_admin(usr)] moved the centcom administration shuttle", 1) - log_admin("[key_name(usr)] moved the centcom administration shuttle") - if("moveferry") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","ShF") - move_ferry() - message_admins("\blue [key_name_admin(usr)] moved the centcom ferry", 1) - log_admin("[key_name(usr)] moved the centcom ferry") - if("movealienship") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","ShX") - move_alien_ship() - message_admins("\blue [key_name_admin(usr)] moved the alien dinghy", 1) - log_admin("[key_name(usr)] moved the alien dinghy") + + 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") + if("togglebombcap") feedback_inc("admin_secrets_fun_used",1) feedback_add_details("admin_secrets_fun_used","BC") diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index c37fa1dc..c80eff22 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -143,11 +143,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+= "Plasma : [env.toxins]\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! @@ -1142,7 +1142,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/plasma/Plasma = new/obj/item/weapon/tank/plasma(Rad) - Plasma.air_contents.toxins = 70 + Plasma.air_contents.gas["phoron"] = 70 Rad.drainratio = 0 Rad.P = Plasma Plasma.loc = Rad diff --git a/code/modules/admin/verbs/playsound.dm b/code/modules/admin/verbs/playsound.dm index d1a86ce8..8aa97fd4 100644 --- a/code/modules/admin/verbs/playsound.dm +++ b/code/modules/admin/verbs/playsound.dm @@ -22,7 +22,7 @@ log_admin("[key_name(src)] played a local sound [S]") message_admins("[key_name_admin(src)] played a local sound [S]", 1) - playsound(get_turf_loc(src.mob), S, 50, 0, 0) + playsound(get_turf(src.mob), S, 50, 0, 0) feedback_add_details("admin_verb","PLS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index 078e4228..92cbd8bc 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -883,7 +883,7 @@ Traitors and the like can also be revived with the previous role mostly intact. set category = "Admin" set name = "Call Shuttle" - if ((!( ticker ) || emergency_shuttle.location)) + if ((!( ticker ) || !emergency_shuttle.location())) return if(!check_rights(R_ADMIN)) return @@ -891,16 +891,21 @@ Traitors and the like can also be revived with the previous role mostly intact. var/confirm = alert(src, "You sure?", "Confirm", "Yes", "No") if(confirm != "Yes") return + var/choice if(ticker.mode.name == "revolution" || ticker.mode.name == "AI malfunction" || ticker.mode.name == "confliction") - var/choice = input("The shuttle will just return if you call it. Call anyway?") in list("Confirm", "Cancel") + choice = input("The shuttle will just return if you call it. Call anyway?") in list("Confirm", "Cancel") if(choice == "Confirm") - emergency_shuttle.fake_recall = rand(300,500) + emergency_shuttle.auto_recall = 1 //enable auto-recall else return - emergency_shuttle.incall() - captain_announce("The emergency shuttle has been called. It will arrive in [round(emergency_shuttle.timeleft()/60)] minutes.") - world << sound('sound/AI/shuttlecalled.ogg') + choice = input("Is this an emergency evacuation or a crew transfer?") in list("Emergency", "Crew Transfer") + if (choice == "Emergency") + emergency_shuttle.call_evac() + else + emergency_shuttle.call_transfer() + + feedback_add_details("admin_verb","CSHUT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! log_admin("[key_name(usr)] admin-called the emergency shuttle.") message_admins("\blue [key_name_admin(usr)] admin-called the emergency shuttle.", 1) @@ -914,7 +919,7 @@ Traitors and the like can also be revived with the previous role mostly intact. if(alert(src, "You sure?", "Confirm", "Yes", "No") != "Yes") return - if(!ticker || emergency_shuttle.location || emergency_shuttle.direction == 0) + if(!ticker || !emergency_shuttle.can_recall()) return emergency_shuttle.recall() diff --git a/code/modules/admin/verbs/striketeam.dm b/code/modules/admin/verbs/striketeam.dm index d545493f..049cfe8b 100644 --- a/code/modules/admin/verbs/striketeam.dm +++ b/code/modules/admin/verbs/striketeam.dm @@ -30,7 +30,7 @@ var/global/sent_strike_team = 0 sent_strike_team = 1 - if (emergency_shuttle.direction == 1 && emergency_shuttle.online == 1) + if (emergency_shuttle.can_recall()) emergency_shuttle.recall() var/commando_number = commandos_possible //for selecting a leader diff --git a/code/modules/admin/verbs/striketeam_syndicate.dm b/code/modules/admin/verbs/striketeam_syndicate.dm index 0309fda3..7a986495 100644 --- a/code/modules/admin/verbs/striketeam_syndicate.dm +++ b/code/modules/admin/verbs/striketeam_syndicate.dm @@ -35,8 +35,8 @@ var/global/sent_syndicate_strike_team = 0 sent_syndicate_strike_team = 1 - if (emergency_shuttle.direction == 1 && emergency_shuttle.online == 1) - emergency_shuttle.recall() + //if (emergency_shuttle.can_recall()) + // emergency_shuttle.recall() //why, exactly? Admins can do this themselves. var/syndicate_commando_number = syndicate_commandos_possible //for selecting a leader var/syndicate_leader_selected = 0 //when the leader is chosen. The last person spawned. diff --git a/code/modules/assembly/assembly.dm b/code/modules/assembly/assembly.dm index c434d4d6..ff749537 100644 --- a/code/modules/assembly/assembly.dm +++ b/code/modules/assembly/assembly.dm @@ -5,9 +5,7 @@ icon_state = "" flags = FPRINT | TABLEPASS| CONDUCT w_class = 2.0 - m_amt = 100 - g_amt = 0 - w_amt = 0 + matter = list("metal" = 100) throwforce = 2 throw_speed = 3 throw_range = 10 diff --git a/code/modules/assembly/bomb.dm b/code/modules/assembly/bomb.dm index 7d4e05f9..bf0a7a13 100644 --- a/code/modules/assembly/bomb.dm +++ b/code/modules/assembly/bomb.dm @@ -101,7 +101,7 @@ return /obj/item/weapon/tank/proc/ignite() //This happens when a bomb is told to explode - var/fuel_moles = air_contents.toxins + air_contents.oxygen/6 + var/fuel_moles = air_contents.gas["toxins"] + air_contents.gas["oxygen"] / 6 var/strength = 1 var/turf/ground_zero = get_turf(loc) @@ -149,7 +149,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/igniter.dm b/code/modules/assembly/igniter.dm index 65af4f34..bd96f655 100644 --- a/code/modules/assembly/igniter.dm +++ b/code/modules/assembly/igniter.dm @@ -2,9 +2,7 @@ name = "igniter" desc = "A small electronic device able to ignite combustable substances." icon_state = "igniter" - m_amt = 500 - g_amt = 50 - w_amt = 10 + matter = list("metal" = 500, "glass" = 50, "waste" = 10) origin_tech = "magnets=1" secured = 1 diff --git a/code/modules/assembly/infrared.dm b/code/modules/assembly/infrared.dm index 090aa799..fc22539b 100644 --- a/code/modules/assembly/infrared.dm +++ b/code/modules/assembly/infrared.dm @@ -4,9 +4,7 @@ name = "infrared emitter" desc = "Emits a visible or invisible beam and is triggered when the beam is interrupted." icon_state = "infrared" - m_amt = 1000 - g_amt = 500 - w_amt = 100 + matter = list("metal" = 1000, "glass" = 500, "waste" = 100) origin_tech = "magnets=2" wires = WIRE_PULSE diff --git a/code/modules/assembly/mousetrap.dm b/code/modules/assembly/mousetrap.dm index 46227e99..aa58c54f 100644 --- a/code/modules/assembly/mousetrap.dm +++ b/code/modules/assembly/mousetrap.dm @@ -2,8 +2,7 @@ name = "mousetrap" desc = "A handy little spring-loaded trap for catching pesty rodents." icon_state = "mousetrap" - m_amt = 100 - w_amt = 10 + matter = list("metal" = 100, "waste" = 10) origin_tech = "combat=1" var/armed = 0 diff --git a/code/modules/assembly/proximity.dm b/code/modules/assembly/proximity.dm index aeeb2902..0d66d1a3 100644 --- a/code/modules/assembly/proximity.dm +++ b/code/modules/assembly/proximity.dm @@ -2,9 +2,7 @@ name = "proximity sensor" desc = "Used for scanning and alerting when someone enters a certain proximity." icon_state = "prox" - m_amt = 800 - g_amt = 200 - w_amt = 50 + matter = list("metal" = 800, "glass" = 200, "waste" = 50) origin_tech = "magnets=1" wires = WIRE_PULSE diff --git a/code/modules/assembly/signaler.dm b/code/modules/assembly/signaler.dm index ffcc6b58..87aa04b9 100644 --- a/code/modules/assembly/signaler.dm +++ b/code/modules/assembly/signaler.dm @@ -3,9 +3,7 @@ desc = "Used to remotely activate devices." icon_state = "signaller" item_state = "signaler" - m_amt = 1000 - g_amt = 200 - w_amt = 100 + matter = list("metal" = 1000, "glass" = 200, "waste" = 100) origin_tech = "magnets=1" wires = WIRE_RECEIVE | WIRE_PULSE | WIRE_RADIO_PULSE | WIRE_RADIO_RECEIVE diff --git a/code/modules/assembly/timer.dm b/code/modules/assembly/timer.dm index 21cfe709..09d7b630 100644 --- a/code/modules/assembly/timer.dm +++ b/code/modules/assembly/timer.dm @@ -2,9 +2,7 @@ name = "timer" desc = "Used to time things. Works well with contraptions which has to count down. Tick tock." icon_state = "timer" - m_amt = 500 - g_amt = 50 - w_amt = 10 + matter = list("metal" = 500, "glass" = 50, "waste" = 10) origin_tech = "magnets=1" wires = WIRE_PULSE @@ -20,7 +18,7 @@ activate() if(!..()) return 0//Cooldown check - + timing = !timing update_icon() diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index f2a67218..fdf4590b 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -53,6 +53,7 @@ datum/preferences var/be_random_name = 0 //whether we are a random name every round var/gender = MALE //gender of character (well duh) var/age = 30 //age of character + var/spawnpoint = "Arrivals Shuttle" //where this character will spawn (0-2). var/b_type = "A+" //blood type (not-chooseable) var/underwear = 1 //underwear type var/undershirt = 1 //undershirt type @@ -72,9 +73,15 @@ datum/preferences var/r_eyes = 0 //Eye color var/g_eyes = 0 //Eye color var/b_eyes = 0 //Eye color - var/species = "Human" //Species datum to use. + var/species = "Human" //Species datum to use. var/language = "None" //Secondary language - var/list/gear //Custom/fluff item loadout. + 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 @@ -104,10 +111,10 @@ datum/preferences // maps each organ to either null(intact), "cyborg" or "amputated" // will probably not be able to do this for head and torso ;) 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,951 +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 += "
    Syndicates
    [M.real_name][M.client ? "" : " (logged out)"][M.stat == 2 ? " (DEAD)" : ""]PM[mob_loc.loc]
    Head not found!
    " - for(var/V in SKILLS) - HTML += "" - for(var/datum/skill/S in SKILLS[V]) - var/level = skills[S.ID] - HTML += "" - HTML += "" - HTML += "" - // secondary skills don't have an amateur level +/datum/preferences/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) - HTML += "" + used_skillpoints += 1 * multiplier else - HTML += "" - HTML += "" - HTML += "" - HTML += "" - HTML += "
    [V]" - HTML += "
    [S.name]\[Untrained\]\[Amateur\]\[Trained\]\[Professional\]
    " - HTML += "\[Done\]" + used_skillpoints += 3 * multiplier + if(SKILL_EXPERT) + // secondary skills cost less + if(S.secondary) + used_skillpoints += 3 * multiplier + else + used_skillpoints += 6 * multiplier - user << browse(null, "window=preferences") - user << browse(HTML, "window=show_skills;size=600x800") - return +/datum/preferences/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/ShowChoices(mob/user) - if(!user || !user.client) return - update_preview_icon() - user << browse_rsc(preview_icon_front, "previewicon.png") - user << browse_rsc(preview_icon_side, "previewicon2.png") - var/dat = "
    " +/datum/preferences/proc/SetSkills(mob/user) + if(SKILLS == null) + setup_skills() - if(path) - dat += "
    " - dat += "Slot [slot_name] - " - dat += "Load slot - " - dat += "Save slot - " - dat += "Reload slot" - dat += "
    " + if(skills.len == 0) + ZeroSkills() - else - dat += "Please create an account to save your preferences." - dat += "

    " + 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 += "" + for(var/datum/skill/S in SKILLS[V]) + var/level = skills[S.ID] + HTML += "" + HTML += "" + HTML += "" + // secondary skills don't have an amateur level + if(S.secondary) + HTML += "" + else + HTML += "" + HTML += "" + HTML += "" + HTML += "" + HTML += "
    [V]" + HTML += "
    [S.name]\[Untrained\]\[Amateur\]\[Trained\]\[Professional\]
    " + HTML += "\[Done\]" - dat += "Name: " - dat += "[real_name]
    " - dat += "(Random Name) " - dat += "(Always Random Name: [be_random_name ? "Yes" : "No"])" + user << browse(null, "window=preferences") + user << browse(HTML, "window=show_skills;size=600x800") + return + +/datum/preferences/proc/ShowChoices(mob/user) + if(!user || !user.client) return + update_preview_icon() + user << browse_rsc(preview_icon_front, "previewicon.png") + user << browse_rsc(preview_icon_side, "previewicon2.png") + var/dat = "
    " + + if(path) + dat += "
    " + dat += "Slot [slot_name] - " + dat += "Load slot - " + dat += "Save slot - " + dat += "Reload slot" + dat += "
    " + + else + dat += "Please create an account to save your preferences." + + dat += "

    " + HTML += "" + HTML += "
    " + + dat += "Name: " + dat += "[real_name]
    " + dat += "(Random Name) " + dat += "(Always Random Name: [be_random_name ? "Yes" : "No"])" + dat += "
    " + + dat += "Gender: [gender == MALE ? "Male" : "Female"]
    " + dat += "Age: [age]
    " + dat += "Spawn Point: [spawnpoint]" + + dat += "
    " + dat += "UI Style: [UI_style]
    " + dat += "Custom UI(recommended for White UI):
    " + dat += "-Color: [UI_style_color]
    __

    " + dat += "-Alpha(transparency): [UI_style_alpha]
    " + dat += "Play admin midis: [(toggles & SOUND_MIDI) ? "Yes" : "No"]
    " + dat += "Play lobby music: [(toggles & SOUND_LOBBY) ? "Yes" : "No"]
    " + dat += "Ghost ears: [(toggles & CHAT_GHOSTEARS) ? "All Speech" : "Nearest Creatures"]
    " + dat += "Ghost sight: [(toggles & CHAT_GHOSTSIGHT) ? "All Emotes" : "Nearest Creatures"]
    " + dat += "Ghost radio: [(toggles & CHAT_GHOSTRADIO) ? "All Chatter" : "Nearest Speakers"]
    " + + if(config.allow_Metadata) + dat += "OOC Notes: Edit
    " + + dat += "
    Custom Loadout: " + var/total_cost = 0 + + if(isnull(gear) || !islist(gear)) gear = list() + + if(gear && gear.len) dat += "
    " + for(var/gear_name in gear) + if(gear_datums[gear_name]) + var/datum/gear/G = gear_datums[gear_name] + total_cost += G.cost + dat += "[gear_name] \[remove\]
    " - dat += "Gender: [gender == MALE ? "Male" : "Female"]
    " - dat += "Age: [age]" - - dat += "
    " - dat += "UI Style: [UI_style]
    " - dat += "Custom UI(recommended for White UI):
    " - dat += "-Color: [UI_style_color]
    __

    " - dat += "-Alpha(transparency): [UI_style_alpha]
    " - dat += "Play admin midis: [(toggles & SOUND_MIDI) ? "Yes" : "No"]
    " - dat += "Play lobby music: [(toggles & SOUND_LOBBY) ? "Yes" : "No"]
    " - dat += "Ghost ears: [(toggles & CHAT_GHOSTEARS) ? "All Speech" : "Nearest Creatures"]
    " - dat += "Ghost sight: [(toggles & CHAT_GHOSTSIGHT) ? "All Emotes" : "Nearest Creatures"]
    " - dat += "Ghost radio: [(toggles & CHAT_GHOSTRADIO) ? "All Chatter" : "Nearest Speakers"]
    " - - if(config.allow_Metadata) - dat += "OOC Notes: Edit
    " - - dat += "
    Custom Loadout: " - var/total_cost = 0 - - if(isnull(gear) || !islist(gear)) gear = list() + dat += "Used: [total_cost] points." + else + dat += "none." + if(total_cost < MAX_GEAR_COST) + dat += " \[add\]" if(gear && gear.len) - dat += "
    " - for(var/gear_name in gear) - if(gear_datums[gear_name]) - var/datum/gear/G = gear_datums[gear_name] - total_cost += G.cost - dat += "[gear_name] \[remove\]
    " + dat += " \[remove\]" - dat += "Used: [total_cost] points." - else - dat += "none." + dat += "

    Occupation Choices
    " + dat += "\tSet Preferences
    " - if(total_cost < MAX_GEAR_COST) - dat += " \[add\]" + dat += "
    Body " + dat += "(®)" + dat += "
    " + dat += "Species: [species]
    " + dat += "Secondary Language:
    [language]
    " + dat += "Blood Type: [b_type]
    " + dat += "Skin Tone: [-s_tone + 35]/220
    " + //dat += "Skin pattern: Adjust
    " + dat += "Needs Glasses: [disabilities == 0 ? "No" : "Yes"]
    " + dat += "Limbs: Adjust
    " + dat += "Internal Organs: Adjust
    " - dat += "

    Occupation Choices
    " - dat += "\tSet Preferences
    " + //display limbs below + var/ind = 0 + for(var/name in organ_data) + //world << "[ind] \ [organ_data.len]" + var/status = organ_data[name] + var/organ_name = null + switch(name) + if("l_arm") + organ_name = "left arm" + if("r_arm") + organ_name = "right arm" + if("l_leg") + organ_name = "left leg" + if("r_leg") + organ_name = "right leg" + if("l_foot") + organ_name = "left foot" + if("r_foot") + organ_name = "right foot" + if("l_hand") + organ_name = "left hand" + if("r_hand") + organ_name = "right hand" + if("heart") + organ_name = "heart" + if("eyes") + organ_name = "eyes" - dat += "
    Body " - dat += "(®)" - dat += "
    " - dat += "Species: [species]
    " - dat += "Secondary Language:
    [language]
    " - dat += "Blood Type: [b_type]
    " - dat += "Skin Tone: [-s_tone + 35]/220
    " - //dat += "Skin pattern: Adjust
    " - dat += "Needs Glasses: [disabilities == 0 ? "No" : "Yes"]
    " - dat += "Limbs: Adjust
    " - dat += "Internal Organs: Adjust
    " - - //display limbs below - var/ind = 0 - for(var/name in organ_data) - //world << "[ind] \ [organ_data.len]" - var/status = organ_data[name] - var/organ_name = null - switch(name) - if("l_arm") - organ_name = "left arm" - if("r_arm") - organ_name = "right arm" - if("l_leg") - organ_name = "left leg" - if("r_leg") - organ_name = "right leg" - if("l_foot") - organ_name = "left foot" - if("r_foot") - organ_name = "right foot" - if("l_hand") - organ_name = "left hand" - if("r_hand") - organ_name = "right hand" + if(status == "cyborg") + ++ind + if(ind > 1) + dat += ", " + dat += "\tMechanical [organ_name] prothesis" + else if(status == "amputated") + ++ind + if(ind > 1) + dat += ", " + dat += "\tAmputated [organ_name]" + else if(status == "mechanical") + ++ind + if(ind > 1) + dat += ", " + dat += "\tMechanical [organ_name]" + else if(status == "assisted") + ++ind + if(ind > 1) + dat += ", " + switch(organ_name) if("heart") - organ_name = "heart" + dat += "\tPacemaker-assisted [organ_name]" + if("voicebox") //on adding voiceboxes for speaking skrell/similar replacements + dat += "\tSurgically altered [organ_name]" if("eyes") - organ_name = "eyes" - - if(status == "cyborg") - ++ind - if(ind > 1) - dat += ", " - dat += "\tMechanical [organ_name] prothesis" - else if(status == "amputated") - ++ind - if(ind > 1) - dat += ", " - dat += "\tAmputated [organ_name]" - else if(status == "mechanical") - ++ind - if(ind > 1) - dat += ", " - dat += "\tMechanical [organ_name]" - else if(status == "assisted") - ++ind - if(ind > 1) - dat += ", " - switch(organ_name) - if("heart") - dat += "\tPacemaker-assisted [organ_name]" - if("voicebox") //on adding voiceboxes for speaking skrell/similar replacements - dat += "\tSurgically altered [organ_name]" - if("eyes") - dat += "\tRetinal overlayed [organ_name]" - else - dat += "\tMechanically assisted [organ_name]" - if(!ind) - dat += "\[...\]

    " - else - dat += "

    " - - if(gender == MALE) - dat += "Underwear: [underwear_m[underwear]]
    " - else - dat += "Underwear: [underwear_f[underwear]]
    " - - dat += "Undershirt: [undershirt_t[undershirt]]
    " - - dat += "Backpack Type:
    [backbaglist[backbag]]
    " - - dat += "Nanotrasen Relation:
    [nanotrasen_relation]
    " - - dat += "
    Preview
    " - - dat += "
    " - - if(jobban_isbanned(user, "Records")) - dat += "You are banned from using character records.
    " - else - dat += "Character Records
    " - - dat += "Set Antag Options
    " - - dat += "\tSet Skills ([GetSkillClass(used_skillpoints)][used_skillpoints > 0 ? " [used_skillpoints]" : "0"])
    " - - dat += "Set Flavor Text
    " - if(lentext(flavor_text) <= 40) - if(!lentext(flavor_text)) - dat += "\[...\]" - else - dat += "[flavor_text]" - else - dat += "[copytext(flavor_text, 1, 37)]...
    " - dat += "
    " - - dat += "
    Hair
    " - dat += "Change Color
    __
    " - dat += " Style: [h_style]
    " - - dat += "
    Facial
    " - dat += "Change Color
    __
    " - dat += " Style: [f_style]
    " - - dat += "
    Eyes
    " - dat += "Change Color
    __

    " - - 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.

    " - HTML += "
    \[Done\]

    " // Easier to press up here. - HTML += "
    " // 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 += "" - HTML += "
      
    " - index = 0 - - HTML += "" - continue - if(!job.player_old_enough(user.client)) - var/available_in_days = job.available_in_days(user.client) - HTML += "[rank]" - continue - if((job_civilian_low & ASSISTANT) && (rank != "Assistant")) - HTML += "[rank]" - continue - if((rank in command_positions) || (rank == "AI"))//Bold head jobs - HTML += "[rank]" - else - HTML += "[rank]" - - HTML += "" - continue + dat += "\tMechanically assisted [organ_name]" + if(!ind) + dat += "\[...\]

    " + else + dat += "

    " - if(GetJobDepartment(job, 1) & job.flag) - HTML += " \[High]" - else if(GetJobDepartment(job, 2) & job.flag) - HTML += " \[Medium]" - else if(GetJobDepartment(job, 3) & job.flag) - HTML += " \[Low]" + if(gender == MALE) + dat += "Underwear: [underwear_m[underwear]]
    " + else + dat += "Underwear: [underwear_f[underwear]]
    " + + dat += "Undershirt: [undershirt_t[undershirt]]
    " + + dat += "Backpack Type:
    [backbaglist[backbag]]
    " + + dat += "Nanotrasen Relation:
    [nanotrasen_relation]
    " + + dat += "
    " - var/rank = job.title - lastJob = job - if(jobban_isbanned(user, rank)) - HTML += "[rank] \[BANNED]
    \[IN [(available_in_days)] DAYS]
    " - - HTML += "" - - if(rank == "Assistant")//Assistant is special - if(job_civilian_low & ASSISTANT) - HTML += " \[Yes]" + dat += "\tRetinal overlayed [organ_name]" else - HTML += " \[No]" - HTML += "
    Preview
    " + + dat += "
    " + + if(jobban_isbanned(user, "Records")) + dat += "You are banned from using character records.
    " + else + dat += "Character Records
    " + + dat += "Set Antag Options
    " + + dat += "\tSet Skills ([GetSkillClass(used_skillpoints)][used_skillpoints > 0 ? " [used_skillpoints]" : "0"])
    " + + dat += "Set Flavor Text
    " + + dat += "pAI Configuration
    " + dat += "
    " + + dat += "
    Hair
    " + dat += "Change Color
    __
    " + dat += " Style: [h_style]
    " + + dat += "
    Facial
    " + dat += "Change Color
    __
    " + dat += " Style: [f_style]
    " + + dat += "
    Eyes
    " + dat += "Change Color
    __

    " + + dat += "
    Body Color
    " + dat += "Change Color
    __
    " + + dat += "

    Background Information
    " + dat += "Home system: [home_system]
    " + dat += "Citizenship: [citizenship]
    " + dat += "Faction: [faction]
    " + dat += "Religion: [religion]
    " + + 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") + +/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.

    " + HTML += "
    \[Done\]

    " // Easier to press up here. + HTML += "
    " // 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 += "" + HTML += "
      
    " + index = 0 + + HTML += "" + continue + if(!job.player_old_enough(user.client)) + var/available_in_days = job.available_in_days(user.client) + HTML += "[rank]" + continue + if((job_civilian_low & ASSISTANT) && (rank != "Assistant")) + HTML += "[rank]" + continue + if((rank in command_positions) || (rank == "AI"))//Bold head jobs + HTML += "[rank]" + else + HTML += "[rank]" + + HTML += "" + HTML += " \[No]" HTML += "" - - HTML += "
    " + var/rank = job.title + lastJob = job + if(jobban_isbanned(user, rank)) + HTML += "[rank] \[BANNED]
    \[IN [(available_in_days)] DAYS]
    " + + HTML += "" + + if(rank == "Assistant")//Assistant is special + if(job_civilian_low & ASSISTANT) + HTML += " \[Yes]" else - HTML += " \[NEVER]" - if(job.alt_titles) - HTML += "
     \[[GetPlayerAltTitle(job)]\]
    " - - HTML += "
    " - - switch(alternate_option) - if(GET_RANDOM_JOB) - HTML += "

    Get random job if preferences unavailable

    " - if(BE_ASSISTANT) - HTML += "

    Be assistant if preference unavailable

    " - if(RETURN_TO_LOBBY) - HTML += "

    Return to lobby if preference unavailable

    " - - HTML += "
    \[Reset\]
    " - HTML += "
    " - - user << browse(null, "window=preferences") - user << browse(HTML, "window=mob_occupation;size=[width]x[height]") - return - - proc/SetDisabilities(mob/user) - var/HTML = "" - HTML += "
    " - HTML += "Choose disabilities
    " - - HTML += "Need Glasses? [disabilities & (1<<0) ? "Yes" : "No"]
    " - HTML += "Seizures? [disabilities & (1<<1) ? "Yes" : "No"]
    " - HTML += "Coughing? [disabilities & (1<<2) ? "Yes" : "No"]
    " - HTML += "Tourettes/Twitching? [disabilities & (1<<3) ? "Yes" : "No"]
    " - HTML += "Nervousness? [disabilities & (1<<4) ? "Yes" : "No"]
    " - HTML += "Deafness? [disabilities & (1<<5) ? "Yes" : "No"]
    " - - HTML += "
    " - HTML += "\[Done\]" - 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 += "
    " - - user << browse(null, "window=preferences") - user << browse(HTML, "window=records;size=350x300") - return - - proc/SetAntagoptions(mob/user) - if(uplinklocation == "" || !uplinklocation) - uplinklocation = "PDA" - var/HTML = "" - HTML += "
    " - HTML += "Antagonist Options
    " - HTML += "
    " - HTML +="Uplink Type : [uplinklocation]" - HTML +="
    " - HTML +="
    " - HTML +="\[Done\]" - - HTML += "
    " - - user << browse(null, "window=preferences") - user << browse(HTML, "window=antagoptions") - return - - - - proc/GetPlayerAltTitle(datum/job/job) - return player_alt_titles.Find(job.title) > 0 \ - ? player_alt_titles[job.title] \ - : job.title - - proc/SetPlayerAltTitle(datum/job/job, new_title) - // remove existing entry - if(player_alt_titles.Find(job.title)) - player_alt_titles -= job.title - // add one if it's not default - if(job.title != new_title) - player_alt_titles[job.title] = new_title - - proc/SetJob(mob/user, role) - var/datum/job/job = job_master.GetJob(role) - if(!job) - user << browse(null, "window=mob_occupation") - ShowChoices(user) - return - - if(role == "Assistant") - if(job_civilian_low & job.flag) - job_civilian_low &= ~job.flag - else - job_civilian_low |= job.flag - SetChoices(user) - return 1 + continue if(GetJobDepartment(job, 1) & job.flag) - SetJobDepartment(job, 1) + HTML += " \[High]" else if(GetJobDepartment(job, 2) & job.flag) - SetJobDepartment(job, 2) + HTML += " \[Medium]" else if(GetJobDepartment(job, 3) & job.flag) - SetJobDepartment(job, 3) - else//job = Never - SetJobDepartment(job, 4) + HTML += " \[Low]" + else + HTML += " \[NEVER]" + if(job.alt_titles) + HTML += "
     \[[GetPlayerAltTitle(job)]\]
    " + + HTML += "
    " + + switch(alternate_option) + if(GET_RANDOM_JOB) + HTML += "

    Get random job if preferences unavailable

    " + if(BE_ASSISTANT) + HTML += "

    Be assistant if preference unavailable

    " + if(RETURN_TO_LOBBY) + HTML += "

    Return to lobby if preference unavailable

    " + + HTML += "
    \[Reset\]
    " + HTML += "" + + user << browse(null, "window=preferences") + user << browse(HTML, "window=mob_occupation;size=[width]x[height]") + return + +/datum/preferences/proc/SetDisabilities(mob/user) + var/HTML = "" + HTML += "
    " + HTML += "Choose disabilities
    " + + HTML += "Need Glasses? [disabilities & (1<<0) ? "Yes" : "No"]
    " + HTML += "Seizures? [disabilities & (1<<1) ? "Yes" : "No"]
    " + HTML += "Coughing? [disabilities & (1<<2) ? "Yes" : "No"]
    " + HTML += "Tourettes/Twitching? [disabilities & (1<<3) ? "Yes" : "No"]
    " + HTML += "Nervousness? [disabilities & (1<<4) ? "Yes" : "No"]
    " + HTML += "Deafness? [disabilities & (1<<5) ? "Yes" : "No"]
    " + + HTML += "
    " + HTML += "\[Done\]" + HTML += "
    " + + user << browse(null, "window=preferences") + user << browse(HTML, "window=disabil;size=350x300") + return + +/datum/preferences/proc/SetRecords(mob/user) + var/HTML = "" + HTML += "
    " + HTML += "Set Character Records
    " + + HTML += "Medical Records
    " + + HTML += TextPreview(med_record,40) + + HTML += "

    Employment Records
    " + + HTML += TextPreview(gen_record,40) + + HTML += "

    Security Records
    " + + HTML += TextPreview(sec_record,40) + + HTML += "
    " + HTML += "\[Done\]" + HTML += "
    " + + user << browse(null, "window=preferences") + user << browse(HTML, "window=records;size=350x300") + return + +/datum/preferences/proc/SetAntagoptions(mob/user) + if(uplinklocation == "" || !uplinklocation) + uplinklocation = "PDA" + var/HTML = "" + HTML += "
    " + HTML += "Antagonist Options
    " + HTML += "
    " + HTML +="Uplink Type : [uplinklocation]" + HTML +="
    " + HTML +="
    " + HTML +="\[Done\]" + + HTML += "
    " + + user << browse(null, "window=preferences") + user << browse(HTML, "window=antagoptions") + return + +/datum/preferences/proc/SetFlavorText(mob/user) + var/HTML = "" + HTML += "
    " + HTML += "Set 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 += "" + user << browse(null, "window=preferences") + user << browse(HTML, "window=flavor_text;size=430x300") + return + +/datum/preferences/proc/GetPlayerAltTitle(datum/job/job) + return player_alt_titles.Find(job.title) > 0 \ + ? player_alt_titles[job.title] \ + : job.title + +/datum/preferences/proc/SetPlayerAltTitle(datum/job/job, new_title) + // remove existing entry + if(player_alt_titles.Find(job.title)) + player_alt_titles -= job.title + // add one if it's not default + if(job.title != new_title) + player_alt_titles[job.title] = new_title + +/datum/preferences/proc/SetJob(mob/user, role) + var/datum/job/job = job_master.GetJob(role) + if(!job) + user << browse(null, "window=mob_occupation") + ShowChoices(user) + return + + if(role == "Assistant") + if(job_civilian_low & job.flag) + job_civilian_low &= ~job.flag + else + job_civilian_low |= job.flag SetChoices(user) return 1 - proc/ResetJobs() - job_civilian_high = 0 - job_civilian_med = 0 - job_civilian_low = 0 + if(GetJobDepartment(job, 1) & job.flag) + SetJobDepartment(job, 1) + else if(GetJobDepartment(job, 2) & job.flag) + SetJobDepartment(job, 2) + else if(GetJobDepartment(job, 3) & job.flag) + SetJobDepartment(job, 3) + else//job = Never + SetJobDepartment(job, 4) - job_medsci_high = 0 - job_medsci_med = 0 - job_medsci_low = 0 + SetChoices(user) + return 1 - job_engsec_high = 0 - job_engsec_med = 0 - job_engsec_low = 0 +/datum/preferences/proc/ResetJobs() + job_civilian_high = 0 + job_civilian_med = 0 + job_civilian_low = 0 + + job_medsci_high = 0 + job_medsci_med = 0 + job_medsci_low = 0 + + job_engsec_high = 0 + job_engsec_med = 0 + job_engsec_low = 0 - proc/GetJobDepartment(var/datum/job/job, var/level) - if(!job || !level) return 0 - switch(job.department_flag) - if(CIVILIAN) - switch(level) - if(1) - return job_civilian_high - if(2) - return job_civilian_med - if(3) - return job_civilian_low - if(MEDSCI) - switch(level) - if(1) - return job_medsci_high - if(2) - return job_medsci_med - if(3) - return job_medsci_low - if(ENGSEC) - switch(level) - if(1) - return job_engsec_high - if(2) - return job_engsec_med - if(3) - return job_engsec_low - return 0 +/datum/preferences/proc/GetJobDepartment(var/datum/job/job, var/level) + if(!job || !level) return 0 + switch(job.department_flag) + if(CIVILIAN) + switch(level) + if(1) + return job_civilian_high + if(2) + return job_civilian_med + if(3) + return job_civilian_low + if(MEDSCI) + switch(level) + if(1) + return job_medsci_high + if(2) + return job_medsci_med + if(3) + return job_medsci_low + if(ENGSEC) + switch(level) + if(1) + return job_engsec_high + if(2) + return job_engsec_med + if(3) + return job_engsec_low + return 0 - proc/SetJobDepartment(var/datum/job/job, var/level) - if(!job || !level) return 0 - switch(level) - if(1)//Only one of these should ever be active at once so clear them all here - job_civilian_high = 0 - job_medsci_high = 0 - job_engsec_high = 0 - return 1 - if(2)//Set current highs to med, then reset them - job_civilian_med |= job_civilian_high - job_medsci_med |= job_medsci_high - job_engsec_med |= job_engsec_high - job_civilian_high = 0 - job_medsci_high = 0 - job_engsec_high = 0 +/datum/preferences/proc/SetJobDepartment(var/datum/job/job, var/level) + if(!job || !level) return 0 + switch(level) + if(1)//Only one of these should ever be active at once so clear them all here + job_civilian_high = 0 + job_medsci_high = 0 + job_engsec_high = 0 + return 1 + if(2)//Set current highs to med, then reset them + job_civilian_med |= job_civilian_high + job_medsci_med |= job_medsci_high + job_engsec_med |= job_engsec_high + job_civilian_high = 0 + job_medsci_high = 0 + job_engsec_high = 0 - switch(job.department_flag) - if(CIVILIAN) - switch(level) - if(2) - job_civilian_high = job.flag - job_civilian_med &= ~job.flag - if(3) - job_civilian_med |= job.flag - job_civilian_low &= ~job.flag - else - job_civilian_low |= job.flag - if(MEDSCI) - switch(level) - if(2) - job_medsci_high = job.flag - job_medsci_med &= ~job.flag - if(3) - job_medsci_med |= job.flag - job_medsci_low &= ~job.flag - else - job_medsci_low |= job.flag - if(ENGSEC) - switch(level) - if(2) - job_engsec_high = job.flag - job_engsec_med &= ~job.flag - if(3) - job_engsec_med |= job.flag - job_engsec_low &= ~job.flag - else - job_engsec_low |= job.flag + switch(job.department_flag) + if(CIVILIAN) + switch(level) + if(2) + job_civilian_high = job.flag + job_civilian_med &= ~job.flag + if(3) + job_civilian_med |= job.flag + job_civilian_low &= ~job.flag + else + job_civilian_low |= job.flag + if(MEDSCI) + switch(level) + if(2) + job_medsci_high = job.flag + job_medsci_med &= ~job.flag + if(3) + job_medsci_med |= job.flag + job_medsci_low &= ~job.flag + else + job_medsci_low |= job.flag + if(ENGSEC) + switch(level) + if(2) + job_engsec_high = job.flag + job_engsec_med &= ~job.flag + if(3) + job_engsec_med |= job.flag + job_engsec_low &= ~job.flag + else + job_engsec_low |= job.flag + return 1 + +/datum/preferences/proc/process_link(mob/user, list/href_list) + if(!user) return + + if(!istype(user, /mob/new_player)) return + if(href_list["preference"] == "job") + switch(href_list["task"]) + if("close") + user << browse(null, "window=mob_occupation") + ShowChoices(user) + if("reset") + ResetJobs() + SetChoices(user) + if("random") + if(alternate_option == GET_RANDOM_JOB || alternate_option == BE_ASSISTANT) + alternate_option += 1 + else if(alternate_option == RETURN_TO_LOBBY) + alternate_option = 0 + else + return 0 + SetChoices(user) + if ("alt_title") + var/datum/job/job = locate(href_list["job"]) + if (job) + var/choices = list(job.title) + job.alt_titles + var/choice = input("Pick a title for [job.title].", "Character Generation", GetPlayerAltTitle(job)) as anything in choices | null + if(choice) + SetPlayerAltTitle(job, choice) + SetChoices(user) + if("input") + SetJob(user, href_list["text"]) + else + SetChoices(user) + return 1 + else if(href_list["preference"] == "skills") + if(href_list["cancel"]) + user << browse(null, "window=show_skills") + ShowChoices(user) + else if(href_list["skillinfo"]) + var/datum/skill/S = locate(href_list["skillinfo"]) + var/HTML = "[S.name]
    [S.desc]" + user << browse(HTML, "window=\ref[user]skillinfo") + else if(href_list["setskill"]) + var/datum/skill/S = locate(href_list["setskill"]) + var/value = text2num(href_list["newvalue"]) + skills[S.ID] = value + CalculateSkillPoints() + SetSkills(user) + else if(href_list["preconfigured"]) + var/selected = input(user, "Select a skillset", "Skillset") as null|anything in SKILL_PRE + if(!selected) return + + ZeroSkills(1) + for(var/V in SKILL_PRE[selected]) + if(V == "field") + skill_specialization = SKILL_PRE[selected]["field"] + continue + skills[V] = SKILL_PRE[selected][V] + CalculateSkillPoints() + + SetSkills(user) + else if(href_list["setspecialization"]) + skill_specialization = href_list["setspecialization"] + CalculateSkillPoints() + SetSkills(user) + else + SetSkills(user) return 1 - proc/process_link(mob/user, list/href_list) - if(!user) return + else if (href_list["preference"] == "loadout") - if(!istype(user, /mob/new_player)) return - if(href_list["preference"] == "job") - switch(href_list["task"]) - if("close") - user << browse(null, "window=mob_occupation") - ShowChoices(user) - if("reset") - ResetJobs() - SetChoices(user) - if("random") - if(alternate_option == GET_RANDOM_JOB || alternate_option == BE_ASSISTANT) - alternate_option += 1 - else if(alternate_option == RETURN_TO_LOBBY) - alternate_option = 0 - else - return 0 - SetChoices(user) - if ("alt_title") - var/datum/job/job = locate(href_list["job"]) - if (job) - var/choices = list(job.title) + job.alt_titles - var/choice = input("Pick a title for [job.title].", "Character Generation", GetPlayerAltTitle(job)) as anything in choices | null - if(choice) - SetPlayerAltTitle(job, choice) - SetChoices(user) - if("input") - SetJob(user, href_list["text"]) + if(href_list["task"] == "input") + + var/list/valid_gear_choices = list() + + for(var/gear_name in gear_datums) + var/datum/gear/G = gear_datums[gear_name] + if(G.whitelisted && !is_alien_whitelisted(user, G.whitelisted)) + continue + valid_gear_choices += gear_name + + var/choice = input(user, "Select gear to add: ") as null|anything in valid_gear_choices + + if(choice && gear_datums[choice]) + + var/total_cost = 0 + + if(isnull(gear) || !islist(gear)) gear = list() + + if(gear && gear.len) + for(var/gear_name in gear) + if(gear_datums[gear_name]) + var/datum/gear/G = gear_datums[gear_name] + total_cost += G.cost + + var/datum/gear/C = gear_datums[choice] + total_cost += C.cost + if(C && total_cost <= MAX_GEAR_COST) + gear += choice + user << "\blue Added [choice] for [C.cost] points ([MAX_GEAR_COST - total_cost] points remaining)." else - SetChoices(user) - return 1 - else if(href_list["preference"] == "skills") - if(href_list["cancel"]) - user << browse(null, "window=show_skills") - ShowChoices(user) - else if(href_list["skillinfo"]) - var/datum/skill/S = locate(href_list["skillinfo"]) - var/HTML = "[S.name]
    [S.desc]" - user << browse(HTML, "window=\ref[user]skillinfo") - else if(href_list["setskill"]) - var/datum/skill/S = locate(href_list["setskill"]) - var/value = text2num(href_list["newvalue"]) - skills[S.ID] = value - CalculateSkillPoints() - SetSkills(user) - else if(href_list["preconfigured"]) - var/selected = input(user, "Select a skillset", "Skillset") as null|anything in SKILL_PRE - if(!selected) return + user << "\red That item will exceed the maximum loadout cost of [MAX_GEAR_COST] points." - ZeroSkills(1) - for(var/V in SKILL_PRE[selected]) - if(V == "field") - skill_specialization = SKILL_PRE[selected]["field"] - continue - skills[V] = SKILL_PRE[selected][V] - CalculateSkillPoints() - - SetSkills(user) - else if(href_list["setspecialization"]) - skill_specialization = href_list["setspecialization"] - CalculateSkillPoints() - SetSkills(user) - else - SetSkills(user) - return 1 - - else if(href_list["preference"] == "records") - if(text2num(href_list["record"]) >= 1) - SetRecords(user) - return - else - user << browse(null, "window=records") - if(href_list["task"] == "med_record") - var/medmsg = input(usr,"Set your medical notes here.","Medical Records",html_decode(med_record)) as message - - if(medmsg != null) - medmsg = copytext(medmsg, 1, MAX_PAPER_MESSAGE_LEN) - medmsg = html_encode(medmsg) - - med_record = medmsg - SetRecords(user) - - if(href_list["task"] == "sec_record") - var/secmsg = input(usr,"Set your security notes here.","Security Records",html_decode(sec_record)) as message - - if(secmsg != null) - secmsg = copytext(secmsg, 1, MAX_PAPER_MESSAGE_LEN) - secmsg = html_encode(secmsg) - - sec_record = secmsg - SetRecords(user) - if(href_list["task"] == "gen_record") - var/genmsg = input(usr,"Set your employment notes here.","Employment Records",html_decode(gen_record)) as message - - if(genmsg != null) - genmsg = copytext(genmsg, 1, MAX_PAPER_MESSAGE_LEN) - genmsg = html_encode(genmsg) - - gen_record = genmsg - SetRecords(user) - - else if (href_list["preference"] == "antagoptions") - if(text2num(href_list["active"]) == 0) - SetAntagoptions(user) - return - if (href_list["antagtask"] == "uplinktype") - if (uplinklocation == "PDA") - uplinklocation = "Headset" - else if(uplinklocation == "Headset") - uplinklocation = "None" - else - uplinklocation = "PDA" - SetAntagoptions(user) - if (href_list["antagtask"] == "done") - user << browse(null, "window=antagoptions") - ShowChoices(user) - return 1 - - - else if (href_list["preference"] == "loadout") - - if(href_list["task"] == "input") - - var/list/valid_gear_choices = list() - - for(var/gear_name in gear_datums) - var/datum/gear/G = gear_datums[gear_name] - if(G.whitelisted && !is_alien_whitelisted(user, G.whitelisted)) - continue - valid_gear_choices += gear_name - - var/choice = input(user, "Select gear to add: ") as null|anything in valid_gear_choices - - if(choice && gear_datums[choice]) - - var/total_cost = 0 - - if(isnull(gear) || !islist(gear)) gear = list() - - if(gear && gear.len) - for(var/gear_name in gear) - if(gear_datums[gear_name]) - var/datum/gear/G = gear_datums[gear_name] - total_cost += G.cost - - var/datum/gear/C = gear_datums[choice] - total_cost += C.cost - if(C && total_cost <= MAX_GEAR_COST) - gear += choice - user << "\blue Added [choice] for [C.cost] points ([MAX_GEAR_COST - total_cost] points remaining)." - else - user << "\red That item will exceed the maximum loadout cost of [MAX_GEAR_COST] points." - - else if(href_list["task"] == "remove") - var/to_remove = href_list["gear"] - if(!to_remove) return - for(var/gear_name in gear) - if(gear_name == to_remove) - gear -= gear_name - break + else if(href_list["task"] == "remove") + var/to_remove = href_list["gear"] + if(!to_remove) return + for(var/gear_name in gear) + if(gear_name == to_remove) + gear -= gear_name + break + else if(href_list["preference"] == "flavor_text") switch(href_list["task"]) - if("random") - switch(href_list["preference"]) - if("name") - real_name = random_name(gender) - if("age") - age = rand(AGE_MIN, AGE_MAX) - if("hair") - r_hair = rand(0,255) - g_hair = rand(0,255) - b_hair = rand(0,255) - if("h_style") - h_style = random_hair_style(gender, species) - if("facial") - r_facial = rand(0,255) - g_facial = rand(0,255) - b_facial = rand(0,255) - if("f_style") - f_style = random_facial_hair_style(gender, species) - if("underwear") - underwear = rand(1,underwear_m.len) - ShowChoices(user) - if("undershirt") - undershirt = rand(1,undershirt_t.len) - ShowChoices(user) - if("eyes") - r_eyes = rand(0,255) - g_eyes = rand(0,255) - b_eyes = rand(0,255) - if("s_tone") - s_tone = random_skin_tone() - if("s_color") - r_skin = rand(0,255) - g_skin = rand(0,255) - b_skin = rand(0,255) - if("bag") - backbag = rand(1,4) - /*if("skin_style") - h_style = random_skin_style(gender)*/ - if("all") - randomize_appearance_for() //no params needed - if("input") - switch(href_list["preference"]) - if("name") - var/new_name = reject_bad_name( input(user, "Choose your character's name:", "Character Preference") as text|null ) + if("open") + SetFlavorText(user) + return + if("done") + user << browse(null, "window=flavor_text") + ShowChoices(user) + return + if("general") + var/msg = input(usr,"Give a 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["task"]])) as message + if(msg != null) + msg = copytext(msg, 1, MAX_MESSAGE_LEN) + msg = html_encode(msg) + flavor_texts[href_list["task"]] = msg + else + var/msg = input(usr,"Set the flavor text for your [href_list["task"]].","Flavor Text",html_decode(flavor_texts[href_list["task"]])) as message + if(msg != null) + msg = copytext(msg, 1, MAX_MESSAGE_LEN) + msg = html_encode(msg) + flavor_texts[href_list["task"]] = msg + SetFlavorText(user) + return + + else if(href_list["preference"] == "pAI") + paiController.recruitWindow(user, 0) + return 1 + + else if(href_list["preference"] == "records") + if(text2num(href_list["record"]) >= 1) + SetRecords(user) + return + else + user << browse(null, "window=records") + if(href_list["task"] == "med_record") + var/medmsg = input(usr,"Set your medical notes here.","Medical Records",html_decode(med_record)) as message + + if(medmsg != null) + medmsg = copytext(medmsg, 1, MAX_PAPER_MESSAGE_LEN) + medmsg = html_encode(medmsg) + + med_record = medmsg + SetRecords(user) + + if(href_list["task"] == "sec_record") + var/secmsg = input(usr,"Set your security notes here.","Security Records",html_decode(sec_record)) as message + + if(secmsg != null) + secmsg = copytext(secmsg, 1, MAX_PAPER_MESSAGE_LEN) + secmsg = html_encode(secmsg) + + sec_record = secmsg + SetRecords(user) + if(href_list["task"] == "gen_record") + var/genmsg = input(usr,"Set your employment notes here.","Employment Records",html_decode(gen_record)) as message + + if(genmsg != null) + genmsg = copytext(genmsg, 1, MAX_PAPER_MESSAGE_LEN) + genmsg = html_encode(genmsg) + + gen_record = genmsg + SetRecords(user) + + else if (href_list["preference"] == "antagoptions") + if(text2num(href_list["active"]) == 0) + SetAntagoptions(user) + return + if (href_list["antagtask"] == "uplinktype") + if (uplinklocation == "PDA") + uplinklocation = "Headset" + else if(uplinklocation == "Headset") + uplinklocation = "None" + else + uplinklocation = "PDA" + SetAntagoptions(user) + if (href_list["antagtask"] == "done") + user << browse(null, "window=antagoptions") + ShowChoices(user) + return 1 + + else if (href_list["preference"] == "loadout") + + if(href_list["task"] == "input") + + var/list/valid_gear_choices = list() + + for(var/gear_name in gear_datums) + var/datum/gear/G = gear_datums[gear_name] + if(G.whitelisted && !is_alien_whitelisted(user, G.whitelisted)) + continue + valid_gear_choices += gear_name + + var/choice = input(user, "Select gear to add: ") as null|anything in valid_gear_choices + + if(choice && gear_datums[choice]) + + var/total_cost = 0 + + if(isnull(gear) || !islist(gear)) gear = list() + + if(gear && gear.len) + for(var/gear_name in gear) + if(gear_datums[gear_name]) + var/datum/gear/G = gear_datums[gear_name] + total_cost += G.cost + + var/datum/gear/C = gear_datums[choice] + total_cost += C.cost + if(C && total_cost <= MAX_GEAR_COST) + gear += choice + user << "\blue Added [choice] for [C.cost] points ([MAX_GEAR_COST - total_cost] points remaining)." + else + user << "\red That item will exceed the maximum loadout cost of [MAX_GEAR_COST] points." + + else if(href_list["task"] == "remove") + + if(isnull(gear) || !islist(gear)) + gear = list() + if(!gear.len) + return + + var/choice = input(user, "Select gear to remove: ") as null|anything in gear + if(!choice) + return + + for(var/gear_name in gear) + if(gear_name == choice) + gear -= gear_name + break + + switch(href_list["task"]) + if("random") + switch(href_list["preference"]) + if("name") + real_name = random_name(gender) + if("age") + age = rand(AGE_MIN, AGE_MAX) + if("hair") + r_hair = rand(0,255) + g_hair = rand(0,255) + b_hair = rand(0,255) + if("h_style") + h_style = random_hair_style(gender, species) + if("facial") + r_facial = rand(0,255) + g_facial = rand(0,255) + b_facial = rand(0,255) + if("f_style") + f_style = random_facial_hair_style(gender, species) + if("underwear") + underwear = rand(1,underwear_m.len) + ShowChoices(user) + if("undershirt") + undershirt = rand(1,undershirt_t.len) + ShowChoices(user) + if("eyes") + r_eyes = rand(0,255) + g_eyes = rand(0,255) + b_eyes = rand(0,255) + if("s_tone") + s_tone = random_skin_tone() + if("s_color") + r_skin = rand(0,255) + g_skin = rand(0,255) + b_skin = rand(0,255) + if("bag") + backbag = rand(1,4) + /*if("skin_style") + h_style = random_skin_style(gender)*/ + if("all") + randomize_appearance_for() //no params needed + if("input") + switch(href_list["preference"]) + if("name") + var/raw_name = input(user, "Choose your character's name:", "Character Preference") as text|null + if (!isnull(raw_name)) // Check to ensure that the user entered text (rather than cancel.) + var/new_name = reject_bad_name(raw_name) if(new_name) real_name = new_name else user << "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and ." - if("age") - var/new_age = input(user, "Choose your character's age:\n([AGE_MIN]-[AGE_MAX])", "Character Preference") as num|null - if(new_age) - age = max(min( round(text2num(new_age)), AGE_MAX),AGE_MIN) - if("species") + if("age") + var/new_age = input(user, "Choose your character's age:\n([AGE_MIN]-[AGE_MAX])", "Character Preference") as num|null + if(new_age) + age = max(min( round(text2num(new_age)), AGE_MAX),AGE_MIN) + if("species") - var/list/new_species = list("Human") - var/prev_species = species - var/whitelisted = 0 + var/list/new_species = list("Human") + var/prev_species = species + var/whitelisted = 0 - if(config.usealienwhitelist) //If we're using the whitelist, make sure to check it! - for(var/S in whitelisted_species) - if(is_alien_whitelisted(user,S)) - new_species += S - whitelisted = 1 - if(!whitelisted) - alert(user, "You cannot change your species as you need to be whitelisted. If you wish to be whitelisted contact an admin in-game, on the forums, or on IRC.") - else //Not using the whitelist? Aliens for everyone! - new_species = whitelisted_species + if(config.usealienwhitelist) //If we're using the whitelist, make sure to check it! + for(var/S in whitelisted_species) + if(is_alien_whitelisted(user,S)) + new_species += S + whitelisted = 1 + if(!whitelisted) + alert(user, "You cannot change your species as you need to be whitelisted. If you wish to be whitelisted contact an admin in-game, on the forums, or on IRC.") + else //Not using the whitelist? Aliens for everyone! + new_species = whitelisted_species - species = input("Please select a species", "Character Generation", null) in new_species + species = input("Please select a species", "Character Generation", null) in new_species - if(prev_species != species) - //grab one of the valid hair styles for the newly chosen species - var/list/valid_hairstyles = list() - for(var/hairstyle in hair_styles_list) - var/datum/sprite_accessory/S = hair_styles_list[hairstyle] - if(gender == MALE && S.gender == FEMALE) - continue - if(gender == FEMALE && S.gender == MALE) - continue - if( !(species in S.species_allowed)) - continue - valid_hairstyles[hairstyle] = hair_styles_list[hairstyle] - - if(valid_hairstyles.len) - h_style = pick(valid_hairstyles) - else - //this shouldn't happen - h_style = hair_styles_list["Bald"] - - //grab one of the valid facial hair styles for the newly chosen species - var/list/valid_facialhairstyles = list() - for(var/facialhairstyle in facial_hair_styles_list) - var/datum/sprite_accessory/S = facial_hair_styles_list[facialhairstyle] - if(gender == MALE && S.gender == FEMALE) - continue - if(gender == FEMALE && S.gender == MALE) - continue - if( !(species in S.species_allowed)) - continue - - valid_facialhairstyles[facialhairstyle] = facial_hair_styles_list[facialhairstyle] - - if(valid_facialhairstyles.len) - f_style = pick(valid_facialhairstyles) - else - //this shouldn't happen - f_style = facial_hair_styles_list["Shaved"] - - //reset hair colour and skin colour - r_hair = 0//hex2num(copytext(new_hair, 2, 4)) - g_hair = 0//hex2num(copytext(new_hair, 4, 6)) - b_hair = 0//hex2num(copytext(new_hair, 6, 8)) - - s_tone = 0 - - if("language") - var/languages_available - var/list/new_languages = list("None") - - if(config.usealienwhitelist) - for(var/L in all_languages) - var/datum/language/lang = all_languages[L] - if((!(lang.flags & RESTRICTED)) && (is_alien_whitelisted(user, L)||(!( lang.flags & WHITELISTED )))) - new_languages += lang - languages_available = 1 - - if(!(languages_available)) - alert(user, "There are not currently any available secondary languages.") - else - for(var/L in all_languages) - var/datum/language/lang = all_languages[L] - if(!(lang.flags & RESTRICTED)) - new_languages += lang.name - - language = input("Please select a secondary language", "Character Generation", null) in new_languages - - if("metadata") - var/new_metadata = input(user, "Enter any information you'd like others to see, such as Roleplay-preferences:", "Game Preference" , metadata) as message|null - if(new_metadata) - metadata = sanitize(copytext(new_metadata,1,MAX_MESSAGE_LEN)) - - if("b_type") -// if(species != "Kocasslani") - var/new_b_type = input(user, "Choose your character's blood-type:", "Character Preference") as null|anything in list( "A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-" ) - if(new_b_type) - b_type = new_b_type -/* else - b_type = "K" -*/ - if("hair") - if(species == "Human" || species == "Unathi" || species == "Tajaran" || species == "Skrell") - var/new_hair = input(user, "Choose your character's hair colour:", "Character Preference") as color|null - if(new_hair) - r_hair = hex2num(copytext(new_hair, 2, 4)) - g_hair = hex2num(copytext(new_hair, 4, 6)) - b_hair = hex2num(copytext(new_hair, 6, 8)) - - if("h_style") + if(prev_species != species) + //grab one of the valid hair styles for the newly chosen species var/list/valid_hairstyles = list() for(var/hairstyle in hair_styles_list) var/datum/sprite_accessory/S = hair_styles_list[hairstyle] + if(gender == MALE && S.gender == FEMALE) + continue + if(gender == FEMALE && S.gender == MALE) + continue if( !(species in S.species_allowed)) continue - valid_hairstyles[hairstyle] = hair_styles_list[hairstyle] - var/new_h_style = input(user, "Choose your character's hair style:", "Character Preference") as null|anything in valid_hairstyles - if(new_h_style) - h_style = new_h_style + if(valid_hairstyles.len) + h_style = pick(valid_hairstyles) + else + //this shouldn't happen + h_style = hair_styles_list["Bald"] - if("facial") - var/new_facial = input(user, "Choose your character's facial-hair colour:", "Character Preference") as color|null - if(new_facial) - r_facial = hex2num(copytext(new_facial, 2, 4)) - g_facial = hex2num(copytext(new_facial, 4, 6)) - b_facial = hex2num(copytext(new_facial, 6, 8)) - - if("f_style") + //grab one of the valid facial hair styles for the newly chosen species var/list/valid_facialhairstyles = list() for(var/facialhairstyle in facial_hair_styles_list) var/datum/sprite_accessory/S = facial_hair_styles_list[facialhairstyle] @@ -1091,366 +1122,492 @@ datum/preferences valid_facialhairstyles[facialhairstyle] = facial_hair_styles_list[facialhairstyle] - var/new_f_style = input(user, "Choose your character's facial-hair style:", "Character Preference") as null|anything in valid_facialhairstyles - if(new_f_style) - f_style = new_f_style - - if("underwear") - var/list/underwear_options - if(gender == MALE) - underwear_options = underwear_m + if(valid_facialhairstyles.len) + f_style = pick(valid_facialhairstyles) else - underwear_options = underwear_f + //this shouldn't happen + f_style = facial_hair_styles_list["Shaved"] - var/new_underwear = input(user, "Choose your character's underwear:", "Character Preference") as null|anything in underwear_options - if(new_underwear) - underwear = underwear_options.Find(new_underwear) - ShowChoices(user) + //reset hair colour and skin colour + r_hair = 0//hex2num(copytext(new_hair, 2, 4)) + g_hair = 0//hex2num(copytext(new_hair, 4, 6)) + b_hair = 0//hex2num(copytext(new_hair, 6, 8)) - if("undershirt") - var/list/undershirt_options - undershirt_options = undershirt_t + s_tone = 0 - var/new_undershirt = input(user, "Choose your character's undershirt:", "Character Preference") as null|anything in undershirt_options - if (new_undershirt) - undershirt = undershirt_options.Find(new_undershirt) - ShowChoices(user) + if("language") + var/languages_available + var/list/new_languages = list("None") + var/datum/species/S = all_species[species] - if("eyes") - var/new_eyes = input(user, "Choose your character's eye colour:", "Character Preference") as color|null - if(new_eyes) - r_eyes = hex2num(copytext(new_eyes, 2, 4)) - g_eyes = hex2num(copytext(new_eyes, 4, 6)) - b_eyes = hex2num(copytext(new_eyes, 6, 8)) + if(config.usealienwhitelist) + for(var/L in all_languages) + var/datum/language/lang = all_languages[L] + if((!(lang.flags & RESTRICTED)) && (is_alien_whitelisted(user, L)||(!( lang.flags & WHITELISTED ))||(S && (L in S.secondary_langs)))) + new_languages += lang + languages_available = 1 - if("s_tone") - if(species != "Human") - return - var/new_s_tone = input(user, "Choose your character's skin-tone:\n(Light 1 - 220 Dark)", "Character Preference") as num|null - if(new_s_tone) - s_tone = 35 - max(min( round(new_s_tone), 220),1) + if(!(languages_available)) + alert(user, "There are not currently any available secondary languages.") + else + for(var/L in all_languages) + var/datum/language/lang = all_languages[L] + if(!(lang.flags & RESTRICTED)) + new_languages += lang.name - if("skin") - if(species == "Unathi" || species == "Tajaran" || species == "Skrell") - var/new_skin = input(user, "Choose your character's skin colour: ", "Character Preference") as color|null - if(new_skin) - r_skin = hex2num(copytext(new_skin, 2, 4)) - g_skin = hex2num(copytext(new_skin, 4, 6)) - b_skin = hex2num(copytext(new_skin, 6, 8)) + language = input("Please select a secondary language", "Character Generation", null) in new_languages - if("ooccolor") - var/new_ooccolor = input(user, "Choose your OOC colour:", "Game Preference") as color|null - if(new_ooccolor) - ooccolor = new_ooccolor + if("metadata") + var/new_metadata = input(user, "Enter any information you'd like others to see, such as Roleplay-preferences:", "Game Preference" , metadata) as message|null + if(new_metadata) + metadata = sanitize(copytext(new_metadata,1,MAX_MESSAGE_LEN)) - if("bag") - var/new_backbag = input(user, "Choose your character's style of bag:", "Character Preference") as null|anything in backbaglist - if(new_backbag) - backbag = backbaglist.Find(new_backbag) + if("b_type") + var/new_b_type = input(user, "Choose your character's blood-type:", "Character Preference") as null|anything in list( "A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-" ) + if(new_b_type) + b_type = new_b_type - if("nt_relation") - var/new_relation = input(user, "Choose your relation to NT. Note that this represents what others can find out about your character by researching your background, not what your character actually thinks.", "Character Preference") as null|anything in list("Loyal", "Supportive", "Neutral", "Skeptical", "Opposed") - if(new_relation) - nanotrasen_relation = new_relation + if("hair") + if(species == "Human" || species == "Unathi" || species == "Tajaran" || species == "Skrell") + var/new_hair = input(user, "Choose your character's hair colour:", "Character Preference") as color|null + if(new_hair) + r_hair = hex2num(copytext(new_hair, 2, 4)) + g_hair = hex2num(copytext(new_hair, 4, 6)) + b_hair = hex2num(copytext(new_hair, 6, 8)) - if("flavor_text") - var/msg = input(usr,"Set the flavor text in your 'examine' verb. This should be something that can be observed at a glance or so!","Flavor Text",html_decode(flavor_text)) as message + if("h_style") + var/list/valid_hairstyles = list() + for(var/hairstyle in hair_styles_list) + var/datum/sprite_accessory/S = hair_styles_list[hairstyle] + if( !(species in S.species_allowed)) + continue - if(msg != null) - msg = copytext(msg, 1, MAX_MESSAGE_LEN) - msg = html_encode(msg) + valid_hairstyles[hairstyle] = hair_styles_list[hairstyle] - flavor_text = msg + var/new_h_style = input(user, "Choose your character's hair style:", "Character Preference") as null|anything in valid_hairstyles + if(new_h_style) + h_style = new_h_style - if("disabilities") - if(text2num(href_list["disabilities"]) >= -1) - if(text2num(href_list["disabilities"]) >= 0) - disabilities ^= (1<= -1) + if(text2num(href_list["disabilities"]) >= 0) + disabilities ^= (1<= 50)) return + UI_style_alpha = UI_style_alpha_new - var/new_state = input(user, "What state do you wish the limb to be in?") as null|anything in list("Normal","Amputated","Prothesis") - if(!new_state) return + if("be_special") + var/num = text2num(href_list["num"]) + be_special ^= (1<= 50)) return - UI_style_alpha = UI_style_alpha_new + ShowChoices(user) + return 1 - if("be_special") - var/num = text2num(href_list["num"]) - be_special ^= (1< underwear_m.len || underwear < 1) + underwear = 0 //I'm sure this is 100% unnecessary, but I'm paranoid... sue me. //HAH NOW NO MORE MAGIC CLONING UNDIES + character.underwear = underwear - if(underwear > underwear_m.len || underwear < 1) - underwear = 0 //I'm sure this is 100% unnecessary, but I'm paranoid... sue me. //HAH NOW NO MORE MAGIC CLONING UNDIES - character.underwear = underwear + if(undershirt > undershirt_t.len || undershirt < 1) + undershirt = 0 + character.undershirt = undershirt - if(undershirt > undershirt_t.len || undershirt < 1) - undershirt = 0 - character.undershirt = undershirt + if(backbag > 4 || backbag < 1) + backbag = 1 //Same as above + character.backbag = backbag - if(backbag > 4 || backbag < 1) - backbag = 1 //Same as above - character.backbag = backbag + //Debugging report to track down a bug, which randomly assigned the plural gender to people. + if(character.gender in list(PLURAL, NEUTER)) + if(isliving(src)) //Ghosts get neuter by default + message_admins("[character] ([character.ckey]) has spawned with their gender as plural or neuter. Please notify coders.") + character.gender = MALE - //Debugging report to track down a bug, which randomly assigned the plural gender to people. - if(character.gender in list(PLURAL, NEUTER)) - if(isliving(src)) //Ghosts get neuter by default - msg_scopes("[character] ([character.ckey]) has spawned with their gender as plural or neuter.") - message_admins("[character] ([character.ckey]) has spawned with their gender as plural or neuter. Please notify coders.") - character.gender = MALE +/datum/preferences/proc/open_load_dialog(mob/user) + var/dat = "" + dat += "
    " - proc/open_load_dialog(mob/user) - var/dat = "" - dat += "
    " + var/savefile/S = new /savefile(path) + if(S) + dat += "Select a character slot to load
    " + var/name + for(var/i=1, i<=MAX_SAVE_SLOTS, i++) + S.cd = "/character[i]" + S["real_name"] >> name + if(!name) name = "Character[i]" + if(i==default_slot) + name = "[name]" + dat += "[name]
    " - var/savefile/S = new /savefile(path) - if(S) - dat += "Select a character slot to load
    " - var/name - for(var/i=1, i<=MAX_SAVE_SLOTS, i++) - S.cd = "/character[i]" - S["real_name"] >> name - if(!name) name = "Character[i]" - if(i==default_slot) - name = "[name]" - dat += "[name]
    " + dat += "
    " + dat += "Close
    " + dat += "
    " + user << browse(dat, "window=saves;size=300x390") - dat += "
    " - dat += "Close
    " - dat += "
    " - user << browse(dat, "window=saves;size=300x390") - - proc/close_load_dialog(mob/user) - user << browse(null, "window=saves") +/datum/preferences/proc/close_load_dialog(mob/user) + user << browse(null, "window=saves") diff --git a/code/modules/client/preferences_factions.dm b/code/modules/client/preferences_factions.dm new file mode 100644 index 00000000..2293be7d --- /dev/null +++ b/code/modules/client/preferences_factions.dm @@ -0,0 +1,53 @@ +var/global/list/seen_citizenships = list() +var/global/list/seen_systems = list() +var/global/list/seen_factions = list() +var/global/list/seen_religions = list() + +//Commenting this out for now until I work the lists it into the event generator/journalist/chaplain. +/proc/UpdateFactionList(mob/living/carbon/human/M) + /*if(M && M.client && M.client.prefs) + seen_citizenships |= M.client.prefs.citizenship + seen_systems |= M.client.prefs.home_system + seen_factions |= M.client.prefs.faction + seen_religions |= M.client.prefs.religion*/ + return + +var/global/list/citizenship_choices = list( + "Earth", + "Mars", + "Moghes", + "Ahdomai", + "Qerrbalak" + ) + +var/global/list/home_system_choices = list( + "Sol", + "Nyx", + "Tau Ceti", + "Epsilon Ursae Majoris", + "S'randarr" + ) + +var/global/list/faction_choices = list( + "Sol Central", + "Vey Med", + "Einstein Engines", + "Free Trade Union", + "NanoTrasen", + "Ward-Takahashi GMB", + "Gilthari Exports", + "Grayson Manufactories Ltd.", + "Aether Atmospherics", + "Zeng-Hu Pharmaceuticals", + "Hesphaistos Industries" + ) + +var/global/list/religion_choices = list( + "Unitarianism", + "Hinduism", + "Buddhist", + "Islamic", + "Christian", + "Agnostic", + "Deist" + ) \ No newline at end of file diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index 57fef69a..cb375f71 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -108,6 +108,7 @@ S["age"] >> age S["species"] >> species S["language"] >> language + S["spawnpoint"] >> spawnpoint //colors to be consolidated into hex strings (requires some work with dna code) S["hair_red"] >> r_hair @@ -142,8 +143,18 @@ S["job_engsec_med"] >> job_engsec_med S["job_engsec_low"] >> job_engsec_low + //Flavour Text + S["flavor_texts_general"] >> flavor_texts["general"] + S["flavor_texts_head"] >> flavor_texts["head"] + S["flavor_texts_face"] >> flavor_texts["face"] + S["flavor_texts_eyes"] >> flavor_texts["eyes"] + S["flavor_texts_torso"] >> flavor_texts["torso"] + S["flavor_texts_arms"] >> flavor_texts["arms"] + S["flavor_texts_hands"] >> flavor_texts["hands"] + S["flavor_texts_legs"] >> flavor_texts["legs"] + S["flavor_texts_feet"] >> flavor_texts["feet"] + //Miscellaneous - S["flavor_text"] >> flavor_text S["med_record"] >> med_record S["sec_record"] >> sec_record S["gen_record"] >> gen_record @@ -154,7 +165,11 @@ S["skills"] >> skills S["skill_specialization"] >> skill_specialization S["organ_data"] >> organ_data - S["gear"] >> gear + S["gear"] >> gear + S["home_system"] >> home_system + S["citizenship"] >> citizenship + S["faction"] >> faction + S["religion"] >> religion S["nanotrasen_relation"] >> nanotrasen_relation //S["skin_style"] >> skin_style @@ -169,6 +184,7 @@ real_name = reject_bad_name(real_name) if(isnull(species)) species = "Human" if(isnull(language)) language = "None" + if(isnull(spawnpoint)) spawnpoint = "Arrivals Shuttle" if(isnull(nanotrasen_relation)) nanotrasen_relation = initial(nanotrasen_relation) if(!real_name) real_name = random_name(gender) be_random_name = sanitize_integer(be_random_name, 0, 1, initial(be_random_name)) @@ -213,6 +229,11 @@ if(!gear) src.gear = list() //if(!skin_style) skin_style = "Default" + if(!home_system) home_system = "Unset" + if(!citizenship) citizenship = "None" + if(!faction) faction = "None" + if(!religion) religion = "None" + return 1 /datum/preferences/proc/save_character() @@ -248,6 +269,7 @@ S["undershirt"] << undershirt S["backbag"] << backbag S["b_type"] << b_type + S["spawnpoint"] << spawnpoint //Jobs S["alternate_option"] << alternate_option @@ -261,8 +283,18 @@ S["job_engsec_med"] << job_engsec_med S["job_engsec_low"] << job_engsec_low + //Flavour Text + S["flavor_texts_general"] << flavor_texts["general"] + S["flavor_texts_head"] << flavor_texts["head"] + S["flavor_texts_face"] << flavor_texts["face"] + S["flavor_texts_eyes"] << flavor_texts["eyes"] + S["flavor_texts_torso"] << flavor_texts["torso"] + S["flavor_texts_arms"] << flavor_texts["arms"] + S["flavor_texts_hands"] << flavor_texts["hands"] + S["flavor_texts_legs"] << flavor_texts["legs"] + S["flavor_texts_feet"] << flavor_texts["feet"] + //Miscellaneous - S["flavor_text"] << flavor_text S["med_record"] << med_record S["sec_record"] << sec_record S["gen_record"] << gen_record @@ -273,7 +305,11 @@ S["skills"] << skills S["skill_specialization"] << skill_specialization S["organ_data"] << organ_data - S["gear"] << gear + S["gear"] << gear + S["home_system"] << home_system + S["citizenship"] << citizenship + S["faction"] << faction + S["religion"] << religion S["nanotrasen_relation"] << nanotrasen_relation //S["skin_style"] << skin_style diff --git a/code/modules/client/preferences_spawnpoints.dm b/code/modules/client/preferences_spawnpoints.dm new file mode 100644 index 00000000..504a9620 --- /dev/null +++ b/code/modules/client/preferences_spawnpoints.dm @@ -0,0 +1,36 @@ +var/list/spawntypes = list() + +/proc/populate_spawn_points() + spawntypes = list() + for(var/type in typesof(/datum/spawnpoint)-/datum/spawnpoint) + var/datum/spawnpoint/S = new type() + spawntypes[S.display_name] = S + +/datum/spawnpoint + var/msg //Message to display on the arrivals computer. + var/list/turfs //List of turfs to spawn on. + var/display_name //Name used in preference setup. + +/datum/spawnpoint/arrivals + display_name = "Arrivals Shuttle" + msg = "has arrived on the station" + +/datum/spawnpoint/arrivals/New() + ..() + turfs = latejoin + +/datum/spawnpoint/gateway + display_name = "Gateway" + msg = "has completed translation from offsite gateway" + +/datum/spawnpoint/gateway/New() + ..() + turfs = latejoin_gateway + +/datum/spawnpoint/cryo + display_name = "Cryogenic Storage" + msg = "has completed cryogenic revival" + +/datum/spawnpoint/cryo/New() + ..() + turfs = latejoin_cryo \ No newline at end of file diff --git a/code/modules/client/preferences_toggles.dm b/code/modules/client/preferences_toggles.dm index 188d0bbb..78d8b6d7 100644 --- a/code/modules/client/preferences_toggles.dm +++ b/code/modules/client/preferences_toggles.dm @@ -25,7 +25,7 @@ src << "As a ghost, you will now [(prefs.toggles & CHAT_GHOSTRADIO) ? "hear all radio chat in the world" : "only hear from nearby speakers"]." prefs.save_preferences() feedback_add_details("admin_verb","TGR") - + /client/proc/toggle_hear_radio() set name = "Show/Hide RadioChatter" set category = "Preferences" @@ -172,10 +172,9 @@ icons.Add(usr.zone_sel) for(var/obj/screen/I in icons) - if(I.color && I.alpha) - I.icon = ui_style2icon(UI_style_new) - I.color = UI_style_color_new - I.alpha = UI_style_alpha_new + I.icon = ui_style2icon(UI_style_new) + I.color = UI_style_color_new + I.alpha = UI_style_alpha_new diff --git a/code/modules/clothing/glasses/glasses.dm b/code/modules/clothing/glasses/glasses.dm index 59fb4894..e01aa71b 100644 --- a/code/modules/clothing/glasses/glasses.dm +++ b/code/modules/clothing/glasses/glasses.dm @@ -106,8 +106,7 @@ icon_state = "sun" item_state = "sunglasses" darkness_view = -1 - g_amt = 1000 - m_amt = 500 + matter = list("metal" = 500, "glass" = 1000) /obj/item/clothing/glasses/welding name = "welding goggles" diff --git a/code/modules/clothing/head/misc_special.dm b/code/modules/clothing/head/misc_special.dm index e703e7a4..cf22301d 100644 --- a/code/modules/clothing/head/misc_special.dm +++ b/code/modules/clothing/head/misc_special.dm @@ -17,8 +17,7 @@ icon_state = "welding" flags = (FPRINT | TABLEPASS | HEADCOVERSEYES | HEADCOVERSMOUTH) item_state = "welding" - m_amt = 3000 - g_amt = 1000 + matter = list("metal" = 3000, "glass" = 1000) var/up = 0 armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) flags_inv = (HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE) diff --git a/code/modules/customitems/baystation_fluff.dm b/code/modules/customitems/baystation_fluff.dm index 7b18520f..e83ef9ea 100644 --- a/code/modules/customitems/baystation_fluff.dm +++ b/code/modules/customitems/baystation_fluff.dm @@ -308,7 +308,7 @@ desc = "The message: 'Please do not be removing this beaker from the chemistry lab. If lost, return to Nashida Bisha'ra' can be seen etched into the side of this 100 unit beaker." icon = 'icons/obj/chemical.dmi' icon_state = "beakerlarge" - g_amt = 5000 + matter = list("glass" = 5000) volume = 100 /obj/item/weapon/reagent_containers/glass/beaker/fluff/eleanor_stone //Rkf45: Eleanor Stone diff --git a/code/modules/mining/mine_items.dm b/code/modules/mining/mine_items.dm index 2c777c8d..cfa1e5af 100644 --- a/code/modules/mining/mine_items.dm +++ b/code/modules/mining/mine_items.dm @@ -170,8 +170,7 @@ proc/move_mining_shuttle() name = "lantern" icon_state = "lantern" desc = "A mining lantern." - m_amt = 1000 - g_amt = 500 + matter = list("glass" = 500,"metal" = 1000) brightness_on = 6 // luminosity when on /*****************************Pickaxe********************************/ @@ -186,7 +185,7 @@ proc/move_mining_shuttle() throwforce = 4.0 item_state = "pickaxe" w_class = 4.0 - m_amt = 3750 //one sheet, but where can you make them? + matter = list("metal" = 3750) var/digspeed = 40 //moving the delay to an item var so R&D can make improved picks. --NEO origin_tech = "materials=1;engineering=1" attack_verb = list("hit", "pierced", "sliced", "attacked") @@ -284,7 +283,7 @@ proc/move_mining_shuttle() throwforce = 4.0 item_state = "shovel" w_class = 3.0 - m_amt = 50 + matter = list("metal" = 50) origin_tech = "materials=1;engineering=1" attack_verb = list("bashed", "bludgeoned", "thrashed", "whacked") diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index c4d96229..c0f82243 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -69,6 +69,7 @@ ..() + /mob/dead/observer/Topic(href, href_list) if (href_list["track"]) var/mob/target = locate(href_list["track"]) in mob_list @@ -173,10 +174,11 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp var/response = alert(src, "Are you -sure- you want to ghost?\n(You are alive. If you ghost, you won't be able to play this round for another 30 minutes! You can't change your mind so choose wisely!)","Are you sure you want to ghost?","Ghost","Stay in body") if(response != "Ghost") return //didn't want to ghost after-all resting = 1 + var/turf/location = get_turf(src) + message_admins("[key_name_admin(usr)] has ghosted. (JMP)") + log_game("[key_name_admin(usr)] has ghosted.") var/mob/dead/observer/ghost = ghostize(0) //0 parameter is so we can never re-enter our body, "Charlie, you can never come baaaack~" :3 ghost.timeofdeath = world.time // Because the living mob won't have a time of death and we want the respawn timer to work properly. - if(istype(src, /mob/living/simple_animal/mouse)) - ghost.client.time_died_as_mouse = world.time return @@ -185,7 +187,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp if(NewLoc) loc = NewLoc for(var/obj/effect/step_trigger/S in NewLoc) - S.HasEntered(src) + S.Crossed(src) return loc = get_turf(src) //Get out of closets and such as a ghost @@ -199,7 +201,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp x-- for(var/obj/effect/step_trigger/S in locate(x, y, z)) //<-- this is dumb - S.HasEntered(src) + S.Crossed(src) /mob/dead/observer/examine() if(usr) @@ -221,10 +223,9 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp if(ticker.mode:malf_mode_declared) stat(null, "Time left: [max(ticker.mode:AI_win_timeleft/(ticker.mode:apcs/3), 0)]") if(emergency_shuttle) - if(emergency_shuttle.online && emergency_shuttle.location < 2) - var/timeleft = emergency_shuttle.timeleft() - if (timeleft) - stat(null, "ETA-[(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]") + var/eta_status = emergency_shuttle.get_status_panel_eta() + if(eta_status) + stat(null, eta_status) /mob/dead/observer/verb/reenter_corpse() set category = "Ghost" @@ -262,16 +263,14 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp set category = "Ghost" set name = "Toggle AntagHUD" set desc = "Toggles AntagHUD allowing you to see who is the antagonist" - if(!config.antag_hud_allowed && !client.holder) - src << "\red Admins have disabled this for this round." - return + if(!client) return var/mob/dead/observer/M = src if(jobban_isbanned(M, "AntagHUD")) src << "\red You have been banned from using this feature" return - if(config.antag_hud_restricted && !M.has_enabled_antagHUD &&!client.holder) + if(config.antag_hud_restricted && !M.has_enabled_antagHUD && !client.holder) var/response = alert(src, "If you turn this on, you will not be able to take any part in the round.","Are you sure you want to turn this feature on?","Yes","No") if(response == "No") return M.can_reenter_corpse = 0 @@ -401,7 +400,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp var/datum/gas_mixture/environment = usr.loc.return_air() var/pressure = environment.return_pressure() - var/total_moles = environment.total_moles() + var/total_moles = environment.total_moles src << "\blue Results:" if(abs(pressure - ONE_ATMOSPHERE) < 10) @@ -409,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/plasma_concentration = environment.toxins/total_moles - - var/unknown_concentration = 1-(o2_concentration+n2_concentration+co2_concentration+plasma_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(plasma_concentration > 0.01) - src << "\red Plasma: [round(plasma_concentration*100)]% ([round(environment.toxins,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/hear_say.dm b/code/modules/mob/hear_say.dm index 49554154..221863ce 100644 --- a/code/modules/mob/hear_say.dm +++ b/code/modules/mob/hear_say.dm @@ -1,14 +1,25 @@ // At minimum every mob has a hear_say proc. -/mob/proc/hear_say(var/message, var/verb = "says", var/datum/language/language = null, var/alt_name = "",var/italics = 0, var/mob/speaker = null) +/mob/proc/hear_say(var/message, var/verb = "says", var/datum/language/language = null, var/alt_name = "",var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol) if(!client) return + if(speaker && !speaker.client && istype(src,/mob/dead/observer) && client.prefs.toggles & CHAT_GHOSTEARS && !speaker in view(src)) + //Does the speaker have a client? It's either random stuff that observers won't care about (Experiment 97B says, 'EHEHEHEHEHEHEHE') + //Or someone snoring. So we make it where they won't hear it. + return + if(sleeping || stat == 1) hear_sleep(message) return var/style = "body" + + //non-verbal languages are garbled if you can't see the speaker. Yes, this includes if they are inside a closet. + if (language && (language.flags & NONVERBAL)) + if (!speaker || (src.sdisabilities & BLIND || src.blinded) || !(speaker in view(src))) + message = stars(message) + if(!say_understands(speaker,language)) if(istype(speaker,/mob/living/simple_animal)) var/mob/living/simple_animal/S = speaker @@ -17,7 +28,6 @@ message = stars(message) if(language) - verb = language.speech_verb style = language.colour var/speaker_name = speaker.name @@ -42,9 +52,12 @@ if(speaker == src) src << "You cannot hear yourself speak!" else - src << "[speaker_name][alt_name] talks but you cannot hear them." + src << "[speaker_name][alt_name] talks but you cannot hear \him." else src << "[speaker_name][alt_name] [track][verb], \"[message]\"" + if (speech_sound && (get_dist(speaker, src) <= world.view && src.z == speaker.z)) + var/turf/source = speaker? get_turf(speaker) : get_turf(src) + src.playsound_local(source, speech_sound, sound_vol, 1) /mob/proc/hear_radio(var/message, var/verb="says", var/datum/language/language=null, var/part_a, var/part_b, var/mob/speaker = null, var/hard_to_hear = 0, var/vname ="") @@ -60,6 +73,11 @@ var/style = "body" + //non-verbal languages are garbled if you can't see the speaker. Yes, this includes if they are inside a closet. + if (language && (language.flags & NONVERBAL)) + if (!speaker || (src.sdisabilities & BLIND || src.blinded) || !(speaker in view(src))) + message = stars(message) + if(!say_understands(speaker,language)) if(istype(speaker,/mob/living/simple_animal)) var/mob/living/simple_animal/S = speaker @@ -76,9 +94,7 @@ if(hard_to_hear) message = stars(message) - var/speaker_name - if(speaker) - speaker_name = speaker.name + var/speaker_name = speaker.name if(vname) speaker_name = vname @@ -145,6 +161,22 @@ else src << "[part_a][speaker_name][part_b][verb], \"[message]\"" +/mob/proc/hear_signlang(var/message, var/verb = "gestures", var/datum/language/language, var/mob/speaker = null) + if(!client) + return + + if(say_understands(speaker, language)) + message = "[src] [verb], \"[message]\"" + else + message = "[src] [verb]." + + if(src.status_flags & PASSEMOTES) + for(var/obj/item/weapon/holder/H in src.contents) + H.show_message(message) + for(var/mob/living/M in src.contents) + M.show_message(message) + src.show_message(message) + /mob/proc/hear_sleep(var/message) var/heard = "" if(prob(15)) @@ -162,48 +194,3 @@ heard = "...You almost hear someone talking..." src << heard - -/mob/proc/see_say(var/message, var/verb = "signs", var/datum/language/language = null, var/alt_name = "",var/italics = 0, var/mob/signer = null) - if(!client) - return - - if(sleeping || stat == 1) - return - - var/style = "body" - if(!say_understands(signer,language)) - if(istype(signer,/mob/living/simple_animal)) - var/mob/living/simple_animal/S = signer - message = pick(S.speak) - else - message = stars(message) - - if(language) - verb = language.speech_verb - style = language.colour - - var/signer_name = signer.name - if(istype(signer, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = signer - signer_name = H.GetVoice() - - if(italics) - message = "[message]" - - var/track = null - if(istype(src, /mob/dead/observer)) - if(italics && client.prefs.toggles & CHAT_GHOSTRADIO) - return - if(signer_name != signer.real_name && signer.real_name) - signer_name = "[signer.real_name] ([signer_name])" - track = "(follow) " - if(client.prefs.toggles & CHAT_GHOSTEARS && signer in view(src)) - message = "[message]" - if(sdisabilities & BLIND) - if(signer == src) - src << "You cannot see yourself sign!" - else - if((signer_name != signer.name) || (istype(src.wear_mask, /obj/item/clothing/mask/gas))) - src << "[signer.name][track] [verb], \"[message]\"" - else - src << "[signer_name][alt_name][track] [verb], \"[message]\"" \ No newline at end of file diff --git a/code/modules/mob/holder.dm b/code/modules/mob/holder.dm index a31c958b..d1adff7d 100644 --- a/code/modules/mob/holder.dm +++ b/code/modules/mob/holder.dm @@ -4,6 +4,7 @@ desc = "You shouldn't ever see this." icon = 'icons/obj/objects.dmi' slot_flags = SLOT_HEAD + sprite_sheets = list("Vox" = 'icons/mob/species/vox/head.dmi') /obj/item/weapon/holder/New() ..() @@ -34,18 +35,44 @@ for(var/mob/living/M in contents) M.show_message(message,m_type) +//Mob procs and vars for scooping up +/mob/living/var/holder_type + +/mob/living/proc/get_scooped(var/mob/living/carbon/grabber) + if(!holder_type) + return + var/obj/item/weapon/holder/H = new holder_type(loc) + src.loc = H + H.name = loc.name + H.attack_hand(grabber) + + grabber << "You scoop up [src]." + src << "[grabber] scoops you up." + grabber.status_flags |= PASSEMOTES + return + //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" \ No newline at end of file + 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 + +/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/language.dm b/code/modules/mob/language.dm index e7155c09..bfac8082 100644 --- a/code/modules/mob/language.dm +++ b/code/modules/mob/language.dm @@ -6,11 +6,11 @@ var/name = "an unknown language" // Fluff name of language if any. var/desc = "A language." // Short description for 'Check Languages'. var/speech_verb = "says" // 'says', 'hisses', 'farts'. + var/signlang_verb = list() // list of emotes that might be displayed if this language has NONVERBAL or SIGNLANG flags var/colour = "body" // CSS style to use for strings in this language. var/key = "x" // Character used to speak in language eg. :o for Unathi. var/flags = 0 // Various language flags. var/native // If set, non-native speakers will have trouble speaking. - var/vocal = 1 // Is a spoken language /datum/language/unathi name = "Sinta'unathi" @@ -82,13 +82,13 @@ key = "z" flags = WHITELISTED -/datum/language/sign +/*/datum/language/sign name = "Sign language" desc = "A mixture of manual communication and body-languaged, used to communication with those who have impaired hearing." speech_verb = "signs" colour = "say_quote" key = "4" - vocal = 0 + vocal = 0*/ // Language handling. /mob/proc/add_language(var/language) @@ -107,6 +107,11 @@ return 0 +// Can we speak this language, as opposed to just understanding it? +/mob/proc/can_speak(datum/language/speaking) + + return (universal_speak || speaking in src.languages) + //TBD /mob/verb/check_languages() set name = "Check Known Languages" @@ -119,4 +124,4 @@ dat += "[L.name] (:[L.key])
    [L.desc]

    " src << browse(dat, "window=checklanguage") - return + return \ No newline at end of file diff --git a/code/modules/mob/living/carbon/alien/alien.dm b/code/modules/mob/living/carbon/alien/alien.dm index 5602158a..ab725929 100644 --- a/code/modules/mob/living/carbon/alien/alien.dm +++ b/code/modules/mob/living/carbon/alien/alien.dm @@ -86,15 +86,14 @@ // Aliens are now weak to fire. //After then, it reacts to the surrounding atmosphere based on your thermal protection - if(!on_fire) // If you're on fire, ignore local air temperature - if(loc_temp > bodytemperature) - //Place is hotter than we are - var/thermal_protection = heat_protection //This returns a 0 - 1 value, which corresponds to the percentage of protection based on what you're wearing and what you're exposed to. - if(thermal_protection < 1) - bodytemperature += (1-thermal_protection) * ((loc_temp - bodytemperature) / BODYTEMP_HEAT_DIVISOR) - else - bodytemperature += 1 * ((loc_temp - bodytemperature) / BODYTEMP_HEAT_DIVISOR) - // bodytemperature -= max((loc_temp - bodytemperature / BODYTEMP_AUTORECOVERY_DIVISOR), BODYTEMP_AUTORECOVERY_MINIMUM) + if(loc_temp > bodytemperature) + //Place is hotter than we are + var/thermal_protection = heat_protection //This returns a 0 - 1 value, which corresponds to the percentage of protection based on what you're wearing and what you're exposed to. + if(thermal_protection < 1) + bodytemperature += (1-thermal_protection) * ((loc_temp - bodytemperature) / BODYTEMP_HEAT_DIVISOR) + else + bodytemperature += 1 * ((loc_temp - bodytemperature) / BODYTEMP_HEAT_DIVISOR) + // bodytemperature -= max((loc_temp - bodytemperature / BODYTEMP_AUTORECOVERY_DIVISOR), BODYTEMP_AUTORECOVERY_MINIMUM) // +/- 50 degrees from 310.15K is the 'safe' zone, where no damage is dealt. if(bodytemperature > 360.15) @@ -166,10 +165,9 @@ stat(null, "Plasma Stored: [getPlasma()]/[max_plasma]") if(emergency_shuttle) - if(emergency_shuttle.online && emergency_shuttle.location < 2) - var/timeleft = emergency_shuttle.timeleft() - if (timeleft) - stat(null, "ETA-[(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]") + var/eta_status = emergency_shuttle.get_status_panel_eta() + if(eta_status) + stat(null, eta_status) /mob/living/carbon/alien/Stun(amount) if(status_flags & CANSTUN) diff --git a/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm b/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm index fb78b696..fe202c86 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 33d73f08..a009edc2 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/life.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/life.dm @@ -125,7 +125,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) @@ -174,24 +174,24 @@ return 0 var/toxins_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 toxins in our breath - var/Toxins_pp = (breath.toxins/breath.total_moles())*breath_pressure + var/Toxins_pp = (breath.gas["toxins"] / breath.total_moles) * breath_pressure if(Toxins_pp) // Detect toxins in air - adjustToxLoss(breath.toxins*250) + adjustToxLoss(breath.gas["toxins"] *250) toxins_alert = max(toxins_alert, 1) - toxins_used = breath.toxins + toxins_used = breath.gas["toxins"] else toxins_alert = 0 //Breathe in toxins and out oxygen - breath.toxins -= toxins_used - breath.oxygen += toxins_used + breath.adjust_gas("toxins", -toxins_used) + breath.adjust_gas("oxygen", toxins_used) if(breath.temperature > (T0C+66) && !(COLD_RESISTANCE in mutations)) // Hot air hurts :( if(prob(20)) @@ -359,22 +359,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 @@ -463,3 +448,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 d91e895e..79abe621 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 a9cc82d5..6d49aa9e 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/toxins_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 toxins in our breath - var/Toxins_pp = (breath.toxins/breath.total_moles())*breath_pressure + //Partial pressure of the phoron in our breath + var/Toxins_pp = (breath.gas["toxins"] / breath.total_moles) * breath_pressure if(Toxins_pp) // Detect toxins in air - adjustToxLoss(breath.toxins*250) + adjustToxLoss(breath.gas["toxins"] * 250) toxins_alert = max(toxins_alert, 1) - toxins_used = breath.toxins + toxins_used = breath.gas["toxins"] else toxins_alert = 0 //Breathe in toxins and out oxygen - breath.toxins -= toxins_used - breath.oxygen += toxins_used + breath.adjust_gas("toxins", -toxins_used) + breath.adjust_gas("oxygen", toxins_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 @@ -379,4 +366,4 @@ FUCK YOU MORE FAT CODE -Hawk*/ if(air_master.current_cycle%3==1) if(!(M.status_flags & GODMODE)) M.adjustBruteLoss(5) - nutrition += 10 + nutrition += 10 \ No newline at end of file diff --git a/code/modules/mob/living/carbon/alien/special/facehugger.dm b/code/modules/mob/living/carbon/alien/special/facehugger.dm index f01d98ca..8192c008 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 @@ -107,6 +108,7 @@ var/const/MAX_ACTIVE_TIME = 400 if(stat == CONSCIOUS) icon_state = "[initial(icon_state)]" Attach(hit_atom) + throwing = 0 /obj/item/clothing/mask/facehugger/proc/Attach(M as mob) if( (!iscorgi(M) && !iscarbon(M)) || isalien(M)) diff --git a/code/modules/mob/living/carbon/brain/brain_item.dm b/code/modules/mob/living/carbon/brain/brain_item.dm index 2b903433..24252f4d 100644 --- a/code/modules/mob/living/carbon/brain/brain_item.dm +++ b/code/modules/mob/living/carbon/brain/brain_item.dm @@ -32,8 +32,9 @@ brainmob.timeofhostdeath = H.timeofdeath if(H.mind) H.mind.transfer_to(brainmob) + brainmob << "\blue You feel slightly disoriented. That's normal when you're just a brain." - return + callHook("debrain", list(brainmob)) /obj/item/brain/examine() // -- TLE set src in oview(12) @@ -95,4 +96,4 @@ del(src) else ..() - return \ No newline at end of file + return diff --git a/code/modules/mob/living/carbon/brain/life.dm b/code/modules/mob/living/carbon/brain/life.dm index becedb1e..c3b1f746 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/login.dm b/code/modules/mob/living/carbon/brain/login.dm index d04d2758..57c41cc6 100644 --- a/code/modules/mob/living/carbon/brain/login.dm +++ b/code/modules/mob/living/carbon/brain/login.dm @@ -1,2 +1,3 @@ /mob/living/carbon/brain/Login() - return ..() \ No newline at end of file + ..() + sleeping = 0 \ No newline at end of file diff --git a/code/modules/mob/living/carbon/brain/posibrain.dm b/code/modules/mob/living/carbon/brain/posibrain.dm index d2e90b55..831e5662 100644 --- a/code/modules/mob/living/carbon/brain/posibrain.dm +++ b/code/modules/mob/living/carbon/brain/posibrain.dm @@ -16,80 +16,82 @@ 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) + /* + Positronic brains should have posibrain-like name, instead of human-MMIlike names. -- ATL - proc/transfer_personality(var/mob/candidate) + 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 - 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])" +/obj/item/device/mmi/posibrain/proc/transfer_personality(var/mob/candidate) - 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.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" - 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" - proc/reset_search() //We give the players sixty seconds to decide, then reset the timer. +/obj/item/device/mmi/posibrain/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 1df0e24a..c1488ef3 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -1,7 +1,10 @@ -/mob/living/carbon/Login() +/mob/living/carbon/Life() ..() - update_hud() - return + + // Increase germ_level regularly + if(germ_level < GERM_LEVEL_AMBIENT && prob(30)) //if you're just standing there, you shouldn't get more germs beyond an ambient level + germ_level++ + /mob/living/carbon/Move(NewLoc, direct) . = ..() if(.) @@ -12,6 +15,10 @@ if((FAT in src.mutations) && src.m_intent == "run" && src.bodytemperature <= 360) src.bodytemperature += 2 + // Moving around increases germ_level faster + if(germ_level < GERM_LEVEL_MOVE_CAP && prob(8)) + germ_level++ + /mob/living/carbon/relaymove(var/mob/user, direction) if(user in src.stomach_contents) if(prob(40)) @@ -92,24 +99,34 @@ return -/mob/living/carbon/electrocute_act(var/shock_damage, var/obj/source, var/siemens_coeff = 1.0) +/mob/living/carbon/electrocute_act(var/shock_damage, var/obj/source, var/siemens_coeff = 1.0, var/def_zone = null) if(status_flags & GODMODE) return 0 //godmode shock_damage *= siemens_coeff if (shock_damage<1) return 0 - src.take_overall_damage(0,shock_damage,used_weapon="Electrocution") - //src.burn_skin(shock_damage) - //src.adjustFireLoss(shock_damage) //burn_skin will do this for us - //src.updatehealth() - src.visible_message( - "\red [src] was shocked by the [source]!", \ - "\red You feel a powerful shock course through your body!", \ - "\red You hear a heavy electrical crack." \ - ) -// if(src.stunned < shock_damage) src.stunned = shock_damage - Stun(10)//This should work for now, more is really silly and makes you lay there forever -// if(src.weakened < 20*siemens_coeff) src.weakened = 20*siemens_coeff - Weaken(10) + + src.apply_damage(shock_damage, BURN, def_zone, used_weapon="Electrocution") + + playsound(loc, "sparks", 50, 1, -1) + if (shock_damage > 10) + src.visible_message( + "\red [src] was shocked by the [source]!", \ + "\red You feel a powerful shock course through your body!", \ + "\red You hear a heavy electrical crack." \ + ) + Stun(10)//This should work for now, more is really silly and makes you lay there forever + Weaken(10) + else + src.visible_message( + "\red [src] was mildly shocked by the [source].", \ + "\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 @@ -200,12 +217,12 @@ var/mob/living/carbon/human/H = src H.w_uniform.add_fingerprint(M) - if(lying) + if(lying || src.sleeping) src.sleeping = max(0,src.sleeping-5) if(src.sleeping == 0) src.resting = 0 - M.visible_message("[M] shakes [src] trying to wake [t_him] up!", \ - "You shake [src] trying to wake [t_him] up!") + M.visible_message("[M] shakes [src] trying to wake [t_him] up!", \ + "You shake [src] trying to wake [t_him] up!") else M.visible_message("[M] hugs [src] to make [t_him] feel better!", \ "You hug [src] to make [t_him] feel better!") @@ -323,7 +340,7 @@ */ - item.throw_at(target, item.throw_range, item.throw_speed) + item.throw_at(target, item.throw_range, item.throw_speed, src) /mob/living/carbon/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) ..() @@ -416,23 +433,17 @@ var/mob/living/simple_animal/borer/B = has_brain_worms() - if(!B) - return - - if(B.controlling) + if(B && B.host_brain) src << "\red You withdraw your probosci, releasing control of [B.host_brain]" - B.host_brain << "\red Your vision swims as the alien parasite releases control of your body." - B.ckey = ckey - B.controlling = 0 - if(B.host_brain.ckey) - ckey = B.host_brain.ckey - B.host_brain.ckey = null - B.host_brain.name = "host brain" - B.host_brain.real_name = "host brain" - verbs -= /mob/living/carbon/proc/release_control - verbs -= /mob/living/carbon/proc/punish_host - verbs -= /mob/living/carbon/proc/spawn_larvae + B.detatch() + + verbs -= /mob/living/carbon/proc/release_control + verbs -= /mob/living/carbon/proc/punish_host + verbs -= /mob/living/carbon/proc/spawn_larvae + + else + src << "\red ERROR NO BORER OR BRAINMOB DETECTED IN THIS MOB, THIS IS A BUG !" //Brain slug proc for tormenting the host. /mob/living/carbon/proc/punish_host() @@ -469,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) @@ -479,4 +491,4 @@ else src << "You do not have enough chemicals stored to reproduce." - return \ No newline at end of file + return diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm index 08ae845b..2b6dbc54 100644 --- a/code/modules/mob/living/carbon/human/death.dm +++ b/code/modules/mob/living/carbon/human/death.dm @@ -58,7 +58,6 @@ dizziness = 0 jitteriness = 0 - hud_updateflag |= 1 << HEALTH_HUD hud_updateflag |= 1 << STATUS_HUD @@ -86,6 +85,8 @@ verbs -= /mob/living/carbon/proc/release_control + callHook("death", list(src, gibbed)) + //Check for heist mode kill count. if(ticker.mode && ( istype( ticker.mode,/datum/game_mode/heist) ) ) //Check for last assailant's mutantrace. @@ -106,7 +107,7 @@ update_canmove() if(client) blind.layer = 0 - worldtod = world.time + tod = worldtime2text() //weasellos time of death patch if(mind) mind.store_memory("Time of death: [tod]", 0) if(ticker && ticker.mode) @@ -115,8 +116,6 @@ ticker.mode.check_win() //Calls the rounds wincheck, mainly for wizard, malf, and changeling now return ..(gibbed) - - /mob/living/carbon/human/proc/makeSkeleton() if(SKELETON in src.mutations) return diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm index 41dd2846..12b84b06 100644 --- a/code/modules/mob/living/carbon/human/emote.dm +++ b/code/modules/mob/living/carbon/human/emote.dm @@ -66,8 +66,10 @@ return custom_emote(m_type, message) if ("me") - if(silent) - return + + //if(silent && silent > 0 && findtext(message,"\"",1, null) > 0) + // return //This check does not work and I have no idea why, I'm leaving it in for reference. + if (src.client) if (client.prefs.muted & MUTE_IC) src << "\red You cannot send IC messages (muted)." @@ -582,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 d043296d..776d5f25 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -1,4 +1,3 @@ - /mob/living/carbon/human name = "unknown" real_name = "unknown" @@ -52,7 +51,7 @@ if(!species) if(new_species) - set_species(new_species) + set_species(new_species,null,1) else set_species() @@ -121,7 +120,7 @@ return //BubbleWrap: people in handcuffs are always switched around as if they were on 'help' intent to prevent a person being pulled from being seperated from their puller - if((tmob.a_intent == "help" || tmob.restrained()) && (a_intent == "help" || src.restrained()) && tmob.canmove && canmove) // mutual brohugs all around! + if((tmob.a_intent == "help" || tmob.restrained()) && (a_intent == "help" || src.restrained()) && tmob.canmove && !tmob.buckled && canmove) // mutual brohugs all around! var/turf/oldloc = loc loc = tmob.loc tmob.loc = oldloc @@ -180,10 +179,9 @@ if(ticker.mode:malf_mode_declared) stat(null, "Time left: [max(ticker.mode:AI_win_timeleft/(ticker.mode:apcs/3), 0)]") if(emergency_shuttle) - if(emergency_shuttle.online && emergency_shuttle.location < 2) - var/timeleft = emergency_shuttle.timeleft() - if (timeleft) - stat(null, "ETA-[(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]") + var/eta_status = emergency_shuttle.get_status_panel_eta() + if(eta_status) + stat(null, eta_status) if (client.statpanel == "Status") if (internal) @@ -326,7 +324,6 @@ apply_damage(damage, BRUTE, affecting, armor) if(armor >= 2) return - /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)) @@ -346,7 +343,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) @@ -449,12 +446,17 @@ return // called when something steps onto a human -// this could be made more general, but for now just handle mulebot -/mob/living/carbon/human/HasEntered(var/atom/movable/AM) - var/obj/machinery/bot/mulebot/MB = AM - if(istype(MB)) +// this handles mulebots and vehicles +/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) +/* if(istype(AM, /obj/vehicle)) + var/obj/vehicle/V = AM + V.RunOver(src) +*/ + //gets assignment from ID or ID inside PDA or PDA itself //Useful when player do something with computers /mob/living/carbon/human/proc/get_assignment(var/if_no_id = "No id", var/if_no_job = "No job") @@ -526,13 +528,18 @@ if(wear_id) return wear_id.GetID() -//Added a safety check in case you want to shock a human mob directly through electrocute_act. -/mob/living/carbon/human/electrocute_act(var/shock_damage, var/obj/source, var/siemens_coeff = 1.0, var/safety = 0) - if(!safety) - if(gloves) - var/obj/item/clothing/gloves/G = gloves - siemens_coeff = G.siemens_coefficient - return ..(shock_damage,source,siemens_coeff) +//Removed the horrible safety parameter. It was only being used by ninja code anyways. +//Now checks siemens_coefficient of the affected area by default +/mob/living/carbon/human/electrocute_act(var/shock_damage, var/obj/source, var/base_siemens_coeff = 1.0, var/def_zone = null) + if(status_flags & GODMODE) return 0 //godmode + + if (!def_zone) + def_zone = pick("l_hand", "r_hand") + + var/datum/organ/external/affected_organ = get_organ(check_zone(def_zone)) + var/siemens_coeff = base_siemens_coeff * get_siemens_coefficient_organ(affected_organ) + + return ..(shock_damage, source, siemens_coeff, def_zone) /mob/living/carbon/human/Topic(href, href_list) @@ -822,6 +829,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 @@ -1091,8 +1119,7 @@ H.brainmob.mind.transfer_to(src) del(H) - for(var/E in internal_organs) - var/datum/organ/internal/I = internal_organs[E] + for(var/datum/organ/internal/I in internal_organs) I.damage = 0 for (var/datum/disease/virus in viruses) @@ -1104,11 +1131,11 @@ ..() /mob/living/carbon/human/proc/is_lung_ruptured() - var/datum/organ/internal/lungs/L = internal_organs["lungs"] + var/datum/organ/internal/lungs/L = internal_organs_by_name["lungs"] return L.is_bruised() /mob/living/carbon/human/proc/rupture_lung() - var/datum/organ/internal/lungs/L = internal_organs["lungs"] + var/datum/organ/internal/lungs/L = internal_organs_by_name["lungs"] if(!L.is_bruised()) src.custom_pain("You feel a stabbing pain in your chest!", 1) @@ -1171,16 +1198,11 @@ var/list/visible_implants = list() for(var/datum/organ/external/organ in src.organs) for(var/obj/item/weapon/O in organ.implants) - if(!istype(O,/obj/item/weapon/implant) && O.w_class > class) + if(!istype(O,/obj/item/weapon/implant) && (O.w_class > class) && !istype(O,/obj/item/weapon/shard/shrapnel)) visible_implants += O return(visible_implants) -/* -/mob/living/carbon/human/generate_name() - name = species.makeName(gender,src) - real_name = name - return name -*/ + /mob/living/carbon/human/proc/handle_embedded_objects() for(var/datum/organ/external/organ in src.organs) @@ -1229,14 +1251,14 @@ return usr << "Don't move until counting is finished." - var/time = world.timeofday + var/time = world.time sleep(60) if(usr.l_move_time >= time) //checks if our mob has moved during the sleep() usr << "You moved while counting. Try again." else usr << "\blue [self ? "Your" : "[src]'s"] pulse is [src.get_pulse(GETPULSE_HAND)]." -/mob/living/carbon/human/proc/set_species(var/new_species, var/force_organs) +/mob/living/carbon/human/proc/set_species(var/new_species, var/force_organs, var/default_colour) if(!dna) if(!new_species) @@ -1261,11 +1283,22 @@ if(species.language) add_language(species.language) + if(species.base_color && default_colour) + //Apply colour. + r_skin = hex2num(copytext(species.base_color,2,4)) + g_skin = hex2num(copytext(species.base_color,4,6)) + b_skin = hex2num(copytext(species.base_color,6,8)) + else + r_skin = 0 + g_skin = 0 + b_skin = 0 + + species.handle_post_spawn(src) + spawn(0) update_icons() if(species) - species.handle_post_spawn(src) return 1 else return 0 @@ -1356,7 +1389,7 @@ if(last_special > world.time) return - if(stat || paralysis || stunned || weakened || lying) + if(stat || paralysis || stunned || weakened || lying || restrained() || buckled) src << "You cannot leap in your current state." return @@ -1366,17 +1399,24 @@ choices += M choices -= src - var/mob/living/T = input(src,"Who do you wish to leap at?") in null|choices + var/mob/living/T = input(src,"Who do you wish to leap at?") as null|anything in choices if(!T || !src || src.stat) return if(get_dist(get_turf(T), get_turf(src)) > 6) return - last_special = world.time + 100 + if(last_special > world.time) + return + + if(stat || paralysis || stunned || weakened || lying || restrained() || buckled) + src << "You cannot leap in your current state." + return + + last_special = world.time + 75 status_flags |= LEAPING src.visible_message("\The [src] leaps at [T]!") - src.throw_at(get_step(get_turf(T),get_turf(src)), 5, 1) + src.throw_at(get_step(get_turf(T),get_turf(src)), 5, 1, src) playsound(src.loc, 'sound/voice/shriek1.ogg', 50, 1) sleep(5) @@ -1389,6 +1429,11 @@ T.Weaken(5) + //Only official raider vox get the grab and no self-prone." + if(src.mind && src.mind.special_role != "Vox Raider") + src.Weaken(5) + return + var/use_hand = "left" if(l_hand) if(r_hand) @@ -1445,3 +1490,77 @@ M.apply_damage(50,BRUTE) if(M.stat == 2) M.gib() + +/mob/living/carbon/human/proc/commune() + set category = "IC" + set name = "Commune with creature" + set desc = "Send a telepathic message to an unlucky recipient." + + var/list/targets = list() + var/target = null + var/text = null + + targets += getmobs() //Fill list, prompt user with list + target = input("Select a creature!", "Speak to creature", null, null) as null|anything in targets + + if(!target) return + + text = input("What would you like to say?", "Speak to creature", null, null) + + text = trim(copytext(sanitize(text), 1, MAX_MESSAGE_LEN)) + + if(!text) return + + var/mob/M = targets[target] + + if(istype(M, /mob/dead/observer) || M.stat == DEAD) + src << "Not even a [src.species.name] can speak to the dead." + return + + log_say("[key_name(src)] communed to [key_name(M)]: [text]") + + M << "\blue Like lead slabs crashing into the ocean, alien thoughts drop into your mind: [text]" + if(istype(M,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + if(H.species.name == src.species.name) + 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] && 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_attackhand.dm b/code/modules/mob/living/carbon/human/human_attackhand.dm index 844d239e..3f127e0c 100644 --- a/code/modules/mob/living/carbon/human/human_attackhand.dm +++ b/code/modules/mob/living/carbon/human/human_attackhand.dm @@ -160,7 +160,7 @@ //Rearranged, so claws don't increase weaken chance. if(damage >= 5 && prob(50)) visible_message("\red [M] has weakened [src]!") - apply_effect(2, WEAKEN, armor_block) + apply_effect(3, WEAKEN, armor_block) damage += attack.damage apply_damage(damage, BRUTE, affecting, armor_block, sharp=attack.sharp, edge=attack.edge) @@ -198,7 +198,7 @@ var/randn = rand(1, 100) if (randn <= 25) - apply_effect(4, WEAKEN, run_armor_check(affecting, "melee")) + apply_effect(3, WEAKEN, run_armor_check(affecting, "melee")) playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) visible_message("\red [M] has pushed [src]!") return diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human_damage.dm index 28b42db8..c62da9af 100644 --- a/code/modules/mob/living/carbon/human/human_damage.dm +++ b/code/modules/mob/living/carbon/human/human_damage.dm @@ -17,7 +17,7 @@ /mob/living/carbon/human/getBrainLoss() var/res = brainloss - var/datum/organ/internal/brain/sponge = internal_organs["brain"] + var/datum/organ/internal/brain/sponge = internal_organs_by_name["brain"] if (sponge.is_bruised()) res += 20 if (sponge.is_broken()) @@ -308,20 +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 projectile code. - if(!organ) return - if(istype(used_weapon,/obj/item/weapon)) - var/obj/item/weapon/W = used_weapon //Sharp objects will always embed if they do enough damage. - if( (damage > (10*W.w_class)) && ( (sharp && !ismob(W.loc)) || prob(damage/W.w_class) ) ) - organ.implants += W - visible_message("\The [W] sticks in the wound!") - embedded_flag = 1 - src.verbs += /mob/proc/yank_out_object - W.add_blood(src) - if(ismob(W.loc)) - var/mob/living/H = W.loc - H.drop_item() - W.loc = src - 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 7e816ac1..f86056a5 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -10,22 +10,12 @@ 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)) @@ -52,87 +42,88 @@ emp_act return -1 // complete projectile permutation - if(check_shields(P.damage, "the [P.name]")) - P.on_hit(src, 2, def_zone) - return 2 - -//BEGIN BOOK'S TASER NERF. - if(istype(P, /obj/item/projectile/energy/electrode)) - var/datum/organ/external/select_area = get_organ(def_zone) // We're checking the outside, buddy! - var/list/body_parts = list(head, wear_mask, wear_suit, w_uniform, gloves, shoes) // What all are we checking? - // var/deflectchance=90 //Is it a CRITICAL HIT with that taser? - for(var/bp in body_parts) //Make an unregulated var to pass around. - if(!bp) - continue //Does this thing we're shooting even exist? - if(bp && istype(bp ,/obj/item/clothing)) // If it exists, and it's clothed - var/obj/item/clothing/C = bp // Then call an argument C to be that clothing! - if(C.body_parts_covered & select_area.body_part) // Is that body part being targeted covered? - P.agony=P.agony*C.siemens_coefficient - 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 - - var/datum/organ/external/organ = get_organ(check_zone(def_zone)) - - var/armor = checkarmor(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.implants += SP - visible_message("The projectile sticks in the wound!") - embedded_flag = 1 - src.verbs += /mob/proc/yank_out_object - SP.add_blood(src) + //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)) - return checkarmor(def_zone, type) - var/datum/organ/external/affecting = get_organ(ran_zone(def_zone)) - return checkarmor(affecting, type) + return getarmor_organ(def_zone, type) + var/datum/organ/external/affecting = get_organ(def_zone) + return getarmor_organ(affecting, type) //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 += checkarmor(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 -/mob/living/carbon/human/proc/checkarmor(var/datum/organ/external/def_zone, var/type) + 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) && (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 @@ -194,11 +185,12 @@ emp_act /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 = get_zone_with_miss_chance(user.zone_sel.selecting, src) + 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) - visible_message("\red [user] misses [src] with \the [I]!") + visible_message("\red [user] misses [src] with \the [I]!") return 0 var/datum/organ/external/affecting = get_organ(target_zone) @@ -231,10 +223,16 @@ emp_act visible_message("\red [src] has been attacked in the [hit_area] with [I.name] by [user]!") var/armor = run_armor_check(affecting, "melee", "Your armor has protected your [hit_area].", "Your armor has softened hit to your [hit_area].") + var/weapon_sharp = is_sharp(I) + var/weapon_edge = has_edge(I) + if ((weapon_sharp || weapon_edge) && prob(getarmor(target_zone, "melee"))) + weapon_sharp = 0 + weapon_edge = 0 + if(armor >= 2) return 0 if(!I.force) return 0 - apply_damage(I.force, I.damtype, affecting, armor , is_sharp(I), has_edge(I), I) + apply_damage(I.force, I.damtype, affecting, armor, sharp=weapon_sharp, edge=weapon_edge, used_weapon=I) var/bloody = 0 if(((I.damtype == BRUTE) || (I.damtype == HALLOSS)) && prob(25 + (I.force * 2))) @@ -273,13 +271,117 @@ emp_act if("chest")//Easier to score a stun but lasts less time if(prob((I.force + 10))) - apply_effect(5, WEAKEN, armor) + apply_effect(6, WEAKEN, armor) visible_message("\red [src] has been knocked down!") 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 +/mob/living/carbon/human/hitby(atom/movable/AM as mob|obj,var/speed = 5) + if(istype(AM,/obj/)) + var/obj/O = AM + var/dtype = BRUTE + if(istype(O,/obj/item/weapon)) + 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) + zone = get_zone_with_miss_chance(zone, src, min(15*(distance-2), 0)) + else + zone = get_zone_with_miss_chance(zone, src, 15) + + 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 + + if(armor < 2) + apply_damage(throw_damage, dtype, zone, armor, is_sharp(O), has_edge(O), O) + + if(ismob(O.thrower)) + var/mob/M = O.thrower + var/client/assailant = M.client + if(assailant) + src.attack_log += text("\[[time_stamp()]\] Has been hit with a [O], thrown by [M.name] ([assailant.ckey])") + M.attack_log += text("\[[time_stamp()]\] Hit [src.name] ([src.ckey]) with a thrown [O]") + 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 + var/momentum = speed/2 + var/dir = get_dir(O.throw_source, src) + + visible_message("\red [src] staggers under the impact!","\red You stagger under the impact!") + src.throw_at(get_edge_target_turf(src,dir),1,momentum) + + if(!W || !src) return + + if(W.loc == src && W.sharp) //Projectile is embedded and suitable for pinning. + var/turf/T = near_wall(dir,2) + + if(T) + src.loc = T + visible_message("[src] is pinned to the wall by [O]!","You are pinned to the wall by [O]!") + 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 82374655..e8c7628f 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/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm index 5bf36827..89e811a0 100644 --- a/code/modules/mob/living/carbon/human/human_movement.dm +++ b/code/modules/mob/living/carbon/human/human_movement.dm @@ -13,16 +13,17 @@ if(reagents.has_reagent("nuka_cola")) return -1 - var/health_deficiency = (100 - health + halloss) + var/health_deficiency = (100 - health) if(health_deficiency >= 40) tally += (health_deficiency / 25) + if(halloss >= 10) tally += (halloss / 10) + var/hungry = (500 - nutrition)/5 // So overeat would be 100 and default level would be 80 if (hungry >= 70) tally += hungry/50 if(wear_suit) tally += wear_suit.slowdown - if(!buckled || (buckled && !istype(buckled, /obj/structure/stool/bed/chair/wheelchair))) if(shoes) tally += shoes.slowdown diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm index ce28bd6a..95079d95 100644 --- a/code/modules/mob/living/carbon/human/inventory.dm +++ b/code/modules/mob/living/carbon/human/inventory.dm @@ -110,6 +110,7 @@ head = null if((W.flags & BLOCKHAIR) || (W.flags & BLOCKHEADHAIR)) update_hair(0) //rebuild hair + update_inv_ears(0) success = 1 update_inv_head() else if (W == l_ear) @@ -133,6 +134,7 @@ success = 1 if((W.flags & BLOCKHAIR) || (W.flags & BLOCKHEADHAIR)) update_hair(0) //rebuild hair + update_inv_ears(0) if(internal) if(internals) internals.icon_state = "internal0" @@ -214,6 +216,7 @@ src.wear_mask = W if((wear_mask.flags & BLOCKHAIR) || (wear_mask.flags & BLOCKHEADHAIR)) update_hair(redraw_mob) //rebuild hair + update_inv_ears(0) W.equipped(src, slot) update_inv_wear_mask(redraw_mob) if(slot_handcuffed) @@ -269,6 +272,7 @@ src.head = W if((head.flags & BLOCKHAIR) || (head.flags & BLOCKHEADHAIR)) update_hair(redraw_mob) //rebuild hair + update_inv_ears(0) if(istype(W,/obj/item/clothing/head/kitty)) W.update_icon(src) W.equipped(src, slot) @@ -671,20 +675,38 @@ It can still be worn/put on as normal. 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) strip_item = target.legcuffed if("splints") - for(var/organ in list("l_leg","r_leg","l_arm","r_arm")) - var/datum/organ/external/o = target.get_organ(organ) - if (o && o.status & ORGAN_SPLINTED) - var/obj/item/W = new /obj/item/stack/medical/splint(amount=1) - o.status &= ~ORGAN_SPLINTED - if (W) - W.loc = target.loc - W.layer = initial(W.layer) - W.add_fingerprint(source) + var/can_reach_splints = 1 + if(target.wear_suit && istype(target.wear_suit,/obj/item/clothing/suit/space)) + var/obj/item/clothing/suit/space/suit = target.wear_suit + if(suit.supporting_limbs && suit.supporting_limbs.len) + source << "You cannot remove the splints - [target]'s [suit] is supporting some of the breaks." + can_reach_splints = 0 + + if(can_reach_splints) + for(var/organ in list("l_leg","r_leg","l_arm","r_arm")) + var/datum/organ/external/o = target.get_organ(organ) + if (o && o.status & ORGAN_SPLINTED) + var/obj/item/W = new /obj/item/stack/medical/splint(amount=1) + o.status &= ~ORGAN_SPLINTED + if (W) + W.loc = target.loc + W.layer = initial(W.layer) + W.add_fingerprint(source) if("CPR") if ((target.health > config.health_threshold_dead && target.health < config.health_threshold_crit)) var/suff = min(target.getOxyLoss(), 5) //Pre-merge level, less healing, more prevention of dieing. diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index f547a875..80eb94c9 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -2,7 +2,7 @@ //NOTE: Breathing happens once per FOUR TICKS, unless the last breath fails. In which case it happens once per ONE TICK! So oxyloss healing is done once per 4 ticks while oxyloss damage is applied once per tick! #define HUMAN_MAX_OXYLOSS 1 //Defines how much oxyloss humans can get per tick. A tile with no air at all (such as space) applies this value, otherwise it's a percentage of it. -#define HUMAN_CRIT_MAX_OXYLOSS ( (last_tick_duration) /5) //The amount of damage you'll get when in critical condition. We want this to be a 5 minute deal = 300s. There are 100HP to get through, so (1/3)*last_tick_duration per second. Breaths however only happen every 4 ticks. +#define HUMAN_CRIT_MAX_OXYLOSS ( (last_tick_duration) /6) //The amount of damage you'll get when in critical condition. We want this to be a 5 minute deal = 300s. There are 50HP to get through, so (1/6)*last_tick_duration per second. Breaths however only happen every 4 ticks. #define HEAT_DAMAGE_LEVEL_1 2 //Amount of damage applied when your body temperature just passes the 360.15k safety point #define HEAT_DAMAGE_LEVEL_2 4 //Amount of damage applied when your body temperature passes the 400K point @@ -21,9 +21,12 @@ #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/toxins_alert = 0 + var/co2_alert = 0 var/fire_alert = 0 var/pressure_alert = 0 var/prev_gender = null // Debug for plural genders @@ -32,6 +35,8 @@ /mob/living/carbon/human/Life() + + set invisibility = 0 set background = 1 @@ -70,7 +75,7 @@ //No need to update all of these procs if the guy is dead. if(stat != DEAD && !in_stasis) - if(air_master.current_cycle%4==2 || failed_last_breath) //First, resolve location and get a breath + if(air_master.current_cycle%4==2 || failed_last_breath || (health < config.health_threshold_crit)) //First, resolve location and get a breath breathe() //Only try to take a breath every 4 ticks, unless suffocating else //Still give containing object the chance to interact @@ -156,8 +161,6 @@ return pressure_adjustment_coefficient // Calculate how much of the enviroment pressure-difference affects the human. - - /mob/living/carbon/human/calculate_affecting_pressure(var/pressure) var/pressure_difference @@ -281,60 +284,54 @@ speech_problem_flag = 1 gene.OnMobLife(src) + radiation = Clamp(radiation,0,100) + if (radiation) - if (radiation > 100) - radiation = 100 - Weaken(10) - src << "\red You feel weak." - emote("collapse") + if(species.flags & RAD_ABSORB) + var/rads = radiation/25 + radiation -= rads + nutrition += rads + adjustBruteLoss(-(rads)) + adjustOxyLoss(-(rads)) + adjustToxLoss(-(rads)) + updatehealth() + return - if (radiation < 0) - radiation = 0 + var/damage = 0 + radiation -= 1 * RADIATION_SPEED_COEFFICIENT + if(prob(25)) + damage = 1 - else + 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) + 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(species.flags & RAD_ABSORB) - var/rads = radiation/25 - radiation -= rads - nutrition += rads - adjustBruteLoss(-(rads)) - adjustOxyLoss(-(rads)) - adjustToxLoss(-(rads)) - updatehealth() - return + 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") - 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) - 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) @@ -343,9 +340,6 @@ if(istype(loc, /obj/machinery/atmospherics/unary/cryo_cell)) return if(species && (species.flags & NO_BREATHE || species.flags & IS_SYNTHETIC)) return - var/datum/organ/internal/lungs/L = internal_organs["lungs"] - L.process() - var/datum/gas_mixture/environment = loc.return_air() var/datum/gas_mixture/breath @@ -377,26 +371,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.toxins *= 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.toxins *= 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)) @@ -459,7 +442,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 @@ -478,166 +461,147 @@ 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 - switch(species.breath_type) - if("nitrogen") - inhaling = breath.nitrogen - if("plasma") - inhaling = breath.toxins - if("C02") - inhaling = breath.carbon_dioxide - else - inhaling = breath.oxygen + var/breath_type + var/poison_type + var/exhale_type - switch(species.poison_type) - if("oxygen") - poison = breath.oxygen - if("nitrogen") - poison = breath.nitrogen - if("C02") - poison = breath.carbon_dioxide - else - poison = breath.toxins + var/failed_inhale = 0 + var/failed_exhale = 0 - switch(species.exhale_type) - if("C02") - exhaling = breath.carbon_dioxide - if("oxygen") - exhaling = breath.oxygen - if("nitrogen") - exhaling = breath.nitrogen - if("plasma") - exhaling = breath.toxins - else - no_exhale = 1 + if(species.breath_type) + breath_type = species.breath_type + else + breath_type = "oxygen" + inhaling = breath.gas[breath_type] - 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 + if(species.poison_type) + poison_type = species.poison_type + else + poison_type = "toxins" + poison = breath.gas[poison_type] + 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 + + // 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!) - adjustOxyLoss(min(5*(1 - ratio), HUMAN_MAX_OXYLOSS)) - failed_last_breath = 1 - inhaled_gas_used = inhaling*ratio/6 - - else - - adjustOxyLoss(HUMAN_MAX_OXYLOSS) - failed_last_breath = 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 - failed_last_breath = 0 - adjustOxyLoss(-5) - inhaled_gas_used = inhaling/6 oxygen_alert = 0 - switch(species.breath_type) - if("nitrogen") - breath.nitrogen -= inhaled_gas_used + inhaled_gas_used = inhaling/6 + + breath.adjust_gas(breath_type, -inhaled_gas_used, update = 0) //update afterwards + + if(exhale_type) + breath.adjust_gas_temp(exhale_type, inhaled_gas_used, bodytemperature, 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 << "You feel [word]." + + adjustOxyLoss(HUMAN_MAX_OXYLOSS) + co2_alert = 1 + failed_exhale = 1 + + 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 << " 120) - - // Lets hurt em a little, let them know we mean business - Paralyse(3) - adjustOxyLoss(3) - - // They've been in here 30s now, lets start to kill them for their own good! - if(world.time - co2overloadtime > 300) - adjustOxyLoss(8) - - // Lets give them some chance to know somethings not right though I guess. - if(prob(20)) - spawn(0) emote("cough") - - else - co2overloadtime = 0 + co2_alert = 0 // Too much poison in the air. if(toxins_pp > safe_toxins_max) var/ratio = (poison/safe_toxins_max) * 10 if(reagents) - //TODO: Fix Ravensdale's shit, make toxins toxins again instead of phoron. reagents.add_reagent("plasma", Clamp(ratio, MIN_PLASMA_DAMAGE, MAX_PLASMA_DAMAGE)) + breath.adjust_gas(poison_type, -poison/6, update = 0) //update after toxins_alert = max(toxins_alert, 1) else toxins_alert = 0 // If there's some other shit in the air lets deal with it here. - if(breath.trace_gases.len) - for(var/datum/gas/sleeping_agent/SA in breath.trace_gases) - var/SA_pp = (SA.moles/breath.total_moles())*breath_pressure + if(breath.gas["sleeping_agent"]) + var/SA_pp = (breath.gas["sleeping_agent"] / breath.total_moles) * breath_pressure - // Enough to make us paralysed for a bit - if(SA_pp > 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 - if( (abs(310.15 - breath.temperature) > 50) && !(COLD_RESISTANCE in mutations)) // Hot air hurts :( + // Were we able to breathe? + if (failed_inhale || failed_exhale) + failed_last_breath = 1 + else + failed_last_breath = 0 + adjustOxyLoss(-5) - if(status_flags & GODMODE) - return 1 + // Hot air hurts :( + if( (breath.temperature < species.cold_level_1 || breath.temperature > species.heat_level_1) && !(COLD_RESISTANCE in mutations)) if(breath.temperature < species.cold_level_1) if(prob(20)) - src << "\red You feel your face freezing and an icicle 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) @@ -659,8 +623,23 @@ apply_damage(HEAT_GAS_DAMAGE_LEVEL_3, BURN, "head", used_weapon = "Excessive Heat") fire_alert = max(fire_alert, 2) - //Temporary fixes to the alerts. + //breathing in hot/cold air also heats/cools you a bit + var/temp_adj = breath.temperature - bodytemperature + if (temp_adj < 0) + temp_adj /= (BODYTEMP_COLD_DIVISOR * 5) //don't raise temperature as much as if we were directly exposed + 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) + 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) @@ -671,68 +650,75 @@ 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)) var/obj/mecha/M = loc loc_temp = M.return_temperature() - else if(istype(get_turf(src), /turf/space)) else if(istype(loc, /obj/machinery/atmospherics/unary/cryo_cell)) loc_temp = loc:air_contents.temperature else loc_temp = environment.temperature - if(adjusted_pressure < species.warning_low_pressure && adjusted_pressure > species.warning_low_pressure && abs(loc_temp - 293.15) < 20 && abs(bodytemperature - 310.14) < 0.5 && environment.toxins < MOLES_PLASMA_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 - //Body temperature is adjusted in two steps. Firstly your body tries to stabilize itself a bit. - if(stat != 2) - stabilize_temperature_from_calories() + //Body temperature adjusts depending on surrounding atmosphere based on your thermal protection + var/temp_adj = 0 + if(loc_temp < bodytemperature) //Place is colder than we are + var/thermal_protection = get_cold_protection(loc_temp) //This returns a 0 - 1 value, which corresponds to the percentage of protection based on what you're wearing and what you're exposed to. + if(thermal_protection < 1) + temp_adj = (1-thermal_protection) * ((loc_temp - bodytemperature) / BODYTEMP_COLD_DIVISOR) //this will be negative + else if (loc_temp > bodytemperature) //Place is hotter than we are + var/thermal_protection = get_heat_protection(loc_temp) //This returns a 0 - 1 value, which corresponds to the percentage of protection based on what you're wearing and what you're exposed to. + if(thermal_protection < 1) + temp_adj = (1-thermal_protection) * ((loc_temp - bodytemperature) / BODYTEMP_HEAT_DIVISOR) - //After then, it reacts to the surrounding atmosphere based on your thermal protection - if(!on_fire) //If you're on fire, you do not heat up or cool down based on surrounding gases - if(loc_temp < BODYTEMP_COLD_DAMAGE_LIMIT) //Place is colder than we are - var/thermal_protection = get_cold_protection(loc_temp) //This returns a 0 - 1 value, which corresponds to the percentage of protection based on what you're wearing and what you're exposed to. - if(thermal_protection < 1) - var/amt = min((1-thermal_protection) * ((loc_temp - bodytemperature) / BODYTEMP_COLD_DIVISOR), BODYTEMP_COOLING_MAX) - // log_debug("[loc_temp] is Cold. Cooling by [amt]") - bodytemperature += amt - else if (loc_temp > BODYTEMP_HEAT_DAMAGE_LIMIT) //Place is hotter than we are - var/thermal_protection = get_heat_protection(loc_temp) //This returns a 0 - 1 value, which corresponds to the percentage of protection based on what you're wearing and what you're exposed to. - if(thermal_protection < 1) - var/amt = min((1-thermal_protection) * ((loc_temp - bodytemperature) / BODYTEMP_HEAT_DIVISOR), BODYTEMP_HEATING_MAX) - // log_debug("[loc_temp] is Heat. Heating up by [amt]") - bodytemperature += amt + //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 + 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 << "Environment: [loc_temp], [src]: [bodytemperature], Adjusting: [temp_adj]" + bodytemperature += temp_adj // +/- 50 degrees from 310.15K is the 'safe' zone, where no damage is dealt. - if(bodytemperature > BODYTEMP_HEAT_DAMAGE_LIMIT) + if(bodytemperature > species.heat_level_1) //Body temperature is too hot. fire_alert = max(fire_alert, 1) if(status_flags & GODMODE) return 1 //godmode switch(bodytemperature) - if(360 to 400) - apply_damage(HEAT_DAMAGE_LEVEL_1, BURN, used_weapon = "High Body Temperature") + if(species.heat_level_1 to species.heat_level_2) + take_overall_damage(burn=HEAT_DAMAGE_LEVEL_1, used_weapon = "High Body Temperature") fire_alert = max(fire_alert, 2) - if(400 to 1000) - apply_damage(HEAT_DAMAGE_LEVEL_2, BURN, used_weapon = "High Body Temperature") + if(species.heat_level_2 to species.heat_level_3) + take_overall_damage(burn=HEAT_DAMAGE_LEVEL_2, used_weapon = "High Body Temperature") fire_alert = max(fire_alert, 2) - if(1000 to INFINITY) - apply_damage(HEAT_DAMAGE_LEVEL_3, BURN, used_weapon = "High Body Temperature") + if(species.heat_level_3 to INFINITY) + take_overall_damage(burn=HEAT_DAMAGE_LEVEL_3, used_weapon = "High Body Temperature") fire_alert = max(fire_alert, 2) - else if(bodytemperature < BODYTEMP_COLD_DAMAGE_LIMIT) + else if(bodytemperature < species.cold_level_1) fire_alert = max(fire_alert, 1) if(status_flags & GODMODE) return 1 //godmode if(!istype(loc, /obj/machinery/atmospherics/unary/cryo_cell)) switch(bodytemperature) - if(200 to 260) - apply_damage(COLD_DAMAGE_LEVEL_1, BURN, used_weapon = "Low Body Temperature") + if(species.cold_level_2 to species.cold_level_1) + take_overall_damage(burn=COLD_DAMAGE_LEVEL_1, used_weapon = "High Body Temperature") fire_alert = max(fire_alert, 1) - if(120 to 200) - apply_damage(COLD_DAMAGE_LEVEL_2, BURN, used_weapon = "Low Body Temperature") + if(species.cold_level_3 to species.cold_level_2) + take_overall_damage(burn=COLD_DAMAGE_LEVEL_2, used_weapon = "High Body Temperature") fire_alert = max(fire_alert, 1) - if(-INFINITY to 120) - apply_damage(COLD_DAMAGE_LEVEL_3, BURN, used_weapon = "Low Body Temperature") + if(-INFINITY to species.cold_level_3) + take_overall_damage(burn=COLD_DAMAGE_LEVEL_3, used_weapon = "High Body Temperature") fire_alert = max(fire_alert, 1) // Account for massive pressure differences. Done by Polymorph @@ -740,7 +726,8 @@ if(status_flags & GODMODE) return 1 //godmode if(adjusted_pressure >= species.hazard_high_pressure) - adjustBruteLoss( min( ( (adjusted_pressure / species.hazard_high_pressure) -1 )*PRESSURE_DAMAGE_COEFFICIENT , MAX_HIGH_PRESSURE_DAMAGE) ) + var/pressure_damage = min( ( (adjusted_pressure / species.hazard_high_pressure) -1 )*PRESSURE_DAMAGE_COEFFICIENT , MAX_HIGH_PRESSURE_DAMAGE) + take_overall_damage(brute=pressure_damage, used_weapon = "High Pressure") pressure_alert = 2 else if(adjusted_pressure >= species.warning_high_pressure) pressure_alert = 1 @@ -748,24 +735,13 @@ pressure_alert = 0 else if(adjusted_pressure >= species.hazard_low_pressure) pressure_alert = -1 - - if(species && species.flags & IS_SYNTHETIC) - bodytemperature += 0.5 * TEMPERATURE_DAMAGE_COEFFICIENT //Synthetics suffer overheating in a vaccuum. ~Z - else - - if(species && species.flags & IS_SYNTHETIC) - bodytemperature += 1 * TEMPERATURE_DAMAGE_COEFFICIENT - if( !(COLD_RESISTANCE in mutations)) - adjustBruteLoss( LOW_PRESSURE_DAMAGE ) + take_overall_damage(brute=LOW_PRESSURE_DAMAGE, used_weapon = "Low Pressure") pressure_alert = -2 else pressure_alert = -1 - if(environment.toxins > MOLES_PLASMA_VISIBLE) - pl_effects() - return ///FIRE CODE handle_fire() @@ -796,26 +772,34 @@ return temp_change */ - proc/stabilize_temperature_from_calories() - var/body_temperature_difference = 310.15 - bodytemperature + proc/stabilize_body_temperature() + if (species.flags & IS_SYNTHETIC) + bodytemperature += species.synth_temp_gain //just keep putting out heat. + return + + var/body_temperature_difference = species.body_temperature - bodytemperature + if (abs(body_temperature_difference) < 0.5) return //fuck this precision - switch(bodytemperature) - if(-INFINITY to 260.15) //260.15 is 310.15 - 50, the temperature where you start to feel effects. - if(nutrition >= 2) //If we are very, very cold we'll use up quite a bit of nutriment to heat us up. - nutrition -= 2 - var/recovery_amt = max((body_temperature_difference / BODYTEMP_AUTORECOVERY_DIVISOR), BODYTEMP_AUTORECOVERY_MINIMUM) + + if(bodytemperature < species.cold_level_1) //260.15 is 310.15 - 50, the temperature where you start to feel effects. + if(nutrition >= 2) //If we are very, very cold we'll use up quite a bit of nutriment to heat us up. + nutrition -= 2 + var/recovery_amt = max((body_temperature_difference / BODYTEMP_AUTORECOVERY_DIVISOR), BODYTEMP_AUTORECOVERY_MINIMUM) + //world << "Cold. Difference = [body_temperature_difference]. Recovering [recovery_amt]" // log_debug("Cold. Difference = [body_temperature_difference]. Recovering [recovery_amt]") - bodytemperature += recovery_amt - if(260.15 to 360.15) - var/recovery_amt = body_temperature_difference / BODYTEMP_AUTORECOVERY_DIVISOR + bodytemperature += recovery_amt + else if(species.cold_level_1 <= bodytemperature && bodytemperature <= species.heat_level_1) + var/recovery_amt = body_temperature_difference / BODYTEMP_AUTORECOVERY_DIVISOR + //world << "Norm. Difference = [body_temperature_difference]. Recovering [recovery_amt]" // log_debug("Norm. Difference = [body_temperature_difference]. Recovering [recovery_amt]") - bodytemperature += recovery_amt - if(360.15 to INFINITY) //360.15 is 310.15 + 50, the temperature where you start to feel effects. - //We totally need a sweat system cause it totally makes sense...~ - var/recovery_amt = min((body_temperature_difference / BODYTEMP_AUTORECOVERY_DIVISOR), -BODYTEMP_AUTORECOVERY_MINIMUM) //We're dealing with negative numbers + bodytemperature += recovery_amt + else if(bodytemperature > species.heat_level_1) //360.15 is 310.15 + 50, the temperature where you start to feel effects. + //We totally need a sweat system cause it totally makes sense...~ + var/recovery_amt = min((body_temperature_difference / BODYTEMP_AUTORECOVERY_DIVISOR), -BODYTEMP_AUTORECOVERY_MINIMUM) //We're dealing with negative numbers + //world << "Hot. Difference = [body_temperature_difference]. Recovering [recovery_amt]" // log_debug("Hot. Difference = [body_temperature_difference]. Recovering [recovery_amt]") - bodytemperature += recovery_amt + bodytemperature += recovery_amt //This proc returns a number made up of the flags for body parts which you are protected on. (such as HEAD, UPPER_TORSO, LOWER_TORSO, etc. See setup.dm for the full list) proc/get_heat_protection_flags(temperature) //Temperature is the temperature you're being exposed to. @@ -995,19 +979,18 @@ proc/handle_chemicals_in_body() if(reagents && !(species.flags & IS_SYNTHETIC)) //Synths don't process reagents. - var/alien = 0 //Not the best way to handle it, but neater than checking this for every single reagent proc. - if(species && species.name == "Diona") - alien = 1 - else if(species && species.name == "Vox") - alien = 2 + var/alien = 0 + if(species && species.reagent_tag) + alien = species.reagent_tag reagents.metabolize(src,alien) - var/total_plasmaloss = 0 - for(var/obj/item/I in src) - if(I.contaminated) - total_plasmaloss += vsc.plc.CONTAMINATION_LOSS + var/total_plasmaloss = 0 + for(var/obj/item/I in src) + if(I.contaminated) + total_plasmaloss += vsc.plc.CONTAMINATION_LOSS + if(!(status_flags & GODMODE)) adjustToxLoss(total_plasmaloss) + if(status_flags & GODMODE) return 0 //godmode - adjustToxLoss(total_plasmaloss) if(species.flags & REQUIRE_LIGHT) var/light_amount = 0 //how much light there is in the place, affects receiving nutrition and healing @@ -1095,23 +1078,21 @@ if(!(species.flags & IS_SYNTHETIC)) handle_trace_chems() - var/datum/organ/internal/liver/liver = internal_organs["liver"] - liver.process() - - var/datum/organ/internal/eyes/eyes = internal_organs["eyes"] - eyes.process() - updatehealth() return //TODO: DEFERRED proc/handle_regular_status_updates() + + if(status_flags & GODMODE) return 0 + if(stat == DEAD) //DEAD. BROWN BREAD. SWIMMING WITH THE SPESS CARP blinded = 1 silent = 0 else //ALIVE. LIGHTS ARE ON updatehealth() //TODO if(!in_stasis) + stabilize_body_temperature() //Body temperature adjusts itself handle_organs() //Optimized. handle_blood() @@ -1223,33 +1204,12 @@ ear_damage = max(ear_damage-0.05, 0) //Other - if(stunned) - speech_problem_flag = 1 - AdjustStunned(-1) + handle_statuses() - 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) -/* - // Increase germ_level regularly - if(prob(40)) - germ_level += 1 // If you're dirty, your gloves will become dirty, too. if(gloves && germ_level > gloves.germ_level && prob(10)) gloves.germ_level += 1 -*/ + return 1 proc/handle_regular_hud_updates() @@ -1267,7 +1227,7 @@ if(copytext(hud.icon_state,1,4) == "hud") //ugly, but icon comparison is worse, I believe client.images.Remove(hud) - client.screen.Remove(global_hud.blurry, global_hud.druggy, global_hud.vimpaired, global_hud.darkMask, global_hud.nvg, global_hud.lum) + client.screen.Remove(global_hud.blurry, global_hud.druggy, global_hud.vimpaired, global_hud.darkMask, global_hud.nvg) update_action_buttons() @@ -1381,7 +1341,7 @@ if(XRAY in mutations) sight |= SEE_TURFS|SEE_MOBS|SEE_OBJS see_in_dark = 8 - see_invisible = SEE_INVISIBLE_LEVEL_TWO + if(!druggy) see_invisible = SEE_INVISIBLE_LEVEL_TWO if(seer==1) var/obj/effect/rune/R = locate() in loc @@ -1391,7 +1351,9 @@ see_invisible = SEE_INVISIBLE_LIVING seer = 0 + var/tmp/has_ninja_mask = 0 if(istype(wear_mask, /obj/item/clothing/mask/gas/voice/space_ninja)) + has_ninja_mask = 1 var/obj/item/clothing/mask/gas/voice/space_ninja/O = wear_mask switch(O.mode) if(0) @@ -1440,7 +1402,7 @@ if(!druggy) see_invisible = SEE_INVISIBLE_LIVING - else if(!seer) + else if(!seer && !has_ninja_mask) see_invisible = SEE_INVISIBLE_LIVING @@ -1491,17 +1453,46 @@ else fire.icon_state = "fire0" if(bodytemp) - switch(bodytemperature) //310.055 optimal body temp - if(370 to INFINITY) bodytemp.icon_state = "temp4" - if(350 to 370) bodytemp.icon_state = "temp3" - if(335 to 350) bodytemp.icon_state = "temp2" - if(320 to 335) bodytemp.icon_state = "temp1" - if(300 to 320) bodytemp.icon_state = "temp0" - if(295 to 300) bodytemp.icon_state = "temp-1" - if(280 to 295) bodytemp.icon_state = "temp-2" - if(260 to 280) bodytemp.icon_state = "temp-3" - else bodytemp.icon_state = "temp-4" + if (!species) + switch(bodytemperature) //310.055 optimal body temp + if(370 to INFINITY) bodytemp.icon_state = "temp4" + if(350 to 370) bodytemp.icon_state = "temp3" + if(335 to 350) bodytemp.icon_state = "temp2" + if(320 to 335) bodytemp.icon_state = "temp1" + if(300 to 320) bodytemp.icon_state = "temp0" + if(295 to 300) bodytemp.icon_state = "temp-1" + if(280 to 295) bodytemp.icon_state = "temp-2" + if(260 to 280) bodytemp.icon_state = "temp-3" + else bodytemp.icon_state = "temp-4" + else + var/temp_step + if (bodytemperature >= species.body_temperature) + temp_step = (species.heat_level_1 - species.body_temperature)/4 + if (bodytemperature >= species.heat_level_1) + bodytemp.icon_state = "temp4" + else if (bodytemperature >= species.body_temperature + temp_step*3) + bodytemp.icon_state = "temp3" + else if (bodytemperature >= species.body_temperature + temp_step*2) + bodytemp.icon_state = "temp2" + else if (bodytemperature >= species.body_temperature + temp_step*1) + bodytemp.icon_state = "temp1" + else + bodytemp.icon_state = "temp0" + + else if (bodytemperature < species.body_temperature) + temp_step = (species.body_temperature - species.cold_level_1)/4 + + if (bodytemperature <= species.cold_level_1) + bodytemp.icon_state = "temp-4" + else if (bodytemperature <= species.body_temperature - temp_step*3) + bodytemp.icon_state = "temp-3" + else if (bodytemperature <= species.body_temperature - temp_step*2) + bodytemp.icon_state = "temp-2" + else if (bodytemperature <= species.body_temperature - temp_step*1) + bodytemp.icon_state = "temp-1" + else + bodytemp.icon_state = "temp0" if(blind) if(blinded) blind.layer = 18 else blind.layer = 0 @@ -1684,26 +1675,19 @@ if(status_flags & FAKEDEATH) temp = PULSE_NONE //pretend that we're dead. unlike actual death, can be inflienced by meds + //handles different chems' influence on pulse for(var/datum/reagent/R in reagents.reagent_list) if(R.id in bradycardics) if(temp <= PULSE_THREADY && temp >= PULSE_NORM) temp-- - break //one reagent is enough - //comment out the breaks to make med effects stack - for(var/datum/reagent/R in reagents.reagent_list) //handles different chems' influence on pulse if(R.id in tachycardics) if(temp <= PULSE_FAST && temp >= PULSE_NONE) temp++ - break - for(var/datum/reagent/R in reagents.reagent_list) //To avoid using fakedeath - if(R.id in heartstopper) + if(R.id in heartstopper) //To avoid using fakedeath temp = PULSE_NONE - break - for(var/datum/reagent/R in reagents.reagent_list) //Conditional heart-stoppage - if(R.id in cheartstopper) + if(R.id in cheartstopper) //Conditional heart-stoppage if(R.volume >= R.overdose) temp = PULSE_NONE - break return temp @@ -1852,6 +1836,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/human/say.dm b/code/modules/mob/living/carbon/human/say.dm index 3839ee2e..859fa02f 100644 --- a/code/modules/mob/living/carbon/human/say.dm +++ b/code/modules/mob/living/carbon/human/say.dm @@ -1,4 +1,5 @@ -/mob/living/carbon/human/say(var/message, var/verb = "says") +/mob/living/carbon/human/say(var/message) + var/verb = "says" var/alt_name = "" var/message_range = world.view var/italics = 0 @@ -13,7 +14,9 @@ if(stat == 2) return say_dead(message) - if (istype(wear_mask, /obj/item/clothing/mask/muzzle)) //Todo: Add this to speech_problem_flag checks. + var/message_mode = parse_message_mode(message, "headset") + + if (istype(wear_mask, /obj/item/clothing/mask/muzzle) && message_mode != "changeling") //Todo: Add this to speech_problem_flag checks. return if(copytext(message,1,2) == "*") @@ -23,10 +26,9 @@ alt_name = "(as [get_id_name("Unknown")])" //parse the radio code and consume it - var/message_mode = parse_message_mode(message) if (message_mode) if (message_mode == "headset") - message = copytext(message,2) + message = copytext(message,2) //it would be really nice if the parse procs could do this for us. else message = copytext(message,3) @@ -35,6 +37,7 @@ if (speaking) verb = speaking.speech_verb message = copytext(message,3) + message = capitalize(trim(message)) if(speech_problem_flag) @@ -46,72 +49,59 @@ if(!message || stat) return - var/ending = copytext(message, length(message)) - if(verb == "says") - if(ending == "!") - verb = pick("exclaims","shouts","yells") - if(ending == "?") - verb = "asks" - else - if(ending == "!") - verb = "orders" - if(ending == "?") - verb = "requests" - - if(speaking) //speaking = null if used through say without a language - if(!speaking.vocal) - sign(message, verb, speaking, alt_name, italics, message_range) - return + if (!speaking) + var/ending = copytext(message, length(message)) + if(ending=="!") + verb=pick("exclaims","shouts","yells") + if(ending=="?") + verb="asks" var/list/obj/item/used_radios = new switch (message_mode) if("headset") - if(!(stunned >= 4)) - if(l_ear && istype(l_ear,/obj/item/device/radio)) - var/obj/item/device/radio/R = l_ear - R.talk_into(src,message,null,verb,speaking) - used_radios += l_ear - else if(r_ear && istype(r_ear,/obj/item/device/radio)) - var/obj/item/device/radio/R = r_ear - R.talk_into(src,message,null,verb,speaking) - used_radios += r_ear + if(l_ear && istype(l_ear,/obj/item/device/radio)) + var/obj/item/device/radio/R = l_ear + R.talk_into(src,message,null,verb,speaking) + used_radios += l_ear + else if(r_ear && istype(r_ear,/obj/item/device/radio)) + var/obj/item/device/radio/R = r_ear + R.talk_into(src,message,null,verb,speaking) + used_radios += r_ear if("right ear") - if(!(stunned >= 4)) - var/obj/item/device/radio/R - var/has_radio = 0 - if(r_ear && istype(r_ear,/obj/item/device/radio)) - R = r_ear - has_radio = 1 - if(r_hand && istype(r_hand, /obj/item/device/radio)) - R = r_hand - has_radio = 1 - if(has_radio) - R.talk_into(src,message,null,verb,speaking) - used_radios += R + var/obj/item/device/radio/R + var/has_radio = 0 + if(r_ear && istype(r_ear,/obj/item/device/radio)) + R = r_ear + has_radio = 1 + if(r_hand && istype(r_hand, /obj/item/device/radio)) + R = r_hand + has_radio = 1 + if(has_radio) + R.talk_into(src,message,null,verb,speaking) + used_radios += R if("left ear") - if(!(stunned >= 4)) - var/obj/item/device/radio/R - var/has_radio = 0 - if(l_ear && istype(l_ear,/obj/item/device/radio)) - R = l_ear - has_radio = 1 - if(l_hand && istype(l_hand,/obj/item/device/radio)) - R = l_hand - has_radio = 1 - if(has_radio) - R.talk_into(src,message,null,verb,speaking) - used_radios += R + var/obj/item/device/radio/R + var/has_radio = 0 + if(l_ear && istype(l_ear,/obj/item/device/radio)) + R = l_ear + has_radio = 1 + if(l_hand && istype(l_hand,/obj/item/device/radio)) + R = l_hand + has_radio = 1 + if(has_radio) + R.talk_into(src,message,null,verb,speaking) + used_radios += R if("intercom") for(var/obj/item/device/radio/intercom/I in view(1, null)) I.talk_into(src, message, verb, speaking) used_radios += I if("whisper") - whisper(message) + whisper_say(message, speaking, alt_name) return if("binary") if(robot_talk_understand || binarycheck()) @@ -124,51 +114,45 @@ Changeling << "[mind.changeling.changelingID]: [message]" return else - if(!(stunned >= 4)) - if(message_mode) - if(message_mode in (radiochannels | "department")) - if(l_ear && istype(l_ear,/obj/item/device/radio)) - l_ear.talk_into(src,message, message_mode, verb, speaking) - used_radios += l_ear - else if(r_ear && istype(r_ear,/obj/item/device/radio)) - r_ear.talk_into(src,message, message_mode, verb, speaking) - used_radios += r_ear - - - if(used_radios.len) - italics = 1 - message_range = 1 - - var/datum/gas_mixture/environment = loc.return_air() - if(environment) - var/pressure = environment.return_pressure() - if(pressure < SAY_MINIMUM_PRESSURE) - italics = 1 - message_range =1 + if(message_mode) + if(message_mode in (radiochannels | "department")) + if(l_ear && istype(l_ear,/obj/item/device/radio)) + l_ear.talk_into(src,message, message_mode, verb, speaking) + used_radios += l_ear + else if(r_ear && istype(r_ear,/obj/item/device/radio)) + r_ear.talk_into(src,message, message_mode, verb, speaking) + used_radios += r_ear + var/sound/speech_sound + var/sound_vol if((species.name == "Vox" || species.name == "Vox Armalis") && prob(20)) - playsound(src.loc, 'sound/voice/shriek1.ogg', 50, 1) + speech_sound = sound('sound/voice/shriek1.ogg') + sound_vol = 50 - ..(message, speaking, verb, alt_name, italics, message_range, used_radios) + ..(message, speaking, verb, alt_name, italics, message_range, used_radios, speech_sound, sound_vol) //ohgod we should really be passing a datum here. /mob/living/carbon/human/say_understands(var/mob/other,var/datum/language/speaking = null) if(has_brain_worms()) //Brain worms translate everything. Even mice and alien speak. return 1 - if (istype(other, /mob/living/carbon/monkey/diona) && !speaking) - if(other.languages.len >= 2) //They've sucked down some blood and can speak common now. + + //These only pertain to common. Languages are handled by mob/say_understands() + if (!speaking) + if (istype(other, /mob/living/carbon/monkey/diona)) + if(other.languages.len >= 2) //They've sucked down some blood and can speak common now. + return 1 + if (istype(other, /mob/living/silicon)) + return 1 + if (istype(other, /mob/living/carbon/brain)) + return 1 + if (istype(other, /mob/living/carbon/slime)) return 1 - if (istype(other, /mob/living/silicon)) - return 1 - if (istype(other, /mob/living/carbon/brain)) - return 1 - if (istype(other, /mob/living/carbon/slime)) - return 1 - if (istype(other, /mob/living/simple_animal)) - if(other.universal_speak || src.universal_speak || src.universal_understand) - return 1 - return 0 + //This is already covered by mob/say_understands() + //if (istype(other, /mob/living/simple_animal)) + // if((other.universal_speak && !speaking) || src.universal_speak || src.universal_understand) + // return 1 + // return 0 return ..() @@ -217,6 +201,9 @@ return verb + + + /mob/living/carbon/human/proc/handle_speech_problems(var/message) var/list/returns[3] var/verb = "says" @@ -237,7 +224,7 @@ handled = 1 if((HULK in mutations) && health >= 25 && length(message)) - //message = "[uppertext(message)]!!!" //this is really irritating. let's not. + message = "[uppertext(message)]!!!" verb = pick("yells","roars","hollers") handled = 1 if(slurring) diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index 5d7187ff..7a9d7918 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -113,13 +113,13 @@ Please contact me on #coderbus IRC. ~Carn x #define ID_LAYER 6 #define SHOES_LAYER 7 #define GLOVES_LAYER 8 -#define EARS_LAYER 9 -#define SUIT_LAYER 10 -#define GLASSES_LAYER 11 -#define BELT_LAYER 12 //Possible make this an overlay of somethign required to wear a belt? -#define SUIT_STORE_LAYER 13 -#define BACK_LAYER 14 -#define HAIR_LAYER 15 //TODO: make part of head layer? +#define SUIT_LAYER 9 +#define GLASSES_LAYER 10 +#define BELT_LAYER 11 //Possible make this an overlay of somethign required to wear a belt? +#define SUIT_STORE_LAYER 12 +#define BACK_LAYER 13 +#define HAIR_LAYER 14 //TODO: make part of head layer? +#define EARS_LAYER 15 #define FACEMASK_LAYER 16 #define HEAD_LAYER 17 #define COLLAR_LAYER 18 @@ -128,29 +128,14 @@ Please contact me on #coderbus IRC. ~Carn x #define L_HAND_LAYER 21 #define R_HAND_LAYER 22 #define TARGETED_LAYER 23 //BS12: Layer for the target overlay from weapon targeting system -#define FIRE_LAYER 23 //If you're on fire -#define TOTAL_LAYERS 23 +#define FIRE_LAYER 24 //If you're on fire +#define TOTAL_LAYERS 24 ////////////////////////////////// /mob/living/carbon/human var/list/overlays_standing[TOTAL_LAYERS] - var/list/overlays_lying[TOTAL_LAYERS] var/previous_damage_appearance // store what the body last looked like, so we only have to update it if something changed - -/mob/living/carbon/human/proc/apply_overlay(cache_index) - var/image/I = lying ? overlays_lying[cache_index] : overlays_standing[cache_index] - if(I) - overlays += I - -/mob/living/carbon/human/proc/remove_overlay(cache_index) - if(overlays_lying[cache_index]) - overlays -= overlays_lying[cache_index] - overlays_lying[cache_index] = null - if(overlays_standing[cache_index]) - overlays -= overlays_standing[cache_index] - overlays_standing[cache_index] = null - //UPDATES OVERLAYS FROM OVERLAYS_LYING/OVERLAYS_STANDING //this proc is messy as I was forced to include some old laggy cloaking code to it so that I don't break cloakers //I'll work on removing that stuff by rewriting some of the cloaking stuff at a later date. @@ -265,7 +250,7 @@ proc/get_damage_icon_part(damage_state, body_part) icon_key = "[icon_key]0" else if(part.status & ORGAN_ROBOT) icon_key = "[icon_key]2" - else if(part.status & ORGAN_DEAD) //Do we even have necrosis in our current code? ~Z + else if(part.status & ORGAN_DEAD) icon_key = "[icon_key]3" else icon_key = "[icon_key]1" @@ -292,6 +277,10 @@ proc/get_damage_icon_part(damage_state, body_part) var/datum/organ/external/chest = get_organ("chest") base_icon = chest.get_icon(race_icon,deform_icon,g) + if(chest.status & ORGAN_DEAD) + base_icon.ColorTone(necrosis_color_mod) + base_icon.SetIntensity(0.7) + for(var/datum/organ/external/part in organs) var/icon/temp //Hold the bodypart icon for processing. @@ -385,11 +374,11 @@ proc/get_damage_icon_part(damage_state, body_part) stand_icon.Blend(new/icon('icons/mob/human_face.dmi', "lips_[lip_style]_s"), ICON_OVERLAY) //Underwear - if(underwear >0 && underwear < 7 && species.flags & HAS_UNDERWEAR) + if(underwear >0 && underwear < 12 && species.flags & HAS_UNDERWEAR) if(!fat && !skeleton) stand_icon.Blend(new /icon('icons/mob/human.dmi', "underwear[underwear]_[g]_s"), ICON_OVERLAY) - if(undershirt > 0 && undershirt < 16 && species.flags & HAS_UNDERWEAR) + if(undershirt>0 && undershirt < 5 && species.flags & HAS_UNDERWEAR) stand_icon.Blend(new /icon('icons/mob/human.dmi', "undershirt[undershirt]_s"), ICON_OVERLAY) if(update_icons) @@ -672,6 +661,7 @@ proc/get_damage_icon_part(damage_state, body_part) if(l_ear || r_ear) if(l_ear) + var/t_type = l_ear.icon_state if(l_ear.icon_override) t_type = "[t_type]_l" @@ -683,6 +673,7 @@ proc/get_damage_icon_part(damage_state, body_part) overlays_standing[EARS_LAYER] = image("icon" = 'icons/mob/ears.dmi', "icon_state" = "[t_type]") if(r_ear) + var/t_type = r_ear.icon_state if(r_ear.icon_override) t_type = "[t_type]_r" diff --git a/code/modules/mob/living/carbon/human/whisper.dm b/code/modules/mob/living/carbon/human/whisper.dm index 0bae026c..c77bef27 100644 --- a/code/modules/mob/living/carbon/human/whisper.dm +++ b/code/modules/mob/living/carbon/human/whisper.dm @@ -1,15 +1,11 @@ //Lallander was here /mob/living/carbon/human/whisper(message as text) + var/alt_name = "" if(say_disabled) //This is here to try to identify lag problems usr << "\red Speech is currently admin-disabled." return - - message = trim(copytext(strip_html_simple(message), 1, MAX_MESSAGE_LEN)) - - if (!message || silent || miming) - return - + log_whisper("[src.name]/[src.key] : [message]") if (src.client) @@ -20,27 +16,51 @@ if (src.client.handle_spam_prevention(message,MUTE_IC)) return - if (src.stat == 2) return src.say_dead(message) if (src.stat) return + + message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) //made consistent with say - var/alt_name = "" - if (istype(src, /mob/living/carbon/human) && src.name != GetVoice()) - var/mob/living/carbon/human/H = src - alt_name = " (as [H.get_id_name("Unknown")])" + if(name != GetVoice()) + alt_name = "(as [get_id_name("Unknown")])" + + //parse the language code and consume it + var/datum/language/speaking = parse_language(message) + if (speaking) + message = copytext(message,3) + + whisper_say(message, speaking, alt_name) + + +//This is used by both the whisper verb and human/say() to handle whispering +/mob/living/carbon/human/proc/whisper_say(var/message, var/datum/language/speaking = null, var/alt_name="", var/verb="whispers") + var/message_range = 1 + var/eavesdropping_range = 2 + var/watching_range = 5 + var/italics = 1 + + if (speaking) + verb = speaking.speech_verb + pick(" quietly", " softly") + + message = capitalize(trim(message)) + + //TODO: handle_speech_problems for silent + if (!message || silent || miming) + return + // Mute disability + //TODO: handle_speech_problems if (src.sdisabilities & MUTE) return + //TODO: handle_speech_problems if (istype(src.wear_mask, /obj/item/clothing/mask/muzzle)) return - var/italics = 1 - var/message_range = 1 - + //looks like this only appears in whisper. Should it be elsewhere as well? Maybe handle_speech_problems? if(istype(src.wear_mask, /obj/item/clothing/mask/gas/voice/space_ninja)&&src.wear_mask:voice=="Unknown") if(copytext(message, 1, 2) != "*") var/list/temp_message = text2list(message, " ") @@ -60,90 +80,57 @@ message = replacetext(message, "u", "µ") message = replacetext(message, "b", "ß") + //TODO: handle_speech_problems if (src.stuttering) message = stutter(message) - for (var/obj/O in view(message_range, src)) - spawn (0) - if (O) - O.hear_talk(src, message) - var/list/listening = hearers(message_range, src) listening |= src - + + //ghosts + for (var/mob/M in dead_mob_list) //does this include players who joined as observers as well? + if (!(M.client)) + continue + if(M.stat == DEAD && M.client && (M.client.prefs.toggles & CHAT_GHOSTEARS)) + listening |= M + //Pass whispers on to anything inside the immediate listeners. for(var/mob/L in listening) for(var/mob/C in L.contents) if(istype(C,/mob/living)) listening += C + + //pass on the message to objects that can hear us. + for (var/obj/O in view(message_range, src)) + spawn (0) + if (O) + O.hear_talk(src, message) //O.hear_talk(src, message, verb, speaking) - var/list/eavesdropping = hearers(2, src) + var/list/eavesdropping = hearers(eavesdropping_range, src) eavesdropping -= src eavesdropping -= listening - var/list/watching = hearers(5, src) + var/list/watching = hearers(watching_range, src) watching -= src watching -= listening watching -= eavesdropping - var/list/heard_a = list() // understood us - var/list/heard_b = list() // didn't understand us - - for (var/mob/M in listening) - if (M.say_understands(src)) - heard_a += M - else - heard_b += M - - var/rendered = null - - for (var/mob/M in watching) - if (M.say_understands(src)) - rendered = "[src.name] whispers something." - else - rendered = "[src.voice_name] whispers something." - M.show_message(rendered, 2) - - if (length(heard_a)) - var/message_a = message - - if (italics) - message_a = "[message_a]" - //This appears copied from carbon/living say.dm so the istype check for mob is probably not needed. Appending for src is also not needed as the game will check that automatically. - rendered = "[GetVoice()][alt_name] whispers, \"[message_a]\"" - - for (var/mob/M in heard_a) - M.show_message(rendered, 2) - - if (length(heard_b)) - var/message_b - - message_b = stars(message) - - if (italics) - message_b = "[message_b]" - - rendered = "[src.voice_name] whispers, \"[message_b]\"" - - for (var/mob/M in heard_b) - M.show_message(rendered, 2) - - for (var/mob/M in eavesdropping) - if (M.say_understands(src)) - var/message_c - message_c = stars(message) - rendered = "[GetVoice()][alt_name] whispers, \"[message_c]\"" - M.show_message(rendered, 2) - else - rendered = "[src.voice_name] whispers something." - M.show_message(rendered, 2) - - if (italics) - message = "[message]" - rendered = "[GetVoice()][alt_name] whispers, \"[message]\"" - - for (var/mob/M in dead_mob_list) - if (!(M.client)) - continue - if (M.stat > 1 && !(M in heard_a) && (M.client.prefs.toggles & CHAT_GHOSTEARS)) + //now mobs + var/speech_bubble_test = say_test(message) + var/image/speech_bubble = image('icons/mob/talk.dmi',src,"h[speech_bubble_test]") + spawn(30) del(speech_bubble) + + for(var/mob/M in listening) + M << speech_bubble + M.hear_say(message, verb, speaking, alt_name, italics, src) + + if (eavesdropping.len) + var/new_message = stars(message) //hopefully passing the message twice through stars() won't hurt... I guess if you already don't understand the language, when they speak it too quietly to hear normally you would be able to catch even less. + for(var/mob/M in eavesdropping) + M << speech_bubble + M.hear_say(new_message, verb, speaking, alt_name, italics, src) + + if (watching.len) + var/rendered = "[src.name] whispers something." + for (var/mob/M in watching) M.show_message(rendered, 2) diff --git a/code/modules/mob/living/carbon/metroid/death.dm b/code/modules/mob/living/carbon/metroid/death.dm index d511d670..d1b3a945 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 c1ef058b..123bcbce 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 17b76171..24a0a6e1 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 82759f19..063f6d4c 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,99 +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(catalyst == 0) - 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) - else - 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) - - 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 - A.catalyst = catalyst - del(src) - + Evolve() /mob/living/carbon/slime/proc/handle_targets() if(Tempstun) @@ -418,8 +290,7 @@ if(attacked > 50) attacked = 50 if(attacked > 0) - if(prob(85)) - attacked-- + attacked-- if(Discipline > 0) @@ -429,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 333e0539..16491096 100644 --- a/code/modules/mob/living/carbon/metroid/metroid.dm +++ b/code/modules/mob/living/carbon/metroid/metroid.dm @@ -3,6 +3,7 @@ icon = 'icons/mob/slimes.dmi' icon_state = "grey baby slime" pass_flags = PASSTABLE + var/is_adult = 0 speak_emote = list("hums") layer = 5 @@ -22,23 +23,27 @@ 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 @@ -68,24 +73,29 @@ /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) @@ -95,8 +105,8 @@ 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 tally *= 5 @@ -104,106 +114,84 @@ 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 @@ -213,25 +201,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) @@ -263,7 +244,7 @@ //paralysis += 1 - show_message("\red The blob attacks you!") + show_message(" The blob attacks you!") adjustFireLoss(damage) @@ -274,7 +255,6 @@ /mob/living/carbon/slime/u_equip(obj/item/W as obj) return - /mob/living/carbon/slime/attack_ui(slot) return @@ -289,52 +269,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." @@ -343,6 +319,7 @@ if (istype(loc, /turf) && istype(loc.loc, /area/start)) M << "No attacking people at spawn, you jackass." return + ..() switch(M.a_intent) @@ -355,9 +332,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 @@ -377,15 +353,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) @@ -405,21 +377,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 @@ -435,18 +403,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! " @@ -458,21 +421,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 @@ -482,8 +442,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++ @@ -495,17 +456,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 @@ -521,48 +479,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) @@ -570,12 +521,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++ @@ -596,68 +547,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" @@ -727,6 +733,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 @@ -737,28 +763,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" @@ -766,27 +795,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 @@ -797,16 +829,16 @@ 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." @@ -842,6 +874,8 @@ mob/living/carbon/slime/var/temperature_resistance = T0C+75 ////////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" @@ -978,6 +1012,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//////////////////////// /* @@ -987,11 +1022,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 @@ -1000,9 +1034,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 @@ -1046,9 +1078,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) @@ -1064,4 +1096,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 440bac88..29edf5b0 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 + //Heal yourself. + adjustOxyLoss(-10) + adjustBruteLoss(-10) + adjustFireLoss(-10) + adjustCloneLoss(-10) - Victim.updatehealth() 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,49 @@ 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 + var/mob/living/carbon/slime/O = new mutationone(loc) + O.nutrition = new_nutrition + O.powerlevel = new_powerlevel if(i != 1) step_away(M,src) - babies += M + babies += O if("two") - var/mob/living/carbon/slime/M = new mutationtwo(loc) - M.nutrition = new_nutrition - M.powerlevel = new_powerlevel + var/mob/living/carbon/slime/K = new mutationtwo(loc) + K.nutrition = new_nutrition + K.powerlevel = new_powerlevel if(i != 1) step_away(M,src) - babies += M + babies += K if("three") - var/mob/living/carbon/slime/M = new mutationthree(loc) - M.nutrition = new_nutrition - M.powerlevel = new_powerlevel + var/mob/living/carbon/slime/L = new mutationthree(loc) + L.nutrition = new_nutrition + L.powerlevel = new_powerlevel if(i != 1) step_away(M,src) - babies += M + babies += L if("four") - var/mob/living/carbon/slime/M = new mutationfour(loc) - M.nutrition = new_nutrition - M.powerlevel = new_powerlevel + var/mob/living/carbon/slime/N = new mutationfour(loc) + N.nutrition = new_nutrition + N.powerlevel = new_powerlevel if(i != 1) step_away(M,src) - babies += M + babies += N 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 00000000..89708595 --- /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/monkey/diona.dm b/code/modules/mob/living/carbon/monkey/diona.dm index 8e6f10df..b0e885e6 100644 --- a/code/modules/mob/living/carbon/monkey/diona.dm +++ b/code/modules/mob/living/carbon/monkey/diona.dm @@ -12,6 +12,7 @@ var/ready_evolve = 0 universal_understand = 0 // Dionaea do not need to speak to people universal_speak = 0 // before becoming an adult. Use *chirp. + holder_type = /obj/item/weapon/holder/diona /mob/living/carbon/monkey/diona/attack_hand(mob/living/carbon/human/M as mob) @@ -24,14 +25,7 @@ src.verbs -= /mob/living/carbon/monkey/diona/proc/merge src.loc = M else - var/obj/item/weapon/holder/diona/D = new(loc) - src.loc = D - D.name = loc.name - D.attack_hand(M) - M << "You scoop up [src]." - src << "[M] scoops you up." - M.status_flags |= PASSEMOTES - return + get_scooped(M) ..() @@ -114,7 +108,7 @@ var/list/trays = list() for(var/obj/machinery/hydroponics/tray in range(1)) - if(tray.nutrilevel < 10) + if(tray.nutrilevel < 10 && src.Adjacent(tray)) trays += tray var/obj/machinery/hydroponics/target = input("Select a tray:") as null|anything in trays @@ -133,7 +127,7 @@ var/list/trays = list() for(var/obj/machinery/hydroponics/tray in range(1)) - if(tray.weedlevel > 0) + if(tray.weedlevel > 0 && src.Adjacent(tray)) trays += tray var/obj/machinery/hydroponics/target = input("Select a tray:") as null|anything in trays @@ -192,7 +186,8 @@ var/list/choices = list() for(var/mob/living/carbon/human/H in oview(1,src)) - choices += H + if(src.Adjacent(H)) + choices += H var/mob/living/carbon/human/M = input(src,"Who do you wish to take a sample from?") in null|choices diff --git a/code/modules/mob/living/carbon/monkey/life.dm b/code/modules/mob/living/carbon/monkey/life.dm index a4ba1849..3e69263f 100644 --- a/code/modules/mob/living/carbon/monkey/life.dm +++ b/code/modules/mob/living/carbon/monkey/life.dm @@ -83,11 +83,6 @@ emote(pick("scratch","jump","roll","tail")) updatehealth() - -/mob/living/carbon/monkey/calculate_affecting_pressure(var/pressure) - ..() - return pressure - /mob/living/carbon/monkey proc/handle_disabilities() @@ -142,8 +137,9 @@ if (radiation > 100) radiation = 100 Weaken(10) - src << "\red You feel weak." - emote("collapse") + if(!lying) + src << "\red You feel weak." + emote("collapse") switch(radiation) if(1 to 49) @@ -157,8 +153,9 @@ if(prob(5)) radiation -= 5 Weaken(3) - src << "\red You feel weak." - emote("collapse") + if(!lying) + src << "\red You feel weak." + emote("collapse") if(75 to 100) radiation -= 3 @@ -238,25 +235,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.toxins *= 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.toxins *= 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) @@ -317,14 +303,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 toxins - var/Toxins_pp = (breath.toxins/breath.total_moles())*breath_pressure + var/Toxins_pp = (breath.gas["toxins"] / 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)) @@ -333,7 +319,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") @@ -343,11 +329,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. @@ -364,7 +350,7 @@ co2overloadtime = 0 if(Toxins_pp > safe_toxins_max) // Too much toxins - var/ratio = (breath.toxins/safe_toxins_max) * 10 + var/ratio = (breath.gas["toxins"] / safe_toxins_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("plasma", Clamp(ratio, MIN_PLASMA_DAMAGE, MAX_PLASMA_DAMAGE)) @@ -372,16 +358,15 @@ else toxins_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 :( @@ -402,9 +387,8 @@ //Moved these vars here for use in the fuck-it-skip-processing check. var/pressure = environment.return_pressure() - var/adjusted_pressure = calculate_affecting_pressure(pressure) //Returns how much pressure actually affects the mob. + if(pressure < WARNING_HIGH_PRESSURE && pressure > WARNING_LOW_PRESSURE && abs(environment.temperature - 293.15) < 20 && abs(bodytemperature - 310.14) < 0.5 && environment.gas["toxins"] < MOLES_PLASMA_VISIBLE) - if(adjusted_pressure < WARNING_HIGH_PRESSURE && adjusted_pressure > WARNING_LOW_PRESSURE && abs(environment.temperature - 293.15) < 20 && abs(bodytemperature - 310.14) < 0.5 && environment.toxins < MOLES_PLASMA_VISIBLE) //Hopefully should fix the walk-inside-still-pressure-warning issue. if(pressure_alert) @@ -427,9 +411,9 @@ bodytemperature += 0.1*(environment.temperature - bodytemperature)*environment_heat_capacity/(environment_heat_capacity + 270000) //Account for massive pressure differences - switch(adjusted_pressure) + switch(pressure) if(HAZARD_HIGH_PRESSURE to INFINITY) - adjustBruteLoss( min( ( (adjusted_pressure / HAZARD_HIGH_PRESSURE) -1 )*PRESSURE_DAMAGE_COEFFICIENT , MAX_HIGH_PRESSURE_DAMAGE) ) + adjustBruteLoss( min( ( (pressure / HAZARD_HIGH_PRESSURE) -1 )*PRESSURE_DAMAGE_COEFFICIENT , MAX_HIGH_PRESSURE_DAMAGE) ) pressure_alert = 2 if(WARNING_HIGH_PRESSURE to HAZARD_HIGH_PRESSURE) pressure_alert = 1 @@ -569,20 +553,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 7b59d520..f8db61b6 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/carbon/species.dm b/code/modules/mob/living/carbon/species.dm index 09b99e63..46edc0c8 100644 --- a/code/modules/mob/living/carbon/species.dm +++ b/code/modules/mob/living/carbon/species.dm @@ -14,11 +14,12 @@ var/language // Default racial language, if any. var/unarmed //For empty hand harm-intent attack var/unarmed_type = /datum/unarmed_attack + var/secondary_langs = list() // The names of secondary languages that are available to this species. var/mutantrace // Safeguard due to old code. var/breath_type = "oxygen" // Non-oxygen gas breathed, if any. var/poison_type = "plasma" // Poisonous air. - var/exhale_type = "C02" // Exhaled gas type. + var/exhale_type = "carbon_dioxide" // Exhaled gas type. var/cold_level_1 = 260 // Cold damage level 1 below this point. var/cold_level_2 = 200 // Cold damage level 2 below this point. @@ -28,6 +29,10 @@ var/heat_level_2 = 400 // Heat damage level 2 above this point. var/heat_level_3 = 1000 // Heat damage level 2 above this point. + var/body_temperature = 310.15 //non-IS_SYNTHETIC species will try to stabilize at this temperature. (also affects temperature processing) + var/synth_temp_gain = 0 //IS_SYNTHETIC species will gain this much temperature every second + var/reagent_tag //Used for metabolizing reagents. + var/darksight = 2 var/hazard_high_pressure = HAZARD_HIGH_PRESSURE // Dangerously high pressure. var/warning_high_pressure = WARNING_HIGH_PRESSURE // High pressure warning. @@ -43,34 +48,12 @@ var/blood_color = "#A10808" //Red. var/flesh_color = "#FFC896" //Pink. + var/base_color //Used when setting species. //Used in icon caching. var/race_key = 0 var/icon/icon_template - //Used for character generation fluff items. - var/list/custom_gear_options - - /* Species-specific sprites, concept stolen from Paradise//vg/. - ex: - sprite_sheets = list( - "held" = 'icons/mob/path', - "uniform" = 'icons/mob/path', - "suit" = 'icons/mob/path', - "belt" = 'icons/mob/path' - "head" = 'icons/mob/path', - "back" = 'icons/mob/path', - "mask" = 'icons/mob/path', - "ears" = 'icons/mob/path', - "eyes" = 'icons/mob/path', - "feet" = 'icons/mob/path', - "gloves" = 'icons/mob/path' - ) - If index term exists and icon_override is not set, this sprite sheet will be used. - */ - - var/list/sprite_sheets = list() - /datum/species/New() unarmed = new unarmed_type() @@ -156,10 +139,8 @@ flesh_color = "#34AF10" - custom_gear_options = list( - "Unathi robe" = /obj/item/clothing/suit/unathi/robe, - "Unathi mantle" = /obj/item/clothing/suit/unathi/mantle - ) + reagent_tag = IS_UNATHI + base_color = "#066000" /datum/species/tajaran name = "Tajaran" @@ -183,11 +164,7 @@ flags = IS_WHITELISTED | HAS_LIPS | HAS_UNDERWEAR | HAS_TAIL | HAS_SKIN_COLOR flesh_color = "#AFA59E" - - custom_gear_options = list( - /obj/item/clothing/head/tajaran/scarf, - /obj/item/clothing/suit/tajaran/furs - ) + base_color = "#333333" /datum/species/skrell name = "Skrell" @@ -201,6 +178,8 @@ flesh_color = "#8CD7A3" + reagent_tag = IS_SKRELL + /datum/species/vox name = "Vox" icobase = 'icons/mob/human_races/r_vox.dmi' @@ -220,27 +199,22 @@ breath_type = "nitrogen" poison_type = "oxygen" - flags = NO_SCAN | NO_BLOOD + flags = NO_SCAN blood_color = "#2299FC" flesh_color = "#808D11" - sprite_sheets = list( - "suit" = 'icons/mob/species/vox/suit.dmi', - "head" = 'icons/mob/species/vox/head.dmi', - "mask" = 'icons/mob/species/vox/mask.dmi', - "feet" = 'icons/mob/species/vox/feet.dmi', - "gloves" = 'icons/mob/species/vox/gloves.dmi' - ) -/* NO MORE LEAPS. NO MORE. + reagent_tag = IS_VOX + /datum/species/vox/handle_post_spawn(var/mob/living/carbon/human/H) H.verbs += /mob/living/carbon/human/proc/leap ..() -*/ + /datum/species/vox/armalis/handle_post_spawn(var/mob/living/carbon/human/H) H.verbs += /mob/living/carbon/human/proc/gut + H.verbs += /mob/living/carbon/human/proc/commune ..() /datum/species/vox/armalis @@ -276,36 +250,7 @@ tail = "armalis_tail" icon_template = 'icons/mob/human_races/r_armalis.dmi' - sprite_sheets = list( - "suit" = 'icons/mob/species/armalis/suit.dmi', - "gloves" = 'icons/mob/species/armalis/gloves.dmi', - "feet" = 'icons/mob/species/armalis/feet.dmi', - "head" = 'icons/mob/species/armalis/head.dmi', - "held" = 'icons/mob/species/armalis/held.dmi' - ) - -/datum/species/vox/create_organs(var/mob/living/carbon/human/H) - - ..() //create organs first. - - //Now apply cortical stack. - var/datum/organ/external/affected = H.get_organ("head") - - //To avoid duplicates. - for(var/obj/item/weapon/implant/cortical/imp in H.contents) - affected.implants -= imp - del(imp) - - var/obj/item/weapon/implant/cortical/I = new(H) - I.imp_in = H - I.implanted = 1 - affected.implants += I - I.part = affected - - if(ticker.mode && ( istype( ticker.mode,/datum/game_mode/heist ) ) ) - var/datum/game_mode/heist/M = ticker.mode - M.cortical_stacks += I - M.raiders[H.mind] = I + reagent_tag = IS_VOX /datum/species/diona name = "Diona" @@ -326,11 +271,15 @@ heat_level_2 = 3000 heat_level_3 = 4000 + body_temperature = T0C + 15 //make the plant people have a bit lower body temperature, why not + flags = IS_WHITELISTED | NO_BREATHE | REQUIRE_LIGHT | NO_SCAN | IS_PLANT | RAD_ABSORB | NO_BLOOD | IS_SLOW | NO_PAIN blood_color = "#004400" flesh_color = "#907E4A" + reagent_tag = IS_DIONA + /datum/species/diona/handle_post_spawn(var/mob/living/carbon/human/H) H.gender = NEUTER @@ -342,7 +291,6 @@ if(H.mind) H.mind.transfer_to(S) - S.key = H for(var/mob/living/carbon/monkey/diona/D in H.contents) if(D.client) @@ -364,15 +312,17 @@ burn_mod = 1 warning_low_pressure = 50 - hazard_low_pressure = 10 + hazard_low_pressure = 0 cold_level_1 = 50 cold_level_2 = -1 cold_level_3 = -1 - heat_level_1 = 2000 - heat_level_2 = 3000 - heat_level_3 = 4000 + heat_level_1 = 500 //gives them about 25 seconds in space before taking damage + heat_level_2 = 1000 + heat_level_3 = 2000 + + synth_temp_gain = 10 //this should cause IPCs to stabilize at ~80 C in a 20 C environment. flags = IS_WHITELISTED | NO_BREATHE | NO_SCAN | NO_BLOOD | NO_PAIN | IS_SYNTHETIC diff --git a/code/modules/mob/living/damage_procs.dm b/code/modules/mob/living/damage_procs.dm index f216a74c..4a9b7f4d 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 c149f0f3..bd96d66d 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -18,7 +18,7 @@ //This proc is used for mobs which are affected by pressure to calculate the amount of pressure that actually //affects them once clothing is factored in. ~Errorage /mob/living/proc/calculate_affecting_pressure(var/pressure) - return 0 + return //sort of a legacy burn method for /electrocute, /shock, and the e_chair @@ -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 @@ -311,6 +302,7 @@ dead_mob_list -= src living_mob_list += src tod = null + timeofdeath = 0 // restore us to conciousness stat = CONSCIOUS @@ -483,7 +475,7 @@ H << "\red You begin doggedly resisting the parasite's control (this will take approximately sixty seconds)." B.host << "\red You feel the captive mind of [src] begin to resist your control." - spawn(rand(350,450)+B.host.brainloss) + spawn(rand(400,500)+B.host.brainloss) if(!B || !B.controlling) return @@ -491,14 +483,8 @@ B.host.adjustBrainLoss(rand(5,10)) H << "\red With an immense exertion of will, you regain control of your body!" B.host << "\red You feel control of the host brain ripped from your grasp, and retract your probosci before the wild neural impulses can damage you." - B.controlling = 0 - B.ckey = B.host.ckey - B.host.ckey = H.ckey - - H.ckey = null - H.name = "host brain" - H.real_name = "host brain" + B.detatch() verbs -= /mob/living/carbon/proc/release_control verbs -= /mob/living/carbon/proc/punish_host @@ -624,7 +610,7 @@ BD.attack_hand(usr) C.open() - //breaking out of handcuffs and stop, drop, and roll + //breaking out of handcuffs else if(iscarbon(L)) var/mob/living/carbon/CM = L if(CM.on_fire && CM.canmove) diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index f124fd37..6115dc37 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -32,11 +32,15 @@ return 0 +//if null is passed for def_zone, then this should return something appropriate for all zones (e.g. area effect damage) /mob/living/proc/getarmor(var/def_zone, var/type) return 0 /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 @@ -44,8 +48,7 @@ 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)) @@ -53,60 +56,105 @@ signaler.signal() var/absorb = run_armor_check(def_zone, P.flag) + var/proj_sharp = is_sharp(P) + var/proj_edge = has_edge(P) + if ((proj_sharp || proj_edge) && prob(getarmor(def_zone, P.flag))) + proj_sharp = 0 + proj_edge = 0 + if(!P.nodamage) - apply_damage(P.damage, P.damage_type, def_zone, absorb, 0, P, sharp=is_sharp(P), edge=has_edge(P)) + apply_damage(P.damage, P.damage_type, def_zone, absorb, 0, P, sharp=proj_sharp, edge=proj_edge) 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/)) var/obj/O = AM - var/zone = ran_zone("chest",75)//Hits a random part of the body, geared towards the chest var/dtype = BRUTE if(istype(O,/obj/item/weapon)) var/obj/item/weapon/W = O dtype = W.damtype - src.visible_message("\red [src] has been hit by [O].") - var/armor = run_armor_check(zone, "melee", "Your armor has protected your [zone].", "Your armor has softened hit to your [zone].") - if(armor < 2) - apply_damage(O.throwforce*(speed/5), dtype, zone, armor, O, sharp=is_sharp(O), edge=has_edge(O)) + var/throw_damage = O.throwforce*(speed/5) - if(!O.fingerprintslast) + 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 - var/client/assailant = directory[ckey(O.fingerprintslast)] - if(assailant && assailant.mob && istype(assailant.mob,/mob)) - var/mob/M = assailant.mob + src.visible_message("\red [src] has been hit by [O].") + var/armor = run_armor_check(null, "melee") - src.attack_log += text("\[[time_stamp()]\] Has been hit with a thrown [O], last touched by [M.name] ([assailant.ckey])") - M.attack_log += text("\[[time_stamp()]\] Hit [src.name] ([src.ckey]) with a thrown [O]") - if(!istype(src,/mob/living/simple_animal/mouse)) - msg_admin_attack("[src.name] ([src.ckey]) was hit by a thrown [O], last touched by [M.name] ([assailant.ckey]) (JMP)") + if(armor < 2) + apply_damage(throw_damage, dtype, null, armor, is_sharp(O), has_edge(O), O) - // Begin BS12 momentum-transfer code. + O.throwing = 0 //it hit, so stop moving - if(speed >= 20) - var/obj/item/weapon/W = O - var/momentum = speed/2 - var/dir = get_dir(M,src) + if(ismob(O.thrower)) + var/mob/M = O.thrower + var/client/assailant = M.client + if(assailant) + src.attack_log += text("\[[time_stamp()]\] Has been hit with a [O], thrown by [M.name] ([assailant.ckey])") + M.attack_log += text("\[[time_stamp()]\] Hit [src.name] ([src.ckey]) with a thrown [O]") + 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)") - visible_message("\red [src] staggers under the impact!","\red You stagger under the impact!") - src.throw_at(get_edge_target_turf(src,dir),1,momentum) + // Begin BS12 momentum-transfer code. + if(O.throw_source && speed >= 15) + var/obj/item/weapon/W = O + var/momentum = speed/2 + var/dir = get_dir(O.throw_source, src) - if(istype(W.loc,/mob/living) && W.sharp) //Projectile is embedded and suitable for pinning. + visible_message("\red [src] staggers under the impact!","\red You stagger under the impact!") + src.throw_at(get_edge_target_turf(src,dir),1,momentum) - if(!istype(src,/mob/living/carbon/human)) //Handles embedding for non-humans and simple_animals. - O.loc = src - src.embedded += O + if(!W || !src) return - var/turf/T = near_wall(dir,2) + if(W.sharp) //Projectile is suitable for pinning. + //Handles embedding for non-humans and simple_animals. + O.loc = src + src.embedded += O - if(T) - src.loc = T - visible_message("[src] is pinned to the wall by [O]!","You are pinned to the wall by [O]!") - src.anchored = 1 - src.pinned += O + var/turf/T = near_wall(dir,2) + if(T) + src.loc = T + 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) + src.take_organ_damage(speed*5) /mob/living/proc/near_wall(var/direction,var/distance=1) var/turf/T = get_step(get_turf(src),direction) @@ -149,7 +197,7 @@ if(!on_fire) return 1 var/datum/gas_mixture/G = loc.return_air() // Check if we're standing in an oxygenless environment - if(G.oxygen < 1) + if(G.oxygen < 1) //Investigate this when it's NOT 2332hrs local, K? - Skull132 ExtinguishMob() //If there's no oxygen in the tile we're on, put out the fire return var/turf/location = get_turf(src) diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index 8a3df150..addc6717 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -1,4 +1,3 @@ -#define SAY_MINIMUM_PRESSURE 10 var/list/department_radio_keys = list( ":r" = "right ear", "#r" = "right ear", ".r" = "right ear", ":l" = "left ear", "#l" = "left ear", ".l" = "left ear", @@ -15,6 +14,7 @@ var/list/department_radio_keys = list( ":t" = "Syndicate", "#t" = "Syndicate", ".t" = "Syndicate", ":u" = "Supply", "#u" = "Supply", ".u" = "Supply", ":g" = "changeling", "#g" = "changeling", ".g" = "changeling", + ":d" = "dronechat", "#d" = "dronechat", ".d" = "dronechat", ":R" = "right ear", "#R" = "right ear", ".R" = "right ear", ":L" = "left ear", "#L" = "left ear", ".L" = "left ear", @@ -31,6 +31,7 @@ var/list/department_radio_keys = list( ":T" = "Syndicate", "#T" = "Syndicate", ".T" = "Syndicate", ":U" = "Supply", "#U" = "Supply", ".U" = "Supply", ":G" = "changeling", "#G" = "changeling", ".G" = "changeling", + ":D" = "dronechat", "#D" = "dronechat", ".D" = "dronechat", //kinda localization -- rastaf0 //same keys as above, but on russian keyboard layout. This file uses cp1251 as encoding. @@ -81,14 +82,49 @@ var/list/department_radio_keys = list( if(!istype(dongle)) return if(dongle.translate_binary) return 1 -/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()) +/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) - var/list/listening = list() - if(T) + //handle nonverbal and sign languages here + if (speaking) + if (speaking.flags & NONVERBAL) + if (prob(30)) + src.custom_emote(1, "[pick(speaking.signlang_verb)].") - var/list/objects = list() + if (speaking.flags & SIGNLANG) + say_signlang(message, pick(speaking.signlang_verb), speaking) + return 1 + + //speaking into radios + if(used_radios.len) + italics = 1 + message_range = 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 + var/datum/gas_mixture/environment = T.return_air() + if(environment) + var/pressure = environment.return_pressure() + 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() @@ -98,11 +134,18 @@ var/list/department_radio_keys = list( listening += M hearturfs += M.locs[1] for(var/obj/O in M.contents) - objects |= O + 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] - objects |= O + listening_obj |= O + for(var/mob/M in player_list) if(M.stat == DEAD && M.client && (M.client.prefs.toggles & CHAT_GHOSTEARS)) @@ -111,76 +154,28 @@ var/list/department_radio_keys = list( if(M.loc && M.locs[1] in hearturfs) listening |= M - for(var/obj/O in objects) - spawn(0) - if(O) //It's possible that it could be deleted in the meantime. - O.hear_talk(src, message, verb, speaking) - var/speech_bubble_test = say_test(message) var/image/speech_bubble = image('icons/mob/talk.dmi',src,"h[speech_bubble_test]") spawn(30) del(speech_bubble) - if(used_radios.len) - for(var/mob/living/M in hearers(5, src)) - if(M != src) - M.show_message("[src] talks into [used_radios.len ? used_radios[1] : "radio"]") - - for(var/mob/M in listening) M << speech_bubble - M.hear_say(message,verb,speaking,alt_name, italics, src) + M.hear_say(message, verb, speaking, alt_name, italics, src, speech_sound, sound_vol) + for(var/obj/O in listening_obj) + 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)) + O.hear_signlang(message, verb, language, src) /obj/effect/speech_bubble var/mob/parent /mob/living/proc/GetVoice() return name - -/mob/living/sign(var/message, var/verb="signs", var/datum/language/speaking = null, var/alt_name="", var/italics=0, var/message_range = world.view) - - var/turf/T = get_turf(src) - - var/list/listening = list() - if(T) - - var/list/objects = list() - var/list/hear = hear(message_range, T) - var/list/hearturfs = list() - - for(var/I in hear) - if(istype(I, /mob/)) - var/mob/M = I - listening += M - hearturfs += M.locs[1] - for(var/obj/O in M.contents) - objects |= O - else if(istype(I, /obj/)) - var/obj/O = I - hearturfs += O.locs[1] - objects |= O - - for(var/mob/M in player_list) - if(M.stat == DEAD && M.client && (M.client.prefs.toggles & CHAT_GHOSTEARS)) - listening |= M - continue - if(M.loc && M.locs[1] in hearturfs) - listening |= M - -// Can object see signing? -// Only if they have cameras. -// Do any objects currently have cameras? -- SoundScopes - -// for(var/obj/O in objects) -// spawn(0) -// if(O) //It's possible that it could be deleted in the meantime. -// O.hear_talk(src, message, verb, speaking) - - for(var/mob/M in listening) - if(istype(M, /mob/new_player)) // for some reason this didn't work like say() this fixes it - continue - M.see_say(message,verb,speaking,alt_name, italics, src) - - log_say("[name]/[key] : [message]") \ No newline at end of file diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 00c72af6..e4b4c5b5 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -1,4 +1,29 @@ +#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_alerts, + /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) @@ -20,16 +45,18 @@ var/list/ai_list = list() density = 1 status_flags = CANSTUN|CANPARALYSE var/list/network = list("SS13") - var/obj/machinery/camera/current = null + var/obj/machinery/camera/camera = null var/list/connected_robots = list() var/aiRestorePowerRoutine = 0 //var/list/laws = 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 @@ -53,6 +80,13 @@ var/list/ai_list = list() var/centcomm_message_cooldown = 0 +/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 @@ -81,21 +115,26 @@ 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, /mob/living/silicon/ai/proc/MessageCentcomm) + add_ai_verbs(src) + //Languages + add_language("Sol Common", 0) + add_language("Sinta'unathi", 0) + add_language("Siik'tajr", 0) + add_language("Skrellian", 0) + add_language("Tradeband", 1) + add_language("Gutter", 0) if(!safety)//Only used by AIize() to successfully spawn an AI. if (!B)//If there is no player/brain inside. @@ -171,7 +210,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) @@ -266,48 +305,70 @@ 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) // hack to display shuttle timer - if(emergency_shuttle.online) + if(emergency_shuttle.online()) var/obj/machinery/computer/communications/C = locate() in machines 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 (!current) + if (!camera) return null - user.reset_view(current) + user.reset_view(camera) return 1 /mob/living/silicon/ai/blob_act() @@ -380,12 +441,12 @@ var/list/ai_list = list() // src << text ("Switching Law [L]'s report status to []", lawcheck[L+1]) checklaws() - //Uncomment this line of code if you are enabling the AI Vocal (VOX) announcements. -/* - if(href_list["say_word"]) - play_vox_word(href_list["say_word"], null, src) - return -*/ + 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"]) @@ -403,7 +464,7 @@ var/list/ai_list = list() if(target && (!istype(target, /mob/living/carbon/human) || html_decode(href_list["trackname"]) == target:get_face_name())) ai_actual_track(target) - else // who forgot this? + else src << "\red System error. Cannot locate [html_decode(href_list["trackname"])]." return @@ -491,10 +552,10 @@ var/list/ai_list = list() updatehealth() /mob/living/silicon/ai/reset_view(atom/A) - if(current) - current.SetLuminosity(0) + if(camera) + camera.SetLuminosity(0) if(istype(A,/obj/machinery/camera)) - current = A + camera = A ..() if(istype(A,/obj/machinery/camera)) if(camera_light_on) A.SetLuminosity(AI_CAMERA_LUMINOSITY) @@ -505,7 +566,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) @@ -558,8 +619,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 @@ -602,10 +662,10 @@ 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", "Sad", "BSOD", "Blank", "Problems?", "Awesome", "Facepalm", "Friend Computer") + + 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 if(istype(M, /obj/machinery/ai_status_display)) @@ -627,6 +687,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") @@ -675,12 +738,15 @@ 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) - if(current) - current.SetLuminosity(0) - current = null + if(camera) + camera.SetLuminosity(0) + camera = null else lightNearbyCamera() @@ -691,23 +757,23 @@ var/list/ai_list = list() /mob/living/silicon/ai/proc/lightNearbyCamera() if(camera_light_on && camera_light_on < world.timeofday) - if(src.current) + if(src.camera) var/obj/machinery/camera/camera = near_range_camera(src.eyeobj) - if(camera && src.current != camera) - src.current.SetLuminosity(0) + if(camera && src.camera != camera) + src.camera.SetLuminosity(0) if(!camera.light_disabled) - src.current = camera - src.current.SetLuminosity(AI_CAMERA_LUMINOSITY) + src.camera = camera + src.camera.SetLuminosity(AI_CAMERA_LUMINOSITY) else - src.current = null + src.camera = null else if(isnull(camera)) - src.current.SetLuminosity(0) - src.current = null + src.camera.SetLuminosity(0) + src.camera = null else var/obj/machinery/camera/camera = near_range_camera(src.eyeobj) if(camera && !camera.light_disabled) - src.current = camera - src.current.SetLuminosity(AI_CAMERA_LUMINOSITY) + src.camera = camera + src.camera.SetLuminosity(AI_CAMERA_LUMINOSITY) camera_light_on = world.timeofday + 1 * 20 // Update the light every 2 seconds. @@ -756,3 +822,30 @@ var/list/ai_list = list() centcomm_message_cooldown = 1 spawn(6000)//10 minute cooldown centcomm_message_cooldown = 0 +/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 d95f9c19..2babb1c3 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 @@ -39,11 +40,11 @@ callshuttle = 0 if(callshuttle == 3) //if all three conditions are met - emergency_shuttle.incall(2) + emergency_shuttle.call_evac() log_game("All the AIs, comm consoles and boards are destroyed. Shuttle called.") message_admins("All the AIs, comm consoles and boards are destroyed. Shuttle called.", 1) message_mods("All the AIs, comm consoles and boards are destroyed. Shuttle called.") - captain_announce("The emergency shuttle has been called. It will arrive in [round(emergency_shuttle.timeleft()/60)] minutes.") +// captain_announce("The emergency shuttle has been called. It will arrive in [round(emergency_shuttle.timeleft()/60)] minutes.") world << sound('sound/AI/shuttlecalled.ogg') if(explosive) diff --git a/code/modules/mob/living/silicon/ai/freelook/cameranet.dm b/code/modules/mob/living/silicon/ai/freelook/cameranet.dm index 802325de..df80ce6f 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 0135e65c..d483a0a1 100644 --- a/code/modules/mob/living/silicon/ai/freelook/eye.dm +++ b/code/modules/mob/living/silicon/ai/freelook/eye.dm @@ -51,10 +51,15 @@ if(ai.client) ai.client.eye = src //Holopad - if(istype(ai.current, /obj/machinery/hologram/holopad)) - var/obj/machinery/hologram/holopad/H = ai.current - H.move_hologram() + 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 @@ -65,7 +70,7 @@ var/sprint = 10 var/cooldown = 0 var/acceleration = 1 - + var/obj/machinery/hologram/holopad/holo = null // Intiliaze the eye by assigning it's "ai" variable to us. Then set it's loc to us. /mob/living/silicon/ai/New() @@ -117,7 +122,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" @@ -125,8 +130,7 @@ /mob/living/silicon/ai/proc/view_core() - - current = null + camera = null cameraFollow = null unset_machine() @@ -143,7 +147,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 7a77516f..f4656526 100644 --- 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 c57985d8..5fe31ac3 100644 --- a/code/modules/mob/living/silicon/ai/life.dm +++ b/code/modules/mob/living/silicon/ai/life.dm @@ -35,6 +35,9 @@ // Gain Power adjustOxyLoss(-1) + // Handle EMP-stun + handle_stunned() + //stage = 1 //if (istype(src, /mob/living/silicon/ai)) // Are we not sure what we are? var/blind = 0 @@ -179,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 e5277aec..c37c5e56 100644 --- a/code/modules/mob/living/silicon/ai/say.dm +++ b/code/modules/mob/living/silicon/ai/say.dm @@ -1,140 +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) - -/mob/living/silicon/say_understands(var/other) - if (istype(other, /mob/living/carbon/human)) - return 1 - if (istype(other, /mob/living/silicon)) - return 1 - if (istype(other, /mob/living/carbon/brain)) - return 1 - return ..() - - - -// These Verbs are commented out since we've disabled the AI vocal (VOX) announcements. -// If you re-enable them there is 3 lines in ai.dm Topic() that you need to uncomment as well. -// just search for VOX in there. - -/* -var/announcing_vox = 0 // Stores the time of the last announcement -var/const/VOX_CHANNEL = 200 -var/const/VOX_DELAY = 100 // 10 seconds -var/const/VOX_PATH = "sound/vox/" - -/mob/living/silicon/ai/verb/announcement_help() - - set name = "Announcement Help" - set desc = "Display a list of vocal words to announce to the crew." - set category = "AI Commands" - - - var/dat = "Here is a list of words you can type into the 'Announcement' button to create sentences to vocally announce to everyone on the same level at you.
    \ -
    • You can also click on the word to preview it.
    • \ -
    • You can only say 30 words for every announcement.
    • \ -
    • Do not use punctuation as you would normally, if you want a pause you can use the full stop and comma characters by separating them with spaces, like so: 'Alpha . Test , Bravo'.
    \ - WARNING:
    Misuse of the announcement system will get you job banned.
    " - - var/index = 0 - var/list/vox_words = flist(VOX_PATH) // flist will return a list of strings with all the files in the path - for(var/word in vox_words) - index++ - var/stripped_word = copytext(word, 1, length(word) - 3) // Remove the .wav - dat += "[capitalize(stripped_word)]" - if(index != vox_words.len) - dat += " / " - - src << browse(dat, "window=announce_help;size=500x400") - - -/mob/living/silicon/ai/verb/announcement() - - set name = "Announcement" - set desc = "Create a vocal announcement by typing in the available words to create a sentence." - set category = "AI Commands" - - if(announcing_vox > world.time) - src << "Please wait [round((announcing_vox - world.time) / 10)] seconds." - return - - var/message = input(src, "WARNING: Misuse of this verb can result in you being job banned. More help is available in 'Announcement Help'", "Announcement", src.last_announcement) as text - - last_announcement = message - - if(!message || announcing_vox > world.time) - return - - var/list/words = text2list(trim(message), " ") - var/list/incorrect_words = list() - - if(words.len > 30) - words.len = 30 - - // Detect incorrect words which aren't .wav files. - for(var/word in words) - word = trim(word) - if(!word) - words -= word - continue - if(!vox_word_exists(word)) - incorrect_words += word - - if(incorrect_words.len) - src << "These words are not available on the announcement system: [english_list(incorrect_words)]." - return - - announcing_vox = world.time + VOX_DELAY - - log_game("[key_name_admin(src)] made a vocal announcement with the following message: [message].") - - for(var/word in words) - play_vox_word(word, src.z, null) - - -/proc/play_vox_word(var/word, var/z_level, var/mob/only_listener) - - word = lowertext(word) - - if(vox_word_exists(word)) - - var/sound_file = get_vox_file(word) - var/sound/voice = sound(sound_file, wait = 1, channel = VOX_CHANNEL) - voice.status = SOUND_STREAM - - // If there is no single listener, broadcast to everyone in the same z level - if(!only_listener) - // Play voice for all mobs in the z level - for(var/mob/M in player_list) - if(M.client) - var/turf/T = get_turf(M) - if(T.z == z_level) - M << voice - else - only_listener << voice - return 1 - return 0 - - -/proc/vox_word_exists(var/word) - return fexists("[VOX_PATH][word].wav") - -/proc/get_vox_file(var/word) - if(vox_word_exists(word)) - return file("[VOX_PATH][word].wav") - -// Dynamically loading it has bad results with sounds overtaking each other, even with the wait variable. -// We send the file to the user when they login. - -/client/proc/preload_vox() - var/list/vox_files = flist(VOX_PATH) - for(var/file in vox_files) - // src << "Downloading [file]" - var/sound/S = sound("[VOX_PATH][file]") - src << browse_rsc(S) - - -*/ + return ..(message) diff --git a/code/modules/mob/living/silicon/login.dm b/code/modules/mob/living/silicon/login.dm index e32be084..0a34871f 100644 --- a/code/modules/mob/living/silicon/login.dm +++ b/code/modules/mob/living/silicon/login.dm @@ -1,4 +1,5 @@ /mob/living/silicon/Login() + sleeping = 0 if(mind && ticker && ticker.mode) ticker.mode.remove_cultist(mind, 1) ticker.mode.remove_revolutionary(mind, 1) diff --git a/code/modules/mob/living/silicon/pai/life.dm b/code/modules/mob/living/silicon/pai/life.dm index 78441fe1..419b6ad0 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 20cb7b6e..8865c770 100644 --- a/code/modules/mob/living/silicon/pai/pai.dm +++ b/code/modules/mob/living/silicon/pai/pai.dm @@ -1,12 +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 - -// Good roleplayers can emote well, why not let them. -// emote_type = 2 // pAIs emotes are heard, not seen, so they can be seen through a container (eg. person) + 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 @@ -17,10 +17,21 @@ 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 @@ -54,8 +65,11 @@ var/obj/item/radio/integrated/signal/sradio // AI's signaller + var/translator_on = 0 // keeps track of the translator module + /mob/living/silicon/pai/New(var/obj/item/device/paicard) + canmove = 0 src.loc = paicard card = paicard @@ -65,6 +79,14 @@ card.radio = new /obj/item/device/radio(src.card) radio = card.radio + //Default languages without universal translator software + add_language("Sol Common", 1) + add_language("Tradeband", 1) + add_language("Gutter", 1) + + verbs += /mob/living/silicon/pai/proc/choose_chassis + verbs += /mob/living/silicon/pai/proc/choose_verbs + //PDA pda = new(src) spawn(5) @@ -110,7 +132,12 @@ return 0 /mob/living/silicon/pai/restrained() - return 0 + if(istype(src.loc,/obj/item/device/paicard)) + return 0 + ..() + +/mob/living/silicon/pai/MouseDrop(atom/over_object) + return /mob/living/silicon/pai/emp_act(severity) // Silence for 2 minutes @@ -228,7 +255,6 @@ src.reset_view(C) return 1 - /mob/living/silicon/pai/cancel_camera() set category = "pAI Commands" set name = "Cancel Camera View" @@ -236,10 +262,6 @@ src.unset_machine() src:cameraFollow = null - -/mob/living/silicon/pai/float(var/on) //No, just no floating for these - SoundScopes - return - //Addition by Mord_Sith to define AI's network change ability /* /mob/living/silicon/pai/proc/pai_network_change() @@ -258,7 +280,7 @@ if(!C.status) continue else - if(C.network != "CREED" && C.network != "thunder" && C.network != "RD" && C.network != "toxins" && C.network != "Prison") COMPILE ERROR! This will have to be updated as camera.network is no longer a string, but a list instead + if(C.network != "CREED" && C.network != "thunder" && C.network != "RD" && C.network != "phoron" && C.network != "Prison") COMPILE ERROR! This will have to be updated as camera.network is no longer a string, but a list instead cameralist[C.network] = C.network src.network = input(usr, "Which network would you like to view?") as null|anything in cameralist @@ -276,3 +298,176 @@ 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/verb/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 + 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/holder = card.loc + holder.drop_from_inventory(card) + else if(istype(card.loc,/obj/item/clothing/suit/space/space_ninja)) + var/obj/item/clothing/suit/space/space_ninja/holder = card.loc + holder.pai = null + else if(istype(card.loc,/obj/item/device/pda)) + var/obj/item/device/pda/holder = card.loc + holder.pai = null + + src.client.perspective = EYE_PERSPECTIVE + src.client.eye = src + src.forceMove(get_turf(card)) + + card.forceMove(src) + card.screen_loc = null + + var/turf/T = get_turf(src) + if(istype(T)) T.visible_message("[src] folds outwards, expanding into a mobile form.") + +/mob/living/silicon/pai/verb/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 + + if(src.loc == card) + return + + 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." + +/mob/living/silicon/pai/examine() + + set src in oview() + + if(!usr || !src) return + if( (usr.sdisabilities & BLIND || usr.blinded || usr.stat) && !istype(usr,/mob/dead/observer) ) + usr << "Something is there but you can't see it." + return + + var/msg = "*---------*\nThis is \icon[src][name], a personal AI!" + + switch(src.stat) + if(CONSCIOUS) + if(!src.client) msg += "\nIt appears to be in stand-by mode." //afk + if(UNCONSCIOUS) msg += "\nIt doesn't seem to be responding." + if(DEAD) msg += "\nIt looks completely unsalvageable." + msg += "\n*---------*" + + if(print_flavor_text()) msg += "\n[print_flavor_text()]\n" + + if (pose) + if( findtext(pose,".",lentext(pose)) == 0 && findtext(pose,"!",lentext(pose)) == 0 && findtext(pose,"?",lentext(pose)) == 0 ) + pose = addtext(pose,".") //Makes sure all emotes end with a period. + msg += "\nIt is [pose]" + + usr << msg \ No newline at end of file diff --git a/code/modules/mob/living/silicon/pai/recruit.dm b/code/modules/mob/living/silicon/pai/recruit.dm index 5b23b7f7..93ae8771 100644 --- a/code/modules/mob/living/silicon/pai/recruit.dm +++ b/code/modules/mob/living/silicon/pai/recruit.dm @@ -19,97 +19,233 @@ var/datum/paiController/paiController // Global handler for pAI candidates /datum/paiController + var/inquirer = null var/list/pai_candidates = list() var/list/asked = list() 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) +/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) - pai_candidates -= candidate - usr << browse(null, "window=findPai") + var/dat = "" + dat += {" + + "} - if(href_list["new"]) - var/datum/paiCandidate/candidate = locate(href_list["candidate"]) - var/option = href_list["option"] - var/t = "" + 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!

    - 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) - - - var/dat = "" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Name:[candidate.name] 
    What you plan to call yourself. Suggestions: Any character name you would choose for a station character OR an AI.
    Description:[candidate.description] 
    What sort of pAI you typically play; your mannerisms, your quirks, etc. This can be as sparse or as detailed as you like.
    Preferred Role:[candidate.role] 
    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.
    OOC Comments:[candidate.comments] 
    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.
    +
    + + + + + + + +
    + Save Personality +
    + Load Personality +

    + "} + 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!

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Name:[candidate.name] 
    What you plan to call yourself. Suggestions: Any character name you would choose for a station character OR an AI.
    Description:[candidate.description] 
    What sort of pAI you typically play; your mannerisms, your quirks, etc. This can be as sparse or as detailed as you like.
    Preferred Role:[candidate.role] 
    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.
    OOC Comments:[candidate.comments] 
    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.
    -
    - - -
    - Save Personality + + + + + + + + + + + + + + + + + +
    Name:[c.name]
    Description:[c.description]
    Preferred Role:[c.role]
    OOC Comments:[c.comments]
    + + - - - - -
    Download [c.name]
    - Load Personality -

    - - -
    Submit Personality

    - - +
    +
    "} - M << browse(dat, "window=paiRecruit;size=580x580;") + dat += {" + + + "} - proc/findPAI(var/obj/item/device/paicard/p, var/mob/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 += {" - - - - - - - - - - - - - - - - - -
    Name:[c.name]
    Description:[c.description]
    Preferred Role:[c.role]
    OOC Comments:[c.comments]
    - - -
    Download [c.name] -
    -
    - "} - - 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) + if(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, "Someone 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) +/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 3ee1b824..bdcf0d31 100644 --- a/code/modules/mob/living/silicon/pai/software.dm +++ b/code/modules/mob/living/silicon/pai/software.dm @@ -266,7 +266,7 @@ src.medHUD = !src.medHUD if("translator") if(href_list["toggle"]) - src.universal_speak = !src.universal_speak + src.translator_toggle() if("doorjack") if(href_list["jack"]) if(src.cable && src.cable.machine) @@ -325,7 +325,7 @@ if(s == "medical HUD") //This file has to be saved as ANSI or this will not display correctly dat += "Medical Analysis Suite [(src.medHUD) ? "" : ""]
    " if(s == "universal translator") //This file has to be saved as ANSI or this will not display correctly - dat += "Universal Translator [(src.universal_speak) ? "" : ""]
    " + dat += "Universal Translator [(src.translator_on) ? "" : ""]
    " if(s == "projection array") dat += "Projection Array
    " if(s == "camera jack") @@ -438,9 +438,9 @@ for (var/ch_name in radio.channels) dat+=radio.text_sec_channel(ch_name, radio.channels[ch_name]) dat+={"[radio.text_wires()]
    "} - + return dat - + // Crew Manifest /mob/living/silicon/pai/proc/softwareManifest() var/dat = "" @@ -497,8 +497,8 @@ // Universal Translator /mob/living/silicon/pai/proc/softwareTranslator() var/dat = {"

    Universal Translator


    - When enabled, this device will automatically convert all spoken and written language into a format that any known recipient can understand.

    - The device is currently [ (src.universal_speak) ? "en" : "dis" ]abled.
    + When enabled, this device will automatically convert all spoken and written languages into a format that any known recipient can understand.

    + The device is currently [ (src.translator_on) ? "en" : "dis" ]abled.
    Toggle Device
    "} return dat @@ -528,17 +528,25 @@ 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"]

    - + Scan Breakdown:
    Respiratory: [M.getOxyLoss() > 50 ? "" : ""][M.getOxyLoss()]
    Toxicology: [M.getToxLoss() > 50 ? "" : ""][M.getToxLoss()]
    @@ -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/plasma_level = environment.toxins/total_moles - var/unknown_level = 1-(o2_level+n2_level+co2_level+plasma_level) - dat += "Nitrogen: [round(n2_level*100)]%
    " - dat += "Oxygen: [round(o2_level*100)]%
    " - dat += "Carbon Dioxide: [round(co2_level*100)]%
    " - dat += "Plasma: [round(plasma_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 @@ -676,7 +675,7 @@ dat += "" dat += "
" dat += "Messages:
" - + dat += "" dat += "" for(var/index in pda.tnote) @@ -686,3 +685,25 @@ dat += addtext("") dat += "
From", index["owner"],": ", index["message"], "
" return dat + +/mob/living/silicon/pai/proc/translator_toggle() + + // Sol Common, Tradeband and Gutter are added with New() and are therefore the current default, always active languages + + if(translator_on) + translator_on = 0 + + remove_language("Sinta'unathi") + remove_language("Siik'tajr") + remove_language("Skrellian") + + src << "\blue Translator Module toggled OFF." + + else + translator_on = 1 + + add_language("Sinta'unathi") + add_language("Siik'tajr") + add_language("Skrellian") + + src << "\blue Translator Module toggled ON." diff --git a/code/modules/mob/living/silicon/robot/component.dm b/code/modules/mob/living/silicon/robot/component.dm index 8c85447f..a28187c5 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 @@ -171,7 +189,7 @@ w_class = 2.0 throw_speed = 5 throw_range = 10 - m_amt = 200 + matter = list("metal" = 200) origin_tech = "magnets=1;biotech=1" var/mode = 1; @@ -188,7 +206,7 @@ if(!(istype(user, /mob/living/carbon/human) || ticker) && ticker.mode.name != "monkey") user << "\red You don't have the dexterity to do this!" return - if(!istype(M, /mob/living/silicon/robot)) + if(!istype(M, /mob/living/silicon/robot) && !(ishuman(M) && (M:species.flags & IS_SYNTHETIC))) user << "\red You can't analyze non-robotic things!" return @@ -200,22 +218,39 @@ user.show_message("\t Damage Specifics: [BU] - [BR]") if(M.tod && M.stat == DEAD) user.show_message("\blue Time of Disable: [M.tod]") - - var/mob/living/silicon/robot/H = M - var/list/damaged = H.get_damaged_components(1,1,1) - user.show_message("\blue Localized Damage:",1) - if(length(damaged)>0) - for(var/datum/robot_component/org in damaged) - user.show_message(text("\blue \t []: [][] - [] - [] - []", \ - capitalize(org.name), \ - (org.installed == -1) ? "DESTROYED " :"",\ - (org.electronics_damage > 0) ? "[org.electronics_damage]" :0, \ - (org.brute_damage > 0) ? "[org.brute_damage]" :0, \ - (org.toggled) ? "Toggled ON" : "Toggled OFF",\ - (org.powered) ? "Power ON" : "Power OFF"),1) - else - user.show_message("\blue \t Components are OK.",1) - if(H.emagged && prob(5)) - user.show_message("\red \t ERROR: INTERNAL SYSTEMS COMPROMISED",1) + + if (istype(M, /mob/living/silicon/robot)) + var/mob/living/silicon/robot/H = M + var/list/damaged = H.get_damaged_components(1,1,1) + user.show_message("\blue Localized Damage:",1) + if(length(damaged)>0) + for(var/datum/robot_component/org in damaged) + user.show_message(text("\blue \t []: [][] - [] - [] - []", \ + capitalize(org.name), \ + (org.installed == -1) ? "DESTROYED " :"",\ + (org.electronics_damage > 0) ? "[org.electronics_damage]" :0, \ + (org.brute_damage > 0) ? "[org.brute_damage]" :0, \ + (org.toggled) ? "Toggled ON" : "Toggled OFF",\ + (org.powered) ? "Power ON" : "Power OFF"),1) + else + user.show_message("\blue \t Components are OK.",1) + if(H.emagged && prob(5)) + user.show_message("\red \t ERROR: INTERNAL SYSTEMS COMPROMISED",1) + + if (ishuman(M) && (M:species.flags & IS_SYNTHETIC)) + var/mob/living/carbon/human/H = M + var/list/damaged = H.get_damaged_organs(1,1) + user.show_message("\blue Localized Damage, Brute/Electronics:",1) + if(length(damaged)>0) + for(var/datum/organ/external/org in damaged) + user.show_message(text("\blue \t []: [] - []", \ + capitalize(org.display_name), \ + (org.brute_dam > 0) ? "\red [org.brute_dam]" :0, \ + (org.burn_dam > 0) ? "[org.burn_dam]" :0),1) + else + user.show_message("\blue \t Components are OK.",1) + + user.show_message("\blue Operating Temperature: [M.bodytemperature-T0C]°C ([M.bodytemperature*1.8-459.67]°F)", 1) + src.add_fingerprint(user) return diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm index 55270907..b4d29907 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone.dm @@ -25,8 +25,7 @@ //Used for self-mailing. var/mail_destination = "" - //Used for pulling. - + holder_type = /obj/item/weapon/holder/drone /mob/living/silicon/robot/drone/New() ..() @@ -62,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)])" @@ -104,7 +110,7 @@ return emote(copytext(message,2)) else if(length(message) >= 2) - if(copytext(message, 1 ,3) == ":d" || copytext(message, 1 ,3) == ":D") + if(parse_message_mode(message, "NONE") == "dronechat") if(!is_component_functioning("radio")) src << "\red Your radio transmitter isn't functional." @@ -132,18 +138,6 @@ else if(M.stat == 2 && M.client.prefs.toggles & CHAT_GHOSTEARS) if(M.client) M << "[src] transmits, \"[message]\"" -//Sick of trying to get this to display properly without redefining it. -/mob/living/silicon/robot/drone/show_system_integrity() - if(!src.stat) - var/temphealth = health+35 //Brings it to 0. - if(temphealth<0) temphealth = 0 - //Convert to percentage. - temphealth = (temphealth / (maxHealth*2)) * 100 - - stat(null, text("System integrity: [temphealth]%")) - else - stat(null, text("Systems nonfunctional")) - //Drones cannot be upgraded with borg modules so we need to catch some items before they get used in ..(). /mob/living/silicon/robot/drone/attackby(obj/item/weapon/W as obj, mob/user as mob) @@ -327,7 +321,13 @@ src << "If a crewmember has noticed you, you are probably breaking your third law." /mob/living/silicon/robot/drone/Bump(atom/movable/AM as mob|obj, yes) - if (!yes || ( !istype(AM,/obj/machinery/door) && !istype(AM,/obj/machinery/recharge_station) && !istype(AM,/obj/machinery/disposal/deliveryChute) ) ) return + if (!yes || ( \ + !istype(AM,/obj/machinery/door) && \ + !istype(AM,/obj/machinery/recharge_station) && \ + !istype(AM,/obj/machinery/disposal/deliveryChute) && \ + !istype(AM,/obj/machinery/teleport/hub) && \ + !istype(AM,/obj/effect/portal) + )) return ..() return diff --git a/code/modules/mob/living/silicon/robot/drone/drone_abilities.dm b/code/modules/mob/living/silicon/robot/drone/drone_abilities.dm index f7482067..2c45af24 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_abilities.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_abilities.dm @@ -33,16 +33,19 @@ layer = MOB_LAYER src << text("\blue You have stopped hiding.") +/mob/living/silicon/robot/drone/verb/light() + set name = "Light On/Off" + set desc = "Activate a low power omnidirectional LED. Toggled on or off." + set category = "Drone" + + if(luminosity) + SetLuminosity(0) + return + SetLuminosity(2) + //Actual picking-up event. /mob/living/silicon/robot/drone/attack_hand(mob/living/carbon/human/M as mob) if(M.a_intent == "help") - var/obj/item/weapon/holder/drone/D = new(loc) - src.loc = D - D.attack_hand(M) - M << "You scoop up [src]." - src << "[M] scoops you up." - M.status_flags |= PASSEMOTES - return - + get_scooped(M) ..() \ No newline at end of file diff --git a/code/modules/mob/living/silicon/robot/drone/drone_items.dm b/code/modules/mob/living/silicon/robot/drone/drone_items.dm index 7c2dc565..18adeccc 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_items.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_items.dm @@ -28,6 +28,19 @@ //Item currently being held. var/obj/item/wrapped = null +/obj/item/weapon/gripper/paperwork + name = "paperwork gripper" + desc = "A simple grasping tool for clerical work." + icon = 'icons/obj/device.dmi' + icon_state = "gripper" + + can_hold = list( + /obj/item/weapon/clipboard, + /obj/item/weapon/paper, + /obj/item/weapon/paper_bundle, + /obj/item/weapon/card/id + ) + /obj/item/weapon/gripper/attack_self(mob/user as mob) if(wrapped) wrapped.attack_self(user) @@ -56,9 +69,9 @@ /obj/item/weapon/gripper/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) return -/obj/item/weapon/gripper/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, flag, params) +/obj/item/weapon/gripper/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, proximity, params) - if(!target || !flag) //Target is invalid or we are not adjacent. + if(!target || !proximity) //Target is invalid or we are not adjacent. return //There's some weirdness with items being lost inside the arm. Trying to fix all cases. ~Z @@ -67,25 +80,22 @@ wrapped = thing break - if(wrapped) //Already have an item. - + if(wrapped) //Already have an item. + + //Temporary put wrapped into user so target's attackby() checks pass. wrapped.loc = user - //Pass the attack on to the target. + + //Pass the attack on to the target. This might delete/relocate wrapped. target.attackby(wrapped,user) - - if(wrapped && src && wrapped.loc == user) + + //If wrapped did neither get deleted nor put into target, put it back into the gripper. + if(wrapped && user && (wrapped.loc == user)) wrapped.loc = src - - //Sanity/item use checks. - - if(!wrapped || !user) - return - - if(wrapped.loc != src.loc) + else wrapped = null return - if(istype(target,/obj/item)) //Check that we're not pocketing a mob. + else if(istype(target,/obj/item)) //Check that we're not pocketing a mob. //...and that the item is not in a container. if(!isturf(target.loc)) @@ -145,9 +155,9 @@ /obj/item/weapon/matter_decompiler/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) return -/obj/item/weapon/matter_decompiler/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, flag, params) +/obj/item/weapon/matter_decompiler/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, proximity, params) - if(!flag) return //Not adjacent. + if(!proximity) return //Not adjacent. //We only want to deal with using this on turfs. Specific items aren't important. var/turf/T = get_turf(target) @@ -344,4 +354,4 @@ stack = stack_plastic stack.amount++ - decompiler.stored_comms[type]--; \ No newline at end of file + decompiler.stored_comms[type]--; 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 3ba05f63..de504bcf 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 8c68e07d..d7161bc6 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/life.dm b/code/modules/mob/living/silicon/robot/life.dm index 252a94aa..3913577e 100644 --- a/code/modules/mob/living/silicon/robot/life.dm +++ b/code/modules/mob/living/silicon/robot/life.dm @@ -133,10 +133,6 @@ else src.blinded = 1 - if(!is_component_functioning("actuator")) - src.Paralyse(3) - - return 1 /mob/living/silicon/robot/proc/handle_regular_hud_updates() @@ -322,7 +318,7 @@ weaponlock_time = 120 /mob/living/silicon/robot/update_canmove() - if(paralysis || stunned || weakened || buckled || lockcharge) canmove = 0 + if(paralysis || stunned || weakened || buckled || lockcharge || !is_component_functioning("actuator")) canmove = 0 else canmove = 1 return canmove 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 00000000..fbcc3bf4 --- /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 16a3b62a..29dcbe23 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -5,12 +5,12 @@ icon_state = "robot" maxHealth = 200 health = 200 - universal_speak = 1 var/sight_mode = 0 var/custom_name = "" var/custom_sprite = 0 //Due to all the sprites involved, a var for our custom borgs may be best var/crisis //Admin-settable for combat module use. + var/crisis_override = 0 //Hud stuff @@ -65,7 +65,6 @@ var/speed = 0 //Cause sec borgs gotta go fast //No they dont! var/scrambledcodes = 0 // Used to determine if a borg shows up on the robotics console. Setting to one hides them. var/braintype = "Cyborg" - var/pose /mob/living/silicon/robot/New(loc,var/syndie = 0,var/unfinished = 0) spark_system = new /datum/effect/effect/system/spark_spread() @@ -89,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) @@ -139,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() @@ -164,8 +160,8 @@ /mob/living/silicon/robot/proc/pick_module() if(module) return - var/list/modules = list("Standard", "Engineering", "Medical", "Miner", "Janitor", "Service", "Security") - if(crisis && security_level == SEC_LEVEL_RED) //Leaving this in until it's balanced appropriately. + var/list/modules = list("Standard", "Engineering", "Construction", "Surgeon", "Crisis", "Miner", "Janitor", "Service", "Clerical", "Security") + if((crisis && security_level == SEC_LEVEL_RED) || crisis_override) //Leaving this in until it's balanced appropriately. src << "\red Crisis mode active. Combat module available." modules+="Combat" modtype = input("Please, select a module!", "Robot", null, null) in modules @@ -190,6 +186,14 @@ module_sprites["Rich"] = "maximillion" module_sprites["Default"] = "Service2" + if("Clerical") + module = new /obj/item/weapon/robot_module/clerical(src) + module_sprites["Waitress"] = "Service" + module_sprites["Kent"] = "toiletbot" + module_sprites["Bro"] = "Brobot" + module_sprites["Rich"] = "maximillion" + module_sprites["Default"] = "Service2" + if("Miner") module = new /obj/item/weapon/robot_module/miner(src) module.channels = list("Supply" = 1) @@ -199,15 +203,26 @@ module_sprites["Advanced Droid"] = "droid-miner" module_sprites["Treadhead"] = "Miner" - if("Medical") - module = new /obj/item/weapon/robot_module/medical(src) + if("Crisis") + module = new /obj/item/weapon/robot_module/crisis(src) module.channels = list("Medical" = 1) if(camera && "Robots" in camera.network) camera.network.Add("Medical") module_sprites["Basic"] = "Medbot" + module_sprites["Standard"] = "surgeon" module_sprites["Advanced Droid"] = "droid-medical" module_sprites["Needles"] = "medicalrobot" + + if("Surgeon") + module = new /obj/item/weapon/robot_module/surgeon(src) + module.channels = list("Medical" = 1) + if(camera && "Robots" in camera.network) + camera.network.Add("Medical") + + module_sprites["Basic"] = "Medbot" module_sprites["Standard"] = "surgeon" + module_sprites["Advanced Droid"] = "droid-medical" + module_sprites["Needles"] = "medicalrobot" if("Security") module = new /obj/item/weapon/robot_module/security(src) @@ -226,6 +241,15 @@ module_sprites["Antique"] = "engineerrobot" module_sprites["Landmate"] = "landmate" + if("Construction") + module = new /obj/item/weapon/robot_module/construction(src) + module.channels = list("Engineering" = 1) + if(camera && "Robots" in camera.network) + camera.network.Add("Engineering") + module_sprites["Basic"] = "Engineering" + module_sprites["Antique"] = "engineerrobot" + module_sprites["Landmate"] = "landmate" + if("Janitor") module = new /obj/item/weapon/robot_module/janitor(src) module_sprites["Basic"] = "JanBot2" @@ -237,6 +261,9 @@ module_sprites["Combat Android"] = "droid-combat" module.channels = list("Security" = 1) + //languages + module.add_languages(src) + //Custom_sprite check and entry if (custom_sprite == 1) module_sprites["Custom"] = "[src.ckey]-[modtype]" @@ -254,10 +281,13 @@ /mob/living/silicon/robot/proc/updatename(var/prefix as text) if(prefix) modtype = prefix - if(istype(mmi, /obj/item/device/mmi/posibrain)) - braintype = "Android" + if(mmi) + if(istype(mmi, /obj/item/device/mmi/posibrain)) + braintype = "Android" + else + braintype = "Cyborg" else - braintype = "Cyborg" + braintype = "Robot" var/changed_name = "" if(custom_name) @@ -315,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() @@ -566,6 +596,10 @@ return if (istype(W, /obj/item/weapon/weldingtool)) + if (src == user) + user << "You lack the reach to be able to repair yourself." + return + if (!getBruteLoss()) user << "Nothing to fix here!" return @@ -870,7 +904,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) @@ -1017,10 +1051,7 @@ else icon_state = base_icon return -/* -/mob/living/silicon/robot/proc/updatefire() - return -*/ + //Call when target overlay should be added/removed /mob/living/silicon/robot/update_targeted() if(!targeted_by && target_locked) @@ -1072,17 +1103,26 @@ /mob/living/silicon/robot/Topic(href, href_list) ..() + if(usr != src) + return + if (href_list["showalerts"]) robot_alerts() return if (href_list["mod"]) var/obj/item/O = locate(href_list["mod"]) - if (O) + if (istype(O) && (O.loc == src)) O.attack_self(src) if (href_list["act"]) var/obj/item/O = locate(href_list["act"]) + if (!istype(O)) + return + + if(!((O in src.module.modules) || (O == src.module.emag))) + return + if(activated(O)) src << "Already activated" return @@ -1228,20 +1268,6 @@ return -/mob/living/silicon/robot/verb/pose() - set name = "Set Pose" - set desc = "Sets a description which will be shown when someone examines you." - set category = "IC" - - pose = copytext(sanitize(input(usr, "This is [src]. It is...", "Pose", null) as text), 1, MAX_MESSAGE_LEN) - -/mob/living/silicon/robot/verb/set_flavor() - set name = "Set Flavour Text" - 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) - /mob/living/silicon/robot/proc/choose_icon(var/triesleft, var/list/module_sprites) if(triesleft<1 || !module_sprites.len) diff --git a/code/modules/mob/living/silicon/robot/robot_damage.dm b/code/modules/mob/living/silicon/robot/robot_damage.dm index 544992cc..15f3fe4f 100644 --- a/code/modules/mob/living/silicon/robot/robot_damage.dm +++ b/code/modules/mob/living/silicon/robot/robot_damage.dm @@ -1,9 +1,9 @@ /mob/living/silicon/robot/updatehealth() if(status_flags & GODMODE) - health = 200 + health = maxHealth stat = CONSCIOUS return - health = 200 - (getBruteLoss() + getFireLoss()) + health = maxHealth - (getBruteLoss() + getFireLoss()) return /mob/living/silicon/robot/getBruteLoss() @@ -145,4 +145,4 @@ brute -= (picked.brute_damage - brute_was) burn -= (picked.electronics_damage - burn_was) - parts -= picked \ No newline at end of file + parts -= picked diff --git a/code/modules/mob/living/silicon/robot/robot_items.dm b/code/modules/mob/living/silicon/robot/robot_items.dm index 1c24a5e7..33748f05 100644 --- a/code/modules/mob/living/silicon/robot/robot_items.dm +++ b/code/modules/mob/living/silicon/robot/robot_items.dm @@ -6,7 +6,9 @@ name = "RoboTray" desc = "An autoloading tray specialized for carrying refreshments." -/obj/item/weapon/tray/robotray/afterattack(atom/target, mob/user as mob) +/obj/item/weapon/tray/robotray/afterattack(atom/target, mob/user as mob, proximity) + if(!proximity) + return if ( !target ) return // pick up items, mostly copied from base tray pickup proc @@ -95,14 +97,26 @@ var/mode = 1 /obj/item/weapon/pen/robopen/attack_self(mob/user as mob) + + var/choice = input("Would you like to change colour or mode?") as null|anything in list("Colour","Mode") + if(!choice) return + playsound(src.loc, 'sound/effects/pop.ogg', 50, 0) - if (mode == 1) - mode = 2 - user << "Changed printing mode to 'Rename Paper'" - return - if (mode == 2) - mode = 1 - user << "Changed printing mode to 'Write Paper'" + + switch(choice) + + if("Colour") + var/newcolour = input("Which colour would you like to use?") as null|anything in list("black","blue","red","green","yellow") + if(newcolour) colour = newcolour + + if("Mode") + if (mode == 1) + mode = 2 + else + mode = 1 + user << "Changed printing mode to '[mode == 2 ? "Rename Paper" : "Write Paper"]'" + + return // Copied over from paper's rename verb // see code\modules\paperwork\paper.dm line 62 @@ -120,6 +134,33 @@ add_fingerprint(user) return +//TODO: Add prewritten forms to dispense when you work out a good way to store the strings. +/obj/item/weapon/form_printer + //name = "paperwork printer" + name = "paper dispenser" + icon = 'icons/obj/bureaucracy.dmi' + icon_state = "paper_bin1" + item_state = "sheet-metal" + +/obj/item/weapon/form_printer/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) + return + +/obj/item/weapon/form_printer/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, flag, params) + + if(!target || !flag) + return + + if(istype(target,/obj/structure/table)) + deploy_paper(get_turf(target)) + +/obj/item/weapon/form_printer/attack_self(mob/user as mob) + deploy_paper(get_turf(src)) + +/obj/item/weapon/form_printer/proc/deploy_paper(var/turf/T) + T.visible_message("\blue \The [src.loc] dispenses a sheet of crisp white paper.") + new /obj/item/weapon/paper(T) + + //Personal shielding for the combat module. /obj/item/borg/combat/shield name = "personal shielding" diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm index 591bb152..2214ae15 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules.dm @@ -9,7 +9,7 @@ var/list/modules = list() var/obj/item/emag = null var/obj/item/borg/upgrade/jetpack = null - + var/list/stacktypes emp_act(severity) if(modules) @@ -32,7 +32,21 @@ /obj/item/weapon/robot_module/proc/respawn_consumable(var/mob/living/silicon/robot/R) - return + + if(!stacktypes || !stacktypes.len) return + + for(var/T in stacktypes) + var/O = locate(T) in src.modules + var/obj/item/stack/S = O + + if(!S) + src.modules -= null + S = new T(src) + src.modules += S + S.amount = 1 + + if(S && S.amount < stacktypes[T]) + S.amount++ /obj/item/weapon/robot_module/proc/rebuild()//Rebuilds the list so it's possible to add/remove items from the module var/list/temp_list = modules @@ -41,6 +55,16 @@ if(O) modules += O +/obj/item/weapon/robot_module/proc/add_languages(var/mob/living/silicon/robot/R) + //full set of languages + R.add_language("Sol Common", 1) + R.add_language("Tradeband", 1) + R.add_language("Sinta'unathi", 0) + R.add_language("Siik'tajr", 0) + R.add_language("Skrellian", 0) + R.add_language("Gutter", 0) + + /obj/item/weapon/robot_module/standard name = "standard robot module" @@ -55,13 +79,50 @@ src.emag = new /obj/item/weapon/melee/energy/sword(src) return -/obj/item/weapon/robot_module/standard/respawn_consumable(var/mob/living/silicon/robot/R) - var/obj/item/weapon/melee/baton/B = locate() in src.modules - if(B.charges < 10) - B.charges += 1 +/obj/item/weapon/robot_module/surgeon + name = "surgeon robot module" + stacktypes = list( + /obj/item/stack/medical/advanced/bruise_pack = 5, + /obj/item/stack/nanopaste = 5 + ) -/obj/item/weapon/robot_module/medical - name = "medical robot module" + New() + src.modules += new /obj/item/device/flashlight(src) + src.modules += new /obj/item/device/flash(src) + src.modules += new /obj/item/device/healthanalyzer(src) + src.modules += new /obj/item/weapon/reagent_containers/borghypo/surgeon(src) + src.modules += new /obj/item/weapon/scalpel(src) + src.modules += new /obj/item/weapon/hemostat(src) + src.modules += new /obj/item/weapon/retractor(src) + src.modules += new /obj/item/weapon/cautery(src) + src.modules += new /obj/item/weapon/bonegel(src) + src.modules += new /obj/item/weapon/FixOVein(src) + src.modules += new /obj/item/weapon/bonesetter(src) + src.modules += new /obj/item/weapon/circular_saw(src) + src.modules += new /obj/item/weapon/surgicaldrill(src) + src.modules += new /obj/item/weapon/extinguisher/mini(src) + src.modules += new /obj/item/stack/medical/advanced/bruise_pack(src) + src.modules += new /obj/item/stack/nanopaste(src) + + src.emag = new /obj/item/weapon/reagent_containers/spray(src) + + src.emag.reagents.add_reagent("pacid", 250) + src.emag.name = "Polyacid spray" + return + +/obj/item/weapon/robot_module/surgeon/respawn_consumable(var/mob/living/silicon/robot/R) + if(src.emag) + var/obj/item/weapon/reagent_containers/spray/PS = src.emag + PS.reagents.add_reagent("pacid", 2) + ..() + +/obj/item/weapon/robot_module/crisis + name = "crisis robot module" + stacktypes = list( + /obj/item/stack/medical/ointment = 5, + /obj/item/stack/medical/bruise_pack = 5, + /obj/item/stack/medical/splint = 5 + ) New() src.modules += new /obj/item/device/flashlight(src) @@ -69,31 +130,69 @@ src.modules += new /obj/item/borg/sight/hud/med(src) src.modules += new /obj/item/device/healthanalyzer(src) src.modules += new /obj/item/device/reagent_scanner/adv(src) - src.modules += new /obj/item/weapon/reagent_containers/borghypo(src) + src.modules += new /obj/item/roller_holder(src) + src.modules += new /obj/item/stack/medical/ointment(src) + src.modules += new /obj/item/stack/medical/bruise_pack(src) + src.modules += new /obj/item/stack/medical/splint(src) + src.modules += new /obj/item/weapon/reagent_containers/borghypo/crisis(src) src.modules += new /obj/item/weapon/reagent_containers/glass/beaker/large(src) src.modules += new /obj/item/weapon/reagent_containers/robodropper(src) src.modules += new /obj/item/weapon/reagent_containers/syringe(src) src.modules += new /obj/item/weapon/extinguisher/mini(src) + src.emag = new /obj/item/weapon/reagent_containers/spray(src) src.emag.reagents.add_reagent("pacid", 250) src.emag.name = "Polyacid spray" return -/obj/item/weapon/robot_module/medical/respawn_consumable(var/mob/living/silicon/robot/R) +/obj/item/weapon/robot_module/crisis/respawn_consumable(var/mob/living/silicon/robot/R) + var/obj/item/weapon/reagent_containers/syringe/S = locate() in src.modules - if(S.mode == 2)//SYRINGE_BROKEN + if(S.mode == 2) S.reagents.clear_reagents() S.mode = initial(S.mode) S.desc = initial(S.desc) S.update_icon() + if(src.emag) var/obj/item/weapon/reagent_containers/spray/PS = src.emag PS.reagents.add_reagent("pacid", 2) + ..() + +/obj/item/weapon/robot_module/construction + name = "construction robot module" + + stacktypes = list( + /obj/item/stack/sheet/metal = 50, + /obj/item/stack/sheet/plasteel = 10, + /obj/item/stack/sheet/rglass = 50, + /obj/item/stack/rods = 50 + ) + + New() + src.modules += new /obj/item/device/flashlight(src) + src.modules += new /obj/item/device/flash(src) + src.modules += new /obj/item/borg/sight/meson(src) + src.modules += new /obj/item/weapon/extinguisher(src) + src.modules += new /obj/item/weapon/rcd/borg(src) + src.modules += new /obj/item/weapon/screwdriver(src) + src.modules += new /obj/item/weapon/wrench(src) + src.modules += new /obj/item/weapon/crowbar(src) + src.modules += new /obj/item/weapon/pickaxe/plasmacutter(src) + /obj/item/weapon/robot_module/engineering name = "engineering robot module" + stacktypes = list( + /obj/item/stack/sheet/metal = 50, + /obj/item/stack/sheet/glass = 50, + /obj/item/stack/sheet/rglass = 50, + /obj/item/weapon/cable_coil = 50, + /obj/item/stack/rods = 15, + /obj/item/stack/tile/plasteel = 15 + ) New() src.modules += new /obj/item/device/flashlight(src) @@ -117,7 +216,11 @@ M.amount = 50 src.modules += M - var/obj/item/stack/sheet/rglass/cyborg/G = new /obj/item/stack/sheet/rglass/cyborg(src) + var/obj/item/stack/sheet/rglass/cyborg/R = new /obj/item/stack/sheet/rglass/cyborg(src) + R.amount = 50 + src.modules += R + + var/obj/item/stack/sheet/glass/G = new /obj/item/stack/sheet/glass(src) G.amount = 50 src.modules += G @@ -127,24 +230,6 @@ return -/obj/item/weapon/robot_module/engineering/respawn_consumable(var/mob/living/silicon/robot/R) - var/list/stacks = list ( - /obj/item/stack/sheet/metal, - /obj/item/stack/sheet/rglass, - /obj/item/weapon/cable_coil, - ) - for(var/T in stacks) - var/O = locate(T) in src.modules - if(O) - if(O:amount < 50) - O:amount++ - else - src.modules -= null - O = new T(src) - src.modules += O - O:amount = 1 - return - /obj/item/weapon/robot_module/security name = "security robot module" @@ -173,9 +258,6 @@ T.update_icon() else T.charge_tick = 0 - var/obj/item/weapon/melee/baton/B = locate() in src.modules - if(B.charges < 10) - B.charges += 1 /obj/item/weapon/robot_module/janitor name = "janitorial robot module" @@ -211,7 +293,7 @@ src.modules += new /obj/item/weapon/pen/robopen(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) @@ -231,6 +313,39 @@ src.emag.name = "Mickey Finn's Special Brew" return + add_languages(var/mob/living/silicon/robot/R) + //full set of languages + R.add_language("Sol Common", 1) + R.add_language("Sinta'unathi", 1) + R.add_language("Siik'maas", 1) + R.add_language("Siik'tajr", 0) + R.add_language("Skrellian", 1) + R.add_language("Rootspeak", 1) + R.add_language("Tradeband", 1) + R.add_language("Gutter", 1) + +/obj/item/weapon/robot_module/clerical + name = "clerical robot module" + + New() + src.modules += new /obj/item/device/flashlight(src) + src.modules += new /obj/item/device/flash(src) + src.modules += new /obj/item/weapon/pen/robopen(src) + src.modules += new /obj/item/weapon/form_printer(src) + src.modules += new /obj/item/weapon/gripper/paperwork(src) + + src.emag = new /obj/item/weapon/stamp/denied(src) + + add_languages(var/mob/living/silicon/robot/R) + R.add_language("Sol Common", 1) + R.add_language("Sinta'unathi", 1) + R.add_language("Siik'maas", 1) + R.add_language("Siik'tajr", 0) + R.add_language("Skrellian", 1) + R.add_language("Rootspeak", 1) + R.add_language("Tradeband", 1) + R.add_language("Gutter", 1) + /obj/item/weapon/robot_module/butler/respawn_consumable(var/mob/living/silicon/robot/R) var/obj/item/weapon/reagent_containers/food/condiment/enzyme/E = locate() in src.modules E.reagents.add_reagent("enzyme", 2) @@ -245,11 +360,12 @@ src.modules += new /obj/item/device/flashlight(src) src.modules += new /obj/item/device/flash(src) src.modules += new /obj/item/borg/sight/meson(src) - src.emag = new /obj/item/borg/stun(src) + src.modules += new /obj/item/weapon/wrench(src) + src.modules += new /obj/item/weapon/screwdriver(src) src.modules += new /obj/item/weapon/storage/bag/ore(src) src.modules += new /obj/item/weapon/pickaxe/borgdrill(src) src.modules += new /obj/item/weapon/storage/bag/sheetsnatcher/borg(src) -// src.modules += new /obj/item/weapon/shovel(src) Uneeded due to buffed drill + src.emag = new /obj/item/weapon/pickaxe/plasmacutter(src) return /obj/item/weapon/robot_module/syndicate @@ -281,20 +397,19 @@ /obj/item/weapon/robot_module/drone name = "drone module" - var/list/stacktypes = list( - /obj/item/stack/sheet/wood/cyborg = 1, - /obj/item/stack/sheet/mineral/plastic/cyborg = 1, - /obj/item/stack/sheet/rglass/cyborg = 5, + stacktypes = list( + /obj/item/stack/sheet/wood = 1, + /obj/item/stack/sheet/mineral/plastic = 1, + /obj/item/stack/sheet/rglass = 5, /obj/item/stack/tile/wood = 5, /obj/item/stack/rods = 15, /obj/item/stack/tile/plasteel = 15, - /obj/item/stack/sheet/metal/cyborg = 20, - /obj/item/stack/sheet/glass/cyborg = 20, + /obj/item/stack/sheet/metal = 20, + /obj/item/stack/sheet/glass = 20, /obj/item/weapon/cable_coil = 30 ) New() - src.modules += new /obj/item/device/flashlight/drone(src) src.modules += new /obj/item/weapon/weldingtool(src) src.modules += new /obj/item/weapon/screwdriver(src) src.modules += new /obj/item/weapon/wrench(src) @@ -306,8 +421,8 @@ src.modules += new /obj/item/weapon/matter_decompiler(src) src.modules += new /obj/item/weapon/reagent_containers/spray/cleaner/drone(src) - src.emag = new /obj/item/weapon/card/emag(src) - src.emag.name = "Cryptographic Sequencer" + src.emag = new /obj/item/weapon/pickaxe/plasmacutter(src) + src.emag.name = "Plasma Cutter" for(var/T in stacktypes) var/obj/item/stack/sheet/W = new T(src) @@ -316,24 +431,24 @@ return + add_languages(var/mob/living/silicon/robot/R) + return //not much ROM to spare in that tiny microprocessor! + /obj/item/weapon/robot_module/drone/respawn_consumable(var/mob/living/silicon/robot/R) var/obj/item/weapon/reagent_containers/spray/cleaner/C = locate() in src.modules C.reagents.add_reagent("cleaner", 3) - for(var/T in stacktypes) - var/O = locate(T) in src.modules - var/obj/item/stack/sheet/S = O - - if(!S) - src.modules -= null - S = new T(src) - src.modules += S - S.amount = 1 - - if(S && S.amount < stacktypes[T]) - S.amount++ - var/obj/item/device/lightreplacer/LR = locate() in src.modules LR.Charge(R) - return \ No newline at end of file + ..() + return + +//checks whether this item is a module of the robot it is located in. +/obj/item/proc/is_robot_module() + if (!istype(src.loc, /mob/living/silicon/robot)) + return 0 + + var/mob/living/silicon/robot/R = src.loc + + return (src in R.module.modules) diff --git a/code/modules/mob/living/silicon/robot/robot_movement.dm b/code/modules/mob/living/silicon/robot/robot_movement.dm index 3dbf98d1..b9133aca 100644 --- a/code/modules/mob/living/silicon/robot/robot_movement.dm +++ b/code/modules/mob/living/silicon/robot/robot_movement.dm @@ -1,3 +1,8 @@ +/mob/living/silicon/robot/Process_Spaceslipping(var/prob_slip) + if(module && (istype(module,/obj/item/weapon/robot_module/construction) || istype(module,/obj/item/weapon/robot_module/drone))) + return 0 + ..(prob_slip) + /mob/living/silicon/robot/Process_Spacemove() if(module) for(var/obj/item/weapon/tank/jetpack/J in module.modules) diff --git a/code/modules/mob/living/silicon/robot/robot_upgrades.dm b/code/modules/mob/living/silicon/robot/robot_upgrades.dm index e512a6cc..1dfb6216 100644 --- a/code/modules/mob/living/silicon/robot/robot_upgrades.dm +++ b/code/modules/mob/living/silicon/robot/robot_upgrades.dm @@ -34,6 +34,8 @@ R.base_icon = "robot" R.icon_state = "robot" R.updateicon() + R.languages = list() + R.speech_synthesizer_langs = list() return 1 diff --git a/code/modules/mob/living/silicon/robot/wires.dm b/code/modules/mob/living/silicon/robot/wires.dm index 71a93c0e..51d416c7 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 ef80c631..c57dbdcd 100644 --- a/code/modules/mob/living/silicon/say.dm +++ b/code/modules/mob/living/silicon/say.dm @@ -2,16 +2,27 @@ 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 #define IS_PAI 3 +/mob/living/silicon/say_understands(var/other,var/datum/language/speaking = null) + //These only pertain to common. Languages are handled by mob/say_understands() + if (!speaking) + if (istype(other, /mob/living/carbon)) + return 1 + if (istype(other, /mob/living/silicon)) + return 1 + if (istype(other, /mob/living/carbon/brain)) + return 1 + return ..() + /mob/living/silicon/say(var/message) if (!message) return @@ -49,39 +60,39 @@ return var/verb = say_quote(message) - var/message_mode = null + + //parse radio key and consume it + var/message_mode = parse_message_mode(message, "general") + if (message_mode) + if (message_mode == "general") + message = trim(copytext(message,2)) + else + message = trim(copytext(message,3)) + + if(message_mode && bot_type == IS_ROBOT && message_mode != "binary" && !R.is_component_functioning("radio")) + src << "\red Your radio isn't functional at this time." + return - if(copytext(message,1,2) == ";") - message_mode = "general" // I don't know why but regular radio = fuck you we ain't broadcasting, pAI mode was what was in old say code. - message = trim(copytext(message,2)) + //parse language key and consume it + var/datum/language/speaking = parse_language(message) + if (speaking) + verb = speaking.speech_verb + message = copytext(message,3) - else if(length(message) >= 2) - var/channel_prefix = copytext(message, 1 ,3) - if(!message_mode) - message_mode = department_radio_keys[channel_prefix] - - if(message_mode && bot_type == IS_ROBOT) - if(message_mode != "binary" && !R.is_component_functioning("radio")) - src << "\red Your radio isn't functional at this time." - return - - - if(message_mode && message_mode != "general") - message = trim(copytext(message,3)) switch(message_mode) 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) + 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) - return + P.radio.talk_into(src,message,message_mode,verb,speaking) + return 1 if("binary") switch(bot_type) @@ -94,35 +105,43 @@ 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) + 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) - return + P.radio.talk_into(src,message,null,verb,speaking) + 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) + 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) - return + P.radio.talk_into(src,message,message_mode,verb,speaking) + return 1 - - return ..(message,null,verb) + return ..(message,speaking,verb) //For holopads only. Usable by AI. /mob/living/silicon/ai/proc/holopad_talk(var/message) @@ -134,8 +153,8 @@ if (!message) return - var/obj/machinery/hologram/holopad/T = src.current - if(istype(T) && T.hologram && T.master == src)//If there is a hologram and its master is the user. + var/obj/machinery/hologram/holopad/T = src.holo + if(T && T.hologram && T.master == src)//If there is a hologram and its master is the user. var/verb = say_quote(message) //Human-like, sorta, heard by those who understand humans. @@ -155,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 c30ab104..575465ac 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -6,6 +6,14 @@ var/datum/ai_laws/laws = null//Now... THEY ALL CAN ALL HAVE LAWS immune_to_ssd = 1 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/pose //Yes, now AIs can pose too. + var/obj/item/device/camera/siliconcam/aiCamera = null //photography /mob/living/silicon/proc/show_laws() return @@ -26,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 @@ -79,7 +106,7 @@ // this function shows the health of the pAI in the Status panel /mob/living/silicon/proc/show_system_integrity() if(!src.stat) - stat(null, text("System integrity: [(src.health+100)/2]%")) + stat(null, text("System integrity: [round((health/maxHealth)*100)]%")) else stat(null, text("Systems nonfunctional")) @@ -96,10 +123,10 @@ // this function displays the shuttles ETA in the status panel if the shuttle has been called /mob/living/silicon/proc/show_emergency_shuttle_eta() - if(emergency_shuttle.online && emergency_shuttle.location < 2) - var/timeleft = emergency_shuttle.timeleft() - if (timeleft) - stat(null, "ETA-[(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]") + if(emergency_shuttle) + var/eta_status = emergency_shuttle.get_status_panel_eta() + if(eta_status) + stat(null, eta_status) // This adds the basic clock, shuttle recall timer, and malf_ai info to all silicon lifeforms @@ -127,3 +154,47 @@ if(error_msg) user << "The armoured plating is too tough." return 0 + + +//Silicon mob language procs + +/mob/living/silicon/can_speak(datum/language/speaking) + return universal_speak || (speaking in src.speech_synthesizer_langs) //need speech synthesizer support to vocalize a language + +/mob/living/silicon/add_language(var/language, var/can_speak=1) + if (..(language) && can_speak) + speech_synthesizer_langs.Add(all_languages[language]) + +/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 + +/mob/living/silicon/check_languages() + set name = "Check Known Languages" + set category = "IC" + set src = usr + + var/dat = "Known Languages

" + + for(var/datum/language/L in languages) + dat += "[L.name] (:[L.key])
Speech Synthesizer: [(L in speech_synthesizer_langs)? "YES":"NOT SUPPORTED"]
[L.desc]

" + + src << browse(dat, "window=checklanguage") + return + +/mob/living/silicon/verb/pose() + set name = "Set Pose" + set desc = "Sets a description which will be shown when someone examines you." + set category = "IC" + + pose = copytext(sanitize(input(usr, "This is [src]. It is...", "Pose", null) as text), 1, MAX_MESSAGE_LEN) + +/mob/living/silicon/verb/set_flavor() + set name = "Set Flavour Text" + 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) \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/borer.dm b/code/modules/mob/living/simple_animal/borer.dm index b78c037b..f377a846 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) @@ -12,6 +15,14 @@ return if(istype(src.loc,/mob/living/simple_animal/borer)) + + message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) + if (!message) + return + log_say("[key_name(src)] : [message]") + if (stat == 2) + return say_dead(message) + var/mob/living/simple_animal/borer/B = src.loc src << "You whisper silently, \"[message]\"" B.host << "The captive mind of [src] whispers, \"[message]\"" @@ -31,7 +42,7 @@ desc = "A small, quivering sluglike creature." speak_emote = list("chirrups") emote_hear = list("chirrups") - response_help = "pokes the" + response_help = "pokes" response_disarm = "prods the" response_harm = "stomps on the" icon_state = "brainslug" @@ -45,6 +56,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 +66,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() @@ -95,10 +113,7 @@ /mob/living/simple_animal/borer/New() ..() 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) @@ -128,6 +143,7 @@ return borer_speak(message) if(!host) + //TODO: have this pick a random mob within 3 tiles to speak for the borer. src << "You have no host to speak to." return //No host, no audible speech. @@ -140,16 +156,14 @@ else if(M.stat == 2 && M.client.prefs.toggles & CHAT_GHOSTEARS) M << "[src.truename] whispers to [host], \"[message]\"" - /mob/living/simple_animal/borer/Stat() ..() statpanel("Status") if(emergency_shuttle) - if(emergency_shuttle.online && emergency_shuttle.location < 2) - var/timeleft = emergency_shuttle.timeleft() - if (timeleft) - stat(null, "ETA-[(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]") + var/eta_status = emergency_shuttle.get_status_panel_eta() + if(eta_status) + stat(null, eta_status) if (client.statpanel == "Status") stat("Chemicals", chemicals) @@ -166,10 +180,10 @@ /mob/living/simple_animal/borer/verb/dominate_victim() set category = "Alien" - set name = "Dominate Victim" + set name = "Paralyze Victim" set desc = "Freeze the limbs of a potential host with supernatural fear." - if(world.time - used_dominate < 300) + if(world.time - used_dominate < 150) src << "You cannot use that ability again so soon." return @@ -186,7 +200,7 @@ if(C.stat != 2) choices += C - if(world.time - used_dominate < 300) + if(world.time - used_dominate < 150) src << "You cannot use that ability again so soon." return @@ -200,7 +214,7 @@ src << "\red You focus your psychic lance on [M] and freeze their limbs with a wave of terrible dread." M << "\red You feel a creeping, horrible sense of dread come over you, freezing your limbs and setting your heart racing." - M.Weaken(3) + M.Weaken(10) used_dominate = world.time @@ -217,17 +231,13 @@ src << "You cannot do that in your current state." return - if(!host.internal_organs_by_name["brain"]) //this should only run in admin-weirdness situations, but it's here non the less - RR - src << "There is no brain here for us to command!" - return - if(docile) src << "\blue You are feeling far too docile to do that." return src << "You begin delicately adjusting your connection to the host brain..." - spawn(300+(host.brainloss*5)) + spawn(100+(host.brainloss*5)) if(!host || !src || controlling) return @@ -235,14 +245,47 @@ src << "\red You plunge your probosci deep into the cortex of the host brain, interfacing directly with their nervous system." host << "\red You feel a strange shifting sensation behind your eyes as an alien consciousness displaces yours." + // host -> brain + var/h2b_id = host.computer_id + var/h2b_ip= host.lastKnownIP + host.computer_id = null + host.lastKnownIP = null + + del(host_brain) + host_brain = new(src) + host_brain.ckey = host.ckey + + host_brain.name = host.name + + if(!host_brain.computer_id) + host_brain.computer_id = h2b_id + + if(!host_brain.lastKnownIP) + host_brain.lastKnownIP = h2b_ip + + // self -> host + var/s2h_id = src.computer_id + var/s2h_ip= src.lastKnownIP + src.computer_id = null + src.lastKnownIP = null + host.ckey = src.ckey + + if(!host.computer_id) + host.computer_id = s2h_id + + if(!host.lastKnownIP) + host.lastKnownIP = s2h_ip + controlling = 1 host.verbs += /mob/living/carbon/proc/release_control host.verbs += /mob/living/carbon/proc/punish_host host.verbs += /mob/living/carbon/proc/spawn_larvae + return + /mob/living/simple_animal/borer/verb/secrete_chemicals() set category = "Alien" set name = "Secrete Chemicals" @@ -268,7 +311,7 @@ return src << "\red You squirt a measure of [chem] from your reservoirs into [host]'s bloodstream." - host.reagents.add_reagent(chem, 15) + host.reagents.add_reagent(chem, 10) chemicals -= 50 /mob/living/simple_animal/borer/verb/release_host() @@ -294,12 +337,12 @@ if(!host.stat) host << "An odd, uncomfortable pressure begins to build inside your skull, behind your ear..." - spawn(200) + spawn(100) if(!host || !src) return if(src.stat) - src << "You cannot infest a target in your current state." + src << "You cannot release your host in your current state." return src << "You wiggle out of [host]'s ear and plop to the ground." @@ -307,43 +350,74 @@ host << "Something slimy wiggles out of your ear and plops to the ground!" detatch() + leave_host() -mob/living/simple_animal/borer/proc/detatch() +/mob/living/simple_animal/borer/proc/detatch() - if(!host) return + if(!host || !controlling) return if(istype(host,/mob/living/carbon/human)) var/mob/living/carbon/human/H = host var/datum/organ/external/head = H.get_organ("head") head.implants -= src - src.loc = get_turf(host) controlling = 0 + host.verbs -= /mob/living/carbon/proc/release_control + host.verbs -= /mob/living/carbon/proc/punish_host + host.verbs -= /mob/living/carbon/proc/spawn_larvae + + if(host_brain) + + // these are here so bans and multikey warnings are not triggered on the wrong people when ckey is changed. + // computer_id and IP are not updated magically on their own in offline mobs -walter0o + + // host -> self + var/h2s_id = host.computer_id + var/h2s_ip= host.lastKnownIP + host.computer_id = null + host.lastKnownIP = null + + src.ckey = host.ckey + + if(!src.computer_id) + src.computer_id = h2s_id + + if(!host_brain.lastKnownIP) + src.lastKnownIP = h2s_ip + + // brain -> host + var/b2h_id = host_brain.computer_id + var/b2h_ip= host_brain.lastKnownIP + host_brain.computer_id = null + host_brain.lastKnownIP = null + + host.ckey = host_brain.ckey + + if(!host.computer_id) + host.computer_id = b2h_id + + if(!host.lastKnownIP) + host.lastKnownIP = b2h_ip + + del(host_brain) + +/mob/living/simple_animal/borer/proc/leave_host() + + if(!host) return + + src.loc = get_turf(host) + reset_view(null) machine = null host.reset_view(null) host.machine = null - host.verbs -= /mob/living/carbon/proc/release_control - host.verbs -= /mob/living/carbon/proc/punish_host - host.verbs -= /mob/living/carbon/proc/spawn_larvae - - if(host_brain.ckey) - src.ckey = host.ckey - host.ckey = host_brain.ckey - host_brain.ckey = null - host_brain.name = "host brain" - host_brain.real_name = "host brain" - var/mob/living/H = host - host = null - - for(var/atom/A in H.contents) - if(istype(A,/mob/living/simple_animal/borer) || istype(A,/obj/item/weapon/holder)) - return H.status_flags &= ~PASSEMOTES + host = null + return /mob/living/simple_animal/borer/verb/infest() set category = "Alien" @@ -382,7 +456,7 @@ mob/living/simple_animal/borer/proc/detatch() M << "Something slimy begins probing at the opening of your ear canal..." src << "You slither up [M] and begin probing at their ear canal..." - if(!do_after(src,50)) + if(!do_after(src,30)) src << "As [M] moves away, you are dislodged and fall to the ground." return @@ -409,8 +483,6 @@ mob/living/simple_animal/borer/proc/detatch() var/datum/organ/external/head = H.get_organ("head") head.implants += src - host_brain.name = M.name - host_brain.real_name = M.real_name host.status_flags |= PASSEMOTES return @@ -477,7 +549,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 @@ -485,7 +557,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") @@ -496,7 +568,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 @@ -504,4 +576,5 @@ mob/living/simple_animal/borer/proc/transfer_personality(var/client/candidate) src.mind = candidate.mob.mind src.ckey = candidate.ckey if(src.mind) - src.mind.assigned_role = "Cortical Borer" \ No newline at end of file + src.mind.assigned_role = "Cortical Borer" + src.mind.special_role = "Cortical Borer" diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm index 808f2d41..988e2b98 100644 --- a/code/modules/mob/living/simple_animal/friendly/cat.dm +++ b/code/modules/mob/living/simple_animal/friendly/cat.dm @@ -21,6 +21,7 @@ min_oxy = 16 //Require atleast 16kPA oxygen minbodytemp = 223 //Below -50 Degrees Celcius maxbodytemp = 323 //Above 50 Degrees Celcius + holder_type = /obj/item/weapon/holder/cat /mob/living/simple_animal/cat/Life() //MICE! @@ -60,6 +61,18 @@ stop_automated_movement = 1 walk_to(src,movement_target,0,3) + +/mob/living/simple_animal/cat/MouseDrop(atom/over_object) + + var/mob/living/carbon/H = over_object + if(!istype(H)) return ..() + + if(H.a_intent == "help") + get_scooped(H) + return + else + return ..() + //RUNTIME IS ALIVE! SQUEEEEEEEE~ /mob/living/simple_animal/cat/Runtime name = "Runtime" diff --git a/code/modules/mob/living/simple_animal/friendly/mouse.dm b/code/modules/mob/living/simple_animal/friendly/mouse.dm index 31cda60c..963bc372 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/friendly/spiderbot.dm b/code/modules/mob/living/simple_animal/friendly/spiderbot.dm index 007872c3..1c489f49 100644 --- a/code/modules/mob/living/simple_animal/friendly/spiderbot.dm +++ b/code/modules/mob/living/simple_animal/friendly/spiderbot.dm @@ -318,7 +318,7 @@ var/list/items = list() for(var/obj/item/I in view(1,src)) - if(I.loc != src && I.w_class <= 2) + if(I.loc != src && I.w_class <= 2 && I.Adjacent(src) ) items.Add(I) var/obj/selection = input("Select an item.", "Pickup") in items diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm index ccb88874..c712c443 100644 --- a/code/modules/mob/living/simple_animal/hostile/hostile.dm +++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm @@ -195,6 +195,10 @@ /mob/living/simple_animal/hostile/proc/DestroySurroundings() if(prob(break_stuff_probability)) for(var/dir in cardinal) // North, South, East, West + for(var/obj/structure/window/obstacle in get_step(src, dir)) + if(obstacle.dir == reverse_dir[dir]) // So that windows get smashed in the right order + obstacle.attack_animal(src) + return var/obj/structure/obstacle = locate(/obj/structure, get_step(src, dir)) if(istype(obstacle, /obj/structure/window) || istype(obstacle, /obj/structure/closet) || istype(obstacle, /obj/structure/table) || istype(obstacle, /obj/structure/grille)) obstacle.attack_animal(src) diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 9643a4e9..47e35c34 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) @@ -142,47 +139,41 @@ var/atmos_suitable = 1 var/atom/A = src.loc - if(isturf(A)) + + if(istype(A,/turf)) var/turf/T = A - var/areatemp = T.temperature - if( abs(areatemp - bodytemperature) > 40 ) - var/diff = areatemp - bodytemperature - diff = diff / 5 - //world << "changed from [bodytemperature] by [diff] to [bodytemperature + diff]" - bodytemperature += diff - if(istype(T,/turf/simulated)) - var/turf/simulated/ST = T - if(ST.air) - var/tox = ST.air.toxins - var/oxy = ST.air.oxygen - var/n2 = ST.air.nitrogen - var/co2 = ST.air.carbon_dioxide + var/datum/gas_mixture/Environment = T.return_air() - if(min_oxy) - if(oxy < min_oxy) - atmos_suitable = 0 - if(max_oxy) - if(oxy > max_oxy) - atmos_suitable = 0 - if(min_tox) - if(tox < min_tox) - atmos_suitable = 0 - if(max_tox) - if(tox > max_tox) - atmos_suitable = 0 - if(min_n2) - if(n2 < min_n2) - atmos_suitable = 0 - if(max_n2) - if(n2 > max_n2) - atmos_suitable = 0 - if(min_co2) - if(co2 < min_co2) - atmos_suitable = 0 - if(max_co2) - if(co2 > max_co2) - atmos_suitable = 0 + if(Environment) + + if( abs(Environment.temperature - bodytemperature) > 40 ) + bodytemperature += ((Environment.temperature - bodytemperature) / 5) + + if(min_oxy) + if(Environment.gas["oxygen"] < min_oxy) + atmos_suitable = 0 + if(max_oxy) + if(Environment.gas["oxygen"] > max_oxy) + atmos_suitable = 0 + if(min_tox) + if(Environment.gas["toxins"] < min_tox) + atmos_suitable = 0 + if(max_tox) + if(Environment.gas["toxins"] > max_tox) + atmos_suitable = 0 + if(min_n2) + if(Environment.gas["nitrogen"] < min_n2) + atmos_suitable = 0 + if(max_n2) + if(Environment.gas["nitrogen"] > max_n2) + atmos_suitable = 0 + if(min_co2) + if(Environment.gas["carbon_dioxide"] < min_co2) + atmos_suitable = 0 + if(max_co2) + if(Environment.gas["carbon_dioxide"] > max_co2) + atmos_suitable = 0 //Atmos effect if(bodytemperature < minbodytemp) @@ -235,7 +226,9 @@ adjustBruteLoss(damage) /mob/living/simple_animal/bullet_act(var/obj/item/projectile/Proj) - if(!Proj) return + if(!Proj || Proj.nodamage) + return + adjustBruteLoss(Proj.damage) return 0 @@ -341,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 4ec81a05..2f3da9ce 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -16,7 +16,7 @@ /mob/proc/generate_name() return name -/mob/proc/Cell() +/*/mob/proc/Cell() set category = "Admin" set hidden = 1 @@ -34,7 +34,7 @@ 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 @@ -607,6 +607,20 @@ var/list/slot_equipment_priority = list( \ /mob/proc/is_active() return (0 >= usr.stat) +/mob/proc/is_dead() + return stat == DEAD + +/mob/proc/is_mechanical() + if(mind && (mind.assigned_role == "Cyborg" || mind.assigned_role == "AI")) + return 1 + return istype(src, /mob/living/silicon) || get_species() == "Machine" + +/mob/proc/is_ready() + return client && !!mind + +/mob/proc/get_gender() + return gender + /mob/proc/see(message) if(!is_active()) return 0 @@ -716,7 +730,7 @@ note dizziness decrements automatically in the mob's Life() proc. stat(null,"Delta Countdown-[delta_level.timeleft()]") 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) @@ -750,24 +764,37 @@ note dizziness decrements automatically in the mob's Life() proc. //Updates canmove, lying and icons. Could perhaps do with a rename but I can't think of anything to describe it. /mob/proc/update_canmove() - if(buckled) +/* if(istype(buckled, /obj/vehicle)) + var/obj/vehicle/V = buckled + if(stat || weakened || paralysis || resting || sleeping || (status_flags & FAKEDEATH)) + lying = 1 + canmove = 0 + pixel_y = V.mob_offset_y - 5 + else + lying = 0 + canmove = 1 + pixel_y = V.mob_offset_y*/ + 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 canmove = 0 lying = 0 - else + else if (!buckled) lying = !can_stand canmove = has_limbs @@ -790,36 +817,34 @@ note dizziness decrements automatically in the mob's Life() proc. return canmove -/mob/verb/eastface() - set hidden = 1 +/mob/proc/facedir(var/ndir) if(!canface()) return 0 - dir = EAST + dir = ndir + if(buckled && buckled.movable) + buckled.dir = ndir + buckled.handle_rotation() client.move_delay += movement_delay() return 1 +/mob/verb/eastface() + set hidden = 1 + return facedir(EAST) + + /mob/verb/westface() set hidden = 1 - if(!canface()) return 0 - dir = WEST - client.move_delay += movement_delay() - return 1 + return facedir(WEST) /mob/verb/northface() set hidden = 1 - if(!canface()) return 0 - dir = NORTH - client.move_delay += movement_delay() - return 1 + return facedir(NORTH) /mob/verb/southface() set hidden = 1 - if(!canface()) return 0 - dir = SOUTH - client.move_delay += movement_delay() - return 1 + return facedir(SOUTH) /mob/proc/IsAdvancedToolUser()//This might need a rename but it should replace the can this mob use things check @@ -937,7 +962,7 @@ mob/proc/yank_out_object() if(S == U) self = 1 // Removing object from yourself. - valid_objects = get_visible_implants(1) + valid_objects = get_visible_implants(0) if(!valid_objects.len) if(self) src << "You have nothing stuck in your body that is large enough to remove." @@ -948,9 +973,9 @@ mob/proc/yank_out_object() var/obj/item/weapon/selection = input("What do you want to yank out?", "Embedded objects") in valid_objects if(self) - src << "You attempt to get a good grip on the [selection] in your body." + src << "You attempt to get a good grip on [selection] in your body." else - U << "You attempt to get a good grip on the [selection] in [S]'s body." + U << "You attempt to get a good grip on [selection] in [S]'s body." if(!do_after(U, 80)) return @@ -965,8 +990,7 @@ mob/proc/yank_out_object() if(valid_objects.len == 1) //Yanking out last object - removing verb. src.verbs -= /mob/proc/yank_out_object - if(istype(src,/mob/living/carbon/human)) - + if(ishuman(src)) var/mob/living/carbon/human/H = src var/datum/organ/external/affected @@ -976,14 +1000,18 @@ mob/proc/yank_out_object() affected = organ affected.implants -= selection - H.shock_stage+=10 - H.bloody_hands(S) + H.shock_stage+=20 + affected.take_damage((selection.w_class * 3), 0, 0, 1, "Embedded object extraction") - if(prob(10)) //I'M SO ANEMIC I COULD JUST -DIE-. - var/datum/wound/internal_bleeding/I = new (15) + if(prob(selection.w_class * 5)) //I'M SO ANEMIC I COULD JUST -DIE-. + 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) + selection.loc = get_turf(src) for(var/obj/item/weapon/O in pinned) @@ -992,3 +1020,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 0777b4df..b32795c9 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,35 +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 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 @@ -173,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 @@ -329,17 +330,25 @@ It's fairly easy to fix if dealing with single letters but not so much with comp /proc/shake_camera(mob/M, duration, strength=1) - if(!M || !M.client || M.shakecamera) + if(!M || !M.client || M.shakecamera) return + M.shakecamera = 1 spawn(1) - var/oldeye=M.client.eye + + var/atom/oldeye=M.client.eye + var/aiEyeFlag = 0 + if(istype(oldeye, /mob/aiEye)) + aiEyeFlag = 1 + var/x - M.shakecamera = 1 for(x=0; x= 60) - speech_verb = "gibbers" - - return "[speech_verb], \"[text]\"" /mob/proc/emote(var/act, var/type, var/message) if(act == "me") @@ -151,11 +138,12 @@ return "2" return "0" -//parses the message mode code (e.g. :h, ;) from text, such as that supplied to say. +//parses the message mode code (e.g. :h, :w) from text, such as that supplied to say. //returns the message mode string or null for no message mode. -/mob/proc/parse_message_mode(var/message) +//standard mode is the mode returned for the special ';' radio code. +/mob/proc/parse_message_mode(var/message, var/standard_mode="headset") if(length(message) >= 1 && copytext(message,1,2) == ";") - return "headset" + return standard_mode if(length(message) >= 2) var/channel_prefix = copytext(message, 1 ,3) @@ -164,12 +152,12 @@ return null //parses the language code (e.g. :j) from text, such as that supplied to say. -//returns the language object only if the code corresponds to a language that src knows, otherwise null. +//returns the language object only if the code corresponds to a language that src can speak, otherwise null. /mob/proc/parse_language(var/message) if(length(message) >= 2) var/language_prefix = lowertext(copytext(message, 1 ,3)) - for(var/datum/language/L in src.languages) - if(language_prefix == ":[L.key]") - return L - return null + var/datum/language/L = language_keys[language_prefix] + if (can_speak(L)) + return L + return null diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index 810a7f9c..055e7afb 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) @@ -179,7 +172,10 @@ else O.mmi = new /obj/item/device/mmi(O) - if(O.mmi) O.mmi.transfer_identity(src) //Does not transfer key/client. + if(O.mmi) + O.mmi.transfer_identity(src) + + callHook("borgify", list(O)) O.Namepick() @@ -243,11 +239,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 2331debd..50e2b706 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 ae938a9a..f3396ecd 100644 --- a/code/modules/nano/nanomanager.dm +++ b/code/modules/nano/nanomanager.dm @@ -5,13 +5,30 @@ var/open_uis[0] // a list of current open /nanoui UIs, not grouped, for use in processing var/list/processing_uis = list() + // a list of asset filenames which are to be sent to the client on user logon + var/list/asset_files = list() /** * Create a new nanomanager instance. + * This proc generates a list of assets which are to be sent to each client on connect * * @return /nanomanager new nanomanager object */ /datum/nanomanager/New() + var/list/nano_asset_dirs = list(\ + "nano/css/",\ + "nano/images/",\ + "nano/js/",\ + "nano/templates/"\ + ) + + var/list/filenames = null + for (var/path in nano_asset_dirs) + filenames = flist(path) + for(var/filename in filenames) + if(copytext(filename, length(filename)) != "/") // filenames which end in "/" are actually directories, which we want to ignore + asset_files.Add(file(path + filename)) // add this file to asset_files for sending to clients when they connect + return /** @@ -22,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 /** @@ -49,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 /** @@ -66,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 @@ -111,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) @@ -119,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 /** @@ -140,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 @@ -159,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 @@ -172,6 +204,7 @@ // /datum/nanomanager/proc/user_logout(var/mob/user) + //testing("nanomanager/user_logout user [user.name]") return close_user_uis(user) /** @@ -184,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)) @@ -208,17 +243,6 @@ */ /datum/nanomanager/proc/send_resources(client) - var/list/nano_asset_dirs = list(\ - "nano/css/",\ - "nano/images/",\ - "nano/js/",\ - "nano/templates/"\ - ) - - var/list/files = null - for (var/path in nano_asset_dirs) - files = flist(path) - for(var/file in files) - if(copytext(file, length(file)) != "/") // files which end in "/" are actually directories, which we want to ignore - client << browse_rsc(file(path + file)) // send the file to the client + for(var/file in asset_files) + client << browse_rsc(file) // send the file to the client diff --git a/code/modules/nano/nanomapgen.dm b/code/modules/nano/nanomapgen.dm new file mode 100644 index 00000000..dd65b7bb --- /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 1a3257ed..b4d20155 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 e9f1563c..3a691d9d 100644 --- a/code/modules/organs/blood.dm +++ b/code/modules/organs/blood.dm @@ -63,7 +63,7 @@ var/const/BLOOD_VOLUME_SURVIVE = 122 // Damaged heart virtually reduces the blood volume, as the blood isn't // being pumped properly anymore. - var/datum/organ/internal/heart/heart = internal_organs["heart"] + var/datum/organ/internal/heart/heart = internal_organs_by_name["heart"] if(heart.damage > 1 && heart.damage < heart.min_bruised_damage) blood_volume *= 0.8 @@ -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.dm b/code/modules/organs/organ.dm index 701f6cf7..f0c28d87 100644 --- a/code/modules/organs/organ.dm +++ b/code/modules/organs/organ.dm @@ -5,6 +5,9 @@ var/list/datum/autopsy_data/autopsy_data = list() var/list/trace_chemicals = list() // traces of chemicals in the organ, // links chemical IDs to number of ticks for which they'll stay in the blood + + var/germ_level = 0 // INTERNAL germs inside the organ, this is BAD if it's greater than INFECTION_LEVEL_ONE + proc/process() return 0 @@ -14,6 +17,20 @@ /datum/organ/proc/get_icon(var/icon/race_icon, var/icon/deform_icon) return icon('icons/mob/human.dmi',"blank") +//Germs +/datum/organ/proc/handle_antibiotics() + var/antibiotics = owner.reagents.get_reagent_amount("spaceacillin") + + if (!germ_level || antibiotics < 5) + return + + if (germ_level < INFECTION_LEVEL_ONE) + germ_level = 0 //cure instantly + else if (germ_level < INFECTION_LEVEL_TWO) + germ_level -= 6 //at germ_level == 500, this should cure the infection in a minute + else + germ_level -= 2 //at germ_level == 1000, this will cure the infection in 5 minutes + //Handles chem traces /mob/living/carbon/human/proc/handle_trace_chems() //New are added for reagents to random organs. @@ -46,13 +63,18 @@ if(damage_this_tick > last_dam) force_process = 1 last_dam = damage_this_tick - if(!force_process && !bad_external_organs.len) - return if(force_process) bad_external_organs.Cut() for(var/datum/organ/external/Ex in organs) bad_external_organs += Ex + //processing internal organs is pretty cheap, do that first. + for(var/datum/organ/internal/I in internal_organs) + I.process() + + if(!force_process && !bad_external_organs.len) + return + for(var/datum/organ/external/E in bad_external_organs) if(!E) continue @@ -62,63 +84,22 @@ else E.process() number_wounds += E.number_wounds - //Robotic limb malfunctions - var/malfunction = 0 - if (E.status & ORGAN_ROBOT && prob(E.brute_dam + E.burn_dam)) - malfunction = 1 - - //Broken limbs hurt too - var/broken = 0 - if(E.status & ORGAN_BROKEN && !(E.status & ORGAN_SPLINTED) ) - broken = 1 + if (!lying && world.time - l_move_time < 15) //Moving around with fractured ribs won't do you any good - if (broken && E.internal_organs && prob(15)) - if (!lying && world.timeofday - l_move_time < 15) + if (E.is_broken() && E.internal_organs && prob(15)) var/datum/organ/internal/I = pick(E.internal_organs) custom_pain("You feel broken bones moving in your [E.display_name]!", 1) I.take_damage(rand(3,5)) - //Special effects for limbs. - if(E.name in list("l_hand","l_arm","r_hand","r_arm")) - var/obj/item/c_hand //Getting what's in this hand - var/hand - if(E.name == "l_hand" || E.name == "l_arm") - c_hand = l_hand - hand = "left hand" - if(E.name == "r_hand" || E.name == "r_arm") - c_hand = r_hand - hand = "right hand" - if (c_hand) + //Moving makes open wounds get infected much faster + if (E.wounds.len) + for(var/datum/wound/W in E.wounds) + if (W.infection_check()) + W.germ_level += 1 - if(broken) - if(istype(c_hand, /obj/item/weapon/gun/offhand)) - var/obj/item/weapon/gun/O = get_inactive_hand() - var/obj/item/weapon/gun/offhand/OH = c_hand - O.unwield() - OH.unwield() - else - u_equip(c_hand) - var/emote_scream = pick("screams in pain and", "let's out a sharp hiss and", "cries out and") - emote("me", 1, "[(species && species.flags & NO_PAIN) ? "" : emote_scream ] drops what they were holding in their [hand]!") - if(malfunction) - if(istype(c_hand, /obj/item/weapon/gun/offhand)) - var/obj/item/weapon/gun/O = get_inactive_hand() - var/obj/item/weapon/gun/offhand/OH = c_hand - O.unwield() - OH.unwield() - else - u_equip(c_hand) - emote("me", 1, "drops what they were holding, their [hand] malfunctioning!") - var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() - spark_system.set_up(5, 0, src) - spark_system.attach(src) - spark_system.start() - spawn(10) - del(spark_system) - - else if(E.name in list("l_leg","l_foot","r_leg","r_foot") && !lying) - if (!E.is_usable() || malfunction || (broken && !(E.status & ORGAN_SPLINTED))) + if(E.name in list("l_leg","l_foot","r_leg","r_foot") && !lying) + if (!E.is_usable() || E.is_malfunctioning() || (E.is_broken() && !(E.status & ORGAN_SPLINTED))) leg_tally-- // let it fail even if just foot&leg // standing is poor diff --git a/code/modules/organs/organ_external.dm b/code/modules/organs/organ_external.dm index 58751c0a..79bbe771 100644 --- a/code/modules/organs/organ_external.dm +++ b/code/modules/organs/organ_external.dm @@ -32,6 +32,7 @@ var/damage_msg = "\red You feel an intense pain" var/broken_description + var/vital //Lose a vital limb, die immediately. var/status = 0 var/open = 0 var/stage = 0 @@ -40,8 +41,6 @@ var/obj/item/hidden = null var/list/implants = list() - // INTERNAL germs inside the organ, this is BAD if it's greater 0 - var/germ_level = 0 // how often wounds should be updated, a higher number means less often var/wound_update_accuracy = 1 @@ -92,15 +91,8 @@ brute *= brmod //~2/3 damage for ROBOLIMBS burn *= bumod //~2/3 damage for ROBOLIMBS - //If limb took enough damage, try to cut or tear it off - if(body_part != UPPER_TORSO && body_part != LOWER_TORSO) //as hilarious as it is, getting hit on the chest too much shouldn't effectively gib you. - if(config.limbs_can_break && brute_dam >= max_damage * config.organ_health_multiplier) - if( (edge && prob(5 * brute)) || (brute > 20 && prob(2 * brute)) ) - droplimb(1) - return - // High brute damage or sharp objects may damage internal organs - if(internal_organs != null) if( (sharp && brute >= 5) || brute >= 10) if(prob(5)) + if(internal_organs && ( (sharp && brute >= 5) || brute >= 10) && prob(5)) // Damage an internal organ var/datum/organ/internal/I = pick(internal_organs) I.take_damage(brute / 2) @@ -163,6 +155,14 @@ // sync the organ's damage with its wounds src.update_damages() + + //If limb took enough damage, try to cut or tear it off + if(body_part != UPPER_TORSO && body_part != LOWER_TORSO) //as hilarious as it is, getting hit on the chest too much shouldn't effectively gib you. + if(config.limbs_can_break && brute_dam >= max_damage * config.organ_health_multiplier) + if( (edge && prob(5 * brute)) || (brute > 20 && prob(2 * brute)) ) + droplimb(1) + return + owner.updatehealth() var/result = update_icon() @@ -194,18 +194,6 @@ var/result = update_icon() return result - -/datum/organ/external/proc/embed(var/obj/item/weapon/W, var/silent = 0) - if(!silent) - owner.visible_message("\The [W] sticks in the wound!") - implants += W - owner.embedded_flag = 1 - owner.verbs += /mob/proc/yank_out_object - W.add_blood(owner) - if(ismob(W.loc)) - var/mob/living/H = W.loc - H.drop_item() - W.loc = owner /* This function completely restores a damaged organ to perfect condition. */ @@ -218,6 +206,9 @@ This function completely restores a damaged organ to perfect condition. perma_injury = 0 brute_dam = 0 burn_dam = 0 + germ_level = 0 + wounds.Cut() + number_wounds = 0 // handle internal organs for(var/datum/organ/internal/current_organ in internal_organs) @@ -235,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) @@ -277,25 +268,6 @@ This function completely restores a damaged organ to perfect condition. if(W) wounds += W -/datum/organ/external/proc/get_wound_type(var/type = CUT, var/damage) - //if you look a the names in the wound's stages list for each wound type you will see the logic behind these values - switch(type) - if(CUT) - if (damage <= 5) return /datum/wound/cut/small - if (damage <= 15) return /datum/wound/cut/deep - if (damage <= 25) return /datum/wound/cut/flesh - if (damage <= 50) return /datum/wound/cut/gaping - if (damage <= 60) return /datum/wound/cut/gaping_big - return /datum/wound/cut/massive - if(BRUISE) - return /datum/wound/bruise - if(BURN) - if (damage <= 5) return /datum/wound/burn/moderate - if (damage <= 15) return /datum/wound/burn/large - if (damage <= 30) return /datum/wound/burn/severe - if (damage <= 40) return /datum/wound/burn/deep - return /datum/wound/burn/carbonised - /**************************************************** PROCESSING & UPDATING ****************************************************/ @@ -303,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) @@ -310,21 +284,13 @@ This function completely restores a damaged organ to perfect condition. if(last_dam != brute_dam + burn_dam) // Process when we are fully healed up. last_dam = brute_dam + burn_dam return 1 - last_dam = brute_dam + burn_dam + else + last_dam = brute_dam + burn_dam + if(germ_level) + return 1 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) @@ -336,56 +302,134 @@ 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() if(!(status & ORGAN_BROKEN)) perma_injury = 0 + //Infections update_germs() - return //Updating germ levels. Handles organ germ levels and necrosis. -#define GANGREN_LEVEL_ONE 100 -#define GANGREN_LEVEL_TWO 1000 -#define GANGREN_LEVEL_TERMINAL 2500 -#define GERM_TRANSFER_AMOUNT germ_level/500 +/* +The INFECTION_LEVEL values defined in setup.dm control the time it takes to reach the different +infection levels. Since infection growth is exponential, you can adjust the time it takes to get +from one germ_level to another using the rough formula: + +desired_germ_level = initial_germ_level*e^(desired_time_in_seconds/1000) + +So if I wanted it to take an average of 15 minutes to get from level one (100) to level two +I would set INFECTION_LEVEL_TWO to 100*e^(15*60/1000) = 245. Note that this is the average time, +the actual time is dependent on RNG. + +INFECTION_LEVEL_ONE below this germ level nothing happens, and the infection doesn't grow +INFECTION_LEVEL_TWO above this germ level the infection will start to spread to internal and adjacent organs +INFECTION_LEVEL_THREE above this germ level the player will take additional toxin damage per second, and will die in minutes without + antitox. also, above this germ level you will need to overdose on spaceacillin to reduce the germ_level. + +Note that amputating the affected organ does in fact remove the infection from the player's body. +*/ /datum/organ/external/proc/update_germs() - if(status & ORGAN_ROBOT|ORGAN_DESTROYED) //Robotic limbs shouldn't be infected, nor should nonexistant limbs. + if(status & (ORGAN_ROBOT|ORGAN_DESTROYED) || (owner.species && owner.species.flags & IS_PLANT)) //Robotic limbs shouldn't be infected, nor should nonexistant limbs. germ_level = 0 return - if(germ_level > 0 && owner.bodytemperature >= 170) //cryo stops germs from moving and doing their bad stuffs - //Syncing germ levels with external wounds + if(owner.bodytemperature >= 170) //cryo stops germs from moving and doing their bad stuffs + //** Syncing germ levels with external wounds + handle_germ_sync() + + //** Handle antibiotics and curing infections + handle_antibiotics() + + //** Handle the effects of infections + handle_germ_effects() + +/datum/organ/external/proc/handle_germ_sync() + var/antibiotics = owner.reagents.get_reagent_amount("spaceacillin") + for(var/datum/wound/W in wounds) + //Open wounds can become infected + if (owner.germ_level > W.germ_level && W.infection_check()) + W.germ_level++ + + if (antibiotics < 5) for(var/datum/wound/W in wounds) - if(!W.bandaged && !W.salved) - W.germ_level = max(W.germ_level, germ_level) //Wounds get all the germs - if (W.germ_level > germ_level) //Badly infected wounds raise internal germ levels - germ_level++ + //Infected wounds raise the organ's germ level + if (W.germ_level > germ_level) + germ_level++ + break //limit increase to a maximum of one per second - if(germ_level > GANGREN_LEVEL_ONE && prob(round(germ_level/100))) - germ_level++ - owner.adjustToxLoss(1) +/datum/organ/external/proc/handle_germ_effects() + var/antibiotics = owner.reagents.get_reagent_amount("spaceacillin") - if(germ_level > GANGREN_LEVEL_TWO) - germ_level++ - owner.adjustToxLoss(1) -/* - if(germ_level > GANGREN_LEVEL_TERMINAL) - if (!(status & ORGAN_DEAD)) - status |= ORGAN_DEAD - owner << "You can't feel your [display_name] anymore..." - owner.update_body(1) - if (prob(10)) //Spreading the fun - if (children) //To child organs - for (var/datum/organ/external/child in children) - if (!(child.status & (ORGAN_DEAD|ORGAN_DESTROYED|ORGAN_ROBOT))) - child.germ_level += round(GERM_TRANSFER_AMOUNT) - if (parent) - if (!(parent.status & (ORGAN_DEAD|ORGAN_DESTROYED|ORGAN_ROBOT))) - parent.germ_level += round(GERM_TRANSFER_AMOUNT) -*/ + if (germ_level > 0 && germ_level < INFECTION_LEVEL_ONE && prob(60)) //this could be an else clause, but it looks cleaner this way + germ_level-- //since germ_level increases at a rate of 1 per second with dirty wounds, prob(60) should give us about 5 minutes before level one. + + if(germ_level >= INFECTION_LEVEL_ONE) + //having an infection raises your body temperature + var/fever_temperature = (owner.species.heat_level_1 - owner.species.body_temperature - 5)* min(germ_level/INFECTION_LEVEL_TWO, 1) + owner.species.body_temperature + //need to make sure we raise temperature fast enough to get around environmental cooling preventing us from reaching fever_temperature + owner.bodytemperature += between(0, (fever_temperature - T20C)/BODYTEMP_COLD_DIVISOR + 1, fever_temperature - owner.bodytemperature) + + if(prob(round(germ_level/10))) + if (antibiotics < 5) + germ_level++ + + if (prob(10)) //adjust this to tweak how fast people take toxin damage from infections + owner.adjustToxLoss(1) + + if(germ_level >= INFECTION_LEVEL_TWO && antibiotics < 5) + //spread the infection to internal organs + var/datum/organ/internal/target_organ = null //make internal organs become infected one at a time instead of all at once + for (var/datum/organ/internal/I in internal_organs) + if (I.germ_level > 0 && I.germ_level < min(germ_level, INFECTION_LEVEL_TWO)) //once the organ reaches whatever we can give it, or level two, switch to a different one + if (!target_organ || I.germ_level > target_organ.germ_level) //choose the organ with the highest germ_level + target_organ = I + + if (!target_organ) + //figure out which organs we can spread germs to and pick one at random + var/list/candidate_organs = list() + for (var/datum/organ/internal/I in internal_organs) + if (I.germ_level < germ_level) + candidate_organs += I + if (candidate_organs.len) + target_organ = pick(candidate_organs) + + if (target_organ) + target_organ.germ_level++ + + //spread the infection to child and parent organs + if (children) + for (var/datum/organ/external/child in children) + if (child.germ_level < germ_level && !(child.status & ORGAN_ROBOT)) + if (child.germ_level < INFECTION_LEVEL_ONE*2 || prob(30)) + child.germ_level++ + + if (parent) + if (parent.germ_level < germ_level && !(parent.status & ORGAN_ROBOT)) + if (parent.germ_level < INFECTION_LEVEL_ONE*2 || prob(30)) + parent.germ_level++ + + if(germ_level >= INFECTION_LEVEL_THREE && antibiotics < 30) //overdosing is necessary to stop severe infections + if (!(status & ORGAN_DEAD)) + status |= ORGAN_DEAD + owner << "You can't feel your [display_name] anymore..." + owner.update_body(1) + + germ_level++ + owner.adjustToxLoss(1) //Updating wounds. Handles wound natural I had some free spachealing, internal bleedings and infections /datum/organ/external/proc/update_wounds() @@ -401,16 +445,15 @@ This function completely restores a damaged organ to perfect condition. // let the GC handle the deletion of the wound // Internal wounds get worse over time. Low temperatures (cryo) stop them. - if(W.internal && !W.is_treated() && 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) @@ -418,7 +461,7 @@ This function completely restores a damaged organ to perfect condition. var/heal_amt = 0 // if damage >= 50 AFTER treatment then it's probably too severe to heal within the timeframe of a round. - if (W.is_treated() && W.wound_damage() < 50) + if (W.can_autoheal() && W.wound_damage() < 50) heal_amt += 0.5 //we only update wounds once in [wound_update_accuracy] ticks so have to emulate realtime @@ -433,8 +476,8 @@ This function completely restores a damaged organ to perfect condition. // Salving also helps against infection if(W.germ_level > 0 && W.salved && prob(2)) - W.germ_level = 0 W.disinfected = 1 + W.germ_level = 0 // sync the organ's damage with its wounds src.update_damages() @@ -525,13 +568,27 @@ This function completely restores a damaged organ to perfect condition. 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) @@ -581,33 +638,37 @@ This function completely restores a damaged organ to perfect condition. if(!(status & ORGAN_ROBOT)) organ = new /obj/item/weapon/organ/l_foot(owner.loc, owner) owner.u_equip(owner.shoes) + + destspawn = 1 + //Robotic limbs explode if sabotaged. + if(status & ORGAN_ROBOT && !no_explode && sabotaged) + owner.visible_message("\red \The [owner]'s [display_name] explodes violently!",\ + "\red Your [display_name] explodes!",\ + "You hear an explosion followed by a scream!") + explosion(get_turf(owner),-1,-1,2,3) + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, owner) + spark_system.attach(owner) + spark_system.start() + spawn(10) + del(spark_system) + + owner.visible_message("\red [owner.name]'s [display_name] flies off in an arc.",\ + "Your [display_name] goes flying off!",\ + "You hear a terrible sound of ripping tendons and flesh.") + if(organ) - destspawn = 1 - //Robotic limbs explode if sabotaged. - if(status & ORGAN_ROBOT && !no_explode && sabotaged) - owner.visible_message("\red \The [owner]'s [display_name] explodes violently!",\ - "\red Your [display_name] explodes!",\ - "You hear an explosion followed by a scream!") - explosion(get_turf(owner),-1,-1,2,3) - var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() - spark_system.set_up(5, 0, owner) - spark_system.attach(owner) - spark_system.start() - spawn(10) - del(spark_system) - - owner.visible_message("\red [owner.name]'s [display_name] flies off in an arc.",\ - "Your [display_name] goes flying off!",\ - "You hear a terrible sound of ripping tendons and flesh.") - //Throw organs around var/lol = pick(cardinal) step(organ,lol) - owner.update_body(1) + owner.update_body(1) - // OK so maybe your limb just flew off, but if it was attached to a pair of cuffs then hooray! Freedom! - release_restraints() + // OK so maybe your limb just flew off, but if it was attached to a pair of cuffs then hooray! Freedom! + release_restraints() + + if(vital) + owner.death() /**************************************************** HELPERS @@ -637,6 +698,15 @@ This function completely restores a damaged organ to perfect condition. W.bandaged = 1 return rval +/datum/organ/external/proc/disinfect() + var/rval = 0 + for(var/datum/wound/W in wounds) + if(W.internal) continue + rval |= !W.disinfected + W.disinfected = 1 + W.germ_level = 0 + return rval + /datum/organ/external/proc/clamp() var/rval = 0 src.status &= ~ORGAN_BLEEDING @@ -654,8 +724,10 @@ This function completely restores a damaged organ to perfect condition. return rval /datum/organ/external/proc/fracture() + if(status & ORGAN_BROKEN) return + owner.visible_message(\ "\red You hear a loud cracking sound coming from \the [owner].",\ "\red Something feels like it shattered in your [display_name]!",\ @@ -672,6 +744,25 @@ This function completely restores a damaged organ to perfect condition. if (prob(25)) release_restraints() + // This is mostly for the ninja suit to stop ninja being so crippled by breaks. + // TODO: consider moving this to a suit proc or process() or something during + // hardsuit rewrite. + if(!(status & ORGAN_SPLINTED) && istype(owner,/mob/living/carbon/human)) + + var/mob/living/carbon/human/H = owner + + if(H.wear_suit && istype(H.wear_suit,/obj/item/clothing/suit/space)) + + var/obj/item/clothing/suit/space/suit = H.wear_suit + + if(isnull(suit.supporting_limbs)) + return + + owner << "You feel \the [suit] constrict about your [display_name], supporting it." + status |= ORGAN_SPLINTED + suit.supporting_limbs |= src + return + /datum/organ/external/proc/robotize() src.status &= ~ORGAN_BROKEN src.status &= ~ORGAN_BLEEDING @@ -696,9 +787,9 @@ This function completely restores a damaged organ to perfect condition. /datum/organ/external/proc/get_damage() //returns total damage return max(brute_dam + burn_dam - perma_injury, perma_injury) //could use health? -/datum/organ/external/proc/is_infected() +/datum/organ/external/proc/has_infected_wound() for(var/datum/wound/W in wounds) - if(W.germ_level > 100) + if(W.germ_level > INFECTION_LEVEL_ONE) return 1 return 0 @@ -715,6 +806,43 @@ This function completely restores a damaged organ to perfect condition. /datum/organ/external/proc/is_usable() return !(status & (ORGAN_DESTROYED|ORGAN_MUTATED|ORGAN_DEAD)) +/datum/organ/external/proc/is_broken() + return ((status & ORGAN_BROKEN) && !(status & ORGAN_SPLINTED)) + +/datum/organ/external/proc/is_malfunctioning() + return ((status & ORGAN_ROBOT) && prob(brute_dam + burn_dam)) + +//for arms and hands +/datum/organ/external/proc/process_grasp(var/obj/item/c_hand, var/hand_name) + if (!c_hand) + return + + if(is_broken()) + owner.u_equip(c_hand) + var/emote_scream = pick("screams in pain and", "lets out a sharp cry and", "cries out and") + owner.emote("me", 1, "[(owner.species && owner.species.flags & NO_PAIN) ? "" : emote_scream ] drops what they were holding in their [hand_name]!") + if(is_malfunctioning()) + owner.u_equip(c_hand) + owner.emote("me", 1, "drops what they were holding, their [hand_name] malfunctioning!") + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, owner) + spark_system.attach(owner) + spark_system.start() + spawn(10) + del(spark_system) + +/datum/organ/external/proc/embed(var/obj/item/weapon/W, var/silent = 0) + if(!silent) + owner.visible_message("\The [W] sticks in the wound!") + implants += W + owner.embedded_flag = 1 + owner.verbs += /mob/proc/yank_out_object + W.add_blood(owner) + if(ismob(W.loc)) + var/mob/living/H = W.loc + H.drop_item() + W.loc = owner + /**************************************************** ORGAN DEFINES ****************************************************/ @@ -726,7 +854,7 @@ This function completely restores a damaged organ to perfect condition. max_damage = 75 min_broken_damage = 40 body_part = UPPER_TORSO - + vital = 1 /datum/organ/external/groin name = "groin" @@ -735,6 +863,7 @@ This function completely restores a damaged organ to perfect condition. max_damage = 50 min_broken_damage = 30 body_part = LOWER_TORSO + vital = 1 /datum/organ/external/l_arm name = "l_arm" @@ -744,6 +873,10 @@ This function completely restores a damaged organ to perfect condition. min_broken_damage = 20 body_part = ARM_LEFT + process() + ..() + process_grasp(owner.l_hand, "left hand") + /datum/organ/external/l_leg name = "l_leg" display_name = "left leg" @@ -761,6 +894,10 @@ This function completely restores a damaged organ to perfect condition. min_broken_damage = 20 body_part = ARM_RIGHT + process() + ..() + process_grasp(owner.r_hand, "right hand") + /datum/organ/external/r_leg name = "r_leg" display_name = "right leg" @@ -796,6 +933,10 @@ This function completely restores a damaged organ to perfect condition. min_broken_damage = 15 body_part = HAND_RIGHT + process() + ..() + process_grasp(owner.r_hand, "right hand") + /datum/organ/external/l_hand name = "l_hand" display_name = "left hand" @@ -804,6 +945,10 @@ This function completely restores a damaged organ to perfect condition. min_broken_damage = 15 body_part = HAND_LEFT + process() + ..() + process_grasp(owner.l_hand, "left hand") + /datum/organ/external/head name = "head" icon_name = "head" @@ -812,6 +957,7 @@ This function completely restores a damaged organ to perfect condition. min_broken_damage = 40 body_part = HEAD var/disfigured = 0 + vital = 1 /datum/organ/external/head/get_icon(var/icon/race_icon, var/icon/deform_icon) if (!owner) diff --git a/code/modules/organs/organ_internal.dm b/code/modules/organs/organ_internal.dm index bb1ce19a..4ed10047 100644 --- a/code/modules/organs/organ_internal.dm +++ b/code/modules/organs/organ_internal.dm @@ -21,15 +21,47 @@ return damage >= min_broken_damage + /datum/organ/internal/New(mob/living/carbon/human/H) ..() var/datum/organ/external/E = H.organs_by_name[src.parent_organ] if(E.internal_organs == null) E.internal_organs = list() - E.internal_organs += src - H.internal_organs[src.name] = src + E.internal_organs |= src + H.internal_organs |= src src.owner = H +/datum/organ/internal/process() + //Process infections + + if (robotic >= 2 || (owner.species && owner.species.flags & IS_PLANT)) //TODO make robotic internal and external organs separate types of organ instead of a flag + germ_level = 0 + return + + if(owner.bodytemperature >= 170) //cryo stops germs from moving and doing their bad stuffs + //** Handle antibiotics and curing infections + handle_antibiotics() + + //** Handle the effects of infections + var/antibiotics = owner.reagents.get_reagent_amount("spaceacillin") + + if (germ_level > 0 && germ_level < INFECTION_LEVEL_ONE/2 && prob(30)) + germ_level-- + + if (germ_level >= INFECTION_LEVEL_ONE/2) + //aiming for germ level to go from ambient to INFECTION_LEVEL_TWO in an average of 15 minutes + if(antibiotics < 5 && prob(round(germ_level/6))) + germ_level++ + + if (germ_level >= INFECTION_LEVEL_TWO) + var/datum/organ/external/parent = owner.get_organ(parent_organ) + //spread germs + if (antibiotics < 5 && parent.germ_level < germ_level && ( parent.germ_level < INFECTION_LEVEL_ONE*2 || prob(30) )) + parent.germ_level++ + + if (prob(3)) //about once every 30 seconds + take_damage(1,silent=prob(30)) + /datum/organ/internal/proc/take_damage(amount, var/silent=0) if(src.robotic == 2) src.damage += (amount * 0.8) @@ -40,7 +72,6 @@ if (!silent) owner.custom_pain("Something inside your [parent.display_name] hurts a lot.", 1) - /datum/organ/internal/proc/emp_act(severity) switch(robotic) if(0) @@ -90,13 +121,18 @@ parent_organ = "chest" process() + ..() + if (germ_level > INFECTION_LEVEL_ONE) + if(prob(5)) + owner.emote("cough") //respitory tract infection + if(is_bruised()) if(prob(2)) spawn owner.emote("me", 1, "coughs up blood!") owner.drip(10) if(prob(4)) spawn owner.emote("me", 1, "gasps for air!") - owner.losebreath += 5 + owner.losebreath += 15 /datum/organ/internal/liver name = "liver" @@ -104,6 +140,14 @@ var/process_accuracy = 10 process() + ..() + if (germ_level > INFECTION_LEVEL_ONE) + if(prob(1)) + owner << "\red Your skin itches." + if (germ_level > INFECTION_LEVEL_TWO) + if(prob(1)) + spawn owner.vomit() + if(owner.life_tick % process_accuracy == 0) if(src.damage < 0) src.damage = 0 @@ -115,9 +159,9 @@ src.damage += 0.2 * process_accuracy //Damaged one shares the fun else - var/victim = pick(owner.internal_organs) - var/datum/organ/internal/O = owner.internal_organs[victim] - O.damage += 0.2 * process_accuracy + var/datum/organ/internal/O = pick(owner.internal_organs) + if(O) + O.damage += 0.2 * process_accuracy //Detox can heal small amounts of damage if (src.damage && src.damage < src.min_bruised_damage && owner.reagents.has_reagent("anti_toxin")) @@ -146,6 +190,7 @@ parent_organ = "head" process() //Eye damage replaces the old eye_stat var. + ..() if(is_bruised()) owner.eye_blurry = 20 if(is_broken()) diff --git a/code/modules/organs/pain.dm b/code/modules/organs/pain.dm index 250e9b98..49cc2073 100644 --- a/code/modules/organs/pain.dm +++ b/code/modules/organs/pain.dm @@ -102,8 +102,7 @@ mob/living/carbon/human/proc/handle_pain() pain(damaged_organ.display_name, maxdam, 0) // Damage to internal organs hurts a lot. - for(var/organ_name in internal_organs) - var/datum/organ/internal/I = internal_organs[organ_name] + for(var/datum/organ/internal/I in internal_organs) if(I.damage > 2) if(prob(2)) var/datum/organ/external/parent = get_organ(I.parent_organ) src.custom_pain("You feel a sharp pain in your [parent.display_name]", 1) diff --git a/code/modules/organs/wound.dm b/code/modules/organs/wound.dm index a7892486..9d4a5364 100644 --- a/code/modules/organs/wound.dm +++ b/code/modules/organs/wound.dm @@ -7,7 +7,7 @@ var/current_stage = 0 // description of the wound - var/desc = "" + var/desc = "wound" //default in case something borks // amount of damage this wound causes var/damage = 0 @@ -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,25 +84,26 @@ // the amount of damage per wound proc/wound_damage() return src.damage / src.amount - - // checks whether the wound has been appropriately treated - // always returns 1 for wounds that are too minor to need treatment - proc/is_treated() + + 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 if (other.current_stage != src.current_stage) return 0 if (other.damage_type != src.damage_type) return 0 - if (!(other.is_treated()) != !(src.is_treated())) return 0 + if (!(other.can_autoheal()) != !(src.can_autoheal())) return 0 if (!(other.bandaged) != !(src.bandaged)) return 0 if (!(other.clamped) != !(src.clamped)) return 0 if (!(other.salved) != !(src.salved)) return 0 @@ -119,21 +120,29 @@ // checks if wound is considered open for external infections // untreated cuts (and bleeding bruises) and burns are possibly infectable, chance higher if wound is bigger - proc/can_infect() - if (is_treated() && damage < 10) + proc/infection_check() + if (damage < 10) //small cuts, tiny bruises, and moderate burns shouldn't be infectable. + return 0 + if (is_treated() && damage < 25) //anything less than a flesh wound (or equivalent) isn't infectable if treated properly return 0 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) - return prob(dam_coef*5) && bleeding() //bruises only infectable if bleeding + return prob(dam_coef*5) if (BURN) return prob(dam_coef*10) if (CUT) return prob(dam_coef*20) return 0 + // heal the given amount of damage, and if the given amount of damage was more // than what needed to be healed, return how much heal was left // set @heals_internal to also heal internal organ damage @@ -170,23 +179,71 @@ 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() - // internal wounds don't bleed in the sense of this function - return ((wound_damage() > 30 || bleed_timer > 0) && !(bandaged||clamped) && (damage_type == BRUISE && wound_damage() >= 20 || damage_type == CUT && wound_damage() >= 5) && current_stage <= max_bleeding_stage && !src.internal) + 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 **/ + +//Note that the MINIMUM damage before a wound can be applied should correspond to +//the damage amount for the stage with the same name as the wound. +//e.g. /datum/wound/cut/deep should only be applied for 15 damage and up, +//because in it's stages list, "deep cut" = 15. +/proc/get_wound_type(var/type = CUT, var/damage) + switch(type) + if(CUT) + switch(damage) + if(70 to INFINITY) + return /datum/wound/cut/massive + if(60 to 70) + return /datum/wound/cut/gaping_big + if(50 to 60) + return /datum/wound/cut/gaping + if(25 to 50) + return /datum/wound/cut/flesh + if(15 to 25) + return /datum/wound/cut/deep + if(0 to 15) + return /datum/wound/cut/small + if(BRUISE) + return /datum/wound/bruise + if(BURN) + switch(damage) + if(50 to INFINITY) + return /datum/wound/burn/carbonised + if(40 to 50) + return /datum/wound/burn/deep + if(30 to 40) + return /datum/wound/burn/severe + if(15 to 30) + return /datum/wound/burn/large + if(0 to 15) + return /datum/wound/burn/moderate + return null //no wound /** CUTS **/ /datum/wound/cut/small @@ -201,7 +258,7 @@ damage_type = CUT /datum/wound/cut/flesh - max_bleeding_stage = 3 + max_bleeding_stage = 4 stages = list("ugly ripped flesh wound" = 35, "ugly flesh wound" = 30, "flesh wound" = 25, "blood soaked clot" = 15, "large scab" = 5, "fresh skin" = 0) damage_type = CUT @@ -230,27 +287,40 @@ datum/wound/cut/massive /** BURNS **/ /datum/wound/burn/moderate - stages = list("ripped burn" = 10, "moderate burn" = 5, "moderate salved burn" = 2, "fresh skin" = 0) + stages = list("ripped burn" = 10, "moderate burn" = 5, "healing moderate burn" = 2, "fresh skin" = 0) damage_type = BURN /datum/wound/burn/large - stages = list("ripped large burn" = 20, "large burn" = 15, "large salved burn" = 5, "fresh skin" = 0) + stages = list("ripped large burn" = 20, "large burn" = 15, "healing large burn" = 5, "fresh skin" = 0) damage_type = BURN /datum/wound/burn/severe - stages = list("ripped severe burn" = 35, "severe burn" = 30, "severe salved burn" = 10, "burn scar" = 0) + stages = list("ripped severe burn" = 35, "severe burn" = 30, "healing severe burn" = 10, "burn scar" = 0) damage_type = BURN /datum/wound/burn/deep - stages = list("ripped deep burn" = 45, "deep burn" = 40, "deep salved burn" = 15, "large burn scar" = 0) + stages = list("ripped deep burn" = 45, "deep burn" = 40, "healing deep burn" = 15, "large burn scar" = 0) damage_type = BURN /datum/wound/burn/carbonised - stages = list("carbonised area" = 50, "treated carbonised area" = 20, "massive burn scar" = 0) + stages = list("carbonised area" = 50, "healing carbonised area" = 20, "massive burn scar" = 0) damage_type = BURN +/** 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/clipboard.dm b/code/modules/paperwork/clipboard.dm index ea49067e..ca128c2c 100644 --- a/code/modules/paperwork/clipboard.dm +++ b/code/modules/paperwork/clipboard.dm @@ -44,6 +44,7 @@ return /obj/item/weapon/clipboard/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W, /obj/item/weapon/paper) || istype(W, /obj/item/weapon/photo)) user.drop_item() W.loc = src @@ -51,9 +52,11 @@ toppaper = W user << "You clip the [W] onto \the [src]." update_icon() - else if(toppaper) - toppaper.attackby(usr.get_active_hand(), usr) + + else if(istype(toppaper) && istype(W, /obj/item/weapon/pen)) + toppaper.attackby(W, usr) update_icon() + return /obj/item/weapon/clipboard/attack_self(mob/user as mob) @@ -85,32 +88,39 @@ if((usr.stat || usr.restrained())) return - if(usr.contents.Find(src)) + if(src.loc == usr) if(href_list["pen"]) - if(haspen) + if(istype(haspen) && (haspen.loc == src)) haspen.loc = usr.loc usr.put_in_hands(haspen) haspen = null - if(href_list["addpen"]) + else if(href_list["addpen"]) if(!haspen) - if(istype(usr.get_active_hand(), /obj/item/weapon/pen)) - var/obj/item/weapon/pen/W = usr.get_active_hand() + var/obj/item/weapon/pen/W = usr.get_active_hand() + if(istype(W, /obj/item/weapon/pen)) usr.drop_item() W.loc = src haspen = W usr << "You slot the pen into \the [src]." - if(href_list["write"]) - var/obj/item/P = locate(href_list["write"]) - if(P) - if(usr.get_active_hand()) - P.attackby(usr.get_active_hand(), usr) + else if(href_list["write"]) + var/obj/item/weapon/P = locate(href_list["write"]) + + if(P && (P.loc == src) && istype(P, /obj/item/weapon/paper) && (P == toppaper) ) + + var/obj/item/I = usr.get_active_hand() + + if(istype(I, /obj/item/weapon/pen)) + + P.attackby(I, usr) - if(href_list["remove"]) + else if(href_list["remove"]) var/obj/item/P = locate(href_list["remove"]) - if(P) + + if(P && (P.loc == src) && (istype(P, /obj/item/weapon/paper) || istype(P, /obj/item/weapon/photo)) ) + P.loc = usr.loc usr.put_in_hands(P) if(P == toppaper) @@ -121,9 +131,11 @@ else toppaper = null - if(href_list["read"]) + else if(href_list["read"]) var/obj/item/weapon/paper/P = locate(href_list["read"]) - if(P) + + if(P && (P.loc == src) && istype(P, /obj/item/weapon/paper) ) + if(!(istype(usr, /mob/living/carbon/human) || istype(usr, /mob/dead/observer) || istype(usr, /mob/living/silicon))) usr << browse("[P.name][stars(P.info)][P.stamps]", "window=[P.name]") onclose(usr, "[P.name]") @@ -131,18 +143,18 @@ usr << browse("[P.name][P.info][P.stamps]", "window=[P.name]") onclose(usr, "[P.name]") - if(href_list["look"]) + else if(href_list["look"]) var/obj/item/weapon/photo/P = locate(href_list["look"]) - if(P) + if(P && (P.loc == src) && istype(P, /obj/item/weapon/photo) ) P.show(usr) - if(href_list["top"]) + else if(href_list["top"]) // currently unused var/obj/item/P = locate(href_list["top"]) - if(P) + if(P && (P.loc == src) && istype(P, /obj/item/weapon/paper) ) toppaper = P usr << "You move [P.name] to the top." //Update everything attack_self(usr) update_icon() - return \ No newline at end of file + return diff --git a/code/modules/paperwork/filingcabinet.dm b/code/modules/paperwork/filingcabinet.dm index 1dd51443..5bb1dde1 100644 --- a/code/modules/paperwork/filingcabinet.dm +++ b/code/modules/paperwork/filingcabinet.dm @@ -57,9 +57,7 @@ user.set_machine(src) var/dat = "
" - var/i - for(i=contents.len, i>=1, i--) - var/obj/item/P = contents[i] + for(var/obj/item/P in src) dat += "" dat += "
[P.name]
" user << browse("[name][dat]", "window=filingcabinet;size=350x300") @@ -89,12 +87,13 @@ //var/retrieveindex = text2num(href_list["retrieve"]) var/obj/item/P = locate(href_list["retrieve"])//contents[retrieveindex] - if(P && in_range(src, usr)) + if(istype(P) && (P.loc == src) && src.Adjacent(usr)) usr.put_in_hands(P) updateUsrDialog() icon_state = "[initial(icon_state)]-open" - sleep(5) - icon_state = initial(icon_state) + spawn(0) + sleep(5) + icon_state = initial(icon_state) /* diff --git a/code/modules/paperwork/folders.dm b/code/modules/paperwork/folders.dm index 0f1b612e..868fac30 100644 --- a/code/modules/paperwork/folders.dm +++ b/code/modules/paperwork/folders.dm @@ -59,34 +59,34 @@ if((usr.stat || usr.restrained())) return - if(usr.contents.Find(src)) + if(src.loc == usr) if(href_list["remove"]) var/obj/item/P = locate(href_list["remove"]) - if(P && P.loc == src) + if(P && (P.loc == src) && istype(P)) P.loc = usr.loc usr.put_in_hands(P) - if(href_list["read"]) + else if(href_list["read"]) var/obj/item/weapon/paper/P = locate(href_list["read"]) - if(P) + if(P && (P.loc == src) && istype(P)) if(!(istype(usr, /mob/living/carbon/human) || istype(usr, /mob/dead/observer) || istype(usr, /mob/living/silicon))) usr << browse("[P.name][stars(P.info)][P.stamps]", "window=[P.name]") onclose(usr, "[P.name]") else usr << browse("[P.name][P.info][P.stamps]", "window=[P.name]") onclose(usr, "[P.name]") - if(href_list["look"]) + else if(href_list["look"]) var/obj/item/weapon/photo/P = locate(href_list["look"]) - if(P) + if(P && (P.loc == src) && istype(P)) P.show(usr) - if(href_list["browse"]) + else if(href_list["browse"]) var/obj/item/weapon/paper_bundle/P = locate(href_list["browse"]) - if(P) + if(P && (P.loc == src) && istype(P)) P.attack_self(usr) onclose(usr, "[P.name]") //Update everything attack_self(usr) update_icon() - return \ No newline at end of file + return diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm index 41eb32e2..9cf7cd69 100644 --- a/code/modules/paperwork/paper.dm +++ b/code/modules/paperwork/paper.dm @@ -40,6 +40,12 @@ pixel_y = rand(-8, 8) pixel_x = rand(-9, 9) stamps = "" + + if(info != initial(info)) + info = html_encode(info) + info = replacetext(info, "\n", "
") + info = parsepencode(info) + spawn(2) update_icon() updateinfolinks() @@ -96,8 +102,8 @@ /obj/item/weapon/paper/attack_ai(var/mob/living/silicon/ai/user as mob) var/dist - if(istype(user) && user.current) //is AI - dist = get_dist(src, user.current) + if(istype(user) && user.camera) //is AI + dist = get_dist(src, user.camera) else //cyborg or AI not seeing through a camera dist = get_dist(src, user) if(dist < 2) @@ -114,6 +120,22 @@ " [user] holds up a paper and shows it to [M]. ") M << examine() + else if(user.zone_sel.selecting == "mouth") // lipstick wiping + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(H == user) + user << "You wipe off the lipstick with [src]." + H.lip_style = null + H.update_body() + else + user.visible_message("[user] begins to wipe [H]'s lipstick off with \the [src].", \ + "You begin to wipe off [H]'s lipstick.") + if(do_after(user, 10) && do_after(H, 10, 5, 0)) //user needs to keep their active hand, H does not. + user.visible_message("[user] wipes [H]'s lipstick off with \the [src].", \ + "You wipe off [H]'s lipstick.") + H.lip_style = null + H.update_body() + /obj/item/weapon/paper/proc/addtofield(var/id, var/text, var/links = 0) var/locid = 0 var/laststart = 1 @@ -182,7 +204,7 @@ t = replacetext(t, "\[/u\]", "") t = replacetext(t, "\[large\]", "") t = replacetext(t, "\[/large\]", "") - t = replacetext(t, "\[sign\]", "[user.real_name]") + t = replacetext(t, "\[sign\]", "[user ? user.real_name : "Anonymous"]") t = replacetext(t, "\[field\]", "") if(!iscrayon) @@ -192,8 +214,15 @@ t = replacetext(t, "\[/small\]", "
") t = replacetext(t, "\[list\]", "
    ") t = replacetext(t, "\[/list\]", "
") + t = replacetext(t, "\[table\]", "") + t = replacetext(t, "\[/table\]", "
") + t = replacetext(t, "\[grid\]", "") + t = replacetext(t, "\[/grid\]", "
") + t = replacetext(t, "\[row\]", "") + t = replacetext(t, "\[cell\]", "") + t = replacetext(t, "\[logo\]", "") - t = "[t]" + t = "[t]" else // If it is a crayon, and he still tries to use these, make them empty! t = replacetext(t, "\[*\]", "") t = replacetext(t, "\[hr\]", "") @@ -201,8 +230,13 @@ t = replacetext(t, "\[/small\]", "") t = replacetext(t, "\[list\]", "") t = replacetext(t, "\[/list\]", "") + t = replacetext(t, "\[table\]", "") + t = replacetext(t, "\[/table\]", "") + t = replacetext(t, "\[row\]", "") + t = replacetext(t, "\[cell\]", "") + t = replacetext(t, "\[logo\]", "") - t = "[t]" + t = "[t]" // t = replacetext(t, "#", "") // Junk converted to nothing! @@ -282,9 +316,10 @@ iscrayon = 1 - if((!in_range(src, usr) && loc != usr && !( istype(loc, /obj/item/weapon/clipboard) ) && loc.loc != usr && usr.get_active_hand() != i)) // Some check to see if he's allowed to write + // if paper is not in usr, then it must be near them, or in a clipboard or folder, which must be in or near usr + if(src.loc != usr && !src.Adjacent(usr) && !((istype(src.loc, /obj/item/weapon/clipboard) || istype(src.loc, /obj/item/weapon/folder)) && (src.loc.loc == usr || src.loc.Adjacent(usr)) ) ) return - +/* t = checkhtml(t) // check for exploits @@ -294,7 +329,8 @@ log_admin("PAPER: [usr] ([usr.ckey]) tried to use forbidden word in [src]: [bad].") message_admins("PAPER: [usr] ([usr.ckey]) tried to use forbidden word in [src]: [bad].") return - +*/ + t = html_encode(t) t = replacetext(t, "\n", "
") t = parsepencode(t, i, usr, iscrayon) // Encode everything from pencode to html @@ -461,4 +497,4 @@ return /obj/item/weapon/paper/crumpled/bloody - icon_state = "scrap_bloodied" \ No newline at end of file + icon_state = "scrap_bloodied" diff --git a/code/modules/paperwork/paperbin.dm b/code/modules/paperwork/paperbin.dm index a1ded78e..03c59d4a 100644 --- a/code/modules/paperwork/paperbin.dm +++ b/code/modules/paperwork/paperbin.dm @@ -8,6 +8,7 @@ throw_speed = 3 throw_range = 7 pressure_resistance = 10 + layer = OBJ_LAYER - 0.1 var/amount = 30 //How much paper is in the bin. var/list/papers = new/list() //List of papers put in the bin for reference. diff --git a/code/modules/paperwork/pen.dm b/code/modules/paperwork/pen.dm index 515de827..5300f2ed 100644 --- a/code/modules/paperwork/pen.dm +++ b/code/modules/paperwork/pen.dm @@ -21,7 +21,7 @@ w_class = 1.0 throw_speed = 7 throw_range = 15 - m_amt = 10 + matter = list("metal" = 10) var/colour = "black" //what colour the ink is! pressure_resistance = 2 diff --git a/code/modules/paperwork/photocopier.dm b/code/modules/paperwork/photocopier.dm index 7f006325..6963fe2f 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.

" + if(istype(user,/mob/living/silicon)) + dat += "Print photo from database

" dat += "Current toner level: [toner]" if(!toner) dat +="
Please insert a new toner cartridge!" @@ -112,6 +114,27 @@ if(copies < maxcopies) copies++ updateUsrDialog() + else if(href_list["aipic"]) + if(!istype(usr,/mob/living/silicon)) return + if(toner >= 5) + var/mob/living/silicon/tempAI = usr + 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 (src.loc) + p.construct(selection) + if (p.desc == "") + p.desc += "Copied by [tempAI.name]" + else + p.desc += " - Copied by [tempAI.name]" + toner -= 5 + sleep(15) + updateUsrDialog() attackby(obj/item/O as obj, mob/user as mob) if(istype(O, /obj/item/weapon/paper)) diff --git a/code/modules/paperwork/photography.dm b/code/modules/paperwork/photography.dm index b6c25239..cef4a337 100644 --- a/code/modules/paperwork/photography.dm +++ b/code/modules/paperwork/photography.dm @@ -53,10 +53,10 @@ /obj/item/weapon/photo/proc/show(mob/user as mob) user << browse_rsc(img, "tmp_photo.png") user << browse("[name]" \ - + "" \ - + "
Written on the back:
[scribble]" : ]"\ - + "", "window=book;size=200x[scribble ? 400 : 200]") + + "" \ + + "" \ + + "[scribble ? "
Written on the back:
[scribble]" : ""]"\ + + "", "window=book;size=192x[scribble ? 400 : 192]") onclose(user, "[name]") return @@ -119,7 +119,7 @@ w_class = 2.0 flags = FPRINT | CONDUCT | TABLEPASS slot_flags = SLOT_BELT - m_amt = 2000 + matter = list("metal" = 2000) var/pictures_max = 10 var/pictures_left = 10 var/on = 1 @@ -152,41 +152,51 @@ ..() -/obj/item/device/camera/proc/get_icon(turf/the_turf as turf) +/obj/item/device/camera/proc/get_icon(list/turfs, turf/center) + //Bigger icon base to capture those icons that were shifted to the next tile //i.e. pretty much all wall-mounted machinery var/icon/res = icon('icons/effects/96x96.dmi', "") - - var/icon/turficon = build_composite_icon(the_turf) - res.Blend(turficon, ICON_OVERLAY, 33, 33) + // Initialize the photograph to black. + res.Blend("#000", ICON_OVERLAY) var/atoms[] = list() - for(var/atom/A in the_turf) - if(A.invisibility) continue - atoms.Add(A) + for(var/turf/the_turf in turfs) + // Add outselves to the list of stuff to draw + atoms.Add(the_turf); + // As well as anything that isn't invisible. + for(var/atom/A in the_turf) + if(A.invisibility) continue + atoms.Add(A) - //Sorting icons based on levels - var/gap = atoms.len - var/swapped = 1 - while (gap > 1 || swapped) - swapped = 0 - if(gap > 1) - gap = round(gap / 1.247330950103979) - if(gap < 1) - gap = 1 - for(var/i = 1; gap + i <= atoms.len; i++) - var/atom/l = atoms[i] //Fucking hate - var/atom/r = atoms[gap+i] //how lists work here - if(l.layer > r.layer) //no "atoms[i].layer" for me - atoms.Swap(i, gap + i) - swapped = 1 + // Sort the atoms into their layers + var/list/sorted = sort_atoms_by_layer(atoms) - for(var/i; i <= atoms.len; i++) - var/atom/A = atoms[i] + for(var/i; i <= sorted.len; i++) + var/atom/A = sorted[i] if(A) - var/icon/img = getFlatIcon(A, A.dir)//build_composite_icon(A) + var/icon/img = getFlatIcon(A)//build_composite_icon(A) + + // If what we got back is actually a picture, draw it. if(istype(img, /icon)) - res.Blend(new/icon(img, "", A.dir), ICON_OVERLAY, 33 + A.pixel_x, 33 + A.pixel_y) + // Check if we're looking at a mob that's lying down + if(istype(A, /mob/living) && A:lying) + // If they are, apply that effect to their picture. + img.BecomeLying() + // Calculate where we are relative to the center of the photo + var/xoff = (A.x - center.x) * 32 + var/yoff = (A.y - center.y) * 32 + if (istype(A,/atom/movable)) + xoff+=A:step_x + yoff+=A:step_y + res.Blend(img, blendMode2iconMode(A.blend_mode), 33 + A.pixel_x + xoff, 33 + A.pixel_y + yoff) + + // Lastly, render any contained effects on top. + for(var/turf/the_turf in turfs) + // Calculate where we are relative to the center of the photo + var/xoff = (the_turf.x - center.x) * 32 + var/yoff = (the_turf.y - center.y) * 32 + res.Blend(getFlatIcon(the_turf.loc), blendMode2iconMode(the_turf.blend_mode),33 + xoff,33 + yoff) return res @@ -209,53 +219,10 @@ mob_detail += "You can also see [A] on the photo[A:health < 75 ? " - [A] looks hurt":""].[holding ? " [holding]":"."]." return mob_detail - /obj/item/device/camera/afterattack(atom/target as mob|obj|turf|area, mob/user as mob, flag) if(!on || !pictures_left || ismob(target.loc)) return + captureimage(target, user, flag) - var/x_c = target.x - 1 - var/y_c = target.y + 1 - var/z_c = target.z - - var/icon/temp = icon('icons/effects/96x96.dmi',"") - var/icon/black = icon('icons/turf/space.dmi', "black") - var/mobs = "" - for(var/i = 1; i <= 3; i++) - for(var/j = 1; j <= 3; j++) - var/turf/T = locate(x_c, y_c, z_c) - var/mob/dummy = new(T) //Go go visibility check dummy - var/viewer = user - if(user.client) //To make shooting through security cameras possible - viewer = user.client.eye - if(dummy in viewers(world.view, viewer)) - temp.Blend(get_icon(T), ICON_OVERLAY, 32 * (j-1-1), 32 - 32 * (i-1)) - else - temp.Blend(black, ICON_OVERLAY, 32 * (j-1), 64 - 32 * (i-1)) - mobs += get_mobs(T) - dummy.loc = null - dummy = null //Alas, nameless creature //garbage collect it instead - x_c++ - y_c-- - x_c = x_c - 3 - - var/obj/item/weapon/photo/P = new/obj/item/weapon/photo() - P.loc = user.loc - if(!user.get_inactive_hand()) - user.put_in_inactive_hand(P) - var/icon/small_img = icon(temp) - var/icon/tiny_img = icon(temp) - var/icon/ic = icon('icons/obj/items.dmi',"photo") - var/icon/pc = icon('icons/obj/bureaucracy.dmi', "photo") - small_img.Scale(8, 8) - tiny_img.Scale(4, 4) - ic.Blend(small_img,ICON_OVERLAY, 10, 13) - pc.Blend(tiny_img,ICON_OVERLAY, 12, 19) - P.icon = ic - P.tiny = pc - P.img = temp - P.desc = mobs - P.pixel_x = rand(-10, 10) - P.pixel_y = rand(-10, 10) playsound(loc, pick('sound/items/polaroid1.ogg', 'sound/items/polaroid2.ogg'), 75, 1, -3) pictures_left-- @@ -265,4 +232,74 @@ on = 0 spawn(64) icon_state = icon_on - on = 1 \ No newline at end of file + on = 1 + +/obj/item/device/camera/proc/can_capture_turf(turf/T, mob/user) + var/mob/dummy = new(T) //Go go visibility check dummy + var/viewer = user + if(user.client) //To make shooting through security cameras possible + viewer = user.client.eye + var/can_see = (dummy in viewers(world.view, viewer)) != null + + dummy.loc = null + dummy = null //Alas, nameless creature //garbage collect it instead + return can_see + +/obj/item/device/camera/proc/captureimage(atom/target, mob/user, flag) + var/x_c = target.x - 1 + var/y_c = target.y + 1 + var/z_c = target.z + + + var/list/turfs = list() + var/mobs = "" + for(var/i = 1; i <= 3; i++) + for(var/j = 1; j <= 3; j++) + var/turf/T = locate(x_c, y_c, z_c) + if(can_capture_turf(T, user)) + turfs.Add(T) + mobs += get_mobs(T) + x_c++ + y_c-- + x_c = x_c - 3 + + var/datum/picture/P = createpicture(target, user, turfs, mobs, flag) + printpicture(user, P) + +/obj/item/device/camera/proc/createpicture(atom/target, mob/user, list/turfs, mobs, flag) + var/icon/photoimage = get_icon(turfs, target) + + var/icon/small_img = icon(photoimage) + var/icon/tiny_img = icon(photoimage) + var/icon/ic = icon('icons/obj/items.dmi',"photo") + var/icon/pc = icon('icons/obj/bureaucracy.dmi', "photo") + small_img.Scale(8, 8) + tiny_img.Scale(4, 4) + ic.Blend(small_img,ICON_OVERLAY, 10, 13) + pc.Blend(tiny_img,ICON_OVERLAY, 12, 19) + + var/datum/picture/P = new() + P.fields["author"] = user + P.fields["icon"] = ic + P.fields["tiny"] = pc + P.fields["img"] = photoimage + P.fields["desc"] = mobs + P.fields["pixel_x"] = rand(-10, 10) + P.fields["pixel_y"] = rand(-10, 10) + + return P + +/obj/item/device/camera/proc/printpicture(mob/user, var/datum/picture/P) + var/obj/item/weapon/photo/Photo = new/obj/item/weapon/photo() + Photo.loc = user.loc + if(!user.get_inactive_hand()) + user.put_in_inactive_hand(Photo) + Photo.construct(P) + +/obj/item/weapon/photo/proc/construct(var/datum/picture/P) + icon = P.fields["icon"] + tiny = P.fields["tiny"] + img = P.fields["img"] + desc = P.fields["desc"] + pixel_x = P.fields["pixel_x"] + pixel_y = P.fields["pixel_y"] diff --git a/code/modules/paperwork/silicon_photography.dm b/code/modules/paperwork/silicon_photography.dm new file mode 100644 index 00000000..4fbe953d --- /dev/null +++ b/code/modules/paperwork/silicon_photography.dm @@ -0,0 +1,162 @@ +/************** +* AI-specific * +**************/ +/datum/picture + var/name = "image" + var/list/fields = list() + +/obj/item/device/camera/siliconcam + var/in_camera_mode = 0 + var/photos_taken = 0 + var/list/aipictures = list() + +/obj/item/device/camera/siliconcam/ai_camera //camera AI can take pictures with + name = "AI photo camera" + +/obj/item/device/camera/siliconcam/robot_camera //camera cyborgs can take pictures with + name = "Cyborg photo camera" + +/obj/item/device/camera/siliconcam/drone_camera //currently doesn't offer the verbs, thus cannot be used + name = "Drone photo camera" + +/obj/item/device/camera/siliconcam/proc/injectaialbum(var/datum/picture/P, var/sufix = "") //stores image information to a list similar to that of the datacore + photos_taken++ + P.fields["name"] = "Image [photos_taken][sufix]" + aipictures += P + +/obj/item/device/camera/siliconcam/proc/injectmasteralbum(var/datum/picture/P) //stores image information to a list similar to that of the datacore + var/mob/living/silicon/robot/C = src.loc + if(C.connected_ai) + var/mob/A = P.fields["author"] + C.connected_ai.aiCamera.injectaialbum(P, " (taken by [A.name])") + C.connected_ai << "Image recorded and saved by [name]" + usr << "Image recorded and saved to remote database" //feedback to the Cyborg player that the picture was taken + else + injectaialbum(P) + usr << "Image recorded" + +/obj/item/device/camera/siliconcam/proc/selectpicture(obj/item/device/camera/siliconcam/cam) + if(!cam) + cam = getsource() + + var/list/nametemp = list() + var/find + if(cam.aipictures.len == 0) + usr << "No images saved" + return + for(var/datum/picture/t in cam.aipictures) + nametemp += t.fields["name"] + find = input("Select image (numbered in order taken)") in nametemp + + for(var/datum/picture/q in cam.aipictures) + if(q.fields["name"] == find) + return q + +/obj/item/device/camera/siliconcam/proc/viewpictures() + var/datum/picture/selection = selectpicture() + + if(!selection) + return + + var/obj/item/weapon/photo/P = new/obj/item/weapon/photo() + P.construct(selection) + P.show(usr) + usr << P.desc + + // TG uses a special garbage collector.. qdel(P) + del(P) //so 10 thousand pictures items are not left in memory should an AI take them and then view them all. + +/obj/item/device/camera/siliconcam/proc/deletepicture(obj/item/device/camera/siliconcam/cam) + var/datum/picture/selection = selectpicture(cam) + + if(!selection) + return + + cam.aipictures -= selection + usr << "Image deleted" + +/obj/item/device/camera/siliconcam/ai_camera/can_capture_turf(turf/T, mob/user) + var/mob/living/silicon/ai = user + return ai.TurfAdjacent(T) + +/obj/item/device/camera/siliconcam/proc/toggle_camera_mode() + if(in_camera_mode) + camera_mode_off() + else + camera_mode_on() + +/obj/item/device/camera/siliconcam/proc/camera_mode_off() + src.in_camera_mode = 0 + usr << "Camera Mode deactivated" + +/obj/item/device/camera/siliconcam/proc/camera_mode_on() + src.in_camera_mode = 1 + usr << "Camera Mode activated" + +/obj/item/device/camera/siliconcam/ai_camera/printpicture(mob/user, datum/picture/P) + injectaialbum(P) + usr << "Image recorded" + +/obj/item/device/camera/siliconcam/robot_camera/printpicture(mob/user, datum/picture/P) + injectmasteralbum(P) + +/obj/item/device/camera/siliconcam/ai_camera/verb/take_image() + set category = "AI Commands" + set name = "Take Image" + set desc = "Takes an image" + set src in usr + + toggle_camera_mode() + +/obj/item/device/camera/siliconcam/ai_camera/verb/view_images() + set category = "AI Commands" + set name = "View Images" + set desc = "View images" + set src in usr + + viewpictures() + +/obj/item/device/camera/siliconcam/ai_camera/verb/delete_images() + set category = "AI Commands" + set name = "Delete Image" + set desc = "Delete image" + set src in usr + + deletepicture() + +/obj/item/device/camera/siliconcam/robot_camera/verb/take_image() + set category ="Robot Commands" + set name = "Take Image" + set desc = "Takes an image" + set src in usr + + toggle_camera_mode() + +/obj/item/device/camera/siliconcam/robot_camera/verb/view_images() + set category ="Robot Commands" + set name = "View Images" + set desc = "View images" + set src in usr + + viewpictures() + +/obj/item/device/camera/siliconcam/robot_camera/verb/delete_images() + set category = "Robot Commands" + set name = "Delete Image" + set desc = "Delete a local image" + set src in usr + + // Explicitly only allow deletion from the local camera + deletepicture(src) + +obj/item/device/camera/siliconcam/proc/getsource() + if(istype(src.loc, /mob/living/silicon/ai)) + return src + + var/mob/living/silicon/robot/C = src.loc + var/obj/item/device/camera/siliconcam/Cinfo + if(C.connected_ai) + Cinfo = C.connected_ai.aiCamera + else + Cinfo = src + return Cinfo diff --git a/code/modules/paperwork/stamps.dm b/code/modules/paperwork/stamps.dm index 9f0378b5..5374d98b 100644 --- a/code/modules/paperwork/stamps.dm +++ b/code/modules/paperwork/stamps.dm @@ -9,7 +9,7 @@ w_class = 1.0 throw_speed = 7 throw_range = 15 - m_amt = 60 + matter = list("metal" = 60) item_color = "cargo" pressure_resistance = 2 attack_verb = list("stamped") diff --git a/code/modules/power/antimatter/shielding.dm b/code/modules/power/antimatter/shielding.dm index 9aa8f85c..4e17b6ca 100644 --- a/code/modules/power/antimatter/shielding.dm +++ b/code/modules/power/antimatter/shielding.dm @@ -210,8 +210,7 @@ proc/cardinalrange(var/center) throwforce = 5 throw_speed = 1 throw_range = 2 - m_amt = 100 - w_amt = 2000 + matter = list("metal" = 100,"waste" = 2000) /obj/item/device/am_shielding_container/attackby(var/obj/item/I, var/mob/user) if(istype(I, /obj/item/device/multitool) && istype(src.loc,/turf)) diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index 42a0db50..33d1d4e4 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -42,6 +42,14 @@ //NOTE: STUFF STOLEN FROM AIRLOCK.DM thx +/obj/machinery/power/apc/high + cell_type = /obj/item/weapon/cell/high + +/obj/machinery/power/apc/super + cell_type = /obj/item/weapon/cell/super + +/obj/machinery/power/apc/hyper + cell_type = /obj/item/weapon/cell/hyper /obj/machinery/power/apc name = "area power controller" @@ -53,7 +61,7 @@ var/areastring = null var/obj/item/weapon/cell/cell var/start_charge = 90 // initial cell charge % - var/cell_type = 5000 // 0=no cell, 1=regular, 2=high-cap (x5) <- old, now it's just 0=no cell, otherwise dictate cellcapacity by changing this value. 1 used to be 1000, 2 was 2500 + var/cell_type = /obj/item/weapon/cell/apc // 0=no cell, 1=regular, 2=high-cap (x5) <- old, now it's just 0=no cell, otherwise dictate cellcapacity by changing this value. 1 used to be 1000, 2 was 2500 var/opened = 0 //0=closed, 1=opened, 2=cover removed var/shorted = 0 var/lighting = 3 @@ -166,8 +174,7 @@ has_electronics = 2 //installed and secured // is starting with a power cell installed, create it and set its charge level if(cell_type) - src.cell = new/obj/item/weapon/cell(src) - cell.maxcharge = cell_type // cell_type is maximum charge (old default was 1000 or 2500 (values one and two respectively) + src.cell = new cell_type(src) cell.charge = start_charge * cell.maxcharge / 100.0 // (convert percentage to actual value) var/area/A = src.loc.loc @@ -507,7 +514,7 @@ user << "You start adding cables to the APC frame..." playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) if(do_after(user, 20) && C.amount >= 10) - var/turf/T = get_turf_loc(src) + var/turf/T = get_turf(src) var/obj/structure/cable/N = T.get_cable_node() if (prob(50) && electrocute_mob(usr, N, N)) var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread @@ -673,7 +680,7 @@ return // do APC interaction - user.set_machine(src) + //user.set_machine(src) src.interact(user) /obj/machinery/power/apc/attack_alien(mob/living/carbon/alien/humanoid/user) @@ -741,7 +748,7 @@ else return 0 // 0 = User is not a Malf AI -/obj/machinery/power/apc/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null) +/obj/machinery/power/apc/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) if(!user) return @@ -752,7 +759,7 @@ "powerCellStatus" = cell ? cell.percent() : null, "chargeMode" = chargemode, "chargingStatus" = charging, - "totalLoad" = lastused_equip + lastused_light + lastused_environ, + "totalLoad" = round(lastused_equip + lastused_light + lastused_environ), "coverLocked" = coverlocked, "siliconUser" = istype(user, /mob/living/silicon), "malfStatus" = get_malf_status(user), @@ -760,7 +767,7 @@ "powerChannels" = list( list( "title" = "Equipment", - "powerLoad" = lastused_equip, + "powerLoad" = round(lastused_equip), "status" = equipment, "topicParams" = list( "auto" = list("eqp" = 3), @@ -770,7 +777,7 @@ ), list( "title" = "Lighting", - "powerLoad" = lastused_light, + "powerLoad" = round(lastused_light), "status" = lighting, "topicParams" = list( "auto" = list("lgt" = 3), @@ -780,7 +787,7 @@ ), list( "title" = "Environment", - "powerLoad" = lastused_environ, + "powerLoad" = round(lastused_environ), "status" = environ, "topicParams" = list( "auto" = list("env" = 3), @@ -792,7 +799,7 @@ ) // 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 @@ -1142,9 +1149,20 @@ else return 0 +/obj/machinery/power/apc/proc/last_surplus() + if(terminal && terminal.powernet) + return terminal.powernet.last_surplus() + else + return 0 + +//Returns 1 if the APC should attempt to charge +/obj/machinery/power/apc/proc/attempt_charging() + return (chargemode && charging == 1 && operating) + /obj/machinery/power/apc/add_load(var/amount) if(terminal && terminal.powernet) - terminal.powernet.newload += amount + return terminal.powernet.draw_power(amount) + return 0 /obj/machinery/power/apc/avail() if(terminal) @@ -1163,9 +1181,6 @@ lastused_light = area.usage(LIGHT) lastused_equip = area.usage(EQUIP) lastused_environ = area.usage(ENVIRON) - if(area.powerupdate) - if(debug) log_debug("power update in [area.name] / [name]") - area.clear_usage() lastused_total = lastused_light + lastused_equip + lastused_environ @@ -1176,13 +1191,7 @@ var/last_ch = charging var/excess = surplus() - - if(!src.avail()) - main_status = 0 - else if(excess < 0) - main_status = 1 - else - main_status = 2 + var/power_excess = 0 var/perapc = 0 if(terminal && terminal.powernet) @@ -1191,31 +1200,36 @@ if(debug) log_debug( "Status: [main_status] - Excess: [excess] - Last Equip: [lastused_equip] - Last Light: [lastused_light]") + if(area.powerupdate) + log_debug("power update in [area.name] / [name]") + if(cell && !shorted) //var/cell_charge = cell.charge var/cell_maxcharge = cell.maxcharge - // draw power from cell as before + // Calculate how much power the APC will try to get from the grid. + var/target_draw = lastused_total + if (src.attempt_charging()) + target_draw += min((cell_maxcharge - cell.charge), (cell_maxcharge*CHARGELEVEL))/CELLRATE + target_draw = min(target_draw, perapc) //limit power draw by perapc - var/cellused = min(cell.charge, CELLRATE * lastused_total) // clamp deduction to a max, amount left in cell - cell.use(cellused) + // try to draw power from the grid + var/power_drawn = 0 + if (src.avail()) + power_drawn = add_load(target_draw) //get some power from the powernet - if(excess > 0 || perapc > lastused_total) // if power excess, or enough anyway, recharge the cell - // by the same amount just used - cell.give(cellused) - add_load(cellused/CELLRATE) // add the load used to recharge the cell + //figure out how much power is left over after meeting demand + power_excess = power_drawn - lastused_total + if (power_excess < 0) //couldn't get enough power from the grid, we will need to take from the power cell. - else // no excess, and not enough per-apc + charging = 0 - if( (cell.charge/CELLRATE+perapc) >= lastused_total) // can we draw enough from cell+grid to cover last usage? - - cell.give(CELLRATE * perapc) //recharge with what we can - add_load(perapc) // so draw what we can from the grid - charging = 0 + var/required_power = -power_excess + if(cell.charge >= required_power*CELLRATE) // can we draw enough from cell to cover what's left over? + cell.use(required_power*CELLRATE) else if (autoflag != 0) // not enough power available to run the last tick! - charging = 0 chargecount = 0 // This turns everything off in the case that there is still a charge left on the battery, just not enough to run the room. equipment = autoset(equipment, 0) @@ -1223,8 +1237,15 @@ environ = autoset(environ, 0) autoflag = 0 + //Set external power status + if (!power_drawn) + main_status = 0 + else if (power_excess < 0) + main_status = 1 + else + main_status = 2 - // set channels depending on how much charge we have left + // Set channels depending on how much charge we have left // Allow the APC to operate as normal if the cell can charge if(charging && longtermpower < 10) @@ -1249,8 +1270,8 @@ environ = autoset(environ, 1) area.poweralert(0, src) autoflag = 2 - else if(cell.charge < 750 && cell.charge > 10 && longtermpower < 0) // <15%, turn off lighting & equipment - if(autoflag != 1) + else if(cell.charge < 750 && cell.charge > 10) // <15%, turn off lighting & equipment + if((autoflag > 1 && longtermpower < 0) || (autoflag > 1 && longtermpower >= 0)) equipment = autoset(equipment, 2) lighting = autoset(lighting, 2) environ = autoset(environ, 1) @@ -1265,32 +1286,28 @@ autoflag = 0 // now trickle-charge the cell - - if(chargemode && charging == 1 && operating) - if(excess > 0) // check to make sure we have enough to charge - // Max charge is perapc share, capped to cell capacity, or % per second constant (Whichever is smallest) - var/ch = min(perapc*CELLRATE, (cell_maxcharge - cell.charge), (cell_maxcharge*CHARGELEVEL)) - add_load(ch/CELLRATE) // Removes the power we're taking from the grid - cell.give(ch) // actually recharge the cell - + if(src.attempt_charging()) + if (power_excess > 0) // check to make sure we have enough to charge + cell.give(power_excess*CELLRATE) // actually recharge the cell else charging = 0 // stop charging chargecount = 0 // show cell as fully charged if so - if(cell.charge >= cell_maxcharge) charging = 2 + //if we have excess power for long enough, think about re-enable charging. if(chargemode) if(!charging) - if(excess > cell_maxcharge*CHARGELEVEL) + //last_surplus() overestimates the amount of power available for charging, but it's equivalent to what APCs were doing before. + if(src.last_surplus()*CELLRATE >= cell_maxcharge*CHARGELEVEL) chargecount++ else chargecount = 0 + charging = 0 - if(chargecount == 10) - + if(chargecount >= 10) chargecount = 0 charging = 1 @@ -1319,21 +1336,21 @@ src.updateDialog() // val 0=off, 1=off(auto) 2=on 3=on(auto) -// on 0=off, 1=on, 2=autooff +// on 0=off, 1=auto-on, 2=auto-off /proc/autoset(var/val, var/on) - if(on==0) + if(on==0) // turn things off if(val==2) // if on, return off return 0 else if(val==3) // if auto-on, return auto-off return 1 - else if(on==1) + else if(on==1) // turn things auto-on if(val==1) // if auto-off, return auto-on return 3 - else if(on==2) + else if(on==2) // turn things auto-off if(val==3) // if auto-on, return auto-off return 1 diff --git a/code/modules/power/batteryrack.dm b/code/modules/power/batteryrack.dm new file mode 100644 index 00000000..e7a46b60 --- /dev/null +++ b/code/modules/power/batteryrack.dm @@ -0,0 +1,252 @@ +//Boards +/obj/item/weapon/circuitboard/batteryrack + name = "Circuit board (Battery rack PSU)" + build_path = "/obj/machinery/power/smes/batteryrack" + board_type = "machine" + origin_tech = "powerstorage=3;engineering=2" + frame_desc = "Requires 3 power cells." + req_components = list("/obj/item/weapon/cell" = 3) + + +/obj/item/weapon/circuitboard/ghettosmes + name = "Circuit board (makeshift PSU)" + desc = "An APC circuit repurposed into some power storage device controller" + build_path = "/obj/machinery/power/smes/batteryrack/makeshift" + board_type = "machine" + frame_desc = "Requires 3 power cells." + req_components = list("/obj/item/weapon/cell" = 3) + + +//Machines +//The one that works safely. +/obj/machinery/power/smes/batteryrack + name = "power cell rack PSU" + desc = "A rack of power cells working as a PSU." + charge = 0 //you dont really want to make a potato PSU which already is overloaded + online = 0 + chargelevel = 0 + output = 0 + input_level_max = 0 + output_level_max = 0 + icon_state = "gsmes" + var/cells_amount = 0 + var/capacitors_amount = 0 + + +/obj/machinery/power/smes/batteryrack/New() + ..() + add_parts() + RefreshParts() + return + + +//Maybe this should be moved up to obj/machinery +/obj/machinery/power/smes/batteryrack/proc/add_parts() + component_parts = list() + component_parts += new /obj/item/weapon/circuitboard/batteryrack + component_parts += new /obj/item/weapon/cell/high + component_parts += new /obj/item/weapon/cell/high + component_parts += new /obj/item/weapon/cell/high + return + + +/obj/machinery/power/smes/batteryrack/RefreshParts() + capacitors_amount = 0 + cells_amount = 0 + var/max_level = 0 //for both input and output + for(var/obj/item/weapon/stock_parts/capacitor/CP in component_parts) + max_level += CP.rating + capacitors_amount++ + input_level_max = 50000 + max_level * 20000 + output_level_max = 50000 + max_level * 20000 + + var/C = 0 + for(var/obj/item/weapon/cell/PC in component_parts) + C += PC.maxcharge + cells_amount++ + capacity = C * 40 //Basic cells are such crap. Hyper cells needed to get on normal SMES levels. + + +/obj/machinery/power/smes/batteryrack/updateicon() + overlays.Cut() + if(stat & BROKEN) return + + if (online) + overlays += image('icons/obj/power.dmi', "gsmes_outputting") + if(charging) + overlays += image('icons/obj/power.dmi', "gsmes_charging") + + var/clevel = chargedisplay() + if(clevel>0) + overlays += image('icons/obj/power.dmi', "gsmes_og[clevel]") + return + + +/obj/machinery/power/smes/batteryrack/chargedisplay() + return round(4 * charge/(capacity ? capacity : 5e6)) + + +/obj/machinery/power/smes/batteryrack/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) //these can only be moved by being reconstructed, solves having to remake the powernet. + ..() //SMES attackby for now handles screwdriver, cable coils and wirecutters, no need to repeat that here + if(open_hatch) + if(istype(W, /obj/item/weapon/crowbar)) + if (charge < (capacity / 100)) + if (!online && !chargemode) + playsound(get_turf(src), 'sound/items/Crowbar.ogg', 50, 1) + var/obj/machinery/constructable_frame/machine_frame/M = new /obj/machinery/constructable_frame/machine_frame(src.loc) + M.state = 2 + M.icon_state = "box_1" + for(var/obj/I in component_parts) + if(I.reliability != 100 && crit_fail) + I.crit_fail = 1 + I.loc = src.loc + del(src) + return 1 + else + user << "Turn off the [src] before dismantling it." + else + user << "Better let [src] discharge before dismantling it." + else if ((istype(W, /obj/item/weapon/stock_parts/capacitor) && (capacitors_amount < 5)) || (istype(W, /obj/item/weapon/cell) && (cells_amount < 5))) + if (charge < (capacity / 100)) + if (!online && !chargemode) + user.drop_item() + component_parts += W + W.loc = src + RefreshParts() + user << "You upgrade the [src] with [W.name]." + else + user << "Turn off the [src] before dismantling it." + else + user << "Better let [src] discharge before putting your hand inside it." + else + user.set_machine(src) + interact(user) + return 1 + return + + +//The shitty one that will blow up. +/obj/machinery/power/smes/batteryrack/makeshift + name = "makeshift PSU" + desc = "A rack of batteries connected by a mess of wires posing as a PSU." + var/overcharge_percent = 0 + + +/obj/machinery/power/smes/batteryrack/makeshift/add_parts() + component_parts = list() + component_parts += new /obj/item/weapon/circuitboard/ghettosmes + component_parts += new /obj/item/weapon/cell/high + component_parts += new /obj/item/weapon/cell/high + component_parts += new /obj/item/weapon/cell/high + return + + +/obj/machinery/power/smes/batteryrack/makeshift/updateicon() + overlays.Cut() + if(stat & BROKEN) return + + if (online) + overlays += image('icons/obj/power.dmi', "gsmes_outputting") + if(charging) + overlays += image('icons/obj/power.dmi', "gsmes_charging") + if (overcharge_percent > 100) + overlays += image('icons/obj/power.dmi', "gsmes_overcharge") + else + var/clevel = chargedisplay() + if(clevel>0) + overlays += image('icons/obj/power.dmi', "gsmes_og[clevel]") + return + +//This mess of if-elses and magic numbers handles what happens if the engies don't pay attention and let it eat too much charge +//What happens depends on how much capacity has the ghetto smes and how much it is overcharged. +//Under 1.2M: 5% of ion_act() per process() tick from 125% and higher overcharges. 1.2M is achieved with 3 high cells. +//[1.2M-2.4M]: 6% ion_act from 120%. 1% of EMP from 140%. +//(2.4M-3.6M] :7% ion_act from 115%. 1% of EMP from 130%. 1% of non-hull-breaching explosion at 150%. +//(3.6M-INFI): 8% ion_act from 115%. 2% of EMP from 125%. 1% of Hull-breaching explosion from 140%. +/obj/machinery/power/smes/batteryrack/makeshift/proc/overcharge_consequences() + switch (capacity) + if (0 to (1.2e6-1)) + if (overcharge_percent >= 125) + if (prob(5)) + ion_act() + if (1.2e6 to 2.4e6) + if (overcharge_percent >= 120) + if (prob(6)) + ion_act() + else + return + if (overcharge_percent >= 140) + if (prob(1)) + empulse(src.loc, 3, 8, 1) + if ((2.4e6+1) to 3.6e6) + if (overcharge_percent >= 115) + if (prob(7)) + ion_act() + else + return + if (overcharge_percent >= 130) + if (prob(1)) + empulse(src.loc, 3, 8, 1) + if (overcharge_percent >= 150) + if (prob(1)) + explosion(src.loc, 0, 1, 3, 5) + if ((3.6e6+1) to INFINITY) + if (overcharge_percent >= 115) + if (prob(8)) + ion_act() + else + return + if (overcharge_percent >= 125) + if (prob(2)) + empulse(src.loc, 4, 10, 1) + if (overcharge_percent >= 140) + if (prob(1)) + explosion(src.loc, 1, 3, 5, 8) + else //how the hell was this proc called for negative charge + charge = 0 + + +#define SMESRATE 0.05 // rate of internal charge to external power +/obj/machinery/power/smes/batteryrack/makeshift/process() + if(stat & BROKEN) return + + //store machine state to see if we need to update the icon overlays + var/last_disp = chargedisplay() + var/last_chrg = charging + var/last_onln = online + var/last_overcharge = overcharge_percent + + if(terminal) + var/excess = terminal.surplus() + + if(charging) + if(excess >= 0) // if there's power available, try to charge + var/load = min((capacity * 1.5 - charge)/SMESRATE, chargelevel) // charge at set rate, limited to spare capacity + load = add_load(load) // add the load to the terminal side network + charge += load * SMESRATE // increase the charge + + + else // if not enough capacity + charging = 0 // stop charging + + else + if (chargemode && excess > 0 && excess >= chargelevel) + charging = 1 + + if(online) // if outputting + lastout = min( charge/SMESRATE, output) //limit output to that stored + charge -= lastout*SMESRATE // reduce the storage (may be recovered in /restore() if excessive) + add_avail(lastout) // add output to powernet (smes side) + if(charge < 0.0001) + online = 0 // stop output if charge falls to zero + + overcharge_percent = round((charge / capacity) * 100) + if (overcharge_percent > 115) //115% is the minimum overcharge for anything to happen + overcharge_consequences() + + // only update icon if state changed + if(last_disp != chargedisplay() || last_chrg != charging || last_onln != online || ((overcharge_percent > 100) ^ (last_overcharge > 100))) + updateicon() + return + +#undef SMESRATE diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index b39973d7..681b4643 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -212,8 +212,7 @@ w_class = 2.0 throw_speed = 2 throw_range = 5 - m_amt = 50 - g_amt = 20 + matter = list("metal" = 50,"glass" = 20) flags = TABLEPASS | FPRINT | CONDUCT slot_flags = SLOT_BELT item_state = "coil" @@ -229,6 +228,8 @@ src.amount = length if (param_color) color = param_color + else + color = item_color pixel_x = rand(-2,2) pixel_y = rand(-2,2) updateicon() @@ -237,6 +238,7 @@ /obj/item/weapon/cable_coil/proc/updateicon() if (!color) color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_ORANGE, COLOR_WHITE, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN) + item_color = color if(amount == 1) icon_state = "coil1" name = "cable piece" @@ -306,7 +308,7 @@ return else - user << "You transfer [MAXCOIL - src.amount ] length\s of cable from one coil to the other." + user << "You transfer [MAXCOIL - C.amount ] length\s of cable from one coil to the other." src.amount -= (MAXCOIL-C.amount) src.updateicon() src.update_wclass() diff --git a/code/modules/power/cable_logic.dm b/code/modules/power/cable_logic.dm index a7d2316c..3b5b67a3 100644 --- a/code/modules/power/cable_logic.dm +++ b/code/modules/power/cable_logic.dm @@ -85,7 +85,7 @@ var/dir_output = 1 var/obj/structure/cable/input var/obj/structure/cable/output - icon = 'icons/obj/pipes/heat.dmi' + icon = 'icons/atmos/heat.dmi' icon_state = "intact" /obj/machinery/logic/oneinput/process() @@ -130,7 +130,7 @@ if( !(pn_input.avail >= LOGIC_HIGH)) pn_output.newavail = max(pn_output.avail, LOGIC_HIGH) //Set the output avilable power to 5 or whatever it was before. else - pn_output.newload += LOGIC_HIGH //Otherwise increase the load to 5 + pn_output.draw_power(LOGIC_HIGH) //Otherwise increase the load to 5 @@ -202,7 +202,7 @@ if( (pn_input1.avail >= LOGIC_HIGH) && (pn_input2.avail >= LOGIC_HIGH) ) pn_output.newavail = max(pn_output.avail, LOGIC_HIGH) //Set the output avilable power to 5 or whatever it was before. else - pn_output.newload += LOGIC_HIGH //Otherwise increase the load to 5 + pn_output.draw_power(LOGIC_HIGH) //Otherwise increase the load to 5 //OR GATE /obj/machinery/logic/twoinput/or/process() @@ -222,7 +222,7 @@ if( (pn_input1.avail >= LOGIC_HIGH) || (pn_input2.avail >= LOGIC_HIGH) ) pn_output.newavail = max(pn_output.avail, LOGIC_HIGH) //Set the output avilable power to 5 or whatever it was before. else - pn_output.newload += LOGIC_HIGH //Otherwise increase the load to 5 + pn_output.draw_power(LOGIC_HIGH) //Otherwise increase the load to 5 //XOR GATE /obj/machinery/logic/twoinput/xor/process() @@ -242,7 +242,7 @@ if( (pn_input1.avail >= LOGIC_HIGH) != (pn_input2.avail >= LOGIC_HIGH) ) pn_output.newavail = max(pn_output.avail, LOGIC_HIGH) //Set the output avilable power to 5 or whatever it was before. else - pn_output.newload += LOGIC_HIGH //Otherwise increase the load to 5 + pn_output.draw_power(LOGIC_HIGH) //Otherwise increase the load to 5 //XNOR GATE (EQUIVALENCE) /obj/machinery/logic/twoinput/xnor/process() @@ -262,7 +262,7 @@ if( (pn_input1.avail >= LOGIC_HIGH) == (pn_input2.avail >= LOGIC_HIGH) ) pn_output.newavail = max(pn_output.avail, LOGIC_HIGH) //Set the output avilable power to 5 or whatever it was before. else - pn_output.newload += LOGIC_HIGH //Otherwise increase the load to 5 + pn_output.draw_power(LOGIC_HIGH) //Otherwise increase the load to 5 #define RELAY_POWER_TRANSFER 2000 //How much power a relay transfers through. @@ -284,7 +284,7 @@ return if(pn_input2.avail >= RELAY_POWER_TRANSFER) - pn_input2.newload += RELAY_POWER_TRANSFER + pn_input2.draw_power(RELAY_POWER_TRANSFER) pn_output.newavail += RELAY_POWER_TRANSFER diff --git a/code/modules/power/cell.dm b/code/modules/power/cell.dm index 1d374f73..fd9fdfbf 100644 --- a/code/modules/power/cell.dm +++ b/code/modules/power/cell.dm @@ -22,6 +22,9 @@ /obj/item/weapon/cell/proc/percent() // return % charge of cell return 100.0*charge/maxcharge +/obj/item/weapon/cell/proc/fully_charged() + return (charge == maxcharge) + // use power from a cell /obj/item/weapon/cell/proc/use(var/amount) if(rigged && amount > 0) @@ -39,15 +42,15 @@ return 0 if(maxcharge < amount) return 0 - var/power_used = min(maxcharge-charge,amount) + var/amount_used = min(maxcharge-charge,amount) if(crit_fail) return 0 if(!prob(reliability)) minor_fault++ if(prob(minor_fault)) crit_fail = 1 return 0 - charge += power_used - return power_used + charge += amount_used + return amount_used /obj/item/weapon/cell/examine() diff --git a/code/modules/power/generator.dm b/code/modules/power/generator.dm index 24391046..1ae2795f 100644 --- a/code/modules/power/generator.dm +++ b/code/modules/power/generator.dm @@ -84,15 +84,17 @@ air2.temperature = air2.temperature + heat/air2_heat_capacity air1.temperature = air1.temperature - energy_transfer/air1_heat_capacity - //Transfer the air - circ1.air2.merge(air1) - circ2.air2.merge(air2) + //Transfer the air + if (air1) + circ1.air2.merge(air1) + if (air2) + circ2.air2.merge(air2) - //Update the gas networks - if(circ1.network2) - circ1.network2.update = 1 - if(circ2.network2) - circ2.network2.update = 1 + //Update the gas networks + if(circ1.network2) + circ1.network2.update = 1 + if(circ2.network2) + circ2.network2.update = 1 // update icon overlays and power usage only if displayed level has changed if(lastgen > 250000 && prob(10)) diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm index 2197fc5c..e5d0e529 100644 --- a/code/modules/power/lighting.dm +++ b/code/modules/power/lighting.dm @@ -34,7 +34,7 @@ var/ndir = get_dir(usr,on_wall) if (!(ndir in cardinal)) return - var/turf/loc = get_turf_loc(usr) + var/turf/loc = get_turf(usr) if (!istype(loc, /turf/simulated/floor)) usr << "\red [src.name] cannot be placed on this spot." return @@ -357,6 +357,7 @@ switchcount = L.switchcount rigged = L.rigged brightness = L.brightness + l_color = L.color on = has_power() update() @@ -516,6 +517,7 @@ L.status = status L.rigged = rigged L.brightness = src.brightness + L.color = l_color // light item inherits the switchcount, then zero it L.switchcount = switchcount @@ -541,6 +543,7 @@ L.status = status L.rigged = rigged L.brightness = brightness + L.color = l_color // light item inherits the switchcount, then zero it L.switchcount = switchcount @@ -648,7 +651,7 @@ var/status = 0 // LIGHT_OK, LIGHT_BURNED or LIGHT_BROKEN var/base_state var/switchcount = 0 // number of times switched - m_amt = 60 + matter = list("metal" = 60) var/rigged = 0 // true if rigged to explode var/brightness = 2 //how much light it gives off @@ -658,7 +661,7 @@ icon_state = "ltube" base_state = "ltube" item_state = "c_tube" - g_amt = 100 + matter = list("glass" = 100) brightness = 8 /obj/item/weapon/light/tube/large @@ -672,7 +675,7 @@ icon_state = "lbulb" base_state = "lbulb" item_state = "contvapour" - g_amt = 100 + matter = list("glass" = 100) brightness = 5 /obj/item/weapon/light/throw_impact(atom/hit_atom) @@ -685,7 +688,7 @@ icon_state = "fbulb" base_state = "fbulb" item_state = "egg4" - g_amt = 100 + matter = list("glass" = 100) brightness = 5 // update the icon state and description of the light diff --git a/code/modules/power/port_gen.dm b/code/modules/power/port_gen.dm index f8d8aea4..86f34b49 100644 --- a/code/modules/power/port_gen.dm +++ b/code/modules/power/port_gen.dm @@ -42,7 +42,7 @@ display round(lastgen) and plasmatank amount //Baseline portable generator. Has all the default handling. Not intended to be used on it's own (since it generates unlimited power). /obj/machinery/power/port_gen - name = "Portable Generator" + name = "Placeholder Generator" //seriously, don't use this. It can't be anchored without VV magic. desc = "A portable generator for emergency backup power" icon = 'icons/obj/power.dmi' icon_state = "portgen0" diff --git a/code/modules/power/power.dm b/code/modules/power/power.dm index 31932ca0..c044742a 100644 --- a/code/modules/power/power.dm +++ b/code/modules/power/power.dm @@ -20,11 +20,12 @@ /obj/machinery/power/proc/add_load(var/amount) if(powernet) - powernet.newload += amount + return powernet.draw_power(amount) + return 0 /obj/machinery/power/proc/surplus() if(powernet) - return powernet.avail-powernet.load + return powernet.surplus() else return 0 @@ -42,8 +43,10 @@ if(!src.loc) return 0 - if(!use_power) - return 1 + //This is bad. This makes machines which are switched off not update their stat flag correctly when power_change() is called. + //If use_power is 0, then you probably shouldn't be checking power to begin with. + //if(!use_power) + // return 1 var/area/A = src.loc.loc // make sure it's in an area if(!A || !isarea(A) || !A.master) @@ -62,18 +65,23 @@ chan = power_channel A.master.use_power(amount, chan) if(!autocalled) + log_power_update_request(A.master, src) A.master.powerupdate = 2 // Decremented by 2 each GC tick, since it's not auto power change we're going to update power twice. -/obj/machinery/proc/power_change() // called whenever the power settings of the containing area change +//The master_area optional argument can be used to save on a lot of processing if the master area is already known. This is mainly intended for when this proc is called by the master controller. +/obj/machinery/proc/power_change(var/area/master_area = null) // called whenever the power settings of the containing area change // by default, check equipment channel & set flag // can override if needed - if(powered(power_channel)) + var/has_power + if (master_area) + has_power = master_area.powered(power_channel) + else + has_power = powered(power_channel) + + if(has_power) stat &= ~NOPOWER else - stat |= NOPOWER - return - // the powernet datum // each contiguous network of cables & nodes @@ -222,234 +230,6 @@ . += C return . - -/proc/powernet_nextlink(var/obj/O, var/datum/powernet/PN) - var/list/P - - while(1) - if( istype(O,/obj/structure/cable) ) - var/obj/structure/cable/C = O - C.powernet = PN - P = C.get_connections() - - else if(O.anchored && istype(O,/obj/machinery/power)) - var/obj/machinery/power/M = O - M.powernet = PN - P = M.get_connections() - - else - return - - if(P.len == 0) return - - O = P[1] - - for(var/L = 2 to P.len) - powernet_nextlink(P[L], PN) - - -// cut a powernet at this cable object -/datum/powernet/proc/cut_cable(var/obj/structure/cable/C) - var/turf/T1 = C.loc - if(!T1) return - var/node = 0 - if(C.d1 == 0) - node = 1 - - var/turf/T2 - if(C.d2) T2 = get_step(T1, C.d2) - if(C.d1) T1 = get_step(T1, C.d1) - - - var/list/P1 = power_list(T1, C, C.d1) // what joins on to cut cable in dir1 - var/list/P2 = power_list(T2, C, C.d2) // what joins on to cut cable in dir2 - -// if(Debug) -// for(var/obj/O in P1) -// world.log << "P1: [O] at [O.x] [O.y] : [istype(O, /obj/structure/cable) ? "[O:d1]/[O:d2]" : null] " -// for(var/obj/O in P2) -// world.log << "P2: [O] at [O.x] [O.y] : [istype(O, /obj/structure/cable) ? "[O:d1]/[O:d2]" : null] " - - - if(P1.len == 0 || P2.len == 0)//if nothing in either list, then the cable was an endpoint no need to rebuild the powernet, - cables -= C //just remove cut cable from the list -// if(Debug) world.log << "Was end of cable" - return - - //null the powernet reference of all cables & nodes in this powernet - var/i=1 - while(i<=cables.len) - var/obj/structure/cable/Cable = cables[i] - if(Cable) - Cable.powernet = null - if(Cable == C) - cables.Cut(i,i+1) - continue - i++ - i=1 - while(i<=nodes.len) - var/obj/machinery/power/Node = nodes[i] - if(Node) - Node.powernet = null - i++ - - // remove the cut cable from the network -// C.netnum = -1 - - C.loc = null - - powernet_nextlink(P1[1], src) // propagate network from 1st side of cable, using current netnum //TODO? - - // now test to see if propagation reached to the other side - // if so, then there's a loop in the network - var/notlooped = 0 - for(var/O in P2) - if( istype(O, /obj/machinery/power) ) - var/obj/machinery/power/Machine = O - if(Machine.powernet != src) - notlooped = 1 - break - else if( istype(O, /obj/structure/cable) ) - var/obj/structure/cable/Cable = O - if(Cable.powernet != src) - notlooped = 1 - break - - if(notlooped) - // not looped, so make a new powernet - var/datum/powernet/PN = new() - powernets += PN - -// if(Debug) world.log << "Was not looped: spliting PN#[number] ([cables.len];[nodes.len])" - - i=1 - while(i<=cables.len) - var/obj/structure/cable/Cable = cables[i] - if(Cable && !Cable.powernet) // non-connected cables will have powernet=null, since they weren't reached by propagation - Cable.powernet = PN - cables.Cut(i,i+1) // remove from old network & add to new one - PN.cables += Cable - continue - i++ - - i=1 - while(i<=nodes.len) - var/obj/machinery/power/Node = nodes[i] - if(Node && !Node.powernet) - Node.powernet = PN - nodes.Cut(i,i+1) - PN.nodes[Node] = Node - continue - i++ - - // Disconnect machines connected to nodes - if(node) - for(var/obj/machinery/power/P in T1) - if(P.powernet && !P.powernet.nodes[src]) - P.disconnect_from_network() -// if(Debug) -// world.log << "Old PN#[number] : ([cables.len];[nodes.len])" -// world.log << "New PN#[PN.number] : ([PN.cables.len];[PN.nodes.len])" -// -// else -// if(Debug) -// world.log << "Was looped." -// //there is a loop, so nothing to be done -// return - - - -/datum/powernet/proc/reset() - load = newload - newload = 0 - avail = newavail - newavail = 0 - - - viewload = 0.8*viewload + 0.2*load - - viewload = round(viewload) - - var/numapc = 0 - - if(nodes && nodes.len) // Added to fix a bad list bug -- TLE - for(var/obj/machinery/power/terminal/term in nodes) - if( istype( term.master, /obj/machinery/power/apc ) ) - numapc++ - - if(numapc) - perapc = avail/numapc - - netexcess = avail - load - - if( netexcess > 100) // if there was excess power last cycle - if(nodes && nodes.len) - for(var/obj/machinery/power/smes/S in nodes) // find the SMESes in the network - if(S.powernet == src) - S.restore() // and restore some of the power that was used - else - error("[S.name] (\ref[S]) had a [S.powernet ? "different (\ref[S.powernet])" : "null"] powernet to our powernet (\ref[src]).") - nodes.Remove(S) - -/datum/powernet/proc/get_electrocute_damage() - switch(avail)/* - if (1300000 to INFINITY) - return min(rand(70,150),rand(70,150)) - if (750000 to 1300000) - return min(rand(50,115),rand(50,115)) - if (100000 to 750000-1) - return min(rand(35,101),rand(35,101)) - if (75000 to 100000-1) - return min(rand(30,95),rand(30,95)) - if (50000 to 75000-1) - return min(rand(25,80),rand(25,80)) - if (25000 to 50000-1) - return min(rand(20,70),rand(20,70)) - if (10000 to 25000-1) - return min(rand(20,65),rand(20,65)) - if (1000 to 10000-1) - return min(rand(10,20),rand(10,20))*/ - if (1000000 to INFINITY) - return min(rand(50,160),rand(50,160)) - if (200000 to 1000000) - return min(rand(25,80),rand(25,80)) - if (100000 to 200000)//Ave powernet - return min(rand(20,60),rand(20,60)) - if (50000 to 100000) - return min(rand(15,40),rand(15,40)) - if (1000 to 50000) - return min(rand(10,20),rand(10,20)) - else - return 0 - -//The powernet that calls this proc will consume the other powernet - Rockdtben -//TODO: rewrite so the larger net absorbs the smaller net -/proc/merge_powernets(var/datum/powernet/net1, var/datum/powernet/net2) - if(!net1 || !net2) return - if(net1 == net2) return - - //We assume net1 is larger. If net2 is in fact larger we are just going to make them switch places to reduce on code. - if(net1.cables.len < net2.cables.len) //net2 is larger than net1. Let's switch them around - var/temp = net1 - net1 = net2 - net2 = temp - - for(var/i=1,i<=net2.nodes.len,i++) //merge net2 into net1 - var/obj/machinery/power/Node = net2.nodes[i] - if(Node) - Node.powernet = net1 - net1.nodes[Node] = Node - - for(var/i=1,i<=net2.cables.len,i++) - var/obj/structure/cable/Cable = net2.cables[i] - if(Cable) - Cable.powernet = net1 - net1.cables += Cable - - del(net2) - return net1 - - /obj/machinery/power/proc/connect_to_network() var/turf/T = src.loc var/obj/structure/cable/C = T.get_cable_node() @@ -490,6 +270,9 @@ //No animations will be performed by this proc. /proc/electrocute_mob(mob/living/carbon/M as mob, var/power_source, var/obj/source, var/siemens_coeff = 1.0) if(istype(M.loc,/obj/mecha)) return 0 //feckin mechs are dumb + + //This is for performance optimization only. + //DO NOT modify siemens_coeff here. That is checked in human/electrocute_act() if(istype(M,/mob/living/carbon/human)) var/mob/living/carbon/human/H = M if(H.gloves) @@ -540,11 +323,11 @@ var/drained_energy = drained_hp*20 if (source_area) - source_area.use_power(drained_energy/CELLRATE) + source_area.use_power(drained_energy) else if (istype(power_source,/datum/powernet)) - var/drained_power = drained_energy/CELLRATE //convert from "joules" to "watts" - PN.newload+=drained_power + //var/drained_power = drained_energy/CELLRATE //convert from "joules" to "watts" <<< NO. THIS IS WRONG. CELLRATE DOES NOT CONVERT TO OR FROM JOULES. + PN.draw_power(drained_energy) else if (istype(power_source, /obj/item/weapon/cell)) - cell.use(drained_energy) + cell.use(drained_energy*CELLRATE) //convert to units of charge. return drained_energy diff --git a/code/modules/power/power_monitor.dm b/code/modules/power/power_monitor.dm new file mode 100644 index 00000000..e1920471 --- /dev/null +++ b/code/modules/power/power_monitor.dm @@ -0,0 +1,136 @@ +// the power monitoring computer +// for the moment, just report the status of all APCs in the same powernet +/obj/machinery/power/monitor + name = "power monitoring computer" + desc = "It monitors power levels across the station." + icon = 'icons/obj/computer.dmi' + icon_state = "power" + + //computer stuff + density = 1 + anchored = 1.0 + var/circuit = /obj/item/weapon/circuitboard/powermonitor + use_power = 1 + idle_power_usage = 300 + active_power_usage = 300 + +/obj/machinery/power/monitor/New() + ..() + var/obj/structure/cable/attached = null + var/turf/T = loc + if(isturf(T)) + attached = locate() in T + if(attached) + powernet = attached.get_powernet() + +/obj/machinery/power/monitor/attack_ai(mob/user) + add_fingerprint(user) + + if(stat & (BROKEN|NOPOWER)) + return + interact(user) + +/obj/machinery/power/monitor/attack_hand(mob/user) + add_fingerprint(user) + + if(stat & (BROKEN|NOPOWER)) + return + interact(user) + +/obj/machinery/power/monitor/interact(mob/user) + + if ( (get_dist(src, user) > 1 ) || (stat & (BROKEN|NOPOWER)) ) + if (!istype(user, /mob/living/silicon)) + user.unset_machine() + user << browse(null, "window=powcomp") + return + + + user.set_machine(src) + var/t = "Power Monitoring
" + + t += "

Refresh" + t += "

Close" + + if(!powernet) + t += "\red No connection" + else + + var/list/L = list() + for(var/obj/machinery/power/terminal/term in powernet.nodes) + if(istype(term.master, /obj/machinery/power/apc)) + var/obj/machinery/power/apc/A = term.master + L += A + + t += "
Total power: [powernet.avail] W
Total load: [num2text(powernet.viewload,10)] W
" + + t += "" + + if(L.len > 0) + var/total_demand = 0 + t += "Area Eqp./Lgt./Env. Load Cell
" + + var/list/S = list(" Off","AOff"," On", " AOn") + var/list/chg = list("N","C","F") + + for(var/obj/machinery/power/apc/A in L) + + t += copytext(add_tspace("\The [A.area]", 30), 1, 30) + t += " [S[A.equipment+1]] [S[A.lighting+1]] [S[A.environ+1]] [add_lspace(A.lastused_total, 6)] [A.cell ? "[add_lspace(round(A.cell.percent()), 3)]% [chg[A.charging+1]]" : " N/C"]
" + total_demand += A.lastused_total + + t += "
Total demand: [total_demand] W
" + t += "
" + + user << browse(t, "window=powcomp;size=420x900") + onclose(user, "powcomp") + + +/obj/machinery/power/monitor/Topic(href, href_list) + ..() + if( href_list["close"] ) + usr << browse(null, "window=powcomp") + usr.unset_machine() + return + if( href_list["update"] ) + src.updateDialog() + return + + +/obj/machinery/power/monitor/power_change() + ..() + if(stat & BROKEN) + icon_state = "broken" + else + if (stat & NOPOWER) + spawn(rand(0, 15)) + src.icon_state = "c_unpowered" + else + icon_state = initial(icon_state) + + +//copied from computer.dm +/obj/machinery/power/monitor/attackby(I as obj, user as mob) + if(istype(I, /obj/item/weapon/screwdriver) && circuit) + playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1) + if(do_after(user, 20)) + var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc ) + var/obj/item/weapon/circuitboard/M = new circuit( A ) + A.circuit = M + A.anchored = 1 + for (var/obj/C in src) + C.loc = src.loc + if (src.stat & BROKEN) + user << "\blue The broken glass falls out." + new /obj/item/weapon/shard( src.loc ) + A.state = 3 + A.icon_state = "3" + else + user << "\blue You disconnect the monitor." + A.state = 4 + A.icon_state = "4" + M.deconstruct(src) + del(src) + else + src.attack_hand(user) + return \ No newline at end of file diff --git a/code/modules/power/powernet.dm b/code/modules/power/powernet.dm new file mode 100644 index 00000000..1fbcc6f1 --- /dev/null +++ b/code/modules/power/powernet.dm @@ -0,0 +1,265 @@ +/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/perapc_excess = 0 + var/netexcess = 0 + +/datum/powernet/proc/process() + load = newload + newload = 0 + avail = newavail + newavail = 0 + + + viewload = 0.8*viewload + 0.2*load + + viewload = round(viewload) + + var/numapc = 0 + + if(nodes && nodes.len) // Added to fix a bad list bug -- TLE + for(var/obj/machinery/power/terminal/term in nodes) + if( istype( term.master, /obj/machinery/power/apc ) ) + numapc++ + + netexcess = avail - load + + if(numapc) + //very simple load balancing. If there was a net excess this tick then it must have been that some APCs used less than perapc, since perapc*numapc = avail + //Therefore we can raise the amount of power rationed out to APCs on the assumption that those APCs that used less than perapc will continue to do so. + //If that assumption fails, then some APCs will miss out on power next tick, however it will be rebalanced for the tick after. + if (netexcess >= 0) + perapc_excess += min(netexcess/numapc, (avail - perapc) - perapc_excess) + else + perapc_excess = 0 + + perapc = avail/numapc + perapc_excess + + if( netexcess > 100) // if there was excess power last cycle + if(nodes && nodes.len) + for(var/obj/machinery/power/smes/S in nodes) // find the SMESes in the network + if(S.powernet == src) + S.restore() // and restore some of the power that was used + else + error("[S.name] (\ref[S]) had a [S.powernet ? "different (\ref[S.powernet])" : "null"] powernet to our powernet (\ref[src]).") + nodes.Remove(S) + + + + +//Returns the amount of available power +/datum/powernet/proc/surplus() + return max(avail - newload, 0) + +//Returns the amount of excess power (before refunding to SMESs) from last tick. +//This is for machines that might adjust their power consumption using this data. +/datum/powernet/proc/last_surplus() + return max(avail - load, 0) + +//Attempts to draw power from a powernet. Returns the actual amount of power drawn +/datum/powernet/proc/draw_power(var/requested_amount) + var/surplus = max(avail - newload, 0) + var/actual_draw = min(requested_amount, surplus) + newload += actual_draw + + return actual_draw + +// cut a powernet at this cable object +/datum/powernet/proc/cut_cable(var/obj/structure/cable/C) + var/turf/T1 = C.loc + if(!T1) return + var/node = 0 + if(C.d1 == 0) + node = 1 + + var/turf/T2 + if(C.d2) T2 = get_step(T1, C.d2) + if(C.d1) T1 = get_step(T1, C.d1) + + + var/list/P1 = power_list(T1, C, C.d1) // what joins on to cut cable in dir1 + var/list/P2 = power_list(T2, C, C.d2) // what joins on to cut cable in dir2 + +// if(Debug) +// for(var/obj/O in P1) +// world.log << "P1: [O] at [O.x] [O.y] : [istype(O, /obj/structure/cable) ? "[O:d1]/[O:d2]" : null] " +// for(var/obj/O in P2) +// world.log << "P2: [O] at [O.x] [O.y] : [istype(O, /obj/structure/cable) ? "[O:d1]/[O:d2]" : null] " + + + if(P1.len == 0 || P2.len == 0)//if nothing in either list, then the cable was an endpoint no need to rebuild the powernet, + cables -= C //just remove cut cable from the list +// if(Debug) world.log << "Was end of cable" + return + + //null the powernet reference of all cables & nodes in this powernet + var/i=1 + while(i<=cables.len) + var/obj/structure/cable/Cable = cables[i] + if(Cable) + Cable.powernet = null + if(Cable == C) + cables.Cut(i,i+1) + continue + i++ + i=1 + while(i<=nodes.len) + var/obj/machinery/power/Node = nodes[i] + if(Node) + Node.powernet = null + i++ + + // remove the cut cable from the network +// C.netnum = -1 + + C.loc = null + + powernet_nextlink(P1[1], src) // propagate network from 1st side of cable, using current netnum //TODO? + + // now test to see if propagation reached to the other side + // if so, then there's a loop in the network + var/notlooped = 0 + for(var/O in P2) + if( istype(O, /obj/machinery/power) ) + var/obj/machinery/power/Machine = O + if(Machine.powernet != src) + notlooped = 1 + break + else if( istype(O, /obj/structure/cable) ) + var/obj/structure/cable/Cable = O + if(Cable.powernet != src) + notlooped = 1 + break + + if(notlooped) + // not looped, so make a new powernet + var/datum/powernet/PN = new() + powernets += PN + +// if(Debug) world.log << "Was not looped: spliting PN#[number] ([cables.len];[nodes.len])" + + i=1 + while(i<=cables.len) + var/obj/structure/cable/Cable = cables[i] + if(Cable && !Cable.powernet) // non-connected cables will have powernet=null, since they weren't reached by propagation + Cable.powernet = PN + cables.Cut(i,i+1) // remove from old network & add to new one + PN.cables += Cable + continue + i++ + + i=1 + while(i<=nodes.len) + var/obj/machinery/power/Node = nodes[i] + if(Node && !Node.powernet) + Node.powernet = PN + nodes.Cut(i,i+1) + PN.nodes[Node] = Node + continue + i++ + + // Disconnect machines connected to nodes + if(node) + for(var/obj/machinery/power/P in T1) + if(P.powernet && !P.powernet.nodes[src]) + P.disconnect_from_network() +// if(Debug) +// world.log << "Old PN#[number] : ([cables.len];[nodes.len])" +// world.log << "New PN#[PN.number] : ([PN.cables.len];[PN.nodes.len])" +// +// else +// if(Debug) +// world.log << "Was looped." +// //there is a loop, so nothing to be done +// return + +/datum/powernet/proc/get_electrocute_damage() + switch(avail)/* + if (1300000 to INFINITY) + return min(rand(70,150),rand(70,150)) + if (750000 to 1300000) + return min(rand(50,115),rand(50,115)) + if (100000 to 750000-1) + return min(rand(35,101),rand(35,101)) + if (75000 to 100000-1) + return min(rand(30,95),rand(30,95)) + if (50000 to 75000-1) + return min(rand(25,80),rand(25,80)) + if (25000 to 50000-1) + return min(rand(20,70),rand(20,70)) + if (10000 to 25000-1) + return min(rand(20,65),rand(20,65)) + if (1000 to 10000-1) + return min(rand(10,20),rand(10,20))*/ + if (1000000 to INFINITY) + return min(rand(50,160),rand(50,160)) + if (200000 to 1000000) + return min(rand(25,80),rand(25,80)) + if (100000 to 200000)//Ave powernet + return min(rand(20,60),rand(20,60)) + if (50000 to 100000) + return min(rand(15,40),rand(15,40)) + if (1000 to 50000) + return min(rand(10,20),rand(10,20)) + else + return 0 + +/proc/powernet_nextlink(var/obj/O, var/datum/powernet/PN) + var/list/P + + while(1) + if( istype(O,/obj/structure/cable) ) + var/obj/structure/cable/C = O + C.powernet = PN + P = C.get_connections() + + else if(O.anchored && istype(O,/obj/machinery/power)) + var/obj/machinery/power/M = O + M.powernet = PN + P = M.get_connections() + + else + return + + if(P.len == 0) return + + O = P[1] + + for(var/L = 2 to P.len) + powernet_nextlink(P[L], PN) + +//The powernet that calls this proc will consume the other powernet - Rockdtben +//TODO: rewrite so the larger net absorbs the smaller net +/proc/merge_powernets(var/datum/powernet/net1, var/datum/powernet/net2) + if(!net1 || !net2) return + if(net1 == net2) return + + //We assume net1 is larger. If net2 is in fact larger we are just going to make them switch places to reduce on code. + if(net1.cables.len < net2.cables.len) //net2 is larger than net1. Let's switch them around + var/temp = net1 + net1 = net2 + net2 = temp + + for(var/i=1,i<=net2.nodes.len,i++) //merge net2 into net1 + var/obj/machinery/power/Node = net2.nodes[i] + if(Node) + Node.powernet = net1 + net1.nodes[Node] = Node + + for(var/i=1,i<=net2.cables.len,i++) + var/obj/structure/cable/Cable = net2.cables[i] + if(Cable) + Cable.powernet = net1 + net1.cables += Cable + + del(net2) + return net1 \ No newline at end of file diff --git a/code/modules/power/profiling.dm b/code/modules/power/profiling.dm new file mode 100644 index 00000000..3cf8c3a0 --- /dev/null +++ b/code/modules/power/profiling.dm @@ -0,0 +1,71 @@ +datum + +var/global/enable_power_update_profiling = 0 + +var/global/power_profiled_time = 0 +var/global/power_last_profile_time = 0 +var/global/list/power_update_requests_by_machine = list() +var/global/list/power_update_requests_by_area = list() + +/proc/log_power_update_request(area/A, obj/machinery/M) + if (!enable_power_update_profiling) + return + + var/machine_type = "[M.type]" + if (machine_type in power_update_requests_by_machine) + power_update_requests_by_machine[machine_type] += 1 + else + power_update_requests_by_machine[machine_type] = 1 + + if (A.name in power_update_requests_by_area) + power_update_requests_by_area[A.name] += 1 + else + power_update_requests_by_area[A.name] = 1 + + power_profiled_time += (world.time - power_last_profile_time) + power_last_profile_time = world.time + +/client/proc/toggle_power_update_profiling() + set name = "Toggle Area Power Update Profiling" + set desc = "Toggles the recording of area power update requests." + set category = "Debug" + if(!check_rights(R_DEBUG)) return + + if(enable_power_update_profiling) + enable_power_update_profiling = 0 + + usr << "Area power update profiling disabled." + message_admins("[key_name(src)] toggled area power update profiling off.") + log_admin("[key_name(src)] toggled area power update profiling off.") + else + enable_power_update_profiling = 1 + power_last_profile_time = world.time + + usr << "Area power update profiling enabled." + message_admins("[key_name(src)] toggled area power update profiling on.") + log_admin("[key_name(src)] toggled area power update profiling on.") + + feedback_add_details("admin_verb","APUP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/view_power_update_stats_machines() + set name = "View Area Power Update Statistics By Machines" + set desc = "See which types of machines are triggering area power updates." + set category = "Debug" + + if(!check_rights(R_DEBUG)) return + + usr << "Total profiling time: [power_profiled_time] ticks" + for (var/M in power_update_requests_by_machine) + usr << "[M] = [power_update_requests_by_machine[M]]" + +/client/proc/view_power_update_stats_area() + set name = "View Area Power Update Statistics By Area" + set desc = "See which areas are having area power updates." + set category = "Debug" + + if(!check_rights(R_DEBUG)) return + + usr << "Total profiling time: [power_profiled_time] ticks" + usr << "Total profiling time: [power_profiled_time] ticks" + for (var/A in power_update_requests_by_area) + usr << "[A] = [power_update_requests_by_area[A]]" \ No newline at end of file diff --git a/code/modules/power/singularity/collector.dm b/code/modules/power/singularity/collector.dm index 7c0c4632..3f6e9416 100644 --- a/code/modules/power/singularity/collector.dm +++ b/code/modules/power/singularity/collector.dm @@ -13,6 +13,7 @@ var/global/list/rad_collectors = list() // use_power = 0 var/obj/item/weapon/tank/plasma/P = null var/last_power = 0 + var/last_power_new = 0 var/active = 0 var/locked = 0 var/drainratio = 1 @@ -26,13 +27,17 @@ var/global/list/rad_collectors = list() ..() /obj/machinery/power/rad_collector/process() + //so that we don't zero out the meter if the SM is processed first. + last_power = last_power_new + last_power_new = 0 + + if(P) - if(P.air_contents.toxins <= 0) + if(P.air_contents.gas["toxins"] == 0) investigate_log("out of fuel.","singulo") - P.air_contents.toxins = 0 eject() else - P.air_contents.adjust(tx = -0.001*drainratio) + P.air_contents.adjust_gas("toxins", -0.001*drainratio) return @@ -42,7 +47,7 @@ var/global/list/rad_collectors = list() toggle_power() user.visible_message("[user.name] turns the [src.name] [active? "on":"off"].", \ "You turn the [src.name] [active? "on":"off"].") - investigate_log("turned [active?"on":"off"] by [user.key]. [P?"Fuel: [round(P.air_contents.toxins/0.29)]%":"It is empty"].","singulo") + investigate_log("turned [active?"on":"off"] by [user.key]. [P?"Fuel: [round(P.air_contents.gas["toxins"]/0.29)]%":"It is empty"].","singulo") return else user << "\red The controls are locked!" @@ -51,10 +56,7 @@ var/global/list/rad_collectors = list() /obj/machinery/power/rad_collector/attackby(obj/item/W, mob/user) - if(istype(W, /obj/item/device/analyzer)) - user << "\blue The [W.name] detects that [last_power]W were recently produced." - return 1 - else if(istype(W, /obj/item/weapon/tank/plasma)) + if(istype(W, /obj/item/weapon/tank/plasma)) if(!src.anchored) user << "\red The [src] needs to be secured to the floor first." return 1 @@ -97,6 +99,11 @@ var/global/list/rad_collectors = list() ..() return 1 +/obj/machinery/power/rad_collector/examine() + ..() + if (get_dist(usr, src) <= 3) + usr << "The meter indicates that \the [src] is collecting [last_power] W." + return 1 /obj/machinery/power/rad_collector/ex_act(severity) switch(severity) @@ -121,9 +128,9 @@ var/global/list/rad_collectors = list() /obj/machinery/power/rad_collector/proc/receive_pulse(var/pulse_strength) if(P && active) var/power_produced = 0 - power_produced = P.air_contents.toxins*pulse_strength*20 + power_produced = P.air_contents.gas["toxins"]*pulse_strength*20 add_avail(power_produced) - last_power = power_produced + last_power_new = power_produced return return diff --git a/code/modules/power/singularity/containment_field.dm b/code/modules/power/singularity/containment_field.dm index e6aa55f2..6cd2b1de 100644 --- a/code/modules/power/singularity/containment_field.dm +++ b/code/modules/power/singularity/containment_field.dm @@ -56,11 +56,7 @@ if(!FG1 || !FG2) del(src) return 0 - if(iscarbon(user)) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(5, 1, user.loc) - s.start() - + if(isliving(user)) hasShocked = 1 var/shock_damage = min(rand(30,40),rand(30,40)) user.electrocute_act(shock_damage, src) @@ -69,27 +65,8 @@ user.throw_at(target, 200, 4) sleep(20) + hasShocked = 0 - return - - else if(issilicon(user)) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(5, 1, user.loc) - s.start() - - hasShocked = 1 - var/shock_damage = rand(15,30) - user.take_overall_damage(0,shock_damage) - user.visible_message("\red [user.name] was shocked by the [src.name]!", \ - "\red Energy pulse detected, system damaged!", \ - "\red You hear an electrical crack") - if(prob(20)) - user.Stun(2) - - sleep(20) - hasShocked = 0 - return - return /obj/machinery/containment_field/proc/set_master(var/master1,var/master2) diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm index 0a22877d..0bf96f02 100644 --- a/code/modules/power/singularity/emitter.dm +++ b/code/modules/power/singularity/emitter.dm @@ -1,4 +1,4 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:33 +#define EMITTER_DAMAGE_POWER_TRANSFER 450 //used to transfer power to containment field generators /obj/machinery/power/emitter name = "Emitter" @@ -8,14 +8,17 @@ anchored = 0 density = 1 req_access = list(access_engine_equip) + var/id = null - use_power = 0 - idle_power_usage = 10 - active_power_usage = 300 + use_power = 0 //uses powernet power, not APC power + active_power_usage = 30000 //30 kW laser. I guess that means 30 kJ per shot. var/active = 0 var/powered = 0 var/fire_delay = 100 + var/max_burst_delay = 100 + var/min_burst_delay = 20 + var/burst_shots = 3 var/last_shot = 0 var/shot_number = 0 var/state = 0 @@ -52,9 +55,11 @@ else icon_state = "emitter" - /obj/machinery/power/emitter/attack_hand(mob/user as mob) src.add_fingerprint(user) + activate(user) + +/obj/machinery/power/emitter/proc/activate(mob/user as mob) if(state == 2) if(!powernet) user << "The emitter isn't connected to a wire." @@ -104,8 +109,7 @@ return if(((src.last_shot + src.fire_delay) <= world.time) && (src.active == 1)) - if(!active_power_usage || avail(active_power_usage)) - add_load(active_power_usage) + if(surplus() >= active_power_usage && add_load(active_power_usage) >= active_power_usage) //does the laser have enough power to shoot? if(!powered) powered = 1 update_icon() @@ -118,13 +122,19 @@ return src.last_shot = world.time - if(src.shot_number < 3) + if(src.shot_number < burst_shots) src.fire_delay = 2 src.shot_number ++ else - src.fire_delay = rand(20,100) + src.fire_delay = rand(min_burst_delay, max_burst_delay) src.shot_number = 0 + + //need to calculate the power per shot as the emitter doesn't fire continuously. + var/burst_time = (min_burst_delay + max_burst_delay)/2 + 2*(burst_shots-1) + var/power_per_shot = active_power_usage * (burst_time/10) / burst_shots var/obj/item/projectile/beam/emitter/A = new /obj/item/projectile/beam/emitter( src.loc ) + A.damage = round(power_per_shot/EMITTER_DAMAGE_POWER_TRANSFER) + playsound(src.loc, 'sound/weapons/emitter.ogg', 25, 1) if(prob(35)) var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread diff --git a/code/modules/power/singularity/field_generator.dm b/code/modules/power/singularity/field_generator.dm index b4289730..96740dd7 100644 --- a/code/modules/power/singularity/field_generator.dm +++ b/code/modules/power/singularity/field_generator.dm @@ -12,7 +12,7 @@ field_generator power level display -Aygar */ -#define field_generator_max_power 250 +#define field_generator_max_power 250000 /obj/machinery/field_generator name = "Field Generator" desc = "A large thermal battery that projects a high amount of energy when powered." @@ -25,12 +25,16 @@ field_generator power level display var/Varedit_start = 0 var/Varpower = 0 var/active = 0 - var/power = 20 // Current amount of power + var/power = 30000 // Current amount of power var/state = 0 var/warming_up = 0 var/list/obj/machinery/containment_field/fields var/list/obj/machinery/field_generator/connected_gens var/clean_up = 0 + + //If keeping field generators powered is hard then increase the emitter active power usage. + var/gen_power_draw = 5500 //power needed per generator + var/field_power_draw = 2000 //power needed per field object /obj/machinery/field_generator/update_icon() @@ -167,8 +171,8 @@ field_generator power level display return 0 /obj/machinery/field_generator/bullet_act(var/obj/item/projectile/Proj) - if(Proj.flag != "bullet") - power += Proj.damage + if(istype(Proj, /obj/item/projectile/beam)) + power += Proj.damage * EMITTER_DAMAGE_POWER_TRANSFER update_icon() return 0 @@ -206,51 +210,44 @@ field_generator power level display if(src.power > field_generator_max_power) src.power = field_generator_max_power - var/power_draw = 2 + var/power_draw = gen_power_draw + for(var/obj/machinery/field_generator/FG in connected_gens) + if (!isnull(FG)) + power_draw += gen_power_draw for (var/obj/machinery/containment_field/F in fields) - if (isnull(F)) - continue - power_draw++ - if(draw_power(round(power_draw/2,1))) + if (!isnull(F)) + power_draw += field_power_draw + power_draw /= 2 //because this will be mirrored for both generators + if(draw_power(round(power_draw)) >= power_draw) return 1 else for(var/mob/M in viewers(src)) - M.show_message("\red The [src.name] shuts down!") + M.show_message("\red \The [src] shuts down!") turn_off() investigate_log("ran out of power and deactivated","singulo") src.power = 0 return 0 -//This could likely be better, it tends to start loopin if you have a complex generator loop setup. Still works well enough to run the engine fields will likely recode the field gens and fields sometime -Mport -/obj/machinery/field_generator/proc/draw_power(var/draw = 0, var/failsafe = 0, var/obj/machinery/field_generator/G = null, var/obj/machinery/field_generator/last = null) - if(Varpower) - return 1 - if((G && G == src) || (failsafe >= 8))//Loopin, set fail - return 0 - else - failsafe++ +//Tries to draw the needed power from our own power reserve, or connected generators if we can. Returns the amount of power we were able to get. +/obj/machinery/field_generator/proc/draw_power(var/draw = 0, var/list/flood_list = list()) + flood_list += src + if(src.power >= draw)//We have enough power src.power -= draw - return 1 - else//Need more power - draw -= src.power - src.power = 0 - for(var/obj/machinery/field_generator/FG in connected_gens) - if(isnull(FG)) - continue - if(FG == last)//We just asked you - continue - if(G)//Another gen is askin for power and we dont have it - if(FG.draw_power(draw,failsafe,G,src))//Can you take the load - return 1 - else - return 0 - else//We are askin another for power - if(FG.draw_power(draw,failsafe,src,src)) - return 1 - else - return 0 - + return draw + + //Need more power + var/actual_draw = src.power //already checked that power < draw + src.power = 0 + + for(var/obj/machinery/field_generator/FG in connected_gens) + if (FG in flood_list) + continue + actual_draw += FG.draw_power(draw - actual_draw, flood_list) //since the flood list reference is shared this actually works. + if (actual_draw >= draw) + return actual_draw + + return actual_draw /obj/machinery/field_generator/proc/start_fields() if(!src.state == 2 || !anchored) diff --git a/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm b/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm index 783cdcfc..60ad2c15 100644 --- a/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm +++ b/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm @@ -6,7 +6,8 @@ proc emit_particle() 1 power box -the only part of this thing that uses power, can hack to mess with the pa/make it better +the only part of this thing that uses power, can hack to mess with the pa/make it better. +Lies, only the control computer draws power. 1 fuel chamber contains procs for mixing gas and whatever other fuel it uses @@ -71,7 +72,6 @@ So, hopefully this is helpful if any more icons are to be added/changed/wonderin var/strength = null var/desc_holder = null - /obj/structure/particle_accelerator/end_cap name = "Alpha Particle Generation Array" desc_holder = "This is where Alpha particles are generated from \[REDACTED\]" @@ -138,33 +138,29 @@ So, hopefully this is helpful if any more icons are to be added/changed/wonderin /obj/structure/particle_accelerator/ex_act(severity) switch(severity) if(1.0) - Del(src) + del(src) return if(2.0) if (prob(50)) - Del(src) + del(src) return if(3.0) if (prob(25)) - Del(src) + del(src) return else return -/obj/structure/particle_accelerator/Del() - if(master && master.connected_parts) - master.connected_parts.Remove(src) - ..() /obj/structure/particle_accelerator/blob_act() if(prob(50)) - Del() + del(src) return /obj/structure/particle_accelerator/meteorhit() if(prob(50)) - Del() + del(src) return /obj/structure/particle_accelerator/update_icon() diff --git a/code/modules/power/singularity/particle_accelerator/particle_control.dm b/code/modules/power/singularity/particle_accelerator/particle_control.dm index cdc4013d..b0eb96e1 100644 --- a/code/modules/power/singularity/particle_accelerator/particle_control.dm +++ b/code/modules/power/singularity/particle_accelerator/particle_control.dm @@ -10,7 +10,7 @@ density = 1 use_power = 0 idle_power_usage = 500 - active_power_usage = 10000 + active_power_usage = 70000 //70 kW per unit of strength construction_state = 0 active = 0 dir = 1 @@ -20,6 +20,7 @@ /obj/machinery/particle_accelerator/control_box/New() connected_parts = list() + active_power_usage = initial(active_power_usage) * (strength + 1) ..() @@ -29,7 +30,7 @@ /obj/machinery/particle_accelerator/control_box/update_state() if(construction_state < 3) - use_power = 0 + update_use_power(0) assembled = 0 active = 0 for(var/obj/structure/particle_accelerator/part in connected_parts) @@ -39,18 +40,12 @@ connected_parts = list() return if(!part_scan()) - use_power = 1 + update_use_power(1) active = 0 connected_parts = list() return -/obj/machinery/particle_accelerator/control_box/Del() - if(active) - toggle_power() - process() - ..() - /obj/machinery/particle_accelerator/control_box/update_icon() if(active) icon_state = "[reference]p1" @@ -93,6 +88,7 @@ else if(href_list["scan"]) src.part_scan() else if(href_list["strengthup"]) + var/old_strength = strength strength++ if(strength > 2) strength = 2 @@ -104,8 +100,13 @@ for(var/obj/structure/particle_accelerator/part in connected_parts) part.strength = strength part.update_icon() + + if (strength != old_strength) + active_power_usage = initial(active_power_usage) * (strength + 1) + use_power(0) //update power usage else if(href_list["strengthdown"]) + var/old_strength = strength strength-- if(strength < 0) strength = 0 @@ -117,6 +118,10 @@ for(var/obj/structure/particle_accelerator/part in connected_parts) part.strength = strength part.update_icon() + + if (strength != old_strength) + active_power_usage = initial(active_power_usage) * (strength + 1) + use_power(0) //update power usage src.updateDialog() src.update_icon() return @@ -126,9 +131,9 @@ ..() if(stat & NOPOWER) active = 0 - use_power = 0 + update_use_power(0) else if(!stat && construction_state == 3) - use_power = 1 + update_use_power(1) return @@ -191,7 +196,6 @@ if(PA.connect_master(src)) if(PA.report_ready(src)) src.connected_parts.Add(PA) - PA.master = src return 1 return 0 @@ -199,13 +203,13 @@ /obj/machinery/particle_accelerator/control_box/proc/toggle_power() src.active = !src.active if(src.active) - src.use_power = 2 + update_use_power(2) for(var/obj/structure/particle_accelerator/part in connected_parts) part.strength = src.strength part.powered = 1 part.update_icon() else - src.use_power = 1 + update_use_power(1) for(var/obj/structure/particle_accelerator/part in connected_parts) part.strength = null part.powered = 0 @@ -225,7 +229,7 @@ dat += "Particle Accelerator Control Panel
" dat += "Close

" dat += "Status:
" - if(!assembled || connected_parts.len < 6) + if(!assembled) dat += "Unable to detect all parts!
" dat += "Run Scan

" else diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm index 010bcd61..89ac5091 100644 --- a/code/modules/power/singularity/singularity.dm +++ b/code/modules/power/singularity/singularity.dm @@ -481,8 +481,9 @@ var/global/list/uneatable = list( /obj/machinery/singularity/narsie/large/New() ..() world << "NAR-SIE HAS RISEN" - if(emergency_shuttle) - emergency_shuttle.incall(0.5) // Cannot recall + if(emergency_shuttle && emergency_shuttle.can_call()) + emergency_shuttle.call_evac() + emergency_shuttle.launch_time = 0 // Cannot recall /obj/machinery/singularity/narsie/process() eat() diff --git a/code/modules/power/smes.dm b/code/modules/power/smes.dm index aded3dfa..512b9429 100644 --- a/code/modules/power/smes.dm +++ b/code/modules/power/smes.dm @@ -1,8 +1,8 @@ // the SMES // stores power -#define SMESMAXCHARGELEVEL 200000 -#define SMESMAXOUTPUT 200000 +#define SMESMAXCHARGELEVEL 250000 +#define SMESMAXOUTPUT 250000 /obj/machinery/power/smes name = "power storage unit" @@ -11,26 +11,33 @@ density = 1 anchored = 1 use_power = 0 - var/output = 50000 - var/lastout = 0 - var/loaddemand = 0 - var/capacity = 5e6 - var/charge = 1e6 - var/charging = 0 - var/chargemode = 0 - var/chargecount = 0 - var/chargelevel = 50000 - var/online = 1 + var/output = 50000 //Amount of power it tries to output + var/lastout = 0 //Amount of power it actually outputs to the powernet + var/loaddemand = 0 //For use in restore() + var/capacity = 5e6 //Maximum amount of power it can hold + var/charge = 1.0e6 //Current amount of power it holds + var/charging = 0 //1 if it's actually charging, 0 if not + var/chargemode = 0 //1 if it's trying to charge, 0 if not. + //var/chargecount = 0 + var/chargelevel = 50000 //Amount of power it tries to charge from powernet + var/online = 1 //1 if it's outputting power, 0 if not. var/name_tag = null var/obj/machinery/power/terminal/terminal = null //Holders for powerout event. var/last_output = 0 var/last_charge = 0 var/last_online = 0 + var/open_hatch = 0 + var/building_terminal = 0 //Suggestions about how to avoid clickspam building several terminals accepted! + var/input_level_max = SMESMAXCHARGELEVEL + var/output_level_max = SMESMAXOUTPUT /obj/machinery/power/smes/New() ..() spawn(5) + if(!powernet) + connect_to_network() + dir_loop: for(var/d in cardinal) var/turf/T = get_step(src, d) @@ -42,6 +49,8 @@ stat |= BROKEN return terminal.master = src + if(!terminal.powernet) + terminal.connect_to_network() updateicon() return @@ -71,7 +80,6 @@ /obj/machinery/power/smes/process() - if(stat & BROKEN) return //store machine state to see if we need to update the icon overlays @@ -80,41 +88,23 @@ var/last_onln = online if(terminal) - var/excess = terminal.surplus() + //If chargemod is set, try to charge + //Use charging to let the player know whether we were able to obtain our target load. + //TODO: Add a meter to tell players how much charge we are actually getting, and only set charging to 0 when we are unable to get any charge at all. + if(chargemode) + var/target_load = min((capacity-charge)/SMESRATE, chargelevel) // charge at set rate, limited to spare capacity + var/actual_load = add_load(target_load) // add the load to the terminal side network + charge += actual_load * SMESRATE // increase the charge - if(charging) - if(excess >= 0) // if there's power available, try to charge - - var/load = min((capacity-charge)/SMESRATE, chargelevel) // charge at set rate, limited to spare capacity - - charge += load * SMESRATE // increase the charge - - add_load(load) // add the load to the terminal side network - - else // if not enough capcity - charging = 0 // stop charging - chargecount = 0 - - else - if(chargemode) - if(chargecount > rand(3,6)) - charging = 1 - chargecount = 0 - - if(excess > chargelevel) - chargecount++ - else - chargecount = 0 + if (actual_load >= target_load) // did the powernet have enough power available for us? + charging = 1 else - chargecount = 0 + charging = 0 if(online) // if outputting lastout = min( charge/SMESRATE, output) //limit output to that stored - charge -= lastout*SMESRATE // reduce the storage (may be recovered in /restore() if excessive) - add_avail(lastout) // add output to powernet (smes side) - if(charge < 0.0001) online = 0 // stop output if charge falls to zero @@ -155,10 +145,40 @@ updateicon() return +//Will return 1 on failure +/obj/machinery/power/smes/proc/make_terminal(const/mob/user) + if (user.loc == loc) + user << "You must not be on the same tile as the [src]." + return 1 + + //Direction the terminal will face to + var/tempDir = get_dir(user, src) + switch(tempDir) + if (NORTHEAST, SOUTHEAST) + tempDir = EAST + if (NORTHWEST, SOUTHWEST) + tempDir = WEST + var/turf/tempLoc = get_step(src, reverse_direction(tempDir)) + if (istype(tempLoc, /turf/space)) + user << "You can't build a terminal on space." + return 1 + else if (istype(tempLoc)) + if(tempLoc.intact) + user << "You must remove the floor plating first." + return 1 + user << "You start adding cable to the [src]." + if(do_after(user, 50)) + terminal = new /obj/machinery/power/terminal(tempLoc) + terminal.dir = tempDir + terminal.master = src + return 0 + return 1 + /obj/machinery/power/smes/add_load(var/amount) if(terminal && terminal.powernet) - terminal.powernet.newload += amount + return terminal.powernet.draw_power(amount) + return 0 /obj/machinery/power/smes/attack_ai(mob/user) @@ -169,11 +189,62 @@ /obj/machinery/power/smes/attack_hand(mob/user) add_fingerprint(user) ui_interact(user) - - -/obj/machinery/power/smes/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null) - if(stat & BROKEN) + +/obj/machinery/power/smes/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + if(istype(W, /obj/item/weapon/screwdriver)) + if(!open_hatch) + open_hatch = 1 + user << "You open the maintenance hatch of [src]." + else + open_hatch = 0 + user << "You close the maintenance hatch of [src]." + if (open_hatch) + if(istype(W, /obj/item/weapon/cable_coil) && !terminal && !building_terminal) + building_terminal = 1 + var/obj/item/weapon/cable_coil/CC = W + if (CC.amount < 10) + user << "You need more cables." + building_terminal = 0 + return + if (make_terminal(user)) + building_terminal = 0 + return + building_terminal = 0 + CC.use(10) + user.visible_message(\ + "[user.name] has added cables to the [src].",\ + "You added cables to the [src].") + terminal.connect_to_network() + stat = 0 + + else if(istype(W, /obj/item/weapon/wirecutters) && terminal && !building_terminal) + building_terminal = 1 + var/turf/tempTDir = terminal.loc + if (istype(tempTDir)) + if(tempTDir.intact) + user << "You must remove the floor plating first." + else + user << "You begin to cut the cables..." + playsound(get_turf(src), 'sound/items/Deconstruct.ogg', 50, 1) + if(do_after(user, 50)) + if (prob(50) && electrocute_mob(usr, terminal.powernet, terminal)) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(5, 1, src) + s.start() + building_terminal = 0 + return + new /obj/item/weapon/cable_coil(loc,10) + user.visible_message(\ + "[user.name] cut the cables and dismantled the power terminal.",\ + "You cut the cables and dismantle the power terminal.") + del(terminal) + building_terminal = 0 + + +/obj/machinery/power/smes/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + + if(stat & BROKEN) return // this is the data which will be sent to the ui @@ -183,25 +254,25 @@ data["charging"] = charging data["chargeMode"] = chargemode data["chargeLevel"] = chargelevel - data["chargeMax"] = SMESMAXCHARGELEVEL + data["chargeMax"] = input_level_max data["outputOnline"] = online data["outputLevel"] = output - data["outputMax"] = SMESMAXOUTPUT + data["outputMax"] = output_level_max data["outputLoad"] = round(loaddemand) // 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 ui = new(user, src, ui_key, "smes.tmpl", "SMES Power Storage Unit", 540, 380) // when the ui is first opened this is the data it will use - ui.set_initial_data(data) + 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/power/smes/Topic(href, href_list) ..() @@ -236,30 +307,29 @@ if("min") chargelevel = 0 if("max") - chargelevel = SMESMAXCHARGELEVEL //30000 + chargelevel = input_level_max if("set") - chargelevel = input(usr, "Enter new input level (0-[SMESMAXCHARGELEVEL])", "SMES Input Power Control", chargelevel) as num - chargelevel = max(0, min(SMESMAXCHARGELEVEL, chargelevel)) // clamp to range + chargelevel = input(usr, "Enter new input level (0-[input_level_max])", "SMES Input Power Control", chargelevel) as num + chargelevel = max(0, min(input_level_max, chargelevel)) // clamp to range else if( href_list["output"] ) switch( href_list["output"] ) if("min") output = 0 if("max") - output = SMESMAXOUTPUT //30000 + output = output_level_max if("set") - output = input(usr, "Enter new output level (0-[SMESMAXOUTPUT])", "SMES Output Power Control", output) as num - output = max(0, min(SMESMAXOUTPUT, output)) // clamp to range + output = input(usr, "Enter new output level (0-[output_level_max])", "SMES Output Power Control", output) as num + output = max(0, min(output_level_max, output)) // clamp to range investigate_log("input/output; [chargelevel>output?"":""][chargelevel]/[output] | Output-mode: [online?"on":"off"] | Input-mode: [chargemode?"auto":"off"] by [usr.key]","singulo") - + return 1 - + /obj/machinery/power/smes/proc/ion_act() if(src.z == 1) if(prob(1)) //explosion - world << "\red SMES explosion in [src.loc.loc]" for(var/mob/M in viewers(src)) M.show_message("\red The [src.name] is making strange noises!", 3, "\red You hear sizzling electronics.", 2) sleep(10*pick(4,5,6,7,10,14)) @@ -267,11 +337,10 @@ smoke.set_up(3, 0, src.loc) smoke.attach(src) smoke.start() - explosion(src.loc, -1, 0, 1, 3, 0) + explosion(src.loc, -1, 0, 1, 3, 1, 0) del(src) return if(prob(15)) //Power drain - world << "\red SMES power drain in [src.loc.loc]" var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread s.set_up(3, 1, src) s.start() @@ -280,7 +349,6 @@ else emp_act(2) if(prob(5)) //smoke only - world << "\red SMES smoke in [src.loc.loc]" var/datum/effect/effect/system/smoke_spread/smoke = new /datum/effect/effect/system/smoke_spread() smoke.set_up(3, 0, src.loc) smoke.attach(src) @@ -305,12 +373,12 @@ /obj/machinery/power/smes/magical name = "magical power storage unit" desc = "A high-capacity superconducting magnetic energy storage (SMES) unit. Magically produces power." - process() - capacity = INFINITY - charge = INFINITY - ..() - + output = SMESMAXOUTPUT +/obj/machinery/power/smes/magical/process() + capacity = INFINITY + charge = INFINITY + ..() /proc/rate_control(var/S, var/V, var/C, var/Min=1, var/Max=5, var/Limit=null) var/href = " 500) - add_load(500) + if(surplus() >= power_usage && add_load(power_usage) >= power_usage) stat &= ~NOPOWER else stat |= NOPOWER diff --git a/code/modules/power/turbine.dm b/code/modules/power/turbine.dm index 466c6bdd..6464bb6f 100644 --- a/code/modules/power/turbine.dm +++ b/code/modules/power/turbine.dm @@ -51,6 +51,9 @@ turbine = locate() in get_step(src, get_dir(inturf, src)) if(!turbine) stat |= BROKEN + else + turbine.stat &= !BROKEN + turbine.compressor = src #define COMPFRICTION 5e5 @@ -67,7 +70,7 @@ return rpm = 0.9* rpm + 0.1 * rpmtarget var/datum/gas_mixture/environment = inturf.return_air() - var/transfer_moles = environment.total_moles()/10 + var/transfer_moles = environment.total_moles / 10 //var/transfer_moles = rpm/10000*capacity var/datum/gas_mixture/removed = inturf.remove_air(transfer_moles) gas_contained.merge(removed) @@ -105,6 +108,9 @@ compressor = locate() in get_step(src, get_dir(outturf, src)) if(!compressor) stat |= BROKEN + else + compressor.stat &= !BROKEN + compressor.turbine = src #define TURBPRES 9000000 @@ -123,14 +129,14 @@ lastgen = ((compressor.rpm / TURBGENQ)**TURBGENG) *TURBGENQ add_avail(lastgen) - var/newrpm = ((compressor.gas_contained.temperature) * compressor.gas_contained.total_moles())/4 + var/newrpm = ((compressor.gas_contained.temperature) * compressor.gas_contained.total_moles)/4 newrpm = max(0, newrpm) if(!compressor.starter || newrpm > 1000) compressor.rpmtarget = newrpm - if(compressor.gas_contained.total_moles()>0) - var/oamount = min(compressor.gas_contained.total_moles(), (compressor.rpm+100)/35000*compressor.capacity) + if(compressor.gas_contained.total_moles>0) + var/oamount = min(compressor.gas_contained.total_moles, (compressor.rpm+100)/35000*compressor.capacity) var/datum/gas_mixture/removed = compressor.gas_contained.remove(oamount) outturf.assume_air(removed) diff --git a/code/modules/projectiles/ammunition.dm b/code/modules/projectiles/ammunition.dm index 64c0952f..bd5b4df3 100644 --- a/code/modules/projectiles/ammunition.dm +++ b/code/modules/projectiles/ammunition.dm @@ -51,7 +51,7 @@ flags = FPRINT | TABLEPASS | CONDUCT slot_flags = SLOT_BELT item_state = "syringe_kit" - m_amt = 50000 + matter = list("metal" = 50000) throwforce = 2 w_class = 2.0 throw_speed = 4 diff --git a/code/modules/projectiles/ammunition/boxes.dm b/code/modules/projectiles/ammunition/boxes.dm index 5f7b38d0..956eb79d 100644 --- a/code/modules/projectiles/ammunition/boxes.dm +++ b/code/modules/projectiles/ammunition/boxes.dm @@ -166,7 +166,7 @@ origin_tech = "combat=2" ammo_type = "/obj/item/ammo_casing/shotgun" max_ammo = 8 - m_amt = 100000 + matter = list("metal" = 100000) /* no buckshots. never again. too hard to steal forever. /obj/item/ammo_magazine/shotgun/buck name = "Ammunition Box (buckshot)" @@ -177,17 +177,17 @@ name = "Ammunition Box (stun shells)" ammo_type = "/obj/item/ammo_casing/shotgun/stunshell" max_ammo = 8 - m_amt = 20000 + matter = list("metal" = 20000) /obj/item/ammo_magazine/shotgun/beanbag name = "Ammunition Box (beanbag shells)" ammo_type = "/obj/item/ammo_casing/shotgun/beanbag" max_ammo = 8 - m_amt = 4000 + matter = list("metal" = 4000) /obj/item/ammo_magazine/shotgun/incendiary name = "Ammunition Box (incendiary shells)" ammo_type = "/obj/item/ammo_casing/shotgun/incendiary" max_ammo = 8 - m_amt = 100000 + matter = list("metal" = 100000) diff --git a/code/modules/projectiles/ammunition/bullets.dm b/code/modules/projectiles/ammunition/bullets.dm index 3ce29735..808d87d5 100644 --- a/code/modules/projectiles/ammunition/bullets.dm +++ b/code/modules/projectiles/ammunition/bullets.dm @@ -65,14 +65,13 @@ icon_state = "gshell" caliber = "shotgun" projectile_type = "/obj/item/projectile/bullet" - m_amt = 12500 + matter = list("metal" = 12500) /obj/item/ammo_casing/shotgun/incendiary name = "shotgun shell" desc = "A 12 gauge shell." icon_state = "ishell" projectile_type = "/obj/item/projectile/bullet/incendiary/shell" - m_amt = 12500 /obj/item/ammo_casing/shotgun/blank @@ -80,7 +79,7 @@ desc = "A blank shell." icon_state = "blshell" projectile_type = "" - m_amt = 250 + matter = list("metal" = 250) /obj/item/ammo_casing/shotgun/beanbag @@ -88,7 +87,7 @@ desc = "A weak beanbag shell." icon_state = "bshell" projectile_type = "/obj/item/projectile/bullet/weakbullet/beanbag" - m_amt = 500 + matter = list("metal" = 500) /obj/item/ammo_casing/shotgun/stunshell @@ -96,7 +95,7 @@ desc = "A stunning shell." icon_state = "stunshell" projectile_type = "/obj/item/projectile/bullet/stunshot" - m_amt = 2500 + matter = list("metal" = 2500) /obj/item/ammo_casing/shotgun/dart @@ -104,7 +103,7 @@ desc = "A dart for use in shotguns." icon_state = "dart" projectile_type = "/obj/item/projectile/energy/dart" - m_amt = 12500 + matter = list("metal" = 12500) /obj/item/ammo_casing/a762 desc = "A 7.62 bullet casing." diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index f9bee693..ee846224 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -6,7 +6,7 @@ item_state = "gun" flags = FPRINT | TABLEPASS | CONDUCT slot_flags = SLOT_BELT - m_amt = 2000 + matter = list("metal" = 2000) w_class = 3.0 throwforce = 5 throw_speed = 4 diff --git a/code/modules/projectiles/guns/energy/erifles.dm b/code/modules/projectiles/guns/energy/erifles.dm index e0899974..96eadadf 100644 --- a/code/modules/projectiles/guns/energy/erifles.dm +++ b/code/modules/projectiles/guns/energy/erifles.dm @@ -58,7 +58,7 @@ fire_sound = 'sound/weapons/Laser.ogg' slot_flags = SLOT_BACK w_class = 4 - m_amt = 2000 + matter = list("metal" = 2000) charge_cost = 50 //odd numbers due to a requirement to have 20 shots. Easiest way. origin_tech = "combat=3;magnets=2" projectile_type = "/obj/item/projectile/beam" diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm index b07e64dd..60e5702c 100644 --- a/code/modules/projectiles/guns/energy/laser.dm +++ b/code/modules/projectiles/guns/energy/laser.dm @@ -6,7 +6,7 @@ fire_sound = 'sound/weapons/Laser.ogg' slot_flags = SLOT_BACK w_class = 4 - m_amt = 2000 + matter = list("metal" = 2000) origin_tech = "combat=3;magnets=2" projectile_type = "/obj/item/projectile/beam" diff --git a/code/modules/projectiles/guns/energy/modular.dm b/code/modules/projectiles/guns/energy/modular.dm index 7b218704..87564873 100644 --- a/code/modules/projectiles/guns/energy/modular.dm +++ b/code/modules/projectiles/guns/energy/modular.dm @@ -6,7 +6,7 @@ fire_sound = 'sound/weapons/Laser.ogg' slot_flags = SLOT_BACK w_class = 4 - m_amt = 2000 + matter = list("metal" = 2000) origin_tech = "combat=3;magnets=2" projectile_type = "/obj/item/projectile/beam/reallyweak" fire_delay = 16 diff --git a/code/modules/projectiles/guns/energy/stun.dm b/code/modules/projectiles/guns/energy/stun.dm index 7d612812..c355d3b2 100644 --- a/code/modules/projectiles/guns/energy/stun.dm +++ b/code/modules/projectiles/guns/energy/stun.dm @@ -69,7 +69,7 @@ icon_state = "crossbow" w_class = 2.0 item_state = "crossbow" - m_amt = 2000 + matter = list("metal" = 2000) origin_tech = "combat=2;magnets=2;syndicate=5" silenced = 1 fire_sound = 'sound/weapons/Genhit.ogg' @@ -108,7 +108,7 @@ desc = "A weapon favored by syndicate infiltration teams." w_class = 4.0 force = 10 - m_amt = 200000 + matter = list("metal" = 200000) projectile_type = "/obj/item/projectile/energy/bolt/large" diff --git a/code/modules/projectiles/guns/projectile.dm b/code/modules/projectiles/guns/projectile.dm index 6d1bdf99..2ce69211 100644 --- a/code/modules/projectiles/guns/projectile.dm +++ b/code/modules/projectiles/guns/projectile.dm @@ -9,7 +9,7 @@ caliber = "357" origin_tech = "combat=2;materials=2" w_class = 3.0 - m_amt = 1000 + matter = list("metal" = 1000) recoil = 1 var/ammo_type = "/obj/item/ammo_casing/a357" var/list/loaded = list() diff --git a/code/modules/reagents/Chemistry-Colours.dm b/code/modules/reagents/Chemistry-Colours.dm index 77a833bb..d9da7d14 100644 --- a/code/modules/reagents/Chemistry-Colours.dm +++ b/code/modules/reagents/Chemistry-Colours.dm @@ -34,7 +34,7 @@ var/blue = mixOneColor(weight,bluecolor) //assemble all the pieces - var/finalcolor = "#[red][green][blue]" + var/finalcolor = rgb(red, green, blue) return finalcolor /proc/mixOneColor(var/list/weight, var/list/color) @@ -58,10 +58,9 @@ mixedcolor = round(mixedcolor) //until someone writes a formal proof for this algorithm, let's keep this in - if(mixedcolor<0x00 || mixedcolor>0xFF) - return 0 +// if(mixedcolor<0x00 || mixedcolor>0xFF) +// return 0 + //that's not the kind of operation we are running here, nerd + mixedcolor=min(max(mixedcolor,0),255) - var/finalcolor = num2hex(mixedcolor) - while(length(finalcolor)<2) - finalcolor = text("0[]",finalcolor) //Takes care of leading zeroes - return finalcolor + return mixedcolor diff --git a/code/modules/reagents/Chemistry-Machinery.dm b/code/modules/reagents/Chemistry-Machinery.dm index b43dc1f7..381ce6fb 100644 --- a/code/modules/reagents/Chemistry-Machinery.dm +++ b/code/modules/reagents/Chemistry-Machinery.dm @@ -14,16 +14,13 @@ var/energy = 100 var/max_energy = 100 var/amount = 30 - var/accept_glass = 0 - var/beaker = null + var/accept_glass = 0 //At 0 ONLY accepts glass containers. Kinda misleading varname. + var/atom/beaker = null var/recharged = 0 var/hackedcheck = 0 var/list/dispensable_reagents = list("hydrogen","lithium","carbon","nitrogen","oxygen","fluorine", "sodium","aluminum","silicon","phosphorus","sulfur","chlorine","potassium","iron", "copper","mercury","radium","water","ethanol","sugar","sacid","tungsten") - var/list/broken_requirements = list() - var/broken_on_spawn = 0 - moveable = 1 /obj/machinery/chem_dispenser/proc/recharge() if(stat & (BROKEN|NOPOWER)) return @@ -35,15 +32,10 @@ nanomanager.update_uis(src) // update all UIs attached to src /obj/machinery/chem_dispenser/power_change() - if(powered()) - stat &= ~NOPOWER - else - spawn(rand(0, 15)) - stat |= NOPOWER + ..() nanomanager.update_uis(src) // update all UIs attached to src /obj/machinery/chem_dispenser/process() - if(recharged <= 0) recharge() recharged = 15 @@ -55,27 +47,6 @@ recharge() dispensable_reagents = sortList(dispensable_reagents) - if(broken_on_spawn) - var/amount = pick(1,2,2,3,4) - var/list/options = list() - options[/obj/item/weapon/stock_parts/capacitor/adv] = "Add an advanced capacitor to fix it." - options[/obj/item/weapon/stock_parts/console_screen] = "Replace the console screen to fix it." - options[/obj/item/weapon/stock_parts/manipulator/pico] = "Upgrade to a pico manipulator to fix it." - options[/obj/item/weapon/stock_parts/matter_bin/adv] = "Give it an advanced matter bin to fix it." - options[/obj/item/stack/sheet/mineral/diamond] = "Line up a cut diamond with the nozzle to fix it." - options[/obj/item/stack/sheet/mineral/uranium] = "Position a uranium sheet inside to fix it." - options[/obj/item/stack/sheet/mineral/plasma] = "Enter a block of plasma to fix it." - options[/obj/item/stack/sheet/mineral/silver] = "Cover the internals with a silver lining to fix it." - options[/obj/item/stack/sheet/mineral/gold] = "Wire a golden filament to fix it." - options[/obj/item/stack/sheet/plasteel] = "Surround the outside with a plasteel cover to fix it." - options[/obj/item/stack/sheet/rglass] = "Insert a pane of reinforced glass to fix it." - stat |= BROKEN - while(amount > 0) - amount -= 1 - - var/index = pick(options) - broken_requirements[index] = options[index] - options -= index /obj/machinery/chem_dispenser/ex_act(severity) switch(severity) @@ -105,18 +76,15 @@ * * @return nothing */ -/obj/machinery/chem_dispenser/ui_interact(mob/user, ui_key = "main",var/datum/nanoui/ui = null) - if(broken_requirements.len) - user << "[src] is broken. [broken_requirements[broken_requirements[1]]]" - return +/obj/machinery/chem_dispenser/ui_interact(mob/user, ui_key = "main",var/datum/nanoui/ui = null, var/force_open = 1) if(stat & (BROKEN|NOPOWER)) return if(user.stat || user.restrained()) return // this is the data which will be sent to the ui var/data[0] data["amount"] = amount - data["energy"] = energy - data["maxEnergy"] = max_energy + data["energy"] = round(energy) + data["maxEnergy"] = round(max_energy) data["isBeakerLoaded"] = beaker ? 1 : 0 data["glass"] = accept_glass var beakerContents[0] @@ -142,11 +110,11 @@ data["chemicals"] = chemicals // 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 - ui = new(user, src, ui_key, "chem_dispenser.tmpl", ui_title, 380, 650) + ui = new(user, src, ui_key, "chem_dispenser.tmpl", ui_title, 390, 655) // when the ui is first opened this is the data it will use ui.set_initial_data(data) // open the new ui window @@ -164,7 +132,7 @@ amount = 100 if(href_list["dispense"]) - if (dispensable_reagents.Find(href_list["dispense"]) && beaker != null) + if (dispensable_reagents.Find(href_list["dispense"]) && beaker != null && beaker.is_open_container()) var/obj/item/weapon/reagent_containers/B = src.beaker var/datum/reagents/R = B.reagents var/space = R.maximum_volume - R.total_volume @@ -184,19 +152,6 @@ /obj/machinery/chem_dispenser/attackby(var/obj/item/weapon/B as obj, var/mob/user as mob) if(isrobot(user)) return - - if(broken_requirements.len && B.type == broken_requirements[1]) - broken_requirements -= broken_requirements[1] - user << "You fix [src]." - if(istype(B,/obj/item/stack)) - var/obj/item/stack/S = B - S.use(1) - else - user.drop_item() - del(B) - if(broken_requirements.len==0) - stat ^= BROKEN - return if(src.beaker) user << "Something is already loaded into the machine." return @@ -210,21 +165,6 @@ nanomanager.update_uis(src) // update all UIs attached to src return - if(istype(B, /obj/item/weapon/wrench)) - if(moveable == 1) - switch(anchored) - if(0) - playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1) - spawn(10) - user.visible_message("[user.name] secures [src.name] to the floor.", "You secure [src.name] to the floor.", "You hear a ratchet") - anchored = 1 - if(1) - playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1) - spawn(10) - user.visible_message("[user.name] unsecures [src.name] reinforcing bolts from the floor.", "You unsecure [src.name] from the floor.", "You hear a ratchet") - anchored = 0 - return - /obj/machinery/chem_dispenser/attack_ai(mob/user as mob) return src.attack_hand(user) @@ -261,6 +201,7 @@ dispensable_reagents -= list("thirteenloko") hackedcheck = 0 return + /obj/machinery/chem_dispenser/beer icon_state = "booze_dispenser" name = "booze dispenser" @@ -297,7 +238,6 @@ icon_state = "mixer0" use_power = 1 idle_power_usage = 20 - moveable = 1 var/beaker = null var/obj/item/weapon/storage/pill_bottle/loaded_pill_bottle = null var/mode = 0 @@ -333,30 +273,8 @@ del(src) return -/obj/machinery/chem_master/power_change() - if(powered()) - stat &= ~NOPOWER - else - spawn(rand(0, 15)) - stat |= NOPOWER - /obj/machinery/chem_master/attackby(var/obj/item/weapon/B as obj, var/mob/user as mob) - if(istype(B, /obj/item/weapon/wrench)) - if(moveable == 1) - switch(anchored) - if(0) - playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1) - spawn(10) - user.visible_message("[user.name] secures [src.name] to the floor.", "You secure [src.name] to the floor.", "You hear a ratchet") - anchored = 1 - if(1) - playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1) - spawn(10) - user.visible_message("[user.name] unsecures [src.name] reinforcing bolts from the floor.", "You unsecure [src.name] from the floor.", "You hear a ratchet") - anchored = 0 - return - if(istype(B, /obj/item/weapon/reagent_containers/glass)) if(src.beaker) @@ -369,7 +287,7 @@ src.updateUsrDialog() icon_state = "mixer1" - if(istype(B, /obj/item/weapon/storage/pill_bottle)) + else if(istype(B, /obj/item/weapon/storage/pill_bottle)) if(src.loaded_pill_bottle) user << "A pill bottle is already loaded into the machine." @@ -646,18 +564,16 @@ /obj/machinery/computer/pandemic/power_change() - + ..() if(stat & BROKEN) icon_state = (src.beaker?"mixer1_b":"mixer0_b") - else if(powered()) + else if(!(stat & NOPOWER)) icon_state = (src.beaker?"mixer1":"mixer0") - stat &= ~NOPOWER else spawn(rand(0, 15)) src.icon_state = (src.beaker?"mixer1_nopower":"mixer0_nopower") - stat |= NOPOWER /obj/machinery/computer/pandemic/Topic(href, href_list) @@ -887,12 +803,11 @@ icon = 'icons/obj/kitchen.dmi' icon_state = "juicer1" layer = 2.9 - density = 1 - anchored = 1 + density = 0 + anchored = 0 use_power = 1 idle_power_usage = 5 active_power_usage = 100 - moveable = 1 var/inuse = 0 var/obj/item/weapon/reagent_containers/beaker = null var/limit = 10 diff --git a/code/modules/reagents/Chemistry-Reagents.dm b/code/modules/reagents/Chemistry-Reagents.dm index b16656d2..546b257a 100644 --- a/code/modules/reagents/Chemistry-Reagents.dm +++ b/code/modules/reagents/Chemistry-Reagents.dm @@ -5,10 +5,6 @@ #define REAGENTS_OVERDOSE 30 #define REM REAGENTS_EFFECT_MULTIPLIER -//Some on_mob_life() procs check for alien races. -#define IS_DIONA 1 -#define IS_VOX 2 - //The reaction procs must ALWAYS set src = null, this detaches the proc from the object (the reagent) //so that it can continue working when the reagent is deleted while the proc is still active. @@ -26,6 +22,7 @@ datum var/custom_metabolism = REAGENTS_METABOLISM var/overdose = 0 var/overdose_dam = 1 + var/scannable = 0 //shows up on health analyzers //var/list/viruses = list() var/color = "#000000" // rgb: 0, 0, 0 (does not support alpha channels - yet!) @@ -241,7 +238,7 @@ datum var/hotspot = (locate(/obj/fire) in T) if(hotspot && !istype(T, /turf/space)) - var/datum/gas_mixture/lowertemp = T.remove_air( T:air:total_moles() ) + var/datum/gas_mixture/lowertemp = T.remove_air( T:air:total_moles ) lowertemp.temperature = max( min(lowertemp.temperature-2000,lowertemp.temperature / 2) ,0) lowertemp.react() T.assume_air(lowertemp) @@ -252,7 +249,7 @@ datum var/turf/T = get_turf(O) var/hotspot = (locate(/obj/fire) in T) if(hotspot && !istype(T, /turf/space)) - var/datum/gas_mixture/lowertemp = T.remove_air( T:air:total_moles() ) + var/datum/gas_mixture/lowertemp = T.remove_air( T:air:total_moles ) lowertemp.temperature = max( min(lowertemp.temperature-2000,lowertemp.temperature / 2) ,0) lowertemp.react() T.assume_air(lowertemp) @@ -278,7 +275,7 @@ datum description = "An ashen-obsidian-water mix, this solution will alter certain sections of the brain's rationality." color = "#E0E8EF" // rgb: 224, 232, 239 - on_mob_life(var/mob/living/carbon/human/M as mob) + on_mob_life(var/mob/living/M as mob) if(ishuman(M)) if((M.mind in ticker.mode.cult) && prob(10)) M << "\blue A cooling sensation from inside you brings you an untold calmness." @@ -297,7 +294,6 @@ datum holder.remove_reagent(src.id, 10 * REAGENTS_METABOLISM) //high metabolism to prevent extended uncult rolls. return - lube name = "Space Lube" id = "lube" @@ -431,6 +427,7 @@ datum reagent_state = LIQUID color = "#C8A5DC" // rgb: 200, 165, 220 overdose = REAGENTS_OVERDOSE*2 + scannable = 1 on_mob_life(var/mob/living/M as mob, var/alien) if(!M) M = holder.my_atom @@ -808,6 +805,8 @@ datum reagent_state = LIQUID color = "#C855DC" overdose = 60 + scannable = 1 + custom_metabolism = 0.025 // Lasts 10 minutes for 15 units on_mob_life(var/mob/living/M as mob) if (volume > overdose) @@ -822,6 +821,8 @@ datum reagent_state = LIQUID color = "#C8A5DC" overdose = 30 + scannable = 1 + custom_metabolism = 0.025 // Lasts 10 minutes for 15 units on_mob_life(var/mob/living/M as mob) if (volume > overdose) @@ -836,6 +837,7 @@ datum reagent_state = LIQUID color = "#C805DC" overdose = 20 + custom_metabolism = 0.25 // Lasts 10 minutes for 15 units on_mob_life(var/mob/living/M as mob) if (volume > overdose) @@ -865,6 +867,18 @@ datum description = "Sterilizes wounds in preparation for surgery." reagent_state = LIQUID color = "#C8A5DC" // rgb: 200, 165, 220 + + //makes you squeaky clean + reaction_mob(var/mob/living/M, var/method=TOUCH, var/volume) + if (method == TOUCH) + M.germ_level -= min(volume*20, M.germ_level) + + reaction_obj(var/obj/O, var/volume) + O.germ_level -= min(volume*20, O.germ_level) + + reaction_turf(var/turf/T, var/volume) + T.germ_level -= min(volume*20, T.germ_level) + /* reaction_mob(var/mob/living/M, var/method=TOUCH, var/volume) src = null if (method==TOUCH) @@ -1689,11 +1703,7 @@ datum on_mob_life(var/mob/living/M as mob, var/alien) if(!M) M = holder.my_atom - if(alien && alien == IS_VOX) - custom_metabolism = 10 //Inhaling O2 constantly puts plasma into voxblood which poisons them to hell. +Metab so one huff isn't death. - toxpwr = 12 //This is a hacky way of doing it. They take 6 points of toxloss every four ticks (Breath tick). Still less than now. - if(holder.has_reagent("inaprovaline")) - holder.remove_reagent("inaprovaline", 2*REM) + holder.remove_reagent("inaprovaline", 2*REM) ..() return reaction_obj(var/obj/O, var/volume) @@ -1704,24 +1714,10 @@ datum egg.Hatch()*/ if((!O) || (!volume)) return 0 var/turf/the_turf = get_turf(O) - var/datum/gas_mixture/napalm = new - var/datum/gas/volatile_fuel/fuel = new - fuel.moles = volume - napalm.trace_gases += fuel - the_turf.assume_air(napalm) + the_turf.assume_gas("volatile_fuel", volume, T20C) reaction_turf(var/turf/T, var/volume) src = null - var/datum/gas_mixture/napalm = new - var/datum/gas/volatile_fuel/fuel = new - fuel.moles = volume - napalm.trace_gases += fuel - T.assume_air(napalm) - return - reaction_mob(var/mob/living/M, var/method=TOUCH, var/volume)//Splashing people with plasma is stronger than fuel! - if(!istype(M, /mob/living)) - return - if(method == TOUCH) - M.adjust_fire_stacks(volume / 5) + T.assume_gas("volatile_fuel", volume, T20C) return toxin/lexorin @@ -2550,7 +2546,7 @@ datum T.wet_overlay = null var/hotspot = (locate(/obj/fire) in T) if(hotspot) - var/datum/gas_mixture/lowertemp = T.remove_air( T:air:total_moles() ) + var/datum/gas_mixture/lowertemp = T.remove_air( T:air:total_moles ) lowertemp.temperature = max( min(lowertemp.temperature-2000,lowertemp.temperature / 2) ,0) lowertemp.react() T.assume_air(lowertemp) diff --git a/code/modules/reagents/Chemistry-Recipes.dm b/code/modules/reagents/Chemistry-Recipes.dm index fba9c3c5..a6cc4928 100644 --- a/code/modules/reagents/Chemistry-Recipes.dm +++ b/code/modules/reagents/Chemistry-Recipes.dm @@ -413,16 +413,7 @@ datum on_reaction(var/datum/reagents/holder, var/created_volume) var/turf/location = get_turf(holder.my_atom.loc) for(var/turf/simulated/floor/target_tile in range(0,location)) - - var/datum/gas_mixture/napalm = new - var/datum/gas/volatile_fuel/fuel = new - fuel.moles = created_volume - napalm.trace_gases += fuel - - napalm.temperature = 400+T0C - napalm.update_values() - - target_tile.assume_air(napalm) + target_tile.assume_gas("volatile_fuel", created_volume, 400+T0C) spawn (0) target_tile.hotspot_expose(700, 400) holder.del_reagent("napalm") return @@ -854,8 +845,8 @@ datum if(chosen) // Calculate previous position for transition - var/turf/FROM = get_turf_loc(holder.my_atom) // the turf of origin we're travelling FROM - var/turf/TO = get_turf_loc(chosen) // the turf of origin we're travelling TO + var/turf/FROM = get_turf(holder.my_atom) // the turf of origin we're travelling FROM + var/turf/TO = get_turf(chosen) // the turf of origin we're travelling TO playsound(TO, 'sound/effects/phasein.ogg', 100, 1) @@ -916,16 +907,16 @@ datum )//exclusion list for things you don't want the reaction to create. var/list/critters = typesof(/mob/living/simple_animal/hostile) - blocked // list of possible hostile mobs - playsound(get_turf_loc(holder.my_atom), 'sound/effects/phasein.ogg', 100, 1) + playsound(get_turf(holder.my_atom), 'sound/effects/phasein.ogg', 100, 1) - for(var/mob/living/carbon/human/M in viewers(get_turf_loc(holder.my_atom), null)) + for(var/mob/living/carbon/human/M in viewers(get_turf(holder.my_atom), null)) if(M:eyecheck() <= 0) flick("e_flash", M.flash) for(var/i = 1, i <= created_volume, i++) var/chosen = pick(critters) var/mob/living/simple_animal/hostile/C = new chosen - C.loc = get_turf_loc(holder.my_atom) + C.loc = get_turf(holder.my_atom) if(prob(50)) for(var/j = 1, j <= rand(1, 3), j++) step(C, pick(NORTH,SOUTH,EAST,WEST)) @@ -942,9 +933,9 @@ datum var/list/borks = typesof(/obj/item/weapon/reagent_containers/food/snacks) - /obj/item/weapon/reagent_containers/food/snacks // BORK BORK BORK - playsound(get_turf_loc(holder.my_atom), 'sound/effects/phasein.ogg', 100, 1) + playsound(get_turf(holder.my_atom), 'sound/effects/phasein.ogg', 100, 1) - for(var/mob/living/carbon/human/M in viewers(get_turf_loc(holder.my_atom), null)) + for(var/mob/living/carbon/human/M in viewers(get_turf(holder.my_atom), null)) if(M:eyecheck() <= 0) flick("e_flash", M.flash) @@ -952,7 +943,7 @@ datum var/chosen = pick(borks) var/obj/B = new chosen if(B) - B.loc = get_turf_loc(holder.my_atom) + B.loc = get_turf(holder.my_atom) if(prob(50)) for(var/j = 1, j <= rand(1, 3), j++) step(B, pick(NORTH,SOUTH,EAST,WEST)) @@ -1022,10 +1013,10 @@ datum required_container = /obj/item/slime_extract/grey required_other = 1 on_reaction(var/datum/reagents/holder) - for(var/mob/O in viewers(get_turf_loc(holder.my_atom), null)) + for(var/mob/O in viewers(get_turf(holder.my_atom), null)) O.show_message(text("\red Infused with plasma, the core begins to quiver and grow, and soon a new baby slime emerges from it!"), 1) var/mob/living/carbon/slime/S = new /mob/living/carbon/slime - S.loc = get_turf_loc(holder.my_atom) + S.loc = get_turf(holder.my_atom) slimemonkey @@ -1039,7 +1030,7 @@ datum on_reaction(var/datum/reagents/holder) for(var/i = 1, i <= 3, i++) var /obj/item/weapon/reagent_containers/food/snacks/monkeycube/M = new /obj/item/weapon/reagent_containers/food/snacks/monkeycube - M.loc = get_turf_loc(holder.my_atom) + M.loc = get_turf(holder.my_atom) //Green slimemutate @@ -1063,10 +1054,10 @@ datum on_reaction(var/datum/reagents/holder) var/obj/item/stack/sheet/metal/M = new /obj/item/stack/sheet/metal M.amount = 15 - M.loc = get_turf_loc(holder.my_atom) + M.loc = get_turf(holder.my_atom) var/obj/item/stack/sheet/plasteel/P = new /obj/item/stack/sheet/plasteel P.amount = 5 - P.loc = get_turf_loc(holder.my_atom) + P.loc = get_turf(holder.my_atom) //Gold slimecrit @@ -1097,20 +1088,20 @@ datum )//exclusion list for things you don't want the reaction to create. var/list/critters = typesof(/mob/living/simple_animal/hostile) - blocked // list of possible hostile mobs - playsound(get_turf_loc(holder.my_atom), 'sound/effects/phasein.ogg', 100, 1) + playsound(get_turf(holder.my_atom), 'sound/effects/phasein.ogg', 100, 1) - for(var/mob/living/carbon/human/M in viewers(get_turf_loc(holder.my_atom), null)) + for(var/mob/living/carbon/human/M in viewers(get_turf(holder.my_atom), null)) if(M:eyecheck() <= 0) flick("e_flash", M.flash) for(var/i = 1, i <= 5, i++) var/chosen = pick(critters) var/mob/living/simple_animal/hostile/C = new chosen C.faction = "slimesummon" - C.loc = get_turf_loc(holder.my_atom) + C.loc = get_turf(holder.my_atom) if(prob(50)) for(var/j = 1, j <= rand(1, 3), j++) step(C, pick(NORTH,SOUTH,EAST,WEST)) -// for(var/mob/O in viewers(get_turf_loc(holder.my_atom), null)) +// for(var/mob/O in viewers(get_turf(holder.my_atom), null)) // O.show_message(text("\red The slime core fizzles disappointingly,"), 1) slimecatalyst @@ -1123,7 +1114,7 @@ datum required_other = 1 on_reaction(var/datum/reagents/holder) var/obj/item/weapon/slimecatalyst/P = new /obj/item/weapon/slimecatalyst - P.loc = get_turf_loc(holder.my_atom) + P.loc = get_turf(holder.my_atom) /* slimecritlesser name = "Slime Crit Lesser" @@ -1190,9 +1181,9 @@ datum var/list/borks = typesof(/obj/item/weapon/reagent_containers/food/snacks) - /obj/item/weapon/reagent_containers/food/snacks // BORK BORK BORK - playsound(get_turf_loc(holder.my_atom), 'sound/effects/phasein.ogg', 100, 1) + playsound(get_turf(holder.my_atom), 'sound/effects/phasein.ogg', 100, 1) - for(var/mob/living/carbon/human/M in viewers(get_turf_loc(holder.my_atom), null)) + for(var/mob/living/carbon/human/M in viewers(get_turf(holder.my_atom), null)) if(M:eyecheck() <= 0) flick("e_flash", M.flash) @@ -1200,7 +1191,7 @@ datum var/chosen = pick(borks) var/obj/B = new chosen if(B) - B.loc = get_turf_loc(holder.my_atom) + B.loc = get_turf(holder.my_atom) if(prob(50)) for(var/j = 1, j <= rand(1, 3), j++) step(B, pick(NORTH,SOUTH,EAST,WEST)) @@ -1225,11 +1216,11 @@ datum required_container = /obj/item/slime_extract/darkblue required_other = 1 on_reaction(var/datum/reagents/holder) - for(var/mob/O in viewers(get_turf_loc(holder.my_atom), null)) + for(var/mob/O in viewers(get_turf(holder.my_atom), null)) O.show_message(text("\red The slime extract begins to vibrate violently !"), 1) sleep(50) - playsound(get_turf_loc(holder.my_atom), 'sound/effects/phasein.ogg', 100, 1) - for(var/mob/living/M in range (get_turf_loc(holder.my_atom), 7)) + playsound(get_turf(holder.my_atom), 'sound/effects/phasein.ogg', 100, 1) + for(var/mob/living/M in range (get_turf(holder.my_atom), 7)) M.bodytemperature -= 140 M << "\blue You feel a chill!" @@ -1252,18 +1243,12 @@ datum required_container = /obj/item/slime_extract/orange required_other = 1 on_reaction(var/datum/reagents/holder) - for(var/mob/O in viewers(get_turf_loc(holder.my_atom), null)) + for(var/mob/O in viewers(get_turf(holder.my_atom), null)) O.show_message(text("\red The slime extract begins to vibrate violently !"), 1) sleep(50) var/turf/location = get_turf(holder.my_atom.loc) for(var/turf/simulated/floor/target_tile in range(0,location)) - - var/datum/gas_mixture/napalm = new - - napalm.toxins = 25 - napalm.temperature = 1400 - - target_tile.assume_air(napalm) + target_tile.assume_gas("toxins", 25, 1400) spawn (0) target_tile.hotspot_expose(700, 400) //Yellow @@ -1276,7 +1261,7 @@ datum required_container = /obj/item/slime_extract/yellow required_other = 1 on_reaction(var/datum/reagents/holder, var/created_volume) - empulse(get_turf_loc(holder.my_atom), 3, 7) + empulse(get_turf(holder.my_atom), 3, 7) slimecell @@ -1289,7 +1274,7 @@ datum required_other = 1 on_reaction(var/datum/reagents/holder, var/created_volume) var/obj/item/weapon/cell/slime/P = new /obj/item/weapon/cell/slime - P.loc = get_turf_loc(holder.my_atom) + P.loc = get_turf(holder.my_atom) slimeglow name = "Slime Glow" @@ -1300,7 +1285,7 @@ datum required_container = /obj/item/slime_extract/yellow required_other = 1 on_reaction(var/datum/reagents/holder) - for(var/mob/O in viewers(get_turf_loc(holder.my_atom), null)) + for(var/mob/O in viewers(get_turf(holder.my_atom), null)) O.show_message(text("\red The contents of the slime core harden and begin to emit a warm, bright light."), 1) var/obj/item/device/flashlight/slime/F = new /obj/item/device/flashlight/slime F.loc = get_turf(holder.my_atom) @@ -1317,7 +1302,7 @@ datum required_other = 1 on_reaction(var/datum/reagents/holder) var/obj/item/weapon/slimesteroid/P = new /obj/item/weapon/slimesteroid - P.loc = get_turf_loc(holder.my_atom) + P.loc = get_turf(holder.my_atom) @@ -1343,7 +1328,7 @@ datum on_reaction(var/datum/reagents/holder) var/obj/item/stack/sheet/mineral/plasma/P = new /obj/item/stack/sheet/mineral/plasma P.amount = 10 - P.loc = get_turf_loc(holder.my_atom) + P.loc = get_turf(holder.my_atom) //Red slimeglycerol @@ -1365,10 +1350,9 @@ datum required_container = /obj/item/slime_extract/red required_other = 1 on_reaction(var/datum/reagents/holder) - for(var/mob/living/carbon/slime/slime in viewers(get_turf_loc(holder.my_atom), null)) - slime.tame = 0 + for(var/mob/living/carbon/slime/slime in viewers(get_turf(holder.my_atom), null)) slime.rabid = 1 - for(var/mob/O in viewers(get_turf_loc(holder.my_atom), null)) + for(var/mob/O in viewers(get_turf(holder.my_atom), null)) O.show_message(text("\red The [slime] is driven into a frenzy!."), 1) //Pink @@ -1382,7 +1366,7 @@ datum required_other = 1 on_reaction(var/datum/reagents/holder) var/obj/item/weapon/slimepotion/P = new /obj/item/weapon/slimepotion - P.loc = get_turf_loc(holder.my_atom) + P.loc = get_turf(holder.my_atom) //Black @@ -1405,10 +1389,10 @@ datum required_container = /obj/item/slime_extract/oil required_other = 1 on_reaction(var/datum/reagents/holder) - for(var/mob/O in viewers(get_turf_loc(holder.my_atom), null)) + for(var/mob/O in viewers(get_turf(holder.my_atom), null)) O.show_message(text("\red The slime extract begins to vibrate violently !"), 1) sleep(50) - explosion(get_turf_loc(holder.my_atom), 1 ,3, 6) + explosion(get_turf(holder.my_atom), 1 ,3, 6) //Light Pink slimepotion2 name = "Slime Potion 2" @@ -1420,7 +1404,7 @@ datum required_other = 1 on_reaction(var/datum/reagents/holder) var/obj/item/weapon/slimepotion2/P = new /obj/item/weapon/slimepotion2 - P.loc = get_turf_loc(holder.my_atom) + P.loc = get_turf(holder.my_atom) //Adamantine slimegolem name = "Slime Golem" @@ -1432,7 +1416,7 @@ datum required_other = 1 on_reaction(var/datum/reagents/holder) var/obj/effect/golemrune/Z = new /obj/effect/golemrune - Z.loc = get_turf_loc(holder.my_atom) + Z.loc = get_turf(holder.my_atom) Z.announce_to_ghosts() //////////////////////////////////////////FOOD MIXTURES//////////////////////////////////// diff --git a/code/modules/reagents/grenade_launcher.dm b/code/modules/reagents/grenade_launcher.dm index 6731c247..0c0e69fc 100644 --- a/code/modules/reagents/grenade_launcher.dm +++ b/code/modules/reagents/grenade_launcher.dm @@ -11,7 +11,7 @@ force = 5.0 var/list/grenades = new/list() var/max_grenades = 3 - m_amt = 2000 + matter = list("metal" = 2000) examine() set src in view() @@ -56,7 +56,7 @@ var/obj/item/weapon/grenade/chem_grenade/F = grenades[1] //Now with less copypasta! grenades -= F F.loc = user.loc - F.throw_at(target, 30, 2) + F.throw_at(target, 30, 2, user) message_admins("[key_name_admin(user)] fired a grenade ([F.name]) from a grenade launcher ([src.name]) (JMP)") message_mods("[key_name_admin(user)] fired a grenade ([F.name]) from a grenade launcher ([src.name]) (JMP)") log_game("[key_name_admin(user)] used a grenade ([src.name]).") diff --git a/code/modules/reagents/reagent_containers.dm b/code/modules/reagents/reagent_containers.dm index 8f4eb31f..50ec2f70 100644 --- a/code/modules/reagents/reagent_containers.dm +++ b/code/modules/reagents/reagent_containers.dm @@ -28,7 +28,9 @@ return /obj/item/weapon/reagent_containers/attack(mob/M as mob, mob/user as mob, def_zone) - return + if (can_operate(M)) //Checks if mob is lying down on table for surgery + if (do_surgery(M,user,src)) + return // this prevented pills, food, and other things from being picked up by bags. // possibly intentional, but removing it allows us to not duplicate functionality. diff --git a/code/modules/reagents/reagent_containers/blood_pack.dm b/code/modules/reagents/reagent_containers/blood_pack.dm index 9f3a9ea5..c091f1db 100644 --- a/code/modules/reagents/reagent_containers/blood_pack.dm +++ b/code/modules/reagents/reagent_containers/blood_pack.dm @@ -18,25 +18,11 @@ update_icon() update_icon() - - - - -/* - if(blood_type != "K") - var/percent = round((reagents.total_volume / volume) * 100) - switch(percent) - if(0 to 9) icon_state = "empty" - if(10 to 50) icon_state = "half" - if(51 to INFINITY) icon_state = "full" - else - var/percent2 = round((reagents.total_volume / volume) * 100) - switch(percent2) - if(0 to 9) icon_state = "empty" - if(10 to 50) icon_state = "kocas_half" - if(51 to INFINITY) icon_state = "kocas_full" - -*/ + var/percent = round((reagents.total_volume / volume) * 100) + switch(percent) + if(0 to 9) icon_state = "empty" + if(10 to 50) icon_state = "half" + if(51 to INFINITY) icon_state = "full" /obj/item/weapon/reagent_containers/blood/APlus blood_type = "A+" diff --git a/code/modules/reagents/reagent_containers/borghydro.dm b/code/modules/reagents/reagent_containers/borghydro.dm index e4af24a5..5e7e7572 100644 --- a/code/modules/reagents/reagent_containers/borghydro.dm +++ b/code/modules/reagents/reagent_containers/borghydro.dm @@ -18,6 +18,12 @@ var/list/reagent_ids = list("tricordrazine", "inaprovaline", "spaceacillin") //var/list/reagent_ids = list("dexalin", "kelotane", "bicaridine", "anti_toxin", "inaprovaline", "spaceacillin") +/obj/item/weapon/reagent_containers/borghypo/surgeon + reagent_ids = list("bicaridine", "inaprovaline", "dexalin") + +/obj/item/weapon/reagent_containers/borghypo/crisis + reagent_ids = list("tricordrazine", "inaprovaline", "tramadol") + /obj/item/weapon/reagent_containers/borghypo/New() ..() for(var/R in reagent_ids) diff --git a/code/modules/reagents/reagent_containers/food.dm b/code/modules/reagents/reagent_containers/food.dm index f87f2735..793f5fb1 100644 --- a/code/modules/reagents/reagent_containers/food.dm +++ b/code/modules/reagents/reagent_containers/food.dm @@ -6,7 +6,29 @@ volume = 50 //Sets the default container amount for all food items. var/filling_color = "#FFFFFF" //Used by sandwiches. + var/list/center_of_mass = newlist() //Center of mass + /obj/item/weapon/reagent_containers/food/New() - ..() - src.pixel_x = rand(-10.0, 10) //Randomizes postion - src.pixel_y = rand(-10.0, 10) \ No newline at end of file + ..() + if (!pixel_x && !pixel_y) + src.pixel_x = rand(-6.0, 6) //Randomizes postion + src.pixel_y = rand(-6.0, 6) + +/obj/item/weapon/reagent_containers/food/afterattack(atom/A, mob/user, proximity, params) + if(proximity && params && istype(A, /obj/structure/table) && center_of_mass.len) + //Places the item on a grid + var/list/mouse_control = params2list(params) + var/cellnumber = 4 + + var/mouse_x = text2num(mouse_control["icon-x"]) + var/mouse_y = text2num(mouse_control["icon-y"]) + + var/grid_x = round(mouse_x, 32/cellnumber) + var/grid_y = round(mouse_y, 32/cellnumber) + + if(mouse_control["icon-x"]) + var/sign = mouse_x - grid_x != 0 ? sign(mouse_x - grid_x) : -1 //positive if rounded down, else negative + pixel_x = grid_x - center_of_mass["x"] + sign*16/cellnumber //center of the cell + if(mouse_control["icon-y"]) + var/sign = mouse_y - grid_y != 0 ? sign(mouse_y - grid_y) : -1 + pixel_y = grid_y - center_of_mass["y"] + sign*16/cellnumber \ No newline at end of file diff --git a/code/modules/reagents/reagent_containers/food/drinks/drinkingglass.dm b/code/modules/reagents/reagent_containers/food/drinks/drinkingglass.dm index 0b10f15f..24e05cbc 100644 --- a/code/modules/reagents/reagent_containers/food/drinks/drinkingglass.dm +++ b/code/modules/reagents/reagent_containers/food/drinks/drinkingglass.dm @@ -5,7 +5,7 @@ desc = "Your standard drinking glass." icon_state = "glass_empty" amount_per_transfer_from_this = 10 - g_amt = 500 + matter = list("glass" = 500) volume = 50 on_reagent_change() diff --git a/code/modules/reagents/reagent_containers/glass.dm b/code/modules/reagents/reagent_containers/glass.dm index 7ff400b8..5d7a95c4 100644 --- a/code/modules/reagents/reagent_containers/glass.dm +++ b/code/modules/reagents/reagent_containers/glass.dm @@ -95,6 +95,7 @@ spawn(5) src.reagents.clear_reagents() return else if(istype(target, /obj/structure/reagent_dispensers)) //A dispenser. Transfer FROM it TO us. + target.add_fingerprint(user) if(!target.reagents.total_volume && target.reagents) user << "\red [target] is empty." @@ -108,6 +109,7 @@ user << "\blue You fill [src] with [trans] units of the contents of [target]." else if(target.is_open_container() && target.reagents) //Something like a glass. Player probably wants to transfer TO it. + if(!reagents.total_volume) user << "\red [src] is empty." return @@ -160,8 +162,7 @@ icon = 'icons/obj/chemical.dmi' icon_state = "beaker" item_state = "beaker" - m_amt = 0 - g_amt = 500 + matter = list("glass" = 500) on_reagent_change() update_icon() @@ -205,7 +206,7 @@ name = "large beaker" desc = "A large beaker. Can hold up to 100 units." icon_state = "beakerlarge" - g_amt = 5000 + matter = list("glass" = 5000) volume = 100 amount_per_transfer_from_this = 10 possible_transfer_amounts = list(5,10,15,25,30,50,100) @@ -215,7 +216,7 @@ name = "cryostasis beaker" desc = "A cryostasis beaker that allows for chemical storage without reactions. Can hold up to 50 units." icon_state = "beakernoreact" - g_amt = 500 + matter = list("glass" = 500) volume = 50 amount_per_transfer_from_this = 10 flags = FPRINT | TABLEPASS | OPENCONTAINER | NOREACT @@ -224,7 +225,7 @@ name = "bluespace beaker" desc = "A bluespace beaker, powered by experimental bluespace technology. Can hold up to 300 units." icon_state = "beakerbluespace" - g_amt = 5000 + matter = list("glass" = 5000) volume = 300 amount_per_transfer_from_this = 10 possible_transfer_amounts = list(5,10,15,25,30,50,100,300) @@ -235,7 +236,7 @@ name = "vial" desc = "A small glass vial. Can hold up to 25 units." icon_state = "vial" - g_amt = 250 + matter = list("glass" = 250) volume = 25 amount_per_transfer_from_this = 10 possible_transfer_amounts = list(5,10,15,25) @@ -265,8 +266,7 @@ icon = 'icons/obj/janitor.dmi' icon_state = "bucket" item_state = "bucket" - m_amt = 200 - g_amt = 0 + matter = list("metal" = 200) w_class = 3.0 amount_per_transfer_from_this = 20 possible_transfer_amounts = list(10,20,30,50,70) diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm index 2fb1e3eb..b3b06c49 100644 --- a/code/modules/reagents/reagent_containers/syringes.dm +++ b/code/modules/reagents/reagent_containers/syringes.dm @@ -11,7 +11,7 @@ icon = 'icons/obj/syringe.dmi' item_state = "syringe_0" icon_state = "0" - g_amt = 150 + matter = list("glass" = 150) amount_per_transfer_from_this = 5 possible_transfer_amounts = null //list(5,10,15) volume = 15 @@ -236,7 +236,7 @@ if(istype(target, /mob/living/carbon/human)) - var/target_zone = check_zone(user.zone_sel.selecting, target) + var/target_zone = ran_zone(check_zone(user.zone_sel.selecting, target)) var/datum/organ/external/affecting = target:get_organ(target_zone) if (!affecting) @@ -436,6 +436,17 @@ mode = SYRINGE_INJECT update_icon() +/obj/item/weapon/reagent_containers/syringe/drugs + name = "Syringe (drugs)" + desc = "Contains aggressive drugs meant for torture." + New() + ..() + reagents.add_reagent("space_drugs", 5) + reagents.add_reagent("mindbreaker", 5) + reagents.add_reagent("cryptobiolin", 5) + mode = SYRINGE_INJECT + update_icon() + /obj/item/weapon/reagent_containers/ld50_syringe/choral New() ..() diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm index 70908cfe..861c8f58 100644 --- a/code/modules/reagents/reagent_dispenser.dm +++ b/code/modules/reagents/reagent_dispenser.dm @@ -116,6 +116,7 @@ overlays = new/list() /obj/structure/reagent_dispensers/fueltank/attackby(obj/item/weapon/W as obj, mob/user as mob) + src.add_fingerprint(user) if (istype(W,/obj/item/weapon/wrench)) user.visible_message("[user] wrenches [src]'s faucet [modded ? "closed" : "open"].", \ "You wrench [src]'s faucet [modded ? "closed" : "open"]") @@ -164,6 +165,10 @@ /obj/structure/reagent_dispensers/fueltank/bullet_act(var/obj/item/projectile/Proj) if(istype(Proj ,/obj/item/projectile/beam)||istype(Proj,/obj/item/projectile/bullet)) + if(istype(Proj.firer)) + message_admins("[key_name_admin(Proj.firer)] shot fueltank at [loc.loc.name] ([loc.x],[loc.y],[loc.z]) (JMP).") + log_game("[key_name(Proj.firer)] shot fueltank at [loc.loc.name] ([loc.x],[loc.y],[loc.z]).") + if(!istype(Proj ,/obj/item/projectile/beam/lastertag) && !istype(Proj ,/obj/item/projectile/beam/practice) ) explode() @@ -200,7 +205,7 @@ amount = min(amount, reagents.total_volume) reagents.remove_reagent("fuel",amount) - new /obj/effect/decal/cleanable/liquid_fuel(src.loc, amount) + new /obj/effect/decal/cleanable/liquid_fuel(src.loc, amount,1) /obj/structure/reagent_dispensers/peppertank name = "Pepper Spray Refiller" diff --git a/code/modules/reagents/syringe_gun.dm b/code/modules/reagents/syringe_gun.dm index 7c3d9b37..5a684f0b 100644 --- a/code/modules/reagents/syringe_gun.dm +++ b/code/modules/reagents/syringe_gun.dm @@ -13,7 +13,7 @@ force = 4.0 var/list/syringes = new/list() var/max_syringes = 1 - m_amt = 2000 + matter = list("metal" = 2000) /obj/item/weapon/gun/syringe/examine() set src in view() diff --git a/code/modules/recycling/conveyor2.dm b/code/modules/recycling/conveyor2.dm index cbd47957..1f973b22 100644 --- a/code/modules/recycling/conveyor2.dm +++ b/code/modules/recycling/conveyor2.dm @@ -6,6 +6,7 @@ icon_state = "conveyor0" name = "conveyor belt" desc = "A conveyor belt." + layer = 2 // so they appear under stuff anchored = 1 var/operating = 0 // 1 if running forward, -1 if backwards, 0 if off var/operable = 1 // true if can operate (no broken segments in this belt run) diff --git a/code/modules/recycling/disposal-construction.dm b/code/modules/recycling/disposal-construction.dm index fd65a573..8b123b93 100644 --- a/code/modules/recycling/disposal-construction.dm +++ b/code/modules/recycling/disposal-construction.dm @@ -10,7 +10,7 @@ anchored = 0 density = 0 pressure_resistance = 5*ONE_ATMOSPHERE - m_amt = 1850 + matter = list("metal" = 1850) level = 2 var/ptype = 0 // 0=straight, 1=bent, 2=junction-j1, 3=junction-j2, 4=junction-y, 5=trunk, 6=disposal bin, 7=outlet, 8=inlet @@ -205,24 +205,6 @@ return var/obj/structure/disposalpipe/CP = locate() in T - if(ptype>=6 && ptype <= 8) // Disposal or outlet - if(CP) // There's something there - if(!istype(CP,/obj/structure/disposalpipe/trunk)) - user << "The [nicetype] requires a trunk underneath it in order to work." - return - else // Nothing under, fuck. - user << "The [nicetype] requires a trunk underneath it in order to work." - return - else - if(CP) - update() - var/pdir = CP.dpdir - if(istype(CP, /obj/structure/disposalpipe/broken)) - pdir = CP.dir - if(pdir & dpdir) - user << "There is already a [nicetype] at that location." - return - if(istype(I, /obj/item/weapon/wrench)) if(anchored) @@ -234,7 +216,25 @@ density = 1 user << "You detach the [nicetype] from the underfloor." else - anchored = 1 + if(ptype>=6 && ptype <= 8) // Disposal or outlet + if(CP) // There's something there + if(!istype(CP,/obj/structure/disposalpipe/trunk)) + user << "The [nicetype] requires a trunk underneath it in order to work." + return + else // Nothing under, fuck. + user << "The [nicetype] requires a trunk underneath it in order to work." + return + else + if(CP) + update() + var/pdir = CP.dpdir + if(istype(CP, /obj/structure/disposalpipe/broken)) + pdir = CP.dir + if(pdir & dpdir) + user << "There is already a [nicetype] at that location." + return + + anchored = 1 if(ispipe) level = 1 // We don't want disposal bins to disappear under the floors density = 0 diff --git a/code/modules/recycling/disposal.dm b/code/modules/recycling/disposal.dm index 82a3f9b9..12478e0a 100644 --- a/code/modules/recycling/disposal.dm +++ b/code/modules/recycling/disposal.dm @@ -5,7 +5,9 @@ // Automatically recharges air (unless off), will flush when ready if pre-set // Can hold items and human size things, no other draggables // Toilets are a type of disposal bin for small objects only and work on magic. By magic, I mean torque rotation -#define SEND_PRESSURE 0.05*ONE_ATMOSPHERE +#define SEND_PRESSURE 50 //kPa +#define PRESSURE_TANK_VOLUME 70 //L - a 0.3 m diameter * 1 m long cylindrical tank. Happens to be the same volume as the regular oxygen tanks, so seems appropriate. +#define PUMP_MAX_FLOW_RATE 50 //L/s - 8 m/s using a 15 cm by 15 cm inlet /obj/machinery/disposal name = "disposal unit" @@ -22,470 +24,459 @@ var/flush_every_ticks = 30 //Every 30 ticks it will look whether it is ready to flush var/flush_count = 0 //this var adds 1 once per tick. When it reaches flush_every_ticks it resets and tries to flush. var/last_sound = 0 - active_power_usage = 600 + active_power_usage = 3500 //the pneumatic pump power. 3 HP ~ 2200W idle_power_usage = 100 - // create a new disposal - // find the attached trunk (if present) and init gas resvr. - New() - ..() - spawn(5) - trunk = locate() in src.loc - if(!trunk) - mode = 0 - flush = 0 - else - trunk.linked = src // link the pipe trunk to self - - air_contents = new/datum/gas_mixture() - //gas.volume = 1.05 * CELLSTANDARD - update() - - - // attack by item places it in to disposal - attackby(var/obj/item/I, var/mob/user) - if(stat & BROKEN || !I || !user) - return - - if(isrobot(user) && !istype(I, /obj/item/weapon/storage/bag/trash)) - return - src.add_fingerprint(user) - if(mode<=0) // It's off - if(istype(I, /obj/item/weapon/screwdriver)) - if(contents.len > 0) - user << "Eject the items first!" - return - if(mode==0) // It's off but still not unscrewed - mode=-1 // Set it to doubleoff l0l - playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1) - user << "You remove the screws around the power connection." - return - else if(mode==-1) - mode=0 - playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1) - user << "You attach the screws around the power connection." - return - else if(istype(I,/obj/item/weapon/weldingtool) && mode==-1) - if(contents.len > 0) - user << "Eject the items first!" - return - var/obj/item/weapon/weldingtool/W = I - if(W.remove_fuel(0,user)) - playsound(src.loc, 'sound/items/Welder2.ogg', 100, 1) - user << "You start slicing the floorweld off the disposal unit." - - if(do_after(user,20)) - if(!src || !W.isOn()) return - user << "You sliced the floorweld off the disposal unit." - var/obj/structure/disposalconstruct/C = new (src.loc) - src.transfer_fingerprints_to(C) - C.ptype = 6 // 6 = disposal unit - C.anchored = 1 - C.density = 1 - C.update() - del(src) - return - else - user << "You need more welding fuel to complete this task." - return - - if(istype(I, /obj/item/weapon/melee/energy/blade)) - user << "You can't place that item inside the disposal unit." - return - - if(istype(I, /obj/item/weapon/storage/bag/trash)) - var/obj/item/weapon/storage/bag/trash/T = I - user << "\blue You empty the bag." - for(var/obj/item/O in T.contents) - T.remove_from_storage(O,src) - T.update_icon() - update() - return - - var/obj/item/weapon/grab/G = I - if(istype(G)) // handle grabbed mob - if(ismob(G.affecting)) - var/mob/GM = G.affecting - for (var/mob/V in viewers(usr)) - V.show_message("[usr] starts putting [GM.name] into the disposal.", 3) - if(do_after(usr, 20)) - if (GM.client) - GM.client.perspective = EYE_PERSPECTIVE - GM.client.eye = src - GM.loc = src - for (var/mob/C in viewers(src)) - C.show_message("\red [GM.name] has been placed in the [src] by [user].", 3) - del(G) - usr.attack_log += text("\[[time_stamp()]\] Has placed [GM.name] ([GM.ckey]) in disposals.") - GM.attack_log += text("\[[time_stamp()]\] Has been placed in disposals by [usr.name] ([usr.ckey])") - msg_admin_attack("[usr] ([usr.ckey]) placed [GM] ([GM.ckey]) in a disposals unit. (JMP)") - return - - if(!I) return - - user.drop_item() - if(I) - I.loc = src - - user << "You place \the [I] into the [src]." - for(var/mob/M in viewers(src)) - if(M == user) - continue - M.show_message("[user.name] places \the [I] into the [src].", 3) - - update() - - // mouse drop another mob or self - // - MouseDrop_T(mob/target, mob/user) - if (!istype(target) || target.buckled || get_dist(user, src) > 1 || get_dist(user, target) > 1 || user.stat || istype(user, /mob/living/silicon/ai)) - return - if(isanimal(user) && target != user) return //animals cannot put mobs other than themselves into disposal - src.add_fingerprint(user) - var/target_loc = target.loc - var/msg - for (var/mob/V in viewers(usr)) - if(target == user && !user.stat && !user.weakened && !user.stunned && !user.paralysis) - V.show_message("[usr] starts climbing into the disposal.", 3) - if(target != user && !user.restrained() && !user.stat && !user.weakened && !user.stunned && !user.paralysis) - if(target.anchored) return - V.show_message("[usr] starts stuffing [target.name] into the disposal.", 3) - if(!do_after(usr, 20)) - return - if(target_loc != target.loc) - return - if(target == user && !user.stat && !user.weakened && !user.stunned && !user.paralysis) // if drop self, then climbed in - // must be awake, not stunned or whatever - msg = "[user.name] climbs into the [src]." - user << "You climb into the [src]." - else if(target != user && !user.restrained() && !user.stat && !user.weakened && !user.stunned && !user.paralysis) - msg = "[user.name] stuffs [target.name] into the [src]!" - user << "You stuff [target.name] into the [src]!" - - user.attack_log += text("\[[time_stamp()]\] Has placed [target.name] ([target.ckey]) in disposals.") - target.attack_log += text("\[[time_stamp()]\] Has been placed in disposals by [user.name] ([user.ckey])") - msg_admin_attack("[user] ([user.ckey]) placed [target] ([target.ckey]) in a disposals unit. (JMP)") - else - return - if (target.client) - target.client.perspective = EYE_PERSPECTIVE - target.client.eye = src - target.loc = src - - for (var/mob/C in viewers(src)) - if(C == user) - continue - C.show_message(msg, 3) - - update() - return - - // can breath normally in the disposal - alter_health() - return get_turf(src) - - // attempt to move while inside - relaymove(mob/user as mob) - if(user.stat || src.flushing) - return - src.go_out(user) - return - - // leave the disposal - proc/go_out(mob/user) - - if (user.client) - user.client.eye = user.client.mob - user.client.perspective = MOB_PERSPECTIVE - user.loc = src.loc - update() - return - - - // monkeys can only pull the flush lever - attack_paw(mob/user as mob) - if(stat & BROKEN) - return - - flush = !flush - update() - return - - // ai as human but can't flush - attack_ai(mob/user as mob) - interact(user, 1) - - // human interact with machine - attack_hand(mob/user as mob) - if(user && user.loc == src) - usr << "\red You cannot reach the controls from inside." - return - /* - if(mode==-1) - usr << "\red The disposal units power is disabled." - return - */ - interact(user, 0) - - // user interaction - interact(mob/user, var/ai=0) - - src.add_fingerprint(user) - - if(istype(user, /mob/living/carbon/human)) - var/mob/living/carbon/human/M = user - if(M.h_style == "Floorlength Braid" || M.h_style == "Very Long Hair") - if(prob(5)) - M.apply_damage(30, BRUTE, "head") - M.apply_damage(45, HALLOSS) - M.visible_message("\red [user]'s hair catches in the [src]!", "\red Your hair gets caught in the [src]!") - M.say("*scream") - - if(stat & BROKEN) - user.unset_machine() - return - - var/dat = "Waste Disposal UnitWaste Disposal Unit
" - - if(!ai) // AI can't pull flush handle - if(flush) - dat += "Disposal handle: Disengage Engaged" - else - dat += "Disposal handle: Disengaged Engage" - - dat += "

Eject contents
" - - if(mode <= 0) - dat += "Pump: Off On
" - else if(mode == 1) - dat += "Pump: Off On (pressurizing)
" - else - dat += "Pump: Off On (idle)
" - - var/per = 100* air_contents.return_pressure() / (SEND_PRESSURE) - - dat += "Pressure: [round(per, 1)]%
" - - - user.set_machine(src) - user << browse(dat, "window=disposal;size=360x170") - onclose(user, "disposal") - - // handle machine interaction - - Topic(href, href_list) - if(usr.loc == src) - usr << "\red You cannot reach the controls from inside." - return - - if(mode==-1 && !href_list["eject"]) // only allow ejecting if mode is -1 - usr << "\red The disposal units power is disabled." - return - ..() - src.add_fingerprint(usr) - if(stat & BROKEN) - return - if(usr.stat || usr.restrained() || src.flushing) - return - - if (in_range(src, usr) && istype(src.loc, /turf)) - usr.set_machine(src) - - if(href_list["close"]) - usr.unset_machine() - usr << browse(null, "window=disposal") - return - - if(href_list["pump"]) - if(text2num(href_list["pump"])) - mode = 1 - else - mode = 0 - update() - - if(href_list["handle"]) - flush = text2num(href_list["handle"]) - update() - - if(href_list["eject"]) - eject() - else - usr << browse(null, "window=disposal") - usr.unset_machine() - return - return - - // eject the contents of the disposal unit - proc/eject() - for(var/atom/movable/AM in src) - AM.loc = src.loc - AM.pipe_eject(0) - update() - - // update the icon & overlays to reflect mode & status - proc/update() - overlays.Cut() - if(stat & BROKEN) - icon_state = "disposal-broken" +// create a new disposal +// find the attached trunk (if present) and init gas resvr. +/obj/machinery/disposal/New() + ..() + spawn(5) + trunk = locate() in src.loc + if(!trunk) mode = 0 flush = 0 - return + else + trunk.linked = src // link the pipe trunk to self - // flush handle - if(flush) - overlays += image('icons/obj/pipes/disposal.dmi', "dispover-handle") - - // only handle is shown if no power - if(stat & NOPOWER || mode == -1) - return - - // check for items in disposal - occupied light - if(contents.len > 0) - overlays += image('icons/obj/pipes/disposal.dmi', "dispover-full") - - // charging and ready light - if(mode == 1) - overlays += image('icons/obj/pipes/disposal.dmi', "dispover-charge") - else if(mode == 2) - overlays += image('icons/obj/pipes/disposal.dmi', "dispover-ready") - - // timed process - // charge the gas reservoir and perform flush if ready - process() - use_power = 0 - if(stat & BROKEN) // nothing can happen if broken - return - - if(!air_contents) // Potentially causes a runtime otherwise (if this is really shitty, blame pete //Donkie) - return - - flush_count++ - if( flush_count >= flush_every_ticks ) - if( contents.len ) - if(mode == 2) - spawn(0) - feedback_inc("disposal_auto_flush",1) - flush() - flush_count = 0 - - src.updateDialog() - - if(flush && air_contents.return_pressure() >= SEND_PRESSURE ) // flush can happen even without power - flush() - - if(stat & NOPOWER) // won't charge if no power - return - - use_power = 1 - - if(mode != 1) // if off or ready, no need to charge - return - - // otherwise charge - use_power = 2 - - var/atom/L = loc // recharging from loc turf - - var/datum/gas_mixture/env = L.return_air() - var/pressure_delta = (SEND_PRESSURE*1.01) - air_contents.return_pressure() - - if(env.temperature > 0) - var/transfer_moles = 0.1 * pressure_delta*air_contents.volume/(env.temperature * R_IDEAL_GAS_EQUATION) - - //Actually transfer the gas - var/datum/gas_mixture/removed = env.remove(transfer_moles) - air_contents.merge(removed) + air_contents = new/datum/gas_mixture() + air_contents.volume = PRESSURE_TANK_VOLUME + update() - // if full enough, switch to ready mode - if(air_contents.return_pressure() >= SEND_PRESSURE) - mode = 2 - update() +// attack by item places it in to disposal +/obj/machinery/disposal/attackby(var/obj/item/I, var/mob/user) + if(stat & BROKEN || !I || !user) return - // perform a flush - proc/flush() + if(isrobot(user) && !istype(I, /obj/item/weapon/storage/bag/trash)) + return + src.add_fingerprint(user) + if(mode<=0) // It's off + if(istype(I, /obj/item/weapon/screwdriver)) + if(contents.len > 0) + user << "Eject the items first!" + return + if(mode==0) // It's off but still not unscrewed + mode=-1 // Set it to doubleoff l0l + playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1) + user << "You remove the screws around the power connection." + return + else if(mode==-1) + mode=0 + playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1) + user << "You attach the screws around the power connection." + return + else if(istype(I,/obj/item/weapon/weldingtool) && mode==-1) + if(contents.len > 0) + user << "Eject the items first!" + return + var/obj/item/weapon/weldingtool/W = I + if(W.remove_fuel(0,user)) + playsound(src.loc, 'sound/items/Welder2.ogg', 100, 1) + user << "You start slicing the floorweld off the disposal unit." - flushing = 1 - flick("[icon_state]-flush", src) + if(do_after(user,20)) + if(!src || !W.isOn()) return + user << "You sliced the floorweld off the disposal unit." + var/obj/structure/disposalconstruct/C = new (src.loc) + src.transfer_fingerprints_to(C) + C.ptype = 6 // 6 = disposal unit + C.anchored = 1 + C.density = 1 + C.update() + del(src) + return + else + user << "You need more welding fuel to complete this task." + return - var/wrapcheck = 0 - var/obj/structure/disposalholder/H = new() // virtual holder object which actually - // travels through the pipes. - //Hacky test to get drones to mail themselves through disposals. - for(var/mob/living/silicon/robot/drone/D in src) - wrapcheck = 1 + if(istype(I, /obj/item/weapon/melee/energy/blade)) + user << "You can't place that item inside the disposal unit." + return - for(var/obj/item/smallDelivery/O in src) - wrapcheck = 1 - - if(wrapcheck == 1) - H.tomail = 1 - - - air_contents = new() // new empty gas resv. - - sleep(10) - if(last_sound < world.time + 1) - playsound(src, 'sound/machines/disposalflush.ogg', 50, 0, 0) - last_sound = world.time - sleep(5) // wait for animation to finish - - - H.init(src) // copy the contents of disposer to holder - - H.start(src) // start the holder processing movement - flushing = 0 - // now reset disposal state - flush = 0 - if(mode == 2) // if was ready, - mode = 1 // switch to charging + if(istype(I, /obj/item/weapon/storage/bag/trash)) + var/obj/item/weapon/storage/bag/trash/T = I + user << "\blue You empty the bag." + for(var/obj/item/O in T.contents) + T.remove_from_storage(O,src) + T.update_icon() update() return - - // called when area power changes - power_change() - ..() // do default setting/reset of stat NOPOWER bit - update() // update icon + var/obj/item/weapon/grab/G = I + if(istype(G)) // handle grabbed mob + if(ismob(G.affecting)) + var/mob/GM = G.affecting + for (var/mob/V in viewers(usr)) + V.show_message("[usr] starts putting [GM.name] into the disposal.", 3) + if(do_after(usr, 20)) + if (GM.client) + GM.client.perspective = EYE_PERSPECTIVE + GM.client.eye = src + GM.loc = src + for (var/mob/C in viewers(src)) + C.show_message("\red [GM.name] has been placed in the [src] by [user].", 3) + del(G) + usr.attack_log += text("\[[time_stamp()]\] Has placed [GM.name] ([GM.ckey]) in disposals.") + GM.attack_log += text("\[[time_stamp()]\] Has been placed in disposals by [usr.name] ([usr.ckey])") + msg_admin_attack("[usr] ([usr.ckey]) placed [GM] ([GM.ckey]) in a disposals unit. (JMP)") return + if(!I) return - // called when holder is expelled from a disposal - // should usually only occur if the pipe network is modified - proc/expel(var/obj/structure/disposalholder/H) + user.drop_item() + if(I) + I.loc = src - var/turf/target - playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) - if(H) // Somehow, someone managed to flush a window which broke mid-transit and caused the disposal to go in an infinite loop trying to expel null, hopefully this fixes it - for(var/atom/movable/AM in H) - target = get_offset_target_turf(src.loc, rand(5)-rand(5), rand(5)-rand(5)) + user << "You place \the [I] into the [src]." + for(var/mob/M in viewers(src)) + if(M == user) + continue + M.show_message("[user.name] places \the [I] into the [src].", 3) - AM.loc = src.loc - AM.pipe_eject(0) - if(!istype(AM,/mob/living/silicon/robot/drone)) //Poor drones kept smashing windows and taking system damage being fired out of disposals. ~Z - spawn(1) - if(AM) - AM.throw_at(target, 5, 1) + update() - H.vent_gas(loc) - del(H) +// mouse drop another mob or self +// +/obj/machinery/disposal/MouseDrop_T(mob/target, mob/user) + if (!istype(target) || target.buckled || get_dist(user, src) > 1 || get_dist(user, target) > 1 || user.stat || istype(user, /mob/living/silicon/ai)) + return + if(isanimal(user) && target != user) return //animals cannot put mobs other than themselves into disposal + src.add_fingerprint(user) + var/target_loc = target.loc + var/msg + for (var/mob/V in viewers(usr)) + if(target == user && !user.stat && !user.weakened && !user.stunned && !user.paralysis) + V.show_message("[usr] starts climbing into the disposal.", 3) + if(target != user && !user.restrained() && !user.stat && !user.weakened && !user.stunned && !user.paralysis) + if(target.anchored) return + V.show_message("[usr] starts stuffing [target.name] into the disposal.", 3) + if(!do_after(usr, 20)) + return + if(target_loc != target.loc) + return + if(target == user && !user.stat && !user.weakened && !user.stunned && !user.paralysis) // if drop self, then climbed in + // must be awake, not stunned or whatever + msg = "[user.name] climbs into the [src]." + user << "You climb into the [src]." + else if(target != user && !user.restrained() && !user.stat && !user.weakened && !user.stunned && !user.paralysis) + msg = "[user.name] stuffs [target.name] into the [src]!" + user << "You stuff [target.name] into the [src]!" - CanPass(atom/movable/mover, turf/target, height=0, air_group=0) - if (istype(mover,/obj/item) && mover.throwing) - var/obj/item/I = mover - if(istype(I, /obj/item/projectile)) - return - if(prob(75)) - I.loc = src - for(var/mob/M in viewers(src)) - M.show_message("\the [I] lands in \the [src].", 3) - else - for(var/mob/M in viewers(src)) - M.show_message("\the [I] bounces off of \the [src]'s rim!.", 3) - return 0 + user.attack_log += text("\[[time_stamp()]\] Has placed [target.name] ([target.ckey]) in disposals.") + target.attack_log += text("\[[time_stamp()]\] Has been placed in disposals by [user.name] ([user.ckey])") + msg_admin_attack("[user] ([user.ckey]) placed [target] ([target.ckey]) in a disposals unit. (JMP)") + else + return + if (target.client) + target.client.perspective = EYE_PERSPECTIVE + target.client.eye = src + target.loc = src + + for (var/mob/C in viewers(src)) + if(C == user) + continue + C.show_message(msg, 3) + + update() + return + +// can breath normally in the disposal +/obj/machinery/disposal/alter_health() + return get_turf(src) + +// attempt to move while inside +/obj/machinery/disposal/relaymove(mob/user as mob) + if(user.stat || src.flushing) + return + src.go_out(user) + return + +// leave the disposal +/obj/machinery/disposal/proc/go_out(mob/user) + + if (user.client) + user.client.eye = user.client.mob + user.client.perspective = MOB_PERSPECTIVE + user.loc = src.loc + update() + return + + +// monkeys can only pull the flush lever +/obj/machinery/disposal/attack_paw(mob/user as mob) + if(stat & BROKEN) + return + + flush = !flush + update() + return + +// ai as human but can't flush +/obj/machinery/disposal/attack_ai(mob/user as mob) + interact(user, 1) + +// human interact with machine +/obj/machinery/disposal/attack_hand(mob/user as mob) + if(user && user.loc == src) + usr << "\red You cannot reach the controls from inside." + return + /* + if(mode==-1) + usr << "\red The disposal units power is disabled." + return + */ + interact(user, 0) + +// user interaction +/obj/machinery/disposal/interact(mob/user, var/ai=0) + + src.add_fingerprint(user) + if(stat & BROKEN) + user.unset_machine() + return + + var/dat = "Waste Disposal UnitWaste Disposal Unit
" + + if(!ai) // AI can't pull flush handle + if(flush) + dat += "Disposal handle: Disengage Engaged" else - return ..(mover, target, height, air_group) + dat += "Disposal handle: Disengaged Engage" + + dat += "

Eject contents
" + + if(mode <= 0) + dat += "Pump: Off On
" + else if(mode == 1) + dat += "Pump: Off On (pressurizing)
" + else + dat += "Pump: Off On (idle)
" + + var/per = 100* air_contents.return_pressure() / (SEND_PRESSURE) + + dat += "Pressure: [round(per, 1)]%
" + + + user.set_machine(src) + user << browse(dat, "window=disposal;size=360x170") + onclose(user, "disposal") + +// handle machine interaction + +/obj/machinery/disposal/Topic(href, href_list) + if(usr.loc == src) + usr << "\red You cannot reach the controls from inside." + return + + if(mode==-1 && !href_list["eject"]) // only allow ejecting if mode is -1 + usr << "\red The disposal units power is disabled." + return + ..() + src.add_fingerprint(usr) + if(stat & BROKEN) + return + if(usr.stat || usr.restrained() || src.flushing) + return + + if (in_range(src, usr) && istype(src.loc, /turf)) + usr.set_machine(src) + + if(href_list["close"]) + usr.unset_machine() + usr << browse(null, "window=disposal") + return + + if(href_list["pump"]) + if(text2num(href_list["pump"])) + mode = 1 + else + mode = 0 + update() + + if(href_list["handle"]) + flush = text2num(href_list["handle"]) + update() + + if(href_list["eject"]) + eject() + else + usr << browse(null, "window=disposal") + usr.unset_machine() + return + return + +// eject the contents of the disposal unit +/obj/machinery/disposal/proc/eject() + for(var/atom/movable/AM in src) + AM.loc = src.loc + AM.pipe_eject(0) + update() + +// update the icon & overlays to reflect mode & status +/obj/machinery/disposal/proc/update() + overlays.Cut() + if(stat & BROKEN) + icon_state = "disposal-broken" + mode = 0 + flush = 0 + return + + // flush handle + if(flush) + overlays += image('icons/obj/pipes/disposal.dmi', "dispover-handle") + + // only handle is shown if no power + if(stat & NOPOWER || mode == -1) + return + + // check for items in disposal - occupied light + if(contents.len > 0) + overlays += image('icons/obj/pipes/disposal.dmi', "dispover-full") + + // charging and ready light + if(mode == 1) + overlays += image('icons/obj/pipes/disposal.dmi', "dispover-charge") + else if(mode == 2) + overlays += image('icons/obj/pipes/disposal.dmi', "dispover-ready") + +// timed process +// charge the gas reservoir and perform flush if ready +/obj/machinery/disposal/process() + if(!air_contents || (stat & BROKEN)) // nothing can happen if broken + update_use_power(0) + return + + flush_count++ + if( flush_count >= flush_every_ticks ) + if( contents.len ) + if(mode == 2) + spawn(0) + feedback_inc("disposal_auto_flush",1) + flush() + flush_count = 0 + + src.updateDialog() + + if(flush && air_contents.return_pressure() >= SEND_PRESSURE ) // flush can happen even without power + flush() + + if(mode != 1) // if off or ready, no need to charge + update_use_power(1) + return + + // otherwise charge + src.pressurize() + + // if full enough, switch to ready mode + if(air_contents.return_pressure() >= SEND_PRESSURE) + mode = 2 + update() + return + +/obj/machinery/disposal/proc/pressurize() + if(stat & NOPOWER) // won't charge if no power + update_use_power(0) + return + + var/atom/L = loc // recharging from loc turf + var/datum/gas_mixture/env = L.return_air() + + var/power_draw = -1 + if(env && env.temperature > 0) + var/transfer_moles = (PUMP_MAX_FLOW_RATE/env.volume)*env.total_moles //group_multiplier is divided out here + power_draw = pump_gas(src, env, air_contents, transfer_moles, active_power_usage) + + if (power_draw < 0) + //update_use_power(0) + use_power = 1 //don't force update - easier on CPU + else + handle_power_draw(power_draw) + +// perform a flush +/obj/machinery/disposal/proc/flush() + + flushing = 1 + flick("[icon_state]-flush", src) + + var/wrapcheck = 0 + var/obj/structure/disposalholder/H = new() // virtual holder object which actually + // travels through the pipes. + //Hacky test to get drones to mail themselves through disposals. + for(var/mob/living/silicon/robot/drone/D in src) + wrapcheck = 1 + + for(var/obj/item/smallDelivery/O in src) + wrapcheck = 1 + + if(wrapcheck == 1) + H.tomail = 1 + + + air_contents = new() // new empty gas resv. + + sleep(10) + if(last_sound < world.time + 1) + playsound(src, 'sound/machines/disposalflush.ogg', 50, 0, 0) + last_sound = world.time + sleep(5) // wait for animation to finish + + + H.init(src) // copy the contents of disposer to holder + + H.start(src) // start the holder processing movement + flushing = 0 + // now reset disposal state + flush = 0 + if(mode == 2) // if was ready, + mode = 1 // switch to charging + update() + return + + +// called when area power changes +/obj/machinery/disposal/power_change() + ..() // do default setting/reset of stat NOPOWER bit + update() // update icon + return + + +// called when holder is expelled from a disposal +// should usually only occur if the pipe network is modified +/obj/machinery/disposal/proc/expel(var/obj/structure/disposalholder/H) + + var/turf/target + playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) + if(H) // Somehow, someone managed to flush a window which broke mid-transit and caused the disposal to go in an infinite loop trying to expel null, hopefully this fixes it + for(var/atom/movable/AM in H) + target = get_offset_target_turf(src.loc, rand(5)-rand(5), rand(5)-rand(5)) + + AM.loc = src.loc + AM.pipe_eject(0) + if(!istype(AM,/mob/living/silicon/robot/drone)) //Poor drones kept smashing windows and taking system damage being fired out of disposals. ~Z + spawn(1) + if(AM) + AM.throw_at(target, 5, 1) + + H.vent_gas(loc) + del(H) + +/obj/machinery/disposal/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) + if (istype(mover,/obj/item) && mover.throwing) + var/obj/item/I = mover + if(istype(I, /obj/item/projectile)) + return + if(prob(75)) + I.loc = src + for(var/mob/M in viewers(src)) + M.show_message("\the [I] lands in \the [src].", 3) + else + for(var/mob/M in viewers(src)) + M.show_message("\the [I] bounces off of \the [src]'s rim!.", 3) + return 0 + else + return ..(mover, target, height, air_group) // virtual disposal object // travels through pipes in lieu of actual items @@ -746,10 +737,10 @@ // this will be revealed if a T-scanner is used // if visible, use regular icon_state proc/updateicon() - if(invisibility) +/* if(invisibility) //we hide things with alpha now, no need for transparent icons icon_state = "[base_icon_state]f" else - icon_state = base_icon_state + icon_state = base_icon_state*/ return diff --git a/code/modules/recycling/sortingmachinery.dm b/code/modules/recycling/sortingmachinery.dm index 5f72368e..e77ef6d0 100644 --- a/code/modules/recycling/sortingmachinery.dm +++ b/code/modules/recycling/sortingmachinery.dm @@ -53,7 +53,7 @@ if(ishuman(user)) user.put_in_hands(wrapped) else - wrapped.loc = get_turf_loc(src) + wrapped.loc = get_turf(src) del(src) return diff --git a/code/modules/research/circuitprinter.dm b/code/modules/research/circuitprinter.dm index 0757e025..69ef2050 100644 --- a/code/modules/research/circuitprinter.dm +++ b/code/modules/research/circuitprinter.dm @@ -14,6 +14,10 @@ using metal and glass, it uses glass and reagents (usually sulfuric acis). var/diamond_amount = 0 var/uranium_amount = 0 var/max_material_amount = 75000.0 + + use_power = 1 + idle_power_usage = 30 + active_power_usage = 2500 New() ..() diff --git a/code/modules/research/designs.dm b/code/modules/research/designs.dm index 198ec3a1..2923343f 100644 --- a/code/modules/research/designs.dm +++ b/code/modules/research/designs.dm @@ -1290,6 +1290,15 @@ datum/design/mrspacman materials = list("$glass" = 2000, "sacid" = 20) build_path = "/obj/item/weapon/circuitboard/pacman/mrs" +datum/design/batteryrack + name = "Cell rack PSU Board" + desc = "The circuit board for a power cell rack PSU." + id = "batteryrack" + req_tech = list("powerstorage" = 3, "engineering" = 2) + build_type = IMPRINTER + materials = list("$glass" = 2000, "sacid" = 20) + build_path = "/obj/item/weapon/circuitboard/batteryrack" + ///////////////////////////////////////// ////////////Medical Tools//////////////// @@ -1836,8 +1845,7 @@ datum/design/security_hud icon_state = "datadisk2" item_state = "card-id" w_class = 1.0 - m_amt = 30 - g_amt = 10 + matter = list("metal" = 30, "glass" = 10) var/datum/design/blueprint /obj/item/weapon/disk/design_disk/New() diff --git a/code/modules/research/destructive_analyzer.dm b/code/modules/research/destructive_analyzer.dm index 48105a55..a5642335 100644 --- a/code/modules/research/destructive_analyzer.dm +++ b/code/modules/research/destructive_analyzer.dm @@ -12,6 +12,10 @@ Note: Must be placed within 3 tiles of the R&D Console icon_state = "d_analyzer" var/obj/item/weapon/loaded_item = null var/decon_mod = 1 + + use_power = 1 + idle_power_usage = 30 + active_power_usage = 2500 /obj/machinery/r_n_d/destructive_analyzer/New() ..() diff --git a/code/modules/research/message_server.dm b/code/modules/research/message_server.dm index e9692de8..2d7f723e 100644 --- a/code/modules/research/message_server.dm +++ b/code/modules/research/message_server.dm @@ -1,3 +1,6 @@ +#define MESSAGE_SERVER_SPAM_REJECT 1 +#define MESSAGE_SERVER_DEFAULT_SPAM_LIMIT 10 + var/global/list/obj/machinery/message_server/message_servers = list() /datum/data_pda_msg @@ -59,6 +62,12 @@ var/global/list/obj/machinery/message_server/message_servers = list() var/active = 1 var/decryptkey = "password" + //Spam filtering stuff + var/list/spamfilter = list("You have won", "your prize", "male enhancement", "shitcurity", \ + "are happy to inform you", "account number", "enter your PIN") + //Messages having theese tokens will be rejected by server. Case sensitive + var/spamfilter_limit = MESSAGE_SERVER_DEFAULT_SPAM_LIMIT //Maximal amount of tokens + /obj/machinery/message_server/New() message_servers += src decryptkey = GenerateKey() @@ -89,7 +98,13 @@ var/global/list/obj/machinery/message_server/message_servers = list() return /obj/machinery/message_server/proc/send_pda_message(var/recipient = "",var/sender = "",var/message = "") + var/result + for (var/token in spamfilter) + if (findtextEx(message,token)) + message = "[message]" //Rejected messages will be indicated by red color. + result = token //Token caused rejection (if there are multiple, last will be chosen>. pda_msgs += new/datum/data_pda_msg(recipient,sender,message) + return result /obj/machinery/message_server/proc/send_rc_message(var/recipient = "",var/sender = "",var/message = "",var/stamp = "", var/id_auth = "", var/priority = 1) rc_msgs += new/datum/data_rc_msg(recipient,sender,message,stamp,id_auth) @@ -102,6 +117,16 @@ var/global/list/obj/machinery/message_server/message_servers = list() return +/obj/machinery/message_server/attackby(obj/item/weapon/O as obj, mob/living/user as mob) + if (active && !(stat & (BROKEN|NOPOWER)) && (spamfilter_limit < MESSAGE_SERVER_DEFAULT_SPAM_LIMIT*2) && \ + istype(O,/obj/item/weapon/circuitboard/message_monitor)) + spamfilter_limit += round(MESSAGE_SERVER_DEFAULT_SPAM_LIMIT / 2) + user.drop_item() + del(O) + user << "You install additional memory and processors into message server. Its filtering capabilities been enhanced." + else + ..(O, user) + /obj/machinery/message_server/update_icon() if((stat & (BROKEN|NOPOWER))) icon_state = "server-nopower" diff --git a/code/modules/research/protolathe.dm b/code/modules/research/protolathe.dm index bdf879d6..5773ecb6 100644 --- a/code/modules/research/protolathe.dm +++ b/code/modules/research/protolathe.dm @@ -11,6 +11,10 @@ Note: Must be placed west/left of and R&D console to function. name = "Protolathe" icon_state = "protolathe" flags = OPENCONTAINER + + use_power = 1 + idle_power_usage = 30 + active_power_usage = 5000 var/max_material_storage = 100000 //All this could probably be done better with a list but meh. var/m_amount = 0.0 diff --git a/code/modules/research/rdconsole.dm b/code/modules/research/rdconsole.dm index 53aff8a2..f1822f9a 100644 --- a/code/modules/research/rdconsole.dm +++ b/code/modules/research/rdconsole.dm @@ -263,9 +263,9 @@ won't update every console in existence) but it's more of a hassle to do. Also, files.UpdateTech(T, temp_tech[T]) if(linked_destroy.loaded_item.reliability < 100 && linked_destroy.loaded_item.crit_fail) files.UpdateDesign(linked_destroy.loaded_item.type) - if(linked_lathe) //Also sends salvaged materials to a linked protolathe, if any. - linked_lathe.m_amount += min((linked_lathe.max_material_storage - linked_lathe.TotalMaterials()), (linked_destroy.loaded_item.m_amt*linked_destroy.decon_mod)) - linked_lathe.g_amount += min((linked_lathe.max_material_storage - linked_lathe.TotalMaterials()), (linked_destroy.loaded_item.g_amt*linked_destroy.decon_mod)) + if(linked_lathe && linked_destroy.loaded_item.matter) //Also sends salvaged materials to a linked protolathe, if any. + linked_lathe.m_amount += min((linked_lathe.max_material_storage - linked_lathe.TotalMaterials()), (linked_destroy.loaded_item.matter["metal"]*linked_destroy.decon_mod)) + linked_lathe.g_amount += min((linked_lathe.max_material_storage - linked_lathe.TotalMaterials()), (linked_destroy.loaded_item.matter["glass"]*linked_destroy.decon_mod)) linked_destroy.loaded_item = null for(var/obj/I in linked_destroy.contents) for(var/mob/M in I.contents) @@ -282,7 +282,7 @@ won't update every console in existence) but it's more of a hassle to do. Also, if(!(I in linked_destroy.component_parts)) del(I) linked_destroy.icon_state = "d_analyzer" - use_power(250) + use_power(linked_destroy.active_power_usage) screen = 1.0 updateUsrDialog() @@ -319,7 +319,7 @@ won't update every console in existence) but it's more of a hassle to do. Also, files.RefreshResearch() server_processed = 1 if(!istype(S, /obj/machinery/r_n_d/server/centcom) && server_processed) - S.produce_heat(100) + S.produce_heat() screen = 1.6 updateUsrDialog() @@ -334,10 +334,10 @@ won't update every console in existence) but it's more of a hassle to do. Also, being_built = D break if(being_built) - var/power = 2000 + var/power = linked_lathe.active_power_usage for(var/M in being_built.materials) power += round(being_built.materials[M] / 5) - power = max(2000, power) + power = max(linked_lathe.active_power_usage, power) screen = 0.3 linked_lathe.busy = 1 flick("protolathe_n",linked_lathe) @@ -390,10 +390,10 @@ won't update every console in existence) but it's more of a hassle to do. Also, being_built = D break if(being_built) - var/power = 2000 + var/power = linked_imprinter.active_power_usage for(var/M in being_built.materials) power += round(being_built.materials[M] / 5) - power = max(2000, power) + power = max(linked_imprinter.active_power_usage, power) screen = 0.4 linked_imprinter.busy = 1 flick("circuit_imprinter_ani",linked_imprinter) diff --git a/code/modules/research/research.dm b/code/modules/research/research.dm index 34913490..741d9050 100644 --- a/code/modules/research/research.dm +++ b/code/modules/research/research.dm @@ -263,8 +263,7 @@ datum/tech/robotics icon_state = "datadisk2" item_state = "card-id" w_class = 1.0 - m_amt = 30 - g_amt = 10 + matter = list("metal" = 30, "glass" = 10) var/datum/tech/stored /obj/item/weapon/disk/tech_disk/New() diff --git a/code/modules/research/server.dm b/code/modules/research/server.dm index 8ab91623..7acb400b 100644 --- a/code/modules/research/server.dm +++ b/code/modules/research/server.dm @@ -9,8 +9,8 @@ var/id_with_upload_string = "" //String versions for easy editing in map editor. var/id_with_download_string = "" var/server_id = 0 - var/heat_gen = 100 - var/heating_power = 40000 + var/produces_heat = 1 + idle_power_usage = 800 var/delay = 10 req_access = list(access_rd) //Only the R&D can change server settings. @@ -32,7 +32,7 @@ var/tot_rating = 0 for(var/obj/item/weapon/stock_parts/SP in src) tot_rating += SP.rating - heat_gen /= max(1, tot_rating) + idle_power_usage /= max(1, tot_rating) /obj/machinery/r_n_d/server/initialize() if(!files) files = new /datum/research(src) @@ -67,7 +67,7 @@ if(delay) delay-- else - produce_heat(heat_gen) + produce_heat() delay = initial(delay) /obj/machinery/r_n_d/server/meteorhit(var/obj/O as obj) @@ -100,25 +100,28 @@ C.files.AddDesign2Known(D) C.files.RefreshResearch() -/obj/machinery/r_n_d/server/proc/produce_heat(heat_amt) - if(!(stat & (NOPOWER|BROKEN))) //Blatently stolen from space heater. +/obj/machinery/r_n_d/server/proc/produce_heat() + if (!produces_heat) + return + + if (!use_power) + return + + if(!(stat & (NOPOWER|BROKEN))) //Blatently stolen from telecoms 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 + + 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) /obj/machinery/r_n_d/server/attackby(var/obj/item/O as obj, var/mob/user as mob) if (disabled) diff --git a/code/modules/research/xenoarchaeology/artifact/artifact_unknown.dm b/code/modules/research/xenoarchaeology/artifact/artifact_unknown.dm index a0762ff5..578f6cc1 100644 --- a/code/modules/research/xenoarchaeology/artifact/artifact_unknown.dm +++ b/code/modules/research/xenoarchaeology/artifact/artifact_unknown.dm @@ -143,13 +143,13 @@ var/list/valid_secondary_effect_types = list(\ else if(env.temperature > 375) trigger_hot = 1 - if(env.toxins >= 10) + if(env.gas["toxins"] >= 10) trigger_plasma = 1 - if(env.oxygen >= 10) + if(env.gas["toxins"] >= 10) trigger_oxy = 1 - if(env.carbon_dioxide >= 10) + if(env.gas["carbon_dioxide"] >= 10) trigger_co2 = 1 - if(env.nitrogen >= 10) + if(env.gas["nitrogen"] >= 10) trigger_nitro = 1 //COLD ACTIVATION diff --git a/code/modules/research/xenoarchaeology/artifact/effects/unknown_effect_gasco2.dm b/code/modules/research/xenoarchaeology/artifact/effects/unknown_effect_gasco2.dm index c9cafa4e..c76ba927 100644 --- a/code/modules/research/xenoarchaeology/artifact/effects/unknown_effect_gasco2.dm +++ b/code/modules/research/xenoarchaeology/artifact/effects/unknown_effect_gasco2.dm @@ -15,12 +15,12 @@ /datum/artifact_effect/gasco2/DoEffectTouch(var/mob/user) if(holder) - var/datum/gas_mixture/env = holder.loc.return_air() - if(env) - env.carbon_dioxide += rand(2,15) + var/turf/holder_loc = holder.loc + if(istype(holder_loc)) + holder_loc.assume_gas("carbon_dioxide", rand(2, 15)) /datum/artifact_effect/gasco2/DoEffectAura() if(holder) - var/datum/gas_mixture/env = holder.loc.return_air() - if(env && env.total_moles < max_pressure) - env.carbon_dioxide += pick(0, 0, 0.1, rand()) + var/turf/holder_loc = holder.loc + if(istype(holder_loc)) + holder_loc.assume_gas("carbon_dioxide", pick(0, 0, 0.1, rand())) diff --git a/code/modules/research/xenoarchaeology/artifact/effects/unknown_effect_gasnitro.dm b/code/modules/research/xenoarchaeology/artifact/effects/unknown_effect_gasnitro.dm index 527c2592..31ab6562 100644 --- a/code/modules/research/xenoarchaeology/artifact/effects/unknown_effect_gasnitro.dm +++ b/code/modules/research/xenoarchaeology/artifact/effects/unknown_effect_gasnitro.dm @@ -12,12 +12,12 @@ /datum/artifact_effect/gasnitro/DoEffectTouch(var/mob/user) if(holder) - var/datum/gas_mixture/env = holder.loc.return_air() - if(env) - env.nitrogen += rand(2,15) + var/turf/holder_loc = holder.loc + if(istype(holder_loc)) + holder_loc.assume_gas("nitrogen", rand(2, 15)) /datum/artifact_effect/gasnitro/DoEffectAura() if(holder) - var/datum/gas_mixture/env = holder.loc.return_air() - if(env && env.total_moles < max_pressure) - env.nitrogen += pick(0, 0, 0.1, rand()) + var/turf/holder_loc = holder.loc + if(istype(holder_loc)) + holder_loc.assume_gas("nitrogen", pick(0, 0, 0.1, rand())) diff --git a/code/modules/research/xenoarchaeology/artifact/effects/unknown_effect_gasoxy.dm b/code/modules/research/xenoarchaeology/artifact/effects/unknown_effect_gasoxy.dm index d0daa695..888589d2 100644 --- a/code/modules/research/xenoarchaeology/artifact/effects/unknown_effect_gasoxy.dm +++ b/code/modules/research/xenoarchaeology/artifact/effects/unknown_effect_gasoxy.dm @@ -12,12 +12,12 @@ /datum/artifact_effect/gasoxy/DoEffectTouch(var/mob/user) if(holder) - var/datum/gas_mixture/env = holder.loc.return_air() - if(env) - env.oxygen += rand(2,15) + var/turf/holder_loc = holder.loc + if(istype(holder_loc)) + holder_loc.assume_gas("oxygen", rand(2, 15)) /datum/artifact_effect/gasoxy/DoEffectAura() if(holder) - var/datum/gas_mixture/env = holder.loc.return_air() - if(env && env.total_moles < max_pressure) - env.oxygen += pick(0, 0, 0.1, rand()) + var/turf/holder_loc = holder.loc + if(istype(holder_loc)) + holder_loc.assume_gas("oxygen", pick(0, 0, 0.1, rand())) diff --git a/code/modules/research/xenoarchaeology/artifact/effects/unknown_effect_gasplasma.dm b/code/modules/research/xenoarchaeology/artifact/effects/unknown_effect_gasplasma.dm index c375e746..c4ee9c58 100644 --- a/code/modules/research/xenoarchaeology/artifact/effects/unknown_effect_gasplasma.dm +++ b/code/modules/research/xenoarchaeology/artifact/effects/unknown_effect_gasplasma.dm @@ -12,12 +12,12 @@ /datum/artifact_effect/gasplasma/DoEffectTouch(var/mob/user) if(holder) - var/datum/gas_mixture/env = holder.loc.return_air() - if(env) - env.toxins += rand(2,15) + var/turf/holder_loc = holder.loc + if(istype(holder_loc)) + holder_loc.assume_gas("toxins", rand(2, 15)) //They had oxygen in the original. Surely they meant plasma? /datum/artifact_effect/gasplasma/DoEffectAura() if(holder) - var/datum/gas_mixture/env = holder.loc.return_air() - if(env && env.total_moles < max_pressure) - env.toxins += pick(0, 0, 0.1, rand()) + var/turf/holder_loc = holder.loc + if(istype(holder_loc)) + holder_loc.assume_gas("toxins", pick(0, 0, 0.1, rand())) diff --git a/code/modules/research/xenoarchaeology/artifact/effects/unknown_effect_gassleeping.dm b/code/modules/research/xenoarchaeology/artifact/effects/unknown_effect_gassleeping.dm index 5ce52567..420a31d6 100644 --- a/code/modules/research/xenoarchaeology/artifact/effects/unknown_effect_gassleeping.dm +++ b/code/modules/research/xenoarchaeology/artifact/effects/unknown_effect_gassleeping.dm @@ -12,20 +12,12 @@ /datum/artifact_effect/gassleeping/DoEffectTouch(var/mob/user) if(holder) - var/datum/gas_mixture/env = holder.loc.return_air() - if(env) - var/datum/gas/sleeping_agent/trace_gas = new - env.trace_gases += trace_gas - trace_gas.moles = rand(2,15) - env.update_values() - + var/turf/holder_loc = holder.loc + if(istype(holder_loc)) + holder_loc.assume_gas("sleeping_agent", rand(2, 15)) /datum/artifact_effect/gassleeping/DoEffectAura() if(holder) - var/datum/gas_mixture/env = holder.loc.return_air() - if(env && env.total_moles < max_pressure) - var/datum/gas/sleeping_agent/trace_gas = new - env.trace_gases += trace_gas - trace_gas.moles = pick(0, 0, 0.1, rand()) - env.update_values() - + var/turf/holder_loc = holder.loc + if(istype(holder_loc)) + holder_loc.assume_gas("sleeping_agent", pick(0, 0, 0.1, rand())) diff --git a/code/modules/research/xenoarchaeology/chemistry.dm b/code/modules/research/xenoarchaeology/chemistry.dm index 13a8367b..e6c622a9 100644 --- a/code/modules/research/xenoarchaeology/chemistry.dm +++ b/code/modules/research/xenoarchaeology/chemistry.dm @@ -75,8 +75,7 @@ datum desc = "A small, open-topped glass container for delicate research samples. It sports a re-useable strip for labelling with a pen." icon = 'icons/obj/device.dmi' icon_state = "solution_tray" - m_amt = 0 - g_amt = 5 + matter = list("glass" = 5) w_class = 2.0 amount_per_transfer_from_this = 1 possible_transfer_amounts = list(1, 2) diff --git a/code/modules/security levels/keycard authentication.dm b/code/modules/security levels/keycard authentication.dm index febdc29e..29300f15 100644 --- a/code/modules/security levels/keycard authentication.dm +++ b/code/modules/security levels/keycard authentication.dm @@ -45,11 +45,9 @@ broadcast_request() //This is the device making the initial event request. It needs to broadcast to other devices /obj/machinery/keycard_auth/power_change() - if(powered(ENVIRON)) - stat &= ~NOPOWER + ..() + if(stat &NOPOWER) icon_state = "auth_off" - else - stat |= NOPOWER /obj/machinery/keycard_auth/attack_hand(mob/user as mob) if(user.stat || stat & (NOPOWER|BROKEN)) @@ -71,7 +69,7 @@ dat += "
  • Red alert
  • " if(!config.ert_admin_call_only) dat += "
  • Emergency Response Team
  • " - + dat += "
  • Grant Emergency Maintenance Access
  • " dat += "
  • Revoke Emergency Maintenance Access
  • " dat += "" diff --git a/code/modules/shuttles/antagonist.dm b/code/modules/shuttles/antagonist.dm new file mode 100644 index 00000000..c49c0990 --- /dev/null +++ b/code/modules/shuttles/antagonist.dm @@ -0,0 +1,9 @@ +/obj/machinery/computer/shuttle_control/multi/vox + name = "skipjack control console" + req_access = list(access_syndicate) + shuttle_tag = "Vox Skipjack" + +/obj/machinery/computer/shuttle_control/multi/syndicate + name = "Syndicate control console" + req_access = list(access_syndicate) + shuttle_tag = "Syndicate" \ No newline at end of file diff --git a/code/modules/shuttles/departmental.dm b/code/modules/shuttles/departmental.dm new file mode 100644 index 00000000..9efd0ed7 --- /dev/null +++ b/code/modules/shuttles/departmental.dm @@ -0,0 +1,17 @@ +/obj/machinery/computer/shuttle_control/mining + name = "mining shuttle console" + shuttle_tag = "Mining" + //req_access = list(access_mining) + circuit = /obj/item/weapon/circuitboard/mining_shuttle + +/obj/machinery/computer/shuttle_control/engineering + name = "engineering shuttle console" + shuttle_tag = "Engineering" + //req_one_access_txt = "11;24" + circuit = /obj/item/weapon/circuitboard/engineering_shuttle + +/obj/machinery/computer/shuttle_control/research + name = "research shuttle console" + shuttle_tag = "Research" + //req_access = list(access_research) + circuit = /obj/item/weapon/circuitboard/research_shuttle \ No newline at end of file diff --git a/code/modules/shuttles/escape_pods.dm b/code/modules/shuttles/escape_pods.dm new file mode 100644 index 00000000..719c29fd --- /dev/null +++ b/code/modules/shuttles/escape_pods.dm @@ -0,0 +1,139 @@ +/datum/shuttle/ferry/escape_pod + var/datum/computer/file/embedded_program/docking/simple/escape_pod/arming_controller + +/datum/shuttle/ferry/escape_pod/can_launch() + if(arming_controller && !arming_controller.armed) //must be armed + return 0 + if(location) + return 0 //it's a one-way trip. + return ..() + +/datum/shuttle/ferry/escape_pod/can_force() + if (arming_controller.eject_time && world.time < arming_controller.eject_time + 50) + return 0 //dont allow force launching until 5 seconds after the arming controller has reached it's countdown + return ..() + +/datum/shuttle/ferry/escape_pod/can_cancel() + return 0 + + +//This controller goes on the escape pod itself +/obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod + name = "escape pod controller" + var/datum/shuttle/ferry/escape_pod/pod + +/obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + var/data[0] + + data = list( + "docking_status" = docking_program.get_docking_status(), + "override_enabled" = docking_program.override_enabled, + "door_state" = docking_program.memory["door_status"]["state"], + "door_lock" = docking_program.memory["door_status"]["lock"], + "can_force" = pod.can_force() || (emergency_shuttle.departed && pod.can_launch()), //allow players to manually launch ahead of time if the shuttle leaves + "is_armed" = pod.arming_controller.armed, + ) + + ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) + + if (!ui) + ui = new(user, src, ui_key, "escape_pod_console.tmpl", name, 470, 290) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod/Topic(href, href_list) + if(..()) //I hate this "return 1 to indicate they are not allowed to use the controller" crap, but not sure how else to do it without being able to call machinery/Topic() directly. + return 1 + + if("manual_arm") + pod.arming_controller.arm() + if("force_launch") + if (pod.can_force()) + pod.force_launch(src) + else if (emergency_shuttle.departed && pod.can_launch()) //allow players to manually launch ahead of time if the shuttle leaves + pod.launch(src) + + return 0 + + + +//This controller is for the escape pod berth (station side) +/obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod_berth + name = "escape pod berth controller" + +/obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod_berth/initialize() + ..() + docking_program = new/datum/computer/file/embedded_program/docking/simple/escape_pod(src) + program = docking_program + +/obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod_berth/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + var/data[0] + + var/armed = null + if (istype(docking_program, /datum/computer/file/embedded_program/docking/simple/escape_pod)) + var/datum/computer/file/embedded_program/docking/simple/escape_pod/P = docking_program + armed = P.armed + + data = list( + "docking_status" = docking_program.get_docking_status(), + "override_enabled" = docking_program.override_enabled, + "armed" = armed, + ) + + ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) + + if (!ui) + ui = new(user, src, ui_key, "escape_pod_berth_console.tmpl", name, 470, 290) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod_berth/attackby(obj/item/weapon/W as obj, mob/user as mob) + if (istype(W, /obj/item/weapon/card/emag) && !emagged) + user << "\blue You emag the [src], arming the escape pod!" + emagged = 1 + if (istype(docking_program, /datum/computer/file/embedded_program/docking/simple/escape_pod)) + var/datum/computer/file/embedded_program/docking/simple/escape_pod/P = docking_program + if (!P.armed) + P.arm() + return + + ..() + + + +//A docking controller program for a simple door based docking port +/datum/computer/file/embedded_program/docking/simple/escape_pod + var/armed = 0 + var/eject_delay = 10 //give latecomers some time to get out of the way if they don't make it onto the pod + var/eject_time = null + var/closing = 0 + +/datum/computer/file/embedded_program/docking/simple/escape_pod/proc/arm() + armed = 1 + open_door() + + +/datum/computer/file/embedded_program/docking/simple/escape_pod/receive_user_command(command) + if (!armed) + return + ..(command) + +/datum/computer/file/embedded_program/docking/simple/escape_pod/process() + ..() + if (eject_time && world.time >= eject_time && !closing) + close_door() + closing = 1 + +/datum/computer/file/embedded_program/docking/simple/escape_pod/prepare_for_docking() + return + +/datum/computer/file/embedded_program/docking/simple/escape_pod/ready_for_docking() + return 1 + +/datum/computer/file/embedded_program/docking/simple/escape_pod/finish_docking() + return //don't do anything - the doors only open when the pod is armed. + +/datum/computer/file/embedded_program/docking/simple/escape_pod/prepare_for_undocking() + eject_time = world.time + eject_delay*10 diff --git a/code/modules/shuttles/shuttle.dm b/code/modules/shuttles/shuttle.dm new file mode 100644 index 00000000..27e42312 --- /dev/null +++ b/code/modules/shuttles/shuttle.dm @@ -0,0 +1,128 @@ +//These lists are populated in /datum/shuttle_controller/New() +//Shuttle controller is instantiated in master_controller.dm. + +//shuttle moving state defines are in setup.dm + +/datum/shuttle + var/warmup_time = 0 + var/moving_status = SHUTTLE_IDLE + + var/docking_controller_tag //tag of the controller used to coordinate docking + var/datum/computer/file/embedded_program/docking/docking_controller //the controller itself. (micro-controller, not game controller) + + var/arrive_time = 0 //the time at which the shuttle arrives when long jumping + +/datum/shuttle/proc/short_jump(var/area/origin,var/area/destination) + if(moving_status != SHUTTLE_IDLE) return + + //it would be cool to play a sound here + moving_status = SHUTTLE_WARMUP + spawn(warmup_time*10) + if (moving_status == SHUTTLE_IDLE) + return //someone cancelled the launch + + move(origin, destination) + moving_status = SHUTTLE_IDLE + +/datum/shuttle/proc/long_jump(var/area/departing, var/area/destination, var/area/interim, var/travel_time, var/direction) + //world << "shuttle/long_jump: departing=[departing], destination=[destination], interim=[interim], travel_time=[travel_time]" + if(moving_status != SHUTTLE_IDLE) return + + //it would be cool to play a sound here + moving_status = SHUTTLE_WARMUP + spawn(warmup_time*10) + if (moving_status == SHUTTLE_IDLE) + return //someone cancelled the launch + + arrive_time = world.time + travel_time*10 + moving_status = SHUTTLE_INTRANSIT + move(departing, interim, direction) + + + while (world.time < arrive_time) + sleep(5) + + move(interim, destination, direction) + moving_status = SHUTTLE_IDLE + +/datum/shuttle/proc/dock() + if (!docking_controller) + return + + var/dock_target = current_dock_target() + if (!dock_target) + return + + docking_controller.initiate_docking(dock_target) + +/datum/shuttle/proc/undock() + if (!docking_controller) + return + docking_controller.initiate_undocking() + +/datum/shuttle/proc/current_dock_target() + return null + +/datum/shuttle/proc/skip_docking_checks() + if (!docking_controller || !current_dock_target()) + return 1 //shuttles without docking controllers or at locations without docking ports act like old-style shuttles + return 0 + +//just moves the shuttle from A to B, if it can be moved +//A note to anyone overriding move in a subtype. move() must absolutely not, under any circumstances, fail to move the shuttle. +//If you want to conditionally cancel shuttle launches, that logic must go in short_jump() or long_jump() +/datum/shuttle/proc/move(var/area/origin, var/area/destination, var/direction=null) + + //world << "move_shuttle() called for [shuttle_tag] leaving [origin] en route to [destination]." + + //world << "area_coming_from: [origin]" + //world << "destination: [destination]" + + if(origin == destination) + //world << "cancelling move, shuttle will overlap." + return + + if (docking_controller && !docking_controller.undocked()) + docking_controller.force_undock() + + var/list/dstturfs = list() + var/throwy = world.maxy + + for(var/turf/T in destination) + dstturfs += T + if(T.y < throwy) + throwy = T.y + + for(var/turf/T in dstturfs) + var/turf/D = locate(T.x, throwy - 1, 1) + for(var/atom/movable/AM as mob|obj in T) + AM.Move(D) + if(istype(T, /turf/simulated)) + del(T) + + for(var/mob/living/carbon/bug in destination) + bug.gib() + + for(var/mob/living/simple_animal/pest in destination) + pest.gib() + + origin.move_contents_to(destination, direction=direction) + + for(var/mob/M in destination) + if(M.client) + spawn(0) + if(M.buckled) + M << "\red Sudden acceleration presses you into your chair!" + shake_camera(M, 3, 1) + else + M << "\red The floor lurches beneath you!" + shake_camera(M, 10, 1) + if(istype(M, /mob/living/carbon)) + if(!M.buckled) + M.Weaken(3) + + return + +//returns 1 if the shuttle has a valid arrive time +/datum/shuttle/proc/has_arrive_time() + return (moving_status == SHUTTLE_INTRANSIT) \ No newline at end of file diff --git a/code/modules/shuttles/shuttle_console.dm b/code/modules/shuttles/shuttle_console.dm new file mode 100644 index 00000000..a5b08a0b --- /dev/null +++ b/code/modules/shuttles/shuttle_console.dm @@ -0,0 +1,97 @@ +/obj/machinery/computer/shuttle_control + name = "shuttle control console" + icon = 'icons/obj/computer.dmi' + icon_state = "shuttle" + circuit = null + + var/shuttle_tag // Used to coordinate data in shuttle controller. + var/hacked = 0 // Has been emagged, no access restrictions. + + +/obj/machinery/computer/shuttle_control/attack_hand(user as mob) + if(..(user)) + return + //src.add_fingerprint(user) //shouldn't need fingerprints just for looking at it. + if(!allowed(user)) + user << "\red Access Denied." + return 1 + + ui_interact(user) + +/obj/machinery/computer/shuttle_control/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)) + return + + var/shuttle_state + switch(shuttle.moving_status) + if(SHUTTLE_IDLE) shuttle_state = "idle" + if(SHUTTLE_WARMUP) shuttle_state = "warmup" + if(SHUTTLE_INTRANSIT) shuttle_state = "in_transit" + + var/shuttle_status + switch (shuttle.process_state) + if(IDLE_STATE) + if (shuttle.in_use) + shuttle_status = "Busy." + else if (!shuttle.location) + shuttle_status = "Standing-by at station." + else + shuttle_status = "Standing-by at offsite location." + if(WAIT_LAUNCH) + shuttle_status = "Shuttle has recieved command and will depart shortly." + if(WAIT_ARRIVE) + shuttle_status = "Proceeding to destination." + if(WAIT_FINISH) + shuttle_status = "Arriving at destination now." + + data = list( + "shuttle_status" = shuttle_status, + "shuttle_state" = shuttle_state, + "has_docking" = shuttle.docking_controller? 1 : 0, + "docking_status" = shuttle.docking_controller? shuttle.docking_controller.get_docking_status() : null, + "docking_override" = shuttle.docking_controller? shuttle.docking_controller.override_enabled : null, + "can_launch" = shuttle.can_launch(), + "can_cancel" = shuttle.can_cancel(), + "can_force" = shuttle.can_force(), + ) + + ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) + + if (!ui) + ui = new(user, src, ui_key, "shuttle_control_console.tmpl", "[shuttle_tag] Shuttle Control", 470, 310) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/computer/shuttle_control/Topic(href, href_list) + if(..()) + return + + usr.set_machine(src) + src.add_fingerprint(usr) + + var/datum/shuttle/ferry/shuttle = shuttle_controller.shuttles[shuttle_tag] + if (!istype(shuttle)) + return + + if(href_list["move"]) + shuttle.launch(src) + if(href_list["force"]) + shuttle.force_launch(src) + else if(href_list["cancel"]) + shuttle.cancel_launch(src) + +/obj/machinery/computer/shuttle_control/attackby(obj/item/weapon/W as obj, mob/user as mob) + + if (istype(W, /obj/item/weapon/card/emag)) + src.req_access = list() + src.req_one_access = list() + hacked = 1 + usr << "You short out the console's ID checking system. It's now available to everyone!" + else + ..() + +/obj/machinery/computer/shuttle_control/bullet_act(var/obj/item/projectile/Proj) + visible_message("[Proj] ricochets off [src]!") diff --git a/code/modules/shuttles/shuttle_emergency.dm b/code/modules/shuttles/shuttle_emergency.dm new file mode 100644 index 00000000..daa783e4 --- /dev/null +++ b/code/modules/shuttles/shuttle_emergency.dm @@ -0,0 +1,236 @@ + +/datum/shuttle/ferry/emergency + //pass + +/datum/shuttle/ferry/emergency/arrived() + if (istype(in_use, /obj/machinery/computer/shuttle_control/emergency)) + var/obj/machinery/computer/shuttle_control/emergency/C = in_use + C.reset_authorization() + + emergency_shuttle.shuttle_arrived() + +/datum/shuttle/ferry/emergency/long_jump(var/area/departing, var/area/destination, var/area/interim, var/travel_time, var/direction) + //world << "shuttle/ferry/emergency/long_jump: departing=[departing], destination=[destination], interim=[interim], travel_time=[travel_time]" + if (!location) + travel_time = SHUTTLE_TRANSIT_DURATION_RETURN + else + travel_time = SHUTTLE_TRANSIT_DURATION + + //update move_time and launch_time so we get correct ETAs + move_time = travel_time + emergency_shuttle.launch_time = world.time + + ..() + +/datum/shuttle/ferry/emergency/move(var/area/origin,var/area/destination) + ..(origin, destination) + + if (origin == area_station) //leaving the station + emergency_shuttle.departed = 1 + + if (emergency_shuttle.evac) + captain_announce("The Emergency Shuttle has left the station. Estimate [round(emergency_shuttle.estimate_arrival_time()/60,1)] minutes until the shuttle docks at Central Command.") + else + captain_announce("The Crew Transfer Shuttle has left the station. Estimate [round(emergency_shuttle.estimate_arrival_time()/60,1)] minutes until the shuttle docks at Central Command.") + +/datum/shuttle/ferry/emergency/can_launch(var/user) + if (istype(user, /obj/machinery/computer/shuttle_control/emergency)) + var/obj/machinery/computer/shuttle_control/emergency/C = user + if (!C.has_authorization()) + return 0 + return ..() + +/datum/shuttle/ferry/emergency/can_force(var/user) + if (istype(user, /obj/machinery/computer/shuttle_control/emergency)) + var/obj/machinery/computer/shuttle_control/emergency/C = user + + //initiating or cancelling a launch ALWAYS requires authorization, but if we are already set to launch anyways than forcing does not. + //this is so that people can force launch if the docking controller cannot safely undock without needing X heads to swipe. + if (process_state != WAIT_LAUNCH && !C.has_authorization()) + return 0 + return ..() + +/datum/shuttle/ferry/emergency/can_cancel(var/user) + if (istype(user, /obj/machinery/computer/shuttle_control/emergency)) + var/obj/machinery/computer/shuttle_control/emergency/C = user + if (!C.has_authorization()) + return 0 + return ..() + +/datum/shuttle/ferry/emergency/launch(var/user) + if (!can_launch(user)) return + + if (istype(user, /obj/machinery/computer/shuttle_control/emergency)) //if we were given a command by an emergency shuttle console + if (emergency_shuttle.autopilot) + emergency_shuttle.autopilot = 0 + world << "\blue Alert: The shuttle autopilot has been overridden. Launch sequence initiated!" + + ..(user) + +/datum/shuttle/ferry/emergency/force_launch(var/user) + if (!can_force(user)) return + + if (istype(user, /obj/machinery/computer/shuttle_control/emergency)) //if we were given a command by an emergency shuttle console + if (emergency_shuttle.autopilot) + emergency_shuttle.autopilot = 0 + world << "\blue Alert: The shuttle autopilot has been overridden. Bluespace drive engaged!" + + ..(user) + +/datum/shuttle/ferry/emergency/cancel_launch(var/user) + if (!can_cancel(user)) return + + if (istype(user, /obj/machinery/computer/shuttle_control/emergency)) //if we were given a command by an emergency shuttle console + if (emergency_shuttle.autopilot) + emergency_shuttle.autopilot = 0 + world << "\blue Alert: The shuttle autopilot has been overridden. Launch sequence aborted!" + + ..(user) + + + +/obj/machinery/computer/shuttle_control/emergency + shuttle_tag = "Escape" + var/debug = 0 + var/req_authorizations = 2 + var/list/authorized = list() + +/obj/machinery/computer/shuttle_control/emergency/proc/has_authorization() + return (authorized.len >= req_authorizations || emagged) + +/obj/machinery/computer/shuttle_control/emergency/proc/reset_authorization() + //no need to reset emagged status. If they really want to go back to the station they can. + authorized = initial(authorized) + +//returns 1 if the ID was accepted and a new authorization was added, 0 otherwise +/obj/machinery/computer/shuttle_control/emergency/proc/read_authorization(var/ident) + if (!ident) + return 0 + if (authorized.len >= req_authorizations) + return 0 //don't need any more + + var/list/access + var/auth_name + var/dna_hash + + if(istype(ident, /obj/item/weapon/card/id)) + var/obj/item/weapon/card/id/ID = ident + access = ID.access + auth_name = "[ID.registered_name] ([ID.assignment])" + dna_hash = ID.dna_hash + + if(istype(ident, /obj/item/device/pda)) + var/obj/item/device/pda/PDA = ident + access = PDA.id.access + auth_name = "[PDA.id.registered_name] ([PDA.id.assignment])" + dna_hash = PDA.id.dna_hash + + if (!access || !istype(access)) + return 0 //not an ID + + if (dna_hash in authorized) + src.visible_message("[src] buzzes. That ID has already been scanned.") + return 0 + + if (!(access_heads in access)) + src.visible_message("[src] buzzes, rejecting [ident].") + return 0 + + src.visible_message("[src] beeps as it scans [ident].") + authorized[dna_hash] = auth_name + if (req_authorizations - authorized.len) + world << "\blue Alert: [req_authorizations - authorized.len] authorization\s needed to override the shuttle autopilot." + return 1 + + + + +/obj/machinery/computer/shuttle_control/emergency/attackby(obj/item/weapon/W as obj, mob/user as mob) + if (istype(W, /obj/item/weapon/card/emag) && !emagged) + user << "\blue You short out the [src]'s authorization protocols." + emagged = 1 + return + + read_authorization(W) + ..() + +/obj/machinery/computer/shuttle_control/emergency/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + var/data[0] + var/datum/shuttle/ferry/emergency/shuttle = shuttle_controller.shuttles[shuttle_tag] + if (!istype(shuttle)) + return + + var/shuttle_state + switch(shuttle.moving_status) + if(SHUTTLE_IDLE) shuttle_state = "idle" + if(SHUTTLE_WARMUP) shuttle_state = "warmup" + if(SHUTTLE_INTRANSIT) shuttle_state = "in_transit" + + var/shuttle_status + switch (shuttle.process_state) + if(IDLE_STATE) + if (shuttle.in_use) + shuttle_status = "Busy." + else if (!shuttle.location) + shuttle_status = "Standing-by at [station_name]." + else + shuttle_status = "Standing-by at Central Command." + if(WAIT_LAUNCH) + shuttle_status = "Shuttle has recieved command and will depart shortly." + if(WAIT_ARRIVE) + shuttle_status = "Proceeding to destination." + if(WAIT_FINISH) + shuttle_status = "Arriving at destination now." + + //build a list of authorizations + var/list/auth_list[req_authorizations] + + if (!emagged) + var/i = 1 + for (var/dna_hash in authorized) + auth_list[i++] = list("auth_name"=authorized[dna_hash], "auth_hash"=dna_hash) + + while (i <= req_authorizations) //fill up the rest of the list with blank entries + auth_list[i++] = list("auth_name"="", "auth_hash"=null) + else + for (var/i = 1; i <= req_authorizations; i++) + auth_list[i] = list("auth_name"="ERROR", "auth_hash"=null) + + var/has_auth = has_authorization() + + data = list( + "shuttle_status" = shuttle_status, + "shuttle_state" = shuttle_state, + "has_docking" = shuttle.docking_controller? 1 : 0, + "docking_status" = shuttle.docking_controller? shuttle.docking_controller.get_docking_status() : null, + "docking_override" = shuttle.docking_controller? shuttle.docking_controller.override_enabled : null, + "can_launch" = shuttle.can_launch(src), + "can_cancel" = shuttle.can_cancel(src), + "can_force" = shuttle.can_force(src), + "auth_list" = auth_list, + "has_auth" = has_auth, + "user" = debug? user : null, + ) + + ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) + + if (!ui) + ui = new(user, src, ui_key, "escape_shuttle_control_console.tmpl", "Shuttle Control", 470, 420) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/computer/shuttle_control/emergency/Topic(href, href_list) + if(..()) + return + + if(href_list["removeid"]) + var/dna_hash = href_list["removeid"] + authorized -= dna_hash + + if(!emagged && href_list["scanid"]) + //They selected an empty entry. Try to scan their id. + if (ishuman(usr)) + var/mob/living/carbon/human/H = usr + if (!read_authorization(H.get_active_hand())) //try to read what's in their hand first + read_authorization(H.wear_id) diff --git a/code/modules/shuttles/shuttle_ferry.dm b/code/modules/shuttles/shuttle_ferry.dm new file mode 100644 index 00000000..8d6d1594 --- /dev/null +++ b/code/modules/shuttles/shuttle_ferry.dm @@ -0,0 +1,165 @@ +#define IDLE_STATE 0 +#define WAIT_LAUNCH 1 +#define WAIT_ARRIVE 2 +#define WAIT_FINISH 3 + +#define DOCK_ATTEMPT_TIMEOUT 200 //how long in ticks we wait before assuming the docking controller is broken or blown up. + +/datum/shuttle/ferry + var/location = 0 //0 = at area_station, 1 = at area_offsite + var/direction = 0 //0 = going to station, 1 = going to offsite. + var/process_state = IDLE_STATE + + var/in_use = null //tells the controller whether this shuttle needs processing + + var/area_transition + var/move_time = 0 //the time spent in the transition area + var/transit_direction = null //needed for area/move_contents_to() to properly handle shuttle corners - not exactly sure how it works. + + var/area_station + var/area_offsite + //TODO: change location to a string and use a mapping for area and dock targets. + var/dock_target_station + var/dock_target_offsite + + var/last_dock_attempt_time = 0 + +/datum/shuttle/ferry/short_jump(var/area/origin,var/area/destination) + if(isnull(location)) + return + + if(!destination) + destination = get_location_area(!location) + if(!origin) + origin = get_location_area(location) + + direction = !location + ..(origin, destination) + +/datum/shuttle/ferry/long_jump(var/area/departing, var/area/destination, var/area/interim, var/travel_time, var/direction) + //world << "shuttle/ferry/long_jump: departing=[departing], destination=[destination], interim=[interim], travel_time=[travel_time]" + if(isnull(location)) + return + + if(!destination) + destination = get_location_area(!location) + if(!departing) + departing = get_location_area(location) + + direction = !location + ..(departing, destination, interim, travel_time, direction) + +/datum/shuttle/ferry/move(var/area/origin,var/area/destination) + ..(origin, destination) + + if (destination == area_station) location = 0 + if (destination == area_offsite) location = 1 + //if this is a long_jump retain the location we were last at until we get to the new one + +/datum/shuttle/ferry/dock() + ..() + last_dock_attempt_time = world.time + +/datum/shuttle/ferry/proc/get_location_area(location_id = null) + if (isnull(location_id)) + location_id = location + + if (!location_id) + return area_station + return area_offsite + +/datum/shuttle/ferry/proc/process() + switch(process_state) + if (WAIT_LAUNCH) + if (skip_docking_checks() || docking_controller.can_launch()) + + //world << "shuttle/ferry/process: area_transition=[area_transition], travel_time=[travel_time]" + if (move_time && area_transition) + long_jump(interim=area_transition, travel_time=move_time, direction=transit_direction) + else + short_jump() + + process_state = WAIT_ARRIVE + if (WAIT_ARRIVE) + if (moving_status == SHUTTLE_IDLE) + dock() + in_use = null //release lock + process_state = WAIT_FINISH + if (WAIT_FINISH) + if (skip_docking_checks() || docking_controller.docked() || world.time > last_dock_attempt_time + DOCK_ATTEMPT_TIMEOUT) + process_state = IDLE_STATE + arrived() + +/datum/shuttle/ferry/current_dock_target() + var/dock_target + if (!location) //station + dock_target = dock_target_station + else + dock_target = dock_target_offsite + return dock_target + + +/datum/shuttle/ferry/proc/launch(var/user) + if (!can_launch()) return + + in_use = user //obtain an exclusive lock on the shuttle + + process_state = WAIT_LAUNCH + undock() + +/datum/shuttle/ferry/proc/force_launch(var/user) + if (!can_force()) return + + in_use = user //obtain an exclusive lock on the shuttle + + if (move_time && area_transition) + long_jump(interim=area_transition, travel_time=move_time, direction=transit_direction) + else + short_jump() + + + process_state = WAIT_ARRIVE + +/datum/shuttle/ferry/proc/cancel_launch(var/user) + if (!can_cancel()) return + + moving_status = SHUTTLE_IDLE + process_state = WAIT_FINISH + in_use = null + + if (docking_controller && !docking_controller.undocked()) + docking_controller.force_undock() + + spawn(10) + dock() + + return + +/datum/shuttle/ferry/proc/can_launch() + if (moving_status != SHUTTLE_IDLE) + return 0 + + if (in_use) + return 0 + + return 1 + +/datum/shuttle/ferry/proc/can_force() + if (moving_status == SHUTTLE_IDLE && process_state == WAIT_LAUNCH) + return 1 + return 0 + +/datum/shuttle/ferry/proc/can_cancel() + if (moving_status == SHUTTLE_WARMUP || process_state == WAIT_LAUNCH) + return 1 + return 0 + +//returns 1 if the shuttle is getting ready to move, but is not in transit yet +/datum/shuttle/ferry/proc/is_launching() + return (moving_status == SHUTTLE_WARMUP || process_state == WAIT_LAUNCH) + +//This gets called when the shuttle finishes arriving at it's destination +//This can be used by subtypes to do things when the shuttle arrives. +/datum/shuttle/ferry/proc/arrived() + return //do nothing for now + diff --git a/code/modules/shuttles/shuttle_specops.dm b/code/modules/shuttles/shuttle_specops.dm new file mode 100644 index 00000000..9bad8d0c --- /dev/null +++ b/code/modules/shuttles/shuttle_specops.dm @@ -0,0 +1,202 @@ +/obj/machinery/computer/shuttle_control/specops + name = "special operations shuttle console" + shuttle_tag = "Special Operations" + req_access = list(access_cent_specops) + +//for shuttles that may use a different docking port at each location +/datum/shuttle/ferry/multidock + var/docking_controller_tag_station + var/docking_controller_tag_offsite + var/datum/computer/file/embedded_program/docking/docking_controller_station + var/datum/computer/file/embedded_program/docking/docking_controller_offsite + +/datum/shuttle/ferry/multidock/move(var/area/origin,var/area/destination) + ..(origin, destination) + if (!location) + docking_controller = docking_controller_station + else + docking_controller = docking_controller_offsite + +/datum/shuttle/ferry/multidock/specops + var/specops_return_delay = 6000 //After moving, the amount of time that must pass before the shuttle may move again + var/specops_countdown_time = 600 //Length of the countdown when moving the shuttle + + var/obj/item/device/radio/intercom/announcer = null + var/reset_time = 0 //the world.time at which the shuttle will be ready to move again. + var/launch_prep = 0 + var/cancel_countdown = 0 + +/datum/shuttle/ferry/multidock/specops/New() + ..() + announcer = new /obj/item/device/radio/intercom(null)//We need a fake AI to announce some stuff below. Otherwise it will be wonky. + announcer.config(list("Response Team" = 0)) + +/datum/shuttle/ferry/multidock/specops/proc/radio_announce(var/message) + if(announcer) + announcer.autosay(message, "A.L.I.C.E.", "Response Team") + +/datum/shuttle/ferry/multidock/specops/launch(var/user) + if (!can_launch()) + return + + if (istype(user, /obj/machinery/computer)) + var/obj/machinery/computer/C = user + + if(world.time <= reset_time) + C.visible_message("\blue Central Command will not allow the Special Operations shuttle to launch yet.") + if (((world.time - reset_time)/10) > 60) + C.visible_message("\blue [-((world.time - reset_time)/10)/60] minutes remain!") + else + C.visible_message("\blue [-(world.time - reset_time)/10] seconds remain!") + return + + C.visible_message("\blue The Special Operations shuttle will depart in [(specops_countdown_time/10)] seconds.") + + if (location) //returning + radio_announce("THE SPECIAL OPERATIONS SHUTTLE IS PREPARING TO RETURN") + else + radio_announce("THE SPECIAL OPERATIONS SHUTTLE IS PREPARING FOR LAUNCH") + + sleep_until_launch() + + //launch + radio_announce("ALERT: INITIATING LAUNCH SEQUENCE") + ..(user) + +/datum/shuttle/ferry/multidock/specops/move(var/area/origin,var/area/destination) + ..(origin, destination) + if (!location) //just arrived home + for(var/turf/T in get_area_turfs(destination)) + var/mob/M = locate(/mob) in T + M << "\red You have arrived at Central Command. Operation has ended!" + else //just left for the station + launch_mauraders() + for(var/turf/T in get_area_turfs(destination)) + var/mob/M = locate(/mob) in T + M << "\red You have arrived at [station_name]. Commence operation!" + + reset_time = world.time + specops_return_delay //set the timeout + +/datum/shuttle/ferry/multidock/specops/cancel_launch() + if (!can_cancel()) + return + + cancel_countdown = 1 + radio_announce("ALERT: LAUNCH SEQUENCE ABORTED") + if (istype(in_use, /obj/machinery/computer)) + var/obj/machinery/computer/C = in_use + C.visible_message("\red Launch sequence aborted.") + + ..() + + + +/datum/shuttle/ferry/multidock/specops/can_launch() + if(launch_prep) + return 0 + return ..() + +//should be fine to allow forcing. process_state only becomes WAIT_LAUNCH after the countdown is over. +///datum/shuttle/ferry/multidock/specops/can_force() +// return 0 + +/datum/shuttle/ferry/multidock/specops/can_cancel() + if(launch_prep) + return 1 + return ..() + +/datum/shuttle/ferry/multidock/specops/proc/sleep_until_launch() + var/message_tracker[] = list(0,1,2,3,5,10,30,45)//Create a a list with potential time values. + + var/launch_time = world.time + specops_countdown_time + var/time_until_launch + + cancel_countdown = 0 + launch_prep = 1 + while(!cancel_countdown && (launch_time - world.time) > 0) + var/ticksleft = launch_time - world.time + + //if(ticksleft > 1e5) + // launch_time = world.timeofday + 10 // midnight rollover + time_until_launch = (ticksleft / 10) + + //All this does is announce the time before launch. + var/rounded_time_left = round(time_until_launch)//Round time so that it will report only once, not in fractions. + if(rounded_time_left in message_tracker)//If that time is in the list for message announce. + radio_announce("ALERT: [rounded_time_left] SECOND[(rounded_time_left!=1)?"S":""] REMAIN") + message_tracker -= rounded_time_left//Remove the number from the list so it won't be called again next cycle. + //Should call all the numbers but lag could mean some issues. Oh well. Not much I can do about that. + + sleep(5) + + launch_prep = 0 + + +/proc/launch_mauraders() + var/area/centcom/specops/special_ops = locate()//Where is the specops area located? + //Begin Marauder launchpad. + spawn(0)//So it parallel processes it. + for(var/obj/machinery/door/poddoor/M in special_ops) + switch(M.id) + if("ASSAULT0") + spawn(10)//1 second delay between each. + M.open() + if("ASSAULT1") + spawn(20) + M.open() + if("ASSAULT2") + spawn(30) + M.open() + if("ASSAULT3") + spawn(40) + M.open() + + sleep(10) + + var/spawn_marauder[] = new() + for(var/obj/effect/landmark/L in world) + if(L.name == "Marauder Entry") + spawn_marauder.Add(L) + for(var/obj/effect/landmark/L in world) + if(L.name == "Marauder Exit") + var/obj/effect/portal/P = new(L.loc) + P.invisibility = 101//So it is not seen by anyone. + P.failchance = 0//So it has no fail chance when teleporting. + P.target = pick(spawn_marauder)//Where the marauder will arrive. + spawn_marauder.Remove(P.target) + + sleep(10) + + for(var/obj/machinery/mass_driver/M in special_ops) + switch(M.id) + if("ASSAULT0") + spawn(10) + M.drive() + if("ASSAULT1") + spawn(20) + M.drive() + if("ASSAULT2") + spawn(30) + M.drive() + if("ASSAULT3") + spawn(40) + M.drive() + + sleep(50)//Doors remain open for 5 seconds. + + for(var/obj/machinery/door/poddoor/M in special_ops) + switch(M.id)//Doors close at the same time. + if("ASSAULT0") + spawn(0) + M.close() + if("ASSAULT1") + spawn(0) + M.close() + if("ASSAULT2") + spawn(0) + M.close() + if("ASSAULT3") + spawn(0) + M.close() + special_ops.readyreset()//Reset firealarm after the team launched. + //End Marauder launchpad. \ No newline at end of file diff --git a/code/modules/shuttles/shuttle_supply.dm b/code/modules/shuttles/shuttle_supply.dm new file mode 100644 index 00000000..82f921ff --- /dev/null +++ b/code/modules/shuttles/shuttle_supply.dm @@ -0,0 +1,79 @@ + + + + + +/datum/shuttle/ferry/supply + var/away_location = 1 //the location to hide at while pretending to be in-transit + var/late_chance = 80 + var/max_late_time = 300 + +/datum/shuttle/ferry/supply/short_jump(var/area/origin,var/area/destination) + if(moving_status != SHUTTLE_IDLE) + return + + if(isnull(location)) + return + + if(!destination) + destination = get_location_area(!location) + if(!origin) + origin = get_location_area(location) + + //it would be cool to play a sound here + moving_status = SHUTTLE_WARMUP + spawn(warmup_time*10) + if (moving_status == SHUTTLE_IDLE) + return //someone cancelled the launch + + if (at_station() && forbidden_atoms_check()) + //cancel the launch because of forbidden atoms. announce over supply channel? + moving_status = SHUTTLE_IDLE + return + + if (!at_station()) //at centcom + supply_controller.buy() + + //We pretend it's a long_jump by making the shuttle stay at centcom for the "in-transit" period. + var/area/away_area = get_location_area(away_location) + moving_status = SHUTTLE_INTRANSIT + + //If we are at the away_area then we are just pretending to move, otherwise actually do the move + if (origin != away_area) + move(origin, away_area) + + //wait ETA here. + arrive_time = world.time + supply_controller.movetime + while (world.time <= arrive_time) + sleep(5) + + if (destination != away_area) + //late + if (prob(late_chance)) + sleep(rand(0,max_late_time)) + + move(away_area, destination) + + moving_status = SHUTTLE_IDLE + + if (!at_station()) //at centcom + supply_controller.sell() + +// returns 1 if the supply shuttle should be prevented from moving because it contains forbidden atoms +/datum/shuttle/ferry/supply/proc/forbidden_atoms_check() + if (!at_station()) + return 0 //if badmins want to send mobs or a nuke on the supply shuttle from centcom we don't care + + return supply_controller.forbidden_atoms_check(get_location_area()) + +/datum/shuttle/ferry/supply/proc/at_station() + return (!location) + +//returns 1 if the shuttle is idle and we can still mess with the cargo shopping list +/datum/shuttle/ferry/supply/proc/idle() + return (moving_status == SHUTTLE_IDLE) + +//returns the ETA in minutes +/datum/shuttle/ferry/supply/proc/eta_minutes() + var/ticksleft = arrive_time - world.time + return round(ticksleft/600,1) \ No newline at end of file diff --git a/code/modules/shuttles/shuttles_multi.dm b/code/modules/shuttles/shuttles_multi.dm new file mode 100644 index 00000000..83c50710 --- /dev/null +++ b/code/modules/shuttles/shuttles_multi.dm @@ -0,0 +1,143 @@ +//This is a holder for things like the Vox and Nuke shuttle. +/datum/shuttle/multi_shuttle + + var/cloaked = 1 + var/at_origin = 1 + var/returned_home = 0 + var/move_time = 240 + var/cooldown = 20 + var/last_move = 0 //the time at which we last moved + + var/announcer + var/arrival_message + var/departure_message + + var/area/interim + var/area/last_departed + var/list/destinations + var/area/origin + var/return_warning = 0 + +/datum/shuttle/multi_shuttle/New() + ..() + if(origin) last_departed = origin + +/datum/shuttle/multi_shuttle/move(var/area/origin, var/area/destination) + ..() + last_move = world.time + if (destination == src.origin) + returned_home = 1 + +/datum/shuttle/multi_shuttle/proc/announce_departure() + + if(cloaked || isnull(departure_message)) + return + + command_alert(departure_message,(announcer ? announcer : "Central Command")) + +/datum/shuttle/multi_shuttle/proc/announce_arrival() + + if(cloaked || isnull(arrival_message)) + return + + command_alert(arrival_message,(announcer ? announcer : "Central Command")) + + +/obj/machinery/computer/shuttle_control/multi + icon_state = "syndishuttle" + +/obj/machinery/computer/shuttle_control/multi/attack_hand(user as mob) + + if(..(user)) + return + src.add_fingerprint(user) + + var/datum/shuttle/multi_shuttle/MS = shuttle_controller.shuttles[shuttle_tag] + if(!istype(MS)) return + + var/dat + dat = "
    [shuttle_tag] Ship Control
    " + + + if(MS.moving_status != SHUTTLE_IDLE) + dat += "Location: Moving
    " + else + var/area/areacheck = get_area(src) + dat += "Location: [areacheck.name]
    " + + if((MS.last_move + MS.cooldown*10) > world.time) + dat += "Engines charging.
    " + else + dat += "Engines ready.
    " + + dat += "
    Toggle cloaking field
    " + dat += "Move ship
    " + dat += "Return to base
    " + + user << browse("[dat]", "window=[shuttle_tag]shuttlecontrol;size=300x600") + +/obj/machinery/computer/shuttle_control/multi/Topic(href, href_list) + if(..()) + return + + usr.set_machine(src) + src.add_fingerprint(usr) + + var/datum/shuttle/multi_shuttle/MS = shuttle_controller.shuttles[shuttle_tag] + if(!istype(MS)) return + + //world << "multi_shuttle: last_departed=[MS.last_departed], origin=[MS.origin], interim=[MS.interim], travel_time=[MS.move_time]" + + if (MS.moving_status != SHUTTLE_IDLE) + usr << "\blue [shuttle_tag] vessel is moving." + return + + if(href_list["start"]) + + if(MS.at_origin) + usr << "\red You are already at your home base." + return + + if(!MS.return_warning) + usr << "\red Returning to your home base will end your mission. If you are sure, press the button again." + //TODO: Actually end the mission. + MS.return_warning = 1 + return + + MS.long_jump(MS.last_departed,MS.origin,MS.interim,MS.move_time) + MS.last_departed = MS.origin + MS.at_origin = 1 + + if(href_list["toggle_cloak"]) + + MS.cloaked = !MS.cloaked + usr << "\red Ship stealth systems have been [(MS.cloaked ? "activated. The station will not" : "deactivated. The station will")] be warned of our arrival." + + if(href_list["move_multi"]) + if((MS.last_move + MS.cooldown*10) > world.time) + usr << "\red The ship's drive is inoperable while the engines are charging." + return + + var/choice = input("Select a destination.") as null|anything in MS.destinations + if(!choice) return + + usr << "\blue [shuttle_tag] main computer recieved message." + + if(MS.at_origin) + MS.announce_arrival() + MS.last_departed = MS.origin + MS.at_origin = 0 + + + MS.long_jump(MS.last_departed, MS.destinations[choice], MS.interim, MS.move_time) + MS.last_departed = MS.destinations[choice] + return + + else if(choice == MS.origin) + + MS.announce_departure() + + MS.short_jump(MS.last_departed, MS.destinations[choice]) + MS.last_departed = MS.destinations[choice] + + updateUsrDialog() \ No newline at end of file diff --git a/code/modules/supermatter/supermatter.dm b/code/modules/supermatter/supermatter.dm index ed13d9ad..69dd0019 100644 --- a/code/modules/supermatter/supermatter.dm +++ b/code/modules/supermatter/supermatter.dm @@ -1,13 +1,27 @@ -#define NITROGEN_RETARDATION_FACTOR 4 //Higher == N2 slows reaction more -#define THERMAL_RELEASE_MODIFIER 10 //Higher == less heat released during reaction -#define PLASMA_RELEASE_MODIFIER 1500 //Higher == less plasma released by reaction -#define OXYGEN_RELEASE_MODIFIER 750 //Higher == less oxygen released at high temperature/power +#define NITROGEN_RETARDATION_FACTOR 0.15 //Higher == N2 slows reaction more #define THERMAL_RELEASE_MODIFIER 750 //Higher == more heat released during reaction #define PLASMA_RELEASE_MODIFIER 1500 //Higher == less plasma released by reaction #define OXYGEN_RELEASE_MODIFIER 1500 //Higher == less oxygen released at high temperature/power #define REACTION_POWER_MODIFIER 1.1 //Higher == more overall power +/* + How to tweak the SM + + POWER_FACTOR directly controls how much power the SM puts out at a given level of excitation (power var). Making this lower means you have to work the SM harder to get the same amount of power. + CRITICAL_TEMPERATURE The temperature at which the SM starts taking damage. + + CHARGING_FACTOR Controls how much emitter shots excite the SM. + DAMAGE_RATE_LIMIT Controls the maximum rate at which the SM will take damage due to high temperatures. +*/ + +//Controls how much power is produced by each collector in range - this is the main parameter for tweaking SM balance, as it basically controls how the power variable relates to the rest of the game. +#define POWER_FACTOR 1.0 +#define DECAY_FACTOR 700 //Affects how fast the supermatter power decays +#define CRITICAL_TEMPERATURE 800 //K +#define CHARGING_FACTOR 0.05 +#define DAMAGE_RATE_LIMIT 3 //damage rate cap at power = 300, scales linearly with power + //These would be what you would get at point blank, decreases with distance #define DETONATION_RADS 200 @@ -38,6 +52,13 @@ var/emergency_alert = "CRYSTAL DELAMINATION IMMINENT." var/explosion_point = 1000 + l_color = "#8A8A00" + var/warning_color = "#B8B800" + var/emergency_color = "#D9D900" + + var/grav_pulling = 0 + var/pull_radius = 14 + var/emergency_issued = 0 var/explosion_power = 8 @@ -57,6 +78,8 @@ var/obj/item/device/radio/radio + var/debug = 0 + shard //Small subtype, less efficient and more sensitive, but less boom. name = "Supermatter Shard" desc = "A strangely translucent and iridescent crystal that looks like it used to be part of a larger structure. \red You get headaches just from looking at it." @@ -69,6 +92,7 @@ gasefficency = 0.125 + pull_radius = 5 explosion_power = 3 //3,6,9,12? Or is that too small? @@ -82,10 +106,20 @@ . = ..() /obj/machinery/power/supermatter/proc/explode() + anchored = 1 + grav_pulling = 1 + spawn(100) explosion(get_turf(src), explosion_power, explosion_power * 2, explosion_power * 3, explosion_power * 4, 1) del src return +//Changes color and luminosity of the light to these values if they were not already set +/obj/machinery/power/supermatter/proc/shift_light(var/lum, var/clr) + if(l_color != clr) + l_color = clr + if(luminosity != lum) + SetLuminosity(lum) + /obj/machinery/power/supermatter/process() var/turf/L = loc @@ -96,106 +130,114 @@ if(!istype(L)) //We are in a crate or somewhere that isn't turf, if we return to turf resume processing but for now. return //Yeah just stop. - if(istype(L, /turf/space)) // Stop processing this stuff if we've been ejected. - return if(damage > warning_point) // while the core is still damaged and it's still worth noting its status + + shift_light(5, warning_color) if((world.timeofday - lastwarning) / 10 >= WARNING_DELAY) var/stability = num2text(round((damage / explosion_point) * 100)) + var/alert_msg if(damage > emergency_point) - - radio.autosay(addtext(emergency_alert, " Instability: ",stability,"%"), "Supermatter Monitor") + shift_light(7, emergency_color) + alert_msg = addtext(emergency_alert, " Instability: ",stability,"%") lastwarning = world.timeofday - else if(damage >= damage_archived) // The damage is still going up - radio.autosay(addtext(warning_alert," Instability: ",stability,"%"), "Supermatter Monitor") + alert_msg = addtext(warning_alert," Instability: ",stability,"%") lastwarning = world.timeofday - 150 - - else // Phew, we're safe - radio.autosay(safe_alert, "Supermatter Monitor") + else // Phew, we're safe + alert_msg = safe_alert lastwarning = world.timeofday + if(!istype(L, /turf/space) && alert_msg) + radio.autosay(alert_msg, "Supermatter Monitor") + if(damage > explosion_point) for(var/mob/living/mob in living_mob_list) - if(istype(mob, /mob/living/carbon/human)) - //Hilariously enough, running into a closet should make you get hit the hardest. - mob:hallucination += max(50, min(300, DETONATION_HALLUCINATION * sqrt(1 / (get_dist(mob, src) + 1)) ) ) - var/rads = DETONATION_RADS * sqrt( 1 / (get_dist(mob, src) + 1) ) - mob.apply_effect(rads, IRRADIATE) + if(loc.z == mob.loc.z) + if(istype(mob, /mob/living/carbon/human)) + //Hilariously enough, running into a closet should make you get hit the hardest. + var/mob/living/carbon/human/H = mob + H.hallucination += max(50, min(300, DETONATION_HALLUCINATION * sqrt(1 / (get_dist(mob, src) + 1)) ) ) + var/rads = DETONATION_RADS * sqrt( 1 / (get_dist(mob, src) + 1) ) + mob.apply_effect(rads, IRRADIATE) explode() + else + shift_light(4,initial(l_color)) + if(grav_pulling) + supermatter_pull() //Ok, get the air from the turf - var/datum/gas_mixture/env = L.return_air() + var/datum/gas_mixture/removed = null + var/datum/gas_mixture/env = null - //Remove gas from surrounding area - var/datum/gas_mixture/removed = env.remove(gasefficency * env.total_moles) + //ensure that damage doesn't increase too quickly due to super high temperatures resulting from no coolant, for example. We dont want the SM exploding before anyone can react. + //We want the cap to scale linearly with power (and explosion_point). Let's aim for a cap of 5 at power = 300 (based on testing, equals roughly 5% per SM alert announcement). + var/damage_inc_limit = (power/300)*(explosion_point/1000)*DAMAGE_RATE_LIMIT - if(!removed || !removed.total_moles) - damage += max((power-1600)/10, 0) - power = min(power, 1600) - return 1 + if(!istype(L, /turf/space)) + env = L.return_air() + removed = env.remove(gasefficency * env.total_moles) //Remove gas from surrounding area - if (!removed) - return 1 - - damage_archived = damage - damage = max( damage + ( (removed.temperature - 800) / 150 ) , 0 ) - //Ok, 100% oxygen atmosphere = best reaction - //Maxes out at 100% oxygen pressure - oxygen = max(min((removed.oxygen - (removed.nitrogen * NITROGEN_RETARDATION_FACTOR)) / MOLES_CELLSTANDARD, 1), 0) - - var/temp_factor = 100 - - if(oxygen > 0.8) - // with a perfect gas mix, make the power less based on heat - icon_state = "[base_icon_state]_glow" + if(!env || !removed || !removed.total_moles) + damage += max((power - 15*POWER_FACTOR)/10, 0) + else if (grav_pulling) //If supermatter is detonating, remove all air from the zone + env.remove(env.total_moles) else - // in normal mode, base the produced energy around the heat - temp_factor = 60 - icon_state = base_icon_state + damage_archived = damage - power = max( (removed.temperature * temp_factor / T0C) * oxygen + power, 0) //Total laser power plus an overload + damage = max( damage + min( ( (removed.temperature - CRITICAL_TEMPERATURE) / 150 ), damage_inc_limit ) , 0 ) + //Ok, 100% oxygen atmosphere = best reaction + //Maxes out at 100% oxygen pressure + oxygen = max(min((removed.gas["oxygen"] - (removed.gas["nitrogen"] * NITROGEN_RETARDATION_FACTOR)) / removed.total_moles, 1), 0) - //We've generated power, now let's transfer it to the collectors for storing/usage - transfer_energy() + //calculate power gain for oxygen reaction + var/temp_factor + var/equilibrium_power + if (oxygen > 0.8) + //If chain reacting at oxygen == 1, we want the power at 800 K to stabilize at a power level of 400 + equilibrium_power = 400 + icon_state = "[base_icon_state]_glow" + else + //If chain reacting at oxygen == 1, we want the power at 800 K to stabilize at a power level of 250 + equilibrium_power = 250 + icon_state = base_icon_state - var/device_energy = power * REACTION_POWER_MODIFIER + temp_factor = ( (equilibrium_power/DECAY_FACTOR)**3 )/800 + power = max( (removed.temperature * temp_factor) * oxygen + power, 0) - //To figure out how much temperature to add each tick, consider that at one atmosphere's worth - //of pure oxygen, with all four lasers firing at standard energy and no N2 present, at room temperature - //that the device energy is around 2140. At that stage, we don't want too much heat to be put out - //Since the core is effectively "cold" + //We've generated power, now let's transfer it to the collectors for storing/usage + transfer_energy() - //Also keep in mind we are only adding this temperature to (efficiency)% of the one tile the rock - //is on. An increase of 4*C @ 25% efficiency here results in an increase of 1*C / (#tilesincore) overall. - - var/thermal_power = THERMAL_RELEASE_MODIFIER - if(removed.total_moles < 35) thermal_power += 750 //If you don't add coolant, you are going to have a bad time. + var/device_energy = power * REACTION_POWER_MODIFIER - removed.temperature += ((device_energy * thermal_power) / max(1, removed.heat_capacity())) + //Release reaction gasses + var/heat_capacity = removed.heat_capacity() + removed.adjust_multi("toxins", max(device_energy / PLASMA_RELEASE_MODIFIER, 0), \ + "oxygen", max((device_energy + removed.temperature - T0C) / OXYGEN_RELEASE_MODIFIER, 0)) - removed.temperature = max(0, min(removed.temperature, 10000)) + var/thermal_power = THERMAL_RELEASE_MODIFIER * device_energy + if (debug) + var/heat_capacity_new = removed.heat_capacity() + visible_message("[src]: Releasing [round(thermal_power)] W.") + visible_message("[src]: Releasing additional [round((heat_capacity_new - heat_capacity)*removed.temperature)] W with exhaust gasses.") - //Calculate how much gas to release - removed.toxins += max(device_energy / PLASMA_RELEASE_MODIFIER, 0) + removed.add_thermal_energy(thermal_power) + removed.temperature = between(0, removed.temperature, 10000) - removed.oxygen += max((device_energy + removed.temperature - T0C) / OXYGEN_RELEASE_MODIFIER, 0) + env.merge(removed) - removed.update_values() - - env.merge(removed) - - for(var/mob/living/carbon/human/l in view(src, min(7, round(power ** 0.25)))) // If they can see it without mesons on. Bad on them. + for(var/mob/living/carbon/human/l in view(src, min(7, round(sqrt(power/6))))) // If they can see it without mesons on. Bad on them. if(!istype(l.glasses, /obj/item/clothing/glasses/meson)) l.hallucination = max(0, min(200, l.hallucination + power * config_hallucination_power * sqrt( 1 / max(1,get_dist(l, src)) ) ) ) - for(var/mob/living/l in range(src, 8)) + //adjusted range so that a power of 300 (pretty high) results in 8 tiles, roughly the distance from the core to the engine monitoring room. + for(var/mob/living/l in range(src, round(sqrt(power / 5)))) var/rads = (power / 10) * sqrt( 1 / get_dist(l, src) ) l.apply_effect(rads, IRRADIATE) - power -= (power/500)**3 + power -= (power/DECAY_FACTOR)**3 //energy losses due to radiation return 1 @@ -207,8 +249,8 @@ // Then bring it inside to explode instantly upon landing on a valid turf. - if(Proj.flag != "bullet") - power += Proj.damage * config_bullet_energy + if(istype(Proj, /obj/item/projectile/beam)) + power += Proj.damage * config_bullet_energy * CHARGING_FACTOR / POWER_FACTOR else damage += Proj.damage * config_bullet_energy return 0 @@ -237,8 +279,10 @@ /obj/machinery/power/supermatter/proc/transfer_energy() for(var/obj/machinery/power/rad_collector/R in rad_collectors) - if(get_dist(R, src) <= 15) // Better than using orange() every process - R.receive_pulse(power) + var/distance = get_dist(R, src) + if(distance <= 15) + //for collectors using standard phoron tanks at 1013 kPa, the actual power generated will be this power*POWER_FACTOR*20*29 = power*POWER_FACTOR*580 + R.receive_pulse(power * POWER_FACTOR * (min(3/distance, 1))**2) return /obj/machinery/power/supermatter/attackby(obj/item/weapon/W as obj, mob/living/user as mob) @@ -257,7 +301,7 @@ AM.visible_message("\The [AM] slams into \the [src] inducing a resonance... \his body starts to glow and catch flame before flashing into ash.",\ "You slam into \the [src] as your ears are filled with unearthly ringing. Your last thought is \"Oh, fuck.\"",\ "You hear an uneartly ringing, then what sounds like a shrilling kettle as you are washed with a wave of heat.") - else + else if(!grav_pulling) //To prevent spam, detonating supermatter does not indicate non-mobs being destroyed AM.visible_message("\The [AM] smacks into \the [src] and rapidly flashes to ash.",\ "You hear a loud crack as you are washed with a wave of heat.") @@ -283,3 +327,45 @@ var/rads = 500 * sqrt( 1 / (get_dist(l, src) + 1) ) l.apply_effect(rads, IRRADIATE) + +/obj/machinery/power/supermatter/proc/supermatter_pull() + + //following is adapted from singulo code + if(defer_powernet_rebuild != 2) + defer_powernet_rebuild = 1 + // Let's just make this one loop. + for(var/atom/X in orange(pull_radius,src)) + // Movable atoms only + if(istype(X, /atom/movable)) + if(is_type_in_list(X, uneatable)) continue + if(((X) && (!istype(X,/mob/living/carbon/human)))) + step_towards(X,src) + if(istype(X, /obj)) //unanchored objects pulled twice as fast + var/obj/O = X + if(!O.anchored) + step_towards(X,src) + else + step_towards(X,src) + if(istype(X, /obj/structure/window)) //shatter windows + var/obj/structure/window/W = X + W.ex_act(2.0) + else if(istype(X,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = X + if(istype(H.shoes,/obj/item/clothing/shoes/magboots)) + var/obj/item/clothing/shoes/magboots/M = H.shoes + if(M.magpulse) + step_towards(H,src) //step just once with magboots + continue + step_towards(H,src) //step twice + step_towards(H,src) + + if(defer_powernet_rebuild != 2) + defer_powernet_rebuild = 0 + return + + +/obj/machinery/power/supermatter/GotoAirflowDest(n) //Supermatter not pushed around by airflow + return + +/obj/machinery/power/supermatter/RepelAirflowDest(n) + return \ No newline at end of file diff --git a/code/modules/surgery/braincore.dm b/code/modules/surgery/braincore.dm index b89c4827..0d4940f3 100644 --- a/code/modules/surgery/braincore.dm +++ b/code/modules/surgery/braincore.dm @@ -107,6 +107,7 @@ B.transfer_identity(target) target.internal_organs -= B + target.internal_organs_by_name -= "brain" target:brain_op_stage = 4.0 target.death()//You want them to die after the brain was transferred, so not to trigger client death() twice. @@ -173,7 +174,7 @@ end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) user.visible_message("\blue [user] mends hematoma in [target]'s brain with \the [tool].", \ "\blue You mend hematoma in [target]'s brain with \the [tool].") - var/datum/organ/internal/brain/sponge = target.internal_organs["brain"] + var/datum/organ/internal/brain/sponge = target.internal_organs_by_name["brain"] if (sponge) sponge.damage = 0 @@ -187,9 +188,12 @@ // SLIME CORE EXTRACTION // ////////////////////////////////////////////////////////////////// -/datum/surgery_step/slime/ +/datum/surgery_step/slime + is_valid_target(mob/living/carbon/slime/target) + return istype(target, /mob/living/carbon/slime/) + can_use(mob/living/user, mob/living/carbon/slime/target, target_zone, obj/item/tool) - return istype(target, /mob/living/carbon/slime/) && target.stat == 2 + return target.stat == 2 /datum/surgery_step/slime/cut_flesh allowed_tools = list( @@ -210,7 +214,7 @@ end_step(mob/living/user, mob/living/carbon/slime/target, target_zone, obj/item/tool) user.visible_message("\blue [user] cuts through [target]'s flesh with \the [tool].", \ - "\blue You cut through [target]'s flesh with \the [tool], exposing the cores.") + "\blue You cut through [target]'s flesh with \the [tool], revealing its silky innards.") target.brain_op_stage = 1 fail_step(mob/living/user, mob/living/carbon/slime/target, target_zone, obj/item/tool) @@ -267,8 +271,7 @@ if(target.cores >= 0) new target.coretype(target.loc) if(target.cores <= 0) - var/origstate = initial(target.icon_state) - target.icon_state = "[origstate] dead-nocore" + target.icon_state = "[target.colour] baby slime dead-nocore" fail_step(mob/living/user, mob/living/carbon/slime/target, target_zone, obj/item/tool) diff --git a/code/modules/surgery/eye.dm b/code/modules/surgery/eye.dm index 9977271c..69e23867 100644 --- a/code/modules/surgery/eye.dm +++ b/code/modules/surgery/eye.dm @@ -39,7 +39,7 @@ target.blinded += 1.5 fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - var/datum/organ/internal/eyes/eyes = target.internal_organs["eyes"] + var/datum/organ/internal/eyes/eyes = target.internal_organs_by_name["eyes"] var/datum/organ/external/affected = target.get_organ(target_zone) user.visible_message("\red [user]'s hand slips, slicing [target]'s eyes wth \the [tool]!" , \ "\red Your hand slips, slicing [target]'s eyes wth \the [tool]!" ) @@ -69,7 +69,7 @@ target.op_stage.eyes = 2 fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - var/datum/organ/internal/eyes/eyes = target.internal_organs["eyes"] + var/datum/organ/internal/eyes/eyes = target.internal_organs_by_name["eyes"] var/datum/organ/external/affected = target.get_organ(target_zone) user.visible_message("\red [user]'s hand slips, damaging [target]'s eyes with \the [tool]!", \ "\red Your hand slips, damaging [target]'s eyes with \the [tool]!") @@ -100,7 +100,7 @@ target.op_stage.eyes = 3 fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - var/datum/organ/internal/eyes/eyes = target.internal_organs["eyes"] + var/datum/organ/internal/eyes/eyes = target.internal_organs_by_name["eyes"] var/datum/organ/external/affected = target.get_organ(target_zone) user.visible_message("\red [user]'s hand slips, stabbing \the [tool] into [target]'s eye!", \ "\red Your hand slips, stabbing \the [tool] into [target]'s eye!") @@ -126,7 +126,7 @@ "You are beginning to cauterize the incision around [target]'s eyes with \the [tool].") end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - var/datum/organ/internal/eyes/eyes = target.internal_organs["eyes"] + var/datum/organ/internal/eyes/eyes = target.internal_organs_by_name["eyes"] user.visible_message("\blue [user] cauterizes the incision around [target]'s eyes with \the [tool].", \ "\blue You cauterize the incision around [target]'s eyes with \the [tool].") if (target.op_stage.eyes == 3) @@ -136,7 +136,7 @@ target.op_stage.eyes = 0 fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - var/datum/organ/internal/eyes/eyes = target.internal_organs["eyes"] + var/datum/organ/internal/eyes/eyes = target.internal_organs_by_name["eyes"] var/datum/organ/external/affected = target.get_organ(target_zone) user.visible_message("\red [user]'s hand slips, searing [target]'s eyes with \the [tool]!", \ "\red Your hand slips, searing [target]'s eyes with \the [tool]!") diff --git a/code/modules/surgery/headreattach.dm b/code/modules/surgery/headreattach.dm index 17db2c30..72da44cc 100644 --- a/code/modules/surgery/headreattach.dm +++ b/code/modules/surgery/headreattach.dm @@ -64,7 +64,7 @@ can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) if(..()) var/datum/organ/external/affected = target.get_organ(target_zone) - return affected.status & ORGAN_CUT_AWAY && affected.open < 3 && !(affected.status & ORGAN_ATTACHABLE) + return affected.status & ORGAN_CUT_AWAY && target.op_stage.head_reattach == 0 && !(affected.status & ORGAN_ATTACHABLE) begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/datum/organ/external/affected = target.get_organ(target_zone) @@ -73,10 +73,9 @@ ..() end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - var/datum/organ/external/affected = target.get_organ(target_zone) user.visible_message("\blue [user] has finished repositioning flesh and tissue to something anatomically recognizable where [target]'s head used to be with \the [tool].", \ "\blue You have finished repositioning flesh and tissue to something anatomically recognizable where [target]'s head used to be with \the [tool].") - affected.open = 3 + target.op_stage.head_reattach = 1 fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/datum/organ/external/affected = target.get_organ(target_zone) @@ -97,8 +96,7 @@ can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) if(..()) - var/datum/organ/external/affected = target.get_organ(target_zone) - return affected.open == 3 + return target.op_stage.head_reattach == 1 begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) user.visible_message("[user] is stapling and suturing flesh into place in [target]'s esophagal and vocal region with \the [tool].", \ @@ -106,10 +104,9 @@ ..() end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - var/datum/organ/external/affected = target.get_organ(target_zone) user.visible_message("\blue [user] has finished stapling [target]'s neck into place with \the [tool].", \ "\blue You have finished stapling [target]'s neck into place with \the [tool].") - affected.open = 4 + target.op_stage.head_reattach = 2 fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/datum/organ/external/affected = target.get_organ(target_zone) @@ -132,8 +129,7 @@ can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) if(..()) - var/datum/organ/external/affected = target.get_organ(target_zone) - return affected.open == 4 + return target.op_stage.head_reattach == 2 begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) user.visible_message("[user] starts adjusting area around [target]'s neck with \the [tool].", \ @@ -144,6 +140,7 @@ var/datum/organ/external/affected = target.get_organ(target_zone) user.visible_message("\blue [user] has finished adjusting the area around [target]'s neck with \the [tool].", \ "\blue You have finished adjusting the area around [target]'s neck with \the [tool].") + target.op_stage.head_reattach = 0 affected.status |= ORGAN_ATTACHABLE affected.amputated = 1 affected.setAmputatedTree() diff --git a/code/modules/surgery/implant.dm b/code/modules/surgery/implant.dm index c3f4f329..b2352dcf 100644 --- a/code/modules/surgery/implant.dm +++ b/code/modules/surgery/implant.dm @@ -128,7 +128,7 @@ "\blue You put \the [tool] inside [target]'s [get_cavity(affected)] cavity." ) if (tool.w_class > get_max_wclass(affected)/2 && prob(50)) user << "\red You tear some blood vessels trying to fit such a big object in this cavity." - var/datum/wound/internal_bleeding/I = new (15) + var/datum/wound/internal_bleeding/I = new (10) affected.wounds += I affected.owner.custom_pain("You feel something rip in your [affected.display_name]!", 1) user.drop_item() diff --git a/code/modules/surgery/other.dm b/code/modules/surgery/other.dm index bd1002b0..7d113950 100644 --- a/code/modules/surgery/other.dm +++ b/code/modules/surgery/other.dm @@ -51,3 +51,121 @@ user.visible_message("\red [user]'s hand slips, smearing [tool] in the incision in [target]'s [affected.display_name]!" , \ "\red Your hand slips, smearing [tool] in the incision in [target]'s [affected.display_name]!") affected.take_damage(5, 0) + +/datum/surgery_step/fix_dead_tissue //Debridement + priority = 2 + allowed_tools = list( + /obj/item/weapon/scalpel = 100, \ + /obj/item/weapon/kitchenknife = 75, \ + /obj/item/weapon/shard = 50, \ + ) + + can_infect = 1 + blood_level = 1 + + min_duration = 110 + max_duration = 160 + + can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + if(!hasorgans(target)) + return 0 + + if (target_zone == "mouth" || target_zone == "eyes") + return 0 + + var/datum/organ/external/affected = target.get_organ(target_zone) + + return affected.open == 2 && (affected.status & ORGAN_DEAD) + + begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + var/datum/organ/external/affected = target.get_organ(target_zone) + user.visible_message("[user] starts cutting away necrotic tissue in [target]'s [affected.display_name] with \the [tool]." , \ + "You start cutting away necrotic tissue in [target]'s [affected.display_name] with \the [tool].") + target.custom_pain("The pain in [affected.display_name] is unbearable!",1) + ..() + + end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + var/datum/organ/external/affected = target.get_organ(target_zone) + user.visible_message("\blue [user] has cut away necrotic tissue in [target]'s [affected.display_name] with \the [tool].", \ + "\blue You have cut away necrotic tissue in [target]'s [affected.display_name] with \the [tool].") + affected.open = 3 + + fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + var/datum/organ/external/affected = target.get_organ(target_zone) + user.visible_message("\red [user]'s hand slips, slicing an artery inside [target]'s [affected.display_name] with \the [tool]!", \ + "\red Your hand slips, slicing an artery inside [target]'s [affected.display_name] with \the [tool]!") + affected.createwound(CUT, 20, 1) + +/datum/surgery_step/treat_necrosis + priority = 2 + allowed_tools = list( + /obj/item/weapon/reagent_containers/dropper = 100, + /obj/item/weapon/reagent_containers/glass/bottle = 75, + /obj/item/weapon/reagent_containers/glass/beaker = 75, + /obj/item/weapon/reagent_containers/spray = 50, + /obj/item/weapon/reagent_containers/glass/bucket = 50, + ) + + can_infect = 0 + blood_level = 0 + + min_duration = 50 + max_duration = 60 + + can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + if (!istype(tool, /obj/item/weapon/reagent_containers)) + return 0 + + var/obj/item/weapon/reagent_containers/container = tool + if(!container.reagents.has_reagent("peridaxon")) + return 0 + + if(!hasorgans(target)) + return 0 + + if (target_zone == "mouth" || target_zone == "eyes") + return 0 + + var/datum/organ/external/affected = target.get_organ(target_zone) + return affected.open == 3 && (affected.status & ORGAN_DEAD) + + begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + var/datum/organ/external/affected = target.get_organ(target_zone) + user.visible_message("[user] starts applying medication to the affected tissue in [target]'s [affected.display_name] with \the [tool]." , \ + "You start applying medication to the affected tissue in [target]'s [affected.display_name] with \the [tool].") + target.custom_pain("Something in your [affected.display_name] is causing you a lot of pain!",1) + ..() + + end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + var/datum/organ/external/affected = target.get_organ(target_zone) + + if (!istype(tool, /obj/item/weapon/reagent_containers)) + return + + var/obj/item/weapon/reagent_containers/container = tool + + var/trans = container.reagents.trans_to(target, container.amount_per_transfer_from_this) + if (trans > 0) + container.reagents.reaction(target, INGEST) //technically it's contact, but the reagents are being applied to internal tissue + + if(container.reagents.has_reagent("peridaxon")) + affected.status &= ~ORGAN_DEAD + + user.visible_message("\blue [user] applies [trans] units of the solution to affected tissue in [target]'s [affected.display_name]", \ + "\blue You apply [trans] units of the solution to affected tissue in [target]'s [affected.display_name] with \the [tool].") + + fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + var/datum/organ/external/affected = target.get_organ(target_zone) + + if (!istype(tool, /obj/item/weapon/reagent_containers)) + return + + var/obj/item/weapon/reagent_containers/container = tool + + var/trans = container.reagents.trans_to(target, container.amount_per_transfer_from_this) + container.reagents.reaction(target, INGEST) //technically it's contact, but the reagents are being applied to internal tissue + + user.visible_message("\red [user]'s hand slips, applying [trans] units of the solution to the wrong place in [target]'s [affected.display_name] with the [tool]!" , \ + "\red Your hand slips, applying [trans] units of the solution to the wrong place in [target]'s [affected.display_name] with the [tool]!") + + //no damage or anything, just wastes medicine diff --git a/code/modules/surgery/ribcage.dm b/code/modules/surgery/ribcage.dm index 7bbad9b5..c14d065c 100644 --- a/code/modules/surgery/ribcage.dm +++ b/code/modules/surgery/ribcage.dm @@ -202,9 +202,10 @@ var/is_chest_organ_damaged = 0 var/datum/organ/external/chest/chest = target.get_organ("chest") - for(var/datum/organ/internal/I in chest.internal_organs) if(I.damage > 0) - is_chest_organ_damaged = 1 - break + for(var/datum/organ/internal/I in chest.internal_organs) + if(I.damage > 0) + is_chest_organ_damaged = 1 + break return ..() && is_chest_organ_damaged && target.op_stage.ribcage == 2 begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) @@ -223,7 +224,7 @@ user.visible_message("[user] starts treating damage to [target]'s [I.name] with [tool_name].", \ "You start treating damage to [target]'s [I.name] with [tool_name]." ) else - user.visible_message("\blue [user] attempts to repair [target]'s mechanical [I.name] with [tool_name]...", \ + user.visible_message("[user] attempts to repair [target]'s mechanical [I.name] with [tool_name]...", \ "\blue You attempt to repair [target]'s mechanical [I.name] with [tool_name]...") target.custom_pain("The pain in your chest is living hell!",1) @@ -242,8 +243,8 @@ for(var/datum/organ/internal/I in chest.internal_organs) if(I && I.damage > 0) if(I.robotic < 2) - user.visible_message("[user] treats damage to [target]'s [I.name] with [tool_name].", \ - "You treat damage to [target]'s [I.name] with [tool_name]." ) + user.visible_message("\blue [user] treats damage to [target]'s [I.name] with [tool_name].", \ + "\blue You treat damage to [target]'s [I.name] with [tool_name]." ) else user.visible_message("\blue [user] pokes [target]'s mechanical [I.name] with [tool_name]...", \ "\blue You poke [target]'s mechanical [I.name] with [tool_name]... \red For no effect, since it's robotic.") @@ -285,7 +286,7 @@ return 0 var/is_chest_organ_damaged = 0 - var/datum/organ/internal/heart/heart = target.internal_organs["heart"] + var/datum/organ/internal/heart/heart = target.internal_organs_by_name["heart"] var/datum/organ/external/chest/chest = target.get_organ("chest") for(var/datum/organ/internal/I in chest.internal_organs) if(I.damage > 0) is_chest_organ_damaged = 1 @@ -293,7 +294,7 @@ return ..() && is_chest_organ_damaged && heart.robotic == 2 && target.op_stage.ribcage == 2 begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - var/datum/organ/internal/heart/heart = target.internal_organs["heart"] + var/datum/organ/internal/heart/heart = target.internal_organs_by_name["heart"] if(heart.damage > 0) user.visible_message("[user] starts mending the mechanisms on [target]'s heart with \the [tool].", \ @@ -302,14 +303,14 @@ ..() end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - var/datum/organ/internal/heart/heart = target.internal_organs["heart"] + var/datum/organ/internal/heart/heart = target.internal_organs_by_name["heart"] if(heart.damage > 0) user.visible_message("\blue [user] repairs [target]'s heart with \the [tool].", \ "\blue You repair [target]'s heart with \the [tool]." ) heart.damage = 0 fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - var/datum/organ/internal/heart/heart = target.internal_organs["heart"] + var/datum/organ/internal/heart/heart = target.internal_organs_by_name["heart"] user.visible_message("\red [user]'s hand slips, smearing [tool] in the incision in [target]'s heart, gumming it up!!" , \ "\red Your hand slips, smearing [tool] in the incision in [target]'s heart, gumming it up!") heart.take_damage(5, 0) diff --git a/code/modules/surgery/surgery.dm b/code/modules/surgery/surgery.dm index 05371f18..b0116585 100644 --- a/code/modules/surgery/surgery.dm +++ b/code/modules/surgery/surgery.dm @@ -25,8 +25,10 @@ return allowed_tools[T] return 0 - // Checks if this step applies to the mutantrace of the user. - proc/is_valid_mutantrace(mob/living/carbon/human/target) + // Checks if this step applies to the user mob at all + proc/is_valid_target(mob/living/carbon/human/target) + if(!hasorgans(target)) + return 0 if(allowed_species) for(var/species in allowed_species) @@ -40,6 +42,7 @@ return 1 + // checks whether this step can be applied with the given user and target proc/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) return 0 @@ -81,12 +84,12 @@ proc/do_surgery(mob/living/M, mob/living/user, obj/item/tool) return 0 for(var/datum/surgery_step/S in surgery_steps) //check if tool is right or close enough and if this step is possible - if( S.tool_quality(tool) && S.can_use(user, M, user.zone_sel.selecting, tool) && S.is_valid_mutantrace(M)) + if( S.tool_quality(tool) && S.can_use(user, M, user.zone_sel.selecting, tool) && S.is_valid_target(M)) S.begin_step(user, M, user.zone_sel.selecting, tool) //start on it //We had proper tools! (or RNG smiled.) and User did not move or change hands. if( prob(S.tool_quality(tool)) && do_mob(user, M, rand(S.min_duration, S.max_duration))) S.end_step(user, M, user.zone_sel.selecting, tool) //finish successfully - else //or + else if (tool in user.contents && user.Adjacent(M)) //or S.fail_step(user, M, user.zone_sel.selecting, tool) //malpractice~ return 1 //don't want to do weapony things after surgery return 0 @@ -111,4 +114,5 @@ proc/sort_surgeries() var/eyes = 0 var/face = 0 var/appendix = 0 - var/ribcage = 0 \ No newline at end of file + var/ribcage = 0 + var/head_reattach = 0 diff --git a/code/setup.dm b/code/setup.dm index ee89e314..e2bb9db0 100644 --- a/code/setup.dm +++ b/code/setup.dm @@ -6,6 +6,13 @@ #define R_IDEAL_GAS_EQUATION 8.31 //kPa*L/(K*mol) #define ONE_ATMOSPHERE 101.325 //kPa +#define IDEAL_GAS_ENTROPY_CONSTANT 1164 //(mol^3 * s^3) / (kg^3 * L). Equal to (4*pi/(avrogadro's number * planck's constant)^2)^(3/2) / (avrogadro's number * 1000 Liters per m^3). + +//radiation constants +#define STEFAN_BOLTZMANN_CONSTANT 0.0000000567 //W/(m^2*K^4) +#define COSMIC_RADIATION_TEMPERATURE 3.15 //K +#define AVERAGE_SOLAR_RADIATION 200 //W/m^2. Kind of arbitrary. Really this should depend on the sun position much like solars. +#define RADIATOR_OPTIMUM_PRESSURE 110 //kPa at 20 C #define CELL_VOLUME 2500 //liters in a cell #define MOLES_CELLSTANDARD (ONE_ATMOSPHERE*CELL_VOLUME/(T20C*R_IDEAL_GAS_EQUATION)) //moles in a 2.5 m^3 cell at 101.325 Pa and 20 degC @@ -27,6 +34,8 @@ #define HUMAN_NEEDED_OXYGEN MOLES_CELLSTANDARD*BREATH_PERCENTAGE*0.16 //Amount of air needed before pass out/suffocation commences +#define SOUND_MINIMUM_PRESSURE 10 + // Pressure limits. #define HAZARD_HIGH_PRESSURE 550 //This determins at what pressure the ultra-high pressure red icon is displayed. (This one is set as a constant) #define WARNING_HIGH_PRESSURE 325 //This determins when the orange pressure icon is displayed (it is 0.7 * HAZARD_HIGH_PRESSURE) @@ -35,10 +44,10 @@ #define TEMPERATURE_DAMAGE_COEFFICIENT 1.5 //This is used in handle_temperature_damage() for humans, and in reagents that affect body temperature. Temperature damage is multiplied by this amount. #define BODYTEMP_AUTORECOVERY_DIVISOR 12 //This is the divisor which handles how much of the temperature difference between the current body temperature and 310.15K (optimal temperature) humans auto-regenerate each tick. The higher the number, the slower the recovery. This is applied each tick, so long as the mob is alive. -#define BODYTEMP_AUTORECOVERY_MINIMUM 10 //Minimum amount of kelvin moved toward 310.15K per tick. So long as abs(310.15 - bodytemp) is more than 50. +#define BODYTEMP_AUTORECOVERY_MINIMUM 1 //Minimum amount of kelvin moved toward 310.15K per tick. So long as abs(310.15 - bodytemp) is more than 50. #define BODYTEMP_COLD_DIVISOR 6 //Similar to the BODYTEMP_AUTORECOVERY_DIVISOR, but this is the divisor which is applied at the stage that follows autorecovery. This is the divisor which comes into play when the human's loc temperature is lower than their body temperature. Make it lower to lose bodytemp faster. #define BODYTEMP_HEAT_DIVISOR 6 //Similar to the BODYTEMP_AUTORECOVERY_DIVISOR, but this is the divisor which is applied at the stage that follows autorecovery. This is the divisor which comes into play when the human's loc temperature is higher than their body temperature. Make it lower to gain bodytemp faster. -#define BODYTEMP_COOLING_MAX 30 //The maximum number of degrees that your body can cool in 1 tick, when in a cold area. +#define BODYTEMP_COOLING_MAX -30 //The maximum number of degrees that your body can cool in 1 tick, when in a cold area. #define BODYTEMP_HEATING_MAX 30 //The maximum number of degrees that your body can heat up in 1 tick, when in a hot area. #define BODYTEMP_HEAT_DAMAGE_LIMIT 360.15 // The limit the human body can take before it starts taking damage from heat. @@ -62,10 +71,7 @@ #define PRESSURE_DAMAGE_COEFFICIENT 4 //The amount of pressure damage someone takes is equal to (pressure / HAZARD_HIGH_PRESSURE)*PRESSURE_DAMAGE_COEFFICIENT, with the maximum of MAX_PRESSURE_DAMAGE #define MAX_HIGH_PRESSURE_DAMAGE 4 //This used to be 20... I got this much random rage for some retarded decision by polymorph?! Polymorph now lies in a pool of blood with a katana jammed in his spleen. ~Errorage --PS: The katana did less than 20 damage to him :( -#define LOW_PRESSURE_DAMAGE 4 //The amount of damage someone takes when in a low pressure area (The pressure threshold is so low that it doesn't make sense to do any calculations, so it just applies this flat value). -//low pressure damage bumped to 4 because current ZAS means cold does not proc half the time. 6 was too much. -#define PRESSURE_SUIT_REDUCTION_COEFFICIENT 0.6 //This is how much (percentual) a suit with the flag STOPSPRESSUREDMAGE reduces pressure. orig. .8 but we had spacewalkers without helmets. -#define PRESSURE_HEAD_REDUCTION_COEFFICIENT 0.4 //This is how much (percentual) a helmet/hat with the flag STOPSPRESSUREDMAGE reduces pressure. +#define LOW_PRESSURE_DAMAGE 4 //The amounb of damage someone takes when in a low pressure area (The pressure threshold is so low that it doesn't make sense to do any calculations, so it just applies this flat value). // Doors! #define DOOR_CRUSH_DAMAGE 10 @@ -133,6 +139,11 @@ #define T20C 293.15 // 20degC #define TCMB 2.7 // -270.3degC +//XGM gas flags +#define XGM_GAS_FUEL 1 +#define XGM_GAS_OXIDIZER 2 +#define XGM_GAS_CONTAMINANT 4 + //Used to be used by FEA //var/turf/space/Space_Tile = locate(/turf/space) // A space tile to reference when atmos wants to remove excess heat. @@ -261,21 +272,23 @@ var/MAX_EXPLOSION_RANGE = 14 // bitflags for clothing parts #define HEAD 1 -#define UPPER_TORSO 2 -#define LOWER_TORSO 4 -#define LEG_LEFT 8 -#define LEG_RIGHT 16 -#define LEGS 24 -#define FOOT_LEFT 32 -#define FOOT_RIGHT 64 -#define FEET 96 -#define ARM_LEFT 128 -#define ARM_RIGHT 256 -#define ARMS 384 -#define HAND_LEFT 512 -#define HAND_RIGHT 1024 -#define HANDS 1536 -#define FULL_BODY 2047 +#define FACE 2 +#define EYES 4 +#define UPPER_TORSO 8 +#define LOWER_TORSO 16 +#define LEG_LEFT 32 +#define LEG_RIGHT 64 +#define LEGS 96 +#define FOOT_LEFT 128 +#define FOOT_RIGHT 256 +#define FEET 384 +#define ARM_LEFT 512 +#define ARM_RIGHT 1024 +#define ARMS 1536 +#define HAND_LEFT 2048 +#define HAND_RIGHT 4096 +#define HANDS 6144 +#define FULL_BODY 8191 // bitflags for the percentual amount of protection a piece of clothing which covers the body part offers. // Used with human/proc/get_heat_protection() and human/proc/get_cold_protection() @@ -745,8 +758,10 @@ var/list/RESTRICTED_CAMERA_NETWORKS = list( //Those networks can only be accesse #define IS_SYNTHETIC 16384 //Language flags. -#define WHITELISTED 1 // Language is available if the speaker is whitelisted. -#define RESTRICTED 2 // Language can only be accquired by spawning or an admin. +#define WHITELISTED 1 // Language is available if the speaker is whitelisted. +#define RESTRICTED 2 // Language can only be accquired by spawning or an admin. +#define NONVERBAL 4 // Language has a significant non-verbal component. Speech is garbled without line-of-sight +#define SIGNLANG 8 // Language is completely non-verbal. Speech is displayed through emotes for those who can understand. //Flags for zone sleeping #define ZONE_ACTIVE 1 @@ -778,8 +793,74 @@ var/list/RESTRICTED_CAMERA_NETWORKS = list( //Those networks can only be accesse #define VAMP_FULL 13 +/* + Germs and infections +*/ + +#define GERM_LEVEL_AMBIENT 110 //maximum germ level you can reach by standing still +#define GERM_LEVEL_MOVE_CAP 200 //maximum germ level you can reach by running around + +#define INFECTION_LEVEL_ONE 100 +#define INFECTION_LEVEL_TWO 500 +#define INFECTION_LEVEL_THREE 1000 +/* + Shuttles +*/ +// these define the time taken for the shuttle to get to SS13 +// and the time before it leaves again +#define SHUTTLE_PREPTIME 300 // 5 minutes = 300 seconds - after this time, the shuttle departs centcom and cannot be recalled +#define SHUTTLE_LEAVETIME 180 // 3 minutes = 180 seconds - the duration for which the shuttle will wait at the station after arriving +#define SHUTTLE_TRANSIT_DURATION 300 // 5 minutes = 300 seconds - how long it takes for the shuttle to get to the station +#define SHUTTLE_TRANSIT_DURATION_RETURN 120 // 2 minutes = 120 seconds - for some reason it takes less time to come back, go figure. + +//Shuttle moving status +#define SHUTTLE_IDLE 0 +#define SHUTTLE_WARMUP 1 +#define SHUTTLE_INTRANSIT 2 + +//Ferry shuttle processing status +#define IDLE_STATE 0 +#define WAIT_LAUNCH 1 +#define WAIT_ARRIVE 2 +#define WAIT_FINISH 3 + +//computer3 error codes, move lower in the file when it passes dev -Sayu + #define PROG_CRASH 1 // Generic crash + #define MISSING_PERIPHERAL 2 // Missing hardware + #define BUSTED_ASS_COMPUTER 4 // Self-perpetuating error. BAC will continue to crash forever. + #define MISSING_PROGRAM 8 // Some files try to automatically launch a program. This is that failing. + #define FILE_DRM 16 // Some files want to not be copied/moved. This is them complaining that you tried. + #define NETWORK_FAILURE 32 + +//Some on_mob_life() procs check for alien races. +#define IS_DIONA 1 +#define IS_VOX 2 +#define IS_SKRELL 3 +#define IS_UNATHI 4 #define MAX_GEAR_COST 5 //Used in chargen for loadout limit. + + +/* + Atmos Machinery +*/ +#define MAX_SIPHON_FLOWRATE 2500 //L/s This can be used to balance how fast a room is siphoned. Anything higher than CELL_VOLUME has no effect. +#define MAX_SCRUBBER_FLOWRATE 200 //L/s Max flow rate when scrubbing from a turf. + +//These balance how easy or hard it is to create huge pressure gradients with pumps and filters. Lower values means it takes longer to create large pressures differences. +//Has no effect on pumping gasses from high pressure to low, only from low to high. Must be between 0 and 1. +#define ATMOS_PUMP_EFFICIENCY 2.5 +#define ATMOS_FILTER_EFFICIENCY 2.5 + +//will not bother pumping or filtering if the gas source as fewer than this amount of moles, to help with performance. +#define MINUMUM_MOLES_TO_PUMP 0.01 +#define MINUMUM_MOLES_TO_FILTER 0.1 + +//The flow rate/effectiveness of various atmos devices is limited by their internal volume, so for many atmos devices these will control maximum flow rates in L/s +#define ATMOS_DEFAULT_VOLUME_PUMP 200 //L +#define ATMOS_DEFAULT_VOLUME_FILTER 200 //L +#define ATMOS_DEFAULT_VOLUME_MIXER 200 //L +#define ATMOS_DEFAULT_VOLUME_PIPE 70 //L diff --git a/code/stylesheet.dm b/code/stylesheet.dm index da5ce74a..e1d214d8 100644 --- a/code/stylesheet.dm +++ b/code/stylesheet.dm @@ -62,6 +62,7 @@ h1.alert, h2.alert {color: #000000;} .modooc {color: #184880; font-weight: bold;} .adminmod {color: #402A14; font-weight: bold;} .tajaran {color: #803B56;} +.tajaran_signlang {color: #941C1C;} .skrell {color: #00CED1;} .soghun {color: #228B22;} .vox {color: #AA00AA;} diff --git a/code/world.dm b/code/world.dm index 95b6cb5c..ee3c38e6 100644 --- a/code/world.dm +++ b/code/world.dm @@ -1,7 +1,7 @@ /world mob = /mob/new_player turf = /turf/space - area = /area + area = /area//space view = "15x15" cache_lifespan = 0 //stops player uploaded stuff from being kept in the rsc past the current session @@ -283,7 +283,7 @@ var/world_topic_spam_protect_time = world.timeofday s += "[station_name()] - Heavy Roleplay"; s += " (" - s += "" //Change this to wherever you want the hub to link to. + s += "" //Change this to wherever you want the hub to link to. // s += "[game_version]" s += "Forums" //Replace this with something else. Or ever better, delete it and uncomment the game version. s += "" diff --git a/config/names/adjectives.txt b/config/names/adjectives.txt new file mode 100644 index 00000000..73b6a407 --- /dev/null +++ b/config/names/adjectives.txt @@ -0,0 +1,397 @@ +adorable +adventurous +aggressive +alert +attractive +average +beautiful +blue-eyed +bloody +blushing +bright +clean +clear +cloudy +colorful +crowded +cute +dark +drab +distinct +dull +elegant +excited +fancy +filthy +glamorous +gleaming +gorgeous +graceful +grotesque +handsome +homely +light +long +magnificent +misty +motionless +muddy +old-fashioned +plain +poised +precious +quaint +shiny +smoggy +sparkling +spotless +stormy +strange +ugly +ugliest +unsightly +unusual +wide-eyed +alive +annoying +bad +better +beautiful +brainy +breakable +busy +careful +cautious +clever +clumsy +concerned +crazy +curious +dead +different +difficult +doubtful +easy +expensive +famous +fragile +frail +gifted +helpful +helpless +horrible +important +impossible +inexpensive +innocent +inquisitive +modern +mushy +odd +open +outstanding +poor +powerful +prickly +puzzled +real +rich +shy +sleepy +stupid +super +talented +tame +tender +tough +uninterested +vast +wandering +wild +wrong + +angry +annoyed +anxious +arrogant +ashamed +awful +bad +bewildered +black +blue +bored +clumsy +combative +condemned +confused +crazy,flipped-out +creepy +cruel +dangerous +defeated +defiant +depressed +disgusted +disturbed +dizzy +dull +embarrassed +envious +evil +fierce +foolish +frantic +frightened +grieving +grumpy +helpless +homeless +hungry +hurt +ill +itchy +jealous +jittery +lazy +lonely +mysterious +nasty +naughty +nervous +nutty +obnoxious +outrageous +panicky +repulsive +scary +selfish +sore +tense +terrible +testy +thoughtless +tired +troubled +upset +uptight +weary +wicked +worried +agreeable +amused +brave +calm +charming +cheerful +comfortable +cooperative +courageous +delightful +determined +eager +elated +enchanting +encouraging +energetic +enthusiastic +excited +exuberant +fair +faithful +fantastic +fine +friendly +funny +gentle +glorious +good +happy +healthy +helpful +hilarious +jolly +joyous +kind +lively +lovely +lucky +nice +obedient +perfect +pleasant +proud +relieved +silly +smiling +splendid +successful +thankful +thoughtful +victorious +vivacious +witty +wonderful +zealous +zany +broad +chubby +crooked +curved +deep +flat +high +hollow +low +narrow +round +shallow +skinny +square +steep +straight +wide +big +colossal +fat +gigantic +great +huge +immense +large +little +mammoth +massive +miniature +petite +puny +scrawny +short +small +tall +teeny +teeny-tiny +tiny +cooing +deafening +faint +harsh +high-pitched +hissing +hushed +husky +loud +melodic +moaning +mute +noisy +purring +quiet +raspy +resonant +screeching +shrill +silent +soft +squealing +thundering +voiceless +whispering +ancient +brief +early +fast +late +long +modern +old +old-fashioned +quick +rapid +short +slow +swift +young +Taste/Touch +bitter +delicious +fresh +juicy +ripe +rotten +salty +sour +spicy +stale +sticky +strong +sweet +tart +tasteless +tasty +thirsty +fluttering +fuzzy +greasy +grubby +hard +hot +icy +loose +melted +nutritious +plastic +prickly +rainy +rough +scattered +shaggy +shaky +sharp +shivering +silky +slimy +slippery +smooth +soft +solid +steady +sticky +tender +tight +uneven +weak +wet +wooden +yummy +boiling +breezy +broken +bumpy +chilly +cold +cool +creepy +crooked +cuddly +curly +damaged +damp +dirty +dry +dusty +filthy +flaky +fluffy +freezing +hot +warm +wet +abundant +empty +few +heavy +light +many +numerous +substantial \ No newline at end of file diff --git a/config/names/ai.txt b/config/names/ai.txt new file mode 100644 index 00000000..01c65cf1 --- /dev/null +++ b/config/names/ai.txt @@ -0,0 +1,147 @@ +1-Rover-1 +16-20 +7-Zark-7 +790 +AM +AMEE +ASTAR +Adaptive Manipulator +Allied Mastercomputer +Alpha 5 +Alpha 6 +Alpha 7 +AmigoBot +Android +Aniel +Asimov +Astor +B-4 +B-9 +B.O.B. +B166ER +Bender +Bishop +Blitz +Box +Brackenridge +C-3PO +Cassandra One +Cell +Chii +Chip +Computer +Conky 2000 +Cutie +Data +Dee Model +Deep Thought +Dor-15 +Dorfl +Dot Matrix +Duey +E.D.I. +ED-209 +E-Man +Emma-2 +Erasmus +Ez-27 +FRIEND COMPUTER +Fagor +Faith +Fi +Frost +Fum +Futura +G2 +George +Gnut +Gort +H.A.R.L.I.E. +H.E.L.P.eR. +H.E.R.B.I.E. +HAL 9000 +Hadaly +Huey +Irona +Jay-Dub +Jinx +Johnny 5 +K-9 +KITT +Klapaucius +Kryten 2X4B-523P +L-76 +L-Ron +LUH 3417 +Louie +MARK13 +Maria +Marvin +Master Control Program +Max 404 +Maximillian +Mechagodzilla +Mechani-Kong +Metalhead +Mr. R.I.N.G. +NCH +Necron-99 +Norby +OMM 0910 +Orange v 3.5 +PTO +Project 2501 +R.I.C. 2.0 +R2-D2 +R4-P17 +Revelation +Ro-Man +Robbie +S.A.M. +S.H.O.C.K. +S.H.R.O.U.D. +S.O.P.H.I.E. +SEN 5241 +SHODAN +SID 6.7 +Setaur +Shrike +Solo +Speedy +Super 17 +Surgeon General Kraken +T-1000 +T-800 +T-850 +THX 1138 +TWA +Terminus +Tidy +Tik-Tok +Tobor +Trurl +ULTRABOT +Ulysses +Uniblab +V.I.N.CENT. +Voltes V +W1k1 +Wikipedia +Windows 3.1 +X-5 +XERXES +XR +Yod +Z-1 +Z-2 +Z-3 +Zed +Zord +Mugsy3000 +Terminus +Decimus +Robot Devil +Optimus +Megatron +Soundwave +Ironhide diff --git a/config/names/clown.txt b/config/names/clown.txt new file mode 100644 index 00000000..c29fca74 --- /dev/null +++ b/config/names/clown.txt @@ -0,0 +1,37 @@ +Gigglesworth +Honkel the III +Goose McSunny +Mr. Shoe +Toodles Sharperton +Dinky Doodle +Honkerbelle +Bo Bo Sassy +Baby Cakes +Ladybug Honks +Ziggy Yoyo +Razzle Dazzle +Buster Frown +Pepinpop +Silly Willy +Jo Jo Bobo Bo +Pocket +Patches +Checkers +Freckle +Honker +Bonker +Skiddle +Scootaloo +Sprinkledinkle +Ronnie Pace +Miss Stockings +Slippy Joe +Redshirt McBeat +Flop O'Honker +Speckles +Bubble +Button +Sparkle +Giggles +Jingle +Candy \ No newline at end of file diff --git a/config/names/death_commando.txt b/config/names/death_commando.txt new file mode 100644 index 00000000..b10e181b --- /dev/null +++ b/config/names/death_commando.txt @@ -0,0 +1,70 @@ +Killiam Shakespeare +Stabby McGee +Sgt. Slaughter +Maxx Power +Sir Killaslot +Slab Bulkhead +Fridge Largemeat +Punt Speedchunk +Butch Deadlift +Bold Bigflank +Splint Chesthair +Flint Ironstag +Bolt Vanderhuge +Thick McRunfast +Blast Hardcheese +Buff Drinklots +Trunk Slamchest +Fist Rockbone +Stump Beefgnaw +Smash Lampjaw +Punch Rockgroin +Buck Plankchest +Stump Chunkman +Dirk Hardpeck +Rip Steakface +Slate Slabrock +Crud Bonemeal +Brick Hardmeat +Rip Sidecheek +Punch Sideiron +Gristle McThornBody +Slake Fistcrunch +Buff Hardback +Blast Thickneck +Crunch Buttsteak +Slab Squatthrust +Lump Beefrock +Touch Rustrod +Reef Blastbody +Smoke Manmuscle +Beat Punchbeef +Pack Blowfist +Roll Fizzlebeef +Lance Killiam +George Melons +Maximilian Murderface +Bob Johnson +Crush McStompbones +Hank Chesthair +Killing McKillingalot +Mancrush McBrorape +Rex Dudekiller VII +Seamus McTosterone +Hans Testosteroneson +Max Pain +Theodore Pain +Sarah Pain +GORE Vidal +Leonardo Da Viking +Noam Bombsky +Al "Otta" Gore +Gibbs McLargehuge +Evil Martin Luther King +Evil Bob Marley +Duke Killington +AMERICA +Toolboxl Rose +Zombie Gandhi +A whole bunch of spiders in a SWAT suit +THAT DAMN FAGGOT TRAITOR GEORGE MELONS \ No newline at end of file diff --git a/config/names/first.txt b/config/names/first.txt new file mode 100644 index 00000000..a33e1811 --- /dev/null +++ b/config/names/first.txt @@ -0,0 +1,1502 @@ +Abel +Adolph +Aida +Alan +Alden +Alex +Alexa +Alexandria +Alexis +Alexus +Alfred +Alfreda +Alger +Alisa +Alisya +Allegra +Allegria +Allen +Alma +Alysha +Alyssia +Amaryllis +Ambrosine +Amos +Angel +Anjelica +Anne +Arabella +Archie +Arielle +Arleen +Arn +Art +Ashlie +Astor +Aubrey +Avalon +Averill +Baldric +Barbra +Bartholomew +Beckah +Becky +Bernice +Bertrand +Bethney +Betsy +Bidelia +Bill +Blake +Brayden +Breanne +Brendan +Brittani +Bronte +Brooke +Bryce +Burt +Byrne +Byron +Bysshe +Cadence +Calanthia +Caleigh +Camryn +Candace +Candice +Candis +Canute +Carly +Carlyle +Carolyn +Carry +Carter +Caryl +Casimir +Cassian +Cecily +Charlton +Cherette +Cheri +Cherry +Chip +Christa +Christiana +Christobel +Claribel +Clark +Claudius +Clement +Cleveland +Cliff +Clinton +Clitus +Clover +Collin +Coreen +Corrine +Cy +Cynthia +Dalya +Damian +Daniella +Danny +Darcey +Darell +Daria +Darin +Dayna +Deangelo +Debbi +Dee +Deena +Della +Delma +Denholm +Denys +Desmond +Devin +Diamond +Dina +Dolores +Dominic +Donella +Donna +Donny +Dorothy +Dortha +Driscoll +Duncan +Easter +Ebba +Edgar +Effie +Eliot +Eliott +Elizabeth +Elle +Elric +Elspet +Elwood +Emma +Emmanuel +Ermintrude +Esmeralda +Eugenia +Euphemia +Eustace +Eveleen +Evelina +Fay +Fitz +Flick +Floella +Flora +Flossie +Fortune +Francis +Frankie +Fulton +Garret +Gaye +Gaylord +Genette +Georgene +Geraldine +Gervase +Gina +Ginger +Gladwyn +Glenna +Goddard +Godwin +Goodwin +Gordon +Graeme +Gratian +Greta +Griselda +Gwenda +Gwenevere +Hadley +Haidee +Hailey +Hal +Haleigh +Happy +Hartley +Hayley +Heather +Hedley +Helen +Henderson +Hepsie +Hervey +Holden +Homer +Hope +Horatio +Hortensia +Huffie +Hugo +Iantha +Ileen +Innocent +Irene +Irvine +Jacaline +Jacquetta +Jacqui +Jake +Jakki +Jalen +Jamar +Jamie +Jamison +Janel +Janelle +Janette +Janie +Janina +Janine +Jasmine +Jaydon +Jaye +Jaylee +Jayne +Jaynie +Jeanna +Jeannie +Jeannine +Jeb +Jed +Jemmy +Jenifer +Jennie +Jera +Jere +Jeri +Jermaine +Jerrie +Jillian +Jillie +Jim +Joachim +Joetta +Joey +Johnathan +Johnny +Joi +Jonathon +Joni +Josepha +Josh +Josiah +Joye +Julia +July +Kaelea +Kaleigh +Karenza +Karly +Karyn +Kat +Kathy +Katlyn +Kayleigh +Keegan +Keira +Keith +Kellie +Kennard +Kerena +Kerensa +Keturah +Keziah +Kimberley +Lacy +Lakeisha +Lalla +Lanny +Latanya +Launce +Laurencia +Laurissa +Leeann +Leia +Leland +Lennox +Leroi +Lessie +Leta +Lexia +Lexus +Linden +Lindsie +Lindy +Linton +Lockie +Loreto +Lori +Lorin +Lou +Luanne +Lucian +Luvenia +Lyndsey +Lynn +Lynsey +Lynwood +Mabelle +Macey +Madyson +Maegan +Malachi +Malcolm +Manley +Marcia +Mariabella +Marilene +Marion +Marion +Marje +Marjory +Marlowe +Marlyn +Marshall +Maryann +Maudie +Maurene +May +Maynard +Melvyn +Merideth +Merrilyn +Meryl +Micheal +Mike +Milton +Minnie +Monna +Montague +Monte +Monty +Muriel +Mya +Myriam +Myrtie +Nan +Nathaniel +Nelle +Nena +Nerissa +Netta +Nettie +Nikolas +Noah +Nonie +Nova +Nowell +Nydia +Olive +Oralie +Osbert +Osborn +Osborne +Osmund +Paget +Patience +Patrick +Patton +Pauleen +Pene +Percival +Peregrine +Pheobe +Phoebe +Phyliss +Phyllida +Phyllis +Porsche +Prosper +Prue +Quanah +Quiana +Raelene +Rain +Randa +Randal +Rastus +Rayner +Rebeckah +Reene +Renie +Reuben +Rexana +Reynard +Rhetta +Rich +Richie +Rick +Rickena +Rickey +Rickie +Rodger +Roger +Romayne +Ronnette +Roscoe +Rosemary +Roswell +Royce +Rubye +Rusty +Sabella +Sachie +Sal +Sally +Saranna +Sawyer +Scotty +Seneca +Seymour +Shan +Shana +Shanika +Shannah +Shannon +Shantae +Sharalyn +Sharla +Sheri +Sherie +Sherill +Sherri +Shiloh +Simon +Sissy +Sloan +Sophie +Sorrel +Spike +Star +Steph +Stephany +Sue +Sukie +Sunshine +Susanna +Susannah +Suzan +Suzy +Sybil +Syd +Sydney +Tamika +Tamsin +Tania +Tansy +Tatyanna +Taylor +Tel +Terrell +Tiffany +Tod +Tolly +Topaz +Tori +Tracee +Tracey +Trinity +Tye +Uland +Ulric +Ulyssa +Valary +Vaughn +Verna +Vince +Vinnie +Vivyan +Walter +Ward +Warner +Wayne +Wendi +Whitaker +William +Willy +Winifred +Wisdom +Woodrow +Woody +Wynonna +Wynter +Yasmin +Yolanda +Ysabel +Zack +Zeke +Zelda +Zune +Jacob +Michael +Ethan +Joshua +Daniel +Alexander +Anthony +William +Christopher +Matthew +Jayden +Andrew +Joseph +David +Noah +Aiden +James +Ryan +Logan +John +Nathan +Elijah +Christian +Gabriel +Benjamin +Jonathan +Tyler +Samuel +Nicholas +Gavin +Dylan +Jackson +Brandon +Caleb +Mason +Angel +Isaac +Evan +Jack +Kevin +Jose +Isaiah +Luke +Landon +Justin +Lucas +Zachary +Jordan +Robert +Aaron +Brayden +Thomas +Cameron +Hunter +Austin +Adrian +Connor +Owen +Aidan +Jason +Julian +Wyatt +Charles +Luis +Carter +Juan +Chase +Diego +Jeremiah +Brody +Xavier +Adam +Carlos +Sebastian +Liam +Hayden +Nathaniel +Henry +Jesus +Ian +Tristan +Bryan +Sean +Cole +Alex +Eric +Brian +Jaden +Carson +Blake +Ayden +Cooper +Dominic +Brady +Caden +Josiah +Kyle +Colton +Kaden +Eli +Miguel +Antonio +Parker +Steven +Alejandro +Riley +Richard +Timothy +Devin +Jesse +Victor +Jake +Joel +Colin +Kaleb +Bryce +Levi +Oliver +Oscar +Vincent +Ashton +Cody +Micah +Preston +Marcus +Max +Patrick +Seth +Jeremy +Peyton +Nolan +Ivan +Damian +Maxwell +Alan +Kenneth +Jonah +Jorge +Mark +Giovanni +Eduardo +Grant +Collin +Gage +Omar +Emmanuel +Trevor +Edward +Ricardo +Cristian +Nicolas +Kayden +George +Jaxon +Paul +Braden +Elias +Andres +Derek +Garrett +Tanner +Malachi +Conner +Fernando +Cesar +Javier +Miles +Jaiden +Alexis +Leonardo +Santiago +Francisco +Cayden +Shane +Edwin +Hudson +Travis +Bryson +Erick +Jace +Hector +Josue +Peter +Jaylen +Mario +Manuel +Abraham +Grayson +Damien +Kaiden +Spencer +Stephen +Edgar +Wesley +Shawn +Trenton +Jared +Jeffrey +Landen +Johnathan +Bradley +Braxton +Ryder +Camden +Roman +Asher +Brendan +Maddox +Sergio +Israel +Andy +Lincoln +Erik +Donovan +Raymond +Avery +Rylan +Dalton +Harrison +Andre +Martin +Keegan +Marco +Jude +Sawyer +Dakota +Leo +Calvin +Kai +Drake +Troy +Zion +Clayton +Roberto +Zane +Gregory +Tucker +Rafael +Kingston +Dominick +Ezekiel +Griffin +Devon +Drew +Lukas +Johnny +Ty +Pedro +Tyson +Caiden +Mateo +Braylon +Cash +Aden +Chance +Taylor +Marcos +Maximus +Ruben +Emanuel +Simon +Corbin +Brennan +Dillon +Skyler +Myles +Xander +Jaxson +Dawson +Kameron +Kyler +Axel +Colby +Jonas +Joaquin +Payton +Brock +Frank +Enrique +Quinn +Emilio +Malik +Grady +Angelo +Julio +Derrick +Raul +Fabian +Corey +Gerardo +Dante +Ezra +Armando +Allen +Theodore +Gael +Amir +Zander +Adan +Maximilian +Randy +Easton +Dustin +Luca +Phillip +Julius +Charlie +Ronald +Jakob +Cade +Brett +Trent +Silas +Keith +Emiliano +Trey +Jalen +Darius +Lane +Jerry +Jaime +Scott +Graham +Weston +Braydon +Anderson +Rodrigo +Pablo +Saul +Danny +Donald +Elliot +Brayan +Dallas +Lorenzo +Casey +Mitchell +Alberto +Tristen +Rowan +Jayson +Gustavo +Aaden +Amari +Dean +Braeden +Declan +Chris +Ismael +Dane +Louis +Arturo +Brenden +Felix +Jimmy +Cohen +Tony +Holden +Reid +Abel +Bennett +Zackary +Arthur +Nehemiah +Ricky +Esteban +Cruz +Finn +Mauricio +Dennis +Keaton +Albert +Marvin +Mathew +Larry +Moises +Issac +Philip +Quentin +Curtis +Greyson +Jameson +Everett +Jayce +Darren +Elliott +Uriel +Alfredo +Hugo +Alec +Jamari +Marshall +Walter +Judah +Jay +Lance +Beau +Ali +Landyn +Yahir +Phoenix +Nickolas +Kobe +Bryant +Maurice +Russell +Leland +Colten +Reed +Davis +Joe +Ernesto +Desmond +Kade +Reece +Morgan +Ramon +Rocco +Orlando +Ryker +Brodie +Paxton +Jacoby +Douglas +Kristopher +Gary +Lawrence +Izaiah +Solomon +Nikolas +Mekhi +Justice +Tate +Jaydon +Salvador +Shaun +Alvin +Eddie +Kane +Davion +Zachariah +Dorian +Titus +Kellen +Camron +Isiah +Javon +Nasir +Milo +Johan +Byron +Jasper +Jonathon +Chad +Marc +Kelvin +Chandler +Sam +Cory +Deandre +River +Reese +Roger +Quinton +Talon +Romeo +Franklin +Noel +Alijah +Guillermo +Gunner +Damon +Jadon +Emerson +Micheal +Bruce +Terry +Kolton +Melvin +Beckett +Porter +August +Brycen +Dayton +Jamarion +Leonel +Karson +Zayden +Keagan +Carl +Khalil +Cristopher +Nelson +Braiden +Moses +Isaias +Roy +Triston +Walker +Kale +Emma +Isabella +Emily +Madison +Ava +Olivia +Sophia +Abigail +Elizabeth +Chloe +Samantha +Addison +Natalie +Mia +Alexis +Alyssa +Hannah +Ashley +Ella +Sarah +Grace +Taylor +Brianna +Lily +Hailey +Anna +Victoria +Kayla +Lillian +Lauren +Kaylee +Allison +Savannah +Nevaeh +Gabriella +Sofia +Makayla +Avery +Riley +Julia +Leah +Aubrey +Jasmine +Audrey +Katherine +Morgan +Brooklyn +Destiny +Sydney +Alexa +Kylie +Brooke +Kaitlyn +Evelyn +Layla +Madeline +Kimberly +Zoe +Jessica +Peyton +Alexandra +Claire +Madelyn +Maria +Mackenzie +Arianna +Jocelyn +Amelia +Angelina +Trinity +Andrea +Maya +Valeria +Sophie +Rachel +Vanessa +Aaliyah +Mariah +Gabrielle +Katelyn +Ariana +Bailey +Camila +Jennifer +Melanie +Gianna +Charlotte +Paige +Autumn +Payton +Faith +Sara +Isabelle +Caroline +Genesis +Isabel +Mary +Zoey +Gracie +Megan +Haley +Mya +Michelle +Molly +Stephanie +Nicole +Jenna +Natalia +Sadie +Jada +Serenity +Lucy +Ruby +Eva +Kennedy +Rylee +Jayla +Naomi +Rebecca +Lydia +Daniela +Bella +Keira +Adriana +Lilly +Hayden +Miley +Katie +Jade +Jordan +Gabriela +Amy +Angela +Melissa +Valerie +Giselle +Diana +Amanda +Kate +Laila +Reagan +Jordyn +Kylee +Danielle +Briana +Marley +Leslie +Kendall +Catherine +Liliana +Mckenzie +Jacqueline +Ashlyn +Reese +Marissa +London +Juliana +Shelby +Cheyenne +Angel +Daisy +Makenzie +Miranda +Erin +Amber +Alana +Ellie +Breanna +Ana +Mikayla +Summer +Piper +Adrianna +Jillian +Sierra +Jayden +Sienna +Alicia +Lila +Margaret +Alivia +Brooklynn +Karen +Violet +Sabrina +Stella +Aniyah +Annabelle +Alexandria +Kathryn +Skylar +Aliyah +Delilah +Julianna +Kelsey +Khloe +Carly +Amaya +Mariana +Christina +Alondra +Tessa +Eliana +Bianca +Jazmin +Clara +Vivian +Josephine +Delaney +Scarlett +Elena +Cadence +Alexia +Maggie +Laura +Nora +Ariel +Elise +Nadia +Mckenna +Chelsea +Lyla +Alaina +Jasmin +Hope +Leila +Caitlyn +Cassidy +Makenna +Allie +Izabella +Eden +Callie +Haylee +Caitlin +Kendra +Karina +Kyra +Kayleigh +Addyson +Kiara +Jazmine +Karla +Camryn +Alina +Lola +Kyla +Kelly +Fatima +Tiffany +Kira +Crystal +Mallory +Esmeralda +Alejandra +Eleanor +Angelica +Jayda +Abby +Kara +Veronica +Carmen +Jamie +Ryleigh +Valentina +Allyson +Dakota +Kamryn +Courtney +Cecilia +Madeleine +Aniya +Alison +Esther +Heaven +Aubree +Lindsey +Leilani +Nina +Melody +Macy +Ashlynn +Joanna +Cassandra +Alayna +Kaydence +Madilyn +Aurora +Heidi +Emerson +Kimora +Madalyn +Erica +Josie +Katelynn +Guadalupe +Harper +Ivy +Lexi +Camille +Savanna +Dulce +Daniella +Lucia +Emely +Joselyn +Kiley +Kailey +Miriam +Cynthia +Rihanna +Georgia +Rylie +Harmony +Kiera +Kyleigh +Monica +Bethany +Kaylie +Cameron +Teagan +Cora +Brynn +Ciara +Genevieve +Alice +Maddison +Eliza +Tatiana +Jaelyn +Erika +Ximena +April +Marely +Julie +Danica +Presley +Brielle +Julissa +Angie +Iris +Brenda +Hazel +Rose +Malia +Shayla +Fiona +Phoebe +Nayeli +Paola +Kaelyn +Selena +Audrina +Rebekah +Carolina +Janiyah +Michaela +Penelope +Janiya +Anastasia +Adeline +Ruth +Sasha +Denise +Holly +Madisyn +Hanna +Tatum +Marlee +Nataly +Helen +Janelle +Lizbeth +Serena +Anya +Jaslene +Kaylin +Jazlyn +Nancy +Lindsay +Desiree +Hayley +Itzel +Imani +Madelynn +Asia +Kadence +Madyson +Talia +Jane +Kayden +Annie +Amari +Bridget +Raegan +Jadyn +Celeste +Jimena +Luna +Yasmin +Emilia +Annika +Estrella +Sarai +Lacey +Ayla +Alessandra +Willow +Nyla +Dayana +Lilah +Lilliana +Natasha +Hadley +Harley +Priscilla +Claudia +Allisson +Baylee +Brenna +Brittany +Skyler +Fernanda +Danna +Melany +Cali +Lia +Macie +Lyric +Logan +Gloria +Lana +Mylee +Cindy +Lilian +Amira +Anahi +Alissa +Anaya +Lena +Ainsley +Sandra +Noelle +Marisol +Meredith +Kailyn +Lesly +Johanna +Diamond +Evangeline +Juliet +Kathleen +Meghan +Paisley +Athena +Hailee +Rosa +Wendy +Emilee +Sage +Alanna +Elaina +Cara +Nia +Paris +Casey +Dana +Emery +Rowan +Aubrie +Kaitlin +Jaden +Kenzie +Kiana +Viviana +Norah +Lauryn +Perla +Amiyah +Alyson +Rachael +Shannon +Aileen +Miracle +Lillie +Danika +Heather +Kassidy +Taryn +Tori +Francesca +Kristen +Amya +Elle +Kristina +Cheyanne +Haylie +Patricia +Anne +Samara diff --git a/config/names/first_female.txt b/config/names/first_female.txt new file mode 100644 index 00000000..69057409 --- /dev/null +++ b/config/names/first_female.txt @@ -0,0 +1,807 @@ +Aida +Alexa +Alexandria +Alexis +Alexus +Alfreda +Alisa +Alisya +Allegra +Allegria +Alma +Alysha +Alyssia +Amaryllis +Ambrosine +Angel +Anjelica +Anne +Arabella +Arielle +Arleen +Ashlie +Astor +Aubrey +Avalona +Averill +Barbara +Beckah +Becky +Bernice +Bethney +Betsy +Bidelia +Breanne +Brittani +Brooke +Cadence +Calanthia +Caleigh +Candace +Candice +Carly +Carlyle +Carolyn +Carry +Caryl +Cecily +Cherette +Cheri +Cherry +Christa +Christiana +Christobelle +Claribel +Clover +Coreen +Corrine +Cynthia +Dalya +Daniella +Daria +Dayna +Debbi +Dee +Deena +Della +Delma +Denys +Diamond +Dina +Dolores +Donella +Donna +Dorothy +Dortha +Easter +Ebba +Effie +Elizabeth +Elle +Emma +Ermintrude +Esmeralda +Eugenia +Euphemia +Eustace +Eveleen +Evelina +Fay +Floella +Flora +Flossie +Fortune +Genette +Georgene +Geraldine +Gervase +Gina +Ginger +Gladwyn +Glenna +Greta +Griselda +Gwenda +Gwenevere +Hadley +Haidee +Hailey +Hal +Haleigh +Hayley +Heather +Hedley +Helen +Hepsie +Hortensia +Iantha +Ileen +Innocent +Irene +Jacaline +Jacquetta +Jacqui +Jakki +Jalen +Janelle +Janette +Janie +Janina +Janine +Jasmine +Jaylee +Jaynie +Jeanna +Jeannie +Jeannine +Jenifer +Jennie +Jera +Jere +Jeri +Jillian +Jillie +Joetta +Joi +Joni +Josepha +Joye +Julia +July +Kaelea +Kaleigh +Karenza +Karly +Karyn +Kat +Kathy +Katlyn +Kayleigh +Keegan +Keira +Keith +Kellie +Kerena +Kerensa +Keturah +Kimberley +Lacy +Lakeisha +Lalla +Latanya +Laurencia +Laurissa +Leeann +Leia +Lessie +Leta +Lexia +Lexus +Lindsie +Lindy +Lockie +Lori +Lorin +Luanne +Lucian +Luvenia +Lyndsey +Lynn +Lynsey +Lynwood +Mabelle +Macey +Madyson +Maegan +Marcia +Mariabella +Marilene +Marion +Marje +Marjory +Marlowe +Marlyn +Marshall +Maryann +Maudie +Maurene +May +Merideth +Merrilyn +Meryl +Minnie +Monna +Muriel +Mya +Myriam +Myrtie +Nan +Nelle +Nena +Nerissa +Netta +Nettie +Nonie +Nova +Nowell +Nydia +Olive +Oralie +Patience +Pauleen +Pene +Peregrine +Pheobe +Phoebe +Phyliss +Phyllida +Phyllis +Porsche +Prosper +Prue +Quanah +Quiana +Raelene +Rain +Randa +Randal +Rebeckah +Reene +Renie +Rexana +Rhetta +Ronnette +Rosemary +Rubye +Sabella +Sachie +Sally +Saranna +Seneca +Shana +Shanika +Shannah +Shannon +Shantae +Sharalyn +Sharla +Sheri +Sherie +Sherill +Sherri +Sissy +Sophie +Star +Steph +Stephany +Sue +Sukie +Sunshine +Susanna +Susannah +Suzan +Suzy +Sydney +Tamika +Tania +Tansy +Tatyanna +Tiffany +Tolly +Topaz +Tori +Tracee +Tracey +Ulyssa +Valary +Verna +Vinnie +Vivyan +Wendi +Wisdom +Wynonna +Wynter +Yasmin +Yolanda +Ysabel +Zelda +Zune +Emma +Isabella +Emily +Madison +Ava +Olivia +Sophia +Abigail +Elizabeth +Chloe +Samantha +Addison +Natalie +Mia +Alexis +Alyssa +Hannah +Ashley +Ella +Sarah +Grace +Taylor +Brianna +Lily +Hailey +Anna +Victoria +Kayla +Lillian +Lauren +Kaylee +Allison +Savannah +Nevaeh +Gabriella +Sofia +Makayla +Avery +Riley +Julia +Leah +Aubrey +Jasmine +Audrey +Katherine +Morgan +Brooklyn +Destiny +Sydney +Alexa +Kylie +Brooke +Kaitlyn +Evelyn +Layla +Madeline +Kimberly +Zoe +Jessica +Peyton +Alexandra +Claire +Madelyn +Maria +Mackenzie +Arianna +Jocelyn +Amelia +Angelina +Trinity +Andrea +Maya +Valeria +Sophie +Rachel +Vanessa +Aaliyah +Mariah +Gabrielle +Katelyn +Ariana +Bailey +Camila +Jennifer +Melanie +Gianna +Charlotte +Paige +Autumn +Payton +Faith +Sara +Isabelle +Caroline +Isabel +Mary +Zoey +Gracie +Megan +Haley +Mya +Michelle +Molly +Stephanie +Nicole +Jenna +Natalia +Sadie +Jada +Serenity +Lucy +Ruby +Eva +Kennedy +Rylee +Jayla +Naomi +Rebecca +Lydia +Daniela +Bella +Keira +Adriana +Lilly +Hayden +Miley +Katie +Jade +Jordan +Gabriela +Amy +Angela +Melissa +Valerie +Giselle +Diana +Amanda +Kate +Laila +Reagan +Jordyn +Kylee +Danielle +Briana +Marley +Leslie +Kendall +Catherine +Liliana +Mckenzie +Jacqueline +Ashlyn +Reese +Marissa +London +Juliana +Shelby +Cheyenne +Angel +Daisy +Makenzie +Miranda +Erin +Amber +Alana +Ellie +Breanna +Ana +Mikayla +Summer +Piper +Adrianna +Jillian +Sierra +Jayden +Sienna +Alicia +Lila +Margaret +Alivia +Brooklynn +Karen +Violet +Sabrina +Stella +Aniyah +Annabelle +Alexandria +Kathryn +Skylar +Aliyah +Delilah +Julianna +Kelsey +Khloe +Carly +Amaya +Mariana +Christina +Alondra +Tessa +Eliana +Bianca +Jazmin +Clara +Vivian +Josephine +Delaney +Scarlett +Elena +Cadence +Alexia +Maggie +Laura +Nora +Ariel +Elise +Nadia +Mckenna +Chelsea +Lyla +Alaina +Jasmin +Hope +Leila +Caitlyn +Cassidy +Makenna +Allie +Izabella +Eden +Callie +Haylee +Caitlin +Kendra +Karina +Kyra +Kayleigh +Addyson +Kiara +Jazmine +Karla +Camryn +Alina +Lola +Kyla +Kelly +Fatima +Tiffany +Kira +Crystal +Mallory +Esmeralda +Alejandra +Eleanor +Angelica +Jayda +Abby +Kara +Veronica +Carmen +Jamie +Ryleigh +Valentina +Allyson +Dakota +Kamryn +Courtney +Cecilia +Madeleine +Aniya +Alison +Esther +Heaven +Aubree +Lindsey +Leilani +Nina +Melody +Macy +Ashlynn +Joanna +Cassandra +Alayna +Kaydence +Madilyn +Aurora +Heidi +Emerson +Kimora +Madalyn +Erica +Josie +Katelynn +Guadalupe +Harper +Ivy +Lexi +Camille +Savanna +Dulce +Daniella +Lucia +Emely +Joselyn +Kiley +Kailey +Miriam +Cynthia +Rihanna +Georgia +Rylie +Harmony +Kiera +Kyleigh +Monica +Bethany +Kaylie +Cameron +Teagan +Cora +Brynn +Ciara +Genevieve +Alice +Maddison +Eliza +Tatiana +Jaelyn +Erika +Ximena +April +Marely +Julie +Danica +Presley +Brielle +Julissa +Angie +Iris +Brenda +Hazel +Rose +Malia +Shayla +Fiona +Phoebe +Nayeli +Paola +Kaelyn +Selena +Audrina +Rebekah +Carolina +Janiyah +Michaela +Penelope +Janiya +Anastasia +Adeline +Ruth +Sasha +Denise +Holly +Madisyn +Hanna +Tatum +Marlee +Nataly +Helen +Janelle +Lizbeth +Serena +Anya +Jaslene +Kaylin +Jazlyn +Nancy +Lindsay +Desiree +Hayley +Itzel +Imani +Madelynn +Asia +Kadence +Madyson +Talia +Jane +Kayden +Annie +Amari +Bridget +Raegan +Jadyn +Celeste +Jimena +Luna +Yasmin +Emilia +Annika +Estrella +Sarai +Lacey +Ayla +Alessandra +Willow +Nyla +Dayana +Lilah +Lilliana +Natasha +Hadley +Harley +Priscilla +Claudia +Allisson +Baylee +Brenna +Brittany +Skyler +Fernanda +Danna +Melany +Cali +Lia +Macie +Lyric +Logan +Gloria +Lana +Mylee +Cindy +Lilian +Amira +Anahi +Alissa +Anaya +Lena +Ainsley +Sandra +Noelle +Marisol +Meredith +Kailyn +Lesly +Johanna +Diamond +Evangeline +Juliet +Kathleen +Meghan +Paisley +Athena +Hailee +Rosa +Wendy +Emilee +Sage +Alanna +Elaina +Cara +Nia +Paris +Casey +Dana +Emery +Rowan +Aubrie +Kaitlin +Jaden +Kenzie +Kiana +Viviana +Norah +Lauryn +Perla +Amiyah +Alyson +Rachael +Shannon +Aileen +Miracle +Lillie +Danika +Heather +Kassidy +Taryn +Tori +Francesca +Kristen +Amya +Elle +Kristina +Cheyanne +Haylie +Patricia +Anne +Samara \ No newline at end of file diff --git a/config/names/first_male.txt b/config/names/first_male.txt new file mode 100644 index 00000000..ebb8f6d2 --- /dev/null +++ b/config/names/first_male.txt @@ -0,0 +1,725 @@ +Abel +Adolph +Alan +Alden +Alex +Alfred +Alger +Allen +Amos +Apple +Archie +Arnie +Art +Arthur +Baldric +Bartholomew +Bill +Blake +Brayden +Brendan +Brock +Bronte +Brick +Bruce +Bryce +Buck +Burt +Butch +Byrne +Byron +Camryn +Carl +Carter +Casimir +Cassian +Charles +Charlton +Chip +Clark +Claudius +Clement +Cleveland +Cliff +Clinton +Cletus +Collin +Crush +Cy +Damian +Danny +Darcey +Darell +Darin +Deangelo +Denholm +Desmond +Devin +Dirk +Dominic +Donny +Driscoll +Duke +Duncan +Edgar +Eliot +Eliott +Elric +Elwood +Emmanuel +Fenton +Fitz +Flick +Flint +Flip +Francis +Frank +Frankie +Fridge +Fulton +Gannon +Garret +Gary +Goddard +Godwin +Goodwin +Gordon +Graeme +Grandpa +Gratian +Grendel +Han +Harry +Hartley +Harvey +Henderson +Holden +Homer +Horatio +Huffie +Hungry +Hugo +Irvine +Jacob +Jake +Jamar +Jamie +Jamison +Janel +Jaydon +Jaye +Jayne +Jean-Luc +Jeb +Jed +Jemmy +Jermaine +Jerrie +Jim +Joachim +Joey +Johnathan +John +Johnny +Jonathon +Josh +Josiah +Kennard +Keziah +Lando +Lanny +Launce +Leland +Lennox +Lenny +Leonard +Leroy +Lief +Linden +Linton +Lorde +Loreto +Lou +Lucas +Luke +Malachi +Malcolm +Manley +Marion +Max +Maynard +Melvyn +Michael +Mike +Milton +Montague +Monte +Monty +Nat +Nathaniel +Nick +Nikolas +Noah +Opie +Osbert +Osborn +Osborne +Osmund +Oswald +Paget +Patrick +Patton +Percival +Persh +Rastus +Raymond +Rayner +Reuben +Reynard +Richard +Rodger +Roger +Romayne +Roscoe +Roswell +Royce +Rube +Rusty +Sal +Sawyer +Scotty +Seymour +Shane +Shiloh +Smoke +Simon +Sloan +Sorrel +Spike +Sybil +Syd +Tamsin +Taylor +Tel +Terrell +Tim +Timothy +Todd +Trip +Tye +Uland +Ulric +Vaughn +Vince +Vinny +Walter +Ward +Warner +Wayne +Whitaker +William +Willy +Woodrow +Zack +Zane +Zeke +Jacob +Michael +Ethan +Joshua +Daniel +Alexander +Anthony +William +Christopher +Matthew +Jayden +Andrew +Joseph +David +Noah +Aiden +James +Ryan +Logan +John +Nathan +Elijah +Christian +Gabriel +Benjamin +Jonathan +Tyler +Samuel +Nicholas +Gavin +Dylan +Jackson +Brandon +Caleb +Mason +Angel +Isaac +Evan +Jack +Kevin +Jose +Isaiah +Luke +Landon +Justin +Lucas +Zachary +Jordan +Robert +Aaron +Brayden +Thomas +Cameron +Hunter +Austin +Adrian +Connor +Owen +Aidan +Jason +Julian +Wyatt +Charles +Luis +Carter +Juan +Chase +Diego +Jeremiah +Brody +Xavier +Adam +Carlos +Sebastian +Liam +Hayden +Nathaniel +Henry +Jesus +Ian +Tristan +Bryan +Sean +Cole +Alex +Eric +Brian +Jaden +Carson +Blake +Ayden +Cooper +Dominic +Brady +Caden +Josiah +Kyle +Colton +Kaden +Eli +Miguel +Antonio +Parker +Steven +Alejandro +Riley +Richard +Timothy +Devin +Jesse +Victor +Jake +Joel +Colin +Kaleb +Bryce +Levi +Oliver +Oscar +Vincent +Ashton +Cody +Micah +Preston +Marcus +Max +Patrick +Seth +Jeremy +Peyton +Nolan +Ivan +Damian +Maxwell +Alan +Kenneth +Jonah +Jorge +Mark +Giovanni +Eduardo +Grant +Collin +Gage +Omar +Emmanuel +Trevor +Edward +Ricardo +Cristian +Nicolas +Kayden +George +Jaxon +Paul +Braden +Elias +Andres +Derek +Garrett +Tanner +Malachi +Conner +Fernando +Cesar +Javier +Miles +Jaiden +Alexis +Leonardo +Santiago +Francisco +Cayden +Shane +Edwin +Hudson +Travis +Bryson +Erick +Jace +Hector +Josue +Peter +Jaylen +Mario +Manuel +Abraham +Grayson +Damien +Kaiden +Spencer +Stephen +Edgar +Wesley +Shawn +Trenton +Jared +Jeffrey +Landen +Johnathan +Bradley +Braxton +Ryder +Camden +Roman +Asher +Brendan +Maddox +Sergio +Israel +Andy +Lincoln +Erik +Donovan +Raymond +Avery +Rylan +Dalton +Harrison +Andre +Martin +Keegan +Marco +Jude +Sawyer +Dakota +Leo +Calvin +Kai +Drake +Troy +Zion +Clayton +Roberto +Zane +Gregory +Tucker +Rafael +Kingston +Dominick +Ezekiel +Griffin +Devon +Drew +Lukas +Johnny +Ty +Pedro +Tyson +Caiden +Mateo +Braylon +Cash +Aden +Chance +Taylor +Marcos +Maximus +Ruben +Emanuel +Simon +Corbin +Brennan +Dillon +Skyler +Myles +Xander +Jaxson +Dawson +Kameron +Kyler +Axel +Colby +Jonas +Joaquin +Payton +Brock +Frank +Enrique +Quinn +Emilio +Malik +Grady +Angelo +Julio +Derrick +Raul +Fabian +Corey +Gerardo +Dante +Ezra +Armando +Allen +Theodore +Gael +Amir +Zander +Adan +Maximilian +Randy +Easton +Dustin +Luca +Phillip +Julius +Charlie +Ronald +Jakob +Cade +Brett +Trent +Silas +Keith +Emiliano +Trey +Jalen +Darius +Lane +Jerry +Jaime +Scott +Graham +Weston +Braydon +Anderson +Rodrigo +Pablo +Saul +Danny +Donald +Elliot +Brayan +Dallas +Lorenzo +Casey +Mitchell +Alberto +Tristen +Rowan +Jayson +Gustavo +Aaden +Amari +Dean +Braeden +Declan +Chris +Ismael +Dane +Louis +Arturo +Brenden +Felix +Jimmy +Cohen +Tony +Holden +Reid +Abel +Bennett +Zackary +Arthur +Nehemiah +Ricky +Esteban +Cruz +Finn +Mauricio +Dennis +Keaton +Albert +Marvin +Mathew +Larry +Moises +Issac +Philip +Quentin +Curtis +Greyson +Jameson +Everett +Jayce +Darren +Elliott +Uriel +Alfredo +Hugo +Alec +Jamari +Marshall +Walter +Judah +Jay +Lance +Beau +Ali +Landyn +Yahir +Phoenix +Nickolas +Kobe +Bryant +Maurice +Russell +Leland +Colten +Reed +Davis +Joe +Ernesto +Desmond +Kade +Reece +Morgan +Ramon +Rocco +Orlando +Ryker +Brodie +Paxton +Jacoby +Douglas +Kristopher +Gary +Lawrence +Izaiah +Solomon +Nikolas +Mekhi +Justice +Tate +Jaydon +Salvador +Shaun +Alvin +Eddie +Kane +Davion +Zachariah +Damien +Titus +Kellen +Camron +Isiah +Javon +Nasir +Milo +Johan +Byron +Jasper +Jonathon +Chad +Marc +Kelvin +Chandler +Sam +Cory +Deandre +River +Reese +Roger +Quinton +Talon +Romeo +Franklin +Noel +Alijah +Guillermo +Gunner +Damon +Jadon +Emerson +Micheal +Bruce +Terry +Kolton +Melvin +Beckett +Porter +August +Brycen +Dayton +Jamarion +Leonel +Karson +Zayden +Keagan +Carl +Khalil +Cristopher +Nelson +Braiden +Moses +Isaias +Roy +Triston +Walker +Kale \ No newline at end of file diff --git a/config/names/last.txt b/config/names/last.txt new file mode 100644 index 00000000..9bdb16eb --- /dev/null +++ b/config/names/last.txt @@ -0,0 +1,620 @@ +Whittier +Dimeling +Blaine +Dennis +Adams +Rader +Murray +Millhouse +Ludwig +Burris +Shupe +Mary +Zadovsky +Philips +Wise +Gronko +Jardine +Black +Mitchell +Enderly +Stall +Harrow +Atweeke +Sealis +Conrad +Lucy +Stewart +Green +Feufer +Warren +Campbell +Shafer +Woodworth +Magor +Logue +Reichard +Day +Dugmore +Murray +Greenawalt +Jyllian +Osterwise +Styles +Cavalet +Garneys +Raub +Sholl +Chauvin +Poley +Todd +Brandenburg +Baer +Pritchard +Pinney +Kadel +Anderson +Clarke +Hunt +Gadow +Stough +Marcotte +Brooks +Watson +Nash +Sheets +Ashbaugh +Zimmer +Noton +Dean +Fleming +Draudy +Bluetenberger +Fischer +Hawkins +Poehl +Addison +Mcintosh +Keppel +Kimple +Alice +Stone +Fiscina +Leichter +Wile +Callison +Cowper +Harrold +Carr +Eckhardstein +Wilkerson +Shirey +Benford +Reade +Baskett +Seidner +Gettemy +Joyce +Judge +Burkett +Kiefer +Carmichael +Hirleman +Wells +Isemann +Cressman +Highlands +Briggs +Rowley +Coldsmith +Berkheimer +Hill +Maclagan +Mcfall +Mens +Braun +James +Sloan +Bould +Overstreet +Kanaga +Polson +Finlay +Sandys +Bousum +Howard +Treeby +Stainforth +Werner +Sulyard +Marriman +Weinstein +Butterfill +Mason +Coates +Peters +Gregory +Wilo +Edwards +Barnes +Harding +Tireman +Lombardi +Roberts +Faqua +Basmanoff +Mccune +Mckendrick +Oppenheimer +Oneal +Focell +Tedrow +Fields +Ryals +Best +Zaun +Knapp +Linton +Jackson +Bullard +Mcloskey +Zoucks +Heckendora +Hoenshell +Woollard +Mueller +Burns +Franks +Goodman +Stern +Robinson +Hooker +David +Fitzgerald +Vanleer +Beach +Flickinger +Metzer +Bynum +Stafford +Osteen +Johnson +Paynter +Thomlinson +Simmons +Basinger +Fisher +Bunten +Compton +Archibald +Catherina +Rahl +Bowchiew +Tennant +Mccullough +Margaret +Schaeffer +Sommer +Beail +Merryman +Knapenberger +Patterson +Houston +Lacon +Levett +Sullivan +Sidower +Laborde +Stroh +Hice +Biery +Christman +Staymates +Sauter +Snyder +Bratton +Sybilla +Altmann +Mathews +Newbern +Baker +Kemble +Mingle +Unk +Otis +Quinn +Bell +Roberts +Wood +Ullman +Bicknell +Gibson +Rohtin +James +Wallick +Eggbert +Losey +Neely +Catleay +McDonald +Beedell +Williamson +Bennett +Potter +Caldwell +Lowe +Durstine +King +Gardner +Ulery +Rifler +Trovato +Thomas +Nehling +Baum +Werry +Mcmullen +Koster +Willey +Mildred +Straub +Haynes +Baxter +Ackerley +Greene +Atkinson +Davis +Weeter +Milne +Leech +Clewett +Ewing +Hook +Reighner +Welty +Jenkins +Bennett +Swarner +Hawker +Agg +Batten +Cherry +Shaffer +Yeskey +Stephenson +Pycroft +Larson +Joghs +Keener +Christopher +Roadman +Echard +Priebe +Auman +Kemerer +Sutton +Prechtl +Cowart +Ringer +Garratt +Siegrist +Seelig +Lafortune +Dryfus +Isaman +Teagarden +Evans +Wolfe +Fiddler +Jowers +Aultman +Olphert +Howe +Leslie +Stocker +Bode +Whirlow +Ann +Mcclymonds +Guess +Taggart +Pratt +Moon +Huey +Hegarty +Meyers +Stahl +Nickolson +Mortland +Perkins +Thorley +Fuchs +Ray +Schrader +Kellogg +Woodward +Faust +Roby +Bashline +Cypret +Laurenzi +Minnie +Houser +Langston +Anderson +Barrett +Wible +Hujsak +Wardle +Pershing +Kuster +Driggers +Wheeler +Garland +Alliman +Hoover +Camp +Hall +Parkinson +Swabey +Mull +Cox +Hanford +Stange +Wolff +Jesse +Brinigh +Koepple +Schmidt +Muller +Schofield +Zalack +Pfeifer +Fea +Blackburn +Ward +Kifer +Costello +Donkin +Osterweis +Brindle +Llora +Duncan +Ehret +Fryer +Summy +Richards +Boyer +Hutton +Mosser +Lester +Stroble +Randolph +Lord +Scott +Nicholas +Smail +Ratcliff +Riggle +Newton +Sanders +Mitchell +Lauffer +Aggley +Moberly +Hynes +Pennington +Woolery +Hardie +Blessig +Tanner +Demuth +Fraser +Henry +Shick +Ironmonger +Scherer +Field +Prevatt +Earl +Paulson +Curry +Powers +Rosensteel +Hoopengarner +Weisgarber +Elliott +Pearsall +Lowstetter +Holdeman +Kepplinger +Bloise +Hunter +Glover +Hayhurst +Wentzel +Owens +Smith +Steele +Leach +Armstrong +Wheeler +Easter +Greenwood +Woodward +Pratt +Buzzard +Cox +Eliza +Rockwell +Zeal +Harshman +Northey +Tilton +Richter +Moore +Jerome +Hardy +Bickerson +White +Beck +Waldron +Fulton +Miller +Sandford +Hughes +Winton +Digson +Byers +Hincken +Shaner +Todd +Whiteman +Lineman +Albright +Hastings +Elderson +Garrison +Rose +Kelley +Quirin +Dickinson +Young +Ramos +Jewell +Endsley +Briner +Jenner +Saylor +Bash +Blyant +Rhinehart +Prescott +Kirkson +Picard +Riker +Spock +Skywalker +Vader +Muggins +Buttersworth +Stamos +Sagan +Hawking +Dawkins +Goebbles +McShain +McDonohugh +Power +Smith +Jones +Williams +Brown +Taylor +Davies +Wilson +Evans +Thomas +Roberts +Johnson +Walker +Wright +Robinson +Thompson +Hughes +White +Edwards +Hall +Patel +Green +Martins +Lewis +Wood +Jackson +Clarke +Harris +Clark +Scott +Turner +Hill +Moore +Cooper +Morris +Ward +Watson +Morgan +Anderson +Harrison +King +Campbell +Young +Mitchell +Baker +James +Kelly +Allen +Bell +Phillips +Lee +Stewart +Miller +Parker +Simpson +Bennett +Davis +Griffiths +Shaw +Price +Cook +Richardson +Murray +Marshall +Begum +Murphy +Khan +Gray +Collins +Bailey +Carter +Robertson +Graham +Adams +Richards +Cox +Singh +Hussain +Ellis +Wilkinson +Foster +Thomson +Russell +Ali +Reid +Rathens +Rathen +Mason +Chapman +Powell +Owen +Ahmed +Gibson +Rogers +Webb +Holmes +Mills +Matthews +Hunt +Palmer +Lloyd +Kaur +Fisher +Ivanov +Smirnov +Vasilyev +Petrov +Kuznetsov +Mikhaylov +Pavlov +Semenov +Andreev +Alekseev \ No newline at end of file diff --git a/config/names/ninjaname.txt b/config/names/ninjaname.txt new file mode 100644 index 00000000..828bc170 --- /dev/null +++ b/config/names/ninjaname.txt @@ -0,0 +1,45 @@ +Shadow +Sarutobi +Smoke +Rain +Scorpion +Zero +Ermac +Saibot +Sun +Moon +Cyrax +Raphael +Michaelangelo +Cloud +Donatello +Leonardo +Splinter +Shredder +Hazuki +Hien +Hiryu +Ryu +Hayabusa +Midnight +Seven +Hanzo +Blood +Iga +Koga +Hero +Hiro +Phantom +Baki +Ogre +Daemon +Goemon +Throat +Death +Aria +Bro +Fox +Null +Raiden +Samurai +Eater \ No newline at end of file diff --git a/config/names/ninjatitle.txt b/config/names/ninjatitle.txt new file mode 100644 index 00000000..429425ca --- /dev/null +++ b/config/names/ninjatitle.txt @@ -0,0 +1,46 @@ +Master +Sensei +Swift +Merciless +Assassin +Rogue +Hunter +Widower +Orphaner +Stalker +Killer +Silent +Silencing +Quick +Agile +Merciful +Ninja +Shinobi +Initiate +Grandmaster +Strider +Striker +Slayer +Awesome +Ender +Dr. +Night +Crimson +Grappler +Ulimate +Remorseless +Deep +Dragon +Cruel +Nightshade +Black +Gray +Solid +Liquid +Solidus +Steel +Nickel +Silver +Singing +Snake +Acid \ No newline at end of file diff --git a/config/names/verbs.txt b/config/names/verbs.txt new file mode 100644 index 00000000..5bb8d6e3 --- /dev/null +++ b/config/names/verbs.txt @@ -0,0 +1,633 @@ +accept +add +admire +admit +advise +afford +agree +alert +allow +amuse +analyse +announce +annoy +answer +apologise +appear +applaud +appreciate +approve +argue +arrange +arrest +arrive +ask +attach +attack +attempt +attend +attract +avoid +back +bake +balance +ban +bang +bare +bat +bathe +battle +beam +beg +behave +belong +bleach +bless +blind +blink +blot +blush +boast +boil +bolt +bomb +book +bore +borrow +bounce +bow +box +brake +brake +branch +breathe +bruise +brush +bubble +bump +burn +bury +buzz +calculate +call +camp +care +carry +carve +cause +challenge +change +charge +chase +cheat +check +cheer +chew +choke +chop +claim +clap +clean +clear +clip +close +coach +coil +collect +colour +comb +command +communicate +compare +compete +complain +complete +concentrate +concern +confess +confuse +connect +consider +consist +contain +continue +copy +correct +cough +count +cover +crack +crash +crawl +cross +crush +cry +cure +curl +curve +cycle +dam +damage +dance +dare +decay +deceive +decide +decorate +delay +delight +deliver +depend +describe +desert +deserve +destroy +detect +develop +disagree +disappear +disapprove +disarm +discover +dislike +divide +double +doubt +drag +drain +dream +dress +drip +drop +drown +drum +dry +dust +earn +educate +embarrass +employ +empty +encourage +end +enjoy +enter +entertain +escape +examine +excite +excuse +exercise +exist +expand +expect +explain +explode +extend +face +fade +fail +fancy +fasten +fax +fear +fence +fetch +file +fill +film +fire +fit +fix +flap +flash +float +flood +flow +flower +fold +follow +fool +force +form +found +frame +frighten +fry +gather +gaze +glow +glue +grab +grate +grease +greet +grin +grip +groan +guarantee +guard +guess +guide +hammer +hand +handle +hang +happen +harass +harm +hate +haunt +head +heal +heap +heat +help +hook +hop +hope +hover +hug +hum +hunt +hurry +identify +ignore +imagine +impress +improve +include +increase +influence +inform +inject +injure +instruct +intend +interest +interfere +interrupt +introduce +invent +invite +irritate +itch +jail +jam +jog +join +joke +judge +juggle +jump +kick +kill +kiss +kneel +knit +knock +knot +label +land +last +laugh +launch +learn +level +license +lick +lie +lighten +like +list +listen +live +load +lock +long +look +love +man +manage +march +mark +marry +match +mate +matter +measure +meddle +melt +memorise +mend +messup +milk +mine +miss +mix +moan +moor +mourn +move +muddle +mug +multiply +murder +nail +name +need +nest +nod +note +notice +number +obey +object +observe +obtain +occur +offend +offer +open +order +overflow +owe +own +pack +paddle +paint +park +part +pass +paste +pat +pause +peck +pedal +peel +peep +perform +permit +phone +pick +pinch +pine +place +plan +plant +play +please +plug +point +poke +polish +pop +possess +post +pour +practise +pray +preach +precede +prefer +prepare +present +preserve +press +pretend +prevent +prick +print +produce +program +promise +protect +provide +pull +pump +punch +puncture +punish +push +question +queue +race +radiate +rain +raise +reach +realise +receive +recognise +record +reduce +reflect +refuse +regret +reign +reject +rejoice +relax +release +rely +remain +remember +remind +remove +repair +repeat +replace +reply +report +reproduce +request +rescue +retire +return +rhyme +rinse +risk +rob +rock +roll +rot +rub +ruin +rule +rush +sack +sail +satisfy +save +saw +scare +scatter +scold +scorch +scrape +scratch +scream +screw +scribble +scrub +seal +search +separate +serve +settle +shade +share +shave +shelter +shiver +shock +shop +shrug +sigh +sign +signal +sin +sip +ski +skip +slap +slip +slow +smash +smell +smile +smoke +snatch +sneeze +sniff +snore +snow +soak +soothe +sound +spare +spark +sparkle +spell +spill +spoil +spot +spray +sprout +squash +squeak +squeal +squeeze +stain +stamp +stare +start +stay +steer +step +stir +stitch +stop +store +strap +strengthen +stretch +strip +stroke +stuff +subtract +succeed +suck +suffer +suggest +suit +supply +support +suppose +surprise +surround +suspect +suspend +switch +talk +tame +tap +taste +tease +telephone +tempt +terrify +test +thank +thaw +tick +tickle +tie +time +tip +tire +touch +tour +tow +trace +trade +train +transport +trap +travel +treat +tremble +trick +trip +trot +trouble +trust +try +tug +tumble +turn +twist +type +undress +unfasten +unite +unlock +unpack +untidy +use +vanish +visit +wail +wait +walk +wander +want +warm +warn +wash +waste +watch +water +wave +weigh +welcome +whine +whip +whirl +whisper +whistle +wink +wipe +wish +wobble +wonder +work +worry +wrap +wreck +wrestle +wriggle +yawn +yell +zip +zoom \ No newline at end of file diff --git a/config/names/wizardfirst.txt b/config/names/wizardfirst.txt new file mode 100644 index 00000000..18806ca7 --- /dev/null +++ b/config/names/wizardfirst.txt @@ -0,0 +1,36 @@ +Jim +Gulstaff +Gandalf +Grimm +Mordenkainen +Elminister +Saruman +Vaarsuvius +Yoda +Zul +Nihilus +Vecna +Mogan +Circe +Prospero +Raistlin +Rasputin +Tzeentch +Khelben +Dumbledor +Houdini +Terefi +Urza +Tenser +Zagyg +Mystryl +Boccob +Merlin +Archchancellor +Radagast +Kreol +Kaschei +Lina +Morgan +Alatar +Palando \ No newline at end of file diff --git a/config/names/wizardsecond.txt b/config/names/wizardsecond.txt new file mode 100644 index 00000000..1fe60bdb --- /dev/null +++ b/config/names/wizardsecond.txt @@ -0,0 +1,39 @@ +the Powerful +the Great +the Magician +the Wise +the Seething +the Amazing +the Spiral King +Darkmagic +the White +the Gray +Shado +the Sorcelator +the Raven +the Emperor +the Brown +Weatherwax +the Destroyer +the Deathless +Yagg +the Remorseful +the Weeping +the Unending +the All Knowing +Dark +Smith +the Conquerer +the Unstoppable +Gray +of Void +Unseen +Darko +Honko +the Bandit Killer +the Dragon Spooker +Inverse +le Fay +the Blue +the Red +the Benevolent \ No newline at end of file diff --git a/data/investigate/gravity.html b/data/investigate/gravity.html index b372edd0..136ba45c 100644 --- a/data/investigate/gravity.html +++ b/data/investigate/gravity.html @@ -1,2 +1,2 @@ -21:23 [0x2009fc1] (92,152,1) || the gravitational generator has regained power.
    -21:23 [0x2009fc1] (92,152,1) || the gravitational generator is now charging.
    +22:07 [0x2009fed] (92,152,1) || the gravitational generator has regained power.
    +22:07 [0x2009fed] (92,152,1) || the gravitational generator is now charging.
    diff --git a/icons/atmos/connector.dmi b/icons/atmos/connector.dmi new file mode 100644 index 00000000..cf4ea7a3 Binary files /dev/null and b/icons/atmos/connector.dmi differ diff --git a/icons/atmos/digital_tvalve.dmi b/icons/atmos/digital_tvalve.dmi new file mode 100644 index 00000000..b34d2903 Binary files /dev/null and b/icons/atmos/digital_tvalve.dmi differ diff --git a/icons/atmos/digital_valve.dmi b/icons/atmos/digital_valve.dmi new file mode 100644 index 00000000..b1adc8b6 Binary files /dev/null and b/icons/atmos/digital_valve.dmi differ diff --git a/icons/atmos/filter.dmi b/icons/atmos/filter.dmi new file mode 100644 index 00000000..8b5cd81c Binary files /dev/null and b/icons/atmos/filter.dmi differ diff --git a/icons/atmos/heat.dmi b/icons/atmos/heat.dmi new file mode 100644 index 00000000..6d1ed47e Binary files /dev/null and b/icons/atmos/heat.dmi differ diff --git a/icons/atmos/injector.dmi b/icons/atmos/injector.dmi new file mode 100644 index 00000000..03a60d2e Binary files /dev/null and b/icons/atmos/injector.dmi differ diff --git a/icons/atmos/junction.dmi b/icons/atmos/junction.dmi new file mode 100644 index 00000000..70286bde Binary files /dev/null and b/icons/atmos/junction.dmi differ diff --git a/icons/atmos/manifold.dmi b/icons/atmos/manifold.dmi new file mode 100644 index 00000000..f6b24341 Binary files /dev/null and b/icons/atmos/manifold.dmi differ diff --git a/icons/atmos/mixer.dmi b/icons/atmos/mixer.dmi new file mode 100644 index 00000000..0fcb9282 Binary files /dev/null and b/icons/atmos/mixer.dmi differ diff --git a/icons/atmos/omni_devices.dmi b/icons/atmos/omni_devices.dmi new file mode 100644 index 00000000..494ea7e2 Binary files /dev/null and b/icons/atmos/omni_devices.dmi differ diff --git a/icons/atmos/passive_gate.dmi b/icons/atmos/passive_gate.dmi new file mode 100644 index 00000000..df8c3897 Binary files /dev/null and b/icons/atmos/passive_gate.dmi differ diff --git a/icons/atmos/pipe_underlays.dmi b/icons/atmos/pipe_underlays.dmi new file mode 100644 index 00000000..0edf79e5 Binary files /dev/null and b/icons/atmos/pipe_underlays.dmi differ diff --git a/icons/atmos/pipes.dmi b/icons/atmos/pipes.dmi new file mode 100644 index 00000000..019be28f Binary files /dev/null and b/icons/atmos/pipes.dmi differ diff --git a/icons/atmos/pump.dmi b/icons/atmos/pump.dmi new file mode 100644 index 00000000..a1e2d43c Binary files /dev/null and b/icons/atmos/pump.dmi differ diff --git a/icons/atmos/tank.dmi b/icons/atmos/tank.dmi new file mode 100644 index 00000000..322f43e8 Binary files /dev/null and b/icons/atmos/tank.dmi differ diff --git a/icons/atmos/tvalve.dmi b/icons/atmos/tvalve.dmi new file mode 100644 index 00000000..197862b3 Binary files /dev/null and b/icons/atmos/tvalve.dmi differ diff --git a/icons/atmos/valve.dmi b/icons/atmos/valve.dmi new file mode 100644 index 00000000..04351917 Binary files /dev/null and b/icons/atmos/valve.dmi differ diff --git a/icons/atmos/vent_pump.dmi b/icons/atmos/vent_pump.dmi new file mode 100644 index 00000000..88461036 Binary files /dev/null and b/icons/atmos/vent_pump.dmi differ diff --git a/icons/atmos/vent_scrubber.dmi b/icons/atmos/vent_scrubber.dmi new file mode 100644 index 00000000..e8e7f916 Binary files /dev/null and b/icons/atmos/vent_scrubber.dmi differ diff --git a/icons/atmos/volume_pump.dmi b/icons/atmos/volume_pump.dmi new file mode 100644 index 00000000..9114b6ac Binary files /dev/null and b/icons/atmos/volume_pump.dmi differ diff --git a/icons/effects/ss13_dark_alpha6.dmi b/icons/effects/ss13_dark_alpha6.dmi new file mode 100644 index 00000000..74233735 Binary files /dev/null and b/icons/effects/ss13_dark_alpha6.dmi differ diff --git a/icons/mob/pai.dmi b/icons/mob/pai.dmi new file mode 100644 index 00000000..f286389b Binary files /dev/null and b/icons/mob/pai.dmi differ diff --git a/icons/obj/pipeturbine.dmi b/icons/obj/pipeturbine.dmi new file mode 100644 index 00000000..fccec4c9 Binary files /dev/null and b/icons/obj/pipeturbine.dmi differ