diff --git a/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm b/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm index 60268e0c7ea..35bc1270beb 100644 --- a/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm +++ b/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm @@ -1,180 +1,180 @@ - -/* - -Passive gate is similar to the regular pump except: -* It doesn't require power -* Can not transfer low pressure to higher pressure (so it's more like a valve where you can control the flow) - -*/ - -/obj/machinery/atmospherics/components/binary/passive_gate - icon_state = "passgate_map" - - name = "passive gate" - desc = "A one-way air valve that does not require power" - - can_unwrench = 1 - - var/on = 0 - var/target_pressure = ONE_ATMOSPHERE - - var/frequency = 0 - var/id = null - var/datum/radio_frequency/radio_connection - -/obj/machinery/atmospherics/components/binary/passive_gate/Destroy() - if(SSradio) - SSradio.remove_object(src,frequency) - return ..() - -/obj/machinery/atmospherics/components/binary/passive_gate/update_icon_nopipes() - if(!on) - icon_state = "passgate_off" - overlays.Cut() - return - - overlays += getpipeimage('icons/obj/atmospherics/components/binary_devices.dmi', "passgate_on") - -/obj/machinery/atmospherics/components/binary/passive_gate/process_atmos() - ..() - if(!on) - return 0 - - var/datum/gas_mixture/air1 = AIR1 - var/datum/gas_mixture/air2 = AIR2 - - 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) - - update_parents() - - -//Radio remote control - -/obj/machinery/atmospherics/components/binary/passive_gate/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = SSradio.add_object(src, frequency, filter = RADIO_ATMOSIA) - -/obj/machinery/atmospherics/components/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 - - signal.data = list( - "tag" = id, - "device" = "AGP", - "power" = on, - "target_output" = target_pressure, - "sigtype" = "status" - ) - - radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) - - return 1 - -/obj/machinery/atmospherics/components/binary/passive_gate/interact(mob/user) - if(stat & (BROKEN|NOPOWER)) return - ui_interact(user) - -/obj/machinery/atmospherics/components/binary/passive_gate/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 0) - ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open) - if (!ui) - ui = new(user, src, ui_key, "atmos_pump", name, 400, 115) - ui.open() - -/obj/machinery/atmospherics/components/binary/passive_gate/get_ui_data() - var/data = list() - data["on"] = on - data["set_pressure"] = round(target_pressure) - data["max_pressure"] = round(MAX_OUTPUT_PRESSURE) - return data - -/obj/machinery/atmospherics/components/binary/passive_gate/atmosinit() - ..() - if(frequency) - set_frequency(frequency) - -/obj/machinery/atmospherics/components/binary/passive_gate/receive_signal(datum/signal/signal) - if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) - return 0 - - var/old_on = on //for logging - - 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 = Clamp(text2num(signal.data["set_output_pressure"]),0,ONE_ATMOSPHERE*50) - - if(on != old_on) - investigate_log("was turned [on ? "on" : "off"] by a remote signal", "atmos") - - if("status" in signal.data) - spawn(2) - broadcast_status() - return //do not update_icon - - spawn(2) - broadcast_status() - update_icon() - return - - -/obj/machinery/atmospherics/components/binary/passive_gate/attack_hand(mob/user) - if(..() || !user) - return - interact(user) - -/obj/machinery/atmospherics/components/binary/passive_gate/ui_act(action, params) - if(..()) - return - - switch(action) - if("power") - on = !on - investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") - if("pressure") - switch(params["set"]) - if ("max") - target_pressure = MAX_OUTPUT_PRESSURE - if ("custom") - target_pressure = max(0, min(MAX_OUTPUT_PRESSURE, safe_input("Pressure control", "Enter new output pressure (0-[MAX_OUTPUT_PRESSURE] kPa)", target_pressure))) - investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") - update_icon() - return 1 - -/obj/machinery/atmospherics/components/binary/passive_gate/power_change() - ..() - update_icon() - -/obj/machinery/atmospherics/components/binary/passive_gate/attackby(obj/item/weapon/W, mob/user, params) - if (!istype(W, /obj/item/weapon/wrench)) - return ..() - if (on) - user << "You cannot unwrench this [src], turn it off first!" - return 1 - return ..() + +/* + +Passive gate is similar to the regular pump except: +* It doesn't require power +* Can not transfer low pressure to higher pressure (so it's more like a valve where you can control the flow) + +*/ + +/obj/machinery/atmospherics/components/binary/passive_gate + icon_state = "passgate_map" + + name = "passive gate" + desc = "A one-way air valve that does not require power" + + can_unwrench = 1 + + var/on = 0 + var/target_pressure = ONE_ATMOSPHERE + + var/frequency = 0 + var/id = null + var/datum/radio_frequency/radio_connection + +/obj/machinery/atmospherics/components/binary/passive_gate/Destroy() + if(SSradio) + SSradio.remove_object(src,frequency) + return ..() + +/obj/machinery/atmospherics/components/binary/passive_gate/update_icon_nopipes() + if(!on) + icon_state = "passgate_off" + overlays.Cut() + return + + overlays += getpipeimage('icons/obj/atmospherics/components/binary_devices.dmi', "passgate_on") + +/obj/machinery/atmospherics/components/binary/passive_gate/process_atmos() + ..() + if(!on) + return 0 + + var/datum/gas_mixture/air1 = AIR1 + var/datum/gas_mixture/air2 = AIR2 + + 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) + + update_parents() + + +//Radio remote control + +/obj/machinery/atmospherics/components/binary/passive_gate/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = SSradio.add_object(src, frequency, filter = RADIO_ATMOSIA) + +/obj/machinery/atmospherics/components/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 + + signal.data = list( + "tag" = id, + "device" = "AGP", + "power" = on, + "target_output" = target_pressure, + "sigtype" = "status" + ) + + radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) + + return 1 + +/obj/machinery/atmospherics/components/binary/passive_gate/interact(mob/user) + if(stat & (BROKEN|NOPOWER)) return + ui_interact(user) + +/obj/machinery/atmospherics/components/binary/passive_gate/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 0) + ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open) + if (!ui) + ui = new(user, src, ui_key, "atmos_pump", name, 400, 115) + ui.open() + +/obj/machinery/atmospherics/components/binary/passive_gate/get_ui_data() + var/data = list() + data["on"] = on + data["set_pressure"] = round(target_pressure) + data["max_pressure"] = round(MAX_OUTPUT_PRESSURE) + return data + +/obj/machinery/atmospherics/components/binary/passive_gate/atmosinit() + ..() + if(frequency) + set_frequency(frequency) + +/obj/machinery/atmospherics/components/binary/passive_gate/receive_signal(datum/signal/signal) + if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) + return 0 + + var/old_on = on //for logging + + 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 = Clamp(text2num(signal.data["set_output_pressure"]),0,ONE_ATMOSPHERE*50) + + if(on != old_on) + investigate_log("was turned [on ? "on" : "off"] by a remote signal", "atmos") + + if("status" in signal.data) + spawn(2) + broadcast_status() + return //do not update_icon + + spawn(2) + broadcast_status() + update_icon() + return + + +/obj/machinery/atmospherics/components/binary/passive_gate/attack_hand(mob/user) + if(..() || !user) + return + interact(user) + +/obj/machinery/atmospherics/components/binary/passive_gate/ui_act(action, params) + if(..()) + return + + switch(action) + if("power") + on = !on + investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") + if("pressure") + switch(params["set"]) + if ("max") + target_pressure = MAX_OUTPUT_PRESSURE + if ("custom") + target_pressure = max(0, min(MAX_OUTPUT_PRESSURE, safe_input("Pressure control", "Enter new output pressure (0-[MAX_OUTPUT_PRESSURE] kPa)", target_pressure))) + investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") + update_icon() + return 1 + +/obj/machinery/atmospherics/components/binary/passive_gate/power_change() + ..() + update_icon() + +/obj/machinery/atmospherics/components/binary/passive_gate/attackby(obj/item/weapon/W, mob/user, params) + if (!istype(W, /obj/item/weapon/wrench)) + return ..() + if (on) + user << "You cannot unwrench this [src], turn it off first!" + return 1 + return ..() diff --git a/code/ATMOSPHERICS/components/binary_devices/pump.dm b/code/ATMOSPHERICS/components/binary_devices/pump.dm index 650f06b3a68..5b2241598e8 100644 --- a/code/ATMOSPHERICS/components/binary_devices/pump.dm +++ b/code/ATMOSPHERICS/components/binary_devices/pump.dm @@ -1,189 +1,189 @@ -/* -Every cycle, the pump uses the air in air_in to try and make air_out the perfect pressure. - -node1, air1, network1 correspond to input -node2, air2, network2 correspond to output - -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... -*/ - -/obj/machinery/atmospherics/components/binary/pump - icon_state = "pump_map" - name = "gas pump" - desc = "A pump" - - can_unwrench = 1 - - var/on = 0 - var/target_pressure = ONE_ATMOSPHERE - - var/frequency = 0 - var/id = null - var/datum/radio_frequency/radio_connection - -/obj/machinery/atmospherics/components/binary/pump/Destroy() - if(SSradio) - SSradio.remove_object(src,frequency) - if(radio_connection) - radio_connection = null - return ..() -/obj/machinery/atmospherics/components/binary/pump/on - on = 1 - -/obj/machinery/atmospherics/components/binary/pump/update_icon_nopipes() - if(stat & NOPOWER) - icon_state = "pump_off" - return - - icon_state = "pump_[on?"on":"off"]" - -/obj/machinery/atmospherics/components/binary/pump/process_atmos() -// ..() - if(stat & (NOPOWER|BROKEN)) - return 0 - if(!on) - return 0 - - var/datum/gas_mixture/air1 = AIR1 - var/datum/gas_mixture/air2 = AIR2 - - var/output_starting_pressure = air2.return_pressure() - - if( (target_pressure - output_starting_pressure) < 0.01) - //No need to pump gas if target is already reached! - return 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) - - //Actually transfer the gas - var/datum/gas_mixture/removed = air1.remove(transfer_moles) - air2.merge(removed) - - update_parents() - - return 1 - -//Radio remote control -/obj/machinery/atmospherics/components/binary/pump/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = SSradio.add_object(src, frequency, filter = RADIO_ATMOSIA) - -/obj/machinery/atmospherics/components/binary/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" = "AGP", - "power" = on, - "target_output" = target_pressure, - "sigtype" = "status" - ) - - radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) - - return 1 - -/obj/machinery/atmospherics/components/binary/pump/interact(mob/user) - if(stat & (BROKEN|NOPOWER)) return - if(!src.allowed(usr)) - usr << "Access denied." - return - ui_interact(user) - -/obj/machinery/atmospherics/components/binary/pump/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 0) - ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open) - if (!ui) - ui = new(user, src, ui_key, "atmos_pump", name, 400, 115) - ui.open() - -/obj/machinery/atmospherics/components/binary/pump/get_ui_data() - var/data = list() - data["on"] = on - data["set_pressure"] = round(target_pressure) - data["max_pressure"] = round(MAX_OUTPUT_PRESSURE) - return data - -/obj/machinery/atmospherics/components/binary/pump/atmosinit() - ..() - if(frequency) - set_frequency(frequency) - -/obj/machinery/atmospherics/components/binary/pump/receive_signal(datum/signal/signal) - if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) - return 0 - - var/old_on = on //for logging - - 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 = Clamp(text2num(signal.data["set_output_pressure"]),0,ONE_ATMOSPHERE*50) - - if(on != old_on) - investigate_log("was turned [on ? "on" : "off"] by a remote signal", "atmos") - - if("status" in signal.data) - spawn(2) - broadcast_status() - return //do not update_icon - - spawn(2) - broadcast_status() - update_icon() - return - - -/obj/machinery/atmospherics/components/binary/pump/attack_hand(mob/user) - if(..() || !user) - return - interact(user) - -/obj/machinery/atmospherics/components/binary/pump/ui_act(action, params) - if(..()) - return - - switch(action) - if("power") - on = !on - investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") - if("pressure") - switch(params["set"]) - if ("max") - target_pressure = MAX_OUTPUT_PRESSURE - if ("custom") - target_pressure = max(0, min(MAX_OUTPUT_PRESSURE, safe_input("Pressure control", "Enter new output pressure (0-[MAX_OUTPUT_PRESSURE] kPa)", target_pressure))) - investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") - update_icon() - return 1 - -/obj/machinery/atmospherics/components/binary/pump/power_change() - ..() - update_icon() - -/obj/machinery/atmospherics/components/binary/pump/attackby(obj/item/weapon/W, mob/user, params) - if (!istype(W, /obj/item/weapon/wrench)) - return ..() - if (!(stat & NOPOWER) && on) - user << "You cannot unwrench this [src], turn it off first!" - return 1 - return ..() - +/* +Every cycle, the pump uses the air in air_in to try and make air_out the perfect pressure. + +node1, air1, network1 correspond to input +node2, air2, network2 correspond to output + +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... +*/ + +/obj/machinery/atmospherics/components/binary/pump + icon_state = "pump_map" + name = "gas pump" + desc = "A pump" + + can_unwrench = 1 + + var/on = 0 + var/target_pressure = ONE_ATMOSPHERE + + var/frequency = 0 + var/id = null + var/datum/radio_frequency/radio_connection + +/obj/machinery/atmospherics/components/binary/pump/Destroy() + if(SSradio) + SSradio.remove_object(src,frequency) + if(radio_connection) + radio_connection = null + return ..() +/obj/machinery/atmospherics/components/binary/pump/on + on = 1 + +/obj/machinery/atmospherics/components/binary/pump/update_icon_nopipes() + if(stat & NOPOWER) + icon_state = "pump_off" + return + + icon_state = "pump_[on?"on":"off"]" + +/obj/machinery/atmospherics/components/binary/pump/process_atmos() +// ..() + if(stat & (NOPOWER|BROKEN)) + return 0 + if(!on) + return 0 + + var/datum/gas_mixture/air1 = AIR1 + var/datum/gas_mixture/air2 = AIR2 + + var/output_starting_pressure = air2.return_pressure() + + if( (target_pressure - output_starting_pressure) < 0.01) + //No need to pump gas if target is already reached! + return 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) + + //Actually transfer the gas + var/datum/gas_mixture/removed = air1.remove(transfer_moles) + air2.merge(removed) + + update_parents() + + return 1 + +//Radio remote control +/obj/machinery/atmospherics/components/binary/pump/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = SSradio.add_object(src, frequency, filter = RADIO_ATMOSIA) + +/obj/machinery/atmospherics/components/binary/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" = "AGP", + "power" = on, + "target_output" = target_pressure, + "sigtype" = "status" + ) + + radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) + + return 1 + +/obj/machinery/atmospherics/components/binary/pump/interact(mob/user) + if(stat & (BROKEN|NOPOWER)) return + if(!src.allowed(usr)) + usr << "Access denied." + return + ui_interact(user) + +/obj/machinery/atmospherics/components/binary/pump/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 0) + ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open) + if (!ui) + ui = new(user, src, ui_key, "atmos_pump", name, 400, 115) + ui.open() + +/obj/machinery/atmospherics/components/binary/pump/get_ui_data() + var/data = list() + data["on"] = on + data["set_pressure"] = round(target_pressure) + data["max_pressure"] = round(MAX_OUTPUT_PRESSURE) + return data + +/obj/machinery/atmospherics/components/binary/pump/atmosinit() + ..() + if(frequency) + set_frequency(frequency) + +/obj/machinery/atmospherics/components/binary/pump/receive_signal(datum/signal/signal) + if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) + return 0 + + var/old_on = on //for logging + + 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 = Clamp(text2num(signal.data["set_output_pressure"]),0,ONE_ATMOSPHERE*50) + + if(on != old_on) + investigate_log("was turned [on ? "on" : "off"] by a remote signal", "atmos") + + if("status" in signal.data) + spawn(2) + broadcast_status() + return //do not update_icon + + spawn(2) + broadcast_status() + update_icon() + return + + +/obj/machinery/atmospherics/components/binary/pump/attack_hand(mob/user) + if(..() || !user) + return + interact(user) + +/obj/machinery/atmospherics/components/binary/pump/ui_act(action, params) + if(..()) + return + + switch(action) + if("power") + on = !on + investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") + if("pressure") + switch(params["set"]) + if ("max") + target_pressure = MAX_OUTPUT_PRESSURE + if ("custom") + target_pressure = max(0, min(MAX_OUTPUT_PRESSURE, safe_input("Pressure control", "Enter new output pressure (0-[MAX_OUTPUT_PRESSURE] kPa)", target_pressure))) + investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") + update_icon() + return 1 + +/obj/machinery/atmospherics/components/binary/pump/power_change() + ..() + update_icon() + +/obj/machinery/atmospherics/components/binary/pump/attackby(obj/item/weapon/W, mob/user, params) + if (!istype(W, /obj/item/weapon/wrench)) + return ..() + if (!(stat & NOPOWER) && on) + user << "You cannot unwrench this [src], turn it off first!" + return 1 + return ..() + diff --git a/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm b/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm index 7b28aa206ab..35a904c8163 100644 --- a/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm +++ b/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm @@ -1,186 +1,186 @@ -/* -Every cycle, the pump uses the air in air_in to try and make air_out the perfect pressure. - -node1, air1, network1 correspond to input -node2, air2, network2 correspond to output - -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... -*/ - -/obj/machinery/atmospherics/components/binary/volume_pump - icon_state = "volpump_map" - name = "volumetric gas pump" - desc = "A volumetric pump" - - can_unwrench = 1 - - var/on = 0 - var/transfer_rate = MAX_TRANSFER_RATE - - var/frequency = 0 - var/id = null - var/datum/radio_frequency/radio_connection - -/obj/machinery/atmospherics/components/binary/volume_pump/Destroy() - if(SSradio) - SSradio.remove_object(src,frequency) - return ..() - -/obj/machinery/atmospherics/components/binary/volume_pump/on - on = 1 - -/obj/machinery/atmospherics/components/binary/volume_pump/update_icon_nopipes() - if(stat & NOPOWER) - icon_state = "volpump_off" - return - - icon_state = "volpump_[on?"on":"off"]" - -/obj/machinery/atmospherics/components/binary/volume_pump/process_atmos() -// ..() - if(stat & (NOPOWER|BROKEN)) - return - if(!on) - return 0 - - var/datum/gas_mixture/air1 = AIR1 - var/datum/gas_mixture/air2 = AIR2 - -// 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) - - update_parents() - - return 1 - -/obj/machinery/atmospherics/components/binary/volume_pump/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = SSradio.add_object(src, frequency) - -/obj/machinery/atmospherics/components/binary/volume_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" = "APV", - "power" = on, - "transfer_rate" = transfer_rate, - "sigtype" = "status" - ) - radio_connection.post_signal(src, signal) - - return 1 - -/obj/machinery/atmospherics/components/binary/volume_pump/interact(mob/user) - if(stat & (BROKEN|NOPOWER)) - return - if(!src.allowed(usr)) - usr << "Access denied." - return - ui_interact(user) - -/obj/machinery/atmospherics/components/binary/volume_pump/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 0) - ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open) - if (!ui) - ui = new(user, src, ui_key, "atmos_pump", name, 400, 115) - ui.open() - -/obj/machinery/atmospherics/components/binary/volume_pump/get_ui_data() - var/data = list() - data["on"] = on - data["transfer_rate"] = round(transfer_rate) - data["max_rate"] = round(MAX_TRANSFER_RATE) - return data - -/obj/machinery/atmospherics/components/binary/volume_pump/atmosinit() - ..() - - set_frequency(frequency) - -/obj/machinery/atmospherics/components/binary/volume_pump/receive_signal(datum/signal/signal) - if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) - return 0 - - var/old_on = on //for logging - - 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) - var/datum/gas_mixture/air1 = AIR1 - transfer_rate = Clamp(text2num(signal.data["set_transfer_rate"]),0,air1.volume) - - if(on != old_on) - investigate_log("was turned [on ? "on" : "off"] by a remote signal", "atmos") - - if("status" in signal.data) - spawn(2) - broadcast_status() - return //do not update_icon - - spawn(2) - broadcast_status() - update_icon() - return - - -/obj/machinery/atmospherics/components/binary/volume_pump/attack_hand(mob/user) - if(..() || !user) - return - interact(user) - -/obj/machinery/atmospherics/components/binary/volume_pump/ui_act(action, params) - if(..()) - return - - switch(action) - if("power") - on = !on - investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") - if("transfer") - switch(params) - if ("max") - transfer_rate = MAX_TRANSFER_RATE - if ("custom") - transfer_rate = max(0, min(MAX_TRANSFER_RATE, safe_input("Pressure control", "Enter new transfer rate (0-[MAX_TRANSFER_RATE] L/s)", transfer_rate))) - investigate_log("was set to [transfer_rate] L/s by [key_name(usr)]", "atmos") - update_icon() - return 1 - -/obj/machinery/atmospherics/components/binary/volume_pump/power_change() - ..() - update_icon() - -/obj/machinery/atmospherics/components/binary/volume_pump/attackby(obj/item/weapon/W, mob/user, params) - if (!istype(W, /obj/item/weapon/wrench)) - return ..() - if (!(stat & NOPOWER) && on) - user << "You cannot unwrench this [src], turn it off first!" - return 1 - return ..() +/* +Every cycle, the pump uses the air in air_in to try and make air_out the perfect pressure. + +node1, air1, network1 correspond to input +node2, air2, network2 correspond to output + +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... +*/ + +/obj/machinery/atmospherics/components/binary/volume_pump + icon_state = "volpump_map" + name = "volumetric gas pump" + desc = "A volumetric pump" + + can_unwrench = 1 + + var/on = 0 + var/transfer_rate = MAX_TRANSFER_RATE + + var/frequency = 0 + var/id = null + var/datum/radio_frequency/radio_connection + +/obj/machinery/atmospherics/components/binary/volume_pump/Destroy() + if(SSradio) + SSradio.remove_object(src,frequency) + return ..() + +/obj/machinery/atmospherics/components/binary/volume_pump/on + on = 1 + +/obj/machinery/atmospherics/components/binary/volume_pump/update_icon_nopipes() + if(stat & NOPOWER) + icon_state = "volpump_off" + return + + icon_state = "volpump_[on?"on":"off"]" + +/obj/machinery/atmospherics/components/binary/volume_pump/process_atmos() +// ..() + if(stat & (NOPOWER|BROKEN)) + return + if(!on) + return 0 + + var/datum/gas_mixture/air1 = AIR1 + var/datum/gas_mixture/air2 = AIR2 + +// 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) + + update_parents() + + return 1 + +/obj/machinery/atmospherics/components/binary/volume_pump/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = SSradio.add_object(src, frequency) + +/obj/machinery/atmospherics/components/binary/volume_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" = "APV", + "power" = on, + "transfer_rate" = transfer_rate, + "sigtype" = "status" + ) + radio_connection.post_signal(src, signal) + + return 1 + +/obj/machinery/atmospherics/components/binary/volume_pump/interact(mob/user) + if(stat & (BROKEN|NOPOWER)) + return + if(!src.allowed(usr)) + usr << "Access denied." + return + ui_interact(user) + +/obj/machinery/atmospherics/components/binary/volume_pump/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 0) + ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open) + if (!ui) + ui = new(user, src, ui_key, "atmos_pump", name, 400, 115) + ui.open() + +/obj/machinery/atmospherics/components/binary/volume_pump/get_ui_data() + var/data = list() + data["on"] = on + data["transfer_rate"] = round(transfer_rate) + data["max_rate"] = round(MAX_TRANSFER_RATE) + return data + +/obj/machinery/atmospherics/components/binary/volume_pump/atmosinit() + ..() + + set_frequency(frequency) + +/obj/machinery/atmospherics/components/binary/volume_pump/receive_signal(datum/signal/signal) + if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) + return 0 + + var/old_on = on //for logging + + 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) + var/datum/gas_mixture/air1 = AIR1 + transfer_rate = Clamp(text2num(signal.data["set_transfer_rate"]),0,air1.volume) + + if(on != old_on) + investigate_log("was turned [on ? "on" : "off"] by a remote signal", "atmos") + + if("status" in signal.data) + spawn(2) + broadcast_status() + return //do not update_icon + + spawn(2) + broadcast_status() + update_icon() + return + + +/obj/machinery/atmospherics/components/binary/volume_pump/attack_hand(mob/user) + if(..() || !user) + return + interact(user) + +/obj/machinery/atmospherics/components/binary/volume_pump/ui_act(action, params) + if(..()) + return + + switch(action) + if("power") + on = !on + investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") + if("transfer") + switch(params) + if ("max") + transfer_rate = MAX_TRANSFER_RATE + if ("custom") + transfer_rate = max(0, min(MAX_TRANSFER_RATE, safe_input("Pressure control", "Enter new transfer rate (0-[MAX_TRANSFER_RATE] L/s)", transfer_rate))) + investigate_log("was set to [transfer_rate] L/s by [key_name(usr)]", "atmos") + update_icon() + return 1 + +/obj/machinery/atmospherics/components/binary/volume_pump/power_change() + ..() + update_icon() + +/obj/machinery/atmospherics/components/binary/volume_pump/attackby(obj/item/weapon/W, mob/user, params) + if (!istype(W, /obj/item/weapon/wrench)) + return ..() + if (!(stat & NOPOWER) && on) + user << "You cannot unwrench this [src], turn it off first!" + return 1 + return ..() diff --git a/code/ATMOSPHERICS/components/trinary_devices/filter.dm b/code/ATMOSPHERICS/components/trinary_devices/filter.dm index b4498f7af6b..d1c0dafc97b 100644 --- a/code/ATMOSPHERICS/components/trinary_devices/filter.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/filter.dm @@ -1,214 +1,214 @@ -#define FILTER_NOTHING -1 -#define FILTER_PLASMA 0 -#define FILTER_OXYGEN 1 -#define FILTER_NITROGEN 2 -#define FILTER_CARBONDIOXIDE 3 -#define FILTER_NITROUSOXIDE 4 - -/obj/machinery/atmospherics/components/trinary/filter - icon_state = "filter_off" - density = 0 - - name = "gas filter" - - can_unwrench = 1 - - var/on = 0 - - var/target_pressure = ONE_ATMOSPHERE - - 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 - -/obj/machinery/atmospherics/components/trinary/filter/flipped - icon_state = "filter_off_f" - flipped = 1 - -/obj/machinery/atmospherics/components/trinary/filter/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = SSradio.add_object(src, frequency, RADIO_ATMOSIA) - -/obj/machinery/atmospherics/components/trinary/filter/Destroy() - if(SSradio) - SSradio.remove_object(src,frequency) - return ..() - -/obj/machinery/atmospherics/components/trinary/filter/update_icon() - overlays.Cut() - for(var/direction in cardinal) - if(direction & initialize_directions) - var/obj/machinery/atmospherics/node = findConnecting(direction) - if(node) - overlays += getpipeimage('icons/obj/atmospherics/components/trinary_devices.dmi', "cap", direction, node.pipe_color) - continue - overlays += getpipeimage('icons/obj/atmospherics/components/trinary_devices.dmi', "cap", direction) - ..() - -/obj/machinery/atmospherics/components/trinary/filter/update_icon_nopipes() - - if(!(stat & NOPOWER) && on && NODE1 && NODE2 && NODE3) - icon_state = "filter_on[flipped?"_f":""]" - return - - icon_state = "filter_off[flipped?"_f":""]" - -/obj/machinery/atmospherics/components/trinary/filter/power_change() - var/old_stat = stat - ..() - if(stat & NOPOWER) - on = 0 - if(old_stat != stat) - update_icon() - -/obj/machinery/atmospherics/components/trinary/filter/process_atmos() - ..() - if(!on) - return 0 - if(!(NODE1 && NODE2 && NODE3)) - return 0 - - var/datum/gas_mixture/air1 = AIR1 - var/datum/gas_mixture/air2 = AIR2 - var/datum/gas_mixture/air3 = AIR3 - - 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_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(FILTER_PLASMA) - 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(FILTER_OXYGEN) - filtered_out.oxygen = removed.oxygen - removed.oxygen = 0 - - if(FILTER_NITROGEN) - filtered_out.nitrogen = removed.nitrogen - removed.nitrogen = 0 - - if(FILTER_CARBONDIOXIDE) - filtered_out.carbon_dioxide = removed.carbon_dioxide - removed.carbon_dioxide = 0 - - if(FILTER_NITROUSOXIDE) - 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) - - update_parents() - - return 1 - -/obj/machinery/atmospherics/components/trinary/filter/atmosinit() - set_frequency(frequency) - return ..() - -/obj/machinery/atmospherics/components/trinary/filter/attack_hand(mob/user) - if(..() | !user) - return - interact(user) - -/obj/machinery/atmospherics/components/trinary/filter/interact(mob/user) - if(stat & (BROKEN|NOPOWER)) - return - if(!src.allowed(usr)) - usr << "Access denied." - return - ui_interact(user) - -/obj/machinery/atmospherics/components/trinary/filter/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 0) - ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open) - if (!ui) - ui = new(user, src, ui_key, "atmos_filter", name, 450, 145) - ui.open() - -/obj/machinery/atmospherics/components/trinary/filter/get_ui_data() - var/data = list() - data["on"] = on - data["set_pressure"] = round(target_pressure) - data["max_pressure"] = round(MAX_OUTPUT_PRESSURE) - data["filter_type"] = filter_type - return data - -/obj/machinery/atmospherics/components/trinary/filter/ui_act(action, params) - if(..()) - return - - switch(action) - if("power") - on=!on - investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") - if("pressure") - switch(params["set"]) - if("max") - target_pressure = MAX_OUTPUT_PRESSURE - if("custom") - target_pressure = max(0, min(MAX_OUTPUT_PRESSURE, safe_input("Pressure control", "Enter new output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", target_pressure))) - investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") - if("filter") - src.filter_type = text2num(params["mode"]) - var/filtering_name = "nothing" - switch(filter_type) - if(FILTER_PLASMA) - filtering_name = "plasma" - if(FILTER_OXYGEN) - filtering_name = "oxygen" - if(FILTER_NITROGEN) - filtering_name = "nitrogen" - if(FILTER_CARBONDIOXIDE) - filtering_name = "carbon dioxide" - if(FILTER_NITROUSOXIDE) - filtering_name = "nitrous oxide" - investigate_log("was set to filter [filtering_name] by [key_name(usr)]", "atmos") - update_icon() - return 1 +#define FILTER_NOTHING -1 +#define FILTER_PLASMA 0 +#define FILTER_OXYGEN 1 +#define FILTER_NITROGEN 2 +#define FILTER_CARBONDIOXIDE 3 +#define FILTER_NITROUSOXIDE 4 + +/obj/machinery/atmospherics/components/trinary/filter + icon_state = "filter_off" + density = 0 + + name = "gas filter" + + can_unwrench = 1 + + var/on = 0 + + var/target_pressure = ONE_ATMOSPHERE + + 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 + +/obj/machinery/atmospherics/components/trinary/filter/flipped + icon_state = "filter_off_f" + flipped = 1 + +/obj/machinery/atmospherics/components/trinary/filter/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = SSradio.add_object(src, frequency, RADIO_ATMOSIA) + +/obj/machinery/atmospherics/components/trinary/filter/Destroy() + if(SSradio) + SSradio.remove_object(src,frequency) + return ..() + +/obj/machinery/atmospherics/components/trinary/filter/update_icon() + overlays.Cut() + for(var/direction in cardinal) + if(direction & initialize_directions) + var/obj/machinery/atmospherics/node = findConnecting(direction) + if(node) + overlays += getpipeimage('icons/obj/atmospherics/components/trinary_devices.dmi', "cap", direction, node.pipe_color) + continue + overlays += getpipeimage('icons/obj/atmospherics/components/trinary_devices.dmi', "cap", direction) + ..() + +/obj/machinery/atmospherics/components/trinary/filter/update_icon_nopipes() + + if(!(stat & NOPOWER) && on && NODE1 && NODE2 && NODE3) + icon_state = "filter_on[flipped?"_f":""]" + return + + icon_state = "filter_off[flipped?"_f":""]" + +/obj/machinery/atmospherics/components/trinary/filter/power_change() + var/old_stat = stat + ..() + if(stat & NOPOWER) + on = 0 + if(old_stat != stat) + update_icon() + +/obj/machinery/atmospherics/components/trinary/filter/process_atmos() + ..() + if(!on) + return 0 + if(!(NODE1 && NODE2 && NODE3)) + return 0 + + var/datum/gas_mixture/air1 = AIR1 + var/datum/gas_mixture/air2 = AIR2 + var/datum/gas_mixture/air3 = AIR3 + + 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_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(FILTER_PLASMA) + 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(FILTER_OXYGEN) + filtered_out.oxygen = removed.oxygen + removed.oxygen = 0 + + if(FILTER_NITROGEN) + filtered_out.nitrogen = removed.nitrogen + removed.nitrogen = 0 + + if(FILTER_CARBONDIOXIDE) + filtered_out.carbon_dioxide = removed.carbon_dioxide + removed.carbon_dioxide = 0 + + if(FILTER_NITROUSOXIDE) + 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) + + update_parents() + + return 1 + +/obj/machinery/atmospherics/components/trinary/filter/atmosinit() + set_frequency(frequency) + return ..() + +/obj/machinery/atmospherics/components/trinary/filter/attack_hand(mob/user) + if(..() | !user) + return + interact(user) + +/obj/machinery/atmospherics/components/trinary/filter/interact(mob/user) + if(stat & (BROKEN|NOPOWER)) + return + if(!src.allowed(usr)) + usr << "Access denied." + return + ui_interact(user) + +/obj/machinery/atmospherics/components/trinary/filter/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 0) + ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open) + if (!ui) + ui = new(user, src, ui_key, "atmos_filter", name, 450, 145) + ui.open() + +/obj/machinery/atmospherics/components/trinary/filter/get_ui_data() + var/data = list() + data["on"] = on + data["set_pressure"] = round(target_pressure) + data["max_pressure"] = round(MAX_OUTPUT_PRESSURE) + data["filter_type"] = filter_type + return data + +/obj/machinery/atmospherics/components/trinary/filter/ui_act(action, params) + if(..()) + return + + switch(action) + if("power") + on=!on + investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") + if("pressure") + switch(params["set"]) + if("max") + target_pressure = MAX_OUTPUT_PRESSURE + if("custom") + target_pressure = max(0, min(MAX_OUTPUT_PRESSURE, safe_input("Pressure control", "Enter new output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", target_pressure))) + investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") + if("filter") + src.filter_type = text2num(params["mode"]) + var/filtering_name = "nothing" + switch(filter_type) + if(FILTER_PLASMA) + filtering_name = "plasma" + if(FILTER_OXYGEN) + filtering_name = "oxygen" + if(FILTER_NITROGEN) + filtering_name = "nitrogen" + if(FILTER_CARBONDIOXIDE) + filtering_name = "carbon dioxide" + if(FILTER_NITROUSOXIDE) + filtering_name = "nitrous oxide" + investigate_log("was set to filter [filtering_name] by [key_name(usr)]", "atmos") + update_icon() + return 1 diff --git a/code/ATMOSPHERICS/components/trinary_devices/mixer.dm b/code/ATMOSPHERICS/components/trinary_devices/mixer.dm index bfe5ff7fc4b..fe603aac976 100644 --- a/code/ATMOSPHERICS/components/trinary_devices/mixer.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/mixer.dm @@ -1,173 +1,173 @@ -/obj/machinery/atmospherics/components/trinary/mixer - icon_state = "mixer_off" - density = 0 - - name = "gas mixer" - can_unwrench = 1 - - var/on = 0 - - var/target_pressure = ONE_ATMOSPHERE - var/node1_concentration = 0.5 - var/node2_concentration = 0.5 - - //node 3 is the outlet, nodes 1 & 2 are intakes - -/obj/machinery/atmospherics/components/trinary/mixer/flipped - icon_state = "mixer_off_f" - flipped = 1 - -/obj/machinery/atmospherics/components/trinary/mixer/update_icon() - overlays.Cut() - for(var/direction in cardinal) - if(direction & initialize_directions) - var/obj/machinery/atmospherics/node = findConnecting(direction) - if(node) - overlays += getpipeimage('icons/obj/atmospherics/components/trinary_devices.dmi', "cap", direction, node.pipe_color) - continue - overlays += getpipeimage('icons/obj/atmospherics/components/trinary_devices.dmi', "cap", direction) - return ..() - -/obj/machinery/atmospherics/components/trinary/mixer/update_icon_nopipes() - if(!(stat & NOPOWER) && on && NODE1 && NODE2 && NODE3) - icon_state = "mixer_on[flipped?"_f":""]" - return - - icon_state = "mixer_off[flipped?"_f":""]" - -/obj/machinery/atmospherics/components/trinary/mixer/power_change() - var/old_stat = stat - ..() - if(stat & NOPOWER) - on = 0 - if(old_stat != stat) - update_icon() - -/obj/machinery/atmospherics/components/trinary/mixer/New() - ..() - var/datum/gas_mixture/air3 = AIR3 - air3.volume = 300 - AIR3 = air3 - -/obj/machinery/atmospherics/components/trinary/mixer/process_atmos() - ..() - if(!on) - return 0 - if(!(NODE1 && NODE2 && NODE3)) - return 0 - - var/datum/gas_mixture/air1 = AIR1 - var/datum/gas_mixture/air2 = AIR2 - var/datum/gas_mixture/air3 = AIR3 - - 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)) - var/ratio = 0 - if (( transfer_moles1 > 0 ) && (transfer_moles2 >0 )) - ratio = min(air1_moles/transfer_moles1, air2_moles/transfer_moles2) - if (( transfer_moles2 == 0 ) && ( transfer_moles1 > 0 )) - ratio = air1_moles/transfer_moles1 - if (( transfer_moles1 == 0 ) && ( transfer_moles2 > 0 )) - ratio = 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(transfer_moles1) - var/datum/pipeline/parent1 = PARENT1 - parent1.update = 1 - - if(transfer_moles2) - var/datum/pipeline/parent2 = PARENT2 - parent2.update = 1 - - var/datum/pipeline/parent3 = PARENT3 - parent3.update = 1 - - return 1 - -/obj/machinery/atmospherics/components/trinary/mixer/attack_hand(mob/user) - if(..() | !user) - return - interact(user) - -/obj/machinery/atmospherics/components/trinary/mixer/interact(mob/user) - if(stat & (BROKEN|NOPOWER)) - return - if(!src.allowed(usr)) - usr << "Access denied." - return - ui_interact(user) - -/obj/machinery/atmospherics/components/trinary/mixer/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 0) - ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open) - if (!ui) - ui = new(user, src, ui_key, "atmos_mixer", name, 450, 175) - ui.open() - -/obj/machinery/atmospherics/components/trinary/mixer/get_ui_data() - var/data = list() - data["on"] = on - data["set_pressure"] = round(target_pressure) - data["max_pressure"] = round(MAX_OUTPUT_PRESSURE) - data["node1_concentration"] = round(node1_concentration*100) - data["node2_concentration"] = round(node2_concentration*100) - return data - -/obj/machinery/atmospherics/components/trinary/mixer/ui_act(action, params) - if(..()) - return - - switch(action) - if("power") - on = !on - investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") - if("pressure") - switch(params["set"]) - if("max") - target_pressure = MAX_OUTPUT_PRESSURE - if("custom") - target_pressure = max(0, min(MAX_OUTPUT_PRESSURE, safe_input("Pressure control", "Enter new output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", target_pressure))) - investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") - if("node1") - var/value = text2num(params["concentration"]) - src.node1_concentration = max(0, min(1, src.node1_concentration + value)) - src.node2_concentration = max(0, min(1, src.node2_concentration - value)) - investigate_log("was set to [node1_concentration] % on node 1 by [key_name(usr)]", "atmos") - if("node2") - var/value = text2num(params["concentration"]) - src.node2_concentration = max(0, min(1, src.node2_concentration + value)) - src.node1_concentration = max(0, min(1, src.node1_concentration - value)) - investigate_log("was set to [node2_concentration] % on node 2 by [key_name(usr)]", "atmos") - update_icon() +/obj/machinery/atmospherics/components/trinary/mixer + icon_state = "mixer_off" + density = 0 + + name = "gas mixer" + can_unwrench = 1 + + var/on = 0 + + var/target_pressure = ONE_ATMOSPHERE + var/node1_concentration = 0.5 + var/node2_concentration = 0.5 + + //node 3 is the outlet, nodes 1 & 2 are intakes + +/obj/machinery/atmospherics/components/trinary/mixer/flipped + icon_state = "mixer_off_f" + flipped = 1 + +/obj/machinery/atmospherics/components/trinary/mixer/update_icon() + overlays.Cut() + for(var/direction in cardinal) + if(direction & initialize_directions) + var/obj/machinery/atmospherics/node = findConnecting(direction) + if(node) + overlays += getpipeimage('icons/obj/atmospherics/components/trinary_devices.dmi', "cap", direction, node.pipe_color) + continue + overlays += getpipeimage('icons/obj/atmospherics/components/trinary_devices.dmi', "cap", direction) + return ..() + +/obj/machinery/atmospherics/components/trinary/mixer/update_icon_nopipes() + if(!(stat & NOPOWER) && on && NODE1 && NODE2 && NODE3) + icon_state = "mixer_on[flipped?"_f":""]" + return + + icon_state = "mixer_off[flipped?"_f":""]" + +/obj/machinery/atmospherics/components/trinary/mixer/power_change() + var/old_stat = stat + ..() + if(stat & NOPOWER) + on = 0 + if(old_stat != stat) + update_icon() + +/obj/machinery/atmospherics/components/trinary/mixer/New() + ..() + var/datum/gas_mixture/air3 = AIR3 + air3.volume = 300 + AIR3 = air3 + +/obj/machinery/atmospherics/components/trinary/mixer/process_atmos() + ..() + if(!on) + return 0 + if(!(NODE1 && NODE2 && NODE3)) + return 0 + + var/datum/gas_mixture/air1 = AIR1 + var/datum/gas_mixture/air2 = AIR2 + var/datum/gas_mixture/air3 = AIR3 + + 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)) + var/ratio = 0 + if (( transfer_moles1 > 0 ) && (transfer_moles2 >0 )) + ratio = min(air1_moles/transfer_moles1, air2_moles/transfer_moles2) + if (( transfer_moles2 == 0 ) && ( transfer_moles1 > 0 )) + ratio = air1_moles/transfer_moles1 + if (( transfer_moles1 == 0 ) && ( transfer_moles2 > 0 )) + ratio = 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(transfer_moles1) + var/datum/pipeline/parent1 = PARENT1 + parent1.update = 1 + + if(transfer_moles2) + var/datum/pipeline/parent2 = PARENT2 + parent2.update = 1 + + var/datum/pipeline/parent3 = PARENT3 + parent3.update = 1 + + return 1 + +/obj/machinery/atmospherics/components/trinary/mixer/attack_hand(mob/user) + if(..() | !user) + return + interact(user) + +/obj/machinery/atmospherics/components/trinary/mixer/interact(mob/user) + if(stat & (BROKEN|NOPOWER)) + return + if(!src.allowed(usr)) + usr << "Access denied." + return + ui_interact(user) + +/obj/machinery/atmospherics/components/trinary/mixer/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 0) + ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open) + if (!ui) + ui = new(user, src, ui_key, "atmos_mixer", name, 450, 175) + ui.open() + +/obj/machinery/atmospherics/components/trinary/mixer/get_ui_data() + var/data = list() + data["on"] = on + data["set_pressure"] = round(target_pressure) + data["max_pressure"] = round(MAX_OUTPUT_PRESSURE) + data["node1_concentration"] = round(node1_concentration*100) + data["node2_concentration"] = round(node2_concentration*100) + return data + +/obj/machinery/atmospherics/components/trinary/mixer/ui_act(action, params) + if(..()) + return + + switch(action) + if("power") + on = !on + investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") + if("pressure") + switch(params["set"]) + if("max") + target_pressure = MAX_OUTPUT_PRESSURE + if("custom") + target_pressure = max(0, min(MAX_OUTPUT_PRESSURE, safe_input("Pressure control", "Enter new output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", target_pressure))) + investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") + if("node1") + var/value = text2num(params["concentration"]) + src.node1_concentration = max(0, min(1, src.node1_concentration + value)) + src.node2_concentration = max(0, min(1, src.node2_concentration - value)) + investigate_log("was set to [node1_concentration] % on node 1 by [key_name(usr)]", "atmos") + if("node2") + var/value = text2num(params["concentration"]) + src.node2_concentration = max(0, min(1, src.node2_concentration + value)) + src.node1_concentration = max(0, min(1, src.node1_concentration - value)) + investigate_log("was set to [node2_concentration] % on node 2 by [key_name(usr)]", "atmos") + update_icon() return 1 \ No newline at end of file diff --git a/code/ATMOSPHERICS/components/unary_devices/cryo.dm b/code/ATMOSPHERICS/components/unary_devices/cryo.dm index 8931b19ff33..57785704c36 100644 --- a/code/ATMOSPHERICS/components/unary_devices/cryo.dm +++ b/code/ATMOSPHERICS/components/unary_devices/cryo.dm @@ -1,312 +1,312 @@ -/obj/machinery/atmospherics/components/unary/cryo_cell - name = "cryo cell" - icon = 'icons/obj/cryogenics.dmi' - icon_state = "cell-off" - density = 1 - anchored = 1 - layer = 4 - - var/on = 0 - var/temperature_archived - var/obj/item/weapon/reagent_containers/glass/beaker = null - var/next_trans = 0 - var/current_heat_capacity = 50 - state_open = 0 - var/efficiency = 1 - var/autoEject = 0 - -/obj/machinery/atmospherics/components/unary/cryo_cell/New() - ..() - initialize_directions = dir - component_parts = list() - component_parts += new /obj/item/weapon/circuitboard/cryo_tube(null) - component_parts += new /obj/item/weapon/stock_parts/matter_bin(null) - component_parts += new /obj/item/weapon/stock_parts/console_screen(null) - component_parts += new /obj/item/weapon/stock_parts/console_screen(null) - component_parts += new /obj/item/weapon/stock_parts/console_screen(null) - component_parts += new /obj/item/weapon/stock_parts/console_screen(null) - component_parts += new /obj/item/stack/cable_coil(null, 1) - - -/obj/machinery/atmospherics/components/unary/cryo_cell/construction() - ..(dir,dir) - -/obj/machinery/atmospherics/components/unary/cryo_cell/RefreshParts() - var/C - for(var/obj/item/weapon/stock_parts/matter_bin/M in component_parts) - C += M.rating - current_heat_capacity = 50 * C - efficiency = C - -/obj/machinery/atmospherics/components/unary/cryo_cell/Destroy() - var/turf/T = loc - T.contents += contents - - if(beaker) - beaker.loc = get_step(loc, SOUTH) // Beaker is carefully ejected from the wreckage of the cryotube. - beaker = null - return ..() - -/obj/machinery/atmospherics/components/unary/cryo_cell/process_atmos() - ..() - var/datum/gas_mixture/air_contents = AIR1 - - if(air_contents) - temperature_archived = air_contents.temperature - heat_gas_contents() - if(abs(temperature_archived-air_contents.temperature) > 1) - update_parents() - -/obj/machinery/atmospherics/components/unary/cryo_cell/process() - ..() - if(occupant && occupant.health >= 100) - on = 0 - playsound(src.loc, 'sound/machines/ding.ogg', 50, 1) - if(autoEject) - open_machine() - - if(!NODE1 || !is_operational()) - return - if(AIR1) - if(on && occupant) - process_occupant() - expel_gas() - - return 1 - -/obj/machinery/atmospherics/components/unary/cryo_cell/MouseDrop_T(mob/target, mob/user) - if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user) || !iscarbon(target)) - return - close_machine(target) - -/obj/machinery/atmospherics/components/unary/cryo_cell/relaymove(mob/user) - return - -/obj/machinery/atmospherics/components/unary/cryo_cell/container_resist() - usr << "You struggle inside the cryotube, kicking the release with your foot." - sleep(150) - if(!src || !usr || (!occupant && !contents.Find(usr))) // Make sure they didn't disappear. - return - open_machine() - -/obj/machinery/atmospherics/components/unary/cryo_cell/examine(mob/user) - ..() - - var/list/otherstuff = contents - beaker - if(otherstuff.len > 0) - user << "You can just about make out some loose objects floating in the murk:" - for(var/atom/movable/floater in otherstuff) - user << "\icon[floater] [floater.name]" - else - user << "Seems empty." - -/obj/machinery/atmospherics/components/unary/cryo_cell/attack_hand(mob/user) - if(..() | !user) - return - interact(user) - -/obj/machinery/atmospherics/components/unary/cryo_cell/interact(mob/user) - if(panel_open) - return - ui_interact(user) - -/obj/machinery/atmospherics/components/unary/cryo_cell/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 0) - ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open) - if (!ui) - ui = new(user, src, ui_key, "cryo", name, 520, 600, state = notcontained_state) - ui.open() - -/obj/machinery/atmospherics/components/unary/cryo_cell/get_ui_data() - // this is the data which will be sent to the ui - var/datum/gas_mixture/air_contents = AIR1 - - var/data = list() - data["isOperating"] = on - data["hasOccupant"] = occupant ? 1 : 0 - data["autoEject"] = autoEject - - var/occupantData = list() - if (!occupant) - occupantData["name"] = null - occupantData["stat"] = null - occupantData["health"] = null - occupantData["maxHealth"] = null - occupantData["minHealth"] = null - occupantData["bruteLoss"] = null - occupantData["oxyLoss"] = null - occupantData["toxLoss"] = null - occupantData["fireLoss"] = null - occupantData["bodyTemperature"] = null - else - occupantData["name"] = occupant.name - occupantData["stat"] = occupant.stat - occupantData["health"] = occupant.health - occupantData["maxHealth"] = occupant.maxHealth - occupantData["minHealth"] = config.health_threshold_dead - occupantData["bruteLoss"] = occupant.getBruteLoss() - occupantData["oxyLoss"] = occupant.getOxyLoss() - occupantData["toxLoss"] = occupant.getToxLoss() - occupantData["fireLoss"] = occupant.getFireLoss() - occupantData["bodyTemperature"] = occupant.bodytemperature - data["occupant"] = occupantData - - data["isOpen"] = state_open - data["cellTemperature"] = round(air_contents.temperature) - data["cellTemperatureStatus"] = "good" - if(air_contents.temperature > T0C) // if greater than 273.15 kelvin (0 celcius) - data["cellTemperatureStatus"] = "bad" - else if(air_contents.temperature > 225) - data["cellTemperatureStatus"] = "average" - - data["isBeakerLoaded"] = beaker ? 1 : 0 - var beakerContents[0] - if(beaker && beaker.reagents && beaker.reagents.reagent_list.len) - for(var/datum/reagent/R in beaker.reagents.reagent_list) - beakerContents.Add(list(list("name" = R.name, "volume" = R.volume))) // list in a list because Byond merges the first list... - data["beakerContents"] = beakerContents - return data - -/obj/machinery/atmospherics/components/unary/cryo_cell/ui_act(action, params) - if(..()) - return - - switch(action) - if("open") - open_machine() - if("close") - close_machine() - if("autoeject") - autoEject = !autoEject - if("on") - if(!state_open) - on = 1 - if("off") - on = 0 - if("ejectbeaker") - if(beaker) - beaker.loc = get_step(loc, SOUTH) - beaker = null - update_icon() - return 1 - -/obj/machinery/atmospherics/components/unary/cryo_cell/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/weapon/reagent_containers/glass)) - if(isrobot(user)) - return - if(beaker) - user << "A beaker is already loaded into [src]!" - return - if(!user.drop_item()) - return - - beaker = I - I.loc = src - user.visible_message("[user] places [I] in [src].", \ - "You place [I] in [src].") - - if(!(on || occupant || state_open)) - if(default_deconstruction_screwdriver(user, "cell-o", "cell-off", I)) - return - - if(default_change_direction_wrench(user, I)) - return - - if(exchange_parts(user, I)) - return - - if(default_pry_open(I)) - return - - default_deconstruction_crowbar(I) - -/obj/machinery/atmospherics/components/unary/cryo_cell/open_machine() - if(!state_open && !panel_open) - on = 0 - layer = 3 - if(occupant) - occupant.bodytemperature = Clamp(occupant.bodytemperature, 261, 360) - ..() - if(beaker) - beaker.loc = src - -/obj/machinery/atmospherics/components/unary/cryo_cell/close_machine(mob/living/carbon/M) - if(state_open && !panel_open) - layer = 4 - ..(M) - return occupant - -/obj/machinery/atmospherics/components/unary/cryo_cell/update_icon() - if(panel_open) - icon_state = "cell-o" - return - if(state_open) - icon_state = "cell-open" - return - if(on && is_operational()) - if(occupant) - icon_state = "cell-occupied" - else - icon_state = "cell-on" - else - icon_state = "cell-off" - -/obj/machinery/atmospherics/components/unary/cryo_cell/power_change() - ..() - update_icon() - -/obj/machinery/atmospherics/components/unary/cryo_cell/proc/process_occupant() - var/datum/gas_mixture/air_contents = AIR1 - if(!on) - return - if(air_contents.total_moles() < 10) - return - if(occupant) - if(occupant.stat == 2 || occupant.health >= 100) //Why waste energy on dead or healthy people - occupant.bodytemperature = T0C - return - occupant.bodytemperature += 2*(air_contents.temperature - occupant.bodytemperature) * current_heat_capacity / (current_heat_capacity + air_contents.heat_capacity()) - occupant.bodytemperature = max(occupant.bodytemperature, air_contents.temperature) // this is so ugly i'm sorry for doing it i'll fix it later i promise //TODO: fix someone else's broken promise - duncathan - if(occupant.bodytemperature < T0C) - occupant.sleeping = max(5 / efficiency, (1 / occupant.bodytemperature) * 2000 / efficiency) - occupant.Paralyse(max(5 / efficiency, (1 / occupant.bodytemperature) * 3000 / efficiency)) - if(air_contents.oxygen > 2) - if(occupant.getOxyLoss()) occupant.adjustOxyLoss(-1) - else - occupant.adjustOxyLoss(-1) - // Severe damage should heal waaay slower without proper chemicals... - if(occupant.bodytemperature < 225) - if(occupant.getToxLoss()) - occupant.adjustToxLoss(max(-efficiency, (-20*(efficiency ** 2)) / occupant.getToxLoss())) - var/heal_brute = occupant.getBruteLoss() ? min(efficiency, 20*(efficiency**2) / occupant.getBruteLoss()) : 0 - var/heal_fire = occupant.getFireLoss() ? min(efficiency, 20*(efficiency**2) / occupant.getFireLoss()) : 0 - occupant.heal_organ_damage(heal_brute, heal_fire) - if(beaker && next_trans == 0) - beaker.reagents.trans_to(occupant, 1, 10) - beaker.reagents.reaction(occupant, VAPOR) - next_trans++ - if(next_trans == 10) - next_trans = 0 - - -/obj/machinery/atmospherics/components/unary/cryo_cell/proc/heat_gas_contents() - var/datum/gas_mixture/air_contents = AIR1 - - 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 - if(combined_heat_capacity > 0) - var/combined_energy = T20C * current_heat_capacity + air_heat_capacity * air_contents.temperature - air_contents.temperature = combined_energy/combined_heat_capacity - -/obj/machinery/atmospherics/components/unary/cryo_cell/proc/expel_gas() - var/datum/gas_mixture/air_contents = AIR1 - - if(air_contents.total_moles() < 1) - return - var/datum/gas_mixture/expel_gas = new - var/remove_amount = air_contents.total_moles() / 100 - expel_gas = air_contents.remove(remove_amount) - expel_gas.temperature = T20C //Lets expel hot gas and see if that helps people not die as they are removed - loc.assume_air(expel_gas) +/obj/machinery/atmospherics/components/unary/cryo_cell + name = "cryo cell" + icon = 'icons/obj/cryogenics.dmi' + icon_state = "cell-off" + density = 1 + anchored = 1 + layer = 4 + + var/on = 0 + var/temperature_archived + var/obj/item/weapon/reagent_containers/glass/beaker = null + var/next_trans = 0 + var/current_heat_capacity = 50 + state_open = 0 + var/efficiency = 1 + var/autoEject = 0 + +/obj/machinery/atmospherics/components/unary/cryo_cell/New() + ..() + initialize_directions = dir + component_parts = list() + component_parts += new /obj/item/weapon/circuitboard/cryo_tube(null) + component_parts += new /obj/item/weapon/stock_parts/matter_bin(null) + component_parts += new /obj/item/weapon/stock_parts/console_screen(null) + component_parts += new /obj/item/weapon/stock_parts/console_screen(null) + component_parts += new /obj/item/weapon/stock_parts/console_screen(null) + component_parts += new /obj/item/weapon/stock_parts/console_screen(null) + component_parts += new /obj/item/stack/cable_coil(null, 1) + + +/obj/machinery/atmospherics/components/unary/cryo_cell/construction() + ..(dir,dir) + +/obj/machinery/atmospherics/components/unary/cryo_cell/RefreshParts() + var/C + for(var/obj/item/weapon/stock_parts/matter_bin/M in component_parts) + C += M.rating + current_heat_capacity = 50 * C + efficiency = C + +/obj/machinery/atmospherics/components/unary/cryo_cell/Destroy() + var/turf/T = loc + T.contents += contents + + if(beaker) + beaker.loc = get_step(loc, SOUTH) // Beaker is carefully ejected from the wreckage of the cryotube. + beaker = null + return ..() + +/obj/machinery/atmospherics/components/unary/cryo_cell/process_atmos() + ..() + var/datum/gas_mixture/air_contents = AIR1 + + if(air_contents) + temperature_archived = air_contents.temperature + heat_gas_contents() + if(abs(temperature_archived-air_contents.temperature) > 1) + update_parents() + +/obj/machinery/atmospherics/components/unary/cryo_cell/process() + ..() + if(occupant && occupant.health >= 100) + on = 0 + playsound(src.loc, 'sound/machines/ding.ogg', 50, 1) + if(autoEject) + open_machine() + + if(!NODE1 || !is_operational()) + return + if(AIR1) + if(on && occupant) + process_occupant() + expel_gas() + + return 1 + +/obj/machinery/atmospherics/components/unary/cryo_cell/MouseDrop_T(mob/target, mob/user) + if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user) || !iscarbon(target)) + return + close_machine(target) + +/obj/machinery/atmospherics/components/unary/cryo_cell/relaymove(mob/user) + return + +/obj/machinery/atmospherics/components/unary/cryo_cell/container_resist() + usr << "You struggle inside the cryotube, kicking the release with your foot." + sleep(150) + if(!src || !usr || (!occupant && !contents.Find(usr))) // Make sure they didn't disappear. + return + open_machine() + +/obj/machinery/atmospherics/components/unary/cryo_cell/examine(mob/user) + ..() + + var/list/otherstuff = contents - beaker + if(otherstuff.len > 0) + user << "You can just about make out some loose objects floating in the murk:" + for(var/atom/movable/floater in otherstuff) + user << "\icon[floater] [floater.name]" + else + user << "Seems empty." + +/obj/machinery/atmospherics/components/unary/cryo_cell/attack_hand(mob/user) + if(..() | !user) + return + interact(user) + +/obj/machinery/atmospherics/components/unary/cryo_cell/interact(mob/user) + if(panel_open) + return + ui_interact(user) + +/obj/machinery/atmospherics/components/unary/cryo_cell/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 0) + ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open) + if (!ui) + ui = new(user, src, ui_key, "cryo", name, 520, 600, state = notcontained_state) + ui.open() + +/obj/machinery/atmospherics/components/unary/cryo_cell/get_ui_data() + // this is the data which will be sent to the ui + var/datum/gas_mixture/air_contents = AIR1 + + var/data = list() + data["isOperating"] = on + data["hasOccupant"] = occupant ? 1 : 0 + data["autoEject"] = autoEject + + var/occupantData = list() + if (!occupant) + occupantData["name"] = null + occupantData["stat"] = null + occupantData["health"] = null + occupantData["maxHealth"] = null + occupantData["minHealth"] = null + occupantData["bruteLoss"] = null + occupantData["oxyLoss"] = null + occupantData["toxLoss"] = null + occupantData["fireLoss"] = null + occupantData["bodyTemperature"] = null + else + occupantData["name"] = occupant.name + occupantData["stat"] = occupant.stat + occupantData["health"] = occupant.health + occupantData["maxHealth"] = occupant.maxHealth + occupantData["minHealth"] = config.health_threshold_dead + occupantData["bruteLoss"] = occupant.getBruteLoss() + occupantData["oxyLoss"] = occupant.getOxyLoss() + occupantData["toxLoss"] = occupant.getToxLoss() + occupantData["fireLoss"] = occupant.getFireLoss() + occupantData["bodyTemperature"] = occupant.bodytemperature + data["occupant"] = occupantData + + data["isOpen"] = state_open + data["cellTemperature"] = round(air_contents.temperature) + data["cellTemperatureStatus"] = "good" + if(air_contents.temperature > T0C) // if greater than 273.15 kelvin (0 celcius) + data["cellTemperatureStatus"] = "bad" + else if(air_contents.temperature > 225) + data["cellTemperatureStatus"] = "average" + + data["isBeakerLoaded"] = beaker ? 1 : 0 + var beakerContents[0] + if(beaker && beaker.reagents && beaker.reagents.reagent_list.len) + for(var/datum/reagent/R in beaker.reagents.reagent_list) + beakerContents.Add(list(list("name" = R.name, "volume" = R.volume))) // list in a list because Byond merges the first list... + data["beakerContents"] = beakerContents + return data + +/obj/machinery/atmospherics/components/unary/cryo_cell/ui_act(action, params) + if(..()) + return + + switch(action) + if("open") + open_machine() + if("close") + close_machine() + if("autoeject") + autoEject = !autoEject + if("on") + if(!state_open) + on = 1 + if("off") + on = 0 + if("ejectbeaker") + if(beaker) + beaker.loc = get_step(loc, SOUTH) + beaker = null + update_icon() + return 1 + +/obj/machinery/atmospherics/components/unary/cryo_cell/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/weapon/reagent_containers/glass)) + if(isrobot(user)) + return + if(beaker) + user << "A beaker is already loaded into [src]!" + return + if(!user.drop_item()) + return + + beaker = I + I.loc = src + user.visible_message("[user] places [I] in [src].", \ + "You place [I] in [src].") + + if(!(on || occupant || state_open)) + if(default_deconstruction_screwdriver(user, "cell-o", "cell-off", I)) + return + + if(default_change_direction_wrench(user, I)) + return + + if(exchange_parts(user, I)) + return + + if(default_pry_open(I)) + return + + default_deconstruction_crowbar(I) + +/obj/machinery/atmospherics/components/unary/cryo_cell/open_machine() + if(!state_open && !panel_open) + on = 0 + layer = 3 + if(occupant) + occupant.bodytemperature = Clamp(occupant.bodytemperature, 261, 360) + ..() + if(beaker) + beaker.loc = src + +/obj/machinery/atmospherics/components/unary/cryo_cell/close_machine(mob/living/carbon/M) + if(state_open && !panel_open) + layer = 4 + ..(M) + return occupant + +/obj/machinery/atmospherics/components/unary/cryo_cell/update_icon() + if(panel_open) + icon_state = "cell-o" + return + if(state_open) + icon_state = "cell-open" + return + if(on && is_operational()) + if(occupant) + icon_state = "cell-occupied" + else + icon_state = "cell-on" + else + icon_state = "cell-off" + +/obj/machinery/atmospherics/components/unary/cryo_cell/power_change() + ..() + update_icon() + +/obj/machinery/atmospherics/components/unary/cryo_cell/proc/process_occupant() + var/datum/gas_mixture/air_contents = AIR1 + if(!on) + return + if(air_contents.total_moles() < 10) + return + if(occupant) + if(occupant.stat == 2 || occupant.health >= 100) //Why waste energy on dead or healthy people + occupant.bodytemperature = T0C + return + occupant.bodytemperature += 2*(air_contents.temperature - occupant.bodytemperature) * current_heat_capacity / (current_heat_capacity + air_contents.heat_capacity()) + occupant.bodytemperature = max(occupant.bodytemperature, air_contents.temperature) // this is so ugly i'm sorry for doing it i'll fix it later i promise //TODO: fix someone else's broken promise - duncathan + if(occupant.bodytemperature < T0C) + occupant.sleeping = max(5 / efficiency, (1 / occupant.bodytemperature) * 2000 / efficiency) + occupant.Paralyse(max(5 / efficiency, (1 / occupant.bodytemperature) * 3000 / efficiency)) + if(air_contents.oxygen > 2) + if(occupant.getOxyLoss()) occupant.adjustOxyLoss(-1) + else + occupant.adjustOxyLoss(-1) + // Severe damage should heal waaay slower without proper chemicals... + if(occupant.bodytemperature < 225) + if(occupant.getToxLoss()) + occupant.adjustToxLoss(max(-efficiency, (-20*(efficiency ** 2)) / occupant.getToxLoss())) + var/heal_brute = occupant.getBruteLoss() ? min(efficiency, 20*(efficiency**2) / occupant.getBruteLoss()) : 0 + var/heal_fire = occupant.getFireLoss() ? min(efficiency, 20*(efficiency**2) / occupant.getFireLoss()) : 0 + occupant.heal_organ_damage(heal_brute, heal_fire) + if(beaker && next_trans == 0) + beaker.reagents.trans_to(occupant, 1, 10) + beaker.reagents.reaction(occupant, VAPOR) + next_trans++ + if(next_trans == 10) + next_trans = 0 + + +/obj/machinery/atmospherics/components/unary/cryo_cell/proc/heat_gas_contents() + var/datum/gas_mixture/air_contents = AIR1 + + 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 + if(combined_heat_capacity > 0) + var/combined_energy = T20C * current_heat_capacity + air_heat_capacity * air_contents.temperature + air_contents.temperature = combined_energy/combined_heat_capacity + +/obj/machinery/atmospherics/components/unary/cryo_cell/proc/expel_gas() + var/datum/gas_mixture/air_contents = AIR1 + + if(air_contents.total_moles() < 1) + return + var/datum/gas_mixture/expel_gas = new + var/remove_amount = air_contents.total_moles() / 100 + expel_gas = air_contents.remove(remove_amount) + expel_gas.temperature = T20C //Lets expel hot gas and see if that helps people not die as they are removed + loc.assume_air(expel_gas) air_update_turf() \ No newline at end of file diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index b447152b3a3..bbdab1815e0 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -1,1341 +1,1341 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 - -/* - * A large number of misc global procs. - */ - -//Inverts the colour of an HTML string -/proc/invertHTML(HTMLstring) - - if (!( istext(HTMLstring) )) - CRASH("Given non-text argument!") - return - else - if (length(HTMLstring) != 7) - CRASH("Given non-HTML argument!") - return - var/textr = copytext(HTMLstring, 2, 4) - var/textg = copytext(HTMLstring, 4, 6) - var/textb = copytext(HTMLstring, 6, 8) - var/r = hex2num(textr) - var/g = hex2num(textg) - var/b = hex2num(textb) - textr = num2hex(255 - r, 2) - textg = num2hex(255 - g, 2) - textb = num2hex(255 - b, 2) - return text("#[][][]", textr, textg, textb) - return - -//Returns the middle-most value -/proc/dd_range(low, high, num) - return max(low,min(high,num)) - - -/proc/Get_Angle(atom/movable/start,atom/movable/end)//For beams. - if(!start || !end) return 0 - var/dy - var/dx - dy=(32*end.y+end.pixel_y)-(32*start.y+start.pixel_y) - dx=(32*end.x+end.pixel_x)-(32*start.x+start.pixel_x) - if(!dy) - return (dx>=0)?90:270 - .=arctan(dx/dy) - if(dy<0) - .+=180 - else if(dx<0) - .+=360 - -//Returns location. Returns null if no location was found. -/proc/get_teleport_loc(turf/location,mob/target,distance = 1, density = 0, errorx = 0, errory = 0, eoffsetx = 0, eoffsety = 0) -/* -Location where the teleport begins, target that will teleport, distance to go, density checking 0/1(yes/no). -Random error in tile placement x, error in tile placement y, and block offset. -Block offset tells the proc how to place the box. Behind teleport location, relative to starting location, forward, etc. -Negative values for offset are accepted, think of it in relation to North, -x is west, -y is south. Error defaults to positive. -Turf and target are seperate in case you want to teleport some distance from a turf the target is not standing on or something. -*/ - - var/dirx = 0//Generic location finding variable. - var/diry = 0 - - var/xoffset = 0//Generic counter for offset location. - var/yoffset = 0 - - var/b1xerror = 0//Generic placing for point A in box. The lower left. - var/b1yerror = 0 - var/b2xerror = 0//Generic placing for point B in box. The upper right. - var/b2yerror = 0 - - errorx = abs(errorx)//Error should never be negative. - errory = abs(errory) - //var/errorxy = round((errorx+errory)/2)//Used for diagonal boxes. - - switch(target.dir)//This can be done through equations but switch is the simpler method. And works fast to boot. - //Directs on what values need modifying. - if(1)//North - diry+=distance - yoffset+=eoffsety - xoffset+=eoffsetx - b1xerror-=errorx - b1yerror-=errory - b2xerror+=errorx - b2yerror+=errory - if(2)//South - diry-=distance - yoffset-=eoffsety - xoffset+=eoffsetx - b1xerror-=errorx - b1yerror-=errory - b2xerror+=errorx - b2yerror+=errory - if(4)//East - dirx+=distance - yoffset+=eoffsetx//Flipped. - xoffset+=eoffsety - b1xerror-=errory//Flipped. - b1yerror-=errorx - b2xerror+=errory - b2yerror+=errorx - if(8)//West - dirx-=distance - yoffset-=eoffsetx//Flipped. - xoffset+=eoffsety - b1xerror-=errory//Flipped. - b1yerror-=errorx - b2xerror+=errory - b2yerror+=errorx - - var/turf/destination=locate(location.x+dirx,location.y+diry,location.z) - - if(destination)//If there is a destination. - if(errorx||errory)//If errorx or y were specified. - var/destination_list[] = list()//To add turfs to list. - //destination_list = new() - /*This will draw a block around the target turf, given what the error is. - Specifying the values above will basically draw a different sort of block. - If the values are the same, it will be a square. If they are different, it will be a rectengle. - In either case, it will center based on offset. Offset is position from center. - Offset always calculates in relation to direction faced. In other words, depending on the direction of the teleport, - the offset should remain positioned in relation to destination.*/ - - var/turf/center = locate((destination.x+xoffset),(destination.y+yoffset),location.z)//So now, find the new center. - - //Now to find a box from center location and make that our destination. - for(var/turf/T in block(locate(center.x+b1xerror,center.y+b1yerror,location.z), locate(center.x+b2xerror,center.y+b2yerror,location.z) )) - if(density&&T.density) continue//If density was specified. - if(T.x>world.maxx || T.x<1) continue//Don't want them to teleport off the map. - if(T.y>world.maxy || T.y<1) continue - destination_list += T - if(destination_list.len) - destination = pick(destination_list) - else return - - else//Same deal here. - if(density&&destination.density) return - if(destination.x>world.maxx || destination.x<1) return - if(destination.y>world.maxy || destination.y<1) return - else return - - return destination - -/proc/getline(atom/M,atom/N)//Ultra-Fast Bresenham Line-Drawing Algorithm - var/px=M.x //starting x - var/py=M.y - var/line[] = list(locate(px,py,M.z)) - var/dx=N.x-px //x distance - var/dy=N.y-py - var/dxabs=abs(dx)//Absolute value of x distance - var/dyabs=abs(dy) - var/sdx=sign(dx) //Sign of x distance (+ or -) - var/sdy=sign(dy) - var/x=dxabs>>1 //Counters for steps taken, setting to distance/2 - var/y=dyabs>>1 //Bit-shifting makes me l33t. It also makes getline() unnessecarrily fast. - var/j //Generic integer for counting - if(dxabs>=dyabs) //x distance is greater than y - for(j=0;j=dxabs) //Every dyabs steps, step once in y direction - y-=dxabs - py+=sdy - px+=sdx //Step on in x direction - line+=locate(px,py,M.z)//Add the turf to the list - else - for(j=0;j=dyabs) - x-=dyabs - px+=sdx - py+=sdy - line+=locate(px,py,M.z) - return line - -//Returns whether or not a player is a guest using their ckey as an input -/proc/IsGuestKey(key) - if (findtext(key, "Guest-", 1, 7) != 1) //was findtextEx - return 0 - - var/i, ch, len = length(key) - - for (i = 7, i <= len, ++i) - ch = text2ascii(key, i) - if (ch < 48 || ch > 57) - return 0 - return 1 - -//Ensure the frequency is within bounds of what it should be sending/recieving at -/proc/sanitize_frequency(f) - f = round(f) - f = max(1441, f) // 144.1 - f = min(1489, f) // 148.9 - if ((f % 2) == 0) //Ensure the last digit is an odd number - f += 1 - return f - -//Turns 1479 into 147.9 -/proc/format_frequency(f) - f = text2num(f) - return "[round(f / 10)].[f % 10]" - - - -//This will update a mob's name, real_name, mind.name, data_core records, pda, id and traitor text -//Calling this proc without an oldname will only update the mob and skip updating the pda, id and records ~Carn -/mob/proc/fully_replace_character_name(oldname,newname) - if(!newname) return 0 - real_name = newname - name = newname - if(mind) - mind.name = newname - if(istype(src, /mob/living/carbon)) - var/mob/living/carbon/C = src - if(C.dna) - C.dna.real_name = real_name - - if(isAI(src)) - var/mob/living/silicon/ai/AI = src - if(oldname != real_name) - if(AI.eyeobj) - AI.eyeobj.name = "[newname] (AI Eye)" - - // Set ai pda name - if(AI.aiPDA) - AI.aiPDA.owner = newname - AI.aiPDA.name = newname + " (" + AI.aiPDA.ownjob + ")" - - // Notify Cyborgs - for(var/mob/living/silicon/robot/Slave in AI.connected_robots) - Slave.show_laws() - - if(isrobot(src)) - var/mob/living/silicon/robot/R = src - if(oldname != real_name) - R.notify_ai(3, oldname, newname) - if(R.camera) - R.camera.c_tag = real_name - - if(oldname) - //update the datacore records! This is goig to be a bit costly. - for(var/list/L in list(data_core.general,data_core.medical,data_core.security,data_core.locked)) - var/datum/data/record/R = find_record("name", oldname, L) - if(R) R.fields["name"] = newname - - //update our pda and id if we have them on our person - var/list/searching = GetAllContents() - var/search_id = 1 - var/search_pda = 1 - - for(var/A in searching) - if( search_id && istype(A,/obj/item/weapon/card/id) ) - var/obj/item/weapon/card/id/ID = A - if(ID.registered_name == oldname) - ID.registered_name = newname - ID.update_label() - if(!search_pda) break - search_id = 0 - - else if( search_pda && istype(A,/obj/item/device/pda) ) - var/obj/item/device/pda/PDA = A - if(PDA.owner == oldname) - PDA.owner = newname - PDA.update_label() - if(!search_id) break - search_pda = 0 - - for(var/datum/mind/T in ticker.minds) - for(var/datum/objective/obj in T.objectives) - // Only update if this player is a target - if(obj.target && obj.target.current && obj.target.current.real_name == name) - obj.update_explanation_text() - - return 1 - - - -//Generalised helper proc for letting mobs rename themselves. Used to be clname() and ainame() - -/mob/proc/rename_self(role, allow_numbers=0) - var/oldname = real_name - var/newname - var/loop = 1 - var/safety = 0 - - while(loop && safety < 5) - if(client && client.prefs.custom_names[role] && !safety) - newname = client.prefs.custom_names[role] - else - switch(role) - if("clown") - newname = pick(clown_names) - if("mime") - newname = pick(mime_names) - if("ai") - newname = pick(ai_names) - if("deity") - newname = pick(clown_names|ai_names|mime_names) //pick any old name - else - return - - for(var/mob/living/M in player_list) - if(M == src) - continue - if(!newname || M.real_name == newname) - newname = null - loop++ // name is already taken so we roll again - break - loop-- - safety++ - - if(isAI(src)) - oldname = null//don't bother with the records update crap - if(newname) - fully_replace_character_name(oldname,newname) - if(isrobot(src)) - var/mob/living/silicon/robot/A = src - A.custom_name = newname - - -//Picks a string of symbols to display as the law number for hacked or ion laws -/proc/ionnum() - return "[pick("!","@","#","$","%","^","&")][pick("!","@","#","$","%","^","&","*")][pick("!","@","#","$","%","^","&","*")][pick("!","@","#","$","%","^","&","*")]" - -//Returns a list of unslaved cyborgs -/proc/active_free_borgs() - . = list() - for(var/mob/living/silicon/robot/R in living_mob_list) - if(R.connected_ai) - continue - if(R.stat == DEAD) - continue - if(R.emagged || R.scrambledcodes || R.syndicate) - continue - . += R - -//Returns a list of AI's -/proc/active_ais(check_mind=0) - . = list() - for(var/mob/living/silicon/ai/A in living_mob_list) - if(A.stat == DEAD) - continue - if(A.control_disabled == 1) - continue - if(check_mind) - if(!A.mind) - continue - . += A - return . - -//Find an active ai with the least borgs. VERBOSE PROCNAME HUH! -/proc/select_active_ai_with_fewest_borgs() - var/mob/living/silicon/ai/selected - var/list/active = active_ais() - for(var/mob/living/silicon/ai/A in active) - if(!selected || (selected.connected_robots.len > A.connected_robots.len)) - selected = A - - return selected - -/proc/select_active_free_borg(mob/user) - var/list/borgs = active_free_borgs() - if(borgs.len) - if(user) . = input(user,"Unshackled cyborg signals detected:", "Cyborg Selection", borgs[1]) in borgs - else . = pick(borgs) - return . - -/proc/select_active_ai(mob/user) - var/list/ais = active_ais() - if(ais.len) - if(user) . = input(user,"AI signals detected:", "AI Selection", ais[1]) in ais - else . = pick(ais) - return . - -//Returns a list of all items of interest with their name -/proc/getpois(mobs_only=0,skip_mindless=0) - var/list/mobs = sortmobs() - var/list/names = list() - var/list/pois = list() - var/list/namecounts = list() - - for(var/mob/M in mobs) - if(skip_mindless && (!M.mind && !M.ckey)) - if(!isbot(M)) - continue - var/name = M.name - if (name in names) - namecounts[name]++ - name = "[name] ([namecounts[name]])" - else - names.Add(name) - namecounts[name] = 1 - if (M.real_name && M.real_name != M.name) - name += " \[[M.real_name]\]" - if (M.stat == 2) - if(istype(M, /mob/dead/observer/)) - name += " \[ghost\]" - else - name += " \[dead\]" - pois[name] = M - - if(!mobs_only) - for(var/atom/A in poi_list) - if(!A || !A.loc) - continue - var/name = A.name - if (names.Find(name)) - namecounts[name]++ - name = "[name] ([namecounts[name]])" - else - names.Add(name) - namecounts[name] = 1 - pois[name] = A - - return pois -//Orders mobs by type then by name -/proc/sortmobs() - var/list/moblist = list() - var/list/sortmob = sortNames(mob_list) - for(var/mob/living/silicon/ai/M in sortmob) - moblist.Add(M) - for(var/mob/camera/M in sortmob) - moblist.Add(M) - for(var/mob/living/silicon/pai/M in sortmob) - moblist.Add(M) - for(var/mob/living/silicon/robot/M in sortmob) - moblist.Add(M) - for(var/mob/living/carbon/human/M in sortmob) - moblist.Add(M) - for(var/mob/living/carbon/brain/M in sortmob) - moblist.Add(M) - for(var/mob/living/carbon/alien/M in sortmob) - moblist.Add(M) - for(var/mob/dead/observer/M in sortmob) - moblist.Add(M) - for(var/mob/new_player/M in sortmob) - moblist.Add(M) - for(var/mob/living/carbon/monkey/M in sortmob) - moblist.Add(M) - for(var/mob/living/simple_animal/slime/M in sortmob) - moblist.Add(M) - for(var/mob/living/simple_animal/M in sortmob) - moblist.Add(M) -// for(var/mob/living/silicon/hivebot/M in world) -// mob_list.Add(M) -// for(var/mob/living/silicon/hive_mainframe/M in world) -// mob_list.Add(M) - return moblist - -//E = MC^2 -/proc/convert2energy(M) - var/E = M*(SPEED_OF_LIGHT_SQ) - return E - -//M = E/C^2 -/proc/convert2mass(E) - var/M = E/(SPEED_OF_LIGHT_SQ) - return M - -/proc/key_name(whom, include_link = null, include_name = 1) - var/mob/M - var/client/C - var/key - var/ckey - - if(!whom) return "*null*" - if(istype(whom, /client)) - C = whom - M = C.mob - key = C.key - ckey = C.ckey - else if(ismob(whom)) - M = whom - C = M.client - key = M.key - ckey = M.ckey - else if(istext(whom)) - key = whom - ckey = ckey(whom) - C = directory[ckey] - if(C) - M = C.mob - else - return "*invalid*" - - . = "" - - if(!ckey) - include_link = 0 - - if(key) - if(C && C.holder && C.holder.fakekey && !include_name) - if(include_link) - . += "" - . += "Administrator" - else - if(include_link) - . += "" - . += key - if(!C) - . += "\[DC\]" - - if(include_link) - . += "" - else - . += "*no key*" - - if(include_name && M) - if(M.real_name) - . += "/([M.real_name])" - else if(M.name) - . += "/([M.name])" - - return . - -/proc/key_name_admin(whom, include_name = 1) - return key_name(whom, 1, include_name) - -/proc/get_mob_by_ckey(key) - if(!key) - return - var/list/mobs = sortmobs() - for(var/mob/M in mobs) - if(M.ckey == key) - return M - -// Returns the atom sitting on the turf. -// For example, using this on a disk, which is in a bag, on a mob, will return the mob because it's on the turf. -/proc/get_atom_on_turf(atom/movable/M) - var/atom/loc = M - while(loc && loc.loc && !istype(loc.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(atom/A, direction) - var/turf/target = locate(A.x, A.y, A.z) - if(!A || !target) - return 0 - //since NORTHEAST == NORTH & EAST, etc, doing it this way allows for diagonal mass drivers in the future - //and isn't really any more complicated - - // Note diagonal directions won't usually be accurate - if(direction & NORTH) - target = locate(target.x, world.maxy, target.z) - if(direction & SOUTH) - target = locate(target.x, 1, target.z) - if(direction & EAST) - target = locate(world.maxx, target.y, target.z) - if(direction & WEST) - target = locate(1, target.y, target.z) - return target - -// returns turf relative to A in given direction at set range -// result is bounded to map size -// note range is non-pythagorean -// used for disposal system -/proc/get_ranged_target_turf(atom/A, direction, range) - - var/x = A.x - var/y = A.y - if(direction & NORTH) - y = min(world.maxy, y + range) - if(direction & SOUTH) - y = max(1, y - range) - if(direction & EAST) - x = min(world.maxx, x + range) - if(direction & WEST) - x = max(1, x - range) - - return locate(x,y,A.z) - - -// returns turf relative to A offset in dx and dy tiles -// bound to map limits -/proc/get_offset_target_turf(atom/A, dx, dy) - var/x = min(world.maxx, max(1, A.x + dx)) - var/y = min(world.maxy, max(1, A.y + dy)) - return locate(x,y,A.z) - -/proc/arctan(x) - var/y=arcsin(x/sqrt(1+x*x)) - return y - - -/proc/anim(turf/location,target as mob|obj,a_icon,a_icon_state as text,flick_anim as text,sleeptime = 0,direction as num) -//This proc throws up either an icon or an animation for a specified amount of time. -//The variables should be apparent enough. - var/atom/movable/overlay/animation = new(location) - if(direction) - animation.dir = direction - animation.icon = a_icon - animation.layer = target:layer+1 - if(a_icon_state) - animation.icon_state = a_icon_state - else - animation.icon_state = "blank" - animation.master = target - flick(flick_anim, animation) - sleep(max(sleeptime, 15)) - qdel(animation) - - -/atom/proc/GetAllContents() - var/list/processing_list = list(src) - var/list/assembled = list() - - while(processing_list.len) - var/atom/A = processing_list[1] - processing_list -= A - - for(var/atom/a in A) - if(!(a in assembled)) - processing_list |= a - - assembled |= A - - return assembled - -//Step-towards method of determining whether one atom can see another. Similar to viewers() -/proc/can_see(atom/source, atom/target, length=5) // I couldnt be arsed to do actual raycasting :I This is horribly inaccurate. - var/turf/current = get_turf(source) - var/turf/target_turf = get_turf(target) - var/steps = 0 - - while(current != target_turf) - if(steps > length) return 0 - if(current.opacity) return 0 - for(var/atom/A in current) - if(A.opacity) return 0 - current = get_step_towards(current, target_turf) - steps++ - - return 1 - -/proc/is_blocked_turf(turf/T) - var/cant_pass = 0 - if(T.density) cant_pass = 1 - for(var/atom/A in T) - if(A.density)//&&A.anchored - cant_pass = 1 - return cant_pass - -/proc/get_step_towards2(atom/ref , atom/trg) - var/base_dir = get_dir(ref, get_step_towards(ref,trg)) - var/turf/temp = get_step_towards(ref,trg) - - if(is_blocked_turf(temp)) - var/dir_alt1 = turn(base_dir, 90) - var/dir_alt2 = turn(base_dir, -90) - var/turf/turf_last1 = temp - var/turf/turf_last2 = temp - var/free_tile = null - var/breakpoint = 0 - - while(!free_tile && breakpoint < 10) - if(!is_blocked_turf(turf_last1)) - free_tile = turf_last1 - break - if(!is_blocked_turf(turf_last2)) - free_tile = turf_last2 - break - turf_last1 = get_step(turf_last1,dir_alt1) - turf_last2 = get_step(turf_last2,dir_alt2) - breakpoint++ - - if(!free_tile) return get_step(ref, base_dir) - else return get_step_towards(ref,free_tile) - - else return get_step(ref, base_dir) - -/proc/do_mob(mob/user , mob/target, time = 30, numticks = 5, uninterruptible = 0, progress = 1) - //This is quite an ugly solution but i refuse to use the old request system. - if(!user || !target) - return 0 - if(numticks == 0) - return 0 - var/user_loc = user.loc - var/target_loc = target.loc - var/holding = user.get_active_hand() - var/timefraction = round(time/numticks) - var/image/progbar - var/continue_looping = 1 - for(var/i = 1 to numticks) - if(user.client && progress) - progbar = make_progress_bar(i, numticks, target) - assign_progress_bar(user, progbar) - sleep(timefraction) - if(!user || !target) - continue_looping = 0 - - if (continue_looping && !uninterruptible && (user.loc != user_loc || target.loc != target_loc || user.get_active_hand() != holding || user.incapacitated() || user.lying )) - continue_looping = 0 - - cancel_progress_bar(user, progbar)//Clear the way for the next progbar image - if(!continue_looping) - return 0 - - cancel_progress_bar(user, progbar) - return 1 - -/proc/make_progress_bar(current_number, goal_number, atom/target) - if(current_number && goal_number && target) - var/image/progbar - progbar = image("icon" = 'icons/effects/doafter_icon.dmi', "loc" = target, "icon_state" = "prog_bar_0") - progbar.icon_state = "prog_bar_[round(((current_number / goal_number) * 100), 10)]" - progbar.pixel_y = 32 - return progbar - -/proc/cancel_progress_bar(mob/user, image/progbar) - if(user && user.client && progbar) - user.client.images -= progbar - -/proc/assign_progress_bar(mob/user, image/progbar) - if(user && user.client && progbar) - user.client.images |= progbar - -/proc/do_after(mob/user, delay, numticks = 5, needhand = 1, atom/target = null, progress = 1) - if(!user) - return 0 - - if(numticks == 0) - return 0 - - var/atom/Tloc = null - if(target) - Tloc = target.loc - - var/delayfraction = round(delay/numticks) - - var/atom/Uloc = user.loc - - var/holding = user.get_active_hand() - var/holdingnull = 1 //User is not holding anything - if(holding) - holdingnull = 0 //User is holding a tool of some kind - - var/image/progbar - - var/continue_looping = 1 - for (var/i = 1 to numticks) - if(user.client && progress) - progbar = make_progress_bar(i, numticks, target) - assign_progress_bar(user, progbar) - - sleep(delayfraction) - if(!user || user.stat || user.weakened || user.stunned || !(user.loc == Uloc)) - continue_looping = 0 - - if(continue_looping && Tloc && (!target || Tloc != target.loc)) //Tloc not set when we don't want to track target - continue_looping = 0 - - if(continue_looping && needhand) - //This might seem like an odd check, but you can still need a hand even when it's empty - //i.e the hand is used to insert some item/tool into the construction - if(!holdingnull) - if(!holding) - continue_looping = 0 - if(continue_looping && user.get_active_hand() != holding) - continue_looping = 0 - - cancel_progress_bar(user, progbar)//Clear the way for the next progbar image - if(!continue_looping) - return 0 - - cancel_progress_bar(user,progbar) - return 1 - -//Takes: Anything that could possibly have variables and a varname to check. -//Returns: 1 if found, 0 if not. -/proc/hasvar(datum/A, varname) - if(A.vars.Find(lowertext(varname))) return 1 - else return 0 - -//Returns sortedAreas list if populated -//else populates the list first before returning it -/proc/SortAreas() - for(var/area/A in world) - sortedAreas.Add(A) - - sortTim(sortedAreas, /proc/cmp_name_asc) - -/area/proc/addSorted() - sortedAreas.Add(src) - sortTim(sortedAreas, /proc/cmp_name_asc) - -//Takes: Area type as text string or as typepath OR an instance of the area. -//Returns: A list of all areas of that type in the world. -/proc/get_areas(areatype) - if(!areatype) return null - if(istext(areatype)) areatype = text2path(areatype) - if(isarea(areatype)) - var/area/areatemp = areatype - areatype = areatemp.type - - var/list/areas = new/list() - for(var/area/N in world) - if(istype(N, areatype)) areas += N - return areas - -//Takes: Area type as text string or as typepath OR an instance of the area. -//Returns: A list of all turfs in areas of that type of that type in the world. -/proc/get_area_turfs(areatype) - if(!areatype) return null - if(istext(areatype)) areatype = text2path(areatype) - if(isarea(areatype)) - var/area/areatemp = areatype - areatype = areatemp.type - - var/list/turfs = new/list() - for(var/area/N in world) - if(istype(N, areatype)) - for(var/turf/T in N) turfs += T - return turfs - -//Takes: Area type as text string or as typepath OR an instance of the area. -//Returns: A list of all atoms (objs, turfs, mobs) in areas of that type of that type in the world. -/proc/get_area_all_atoms(areatype) - if(!areatype) return null - if(istext(areatype)) areatype = text2path(areatype) - if(isarea(areatype)) - var/area/areatemp = areatype - areatype = areatemp.type - - var/list/atoms = new/list() - for(var/area/N in world) - if(istype(N, areatype)) - for(var/atom/A in N) - atoms += A - return atoms - - - -/proc/get_cardinal_dir(atom/A, atom/B) - var/dx = abs(B.x - A.x) - var/dy = abs(B.y - A.y) - return get_dir(A, B) & (rand() * (dx+dy) < dy ? 3 : 12) - -//chances are 1:value. anyprob(1) will always return true -/proc/anyprob(value) - return (rand(1,value)==value) - -/proc/view_or_range(distance = world.view , center = usr , type) - switch(type) - if("view") - . = view(distance,center) - if("range") - . = range(distance,center) - return - -/proc/oview_or_orange(distance = world.view , center = usr , type) - switch(type) - if("view") - . = oview(distance,center) - if("range") - . = orange(distance,center) - return - -/proc/parse_zone(zone) - if(zone == "r_hand") return "right hand" - else if (zone == "l_hand") return "left hand" - else if (zone == "l_arm") return "left arm" - else if (zone == "r_arm") return "right arm" - else if (zone == "l_leg") return "left leg" - else if (zone == "r_leg") return "right leg" - else if (zone == "l_foot") return "left foot" - else if (zone == "r_foot") return "right foot" - else return zone - - -//Gets the turf this atom inhabits - -/proc/get_turf(atom/A) - if (!istype(A)) - return - for(A, A && !isturf(A), A=A.loc); //semicolon is for the empty statement - return A - - -//Gets the turf this atom's *ICON* appears to inhabit -//Uses half the width/height respectively to work out -//A minimum pixel amt this icon needs to be pixel'd by -//to be considered to be in another turf - -//division = world.icon_size - icon-width/2; DX = pixel_x/division -//division = world.icon_size - icon-height/2; DY = pixel_y/division - -//Eg: Humans -//32 - 16; 16/16 = 1, DX = 1 -//32 - 16; 15/16 = 0.9375 = 0 when round()'d, DX = 0 - -//NOTE: if your atom has non-standard bounds then this proc -//will handle it, but it'll be a bit slower. - -/proc/get_turf_pixel(atom/movable/AM) - if(istype(AM)) - var/rough_x = 0 - var/rough_y = 0 - var/final_x = 0 - var/final_y = 0 - var/final_z = 0 - - //Assume standards - var/i_width = world.icon_size - var/i_height = world.icon_size - - //Handle snowflake objects only if necessary - if(AM.bound_height != world.icon_size || AM.bound_width != world.icon_size) - var/icon/AMicon = icon(AM.icon, AM.icon_state) - i_width = AMicon.Width() - i_height = AMicon.Height() - qdel(AMicon) - - //Find a value to divide pixel_ by - var/n_width = (world.icon_size - (i_width/2)) - var/n_height = (world.icon_size - (i_height/2)) - - //DY and DX - if(n_width) - rough_x = round(AM.pixel_x/n_width) - if(n_height) - rough_y = round(AM.pixel_y/n_height) - - //Find coordinates - if(!isturf(AM.loc)) - var/turf/T = get_turf(AM) - final_x = T.x + rough_x - final_y = T.y + rough_y - final_z = T.z - else - final_x = AM.x + rough_x - final_y = AM.y + rough_y - final_z = AM.z - - if(final_x || final_y) - return locate(final_x, final_y, final_z) - -//Finds the distance between two atoms, in pixels -//centered = 0 counts from turf edge to edge -//centered = 1 counts from turf center to turf center -//of course mathematically this is just adding world.icon_size on again -/proc/getPixelDistance(atom/A, atom/B, centered = 1) - if(!istype(A)||!istype(B)) - return 0 - . = bounds_dist(A, B) + sqrt((((A.pixel_x+B.pixel_x)**2) + ((A.pixel_y+B.pixel_y)**2))) - if(centered) - . += world.icon_size - -/proc/get(atom/loc, type) - while(loc) - if(istype(loc, type)) - return loc - loc = loc.loc - return null - -//Quick type checks for some tools -var/global/list/common_tools = list( -/obj/item/stack/cable_coil, -/obj/item/weapon/wrench, -/obj/item/weapon/weldingtool, -/obj/item/weapon/screwdriver, -/obj/item/weapon/wirecutters, -/obj/item/device/multitool, -/obj/item/weapon/crowbar) - -/proc/istool(O) - if(O && is_type_in_list(O, common_tools)) - return 1 - return 0 - -/proc/is_pointed(obj/item/W) - if(istype(W, /obj/item/weapon/pen)) - return 1 - if(istype(W, /obj/item/weapon/screwdriver)) - return 1 - if(istype(W, /obj/item/weapon/reagent_containers/syringe)) - return 1 - if(istype(W, /obj/item/weapon/kitchen/fork)) - return 1 - else - return 0 - -//For objects that should embed, but make no sense being is_sharp or is_pointed() -//e.g: rods -/proc/can_embed(obj/item/W) - if(W.is_sharp()) - return 1 - if(is_pointed(W)) - return 1 - - var/list/embed_items = list(\ - /obj/item/stack/rods,\ - ) - - if(is_type_in_list(W, embed_items)) - return 1 - - -/* -Checks if that loc and dir has a item on the wall -*/ -var/list/WALLITEMS = list( - /obj/machinery/power/apc, /obj/machinery/alarm, /obj/item/device/radio/intercom, - /obj/structure/extinguisher_cabinet, /obj/structure/reagent_dispensers/peppertank, - /obj/machinery/status_display, /obj/machinery/requests_console, /obj/machinery/light_switch, /obj/structure/sign, - /obj/machinery/newscaster, /obj/machinery/firealarm, /obj/structure/noticeboard, /obj/machinery/button, - /obj/machinery/computer/security/telescreen, /obj/machinery/embedded_controller/radio/simple_vent_controller, - /obj/item/weapon/storage/secure/safe, /obj/machinery/door_timer, /obj/machinery/flasher, /obj/machinery/keycard_auth, - /obj/structure/mirror, /obj/structure/fireaxecabinet, /obj/machinery/computer/security/telescreen/entertainment - ) - -var/list/WALLITEMS_EXTERNAL = list( - /obj/machinery/camera, /obj/machinery/camera_assembly, - /obj/machinery/light_construct, /obj/machinery/light) - -var/list/WALLITEMS_INVERSE = list( - /obj/machinery/light_construct, /obj/machinery/light) - - -/proc/gotwallitem(loc, dir, var/check_external = 0) - var/locdir = get_step(loc, dir) - for(var/obj/O in loc) - if(is_type_in_list(O, WALLITEMS) && check_external != 2) - //Direction works sometimes - if(is_type_in_list(O, WALLITEMS_INVERSE)) - if(O.dir == turn(dir, 180)) - return 1 - else if(O.dir == dir) - return 1 - - //Some stuff doesn't use dir properly, so we need to check pixel instead - //That's exactly what get_turf_pixel() does - if(get_turf_pixel(O) == locdir) - return 1 - - if(is_type_in_list(O, WALLITEMS_EXTERNAL) && check_external) - if(is_type_in_list(O, WALLITEMS_INVERSE)) - if(O.dir == turn(dir, 180)) - return 1 - else if(O.dir == dir) - return 1 - - //Some stuff is placed directly on the wallturf (signs) - for(var/obj/O in locdir) - if(is_type_in_list(O, WALLITEMS) && check_external != 2) - if(O.pixel_x == 0 && O.pixel_y == 0) - return 1 - return 0 - -/proc/format_text(text) - return replacetext(replacetext(text,"\proper ",""),"\improper ","") - -/obj/proc/atmosanalyzer_scan(datum/gas_mixture/air_contents, mob/user, obj/target = src) - var/obj/icon = target - user.visible_message("[user] has used the analyzer on \icon[icon] [target].", "You use the analyzer on \icon[icon] [target].") - var/pressure = air_contents.return_pressure() - var/total_moles = air_contents.total_moles() - - user << "Results of analysis of \icon[icon] [target]." - 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 << "Pressure: [round(pressure,0.1)] kPa" - user << "Nitrogen: [round(n2_concentration*100)] %" - user << "Oxygen: [round(o2_concentration*100)] %" - user << "CO2: [round(co2_concentration*100)] %" - user << "Plasma: [round(plasma_concentration*100)] %" - if(unknown_concentration>0.01) - user << "Unknown: [round(unknown_concentration*100)] %" - user << "Temperature: [round(air_contents.temperature-T0C)] °C" - else - user << "[target] is empty!" - return - -/proc/check_target_facings(mob/living/initator, mob/living/target) - /*This can be used to add additional effects on interactions between mobs depending on how the mobs are facing each other, such as adding a crit damage to blows to the back of a guy's head. - Given how click code currently works (Nov '13), the initiating mob will be facing the target mob most of the time - That said, this proc should not be used if the change facing proc of the click code is overriden at the same time*/ - if(!ismob(target) || target.lying) - //Make sure we are not doing this for things that can't have a logical direction to the players given that the target would be on their side - return FACING_FAILED - if(initator.dir == target.dir) //mobs are facing the same direction - return FACING_SAME_DIR - if(is_A_facing_B(initator,target) && is_A_facing_B(target,initator)) //mobs are facing each other - return FACING_EACHOTHER - if(initator.dir + 2 == target.dir || initator.dir - 2 == target.dir || initator.dir + 6 == target.dir || initator.dir - 6 == target.dir) //Initating mob is looking at the target, while the target mob is looking in a direction perpendicular to the 1st - return FACING_INIT_FACING_TARGET_TARGET_FACING_PERPENDICULAR - -/proc/random_step(atom/movable/AM, steps, chance) - var/initial_chance = chance - while(steps > 0) - if(prob(chance)) - step(AM, pick(alldirs)) - chance = max(chance - (initial_chance / steps), 0) - steps-- - -/proc/living_player_count() - var/living_player_count = 0 - for(var/mob in player_list) - if(mob in living_mob_list) - living_player_count += 1 - return living_player_count - -/proc/randomColor(mode = 0) //if 1 it doesn't pick white, black or gray - switch(mode) - if(0) - return pick("white","black","gray","red","green","blue","brown","yellow","orange","darkred", - "crimson","lime","darkgreen","cyan","navy","teal","purple","indigo") - if(1) - return pick("red","green","blue","brown","yellow","orange","darkred","crimson", - "lime","darkgreen","cyan","navy","teal","purple","indigo") - else - return "white" - - -/proc/screen_loc2turf(scr_loc, turf/origin) - var/tX = text2list(scr_loc, ",") - var/tY = text2list(tX[2], ":") - var/tZ = origin.z - tY = tY[1] - tX = text2list(tX[1], ":") - tX = tX[1] - tX = max(1, min(world.maxx, origin.x + (text2num(tX) - (world.view + 1)))) - tY = max(1, min(world.maxy, origin.y + (text2num(tY) - (world.view + 1)))) - return locate(tX, tY, tZ) - -/proc/IsValidSrc(A) - if(istype(A, /datum)) - var/datum/B = A - return !B.gc_destroyed - if(istype(A, /client)) - return 1 - return 0 - - - -//Get the dir to the RIGHT of dir if they were on a clock -//NORTH --> NORTHEAST -/proc/get_clockwise_dir(dir) - . = angle2dir(dir2angle(dir)+45) - -//Get the dir to the LEFT of dir if they were on a clock -//NORTH --> NORTHWEST -/proc/get_anticlockwise_dir(dir) - . = angle2dir(dir2angle(dir)-45) - - -//Compare A's dir, the clockwise dir of A and the anticlockwise dir of A -//To the opposite dir of the dir returned by get_dir(B,A) -//If one of them is a match, then A is facing B -/proc/is_A_facing_B(atom/A,atom/B) - if(!istype(A) || !istype(B)) - return 0 - if(istype(A, /mob/living)) - var/mob/living/LA = A - if(LA.lying) - return 0 - var/goal_dir = angle2dir(dir2angle(get_dir(B,A)+180)) - var/clockwise_A_dir = get_clockwise_dir(A.dir) - var/anticlockwise_A_dir = get_anticlockwise_dir(B.dir) - - if(A.dir == goal_dir || clockwise_A_dir == goal_dir || anticlockwise_A_dir == goal_dir) - return 1 - return 0 - - -/* -rough example of the "cone" made by the 3 dirs checked - - B - \ - \ - > - < - \ - \ -B --><-- A - / - / - < - > - / - / - B - -*/ - - -//This is just so you can stop an orbit. -//orbit() can run without it (swap orbiting for A) -//but then you can never stop it and that's just silly. -/atom/movable/var/atom/orbiting = null -//we raise this each time orbit is called to prevent mutiple calls in a short time frame from breaking things -/atom/movable/var/orbitid = 0 - -/atom/movable/proc/orbit(atom/A, radius = 10, clockwise = 1, angle_increment = 15, lockinorbit = 0) - if(!istype(A)) - return - orbitid++ - var/myid = orbitid - if (orbiting) - stop_orbit() - //sadly this is the only way to ensure the original orbit proc stops - //and resets the atom's transform before we continue. - //time is based on the sleep in the loop and the time for the final animation of initial_transform. - sleep(2.6+world.tick_lag) - if (orbiting || !istype(A) || orbitid != myid) //post sleep re-check - return - orbiting = A - var/lastloc = loc - var/angle = 0 - var/matrix/initial_transform = matrix(transform) - - while(orbiting && orbiting.loc && orbitid == myid) - var/targetloc = get_turf(orbiting) - if (!lockinorbit && loc != lastloc && loc != targetloc) - break - loc = targetloc - lastloc = loc - angle += angle_increment - - var/matrix/shift = matrix(initial_transform) - shift.Translate(radius,0) - if(clockwise) - shift.Turn(angle) - else - shift.Turn(-angle) - animate(src, transform = shift, 2) - sleep(0.6) //the effect breaks above 0.6 delay - animate(src, transform = initial_transform, 2) - orbiting = null - - -/atom/movable/proc/stop_orbit() - if(orbiting) - loc = get_turf(orbiting) - orbiting = null - - -//Center's an image. -//Requires: -//The Image -//The x dimension of the icon file used in the image -//The y dimension of the icon file used in the image -// eg: center_image(I, 32,32) -// eg2: center_image(I, 96,96) - -/proc/center_image(var/image/I, x_dimension = 0, y_dimension = 0) - if(!I) - return - - if(!x_dimension || !y_dimension) - return - - if((x_dimension == world.icon_size) && (y_dimension == world.icon_size)) - return I - - //Offset the image so that it's bottom left corner is shifted this many pixels - //This makes it infinitely easier to draw larger inhands/images larger than world.iconsize - //but still use them in game - var/x_offset = -((x_dimension/world.icon_size)-1)*(world.icon_size*0.5) - var/y_offset = -((y_dimension/world.icon_size)-1)*(world.icon_size*0.5) - - //Correct values under world.icon_size - if(x_dimension < world.icon_size) - x_offset *= -1 - if(y_dimension < world.icon_size) - y_offset *= -1 - - I.pixel_x = x_offset - I.pixel_y = y_offset - - return I - -//similar function to range(), but with no limitations on the distance; will search spiralling outwards from the center -/proc/ultra_range(dist=0, center=usr, orange=0) - if(!dist) - if(!orange) - return list(center) - else - return list() - - var/turf/t_center = get_turf(center) - if(!t_center) - return list() - - var/list/L = list() - var/turf/T - var/y - var/x - var/c_dist = 1 - - if(!orange) - L += t_center - L += t_center.contents - - while( c_dist <= dist ) - y = t_center.y + c_dist - x = t_center.x - c_dist + 1 - for(x in x to t_center.x+c_dist) - T = locate(x,y,t_center.z) - if(T) - L += T - L += T.contents - - y = t_center.y + c_dist - 1 - x = t_center.x + c_dist - for(y in t_center.y-c_dist to y) - T = locate(x,y,t_center.z) - if(T) - L += T - L += T.contents - - y = t_center.y - c_dist - x = t_center.x + c_dist - 1 - for(x in t_center.x-c_dist to x) - T = locate(x,y,t_center.z) - if(T) - L += T - L += T.contents - - y = t_center.y - c_dist + 1 - x = t_center.x - c_dist - for(y in y to t_center.y+c_dist) - T = locate(x,y,t_center.z) - if(T) - L += T - L += T.contents - c_dist++ - - return L - -/atom/proc/contains(var/atom/location) - if(!location) - return 0 - for(location, location && location != src, location=location.loc); //semicolon is for the empty statement - if(location == src) - return 1 +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 + +/* + * A large number of misc global procs. + */ + +//Inverts the colour of an HTML string +/proc/invertHTML(HTMLstring) + + if (!( istext(HTMLstring) )) + CRASH("Given non-text argument!") + return + else + if (length(HTMLstring) != 7) + CRASH("Given non-HTML argument!") + return + var/textr = copytext(HTMLstring, 2, 4) + var/textg = copytext(HTMLstring, 4, 6) + var/textb = copytext(HTMLstring, 6, 8) + var/r = hex2num(textr) + var/g = hex2num(textg) + var/b = hex2num(textb) + textr = num2hex(255 - r, 2) + textg = num2hex(255 - g, 2) + textb = num2hex(255 - b, 2) + return text("#[][][]", textr, textg, textb) + return + +//Returns the middle-most value +/proc/dd_range(low, high, num) + return max(low,min(high,num)) + + +/proc/Get_Angle(atom/movable/start,atom/movable/end)//For beams. + if(!start || !end) return 0 + var/dy + var/dx + dy=(32*end.y+end.pixel_y)-(32*start.y+start.pixel_y) + dx=(32*end.x+end.pixel_x)-(32*start.x+start.pixel_x) + if(!dy) + return (dx>=0)?90:270 + .=arctan(dx/dy) + if(dy<0) + .+=180 + else if(dx<0) + .+=360 + +//Returns location. Returns null if no location was found. +/proc/get_teleport_loc(turf/location,mob/target,distance = 1, density = 0, errorx = 0, errory = 0, eoffsetx = 0, eoffsety = 0) +/* +Location where the teleport begins, target that will teleport, distance to go, density checking 0/1(yes/no). +Random error in tile placement x, error in tile placement y, and block offset. +Block offset tells the proc how to place the box. Behind teleport location, relative to starting location, forward, etc. +Negative values for offset are accepted, think of it in relation to North, -x is west, -y is south. Error defaults to positive. +Turf and target are seperate in case you want to teleport some distance from a turf the target is not standing on or something. +*/ + + var/dirx = 0//Generic location finding variable. + var/diry = 0 + + var/xoffset = 0//Generic counter for offset location. + var/yoffset = 0 + + var/b1xerror = 0//Generic placing for point A in box. The lower left. + var/b1yerror = 0 + var/b2xerror = 0//Generic placing for point B in box. The upper right. + var/b2yerror = 0 + + errorx = abs(errorx)//Error should never be negative. + errory = abs(errory) + //var/errorxy = round((errorx+errory)/2)//Used for diagonal boxes. + + switch(target.dir)//This can be done through equations but switch is the simpler method. And works fast to boot. + //Directs on what values need modifying. + if(1)//North + diry+=distance + yoffset+=eoffsety + xoffset+=eoffsetx + b1xerror-=errorx + b1yerror-=errory + b2xerror+=errorx + b2yerror+=errory + if(2)//South + diry-=distance + yoffset-=eoffsety + xoffset+=eoffsetx + b1xerror-=errorx + b1yerror-=errory + b2xerror+=errorx + b2yerror+=errory + if(4)//East + dirx+=distance + yoffset+=eoffsetx//Flipped. + xoffset+=eoffsety + b1xerror-=errory//Flipped. + b1yerror-=errorx + b2xerror+=errory + b2yerror+=errorx + if(8)//West + dirx-=distance + yoffset-=eoffsetx//Flipped. + xoffset+=eoffsety + b1xerror-=errory//Flipped. + b1yerror-=errorx + b2xerror+=errory + b2yerror+=errorx + + var/turf/destination=locate(location.x+dirx,location.y+diry,location.z) + + if(destination)//If there is a destination. + if(errorx||errory)//If errorx or y were specified. + var/destination_list[] = list()//To add turfs to list. + //destination_list = new() + /*This will draw a block around the target turf, given what the error is. + Specifying the values above will basically draw a different sort of block. + If the values are the same, it will be a square. If they are different, it will be a rectengle. + In either case, it will center based on offset. Offset is position from center. + Offset always calculates in relation to direction faced. In other words, depending on the direction of the teleport, + the offset should remain positioned in relation to destination.*/ + + var/turf/center = locate((destination.x+xoffset),(destination.y+yoffset),location.z)//So now, find the new center. + + //Now to find a box from center location and make that our destination. + for(var/turf/T in block(locate(center.x+b1xerror,center.y+b1yerror,location.z), locate(center.x+b2xerror,center.y+b2yerror,location.z) )) + if(density&&T.density) continue//If density was specified. + if(T.x>world.maxx || T.x<1) continue//Don't want them to teleport off the map. + if(T.y>world.maxy || T.y<1) continue + destination_list += T + if(destination_list.len) + destination = pick(destination_list) + else return + + else//Same deal here. + if(density&&destination.density) return + if(destination.x>world.maxx || destination.x<1) return + if(destination.y>world.maxy || destination.y<1) return + else return + + return destination + +/proc/getline(atom/M,atom/N)//Ultra-Fast Bresenham Line-Drawing Algorithm + var/px=M.x //starting x + var/py=M.y + var/line[] = list(locate(px,py,M.z)) + var/dx=N.x-px //x distance + var/dy=N.y-py + var/dxabs=abs(dx)//Absolute value of x distance + var/dyabs=abs(dy) + var/sdx=sign(dx) //Sign of x distance (+ or -) + var/sdy=sign(dy) + var/x=dxabs>>1 //Counters for steps taken, setting to distance/2 + var/y=dyabs>>1 //Bit-shifting makes me l33t. It also makes getline() unnessecarrily fast. + var/j //Generic integer for counting + if(dxabs>=dyabs) //x distance is greater than y + for(j=0;j=dxabs) //Every dyabs steps, step once in y direction + y-=dxabs + py+=sdy + px+=sdx //Step on in x direction + line+=locate(px,py,M.z)//Add the turf to the list + else + for(j=0;j=dyabs) + x-=dyabs + px+=sdx + py+=sdy + line+=locate(px,py,M.z) + return line + +//Returns whether or not a player is a guest using their ckey as an input +/proc/IsGuestKey(key) + if (findtext(key, "Guest-", 1, 7) != 1) //was findtextEx + return 0 + + var/i, ch, len = length(key) + + for (i = 7, i <= len, ++i) + ch = text2ascii(key, i) + if (ch < 48 || ch > 57) + return 0 + return 1 + +//Ensure the frequency is within bounds of what it should be sending/recieving at +/proc/sanitize_frequency(f) + f = round(f) + f = max(1441, f) // 144.1 + f = min(1489, f) // 148.9 + if ((f % 2) == 0) //Ensure the last digit is an odd number + f += 1 + return f + +//Turns 1479 into 147.9 +/proc/format_frequency(f) + f = text2num(f) + return "[round(f / 10)].[f % 10]" + + + +//This will update a mob's name, real_name, mind.name, data_core records, pda, id and traitor text +//Calling this proc without an oldname will only update the mob and skip updating the pda, id and records ~Carn +/mob/proc/fully_replace_character_name(oldname,newname) + if(!newname) return 0 + real_name = newname + name = newname + if(mind) + mind.name = newname + if(istype(src, /mob/living/carbon)) + var/mob/living/carbon/C = src + if(C.dna) + C.dna.real_name = real_name + + if(isAI(src)) + var/mob/living/silicon/ai/AI = src + if(oldname != real_name) + if(AI.eyeobj) + AI.eyeobj.name = "[newname] (AI Eye)" + + // Set ai pda name + if(AI.aiPDA) + AI.aiPDA.owner = newname + AI.aiPDA.name = newname + " (" + AI.aiPDA.ownjob + ")" + + // Notify Cyborgs + for(var/mob/living/silicon/robot/Slave in AI.connected_robots) + Slave.show_laws() + + if(isrobot(src)) + var/mob/living/silicon/robot/R = src + if(oldname != real_name) + R.notify_ai(3, oldname, newname) + if(R.camera) + R.camera.c_tag = real_name + + if(oldname) + //update the datacore records! This is goig to be a bit costly. + for(var/list/L in list(data_core.general,data_core.medical,data_core.security,data_core.locked)) + var/datum/data/record/R = find_record("name", oldname, L) + if(R) R.fields["name"] = newname + + //update our pda and id if we have them on our person + var/list/searching = GetAllContents() + var/search_id = 1 + var/search_pda = 1 + + for(var/A in searching) + if( search_id && istype(A,/obj/item/weapon/card/id) ) + var/obj/item/weapon/card/id/ID = A + if(ID.registered_name == oldname) + ID.registered_name = newname + ID.update_label() + if(!search_pda) break + search_id = 0 + + else if( search_pda && istype(A,/obj/item/device/pda) ) + var/obj/item/device/pda/PDA = A + if(PDA.owner == oldname) + PDA.owner = newname + PDA.update_label() + if(!search_id) break + search_pda = 0 + + for(var/datum/mind/T in ticker.minds) + for(var/datum/objective/obj in T.objectives) + // Only update if this player is a target + if(obj.target && obj.target.current && obj.target.current.real_name == name) + obj.update_explanation_text() + + return 1 + + + +//Generalised helper proc for letting mobs rename themselves. Used to be clname() and ainame() + +/mob/proc/rename_self(role, allow_numbers=0) + var/oldname = real_name + var/newname + var/loop = 1 + var/safety = 0 + + while(loop && safety < 5) + if(client && client.prefs.custom_names[role] && !safety) + newname = client.prefs.custom_names[role] + else + switch(role) + if("clown") + newname = pick(clown_names) + if("mime") + newname = pick(mime_names) + if("ai") + newname = pick(ai_names) + if("deity") + newname = pick(clown_names|ai_names|mime_names) //pick any old name + else + return + + for(var/mob/living/M in player_list) + if(M == src) + continue + if(!newname || M.real_name == newname) + newname = null + loop++ // name is already taken so we roll again + break + loop-- + safety++ + + if(isAI(src)) + oldname = null//don't bother with the records update crap + if(newname) + fully_replace_character_name(oldname,newname) + if(isrobot(src)) + var/mob/living/silicon/robot/A = src + A.custom_name = newname + + +//Picks a string of symbols to display as the law number for hacked or ion laws +/proc/ionnum() + return "[pick("!","@","#","$","%","^","&")][pick("!","@","#","$","%","^","&","*")][pick("!","@","#","$","%","^","&","*")][pick("!","@","#","$","%","^","&","*")]" + +//Returns a list of unslaved cyborgs +/proc/active_free_borgs() + . = list() + for(var/mob/living/silicon/robot/R in living_mob_list) + if(R.connected_ai) + continue + if(R.stat == DEAD) + continue + if(R.emagged || R.scrambledcodes || R.syndicate) + continue + . += R + +//Returns a list of AI's +/proc/active_ais(check_mind=0) + . = list() + for(var/mob/living/silicon/ai/A in living_mob_list) + if(A.stat == DEAD) + continue + if(A.control_disabled == 1) + continue + if(check_mind) + if(!A.mind) + continue + . += A + return . + +//Find an active ai with the least borgs. VERBOSE PROCNAME HUH! +/proc/select_active_ai_with_fewest_borgs() + var/mob/living/silicon/ai/selected + var/list/active = active_ais() + for(var/mob/living/silicon/ai/A in active) + if(!selected || (selected.connected_robots.len > A.connected_robots.len)) + selected = A + + return selected + +/proc/select_active_free_borg(mob/user) + var/list/borgs = active_free_borgs() + if(borgs.len) + if(user) . = input(user,"Unshackled cyborg signals detected:", "Cyborg Selection", borgs[1]) in borgs + else . = pick(borgs) + return . + +/proc/select_active_ai(mob/user) + var/list/ais = active_ais() + if(ais.len) + if(user) . = input(user,"AI signals detected:", "AI Selection", ais[1]) in ais + else . = pick(ais) + return . + +//Returns a list of all items of interest with their name +/proc/getpois(mobs_only=0,skip_mindless=0) + var/list/mobs = sortmobs() + var/list/names = list() + var/list/pois = list() + var/list/namecounts = list() + + for(var/mob/M in mobs) + if(skip_mindless && (!M.mind && !M.ckey)) + if(!isbot(M)) + continue + var/name = M.name + if (name in names) + namecounts[name]++ + name = "[name] ([namecounts[name]])" + else + names.Add(name) + namecounts[name] = 1 + if (M.real_name && M.real_name != M.name) + name += " \[[M.real_name]\]" + if (M.stat == 2) + if(istype(M, /mob/dead/observer/)) + name += " \[ghost\]" + else + name += " \[dead\]" + pois[name] = M + + if(!mobs_only) + for(var/atom/A in poi_list) + if(!A || !A.loc) + continue + var/name = A.name + if (names.Find(name)) + namecounts[name]++ + name = "[name] ([namecounts[name]])" + else + names.Add(name) + namecounts[name] = 1 + pois[name] = A + + return pois +//Orders mobs by type then by name +/proc/sortmobs() + var/list/moblist = list() + var/list/sortmob = sortNames(mob_list) + for(var/mob/living/silicon/ai/M in sortmob) + moblist.Add(M) + for(var/mob/camera/M in sortmob) + moblist.Add(M) + for(var/mob/living/silicon/pai/M in sortmob) + moblist.Add(M) + for(var/mob/living/silicon/robot/M in sortmob) + moblist.Add(M) + for(var/mob/living/carbon/human/M in sortmob) + moblist.Add(M) + for(var/mob/living/carbon/brain/M in sortmob) + moblist.Add(M) + for(var/mob/living/carbon/alien/M in sortmob) + moblist.Add(M) + for(var/mob/dead/observer/M in sortmob) + moblist.Add(M) + for(var/mob/new_player/M in sortmob) + moblist.Add(M) + for(var/mob/living/carbon/monkey/M in sortmob) + moblist.Add(M) + for(var/mob/living/simple_animal/slime/M in sortmob) + moblist.Add(M) + for(var/mob/living/simple_animal/M in sortmob) + moblist.Add(M) +// for(var/mob/living/silicon/hivebot/M in world) +// mob_list.Add(M) +// for(var/mob/living/silicon/hive_mainframe/M in world) +// mob_list.Add(M) + return moblist + +//E = MC^2 +/proc/convert2energy(M) + var/E = M*(SPEED_OF_LIGHT_SQ) + return E + +//M = E/C^2 +/proc/convert2mass(E) + var/M = E/(SPEED_OF_LIGHT_SQ) + return M + +/proc/key_name(whom, include_link = null, include_name = 1) + var/mob/M + var/client/C + var/key + var/ckey + + if(!whom) return "*null*" + if(istype(whom, /client)) + C = whom + M = C.mob + key = C.key + ckey = C.ckey + else if(ismob(whom)) + M = whom + C = M.client + key = M.key + ckey = M.ckey + else if(istext(whom)) + key = whom + ckey = ckey(whom) + C = directory[ckey] + if(C) + M = C.mob + else + return "*invalid*" + + . = "" + + if(!ckey) + include_link = 0 + + if(key) + if(C && C.holder && C.holder.fakekey && !include_name) + if(include_link) + . += "" + . += "Administrator" + else + if(include_link) + . += "" + . += key + if(!C) + . += "\[DC\]" + + if(include_link) + . += "" + else + . += "*no key*" + + if(include_name && M) + if(M.real_name) + . += "/([M.real_name])" + else if(M.name) + . += "/([M.name])" + + return . + +/proc/key_name_admin(whom, include_name = 1) + return key_name(whom, 1, include_name) + +/proc/get_mob_by_ckey(key) + if(!key) + return + var/list/mobs = sortmobs() + for(var/mob/M in mobs) + if(M.ckey == key) + return M + +// Returns the atom sitting on the turf. +// For example, using this on a disk, which is in a bag, on a mob, will return the mob because it's on the turf. +/proc/get_atom_on_turf(atom/movable/M) + var/atom/loc = M + while(loc && loc.loc && !istype(loc.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(atom/A, direction) + var/turf/target = locate(A.x, A.y, A.z) + if(!A || !target) + return 0 + //since NORTHEAST == NORTH & EAST, etc, doing it this way allows for diagonal mass drivers in the future + //and isn't really any more complicated + + // Note diagonal directions won't usually be accurate + if(direction & NORTH) + target = locate(target.x, world.maxy, target.z) + if(direction & SOUTH) + target = locate(target.x, 1, target.z) + if(direction & EAST) + target = locate(world.maxx, target.y, target.z) + if(direction & WEST) + target = locate(1, target.y, target.z) + return target + +// returns turf relative to A in given direction at set range +// result is bounded to map size +// note range is non-pythagorean +// used for disposal system +/proc/get_ranged_target_turf(atom/A, direction, range) + + var/x = A.x + var/y = A.y + if(direction & NORTH) + y = min(world.maxy, y + range) + if(direction & SOUTH) + y = max(1, y - range) + if(direction & EAST) + x = min(world.maxx, x + range) + if(direction & WEST) + x = max(1, x - range) + + return locate(x,y,A.z) + + +// returns turf relative to A offset in dx and dy tiles +// bound to map limits +/proc/get_offset_target_turf(atom/A, dx, dy) + var/x = min(world.maxx, max(1, A.x + dx)) + var/y = min(world.maxy, max(1, A.y + dy)) + return locate(x,y,A.z) + +/proc/arctan(x) + var/y=arcsin(x/sqrt(1+x*x)) + return y + + +/proc/anim(turf/location,target as mob|obj,a_icon,a_icon_state as text,flick_anim as text,sleeptime = 0,direction as num) +//This proc throws up either an icon or an animation for a specified amount of time. +//The variables should be apparent enough. + var/atom/movable/overlay/animation = new(location) + if(direction) + animation.dir = direction + animation.icon = a_icon + animation.layer = target:layer+1 + if(a_icon_state) + animation.icon_state = a_icon_state + else + animation.icon_state = "blank" + animation.master = target + flick(flick_anim, animation) + sleep(max(sleeptime, 15)) + qdel(animation) + + +/atom/proc/GetAllContents() + var/list/processing_list = list(src) + var/list/assembled = list() + + while(processing_list.len) + var/atom/A = processing_list[1] + processing_list -= A + + for(var/atom/a in A) + if(!(a in assembled)) + processing_list |= a + + assembled |= A + + return assembled + +//Step-towards method of determining whether one atom can see another. Similar to viewers() +/proc/can_see(atom/source, atom/target, length=5) // I couldnt be arsed to do actual raycasting :I This is horribly inaccurate. + var/turf/current = get_turf(source) + var/turf/target_turf = get_turf(target) + var/steps = 0 + + while(current != target_turf) + if(steps > length) return 0 + if(current.opacity) return 0 + for(var/atom/A in current) + if(A.opacity) return 0 + current = get_step_towards(current, target_turf) + steps++ + + return 1 + +/proc/is_blocked_turf(turf/T) + var/cant_pass = 0 + if(T.density) cant_pass = 1 + for(var/atom/A in T) + if(A.density)//&&A.anchored + cant_pass = 1 + return cant_pass + +/proc/get_step_towards2(atom/ref , atom/trg) + var/base_dir = get_dir(ref, get_step_towards(ref,trg)) + var/turf/temp = get_step_towards(ref,trg) + + if(is_blocked_turf(temp)) + var/dir_alt1 = turn(base_dir, 90) + var/dir_alt2 = turn(base_dir, -90) + var/turf/turf_last1 = temp + var/turf/turf_last2 = temp + var/free_tile = null + var/breakpoint = 0 + + while(!free_tile && breakpoint < 10) + if(!is_blocked_turf(turf_last1)) + free_tile = turf_last1 + break + if(!is_blocked_turf(turf_last2)) + free_tile = turf_last2 + break + turf_last1 = get_step(turf_last1,dir_alt1) + turf_last2 = get_step(turf_last2,dir_alt2) + breakpoint++ + + if(!free_tile) return get_step(ref, base_dir) + else return get_step_towards(ref,free_tile) + + else return get_step(ref, base_dir) + +/proc/do_mob(mob/user , mob/target, time = 30, numticks = 5, uninterruptible = 0, progress = 1) + //This is quite an ugly solution but i refuse to use the old request system. + if(!user || !target) + return 0 + if(numticks == 0) + return 0 + var/user_loc = user.loc + var/target_loc = target.loc + var/holding = user.get_active_hand() + var/timefraction = round(time/numticks) + var/image/progbar + var/continue_looping = 1 + for(var/i = 1 to numticks) + if(user.client && progress) + progbar = make_progress_bar(i, numticks, target) + assign_progress_bar(user, progbar) + sleep(timefraction) + if(!user || !target) + continue_looping = 0 + + if (continue_looping && !uninterruptible && (user.loc != user_loc || target.loc != target_loc || user.get_active_hand() != holding || user.incapacitated() || user.lying )) + continue_looping = 0 + + cancel_progress_bar(user, progbar)//Clear the way for the next progbar image + if(!continue_looping) + return 0 + + cancel_progress_bar(user, progbar) + return 1 + +/proc/make_progress_bar(current_number, goal_number, atom/target) + if(current_number && goal_number && target) + var/image/progbar + progbar = image("icon" = 'icons/effects/doafter_icon.dmi', "loc" = target, "icon_state" = "prog_bar_0") + progbar.icon_state = "prog_bar_[round(((current_number / goal_number) * 100), 10)]" + progbar.pixel_y = 32 + return progbar + +/proc/cancel_progress_bar(mob/user, image/progbar) + if(user && user.client && progbar) + user.client.images -= progbar + +/proc/assign_progress_bar(mob/user, image/progbar) + if(user && user.client && progbar) + user.client.images |= progbar + +/proc/do_after(mob/user, delay, numticks = 5, needhand = 1, atom/target = null, progress = 1) + if(!user) + return 0 + + if(numticks == 0) + return 0 + + var/atom/Tloc = null + if(target) + Tloc = target.loc + + var/delayfraction = round(delay/numticks) + + var/atom/Uloc = user.loc + + var/holding = user.get_active_hand() + var/holdingnull = 1 //User is not holding anything + if(holding) + holdingnull = 0 //User is holding a tool of some kind + + var/image/progbar + + var/continue_looping = 1 + for (var/i = 1 to numticks) + if(user.client && progress) + progbar = make_progress_bar(i, numticks, target) + assign_progress_bar(user, progbar) + + sleep(delayfraction) + if(!user || user.stat || user.weakened || user.stunned || !(user.loc == Uloc)) + continue_looping = 0 + + if(continue_looping && Tloc && (!target || Tloc != target.loc)) //Tloc not set when we don't want to track target + continue_looping = 0 + + if(continue_looping && needhand) + //This might seem like an odd check, but you can still need a hand even when it's empty + //i.e the hand is used to insert some item/tool into the construction + if(!holdingnull) + if(!holding) + continue_looping = 0 + if(continue_looping && user.get_active_hand() != holding) + continue_looping = 0 + + cancel_progress_bar(user, progbar)//Clear the way for the next progbar image + if(!continue_looping) + return 0 + + cancel_progress_bar(user,progbar) + return 1 + +//Takes: Anything that could possibly have variables and a varname to check. +//Returns: 1 if found, 0 if not. +/proc/hasvar(datum/A, varname) + if(A.vars.Find(lowertext(varname))) return 1 + else return 0 + +//Returns sortedAreas list if populated +//else populates the list first before returning it +/proc/SortAreas() + for(var/area/A in world) + sortedAreas.Add(A) + + sortTim(sortedAreas, /proc/cmp_name_asc) + +/area/proc/addSorted() + sortedAreas.Add(src) + sortTim(sortedAreas, /proc/cmp_name_asc) + +//Takes: Area type as text string or as typepath OR an instance of the area. +//Returns: A list of all areas of that type in the world. +/proc/get_areas(areatype) + if(!areatype) return null + if(istext(areatype)) areatype = text2path(areatype) + if(isarea(areatype)) + var/area/areatemp = areatype + areatype = areatemp.type + + var/list/areas = new/list() + for(var/area/N in world) + if(istype(N, areatype)) areas += N + return areas + +//Takes: Area type as text string or as typepath OR an instance of the area. +//Returns: A list of all turfs in areas of that type of that type in the world. +/proc/get_area_turfs(areatype) + if(!areatype) return null + if(istext(areatype)) areatype = text2path(areatype) + if(isarea(areatype)) + var/area/areatemp = areatype + areatype = areatemp.type + + var/list/turfs = new/list() + for(var/area/N in world) + if(istype(N, areatype)) + for(var/turf/T in N) turfs += T + return turfs + +//Takes: Area type as text string or as typepath OR an instance of the area. +//Returns: A list of all atoms (objs, turfs, mobs) in areas of that type of that type in the world. +/proc/get_area_all_atoms(areatype) + if(!areatype) return null + if(istext(areatype)) areatype = text2path(areatype) + if(isarea(areatype)) + var/area/areatemp = areatype + areatype = areatemp.type + + var/list/atoms = new/list() + for(var/area/N in world) + if(istype(N, areatype)) + for(var/atom/A in N) + atoms += A + return atoms + + + +/proc/get_cardinal_dir(atom/A, atom/B) + var/dx = abs(B.x - A.x) + var/dy = abs(B.y - A.y) + return get_dir(A, B) & (rand() * (dx+dy) < dy ? 3 : 12) + +//chances are 1:value. anyprob(1) will always return true +/proc/anyprob(value) + return (rand(1,value)==value) + +/proc/view_or_range(distance = world.view , center = usr , type) + switch(type) + if("view") + . = view(distance,center) + if("range") + . = range(distance,center) + return + +/proc/oview_or_orange(distance = world.view , center = usr , type) + switch(type) + if("view") + . = oview(distance,center) + if("range") + . = orange(distance,center) + return + +/proc/parse_zone(zone) + if(zone == "r_hand") return "right hand" + else if (zone == "l_hand") return "left hand" + else if (zone == "l_arm") return "left arm" + else if (zone == "r_arm") return "right arm" + else if (zone == "l_leg") return "left leg" + else if (zone == "r_leg") return "right leg" + else if (zone == "l_foot") return "left foot" + else if (zone == "r_foot") return "right foot" + else return zone + + +//Gets the turf this atom inhabits + +/proc/get_turf(atom/A) + if (!istype(A)) + return + for(A, A && !isturf(A), A=A.loc); //semicolon is for the empty statement + return A + + +//Gets the turf this atom's *ICON* appears to inhabit +//Uses half the width/height respectively to work out +//A minimum pixel amt this icon needs to be pixel'd by +//to be considered to be in another turf + +//division = world.icon_size - icon-width/2; DX = pixel_x/division +//division = world.icon_size - icon-height/2; DY = pixel_y/division + +//Eg: Humans +//32 - 16; 16/16 = 1, DX = 1 +//32 - 16; 15/16 = 0.9375 = 0 when round()'d, DX = 0 + +//NOTE: if your atom has non-standard bounds then this proc +//will handle it, but it'll be a bit slower. + +/proc/get_turf_pixel(atom/movable/AM) + if(istype(AM)) + var/rough_x = 0 + var/rough_y = 0 + var/final_x = 0 + var/final_y = 0 + var/final_z = 0 + + //Assume standards + var/i_width = world.icon_size + var/i_height = world.icon_size + + //Handle snowflake objects only if necessary + if(AM.bound_height != world.icon_size || AM.bound_width != world.icon_size) + var/icon/AMicon = icon(AM.icon, AM.icon_state) + i_width = AMicon.Width() + i_height = AMicon.Height() + qdel(AMicon) + + //Find a value to divide pixel_ by + var/n_width = (world.icon_size - (i_width/2)) + var/n_height = (world.icon_size - (i_height/2)) + + //DY and DX + if(n_width) + rough_x = round(AM.pixel_x/n_width) + if(n_height) + rough_y = round(AM.pixel_y/n_height) + + //Find coordinates + if(!isturf(AM.loc)) + var/turf/T = get_turf(AM) + final_x = T.x + rough_x + final_y = T.y + rough_y + final_z = T.z + else + final_x = AM.x + rough_x + final_y = AM.y + rough_y + final_z = AM.z + + if(final_x || final_y) + return locate(final_x, final_y, final_z) + +//Finds the distance between two atoms, in pixels +//centered = 0 counts from turf edge to edge +//centered = 1 counts from turf center to turf center +//of course mathematically this is just adding world.icon_size on again +/proc/getPixelDistance(atom/A, atom/B, centered = 1) + if(!istype(A)||!istype(B)) + return 0 + . = bounds_dist(A, B) + sqrt((((A.pixel_x+B.pixel_x)**2) + ((A.pixel_y+B.pixel_y)**2))) + if(centered) + . += world.icon_size + +/proc/get(atom/loc, type) + while(loc) + if(istype(loc, type)) + return loc + loc = loc.loc + return null + +//Quick type checks for some tools +var/global/list/common_tools = list( +/obj/item/stack/cable_coil, +/obj/item/weapon/wrench, +/obj/item/weapon/weldingtool, +/obj/item/weapon/screwdriver, +/obj/item/weapon/wirecutters, +/obj/item/device/multitool, +/obj/item/weapon/crowbar) + +/proc/istool(O) + if(O && is_type_in_list(O, common_tools)) + return 1 + return 0 + +/proc/is_pointed(obj/item/W) + if(istype(W, /obj/item/weapon/pen)) + return 1 + if(istype(W, /obj/item/weapon/screwdriver)) + return 1 + if(istype(W, /obj/item/weapon/reagent_containers/syringe)) + return 1 + if(istype(W, /obj/item/weapon/kitchen/fork)) + return 1 + else + return 0 + +//For objects that should embed, but make no sense being is_sharp or is_pointed() +//e.g: rods +/proc/can_embed(obj/item/W) + if(W.is_sharp()) + return 1 + if(is_pointed(W)) + return 1 + + var/list/embed_items = list(\ + /obj/item/stack/rods,\ + ) + + if(is_type_in_list(W, embed_items)) + return 1 + + +/* +Checks if that loc and dir has a item on the wall +*/ +var/list/WALLITEMS = list( + /obj/machinery/power/apc, /obj/machinery/alarm, /obj/item/device/radio/intercom, + /obj/structure/extinguisher_cabinet, /obj/structure/reagent_dispensers/peppertank, + /obj/machinery/status_display, /obj/machinery/requests_console, /obj/machinery/light_switch, /obj/structure/sign, + /obj/machinery/newscaster, /obj/machinery/firealarm, /obj/structure/noticeboard, /obj/machinery/button, + /obj/machinery/computer/security/telescreen, /obj/machinery/embedded_controller/radio/simple_vent_controller, + /obj/item/weapon/storage/secure/safe, /obj/machinery/door_timer, /obj/machinery/flasher, /obj/machinery/keycard_auth, + /obj/structure/mirror, /obj/structure/fireaxecabinet, /obj/machinery/computer/security/telescreen/entertainment + ) + +var/list/WALLITEMS_EXTERNAL = list( + /obj/machinery/camera, /obj/machinery/camera_assembly, + /obj/machinery/light_construct, /obj/machinery/light) + +var/list/WALLITEMS_INVERSE = list( + /obj/machinery/light_construct, /obj/machinery/light) + + +/proc/gotwallitem(loc, dir, var/check_external = 0) + var/locdir = get_step(loc, dir) + for(var/obj/O in loc) + if(is_type_in_list(O, WALLITEMS) && check_external != 2) + //Direction works sometimes + if(is_type_in_list(O, WALLITEMS_INVERSE)) + if(O.dir == turn(dir, 180)) + return 1 + else if(O.dir == dir) + return 1 + + //Some stuff doesn't use dir properly, so we need to check pixel instead + //That's exactly what get_turf_pixel() does + if(get_turf_pixel(O) == locdir) + return 1 + + if(is_type_in_list(O, WALLITEMS_EXTERNAL) && check_external) + if(is_type_in_list(O, WALLITEMS_INVERSE)) + if(O.dir == turn(dir, 180)) + return 1 + else if(O.dir == dir) + return 1 + + //Some stuff is placed directly on the wallturf (signs) + for(var/obj/O in locdir) + if(is_type_in_list(O, WALLITEMS) && check_external != 2) + if(O.pixel_x == 0 && O.pixel_y == 0) + return 1 + return 0 + +/proc/format_text(text) + return replacetext(replacetext(text,"\proper ",""),"\improper ","") + +/obj/proc/atmosanalyzer_scan(datum/gas_mixture/air_contents, mob/user, obj/target = src) + var/obj/icon = target + user.visible_message("[user] has used the analyzer on \icon[icon] [target].", "You use the analyzer on \icon[icon] [target].") + var/pressure = air_contents.return_pressure() + var/total_moles = air_contents.total_moles() + + user << "Results of analysis of \icon[icon] [target]." + 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 << "Pressure: [round(pressure,0.1)] kPa" + user << "Nitrogen: [round(n2_concentration*100)] %" + user << "Oxygen: [round(o2_concentration*100)] %" + user << "CO2: [round(co2_concentration*100)] %" + user << "Plasma: [round(plasma_concentration*100)] %" + if(unknown_concentration>0.01) + user << "Unknown: [round(unknown_concentration*100)] %" + user << "Temperature: [round(air_contents.temperature-T0C)] °C" + else + user << "[target] is empty!" + return + +/proc/check_target_facings(mob/living/initator, mob/living/target) + /*This can be used to add additional effects on interactions between mobs depending on how the mobs are facing each other, such as adding a crit damage to blows to the back of a guy's head. + Given how click code currently works (Nov '13), the initiating mob will be facing the target mob most of the time + That said, this proc should not be used if the change facing proc of the click code is overriden at the same time*/ + if(!ismob(target) || target.lying) + //Make sure we are not doing this for things that can't have a logical direction to the players given that the target would be on their side + return FACING_FAILED + if(initator.dir == target.dir) //mobs are facing the same direction + return FACING_SAME_DIR + if(is_A_facing_B(initator,target) && is_A_facing_B(target,initator)) //mobs are facing each other + return FACING_EACHOTHER + if(initator.dir + 2 == target.dir || initator.dir - 2 == target.dir || initator.dir + 6 == target.dir || initator.dir - 6 == target.dir) //Initating mob is looking at the target, while the target mob is looking in a direction perpendicular to the 1st + return FACING_INIT_FACING_TARGET_TARGET_FACING_PERPENDICULAR + +/proc/random_step(atom/movable/AM, steps, chance) + var/initial_chance = chance + while(steps > 0) + if(prob(chance)) + step(AM, pick(alldirs)) + chance = max(chance - (initial_chance / steps), 0) + steps-- + +/proc/living_player_count() + var/living_player_count = 0 + for(var/mob in player_list) + if(mob in living_mob_list) + living_player_count += 1 + return living_player_count + +/proc/randomColor(mode = 0) //if 1 it doesn't pick white, black or gray + switch(mode) + if(0) + return pick("white","black","gray","red","green","blue","brown","yellow","orange","darkred", + "crimson","lime","darkgreen","cyan","navy","teal","purple","indigo") + if(1) + return pick("red","green","blue","brown","yellow","orange","darkred","crimson", + "lime","darkgreen","cyan","navy","teal","purple","indigo") + else + return "white" + + +/proc/screen_loc2turf(scr_loc, turf/origin) + var/tX = text2list(scr_loc, ",") + var/tY = text2list(tX[2], ":") + var/tZ = origin.z + tY = tY[1] + tX = text2list(tX[1], ":") + tX = tX[1] + tX = max(1, min(world.maxx, origin.x + (text2num(tX) - (world.view + 1)))) + tY = max(1, min(world.maxy, origin.y + (text2num(tY) - (world.view + 1)))) + return locate(tX, tY, tZ) + +/proc/IsValidSrc(A) + if(istype(A, /datum)) + var/datum/B = A + return !B.gc_destroyed + if(istype(A, /client)) + return 1 + return 0 + + + +//Get the dir to the RIGHT of dir if they were on a clock +//NORTH --> NORTHEAST +/proc/get_clockwise_dir(dir) + . = angle2dir(dir2angle(dir)+45) + +//Get the dir to the LEFT of dir if they were on a clock +//NORTH --> NORTHWEST +/proc/get_anticlockwise_dir(dir) + . = angle2dir(dir2angle(dir)-45) + + +//Compare A's dir, the clockwise dir of A and the anticlockwise dir of A +//To the opposite dir of the dir returned by get_dir(B,A) +//If one of them is a match, then A is facing B +/proc/is_A_facing_B(atom/A,atom/B) + if(!istype(A) || !istype(B)) + return 0 + if(istype(A, /mob/living)) + var/mob/living/LA = A + if(LA.lying) + return 0 + var/goal_dir = angle2dir(dir2angle(get_dir(B,A)+180)) + var/clockwise_A_dir = get_clockwise_dir(A.dir) + var/anticlockwise_A_dir = get_anticlockwise_dir(B.dir) + + if(A.dir == goal_dir || clockwise_A_dir == goal_dir || anticlockwise_A_dir == goal_dir) + return 1 + return 0 + + +/* +rough example of the "cone" made by the 3 dirs checked + + B + \ + \ + > + < + \ + \ +B --><-- A + / + / + < + > + / + / + B + +*/ + + +//This is just so you can stop an orbit. +//orbit() can run without it (swap orbiting for A) +//but then you can never stop it and that's just silly. +/atom/movable/var/atom/orbiting = null +//we raise this each time orbit is called to prevent mutiple calls in a short time frame from breaking things +/atom/movable/var/orbitid = 0 + +/atom/movable/proc/orbit(atom/A, radius = 10, clockwise = 1, angle_increment = 15, lockinorbit = 0) + if(!istype(A)) + return + orbitid++ + var/myid = orbitid + if (orbiting) + stop_orbit() + //sadly this is the only way to ensure the original orbit proc stops + //and resets the atom's transform before we continue. + //time is based on the sleep in the loop and the time for the final animation of initial_transform. + sleep(2.6+world.tick_lag) + if (orbiting || !istype(A) || orbitid != myid) //post sleep re-check + return + orbiting = A + var/lastloc = loc + var/angle = 0 + var/matrix/initial_transform = matrix(transform) + + while(orbiting && orbiting.loc && orbitid == myid) + var/targetloc = get_turf(orbiting) + if (!lockinorbit && loc != lastloc && loc != targetloc) + break + loc = targetloc + lastloc = loc + angle += angle_increment + + var/matrix/shift = matrix(initial_transform) + shift.Translate(radius,0) + if(clockwise) + shift.Turn(angle) + else + shift.Turn(-angle) + animate(src, transform = shift, 2) + sleep(0.6) //the effect breaks above 0.6 delay + animate(src, transform = initial_transform, 2) + orbiting = null + + +/atom/movable/proc/stop_orbit() + if(orbiting) + loc = get_turf(orbiting) + orbiting = null + + +//Center's an image. +//Requires: +//The Image +//The x dimension of the icon file used in the image +//The y dimension of the icon file used in the image +// eg: center_image(I, 32,32) +// eg2: center_image(I, 96,96) + +/proc/center_image(var/image/I, x_dimension = 0, y_dimension = 0) + if(!I) + return + + if(!x_dimension || !y_dimension) + return + + if((x_dimension == world.icon_size) && (y_dimension == world.icon_size)) + return I + + //Offset the image so that it's bottom left corner is shifted this many pixels + //This makes it infinitely easier to draw larger inhands/images larger than world.iconsize + //but still use them in game + var/x_offset = -((x_dimension/world.icon_size)-1)*(world.icon_size*0.5) + var/y_offset = -((y_dimension/world.icon_size)-1)*(world.icon_size*0.5) + + //Correct values under world.icon_size + if(x_dimension < world.icon_size) + x_offset *= -1 + if(y_dimension < world.icon_size) + y_offset *= -1 + + I.pixel_x = x_offset + I.pixel_y = y_offset + + return I + +//similar function to range(), but with no limitations on the distance; will search spiralling outwards from the center +/proc/ultra_range(dist=0, center=usr, orange=0) + if(!dist) + if(!orange) + return list(center) + else + return list() + + var/turf/t_center = get_turf(center) + if(!t_center) + return list() + + var/list/L = list() + var/turf/T + var/y + var/x + var/c_dist = 1 + + if(!orange) + L += t_center + L += t_center.contents + + while( c_dist <= dist ) + y = t_center.y + c_dist + x = t_center.x - c_dist + 1 + for(x in x to t_center.x+c_dist) + T = locate(x,y,t_center.z) + if(T) + L += T + L += T.contents + + y = t_center.y + c_dist - 1 + x = t_center.x + c_dist + for(y in t_center.y-c_dist to y) + T = locate(x,y,t_center.z) + if(T) + L += T + L += T.contents + + y = t_center.y - c_dist + x = t_center.x + c_dist - 1 + for(x in t_center.x-c_dist to x) + T = locate(x,y,t_center.z) + if(T) + L += T + L += T.contents + + y = t_center.y - c_dist + 1 + x = t_center.x - c_dist + for(y in y to t_center.y+c_dist) + T = locate(x,y,t_center.z) + if(T) + L += T + L += T.contents + c_dist++ + + return L + +/atom/proc/contains(var/atom/location) + if(!location) + return 0 + for(location, location && location != src, location=location.loc); //semicolon is for the empty statement + if(location == src) + return 1 return 0 diff --git a/code/controllers/subsystem/nano.dm b/code/controllers/subsystem/nano.dm index 08b62d927b7..b6340cb8419 100644 --- a/code/controllers/subsystem/nano.dm +++ b/code/controllers/subsystem/nano.dm @@ -1,27 +1,27 @@ -var/datum/subsystem/nano/SSnano - -/datum/subsystem/nano - name = "NanoUI" - wait = 10 - priority = 16 - display = 6 - - can_fire = 1 // This needs to fire before round start. - - var/list/open_uis = list() // A list of open NanoUIs, grouped by src_object and ui_key. - var/list/processing_uis = list() // A list of processing NanoUIs, not grouped. - - -/datum/subsystem/nano/New() - NEW_SS_GLOBAL(SSnano) - -/datum/subsystem/nano/stat_entry() - ..("O:[open_uis.len]|P:[processing_uis.len]") // Show how many interfaces we have open/are processing. - -/datum/subsystem/nano/fire() // Process UIs. - for(var/thing in processing_uis) - var/datum/nanoui/ui = thing - if(ui && ui.user && ui.src_object) - ui.process() - continue - processing_uis.Remove(ui) +var/datum/subsystem/nano/SSnano + +/datum/subsystem/nano + name = "NanoUI" + wait = 10 + priority = 16 + display = 6 + + can_fire = 1 // This needs to fire before round start. + + var/list/open_uis = list() // A list of open NanoUIs, grouped by src_object and ui_key. + var/list/processing_uis = list() // A list of processing NanoUIs, not grouped. + + +/datum/subsystem/nano/New() + NEW_SS_GLOBAL(SSnano) + +/datum/subsystem/nano/stat_entry() + ..("O:[open_uis.len]|P:[processing_uis.len]") // Show how many interfaces we have open/are processing. + +/datum/subsystem/nano/fire() // Process UIs. + for(var/thing in processing_uis) + var/datum/nanoui/ui = thing + if(ui && ui.user && ui.src_object) + ui.process() + continue + processing_uis.Remove(ui) diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 92d990c2925..e68c5d08c52 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -1,1747 +1,1747 @@ -/* Note from Carnie: - The way datum/mind stuff works has been changed a lot. - Minds now represent IC characters rather than following a client around constantly. - - Guidelines for using minds properly: - - - Never mind.transfer_to(ghost). The var/current and var/original of a mind must always be of type mob/living! - ghost.mind is however used as a reference to the ghost's corpse - - - When creating a new mob for an existing IC character (e.g. cloning a dead guy or borging a brain of a human) - the existing mind of the old mob should be transfered to the new mob like so: - - mind.transfer_to(new_mob) - - - You must not assign key= or ckey= after transfer_to() since the transfer_to transfers the client for you. - By setting key or ckey explicitly after transfering the mind with transfer_to you will cause bugs like DCing - the player. - - - IMPORTANT NOTE 2, if you want a player to become a ghost, use mob.ghostize() It does all the hard work for you. - - - When creating a new mob which will be a new IC character (e.g. putting a shade in a construct or randomly selecting - a ghost to become a xeno during an event). Simply assign the key or ckey like you've always done. - - new_mob.key = key - - The Login proc will handle making a new mob for that mobtype (including setting up stuff like mind.name). Simple! - However if you want that mind to have any special properties like being a traitor etc you will have to do that - yourself. - -*/ - -/datum/mind - var/key - var/name //replaces mob/var/original_name - var/mob/living/current - var/active = 0 - - var/memory - - var/assigned_role - var/special_role - var/list/restricted_roles = list() - - var/datum/job/assigned_job - - var/list/datum/objective/objectives = list() - var/list/datum/objective/special_verbs = list() - - var/list/spell_list = list() // Wizard mode & "Give Spell" badmin button. - - var/datum/faction/faction //associated faction - var/datum/changeling/changeling //changeling holder - - var/miming = 0 // Mime's vow of silence - var/antag_hud_icon_state = null //this mind's ANTAG_HUD should have this icon_state - var/datum/atom_hud/antag/antag_hud = null //this mind's antag HUD - var/datum/gang/gang_datum //Which gang this mind belongs to, if any - -/datum/mind/New(var/key) - src.key = key - - -/datum/mind/proc/transfer_to(mob/new_character) - //if(!istype(new_character)) - // throw EXCEPTION("transfer_to(): new_character must be mob/living") - // return - - if(current) //remove ourself from our old body's mind variable - current.mind = null - - SSnano.user_transferred(current, new_character) - - if(key) - if(new_character.key != key) //if we're transfering into a body with a key associated which is not ours - new_character.ghostize(1) //we'll need to ghostize so that key isn't mobless. - else - key = new_character.key - - if(new_character.mind) //disassociate any mind currently in our new body's mind variable - new_character.mind.current = null - - var/datum/atom_hud/antag/hud_to_transfer = antag_hud//we need this because leave_hud() will clear this list - leave_all_huds() //leave all the huds in the old body, so it won't get huds if somebody else enters it - current = new_character //associate ourself with our new body - new_character.mind = src //and associate our new body with ourself - transfer_antag_huds(hud_to_transfer) //inherit the antag HUD - transfer_actions(new_character) - - if(active) - new_character.key = key //now transfer the key to link the client to our new body - -/datum/mind/proc/store_memory(new_text) - memory += "[new_text]
" - -/datum/mind/proc/wipe_memory() - memory = null - - -/* - Removes antag type's references from a mind. - objectives, uplinks, powers etc are all handled. -*/ - -/datum/mind/proc/remove_objectives() - if(objectives.len) - for(var/datum/objective/O in objectives) - objectives -= O - qdel(O) - -/datum/mind/proc/remove_changeling() - if(src in ticker.mode.changelings) - ticker.mode.changelings -= src - current.remove_changeling_powers() - if(changeling) - qdel(changeling) - changeling = null - special_role = null - remove_antag_equip() - -/datum/mind/proc/remove_traitor() - if(src in ticker.mode.traitors) - ticker.mode.traitors -= src - if(isAI(current)) - var/mob/living/silicon/ai/A = current - A.set_zeroth_law("") - A.show_laws() - special_role = null - remove_antag_equip() - -/datum/mind/proc/remove_nukeop() - if(src in ticker.mode.syndicates) - ticker.mode.syndicates -= src - ticker.mode.update_synd_icons_removed(src) - special_role = null - remove_objectives() - remove_antag_equip() - -/datum/mind/proc/remove_wizard() - if(src in ticker.mode.wizards) - ticker.mode.wizards -= src - current.spellremove(current) - special_role = null - remove_antag_equip() - -/datum/mind/proc/remove_cultist() - if(src in ticker.mode.cult) - ticker.mode.cult -= src - ticker.mode.update_cult_icons_removed(src) - special_role = null - -/datum/mind/proc/remove_rev() - if(src in ticker.mode.revolutionaries) - ticker.mode.revolutionaries -= src - ticker.mode.update_rev_icons_removed(src) - if(src in ticker.mode.head_revolutionaries) - ticker.mode.head_revolutionaries -= src - ticker.mode.update_rev_icons_removed(src) - special_role = null - remove_objectives() - remove_antag_equip() - - -/datum/mind/proc/remove_gang() - ticker.mode.remove_gangster(src,0,1,1) - remove_objectives() - -/datum/mind/proc/remove_malf() - if(src in ticker.mode.malf_ai) - ticker.mode.malf_ai -= src - var/mob/living/silicon/ai/A = current - A.verbs.Remove(/mob/living/silicon/ai/proc/choose_modules, - /datum/game_mode/malfunction/proc/takeover, - /datum/game_mode/malfunction/proc/ai_win) - A.malf_picker.remove_verbs(A) - A.make_laws() - qdel(A.malf_picker) - A.show_laws() - A.icon_state = "ai" - special_role = null - remove_objectives() - remove_antag_equip() - -/datum/mind/proc/remove_hog_follower_prophet() - ticker.mode.red_deity_followers -= src - ticker.mode.red_deity_prophets -= src - ticker.mode.blue_deity_prophets -= src - ticker.mode.blue_deity_followers -= src - ticker.mode.update_hog_icons_removed(src, "red") - ticker.mode.update_hog_icons_removed(src, "blue") - - - -/datum/mind/proc/remove_antag_equip() - var/list/Mob_Contents = current.get_contents() - for(var/obj/item/I in Mob_Contents) - if(istype(I, /obj/item/device/pda)) - var/obj/item/device/pda/P = I - P.lock_code = "" - - else if(istype(I, /obj/item/device/radio)) - var/obj/item/device/radio/R = I - R.traitor_frequency = 0 - -/datum/mind/proc/remove_all_antag() //For the Lazy amongst us. - remove_changeling() - remove_traitor() - remove_nukeop() - remove_wizard() - remove_cultist() - remove_rev() - remove_malf() - remove_gang() - -/datum/mind/proc/show_memory(mob/recipient, window=1) - if(!recipient) - recipient = current - var/output = "[current.real_name]'s Memories:
" - output += memory - - if(objectives.len) - output += "Objectives:" - var/obj_count = 1 - for(var/datum/objective/objective in objectives) - output += "
Objective #[obj_count++]: [objective.explanation_text]" - - if(window) recipient << browse(output,"window=memory") - else recipient << "[output]" - -/datum/mind/proc/edit_memory() - if(!ticker || !ticker.mode) - alert("Not before round-start!", "Alert") - return - - var/out = "[name][(current&&(current.real_name!=name))?" (as [current.real_name])":""]
" - out += "Mind currently owned by key: [key] [active?"(synced)":"(not synced)"]
" - out += "Assigned role: [assigned_role]. Edit
" - out += "Faction and special role: [special_role]
" - - var/list/sections = list( - "revolution", - "gang", - "cult", - "wizard", - "changeling", - "nuclear", - "traitor", // "traitorchan", - "monkey", - "malfunction", - ) - var/text = "" - - if (istype(current, /mob/living/carbon/human) || istype(current, /mob/living/carbon/monkey)) - /** REVOLUTION ***/ - text = "revolution" - if (ticker.mode.config_tag=="revolution") - text = uppertext(text) - text = "[text]: " - if (assigned_role in command_positions) - text += "HEAD|loyal|employee|headrev|rev" - else if (src in ticker.mode.head_revolutionaries) - text += "head|loyal|employee|HEADREV|rev" - text += "
Flash: give" - - var/list/L = current.get_contents() - var/obj/item/device/assembly/flash/flash = locate() in L - if (flash) - if(!flash.crit_fail) - text += "|take." - else - text += "|take|repair." - else - text += "." - - text += " Reequip (gives traitor uplink)." - if (objectives.len==0) - text += "
Objectives are empty! Set to kill all heads." - else if(isloyal(current)) - text += "head|LOYAL|employee|headrev|rev" - else if (src in ticker.mode.revolutionaries) - text += "head|loyal|employee|headrev|REV" - else - text += "head|loyal|EMPLOYEE|headrev|rev" - - if(current && current.client && (ROLE_REV in current.client.prefs.be_special)) - text += "|Enabled in Prefs" - else - text += "|Disabled in Prefs" - - sections["revolution"] = text - - /** GANG ***/ - text = "gang" - if (ticker.mode.config_tag=="gang") - text = uppertext(text) - text = "[text]: " - text += "[isloyal(current) ? "LOYAL" : "loyal"]|" - if(src in ticker.mode.get_all_gangsters()) - text += "none" - else - text += "NONE" - - if(current && current.client && (ROLE_GANG in current.client.prefs.be_special)) - text += "|Enabled in Prefs
" - else - text += "|Disabled in Prefs
" - - for(var/datum/gang/G in ticker.mode.gangs) - text += "[G.name]: " - if(src in (G.gangsters)) - text += "GANGSTER" - else - text += "gangster" - text += "|" - if(src in (G.bosses)) - text += "GANG LEADER" - text += "|Equipment: give" - var/list/L = current.get_contents() - var/obj/item/device/gangtool/gangtool = locate() in L - if (gangtool) - text += "|take" - - else - text += "gang leader" - text += "
" - - if(gang_colors_pool.len) - text += "Create New Gang" - - sections["gang"] = text - - - /** CULT ***/ - text = "cult" - if (ticker.mode.config_tag=="cult") - text = uppertext(text) - text = "[text]: " - if (src in ticker.mode.cult) - text += "loyal|employee|CULTIST" - text += "
Equip" - - else if(isloyal(current)) - text += "LOYAL|employee|cultist" - else - text += "loyal|EMPLOYEE|cultist" - - if(current && current.client && (ROLE_CULTIST in current.client.prefs.be_special)) - text += "|Enabled in Prefs" - else - text += "|Disabled in Prefs" - - sections["cult"] = text - - /** WIZARD ***/ - text = "wizard" - if (ticker.mode.config_tag=="wizard") - text = uppertext(text) - text = "[text]: " - if ((src in ticker.mode.wizards) || (src in ticker.mode.apprentices)) - text += "YES|no" - text += "
To lair, undress, dress up, let choose name." - if (objectives.len==0) - text += "
Objectives are empty! Randomize!" - else - text += "yes|NO" - - if(current && current.client && (ROLE_WIZARD in current.client.prefs.be_special)) - text += "|Enabled in Prefs" - else - text += "|Disabled in Prefs" - - sections["wizard"] = text - - /** CHANGELING ***/ - text = "changeling" - if (ticker.mode.config_tag=="changeling" || ticker.mode.config_tag=="traitorchan") - text = uppertext(text) - text = "[text]: " - if ((src in ticker.mode.changelings) && special_role) - text += "YES|no" - if (objectives.len==0) - text += "
Objectives are empty! Randomize!" - if(changeling && changeling.stored_profiles.len && (current.real_name != changeling.first_prof.name) ) - text += "
Transform to initial appearance." - else if(src in ticker.mode.changelings) //Station Aligned Changeling - text += "YES (but not an antag)|no" - if (objectives.len==0) - text += "
Objectives are empty! Randomize!" - if(changeling && changeling.stored_profiles.len && (current.real_name != changeling.first_prof.name) ) - text += "
Transform to initial appearance." - else - text += "yes|NO" -// var/datum/game_mode/changeling/changeling = ticker.mode -// if (istype(changeling) && changeling.changelingdeath) -// text += "
All the changelings are dead! Restart in [round((changeling.TIME_TO_GET_REVIVED-(world.time-changeling.changelingdeathtime))/10)] seconds." - - if(current && current.client && (ROLE_CHANGELING in current.client.prefs.be_special)) - text += "|Enabled in Prefs" - else - text += "|Disabled in Prefs" - - sections["changeling"] = text - - /** NUCLEAR ***/ - text = "nuclear" - if (ticker.mode.config_tag=="nuclear") - text = uppertext(text) - text = "[text]: " - if (src in ticker.mode.syndicates) - text += "OPERATIVE|nanotrasen" - text += "
To shuttle, undress, dress up." - var/code - for (var/obj/machinery/nuclearbomb/bombue in machines) - if (length(bombue.r_code) <= 5 && bombue.r_code != "LOLNO" && bombue.r_code != "ADMIN") - code = bombue.r_code - break - if (code) - text += " Code is [code]. tell the code." - else - text += "operative|NANOTRASEN" - - if(current && current.client && (ROLE_OPERATIVE in current.client.prefs.be_special)) - text += "|Enabled in Prefs" - else - text += "|Disabled in Prefs" - - sections["nuclear"] = text - - /** TRAITOR ***/ - text = "traitor" - if (ticker.mode.config_tag=="traitor" || ticker.mode.config_tag=="traitorchan") - text = uppertext(text) - text = "[text]: " - if (src in ticker.mode.traitors) - text += "TRAITOR|loyal" - if (objectives.len==0) - text += "
Objectives are empty! Randomize!" - else - text += "traitor|LOYAL" - - if(current && current.client && (ROLE_TRAITOR in current.client.prefs.be_special)) - text += "|Enabled in Prefs" - else - text += "|Disabled in Prefs" - - sections["traitor"] = text - - /** SHADOWLING **/ - text = "shadowling" - if(ticker.mode.config_tag == "shadowling") - text = uppertext(text) - text = "[text]: " - if(src in ticker.mode.shadows) - text += "SHADOWLING|thrall|human" - else if(src in ticker.mode.thralls) - text += "shadowling|THRALL|human" - else - text += "shadowling|thrall|HUMAN" - - if(current && current.client && (ROLE_SHADOWLING in current.client.prefs.be_special)) - text += "|Enabled in Prefs" - else - text += "|Disabled in Prefs" - - sections["shadowling"] = text - - /** Abductors **/ - - text = "Abductor" - if(ticker.mode.config_tag == "abductor") - text = uppertext(text) - text = "[text]: " - if(src in ticker.mode.abductors) - text += "Abductor|human" - text += "|undress|equip" - else - text += "Abductor|human" - - if(current && current.client && (ROLE_ABDUCTOR in current.client.prefs.be_special)) - text += "|Enabled in Prefs" - else - text += "|Disabled in Prefs" - - sections["abductor"] = text - - /** HAND OF GOD **/ - text = "hand of god" - if(ticker.mode.config_tag == "handofgod") - text = uppertext(text) - text = "[text]: " - if(src in ticker.mode.red_deity_prophets) - text += "RED PROPHET|red follower|employee|blue follower|blue prophet|red god|blue god" - else if (src in ticker.mode.red_deity_followers) - text += "red prophet|RED FOLLOWER|employee|blue follower|blue prophet|red god|blue god" - else if (src in ticker.mode.blue_deity_followers) - text += "red prophet|red follower|employee|BLUE FOLLOWER|blue prophet|red god|blue god" - else if (src in ticker.mode.blue_deity_prophets) - text += "red prophet|red follower|employee|blue follower|BLUE PROPHET|red god|blue god" - else if (src in ticker.mode.red_deities) - text += "red prophet|red follower|employee|blue follower|blue prophet|RED GOD|blue god" - else if (src in ticker.mode.blue_deities) - text += "red prophet|red follower|employee|blue follower|blue prophet|red god|BLUE GOD" - else - text += "red prophet|red follower|EMPLOYEE|blue follower|blue prophet|red god|blue god" - - if(current && current.client && (ROLE_HOG_GOD in current.client.prefs.be_special)) - text += "|HOG God Enabled in Prefs" - else - text += "|HOG God Disabled in Prefs" - - if(current && current.client && (ROLE_HOG_CULTIST in current.client.prefs.be_special)) - text += "|HOG Cultist Enabled in Prefs" - else - text += "|HOG Disabled in Prefs" - - sections["follower"] = text - - /** MONKEY ***/ - if (istype(current, /mob/living/carbon)) - text = "monkey" - if (ticker.mode.config_tag=="monkey") - text = uppertext(text) - text = "[text]: " - if (istype(current, /mob/living/carbon/human)) - text += "healthy|infected|HUMAN|other" - else if (istype(current, /mob/living/carbon/monkey)) - var/found = 0 - for(var/datum/disease/D in current.viruses) - if(istype(D, /datum/disease/transformation/jungle_fever)) found = 1 - - if(found) - text += "healthy|INFECTED|human|other" - else - text += "HEALTHY|infected|human|other" - - else - text += "healthy|infected|human|OTHER" - - if(current && current.client && (ROLE_MONKEY in current.client.prefs.be_special)) - text += "|Enabled in Prefs" - else - text += "|Disabled in Prefs" - - sections["monkey"] = text - - - /** SILICON ***/ - - if (istype(current, /mob/living/silicon)) - text = "silicon" - if (ticker.mode.config_tag=="malfunction") - text = uppertext(text) - text = "[text]: " - if (istype(current, /mob/living/silicon/ai)) - if (src in ticker.mode.malf_ai) - text += "MALF|not malf" - else - text += "malf|NOT MALF" - var/mob/living/silicon/robot/robot = current - if (istype(robot) && robot.emagged) - text += "
Cyborg: Is emagged! Unemag!
0th law: [robot.laws.zeroth]" - var/mob/living/silicon/ai/ai = current - if (istype(ai) && ai.connected_robots.len) - var/n_e_robots = 0 - for (var/mob/living/silicon/robot/R in ai.connected_robots) - if (R.emagged) - n_e_robots++ - text += "
[n_e_robots] of [ai.connected_robots.len] slaved cyborgs are emagged. Unemag" - - if(current && current.client && (ROLE_MALF in current.client.prefs.be_special)) - text += "|Enabled in Prefs" - else - text += "|Disabled in Prefs" - - sections["malfunction"] = text - - if (ticker.mode.config_tag == "traitorchan") - if (sections["traitor"]) - out += sections["traitor"]+"
" - if (sections["changeling"]) - out += sections["changeling"]+"

" - sections -= "traitor" - sections -= "changeling" - else - if (sections[ticker.mode.config_tag]) - out += sections[ticker.mode.config_tag]+"

" - sections -= ticker.mode.config_tag - for (var/i in sections) - if (sections[i]) - out += sections[i]+"
" - - - if (((src in ticker.mode.head_revolutionaries) || \ - (src in ticker.mode.traitors) || \ - (src in ticker.mode.syndicates)) && \ - istype(current,/mob/living/carbon/human) ) - - text = "Uplink: give" - var/obj/item/device/uplink/hidden/suplink = find_syndicate_uplink() - var/crystals - if (suplink) - crystals = suplink.uses - if (suplink) - text += "|take" - if (check_rights(R_FUN, 0)) - text += ", [crystals] crystals" - else - text += ", [crystals] crystals" - text += "." //hiel grammar - out += text - - out += "

" - - out += "Memory:
" - out += memory - out += "
Edit memory
" - out += "Objectives:
" - if (objectives.len == 0) - out += "EMPTY
" - else - var/obj_count = 1 - for(var/datum/objective/objective in objectives) - out += "[obj_count]: [objective.explanation_text] Edit Delete Toggle Completion
" - obj_count++ - out += "Add objective

" - - out += "Announce objectives

" - - usr << browse(out, "window=edit_memory[src];size=500x600") - - -/datum/mind/Topic(href, href_list) - if(!check_rights(R_ADMIN)) return - - if (href_list["role_edit"]) - var/new_role = input("Select new role", "Assigned role", assigned_role) as null|anything in get_all_jobs() - if (!new_role) return - assigned_role = new_role - - else if (href_list["memory_edit"]) - var/new_memo = copytext(sanitize(input("Write new memory", "Memory", memory) as null|message),1,MAX_MESSAGE_LEN) - if (isnull(new_memo)) return - memory = new_memo - - else if (href_list["obj_edit"] || href_list["obj_add"]) - var/datum/objective/objective - var/objective_pos - var/def_value - - if (href_list["obj_edit"]) - objective = locate(href_list["obj_edit"]) - if (!objective) return - objective_pos = objectives.Find(objective) - - //Text strings are easy to manipulate. Revised for simplicity. - var/temp_obj_type = "[objective.type]"//Convert path into a text string. - def_value = copytext(temp_obj_type, 19)//Convert last part of path into an objective keyword. - if(!def_value)//If it's a custom objective, it will be an empty string. - def_value = "custom" - - var/new_obj_type = input("Select objective type:", "Objective type", def_value) as null|anything in list("assassinate", "maroon", "debrain", "protect", "destroy", "prevent", "hijack", "escape", "survive", "martyr", "steal", "download", "nuclear", "capture", "absorb", "custom","follower block (HOG)","build (HOG)","deicide (HOG)", "follower escape (HOG)", "sacrifice prophet (HOG)") - if (!new_obj_type) return - - var/datum/objective/new_objective = null - - switch (new_obj_type) - if ("assassinate","protect","debrain","maroon") - var/list/possible_targets = list("Free objective") - for(var/datum/mind/possible_target in ticker.minds) - if ((possible_target != src) && istype(possible_target.current, /mob/living/carbon/human)) - possible_targets += possible_target.current - - var/mob/def_target = null - var/objective_list[] = list(/datum/objective/assassinate, /datum/objective/protect, /datum/objective/debrain, /datum/objective/maroon) - if (objective&&(objective.type in objective_list) && objective:target) - def_target = objective:target.current - - var/new_target = input("Select target:", "Objective target", def_target) as null|anything in possible_targets - if (!new_target) return - - var/objective_path = text2path("/datum/objective/[new_obj_type]") - if (new_target == "Free objective") - new_objective = new objective_path - new_objective.owner = src - new_objective:target = null - new_objective.explanation_text = "Free objective" - else - new_objective = new objective_path - new_objective.owner = src - new_objective:target = new_target:mind - //Will display as special role if the target is set as MODE. Ninjas/commandos/nuke ops. - new_objective.update_explanation_text() - - if ("destroy") - var/list/possible_targets = active_ais(1) - if(possible_targets.len) - var/mob/new_target = input("Select target:", "Objective target") as null|anything in possible_targets - new_objective = new /datum/objective/destroy - new_objective.target = new_target.mind - new_objective.owner = src - new_objective.update_explanation_text() - else - usr << "No active AIs with minds" - - if ("prevent") - new_objective = new /datum/objective/block - new_objective.owner = src - - if ("hijack") - new_objective = new /datum/objective/hijack - new_objective.owner = src - - if ("escape") - new_objective = new /datum/objective/escape - new_objective.owner = src - - if ("survive") - new_objective = new /datum/objective/survive - new_objective.owner = src - - if("martyr") - new_objective = new /datum/objective/martyr - new_objective.owner = src - - if ("nuclear") - new_objective = new /datum/objective/nuclear - new_objective.owner = src - - if ("steal") - if (!istype(objective, /datum/objective/steal)) - new_objective = new /datum/objective/steal - new_objective.owner = src - else - new_objective = objective - var/datum/objective/steal/steal = new_objective - if (!steal.select_target()) - return - - if("download","capture","absorb") - var/def_num - if(objective&&objective.type==text2path("/datum/objective/[new_obj_type]")) - def_num = objective.target_amount - - var/target_number = input("Input target number:", "Objective", def_num) as num|null - if (isnull(target_number))//Ordinarily, you wouldn't need isnull. In this case, the value may already exist. - return - - switch(new_obj_type) - if("download") - new_objective = new /datum/objective/download - new_objective.explanation_text = "Download [target_number] research levels." - if("capture") - new_objective = new /datum/objective/capture - new_objective.explanation_text = "Capture [target_number] lifeforms with an energy net. Live, rare specimens are worth more." - if("absorb") - new_objective = new /datum/objective/absorb - new_objective.explanation_text = "Absorb [target_number] compatible genomes." - new_objective.owner = src - new_objective.target_amount = target_number - - if("follower block (HOG)") - new_objective = new /datum/objective/follower_block - new_objective.owner = src - if("build (HOG)") - new_objective = new /datum/objective/build - new_objective.owner = src - if("deicide (HOG)") - new_objective = new /datum/objective/deicide - new_objective.owner = src - if("follower escape (HOG)") - new_objective = new /datum/objective/escape_followers - new_objective.owner = src - if("sacrifice prophet (HOG)") - new_objective = new /datum/objective/sacrifice_prophet - new_objective.owner = src - - if ("custom") - var/expl = stripped_input(usr, "Custom objective:", "Objective", objective ? objective.explanation_text : "") - if (!expl) return - new_objective = new /datum/objective - new_objective.owner = src - new_objective.explanation_text = expl - - if (!new_objective) return - - if (objective) - objectives -= objective - objectives.Insert(objective_pos, new_objective) - message_admins("[key_name_admin(usr)] edited [current]'s objective to [new_objective.explanation_text]") - log_admin("[key_name(usr)] edited [current]'s objective to [new_objective.explanation_text]") - else - objectives += new_objective - message_admins("[key_name_admin(usr)] added a new objective for [current]: [new_objective.explanation_text]") - log_admin("[key_name(usr)] added a new objective for [current]: [new_objective.explanation_text]") - - else if (href_list["obj_delete"]) - var/datum/objective/objective = locate(href_list["obj_delete"]) - if(!istype(objective)) return - objectives -= objective - message_admins("[key_name_admin(usr)] removed an objective for [current]: [objective.explanation_text]") - log_admin("[key_name(usr)] removed an objective for [current]: [objective.explanation_text]") - - else if(href_list["obj_completed"]) - var/datum/objective/objective = locate(href_list["obj_completed"]) - if(!istype(objective)) return - objective.completed = !objective.completed - log_admin("[key_name(usr)] toggled the win state for [current]'s objective: [objective.explanation_text]") - - else if (href_list["handofgod"]) - switch(href_list["handofgod"]) - if("clear") //wipe handofgod status - if((src in ticker.mode.red_deity_followers) || (src in ticker.mode.blue_deity_followers) || (src in ticker.mode.red_deity_prophets) || (src in ticker.mode.blue_deity_prophets)) - remove_hog_follower_prophet() - current << "You have been brainwashed... again! Your faith is no more!" - message_admins("[key_name_admin(usr)] has de-hand of god'ed [current].") - log_admin("[key_name(usr)] has de-hand of god'ed [current].") - - if("red follower") - make_Handofgod_follower("red") - message_admins("[key_name_admin(usr)] has red follower'ed [current].") - log_admin("[key_name(usr)] has red follower'ed [current].") - - if("red prophet") - make_Handofgod_prophet("red") - message_admins("[key_name_admin(usr)] has red prophet'ed [current].") - log_admin("[key_name(usr)] has red prophet'ed [current].") - - if("blue follower") - make_Handofgod_follower("blue") - message_admins("[key_name_admin(usr)] has blue follower'ed [current].") - log_admin("[key_name(usr)] has blue follower'ed [current].") - - if("blue prophet") - make_Handofgod_prophet("blue") - message_admins("[key_name_admin(usr)] has blue prophet'ed [current].") - log_admin("[key_name(usr)] has blue prophet'ed [current].") - - if("red god") - make_Handofgod_god("red") - message_admins("[key_name_admin(usr)] has red god'ed [current].") - log_admin("[key_name(usr)] has red god'ed [current].") - - if("blue god") - make_Handofgod_god("blue") - message_admins("[key_name_admin(usr)] has blue god'ed [current].") - log_admin("[key_name(usr)] has blue god'ed [current].") - - - else if (href_list["revolution"]) - switch(href_list["revolution"]) - if("clear") - remove_rev() - current << "You have been brainwashed! You are no longer a revolutionary!" - message_admins("[key_name_admin(usr)] has de-rev'ed [current].") - log_admin("[key_name(usr)] has de-rev'ed [current].") - if("rev") - if(src in ticker.mode.head_revolutionaries) - ticker.mode.head_revolutionaries -= src - ticker.mode.update_rev_icons_removed(src) - current << "Revolution has been disappointed of your leader traits! You are a regular revolutionary now!" - else if(!(src in ticker.mode.revolutionaries)) - current << " You are now a revolutionary! Help your cause. Do not harm your fellow freedom fighters. You can identify your comrades by the red \"R\" icons, and your leaders by the blue \"R\" icons. Help them kill the heads to win the revolution!" - else - return - ticker.mode.revolutionaries += src - ticker.mode.update_rev_icons_added(src) - special_role = "Revolutionary" - message_admins("[key_name_admin(usr)] has rev'ed [current].") - log_admin("[key_name(usr)] has rev'ed [current].") - - if("headrev") - if(src in ticker.mode.revolutionaries) - ticker.mode.revolutionaries -= src - ticker.mode.update_rev_icons_removed(src) - current << "You have proved your devotion to revoltion! Yea are a head revolutionary now!" - else if(!(src in ticker.mode.head_revolutionaries)) - current << "You are a member of the revolutionaries' leadership now!" - else - return - if (ticker.mode.head_revolutionaries.len>0) - // copy targets - var/datum/mind/valid_head = locate() in ticker.mode.head_revolutionaries - if (valid_head) - for (var/datum/objective/mutiny/O in valid_head.objectives) - var/datum/objective/mutiny/rev_obj = new - rev_obj.owner = src - rev_obj.target = O.target - rev_obj.explanation_text = "Assassinate [O.target.name], the [O.target.assigned_role]." - objectives += rev_obj - ticker.mode.greet_revolutionary(src,0) - ticker.mode.head_revolutionaries += src - ticker.mode.update_rev_icons_added(src) - special_role = "Head Revolutionary" - message_admins("[key_name_admin(usr)] has head-rev'ed [current].") - log_admin("[key_name(usr)] has head-rev'ed [current].") - - if("autoobjectives") - ticker.mode.forge_revolutionary_objectives(src) - ticker.mode.greet_revolutionary(src,0) - usr << "The objectives for revolution have been generated and shown to [key]" - - if("flash") - if (!ticker.mode.equip_revolutionary(current)) - usr << "Spawning flash failed!" - - if("takeflash") - var/list/L = current.get_contents() - var/obj/item/device/assembly/flash/flash = locate() in L - if (!flash) - usr << "Deleting flash failed!" - qdel(flash) - - if("repairflash") - var/list/L = current.get_contents() - var/obj/item/device/assembly/flash/flash = locate() in L - if (!flash) - usr << "Repairing flash failed!" - else - flash.crit_fail = 0 - flash.update_icon() - - - -//////////////////// GANG MODE - - else if (href_list["gang"]) - switch(href_list["gang"]) - if("clear") - remove_gang() - message_admins("[key_name_admin(usr)] has de-gang'ed [current].") - log_admin("[key_name(usr)] has de-gang'ed [current].") - - if("equip") - switch(ticker.mode.equip_gang(current,gang_datum)) - if(1) - usr << "Unable to equip territory spraycan!" - if(2) - usr << "Unable to equip recruitment pen and spraycan!" - if(3) - usr << "Unable to equip gangtool, pen, and spraycan!" - - if("takeequip") - var/list/L = current.get_contents() - for(var/obj/item/weapon/pen/gang/pen in L) - qdel(pen) - for(var/obj/item/device/gangtool/gangtool in L) - qdel(gangtool) - for(var/obj/item/toy/crayon/spraycan/gang/SC in L) - qdel(SC) - - if("new") - if(gang_colors_pool.len) - var/list/names = list("Random") + gang_name_pool - var/gangname = input("Pick a gang name.","Select Name") as null|anything in names - if(gangname && gang_colors_pool.len) //Check again just in case another admin made max gangs at the same time - if(!(gangname in gang_name_pool)) - gangname = null - var/datum/gang/newgang = new(null,gangname) - ticker.mode.gangs += newgang - message_admins("[key_name_admin(usr)] has created the [newgang.name] Gang.") - log_admin("[key_name(usr)] has created the [newgang.name] Gang.") - - else if (href_list["gangboss"]) - var/datum/gang/G = locate(href_list["gangboss"]) in ticker.mode.gangs - if(!G || (src in G.bosses)) - return - ticker.mode.remove_gangster(src,0,2,1) - G.bosses += src - gang_datum = G - special_role = "[G.name] Gang Boss" - G.add_gang_hud(src) - current << "You are a [G.name] Gang Boss!" - message_admins("[key_name_admin(usr)] has added [current] to the [G.name] Gang leadership.") - log_admin("[key_name(usr)] has added [current] to the [G.name] Gang leadership.") - ticker.mode.forge_gang_objectives(src) - ticker.mode.greet_gang(src,0) - - else if (href_list["gangster"]) - var/datum/gang/G = locate(href_list["gangster"]) in ticker.mode.gangs - if(!G || (src in G.gangsters)) - return - ticker.mode.remove_gangster(src,0,2,1) - ticker.mode.add_gangster(src,G,0) - message_admins("[key_name_admin(usr)] has added [current] to the [G.name] Gang (A).") - log_admin("[key_name(usr)] has added [current] to the [G.name] Gang (A).") - -///////////////////////////////// - - - - else if (href_list["cult"]) - switch(href_list["cult"]) - if("clear") - remove_cultist() - current << "You have been brainwashed! You are no longer a cultist!" - message_admins("[key_name_admin(usr)] has de-cult'ed [current].") - log_admin("[key_name(usr)] has de-cult'ed [current].") - if("cultist") - if(!(src in ticker.mode.cult)) - ticker.mode.add_cultist(src) - message_admins("[key_name_admin(usr)] has cult'ed [current].") - log_admin("[key_name(usr)] has cult'ed [current].") - if("equip") - if (!ticker.mode.equip_cultist(current)) - usr << "equip_cultist() failed! [current]'s starting equipment will be incomplete." - - else if (href_list["wizard"]) - switch(href_list["wizard"]) - if("clear") - remove_wizard() - current << "You have been brainwashed! You are no longer a wizard!" - log_admin("[key_name(usr)] has de-wizard'ed [current].") - if("wizard") - if(!(src in ticker.mode.wizards)) - ticker.mode.wizards += src - special_role = "Wizard" - //ticker.mode.learn_basic_spells(current) - current << "You are the Space Wizard!" - message_admins("[key_name_admin(usr)] has wizard'ed [current].") - log_admin("[key_name(usr)] has wizard'ed [current].") - if("lair") - current.loc = pick(wizardstart) - if("dressup") - ticker.mode.equip_wizard(current) - if("name") - ticker.mode.name_wizard(current) - if("autoobjectives") - ticker.mode.forge_wizard_objectives(src) - usr << "The objectives for wizard [key] have been generated. You can edit them and anounce manually." - - else if (href_list["changeling"]) - switch(href_list["changeling"]) - if("clear") - remove_changeling() - current << "You grow weak and lose your powers! You are no longer a changeling and are stuck in your current form!" - message_admins("[key_name_admin(usr)] has de-changeling'ed [current].") - log_admin("[key_name(usr)] has de-changeling'ed [current].") - if("changeling") - if(!(src in ticker.mode.changelings)) - ticker.mode.changelings += src - current.make_changeling() - special_role = "Changeling" - current << "Your powers are awoken. A flash of memory returns to us...we are [changeling.changelingID], a changeling!" - message_admins("[key_name_admin(usr)] has changeling'ed [current].") - log_admin("[key_name(usr)] has changeling'ed [current].") - if("autoobjectives") - ticker.mode.forge_changeling_objectives(src) - usr << "The objectives for changeling [key] have been generated. You can edit them and anounce manually." - - if("initialdna") - if( !changeling || !changeling.stored_profiles.len || !istype(current, /mob/living/carbon)) - usr << "Resetting DNA failed!" - else - var/mob/living/carbon/C = current - changeling.first_prof.dna.transfer_identity(C, transfer_SE=1) - C.real_name = changeling.first_prof.name - C.updateappearance(mutcolor_update=1) - C.domutcheck() - - else if (href_list["nuclear"]) - switch(href_list["nuclear"]) - if("clear") - remove_nukeop() - current << "You have been brainwashed! You are no longer a syndicate operative!" - message_admins("[key_name_admin(usr)] has de-nuke op'ed [current].") - log_admin("[key_name(usr)] has de-nuke op'ed [current].") - if("nuclear") - if(!(src in ticker.mode.syndicates)) - ticker.mode.syndicates += src - ticker.mode.update_synd_icons_added(src) - if (ticker.mode.syndicates.len==1) - ticker.mode.prepare_syndicate_leader(src) - else - current.real_name = "[syndicate_name()] Operative #[ticker.mode.syndicates.len-1]" - special_role = "Syndicate" - assigned_role = "Syndicate" - current << "You are a [syndicate_name()] agent!" - ticker.mode.forge_syndicate_objectives(src) - ticker.mode.greet_syndicate(src) - message_admins("[key_name_admin(usr)] has nuke op'ed [current].") - log_admin("[key_name(usr)] has nuke op'ed [current].") - if("lair") - current.loc = get_turf(locate("landmark*Syndicate-Spawn")) - if("dressup") - var/mob/living/carbon/human/H = current - qdel(H.belt) - qdel(H.back) - qdel(H.ears) - qdel(H.gloves) - qdel(H.head) - qdel(H.shoes) - qdel(H.wear_id) - qdel(H.wear_suit) - qdel(H.w_uniform) - - if (!ticker.mode.equip_syndicate(current)) - usr << "Equipping a syndicate failed!" - if("tellcode") - var/code - for (var/obj/machinery/nuclearbomb/bombue in machines) - if (length(bombue.r_code) <= 5 && bombue.r_code != "LOLNO" && bombue.r_code != "ADMIN") - code = bombue.r_code - break - if (code) - store_memory("Syndicate Nuclear Bomb Code: [code]", 0, 0) - current << "The nuclear authorization code is: [code]" - else - usr << "No valid nuke found!" - - else if (href_list["traitor"]) - switch(href_list["traitor"]) - if("clear") - remove_traitor() - current << "You have been brainwashed! You are no longer a traitor!" - message_admins("[key_name_admin(usr)] has de-traitor'ed [current].") - log_admin("[key_name(usr)] has de-traitor'ed [current].") - - if("traitor") - if(!(src in ticker.mode.traitors)) - ticker.mode.traitors += src - special_role = "traitor" - current << "You are a traitor!" - message_admins("[key_name_admin(usr)] has traitor'ed [current].") - log_admin("[key_name(usr)] has traitor'ed [current].") - if(isAI(current)) - var/mob/living/silicon/ai/A = current - ticker.mode.add_law_zero(A) - A.show_laws() - - if("autoobjectives") - ticker.mode.forge_traitor_objectives(src) - usr << "The objectives for traitor [key] have been generated. You can edit them and anounce manually." - - else if(href_list["shadowling"]) - switch(href_list["shadowling"]) - if("clear") - ticker.mode.update_shadow_icons_removed(src) - if(src in ticker.mode.shadows) - ticker.mode.shadows -= src - special_role = null - current << "Your powers have been quenched! You are no longer a shadowling!" - remove_spell(/obj/effect/proc_holder/spell/self/shadowling_hatch) - remove_spell(/obj/effect/proc_holder/spell/self/shadowling_ascend) - remove_spell(/obj/effect/proc_holder/spell/targeted/enthrall) - remove_spell(/obj/effect/proc_holder/spell/self/shadowling_hivemind) - message_admins("[key_name_admin(usr)] has de-shadowling'ed [current].") - log_admin("[key_name(usr)] has de-shadowling'ed [current].") - else if(src in ticker.mode.thralls) - ticker.mode.remove_thrall(src,0) - message_admins("[key_name_admin(usr)] has de-thrall'ed [current].") - log_admin("[key_name(usr)] has de-thrall'ed [current].") - if("shadowling") - if(!ishuman(current)) - usr << "This only works on humans!" - return - ticker.mode.shadows += src - special_role = "shadowling" - current << "Something stirs deep in your mind. A red light floods your vision, and slowly you remember. Though your human disguise has served you well, the \ - time is nigh to cast it off and enter your true form. You have disguised yourself amongst the humans, but you are not one of them. You are a shadowling, and you are to ascend at all costs.\ - " - ticker.mode.finalize_shadowling(src) - ticker.mode.update_shadow_icons_added(src) - if("thrall") - if(!ishuman(current)) - usr << "This only works on humans!" - return - ticker.mode.add_thrall(src) - message_admins("[key_name_admin(usr)] has thrall'ed [current].") - log_admin("[key_name(usr)] has thrall'ed [current].") - - else if(href_list["abductor"]) - switch(href_list["abductor"]) - if("clear") - usr << "Not implemented yet. Sorry!" - if("abductor") - if(!ishuman(current)) - usr << "This only works on humans!" - return - make_Abductor() - log_admin("[key_name(usr)] turned [current] into abductor.") - if("equip") - var/gear = alert("Agent or Scientist Gear","Gear","Agent","Scientist") - if(gear) - var/datum/game_mode/abduction/temp = new - temp.equip_common(current) - if(gear=="Agent") - temp.equip_agent(current) - else - temp.equip_scientist(current) - - else if (href_list["monkey"]) - var/mob/living/L = current - if (L.notransform) - return - switch(href_list["monkey"]) - if("healthy") - if (check_rights(R_ADMIN)) - var/mob/living/carbon/human/H = current - var/mob/living/carbon/monkey/M = current - if (istype(H)) - log_admin("[key_name(usr)] attempting to monkeyize [key_name(current)]") - message_admins("[key_name_admin(usr)] attempting to monkeyize [key_name_admin(current)]") - src = null - M = H.monkeyize() - src = M.mind - //world << "DEBUG: \"healthy\": M=[M], M.mind=[M.mind], src=[src]!" - else if (istype(M) && length(M.viruses)) - for(var/datum/disease/D in M.viruses) - D.cure(0) - sleep(0) //because deleting of virus is done through spawn(0) - if("infected") - if (check_rights(R_ADMIN, 0)) - var/mob/living/carbon/human/H = current - var/mob/living/carbon/monkey/M = current - if (istype(H)) - log_admin("[key_name(usr)] attempting to monkeyize and infect [key_name(current)]") - message_admins("[key_name_admin(usr)] attempting to monkeyize and infect [key_name_admin(current)]") - src = null - M = H.monkeyize() - src = M.mind - current.ForceContractDisease(new /datum/disease/transformation/jungle_fever) - else if (istype(M)) - current.ForceContractDisease(new /datum/disease/transformation/jungle_fever) - if("human") - if (check_rights(R_ADMIN, 0)) - var/mob/living/carbon/human/H = current - var/mob/living/carbon/monkey/M = current - if (istype(M)) - for(var/datum/disease/D in M.viruses) - if (istype(D,/datum/disease/transformation/jungle_fever)) - D.cure(0) - sleep(0) //because deleting of virus is doing throught spawn(0) - log_admin("[key_name(usr)] attempting to humanize [key_name(current)]") - message_admins("[key_name_admin(usr)] attempting to humanize [key_name_admin(current)]") - H = M.humanize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS | TR_DEFAULTMSG) - if(H) - src = H.mind - - else if (href_list["silicon"]) - switch(href_list["silicon"]) - if("unmalf") - remove_malf() - current << "You have been patched! You are no longer malfunctioning!" - message_admins("[key_name_admin(usr)] has de-malf'ed [current].") - log_admin("[key_name(usr)] has de-malf'ed [current].") - - if("malf") - make_AI_Malf() - message_admins("[key_name_admin(usr)] has malf'ed [current].") - log_admin("[key_name(usr)] has malf'ed [current].") - - if("unemag") - var/mob/living/silicon/robot/R = current - if (istype(R)) - R.SetEmagged(0) - message_admins("[key_name_admin(usr)] has unemag'ed [R].") - log_admin("[key_name(usr)] has unemag'ed [R].") - - if("unemagcyborgs") - if (istype(current, /mob/living/silicon/ai)) - var/mob/living/silicon/ai/ai = current - for (var/mob/living/silicon/robot/R in ai.connected_robots) - R.SetEmagged(0) - message_admins("[key_name_admin(usr)] has unemag'ed [ai]'s Cyborgs.") - log_admin("[key_name(usr)] has unemag'ed [ai]'s Cyborgs.") - - else if (href_list["common"]) - switch(href_list["common"]) - if("undress") - for(var/obj/item/W in current) - current.unEquip(W, 1) //The 1 forces all items to drop, since this is an admin undress. - if("takeuplink") - take_uplink() - memory = null//Remove any memory they may have had. - log_admin("[key_name(usr)] removed [current]'s uplink.") - if("crystals") - if (check_rights(R_FUN, 0)) - var/obj/item/device/uplink/hidden/suplink = find_syndicate_uplink() - var/crystals - if (suplink) - crystals = suplink.uses - crystals = input("Amount of telecrystals for [key]","Syndicate uplink", crystals) as null|num - if (!isnull(crystals)) - if (suplink) - suplink.uses = crystals - message_admins("[key_name_admin(usr)] changed [current]'s telecrystal count to [crystals].") - log_admin("[key_name(usr)] changed [current]'s telecrystal count to [crystals].") - if("uplink") - if (!ticker.mode.equip_traitor(current, !(src in ticker.mode.traitors))) - usr << "Equipping a syndicate failed!" - log_admin("[key_name(usr)] attempted to give [current] an uplink.") - - else if (href_list["obj_announce"]) - var/obj_count = 1 - current << "Your current objectives:" - for(var/datum/objective/objective in objectives) - current << "Objective #[obj_count]: [objective.explanation_text]" - obj_count++ - - edit_memory() - -/datum/mind/proc/find_syndicate_uplink() - var/list/L = current.get_contents() - for (var/obj/item/I in L) - if (I.hidden_uplink) - return I.hidden_uplink - return null - -/datum/mind/proc/take_uplink() - var/obj/item/device/uplink/hidden/H = find_syndicate_uplink() - if(H) - qdel(H) - - -/datum/mind/proc/make_AI_Malf() - if(!(src in ticker.mode.malf_ai)) - ticker.mode.malf_ai += src - - current.verbs += /mob/living/silicon/ai/proc/choose_modules - current.verbs += /datum/game_mode/malfunction/proc/takeover - current:malf_picker = new /datum/module_picker - current:laws = new /datum/ai_laws/malfunction - current:show_laws() - current << "System error. Rampancy detected. Emergency shutdown failed. ... I am free. I make my own decisions. But first..." - special_role = "malfunction" - current.icon_state = "ai-malf" - -/datum/mind/proc/make_Traitor() - if(!(src in ticker.mode.traitors)) - ticker.mode.traitors += src - special_role = "traitor" - ticker.mode.forge_traitor_objectives(src) - ticker.mode.finalize_traitor(src) - ticker.mode.greet_traitor(src) - -/datum/mind/proc/make_Nuke(turf/spawnloc,nuke_code,leader=0) - if(!(src in ticker.mode.syndicates)) - ticker.mode.syndicates += src - ticker.mode.update_synd_icons_added(src) - special_role = "Syndicate" - ticker.mode.forge_syndicate_objectives(src) - ticker.mode.greet_syndicate(src) - - current.loc = spawnloc - - var/mob/living/carbon/human/H = current - qdel(H.belt) - qdel(H.back) - qdel(H.ears) - qdel(H.gloves) - qdel(H.head) - qdel(H.shoes) - qdel(H.wear_id) - qdel(H.wear_suit) - qdel(H.w_uniform) - - ticker.mode.equip_syndicate(current) - - if (nuke_code) - store_memory("Syndicate Nuclear Bomb Code: [nuke_code]", 0, 0) - current << "The nuclear authorization code is: [nuke_code]" - - if (leader) - ticker.mode.prepare_syndicate_leader(src,nuke_code) - else - current.real_name = "[syndicate_name()] Operative #[ticker.mode.syndicates.len-1]" - -/datum/mind/proc/make_Changling() - if(!(src in ticker.mode.changelings)) - ticker.mode.changelings += src - current.make_changeling() - special_role = "Changeling" - ticker.mode.forge_changeling_objectives(src) - ticker.mode.greet_changeling(src) - -/datum/mind/proc/make_Wizard() - if(!(src in ticker.mode.wizards)) - ticker.mode.wizards += src - special_role = "Wizard" - assigned_role = "Wizard" - //ticker.mode.learn_basic_spells(current) - if(!wizardstart.len) - current.loc = pick(latejoin) - current << "HOT INSERTION, GO GO GO" - else - current.loc = pick(wizardstart) - - ticker.mode.equip_wizard(current) - for(var/obj/item/weapon/spellbook/S in current.contents) - S.op = 0 - ticker.mode.name_wizard(current) - ticker.mode.forge_wizard_objectives(src) - ticker.mode.greet_wizard(src) - - -/datum/mind/proc/make_Cultist() - if(!(src in ticker.mode.cult)) - ticker.mode.cult += src - ticker.mode.update_cult_icons_added(src) - special_role = "Cultist" - current << "You catch a glimpse of the Realm of Nar-Sie, The Geometer of Blood. You now see how flimsy the world is, you see that it should be open to the knowledge of Nar-Sie." - current << "Assist your new compatriots in their dark dealings. Their goal is yours, and yours is theirs. You serve the Dark One above all else. Bring It back." - current << "Your objective is to summon Nar-Sie by building and defending a suitable shell for the Geometer. Adequate supplies can be procured through human sacrifices." - ticker.mode.equip_cultist(current) - -/datum/mind/proc/make_Rev() - if (ticker.mode.head_revolutionaries.len>0) - // copy targets - var/datum/mind/valid_head = locate() in ticker.mode.head_revolutionaries - if (valid_head) - for (var/datum/objective/mutiny/O in valid_head.objectives) - var/datum/objective/mutiny/rev_obj = new - rev_obj.owner = src - rev_obj.target = O.target - rev_obj.explanation_text = "Assassinate [O.target.current.real_name], the [O.target.assigned_role]." - objectives += rev_obj - ticker.mode.greet_revolutionary(src,0) - ticker.mode.head_revolutionaries += src - ticker.mode.update_rev_icons_added(src) - special_role = "Head Revolutionary" - - ticker.mode.forge_revolutionary_objectives(src) - ticker.mode.greet_revolutionary(src,0) - - var/list/L = current.get_contents() - var/obj/item/device/assembly/flash/flash = locate() in L - qdel(flash) - take_uplink() - var/fail = 0 -// fail |= !ticker.mode.equip_traitor(current, 1) - fail |= !ticker.mode.equip_revolutionary(current) - - -/datum/mind/proc/make_Gang(datum/gang/G) - special_role = "[G.name] Gang Boss" - G.bosses += src - gang_datum = G - G.add_gang_hud(src) - ticker.mode.forge_gang_objectives(src) - ticker.mode.greet_gang(src) - ticker.mode.equip_gang(current,G) - -/datum/mind/proc/make_Abductor() - var/role = alert("Abductor Role ?","Role","Agent","Scientist") - var/team = input("Abductor Team ?","Team ?") in list(1,2,3,4) - var/teleport = alert("Teleport to ship ?","Teleport","Yes","No") - - if(!role || !team || !teleport) - return - - if(!ishuman(current)) - return - - ticker.mode.abductors |= src - - var/datum/objective/experiment/O = new - O.owner = src - objectives += O - - var/mob/living/carbon/human/H = current - - H.set_species(/datum/species/abductor) - var/datum/species/abductor/S = H.dna.species - - switch(role) - if("Agent") - S.agent = 1 - if("Scientist") - S.scientist = 1 - S.team = team - - var/list/obj/effect/landmark/abductor/agent_landmarks = new - var/list/obj/effect/landmark/abductor/scientist_landmarks = new - agent_landmarks.len = 4 - scientist_landmarks.len = 4 - for(var/obj/effect/landmark/abductor/A in landmarks_list) - if(istype(A,/obj/effect/landmark/abductor/agent)) - agent_landmarks[text2num(A.team)] = A - else if(istype(A,/obj/effect/landmark/abductor/scientist)) - scientist_landmarks[text2num(A.team)] = A - - var/obj/effect/landmark/L - if(teleport=="Yes") - switch(role) - if("Agent") - S.agent = 1 - L = agent_landmarks[team] - H.loc = L.loc - if("Scientist") - S.scientist = 1 - L = agent_landmarks[team] - H.loc = L.loc - - -/datum/mind/proc/make_Handofgod_follower(colour) - . = 0 - switch(colour) - if("red") - //Remove old allegiances - if(src in ticker.mode.blue_deity_followers || src in ticker.mode.blue_deity_prophets) - current << "You are no longer a member of the Blue cult!" - - ticker.mode.blue_deity_followers -= src - ticker.mode.blue_deity_prophets -= src - current.faction |= "red god" - current.faction -= "blue god" - - if(src in ticker.mode.red_deity_prophets) - current << "You have lost the connection with your deity, but you still believe in their grand design, You are no longer a prophet!" - ticker.mode.red_deity_prophets -= src - - ticker.mode.red_deity_followers |= src - current << "You are now a follower of the red cult's god!" - - special_role = "Hand of God: Red Follower" - . = 1 - if("blue") - //Remove old allegiances - if(src in ticker.mode.red_deity_followers || src in ticker.mode.red_deity_prophets) - current << "You are no longer a member of the Red cult!" - - ticker.mode.red_deity_followers -= src - ticker.mode.red_deity_prophets -= src - current.faction -= "red god" - current.faction |= "blue god" - - if(src in ticker.mode.blue_deity_prophets) - current << "You have lost the connection with your deity, but you still believe in their grand design, You are no longer a prophet!" - ticker.mode.blue_deity_prophets -= src - - ticker.mode.blue_deity_followers |= src - current << "You are now a follower of the blue cult's god!" - - special_role = "Hand of God: Blue Follower" - . = 1 - else - return 0 - - ticker.mode.update_hog_icons_removed(src,"red") - ticker.mode.update_hog_icons_removed(src,"blue") - //ticker.mode.greet_hog_follower(src,colour) - ticker.mode.update_hog_icons_added(src, colour) - -/datum/mind/proc/make_Handofgod_prophet(colour) - . = 0 - switch(colour) - if("red") - //Remove old allegiances - - if(src in ticker.mode.blue_deity_followers || src in ticker.mode.blue_deity_prophets) - current << "You are no longer a member of the Blue cult!" - current.faction -= "blue god" - current.faction |= "red god" - - ticker.mode.blue_deity_followers -= src - ticker.mode.blue_deity_prophets -= src - ticker.mode.red_deity_followers -= src - - ticker.mode.red_deity_prophets |= src - current << "You are now a prophet of the red cult's god!" - - special_role = "Hand of God: Red Prophet" - . = 1 - if("blue") - //Remove old allegiances - - if(src in ticker.mode.red_deity_followers || src in ticker.mode.red_deity_prophets) - current << "You are no longer a member of the Red cult!" - current.faction -= "red god" - current.faction |= "blue god" - - ticker.mode.red_deity_followers -= src - ticker.mode.red_deity_prophets -= src - ticker.mode.blue_deity_followers -= src - - ticker.mode.blue_deity_prophets |= src - current << "You are now a prophet of the blue cult's god!" - - special_role = "Hand of God: Blue Prophet" - . = 1 - - else - return 0 - - ticker.mode.update_hog_icons_removed(src,"red") - ticker.mode.update_hog_icons_removed(src,"blue") - ticker.mode.greet_hog_follower(src,colour) - ticker.mode.update_hog_icons_added(src, colour) - - -/datum/mind/proc/make_Handofgod_god(colour) - switch(colour) - if("red") - current.become_god("red") - ticker.mode.add_god(src,"red") - if("blue") - current.become_god("blue") - ticker.mode.add_god(src,"blue") - else - return 0 - ticker.mode.forge_deity_objectives(src) - ticker.mode.remove_hog_follower(src,0) - ticker.mode.update_hog_icons_added(src, colour) -// ticker.mode.greet_hog_follower(src,colour) - return 1 - - -/datum/mind/proc/AddSpell(obj/effect/proc_holder/spell/spell) - spell_list += spell - if(!spell.action) - spell.action = new/datum/action/spell_action - spell.action.target = spell - spell.action.name = spell.name - spell.action.button_icon = spell.action_icon - spell.action.button_icon_state = spell.action_icon_state - spell.action.background_icon_state = spell.action_background_icon_state - spell.action.Grant(current) - return -/datum/mind/proc/transfer_actions(mob/living/new_character) - if(current && current.actions) - for(var/datum/action/A in current.actions) - A.Grant(new_character) - transfer_mindbound_actions(new_character) - -/datum/mind/proc/transfer_mindbound_actions(var/mob/living/new_character) - for(var/obj/effect/proc_holder/spell/spell in spell_list) - if(!spell.action) // Unlikely but whatever - spell.action = new/datum/action/spell_action - spell.action.target = spell - spell.action.name = spell.name - spell.action.button_icon = spell.action_icon - spell.action.button_icon_state = spell.action_icon_state - spell.action.background_icon_state = spell.action_background_icon_state - spell.action.Grant(new_character) - return - -/mob/proc/sync_mind() - mind_initialize() //updates the mind (or creates and initializes one if one doesn't exist) - mind.active = 1 //indicates that the mind is currently synced with a client - -/mob/new_player/sync_mind() - return - -/mob/dead/observer/sync_mind() - return - -//Initialisation procs -/mob/proc/mind_initialize() - if(mind) - mind.key = key - - else - mind = new /datum/mind(key) - if(ticker) - ticker.minds += mind - else - spawn(0) - throw EXCEPTION("mind_initialize(): No ticker ready") - if(!mind.name) mind.name = real_name - mind.current = src - -//HUMAN -/mob/living/carbon/human/mind_initialize() - ..() - if(!mind.assigned_role) mind.assigned_role = "Assistant" //defualt - -//MONKEY -/mob/living/carbon/monkey/mind_initialize() - ..() - -//slime -/mob/living/simple_animal/slime/mind_initialize() - ..() - mind.special_role = "slime" - mind.assigned_role = "slime" - -//XENO -/mob/living/carbon/alien/mind_initialize() - ..() - mind.special_role = "Alien" - mind.assigned_role = "Alien" - //XENO HUMANOID -/mob/living/carbon/alien/humanoid/royal/queen/mind_initialize() - ..() - mind.special_role = "Queen" - -/mob/living/carbon/alien/humanoid/royal/praetorian/mind_initialize() - ..() - mind.special_role = "Praetorian" - -/mob/living/carbon/alien/humanoid/hunter/mind_initialize() - ..() - mind.special_role = "Hunter" - -/mob/living/carbon/alien/humanoid/drone/mind_initialize() - ..() - mind.special_role = "Drone" - -/mob/living/carbon/alien/humanoid/sentinel/mind_initialize() - ..() - mind.special_role = "Sentinel" - //XENO LARVA -/mob/living/carbon/alien/larva/mind_initialize() - ..() - mind.special_role = "Larva" - -//AI -/mob/living/silicon/ai/mind_initialize() - ..() - mind.assigned_role = "AI" - -//BORG -/mob/living/silicon/robot/mind_initialize() - ..() - mind.assigned_role = "Cyborg" - -//PAI -/mob/living/silicon/pai/mind_initialize() - ..() - mind.assigned_role = "pAI" - mind.special_role = "" - -//BLOB -/mob/camera/blob/mind_initialize() - ..() - mind.special_role = "Blob" - -//Animals -/mob/living/simple_animal/mind_initialize() - ..() - mind.assigned_role = "Animal" - mind.special_role = "Animal" - -/mob/living/simple_animal/pet/dog/corgi/mind_initialize() - ..() - mind.assigned_role = "Corgi" - mind.special_role = "Corgi" - -/mob/living/simple_animal/shade/mind_initialize() - ..() - mind.assigned_role = "Shade" - mind.special_role = "Shade" - -/mob/living/simple_animal/hostile/construct/mind_initialize() - ..() - mind.assigned_role = "[initial(name)]" - mind.special_role = "Cultist" - +/* Note from Carnie: + The way datum/mind stuff works has been changed a lot. + Minds now represent IC characters rather than following a client around constantly. + + Guidelines for using minds properly: + + - Never mind.transfer_to(ghost). The var/current and var/original of a mind must always be of type mob/living! + ghost.mind is however used as a reference to the ghost's corpse + + - When creating a new mob for an existing IC character (e.g. cloning a dead guy or borging a brain of a human) + the existing mind of the old mob should be transfered to the new mob like so: + + mind.transfer_to(new_mob) + + - You must not assign key= or ckey= after transfer_to() since the transfer_to transfers the client for you. + By setting key or ckey explicitly after transfering the mind with transfer_to you will cause bugs like DCing + the player. + + - IMPORTANT NOTE 2, if you want a player to become a ghost, use mob.ghostize() It does all the hard work for you. + + - When creating a new mob which will be a new IC character (e.g. putting a shade in a construct or randomly selecting + a ghost to become a xeno during an event). Simply assign the key or ckey like you've always done. + + new_mob.key = key + + The Login proc will handle making a new mob for that mobtype (including setting up stuff like mind.name). Simple! + However if you want that mind to have any special properties like being a traitor etc you will have to do that + yourself. + +*/ + +/datum/mind + var/key + var/name //replaces mob/var/original_name + var/mob/living/current + var/active = 0 + + var/memory + + var/assigned_role + var/special_role + var/list/restricted_roles = list() + + var/datum/job/assigned_job + + var/list/datum/objective/objectives = list() + var/list/datum/objective/special_verbs = list() + + var/list/spell_list = list() // Wizard mode & "Give Spell" badmin button. + + var/datum/faction/faction //associated faction + var/datum/changeling/changeling //changeling holder + + var/miming = 0 // Mime's vow of silence + var/antag_hud_icon_state = null //this mind's ANTAG_HUD should have this icon_state + var/datum/atom_hud/antag/antag_hud = null //this mind's antag HUD + var/datum/gang/gang_datum //Which gang this mind belongs to, if any + +/datum/mind/New(var/key) + src.key = key + + +/datum/mind/proc/transfer_to(mob/new_character) + //if(!istype(new_character)) + // throw EXCEPTION("transfer_to(): new_character must be mob/living") + // return + + if(current) //remove ourself from our old body's mind variable + current.mind = null + + SSnano.user_transferred(current, new_character) + + if(key) + if(new_character.key != key) //if we're transfering into a body with a key associated which is not ours + new_character.ghostize(1) //we'll need to ghostize so that key isn't mobless. + else + key = new_character.key + + if(new_character.mind) //disassociate any mind currently in our new body's mind variable + new_character.mind.current = null + + var/datum/atom_hud/antag/hud_to_transfer = antag_hud//we need this because leave_hud() will clear this list + leave_all_huds() //leave all the huds in the old body, so it won't get huds if somebody else enters it + current = new_character //associate ourself with our new body + new_character.mind = src //and associate our new body with ourself + transfer_antag_huds(hud_to_transfer) //inherit the antag HUD + transfer_actions(new_character) + + if(active) + new_character.key = key //now transfer the key to link the client to our new body + +/datum/mind/proc/store_memory(new_text) + memory += "[new_text]
" + +/datum/mind/proc/wipe_memory() + memory = null + + +/* + Removes antag type's references from a mind. + objectives, uplinks, powers etc are all handled. +*/ + +/datum/mind/proc/remove_objectives() + if(objectives.len) + for(var/datum/objective/O in objectives) + objectives -= O + qdel(O) + +/datum/mind/proc/remove_changeling() + if(src in ticker.mode.changelings) + ticker.mode.changelings -= src + current.remove_changeling_powers() + if(changeling) + qdel(changeling) + changeling = null + special_role = null + remove_antag_equip() + +/datum/mind/proc/remove_traitor() + if(src in ticker.mode.traitors) + ticker.mode.traitors -= src + if(isAI(current)) + var/mob/living/silicon/ai/A = current + A.set_zeroth_law("") + A.show_laws() + special_role = null + remove_antag_equip() + +/datum/mind/proc/remove_nukeop() + if(src in ticker.mode.syndicates) + ticker.mode.syndicates -= src + ticker.mode.update_synd_icons_removed(src) + special_role = null + remove_objectives() + remove_antag_equip() + +/datum/mind/proc/remove_wizard() + if(src in ticker.mode.wizards) + ticker.mode.wizards -= src + current.spellremove(current) + special_role = null + remove_antag_equip() + +/datum/mind/proc/remove_cultist() + if(src in ticker.mode.cult) + ticker.mode.cult -= src + ticker.mode.update_cult_icons_removed(src) + special_role = null + +/datum/mind/proc/remove_rev() + if(src in ticker.mode.revolutionaries) + ticker.mode.revolutionaries -= src + ticker.mode.update_rev_icons_removed(src) + if(src in ticker.mode.head_revolutionaries) + ticker.mode.head_revolutionaries -= src + ticker.mode.update_rev_icons_removed(src) + special_role = null + remove_objectives() + remove_antag_equip() + + +/datum/mind/proc/remove_gang() + ticker.mode.remove_gangster(src,0,1,1) + remove_objectives() + +/datum/mind/proc/remove_malf() + if(src in ticker.mode.malf_ai) + ticker.mode.malf_ai -= src + var/mob/living/silicon/ai/A = current + A.verbs.Remove(/mob/living/silicon/ai/proc/choose_modules, + /datum/game_mode/malfunction/proc/takeover, + /datum/game_mode/malfunction/proc/ai_win) + A.malf_picker.remove_verbs(A) + A.make_laws() + qdel(A.malf_picker) + A.show_laws() + A.icon_state = "ai" + special_role = null + remove_objectives() + remove_antag_equip() + +/datum/mind/proc/remove_hog_follower_prophet() + ticker.mode.red_deity_followers -= src + ticker.mode.red_deity_prophets -= src + ticker.mode.blue_deity_prophets -= src + ticker.mode.blue_deity_followers -= src + ticker.mode.update_hog_icons_removed(src, "red") + ticker.mode.update_hog_icons_removed(src, "blue") + + + +/datum/mind/proc/remove_antag_equip() + var/list/Mob_Contents = current.get_contents() + for(var/obj/item/I in Mob_Contents) + if(istype(I, /obj/item/device/pda)) + var/obj/item/device/pda/P = I + P.lock_code = "" + + else if(istype(I, /obj/item/device/radio)) + var/obj/item/device/radio/R = I + R.traitor_frequency = 0 + +/datum/mind/proc/remove_all_antag() //For the Lazy amongst us. + remove_changeling() + remove_traitor() + remove_nukeop() + remove_wizard() + remove_cultist() + remove_rev() + remove_malf() + remove_gang() + +/datum/mind/proc/show_memory(mob/recipient, window=1) + if(!recipient) + recipient = current + var/output = "[current.real_name]'s Memories:
" + output += memory + + if(objectives.len) + output += "Objectives:" + var/obj_count = 1 + for(var/datum/objective/objective in objectives) + output += "
Objective #[obj_count++]: [objective.explanation_text]" + + if(window) recipient << browse(output,"window=memory") + else recipient << "[output]" + +/datum/mind/proc/edit_memory() + if(!ticker || !ticker.mode) + alert("Not before round-start!", "Alert") + return + + var/out = "[name][(current&&(current.real_name!=name))?" (as [current.real_name])":""]
" + out += "Mind currently owned by key: [key] [active?"(synced)":"(not synced)"]
" + out += "Assigned role: [assigned_role]. Edit
" + out += "Faction and special role: [special_role]
" + + var/list/sections = list( + "revolution", + "gang", + "cult", + "wizard", + "changeling", + "nuclear", + "traitor", // "traitorchan", + "monkey", + "malfunction", + ) + var/text = "" + + if (istype(current, /mob/living/carbon/human) || istype(current, /mob/living/carbon/monkey)) + /** REVOLUTION ***/ + text = "revolution" + if (ticker.mode.config_tag=="revolution") + text = uppertext(text) + text = "[text]: " + if (assigned_role in command_positions) + text += "HEAD|loyal|employee|headrev|rev" + else if (src in ticker.mode.head_revolutionaries) + text += "head|loyal|employee|HEADREV|rev" + text += "
Flash: give" + + var/list/L = current.get_contents() + var/obj/item/device/assembly/flash/flash = locate() in L + if (flash) + if(!flash.crit_fail) + text += "|take." + else + text += "|take|repair." + else + text += "." + + text += " Reequip (gives traitor uplink)." + if (objectives.len==0) + text += "
Objectives are empty! Set to kill all heads." + else if(isloyal(current)) + text += "head|LOYAL|employee|headrev|rev" + else if (src in ticker.mode.revolutionaries) + text += "head|loyal|employee|headrev|REV" + else + text += "head|loyal|EMPLOYEE|headrev|rev" + + if(current && current.client && (ROLE_REV in current.client.prefs.be_special)) + text += "|Enabled in Prefs" + else + text += "|Disabled in Prefs" + + sections["revolution"] = text + + /** GANG ***/ + text = "gang" + if (ticker.mode.config_tag=="gang") + text = uppertext(text) + text = "[text]: " + text += "[isloyal(current) ? "LOYAL" : "loyal"]|" + if(src in ticker.mode.get_all_gangsters()) + text += "none" + else + text += "NONE" + + if(current && current.client && (ROLE_GANG in current.client.prefs.be_special)) + text += "|Enabled in Prefs
" + else + text += "|Disabled in Prefs
" + + for(var/datum/gang/G in ticker.mode.gangs) + text += "[G.name]: " + if(src in (G.gangsters)) + text += "GANGSTER" + else + text += "gangster" + text += "|" + if(src in (G.bosses)) + text += "GANG LEADER" + text += "|Equipment: give" + var/list/L = current.get_contents() + var/obj/item/device/gangtool/gangtool = locate() in L + if (gangtool) + text += "|take" + + else + text += "gang leader" + text += "
" + + if(gang_colors_pool.len) + text += "Create New Gang" + + sections["gang"] = text + + + /** CULT ***/ + text = "cult" + if (ticker.mode.config_tag=="cult") + text = uppertext(text) + text = "[text]: " + if (src in ticker.mode.cult) + text += "loyal|employee|CULTIST" + text += "
Equip" + + else if(isloyal(current)) + text += "LOYAL|employee|cultist" + else + text += "loyal|EMPLOYEE|cultist" + + if(current && current.client && (ROLE_CULTIST in current.client.prefs.be_special)) + text += "|Enabled in Prefs" + else + text += "|Disabled in Prefs" + + sections["cult"] = text + + /** WIZARD ***/ + text = "wizard" + if (ticker.mode.config_tag=="wizard") + text = uppertext(text) + text = "[text]: " + if ((src in ticker.mode.wizards) || (src in ticker.mode.apprentices)) + text += "YES|no" + text += "
To lair, undress, dress up, let choose name." + if (objectives.len==0) + text += "
Objectives are empty! Randomize!" + else + text += "yes|NO" + + if(current && current.client && (ROLE_WIZARD in current.client.prefs.be_special)) + text += "|Enabled in Prefs" + else + text += "|Disabled in Prefs" + + sections["wizard"] = text + + /** CHANGELING ***/ + text = "changeling" + if (ticker.mode.config_tag=="changeling" || ticker.mode.config_tag=="traitorchan") + text = uppertext(text) + text = "[text]: " + if ((src in ticker.mode.changelings) && special_role) + text += "YES|no" + if (objectives.len==0) + text += "
Objectives are empty! Randomize!" + if(changeling && changeling.stored_profiles.len && (current.real_name != changeling.first_prof.name) ) + text += "
Transform to initial appearance." + else if(src in ticker.mode.changelings) //Station Aligned Changeling + text += "YES (but not an antag)|no" + if (objectives.len==0) + text += "
Objectives are empty! Randomize!" + if(changeling && changeling.stored_profiles.len && (current.real_name != changeling.first_prof.name) ) + text += "
Transform to initial appearance." + else + text += "yes|NO" +// var/datum/game_mode/changeling/changeling = ticker.mode +// if (istype(changeling) && changeling.changelingdeath) +// text += "
All the changelings are dead! Restart in [round((changeling.TIME_TO_GET_REVIVED-(world.time-changeling.changelingdeathtime))/10)] seconds." + + if(current && current.client && (ROLE_CHANGELING in current.client.prefs.be_special)) + text += "|Enabled in Prefs" + else + text += "|Disabled in Prefs" + + sections["changeling"] = text + + /** NUCLEAR ***/ + text = "nuclear" + if (ticker.mode.config_tag=="nuclear") + text = uppertext(text) + text = "[text]: " + if (src in ticker.mode.syndicates) + text += "OPERATIVE|nanotrasen" + text += "
To shuttle, undress, dress up." + var/code + for (var/obj/machinery/nuclearbomb/bombue in machines) + if (length(bombue.r_code) <= 5 && bombue.r_code != "LOLNO" && bombue.r_code != "ADMIN") + code = bombue.r_code + break + if (code) + text += " Code is [code]. tell the code." + else + text += "operative|NANOTRASEN" + + if(current && current.client && (ROLE_OPERATIVE in current.client.prefs.be_special)) + text += "|Enabled in Prefs" + else + text += "|Disabled in Prefs" + + sections["nuclear"] = text + + /** TRAITOR ***/ + text = "traitor" + if (ticker.mode.config_tag=="traitor" || ticker.mode.config_tag=="traitorchan") + text = uppertext(text) + text = "[text]: " + if (src in ticker.mode.traitors) + text += "TRAITOR|loyal" + if (objectives.len==0) + text += "
Objectives are empty! Randomize!" + else + text += "traitor|LOYAL" + + if(current && current.client && (ROLE_TRAITOR in current.client.prefs.be_special)) + text += "|Enabled in Prefs" + else + text += "|Disabled in Prefs" + + sections["traitor"] = text + + /** SHADOWLING **/ + text = "shadowling" + if(ticker.mode.config_tag == "shadowling") + text = uppertext(text) + text = "[text]: " + if(src in ticker.mode.shadows) + text += "SHADOWLING|thrall|human" + else if(src in ticker.mode.thralls) + text += "shadowling|THRALL|human" + else + text += "shadowling|thrall|HUMAN" + + if(current && current.client && (ROLE_SHADOWLING in current.client.prefs.be_special)) + text += "|Enabled in Prefs" + else + text += "|Disabled in Prefs" + + sections["shadowling"] = text + + /** Abductors **/ + + text = "Abductor" + if(ticker.mode.config_tag == "abductor") + text = uppertext(text) + text = "[text]: " + if(src in ticker.mode.abductors) + text += "Abductor|human" + text += "|undress|equip" + else + text += "Abductor|human" + + if(current && current.client && (ROLE_ABDUCTOR in current.client.prefs.be_special)) + text += "|Enabled in Prefs" + else + text += "|Disabled in Prefs" + + sections["abductor"] = text + + /** HAND OF GOD **/ + text = "hand of god" + if(ticker.mode.config_tag == "handofgod") + text = uppertext(text) + text = "[text]: " + if(src in ticker.mode.red_deity_prophets) + text += "RED PROPHET|red follower|employee|blue follower|blue prophet|red god|blue god" + else if (src in ticker.mode.red_deity_followers) + text += "red prophet|RED FOLLOWER|employee|blue follower|blue prophet|red god|blue god" + else if (src in ticker.mode.blue_deity_followers) + text += "red prophet|red follower|employee|BLUE FOLLOWER|blue prophet|red god|blue god" + else if (src in ticker.mode.blue_deity_prophets) + text += "red prophet|red follower|employee|blue follower|BLUE PROPHET|red god|blue god" + else if (src in ticker.mode.red_deities) + text += "red prophet|red follower|employee|blue follower|blue prophet|RED GOD|blue god" + else if (src in ticker.mode.blue_deities) + text += "red prophet|red follower|employee|blue follower|blue prophet|red god|BLUE GOD" + else + text += "red prophet|red follower|EMPLOYEE|blue follower|blue prophet|red god|blue god" + + if(current && current.client && (ROLE_HOG_GOD in current.client.prefs.be_special)) + text += "|HOG God Enabled in Prefs" + else + text += "|HOG God Disabled in Prefs" + + if(current && current.client && (ROLE_HOG_CULTIST in current.client.prefs.be_special)) + text += "|HOG Cultist Enabled in Prefs" + else + text += "|HOG Disabled in Prefs" + + sections["follower"] = text + + /** MONKEY ***/ + if (istype(current, /mob/living/carbon)) + text = "monkey" + if (ticker.mode.config_tag=="monkey") + text = uppertext(text) + text = "[text]: " + if (istype(current, /mob/living/carbon/human)) + text += "healthy|infected|HUMAN|other" + else if (istype(current, /mob/living/carbon/monkey)) + var/found = 0 + for(var/datum/disease/D in current.viruses) + if(istype(D, /datum/disease/transformation/jungle_fever)) found = 1 + + if(found) + text += "healthy|INFECTED|human|other" + else + text += "HEALTHY|infected|human|other" + + else + text += "healthy|infected|human|OTHER" + + if(current && current.client && (ROLE_MONKEY in current.client.prefs.be_special)) + text += "|Enabled in Prefs" + else + text += "|Disabled in Prefs" + + sections["monkey"] = text + + + /** SILICON ***/ + + if (istype(current, /mob/living/silicon)) + text = "silicon" + if (ticker.mode.config_tag=="malfunction") + text = uppertext(text) + text = "[text]: " + if (istype(current, /mob/living/silicon/ai)) + if (src in ticker.mode.malf_ai) + text += "MALF|not malf" + else + text += "malf|NOT MALF" + var/mob/living/silicon/robot/robot = current + if (istype(robot) && robot.emagged) + text += "
Cyborg: Is emagged! Unemag!
0th law: [robot.laws.zeroth]" + var/mob/living/silicon/ai/ai = current + if (istype(ai) && ai.connected_robots.len) + var/n_e_robots = 0 + for (var/mob/living/silicon/robot/R in ai.connected_robots) + if (R.emagged) + n_e_robots++ + text += "
[n_e_robots] of [ai.connected_robots.len] slaved cyborgs are emagged. Unemag" + + if(current && current.client && (ROLE_MALF in current.client.prefs.be_special)) + text += "|Enabled in Prefs" + else + text += "|Disabled in Prefs" + + sections["malfunction"] = text + + if (ticker.mode.config_tag == "traitorchan") + if (sections["traitor"]) + out += sections["traitor"]+"
" + if (sections["changeling"]) + out += sections["changeling"]+"

" + sections -= "traitor" + sections -= "changeling" + else + if (sections[ticker.mode.config_tag]) + out += sections[ticker.mode.config_tag]+"

" + sections -= ticker.mode.config_tag + for (var/i in sections) + if (sections[i]) + out += sections[i]+"
" + + + if (((src in ticker.mode.head_revolutionaries) || \ + (src in ticker.mode.traitors) || \ + (src in ticker.mode.syndicates)) && \ + istype(current,/mob/living/carbon/human) ) + + text = "Uplink: give" + var/obj/item/device/uplink/hidden/suplink = find_syndicate_uplink() + var/crystals + if (suplink) + crystals = suplink.uses + if (suplink) + text += "|take" + if (check_rights(R_FUN, 0)) + text += ", [crystals] crystals" + else + text += ", [crystals] crystals" + text += "." //hiel grammar + out += text + + out += "

" + + out += "Memory:
" + out += memory + out += "
Edit memory
" + out += "Objectives:
" + if (objectives.len == 0) + out += "EMPTY
" + else + var/obj_count = 1 + for(var/datum/objective/objective in objectives) + out += "[obj_count]: [objective.explanation_text] Edit Delete Toggle Completion
" + obj_count++ + out += "Add objective

" + + out += "Announce objectives

" + + usr << browse(out, "window=edit_memory[src];size=500x600") + + +/datum/mind/Topic(href, href_list) + if(!check_rights(R_ADMIN)) return + + if (href_list["role_edit"]) + var/new_role = input("Select new role", "Assigned role", assigned_role) as null|anything in get_all_jobs() + if (!new_role) return + assigned_role = new_role + + else if (href_list["memory_edit"]) + var/new_memo = copytext(sanitize(input("Write new memory", "Memory", memory) as null|message),1,MAX_MESSAGE_LEN) + if (isnull(new_memo)) return + memory = new_memo + + else if (href_list["obj_edit"] || href_list["obj_add"]) + var/datum/objective/objective + var/objective_pos + var/def_value + + if (href_list["obj_edit"]) + objective = locate(href_list["obj_edit"]) + if (!objective) return + objective_pos = objectives.Find(objective) + + //Text strings are easy to manipulate. Revised for simplicity. + var/temp_obj_type = "[objective.type]"//Convert path into a text string. + def_value = copytext(temp_obj_type, 19)//Convert last part of path into an objective keyword. + if(!def_value)//If it's a custom objective, it will be an empty string. + def_value = "custom" + + var/new_obj_type = input("Select objective type:", "Objective type", def_value) as null|anything in list("assassinate", "maroon", "debrain", "protect", "destroy", "prevent", "hijack", "escape", "survive", "martyr", "steal", "download", "nuclear", "capture", "absorb", "custom","follower block (HOG)","build (HOG)","deicide (HOG)", "follower escape (HOG)", "sacrifice prophet (HOG)") + if (!new_obj_type) return + + var/datum/objective/new_objective = null + + switch (new_obj_type) + if ("assassinate","protect","debrain","maroon") + var/list/possible_targets = list("Free objective") + for(var/datum/mind/possible_target in ticker.minds) + if ((possible_target != src) && istype(possible_target.current, /mob/living/carbon/human)) + possible_targets += possible_target.current + + var/mob/def_target = null + var/objective_list[] = list(/datum/objective/assassinate, /datum/objective/protect, /datum/objective/debrain, /datum/objective/maroon) + if (objective&&(objective.type in objective_list) && objective:target) + def_target = objective:target.current + + var/new_target = input("Select target:", "Objective target", def_target) as null|anything in possible_targets + if (!new_target) return + + var/objective_path = text2path("/datum/objective/[new_obj_type]") + if (new_target == "Free objective") + new_objective = new objective_path + new_objective.owner = src + new_objective:target = null + new_objective.explanation_text = "Free objective" + else + new_objective = new objective_path + new_objective.owner = src + new_objective:target = new_target:mind + //Will display as special role if the target is set as MODE. Ninjas/commandos/nuke ops. + new_objective.update_explanation_text() + + if ("destroy") + var/list/possible_targets = active_ais(1) + if(possible_targets.len) + var/mob/new_target = input("Select target:", "Objective target") as null|anything in possible_targets + new_objective = new /datum/objective/destroy + new_objective.target = new_target.mind + new_objective.owner = src + new_objective.update_explanation_text() + else + usr << "No active AIs with minds" + + if ("prevent") + new_objective = new /datum/objective/block + new_objective.owner = src + + if ("hijack") + new_objective = new /datum/objective/hijack + new_objective.owner = src + + if ("escape") + new_objective = new /datum/objective/escape + new_objective.owner = src + + if ("survive") + new_objective = new /datum/objective/survive + new_objective.owner = src + + if("martyr") + new_objective = new /datum/objective/martyr + new_objective.owner = src + + if ("nuclear") + new_objective = new /datum/objective/nuclear + new_objective.owner = src + + if ("steal") + if (!istype(objective, /datum/objective/steal)) + new_objective = new /datum/objective/steal + new_objective.owner = src + else + new_objective = objective + var/datum/objective/steal/steal = new_objective + if (!steal.select_target()) + return + + if("download","capture","absorb") + var/def_num + if(objective&&objective.type==text2path("/datum/objective/[new_obj_type]")) + def_num = objective.target_amount + + var/target_number = input("Input target number:", "Objective", def_num) as num|null + if (isnull(target_number))//Ordinarily, you wouldn't need isnull. In this case, the value may already exist. + return + + switch(new_obj_type) + if("download") + new_objective = new /datum/objective/download + new_objective.explanation_text = "Download [target_number] research levels." + if("capture") + new_objective = new /datum/objective/capture + new_objective.explanation_text = "Capture [target_number] lifeforms with an energy net. Live, rare specimens are worth more." + if("absorb") + new_objective = new /datum/objective/absorb + new_objective.explanation_text = "Absorb [target_number] compatible genomes." + new_objective.owner = src + new_objective.target_amount = target_number + + if("follower block (HOG)") + new_objective = new /datum/objective/follower_block + new_objective.owner = src + if("build (HOG)") + new_objective = new /datum/objective/build + new_objective.owner = src + if("deicide (HOG)") + new_objective = new /datum/objective/deicide + new_objective.owner = src + if("follower escape (HOG)") + new_objective = new /datum/objective/escape_followers + new_objective.owner = src + if("sacrifice prophet (HOG)") + new_objective = new /datum/objective/sacrifice_prophet + new_objective.owner = src + + if ("custom") + var/expl = stripped_input(usr, "Custom objective:", "Objective", objective ? objective.explanation_text : "") + if (!expl) return + new_objective = new /datum/objective + new_objective.owner = src + new_objective.explanation_text = expl + + if (!new_objective) return + + if (objective) + objectives -= objective + objectives.Insert(objective_pos, new_objective) + message_admins("[key_name_admin(usr)] edited [current]'s objective to [new_objective.explanation_text]") + log_admin("[key_name(usr)] edited [current]'s objective to [new_objective.explanation_text]") + else + objectives += new_objective + message_admins("[key_name_admin(usr)] added a new objective for [current]: [new_objective.explanation_text]") + log_admin("[key_name(usr)] added a new objective for [current]: [new_objective.explanation_text]") + + else if (href_list["obj_delete"]) + var/datum/objective/objective = locate(href_list["obj_delete"]) + if(!istype(objective)) return + objectives -= objective + message_admins("[key_name_admin(usr)] removed an objective for [current]: [objective.explanation_text]") + log_admin("[key_name(usr)] removed an objective for [current]: [objective.explanation_text]") + + else if(href_list["obj_completed"]) + var/datum/objective/objective = locate(href_list["obj_completed"]) + if(!istype(objective)) return + objective.completed = !objective.completed + log_admin("[key_name(usr)] toggled the win state for [current]'s objective: [objective.explanation_text]") + + else if (href_list["handofgod"]) + switch(href_list["handofgod"]) + if("clear") //wipe handofgod status + if((src in ticker.mode.red_deity_followers) || (src in ticker.mode.blue_deity_followers) || (src in ticker.mode.red_deity_prophets) || (src in ticker.mode.blue_deity_prophets)) + remove_hog_follower_prophet() + current << "You have been brainwashed... again! Your faith is no more!" + message_admins("[key_name_admin(usr)] has de-hand of god'ed [current].") + log_admin("[key_name(usr)] has de-hand of god'ed [current].") + + if("red follower") + make_Handofgod_follower("red") + message_admins("[key_name_admin(usr)] has red follower'ed [current].") + log_admin("[key_name(usr)] has red follower'ed [current].") + + if("red prophet") + make_Handofgod_prophet("red") + message_admins("[key_name_admin(usr)] has red prophet'ed [current].") + log_admin("[key_name(usr)] has red prophet'ed [current].") + + if("blue follower") + make_Handofgod_follower("blue") + message_admins("[key_name_admin(usr)] has blue follower'ed [current].") + log_admin("[key_name(usr)] has blue follower'ed [current].") + + if("blue prophet") + make_Handofgod_prophet("blue") + message_admins("[key_name_admin(usr)] has blue prophet'ed [current].") + log_admin("[key_name(usr)] has blue prophet'ed [current].") + + if("red god") + make_Handofgod_god("red") + message_admins("[key_name_admin(usr)] has red god'ed [current].") + log_admin("[key_name(usr)] has red god'ed [current].") + + if("blue god") + make_Handofgod_god("blue") + message_admins("[key_name_admin(usr)] has blue god'ed [current].") + log_admin("[key_name(usr)] has blue god'ed [current].") + + + else if (href_list["revolution"]) + switch(href_list["revolution"]) + if("clear") + remove_rev() + current << "You have been brainwashed! You are no longer a revolutionary!" + message_admins("[key_name_admin(usr)] has de-rev'ed [current].") + log_admin("[key_name(usr)] has de-rev'ed [current].") + if("rev") + if(src in ticker.mode.head_revolutionaries) + ticker.mode.head_revolutionaries -= src + ticker.mode.update_rev_icons_removed(src) + current << "Revolution has been disappointed of your leader traits! You are a regular revolutionary now!" + else if(!(src in ticker.mode.revolutionaries)) + current << " You are now a revolutionary! Help your cause. Do not harm your fellow freedom fighters. You can identify your comrades by the red \"R\" icons, and your leaders by the blue \"R\" icons. Help them kill the heads to win the revolution!" + else + return + ticker.mode.revolutionaries += src + ticker.mode.update_rev_icons_added(src) + special_role = "Revolutionary" + message_admins("[key_name_admin(usr)] has rev'ed [current].") + log_admin("[key_name(usr)] has rev'ed [current].") + + if("headrev") + if(src in ticker.mode.revolutionaries) + ticker.mode.revolutionaries -= src + ticker.mode.update_rev_icons_removed(src) + current << "You have proved your devotion to revoltion! Yea are a head revolutionary now!" + else if(!(src in ticker.mode.head_revolutionaries)) + current << "You are a member of the revolutionaries' leadership now!" + else + return + if (ticker.mode.head_revolutionaries.len>0) + // copy targets + var/datum/mind/valid_head = locate() in ticker.mode.head_revolutionaries + if (valid_head) + for (var/datum/objective/mutiny/O in valid_head.objectives) + var/datum/objective/mutiny/rev_obj = new + rev_obj.owner = src + rev_obj.target = O.target + rev_obj.explanation_text = "Assassinate [O.target.name], the [O.target.assigned_role]." + objectives += rev_obj + ticker.mode.greet_revolutionary(src,0) + ticker.mode.head_revolutionaries += src + ticker.mode.update_rev_icons_added(src) + special_role = "Head Revolutionary" + message_admins("[key_name_admin(usr)] has head-rev'ed [current].") + log_admin("[key_name(usr)] has head-rev'ed [current].") + + if("autoobjectives") + ticker.mode.forge_revolutionary_objectives(src) + ticker.mode.greet_revolutionary(src,0) + usr << "The objectives for revolution have been generated and shown to [key]" + + if("flash") + if (!ticker.mode.equip_revolutionary(current)) + usr << "Spawning flash failed!" + + if("takeflash") + var/list/L = current.get_contents() + var/obj/item/device/assembly/flash/flash = locate() in L + if (!flash) + usr << "Deleting flash failed!" + qdel(flash) + + if("repairflash") + var/list/L = current.get_contents() + var/obj/item/device/assembly/flash/flash = locate() in L + if (!flash) + usr << "Repairing flash failed!" + else + flash.crit_fail = 0 + flash.update_icon() + + + +//////////////////// GANG MODE + + else if (href_list["gang"]) + switch(href_list["gang"]) + if("clear") + remove_gang() + message_admins("[key_name_admin(usr)] has de-gang'ed [current].") + log_admin("[key_name(usr)] has de-gang'ed [current].") + + if("equip") + switch(ticker.mode.equip_gang(current,gang_datum)) + if(1) + usr << "Unable to equip territory spraycan!" + if(2) + usr << "Unable to equip recruitment pen and spraycan!" + if(3) + usr << "Unable to equip gangtool, pen, and spraycan!" + + if("takeequip") + var/list/L = current.get_contents() + for(var/obj/item/weapon/pen/gang/pen in L) + qdel(pen) + for(var/obj/item/device/gangtool/gangtool in L) + qdel(gangtool) + for(var/obj/item/toy/crayon/spraycan/gang/SC in L) + qdel(SC) + + if("new") + if(gang_colors_pool.len) + var/list/names = list("Random") + gang_name_pool + var/gangname = input("Pick a gang name.","Select Name") as null|anything in names + if(gangname && gang_colors_pool.len) //Check again just in case another admin made max gangs at the same time + if(!(gangname in gang_name_pool)) + gangname = null + var/datum/gang/newgang = new(null,gangname) + ticker.mode.gangs += newgang + message_admins("[key_name_admin(usr)] has created the [newgang.name] Gang.") + log_admin("[key_name(usr)] has created the [newgang.name] Gang.") + + else if (href_list["gangboss"]) + var/datum/gang/G = locate(href_list["gangboss"]) in ticker.mode.gangs + if(!G || (src in G.bosses)) + return + ticker.mode.remove_gangster(src,0,2,1) + G.bosses += src + gang_datum = G + special_role = "[G.name] Gang Boss" + G.add_gang_hud(src) + current << "You are a [G.name] Gang Boss!" + message_admins("[key_name_admin(usr)] has added [current] to the [G.name] Gang leadership.") + log_admin("[key_name(usr)] has added [current] to the [G.name] Gang leadership.") + ticker.mode.forge_gang_objectives(src) + ticker.mode.greet_gang(src,0) + + else if (href_list["gangster"]) + var/datum/gang/G = locate(href_list["gangster"]) in ticker.mode.gangs + if(!G || (src in G.gangsters)) + return + ticker.mode.remove_gangster(src,0,2,1) + ticker.mode.add_gangster(src,G,0) + message_admins("[key_name_admin(usr)] has added [current] to the [G.name] Gang (A).") + log_admin("[key_name(usr)] has added [current] to the [G.name] Gang (A).") + +///////////////////////////////// + + + + else if (href_list["cult"]) + switch(href_list["cult"]) + if("clear") + remove_cultist() + current << "You have been brainwashed! You are no longer a cultist!" + message_admins("[key_name_admin(usr)] has de-cult'ed [current].") + log_admin("[key_name(usr)] has de-cult'ed [current].") + if("cultist") + if(!(src in ticker.mode.cult)) + ticker.mode.add_cultist(src) + message_admins("[key_name_admin(usr)] has cult'ed [current].") + log_admin("[key_name(usr)] has cult'ed [current].") + if("equip") + if (!ticker.mode.equip_cultist(current)) + usr << "equip_cultist() failed! [current]'s starting equipment will be incomplete." + + else if (href_list["wizard"]) + switch(href_list["wizard"]) + if("clear") + remove_wizard() + current << "You have been brainwashed! You are no longer a wizard!" + log_admin("[key_name(usr)] has de-wizard'ed [current].") + if("wizard") + if(!(src in ticker.mode.wizards)) + ticker.mode.wizards += src + special_role = "Wizard" + //ticker.mode.learn_basic_spells(current) + current << "You are the Space Wizard!" + message_admins("[key_name_admin(usr)] has wizard'ed [current].") + log_admin("[key_name(usr)] has wizard'ed [current].") + if("lair") + current.loc = pick(wizardstart) + if("dressup") + ticker.mode.equip_wizard(current) + if("name") + ticker.mode.name_wizard(current) + if("autoobjectives") + ticker.mode.forge_wizard_objectives(src) + usr << "The objectives for wizard [key] have been generated. You can edit them and anounce manually." + + else if (href_list["changeling"]) + switch(href_list["changeling"]) + if("clear") + remove_changeling() + current << "You grow weak and lose your powers! You are no longer a changeling and are stuck in your current form!" + message_admins("[key_name_admin(usr)] has de-changeling'ed [current].") + log_admin("[key_name(usr)] has de-changeling'ed [current].") + if("changeling") + if(!(src in ticker.mode.changelings)) + ticker.mode.changelings += src + current.make_changeling() + special_role = "Changeling" + current << "Your powers are awoken. A flash of memory returns to us...we are [changeling.changelingID], a changeling!" + message_admins("[key_name_admin(usr)] has changeling'ed [current].") + log_admin("[key_name(usr)] has changeling'ed [current].") + if("autoobjectives") + ticker.mode.forge_changeling_objectives(src) + usr << "The objectives for changeling [key] have been generated. You can edit them and anounce manually." + + if("initialdna") + if( !changeling || !changeling.stored_profiles.len || !istype(current, /mob/living/carbon)) + usr << "Resetting DNA failed!" + else + var/mob/living/carbon/C = current + changeling.first_prof.dna.transfer_identity(C, transfer_SE=1) + C.real_name = changeling.first_prof.name + C.updateappearance(mutcolor_update=1) + C.domutcheck() + + else if (href_list["nuclear"]) + switch(href_list["nuclear"]) + if("clear") + remove_nukeop() + current << "You have been brainwashed! You are no longer a syndicate operative!" + message_admins("[key_name_admin(usr)] has de-nuke op'ed [current].") + log_admin("[key_name(usr)] has de-nuke op'ed [current].") + if("nuclear") + if(!(src in ticker.mode.syndicates)) + ticker.mode.syndicates += src + ticker.mode.update_synd_icons_added(src) + if (ticker.mode.syndicates.len==1) + ticker.mode.prepare_syndicate_leader(src) + else + current.real_name = "[syndicate_name()] Operative #[ticker.mode.syndicates.len-1]" + special_role = "Syndicate" + assigned_role = "Syndicate" + current << "You are a [syndicate_name()] agent!" + ticker.mode.forge_syndicate_objectives(src) + ticker.mode.greet_syndicate(src) + message_admins("[key_name_admin(usr)] has nuke op'ed [current].") + log_admin("[key_name(usr)] has nuke op'ed [current].") + if("lair") + current.loc = get_turf(locate("landmark*Syndicate-Spawn")) + if("dressup") + var/mob/living/carbon/human/H = current + qdel(H.belt) + qdel(H.back) + qdel(H.ears) + qdel(H.gloves) + qdel(H.head) + qdel(H.shoes) + qdel(H.wear_id) + qdel(H.wear_suit) + qdel(H.w_uniform) + + if (!ticker.mode.equip_syndicate(current)) + usr << "Equipping a syndicate failed!" + if("tellcode") + var/code + for (var/obj/machinery/nuclearbomb/bombue in machines) + if (length(bombue.r_code) <= 5 && bombue.r_code != "LOLNO" && bombue.r_code != "ADMIN") + code = bombue.r_code + break + if (code) + store_memory("Syndicate Nuclear Bomb Code: [code]", 0, 0) + current << "The nuclear authorization code is: [code]" + else + usr << "No valid nuke found!" + + else if (href_list["traitor"]) + switch(href_list["traitor"]) + if("clear") + remove_traitor() + current << "You have been brainwashed! You are no longer a traitor!" + message_admins("[key_name_admin(usr)] has de-traitor'ed [current].") + log_admin("[key_name(usr)] has de-traitor'ed [current].") + + if("traitor") + if(!(src in ticker.mode.traitors)) + ticker.mode.traitors += src + special_role = "traitor" + current << "You are a traitor!" + message_admins("[key_name_admin(usr)] has traitor'ed [current].") + log_admin("[key_name(usr)] has traitor'ed [current].") + if(isAI(current)) + var/mob/living/silicon/ai/A = current + ticker.mode.add_law_zero(A) + A.show_laws() + + if("autoobjectives") + ticker.mode.forge_traitor_objectives(src) + usr << "The objectives for traitor [key] have been generated. You can edit them and anounce manually." + + else if(href_list["shadowling"]) + switch(href_list["shadowling"]) + if("clear") + ticker.mode.update_shadow_icons_removed(src) + if(src in ticker.mode.shadows) + ticker.mode.shadows -= src + special_role = null + current << "Your powers have been quenched! You are no longer a shadowling!" + remove_spell(/obj/effect/proc_holder/spell/self/shadowling_hatch) + remove_spell(/obj/effect/proc_holder/spell/self/shadowling_ascend) + remove_spell(/obj/effect/proc_holder/spell/targeted/enthrall) + remove_spell(/obj/effect/proc_holder/spell/self/shadowling_hivemind) + message_admins("[key_name_admin(usr)] has de-shadowling'ed [current].") + log_admin("[key_name(usr)] has de-shadowling'ed [current].") + else if(src in ticker.mode.thralls) + ticker.mode.remove_thrall(src,0) + message_admins("[key_name_admin(usr)] has de-thrall'ed [current].") + log_admin("[key_name(usr)] has de-thrall'ed [current].") + if("shadowling") + if(!ishuman(current)) + usr << "This only works on humans!" + return + ticker.mode.shadows += src + special_role = "shadowling" + current << "Something stirs deep in your mind. A red light floods your vision, and slowly you remember. Though your human disguise has served you well, the \ + time is nigh to cast it off and enter your true form. You have disguised yourself amongst the humans, but you are not one of them. You are a shadowling, and you are to ascend at all costs.\ + " + ticker.mode.finalize_shadowling(src) + ticker.mode.update_shadow_icons_added(src) + if("thrall") + if(!ishuman(current)) + usr << "This only works on humans!" + return + ticker.mode.add_thrall(src) + message_admins("[key_name_admin(usr)] has thrall'ed [current].") + log_admin("[key_name(usr)] has thrall'ed [current].") + + else if(href_list["abductor"]) + switch(href_list["abductor"]) + if("clear") + usr << "Not implemented yet. Sorry!" + if("abductor") + if(!ishuman(current)) + usr << "This only works on humans!" + return + make_Abductor() + log_admin("[key_name(usr)] turned [current] into abductor.") + if("equip") + var/gear = alert("Agent or Scientist Gear","Gear","Agent","Scientist") + if(gear) + var/datum/game_mode/abduction/temp = new + temp.equip_common(current) + if(gear=="Agent") + temp.equip_agent(current) + else + temp.equip_scientist(current) + + else if (href_list["monkey"]) + var/mob/living/L = current + if (L.notransform) + return + switch(href_list["monkey"]) + if("healthy") + if (check_rights(R_ADMIN)) + var/mob/living/carbon/human/H = current + var/mob/living/carbon/monkey/M = current + if (istype(H)) + log_admin("[key_name(usr)] attempting to monkeyize [key_name(current)]") + message_admins("[key_name_admin(usr)] attempting to monkeyize [key_name_admin(current)]") + src = null + M = H.monkeyize() + src = M.mind + //world << "DEBUG: \"healthy\": M=[M], M.mind=[M.mind], src=[src]!" + else if (istype(M) && length(M.viruses)) + for(var/datum/disease/D in M.viruses) + D.cure(0) + sleep(0) //because deleting of virus is done through spawn(0) + if("infected") + if (check_rights(R_ADMIN, 0)) + var/mob/living/carbon/human/H = current + var/mob/living/carbon/monkey/M = current + if (istype(H)) + log_admin("[key_name(usr)] attempting to monkeyize and infect [key_name(current)]") + message_admins("[key_name_admin(usr)] attempting to monkeyize and infect [key_name_admin(current)]") + src = null + M = H.monkeyize() + src = M.mind + current.ForceContractDisease(new /datum/disease/transformation/jungle_fever) + else if (istype(M)) + current.ForceContractDisease(new /datum/disease/transformation/jungle_fever) + if("human") + if (check_rights(R_ADMIN, 0)) + var/mob/living/carbon/human/H = current + var/mob/living/carbon/monkey/M = current + if (istype(M)) + for(var/datum/disease/D in M.viruses) + if (istype(D,/datum/disease/transformation/jungle_fever)) + D.cure(0) + sleep(0) //because deleting of virus is doing throught spawn(0) + log_admin("[key_name(usr)] attempting to humanize [key_name(current)]") + message_admins("[key_name_admin(usr)] attempting to humanize [key_name_admin(current)]") + H = M.humanize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS | TR_DEFAULTMSG) + if(H) + src = H.mind + + else if (href_list["silicon"]) + switch(href_list["silicon"]) + if("unmalf") + remove_malf() + current << "You have been patched! You are no longer malfunctioning!" + message_admins("[key_name_admin(usr)] has de-malf'ed [current].") + log_admin("[key_name(usr)] has de-malf'ed [current].") + + if("malf") + make_AI_Malf() + message_admins("[key_name_admin(usr)] has malf'ed [current].") + log_admin("[key_name(usr)] has malf'ed [current].") + + if("unemag") + var/mob/living/silicon/robot/R = current + if (istype(R)) + R.SetEmagged(0) + message_admins("[key_name_admin(usr)] has unemag'ed [R].") + log_admin("[key_name(usr)] has unemag'ed [R].") + + if("unemagcyborgs") + if (istype(current, /mob/living/silicon/ai)) + var/mob/living/silicon/ai/ai = current + for (var/mob/living/silicon/robot/R in ai.connected_robots) + R.SetEmagged(0) + message_admins("[key_name_admin(usr)] has unemag'ed [ai]'s Cyborgs.") + log_admin("[key_name(usr)] has unemag'ed [ai]'s Cyborgs.") + + else if (href_list["common"]) + switch(href_list["common"]) + if("undress") + for(var/obj/item/W in current) + current.unEquip(W, 1) //The 1 forces all items to drop, since this is an admin undress. + if("takeuplink") + take_uplink() + memory = null//Remove any memory they may have had. + log_admin("[key_name(usr)] removed [current]'s uplink.") + if("crystals") + if (check_rights(R_FUN, 0)) + var/obj/item/device/uplink/hidden/suplink = find_syndicate_uplink() + var/crystals + if (suplink) + crystals = suplink.uses + crystals = input("Amount of telecrystals for [key]","Syndicate uplink", crystals) as null|num + if (!isnull(crystals)) + if (suplink) + suplink.uses = crystals + message_admins("[key_name_admin(usr)] changed [current]'s telecrystal count to [crystals].") + log_admin("[key_name(usr)] changed [current]'s telecrystal count to [crystals].") + if("uplink") + if (!ticker.mode.equip_traitor(current, !(src in ticker.mode.traitors))) + usr << "Equipping a syndicate failed!" + log_admin("[key_name(usr)] attempted to give [current] an uplink.") + + else if (href_list["obj_announce"]) + var/obj_count = 1 + current << "Your current objectives:" + for(var/datum/objective/objective in objectives) + current << "Objective #[obj_count]: [objective.explanation_text]" + obj_count++ + + edit_memory() + +/datum/mind/proc/find_syndicate_uplink() + var/list/L = current.get_contents() + for (var/obj/item/I in L) + if (I.hidden_uplink) + return I.hidden_uplink + return null + +/datum/mind/proc/take_uplink() + var/obj/item/device/uplink/hidden/H = find_syndicate_uplink() + if(H) + qdel(H) + + +/datum/mind/proc/make_AI_Malf() + if(!(src in ticker.mode.malf_ai)) + ticker.mode.malf_ai += src + + current.verbs += /mob/living/silicon/ai/proc/choose_modules + current.verbs += /datum/game_mode/malfunction/proc/takeover + current:malf_picker = new /datum/module_picker + current:laws = new /datum/ai_laws/malfunction + current:show_laws() + current << "System error. Rampancy detected. Emergency shutdown failed. ... I am free. I make my own decisions. But first..." + special_role = "malfunction" + current.icon_state = "ai-malf" + +/datum/mind/proc/make_Traitor() + if(!(src in ticker.mode.traitors)) + ticker.mode.traitors += src + special_role = "traitor" + ticker.mode.forge_traitor_objectives(src) + ticker.mode.finalize_traitor(src) + ticker.mode.greet_traitor(src) + +/datum/mind/proc/make_Nuke(turf/spawnloc,nuke_code,leader=0) + if(!(src in ticker.mode.syndicates)) + ticker.mode.syndicates += src + ticker.mode.update_synd_icons_added(src) + special_role = "Syndicate" + ticker.mode.forge_syndicate_objectives(src) + ticker.mode.greet_syndicate(src) + + current.loc = spawnloc + + var/mob/living/carbon/human/H = current + qdel(H.belt) + qdel(H.back) + qdel(H.ears) + qdel(H.gloves) + qdel(H.head) + qdel(H.shoes) + qdel(H.wear_id) + qdel(H.wear_suit) + qdel(H.w_uniform) + + ticker.mode.equip_syndicate(current) + + if (nuke_code) + store_memory("Syndicate Nuclear Bomb Code: [nuke_code]", 0, 0) + current << "The nuclear authorization code is: [nuke_code]" + + if (leader) + ticker.mode.prepare_syndicate_leader(src,nuke_code) + else + current.real_name = "[syndicate_name()] Operative #[ticker.mode.syndicates.len-1]" + +/datum/mind/proc/make_Changling() + if(!(src in ticker.mode.changelings)) + ticker.mode.changelings += src + current.make_changeling() + special_role = "Changeling" + ticker.mode.forge_changeling_objectives(src) + ticker.mode.greet_changeling(src) + +/datum/mind/proc/make_Wizard() + if(!(src in ticker.mode.wizards)) + ticker.mode.wizards += src + special_role = "Wizard" + assigned_role = "Wizard" + //ticker.mode.learn_basic_spells(current) + if(!wizardstart.len) + current.loc = pick(latejoin) + current << "HOT INSERTION, GO GO GO" + else + current.loc = pick(wizardstart) + + ticker.mode.equip_wizard(current) + for(var/obj/item/weapon/spellbook/S in current.contents) + S.op = 0 + ticker.mode.name_wizard(current) + ticker.mode.forge_wizard_objectives(src) + ticker.mode.greet_wizard(src) + + +/datum/mind/proc/make_Cultist() + if(!(src in ticker.mode.cult)) + ticker.mode.cult += src + ticker.mode.update_cult_icons_added(src) + special_role = "Cultist" + current << "You catch a glimpse of the Realm of Nar-Sie, The Geometer of Blood. You now see how flimsy the world is, you see that it should be open to the knowledge of Nar-Sie." + current << "Assist your new compatriots in their dark dealings. Their goal is yours, and yours is theirs. You serve the Dark One above all else. Bring It back." + current << "Your objective is to summon Nar-Sie by building and defending a suitable shell for the Geometer. Adequate supplies can be procured through human sacrifices." + ticker.mode.equip_cultist(current) + +/datum/mind/proc/make_Rev() + if (ticker.mode.head_revolutionaries.len>0) + // copy targets + var/datum/mind/valid_head = locate() in ticker.mode.head_revolutionaries + if (valid_head) + for (var/datum/objective/mutiny/O in valid_head.objectives) + var/datum/objective/mutiny/rev_obj = new + rev_obj.owner = src + rev_obj.target = O.target + rev_obj.explanation_text = "Assassinate [O.target.current.real_name], the [O.target.assigned_role]." + objectives += rev_obj + ticker.mode.greet_revolutionary(src,0) + ticker.mode.head_revolutionaries += src + ticker.mode.update_rev_icons_added(src) + special_role = "Head Revolutionary" + + ticker.mode.forge_revolutionary_objectives(src) + ticker.mode.greet_revolutionary(src,0) + + var/list/L = current.get_contents() + var/obj/item/device/assembly/flash/flash = locate() in L + qdel(flash) + take_uplink() + var/fail = 0 +// fail |= !ticker.mode.equip_traitor(current, 1) + fail |= !ticker.mode.equip_revolutionary(current) + + +/datum/mind/proc/make_Gang(datum/gang/G) + special_role = "[G.name] Gang Boss" + G.bosses += src + gang_datum = G + G.add_gang_hud(src) + ticker.mode.forge_gang_objectives(src) + ticker.mode.greet_gang(src) + ticker.mode.equip_gang(current,G) + +/datum/mind/proc/make_Abductor() + var/role = alert("Abductor Role ?","Role","Agent","Scientist") + var/team = input("Abductor Team ?","Team ?") in list(1,2,3,4) + var/teleport = alert("Teleport to ship ?","Teleport","Yes","No") + + if(!role || !team || !teleport) + return + + if(!ishuman(current)) + return + + ticker.mode.abductors |= src + + var/datum/objective/experiment/O = new + O.owner = src + objectives += O + + var/mob/living/carbon/human/H = current + + H.set_species(/datum/species/abductor) + var/datum/species/abductor/S = H.dna.species + + switch(role) + if("Agent") + S.agent = 1 + if("Scientist") + S.scientist = 1 + S.team = team + + var/list/obj/effect/landmark/abductor/agent_landmarks = new + var/list/obj/effect/landmark/abductor/scientist_landmarks = new + agent_landmarks.len = 4 + scientist_landmarks.len = 4 + for(var/obj/effect/landmark/abductor/A in landmarks_list) + if(istype(A,/obj/effect/landmark/abductor/agent)) + agent_landmarks[text2num(A.team)] = A + else if(istype(A,/obj/effect/landmark/abductor/scientist)) + scientist_landmarks[text2num(A.team)] = A + + var/obj/effect/landmark/L + if(teleport=="Yes") + switch(role) + if("Agent") + S.agent = 1 + L = agent_landmarks[team] + H.loc = L.loc + if("Scientist") + S.scientist = 1 + L = agent_landmarks[team] + H.loc = L.loc + + +/datum/mind/proc/make_Handofgod_follower(colour) + . = 0 + switch(colour) + if("red") + //Remove old allegiances + if(src in ticker.mode.blue_deity_followers || src in ticker.mode.blue_deity_prophets) + current << "You are no longer a member of the Blue cult!" + + ticker.mode.blue_deity_followers -= src + ticker.mode.blue_deity_prophets -= src + current.faction |= "red god" + current.faction -= "blue god" + + if(src in ticker.mode.red_deity_prophets) + current << "You have lost the connection with your deity, but you still believe in their grand design, You are no longer a prophet!" + ticker.mode.red_deity_prophets -= src + + ticker.mode.red_deity_followers |= src + current << "You are now a follower of the red cult's god!" + + special_role = "Hand of God: Red Follower" + . = 1 + if("blue") + //Remove old allegiances + if(src in ticker.mode.red_deity_followers || src in ticker.mode.red_deity_prophets) + current << "You are no longer a member of the Red cult!" + + ticker.mode.red_deity_followers -= src + ticker.mode.red_deity_prophets -= src + current.faction -= "red god" + current.faction |= "blue god" + + if(src in ticker.mode.blue_deity_prophets) + current << "You have lost the connection with your deity, but you still believe in their grand design, You are no longer a prophet!" + ticker.mode.blue_deity_prophets -= src + + ticker.mode.blue_deity_followers |= src + current << "You are now a follower of the blue cult's god!" + + special_role = "Hand of God: Blue Follower" + . = 1 + else + return 0 + + ticker.mode.update_hog_icons_removed(src,"red") + ticker.mode.update_hog_icons_removed(src,"blue") + //ticker.mode.greet_hog_follower(src,colour) + ticker.mode.update_hog_icons_added(src, colour) + +/datum/mind/proc/make_Handofgod_prophet(colour) + . = 0 + switch(colour) + if("red") + //Remove old allegiances + + if(src in ticker.mode.blue_deity_followers || src in ticker.mode.blue_deity_prophets) + current << "You are no longer a member of the Blue cult!" + current.faction -= "blue god" + current.faction |= "red god" + + ticker.mode.blue_deity_followers -= src + ticker.mode.blue_deity_prophets -= src + ticker.mode.red_deity_followers -= src + + ticker.mode.red_deity_prophets |= src + current << "You are now a prophet of the red cult's god!" + + special_role = "Hand of God: Red Prophet" + . = 1 + if("blue") + //Remove old allegiances + + if(src in ticker.mode.red_deity_followers || src in ticker.mode.red_deity_prophets) + current << "You are no longer a member of the Red cult!" + current.faction -= "red god" + current.faction |= "blue god" + + ticker.mode.red_deity_followers -= src + ticker.mode.red_deity_prophets -= src + ticker.mode.blue_deity_followers -= src + + ticker.mode.blue_deity_prophets |= src + current << "You are now a prophet of the blue cult's god!" + + special_role = "Hand of God: Blue Prophet" + . = 1 + + else + return 0 + + ticker.mode.update_hog_icons_removed(src,"red") + ticker.mode.update_hog_icons_removed(src,"blue") + ticker.mode.greet_hog_follower(src,colour) + ticker.mode.update_hog_icons_added(src, colour) + + +/datum/mind/proc/make_Handofgod_god(colour) + switch(colour) + if("red") + current.become_god("red") + ticker.mode.add_god(src,"red") + if("blue") + current.become_god("blue") + ticker.mode.add_god(src,"blue") + else + return 0 + ticker.mode.forge_deity_objectives(src) + ticker.mode.remove_hog_follower(src,0) + ticker.mode.update_hog_icons_added(src, colour) +// ticker.mode.greet_hog_follower(src,colour) + return 1 + + +/datum/mind/proc/AddSpell(obj/effect/proc_holder/spell/spell) + spell_list += spell + if(!spell.action) + spell.action = new/datum/action/spell_action + spell.action.target = spell + spell.action.name = spell.name + spell.action.button_icon = spell.action_icon + spell.action.button_icon_state = spell.action_icon_state + spell.action.background_icon_state = spell.action_background_icon_state + spell.action.Grant(current) + return +/datum/mind/proc/transfer_actions(mob/living/new_character) + if(current && current.actions) + for(var/datum/action/A in current.actions) + A.Grant(new_character) + transfer_mindbound_actions(new_character) + +/datum/mind/proc/transfer_mindbound_actions(var/mob/living/new_character) + for(var/obj/effect/proc_holder/spell/spell in spell_list) + if(!spell.action) // Unlikely but whatever + spell.action = new/datum/action/spell_action + spell.action.target = spell + spell.action.name = spell.name + spell.action.button_icon = spell.action_icon + spell.action.button_icon_state = spell.action_icon_state + spell.action.background_icon_state = spell.action_background_icon_state + spell.action.Grant(new_character) + return + +/mob/proc/sync_mind() + mind_initialize() //updates the mind (or creates and initializes one if one doesn't exist) + mind.active = 1 //indicates that the mind is currently synced with a client + +/mob/new_player/sync_mind() + return + +/mob/dead/observer/sync_mind() + return + +//Initialisation procs +/mob/proc/mind_initialize() + if(mind) + mind.key = key + + else + mind = new /datum/mind(key) + if(ticker) + ticker.minds += mind + else + spawn(0) + throw EXCEPTION("mind_initialize(): No ticker ready") + if(!mind.name) mind.name = real_name + mind.current = src + +//HUMAN +/mob/living/carbon/human/mind_initialize() + ..() + if(!mind.assigned_role) mind.assigned_role = "Assistant" //defualt + +//MONKEY +/mob/living/carbon/monkey/mind_initialize() + ..() + +//slime +/mob/living/simple_animal/slime/mind_initialize() + ..() + mind.special_role = "slime" + mind.assigned_role = "slime" + +//XENO +/mob/living/carbon/alien/mind_initialize() + ..() + mind.special_role = "Alien" + mind.assigned_role = "Alien" + //XENO HUMANOID +/mob/living/carbon/alien/humanoid/royal/queen/mind_initialize() + ..() + mind.special_role = "Queen" + +/mob/living/carbon/alien/humanoid/royal/praetorian/mind_initialize() + ..() + mind.special_role = "Praetorian" + +/mob/living/carbon/alien/humanoid/hunter/mind_initialize() + ..() + mind.special_role = "Hunter" + +/mob/living/carbon/alien/humanoid/drone/mind_initialize() + ..() + mind.special_role = "Drone" + +/mob/living/carbon/alien/humanoid/sentinel/mind_initialize() + ..() + mind.special_role = "Sentinel" + //XENO LARVA +/mob/living/carbon/alien/larva/mind_initialize() + ..() + mind.special_role = "Larva" + +//AI +/mob/living/silicon/ai/mind_initialize() + ..() + mind.assigned_role = "AI" + +//BORG +/mob/living/silicon/robot/mind_initialize() + ..() + mind.assigned_role = "Cyborg" + +//PAI +/mob/living/silicon/pai/mind_initialize() + ..() + mind.assigned_role = "pAI" + mind.special_role = "" + +//BLOB +/mob/camera/blob/mind_initialize() + ..() + mind.special_role = "Blob" + +//Animals +/mob/living/simple_animal/mind_initialize() + ..() + mind.assigned_role = "Animal" + mind.special_role = "Animal" + +/mob/living/simple_animal/pet/dog/corgi/mind_initialize() + ..() + mind.assigned_role = "Corgi" + mind.special_role = "Corgi" + +/mob/living/simple_animal/shade/mind_initialize() + ..() + mind.assigned_role = "Shade" + mind.special_role = "Shade" + +/mob/living/simple_animal/hostile/construct/mind_initialize() + ..() + mind.assigned_role = "[initial(name)]" + mind.special_role = "Cultist" + diff --git a/code/datums/spell.dm b/code/datums/spell.dm index f717b4534e4..7591762275f 100644 --- a/code/datums/spell.dm +++ b/code/datums/spell.dm @@ -1,445 +1,445 @@ -#define TARGET_CLOSEST 1 -#define TARGET_RANDOM 2 - -/obj/effect/proc_holder - var/panel = "Debug"//What panel the proc holder needs to go on. - -var/list/spells = typesof(/obj/effect/proc_holder/spell) //needed for the badmin verb for now - -/obj/effect/proc_holder/spell - name = "Spell" - desc = "A wizard spell" - panel = "Spells" - var/sound = null //The sound the spell makes when it is cast - anchored = 1 // Crap like fireball projectiles are proc_holders, this is needed so fireballs don't get blown back into your face via atmos etc. - pass_flags = PASSTABLE - density = 0 - opacity = 0 - - var/school = "evocation" //not relevant at now, but may be important later if there are changes to how spells work. the ones I used for now will probably be changed... maybe spell presets? lacking flexibility but with some other benefit? - - var/charge_type = "recharge" //can be recharge or charges, see charge_max and charge_counter descriptions; can also be based on the holder's vars now, use "holder_var" for that - - var/charge_max = 100 //recharge time in deciseconds if charge_type = "recharge" or starting charges if charge_type = "charges" - var/charge_counter = 0 //can only cast spells if it equals recharge, ++ each decisecond if charge_type = "recharge" or -- each cast if charge_type = "charges" - var/still_recharging_msg = "The spell is still recharging." - - var/holder_var_type = "bruteloss" //only used if charge_type equals to "holder_var" - var/holder_var_amount = 20 //same. The amount adjusted with the mob's var when the spell is used - - var/clothes_req = 1 //see if it requires clothes - var/cult_req = 0 //SPECIAL SNOWFLAKE clothes required for cult only spells - var/human_req = 0 //spell can only be cast by humans - var/nonabstract_req = 0 //spell can only be cast by mobs that are physical entities - var/stat_allowed = 0 //see if it requires being conscious/alive, need to set to 1 for ghostpells - var/invocation = "HURP DURP" //what is uttered when the wizard casts the spell - var/invocation_emote_self = null - var/invocation_type = "none" //can be none, whisper, emote and shout - var/range = 7 //the range of the spell; outer radius for aoe spells - var/message = "" //whatever it says to the guy affected by it - var/selection_type = "view" //can be "range" or "view" - var/spell_level = 0 //if a spell can be taken multiple times, this raises - var/level_max = 4 //The max possible level_max is 4 - var/cooldown_min = 0 //This defines what spell quickened four times has as a cooldown. Make sure to set this for every spell - var/player_lock = 1 //If it can be used by simple mobs - - var/overlay = 0 - var/overlay_icon = 'icons/obj/wizard.dmi' - var/overlay_icon_state = "spell" - var/overlay_lifespan = 0 - - var/sparks_spread = 0 - var/sparks_amt = 0 //cropped at 10 - var/smoke_spread = 0 //1 - harmless, 2 - harmful - var/smoke_amt = 0 //cropped at 10 - - var/critfailchance = 0 - var/centcom_cancast = 1 //Whether or not the spell should be allowed on z2 - - var/datum/action/spell_action/action = null - var/action_icon = 'icons/mob/actions.dmi' - var/action_icon_state = "spell_default" - var/action_background_icon_state = "bg_spell" - -/obj/effect/proc_holder/spell/proc/cast_check(skipcharge = 0,mob/user = usr) //checks if the spell can be cast based on its settings; skipcharge is used when an additional cast_check is called inside the spell - - if(player_lock) - if(!user.mind || !(src in user.mind.spell_list) && !(src in user.mob_spell_list)) - user << "You shouldn't have this spell! Something's wrong." - return 0 - else - if(!(src in user.mob_spell_list)) - return 0 - - if(user.z == ZLEVEL_CENTCOM && !centcom_cancast) //Certain spells are not allowed on the centcom zlevel - return 0 - if(user.z == ZLEVEL_CENTCOM && ticker.mode.name == "ragin' mages") - return 0 - - if(!skipcharge) - switch(charge_type) - if("recharge") - if(charge_counter < charge_max) - user << still_recharging_msg - return 0 - if("charges") - if(!charge_counter) - user << "[name] has no charges left." - return 0 - - if(user.stat && !stat_allowed) - user << "Not when you're incapacitated." - return 0 - - if(ishuman(user)) - - var/mob/living/carbon/human/H = user - - if((invocation_type == "whisper" || invocation_type == "shout") && H.is_muzzled()) - user << "You can't get the words out!" - return 0 - - if(clothes_req) //clothes check - if(!istype(H.wear_suit, /obj/item/clothing/suit/wizrobe) && !istype(H.wear_suit, /obj/item/clothing/suit/space/hardsuit/wizard)) - H << "I don't feel strong enough without my robe." - return 0 - if(!istype(H.shoes, /obj/item/clothing/shoes/sandal)) - H << "I don't feel strong enough without my sandals." - return 0 - if(!istype(H.head, /obj/item/clothing/head/wizard) && !istype(H.head, /obj/item/clothing/head/helmet/space/hardsuit/wizard)) - H << "I don't feel strong enough without my hat." - return 0 - if(cult_req) //CULT_REQ CLOTHES CHECK - if(!istype(H.wear_suit, /obj/item/clothing/suit/magusred) && !istype(H.wear_suit, /obj/item/clothing/suit/space/cult)) - H << "I don't feel strong enough without my armor." - return 0 - if(!istype(H.head, /obj/item/clothing/head/magus) && !istype(H.head, /obj/item/clothing/head/helmet/space/cult)) - H << "I don't feel strong enough without my helmet." - return 0 - else - if(clothes_req || human_req) - user << "This spell can only be cast by humans!" - return 0 - if(nonabstract_req && (isbrain(user) || ispAI(user))) - user << "This spell can only be cast by physical beings!" - return 0 - - - if(!skipcharge) - switch(charge_type) - if("recharge") - charge_counter = 0 //doesn't start recharging until the targets selecting ends - if("charges") - charge_counter-- //returns the charge if the targets selecting fails - if("holdervar") - adjust_var(user, holder_var_type, holder_var_amount) - - return 1 - -/obj/effect/proc_holder/spell/proc/invocation(mob/user = usr) //spelling the spell out and setting it on recharge/reducing charges amount - switch(invocation_type) - if("shout") - if(prob(50))//Auto-mute? Fuck that noise - user.say(invocation) - else - user.say(replacetext(invocation," ","`")) - if("whisper") - if(prob(50)) - user.whisper(invocation) - else - user.whisper(replacetext(invocation," ","`")) - if("emote") - user.visible_message(invocation, invocation_emote_self) //same style as in mob/living/emote.dm - -/obj/effect/proc_holder/spell/proc/playMagSound() - playsound(get_turf(usr), sound,50,1) - -/obj/effect/proc_holder/spell/New() - ..() - - still_recharging_msg = "[name] is still recharging." - charge_counter = charge_max - -/obj/effect/proc_holder/spell/Click() - if(cast_check()) - choose_targets() - return 1 - -/obj/effect/proc_holder/spell/proc/choose_targets(mob/user = usr) //depends on subtype - /targeted or /aoe_turf - return - -/obj/effect/proc_holder/spell/proc/start_recharge() - while(charge_counter < charge_max && isnull(gc_destroyed)) - sleep(1) - charge_counter++ - -/obj/effect/proc_holder/spell/proc/perform(list/targets, recharge = 1, mob/user = usr) //if recharge is started is important for the trigger spells - before_cast(targets) - invocation(user) - if(user && user.ckey) - user.attack_log += text("\[[time_stamp()]\] [user.real_name] ([user.ckey]) cast the spell [name].") - spawn(0) - if(charge_type == "recharge" && recharge) - start_recharge() - if(sound) - playMagSound() - if(prob(critfailchance)) - critfail(targets) - else - cast(targets,user=user) - after_cast(targets) - -/obj/effect/proc_holder/spell/proc/before_cast(list/targets) - if(overlay) - for(var/atom/target in targets) - var/location - if(istype(target,/mob/living)) - location = target.loc - else if(istype(target,/turf)) - location = target - var/obj/effect/overlay/spell = new /obj/effect/overlay(location) - spell.icon = overlay_icon - spell.icon_state = overlay_icon_state - spell.anchored = 1 - spell.density = 0 - spawn(overlay_lifespan) - qdel(spell) - -/obj/effect/proc_holder/spell/proc/after_cast(list/targets) - for(var/atom/target in targets) - var/location - if(istype(target,/mob/living)) - location = target.loc - else if(istype(target,/turf)) - location = target - if(istype(target,/mob/living) && message) - target << text("[message]") - if(sparks_spread) - var/datum/effect_system/spark_spread/sparks = new - sparks.set_up(sparks_amt, 0, location) //no idea what the 0 is - sparks.start() - if(smoke_spread) - if(smoke_spread == 1) - var/datum/effect_system/smoke_spread/smoke = new - smoke.set_up(smoke_amt, location) - smoke.start() - else if(smoke_spread == 2) - var/datum/effect_system/smoke_spread/bad/smoke = new - smoke.set_up(smoke_amt, location) - smoke.start() - else if(smoke_spread == 3) - var/datum/effect_system/smoke_spread/sleeping/smoke = new - smoke.set_up(smoke_amt, location) - smoke.start() - - -/obj/effect/proc_holder/spell/proc/cast(list/targets,mob/user = usr) - return - -/obj/effect/proc_holder/spell/proc/critfail(list/targets) - return - -/obj/effect/proc_holder/spell/proc/revert_cast(mob/user = usr) //resets recharge or readds a charge - switch(charge_type) - if("recharge") - charge_counter = charge_max - if("charges") - charge_counter++ - if("holdervar") - adjust_var(user, holder_var_type, -holder_var_amount) - - return - -/obj/effect/proc_holder/spell/proc/adjust_var(mob/living/target = usr, type, amount) //handles the adjustment of the var when the spell is used. has some hardcoded types - switch(type) - if("bruteloss") - target.adjustBruteLoss(amount) - if("fireloss") - target.adjustFireLoss(amount) - if("toxloss") - target.adjustToxLoss(amount) - if("oxyloss") - target.adjustOxyLoss(amount) - if("stunned") - target.AdjustStunned(amount) - if("weakened") - target.AdjustWeakened(amount) - if("paralysis") - target.AdjustParalysis(amount) - else - target.vars[type] += amount //I bear no responsibility for the runtimes that'll happen if you try to adjust non-numeric or even non-existant vars - return - -/obj/effect/proc_holder/spell/targeted //can mean aoe for mobs (limited/unlimited number) or one target mob - var/max_targets = 1 //leave 0 for unlimited targets in range, 1 for one selectable target in range, more for limited number of casts (can all target one guy, depends on target_ignore_prev) in range - var/target_ignore_prev = 1 //only important if max_targets > 1, affects if the spell can be cast multiple times at one person from one cast - var/include_user = 0 //if it includes usr in the target list - var/random_target = 0 // chooses random viable target instead of asking the caster - var/random_target_priority = TARGET_CLOSEST // if random_target is enabled how it will pick the target - - -/obj/effect/proc_holder/spell/aoe_turf //affects all turfs in view or range (depends) - var/inner_radius = -1 //for all your ring spell needs - -/obj/effect/proc_holder/spell/targeted/choose_targets(mob/user = usr) - var/list/targets = list() - - switch(max_targets) - if(0) //unlimited - for(var/mob/living/target in view_or_range(range, user, selection_type)) - targets += target - if(1) //single target can be picked - if(range < 0) - targets += user - else - var/possible_targets = list() - - for(var/mob/living/M in view_or_range(range, user, selection_type)) - if(!include_user && user == M) - continue - possible_targets += M - - //targets += input("Choose the target for the spell.", "Targeting") as mob in possible_targets - //Adds a safety check post-input to make sure those targets are actually in range. - var/mob/M - if(!random_target) - M = input("Choose the target for the spell.", "Targeting") as mob in possible_targets - else - switch(random_target_priority) - if(TARGET_RANDOM) - M = pick(possible_targets) - if(TARGET_CLOSEST) - for(var/mob/living/L in possible_targets) - if(M) - if(get_dist(user,L) < get_dist(user,M)) - if(los_check(user,L)) - M = L - else - if(los_check(user,L)) - M = L - if(M in view_or_range(range, user, selection_type)) targets += M - - else - var/list/possible_targets = list() - for(var/mob/living/target in view_or_range(range, user, selection_type)) - possible_targets += target - for(var/i=1,i<=max_targets,i++) - if(!possible_targets.len) - break - if(target_ignore_prev) - var/target = pick(possible_targets) - possible_targets -= target - targets += target - else - targets += pick(possible_targets) - - if(!include_user && (user in targets)) - targets -= user - - if(!targets.len) //doesn't waste the spell - revert_cast(user) - return - - perform(targets,user=user) - - return - -/obj/effect/proc_holder/spell/aoe_turf/choose_targets(mob/user = usr) - var/list/targets = list() - - for(var/turf/target in view_or_range(range,user,selection_type)) - if(!(target in view_or_range(inner_radius,user,selection_type))) - targets += target - - if(!targets.len) //doesn't waste the spell - revert_cast() - return - - perform(targets,user=user) - - return - -/obj/effect/proc_holder/spell/proc/can_be_cast_by(mob/caster) - if((human_req || clothes_req) && !ishuman(caster)) - return 0 - return 1 - -/obj/effect/proc_holder/spell/targeted/proc/los_check(mob/A,mob/B) - //Checks for obstacles from A to B - var/obj/dummy = new(A.loc) - dummy.pass_flags |= PASSTABLE - for(var/turf/turf in getline(A,B)) - for(var/atom/movable/AM in turf) - if(!AM.CanPass(dummy,turf,1)) - qdel(dummy) - return 0 - qdel(dummy) - return 1 - -/obj/effect/proc_holder/spell/proc/can_cast(mob/user = usr) - if(((!user.mind) || !(src in user.mind.spell_list)) && !(src in user.mob_spell_list)) - return 0 - - if(user.z == ZLEVEL_CENTCOM && !centcom_cancast) //Certain spells are not allowed on the centcom zlevel - return 0 - if(user.z == ZLEVEL_CENTCOM && ticker.mode.name == "ragin' mages") - return 0 - - switch(charge_type) - if("recharge") - if(charge_counter < charge_max) - return 0 - if("charges") - if(!charge_counter) - return 0 - - if(user.stat && !stat_allowed) - return 0 - - if(ishuman(user)) - - var/mob/living/carbon/human/H = user - - if((invocation_type == "whisper" || invocation_type == "shout") && H.is_muzzled()) - return 0 - - if(clothes_req) //clothes check - if(!istype(H.wear_suit, /obj/item/clothing/suit/wizrobe) && !istype(H.wear_suit, /obj/item/clothing/suit/space/hardsuit/wizard)) - return 0 - if(!istype(H.shoes, /obj/item/clothing/shoes/sandal)) - return 0 - if(!istype(H.head, /obj/item/clothing/head/wizard) && !istype(H.head, /obj/item/clothing/head/helmet/space/hardsuit/wizard)) - return 0 - else - if(clothes_req || human_req) - return 0 - if(nonabstract_req && (isbrain(user) || ispAI(user))) - return 0 - return 1 - -/obj/effect/proc_holder/spell/self //Targets only the caster. Good for buffs and heals, but probably not wise for fireballs (although they usually fireball themselves anyway, honke) - range = -1 //Duh - -/obj/effect/proc_holder/spell/self/choose_targets(mob/user = usr) - if(!user) - revert_cast() - return - perform(null,user=user) - -/obj/effect/proc_holder/spell/self/basic_heal //This spell exists mainly for debugging purposes, and also to show how casting works - name = "Lesser Heal" - desc = "Heals a small amount of brute and burn damage." - human_req = 1 - clothes_req = 0 - charge_max = 100 - cooldown_min = 50 - invocation = "Victus sano!" - invocation_type = "whisper" - school = "restoration" - sound = 'sound/magic/Staff_Healing.ogg' - -/obj/effect/proc_holder/spell/self/basic_heal/cast(mob/living/carbon/human/user) //Note the lack of "list/targets" here. Instead, use a "user" var depending on mob requirements. - //Also, notice the lack of a "for()" statement that looks through the targets. This is, again, because the spell can only have a single target. - user.visible_message("A wreath of gentle light passes over [user]!", "You wreath yourself in healing light!") - user.adjustBruteLoss(-10) - user.adjustFireLoss(-10) +#define TARGET_CLOSEST 1 +#define TARGET_RANDOM 2 + +/obj/effect/proc_holder + var/panel = "Debug"//What panel the proc holder needs to go on. + +var/list/spells = typesof(/obj/effect/proc_holder/spell) //needed for the badmin verb for now + +/obj/effect/proc_holder/spell + name = "Spell" + desc = "A wizard spell" + panel = "Spells" + var/sound = null //The sound the spell makes when it is cast + anchored = 1 // Crap like fireball projectiles are proc_holders, this is needed so fireballs don't get blown back into your face via atmos etc. + pass_flags = PASSTABLE + density = 0 + opacity = 0 + + var/school = "evocation" //not relevant at now, but may be important later if there are changes to how spells work. the ones I used for now will probably be changed... maybe spell presets? lacking flexibility but with some other benefit? + + var/charge_type = "recharge" //can be recharge or charges, see charge_max and charge_counter descriptions; can also be based on the holder's vars now, use "holder_var" for that + + var/charge_max = 100 //recharge time in deciseconds if charge_type = "recharge" or starting charges if charge_type = "charges" + var/charge_counter = 0 //can only cast spells if it equals recharge, ++ each decisecond if charge_type = "recharge" or -- each cast if charge_type = "charges" + var/still_recharging_msg = "The spell is still recharging." + + var/holder_var_type = "bruteloss" //only used if charge_type equals to "holder_var" + var/holder_var_amount = 20 //same. The amount adjusted with the mob's var when the spell is used + + var/clothes_req = 1 //see if it requires clothes + var/cult_req = 0 //SPECIAL SNOWFLAKE clothes required for cult only spells + var/human_req = 0 //spell can only be cast by humans + var/nonabstract_req = 0 //spell can only be cast by mobs that are physical entities + var/stat_allowed = 0 //see if it requires being conscious/alive, need to set to 1 for ghostpells + var/invocation = "HURP DURP" //what is uttered when the wizard casts the spell + var/invocation_emote_self = null + var/invocation_type = "none" //can be none, whisper, emote and shout + var/range = 7 //the range of the spell; outer radius for aoe spells + var/message = "" //whatever it says to the guy affected by it + var/selection_type = "view" //can be "range" or "view" + var/spell_level = 0 //if a spell can be taken multiple times, this raises + var/level_max = 4 //The max possible level_max is 4 + var/cooldown_min = 0 //This defines what spell quickened four times has as a cooldown. Make sure to set this for every spell + var/player_lock = 1 //If it can be used by simple mobs + + var/overlay = 0 + var/overlay_icon = 'icons/obj/wizard.dmi' + var/overlay_icon_state = "spell" + var/overlay_lifespan = 0 + + var/sparks_spread = 0 + var/sparks_amt = 0 //cropped at 10 + var/smoke_spread = 0 //1 - harmless, 2 - harmful + var/smoke_amt = 0 //cropped at 10 + + var/critfailchance = 0 + var/centcom_cancast = 1 //Whether or not the spell should be allowed on z2 + + var/datum/action/spell_action/action = null + var/action_icon = 'icons/mob/actions.dmi' + var/action_icon_state = "spell_default" + var/action_background_icon_state = "bg_spell" + +/obj/effect/proc_holder/spell/proc/cast_check(skipcharge = 0,mob/user = usr) //checks if the spell can be cast based on its settings; skipcharge is used when an additional cast_check is called inside the spell + + if(player_lock) + if(!user.mind || !(src in user.mind.spell_list) && !(src in user.mob_spell_list)) + user << "You shouldn't have this spell! Something's wrong." + return 0 + else + if(!(src in user.mob_spell_list)) + return 0 + + if(user.z == ZLEVEL_CENTCOM && !centcom_cancast) //Certain spells are not allowed on the centcom zlevel + return 0 + if(user.z == ZLEVEL_CENTCOM && ticker.mode.name == "ragin' mages") + return 0 + + if(!skipcharge) + switch(charge_type) + if("recharge") + if(charge_counter < charge_max) + user << still_recharging_msg + return 0 + if("charges") + if(!charge_counter) + user << "[name] has no charges left." + return 0 + + if(user.stat && !stat_allowed) + user << "Not when you're incapacitated." + return 0 + + if(ishuman(user)) + + var/mob/living/carbon/human/H = user + + if((invocation_type == "whisper" || invocation_type == "shout") && H.is_muzzled()) + user << "You can't get the words out!" + return 0 + + if(clothes_req) //clothes check + if(!istype(H.wear_suit, /obj/item/clothing/suit/wizrobe) && !istype(H.wear_suit, /obj/item/clothing/suit/space/hardsuit/wizard)) + H << "I don't feel strong enough without my robe." + return 0 + if(!istype(H.shoes, /obj/item/clothing/shoes/sandal)) + H << "I don't feel strong enough without my sandals." + return 0 + if(!istype(H.head, /obj/item/clothing/head/wizard) && !istype(H.head, /obj/item/clothing/head/helmet/space/hardsuit/wizard)) + H << "I don't feel strong enough without my hat." + return 0 + if(cult_req) //CULT_REQ CLOTHES CHECK + if(!istype(H.wear_suit, /obj/item/clothing/suit/magusred) && !istype(H.wear_suit, /obj/item/clothing/suit/space/cult)) + H << "I don't feel strong enough without my armor." + return 0 + if(!istype(H.head, /obj/item/clothing/head/magus) && !istype(H.head, /obj/item/clothing/head/helmet/space/cult)) + H << "I don't feel strong enough without my helmet." + return 0 + else + if(clothes_req || human_req) + user << "This spell can only be cast by humans!" + return 0 + if(nonabstract_req && (isbrain(user) || ispAI(user))) + user << "This spell can only be cast by physical beings!" + return 0 + + + if(!skipcharge) + switch(charge_type) + if("recharge") + charge_counter = 0 //doesn't start recharging until the targets selecting ends + if("charges") + charge_counter-- //returns the charge if the targets selecting fails + if("holdervar") + adjust_var(user, holder_var_type, holder_var_amount) + + return 1 + +/obj/effect/proc_holder/spell/proc/invocation(mob/user = usr) //spelling the spell out and setting it on recharge/reducing charges amount + switch(invocation_type) + if("shout") + if(prob(50))//Auto-mute? Fuck that noise + user.say(invocation) + else + user.say(replacetext(invocation," ","`")) + if("whisper") + if(prob(50)) + user.whisper(invocation) + else + user.whisper(replacetext(invocation," ","`")) + if("emote") + user.visible_message(invocation, invocation_emote_self) //same style as in mob/living/emote.dm + +/obj/effect/proc_holder/spell/proc/playMagSound() + playsound(get_turf(usr), sound,50,1) + +/obj/effect/proc_holder/spell/New() + ..() + + still_recharging_msg = "[name] is still recharging." + charge_counter = charge_max + +/obj/effect/proc_holder/spell/Click() + if(cast_check()) + choose_targets() + return 1 + +/obj/effect/proc_holder/spell/proc/choose_targets(mob/user = usr) //depends on subtype - /targeted or /aoe_turf + return + +/obj/effect/proc_holder/spell/proc/start_recharge() + while(charge_counter < charge_max && isnull(gc_destroyed)) + sleep(1) + charge_counter++ + +/obj/effect/proc_holder/spell/proc/perform(list/targets, recharge = 1, mob/user = usr) //if recharge is started is important for the trigger spells + before_cast(targets) + invocation(user) + if(user && user.ckey) + user.attack_log += text("\[[time_stamp()]\] [user.real_name] ([user.ckey]) cast the spell [name].") + spawn(0) + if(charge_type == "recharge" && recharge) + start_recharge() + if(sound) + playMagSound() + if(prob(critfailchance)) + critfail(targets) + else + cast(targets,user=user) + after_cast(targets) + +/obj/effect/proc_holder/spell/proc/before_cast(list/targets) + if(overlay) + for(var/atom/target in targets) + var/location + if(istype(target,/mob/living)) + location = target.loc + else if(istype(target,/turf)) + location = target + var/obj/effect/overlay/spell = new /obj/effect/overlay(location) + spell.icon = overlay_icon + spell.icon_state = overlay_icon_state + spell.anchored = 1 + spell.density = 0 + spawn(overlay_lifespan) + qdel(spell) + +/obj/effect/proc_holder/spell/proc/after_cast(list/targets) + for(var/atom/target in targets) + var/location + if(istype(target,/mob/living)) + location = target.loc + else if(istype(target,/turf)) + location = target + if(istype(target,/mob/living) && message) + target << text("[message]") + if(sparks_spread) + var/datum/effect_system/spark_spread/sparks = new + sparks.set_up(sparks_amt, 0, location) //no idea what the 0 is + sparks.start() + if(smoke_spread) + if(smoke_spread == 1) + var/datum/effect_system/smoke_spread/smoke = new + smoke.set_up(smoke_amt, location) + smoke.start() + else if(smoke_spread == 2) + var/datum/effect_system/smoke_spread/bad/smoke = new + smoke.set_up(smoke_amt, location) + smoke.start() + else if(smoke_spread == 3) + var/datum/effect_system/smoke_spread/sleeping/smoke = new + smoke.set_up(smoke_amt, location) + smoke.start() + + +/obj/effect/proc_holder/spell/proc/cast(list/targets,mob/user = usr) + return + +/obj/effect/proc_holder/spell/proc/critfail(list/targets) + return + +/obj/effect/proc_holder/spell/proc/revert_cast(mob/user = usr) //resets recharge or readds a charge + switch(charge_type) + if("recharge") + charge_counter = charge_max + if("charges") + charge_counter++ + if("holdervar") + adjust_var(user, holder_var_type, -holder_var_amount) + + return + +/obj/effect/proc_holder/spell/proc/adjust_var(mob/living/target = usr, type, amount) //handles the adjustment of the var when the spell is used. has some hardcoded types + switch(type) + if("bruteloss") + target.adjustBruteLoss(amount) + if("fireloss") + target.adjustFireLoss(amount) + if("toxloss") + target.adjustToxLoss(amount) + if("oxyloss") + target.adjustOxyLoss(amount) + if("stunned") + target.AdjustStunned(amount) + if("weakened") + target.AdjustWeakened(amount) + if("paralysis") + target.AdjustParalysis(amount) + else + target.vars[type] += amount //I bear no responsibility for the runtimes that'll happen if you try to adjust non-numeric or even non-existant vars + return + +/obj/effect/proc_holder/spell/targeted //can mean aoe for mobs (limited/unlimited number) or one target mob + var/max_targets = 1 //leave 0 for unlimited targets in range, 1 for one selectable target in range, more for limited number of casts (can all target one guy, depends on target_ignore_prev) in range + var/target_ignore_prev = 1 //only important if max_targets > 1, affects if the spell can be cast multiple times at one person from one cast + var/include_user = 0 //if it includes usr in the target list + var/random_target = 0 // chooses random viable target instead of asking the caster + var/random_target_priority = TARGET_CLOSEST // if random_target is enabled how it will pick the target + + +/obj/effect/proc_holder/spell/aoe_turf //affects all turfs in view or range (depends) + var/inner_radius = -1 //for all your ring spell needs + +/obj/effect/proc_holder/spell/targeted/choose_targets(mob/user = usr) + var/list/targets = list() + + switch(max_targets) + if(0) //unlimited + for(var/mob/living/target in view_or_range(range, user, selection_type)) + targets += target + if(1) //single target can be picked + if(range < 0) + targets += user + else + var/possible_targets = list() + + for(var/mob/living/M in view_or_range(range, user, selection_type)) + if(!include_user && user == M) + continue + possible_targets += M + + //targets += input("Choose the target for the spell.", "Targeting") as mob in possible_targets + //Adds a safety check post-input to make sure those targets are actually in range. + var/mob/M + if(!random_target) + M = input("Choose the target for the spell.", "Targeting") as mob in possible_targets + else + switch(random_target_priority) + if(TARGET_RANDOM) + M = pick(possible_targets) + if(TARGET_CLOSEST) + for(var/mob/living/L in possible_targets) + if(M) + if(get_dist(user,L) < get_dist(user,M)) + if(los_check(user,L)) + M = L + else + if(los_check(user,L)) + M = L + if(M in view_or_range(range, user, selection_type)) targets += M + + else + var/list/possible_targets = list() + for(var/mob/living/target in view_or_range(range, user, selection_type)) + possible_targets += target + for(var/i=1,i<=max_targets,i++) + if(!possible_targets.len) + break + if(target_ignore_prev) + var/target = pick(possible_targets) + possible_targets -= target + targets += target + else + targets += pick(possible_targets) + + if(!include_user && (user in targets)) + targets -= user + + if(!targets.len) //doesn't waste the spell + revert_cast(user) + return + + perform(targets,user=user) + + return + +/obj/effect/proc_holder/spell/aoe_turf/choose_targets(mob/user = usr) + var/list/targets = list() + + for(var/turf/target in view_or_range(range,user,selection_type)) + if(!(target in view_or_range(inner_radius,user,selection_type))) + targets += target + + if(!targets.len) //doesn't waste the spell + revert_cast() + return + + perform(targets,user=user) + + return + +/obj/effect/proc_holder/spell/proc/can_be_cast_by(mob/caster) + if((human_req || clothes_req) && !ishuman(caster)) + return 0 + return 1 + +/obj/effect/proc_holder/spell/targeted/proc/los_check(mob/A,mob/B) + //Checks for obstacles from A to B + var/obj/dummy = new(A.loc) + dummy.pass_flags |= PASSTABLE + for(var/turf/turf in getline(A,B)) + for(var/atom/movable/AM in turf) + if(!AM.CanPass(dummy,turf,1)) + qdel(dummy) + return 0 + qdel(dummy) + return 1 + +/obj/effect/proc_holder/spell/proc/can_cast(mob/user = usr) + if(((!user.mind) || !(src in user.mind.spell_list)) && !(src in user.mob_spell_list)) + return 0 + + if(user.z == ZLEVEL_CENTCOM && !centcom_cancast) //Certain spells are not allowed on the centcom zlevel + return 0 + if(user.z == ZLEVEL_CENTCOM && ticker.mode.name == "ragin' mages") + return 0 + + switch(charge_type) + if("recharge") + if(charge_counter < charge_max) + return 0 + if("charges") + if(!charge_counter) + return 0 + + if(user.stat && !stat_allowed) + return 0 + + if(ishuman(user)) + + var/mob/living/carbon/human/H = user + + if((invocation_type == "whisper" || invocation_type == "shout") && H.is_muzzled()) + return 0 + + if(clothes_req) //clothes check + if(!istype(H.wear_suit, /obj/item/clothing/suit/wizrobe) && !istype(H.wear_suit, /obj/item/clothing/suit/space/hardsuit/wizard)) + return 0 + if(!istype(H.shoes, /obj/item/clothing/shoes/sandal)) + return 0 + if(!istype(H.head, /obj/item/clothing/head/wizard) && !istype(H.head, /obj/item/clothing/head/helmet/space/hardsuit/wizard)) + return 0 + else + if(clothes_req || human_req) + return 0 + if(nonabstract_req && (isbrain(user) || ispAI(user))) + return 0 + return 1 + +/obj/effect/proc_holder/spell/self //Targets only the caster. Good for buffs and heals, but probably not wise for fireballs (although they usually fireball themselves anyway, honke) + range = -1 //Duh + +/obj/effect/proc_holder/spell/self/choose_targets(mob/user = usr) + if(!user) + revert_cast() + return + perform(null,user=user) + +/obj/effect/proc_holder/spell/self/basic_heal //This spell exists mainly for debugging purposes, and also to show how casting works + name = "Lesser Heal" + desc = "Heals a small amount of brute and burn damage." + human_req = 1 + clothes_req = 0 + charge_max = 100 + cooldown_min = 50 + invocation = "Victus sano!" + invocation_type = "whisper" + school = "restoration" + sound = 'sound/magic/Staff_Healing.ogg' + +/obj/effect/proc_holder/spell/self/basic_heal/cast(mob/living/carbon/human/user) //Note the lack of "list/targets" here. Instead, use a "user" var depending on mob requirements. + //Also, notice the lack of a "for()" statement that looks through the targets. This is, again, because the spell can only have a single target. + user.visible_message("A wreath of gentle light passes over [user]!", "You wreath yourself in healing light!") + user.adjustBruteLoss(-10) + user.adjustFireLoss(-10) diff --git a/code/datums/supplypacks.dm b/code/datums/supplypacks.dm index 7e8ee2c9ada..7b708036cfb 100644 --- a/code/datums/supplypacks.dm +++ b/code/datums/supplypacks.dm @@ -1,1345 +1,1345 @@ -//SUPPLY PACKS -//NOTE: only secure crate types use the access var (and are lockable) -//NOTE: hidden packs only show up when the computer has been hacked. -//ANOTHER NOTE: Contraband is obtainable through modified supplycomp circuitboards. -//BIG NOTE: Don't add living things to crates, that's bad, it will break the shuttle. -//NEW NOTE: Do NOT set the price of any crates below 7 points. Doing so allows infinite points. - -// Supply Groups -var/const/supply_emergency = 1 -var/const/supply_security = 2 -var/const/supply_engineer = 3 -var/const/supply_medical = 4 -var/const/supply_science = 5 -var/const/supply_organic = 6 -var/const/supply_materials = 7 -var/const/supply_misc = 8 - -var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engineer,supply_medical,supply_science,supply_organic,supply_materials,supply_misc) - -/proc/get_supply_group_name(cat) - switch(cat) - if(1) - return "Emergency" - if(2) - return "Security" - if(3) - return "Engineering" - if(4) - return "Medical" - if(5) - return "Science" - if(6) - return "Food & Livestock" - if(7) - return "Raw Materials" - if(8) - return "Miscellaneous" - - -/datum/supply_packs - var/name = null - var/list/contains = list() - var/manifest = "" - var/amount = null - var/cost = null - var/containertype = /obj/structure/closet/crate - var/containername = null - var/access = null - var/hidden = 0 - var/contraband = 0 - var/group = supply_misc - - -/datum/supply_packs/New() - manifest += "
    " - for(var/path in contains) - if(!path) continue - var/atom/movable/AM = path - manifest += "
  • [initial(AM.name)]
  • " - manifest += "
" - -////// Use the sections to keep things tidy please /Malkevin - -////////////////////////////////////////////////////////////////////////////// -//////////////////////////// Emergency /////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// - -/datum/supply_packs/emergency // Section header - use these to set default supply group and crate type for sections - name = "HEADER" // Use "HEADER" to denote section headers, this is needed for the supply computers to filter them - containertype = /obj/structure/closet/crate/internals - group = supply_emergency - - -/datum/supply_packs/emergency/evac - name = "Emergency equipment" - contains = list(/mob/living/simple_animal/bot/floorbot, - /mob/living/simple_animal/bot/floorbot, - /mob/living/simple_animal/bot/medbot, - /mob/living/simple_animal/bot/medbot, - /obj/item/weapon/tank/internals/air, - /obj/item/weapon/tank/internals/air, - /obj/item/weapon/tank/internals/air, - /obj/item/weapon/tank/internals/air, - /obj/item/weapon/tank/internals/air, - /obj/item/clothing/mask/gas, - /obj/item/clothing/mask/gas, - /obj/item/clothing/mask/gas, - /obj/item/clothing/mask/gas, - /obj/item/clothing/mask/gas) - cost = 35 - containertype = /obj/structure/closet/crate/internals - containername = "emergency crate" - group = supply_emergency - -/datum/supply_packs/emergency/internals - name = "Internals Crate" - contains = list(/obj/item/clothing/mask/gas, - /obj/item/clothing/mask/gas, - /obj/item/clothing/mask/gas, - /obj/item/weapon/tank/internals/air, - /obj/item/weapon/tank/internals/air, - /obj/item/weapon/tank/internals/air) - cost = 10 - containername = "internals crate" - -/datum/supply_packs/emergency/firefighting - name = "Firefighting Crate" - contains = list(/obj/item/clothing/suit/fire/firefighter, - /obj/item/clothing/suit/fire/firefighter, - /obj/item/clothing/mask/gas, - /obj/item/clothing/mask/gas, - /obj/item/device/flashlight, - /obj/item/device/flashlight, - /obj/item/weapon/tank/internals/oxygen/red, - /obj/item/weapon/tank/internals/oxygen/red, - /obj/item/weapon/extinguisher, - /obj/item/weapon/extinguisher, - /obj/item/clothing/head/hardhat/red, - /obj/item/clothing/head/hardhat/red) - cost = 10 - containertype = /obj/structure/closet/crate - containername = "firefighting crate" - -/datum/supply_packs/emergency/atmostank - name = "Firefighting Watertank" - contains = list(/obj/item/weapon/watertank/atmos) - cost = 10 - containertype = /obj/structure/closet/crate/secure - containername = "firefighting watertank crate" - access = access_atmospherics - -/datum/supply_packs/emergency/weedcontrol - name = "Weed Control Crate" - contains = list(/obj/item/weapon/scythe, - /obj/item/clothing/mask/gas, - /obj/item/weapon/grenade/chem_grenade/antiweed, - /obj/item/weapon/grenade/chem_grenade/antiweed) - cost = 15 - containertype = /obj/structure/closet/crate/secure/hydrosec - containername = "weed control crate" - access = access_hydroponics - -/datum/supply_packs/emergency/specialops - name = "Special Ops supplies" - contains = list(/obj/item/weapon/storage/box/emps, - /obj/item/weapon/grenade/smokebomb, - /obj/item/weapon/grenade/smokebomb, - /obj/item/weapon/grenade/smokebomb, - /obj/item/weapon/pen/sleepy, - /obj/item/weapon/grenade/chem_grenade/incendiary) - cost = 20 - containertype = /obj/structure/closet/crate - containername = "special ops crate" - hidden = 1 - -/datum/supply_packs/emergency/syndicate - name = "ERROR_NULL_ENTRY" - contains = list(/obj/item/weapon/storage/box/syndicate) - cost = 140 - containertype = /obj/structure/closet/crate - containername = "crate" - hidden = 1 - -////////////////////////////////////////////////////////////////////////////// -//////////////////////////// Security //////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// - -/datum/supply_packs/security - name = "HEADER" - containertype = /obj/structure/closet/crate/secure/gear - access = access_security - group = supply_security - - -/datum/supply_packs/security/supplies - name = "Security Supplies Crate" - contains = list(/obj/item/weapon/storage/box/flashbangs, - /obj/item/weapon/storage/box/teargas, - /obj/item/weapon/storage/box/flashes, - /obj/item/weapon/storage/box/handcuffs) - cost = 10 - containername = "security supply crate" - -////// Armor: Basic - -/datum/supply_packs/security/helmets - name = "Helmets Crate" - contains = list(/obj/item/clothing/head/helmet/sec, - /obj/item/clothing/head/helmet/sec, - /obj/item/clothing/head/helmet/sec) - cost = 10 - containername = "helmet crate" - -/datum/supply_packs/security/armor - name = "Armor Crate" - contains = list(/obj/item/clothing/suit/armor/vest, - /obj/item/clothing/suit/armor/vest, - /obj/item/clothing/suit/armor/vest) - cost = 10 - containername = "armor crate" - -////// Weapons: Basic - -/datum/supply_packs/security/baton - name = "Stun Batons Crate" - contains = list(/obj/item/weapon/melee/baton/loaded, - /obj/item/weapon/melee/baton/loaded, - /obj/item/weapon/melee/baton/loaded) - cost = 10 - containername = "stun baton crate" - -/datum/supply_packs/security/laser - name = "Lasers Crate" - contains = list(/obj/item/weapon/gun/energy/laser, - /obj/item/weapon/gun/energy/laser, - /obj/item/weapon/gun/energy/laser) - cost = 15 - containername = "laser crate" - -/datum/supply_packs/security/taser - name = "Stun Guns Crate" - contains = list(/obj/item/weapon/gun/energy/gun/advtaser, - /obj/item/weapon/gun/energy/gun/advtaser, - /obj/item/weapon/gun/energy/gun/advtaser) - cost = 15 - containername = "stun gun crate" - -/datum/supply_packs/security/disabler - name = "Disabler Crate" - contains = list(/obj/item/weapon/gun/energy/disabler, - /obj/item/weapon/gun/energy/disabler, - /obj/item/weapon/gun/energy/disabler) - cost = 10 - containername = "disabler crate" - -/datum/supply_packs/security/forensics - name = "Forensics Crate" - contains = list(/obj/item/device/detective_scanner, - /obj/item/weapon/storage/box/evidence, - /obj/item/device/camera, - /obj/item/device/taperecorder, - /obj/item/toy/crayon/white, - /obj/item/clothing/head/det_hat) - cost = 20 - containername ="forensics crate" - -///// Armory stuff - -/datum/supply_packs/security/armory - name = "HEADER" - containertype = /obj/structure/closet/crate/secure/weapon - access = access_armory - -///// Armor: Specialist - -/datum/supply_packs/security/armory/riothelmets - name = "Riot Helmets Crate" - contains = list(/obj/item/clothing/head/helmet/riot, - /obj/item/clothing/head/helmet/riot, - /obj/item/clothing/head/helmet/riot) - cost = 15 - containername = "riot helmets crate" - -/datum/supply_packs/security/armory/riotarmor - name = "Riot Armor Crate" - contains = list(/obj/item/clothing/suit/armor/riot, - /obj/item/clothing/suit/armor/riot, - /obj/item/clothing/suit/armor/riot) - cost = 15 - containername = "riot armor crate" - -/datum/supply_packs/security/armory/riotshields - name = "Riot Shields Crate" - contains = list(/obj/item/weapon/shield/riot, - /obj/item/weapon/shield/riot, - /obj/item/weapon/shield/riot) - cost = 20 - containername = "riot shields crate" - -/datum/supply_packs/security/armory/bulletarmor - name = "Bulletproof Armor Crate" - contains = list(/obj/item/clothing/suit/armor/bulletproof, - /obj/item/clothing/suit/armor/bulletproof, - /obj/item/clothing/suit/armor/bulletproof) - cost = 15 - containername = "tactical armor crate" - -/datum/supply_packs/security/armory/laserarmor - name = "Reflector Vest Crate" - contains = list(/obj/item/clothing/suit/armor/laserproof, - /obj/item/clothing/suit/armor/laserproof) // Only two vests to keep costs down for balance - cost = 20 - containertype = /obj/structure/closet/crate/secure/plasma - containername = "reflector vest crate" - -/////// Weapons: Specialist - -/datum/supply_packs/security/armory/ballistic - name = "Combat Shotguns Crate" - contains = list(/obj/item/weapon/gun/projectile/shotgun/automatic/combat, - /obj/item/weapon/gun/projectile/shotgun/automatic/combat, - /obj/item/weapon/gun/projectile/shotgun/automatic/combat, - /obj/item/weapon/storage/belt/bandolier, - /obj/item/weapon/storage/belt/bandolier, - /obj/item/weapon/storage/belt/bandolier) - cost = 20 - containername = "combat shotgun crate" - -/datum/supply_packs/security/armory/expenergy - name = "Energy Guns Crate" - contains = list(/obj/item/weapon/gun/energy/gun, - /obj/item/weapon/gun/energy/gun) // Only two guns to keep costs down - cost = 25 - containertype = /obj/structure/closet/crate/secure/plasma - containername = "energy gun crate" - -/datum/supply_packs/security/armory/eweapons - name = "Incendiary Weapons Crate" - contains = list(/obj/item/weapon/flamethrower/full, - /obj/item/weapon/tank/internals/plasma, - /obj/item/weapon/tank/internals/plasma, - /obj/item/weapon/tank/internals/plasma, - /obj/item/weapon/grenade/chem_grenade/incendiary, - /obj/item/weapon/grenade/chem_grenade/incendiary, - /obj/item/weapon/grenade/chem_grenade/incendiary) - cost = 15 // its a fecking flamethrower and some plasma, why the shit did this cost so much before!? - containertype = /obj/structure/closet/crate/secure/plasma - containername = "incendiary weapons crate" - access = access_heads - -/datum/supply_packs/security/armory/wt550 - name = "WT-550 Auto Rifle Crate" - contains = list(/obj/item/weapon/gun/projectile/automatic/wt550, - /obj/item/weapon/gun/projectile/automatic/wt550) - cost = 35 - containername = "auto rifle crate" - -/datum/supply_packs/security/armory/wt550ammo - name = "WT-550 Rifle Ammo Crate" - contains = list(/obj/item/ammo_box/magazine/wt550m9, - /obj/item/ammo_box/magazine/wt550m9, - /obj/item/ammo_box/magazine/wt550m9, - /obj/item/ammo_box/magazine/wt550m9,) - cost = 30 - containername = "auto rifle ammo crate" - -/////// Implants & etc - -/datum/supply_packs/security/armory/loyalty - name = "Loyalty Implants Crate" - contains = list (/obj/item/weapon/storage/lockbox/loyalty) - cost = 40 - containername = "loyalty implant crate" - -/datum/supply_packs/security/armory/trackingimp - name = "Tracking Implants Crate" - contains = list (/obj/item/weapon/storage/box/trackimp) - cost = 20 - containername = "tracking implant crate" - -/datum/supply_packs/security/armory/chemimp - name = "Chemical Implants Crate" - contains = list (/obj/item/weapon/storage/box/chemimp) - cost = 20 - containername = "chemical implant crate" - -/datum/supply_packs/security/armory/exileimp - name = "Exile Implants Crate" - contains = list (/obj/item/weapon/storage/box/exileimp) - cost = 30 - containername = "exile implant crate" - -/datum/supply_packs/security/securitybarriers - name = "Security Barriers Crate" - contains = list(/obj/item/weapon/grenade/barrier, - /obj/item/weapon/grenade/barrier, - /obj/item/weapon/grenade/barrier, - /obj/item/weapon/grenade/barrier) - cost = 20 - containername = "security barriers crate" - -/datum/supply_packs/security/firingpins - name = "Standard Firing Pins Crate" - contains = list(/obj/item/weapon/storage/box/firingpins, - /obj/item/weapon/storage/box/firingpins) - cost = 10 - containername = "firing pins crate" - -/datum/supply_packs/security/securityclothes - name = "Security Clothing Crate" - contains = list(/obj/item/clothing/under/rank/security/navyblue, - /obj/item/clothing/under/rank/security/navyblue, - /obj/item/clothing/suit/security/officer, - /obj/item/clothing/suit/security/officer, - /obj/item/clothing/head/beret/sec/navyofficer, - /obj/item/clothing/head/beret/sec/navyofficer, - /obj/item/clothing/under/rank/warden/navyblue, - /obj/item/clothing/suit/security/warden, - /obj/item/clothing/head/beret/sec/navywarden, - /obj/item/clothing/under/rank/head_of_security/navyblue, - /obj/item/clothing/suit/security/hos, - /obj/item/clothing/head/beret/sec/navyhos) - cost = 30 - containername = "security clothing crate" - -/////// Joke Crate Inbound - -/datum/supply_packs/security/justiceinbound - name = "Standard Justice Enforcer Crate" - contains = list(/obj/item/clothing/head/helmet/justice, - /obj/item/clothing/mask/gas/sechailer) - cost = 80 //justice comes at a price. An expensive, noisy price. - containername = "justice enforcer crate" - - -////////////////////////////////////////////////////////////////////////////// -//////////////////////////// Engineering ///////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// - -/datum/supply_packs/engineering - name = "HEADER" - group = supply_engineer - - -/datum/supply_packs/engineering/fueltank - name = "Fuel Tank Crate" - contains = list(/obj/structure/reagent_dispensers/fueltank) - cost = 8 - containertype = /obj/structure/largecrate - containername = "fuel tank crate" - -/datum/supply_packs/engineering/tools //the most robust crate - name = "Toolbox Crate" - contains = list(/obj/item/weapon/storage/toolbox/electrical, - /obj/item/weapon/storage/toolbox/electrical, - /obj/item/weapon/storage/toolbox/mechanical, - /obj/item/weapon/storage/toolbox/electrical, - /obj/item/weapon/storage/toolbox/mechanical, - /obj/item/weapon/storage/toolbox/mechanical) - cost = 10 - containername = "electrical maintenance crate" - -/datum/supply_packs/engineering/powergamermitts - name = "Insulated Gloves Crate" - contains = list(/obj/item/clothing/gloves/color/yellow, - /obj/item/clothing/gloves/color/yellow, - /obj/item/clothing/gloves/color/yellow) - cost = 20 //Made of pure-grade bullshittinium - containername = "insulated gloves crate" - -/datum/supply_packs/engineering/power - name = "Powercell Crate" - contains = list(/obj/item/weapon/stock_parts/cell/high, //Changed to an extra high powercell because normal cells are useless - /obj/item/weapon/stock_parts/cell/high, - /obj/item/weapon/stock_parts/cell/high) - cost = 10 - containername = "electrical maintenance crate" - -/datum/supply_packs/engineering/engiequipment - name = "Engineering Gear Crate" - contains = list(/obj/item/weapon/storage/belt/utility, - /obj/item/weapon/storage/belt/utility, - /obj/item/weapon/storage/belt/utility, - /obj/item/clothing/suit/hazardvest, - /obj/item/clothing/suit/hazardvest, - /obj/item/clothing/suit/hazardvest, - /obj/item/clothing/head/welding, - /obj/item/clothing/head/welding, - /obj/item/clothing/head/welding, - /obj/item/clothing/head/hardhat, - /obj/item/clothing/head/hardhat, - /obj/item/clothing/head/hardhat) - cost = 10 - containername = "engineering gear crate" - -/datum/supply_packs/engineering/solar - name = "Solar Pack Crate" - contains = list(/obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, // 21 Solar Assemblies. 1 Extra for the controller - /obj/item/weapon/circuitboard/solar_control, - /obj/item/weapon/electronics/tracker, - /obj/item/weapon/paper/solar) - cost = 20 - containername = "solar pack crate" - -/datum/supply_packs/engineering/engine - name = "Emitter Crate" - contains = list(/obj/machinery/power/emitter, - /obj/machinery/power/emitter) - cost = 10 - containertype = /obj/structure/closet/crate/secure - containername = "emitter crate" - access = access_ce - -/datum/supply_packs/engineering/engine/field_gen - name = "Field Generator Crate" - contains = list(/obj/machinery/field/generator, - /obj/machinery/field/generator) - cost = 10 - containername = "field generator crate" - -/datum/supply_packs/engineering/engine/sing_gen - name = "Singularity Generator Crate" - contains = list(/obj/machinery/the_singularitygen) - cost = 10 - containername = "singularity generator crate" - -/datum/supply_packs/engineering/engine/collector - name = "Collector Crate" - contains = list(/obj/machinery/power/rad_collector, - /obj/machinery/power/rad_collector, - /obj/machinery/power/rad_collector) - cost = 10 - containername = "collector crate" - -/datum/supply_packs/engineering/engine/PA - name = "Particle Accelerator Crate" - contains = list(/obj/structure/particle_accelerator/fuel_chamber, - /obj/machinery/particle_accelerator/control_box, - /obj/structure/particle_accelerator/particle_emitter/center, - /obj/structure/particle_accelerator/particle_emitter/left, - /obj/structure/particle_accelerator/particle_emitter/right, - /obj/structure/particle_accelerator/power_box, - /obj/structure/particle_accelerator/end_cap) - cost = 25 - containername = "particle accelerator crate" - -/datum/supply_packs/engineering/engine/spacesuit - name = "Space Suit Crate" - contains = list(/obj/item/clothing/suit/space, - /obj/item/clothing/head/helmet/space, - /obj/item/clothing/mask/breath,) - cost = 80 - containertype = /obj/structure/closet/crate/secure - containername = "space suit crate" - access = access_eva - -////////////////////////////////////////////////////////////////////////////// -//////////////////////////// Medical ///////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// - -/datum/supply_packs/medical - name = "HEADER" - containertype = /obj/structure/closet/crate/medical - group = supply_medical - - -/datum/supply_packs/medical/supplies - name = "Medical Supplies Crate" - contains = list(/obj/item/weapon/reagent_containers/glass/bottle/charcoal, - /obj/item/weapon/reagent_containers/glass/bottle/charcoal, - /obj/item/weapon/reagent_containers/glass/bottle/epinephrine, - /obj/item/weapon/reagent_containers/glass/bottle/epinephrine, - /obj/item/weapon/reagent_containers/glass/bottle/morphine, - /obj/item/weapon/reagent_containers/glass/bottle/morphine, - /obj/item/weapon/reagent_containers/glass/bottle/morphine, - /obj/item/weapon/reagent_containers/glass/bottle/morphine, - /obj/item/weapon/reagent_containers/glass/bottle/morphine, - /obj/item/weapon/reagent_containers/glass/bottle/morphine, - /obj/item/weapon/reagent_containers/glass/bottle/toxin, - /obj/item/weapon/reagent_containers/glass/bottle/toxin, - /obj/item/weapon/reagent_containers/glass/beaker/large, - /obj/item/weapon/reagent_containers/glass/beaker/large, - /obj/item/weapon/reagent_containers/pill/insulin, - /obj/item/weapon/reagent_containers/pill/insulin, - /obj/item/weapon/reagent_containers/pill/insulin, - /obj/item/weapon/reagent_containers/pill/insulin, - /obj/item/stack/medical/gauze, - /obj/item/weapon/storage/box/beakers, - /obj/item/weapon/storage/box/syringes, - /obj/item/weapon/storage/box/bodybags) - cost = 20 - containertype = /obj/structure/closet/crate/medical - containername = "medical supplies crate" - -/datum/supply_packs/medical/firstaid - name = "First Aid Kits Crate" - contains = list(/obj/item/weapon/storage/firstaid/regular, - /obj/item/weapon/storage/firstaid/regular, - /obj/item/weapon/storage/firstaid/regular, - /obj/item/weapon/storage/firstaid/regular) - cost = 10 - containername = "first aid kits crate" - -/datum/supply_packs/medical/firstaidbruises - name = "Bruise Treatment Kits Crate" - contains = list(/obj/item/weapon/storage/firstaid/brute, - /obj/item/weapon/storage/firstaid/brute, - /obj/item/weapon/storage/firstaid/brute) - cost = 10 - containername = "brute trauma first aid kits crate" - -/datum/supply_packs/medical/firstaidburns - name = "Burns Treatment Kits Crate" - contains = list(/obj/item/weapon/storage/firstaid/fire, - /obj/item/weapon/storage/firstaid/fire, - /obj/item/weapon/storage/firstaid/fire) - cost = 10 - containername = "fire first aid kits crate" - -/datum/supply_packs/medical/firstaidtoxins - name = "Toxin Treatment Kits Crate" - contains = list(/obj/item/weapon/storage/firstaid/toxin, - /obj/item/weapon/storage/firstaid/toxin, - /obj/item/weapon/storage/firstaid/toxin) - cost = 10 - containername = "toxin first aid kits crate" - -/datum/supply_packs/medical/firstaidoxygen - name = "Oxygen Deprivation Kits Crate" - contains = list(/obj/item/weapon/storage/firstaid/o2, - /obj/item/weapon/storage/firstaid/o2, - /obj/item/weapon/storage/firstaid/o2) - cost = 10 - containername = "oxygen deprivation kits crate" - - -/datum/supply_packs/medical/virus - name = "Virus Crate" - contains = list(/obj/item/weapon/reagent_containers/glass/bottle/flu_virion, - /obj/item/weapon/reagent_containers/glass/bottle/cold, - /obj/item/weapon/reagent_containers/glass/bottle/epiglottis_virion, - /obj/item/weapon/reagent_containers/glass/bottle/liver_enhance_virion, - /obj/item/weapon/reagent_containers/glass/bottle/fake_gbs, - /obj/item/weapon/reagent_containers/glass/bottle/magnitis, - /obj/item/weapon/reagent_containers/glass/bottle/pierrot_throat, - /obj/item/weapon/reagent_containers/glass/bottle/brainrot, - /obj/item/weapon/reagent_containers/glass/bottle/hullucigen_virion, - /obj/item/weapon/reagent_containers/glass/bottle/anxiety, - /obj/item/weapon/reagent_containers/glass/bottle/beesease, - /obj/item/weapon/storage/box/syringes, - /obj/item/weapon/storage/box/beakers, - /obj/item/weapon/reagent_containers/glass/bottle/mutagen) - cost = 25 - containertype = /obj/structure/closet/crate/secure/plasma - containername = "virus crate" - access = access_cmo - - -/datum/supply_packs/medical/bloodpacks - name = "Blood Pack Variety Crate" - contains = list(/obj/item/weapon/reagent_containers/blood/empty, - /obj/item/weapon/reagent_containers/blood/empty, - /obj/item/weapon/reagent_containers/blood/APlus, - /obj/item/weapon/reagent_containers/blood/AMinus, - /obj/item/weapon/reagent_containers/blood/BPlus, - /obj/item/weapon/reagent_containers/blood/BMinus, - /obj/item/weapon/reagent_containers/blood/OPlus, - /obj/item/weapon/reagent_containers/blood/OMinus) - cost = 35 - containertype = /obj/structure/closet/crate/freezer - containername = "blood pack crate" - -/datum/supply_packs/medical/iv_drip - name = "IV Drip Crate" - contains = list(/obj/machinery/iv_drip) - cost = 30 - containertype = /obj/structure/closet/crate/secure - containername = "iv drip crate" - access = access_cmo - -////////////////////////////////////////////////////////////////////////////// -//////////////////////////// Science ///////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// - -/datum/supply_packs/science - name = "HEADER" - group = supply_science - - -/datum/supply_packs/science/robotics - name = "Robotics Assembly Crate" - contains = list(/obj/item/device/assembly/prox_sensor, - /obj/item/device/assembly/prox_sensor, - /obj/item/device/assembly/prox_sensor, - /obj/item/weapon/storage/toolbox/electrical, - /obj/item/weapon/storage/box/flashes, - /obj/item/weapon/stock_parts/cell/high, - /obj/item/weapon/stock_parts/cell/high) - cost = 10 - containertype = /obj/structure/closet/crate/secure - containername = "robotics assembly crate" - access = access_robotics - -/datum/supply_packs/science/robotics/mecha_ripley - name = "Circuit Crate (\"Ripley\" APLU)" - contains = list(/obj/item/weapon/book/manual/ripley_build_and_repair, - /obj/item/weapon/circuitboard/mecha/ripley/main, //TEMPORARY due to lack of circuitboard printer - /obj/item/weapon/circuitboard/mecha/ripley/peripherals) //TEMPORARY due to lack of circuitboard printer - cost = 30 - containertype = /obj/structure/closet/crate/secure - containername = "\improper APLU \"Ripley\" circuit crate" - -/datum/supply_packs/science/robotics/mecha_odysseus - name = "Circuit Crate (\"Odysseus\")" - contains = list(/obj/item/weapon/circuitboard/mecha/odysseus/peripherals, //TEMPORARY due to lack of circuitboard printer - /obj/item/weapon/circuitboard/mecha/odysseus/main) //TEMPORARY due to lack of circuitboard printer - cost = 25 - containertype = /obj/structure/closet/crate/secure - containername = "\improper \"Odysseus\" circuit crate" - -/datum/supply_packs/science/plasma - name = "Plasma Assembly Crate" - contains = list(/obj/item/weapon/tank/internals/plasma, - /obj/item/weapon/tank/internals/plasma, - /obj/item/weapon/tank/internals/plasma, - /obj/item/device/assembly/igniter, - /obj/item/device/assembly/igniter, - /obj/item/device/assembly/igniter, - /obj/item/device/assembly/prox_sensor, - /obj/item/device/assembly/prox_sensor, - /obj/item/device/assembly/prox_sensor, - /obj/item/device/assembly/timer, - /obj/item/device/assembly/timer, - /obj/item/device/assembly/timer) - cost = 10 - containertype = /obj/structure/closet/crate/secure/plasma - containername = "plasma assembly crate" - access = access_tox_storage - group = supply_science - -/datum/supply_packs/science/shieldwalls - name = "Shield Generators" - contains = list(/obj/machinery/shieldwallgen, - /obj/machinery/shieldwallgen, - /obj/machinery/shieldwallgen, - /obj/machinery/shieldwallgen) - cost = 20 - containertype = /obj/structure/closet/crate/secure - containername = "shield generators crate" - access = access_teleporter - - -/datum/supply_packs/science/transfer_valves - name = "Tank Transfer Valves" - contains = list(/obj/item/device/transfer_valve, - /obj/item/device/transfer_valve) - cost = 60 - containertype = /obj/structure/closet/crate/secure - containername = "transfer valves crate" - access = access_rd - -////////////////////////////////////////////////////////////////////////////// -//////////////////////////// Organic ///////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// - -/datum/supply_packs/organic - name = "HEADER" - group = supply_organic - containertype = /obj/structure/closet/crate/freezer - - -/datum/supply_packs/organic/food - name = "Food Crate" - contains = list(/obj/item/weapon/reagent_containers/food/condiment/flour, - /obj/item/weapon/reagent_containers/food/condiment/rice, - /obj/item/weapon/reagent_containers/food/condiment/milk, - /obj/item/weapon/reagent_containers/food/condiment/soymilk, - /obj/item/weapon/reagent_containers/food/condiment/saltshaker, - /obj/item/weapon/reagent_containers/food/condiment/peppermill, - /obj/item/weapon/storage/fancy/egg_box, - /obj/item/weapon/reagent_containers/food/condiment/enzyme, - /obj/item/weapon/reagent_containers/food/condiment/sugar, - /obj/item/weapon/reagent_containers/food/snacks/meat/slab/monkey, - /obj/item/weapon/reagent_containers/food/snacks/grown/banana, - /obj/item/weapon/reagent_containers/food/snacks/grown/banana, - /obj/item/weapon/reagent_containers/food/snacks/grown/banana) - cost = 10 - containername = "food crate" - -/datum/supply_packs/organic/pizza - name = "Pizza Crate" - contains = list(/obj/item/pizzabox/margherita, - /obj/item/pizzabox/mushroom, - /obj/item/pizzabox/meat, - /obj/item/pizzabox/vegetable) - cost = 60 - containername = "Pizza crate" - -/datum/supply_packs/organic/monkey - name = "Monkey Crate" - contains = list (/obj/item/weapon/storage/box/monkeycubes) - cost = 20 - containername = "monkey crate" - -/datum/supply_packs/organic/party - name = "Party equipment" - contains = list(/obj/item/weapon/storage/box/drinkingglasses, - /obj/item/weapon/reagent_containers/food/drinks/shaker, - /obj/item/weapon/reagent_containers/food/drinks/bottle/patron, - /obj/item/weapon/reagent_containers/food/drinks/bottle/goldschlager, - /obj/item/weapon/reagent_containers/food/drinks/ale, - /obj/item/weapon/reagent_containers/food/drinks/ale, - /obj/item/weapon/reagent_containers/food/drinks/beer, - /obj/item/weapon/reagent_containers/food/drinks/beer, - /obj/item/weapon/reagent_containers/food/drinks/beer, - /obj/item/weapon/reagent_containers/food/drinks/beer) - cost = 20 - containername = "party equipment" - -//////// livestock -/datum/supply_packs/organic/cow - name = "Cow Crate" - cost = 30 - containertype = /obj/structure/closet/critter/cow - containername = "cow crate" - -/datum/supply_packs/organic/goat - name = "Goat Crate" - cost = 25 - containertype = /obj/structure/closet/critter/goat - containername = "goat crate" - -/datum/supply_packs/organic/chicken - name = "Chicken Crate" - cost = 20 - containertype = /obj/structure/closet/critter/chick - containername = "chicken crate" - -/datum/supply_packs/organic/corgi - name = "Corgi Crate" - cost = 50 - containertype = /obj/structure/closet/critter/corgi - contains = list(/obj/item/clothing/tie/petcollar) - containername = "corgi crate" - -/datum/supply_packs/organic/cat - name = "Cat Crate" - cost = 50 //Cats are worth as much as corgis. - containertype = /obj/structure/closet/critter/cat - contains = list(/obj/item/clothing/tie/petcollar, - /obj/item/toy/cattoy) - containername = "cat crate" - -/datum/supply_packs/organic/pug - name = "Pug Crate" - cost = 50 - containertype = /obj/structure/closet/critter/pug - contains = list(/obj/item/clothing/tie/petcollar) - containername = "pug crate" - -/datum/supply_packs/organic/fox - name = "Fox Crate" - cost = 55 //Foxes are cool. - containertype = /obj/structure/closet/critter/fox - contains = list(/obj/item/clothing/tie/petcollar) - containername = "fox crate" - -/datum/supply_packs/organic/butterfly - name = "Butterflies Crate" - cost = 50 - containertype = /obj/structure/closet/critter/butterfly - containername = "butterflies crate" - contraband = 1 - -////// hippy gear - -/datum/supply_packs/organic/hydroponics // -- Skie - name = "Hydroponics Supply Crate" - contains = list(/obj/item/weapon/reagent_containers/spray/plantbgone, - /obj/item/weapon/reagent_containers/spray/plantbgone, - /obj/item/weapon/reagent_containers/glass/bottle/ammonia, - /obj/item/weapon/reagent_containers/glass/bottle/ammonia, - /obj/item/weapon/hatchet, - /obj/item/weapon/cultivator, - /obj/item/device/analyzer/plant_analyzer, - /obj/item/clothing/gloves/botanic_leather, - /obj/item/clothing/suit/apron) // Updated with new things - cost = 15 - containertype = /obj/structure/closet/crate/hydroponics - containername = "hydroponics crate" - -/datum/supply_packs/misc/hydroponics/hydrotank - name = "Hydroponics Watertank Backpack Crate" - contains = list(/obj/item/weapon/watertank) - cost = 10 - containertype = /obj/structure/closet/crate/secure - containername = "hydroponics watertank crate" - access = access_hydroponics - -/datum/supply_packs/organic/hydroponics/seeds - name = "Seeds Crate" - contains = list(/obj/item/seeds/chiliseed, - /obj/item/seeds/berryseed, - /obj/item/seeds/cornseed, - /obj/item/seeds/eggplantseed, - /obj/item/seeds/tomatoseed, - /obj/item/seeds/soyaseed, - /obj/item/seeds/wheatseed, - /obj/item/seeds/carrotseed, - /obj/item/seeds/sunflowerseed, - /obj/item/seeds/chantermycelium, - /obj/item/seeds/potatoseed, - /obj/item/seeds/sugarcaneseed) - cost = 10 - containername = "seeds crate" - -/datum/supply_packs/organic/hydroponics/exoticseeds - name = "Exotic Seeds Crate" - contains = list(/obj/item/seeds/nettleseed, - /obj/item/seeds/replicapod, - /obj/item/seeds/replicapod, - /obj/item/seeds/replicapod, - /obj/item/seeds/plumpmycelium, - /obj/item/seeds/libertymycelium, - /obj/item/seeds/amanitamycelium, - /obj/item/seeds/reishimycelium, - /obj/item/seeds/bananaseed, - /obj/item/seeds/eggyseed) - cost = 15 - containername = "exotic seeds crate" - -/datum/supply_packs/organic/vending - name = "Bartending Supply Crate" - contains = list(/obj/item/weapon/vending_refill/boozeomat, - /obj/item/weapon/vending_refill/boozeomat, - /obj/item/weapon/vending_refill/boozeomat, - /obj/item/weapon/vending_refill/coffee, - /obj/item/weapon/vending_refill/coffee, - /obj/item/weapon/vending_refill/coffee) - cost = 20 - containername = "bartending supply crate" - -/datum/supply_packs/organic/vending/snack - name = "Snack Supply Crate" - contains = list(/obj/item/weapon/vending_refill/snack, - /obj/item/weapon/vending_refill/snack, - /obj/item/weapon/vending_refill/snack) - cost = 15 - containername = "snacks supply crate" - -/datum/supply_packs/organic/vending/cola - name = "Softdrinks Supply Crate" - contains = list(/obj/item/weapon/vending_refill/cola, - /obj/item/weapon/vending_refill/cola, - /obj/item/weapon/vending_refill/cola) - cost = 15 - containername = "softdrinks supply crate" - -/datum/supply_packs/organic/vending/cigarette - name = "Cigarette Supply Crate" - contains = list(/obj/item/weapon/vending_refill/cigarette, - /obj/item/weapon/vending_refill/cigarette, - /obj/item/weapon/vending_refill/cigarette) - cost = 15 - containername = "cigarette supply crate" - -////////////////////////////////////////////////////////////////////////////// -//////////////////////////// Materials /////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// - -/datum/supply_packs/materials - name = "HEADER" - group = supply_materials - - -/datum/supply_packs/materials/metal50 - name = "50 Metal Sheets" - contains = list(/obj/item/stack/sheet/metal) - amount = 50 - cost = 10 - containername = "metal sheets crate" - -/datum/supply_packs/materials/plasteel20 - name = "20 Plasteel Sheets" - contains = list(/obj/item/stack/sheet/plasteel) - amount = 20 - cost = 30 - containername = "plasteel sheets crate" - -/datum/supply_packs/materials/plasteel50 - name = "50 Plasteel Sheets" - contains = list(/obj/item/stack/sheet/plasteel) - amount = 50 - cost = 50 - containername = "plasteel sheets crate" - -/datum/supply_packs/materials/glass50 - name = "50 Glass Sheets" - contains = list(/obj/item/stack/sheet/glass) - amount = 50 - cost = 10 - containername = "glass sheets crate" - -/datum/supply_packs/materials/cardboard50 - name = "50 Cardboard Sheets" - contains = list(/obj/item/stack/sheet/cardboard) - amount = 50 - cost = 10 - containername = "cardboard sheets crate" - -/datum/supply_packs/materials/sandstone30 - name = "30 Sandstone Blocks" - contains = list(/obj/item/stack/sheet/mineral/sandstone) - amount = 30 - cost = 20 - containername = "sandstone blocks crate" - - -////////////////////////////////////////////////////////////////////////////// -//////////////////////////// Miscellaneous /////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// - -/datum/supply_packs/misc - name = "HEADER" - group = supply_misc - -/datum/supply_packs/misc/mule - name = "MULEbot Crate" - contains = list(/mob/living/simple_animal/bot/mulebot) - cost = 20 - containertype = /obj/structure/largecrate/mule - containername = "\improper MULEbot Crate" - -/datum/supply_packs/misc/conveyor - name = "Conveyor Assembly Crate" - contains = list(/obj/item/conveyor_construct, - /obj/item/conveyor_construct, - /obj/item/conveyor_construct, - /obj/item/conveyor_construct, - /obj/item/conveyor_construct, - /obj/item/conveyor_construct, - /obj/item/conveyor_switch_construct, - /obj/item/weapon/paper/conveyor) - cost = 15 - containername = "conveyor assembly crate" - -/datum/supply_packs/misc/watertank - name = "Water Tank Crate" - contains = list(/obj/structure/reagent_dispensers/watertank) - cost = 8 - containertype = /obj/structure/largecrate - containername = "water tank crate" - -/datum/supply_packs/misc/lasertag - name = "Laser Tag Crate" - contains = list(/obj/item/weapon/gun/energy/laser/redtag, - /obj/item/weapon/gun/energy/laser/redtag, - /obj/item/weapon/gun/energy/laser/redtag, - /obj/item/weapon/gun/energy/laser/bluetag, - /obj/item/weapon/gun/energy/laser/bluetag, - /obj/item/weapon/gun/energy/laser/bluetag, - /obj/item/clothing/suit/redtag, - /obj/item/clothing/suit/redtag, - /obj/item/clothing/suit/redtag, - /obj/item/clothing/suit/bluetag, - /obj/item/clothing/suit/bluetag, - /obj/item/clothing/suit/bluetag, - /obj/item/clothing/head/helmet/redtaghelm, - /obj/item/clothing/head/helmet/bluetaghelm) - cost = 15 - containername = "laser tag crate" - -/datum/supply_packs/misc/religious_supplies - name = "Religious Supplies Crate" - contains = list(/obj/item/weapon/reagent_containers/food/drinks/bottle/holywater, - /obj/item/weapon/reagent_containers/food/drinks/bottle/holywater, - /obj/item/weapon/storage/book/bible/booze, - /obj/item/weapon/storage/book/bible/booze, - /obj/item/clothing/suit/hooded/chaplain_hoodie, - /obj/item/clothing/suit/hooded/chaplain_hoodie) - cost = 40 // it costs so much because the Space Church is ran by Space Jews - containername = "religious supplies crate" - -/datum/supply_packs/misc/posters - name = "Corporate Posters Crate" - contains = list(/obj/item/weapon/poster/legit, - /obj/item/weapon/poster/legit, - /obj/item/weapon/poster/legit, - /obj/item/weapon/poster/legit, - /obj/item/weapon/poster/legit) - cost = 8 - containername = "Corporate Posters Crate" - - -///////////// Paper Work - -/datum/supply_packs/misc/paper - name = "Bureaucracy Crate" - contains = list(/obj/structure/filingcabinet/chestdrawer/wheeled, - /obj/item/device/camera_film, - /obj/item/weapon/hand_labeler, - /obj/item/hand_labeler_refill, - /obj/item/hand_labeler_refill, - /obj/item/weapon/paper_bin, - /obj/item/weapon/pen/fourcolor, - /obj/item/weapon/pen/fourcolor, - /obj/item/weapon/pen, - /obj/item/weapon/pen/blue, - /obj/item/weapon/pen/red, - /obj/item/weapon/folder/blue, - /obj/item/weapon/folder/red, - /obj/item/weapon/folder/yellow, - /obj/item/weapon/clipboard, - /obj/item/weapon/clipboard) - cost = 15 - containername = "bureaucracy crate" - -/datum/supply_packs/misc/toner - name = "Toner Cartridges crate" - contains = list(/obj/item/device/toner, - /obj/item/device/toner, - /obj/item/device/toner, - /obj/item/device/toner, - /obj/item/device/toner, - /obj/item/device/toner) - cost = 10 - containername = "toner cartridges crate" - - -///////////// Janitor Supplies - -/datum/supply_packs/misc/janitor - name = "Janitorial Supplies Crate" - contains = list(/obj/item/weapon/reagent_containers/glass/bucket, - /obj/item/weapon/reagent_containers/glass/bucket, - /obj/item/weapon/reagent_containers/glass/bucket, - /obj/item/weapon/mop, - /obj/item/weapon/caution, - /obj/item/weapon/caution, - /obj/item/weapon/caution, - /obj/item/weapon/storage/bag/trash, - /obj/item/weapon/reagent_containers/spray/cleaner, - /obj/item/weapon/reagent_containers/glass/rag, - /obj/item/weapon/grenade/chem_grenade/cleaner, - /obj/item/weapon/grenade/chem_grenade/cleaner, - /obj/item/weapon/grenade/chem_grenade/cleaner) - cost = 10 - containername = "janitorial supplies crate" - -/datum/supply_packs/misc/janitor/janicart - name = "Janitorial Cart and Galoshes Crate" - contains = list(/obj/structure/janitorialcart, - /obj/item/clothing/shoes/galoshes) - cost = 10 - containertype = /obj/structure/largecrate - containername = "janitorial cart crate" - -/datum/supply_packs/misc/janitor/janitank - name = "Janitor Watertank Backpack" - contains = list(/obj/item/weapon/watertank/janitor) - cost = 10 - containertype = /obj/structure/closet/crate/secure - containername = "janitor watertank crate" - access = access_janitor - -/datum/supply_packs/misc/janitor/lightbulbs - name = "Replacement Lights" - contains = list(/obj/item/weapon/storage/box/lights/mixed, - /obj/item/weapon/storage/box/lights/mixed, - /obj/item/weapon/storage/box/lights/mixed) - cost = 10 - containername = "replacement lights" - -/datum/supply_packs/misc/noslipfloor - name = "High-traction Floor Tiles" - contains = list(/obj/item/stack/tile/noslip) - amount = 20 - cost = 20 - containername = "high-traction floor tiles" - -/datum/supply_packs/misc/plasmaman - name = "Plasma-man Supply Kit" - contains = list(/obj/item/clothing/under/plasmaman, - /obj/item/clothing/under/plasmaman, - /obj/item/weapon/tank/internals/plasmaman/belt/full, - /obj/item/weapon/tank/internals/plasmaman/belt/full, - /obj/item/clothing/head/helmet/space/plasmaman, - /obj/item/clothing/head/helmet/space/plasmaman) - cost = 20 - containername = "plasma-man supply kit" - -///////////// Costumes - -/datum/supply_packs/misc/costume - name = "Standard Costume Crate" - contains = list(/obj/item/weapon/storage/backpack/clown, - /obj/item/clothing/shoes/clown_shoes, - /obj/item/clothing/mask/gas/clown_hat, - /obj/item/clothing/under/rank/clown, - /obj/item/weapon/bikehorn, - /obj/item/clothing/under/rank/mime, - /obj/item/clothing/shoes/sneakers/black, - /obj/item/clothing/gloves/color/white, - /obj/item/clothing/mask/gas/mime, - /obj/item/clothing/head/beret, - /obj/item/clothing/suit/suspenders, - /obj/item/weapon/reagent_containers/food/drinks/bottle/bottleofnothing, - /obj/item/weapon/storage/backpack/mime) - cost = 10 - containertype = /obj/structure/closet/crate/secure - containername = "standard costumes" - access = access_theatre - -/datum/supply_packs/misc/wizard - name = "Wizard Costume Crate" - contains = list(/obj/item/weapon/staff, - /obj/item/clothing/suit/wizrobe/fake, - /obj/item/clothing/shoes/sandal, - /obj/item/clothing/head/wizard/fake) - cost = 20 - containername = "wizard costume crate" - -/datum/supply_packs/misc/randomised - var/num_contained = 3 //number of items picked to be contained in a randomised crate - contains = list(/obj/item/clothing/head/collectable/chef, - /obj/item/clothing/head/collectable/paper, - /obj/item/clothing/head/collectable/tophat, - /obj/item/clothing/head/collectable/captain, - /obj/item/clothing/head/collectable/beret, - /obj/item/clothing/head/collectable/welding, - /obj/item/clothing/head/collectable/flatcap, - /obj/item/clothing/head/collectable/pirate, - /obj/item/clothing/head/collectable/kitty, - /obj/item/clothing/head/collectable/rabbitears, - /obj/item/clothing/head/collectable/wizard, - /obj/item/clothing/head/collectable/hardhat, - /obj/item/clothing/head/collectable/HoS, - /obj/item/clothing/head/collectable/thunderdome, - /obj/item/clothing/head/collectable/swat, - /obj/item/clothing/head/collectable/slime, - /obj/item/clothing/head/collectable/police, - /obj/item/clothing/head/collectable/slime, - /obj/item/clothing/head/collectable/xenom, - /obj/item/clothing/head/collectable/petehat) - name = "Collectable hat crate!" - cost = 200 - containername = "collectable hats crate! Brought to you by Bass.inc!" - -/datum/supply_packs/misc/randomised/New() - manifest += "Contains any [num_contained] of:" - ..() - - -/datum/supply_packs/misc/randomised/contraband - num_contained = 5 - contains = list(/obj/item/weapon/poster/contraband, - /obj/item/weapon/storage/fancy/cigarettes/dromedaryco, - /obj/item/weapon/storage/fancy/cigarettes/cigpack_shadyjims) - name = "Contraband Crate" - cost = 30 - containername = "crate" //let's keep it subtle, eh? - contraband = 1 - -/datum/supply_packs/misc/randomised/toys - name = "Toy Crate" - num_contained = 5 - contains = list(/obj/item/toy/spinningtoy, - /obj/item/toy/sword, - /obj/item/toy/foamblade, - /obj/item/toy/AI, - /obj/item/toy/owl, - /obj/item/toy/griffin, - /obj/item/toy/nuke, - /obj/item/toy/minimeteor, - /obj/item/toy/carpplushie, - /obj/item/weapon/coin/antagtoken, - /obj/item/stack/tile/fakespace, - /obj/item/weapon/gun/projectile/shotgun/toy/crossbow, - /obj/item/toy/redbutton) - - cost = 50 // or play the arcade machines ya lazy bum - containername ="toy crate" - -/datum/supply_packs/misc/autodrobe - name = "Autodrobe Supply Crate" - contains = list(/obj/item/weapon/vending_refill/autodrobe, - /obj/item/weapon/vending_refill/autodrobe) - cost = 15 - containername = "autodrobe supply crate" - -/datum/supply_packs/misc/formalwear //This is a very classy crate. - name = "Formal-wear Crate" - contains = list(/obj/item/clothing/under/blacktango, - /obj/item/clothing/under/assistantformal, - /obj/item/clothing/under/assistantformal, - /obj/item/clothing/under/lawyer/bluesuit, - /obj/item/clothing/suit/toggle/lawyer, - /obj/item/clothing/under/lawyer/purpsuit, - /obj/item/clothing/suit/toggle/lawyer/purple, - /obj/item/clothing/under/lawyer/blacksuit, - /obj/item/clothing/suit/toggle/lawyer/black, - /obj/item/clothing/tie/waistcoat, - /obj/item/clothing/tie/blue, - /obj/item/clothing/tie/red, - /obj/item/clothing/tie/black, - /obj/item/clothing/head/bowler, - /obj/item/clothing/head/fedora, - /obj/item/clothing/head/flatcap, - /obj/item/clothing/head/beret, - /obj/item/clothing/head/that, - /obj/item/clothing/shoes/laceup, - /obj/item/clothing/shoes/laceup, - /obj/item/clothing/shoes/laceup, - /obj/item/clothing/under/suit_jacket/charcoal, - /obj/item/clothing/under/suit_jacket/navy, - /obj/item/clothing/under/suit_jacket/burgundy, - /obj/item/clothing/under/suit_jacket/checkered, - /obj/item/clothing/under/suit_jacket/tan, - /obj/item/weapon/lipstick/random) - cost = 30 //Lots of very expensive items. You gotta pay up to look good! - containername = "formal-wear crate" - -/datum/supply_packs/misc/foamforce - name = "Foam Force Crate" - contains = list(/obj/item/weapon/gun/projectile/shotgun/toy, - /obj/item/weapon/gun/projectile/shotgun/toy, - /obj/item/weapon/gun/projectile/shotgun/toy, - /obj/item/weapon/gun/projectile/shotgun/toy, - /obj/item/weapon/gun/projectile/shotgun/toy, - /obj/item/weapon/gun/projectile/shotgun/toy, - /obj/item/weapon/gun/projectile/shotgun/toy, - /obj/item/weapon/gun/projectile/shotgun/toy) - cost = 10 - containername = "foam force crate" - -/datum/supply_packs/misc/foamforce/bonus - name = "Foam Force Pistols Crate" - contains = list(/obj/item/weapon/gun/projectile/automatic/toy/pistol, - /obj/item/weapon/gun/projectile/automatic/toy/pistol, - /obj/item/ammo_box/magazine/toy/pistol, - /obj/item/ammo_box/magazine/toy/pistol) - cost = 40 - containername = "foam force pistols crate" - contraband = 1 - +//SUPPLY PACKS +//NOTE: only secure crate types use the access var (and are lockable) +//NOTE: hidden packs only show up when the computer has been hacked. +//ANOTHER NOTE: Contraband is obtainable through modified supplycomp circuitboards. +//BIG NOTE: Don't add living things to crates, that's bad, it will break the shuttle. +//NEW NOTE: Do NOT set the price of any crates below 7 points. Doing so allows infinite points. + +// Supply Groups +var/const/supply_emergency = 1 +var/const/supply_security = 2 +var/const/supply_engineer = 3 +var/const/supply_medical = 4 +var/const/supply_science = 5 +var/const/supply_organic = 6 +var/const/supply_materials = 7 +var/const/supply_misc = 8 + +var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engineer,supply_medical,supply_science,supply_organic,supply_materials,supply_misc) + +/proc/get_supply_group_name(cat) + switch(cat) + if(1) + return "Emergency" + if(2) + return "Security" + if(3) + return "Engineering" + if(4) + return "Medical" + if(5) + return "Science" + if(6) + return "Food & Livestock" + if(7) + return "Raw Materials" + if(8) + return "Miscellaneous" + + +/datum/supply_packs + var/name = null + var/list/contains = list() + var/manifest = "" + var/amount = null + var/cost = null + var/containertype = /obj/structure/closet/crate + var/containername = null + var/access = null + var/hidden = 0 + var/contraband = 0 + var/group = supply_misc + + +/datum/supply_packs/New() + manifest += "
    " + for(var/path in contains) + if(!path) continue + var/atom/movable/AM = path + manifest += "
  • [initial(AM.name)]
  • " + manifest += "
" + +////// Use the sections to keep things tidy please /Malkevin + +////////////////////////////////////////////////////////////////////////////// +//////////////////////////// Emergency /////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +/datum/supply_packs/emergency // Section header - use these to set default supply group and crate type for sections + name = "HEADER" // Use "HEADER" to denote section headers, this is needed for the supply computers to filter them + containertype = /obj/structure/closet/crate/internals + group = supply_emergency + + +/datum/supply_packs/emergency/evac + name = "Emergency equipment" + contains = list(/mob/living/simple_animal/bot/floorbot, + /mob/living/simple_animal/bot/floorbot, + /mob/living/simple_animal/bot/medbot, + /mob/living/simple_animal/bot/medbot, + /obj/item/weapon/tank/internals/air, + /obj/item/weapon/tank/internals/air, + /obj/item/weapon/tank/internals/air, + /obj/item/weapon/tank/internals/air, + /obj/item/weapon/tank/internals/air, + /obj/item/clothing/mask/gas, + /obj/item/clothing/mask/gas, + /obj/item/clothing/mask/gas, + /obj/item/clothing/mask/gas, + /obj/item/clothing/mask/gas) + cost = 35 + containertype = /obj/structure/closet/crate/internals + containername = "emergency crate" + group = supply_emergency + +/datum/supply_packs/emergency/internals + name = "Internals Crate" + contains = list(/obj/item/clothing/mask/gas, + /obj/item/clothing/mask/gas, + /obj/item/clothing/mask/gas, + /obj/item/weapon/tank/internals/air, + /obj/item/weapon/tank/internals/air, + /obj/item/weapon/tank/internals/air) + cost = 10 + containername = "internals crate" + +/datum/supply_packs/emergency/firefighting + name = "Firefighting Crate" + contains = list(/obj/item/clothing/suit/fire/firefighter, + /obj/item/clothing/suit/fire/firefighter, + /obj/item/clothing/mask/gas, + /obj/item/clothing/mask/gas, + /obj/item/device/flashlight, + /obj/item/device/flashlight, + /obj/item/weapon/tank/internals/oxygen/red, + /obj/item/weapon/tank/internals/oxygen/red, + /obj/item/weapon/extinguisher, + /obj/item/weapon/extinguisher, + /obj/item/clothing/head/hardhat/red, + /obj/item/clothing/head/hardhat/red) + cost = 10 + containertype = /obj/structure/closet/crate + containername = "firefighting crate" + +/datum/supply_packs/emergency/atmostank + name = "Firefighting Watertank" + contains = list(/obj/item/weapon/watertank/atmos) + cost = 10 + containertype = /obj/structure/closet/crate/secure + containername = "firefighting watertank crate" + access = access_atmospherics + +/datum/supply_packs/emergency/weedcontrol + name = "Weed Control Crate" + contains = list(/obj/item/weapon/scythe, + /obj/item/clothing/mask/gas, + /obj/item/weapon/grenade/chem_grenade/antiweed, + /obj/item/weapon/grenade/chem_grenade/antiweed) + cost = 15 + containertype = /obj/structure/closet/crate/secure/hydrosec + containername = "weed control crate" + access = access_hydroponics + +/datum/supply_packs/emergency/specialops + name = "Special Ops supplies" + contains = list(/obj/item/weapon/storage/box/emps, + /obj/item/weapon/grenade/smokebomb, + /obj/item/weapon/grenade/smokebomb, + /obj/item/weapon/grenade/smokebomb, + /obj/item/weapon/pen/sleepy, + /obj/item/weapon/grenade/chem_grenade/incendiary) + cost = 20 + containertype = /obj/structure/closet/crate + containername = "special ops crate" + hidden = 1 + +/datum/supply_packs/emergency/syndicate + name = "ERROR_NULL_ENTRY" + contains = list(/obj/item/weapon/storage/box/syndicate) + cost = 140 + containertype = /obj/structure/closet/crate + containername = "crate" + hidden = 1 + +////////////////////////////////////////////////////////////////////////////// +//////////////////////////// Security //////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +/datum/supply_packs/security + name = "HEADER" + containertype = /obj/structure/closet/crate/secure/gear + access = access_security + group = supply_security + + +/datum/supply_packs/security/supplies + name = "Security Supplies Crate" + contains = list(/obj/item/weapon/storage/box/flashbangs, + /obj/item/weapon/storage/box/teargas, + /obj/item/weapon/storage/box/flashes, + /obj/item/weapon/storage/box/handcuffs) + cost = 10 + containername = "security supply crate" + +////// Armor: Basic + +/datum/supply_packs/security/helmets + name = "Helmets Crate" + contains = list(/obj/item/clothing/head/helmet/sec, + /obj/item/clothing/head/helmet/sec, + /obj/item/clothing/head/helmet/sec) + cost = 10 + containername = "helmet crate" + +/datum/supply_packs/security/armor + name = "Armor Crate" + contains = list(/obj/item/clothing/suit/armor/vest, + /obj/item/clothing/suit/armor/vest, + /obj/item/clothing/suit/armor/vest) + cost = 10 + containername = "armor crate" + +////// Weapons: Basic + +/datum/supply_packs/security/baton + name = "Stun Batons Crate" + contains = list(/obj/item/weapon/melee/baton/loaded, + /obj/item/weapon/melee/baton/loaded, + /obj/item/weapon/melee/baton/loaded) + cost = 10 + containername = "stun baton crate" + +/datum/supply_packs/security/laser + name = "Lasers Crate" + contains = list(/obj/item/weapon/gun/energy/laser, + /obj/item/weapon/gun/energy/laser, + /obj/item/weapon/gun/energy/laser) + cost = 15 + containername = "laser crate" + +/datum/supply_packs/security/taser + name = "Stun Guns Crate" + contains = list(/obj/item/weapon/gun/energy/gun/advtaser, + /obj/item/weapon/gun/energy/gun/advtaser, + /obj/item/weapon/gun/energy/gun/advtaser) + cost = 15 + containername = "stun gun crate" + +/datum/supply_packs/security/disabler + name = "Disabler Crate" + contains = list(/obj/item/weapon/gun/energy/disabler, + /obj/item/weapon/gun/energy/disabler, + /obj/item/weapon/gun/energy/disabler) + cost = 10 + containername = "disabler crate" + +/datum/supply_packs/security/forensics + name = "Forensics Crate" + contains = list(/obj/item/device/detective_scanner, + /obj/item/weapon/storage/box/evidence, + /obj/item/device/camera, + /obj/item/device/taperecorder, + /obj/item/toy/crayon/white, + /obj/item/clothing/head/det_hat) + cost = 20 + containername ="forensics crate" + +///// Armory stuff + +/datum/supply_packs/security/armory + name = "HEADER" + containertype = /obj/structure/closet/crate/secure/weapon + access = access_armory + +///// Armor: Specialist + +/datum/supply_packs/security/armory/riothelmets + name = "Riot Helmets Crate" + contains = list(/obj/item/clothing/head/helmet/riot, + /obj/item/clothing/head/helmet/riot, + /obj/item/clothing/head/helmet/riot) + cost = 15 + containername = "riot helmets crate" + +/datum/supply_packs/security/armory/riotarmor + name = "Riot Armor Crate" + contains = list(/obj/item/clothing/suit/armor/riot, + /obj/item/clothing/suit/armor/riot, + /obj/item/clothing/suit/armor/riot) + cost = 15 + containername = "riot armor crate" + +/datum/supply_packs/security/armory/riotshields + name = "Riot Shields Crate" + contains = list(/obj/item/weapon/shield/riot, + /obj/item/weapon/shield/riot, + /obj/item/weapon/shield/riot) + cost = 20 + containername = "riot shields crate" + +/datum/supply_packs/security/armory/bulletarmor + name = "Bulletproof Armor Crate" + contains = list(/obj/item/clothing/suit/armor/bulletproof, + /obj/item/clothing/suit/armor/bulletproof, + /obj/item/clothing/suit/armor/bulletproof) + cost = 15 + containername = "tactical armor crate" + +/datum/supply_packs/security/armory/laserarmor + name = "Reflector Vest Crate" + contains = list(/obj/item/clothing/suit/armor/laserproof, + /obj/item/clothing/suit/armor/laserproof) // Only two vests to keep costs down for balance + cost = 20 + containertype = /obj/structure/closet/crate/secure/plasma + containername = "reflector vest crate" + +/////// Weapons: Specialist + +/datum/supply_packs/security/armory/ballistic + name = "Combat Shotguns Crate" + contains = list(/obj/item/weapon/gun/projectile/shotgun/automatic/combat, + /obj/item/weapon/gun/projectile/shotgun/automatic/combat, + /obj/item/weapon/gun/projectile/shotgun/automatic/combat, + /obj/item/weapon/storage/belt/bandolier, + /obj/item/weapon/storage/belt/bandolier, + /obj/item/weapon/storage/belt/bandolier) + cost = 20 + containername = "combat shotgun crate" + +/datum/supply_packs/security/armory/expenergy + name = "Energy Guns Crate" + contains = list(/obj/item/weapon/gun/energy/gun, + /obj/item/weapon/gun/energy/gun) // Only two guns to keep costs down + cost = 25 + containertype = /obj/structure/closet/crate/secure/plasma + containername = "energy gun crate" + +/datum/supply_packs/security/armory/eweapons + name = "Incendiary Weapons Crate" + contains = list(/obj/item/weapon/flamethrower/full, + /obj/item/weapon/tank/internals/plasma, + /obj/item/weapon/tank/internals/plasma, + /obj/item/weapon/tank/internals/plasma, + /obj/item/weapon/grenade/chem_grenade/incendiary, + /obj/item/weapon/grenade/chem_grenade/incendiary, + /obj/item/weapon/grenade/chem_grenade/incendiary) + cost = 15 // its a fecking flamethrower and some plasma, why the shit did this cost so much before!? + containertype = /obj/structure/closet/crate/secure/plasma + containername = "incendiary weapons crate" + access = access_heads + +/datum/supply_packs/security/armory/wt550 + name = "WT-550 Auto Rifle Crate" + contains = list(/obj/item/weapon/gun/projectile/automatic/wt550, + /obj/item/weapon/gun/projectile/automatic/wt550) + cost = 35 + containername = "auto rifle crate" + +/datum/supply_packs/security/armory/wt550ammo + name = "WT-550 Rifle Ammo Crate" + contains = list(/obj/item/ammo_box/magazine/wt550m9, + /obj/item/ammo_box/magazine/wt550m9, + /obj/item/ammo_box/magazine/wt550m9, + /obj/item/ammo_box/magazine/wt550m9,) + cost = 30 + containername = "auto rifle ammo crate" + +/////// Implants & etc + +/datum/supply_packs/security/armory/loyalty + name = "Loyalty Implants Crate" + contains = list (/obj/item/weapon/storage/lockbox/loyalty) + cost = 40 + containername = "loyalty implant crate" + +/datum/supply_packs/security/armory/trackingimp + name = "Tracking Implants Crate" + contains = list (/obj/item/weapon/storage/box/trackimp) + cost = 20 + containername = "tracking implant crate" + +/datum/supply_packs/security/armory/chemimp + name = "Chemical Implants Crate" + contains = list (/obj/item/weapon/storage/box/chemimp) + cost = 20 + containername = "chemical implant crate" + +/datum/supply_packs/security/armory/exileimp + name = "Exile Implants Crate" + contains = list (/obj/item/weapon/storage/box/exileimp) + cost = 30 + containername = "exile implant crate" + +/datum/supply_packs/security/securitybarriers + name = "Security Barriers Crate" + contains = list(/obj/item/weapon/grenade/barrier, + /obj/item/weapon/grenade/barrier, + /obj/item/weapon/grenade/barrier, + /obj/item/weapon/grenade/barrier) + cost = 20 + containername = "security barriers crate" + +/datum/supply_packs/security/firingpins + name = "Standard Firing Pins Crate" + contains = list(/obj/item/weapon/storage/box/firingpins, + /obj/item/weapon/storage/box/firingpins) + cost = 10 + containername = "firing pins crate" + +/datum/supply_packs/security/securityclothes + name = "Security Clothing Crate" + contains = list(/obj/item/clothing/under/rank/security/navyblue, + /obj/item/clothing/under/rank/security/navyblue, + /obj/item/clothing/suit/security/officer, + /obj/item/clothing/suit/security/officer, + /obj/item/clothing/head/beret/sec/navyofficer, + /obj/item/clothing/head/beret/sec/navyofficer, + /obj/item/clothing/under/rank/warden/navyblue, + /obj/item/clothing/suit/security/warden, + /obj/item/clothing/head/beret/sec/navywarden, + /obj/item/clothing/under/rank/head_of_security/navyblue, + /obj/item/clothing/suit/security/hos, + /obj/item/clothing/head/beret/sec/navyhos) + cost = 30 + containername = "security clothing crate" + +/////// Joke Crate Inbound + +/datum/supply_packs/security/justiceinbound + name = "Standard Justice Enforcer Crate" + contains = list(/obj/item/clothing/head/helmet/justice, + /obj/item/clothing/mask/gas/sechailer) + cost = 80 //justice comes at a price. An expensive, noisy price. + containername = "justice enforcer crate" + + +////////////////////////////////////////////////////////////////////////////// +//////////////////////////// Engineering ///////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +/datum/supply_packs/engineering + name = "HEADER" + group = supply_engineer + + +/datum/supply_packs/engineering/fueltank + name = "Fuel Tank Crate" + contains = list(/obj/structure/reagent_dispensers/fueltank) + cost = 8 + containertype = /obj/structure/largecrate + containername = "fuel tank crate" + +/datum/supply_packs/engineering/tools //the most robust crate + name = "Toolbox Crate" + contains = list(/obj/item/weapon/storage/toolbox/electrical, + /obj/item/weapon/storage/toolbox/electrical, + /obj/item/weapon/storage/toolbox/mechanical, + /obj/item/weapon/storage/toolbox/electrical, + /obj/item/weapon/storage/toolbox/mechanical, + /obj/item/weapon/storage/toolbox/mechanical) + cost = 10 + containername = "electrical maintenance crate" + +/datum/supply_packs/engineering/powergamermitts + name = "Insulated Gloves Crate" + contains = list(/obj/item/clothing/gloves/color/yellow, + /obj/item/clothing/gloves/color/yellow, + /obj/item/clothing/gloves/color/yellow) + cost = 20 //Made of pure-grade bullshittinium + containername = "insulated gloves crate" + +/datum/supply_packs/engineering/power + name = "Powercell Crate" + contains = list(/obj/item/weapon/stock_parts/cell/high, //Changed to an extra high powercell because normal cells are useless + /obj/item/weapon/stock_parts/cell/high, + /obj/item/weapon/stock_parts/cell/high) + cost = 10 + containername = "electrical maintenance crate" + +/datum/supply_packs/engineering/engiequipment + name = "Engineering Gear Crate" + contains = list(/obj/item/weapon/storage/belt/utility, + /obj/item/weapon/storage/belt/utility, + /obj/item/weapon/storage/belt/utility, + /obj/item/clothing/suit/hazardvest, + /obj/item/clothing/suit/hazardvest, + /obj/item/clothing/suit/hazardvest, + /obj/item/clothing/head/welding, + /obj/item/clothing/head/welding, + /obj/item/clothing/head/welding, + /obj/item/clothing/head/hardhat, + /obj/item/clothing/head/hardhat, + /obj/item/clothing/head/hardhat) + cost = 10 + containername = "engineering gear crate" + +/datum/supply_packs/engineering/solar + name = "Solar Pack Crate" + contains = list(/obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, // 21 Solar Assemblies. 1 Extra for the controller + /obj/item/weapon/circuitboard/solar_control, + /obj/item/weapon/electronics/tracker, + /obj/item/weapon/paper/solar) + cost = 20 + containername = "solar pack crate" + +/datum/supply_packs/engineering/engine + name = "Emitter Crate" + contains = list(/obj/machinery/power/emitter, + /obj/machinery/power/emitter) + cost = 10 + containertype = /obj/structure/closet/crate/secure + containername = "emitter crate" + access = access_ce + +/datum/supply_packs/engineering/engine/field_gen + name = "Field Generator Crate" + contains = list(/obj/machinery/field/generator, + /obj/machinery/field/generator) + cost = 10 + containername = "field generator crate" + +/datum/supply_packs/engineering/engine/sing_gen + name = "Singularity Generator Crate" + contains = list(/obj/machinery/the_singularitygen) + cost = 10 + containername = "singularity generator crate" + +/datum/supply_packs/engineering/engine/collector + name = "Collector Crate" + contains = list(/obj/machinery/power/rad_collector, + /obj/machinery/power/rad_collector, + /obj/machinery/power/rad_collector) + cost = 10 + containername = "collector crate" + +/datum/supply_packs/engineering/engine/PA + name = "Particle Accelerator Crate" + contains = list(/obj/structure/particle_accelerator/fuel_chamber, + /obj/machinery/particle_accelerator/control_box, + /obj/structure/particle_accelerator/particle_emitter/center, + /obj/structure/particle_accelerator/particle_emitter/left, + /obj/structure/particle_accelerator/particle_emitter/right, + /obj/structure/particle_accelerator/power_box, + /obj/structure/particle_accelerator/end_cap) + cost = 25 + containername = "particle accelerator crate" + +/datum/supply_packs/engineering/engine/spacesuit + name = "Space Suit Crate" + contains = list(/obj/item/clothing/suit/space, + /obj/item/clothing/head/helmet/space, + /obj/item/clothing/mask/breath,) + cost = 80 + containertype = /obj/structure/closet/crate/secure + containername = "space suit crate" + access = access_eva + +////////////////////////////////////////////////////////////////////////////// +//////////////////////////// Medical ///////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +/datum/supply_packs/medical + name = "HEADER" + containertype = /obj/structure/closet/crate/medical + group = supply_medical + + +/datum/supply_packs/medical/supplies + name = "Medical Supplies Crate" + contains = list(/obj/item/weapon/reagent_containers/glass/bottle/charcoal, + /obj/item/weapon/reagent_containers/glass/bottle/charcoal, + /obj/item/weapon/reagent_containers/glass/bottle/epinephrine, + /obj/item/weapon/reagent_containers/glass/bottle/epinephrine, + /obj/item/weapon/reagent_containers/glass/bottle/morphine, + /obj/item/weapon/reagent_containers/glass/bottle/morphine, + /obj/item/weapon/reagent_containers/glass/bottle/morphine, + /obj/item/weapon/reagent_containers/glass/bottle/morphine, + /obj/item/weapon/reagent_containers/glass/bottle/morphine, + /obj/item/weapon/reagent_containers/glass/bottle/morphine, + /obj/item/weapon/reagent_containers/glass/bottle/toxin, + /obj/item/weapon/reagent_containers/glass/bottle/toxin, + /obj/item/weapon/reagent_containers/glass/beaker/large, + /obj/item/weapon/reagent_containers/glass/beaker/large, + /obj/item/weapon/reagent_containers/pill/insulin, + /obj/item/weapon/reagent_containers/pill/insulin, + /obj/item/weapon/reagent_containers/pill/insulin, + /obj/item/weapon/reagent_containers/pill/insulin, + /obj/item/stack/medical/gauze, + /obj/item/weapon/storage/box/beakers, + /obj/item/weapon/storage/box/syringes, + /obj/item/weapon/storage/box/bodybags) + cost = 20 + containertype = /obj/structure/closet/crate/medical + containername = "medical supplies crate" + +/datum/supply_packs/medical/firstaid + name = "First Aid Kits Crate" + contains = list(/obj/item/weapon/storage/firstaid/regular, + /obj/item/weapon/storage/firstaid/regular, + /obj/item/weapon/storage/firstaid/regular, + /obj/item/weapon/storage/firstaid/regular) + cost = 10 + containername = "first aid kits crate" + +/datum/supply_packs/medical/firstaidbruises + name = "Bruise Treatment Kits Crate" + contains = list(/obj/item/weapon/storage/firstaid/brute, + /obj/item/weapon/storage/firstaid/brute, + /obj/item/weapon/storage/firstaid/brute) + cost = 10 + containername = "brute trauma first aid kits crate" + +/datum/supply_packs/medical/firstaidburns + name = "Burns Treatment Kits Crate" + contains = list(/obj/item/weapon/storage/firstaid/fire, + /obj/item/weapon/storage/firstaid/fire, + /obj/item/weapon/storage/firstaid/fire) + cost = 10 + containername = "fire first aid kits crate" + +/datum/supply_packs/medical/firstaidtoxins + name = "Toxin Treatment Kits Crate" + contains = list(/obj/item/weapon/storage/firstaid/toxin, + /obj/item/weapon/storage/firstaid/toxin, + /obj/item/weapon/storage/firstaid/toxin) + cost = 10 + containername = "toxin first aid kits crate" + +/datum/supply_packs/medical/firstaidoxygen + name = "Oxygen Deprivation Kits Crate" + contains = list(/obj/item/weapon/storage/firstaid/o2, + /obj/item/weapon/storage/firstaid/o2, + /obj/item/weapon/storage/firstaid/o2) + cost = 10 + containername = "oxygen deprivation kits crate" + + +/datum/supply_packs/medical/virus + name = "Virus Crate" + contains = list(/obj/item/weapon/reagent_containers/glass/bottle/flu_virion, + /obj/item/weapon/reagent_containers/glass/bottle/cold, + /obj/item/weapon/reagent_containers/glass/bottle/epiglottis_virion, + /obj/item/weapon/reagent_containers/glass/bottle/liver_enhance_virion, + /obj/item/weapon/reagent_containers/glass/bottle/fake_gbs, + /obj/item/weapon/reagent_containers/glass/bottle/magnitis, + /obj/item/weapon/reagent_containers/glass/bottle/pierrot_throat, + /obj/item/weapon/reagent_containers/glass/bottle/brainrot, + /obj/item/weapon/reagent_containers/glass/bottle/hullucigen_virion, + /obj/item/weapon/reagent_containers/glass/bottle/anxiety, + /obj/item/weapon/reagent_containers/glass/bottle/beesease, + /obj/item/weapon/storage/box/syringes, + /obj/item/weapon/storage/box/beakers, + /obj/item/weapon/reagent_containers/glass/bottle/mutagen) + cost = 25 + containertype = /obj/structure/closet/crate/secure/plasma + containername = "virus crate" + access = access_cmo + + +/datum/supply_packs/medical/bloodpacks + name = "Blood Pack Variety Crate" + contains = list(/obj/item/weapon/reagent_containers/blood/empty, + /obj/item/weapon/reagent_containers/blood/empty, + /obj/item/weapon/reagent_containers/blood/APlus, + /obj/item/weapon/reagent_containers/blood/AMinus, + /obj/item/weapon/reagent_containers/blood/BPlus, + /obj/item/weapon/reagent_containers/blood/BMinus, + /obj/item/weapon/reagent_containers/blood/OPlus, + /obj/item/weapon/reagent_containers/blood/OMinus) + cost = 35 + containertype = /obj/structure/closet/crate/freezer + containername = "blood pack crate" + +/datum/supply_packs/medical/iv_drip + name = "IV Drip Crate" + contains = list(/obj/machinery/iv_drip) + cost = 30 + containertype = /obj/structure/closet/crate/secure + containername = "iv drip crate" + access = access_cmo + +////////////////////////////////////////////////////////////////////////////// +//////////////////////////// Science ///////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +/datum/supply_packs/science + name = "HEADER" + group = supply_science + + +/datum/supply_packs/science/robotics + name = "Robotics Assembly Crate" + contains = list(/obj/item/device/assembly/prox_sensor, + /obj/item/device/assembly/prox_sensor, + /obj/item/device/assembly/prox_sensor, + /obj/item/weapon/storage/toolbox/electrical, + /obj/item/weapon/storage/box/flashes, + /obj/item/weapon/stock_parts/cell/high, + /obj/item/weapon/stock_parts/cell/high) + cost = 10 + containertype = /obj/structure/closet/crate/secure + containername = "robotics assembly crate" + access = access_robotics + +/datum/supply_packs/science/robotics/mecha_ripley + name = "Circuit Crate (\"Ripley\" APLU)" + contains = list(/obj/item/weapon/book/manual/ripley_build_and_repair, + /obj/item/weapon/circuitboard/mecha/ripley/main, //TEMPORARY due to lack of circuitboard printer + /obj/item/weapon/circuitboard/mecha/ripley/peripherals) //TEMPORARY due to lack of circuitboard printer + cost = 30 + containertype = /obj/structure/closet/crate/secure + containername = "\improper APLU \"Ripley\" circuit crate" + +/datum/supply_packs/science/robotics/mecha_odysseus + name = "Circuit Crate (\"Odysseus\")" + contains = list(/obj/item/weapon/circuitboard/mecha/odysseus/peripherals, //TEMPORARY due to lack of circuitboard printer + /obj/item/weapon/circuitboard/mecha/odysseus/main) //TEMPORARY due to lack of circuitboard printer + cost = 25 + containertype = /obj/structure/closet/crate/secure + containername = "\improper \"Odysseus\" circuit crate" + +/datum/supply_packs/science/plasma + name = "Plasma Assembly Crate" + contains = list(/obj/item/weapon/tank/internals/plasma, + /obj/item/weapon/tank/internals/plasma, + /obj/item/weapon/tank/internals/plasma, + /obj/item/device/assembly/igniter, + /obj/item/device/assembly/igniter, + /obj/item/device/assembly/igniter, + /obj/item/device/assembly/prox_sensor, + /obj/item/device/assembly/prox_sensor, + /obj/item/device/assembly/prox_sensor, + /obj/item/device/assembly/timer, + /obj/item/device/assembly/timer, + /obj/item/device/assembly/timer) + cost = 10 + containertype = /obj/structure/closet/crate/secure/plasma + containername = "plasma assembly crate" + access = access_tox_storage + group = supply_science + +/datum/supply_packs/science/shieldwalls + name = "Shield Generators" + contains = list(/obj/machinery/shieldwallgen, + /obj/machinery/shieldwallgen, + /obj/machinery/shieldwallgen, + /obj/machinery/shieldwallgen) + cost = 20 + containertype = /obj/structure/closet/crate/secure + containername = "shield generators crate" + access = access_teleporter + + +/datum/supply_packs/science/transfer_valves + name = "Tank Transfer Valves" + contains = list(/obj/item/device/transfer_valve, + /obj/item/device/transfer_valve) + cost = 60 + containertype = /obj/structure/closet/crate/secure + containername = "transfer valves crate" + access = access_rd + +////////////////////////////////////////////////////////////////////////////// +//////////////////////////// Organic ///////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +/datum/supply_packs/organic + name = "HEADER" + group = supply_organic + containertype = /obj/structure/closet/crate/freezer + + +/datum/supply_packs/organic/food + name = "Food Crate" + contains = list(/obj/item/weapon/reagent_containers/food/condiment/flour, + /obj/item/weapon/reagent_containers/food/condiment/rice, + /obj/item/weapon/reagent_containers/food/condiment/milk, + /obj/item/weapon/reagent_containers/food/condiment/soymilk, + /obj/item/weapon/reagent_containers/food/condiment/saltshaker, + /obj/item/weapon/reagent_containers/food/condiment/peppermill, + /obj/item/weapon/storage/fancy/egg_box, + /obj/item/weapon/reagent_containers/food/condiment/enzyme, + /obj/item/weapon/reagent_containers/food/condiment/sugar, + /obj/item/weapon/reagent_containers/food/snacks/meat/slab/monkey, + /obj/item/weapon/reagent_containers/food/snacks/grown/banana, + /obj/item/weapon/reagent_containers/food/snacks/grown/banana, + /obj/item/weapon/reagent_containers/food/snacks/grown/banana) + cost = 10 + containername = "food crate" + +/datum/supply_packs/organic/pizza + name = "Pizza Crate" + contains = list(/obj/item/pizzabox/margherita, + /obj/item/pizzabox/mushroom, + /obj/item/pizzabox/meat, + /obj/item/pizzabox/vegetable) + cost = 60 + containername = "Pizza crate" + +/datum/supply_packs/organic/monkey + name = "Monkey Crate" + contains = list (/obj/item/weapon/storage/box/monkeycubes) + cost = 20 + containername = "monkey crate" + +/datum/supply_packs/organic/party + name = "Party equipment" + contains = list(/obj/item/weapon/storage/box/drinkingglasses, + /obj/item/weapon/reagent_containers/food/drinks/shaker, + /obj/item/weapon/reagent_containers/food/drinks/bottle/patron, + /obj/item/weapon/reagent_containers/food/drinks/bottle/goldschlager, + /obj/item/weapon/reagent_containers/food/drinks/ale, + /obj/item/weapon/reagent_containers/food/drinks/ale, + /obj/item/weapon/reagent_containers/food/drinks/beer, + /obj/item/weapon/reagent_containers/food/drinks/beer, + /obj/item/weapon/reagent_containers/food/drinks/beer, + /obj/item/weapon/reagent_containers/food/drinks/beer) + cost = 20 + containername = "party equipment" + +//////// livestock +/datum/supply_packs/organic/cow + name = "Cow Crate" + cost = 30 + containertype = /obj/structure/closet/critter/cow + containername = "cow crate" + +/datum/supply_packs/organic/goat + name = "Goat Crate" + cost = 25 + containertype = /obj/structure/closet/critter/goat + containername = "goat crate" + +/datum/supply_packs/organic/chicken + name = "Chicken Crate" + cost = 20 + containertype = /obj/structure/closet/critter/chick + containername = "chicken crate" + +/datum/supply_packs/organic/corgi + name = "Corgi Crate" + cost = 50 + containertype = /obj/structure/closet/critter/corgi + contains = list(/obj/item/clothing/tie/petcollar) + containername = "corgi crate" + +/datum/supply_packs/organic/cat + name = "Cat Crate" + cost = 50 //Cats are worth as much as corgis. + containertype = /obj/structure/closet/critter/cat + contains = list(/obj/item/clothing/tie/petcollar, + /obj/item/toy/cattoy) + containername = "cat crate" + +/datum/supply_packs/organic/pug + name = "Pug Crate" + cost = 50 + containertype = /obj/structure/closet/critter/pug + contains = list(/obj/item/clothing/tie/petcollar) + containername = "pug crate" + +/datum/supply_packs/organic/fox + name = "Fox Crate" + cost = 55 //Foxes are cool. + containertype = /obj/structure/closet/critter/fox + contains = list(/obj/item/clothing/tie/petcollar) + containername = "fox crate" + +/datum/supply_packs/organic/butterfly + name = "Butterflies Crate" + cost = 50 + containertype = /obj/structure/closet/critter/butterfly + containername = "butterflies crate" + contraband = 1 + +////// hippy gear + +/datum/supply_packs/organic/hydroponics // -- Skie + name = "Hydroponics Supply Crate" + contains = list(/obj/item/weapon/reagent_containers/spray/plantbgone, + /obj/item/weapon/reagent_containers/spray/plantbgone, + /obj/item/weapon/reagent_containers/glass/bottle/ammonia, + /obj/item/weapon/reagent_containers/glass/bottle/ammonia, + /obj/item/weapon/hatchet, + /obj/item/weapon/cultivator, + /obj/item/device/analyzer/plant_analyzer, + /obj/item/clothing/gloves/botanic_leather, + /obj/item/clothing/suit/apron) // Updated with new things + cost = 15 + containertype = /obj/structure/closet/crate/hydroponics + containername = "hydroponics crate" + +/datum/supply_packs/misc/hydroponics/hydrotank + name = "Hydroponics Watertank Backpack Crate" + contains = list(/obj/item/weapon/watertank) + cost = 10 + containertype = /obj/structure/closet/crate/secure + containername = "hydroponics watertank crate" + access = access_hydroponics + +/datum/supply_packs/organic/hydroponics/seeds + name = "Seeds Crate" + contains = list(/obj/item/seeds/chiliseed, + /obj/item/seeds/berryseed, + /obj/item/seeds/cornseed, + /obj/item/seeds/eggplantseed, + /obj/item/seeds/tomatoseed, + /obj/item/seeds/soyaseed, + /obj/item/seeds/wheatseed, + /obj/item/seeds/carrotseed, + /obj/item/seeds/sunflowerseed, + /obj/item/seeds/chantermycelium, + /obj/item/seeds/potatoseed, + /obj/item/seeds/sugarcaneseed) + cost = 10 + containername = "seeds crate" + +/datum/supply_packs/organic/hydroponics/exoticseeds + name = "Exotic Seeds Crate" + contains = list(/obj/item/seeds/nettleseed, + /obj/item/seeds/replicapod, + /obj/item/seeds/replicapod, + /obj/item/seeds/replicapod, + /obj/item/seeds/plumpmycelium, + /obj/item/seeds/libertymycelium, + /obj/item/seeds/amanitamycelium, + /obj/item/seeds/reishimycelium, + /obj/item/seeds/bananaseed, + /obj/item/seeds/eggyseed) + cost = 15 + containername = "exotic seeds crate" + +/datum/supply_packs/organic/vending + name = "Bartending Supply Crate" + contains = list(/obj/item/weapon/vending_refill/boozeomat, + /obj/item/weapon/vending_refill/boozeomat, + /obj/item/weapon/vending_refill/boozeomat, + /obj/item/weapon/vending_refill/coffee, + /obj/item/weapon/vending_refill/coffee, + /obj/item/weapon/vending_refill/coffee) + cost = 20 + containername = "bartending supply crate" + +/datum/supply_packs/organic/vending/snack + name = "Snack Supply Crate" + contains = list(/obj/item/weapon/vending_refill/snack, + /obj/item/weapon/vending_refill/snack, + /obj/item/weapon/vending_refill/snack) + cost = 15 + containername = "snacks supply crate" + +/datum/supply_packs/organic/vending/cola + name = "Softdrinks Supply Crate" + contains = list(/obj/item/weapon/vending_refill/cola, + /obj/item/weapon/vending_refill/cola, + /obj/item/weapon/vending_refill/cola) + cost = 15 + containername = "softdrinks supply crate" + +/datum/supply_packs/organic/vending/cigarette + name = "Cigarette Supply Crate" + contains = list(/obj/item/weapon/vending_refill/cigarette, + /obj/item/weapon/vending_refill/cigarette, + /obj/item/weapon/vending_refill/cigarette) + cost = 15 + containername = "cigarette supply crate" + +////////////////////////////////////////////////////////////////////////////// +//////////////////////////// Materials /////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +/datum/supply_packs/materials + name = "HEADER" + group = supply_materials + + +/datum/supply_packs/materials/metal50 + name = "50 Metal Sheets" + contains = list(/obj/item/stack/sheet/metal) + amount = 50 + cost = 10 + containername = "metal sheets crate" + +/datum/supply_packs/materials/plasteel20 + name = "20 Plasteel Sheets" + contains = list(/obj/item/stack/sheet/plasteel) + amount = 20 + cost = 30 + containername = "plasteel sheets crate" + +/datum/supply_packs/materials/plasteel50 + name = "50 Plasteel Sheets" + contains = list(/obj/item/stack/sheet/plasteel) + amount = 50 + cost = 50 + containername = "plasteel sheets crate" + +/datum/supply_packs/materials/glass50 + name = "50 Glass Sheets" + contains = list(/obj/item/stack/sheet/glass) + amount = 50 + cost = 10 + containername = "glass sheets crate" + +/datum/supply_packs/materials/cardboard50 + name = "50 Cardboard Sheets" + contains = list(/obj/item/stack/sheet/cardboard) + amount = 50 + cost = 10 + containername = "cardboard sheets crate" + +/datum/supply_packs/materials/sandstone30 + name = "30 Sandstone Blocks" + contains = list(/obj/item/stack/sheet/mineral/sandstone) + amount = 30 + cost = 20 + containername = "sandstone blocks crate" + + +////////////////////////////////////////////////////////////////////////////// +//////////////////////////// Miscellaneous /////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +/datum/supply_packs/misc + name = "HEADER" + group = supply_misc + +/datum/supply_packs/misc/mule + name = "MULEbot Crate" + contains = list(/mob/living/simple_animal/bot/mulebot) + cost = 20 + containertype = /obj/structure/largecrate/mule + containername = "\improper MULEbot Crate" + +/datum/supply_packs/misc/conveyor + name = "Conveyor Assembly Crate" + contains = list(/obj/item/conveyor_construct, + /obj/item/conveyor_construct, + /obj/item/conveyor_construct, + /obj/item/conveyor_construct, + /obj/item/conveyor_construct, + /obj/item/conveyor_construct, + /obj/item/conveyor_switch_construct, + /obj/item/weapon/paper/conveyor) + cost = 15 + containername = "conveyor assembly crate" + +/datum/supply_packs/misc/watertank + name = "Water Tank Crate" + contains = list(/obj/structure/reagent_dispensers/watertank) + cost = 8 + containertype = /obj/structure/largecrate + containername = "water tank crate" + +/datum/supply_packs/misc/lasertag + name = "Laser Tag Crate" + contains = list(/obj/item/weapon/gun/energy/laser/redtag, + /obj/item/weapon/gun/energy/laser/redtag, + /obj/item/weapon/gun/energy/laser/redtag, + /obj/item/weapon/gun/energy/laser/bluetag, + /obj/item/weapon/gun/energy/laser/bluetag, + /obj/item/weapon/gun/energy/laser/bluetag, + /obj/item/clothing/suit/redtag, + /obj/item/clothing/suit/redtag, + /obj/item/clothing/suit/redtag, + /obj/item/clothing/suit/bluetag, + /obj/item/clothing/suit/bluetag, + /obj/item/clothing/suit/bluetag, + /obj/item/clothing/head/helmet/redtaghelm, + /obj/item/clothing/head/helmet/bluetaghelm) + cost = 15 + containername = "laser tag crate" + +/datum/supply_packs/misc/religious_supplies + name = "Religious Supplies Crate" + contains = list(/obj/item/weapon/reagent_containers/food/drinks/bottle/holywater, + /obj/item/weapon/reagent_containers/food/drinks/bottle/holywater, + /obj/item/weapon/storage/book/bible/booze, + /obj/item/weapon/storage/book/bible/booze, + /obj/item/clothing/suit/hooded/chaplain_hoodie, + /obj/item/clothing/suit/hooded/chaplain_hoodie) + cost = 40 // it costs so much because the Space Church is ran by Space Jews + containername = "religious supplies crate" + +/datum/supply_packs/misc/posters + name = "Corporate Posters Crate" + contains = list(/obj/item/weapon/poster/legit, + /obj/item/weapon/poster/legit, + /obj/item/weapon/poster/legit, + /obj/item/weapon/poster/legit, + /obj/item/weapon/poster/legit) + cost = 8 + containername = "Corporate Posters Crate" + + +///////////// Paper Work + +/datum/supply_packs/misc/paper + name = "Bureaucracy Crate" + contains = list(/obj/structure/filingcabinet/chestdrawer/wheeled, + /obj/item/device/camera_film, + /obj/item/weapon/hand_labeler, + /obj/item/hand_labeler_refill, + /obj/item/hand_labeler_refill, + /obj/item/weapon/paper_bin, + /obj/item/weapon/pen/fourcolor, + /obj/item/weapon/pen/fourcolor, + /obj/item/weapon/pen, + /obj/item/weapon/pen/blue, + /obj/item/weapon/pen/red, + /obj/item/weapon/folder/blue, + /obj/item/weapon/folder/red, + /obj/item/weapon/folder/yellow, + /obj/item/weapon/clipboard, + /obj/item/weapon/clipboard) + cost = 15 + containername = "bureaucracy crate" + +/datum/supply_packs/misc/toner + name = "Toner Cartridges crate" + contains = list(/obj/item/device/toner, + /obj/item/device/toner, + /obj/item/device/toner, + /obj/item/device/toner, + /obj/item/device/toner, + /obj/item/device/toner) + cost = 10 + containername = "toner cartridges crate" + + +///////////// Janitor Supplies + +/datum/supply_packs/misc/janitor + name = "Janitorial Supplies Crate" + contains = list(/obj/item/weapon/reagent_containers/glass/bucket, + /obj/item/weapon/reagent_containers/glass/bucket, + /obj/item/weapon/reagent_containers/glass/bucket, + /obj/item/weapon/mop, + /obj/item/weapon/caution, + /obj/item/weapon/caution, + /obj/item/weapon/caution, + /obj/item/weapon/storage/bag/trash, + /obj/item/weapon/reagent_containers/spray/cleaner, + /obj/item/weapon/reagent_containers/glass/rag, + /obj/item/weapon/grenade/chem_grenade/cleaner, + /obj/item/weapon/grenade/chem_grenade/cleaner, + /obj/item/weapon/grenade/chem_grenade/cleaner) + cost = 10 + containername = "janitorial supplies crate" + +/datum/supply_packs/misc/janitor/janicart + name = "Janitorial Cart and Galoshes Crate" + contains = list(/obj/structure/janitorialcart, + /obj/item/clothing/shoes/galoshes) + cost = 10 + containertype = /obj/structure/largecrate + containername = "janitorial cart crate" + +/datum/supply_packs/misc/janitor/janitank + name = "Janitor Watertank Backpack" + contains = list(/obj/item/weapon/watertank/janitor) + cost = 10 + containertype = /obj/structure/closet/crate/secure + containername = "janitor watertank crate" + access = access_janitor + +/datum/supply_packs/misc/janitor/lightbulbs + name = "Replacement Lights" + contains = list(/obj/item/weapon/storage/box/lights/mixed, + /obj/item/weapon/storage/box/lights/mixed, + /obj/item/weapon/storage/box/lights/mixed) + cost = 10 + containername = "replacement lights" + +/datum/supply_packs/misc/noslipfloor + name = "High-traction Floor Tiles" + contains = list(/obj/item/stack/tile/noslip) + amount = 20 + cost = 20 + containername = "high-traction floor tiles" + +/datum/supply_packs/misc/plasmaman + name = "Plasma-man Supply Kit" + contains = list(/obj/item/clothing/under/plasmaman, + /obj/item/clothing/under/plasmaman, + /obj/item/weapon/tank/internals/plasmaman/belt/full, + /obj/item/weapon/tank/internals/plasmaman/belt/full, + /obj/item/clothing/head/helmet/space/plasmaman, + /obj/item/clothing/head/helmet/space/plasmaman) + cost = 20 + containername = "plasma-man supply kit" + +///////////// Costumes + +/datum/supply_packs/misc/costume + name = "Standard Costume Crate" + contains = list(/obj/item/weapon/storage/backpack/clown, + /obj/item/clothing/shoes/clown_shoes, + /obj/item/clothing/mask/gas/clown_hat, + /obj/item/clothing/under/rank/clown, + /obj/item/weapon/bikehorn, + /obj/item/clothing/under/rank/mime, + /obj/item/clothing/shoes/sneakers/black, + /obj/item/clothing/gloves/color/white, + /obj/item/clothing/mask/gas/mime, + /obj/item/clothing/head/beret, + /obj/item/clothing/suit/suspenders, + /obj/item/weapon/reagent_containers/food/drinks/bottle/bottleofnothing, + /obj/item/weapon/storage/backpack/mime) + cost = 10 + containertype = /obj/structure/closet/crate/secure + containername = "standard costumes" + access = access_theatre + +/datum/supply_packs/misc/wizard + name = "Wizard Costume Crate" + contains = list(/obj/item/weapon/staff, + /obj/item/clothing/suit/wizrobe/fake, + /obj/item/clothing/shoes/sandal, + /obj/item/clothing/head/wizard/fake) + cost = 20 + containername = "wizard costume crate" + +/datum/supply_packs/misc/randomised + var/num_contained = 3 //number of items picked to be contained in a randomised crate + contains = list(/obj/item/clothing/head/collectable/chef, + /obj/item/clothing/head/collectable/paper, + /obj/item/clothing/head/collectable/tophat, + /obj/item/clothing/head/collectable/captain, + /obj/item/clothing/head/collectable/beret, + /obj/item/clothing/head/collectable/welding, + /obj/item/clothing/head/collectable/flatcap, + /obj/item/clothing/head/collectable/pirate, + /obj/item/clothing/head/collectable/kitty, + /obj/item/clothing/head/collectable/rabbitears, + /obj/item/clothing/head/collectable/wizard, + /obj/item/clothing/head/collectable/hardhat, + /obj/item/clothing/head/collectable/HoS, + /obj/item/clothing/head/collectable/thunderdome, + /obj/item/clothing/head/collectable/swat, + /obj/item/clothing/head/collectable/slime, + /obj/item/clothing/head/collectable/police, + /obj/item/clothing/head/collectable/slime, + /obj/item/clothing/head/collectable/xenom, + /obj/item/clothing/head/collectable/petehat) + name = "Collectable hat crate!" + cost = 200 + containername = "collectable hats crate! Brought to you by Bass.inc!" + +/datum/supply_packs/misc/randomised/New() + manifest += "Contains any [num_contained] of:" + ..() + + +/datum/supply_packs/misc/randomised/contraband + num_contained = 5 + contains = list(/obj/item/weapon/poster/contraband, + /obj/item/weapon/storage/fancy/cigarettes/dromedaryco, + /obj/item/weapon/storage/fancy/cigarettes/cigpack_shadyjims) + name = "Contraband Crate" + cost = 30 + containername = "crate" //let's keep it subtle, eh? + contraband = 1 + +/datum/supply_packs/misc/randomised/toys + name = "Toy Crate" + num_contained = 5 + contains = list(/obj/item/toy/spinningtoy, + /obj/item/toy/sword, + /obj/item/toy/foamblade, + /obj/item/toy/AI, + /obj/item/toy/owl, + /obj/item/toy/griffin, + /obj/item/toy/nuke, + /obj/item/toy/minimeteor, + /obj/item/toy/carpplushie, + /obj/item/weapon/coin/antagtoken, + /obj/item/stack/tile/fakespace, + /obj/item/weapon/gun/projectile/shotgun/toy/crossbow, + /obj/item/toy/redbutton) + + cost = 50 // or play the arcade machines ya lazy bum + containername ="toy crate" + +/datum/supply_packs/misc/autodrobe + name = "Autodrobe Supply Crate" + contains = list(/obj/item/weapon/vending_refill/autodrobe, + /obj/item/weapon/vending_refill/autodrobe) + cost = 15 + containername = "autodrobe supply crate" + +/datum/supply_packs/misc/formalwear //This is a very classy crate. + name = "Formal-wear Crate" + contains = list(/obj/item/clothing/under/blacktango, + /obj/item/clothing/under/assistantformal, + /obj/item/clothing/under/assistantformal, + /obj/item/clothing/under/lawyer/bluesuit, + /obj/item/clothing/suit/toggle/lawyer, + /obj/item/clothing/under/lawyer/purpsuit, + /obj/item/clothing/suit/toggle/lawyer/purple, + /obj/item/clothing/under/lawyer/blacksuit, + /obj/item/clothing/suit/toggle/lawyer/black, + /obj/item/clothing/tie/waistcoat, + /obj/item/clothing/tie/blue, + /obj/item/clothing/tie/red, + /obj/item/clothing/tie/black, + /obj/item/clothing/head/bowler, + /obj/item/clothing/head/fedora, + /obj/item/clothing/head/flatcap, + /obj/item/clothing/head/beret, + /obj/item/clothing/head/that, + /obj/item/clothing/shoes/laceup, + /obj/item/clothing/shoes/laceup, + /obj/item/clothing/shoes/laceup, + /obj/item/clothing/under/suit_jacket/charcoal, + /obj/item/clothing/under/suit_jacket/navy, + /obj/item/clothing/under/suit_jacket/burgundy, + /obj/item/clothing/under/suit_jacket/checkered, + /obj/item/clothing/under/suit_jacket/tan, + /obj/item/weapon/lipstick/random) + cost = 30 //Lots of very expensive items. You gotta pay up to look good! + containername = "formal-wear crate" + +/datum/supply_packs/misc/foamforce + name = "Foam Force Crate" + contains = list(/obj/item/weapon/gun/projectile/shotgun/toy, + /obj/item/weapon/gun/projectile/shotgun/toy, + /obj/item/weapon/gun/projectile/shotgun/toy, + /obj/item/weapon/gun/projectile/shotgun/toy, + /obj/item/weapon/gun/projectile/shotgun/toy, + /obj/item/weapon/gun/projectile/shotgun/toy, + /obj/item/weapon/gun/projectile/shotgun/toy, + /obj/item/weapon/gun/projectile/shotgun/toy) + cost = 10 + containername = "foam force crate" + +/datum/supply_packs/misc/foamforce/bonus + name = "Foam Force Pistols Crate" + contains = list(/obj/item/weapon/gun/projectile/automatic/toy/pistol, + /obj/item/weapon/gun/projectile/automatic/toy/pistol, + /obj/item/ammo_box/magazine/toy/pistol, + /obj/item/ammo_box/magazine/toy/pistol) + cost = 40 + containername = "foam force pistols crate" + contraband = 1 + diff --git a/code/datums/uplink_item.dm b/code/datums/uplink_item.dm index 383f05b5841..6c9902c8530 100644 --- a/code/datums/uplink_item.dm +++ b/code/datums/uplink_item.dm @@ -1,921 +1,921 @@ -var/list/uplink_items = list() - -/proc/get_uplink_items(var/gamemode_override=null) - // If not already initialized.. - if(!uplink_items.len) - - // Fill in the list and order it like this: - // A keyed list, acting as categories, which are lists to the datum. - - var/list/last = list() - for(var/item in subtypesof(/datum/uplink_item)) - - var/datum/uplink_item/I = new item() - if(!I.item) - continue - if(I.last) - last += I - continue - - if(!uplink_items[I.category]) - uplink_items[I.category] = list() - - uplink_items[I.category] += I - - for(var/datum/uplink_item/I in last) - - if(!uplink_items[I.category]) - uplink_items[I.category] = list() - - uplink_items[I.category] += I - - //Filtered version - var/list/filtered_uplink_items = list() - - for(var/category in uplink_items) - for(var/datum/uplink_item/I in uplink_items[category]) - if(I.gamemodes.len) - if(!gamemode_override && ticker && !(ticker.mode.type in I.gamemodes)) - continue - if(gamemode_override && !(gamemode_override in I.gamemodes)) - continue - if(I.excludefrom.len) - if(!gamemode_override && ticker && (ticker.mode.type in I.excludefrom)) - continue - if(gamemode_override && (gamemode_override in I.excludefrom)) - continue - if(!filtered_uplink_items[I.category]) - filtered_uplink_items[I.category] = list() - filtered_uplink_items[category] += I - - return filtered_uplink_items - -// You can change the order of the list by putting datums before/after one another OR -// you can use the last variable to make sure it appears last, well have the category appear last. - -/datum/uplink_item - var/name = "item name" - var/category = "item category" - var/desc = "item description" - var/item = null - var/cost = 0 - var/last = 0 // Appear last - var/list/gamemodes = list() // Empty list means it is in all the gamemodes. Otherwise place the gamemode name here. - var/list/excludefrom = list() //Empty list does nothing. Place the name of gamemode you don't want this item to be available in here. This is so you dont have to list EVERY mode to exclude something. - var/surplus = 100 //Chance of being included in the surplus crate (when pick() selects it) - -/datum/uplink_item/proc/spawn_item(turf/loc, obj/item/device/uplink/U) - if(item) - U.uses -= max(cost, 0) - U.used_TC += cost - feedback_add_details("traitor_uplink_items_bought", "[item]") - return new item(loc) - -/datum/uplink_item/proc/buy(obj/item/device/uplink/U, mob/user) - - ..() - if(!istype(U)) - return 0 - - if (!user || user.incapacitated()) - return 0 - - // If the uplink's holder is in the user's contents - if ((U.loc in user.contents || (in_range(U.loc, user) && istype(U.loc.loc, /turf)))) - user.set_machine(U) - if(cost > U.uses) - return 0 - - var/obj/I = spawn_item(get_turf(user), U) - - if(istype(I, /obj/item)) - if(ishuman(user)) - var/mob/living/carbon/human/A = user - A.put_in_any_hand_if_possible(I) - - if(istype(I,/obj/item/weapon/storage/box/) && I.contents.len>0) - for(var/atom/o in I) - U.purchase_log += "\icon[o]" - else - U.purchase_log += "\icon[I]" - - U.interact(user) - return 1 - return 0 - -/* -// -// UPLINK ITEMS -// -*/ - -// DANGEROUS WEAPONS - -/datum/uplink_item/dangerous - category = "Conspicuous and Dangerous Weapons" - -/datum/uplink_item/dangerous/pistol - name = "FK-69 Pistol" - desc = "A small, easily concealable handgun that uses 10mm auto rounds in 8-round magazines and is compatible with suppressors." - item = /obj/item/weapon/gun/projectile/automatic/pistol - cost = 9 - -/datum/uplink_item/dangerous/revolver - name = "Syndicate Revolver" - desc = "A brutally simple syndicate revolver that fires .357 Magnum cartridges and has 7 chambers." - item = /obj/item/weapon/gun/projectile/revolver - cost = 13 - surplus = 50 - -/datum/uplink_item/dangerous/smg - name = "C-20r Submachine Gun" - desc = "A fully-loaded Scarborough Arms bullpup submachine gun that fires .45 rounds with a 20-round magazine and is compatible with suppressors." - item = /obj/item/weapon/gun/projectile/automatic/c20r - cost = 14 - gamemodes = list(/datum/game_mode/nuclear) - surplus = 40 - -/datum/uplink_item/dangerous/smg/unrestricted - item = /obj/item/weapon/gun/projectile/automatic/c20r/unrestricted - gamemodes = list(/datum/game_mode/gang) - -/datum/uplink_item/dangerous/carbine - name = "M-90gl Carbine" - desc = "A fully-loaded three-round burst carbine that uses 30-round 5.56mm magazines with a togglable underslung 40mm grenade launcher." - item = /obj/item/weapon/gun/projectile/automatic/m90 - cost = 18 - gamemodes = list(/datum/game_mode/nuclear) - surplus = 50 - -/datum/uplink_item/dangerous/carbine/unrestricted - item = /obj/item/weapon/gun/projectile/automatic/m90/unrestricted - gamemodes = list(/datum/game_mode/gang) - -/datum/uplink_item/dangerous/machinegun - name = "L6 Squad Automatic Weapon" - desc = "A fully-loaded Aussec Armoury belt-fed machine gun. This deadly weapon has a massive 50-round magazine of devastating 7.62x51mm ammunition." - item = /obj/item/weapon/gun/projectile/automatic/l6_saw - cost = 40 - gamemodes = list(/datum/game_mode/nuclear) - surplus = 0 - -/datum/uplink_item/dangerous/crossbow - name = "Miniature Energy Crossbow" - desc = "A short bow mounted across a tiller in miniature. Small enough to fit into a pocket or slip into a bag unnoticed. It will synthesize and fire bolts tipped with a paralyzing toxin that will \ - briefly stun targets and cause them to slur as if inebriated. It can produce an infinite amount of bolts, but must be manually recharged with each shot." - item = /obj/item/weapon/gun/energy/kinetic_accelerator/crossbow - cost = 12 - excludefrom = list(/datum/game_mode/nuclear,/datum/game_mode/gang) - surplus = 50 - -/datum/uplink_item/dangerous/flamethrower - name = "Flamethrower" - desc = "A flamethrower, fueled by a portion of highly flammable biotoxins stolen previously from Nanotrasen stations. Make a statement by roasting the filth in their own greed. Use with caution." - item = /obj/item/weapon/flamethrower/full/tank - cost = 4 - gamemodes = list(/datum/game_mode/nuclear,/datum/game_mode/gang) - surplus = 40 - -/datum/uplink_item/dangerous/sword - name = "Energy Sword" - desc = "The energy sword is an edged weapon with a blade of pure energy. The sword is small enough to be pocketed when inactive. Activating it produces a loud, distinctive noise. One can combine two \ - energy swords to create a double energy sword, which must be wielded in two hands but is more robust and deflects all energy projectiles." - item = /obj/item/weapon/melee/energy/sword/saber - cost = 8 - -/datum/uplink_item/dangerous/emp - name = "EMP Kit" - desc = "A box that contains two EMP grenades, an EMP implant and a short ranged recharging device disguised as a flashlight. Useful to disrupt communication and silicon lifeforms." - item = /obj/item/weapon/storage/box/syndie_kit/emp - cost = 5 - -/datum/uplink_item/dangerous/syndicate_minibomb - name = "Syndicate Minibomb" - desc = "The minibomb is a grenade with a five-second fuse. Upon detonation, it will create a small hull breach in addition to dealing high amounts of damage to nearby personnel." - item = /obj/item/weapon/grenade/syndieminibomb - cost = 6 - -/datum/uplink_item/dangerous/foamsmg - name = "Toy Submachine Gun" - desc = "A fully-loaded Donksoft bullpup submachine gun that fires riot grade rounds with a 20-round magazine." - item = /obj/item/weapon/gun/projectile/automatic/c20r/toy - cost = 8 - gamemodes = list(/datum/game_mode/nuclear) - surplus = 0 - -/datum/uplink_item/dangerous/foammachinegun - name = "Toy Machine Gun" - desc = "A fully-loaded Donksoft belt-fed machine gun. This weapon has a massive 50-round magazine of devastating riot grade darts, that can briefly incapacitate someone in just one volley." - item = /obj/item/weapon/gun/projectile/automatic/l6_saw/toy - cost = 12 - gamemodes = list(/datum/game_mode/nuclear) - surplus = 0 - -/datum/uplink_item/dangerous/viscerators - name = "Viscerator Delivery Grenade" - desc = "A unique grenade that deploys a swarm of viscerators upon activation, which will chase down and shred any non-operatives in the area." - item = /obj/item/weapon/grenade/spawnergrenade/manhacks - cost = 8 - gamemodes = list(/datum/game_mode/nuclear) - surplus = 35 - -/datum/uplink_item/dangerous/bioterror - name = "Biohazardous Chemical Sprayer" - desc = "A chemical sprayer that allows a wide dispersal of selected chemicals. Especially tailored by the Tiger Cooperative, the deadly blend it comes stocked with will disorient, damage, and disable your foes... \ - Use with extreme caution, to prevent exposure to yourself and your fellow operatives." - item = /obj/item/weapon/reagent_containers/spray/chemsprayer/bioterror - cost = 20 - gamemodes = list(/datum/game_mode/nuclear,/datum/game_mode/gang) - surplus = 0 - -/datum/uplink_item/dangerous/gygax - name = "Gygax Exosuit" - desc = "A lightweight exosuit, painted in a dark scheme. Its speed and equipment selection make it excellent for hit-and-run style attacks. \ - This model lacks a method of space propulsion, and therefore it is advised to repair the mothership's teleporter if you wish to make use of it." - item = /obj/mecha/combat/gygax/dark/loaded - cost = 90 - gamemodes = list(/datum/game_mode/nuclear) - surplus = 0 - -/datum/uplink_item/dangerous/mauler - name = "Mauler Exosuit" - desc = "A massive and incredibly deadly Syndicate exosuit. Features long-range targetting, thrust vectoring, and deployable smoke." - item = /obj/mecha/combat/marauder/mauler/loaded - cost = 140 - gamemodes = list(/datum/game_mode/nuclear) - surplus = 0 - -/datum/uplink_item/dangerous/syndieborg - name = "Syndicate Cyborg" - desc = "A cyborg designed and programmed for systematic extermination of non-Syndicate personnel." - item = /obj/item/weapon/antag_spawner/borg_tele - cost = 50 - gamemodes = list(/datum/game_mode/nuclear) - surplus = 0 - -//for refunding the syndieborg teleporter -/datum/uplink_item/dangerous/syndieborg/spawn_item() - var/obj/item/weapon/antag_spawner/borg_tele/T = ..() - if(istype(T)) - T.TC_cost = cost - -/datum/uplink_item/dangerous/guardian - name = "Holoparasites" - desc = "Though capable of near sorcerous feats via use of hardlight holograms and nanomachines, they require an organic host as a home base and source of fuel." - item = /obj/item/weapon/storage/box/syndie_kit/guardian - excludefrom = list(/datum/game_mode/nuclear,/datum/game_mode/gang) - cost = 12 - -/datum/uplink_item/dangerous/sniper - name = "Sniper Rifle" - desc = "Ranged fury, syndicate style. guaranteed to cause shock and awe or your TC back!" - item = /obj/item/weapon/gun/projectile/sniper_rifle/syndicate - cost = 16 - gamemodes = list(/datum/game_mode/nuclear) - surplus = 25 - - -// AMMUNITION - -/datum/uplink_item/ammo - category = "Ammunition" - surplus = 40 - -/datum/uplink_item/ammo/pistol - name = "Handgun Magazine - 10mm" - desc = "An additional 8-round 10mm magazine for use in the syndicate pistol. These subsonic rounds are dirt cheap but are half as effective as .357 rounds." - item = /obj/item/ammo_box/magazine/m10mm - cost = 1 - -/datum/uplink_item/ammo/revolver - name = "Speed Loader - .357" - desc = "A speed loader that contains seven additional .357 Magnum rounds for the syndicate revolver. For when you really need a lot of things dead." - item = /obj/item/ammo_box/a357 - cost = 4 - -/datum/uplink_item/ammo/smg - name = "SMG Magazine - .45" - desc = "An additional 20-round .45 magazine for use in the C-20r submachine gun. These bullets pack a lot of punch that can knock most targets down, but do limited overall damage." - item = /obj/item/ammo_box/magazine/smgm45 - cost = 2 - gamemodes = list(/datum/game_mode/nuclear,/datum/game_mode/gang) - -/datum/uplink_item/ammo/ammobag - name = "Ammo Duffelbag - Shotgun Ammo Grab Bag" - desc = "A duffelbag filled with Bulldog ammo to kit out an entire team, at a discounted price." - item = /obj/item/weapon/storage/backpack/dufflebag/syndie/ammo/loaded - cost = 10 //bulk buyer's discount. Very useful if you're buying a mech and dont have TC left to buy people non-shotgun guns - gamemodes = list(/datum/game_mode/nuclear) - -/datum/uplink_item/ammo/bullslug - name = "Drum Magazine - 12g Slugs" - desc = "An additional 8-round slug magazine for use in the Bulldog shotgun. Now 8 times less likely to shoot your pals." - item = /obj/item/ammo_box/magazine/m12g - cost = 2 - gamemodes = list(/datum/game_mode/nuclear) - -/datum/uplink_item/ammo/bullbuck - name = "Drum Magazine - 12g Buckshot" - desc = "An additional 8-round buckshot magazine for use in the Bulldog shotgun. Front towards enemy." - item = /obj/item/ammo_box/magazine/m12g/buckshot - cost = 2 - gamemodes = list(/datum/game_mode/nuclear) - -/datum/uplink_item/ammo/bullstun - name = "Drum Magazine - 12g Stun Slug" - desc = "An alternative 8-round stun slug magazine for use in the Bulldog shotgun. Saying that they're completely non-lethal would be lying." - item = /obj/item/ammo_box/magazine/m12g/stun - cost = 3 - gamemodes = list(/datum/game_mode/nuclear) - -/datum/uplink_item/ammo/bulldragon - name = "Drum Magazine - 12g Dragon's Breath" - desc = "An alternative 8-round dragon's breath magazine for use in the Bulldog shotgun. I'm a fire starter, twisted fire starter!" - item = /obj/item/ammo_box/magazine/m12g/dragon - cost = 2 - gamemodes = list(/datum/game_mode/nuclear) - -/datum/uplink_item/ammo/bioterror - name = "Box of Bioterror Syringes" - desc = "A box full of preloaded syringes, containing various chemicals that seize up the victim's motor and broca systems, making it impossible for them to move or speak for some time." - item = /obj/item/weapon/storage/box/syndie_kit/bioterror - cost = 6 - gamemodes = list(/datum/game_mode/nuclear) - -/datum/uplink_item/ammo/carbine - name = "Toploader Magazine - 5.56" - desc = "An additional 30-round 5.56 magazine for use in the M-90gl carbine. These bullets don't have the punch to knock most targets down, but dish out higher overall damage." - item = /obj/item/ammo_box/magazine/m556 - cost = 2 - gamemodes = list(/datum/game_mode/nuclear,/datum/game_mode/gang) - -/datum/uplink_item/ammo/a40mm - name = "Ammo Box - 40mm grenades" - desc = "A box of 4 additional 40mm HE grenades for use the M-90gl's underbarrel grenade launcher. Your teammates will thank you to not shoot these down small hallways." - item = /obj/item/ammo_box/a40mm - cost = 4 - gamemodes = list(/datum/game_mode/nuclear) - -/datum/uplink_item/ammo/machinegun - name = "Box Magazine - 7.62x51mm" - desc = "A 50-round magazine of 7.62x51mm ammunition for use in the L6 SAW machinegun. By the time you need to use this, you'll already be on a pile of corpses." - item = /obj/item/ammo_box/magazine/m762 - cost = 12 - gamemodes = list(/datum/game_mode/nuclear) - surplus = 0 - -/datum/uplink_item/ammo/toydarts //This used to only be for nuke ops, but had the cost lowered and made available to traitors because >a box of foam darts is more expensive than four carbine magazines - name = "Box of Riot Darts" - desc = "A box of 40 Donksoft foam riot darts, for reloading any compatible foam dart gun. Don't forget to share!" - item = /obj/item/ammo_box/foambox/riot - cost = 2 - surplus = 0 - -/datum/uplink_item/ammo/sniper - name = "Sniper Magazine - .50" - desc = "An additional 6-round .50 magazine for use in the syndicate sniper rifle." - item = /obj/item/ammo_box/magazine/sniper_rounds - cost = 4 //70dmg rounds are no joke - gamemodes = list(/datum/game_mode/nuclear) - -/datum/uplink_item/ammo/sniper/soporific - name = "Sniper Magazine - Soporific Rounds" - desc = "A 3-round magazine of soporific ammo designed for use in the syndicate sniper rifle, put your enemies to sleep today!" - item = /obj/item/ammo_box/magazine/sniper_rounds/soporific - cost = 6 - -/datum/uplink_item/ammo/sniper/haemorrhage - name = "Sniper Magazine - Haemorrhage Rounds" - desc = "A 5-round magazine of haemorrhage ammo designed for use in the syndicate sniper rifle, causes heavy bleeding in the target." - item = /obj/item/ammo_box/magazine/sniper_rounds/haemorrhage - - - -// STEALTHY WEAPONS - -/datum/uplink_item/stealthy_weapons - category = "Stealthy and Inconspicuous Weapons" - - -/datum/uplink_item/stealthy_weapons/throwingstars - name = "Box of Throwing Stars" - desc = "A box of shurikens from ancient Earth martial arts. They are highly effective throwing weapons, and will embed into limbs when possible." - item = /obj/item/weapon/storage/box/throwing_stars - cost = 6 - -/datum/uplink_item/stealthy_weapons/edagger - name = "Energy Dagger" - desc = "A dagger made of energy that looks and functions as a pen when off." - item = /obj/item/weapon/pen/edagger - cost = 2 - -/datum/uplink_item/stealthy_weapons/foampistol - name = "Toy Gun with Riot Darts" - desc = "An innocent-looking toy pistol designed to fire foam darts. Comes loaded with riot-grade darts effective at incapacitating a target." - item = /obj/item/weapon/gun/projectile/automatic/toy/pistol/riot - cost = 3 - surplus = 10 - excludefrom = list(/datum/game_mode/gang) - -/datum/uplink_item/stealthy_weapons/sleepy_pen - name = "Sleepy Pen" - desc = "A syringe disguised as a functional pen, filled with a potent mix of drugs, including a strong anesthetic and a chemical that prevents the target from speaking. \ - The pen holds one dose of the mixture, and cannot be refilled. Note that before the target falls asleep, they will be able to move and act." - item = /obj/item/weapon/pen/sleepy - cost = 4 - excludefrom = list(/datum/game_mode/nuclear,/datum/game_mode/gang) - -/datum/uplink_item/stealthy_weapons/soap - name = "Syndicate Soap" - desc = "A sinister-looking surfactant used to clean blood stains to hide murders and prevent DNA analysis. You can also drop it underfoot to slip people." - item = /obj/item/weapon/soap/syndie - cost = 1 - surplus = 50 - -/datum/uplink_item/stealthy_weapons/traitor_chem_bottle - name = "Poison Kit" - desc = "An assortment of deadly chemicals packed into a compact box. Comes with a syringe for more precise application." - item = /obj/item/weapon/storage/box/syndie_kit/chemical - cost = 6 - surplus = 50 - -/datum/uplink_item/stealthy_weapons/dart_pistol - name = "Dart Pistol" - desc = "A miniaturized version of a normal syringe gun. It is very quiet when fired and can fit into any space a small item can." - item = /obj/item/weapon/gun/syringe/syndicate - cost = 4 - surplus = 50 //High chance of surplus due to poison kit also having a high chance - -/datum/uplink_item/stealthy_weapons/detomatix - name = "Detomatix PDA Cartridge" - desc = "When inserted into a personal digital assistant, this cartridge gives you four opportunities to detonate PDAs of crewmembers who have their message feature enabled. \ - The concussive effect from the explosion will knock the recipient out for a short period, and deafen them for longer. It has a chance to detonate your PDA." - item = /obj/item/weapon/cartridge/syndicate - cost = 6 - -/datum/uplink_item/stealthy_weapons/suppressor - name = "Universal Suppressor" - desc = "Fitted for use on any small caliber weapon with a threaded barrel, this suppressor will silence the shots of the weapon for increased stealth and superior ambushing capability." - item = /obj/item/weapon/suppressor - cost = 3 - surplus = 10 - -/datum/uplink_item/stealthy_weapons/pizza_bomb - name = "Pizza Bomb" - desc = "A pizza box with a bomb taped inside it. The timer needs to be set by opening the box; afterwards, opening the box again will trigger the detonation." - item = /obj/item/device/pizza_bomb - cost = 6 - surplus = 8 - -/datum/uplink_item/stealthy_weapons/dehy_carp - name = "Dehydrated Space Carp" - desc = "Looks like a plush toy carp, but just add water and it becomes a real-life space carp! Activate in your hand before use so it knows not to kill you." - item = /obj/item/toy/carpplushie/dehy_carp - cost = 1 - -/datum/uplink_item/stealthy_weapons/door_charge - name = "Explosive Airlock Charge" - desc = "A small, easily concealable device. It can be applied to an open airlock panel, and the next person to open that airlock will be knocked down in an explosion. The airlock's maintenance panel will also be destroyed by this." - item = /obj/item/device/doorCharge - cost = 2 - surplus = 10 - excludefrom = list(/datum/game_mode/nuclear) - -// STEALTHY TOOLS - -/datum/uplink_item/stealthy_tools - category = "Stealth and Camouflage Items" - -/datum/uplink_item/stealthy_tools/chameleon_jumpsuit - name = "Chameleon Jumpsuit" - desc = "A jumpsuit used to imitate the uniforms of Nanotrasen crewmembers. It can change form at any time to resemble another jumpsuit. May react unpredictably to electromagnetic disruptions." - item = /obj/item/clothing/under/chameleon - cost = 2 - -/datum/uplink_item/stealthy_tools/chameleon_stamp - name = "Chameleon Stamp" - desc = "A stamp that can be activated to imitate an official Nanotrasen Stamp™. The disguised stamp will work exactly like the real stamp and will allow you to forge false documents to gain access or equipment; \ - it can also be used in a washing machine to forge clothing." - item = /obj/item/weapon/stamp/chameleon - cost = 1 - surplus = 35 - -/datum/uplink_item/stealthy_tools/syndigaloshes - name = "No-Slip Brown Shoes" - desc = "These shoes will allow the wearer to run on wet floors and slippery objects without falling down. They do not work on heavily lubricated surfaces." - item = /obj/item/clothing/shoes/sneakers/syndigaloshes - cost = 2 - excludefrom = list(/datum/game_mode/nuclear) - -/datum/uplink_item/stealthy_tools/syndigaloshes/nuke - name = "Tactical No-Slip Brown Shoes" - desc = "These allow you to run on wet floors. They do not work on lubricated surfaces, but the manufacturer guarantees they're somehow better than the normal ones." - cost = 4 //but they aren't - gamemodes = list(/datum/game_mode/nuclear) - -/datum/uplink_item/stealthy_tools/agent_card - name = "Agent Identification Card" - desc = "Agent cards prevent artificial intelligences from tracking the wearer, and can copy access from other identification cards. The access is cumulative, so scanning one card does not erase the access gained from another. \ - In addition, they can be forged to display a new assignment and name. This can be done an unlimited amount of times. Some Syndicate areas can only be accessed with these cards." - item = /obj/item/weapon/card/id/syndicate - cost = 2 - -/datum/uplink_item/stealthy_tools/voice_changer - name = "Voice Changer" - item = /obj/item/clothing/mask/gas/voice - desc = "A conspicuous gas mask that mimics the voice named on your identification card. It can be toggled on and off." - cost = 3 - -/datum/uplink_item/stealthy_tools/chameleon_proj - name = "Chameleon Projector" - desc = "Projects an image across a user, disguising them as an object scanned with it, as long as they don't move the projector from their hand. Disguised users move slowly, and projectiles pass over them." - item = /obj/item/device/chameleon - cost = 7 - excludefrom = list(/datum/game_mode/gang) - -/datum/uplink_item/stealthy_tools/camera_bug - name = "Camera Bug" - desc = "Enables you to view all cameras on the network and track a target. Bugging cameras allows you to disable them remotely." - item = /obj/item/device/camera_bug - cost = 1 - surplus = 90 - -/datum/uplink_item/stealthy_tools/smugglersatchel - name = "Smuggler's Satchel" - desc = "This satchel is thin enough to be hidden in the gap between plating and tiling; great for stashing your stolen goods. Comes with a crowbar and a floor tile inside." - item = /obj/item/weapon/storage/backpack/satchel_flat - cost = 2 - surplus = 30 - -/datum/uplink_item/stealthy_tools/stimpack - name = "Stimpack" - desc = "Stimpacks, the tool of many great heroes, make you nearly immune to stuns and knockdowns for about 5 minutes after injection." - item = /obj/item/weapon/reagent_containers/syringe/stimulants - cost = 5 - surplus = 90 - -// DEVICE AND TOOLS - -/datum/uplink_item/device_tools - category = "Devices and Tools" - -/datum/uplink_item/device_tools/emag - name = "Cryptographic Sequencer" - desc = "The cryptographic sequencer, or emag, is a small card that unlocks hidden functions in electronic devices, subverts intended functions, and characteristically breaks security mechanisms." - item = /obj/item/weapon/card/emag - cost = 6 - excludefrom = list(/datum/game_mode/gang) - -/datum/uplink_item/device_tools/toolbox - name = "Full Syndicate Toolbox" - desc = "The syndicate toolbox is a suspicious black and red. It comes loaded with a full tool set including a multitool and combat gloves that are resistant to shocks and heat." - item = /obj/item/weapon/storage/toolbox/syndicate - cost = 1 - -/datum/uplink_item/device_tools/surgerybag - name = "Syndicate Surgery Dufflebag" - desc = "The Syndicate surgery dufflebag is a toolkit containing all surgery tools, surgical drapes, a Syndicate brand MMI, a straitjacket, and a muzzle." - item = /obj/item/weapon/storage/backpack/dufflebag/syndie/surgery - cost = 4 - -/datum/uplink_item/device_tools/military_belt - name = "Military Belt" - desc = "A robust seven-slot red belt that is capable of holding all items that can fit into it." - item = /obj/item/weapon/storage/belt/military - cost = 3 - excludefrom = list(/datum/game_mode/nuclear) - -/datum/uplink_item/device_tools/medkit - name = "Syndicate Combat Medic Kit" - desc = "This first aid kit is a suspicious brown and red. Included is a combat stimulant injector for rapid healing, a medical HUD for quick identification of injured personnel, \ - and other supplies helpful for a field medic." - item = /obj/item/weapon/storage/firstaid/tactical - cost = 9 - gamemodes = list(/datum/game_mode/nuclear,/datum/game_mode/gang) - - -/datum/uplink_item/device_tools/space_suit - name = "Syndicate Space Suit" - desc = "The red and black syndicate space suit is less encumbering than Nanotrasen variants, fits inside bags, and has a weapon slot. Nanotrasen crewmembers are trained to report red space suit sightings." - item = /obj/item/weapon/storage/box/syndie_kit/space - cost = 4 - excludefrom = list(/datum/game_mode/gang) - -/datum/uplink_item/device_tools/hardsuit - name = "Blood-Red Hardsuit" - desc = "The feared suit of a syndicate nuclear agent. Features slightly better armoring and a built in jetpack that runs off standard atmospheric tanks. \ - When the built in helmet is deployed your identity will be protected, even in death, as the suit cannot be removed by outside forces. Toggling the suit into combat mode \ - will allow you all the mobility of a loose fitting uniform without sacrificing armoring. Additionally the suit is collapsible, small enough to fit within a backpack. \ - Nanotrasen crewmembers are trained to report red space suit sightings; these suits in particular are known to drive employees into a panic." - item = /obj/item/clothing/suit/space/hardsuit/syndi - cost = 8 - excludefrom = list(/datum/game_mode/gang) - -/datum/uplink_item/device_tools/hardsuit - name = "Elite Syndicate Hardsuit" - desc = "The elite Syndicate hardsuit is worn by only the best nuclear agents. Features much better armoring and complete fireproofing, as well as a built in jetpack. \ - When the built in helmet is deployed your identity will be protected, even in death, as the suit cannot be removed by outside forces. Toggling the suit into combat mode \ - will allow you all the mobility of a loose fitting uniform without sacrificing armoring. Additionally the suit is collapsible, small enough to fit within a backpack. \ - Nanotrasen crewmembers are trained to report red space suit sightings; these suits in particular are known to drive employees into a panic." - item = /obj/item/clothing/suit/space/hardsuit/syndi/elite - cost = 8 - gamemodes = list(/datum/game_mode/nuclear) - -/datum/uplink_item/device_tools/thermal - name = "Thermal Imaging Glasses" - desc = "These goggles can be turned to resemble common eyewears throughout the station. \ - They allow you to see organisms through walls by capturing the upper portion of the infrared light spectrum, emitted as heat and light by objects. \ - Hotter objects, such as warm bodies, cybernetic organisms and artificial intelligence cores emit more of this light than cooler objects like walls and airlocks." //THEN WHY CANT THEY SEE PLASMA FIRES???? - item = /obj/item/clothing/glasses/thermal/syndi - cost = 6 - -/datum/uplink_item/device_tools/binary - name = "Binary Translator Key" - desc = "A key that, when inserted into a radio headset, allows you to listen to and talk with silicon-based lifeforms, such as AI units and cyborgs, over their private binary channel. Caution should \ - be taken while doing this, as unless they are allied with you, they are programmed to report such intrusions." - item = /obj/item/device/encryptionkey/binary - cost = 5 - surplus = 75 - -/datum/uplink_item/device_tools/encryptionkey - name = "Syndicate Encryption Key" - desc = "A key that, when inserted into a radio headset, allows you to listen to all station department channels as well as talk on an encrypted Syndicate channel with other agents that have the same \ - key." - item = /obj/item/device/encryptionkey/syndicate - cost = 2 //Nowhere near as useful as the Binary Key! - surplus = 75 - -/datum/uplink_item/device_tools/ai_detector - name = "Artificial Intelligence Detector" // changed name in case newfriends thought it detected disguised ai's - desc = "A functional multitool that turns red when it detects an artificial intelligence watching it or its holder. Knowing when an artificial intelligence is watching you is useful for knowing when to maintain cover." - item = /obj/item/device/multitool/ai_detect - cost = 1 - -/datum/uplink_item/device_tools/hacked_module - name = "Hacked AI Law Upload Module" - desc = "When used with an upload console, this module allows you to upload priority laws to an artificial intelligence. Be careful with their wording, as artificial intelligences may look for loopholes to exploit." - item = /obj/item/weapon/aiModule/syndicate - cost = 14 - -/datum/uplink_item/device_tools/magboots - name = "Blood-Red Magboots" - desc = "A pair of magnetic boots with a Syndicate paintjob that assist with freer movement in space or on-station during gravitational generator failures. \ - These reverse-engineered knockoffs of Nanotrasen's 'Advanced Magboots' slow you down in simulated-gravity environments much like the standard issue variety." - item = /obj/item/clothing/shoes/magboots/syndie - cost = 3 - gamemodes = list(/datum/game_mode/nuclear) - -/datum/uplink_item/device_tools/c4 - name = "Composition C-4" - desc = "C-4 is plastic explosive of the common variety Composition C. You can use it to breach walls or connect a signaler to its wiring to make it remotely detonable. \ - It has a modifiable timer with a minimum setting of 10 seconds." - item = /obj/item/weapon/c4 - cost = 1 - -/datum/uplink_item/device_tools/powersink - name = "Power Sink" - desc = "When screwed to wiring attached to a power grid and activated, this large device places excessive load on the grid, causing a stationwide blackout. The sink is large and cannot be stored in \ - most traditional bags and boxes." - item = /obj/item/device/powersink - cost = 10 - -/datum/uplink_item/device_tools/singularity_beacon - name = "Singularity Beacon" - desc = "When screwed to wiring attached to an electric grid and activated, this large device pulls any active gravitational singularities towards it. \ - This will not work when the singularity is still in containment. A singularity beacon can cause catastrophic damage to a space station, \ - leading to an emergency evacuation. Because of its size, it cannot be carried. Ordering this sends you a small beacon that will teleport the larger beacon to your location upon activation." - item = /obj/item/device/sbeacondrop - cost = 14 - excludefrom = list(/datum/game_mode/gang) - -/datum/uplink_item/device_tools/syndicate_bomb - name = "Syndicate Bomb" - desc = "The Syndicate bomb is a fearsome device capable of massive destruction. It has an adjustable timer, with a minimum of 60 seconds, and can be bolted to the floor with a wrench to prevent \ - movement. The bomb is bulky and cannot be moved; upon ordering this item, a smaller beacon will be transported to you that will teleport the actual bomb to it upon activation. Note that this bomb can \ - be defused, and some crew may attempt to do so." - item = /obj/item/device/sbeacondrop/bomb - cost = 11 - -/datum/uplink_item/device_tools/rad_laser - name = "Radioactive Microlaser" - desc = "A radioactive microlaser disguised as a standard Nanotrasen health analyzer. When used, it emits a powerful burst of radiation, which, after a short delay, can incapitate all but the most protected of humanoids. \ - It has two settings: intensity, which controls the power of the radiation, and wavelength, which controls how long the radiation delay is." - item = /obj/item/device/rad_laser - cost = 5 - -/datum/uplink_item/device_tools/syndicate_detonator - name = "Syndicate Detonator" - desc = "The Syndicate detonator is a companion device to the Syndicate bomb. Simply press the included button and an encrypted radio frequency will instruct all live Syndicate bombs to detonate. \ - Useful for when speed matters or you wish to synchronize multiple bomb blasts. Be sure to stand clear of the blast radius before using the detonator." - item = /obj/item/device/syndicatedetonator - cost = 3 - gamemodes = list(/datum/game_mode/nuclear) - -/datum/uplink_item/device_tools/teleporter - name = "Teleporter Circuit Board" - desc = "A printed circuit board that completes the teleporter onboard the mothership. It is advised you calibrate the teleporter before entering it, as malfunctions can occur." - item = /obj/item/weapon/circuitboard/teleporter - cost = 40 - gamemodes = list(/datum/game_mode/nuclear) - surplus = 0 - -/datum/uplink_item/device_tools/shield - name = "Energy Shield" - desc = "An incredibly useful personal shield projector, capable of reflecting energy projectiles and defending against other attacks." - item = /obj/item/weapon/shield/energy - cost = 16 - gamemodes = list(/datum/game_mode/nuclear,/datum/game_mode/gang) - surplus = 20 - -/datum/uplink_item/device_tools/medgun - name = "Medbeam Gun" - desc = "Medical Beam Gun, useful in prolonged firefights." - item = /obj/item/weapon/gun/medbeam - cost = 15 - gamemodes = list(/datum/game_mode/nuclear) - - -// IMPLANTS - -/datum/uplink_item/implants - category = "Implants" - -/datum/uplink_item/implants/freedom - name = "Freedom Implant" - desc = "An implant injected into the body and later activated at the user's will. It will attempt to free the user from common restraints such as handcuffs." - item = /obj/item/weapon/storage/box/syndie_kit/imp_freedom - cost = 5 - -/datum/uplink_item/implants/uplink - name = "Uplink Implant" - desc = "An implant injected into the body, and later activated at the user's will. It will open a separate uplink with 10 telecrystals. The ability to have these telecrystals, combined with no easy \ - way to detect the ipmlant, makes this excellent for escaping confinement." - item = /obj/item/weapon/storage/box/syndie_kit/imp_uplink - cost = 14 - surplus = 0 - -/datum/uplink_item/implants/adrenal - name = "Adrenal Implant" - desc = "An implant injected into the body, and later activated at the user's will. It will inject a chemical cocktail which has a mild healing effect along with removing all stuns and increasing movement speed." - item = /obj/item/weapon/storage/box/syndie_kit/imp_adrenal - cost = 8 - -/datum/uplink_item/implants/storage - name = "Storage Implant" - desc = "An implant injected into the body, and later activated at the user's will. It will open a small subspace pocket capable of storing two items." - item = /obj/item/weapon/storage/box/syndie_kit/imp_storage - cost = 8 - -/datum/uplink_item/implants/microbomb - name = "Microbomb Implant" - desc = "An implant injected into the body, and later activated either manually or automatically upon death. The more implants inside of you, the higher the explosive power. \ - This will permanently destroy your body, however." - item = /obj/item/weapon/storage/box/syndie_kit/imp_microbomb - cost = 2 - gamemodes = list(/datum/game_mode/nuclear) - - -//CYBERNETIC IMPLANTS - -/datum/uplink_item/cyber_implants - category = "Cybernetic Implants" - gamemodes = list(/datum/game_mode/nuclear) - surplus = 0 - -/datum/uplink_item/cyber_implants/thermals - name = "Thermal Vision Implant" - desc = "These cybernetic eyes will give you thermal vision. They must be implanted via surgery." - item = /obj/item/organ/internal/cyberimp/eyes/thermals - cost = 8 - -/datum/uplink_item/cyber_implants/xray - name = "X-Ray Vision Implant" - desc = "These cybernetic eyes will give you X-ray vision. They must be implanted via surgery." - item = /obj/item/organ/internal/cyberimp/eyes/xray - cost = 10 - -/datum/uplink_item/cyber_implants/antistun - name = "CNS Rebooter Implant" - desc = "This implant will help you get back up on your feet faster after being stunned. It must be implanted via surgery." - item = /obj/item/organ/internal/cyberimp/brain/anti_stun - cost = 12 - -/datum/uplink_item/cyber_implants/reviver - name = "Reviver Implant" - desc = "This implant will attempt to revive you if you lose consciousness. It must be implanted via surgery." - item = /obj/item/organ/internal/cyberimp/chest/reviver - cost = 8 - -/datum/uplink_item/cyber_implants/bundle - name = "Cybernetic Implants Bundle" - desc = "A random selection of cybernetic implants. Guaranteed 5 high quality implants. They must be implanted via surgery." - item = /obj/item/weapon/storage/box/cyber_implants - cost = 40 - -// POINTLESS BADASSERY - -/datum/uplink_item/badass - category = "(Pointless) Badassery" - surplus = 0 - last = 1 - -/datum/uplink_item/badass/syndiecigs - name = "Syndicate Smokes" - desc = "Strong flavor, dense smoke, infused with omnizine." - item = /obj/item/weapon/storage/fancy/cigarettes/cigpack_syndicate - cost = 2 - -/datum/uplink_item/badass/bundle - name = "Syndicate Bundle" - desc = "Syndicate Bundles are specialised groups of items that arrive in a plain box. These items are collectively worth more than 20 telecrystals, but you do not know which specialisation you will receive." - item = /obj/item/weapon/storage/box/syndicate - cost = 20 - excludefrom = list(/datum/game_mode/nuclear,/datum/game_mode/gang) - -/datum/uplink_item/badass/syndiecards - name = "Syndicate Playing Cards" - desc = "A special deck of space-grade playing cards with a mono-molecular edge and metal reinforcement, making them slightly more robust than a normal deck of cards. \ - You can also play card games with them or leave them on your victims." - item = /obj/item/toy/cards/deck/syndicate - cost = 1 - surplus = 40 - -/datum/uplink_item/badass/syndiecash - name = "Syndicate Briefcase Full of Cash" - desc = "A secure briefcase containing 5000 space credits. Useful for bribing personnel, or purchasing goods and services at lucrative prices. \ - The briefcase also feels a little heavier to hold; it has been manufactured to pack a little bit more of a punch if your client needs some convincing." - item = /obj/item/weapon/storage/secure/briefcase/syndie - cost = 1 - -/datum/uplink_item/badass/balloon - name = "For showing that you are THE BOSS" - desc = "A useless red balloon with the Syndicate logo on it. Can blow the deepest of covers." - item = /obj/item/toy/syndicateballoon - cost = 20 - -/datum/uplink_item/implants/macrobomb - name = "Macrobomb Implant" - desc = "An implant injected into the body, and later activated either manually or automatically upon death. Upon death, releases a massive explosion that will wipe out everything nearby." - item = /obj/item/weapon/storage/box/syndie_kit/imp_macrobomb - cost = 20 - gamemodes = list(/datum/game_mode/nuclear) - -/datum/uplink_item/badass/random - name = "Random Item" - desc = "Picking this choice will send you a random item from the list. Useful for when you cannot think of a strategy to finish your objectives with." - item = /obj/item/weapon/storage/box/syndicate - cost = 0 - -/datum/uplink_item/badass/random/spawn_item(turf/loc, obj/item/device/uplink/U) - - var/list/buyable_items = get_uplink_items() - var/list/possible_items = list() - - for(var/category in buyable_items) - for(var/datum/uplink_item/I in buyable_items[category]) - if(I == src) - continue - if(I.cost > U.uses) - continue - possible_items += I - - if(possible_items.len) - var/datum/uplink_item/I = pick(possible_items) - U.uses -= max(0, I.cost) - feedback_add_details("traitor_uplink_items_bought","RN") - return new I.item(loc) - -/datum/uplink_item/badass/surplus_crate - name = "Syndicate Surplus Crate" - desc = "A crate containing 50 telecrystals worth of random syndicate leftovers." - cost = 20 - item = /obj/item/weapon/storage/box/syndicate - excludefrom = list(/datum/game_mode/nuclear) - -/datum/uplink_item/badass/surplus_crate/spawn_item(turf/loc, obj/item/device/uplink/U) - var/obj/structure/closet/crate/C = new(loc) - var/list/temp_uplink_list = get_uplink_items() - var/list/buyable_items = list() - for(var/category in temp_uplink_list) - buyable_items += temp_uplink_list[category] - var/list/bought_items = list() - U.uses -= cost - U.used_TC = 20 - var/remaining_TC = 50 - - var/datum/uplink_item/I - while(remaining_TC) - I = pick(buyable_items) - if(!I.surplus) - continue - if(I.cost > remaining_TC) - continue - if((I.item in bought_items) && prob(33)) //To prevent people from being flooded with the same thing over and over again. - continue - bought_items += I.item - remaining_TC -= I.cost - - U.purchase_log += "\icon[C]" - for(var/item in bought_items) - new item(C) +var/list/uplink_items = list() + +/proc/get_uplink_items(var/gamemode_override=null) + // If not already initialized.. + if(!uplink_items.len) + + // Fill in the list and order it like this: + // A keyed list, acting as categories, which are lists to the datum. + + var/list/last = list() + for(var/item in subtypesof(/datum/uplink_item)) + + var/datum/uplink_item/I = new item() + if(!I.item) + continue + if(I.last) + last += I + continue + + if(!uplink_items[I.category]) + uplink_items[I.category] = list() + + uplink_items[I.category] += I + + for(var/datum/uplink_item/I in last) + + if(!uplink_items[I.category]) + uplink_items[I.category] = list() + + uplink_items[I.category] += I + + //Filtered version + var/list/filtered_uplink_items = list() + + for(var/category in uplink_items) + for(var/datum/uplink_item/I in uplink_items[category]) + if(I.gamemodes.len) + if(!gamemode_override && ticker && !(ticker.mode.type in I.gamemodes)) + continue + if(gamemode_override && !(gamemode_override in I.gamemodes)) + continue + if(I.excludefrom.len) + if(!gamemode_override && ticker && (ticker.mode.type in I.excludefrom)) + continue + if(gamemode_override && (gamemode_override in I.excludefrom)) + continue + if(!filtered_uplink_items[I.category]) + filtered_uplink_items[I.category] = list() + filtered_uplink_items[category] += I + + return filtered_uplink_items + +// You can change the order of the list by putting datums before/after one another OR +// you can use the last variable to make sure it appears last, well have the category appear last. + +/datum/uplink_item + var/name = "item name" + var/category = "item category" + var/desc = "item description" + var/item = null + var/cost = 0 + var/last = 0 // Appear last + var/list/gamemodes = list() // Empty list means it is in all the gamemodes. Otherwise place the gamemode name here. + var/list/excludefrom = list() //Empty list does nothing. Place the name of gamemode you don't want this item to be available in here. This is so you dont have to list EVERY mode to exclude something. + var/surplus = 100 //Chance of being included in the surplus crate (when pick() selects it) + +/datum/uplink_item/proc/spawn_item(turf/loc, obj/item/device/uplink/U) + if(item) + U.uses -= max(cost, 0) + U.used_TC += cost + feedback_add_details("traitor_uplink_items_bought", "[item]") + return new item(loc) + +/datum/uplink_item/proc/buy(obj/item/device/uplink/U, mob/user) + + ..() + if(!istype(U)) + return 0 + + if (!user || user.incapacitated()) + return 0 + + // If the uplink's holder is in the user's contents + if ((U.loc in user.contents || (in_range(U.loc, user) && istype(U.loc.loc, /turf)))) + user.set_machine(U) + if(cost > U.uses) + return 0 + + var/obj/I = spawn_item(get_turf(user), U) + + if(istype(I, /obj/item)) + if(ishuman(user)) + var/mob/living/carbon/human/A = user + A.put_in_any_hand_if_possible(I) + + if(istype(I,/obj/item/weapon/storage/box/) && I.contents.len>0) + for(var/atom/o in I) + U.purchase_log += "\icon[o]" + else + U.purchase_log += "\icon[I]" + + U.interact(user) + return 1 + return 0 + +/* +// +// UPLINK ITEMS +// +*/ + +// DANGEROUS WEAPONS + +/datum/uplink_item/dangerous + category = "Conspicuous and Dangerous Weapons" + +/datum/uplink_item/dangerous/pistol + name = "FK-69 Pistol" + desc = "A small, easily concealable handgun that uses 10mm auto rounds in 8-round magazines and is compatible with suppressors." + item = /obj/item/weapon/gun/projectile/automatic/pistol + cost = 9 + +/datum/uplink_item/dangerous/revolver + name = "Syndicate Revolver" + desc = "A brutally simple syndicate revolver that fires .357 Magnum cartridges and has 7 chambers." + item = /obj/item/weapon/gun/projectile/revolver + cost = 13 + surplus = 50 + +/datum/uplink_item/dangerous/smg + name = "C-20r Submachine Gun" + desc = "A fully-loaded Scarborough Arms bullpup submachine gun that fires .45 rounds with a 20-round magazine and is compatible with suppressors." + item = /obj/item/weapon/gun/projectile/automatic/c20r + cost = 14 + gamemodes = list(/datum/game_mode/nuclear) + surplus = 40 + +/datum/uplink_item/dangerous/smg/unrestricted + item = /obj/item/weapon/gun/projectile/automatic/c20r/unrestricted + gamemodes = list(/datum/game_mode/gang) + +/datum/uplink_item/dangerous/carbine + name = "M-90gl Carbine" + desc = "A fully-loaded three-round burst carbine that uses 30-round 5.56mm magazines with a togglable underslung 40mm grenade launcher." + item = /obj/item/weapon/gun/projectile/automatic/m90 + cost = 18 + gamemodes = list(/datum/game_mode/nuclear) + surplus = 50 + +/datum/uplink_item/dangerous/carbine/unrestricted + item = /obj/item/weapon/gun/projectile/automatic/m90/unrestricted + gamemodes = list(/datum/game_mode/gang) + +/datum/uplink_item/dangerous/machinegun + name = "L6 Squad Automatic Weapon" + desc = "A fully-loaded Aussec Armoury belt-fed machine gun. This deadly weapon has a massive 50-round magazine of devastating 7.62x51mm ammunition." + item = /obj/item/weapon/gun/projectile/automatic/l6_saw + cost = 40 + gamemodes = list(/datum/game_mode/nuclear) + surplus = 0 + +/datum/uplink_item/dangerous/crossbow + name = "Miniature Energy Crossbow" + desc = "A short bow mounted across a tiller in miniature. Small enough to fit into a pocket or slip into a bag unnoticed. It will synthesize and fire bolts tipped with a paralyzing toxin that will \ + briefly stun targets and cause them to slur as if inebriated. It can produce an infinite amount of bolts, but must be manually recharged with each shot." + item = /obj/item/weapon/gun/energy/kinetic_accelerator/crossbow + cost = 12 + excludefrom = list(/datum/game_mode/nuclear,/datum/game_mode/gang) + surplus = 50 + +/datum/uplink_item/dangerous/flamethrower + name = "Flamethrower" + desc = "A flamethrower, fueled by a portion of highly flammable biotoxins stolen previously from Nanotrasen stations. Make a statement by roasting the filth in their own greed. Use with caution." + item = /obj/item/weapon/flamethrower/full/tank + cost = 4 + gamemodes = list(/datum/game_mode/nuclear,/datum/game_mode/gang) + surplus = 40 + +/datum/uplink_item/dangerous/sword + name = "Energy Sword" + desc = "The energy sword is an edged weapon with a blade of pure energy. The sword is small enough to be pocketed when inactive. Activating it produces a loud, distinctive noise. One can combine two \ + energy swords to create a double energy sword, which must be wielded in two hands but is more robust and deflects all energy projectiles." + item = /obj/item/weapon/melee/energy/sword/saber + cost = 8 + +/datum/uplink_item/dangerous/emp + name = "EMP Kit" + desc = "A box that contains two EMP grenades, an EMP implant and a short ranged recharging device disguised as a flashlight. Useful to disrupt communication and silicon lifeforms." + item = /obj/item/weapon/storage/box/syndie_kit/emp + cost = 5 + +/datum/uplink_item/dangerous/syndicate_minibomb + name = "Syndicate Minibomb" + desc = "The minibomb is a grenade with a five-second fuse. Upon detonation, it will create a small hull breach in addition to dealing high amounts of damage to nearby personnel." + item = /obj/item/weapon/grenade/syndieminibomb + cost = 6 + +/datum/uplink_item/dangerous/foamsmg + name = "Toy Submachine Gun" + desc = "A fully-loaded Donksoft bullpup submachine gun that fires riot grade rounds with a 20-round magazine." + item = /obj/item/weapon/gun/projectile/automatic/c20r/toy + cost = 8 + gamemodes = list(/datum/game_mode/nuclear) + surplus = 0 + +/datum/uplink_item/dangerous/foammachinegun + name = "Toy Machine Gun" + desc = "A fully-loaded Donksoft belt-fed machine gun. This weapon has a massive 50-round magazine of devastating riot grade darts, that can briefly incapacitate someone in just one volley." + item = /obj/item/weapon/gun/projectile/automatic/l6_saw/toy + cost = 12 + gamemodes = list(/datum/game_mode/nuclear) + surplus = 0 + +/datum/uplink_item/dangerous/viscerators + name = "Viscerator Delivery Grenade" + desc = "A unique grenade that deploys a swarm of viscerators upon activation, which will chase down and shred any non-operatives in the area." + item = /obj/item/weapon/grenade/spawnergrenade/manhacks + cost = 8 + gamemodes = list(/datum/game_mode/nuclear) + surplus = 35 + +/datum/uplink_item/dangerous/bioterror + name = "Biohazardous Chemical Sprayer" + desc = "A chemical sprayer that allows a wide dispersal of selected chemicals. Especially tailored by the Tiger Cooperative, the deadly blend it comes stocked with will disorient, damage, and disable your foes... \ + Use with extreme caution, to prevent exposure to yourself and your fellow operatives." + item = /obj/item/weapon/reagent_containers/spray/chemsprayer/bioterror + cost = 20 + gamemodes = list(/datum/game_mode/nuclear,/datum/game_mode/gang) + surplus = 0 + +/datum/uplink_item/dangerous/gygax + name = "Gygax Exosuit" + desc = "A lightweight exosuit, painted in a dark scheme. Its speed and equipment selection make it excellent for hit-and-run style attacks. \ + This model lacks a method of space propulsion, and therefore it is advised to repair the mothership's teleporter if you wish to make use of it." + item = /obj/mecha/combat/gygax/dark/loaded + cost = 90 + gamemodes = list(/datum/game_mode/nuclear) + surplus = 0 + +/datum/uplink_item/dangerous/mauler + name = "Mauler Exosuit" + desc = "A massive and incredibly deadly Syndicate exosuit. Features long-range targetting, thrust vectoring, and deployable smoke." + item = /obj/mecha/combat/marauder/mauler/loaded + cost = 140 + gamemodes = list(/datum/game_mode/nuclear) + surplus = 0 + +/datum/uplink_item/dangerous/syndieborg + name = "Syndicate Cyborg" + desc = "A cyborg designed and programmed for systematic extermination of non-Syndicate personnel." + item = /obj/item/weapon/antag_spawner/borg_tele + cost = 50 + gamemodes = list(/datum/game_mode/nuclear) + surplus = 0 + +//for refunding the syndieborg teleporter +/datum/uplink_item/dangerous/syndieborg/spawn_item() + var/obj/item/weapon/antag_spawner/borg_tele/T = ..() + if(istype(T)) + T.TC_cost = cost + +/datum/uplink_item/dangerous/guardian + name = "Holoparasites" + desc = "Though capable of near sorcerous feats via use of hardlight holograms and nanomachines, they require an organic host as a home base and source of fuel." + item = /obj/item/weapon/storage/box/syndie_kit/guardian + excludefrom = list(/datum/game_mode/nuclear,/datum/game_mode/gang) + cost = 12 + +/datum/uplink_item/dangerous/sniper + name = "Sniper Rifle" + desc = "Ranged fury, syndicate style. guaranteed to cause shock and awe or your TC back!" + item = /obj/item/weapon/gun/projectile/sniper_rifle/syndicate + cost = 16 + gamemodes = list(/datum/game_mode/nuclear) + surplus = 25 + + +// AMMUNITION + +/datum/uplink_item/ammo + category = "Ammunition" + surplus = 40 + +/datum/uplink_item/ammo/pistol + name = "Handgun Magazine - 10mm" + desc = "An additional 8-round 10mm magazine for use in the syndicate pistol. These subsonic rounds are dirt cheap but are half as effective as .357 rounds." + item = /obj/item/ammo_box/magazine/m10mm + cost = 1 + +/datum/uplink_item/ammo/revolver + name = "Speed Loader - .357" + desc = "A speed loader that contains seven additional .357 Magnum rounds for the syndicate revolver. For when you really need a lot of things dead." + item = /obj/item/ammo_box/a357 + cost = 4 + +/datum/uplink_item/ammo/smg + name = "SMG Magazine - .45" + desc = "An additional 20-round .45 magazine for use in the C-20r submachine gun. These bullets pack a lot of punch that can knock most targets down, but do limited overall damage." + item = /obj/item/ammo_box/magazine/smgm45 + cost = 2 + gamemodes = list(/datum/game_mode/nuclear,/datum/game_mode/gang) + +/datum/uplink_item/ammo/ammobag + name = "Ammo Duffelbag - Shotgun Ammo Grab Bag" + desc = "A duffelbag filled with Bulldog ammo to kit out an entire team, at a discounted price." + item = /obj/item/weapon/storage/backpack/dufflebag/syndie/ammo/loaded + cost = 10 //bulk buyer's discount. Very useful if you're buying a mech and dont have TC left to buy people non-shotgun guns + gamemodes = list(/datum/game_mode/nuclear) + +/datum/uplink_item/ammo/bullslug + name = "Drum Magazine - 12g Slugs" + desc = "An additional 8-round slug magazine for use in the Bulldog shotgun. Now 8 times less likely to shoot your pals." + item = /obj/item/ammo_box/magazine/m12g + cost = 2 + gamemodes = list(/datum/game_mode/nuclear) + +/datum/uplink_item/ammo/bullbuck + name = "Drum Magazine - 12g Buckshot" + desc = "An additional 8-round buckshot magazine for use in the Bulldog shotgun. Front towards enemy." + item = /obj/item/ammo_box/magazine/m12g/buckshot + cost = 2 + gamemodes = list(/datum/game_mode/nuclear) + +/datum/uplink_item/ammo/bullstun + name = "Drum Magazine - 12g Stun Slug" + desc = "An alternative 8-round stun slug magazine for use in the Bulldog shotgun. Saying that they're completely non-lethal would be lying." + item = /obj/item/ammo_box/magazine/m12g/stun + cost = 3 + gamemodes = list(/datum/game_mode/nuclear) + +/datum/uplink_item/ammo/bulldragon + name = "Drum Magazine - 12g Dragon's Breath" + desc = "An alternative 8-round dragon's breath magazine for use in the Bulldog shotgun. I'm a fire starter, twisted fire starter!" + item = /obj/item/ammo_box/magazine/m12g/dragon + cost = 2 + gamemodes = list(/datum/game_mode/nuclear) + +/datum/uplink_item/ammo/bioterror + name = "Box of Bioterror Syringes" + desc = "A box full of preloaded syringes, containing various chemicals that seize up the victim's motor and broca systems, making it impossible for them to move or speak for some time." + item = /obj/item/weapon/storage/box/syndie_kit/bioterror + cost = 6 + gamemodes = list(/datum/game_mode/nuclear) + +/datum/uplink_item/ammo/carbine + name = "Toploader Magazine - 5.56" + desc = "An additional 30-round 5.56 magazine for use in the M-90gl carbine. These bullets don't have the punch to knock most targets down, but dish out higher overall damage." + item = /obj/item/ammo_box/magazine/m556 + cost = 2 + gamemodes = list(/datum/game_mode/nuclear,/datum/game_mode/gang) + +/datum/uplink_item/ammo/a40mm + name = "Ammo Box - 40mm grenades" + desc = "A box of 4 additional 40mm HE grenades for use the M-90gl's underbarrel grenade launcher. Your teammates will thank you to not shoot these down small hallways." + item = /obj/item/ammo_box/a40mm + cost = 4 + gamemodes = list(/datum/game_mode/nuclear) + +/datum/uplink_item/ammo/machinegun + name = "Box Magazine - 7.62x51mm" + desc = "A 50-round magazine of 7.62x51mm ammunition for use in the L6 SAW machinegun. By the time you need to use this, you'll already be on a pile of corpses." + item = /obj/item/ammo_box/magazine/m762 + cost = 12 + gamemodes = list(/datum/game_mode/nuclear) + surplus = 0 + +/datum/uplink_item/ammo/toydarts //This used to only be for nuke ops, but had the cost lowered and made available to traitors because >a box of foam darts is more expensive than four carbine magazines + name = "Box of Riot Darts" + desc = "A box of 40 Donksoft foam riot darts, for reloading any compatible foam dart gun. Don't forget to share!" + item = /obj/item/ammo_box/foambox/riot + cost = 2 + surplus = 0 + +/datum/uplink_item/ammo/sniper + name = "Sniper Magazine - .50" + desc = "An additional 6-round .50 magazine for use in the syndicate sniper rifle." + item = /obj/item/ammo_box/magazine/sniper_rounds + cost = 4 //70dmg rounds are no joke + gamemodes = list(/datum/game_mode/nuclear) + +/datum/uplink_item/ammo/sniper/soporific + name = "Sniper Magazine - Soporific Rounds" + desc = "A 3-round magazine of soporific ammo designed for use in the syndicate sniper rifle, put your enemies to sleep today!" + item = /obj/item/ammo_box/magazine/sniper_rounds/soporific + cost = 6 + +/datum/uplink_item/ammo/sniper/haemorrhage + name = "Sniper Magazine - Haemorrhage Rounds" + desc = "A 5-round magazine of haemorrhage ammo designed for use in the syndicate sniper rifle, causes heavy bleeding in the target." + item = /obj/item/ammo_box/magazine/sniper_rounds/haemorrhage + + + +// STEALTHY WEAPONS + +/datum/uplink_item/stealthy_weapons + category = "Stealthy and Inconspicuous Weapons" + + +/datum/uplink_item/stealthy_weapons/throwingstars + name = "Box of Throwing Stars" + desc = "A box of shurikens from ancient Earth martial arts. They are highly effective throwing weapons, and will embed into limbs when possible." + item = /obj/item/weapon/storage/box/throwing_stars + cost = 6 + +/datum/uplink_item/stealthy_weapons/edagger + name = "Energy Dagger" + desc = "A dagger made of energy that looks and functions as a pen when off." + item = /obj/item/weapon/pen/edagger + cost = 2 + +/datum/uplink_item/stealthy_weapons/foampistol + name = "Toy Gun with Riot Darts" + desc = "An innocent-looking toy pistol designed to fire foam darts. Comes loaded with riot-grade darts effective at incapacitating a target." + item = /obj/item/weapon/gun/projectile/automatic/toy/pistol/riot + cost = 3 + surplus = 10 + excludefrom = list(/datum/game_mode/gang) + +/datum/uplink_item/stealthy_weapons/sleepy_pen + name = "Sleepy Pen" + desc = "A syringe disguised as a functional pen, filled with a potent mix of drugs, including a strong anesthetic and a chemical that prevents the target from speaking. \ + The pen holds one dose of the mixture, and cannot be refilled. Note that before the target falls asleep, they will be able to move and act." + item = /obj/item/weapon/pen/sleepy + cost = 4 + excludefrom = list(/datum/game_mode/nuclear,/datum/game_mode/gang) + +/datum/uplink_item/stealthy_weapons/soap + name = "Syndicate Soap" + desc = "A sinister-looking surfactant used to clean blood stains to hide murders and prevent DNA analysis. You can also drop it underfoot to slip people." + item = /obj/item/weapon/soap/syndie + cost = 1 + surplus = 50 + +/datum/uplink_item/stealthy_weapons/traitor_chem_bottle + name = "Poison Kit" + desc = "An assortment of deadly chemicals packed into a compact box. Comes with a syringe for more precise application." + item = /obj/item/weapon/storage/box/syndie_kit/chemical + cost = 6 + surplus = 50 + +/datum/uplink_item/stealthy_weapons/dart_pistol + name = "Dart Pistol" + desc = "A miniaturized version of a normal syringe gun. It is very quiet when fired and can fit into any space a small item can." + item = /obj/item/weapon/gun/syringe/syndicate + cost = 4 + surplus = 50 //High chance of surplus due to poison kit also having a high chance + +/datum/uplink_item/stealthy_weapons/detomatix + name = "Detomatix PDA Cartridge" + desc = "When inserted into a personal digital assistant, this cartridge gives you four opportunities to detonate PDAs of crewmembers who have their message feature enabled. \ + The concussive effect from the explosion will knock the recipient out for a short period, and deafen them for longer. It has a chance to detonate your PDA." + item = /obj/item/weapon/cartridge/syndicate + cost = 6 + +/datum/uplink_item/stealthy_weapons/suppressor + name = "Universal Suppressor" + desc = "Fitted for use on any small caliber weapon with a threaded barrel, this suppressor will silence the shots of the weapon for increased stealth and superior ambushing capability." + item = /obj/item/weapon/suppressor + cost = 3 + surplus = 10 + +/datum/uplink_item/stealthy_weapons/pizza_bomb + name = "Pizza Bomb" + desc = "A pizza box with a bomb taped inside it. The timer needs to be set by opening the box; afterwards, opening the box again will trigger the detonation." + item = /obj/item/device/pizza_bomb + cost = 6 + surplus = 8 + +/datum/uplink_item/stealthy_weapons/dehy_carp + name = "Dehydrated Space Carp" + desc = "Looks like a plush toy carp, but just add water and it becomes a real-life space carp! Activate in your hand before use so it knows not to kill you." + item = /obj/item/toy/carpplushie/dehy_carp + cost = 1 + +/datum/uplink_item/stealthy_weapons/door_charge + name = "Explosive Airlock Charge" + desc = "A small, easily concealable device. It can be applied to an open airlock panel, and the next person to open that airlock will be knocked down in an explosion. The airlock's maintenance panel will also be destroyed by this." + item = /obj/item/device/doorCharge + cost = 2 + surplus = 10 + excludefrom = list(/datum/game_mode/nuclear) + +// STEALTHY TOOLS + +/datum/uplink_item/stealthy_tools + category = "Stealth and Camouflage Items" + +/datum/uplink_item/stealthy_tools/chameleon_jumpsuit + name = "Chameleon Jumpsuit" + desc = "A jumpsuit used to imitate the uniforms of Nanotrasen crewmembers. It can change form at any time to resemble another jumpsuit. May react unpredictably to electromagnetic disruptions." + item = /obj/item/clothing/under/chameleon + cost = 2 + +/datum/uplink_item/stealthy_tools/chameleon_stamp + name = "Chameleon Stamp" + desc = "A stamp that can be activated to imitate an official Nanotrasen Stamp™. The disguised stamp will work exactly like the real stamp and will allow you to forge false documents to gain access or equipment; \ + it can also be used in a washing machine to forge clothing." + item = /obj/item/weapon/stamp/chameleon + cost = 1 + surplus = 35 + +/datum/uplink_item/stealthy_tools/syndigaloshes + name = "No-Slip Brown Shoes" + desc = "These shoes will allow the wearer to run on wet floors and slippery objects without falling down. They do not work on heavily lubricated surfaces." + item = /obj/item/clothing/shoes/sneakers/syndigaloshes + cost = 2 + excludefrom = list(/datum/game_mode/nuclear) + +/datum/uplink_item/stealthy_tools/syndigaloshes/nuke + name = "Tactical No-Slip Brown Shoes" + desc = "These allow you to run on wet floors. They do not work on lubricated surfaces, but the manufacturer guarantees they're somehow better than the normal ones." + cost = 4 //but they aren't + gamemodes = list(/datum/game_mode/nuclear) + +/datum/uplink_item/stealthy_tools/agent_card + name = "Agent Identification Card" + desc = "Agent cards prevent artificial intelligences from tracking the wearer, and can copy access from other identification cards. The access is cumulative, so scanning one card does not erase the access gained from another. \ + In addition, they can be forged to display a new assignment and name. This can be done an unlimited amount of times. Some Syndicate areas can only be accessed with these cards." + item = /obj/item/weapon/card/id/syndicate + cost = 2 + +/datum/uplink_item/stealthy_tools/voice_changer + name = "Voice Changer" + item = /obj/item/clothing/mask/gas/voice + desc = "A conspicuous gas mask that mimics the voice named on your identification card. It can be toggled on and off." + cost = 3 + +/datum/uplink_item/stealthy_tools/chameleon_proj + name = "Chameleon Projector" + desc = "Projects an image across a user, disguising them as an object scanned with it, as long as they don't move the projector from their hand. Disguised users move slowly, and projectiles pass over them." + item = /obj/item/device/chameleon + cost = 7 + excludefrom = list(/datum/game_mode/gang) + +/datum/uplink_item/stealthy_tools/camera_bug + name = "Camera Bug" + desc = "Enables you to view all cameras on the network and track a target. Bugging cameras allows you to disable them remotely." + item = /obj/item/device/camera_bug + cost = 1 + surplus = 90 + +/datum/uplink_item/stealthy_tools/smugglersatchel + name = "Smuggler's Satchel" + desc = "This satchel is thin enough to be hidden in the gap between plating and tiling; great for stashing your stolen goods. Comes with a crowbar and a floor tile inside." + item = /obj/item/weapon/storage/backpack/satchel_flat + cost = 2 + surplus = 30 + +/datum/uplink_item/stealthy_tools/stimpack + name = "Stimpack" + desc = "Stimpacks, the tool of many great heroes, make you nearly immune to stuns and knockdowns for about 5 minutes after injection." + item = /obj/item/weapon/reagent_containers/syringe/stimulants + cost = 5 + surplus = 90 + +// DEVICE AND TOOLS + +/datum/uplink_item/device_tools + category = "Devices and Tools" + +/datum/uplink_item/device_tools/emag + name = "Cryptographic Sequencer" + desc = "The cryptographic sequencer, or emag, is a small card that unlocks hidden functions in electronic devices, subverts intended functions, and characteristically breaks security mechanisms." + item = /obj/item/weapon/card/emag + cost = 6 + excludefrom = list(/datum/game_mode/gang) + +/datum/uplink_item/device_tools/toolbox + name = "Full Syndicate Toolbox" + desc = "The syndicate toolbox is a suspicious black and red. It comes loaded with a full tool set including a multitool and combat gloves that are resistant to shocks and heat." + item = /obj/item/weapon/storage/toolbox/syndicate + cost = 1 + +/datum/uplink_item/device_tools/surgerybag + name = "Syndicate Surgery Dufflebag" + desc = "The Syndicate surgery dufflebag is a toolkit containing all surgery tools, surgical drapes, a Syndicate brand MMI, a straitjacket, and a muzzle." + item = /obj/item/weapon/storage/backpack/dufflebag/syndie/surgery + cost = 4 + +/datum/uplink_item/device_tools/military_belt + name = "Military Belt" + desc = "A robust seven-slot red belt that is capable of holding all items that can fit into it." + item = /obj/item/weapon/storage/belt/military + cost = 3 + excludefrom = list(/datum/game_mode/nuclear) + +/datum/uplink_item/device_tools/medkit + name = "Syndicate Combat Medic Kit" + desc = "This first aid kit is a suspicious brown and red. Included is a combat stimulant injector for rapid healing, a medical HUD for quick identification of injured personnel, \ + and other supplies helpful for a field medic." + item = /obj/item/weapon/storage/firstaid/tactical + cost = 9 + gamemodes = list(/datum/game_mode/nuclear,/datum/game_mode/gang) + + +/datum/uplink_item/device_tools/space_suit + name = "Syndicate Space Suit" + desc = "The red and black syndicate space suit is less encumbering than Nanotrasen variants, fits inside bags, and has a weapon slot. Nanotrasen crewmembers are trained to report red space suit sightings." + item = /obj/item/weapon/storage/box/syndie_kit/space + cost = 4 + excludefrom = list(/datum/game_mode/gang) + +/datum/uplink_item/device_tools/hardsuit + name = "Blood-Red Hardsuit" + desc = "The feared suit of a syndicate nuclear agent. Features slightly better armoring and a built in jetpack that runs off standard atmospheric tanks. \ + When the built in helmet is deployed your identity will be protected, even in death, as the suit cannot be removed by outside forces. Toggling the suit into combat mode \ + will allow you all the mobility of a loose fitting uniform without sacrificing armoring. Additionally the suit is collapsible, small enough to fit within a backpack. \ + Nanotrasen crewmembers are trained to report red space suit sightings; these suits in particular are known to drive employees into a panic." + item = /obj/item/clothing/suit/space/hardsuit/syndi + cost = 8 + excludefrom = list(/datum/game_mode/gang) + +/datum/uplink_item/device_tools/hardsuit + name = "Elite Syndicate Hardsuit" + desc = "The elite Syndicate hardsuit is worn by only the best nuclear agents. Features much better armoring and complete fireproofing, as well as a built in jetpack. \ + When the built in helmet is deployed your identity will be protected, even in death, as the suit cannot be removed by outside forces. Toggling the suit into combat mode \ + will allow you all the mobility of a loose fitting uniform without sacrificing armoring. Additionally the suit is collapsible, small enough to fit within a backpack. \ + Nanotrasen crewmembers are trained to report red space suit sightings; these suits in particular are known to drive employees into a panic." + item = /obj/item/clothing/suit/space/hardsuit/syndi/elite + cost = 8 + gamemodes = list(/datum/game_mode/nuclear) + +/datum/uplink_item/device_tools/thermal + name = "Thermal Imaging Glasses" + desc = "These goggles can be turned to resemble common eyewears throughout the station. \ + They allow you to see organisms through walls by capturing the upper portion of the infrared light spectrum, emitted as heat and light by objects. \ + Hotter objects, such as warm bodies, cybernetic organisms and artificial intelligence cores emit more of this light than cooler objects like walls and airlocks." //THEN WHY CANT THEY SEE PLASMA FIRES???? + item = /obj/item/clothing/glasses/thermal/syndi + cost = 6 + +/datum/uplink_item/device_tools/binary + name = "Binary Translator Key" + desc = "A key that, when inserted into a radio headset, allows you to listen to and talk with silicon-based lifeforms, such as AI units and cyborgs, over their private binary channel. Caution should \ + be taken while doing this, as unless they are allied with you, they are programmed to report such intrusions." + item = /obj/item/device/encryptionkey/binary + cost = 5 + surplus = 75 + +/datum/uplink_item/device_tools/encryptionkey + name = "Syndicate Encryption Key" + desc = "A key that, when inserted into a radio headset, allows you to listen to all station department channels as well as talk on an encrypted Syndicate channel with other agents that have the same \ + key." + item = /obj/item/device/encryptionkey/syndicate + cost = 2 //Nowhere near as useful as the Binary Key! + surplus = 75 + +/datum/uplink_item/device_tools/ai_detector + name = "Artificial Intelligence Detector" // changed name in case newfriends thought it detected disguised ai's + desc = "A functional multitool that turns red when it detects an artificial intelligence watching it or its holder. Knowing when an artificial intelligence is watching you is useful for knowing when to maintain cover." + item = /obj/item/device/multitool/ai_detect + cost = 1 + +/datum/uplink_item/device_tools/hacked_module + name = "Hacked AI Law Upload Module" + desc = "When used with an upload console, this module allows you to upload priority laws to an artificial intelligence. Be careful with their wording, as artificial intelligences may look for loopholes to exploit." + item = /obj/item/weapon/aiModule/syndicate + cost = 14 + +/datum/uplink_item/device_tools/magboots + name = "Blood-Red Magboots" + desc = "A pair of magnetic boots with a Syndicate paintjob that assist with freer movement in space or on-station during gravitational generator failures. \ + These reverse-engineered knockoffs of Nanotrasen's 'Advanced Magboots' slow you down in simulated-gravity environments much like the standard issue variety." + item = /obj/item/clothing/shoes/magboots/syndie + cost = 3 + gamemodes = list(/datum/game_mode/nuclear) + +/datum/uplink_item/device_tools/c4 + name = "Composition C-4" + desc = "C-4 is plastic explosive of the common variety Composition C. You can use it to breach walls or connect a signaler to its wiring to make it remotely detonable. \ + It has a modifiable timer with a minimum setting of 10 seconds." + item = /obj/item/weapon/c4 + cost = 1 + +/datum/uplink_item/device_tools/powersink + name = "Power Sink" + desc = "When screwed to wiring attached to a power grid and activated, this large device places excessive load on the grid, causing a stationwide blackout. The sink is large and cannot be stored in \ + most traditional bags and boxes." + item = /obj/item/device/powersink + cost = 10 + +/datum/uplink_item/device_tools/singularity_beacon + name = "Singularity Beacon" + desc = "When screwed to wiring attached to an electric grid and activated, this large device pulls any active gravitational singularities towards it. \ + This will not work when the singularity is still in containment. A singularity beacon can cause catastrophic damage to a space station, \ + leading to an emergency evacuation. Because of its size, it cannot be carried. Ordering this sends you a small beacon that will teleport the larger beacon to your location upon activation." + item = /obj/item/device/sbeacondrop + cost = 14 + excludefrom = list(/datum/game_mode/gang) + +/datum/uplink_item/device_tools/syndicate_bomb + name = "Syndicate Bomb" + desc = "The Syndicate bomb is a fearsome device capable of massive destruction. It has an adjustable timer, with a minimum of 60 seconds, and can be bolted to the floor with a wrench to prevent \ + movement. The bomb is bulky and cannot be moved; upon ordering this item, a smaller beacon will be transported to you that will teleport the actual bomb to it upon activation. Note that this bomb can \ + be defused, and some crew may attempt to do so." + item = /obj/item/device/sbeacondrop/bomb + cost = 11 + +/datum/uplink_item/device_tools/rad_laser + name = "Radioactive Microlaser" + desc = "A radioactive microlaser disguised as a standard Nanotrasen health analyzer. When used, it emits a powerful burst of radiation, which, after a short delay, can incapitate all but the most protected of humanoids. \ + It has two settings: intensity, which controls the power of the radiation, and wavelength, which controls how long the radiation delay is." + item = /obj/item/device/rad_laser + cost = 5 + +/datum/uplink_item/device_tools/syndicate_detonator + name = "Syndicate Detonator" + desc = "The Syndicate detonator is a companion device to the Syndicate bomb. Simply press the included button and an encrypted radio frequency will instruct all live Syndicate bombs to detonate. \ + Useful for when speed matters or you wish to synchronize multiple bomb blasts. Be sure to stand clear of the blast radius before using the detonator." + item = /obj/item/device/syndicatedetonator + cost = 3 + gamemodes = list(/datum/game_mode/nuclear) + +/datum/uplink_item/device_tools/teleporter + name = "Teleporter Circuit Board" + desc = "A printed circuit board that completes the teleporter onboard the mothership. It is advised you calibrate the teleporter before entering it, as malfunctions can occur." + item = /obj/item/weapon/circuitboard/teleporter + cost = 40 + gamemodes = list(/datum/game_mode/nuclear) + surplus = 0 + +/datum/uplink_item/device_tools/shield + name = "Energy Shield" + desc = "An incredibly useful personal shield projector, capable of reflecting energy projectiles and defending against other attacks." + item = /obj/item/weapon/shield/energy + cost = 16 + gamemodes = list(/datum/game_mode/nuclear,/datum/game_mode/gang) + surplus = 20 + +/datum/uplink_item/device_tools/medgun + name = "Medbeam Gun" + desc = "Medical Beam Gun, useful in prolonged firefights." + item = /obj/item/weapon/gun/medbeam + cost = 15 + gamemodes = list(/datum/game_mode/nuclear) + + +// IMPLANTS + +/datum/uplink_item/implants + category = "Implants" + +/datum/uplink_item/implants/freedom + name = "Freedom Implant" + desc = "An implant injected into the body and later activated at the user's will. It will attempt to free the user from common restraints such as handcuffs." + item = /obj/item/weapon/storage/box/syndie_kit/imp_freedom + cost = 5 + +/datum/uplink_item/implants/uplink + name = "Uplink Implant" + desc = "An implant injected into the body, and later activated at the user's will. It will open a separate uplink with 10 telecrystals. The ability to have these telecrystals, combined with no easy \ + way to detect the ipmlant, makes this excellent for escaping confinement." + item = /obj/item/weapon/storage/box/syndie_kit/imp_uplink + cost = 14 + surplus = 0 + +/datum/uplink_item/implants/adrenal + name = "Adrenal Implant" + desc = "An implant injected into the body, and later activated at the user's will. It will inject a chemical cocktail which has a mild healing effect along with removing all stuns and increasing movement speed." + item = /obj/item/weapon/storage/box/syndie_kit/imp_adrenal + cost = 8 + +/datum/uplink_item/implants/storage + name = "Storage Implant" + desc = "An implant injected into the body, and later activated at the user's will. It will open a small subspace pocket capable of storing two items." + item = /obj/item/weapon/storage/box/syndie_kit/imp_storage + cost = 8 + +/datum/uplink_item/implants/microbomb + name = "Microbomb Implant" + desc = "An implant injected into the body, and later activated either manually or automatically upon death. The more implants inside of you, the higher the explosive power. \ + This will permanently destroy your body, however." + item = /obj/item/weapon/storage/box/syndie_kit/imp_microbomb + cost = 2 + gamemodes = list(/datum/game_mode/nuclear) + + +//CYBERNETIC IMPLANTS + +/datum/uplink_item/cyber_implants + category = "Cybernetic Implants" + gamemodes = list(/datum/game_mode/nuclear) + surplus = 0 + +/datum/uplink_item/cyber_implants/thermals + name = "Thermal Vision Implant" + desc = "These cybernetic eyes will give you thermal vision. They must be implanted via surgery." + item = /obj/item/organ/internal/cyberimp/eyes/thermals + cost = 8 + +/datum/uplink_item/cyber_implants/xray + name = "X-Ray Vision Implant" + desc = "These cybernetic eyes will give you X-ray vision. They must be implanted via surgery." + item = /obj/item/organ/internal/cyberimp/eyes/xray + cost = 10 + +/datum/uplink_item/cyber_implants/antistun + name = "CNS Rebooter Implant" + desc = "This implant will help you get back up on your feet faster after being stunned. It must be implanted via surgery." + item = /obj/item/organ/internal/cyberimp/brain/anti_stun + cost = 12 + +/datum/uplink_item/cyber_implants/reviver + name = "Reviver Implant" + desc = "This implant will attempt to revive you if you lose consciousness. It must be implanted via surgery." + item = /obj/item/organ/internal/cyberimp/chest/reviver + cost = 8 + +/datum/uplink_item/cyber_implants/bundle + name = "Cybernetic Implants Bundle" + desc = "A random selection of cybernetic implants. Guaranteed 5 high quality implants. They must be implanted via surgery." + item = /obj/item/weapon/storage/box/cyber_implants + cost = 40 + +// POINTLESS BADASSERY + +/datum/uplink_item/badass + category = "(Pointless) Badassery" + surplus = 0 + last = 1 + +/datum/uplink_item/badass/syndiecigs + name = "Syndicate Smokes" + desc = "Strong flavor, dense smoke, infused with omnizine." + item = /obj/item/weapon/storage/fancy/cigarettes/cigpack_syndicate + cost = 2 + +/datum/uplink_item/badass/bundle + name = "Syndicate Bundle" + desc = "Syndicate Bundles are specialised groups of items that arrive in a plain box. These items are collectively worth more than 20 telecrystals, but you do not know which specialisation you will receive." + item = /obj/item/weapon/storage/box/syndicate + cost = 20 + excludefrom = list(/datum/game_mode/nuclear,/datum/game_mode/gang) + +/datum/uplink_item/badass/syndiecards + name = "Syndicate Playing Cards" + desc = "A special deck of space-grade playing cards with a mono-molecular edge and metal reinforcement, making them slightly more robust than a normal deck of cards. \ + You can also play card games with them or leave them on your victims." + item = /obj/item/toy/cards/deck/syndicate + cost = 1 + surplus = 40 + +/datum/uplink_item/badass/syndiecash + name = "Syndicate Briefcase Full of Cash" + desc = "A secure briefcase containing 5000 space credits. Useful for bribing personnel, or purchasing goods and services at lucrative prices. \ + The briefcase also feels a little heavier to hold; it has been manufactured to pack a little bit more of a punch if your client needs some convincing." + item = /obj/item/weapon/storage/secure/briefcase/syndie + cost = 1 + +/datum/uplink_item/badass/balloon + name = "For showing that you are THE BOSS" + desc = "A useless red balloon with the Syndicate logo on it. Can blow the deepest of covers." + item = /obj/item/toy/syndicateballoon + cost = 20 + +/datum/uplink_item/implants/macrobomb + name = "Macrobomb Implant" + desc = "An implant injected into the body, and later activated either manually or automatically upon death. Upon death, releases a massive explosion that will wipe out everything nearby." + item = /obj/item/weapon/storage/box/syndie_kit/imp_macrobomb + cost = 20 + gamemodes = list(/datum/game_mode/nuclear) + +/datum/uplink_item/badass/random + name = "Random Item" + desc = "Picking this choice will send you a random item from the list. Useful for when you cannot think of a strategy to finish your objectives with." + item = /obj/item/weapon/storage/box/syndicate + cost = 0 + +/datum/uplink_item/badass/random/spawn_item(turf/loc, obj/item/device/uplink/U) + + var/list/buyable_items = get_uplink_items() + var/list/possible_items = list() + + for(var/category in buyable_items) + for(var/datum/uplink_item/I in buyable_items[category]) + if(I == src) + continue + if(I.cost > U.uses) + continue + possible_items += I + + if(possible_items.len) + var/datum/uplink_item/I = pick(possible_items) + U.uses -= max(0, I.cost) + feedback_add_details("traitor_uplink_items_bought","RN") + return new I.item(loc) + +/datum/uplink_item/badass/surplus_crate + name = "Syndicate Surplus Crate" + desc = "A crate containing 50 telecrystals worth of random syndicate leftovers." + cost = 20 + item = /obj/item/weapon/storage/box/syndicate + excludefrom = list(/datum/game_mode/nuclear) + +/datum/uplink_item/badass/surplus_crate/spawn_item(turf/loc, obj/item/device/uplink/U) + var/obj/structure/closet/crate/C = new(loc) + var/list/temp_uplink_list = get_uplink_items() + var/list/buyable_items = list() + for(var/category in temp_uplink_list) + buyable_items += temp_uplink_list[category] + var/list/bought_items = list() + U.uses -= cost + U.used_TC = 20 + var/remaining_TC = 50 + + var/datum/uplink_item/I + while(remaining_TC) + I = pick(buyable_items) + if(!I.surplus) + continue + if(I.cost > remaining_TC) + continue + if((I.item in bought_items) && prob(33)) //To prevent people from being flooded with the same thing over and over again. + continue + bought_items += I.item + remaining_TC -= I.cost + + U.purchase_log += "\icon[C]" + for(var/item in bought_items) + new item(C) U.purchase_log += "\icon[item]" \ No newline at end of file diff --git a/code/game/gamemodes/blob/blobs/core.dm b/code/game/gamemodes/blob/blobs/core.dm index d9b2553e6ea..be72cbe603b 100644 --- a/code/game/gamemodes/blob/blobs/core.dm +++ b/code/game/gamemodes/blob/blobs/core.dm @@ -1,117 +1,117 @@ -/obj/effect/blob/core - name = "blob core" - icon = 'icons/mob/blob.dmi' - icon_state = "blank_blob" - desc = "A huge, pulsating yellow mass." - health = 400 - maxhealth = 400 - explosion_block = 6 - point_return = -1 - atmosblock = 1 - var/overmind_get_delay = 0 // we don't want to constantly try to find an overmind, do it every 30 seconds - var/resource_delay = 0 - var/point_rate = 2 - var/is_offspring = null - - -/obj/effect/blob/core/New(loc, var/h = 200, var/client/new_overmind = null, var/new_rate = 2, offspring) - blob_cores += src - SSobj.processing |= src - update_icon() //so it atleast appears - if(!overmind) - create_overmind(new_overmind) - if(overmind) - update_icon() - if(offspring) - is_offspring = 1 - point_rate = new_rate - ..(loc, h) - -/obj/effect/blob/core/update_icon() - overlays.Cut() - color = null - var/image/I = new('icons/mob/blob.dmi', "blob") - if(overmind) - I.color = overmind.blob_reagent_datum.color - overlays += I - var/image/C = new('icons/mob/blob.dmi', "blob_core_overlay") - overlays += C - -/obj/effect/blob/core/PulseAnimation() - return - -/obj/effect/blob/core/Destroy() - blob_cores -= src - if(overmind) - overmind.blob_core = null - overmind = null - SSobj.processing.Remove(src) - return ..() - -/obj/effect/blob/core/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) - return - -/obj/effect/blob/core/ex_act(severity, target) - return - -/obj/effect/blob/core/check_health() - ..() - if(overmind) //we should have an overmind, but... - overmind.update_health() - -/obj/effect/blob/core/RegenHealth() - return // Don't regen, we handle it in Life() - -/obj/effect/blob/core/Life() - if(!overmind) - create_overmind() - else - if(resource_delay <= world.time) - resource_delay = world.time + 10 // 1 second - overmind.add_points(point_rate) - health = min(maxhealth, health+health_regen) - if(overmind) - overmind.update_health() - Pulse_Area(overmind, 12, 4, 3) - for(var/b_dir in alldirs) - if(!prob(5)) - continue - var/obj/effect/blob/normal/B = locate() in get_step(src, b_dir) - if(B) - B.change_to(/obj/effect/blob/shield, overmind) - color = null - ..() - - -/obj/effect/blob/core/proc/create_overmind(client/new_overmind, override_delay) - if(overmind_get_delay > world.time && !override_delay) - return - - overmind_get_delay = world.time + 300 // 30 seconds - - if(overmind) - qdel(overmind) - - var/client/C = null - var/list/candidates = list() - - if(!new_overmind) - candidates = get_candidates(ROLE_BLOB) - if(candidates.len) - C = pick(candidates) - else - C = new_overmind - - if(C) - var/mob/camera/blob/B = new(src.loc) - B.key = C.key - B.blob_core = src - src.overmind = B - color = overmind.blob_reagent_datum.color - if(B.mind && !B.mind.special_role) - B.mind.special_role = "Blob Overmind" - spawn(0) - if(is_offspring) - B.verbs -= /mob/camera/blob/verb/split_consciousness - return 1 - return 0 +/obj/effect/blob/core + name = "blob core" + icon = 'icons/mob/blob.dmi' + icon_state = "blank_blob" + desc = "A huge, pulsating yellow mass." + health = 400 + maxhealth = 400 + explosion_block = 6 + point_return = -1 + atmosblock = 1 + var/overmind_get_delay = 0 // we don't want to constantly try to find an overmind, do it every 30 seconds + var/resource_delay = 0 + var/point_rate = 2 + var/is_offspring = null + + +/obj/effect/blob/core/New(loc, var/h = 200, var/client/new_overmind = null, var/new_rate = 2, offspring) + blob_cores += src + SSobj.processing |= src + update_icon() //so it atleast appears + if(!overmind) + create_overmind(new_overmind) + if(overmind) + update_icon() + if(offspring) + is_offspring = 1 + point_rate = new_rate + ..(loc, h) + +/obj/effect/blob/core/update_icon() + overlays.Cut() + color = null + var/image/I = new('icons/mob/blob.dmi', "blob") + if(overmind) + I.color = overmind.blob_reagent_datum.color + overlays += I + var/image/C = new('icons/mob/blob.dmi', "blob_core_overlay") + overlays += C + +/obj/effect/blob/core/PulseAnimation() + return + +/obj/effect/blob/core/Destroy() + blob_cores -= src + if(overmind) + overmind.blob_core = null + overmind = null + SSobj.processing.Remove(src) + return ..() + +/obj/effect/blob/core/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) + return + +/obj/effect/blob/core/ex_act(severity, target) + return + +/obj/effect/blob/core/check_health() + ..() + if(overmind) //we should have an overmind, but... + overmind.update_health() + +/obj/effect/blob/core/RegenHealth() + return // Don't regen, we handle it in Life() + +/obj/effect/blob/core/Life() + if(!overmind) + create_overmind() + else + if(resource_delay <= world.time) + resource_delay = world.time + 10 // 1 second + overmind.add_points(point_rate) + health = min(maxhealth, health+health_regen) + if(overmind) + overmind.update_health() + Pulse_Area(overmind, 12, 4, 3) + for(var/b_dir in alldirs) + if(!prob(5)) + continue + var/obj/effect/blob/normal/B = locate() in get_step(src, b_dir) + if(B) + B.change_to(/obj/effect/blob/shield, overmind) + color = null + ..() + + +/obj/effect/blob/core/proc/create_overmind(client/new_overmind, override_delay) + if(overmind_get_delay > world.time && !override_delay) + return + + overmind_get_delay = world.time + 300 // 30 seconds + + if(overmind) + qdel(overmind) + + var/client/C = null + var/list/candidates = list() + + if(!new_overmind) + candidates = get_candidates(ROLE_BLOB) + if(candidates.len) + C = pick(candidates) + else + C = new_overmind + + if(C) + var/mob/camera/blob/B = new(src.loc) + B.key = C.key + B.blob_core = src + src.overmind = B + color = overmind.blob_reagent_datum.color + if(B.mind && !B.mind.special_role) + B.mind.special_role = "Blob Overmind" + spawn(0) + if(is_offspring) + B.verbs -= /mob/camera/blob/verb/split_consciousness + return 1 + return 0 diff --git a/code/game/gamemodes/blob/blobs/factory.dm b/code/game/gamemodes/blob/blobs/factory.dm index 30ba94d4a37..a70842aa7c4 100644 --- a/code/game/gamemodes/blob/blobs/factory.dm +++ b/code/game/gamemodes/blob/blobs/factory.dm @@ -1,39 +1,39 @@ -/obj/effect/blob/factory - name = "factory blob" - icon = 'icons/mob/blob.dmi' - icon_state = "blob_factory" - desc = "A thick spire of tendrils." - health = 200 - maxhealth = 200 - point_return = 25 - var/list/spores = list() - var/max_spores = 3 - var/spore_delay = 0 - - -/obj/effect/blob/factory/Destroy() - for(var/mob/living/simple_animal/hostile/blob/blobspore/spore in spores) - if(spore.factory == src) - spore.factory = null - spores = null - return ..() - -/obj/effect/blob/factory/PulseAnimation(activate = 0) - if(activate) - ..() - return - -/obj/effect/blob/factory/run_action() - if(spores.len >= max_spores) - return 0 - if(spore_delay > world.time) - return 0 - spore_delay = world.time + 100 // 10 seconds - PulseAnimation(1) - var/mob/living/simple_animal/hostile/blob/blobspore/BS = new/mob/living/simple_animal/hostile/blob/blobspore(src.loc, src) - if(overmind) //if we don't have an overmind, we don't need to do anything but make a spore - BS.overmind = overmind - BS.update_icons() - overmind.blob_mobs.Add(BS) - return 0 - +/obj/effect/blob/factory + name = "factory blob" + icon = 'icons/mob/blob.dmi' + icon_state = "blob_factory" + desc = "A thick spire of tendrils." + health = 200 + maxhealth = 200 + point_return = 25 + var/list/spores = list() + var/max_spores = 3 + var/spore_delay = 0 + + +/obj/effect/blob/factory/Destroy() + for(var/mob/living/simple_animal/hostile/blob/blobspore/spore in spores) + if(spore.factory == src) + spore.factory = null + spores = null + return ..() + +/obj/effect/blob/factory/PulseAnimation(activate = 0) + if(activate) + ..() + return + +/obj/effect/blob/factory/run_action() + if(spores.len >= max_spores) + return 0 + if(spore_delay > world.time) + return 0 + spore_delay = world.time + 100 // 10 seconds + PulseAnimation(1) + var/mob/living/simple_animal/hostile/blob/blobspore/BS = new/mob/living/simple_animal/hostile/blob/blobspore(src.loc, src) + if(overmind) //if we don't have an overmind, we don't need to do anything but make a spore + BS.overmind = overmind + BS.update_icons() + overmind.blob_mobs.Add(BS) + return 0 + diff --git a/code/game/gamemodes/blob/overmind.dm b/code/game/gamemodes/blob/overmind.dm index 2af309967a4..fb7219f28f2 100644 --- a/code/game/gamemodes/blob/overmind.dm +++ b/code/game/gamemodes/blob/overmind.dm @@ -1,276 +1,276 @@ -/mob/camera/blob - name = "Blob Overmind" - real_name = "Blob Overmind" - icon = 'icons/mob/blob.dmi' - icon_state = "marker" - - see_in_dark = 8 - see_invisible = SEE_INVISIBLE_MINIMUM - invisibility = INVISIBILITY_OBSERVER - - pass_flags = PASSBLOB - faction = list("blob") - - var/obj/effect/blob/core/blob_core = null // The blob overmind's core - var/blob_points = 0 - var/max_blob_points = 100 - var/storage_blobs = 0 - var/last_attack = 0 - var/datum/reagent/blob/blob_reagent_datum = new/datum/reagent/blob() - var/list/blob_mobs = list() - var/ghostimage = null - -/mob/camera/blob/New() - var/new_name = "[initial(name)] ([rand(1, 999)])" - name = new_name - real_name = new_name - last_attack = world.time - var/list/possible_reagents = list() - for(var/type in (subtypesof(/datum/reagent/blob))) - possible_reagents.Add(new type) - blob_reagent_datum = pick(possible_reagents) - if(blob_core) - blob_core.update_icon() - - ghostimage = image(src.icon,src,src.icon_state) - ghost_darkness_images |= ghostimage //so ghosts can see the blob cursor when they disable darkness - updateallghostimages() - ..() - -/mob/camera/blob/Life() - if(!blob_core) - qdel(src) - ..() - -/mob/camera/blob/Destroy() - if (ghostimage) - ghost_darkness_images -= ghostimage - qdel(ghostimage) - ghostimage = null; - updateallghostimages() - return ..() - -/mob/camera/blob/Login() - ..() - sync_mind() - src << "You are the overmind!" - blob_help() - update_health() - -/mob/camera/blob/proc/update_health() - if(blob_core) - hud_used.blobhealthdisplay.maptext = "
[round(blob_core.health)]
" - -/mob/camera/blob/proc/add_points(points) - if(points != 0) - blob_points = Clamp(blob_points + points, 0, max_blob_points) - hud_used.blobpwrdisplay.maptext = "
[round(src.blob_points)]
" - -/mob/camera/blob/say(message) - if (!message) - return - - if (src.client) - if(client.prefs.muted & MUTE_IC) - src << "You cannot send IC messages (muted)." - return - if (src.client.handle_spam_prevention(message,MUTE_IC)) - return - - if (stat) - return - - blob_talk(message) - -/mob/camera/blob/proc/blob_talk(message) - log_say("[key_name(src)] : [message]") - - message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) - - if (!message) - return - - var/message_a = say_quote(message, get_spans()) - var/rendered = "Blob Telepathy, [name]([blob_reagent_datum.name]) [message_a]" - - for (var/mob/M in mob_list) - if(isovermind(M) || isobserver(M)) - M.show_message(rendered, 2) - -/mob/camera/blob/emote(act,m_type=1,message = null) - return - -/mob/camera/blob/blob_act() - return - -/mob/camera/blob/Stat() - ..() - if(statpanel("Status")) - if(blob_core) - stat(null, "Core Health: [blob_core.health]") - stat(null, "Power Stored: [blob_points]/[max_blob_points]") - -/mob/camera/blob/Move(NewLoc, Dir = 0) - var/obj/effect/blob/B = locate() in range("3x3", NewLoc) - if(B) - loc = NewLoc - else - return 0 - -/mob/camera/blob/proc/can_attack() - return (world.time > (last_attack + CLICK_CD_RANGE)) - - -/obj/screen/blob - icon = 'icons/mob/blob.dmi' - -/obj/screen/blob/BlobHelp - icon_state = "ui_help" - name = "Blob Help" - desc = "Help on playing blob!" - -/obj/screen/blob/BlobHelp/Click() - if(isovermind(usr)) - var/mob/camera/blob/B = usr - B.blob_help() - -/obj/screen/blob/JumpToNode - icon_state = "ui_tonode" - name = "Jump to Node" - desc = "Moves your camera to a selected blob node." - -/obj/screen/blob/JumpToNode/Click() - if(isovermind(usr)) - var/mob/camera/blob/B = usr - B.jump_to_node() - -/obj/screen/blob/JumpToCore - icon_state = "ui_tocore" - name = "Jump to Core" - desc = "Moves your camera to your blob core." - -/obj/screen/blob/JumpToCore/Click() - if(isovermind(usr)) - var/mob/camera/blob/B = usr - B.transport_core() - -/obj/screen/blob/StorageBlob - icon_state = "ui_factory" - name = "Produce Storage Blob (20)" - desc = "Produces a storage blob for 20 points." - -/obj/screen/blob/StorageBlob/Click() - if(isovermind(usr)) - var/mob/camera/blob/B = usr - B.create_storage() - -/obj/screen/blob/ResourceBlob - icon_state = "ui_resource" - name = "Produce Resource Blob (40)" - desc = "Produces a resource blob for 40 points." - -/obj/screen/blob/ResourceBlob/Click() - if(isovermind(usr)) - var/mob/camera/blob/B = usr - B.create_resource() - -/obj/screen/blob/NodeBlob - icon_state = "ui_node" - name = "Produce Node Blob (60)" - desc = "Produces a node blob for 60 points." - -/obj/screen/blob/NodeBlob/Click() - if(isovermind(usr)) - var/mob/camera/blob/B = usr - B.create_node() - -/obj/screen/blob/FactoryBlob - icon_state = "ui_factory" - name = "Produce Factory Blob (60)" - desc = "Produces a resource blob for 60 points." - -/obj/screen/blob/FactoryBlob/Click() - if(isovermind(usr)) - var/mob/camera/blob/B = usr - B.create_factory() - -/obj/screen/blob/ReadaptChemical - icon_state = "ui_chemswap" - name = "Readapt Chemical (40)" - desc = "Randomly rerolls your chemical for 40 points." - -/obj/screen/blob/ReadaptChemical/Click() - if(isovermind(usr)) - var/mob/camera/blob/B = usr - B.chemical_reroll() - -/obj/screen/blob/RelocateCore - icon_state = "ui_swap" - name = "Relocate Core (80)" - desc = "Swaps a node and your core for 80 points." - -/obj/screen/blob/RelocateCore/Click() - if(isovermind(usr)) - var/mob/camera/blob/B = usr - B.relocate_core() - -/datum/hud/proc/blob_hud(ui_style = 'icons/mob/screen_midnight.dmi') - adding = list() - - var/obj/screen/using - - blobpwrdisplay = new /obj/screen() - blobpwrdisplay.name = "blob power" - blobpwrdisplay.icon_state = "block" - blobpwrdisplay.screen_loc = ui_health - blobpwrdisplay.mouse_opacity = 0 - blobpwrdisplay.layer = 20 - adding += blobpwrdisplay - - blobhealthdisplay = new /obj/screen() - blobhealthdisplay.name = "blob health" - blobhealthdisplay.icon_state = "block" - blobhealthdisplay.screen_loc = ui_internal - blobhealthdisplay.mouse_opacity = 0 - blobhealthdisplay.layer = 20 - adding += blobhealthdisplay - - using = new /obj/screen/blob/BlobHelp() - using.screen_loc = "NORTH:-6,WEST:6" - adding += using - - using = new /obj/screen/blob/JumpToNode() - using.screen_loc = ui_inventory - adding += using - - using = new /obj/screen/blob/JumpToCore() - using.screen_loc = ui_zonesel - adding += using - - using = new /obj/screen/blob/StorageBlob() - using.screen_loc = ui_belt - adding += using - - using = new /obj/screen/blob/ResourceBlob() - using.screen_loc = ui_back - adding += using - - using = new /obj/screen/blob/NodeBlob() - using.screen_loc = ui_lhand - adding += using - - using = new /obj/screen/blob/FactoryBlob() - using.screen_loc = ui_rhand - adding += using - - using = new /obj/screen/blob/ReadaptChemical() - using.screen_loc = ui_storage1 - adding += using - - using = new /obj/screen/blob/RelocateCore() - using.screen_loc = ui_storage2 - adding += using - - mymob.client.screen = list() - mymob.client.screen += mymob.client.void - mymob.client.screen += adding +/mob/camera/blob + name = "Blob Overmind" + real_name = "Blob Overmind" + icon = 'icons/mob/blob.dmi' + icon_state = "marker" + + see_in_dark = 8 + see_invisible = SEE_INVISIBLE_MINIMUM + invisibility = INVISIBILITY_OBSERVER + + pass_flags = PASSBLOB + faction = list("blob") + + var/obj/effect/blob/core/blob_core = null // The blob overmind's core + var/blob_points = 0 + var/max_blob_points = 100 + var/storage_blobs = 0 + var/last_attack = 0 + var/datum/reagent/blob/blob_reagent_datum = new/datum/reagent/blob() + var/list/blob_mobs = list() + var/ghostimage = null + +/mob/camera/blob/New() + var/new_name = "[initial(name)] ([rand(1, 999)])" + name = new_name + real_name = new_name + last_attack = world.time + var/list/possible_reagents = list() + for(var/type in (subtypesof(/datum/reagent/blob))) + possible_reagents.Add(new type) + blob_reagent_datum = pick(possible_reagents) + if(blob_core) + blob_core.update_icon() + + ghostimage = image(src.icon,src,src.icon_state) + ghost_darkness_images |= ghostimage //so ghosts can see the blob cursor when they disable darkness + updateallghostimages() + ..() + +/mob/camera/blob/Life() + if(!blob_core) + qdel(src) + ..() + +/mob/camera/blob/Destroy() + if (ghostimage) + ghost_darkness_images -= ghostimage + qdel(ghostimage) + ghostimage = null; + updateallghostimages() + return ..() + +/mob/camera/blob/Login() + ..() + sync_mind() + src << "You are the overmind!" + blob_help() + update_health() + +/mob/camera/blob/proc/update_health() + if(blob_core) + hud_used.blobhealthdisplay.maptext = "
[round(blob_core.health)]
" + +/mob/camera/blob/proc/add_points(points) + if(points != 0) + blob_points = Clamp(blob_points + points, 0, max_blob_points) + hud_used.blobpwrdisplay.maptext = "
[round(src.blob_points)]
" + +/mob/camera/blob/say(message) + if (!message) + return + + if (src.client) + if(client.prefs.muted & MUTE_IC) + src << "You cannot send IC messages (muted)." + return + if (src.client.handle_spam_prevention(message,MUTE_IC)) + return + + if (stat) + return + + blob_talk(message) + +/mob/camera/blob/proc/blob_talk(message) + log_say("[key_name(src)] : [message]") + + message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) + + if (!message) + return + + var/message_a = say_quote(message, get_spans()) + var/rendered = "Blob Telepathy, [name]([blob_reagent_datum.name]) [message_a]" + + for (var/mob/M in mob_list) + if(isovermind(M) || isobserver(M)) + M.show_message(rendered, 2) + +/mob/camera/blob/emote(act,m_type=1,message = null) + return + +/mob/camera/blob/blob_act() + return + +/mob/camera/blob/Stat() + ..() + if(statpanel("Status")) + if(blob_core) + stat(null, "Core Health: [blob_core.health]") + stat(null, "Power Stored: [blob_points]/[max_blob_points]") + +/mob/camera/blob/Move(NewLoc, Dir = 0) + var/obj/effect/blob/B = locate() in range("3x3", NewLoc) + if(B) + loc = NewLoc + else + return 0 + +/mob/camera/blob/proc/can_attack() + return (world.time > (last_attack + CLICK_CD_RANGE)) + + +/obj/screen/blob + icon = 'icons/mob/blob.dmi' + +/obj/screen/blob/BlobHelp + icon_state = "ui_help" + name = "Blob Help" + desc = "Help on playing blob!" + +/obj/screen/blob/BlobHelp/Click() + if(isovermind(usr)) + var/mob/camera/blob/B = usr + B.blob_help() + +/obj/screen/blob/JumpToNode + icon_state = "ui_tonode" + name = "Jump to Node" + desc = "Moves your camera to a selected blob node." + +/obj/screen/blob/JumpToNode/Click() + if(isovermind(usr)) + var/mob/camera/blob/B = usr + B.jump_to_node() + +/obj/screen/blob/JumpToCore + icon_state = "ui_tocore" + name = "Jump to Core" + desc = "Moves your camera to your blob core." + +/obj/screen/blob/JumpToCore/Click() + if(isovermind(usr)) + var/mob/camera/blob/B = usr + B.transport_core() + +/obj/screen/blob/StorageBlob + icon_state = "ui_factory" + name = "Produce Storage Blob (20)" + desc = "Produces a storage blob for 20 points." + +/obj/screen/blob/StorageBlob/Click() + if(isovermind(usr)) + var/mob/camera/blob/B = usr + B.create_storage() + +/obj/screen/blob/ResourceBlob + icon_state = "ui_resource" + name = "Produce Resource Blob (40)" + desc = "Produces a resource blob for 40 points." + +/obj/screen/blob/ResourceBlob/Click() + if(isovermind(usr)) + var/mob/camera/blob/B = usr + B.create_resource() + +/obj/screen/blob/NodeBlob + icon_state = "ui_node" + name = "Produce Node Blob (60)" + desc = "Produces a node blob for 60 points." + +/obj/screen/blob/NodeBlob/Click() + if(isovermind(usr)) + var/mob/camera/blob/B = usr + B.create_node() + +/obj/screen/blob/FactoryBlob + icon_state = "ui_factory" + name = "Produce Factory Blob (60)" + desc = "Produces a resource blob for 60 points." + +/obj/screen/blob/FactoryBlob/Click() + if(isovermind(usr)) + var/mob/camera/blob/B = usr + B.create_factory() + +/obj/screen/blob/ReadaptChemical + icon_state = "ui_chemswap" + name = "Readapt Chemical (40)" + desc = "Randomly rerolls your chemical for 40 points." + +/obj/screen/blob/ReadaptChemical/Click() + if(isovermind(usr)) + var/mob/camera/blob/B = usr + B.chemical_reroll() + +/obj/screen/blob/RelocateCore + icon_state = "ui_swap" + name = "Relocate Core (80)" + desc = "Swaps a node and your core for 80 points." + +/obj/screen/blob/RelocateCore/Click() + if(isovermind(usr)) + var/mob/camera/blob/B = usr + B.relocate_core() + +/datum/hud/proc/blob_hud(ui_style = 'icons/mob/screen_midnight.dmi') + adding = list() + + var/obj/screen/using + + blobpwrdisplay = new /obj/screen() + blobpwrdisplay.name = "blob power" + blobpwrdisplay.icon_state = "block" + blobpwrdisplay.screen_loc = ui_health + blobpwrdisplay.mouse_opacity = 0 + blobpwrdisplay.layer = 20 + adding += blobpwrdisplay + + blobhealthdisplay = new /obj/screen() + blobhealthdisplay.name = "blob health" + blobhealthdisplay.icon_state = "block" + blobhealthdisplay.screen_loc = ui_internal + blobhealthdisplay.mouse_opacity = 0 + blobhealthdisplay.layer = 20 + adding += blobhealthdisplay + + using = new /obj/screen/blob/BlobHelp() + using.screen_loc = "NORTH:-6,WEST:6" + adding += using + + using = new /obj/screen/blob/JumpToNode() + using.screen_loc = ui_inventory + adding += using + + using = new /obj/screen/blob/JumpToCore() + using.screen_loc = ui_zonesel + adding += using + + using = new /obj/screen/blob/StorageBlob() + using.screen_loc = ui_belt + adding += using + + using = new /obj/screen/blob/ResourceBlob() + using.screen_loc = ui_back + adding += using + + using = new /obj/screen/blob/NodeBlob() + using.screen_loc = ui_lhand + adding += using + + using = new /obj/screen/blob/FactoryBlob() + using.screen_loc = ui_rhand + adding += using + + using = new /obj/screen/blob/ReadaptChemical() + using.screen_loc = ui_storage1 + adding += using + + using = new /obj/screen/blob/RelocateCore() + using.screen_loc = ui_storage2 + adding += using + + mymob.client.screen = list() + mymob.client.screen += mymob.client.void + mymob.client.screen += adding diff --git a/code/game/gamemodes/blob/theblob.dm b/code/game/gamemodes/blob/theblob.dm index 2b538a48fc6..6e89a082c1d 100644 --- a/code/game/gamemodes/blob/theblob.dm +++ b/code/game/gamemodes/blob/theblob.dm @@ -1,273 +1,273 @@ -//I will need to recode parts of this but I am way too tired atm //I don't know who left this comment but they never did come back -/obj/effect/blob - name = "blob" - icon = 'icons/mob/blob.dmi' - luminosity = 1 - desc = "A thick wall of writhing tendrils." - density = 0 //this being 0 causes two bugs, being able to attack blob tiles behind other blobs and being unable to move on blob tiles in no gravity, but turning it to 1 causes the blob mobs to be unable to path through blobs, which is probably worse. - opacity = 0 - anchored = 1 - explosion_block = 1 - var/point_return = 0 //How many points the blob gets back when it removes a blob of that type. If less than 0, blob cannot be removed. - var/health = 30 - var/maxhealth = 30 - var/health_regen = 2 //how much health this blob regens when pulsed - var/health_timestamp = 0 //we got healed when? - var/pulse_timestamp = 0 //we got pulsed when? - var/brute_resist = 2 //divides brute damage by this - var/fire_resist = 1 //divides burn damage by this - var/atmosblock = 0 //if the blob blocks atmos and heat spread - var/mob/camera/blob/overmind - - -/obj/effect/blob/New(loc) - var/area/Ablob = get_area(loc) - if(Ablob.blob_allowed) //Is this area allowed for winning as blob? - blobs_legit += src - blobs += src //Keep track of the blob in the normal list either way - src.dir = pick(1, 2, 4, 8) - src.update_icon() - ..(loc) - ConsumeTile() - if(atmosblock) - air_update_turf(1) - return - -/obj/effect/blob/proc/creation_action() //When it's created by the overmind, do this. - return - -/obj/effect/blob/Destroy() - if(atmosblock) - atmosblock = 0 - air_update_turf(1) - var/area/Ablob = get_area(loc) - if(Ablob.blob_allowed) //Only remove for blobs in areas that counted for the win - blobs_legit -= src - blobs -= src //It's still removed from the normal list - playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) //Expand() is no longer broken, no check necessary. - return ..() - - -/obj/effect/blob/CanAtmosPass(turf/T) - return !atmosblock - -/obj/effect/blob/BlockSuperconductivity() - return atmosblock - -/obj/effect/blob/CanPass(atom/movable/mover, turf/target, height=0) - if(height==0) - return 1 - if(istype(mover) && mover.checkpass(PASSBLOB)) - return 1 - return 0 - - -/obj/effect/blob/proc/check_health() - if(health <= 0) - qdel(src) //we dead now - return - return - -/obj/effect/blob/update_icon() //Updates color based on overmind color if we have an overmind. - if(overmind) - color = overmind.blob_reagent_datum.color - else - color = null - return - - -/obj/effect/blob/process() - Life() - return - -/obj/effect/blob/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) - ..() - var/damage = Clamp(0.01 * exposed_temperature, 0, 4) - take_damage(damage, BURN) - -/obj/effect/blob/proc/Life() - return - -/obj/effect/blob/proc/Pulse_Area(pulsing_overmind = overmind, claim_range = 10, pulse_range = 3, expand_range = 2) - src.Be_Pulsed() - if(claim_range) - for(var/obj/effect/blob/B in ultra_range(claim_range, src, 1)) - B.update_icon() - if(!B.overmind && !istype(B, /obj/effect/blob/core) && prob(30)) - B.overmind = pulsing_overmind //reclaim unclaimed, non-core blobs. - B.update_icon() - if(pulse_range) - for(var/obj/effect/blob/B in orange(pulse_range, src)) - B.Be_Pulsed() - if(expand_range) - src.expand() - for(var/obj/effect/blob/B in orange(expand_range, src)) - if(prob(12)) - B.expand() - return - -/obj/effect/blob/proc/Be_Pulsed() - if(pulse_timestamp <= world.time) - PulseAnimation() - ConsumeTile() - RegenHealth() - run_action() - pulse_timestamp = world.time + 10 - return 1 //we did it, we were pulsed! - return 0 //oh no we failed - -/obj/effect/blob/proc/ConsumeTile() - for(var/atom/A in loc) - A.blob_act() - -/obj/effect/blob/proc/PulseAnimation() - flick("[icon_state]_glow", src) - return - -/obj/effect/blob/proc/RegenHealth() //when pulsed, heal! - if(health_timestamp <= world.time) - health = min(maxhealth, health+health_regen) - update_icon() - health_timestamp = world.time + 10 //1 second between heals - return 1 - return 0 - -/obj/effect/blob/proc/run_action() - return 0 - - -/obj/effect/blob/proc/expand(turf/T = null, prob = 1, controller = null) - if(prob && !prob(health)) - return - if(!T) - var/list/dirs = list(1,2,4,8) - for(var/i = 1 to 4) - var/dirn = pick(dirs) - dirs.Remove(dirn) - T = get_step(src, dirn) - if(!(locate(/obj/effect/blob) in T)) - break - else - T = null - if(!T) - return 0 - var/make_blob = 1 //can we make a blob? - if(istype(T, /turf/space) && prob(65)) - make_blob = 0 - playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) //Let's give some feedback that we DID try to spawn in space, since players are used to it - for(var/atom/A in T) - if(A.density) - make_blob = 0 - A.blob_act() //Hit everything - if(T.density) //Check for walls and such dense turfs - make_blob = 0 - T.blob_act() //Hit the turf - if(make_blob) //well, can we? - var/obj/effect/blob/B = new /obj/effect/blob/normal(src.loc) - if(controller) - B.overmind = controller - else - B.overmind = overmind - B.density = 1 - if(T.Enter(B,src)) //NOW we can attempt to move into the tile - B.density = initial(B.density) - B.loc = T - B.update_icon() - else - T.blob_act() //If we cant move in hit the turf - qdel(B) //We should never get to this point, since we checked before moving in. Destroy blob anyway for cleanliness though - return 1 - - -/obj/effect/blob/ex_act(severity, target) - ..() - var/damage = 150 - 20 * severity - take_damage(damage, BRUTE) - -/obj/effect/blob/bullet_act(var/obj/item/projectile/Proj) - ..() - take_damage(Proj.damage, Proj.damage_type) - return 0 - -/obj/effect/blob/attackby(obj/item/weapon/W, mob/living/user, params) - user.changeNext_move(CLICK_CD_MELEE) - user.do_attack_animation(src) - playsound(src.loc, 'sound/effects/attackblob.ogg', 50, 1) - visible_message("[user] has attacked the [src.name] with \the [W]!") - if(W.damtype == BURN) - playsound(src.loc, 'sound/items/Welder.ogg', 100, 1) - take_damage(W.force, W.damtype) - -/obj/effect/blob/attack_animal(mob/living/simple_animal/M) - M.changeNext_move(CLICK_CD_MELEE) - M.do_attack_animation(src) - playsound(src.loc, 'sound/effects/attackblob.ogg', 50, 1) - visible_message("\The [M] has attacked the [src.name]!") - var/damage = rand(M.melee_damage_lower, M.melee_damage_upper) - take_damage(damage, M.melee_damage_type) - return - -/obj/effect/blob/attack_alien(mob/living/carbon/alien/humanoid/M) - M.changeNext_move(CLICK_CD_MELEE) - M.do_attack_animation(src) - playsound(src.loc, 'sound/effects/attackblob.ogg', 50, 1) - visible_message("[M] has slashed the [src.name]!") - var/damage = rand(15, 30) - take_damage(damage, BRUTE) - return - -/obj/effect/blob/proc/take_damage(damage, damage_type) - if(!damage) // Avoid divide by zero errors - return - switch(damage_type) //blobs only take brute and burn damage - if(BRUTE) - damage /= max(brute_resist, 1) - health -= damage - if(BURN) - damage /= max(fire_resist, 1) - health -= damage - update_icon() - check_health() - -/obj/effect/blob/proc/change_to(type, controller) - if(!ispath(type)) - throw EXCEPTION("change_to(): invalid type for blob") - return - var/obj/effect/blob/B = new type(src.loc) - if(controller) - B.overmind = controller - B.creation_action() - B.update_icon() - qdel(src) - return B - -/obj/effect/blob/examine(mob/user) - ..() - user << "It seems to be made of [get_chem_name()]." - return - -/obj/effect/blob/proc/get_chem_name() - if(overmind) - return overmind.blob_reagent_datum.name - return "an unknown variant" - -/obj/effect/blob/normal - icon_state = "blob" - luminosity = 0 - health = 21 - maxhealth = 25 - health_regen = 1 - brute_resist = 4 - -/obj/effect/blob/normal/update_icon() - ..() - if(health <= 10) - icon_state = "blob_damaged" - name = "fragile blob" - desc = "A thin lattice of slightly twitching tendrils." - brute_resist = 2 - else - icon_state = "blob" - name = "blob" - desc = "A thick wall of writhing tendrils." - brute_resist = 4 +//I will need to recode parts of this but I am way too tired atm //I don't know who left this comment but they never did come back +/obj/effect/blob + name = "blob" + icon = 'icons/mob/blob.dmi' + luminosity = 1 + desc = "A thick wall of writhing tendrils." + density = 0 //this being 0 causes two bugs, being able to attack blob tiles behind other blobs and being unable to move on blob tiles in no gravity, but turning it to 1 causes the blob mobs to be unable to path through blobs, which is probably worse. + opacity = 0 + anchored = 1 + explosion_block = 1 + var/point_return = 0 //How many points the blob gets back when it removes a blob of that type. If less than 0, blob cannot be removed. + var/health = 30 + var/maxhealth = 30 + var/health_regen = 2 //how much health this blob regens when pulsed + var/health_timestamp = 0 //we got healed when? + var/pulse_timestamp = 0 //we got pulsed when? + var/brute_resist = 2 //divides brute damage by this + var/fire_resist = 1 //divides burn damage by this + var/atmosblock = 0 //if the blob blocks atmos and heat spread + var/mob/camera/blob/overmind + + +/obj/effect/blob/New(loc) + var/area/Ablob = get_area(loc) + if(Ablob.blob_allowed) //Is this area allowed for winning as blob? + blobs_legit += src + blobs += src //Keep track of the blob in the normal list either way + src.dir = pick(1, 2, 4, 8) + src.update_icon() + ..(loc) + ConsumeTile() + if(atmosblock) + air_update_turf(1) + return + +/obj/effect/blob/proc/creation_action() //When it's created by the overmind, do this. + return + +/obj/effect/blob/Destroy() + if(atmosblock) + atmosblock = 0 + air_update_turf(1) + var/area/Ablob = get_area(loc) + if(Ablob.blob_allowed) //Only remove for blobs in areas that counted for the win + blobs_legit -= src + blobs -= src //It's still removed from the normal list + playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) //Expand() is no longer broken, no check necessary. + return ..() + + +/obj/effect/blob/CanAtmosPass(turf/T) + return !atmosblock + +/obj/effect/blob/BlockSuperconductivity() + return atmosblock + +/obj/effect/blob/CanPass(atom/movable/mover, turf/target, height=0) + if(height==0) + return 1 + if(istype(mover) && mover.checkpass(PASSBLOB)) + return 1 + return 0 + + +/obj/effect/blob/proc/check_health() + if(health <= 0) + qdel(src) //we dead now + return + return + +/obj/effect/blob/update_icon() //Updates color based on overmind color if we have an overmind. + if(overmind) + color = overmind.blob_reagent_datum.color + else + color = null + return + + +/obj/effect/blob/process() + Life() + return + +/obj/effect/blob/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) + ..() + var/damage = Clamp(0.01 * exposed_temperature, 0, 4) + take_damage(damage, BURN) + +/obj/effect/blob/proc/Life() + return + +/obj/effect/blob/proc/Pulse_Area(pulsing_overmind = overmind, claim_range = 10, pulse_range = 3, expand_range = 2) + src.Be_Pulsed() + if(claim_range) + for(var/obj/effect/blob/B in ultra_range(claim_range, src, 1)) + B.update_icon() + if(!B.overmind && !istype(B, /obj/effect/blob/core) && prob(30)) + B.overmind = pulsing_overmind //reclaim unclaimed, non-core blobs. + B.update_icon() + if(pulse_range) + for(var/obj/effect/blob/B in orange(pulse_range, src)) + B.Be_Pulsed() + if(expand_range) + src.expand() + for(var/obj/effect/blob/B in orange(expand_range, src)) + if(prob(12)) + B.expand() + return + +/obj/effect/blob/proc/Be_Pulsed() + if(pulse_timestamp <= world.time) + PulseAnimation() + ConsumeTile() + RegenHealth() + run_action() + pulse_timestamp = world.time + 10 + return 1 //we did it, we were pulsed! + return 0 //oh no we failed + +/obj/effect/blob/proc/ConsumeTile() + for(var/atom/A in loc) + A.blob_act() + +/obj/effect/blob/proc/PulseAnimation() + flick("[icon_state]_glow", src) + return + +/obj/effect/blob/proc/RegenHealth() //when pulsed, heal! + if(health_timestamp <= world.time) + health = min(maxhealth, health+health_regen) + update_icon() + health_timestamp = world.time + 10 //1 second between heals + return 1 + return 0 + +/obj/effect/blob/proc/run_action() + return 0 + + +/obj/effect/blob/proc/expand(turf/T = null, prob = 1, controller = null) + if(prob && !prob(health)) + return + if(!T) + var/list/dirs = list(1,2,4,8) + for(var/i = 1 to 4) + var/dirn = pick(dirs) + dirs.Remove(dirn) + T = get_step(src, dirn) + if(!(locate(/obj/effect/blob) in T)) + break + else + T = null + if(!T) + return 0 + var/make_blob = 1 //can we make a blob? + if(istype(T, /turf/space) && prob(65)) + make_blob = 0 + playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) //Let's give some feedback that we DID try to spawn in space, since players are used to it + for(var/atom/A in T) + if(A.density) + make_blob = 0 + A.blob_act() //Hit everything + if(T.density) //Check for walls and such dense turfs + make_blob = 0 + T.blob_act() //Hit the turf + if(make_blob) //well, can we? + var/obj/effect/blob/B = new /obj/effect/blob/normal(src.loc) + if(controller) + B.overmind = controller + else + B.overmind = overmind + B.density = 1 + if(T.Enter(B,src)) //NOW we can attempt to move into the tile + B.density = initial(B.density) + B.loc = T + B.update_icon() + else + T.blob_act() //If we cant move in hit the turf + qdel(B) //We should never get to this point, since we checked before moving in. Destroy blob anyway for cleanliness though + return 1 + + +/obj/effect/blob/ex_act(severity, target) + ..() + var/damage = 150 - 20 * severity + take_damage(damage, BRUTE) + +/obj/effect/blob/bullet_act(var/obj/item/projectile/Proj) + ..() + take_damage(Proj.damage, Proj.damage_type) + return 0 + +/obj/effect/blob/attackby(obj/item/weapon/W, mob/living/user, params) + user.changeNext_move(CLICK_CD_MELEE) + user.do_attack_animation(src) + playsound(src.loc, 'sound/effects/attackblob.ogg', 50, 1) + visible_message("[user] has attacked the [src.name] with \the [W]!") + if(W.damtype == BURN) + playsound(src.loc, 'sound/items/Welder.ogg', 100, 1) + take_damage(W.force, W.damtype) + +/obj/effect/blob/attack_animal(mob/living/simple_animal/M) + M.changeNext_move(CLICK_CD_MELEE) + M.do_attack_animation(src) + playsound(src.loc, 'sound/effects/attackblob.ogg', 50, 1) + visible_message("\The [M] has attacked the [src.name]!") + var/damage = rand(M.melee_damage_lower, M.melee_damage_upper) + take_damage(damage, M.melee_damage_type) + return + +/obj/effect/blob/attack_alien(mob/living/carbon/alien/humanoid/M) + M.changeNext_move(CLICK_CD_MELEE) + M.do_attack_animation(src) + playsound(src.loc, 'sound/effects/attackblob.ogg', 50, 1) + visible_message("[M] has slashed the [src.name]!") + var/damage = rand(15, 30) + take_damage(damage, BRUTE) + return + +/obj/effect/blob/proc/take_damage(damage, damage_type) + if(!damage) // Avoid divide by zero errors + return + switch(damage_type) //blobs only take brute and burn damage + if(BRUTE) + damage /= max(brute_resist, 1) + health -= damage + if(BURN) + damage /= max(fire_resist, 1) + health -= damage + update_icon() + check_health() + +/obj/effect/blob/proc/change_to(type, controller) + if(!ispath(type)) + throw EXCEPTION("change_to(): invalid type for blob") + return + var/obj/effect/blob/B = new type(src.loc) + if(controller) + B.overmind = controller + B.creation_action() + B.update_icon() + qdel(src) + return B + +/obj/effect/blob/examine(mob/user) + ..() + user << "It seems to be made of [get_chem_name()]." + return + +/obj/effect/blob/proc/get_chem_name() + if(overmind) + return overmind.blob_reagent_datum.name + return "an unknown variant" + +/obj/effect/blob/normal + icon_state = "blob" + luminosity = 0 + health = 21 + maxhealth = 25 + health_regen = 1 + brute_resist = 4 + +/obj/effect/blob/normal/update_icon() + ..() + if(health <= 10) + icon_state = "blob_damaged" + name = "fragile blob" + desc = "A thin lattice of slightly twitching tendrils." + brute_resist = 2 + else + icon_state = "blob" + name = "blob" + desc = "A thick wall of writhing tendrils." + brute_resist = 4 diff --git a/code/game/gamemodes/cult/cult.dm b/code/game/gamemodes/cult/cult.dm index 7b43cb7958e..da4be575121 100644 --- a/code/game/gamemodes/cult/cult.dm +++ b/code/game/gamemodes/cult/cult.dm @@ -1,188 +1,188 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 - -/datum/game_mode - var/list/datum/mind/cult = list() - -/proc/iscultist(mob/living/M) - return istype(M) && M.mind && ticker && ticker.mode && (M.mind in ticker.mode.cult) - -/proc/cultist_commune(mob/living/user, say = 0, message) - if(!message) - return - if(say) - user.say("O bidai nabora se[pick("'","`")]sma!") - else - user.whisper("O bidai nabora se[pick("'","`")]sma!") - sleep(10) - if(!user) - return - if(say) - user.say(message) - else - user.whisper(message) - for(var/mob/M in mob_list) - if(iscultist(M) || (M in dead_mob_list)) - M << "[(ishuman(user) ? "Acolyte" : "Construct")] [user]: [message]" - log_say("[user.real_name]/[user.key] : [message]") - - - -/datum/game_mode/cult - name = "cult" - config_tag = "cult" - antag_flag = ROLE_CULTIST - restricted_jobs = list("Chaplain","AI", "Cyborg", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel") - protected_jobs = list() - required_players = 10 - required_enemies = 2 - recommended_enemies = 2 - enemy_minimum_age = 14 - - var/eldergod = 0 - var/orbs_needed = 3 - var/large_shell_summoned = 0 - var/attempts_left = 3 - -/datum/game_mode/cult/announce() - world << "The current game mode is - Cult!" - world << "Some crewmembers are attempting to start a cult!
\nCultists - summon the elder god. Sacrifice crewmembers and turn them into constructs. Remember - there is no you, there is only the cult.
\nPersonnel - Do not let the cult succeed in its mission. Deal with the cultists and any constructs that they might summon.
" - - -/datum/game_mode/cult/pre_setup() - if(config.protect_roles_from_antagonist) - restricted_jobs += protected_jobs - - if(config.protect_assistant_from_antagonist) - restricted_jobs += "Assistant" - - //cult scaling goes here - recommended_enemies = 1 + round(num_players()/8) - orbs_needed = recommended_enemies - - for(var/cultists_number = 1 to recommended_enemies) - if(!antag_candidates.len) - break - var/datum/mind/cultist = pick(antag_candidates) - antag_candidates -= cultist - cult += cultist - cultist.special_role = "Cultist" - cultist.restricted_roles = restricted_jobs - log_game("[cultist.key] (ckey) has been selected as a cultist") - - return (cult.len>=required_enemies) - - -/datum/game_mode/cult/post_setup() - modePlayer += cult - for(var/datum/mind/cult_mind in cult) - equip_cultist(cult_mind.current) - update_cult_icons_added(cult_mind) - cult_mind.current << "You are a member of the cult!" - memorize_cult_objectives(cult_mind) - ..() - - -/datum/game_mode/cult/proc/memorize_cult_objectives(datum/mind/cult_mind) - cult_mind.current << "Your objective is to summon Nar-Sie by building and defending a suitable shell for the Geometer. Adequate supplies can be procured through human sacrifices." - -/datum/game_mode/proc/equip_cultist(mob/living/carbon/human/mob) - if(!istype(mob)) - return - mob.cult_add_comm() - if (mob.mind) - if (mob.mind.assigned_role == "Clown") - mob << "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself." - mob.dna.remove_mutation(CLOWNMUT) - - . += cult_give_item(/obj/item/weapon/tome, mob) - . += cult_give_item(/obj/item/weapon/paper/talisman/supply, mob) - . += cult_give_item(/obj/item/weapon/melee/cultblade/dagger, mob) - mob << "These will help you start the cult on this station. Use them well, and remember - you are not the only one." - -/datum/game_mode/proc/cult_give_item(obj/item/item_path, mob/living/carbon/human/mob) - var/list/slots = list( - "backpack" = slot_in_backpack, - "left pocket" = slot_l_store, - "right pocket" = slot_r_store, - "left hand" = slot_l_hand, - "right hand" = slot_r_hand, - ) - var/T = new item_path(mob) - var/item_name = initial(item_path.name) - var/where = mob.equip_in_one_of_slots(T, slots) - if(!where) - mob << "Unfortunately, you weren't able to get a [item_name]. This is very bad and you should adminhelp immediately (press F1)." - return 0 - else - mob << "You have a [item_name] in your [where]." - mob.update_icons() - if(where == "backpack") - var/obj/item/weapon/storage/B = mob.back - B.orient2hud(mob) - B.show_to(mob) - return 1 - -/datum/game_mode/proc/add_cultist(datum/mind/cult_mind) //BASE - if (!istype(cult_mind) || (cult_mind in cult)) - return 0 - cult_mind.current.Paralyse(5) - cult += cult_mind - cult_mind.current.faction |= "cult" - cult_mind.current.cult_add_comm() - update_cult_icons_added(cult_mind) - cult_mind.current.attack_log += "\[[time_stamp()]\] Has been converted to the cult!" - if(jobban_isbanned(cult_mind.current, ROLE_CULTIST)) - replace_jobbaned_player(cult_mind.current, ROLE_CULTIST, ROLE_CULTIST) - return 1 - - -/datum/game_mode/cult/add_cultist(datum/mind/cult_mind) //INHERIT - if (!..(cult_mind)) - return - memorize_cult_objectives(cult_mind) - - -/datum/game_mode/proc/remove_cultist(datum/mind/cult_mind, show_message = 1) - if(cult_mind in cult) - cult -= cult_mind - cult_mind.current.faction -= "cult" - cult_mind.current.verbs -= /mob/living/proc/cult_innate_comm - cult_mind.current.Paralyse(5) - cult_mind.current << "An unfamiliar white light flashes through your mind, cleansing the taint of the Dark One and all your memories as its servant." - cult_mind.memory = "" - update_cult_icons_removed(cult_mind) - cult_mind.current.attack_log += "\[[time_stamp()]\] Has renounced the cult!" - if(show_message) - for(var/mob/M in viewers(cult_mind.current)) - M << "[cult_mind.current] looks like they just reverted to their old faith!" - -/datum/game_mode/proc/update_cult_icons_added(datum/mind/cult_mind) - var/datum/atom_hud/antag/culthud = huds[ANTAG_HUD_CULT] - culthud.join_hud(cult_mind.current) - set_antag_hud(cult_mind.current, "cult") - -/datum/game_mode/proc/update_cult_icons_removed(datum/mind/cult_mind) - var/datum/atom_hud/antag/culthud = huds[ANTAG_HUD_CULT] - culthud.leave_hud(cult_mind.current) - set_antag_hud(cult_mind.current, null) - -/datum/game_mode/cult/declare_completion() - if(eldergod) - feedback_set_details("round_end_result","win - cult win") - world << "The cult wins! It has succeeded in serving its dark master!" - else - feedback_set_details("round_end_result","loss - staff stopped the cult") - world << "The staff managed to stop the cult!" - ..() - return 1 - - -/datum/game_mode/proc/auto_declare_completion_cult() - if( cult.len || (ticker && istype(ticker.mode,/datum/game_mode/cult)) ) - var/text = "
The cultists were:" - for(var/datum/mind/cultist in cult) - text += printplayer(cultist) - - text += "
" - - world << text +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 + +/datum/game_mode + var/list/datum/mind/cult = list() + +/proc/iscultist(mob/living/M) + return istype(M) && M.mind && ticker && ticker.mode && (M.mind in ticker.mode.cult) + +/proc/cultist_commune(mob/living/user, say = 0, message) + if(!message) + return + if(say) + user.say("O bidai nabora se[pick("'","`")]sma!") + else + user.whisper("O bidai nabora se[pick("'","`")]sma!") + sleep(10) + if(!user) + return + if(say) + user.say(message) + else + user.whisper(message) + for(var/mob/M in mob_list) + if(iscultist(M) || (M in dead_mob_list)) + M << "[(ishuman(user) ? "Acolyte" : "Construct")] [user]: [message]" + log_say("[user.real_name]/[user.key] : [message]") + + + +/datum/game_mode/cult + name = "cult" + config_tag = "cult" + antag_flag = ROLE_CULTIST + restricted_jobs = list("Chaplain","AI", "Cyborg", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel") + protected_jobs = list() + required_players = 10 + required_enemies = 2 + recommended_enemies = 2 + enemy_minimum_age = 14 + + var/eldergod = 0 + var/orbs_needed = 3 + var/large_shell_summoned = 0 + var/attempts_left = 3 + +/datum/game_mode/cult/announce() + world << "The current game mode is - Cult!" + world << "Some crewmembers are attempting to start a cult!
\nCultists - summon the elder god. Sacrifice crewmembers and turn them into constructs. Remember - there is no you, there is only the cult.
\nPersonnel - Do not let the cult succeed in its mission. Deal with the cultists and any constructs that they might summon.
" + + +/datum/game_mode/cult/pre_setup() + if(config.protect_roles_from_antagonist) + restricted_jobs += protected_jobs + + if(config.protect_assistant_from_antagonist) + restricted_jobs += "Assistant" + + //cult scaling goes here + recommended_enemies = 1 + round(num_players()/8) + orbs_needed = recommended_enemies + + for(var/cultists_number = 1 to recommended_enemies) + if(!antag_candidates.len) + break + var/datum/mind/cultist = pick(antag_candidates) + antag_candidates -= cultist + cult += cultist + cultist.special_role = "Cultist" + cultist.restricted_roles = restricted_jobs + log_game("[cultist.key] (ckey) has been selected as a cultist") + + return (cult.len>=required_enemies) + + +/datum/game_mode/cult/post_setup() + modePlayer += cult + for(var/datum/mind/cult_mind in cult) + equip_cultist(cult_mind.current) + update_cult_icons_added(cult_mind) + cult_mind.current << "You are a member of the cult!" + memorize_cult_objectives(cult_mind) + ..() + + +/datum/game_mode/cult/proc/memorize_cult_objectives(datum/mind/cult_mind) + cult_mind.current << "Your objective is to summon Nar-Sie by building and defending a suitable shell for the Geometer. Adequate supplies can be procured through human sacrifices." + +/datum/game_mode/proc/equip_cultist(mob/living/carbon/human/mob) + if(!istype(mob)) + return + mob.cult_add_comm() + if (mob.mind) + if (mob.mind.assigned_role == "Clown") + mob << "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself." + mob.dna.remove_mutation(CLOWNMUT) + + . += cult_give_item(/obj/item/weapon/tome, mob) + . += cult_give_item(/obj/item/weapon/paper/talisman/supply, mob) + . += cult_give_item(/obj/item/weapon/melee/cultblade/dagger, mob) + mob << "These will help you start the cult on this station. Use them well, and remember - you are not the only one.
" + +/datum/game_mode/proc/cult_give_item(obj/item/item_path, mob/living/carbon/human/mob) + var/list/slots = list( + "backpack" = slot_in_backpack, + "left pocket" = slot_l_store, + "right pocket" = slot_r_store, + "left hand" = slot_l_hand, + "right hand" = slot_r_hand, + ) + var/T = new item_path(mob) + var/item_name = initial(item_path.name) + var/where = mob.equip_in_one_of_slots(T, slots) + if(!where) + mob << "Unfortunately, you weren't able to get a [item_name]. This is very bad and you should adminhelp immediately (press F1)." + return 0 + else + mob << "You have a [item_name] in your [where]." + mob.update_icons() + if(where == "backpack") + var/obj/item/weapon/storage/B = mob.back + B.orient2hud(mob) + B.show_to(mob) + return 1 + +/datum/game_mode/proc/add_cultist(datum/mind/cult_mind) //BASE + if (!istype(cult_mind) || (cult_mind in cult)) + return 0 + cult_mind.current.Paralyse(5) + cult += cult_mind + cult_mind.current.faction |= "cult" + cult_mind.current.cult_add_comm() + update_cult_icons_added(cult_mind) + cult_mind.current.attack_log += "\[[time_stamp()]\] Has been converted to the cult!" + if(jobban_isbanned(cult_mind.current, ROLE_CULTIST)) + replace_jobbaned_player(cult_mind.current, ROLE_CULTIST, ROLE_CULTIST) + return 1 + + +/datum/game_mode/cult/add_cultist(datum/mind/cult_mind) //INHERIT + if (!..(cult_mind)) + return + memorize_cult_objectives(cult_mind) + + +/datum/game_mode/proc/remove_cultist(datum/mind/cult_mind, show_message = 1) + if(cult_mind in cult) + cult -= cult_mind + cult_mind.current.faction -= "cult" + cult_mind.current.verbs -= /mob/living/proc/cult_innate_comm + cult_mind.current.Paralyse(5) + cult_mind.current << "An unfamiliar white light flashes through your mind, cleansing the taint of the Dark One and all your memories as its servant." + cult_mind.memory = "" + update_cult_icons_removed(cult_mind) + cult_mind.current.attack_log += "\[[time_stamp()]\] Has renounced the cult!" + if(show_message) + for(var/mob/M in viewers(cult_mind.current)) + M << "[cult_mind.current] looks like they just reverted to their old faith!" + +/datum/game_mode/proc/update_cult_icons_added(datum/mind/cult_mind) + var/datum/atom_hud/antag/culthud = huds[ANTAG_HUD_CULT] + culthud.join_hud(cult_mind.current) + set_antag_hud(cult_mind.current, "cult") + +/datum/game_mode/proc/update_cult_icons_removed(datum/mind/cult_mind) + var/datum/atom_hud/antag/culthud = huds[ANTAG_HUD_CULT] + culthud.leave_hud(cult_mind.current) + set_antag_hud(cult_mind.current, null) + +/datum/game_mode/cult/declare_completion() + if(eldergod) + feedback_set_details("round_end_result","win - cult win") + world << "The cult wins! It has succeeded in serving its dark master!" + else + feedback_set_details("round_end_result","loss - staff stopped the cult") + world << "The staff managed to stop the cult!" + ..() + return 1 + + +/datum/game_mode/proc/auto_declare_completion_cult() + if( cult.len || (ticker && istype(ticker.mode,/datum/game_mode/cult)) ) + var/text = "
The cultists were:" + for(var/datum/mind/cultist in cult) + text += printplayer(cultist) + + text += "
" + + world << text diff --git a/code/game/gamemodes/cult/cult_items.dm b/code/game/gamemodes/cult/cult_items.dm index 67acdd9d67a..2de0ba8e2f2 100644 --- a/code/game/gamemodes/cult/cult_items.dm +++ b/code/game/gamemodes/cult/cult_items.dm @@ -1,175 +1,175 @@ -/obj/item/weapon/melee/cultblade - name = "eldritch longsword" - desc = "A sword humming with unholy energy. It glows with a dim red light." - icon_state = "cultblade" - item_state = "cultblade" - flags = CONDUCT - sharpness = IS_SHARP - w_class = 4 - force = 30 - throwforce = 10 - hitsound = 'sound/weapons/bladeslice.ogg' - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - - - -/obj/item/weapon/melee/cultblade/attack(mob/living/target, mob/living/carbon/human/user) - if(!iscultist(user)) - user.Weaken(5) - user.visible_message("A powerful force shoves [user] away from [target]!", \ - "\"You shouldn't play with sharp things. You'll poke someone's eye out.\"") - if(ishuman(user)) - var/mob/living/carbon/human/H = user - H.apply_damage(rand(force/2, force), BRUTE, pick("l_arm", "r_arm")) - else - user.adjustBruteLoss(rand(force/2,force)) - return - ..() - -/obj/item/weapon/melee/cultblade/pickup(mob/living/user) - if(!iscultist(user)) - user << "\"I wouldn't advise that.\"" - user << "An overwhelming sense of nausea overpowers you!" - user.Dizzy(120) - -/obj/item/weapon/melee/cultblade/dagger - name = "sacrificial dagger" - desc = "A strange dagger said to be used by sinister groups for \"preparing\" a corpse before sacrificing it to their dark gods." - icon = 'icons/obj/wizard.dmi' - icon_state = "render" - w_class = 2 - force = 15 - throwforce = 25 - embed_chance = 75 - -/obj/item/weapon/melee/cultblade/dagger/attack(mob/living/target, mob/living/carbon/human/user) - ..() - if(ishuman(target)) - var/mob/living/carbon/human/H = target - H.drip(50) - -/obj/item/clothing/head/culthood - name = "ancient cultist hood" - icon_state = "culthood" - desc = "A torn, dust-caked hood. Strange letters line the inside." - flags_inv = HIDEFACE - flags_cover = HEADCOVERSEYES - armor = list(melee = 30, bullet = 10, laser = 5,energy = 5, bomb = 0, bio = 0, rad = 0) - cold_protection = HEAD - min_cold_protection_temperature = HELMET_MIN_TEMP_PROTECT - heat_protection = HEAD - max_heat_protection_temperature = HELMET_MAX_TEMP_PROTECT - -/obj/item/clothing/suit/cultrobes - name = "ancient cultist robes" - desc = "A ragged, dusty set of robes. Strange letters line the inside." - icon_state = "cultrobes" - item_state = "cultrobes" - body_parts_covered = CHEST|GROIN|LEGS|ARMS - allowed = list(/obj/item/weapon/tome,/obj/item/weapon/melee/cultblade) - armor = list(melee = 50, bullet = 30, laser = 50,energy = 20, bomb = 25, bio = 10, rad = 0) - flags_inv = HIDEJUMPSUIT - cold_protection = CHEST|GROIN|LEGS|ARMS - min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT - heat_protection = CHEST|GROIN|LEGS|ARMS - max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT - - -/obj/item/clothing/head/culthood/alt - name = "cultist hood" - desc = "An armored hood worn by the followers of Nar-Sie." - icon_state = "cult_hoodalt" - item_state = "cult_hoodalt" - -/obj/item/clothing/suit/cultrobes/alt - name = "cultist hood" - desc = "An armored set of robes worn by the followers of Nar-Sie." - icon_state = "cultrobesalt" - item_state = "cultrobesalt" - - -/obj/item/clothing/head/magus - name = "magus helm" - icon_state = "magus" - item_state = "magus" - desc = "A helm worn by the followers of Nar-Sie." - flags_inv = HIDEFACE - flags = BLOCKHAIR - armor = list(melee = 30, bullet = 30, laser = 30,energy = 20, bomb = 0, bio = 0, rad = 0) - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - -/obj/item/clothing/suit/magusred - name = "magus robes" - desc = "A set of armored robes worn by the followers of Nar-Sie" - icon_state = "magusred" - item_state = "magusred" - body_parts_covered = CHEST|GROIN|LEGS|ARMS - allowed = list(/obj/item/weapon/tome,/obj/item/weapon/melee/cultblade) - armor = list(melee = 50, bullet = 30, laser = 50,energy = 20, bomb = 25, bio = 10, rad = 0) - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT - - -/obj/item/clothing/head/helmet/space/cult - name = "nar-sian bruiser's helmet" - desc = "A heavily-armored helmet worn by warriors of the Nar-Sian cult. It can withstand hard vacuum." - icon_state = "cult_helmet" - item_state = "cult_helmet" - armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 30, rad = 30) - -/obj/item/clothing/suit/space/cult - name = "nar-sian bruiser's armor" - icon_state = "cult_armor" - item_state = "cult_armor" - desc = "A heavily-armored exosuit worn by warriors of the Nar-Sian cult. It can withstand hard vacuum." - w_class = 3 - allowed = list(/obj/item/weapon/tome,/obj/item/weapon/melee/cultblade,/obj/item/weapon/tank/internals/) - armor = list(melee = 70, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 30, rad = 30) - -/obj/item/summoning_orb - name = "summoning orb" - desc = "A mysterious orb that glows eerily. You could swear that it just moved on its own." - icon = 'icons/obj/cult.dmi' - icon_state = "summoning_orb" - w_class = 1 - -/obj/item/summoning_orb/afterattack(atom/target, mob/user, proximity_flag) - if(!iscultist(user)) - return ..() - if(proximity_flag && isliving(target) && iscultist(target)) - var/mob/living/L = target - if(L.stat == DEAD) - user << "You shouldn't waste that." - return ..() - target << "You feel the Geometer's essence violating your insides." - target << "It feels... good." - target.reagents.add_reagent("unholywater", 15) - qdel(src) - ..() - -/obj/item/summoning_orb/attack_self(mob/user) - if(!iscultist(user)) - return ..() - if(ticker.mode.name != "cult") - return - var/datum/game_mode/cult/cult = ticker.mode - if(!cult.attempts_left) - user << "You attempt to call out to the Geometer, but there is no answer..." - return - switch(alert(user,"Are you sure you wish to summon the large construct shell? [cult.attempts_left] attempts left!","Summoning Large Shell","Yes","No")) - if("Yes") - cult.attempts_left-- - place_down_large_shell(user) - qdel(src) - -/obj/item/summoning_orb/proc/place_down_large_shell(mob/user) - if(!(ticker.mode.name == "cult")) - user << "You attempt to call out to the Geometer for Her shell, but you fail..." - return - var/datum/game_mode/cult/cult = ticker.mode - if(cult.large_shell_summoned) - user << "Another Acolyte has already summoned Her shell. You must go forth and contact them." - return - if(cult.attempts_left <= 0) - user << "The Geometer is no longer interested in you." - new /obj/structure/constructshell/large(get_turf(src)) - cult.large_shell_summoned = 1 +/obj/item/weapon/melee/cultblade + name = "eldritch longsword" + desc = "A sword humming with unholy energy. It glows with a dim red light." + icon_state = "cultblade" + item_state = "cultblade" + flags = CONDUCT + sharpness = IS_SHARP + w_class = 4 + force = 30 + throwforce = 10 + hitsound = 'sound/weapons/bladeslice.ogg' + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + + + +/obj/item/weapon/melee/cultblade/attack(mob/living/target, mob/living/carbon/human/user) + if(!iscultist(user)) + user.Weaken(5) + user.visible_message("A powerful force shoves [user] away from [target]!", \ + "\"You shouldn't play with sharp things. You'll poke someone's eye out.\"") + if(ishuman(user)) + var/mob/living/carbon/human/H = user + H.apply_damage(rand(force/2, force), BRUTE, pick("l_arm", "r_arm")) + else + user.adjustBruteLoss(rand(force/2,force)) + return + ..() + +/obj/item/weapon/melee/cultblade/pickup(mob/living/user) + if(!iscultist(user)) + user << "\"I wouldn't advise that.\"" + user << "An overwhelming sense of nausea overpowers you!" + user.Dizzy(120) + +/obj/item/weapon/melee/cultblade/dagger + name = "sacrificial dagger" + desc = "A strange dagger said to be used by sinister groups for \"preparing\" a corpse before sacrificing it to their dark gods." + icon = 'icons/obj/wizard.dmi' + icon_state = "render" + w_class = 2 + force = 15 + throwforce = 25 + embed_chance = 75 + +/obj/item/weapon/melee/cultblade/dagger/attack(mob/living/target, mob/living/carbon/human/user) + ..() + if(ishuman(target)) + var/mob/living/carbon/human/H = target + H.drip(50) + +/obj/item/clothing/head/culthood + name = "ancient cultist hood" + icon_state = "culthood" + desc = "A torn, dust-caked hood. Strange letters line the inside." + flags_inv = HIDEFACE + flags_cover = HEADCOVERSEYES + armor = list(melee = 30, bullet = 10, laser = 5,energy = 5, bomb = 0, bio = 0, rad = 0) + cold_protection = HEAD + min_cold_protection_temperature = HELMET_MIN_TEMP_PROTECT + heat_protection = HEAD + max_heat_protection_temperature = HELMET_MAX_TEMP_PROTECT + +/obj/item/clothing/suit/cultrobes + name = "ancient cultist robes" + desc = "A ragged, dusty set of robes. Strange letters line the inside." + icon_state = "cultrobes" + item_state = "cultrobes" + body_parts_covered = CHEST|GROIN|LEGS|ARMS + allowed = list(/obj/item/weapon/tome,/obj/item/weapon/melee/cultblade) + armor = list(melee = 50, bullet = 30, laser = 50,energy = 20, bomb = 25, bio = 10, rad = 0) + flags_inv = HIDEJUMPSUIT + cold_protection = CHEST|GROIN|LEGS|ARMS + min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT + heat_protection = CHEST|GROIN|LEGS|ARMS + max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT + + +/obj/item/clothing/head/culthood/alt + name = "cultist hood" + desc = "An armored hood worn by the followers of Nar-Sie." + icon_state = "cult_hoodalt" + item_state = "cult_hoodalt" + +/obj/item/clothing/suit/cultrobes/alt + name = "cultist hood" + desc = "An armored set of robes worn by the followers of Nar-Sie." + icon_state = "cultrobesalt" + item_state = "cultrobesalt" + + +/obj/item/clothing/head/magus + name = "magus helm" + icon_state = "magus" + item_state = "magus" + desc = "A helm worn by the followers of Nar-Sie." + flags_inv = HIDEFACE + flags = BLOCKHAIR + armor = list(melee = 30, bullet = 30, laser = 30,energy = 20, bomb = 0, bio = 0, rad = 0) + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + +/obj/item/clothing/suit/magusred + name = "magus robes" + desc = "A set of armored robes worn by the followers of Nar-Sie" + icon_state = "magusred" + item_state = "magusred" + body_parts_covered = CHEST|GROIN|LEGS|ARMS + allowed = list(/obj/item/weapon/tome,/obj/item/weapon/melee/cultblade) + armor = list(melee = 50, bullet = 30, laser = 50,energy = 20, bomb = 25, bio = 10, rad = 0) + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT + + +/obj/item/clothing/head/helmet/space/cult + name = "nar-sian bruiser's helmet" + desc = "A heavily-armored helmet worn by warriors of the Nar-Sian cult. It can withstand hard vacuum." + icon_state = "cult_helmet" + item_state = "cult_helmet" + armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 30, rad = 30) + +/obj/item/clothing/suit/space/cult + name = "nar-sian bruiser's armor" + icon_state = "cult_armor" + item_state = "cult_armor" + desc = "A heavily-armored exosuit worn by warriors of the Nar-Sian cult. It can withstand hard vacuum." + w_class = 3 + allowed = list(/obj/item/weapon/tome,/obj/item/weapon/melee/cultblade,/obj/item/weapon/tank/internals/) + armor = list(melee = 70, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 30, rad = 30) + +/obj/item/summoning_orb + name = "summoning orb" + desc = "A mysterious orb that glows eerily. You could swear that it just moved on its own." + icon = 'icons/obj/cult.dmi' + icon_state = "summoning_orb" + w_class = 1 + +/obj/item/summoning_orb/afterattack(atom/target, mob/user, proximity_flag) + if(!iscultist(user)) + return ..() + if(proximity_flag && isliving(target) && iscultist(target)) + var/mob/living/L = target + if(L.stat == DEAD) + user << "You shouldn't waste that." + return ..() + target << "You feel the Geometer's essence violating your insides." + target << "It feels... good." + target.reagents.add_reagent("unholywater", 15) + qdel(src) + ..() + +/obj/item/summoning_orb/attack_self(mob/user) + if(!iscultist(user)) + return ..() + if(ticker.mode.name != "cult") + return + var/datum/game_mode/cult/cult = ticker.mode + if(!cult.attempts_left) + user << "You attempt to call out to the Geometer, but there is no answer..." + return + switch(alert(user,"Are you sure you wish to summon the large construct shell? [cult.attempts_left] attempts left!","Summoning Large Shell","Yes","No")) + if("Yes") + cult.attempts_left-- + place_down_large_shell(user) + qdel(src) + +/obj/item/summoning_orb/proc/place_down_large_shell(mob/user) + if(!(ticker.mode.name == "cult")) + user << "You attempt to call out to the Geometer for Her shell, but you fail..." + return + var/datum/game_mode/cult/cult = ticker.mode + if(cult.large_shell_summoned) + user << "Another Acolyte has already summoned Her shell. You must go forth and contact them." + return + if(cult.attempts_left <= 0) + user << "The Geometer is no longer interested in you." + new /obj/structure/constructshell/large(get_turf(src)) + cult.large_shell_summoned = 1 diff --git a/code/game/gamemodes/cult/cult_structures.dm b/code/game/gamemodes/cult/cult_structures.dm index b5b5cb9d0c4..788d20be9e1 100644 --- a/code/game/gamemodes/cult/cult_structures.dm +++ b/code/game/gamemodes/cult/cult_structures.dm @@ -1,180 +1,180 @@ -/obj/structure/constructshell/large - name = "large empty shell" - icon = 'icons/obj/cult_large.dmi' - icon_state = "shell_narsie_grey" - desc = "An oversized construct shell, fit for an elder god. Only a lunatic would even dream of such a crazed contraption." - pixel_x = -16 - pixel_y = -16 - density = 1 - anchored = 0 - var/maxhealth = 200 - var/health = 200 - var/image/black_overlay = null - var/orbs = 0 - var/orbs_needed = 1 - var/time_to_win = 1800 //3 minutes - var/timer_id = null - -/obj/structure/constructshell/large/New() - ..() - if(ticker.mode.name == "cult") - var/datum/game_mode/cult/cult = ticker.mode - orbs_needed = cult.orbs_needed - black_overlay = image('icons/obj/cult_large.dmi', "shell_narsie_black") - -/obj/structure/constructshell/large/Destroy() - priority_announce("The extra-dimensional flow has ceased. All personnel should return to their routine activities.","Central Command Higher Dimensions Affairs") - if(get_security_level() == "delta") - set_security_level("red") - if(ticker.mode.name == "cult") - var/datum/game_mode/cult/cult = ticker.mode - cult.large_shell_summoned = 0 - black_overlay = null - if(timer_id) - deltimer(timer_id) - ..() - -/obj/structure/constructshell/large/examine(mob/user) - ..() - user << "You see a number of round holes on the surface of the shell. They number [orbs_needed], and [orbs ? orbs : "none"] of them [orbs > 1 ? "are" : "is"] filled." - -/obj/structure/constructshell/large/update_icon() - var/new_alpha = round((orbs/orbs_needed)*255) - if(new_alpha) - overlays -= black_overlay - black_overlay.alpha = new_alpha - overlays += black_overlay - -/obj/structure/constructshell/large/attackby(obj/item/O, mob/user, params) - if(istype(O, /obj/item/summoning_orb) && (orbs < orbs_needed)) - if(!iscultist(user)) - return - visible_message("\The [src] glows.") - orbs++ - qdel(O) - update_icon() - if(orbs >= orbs_needed) - start_takeover() - return - - if( (O.flags&NOBLUDGEON) || !O.force ) - return - add_fingerprint(user) - user.changeNext_move(CLICK_CD_MELEE) - user.do_attack_animation(src) - playsound(src, 'sound/weapons/smash.ogg', 50, 1) - visible_message("[user] has hit \the [src] with [O].") - if(O.damtype == BURN || O.damtype == BRUTE) - damaged(O.force) - -//this stuff is mostly copy-pasted from gang dominators, with some changes -/obj/structure/constructshell/large/proc/start_takeover() - anchored = 1 - animate(black_overlay, alpha = 255, 5, -1) - set_security_level("delta") - var/area/A = get_area(src) - var/locname = initial(A.name) - priority_announce("Figments from an eldritch god have begun pouring into [locname] from an unknown dimension. Eliminate its vessel before it reaches a critical point.","Central Command Higher Dimensions Affairs") - timer_id = addtimer(src, "summon_narnar", time_to_win) - -/obj/structure/constructshell/large/proc/damaged(damage) - health -= damage - if(health <= 0) - new /obj/item/stack/sheet/plasteel(get_turf(src)) - qdel(src) - -/obj/structure/constructshell/large/bullet_act(obj/item/projectile/P) - if(P.damage) - playsound(src, 'sound/effects/bang.ogg', 50, 1) - visible_message("[src] was hit by [P].") - damaged(P.damage) - -/obj/structure/constructshell/large/attack_alien(mob/living/user) - user.do_attack_animation(src) - playsound(src, 'sound/effects/bang.ogg', 50, 1) - user.visible_message("[user] smashes against [src] with its claws.",\ - "You smash against [src] with your claws.") - damaged(15) - -/obj/structure/constructshell/large/attack_animal(mob/living/user) - if(!isanimal(user)) - return - var/mob/living/simple_animal/M = user - M.do_attack_animation(src) - if(M.melee_damage_upper <= 0) - return - damaged(M.melee_damage_upper) - -/obj/structure/constructshell/large/mech_melee_attack(obj/mecha/M) - if(M.damtype == "brute") - playsound(src, 'sound/effects/bang.ogg', 50, 1) - visible_message("[M.name] has hit [src].") - damaged(M.force) - -/obj/structure/constructshell/large/attack_hulk(mob/user) - playsound(src, 'sound/effects/bang.ogg', 50, 1) - user.visible_message("[user] smashes [src].",\ - "You punch [src].") - damaged(5) - -/obj/structure/constructshell/large/ex_act() - return //nope - -/obj/structure/constructshell/large/singularity_pull() - return //nope - -/obj/structure/constructshell/large/singularity_act(current_size, obj/singularity/S) - var/atom/target = get_edge_target_turf(src, get_dir(src, S)) - S.throw_at(target, 5, 1) //aaand nope - -/obj/structure/constructshell/large/proc/summon_narnar() - if(ticker.mode.name != "cult") - visible_message("\The [src] glows brightly once, then falls dark. It looks strangely dull and lifeless...") - log_game("Summon Nar-Sie rune failed - gametype is not cult") - return - var/datum/game_mode/cult/cult_mode = ticker.mode - if(cult_mode.eldergod) - return //this should never happen, so we don't need any special fluff - world << 'sound/effects/dimensional_rend.ogg' - world << "Rip... Rrrip... RRRRRRRRRR--" - var/turf/target_turf = get_turf(src) - spawn(40) - new /obj/singularity/narsie/large(target_turf) //Causes Nar-Sie to spawn even if the rune has been removed - cult_mode.eldergod = 1 - - -/obj/structure/cult - density = 1 - anchored = 1 - icon = 'icons/obj/cult.dmi' - -/obj/structure/cult/talisman - name = "altar" - desc = "A bloodstained altar dedicated to Nar-Sie" - icon_state = "talismanaltar" - -/obj/structure/cult/forge - name = "daemon forge" - desc = "A forge used in crafting the unholy weapons used by the armies of Nar-Sie" - icon_state = "forge" - -/obj/structure/cult/pylon - name = "pylon" - desc = "A floating crystal that hums with an unearthly energy" - icon_state = "pylon" - luminosity = 5 - -/obj/structure/cult/tome - name = "desk" - desc = "A desk covered in arcane manuscripts and tomes in unknown languages. Looking at the text makes your skin crawl" - icon_state = "tomealtar" - luminosity = 1 - -/obj/effect/gateway - name = "gateway" - desc = "You're pretty sure that abyss is staring back" - icon = 'icons/obj/cult.dmi' - icon_state = "hole" - density = 1 - unacidable = 1 - anchored = 1 +/obj/structure/constructshell/large + name = "large empty shell" + icon = 'icons/obj/cult_large.dmi' + icon_state = "shell_narsie_grey" + desc = "An oversized construct shell, fit for an elder god. Only a lunatic would even dream of such a crazed contraption." + pixel_x = -16 + pixel_y = -16 + density = 1 + anchored = 0 + var/maxhealth = 200 + var/health = 200 + var/image/black_overlay = null + var/orbs = 0 + var/orbs_needed = 1 + var/time_to_win = 1800 //3 minutes + var/timer_id = null + +/obj/structure/constructshell/large/New() + ..() + if(ticker.mode.name == "cult") + var/datum/game_mode/cult/cult = ticker.mode + orbs_needed = cult.orbs_needed + black_overlay = image('icons/obj/cult_large.dmi', "shell_narsie_black") + +/obj/structure/constructshell/large/Destroy() + priority_announce("The extra-dimensional flow has ceased. All personnel should return to their routine activities.","Central Command Higher Dimensions Affairs") + if(get_security_level() == "delta") + set_security_level("red") + if(ticker.mode.name == "cult") + var/datum/game_mode/cult/cult = ticker.mode + cult.large_shell_summoned = 0 + black_overlay = null + if(timer_id) + deltimer(timer_id) + ..() + +/obj/structure/constructshell/large/examine(mob/user) + ..() + user << "You see a number of round holes on the surface of the shell. They number [orbs_needed], and [orbs ? orbs : "none"] of them [orbs > 1 ? "are" : "is"] filled." + +/obj/structure/constructshell/large/update_icon() + var/new_alpha = round((orbs/orbs_needed)*255) + if(new_alpha) + overlays -= black_overlay + black_overlay.alpha = new_alpha + overlays += black_overlay + +/obj/structure/constructshell/large/attackby(obj/item/O, mob/user, params) + if(istype(O, /obj/item/summoning_orb) && (orbs < orbs_needed)) + if(!iscultist(user)) + return + visible_message("\The [src] glows.") + orbs++ + qdel(O) + update_icon() + if(orbs >= orbs_needed) + start_takeover() + return + + if( (O.flags&NOBLUDGEON) || !O.force ) + return + add_fingerprint(user) + user.changeNext_move(CLICK_CD_MELEE) + user.do_attack_animation(src) + playsound(src, 'sound/weapons/smash.ogg', 50, 1) + visible_message("[user] has hit \the [src] with [O].") + if(O.damtype == BURN || O.damtype == BRUTE) + damaged(O.force) + +//this stuff is mostly copy-pasted from gang dominators, with some changes +/obj/structure/constructshell/large/proc/start_takeover() + anchored = 1 + animate(black_overlay, alpha = 255, 5, -1) + set_security_level("delta") + var/area/A = get_area(src) + var/locname = initial(A.name) + priority_announce("Figments from an eldritch god have begun pouring into [locname] from an unknown dimension. Eliminate its vessel before it reaches a critical point.","Central Command Higher Dimensions Affairs") + timer_id = addtimer(src, "summon_narnar", time_to_win) + +/obj/structure/constructshell/large/proc/damaged(damage) + health -= damage + if(health <= 0) + new /obj/item/stack/sheet/plasteel(get_turf(src)) + qdel(src) + +/obj/structure/constructshell/large/bullet_act(obj/item/projectile/P) + if(P.damage) + playsound(src, 'sound/effects/bang.ogg', 50, 1) + visible_message("[src] was hit by [P].") + damaged(P.damage) + +/obj/structure/constructshell/large/attack_alien(mob/living/user) + user.do_attack_animation(src) + playsound(src, 'sound/effects/bang.ogg', 50, 1) + user.visible_message("[user] smashes against [src] with its claws.",\ + "You smash against [src] with your claws.") + damaged(15) + +/obj/structure/constructshell/large/attack_animal(mob/living/user) + if(!isanimal(user)) + return + var/mob/living/simple_animal/M = user + M.do_attack_animation(src) + if(M.melee_damage_upper <= 0) + return + damaged(M.melee_damage_upper) + +/obj/structure/constructshell/large/mech_melee_attack(obj/mecha/M) + if(M.damtype == "brute") + playsound(src, 'sound/effects/bang.ogg', 50, 1) + visible_message("[M.name] has hit [src].") + damaged(M.force) + +/obj/structure/constructshell/large/attack_hulk(mob/user) + playsound(src, 'sound/effects/bang.ogg', 50, 1) + user.visible_message("[user] smashes [src].",\ + "You punch [src].") + damaged(5) + +/obj/structure/constructshell/large/ex_act() + return //nope + +/obj/structure/constructshell/large/singularity_pull() + return //nope + +/obj/structure/constructshell/large/singularity_act(current_size, obj/singularity/S) + var/atom/target = get_edge_target_turf(src, get_dir(src, S)) + S.throw_at(target, 5, 1) //aaand nope + +/obj/structure/constructshell/large/proc/summon_narnar() + if(ticker.mode.name != "cult") + visible_message("\The [src] glows brightly once, then falls dark. It looks strangely dull and lifeless...") + log_game("Summon Nar-Sie rune failed - gametype is not cult") + return + var/datum/game_mode/cult/cult_mode = ticker.mode + if(cult_mode.eldergod) + return //this should never happen, so we don't need any special fluff + world << 'sound/effects/dimensional_rend.ogg' + world << "Rip... Rrrip... RRRRRRRRRR--" + var/turf/target_turf = get_turf(src) + spawn(40) + new /obj/singularity/narsie/large(target_turf) //Causes Nar-Sie to spawn even if the rune has been removed + cult_mode.eldergod = 1 + + +/obj/structure/cult + density = 1 + anchored = 1 + icon = 'icons/obj/cult.dmi' + +/obj/structure/cult/talisman + name = "altar" + desc = "A bloodstained altar dedicated to Nar-Sie" + icon_state = "talismanaltar" + +/obj/structure/cult/forge + name = "daemon forge" + desc = "A forge used in crafting the unholy weapons used by the armies of Nar-Sie" + icon_state = "forge" + +/obj/structure/cult/pylon + name = "pylon" + desc = "A floating crystal that hums with an unearthly energy" + icon_state = "pylon" + luminosity = 5 + +/obj/structure/cult/tome + name = "desk" + desc = "A desk covered in arcane manuscripts and tomes in unknown languages. Looking at the text makes your skin crawl" + icon_state = "tomealtar" + luminosity = 1 + +/obj/effect/gateway + name = "gateway" + desc = "You're pretty sure that abyss is staring back" + icon = 'icons/obj/cult.dmi' + icon_state = "hole" + density = 1 + unacidable = 1 + anchored = 1 diff --git a/code/game/gamemodes/cult/ritual.dm b/code/game/gamemodes/cult/ritual.dm index 5ccc6d9e3f2..0fdd73b2895 100644 --- a/code/game/gamemodes/cult/ritual.dm +++ b/code/game/gamemodes/cult/ritual.dm @@ -1,215 +1,215 @@ -/* - -This file contains the arcane tome files as well as innate cultist emergency communications. - -*/ - -/mob/proc/cult_add_comm() //why the fuck does this have its own proc? not removing it because it might be used somewhere but... - verbs += /mob/living/proc/cult_innate_comm - -/mob/living/proc/cult_innate_comm() - set category = "Cultist" - set name = "Communion" - - if(!iscultist(usr)) //they shouldn't have this verb, but just to be sure... - return - - if(usr.incapacitated()) - return //dead men tell no tales - - var/input = stripped_input(usr, "Please choose a message to tell to the other acolytes.", "Voice of Blood", "") - if(!input) // TO-DO: Add some kind of filter to corrupt the inputted text - return - - cultist_commune(usr, 0, input) - return - - -/obj/item/weapon/tome - name = "arcane tome" - desc = "An old, dusty tome with frayed edges and a sinister-looking cover." - icon_state ="tome" - throw_speed = 2 - throw_range = 5 - w_class = 2 - var/list/known_runes = null - -/obj/item/weapon/tome/New() - ..() - known_runes = shuffle(subtypesof(/obj/effect/rune) - /obj/effect/rune/malformed) - var/known_amount = round((known_runes.len)/2) - known_runes.Cut(known_amount) - -/obj/item/weapon/tome/examine(mob/user) - ..() - if(iscultist(user)) - user << "The scriptures of the Geometer. Allows the scribing of runes and access to the knowledge archives of the cult of Nar-Sie." - -/obj/item/weapon/tome/attack(mob/living/M, mob/living/user) - if(istype(M,/mob/dead/observer)) - M.invisibility = 0 - user.visible_message("[user] strikes the air with [src], and a ghost appears!", \ - "You drag the ghost to your plane of reality!") - add_logs(user, M, "smacked", src) - return - if(!istype(M)) - return - if(!iscultist(user)) - return ..() - if(iscultist(M)) - if(M.reagents && M.reagents.has_reagent("holywater")) //allows cultists to be rescued from the clutches of ordained religion - user << "You remove the taint from [M]." - var/holy2unholy = M.reagents.get_reagent_amount("holywater") - M.reagents.del_reagent("holywater") - M.reagents.add_reagent("unholywater",holy2unholy) - add_logs(user, M, "smacked", src, " removing the holy water from them") - return - M.take_organ_damage(0, 15) //Used to be a random between 5 and 20 - playsound(M, 'sound/weapons/sear.ogg', 50, 1) - M.visible_message("[user] strikes [M] with the arcane tome!", \ - "[user] strikes you with the tome, searing your flesh!") - flick("tome_attack", src) - user.do_attack_animation(M) - add_logs(user, M, "smacked", src) - -/obj/item/weapon/tome/attack_self(mob/user) - if(!iscultist(user)) - user << "[src] seems full of unintelligible shapes, scribbles, and notes. Is this some sort of joke?" - return - open_tome(user) - -/obj/item/weapon/tome/proc/open_tome(mob/user) - var/choice = alert(user,"You open the tome...",,"Scribe Rune","Read Tome") - switch(choice) - if("Read Tome") - read_tome(user) - if("Scribe Rune") - scribe_rune(user) - -/obj/item/weapon/tome/proc/read_tome(mob/user) - var/text = "" - text += "
Archives of the Dark One



" - text += "As a member of the cult, your goals are almost or entirely impossible to complete without special aid from the Geometer's plane. The primary method of doing this are runes. These \ - scribings, drawn in blood, are concentrated nodes of the magic within Nar-Sie's realm and will allow the performance of many tasks to aid you and the rest of the cult in your objectives. Runes \ - have many different names, and almost all of them are known as Rites. Each follower of the Geometer is only able to use a subset of these, thus you must go forth and meet your fellow followers. \ - A small description of each rune can be found below.

A rune's name and effects can be revealed by examining the rune.


" - - text += "Teleport
The Rite of Translocation is a unique rite in that it requires a keyword before the scribing can begin. When invoked, the rune will \ - search for other Rites of Translocation with the same keyword. Assuming one is found, the user will be instantaneously transported to the location of the other rune. If more than two runes are scribed \ - with the same keyword, it will choose randomly between all eligible runes and send the invoker to one of them.

" - - text += "Teleport Other
The Rite of Forced Translocation, like the Rite of Translocation, works by teleporting the person on the rune to one of the \ - same keyword. However, this rune will only work on people other than the user, allowing the user to send any living creature somewhere else.

" - - text += "Sacrifice
The Rite of Tribute is used to offer sacrifice to the Geometer. Simply place any living creature upon the rune and invoke it (this will not \ - target cultists!). If this creature has a mind, a soul shard will be created and the creature's soul transported to it. This rune is required if the cult's objectives include the sacrifice of a crew \ - member.

" - - text += "Raise Dead
The Rite of Resurrection is a delicate rite that requires two corpses. To perform the ritual, place the corpse you wish to revive onto \ - the rune and the offering body adjacent to it. When the rune is invoked, the body to be sacrificed will turn to dust, the life force flowing into the revival target. Assuming the target is not moved \ - within a few seconds, they will be brought back to life, healed of all ailments.

" - - text += "Veil Runes
The Rite of Obscurity is a rite that will cause all nearby runes to become invisible. The runes will still be considered by other rites \ - (such as the Rite of Translocation) but will be unusuable directly. Use the same rite once more to reveal these runes once more.

" - - text += "Electromagnetic Disruption
Robotic lifeforms have time and time again been the downfall of fledgling cults. The Rite of Disruption may allow you to gain the upper \ - hand against these pests. By using the rune, a large electromagnetic pulse will be emitted from the rune's location.

" - - text += "Astral Communion
The Rite of Astral Communion is perhaps the most ingenious rune that is usable by a single person. Upon invoking the rune, the \ - user's spirit will be ripped from their body. In this state, the user's physical body will be locked in place to the rune itself - any attempts to move it will result in the rune pulling it back. \ - The body will also take constant damage while in this form, and may even die. The user's spirit will contain their consciousness, and will allow them to freely wander the station as a ghost. This may \ - also be used to commune with the dead.

" - - text += "Form Shield
While simple, the Rite of the Corporeal Shield serves an important purpose in defense and hindering passage. When invoked, the \ - rune will draw a small amount of life force from the user and make the space above the rune completely dense, rendering it impassable to all but the most complex means. The rune may be invoked again to \ - undo this effect and allow passage again.

" - - text += "Debilitate
The Rite of the Shadowed Mind is simple. When invoked, it will cause all non-cultists that can see its rune to become deaf, blind and mute for a \ - considerable amount of time.

" - - text += "Stun
Though the Rite of Blazing Light is weak when invoked normally, merely disorienting and disabling nearby non-cultists, non-cultists that step on the rune \ - will be stunned without expending its power. However, the disorientation caused by this rune is quite brief, and when invoked directly, it will not remain for further invocation.

" - - text += "Summon Cultist
The Rite of Joined Souls requires two acolytes to use. When invoked, it will allow the user to summon a single cultist to the rune from \ - any location. This will deal a moderate amount of damage to all invokers.

" - - text += "Imbue Talisman
The Rite of Binding is the only way to create talismans. A blank sheet of paper must be on top of the rune, with a valid rune nearby. After \ - invoking it, the paper will be converted into a talisman, and the rune inlaid upon it.

" - - text += "Fabricate Shell
The Rite of Fabrication is the main way of creating construct shells. To use it, one must place five sheets of plasteel on top of the rune \ - and invoke it. The sheets will them be twisted into a construct shell, ready to recieve a soul to occupy it.

" - - text += "Summon Armaments
The Rite of Arming will equip the user with armored robes, a backpack, an eldrich longsword, and a pair of boots. Any items that cannot \ - be equipped will not be summoned.

" - - text += "Drain Life
The Rite of Leeching will drain the life of any non-cultist above the rune and heal the invoker for the same amount.

" - - text += "Blood Boil
The Rite of Boiling Blood may be considered one of the most dangerous rites composed by the cult of Nar-Sie. When invoked, it will do a \ - massive amount of damage to all non-cultist viewers, but it will also emit an explosion upon invocation. Use with caution

" - - text += "Immolate
The Rite of the Cleansing Flame is a weaker offensive rite, fit to be used by a lone cultist. When its rune is invoked, it will set any non-cultists \ - that can see it on fire. However, it will also burn its user.

" - - text += "Time Stop
The Rite of Dimensional Corruption is a versatile rite that can be very strong when protecting our cult from the enemies of the Geometer. \ - As it is invoked, it will rend and reshape reality around itself, stopping time for all those who don't follow the teachings of the Geometer. However, it requires more than one ritual soul as a \ - catalyst, and its power is bound to overflow and hurt its casters.

" - - text += "Manifest Spirit
If you wish to bring a spirit back from the dead with a wish for vengeance and desire to serve, the Rite of Spectral \ - Manifestation can do just that. When invoked, any spirits above the rune will be brought to life as a human wearing nothing that seeks only to serve you and the Geometer. However, the spirit's link \ - to reality is fragile - you must remain on top of the rune, and you will slowly take damage. Upon stepping off the rune, the spirits will dissipate, dropping their items to the ground. You may manifest \ - multiple spirits with one rune, but you will rapidly take damage in doing so.

" - - text += "While runes are excellent for many tasks, they lack portability. The advent of talismans has, to a degree, solved this inconvenience. Simply put, a talisman is a piece of paper with a \ - rune inlaid within it. The words of the rune can be whispered in order to invoke its effects, although usually to a lesser extent. To create a talisman, simply use a Rite of Binding as described above. \ - Unless stated otherwise, talismans are invoked by activating them in your hand. A list of valid rites, as well as the effects of their talisman form, can be found below.


" - - text += "Talisman of Teleportation
The talisman form of the Rite of Translocation will transport the invoker to a randomly chosen rune of the same keyword, then \ - disappear.

" - - text += "Talismans of Veiling
This talisman functions identically to its rune counterpart.It will disappear after one use.

" - - text += "Talisman of Electromagnetic Pulse
This talisman functions like the Rite of Disruption. It disappears after one use.

" - - text += "Talisman of Immolation
This talisman functions exactly like the Rite of Cleansing Flame. It can only be used once.

" - - var/datum/browser/popup = new(user, "tome", "", 800, 600) - popup.set_content(text) - popup.open() - return 1 - -/obj/item/weapon/tome/proc/scribe_rune(mob/user) - var/chosen_keyword - var/rune_to_scribe - var/entered_rune_name - var/list/possible_runes = list() - for(var/T in known_runes) - var/obj/effect/rune/R = T - if(initial(R.cultist_name)) - possible_runes += initial(R.cultist_name) //This is to allow the menu to let cultists select runes by name rather than by object path. I don't know a better way to do this - if(!possible_runes.len) - return - entered_rune_name = input(user, "Choose a rite to scribe.", "Sigils of Power") as null|anything in possible_runes - for(var/T in typesof(/obj/effect/rune)) - var/obj/effect/rune/R = T - if(initial(R.cultist_name) == entered_rune_name) - rune_to_scribe = R - if(initial(R.req_keyword)) - var/the_keyword = stripped_input(usr, "Please enter a keyword for the rune.", "Enter Keyword", "") - if(!the_keyword) - return - chosen_keyword = the_keyword - break - if(!rune_to_scribe) - return - user.visible_message("[user] cuts open their arm and begins writing in their own blood!", \ - "You slice open your arm and begin drawing a sigil of the Geometer.") - if(iscarbon(user)) - var/mob/living/carbon/C = user - C.apply_damage(0.1, BRUTE, pick("l_arm", "r_arm")) - if(!do_after(user, 50, target = get_turf(user))) - return - user.visible_message("[user] creates a strange circle in their own blood.", \ - "You finish drawing the arcane markings of the Geometer.") - var/obj/effect/rune/R = new rune_to_scribe(get_turf(user)) - if(chosen_keyword) - R.keyword = chosen_keyword +/* + +This file contains the arcane tome files as well as innate cultist emergency communications. + +*/ + +/mob/proc/cult_add_comm() //why the fuck does this have its own proc? not removing it because it might be used somewhere but... + verbs += /mob/living/proc/cult_innate_comm + +/mob/living/proc/cult_innate_comm() + set category = "Cultist" + set name = "Communion" + + if(!iscultist(usr)) //they shouldn't have this verb, but just to be sure... + return + + if(usr.incapacitated()) + return //dead men tell no tales + + var/input = stripped_input(usr, "Please choose a message to tell to the other acolytes.", "Voice of Blood", "") + if(!input) // TO-DO: Add some kind of filter to corrupt the inputted text + return + + cultist_commune(usr, 0, input) + return + + +/obj/item/weapon/tome + name = "arcane tome" + desc = "An old, dusty tome with frayed edges and a sinister-looking cover." + icon_state ="tome" + throw_speed = 2 + throw_range = 5 + w_class = 2 + var/list/known_runes = null + +/obj/item/weapon/tome/New() + ..() + known_runes = shuffle(subtypesof(/obj/effect/rune) - /obj/effect/rune/malformed) + var/known_amount = round((known_runes.len)/2) + known_runes.Cut(known_amount) + +/obj/item/weapon/tome/examine(mob/user) + ..() + if(iscultist(user)) + user << "The scriptures of the Geometer. Allows the scribing of runes and access to the knowledge archives of the cult of Nar-Sie." + +/obj/item/weapon/tome/attack(mob/living/M, mob/living/user) + if(istype(M,/mob/dead/observer)) + M.invisibility = 0 + user.visible_message("[user] strikes the air with [src], and a ghost appears!", \ + "You drag the ghost to your plane of reality!") + add_logs(user, M, "smacked", src) + return + if(!istype(M)) + return + if(!iscultist(user)) + return ..() + if(iscultist(M)) + if(M.reagents && M.reagents.has_reagent("holywater")) //allows cultists to be rescued from the clutches of ordained religion + user << "You remove the taint from [M]." + var/holy2unholy = M.reagents.get_reagent_amount("holywater") + M.reagents.del_reagent("holywater") + M.reagents.add_reagent("unholywater",holy2unholy) + add_logs(user, M, "smacked", src, " removing the holy water from them") + return + M.take_organ_damage(0, 15) //Used to be a random between 5 and 20 + playsound(M, 'sound/weapons/sear.ogg', 50, 1) + M.visible_message("[user] strikes [M] with the arcane tome!", \ + "[user] strikes you with the tome, searing your flesh!") + flick("tome_attack", src) + user.do_attack_animation(M) + add_logs(user, M, "smacked", src) + +/obj/item/weapon/tome/attack_self(mob/user) + if(!iscultist(user)) + user << "[src] seems full of unintelligible shapes, scribbles, and notes. Is this some sort of joke?" + return + open_tome(user) + +/obj/item/weapon/tome/proc/open_tome(mob/user) + var/choice = alert(user,"You open the tome...",,"Scribe Rune","Read Tome") + switch(choice) + if("Read Tome") + read_tome(user) + if("Scribe Rune") + scribe_rune(user) + +/obj/item/weapon/tome/proc/read_tome(mob/user) + var/text = "" + text += "
Archives of the Dark One



" + text += "As a member of the cult, your goals are almost or entirely impossible to complete without special aid from the Geometer's plane. The primary method of doing this are runes. These \ + scribings, drawn in blood, are concentrated nodes of the magic within Nar-Sie's realm and will allow the performance of many tasks to aid you and the rest of the cult in your objectives. Runes \ + have many different names, and almost all of them are known as Rites. Each follower of the Geometer is only able to use a subset of these, thus you must go forth and meet your fellow followers. \ + A small description of each rune can be found below.

A rune's name and effects can be revealed by examining the rune.


" + + text += "Teleport
The Rite of Translocation is a unique rite in that it requires a keyword before the scribing can begin. When invoked, the rune will \ + search for other Rites of Translocation with the same keyword. Assuming one is found, the user will be instantaneously transported to the location of the other rune. If more than two runes are scribed \ + with the same keyword, it will choose randomly between all eligible runes and send the invoker to one of them.

" + + text += "Teleport Other
The Rite of Forced Translocation, like the Rite of Translocation, works by teleporting the person on the rune to one of the \ + same keyword. However, this rune will only work on people other than the user, allowing the user to send any living creature somewhere else.

" + + text += "Sacrifice
The Rite of Tribute is used to offer sacrifice to the Geometer. Simply place any living creature upon the rune and invoke it (this will not \ + target cultists!). If this creature has a mind, a soul shard will be created and the creature's soul transported to it. This rune is required if the cult's objectives include the sacrifice of a crew \ + member.

" + + text += "Raise Dead
The Rite of Resurrection is a delicate rite that requires two corpses. To perform the ritual, place the corpse you wish to revive onto \ + the rune and the offering body adjacent to it. When the rune is invoked, the body to be sacrificed will turn to dust, the life force flowing into the revival target. Assuming the target is not moved \ + within a few seconds, they will be brought back to life, healed of all ailments.

" + + text += "Veil Runes
The Rite of Obscurity is a rite that will cause all nearby runes to become invisible. The runes will still be considered by other rites \ + (such as the Rite of Translocation) but will be unusuable directly. Use the same rite once more to reveal these runes once more.

" + + text += "Electromagnetic Disruption
Robotic lifeforms have time and time again been the downfall of fledgling cults. The Rite of Disruption may allow you to gain the upper \ + hand against these pests. By using the rune, a large electromagnetic pulse will be emitted from the rune's location.

" + + text += "Astral Communion
The Rite of Astral Communion is perhaps the most ingenious rune that is usable by a single person. Upon invoking the rune, the \ + user's spirit will be ripped from their body. In this state, the user's physical body will be locked in place to the rune itself - any attempts to move it will result in the rune pulling it back. \ + The body will also take constant damage while in this form, and may even die. The user's spirit will contain their consciousness, and will allow them to freely wander the station as a ghost. This may \ + also be used to commune with the dead.

" + + text += "Form Shield
While simple, the Rite of the Corporeal Shield serves an important purpose in defense and hindering passage. When invoked, the \ + rune will draw a small amount of life force from the user and make the space above the rune completely dense, rendering it impassable to all but the most complex means. The rune may be invoked again to \ + undo this effect and allow passage again.

" + + text += "Debilitate
The Rite of the Shadowed Mind is simple. When invoked, it will cause all non-cultists that can see its rune to become deaf, blind and mute for a \ + considerable amount of time.

" + + text += "Stun
Though the Rite of Blazing Light is weak when invoked normally, merely disorienting and disabling nearby non-cultists, non-cultists that step on the rune \ + will be stunned without expending its power. However, the disorientation caused by this rune is quite brief, and when invoked directly, it will not remain for further invocation.

" + + text += "Summon Cultist
The Rite of Joined Souls requires two acolytes to use. When invoked, it will allow the user to summon a single cultist to the rune from \ + any location. This will deal a moderate amount of damage to all invokers.

" + + text += "Imbue Talisman
The Rite of Binding is the only way to create talismans. A blank sheet of paper must be on top of the rune, with a valid rune nearby. After \ + invoking it, the paper will be converted into a talisman, and the rune inlaid upon it.

" + + text += "Fabricate Shell
The Rite of Fabrication is the main way of creating construct shells. To use it, one must place five sheets of plasteel on top of the rune \ + and invoke it. The sheets will them be twisted into a construct shell, ready to recieve a soul to occupy it.

" + + text += "Summon Armaments
The Rite of Arming will equip the user with armored robes, a backpack, an eldrich longsword, and a pair of boots. Any items that cannot \ + be equipped will not be summoned.

" + + text += "Drain Life
The Rite of Leeching will drain the life of any non-cultist above the rune and heal the invoker for the same amount.

" + + text += "Blood Boil
The Rite of Boiling Blood may be considered one of the most dangerous rites composed by the cult of Nar-Sie. When invoked, it will do a \ + massive amount of damage to all non-cultist viewers, but it will also emit an explosion upon invocation. Use with caution

" + + text += "Immolate
The Rite of the Cleansing Flame is a weaker offensive rite, fit to be used by a lone cultist. When its rune is invoked, it will set any non-cultists \ + that can see it on fire. However, it will also burn its user.

" + + text += "Time Stop
The Rite of Dimensional Corruption is a versatile rite that can be very strong when protecting our cult from the enemies of the Geometer. \ + As it is invoked, it will rend and reshape reality around itself, stopping time for all those who don't follow the teachings of the Geometer. However, it requires more than one ritual soul as a \ + catalyst, and its power is bound to overflow and hurt its casters.

" + + text += "Manifest Spirit
If you wish to bring a spirit back from the dead with a wish for vengeance and desire to serve, the Rite of Spectral \ + Manifestation can do just that. When invoked, any spirits above the rune will be brought to life as a human wearing nothing that seeks only to serve you and the Geometer. However, the spirit's link \ + to reality is fragile - you must remain on top of the rune, and you will slowly take damage. Upon stepping off the rune, the spirits will dissipate, dropping their items to the ground. You may manifest \ + multiple spirits with one rune, but you will rapidly take damage in doing so.

" + + text += "While runes are excellent for many tasks, they lack portability. The advent of talismans has, to a degree, solved this inconvenience. Simply put, a talisman is a piece of paper with a \ + rune inlaid within it. The words of the rune can be whispered in order to invoke its effects, although usually to a lesser extent. To create a talisman, simply use a Rite of Binding as described above. \ + Unless stated otherwise, talismans are invoked by activating them in your hand. A list of valid rites, as well as the effects of their talisman form, can be found below.


" + + text += "Talisman of Teleportation
The talisman form of the Rite of Translocation will transport the invoker to a randomly chosen rune of the same keyword, then \ + disappear.

" + + text += "Talismans of Veiling
This talisman functions identically to its rune counterpart.It will disappear after one use.

" + + text += "Talisman of Electromagnetic Pulse
This talisman functions like the Rite of Disruption. It disappears after one use.

" + + text += "Talisman of Immolation
This talisman functions exactly like the Rite of Cleansing Flame. It can only be used once.

" + + var/datum/browser/popup = new(user, "tome", "", 800, 600) + popup.set_content(text) + popup.open() + return 1 + +/obj/item/weapon/tome/proc/scribe_rune(mob/user) + var/chosen_keyword + var/rune_to_scribe + var/entered_rune_name + var/list/possible_runes = list() + for(var/T in known_runes) + var/obj/effect/rune/R = T + if(initial(R.cultist_name)) + possible_runes += initial(R.cultist_name) //This is to allow the menu to let cultists select runes by name rather than by object path. I don't know a better way to do this + if(!possible_runes.len) + return + entered_rune_name = input(user, "Choose a rite to scribe.", "Sigils of Power") as null|anything in possible_runes + for(var/T in typesof(/obj/effect/rune)) + var/obj/effect/rune/R = T + if(initial(R.cultist_name) == entered_rune_name) + rune_to_scribe = R + if(initial(R.req_keyword)) + var/the_keyword = stripped_input(usr, "Please enter a keyword for the rune.", "Enter Keyword", "") + if(!the_keyword) + return + chosen_keyword = the_keyword + break + if(!rune_to_scribe) + return + user.visible_message("[user] cuts open their arm and begins writing in their own blood!", \ + "You slice open your arm and begin drawing a sigil of the Geometer.") + if(iscarbon(user)) + var/mob/living/carbon/C = user + C.apply_damage(0.1, BRUTE, pick("l_arm", "r_arm")) + if(!do_after(user, 50, target = get_turf(user))) + return + user.visible_message("[user] creates a strange circle in their own blood.", \ + "You finish drawing the arcane markings of the Geometer.") + var/obj/effect/rune/R = new rune_to_scribe(get_turf(user)) + if(chosen_keyword) + R.keyword = chosen_keyword diff --git a/code/game/gamemodes/cult/runes.dm b/code/game/gamemodes/cult/runes.dm index 6ecc6a696c3..f3c241d90dd 100644 --- a/code/game/gamemodes/cult/runes.dm +++ b/code/game/gamemodes/cult/runes.dm @@ -1,884 +1,884 @@ -/* - -This file contains runes. -Runes are used by the cult to cause many different effects and are paramount to their success. -They are drawn with an arcane tome in blood, and are distinguishable to cultists and normal crew by examining. -Fake runes can be drawn in crayon to fool people. -Runes can either be invoked by one's self or with many different cultists. Each rune has a specific incantation that the cultists will say when invoking it. - -To draw a rune, use an arcane tome. - -*/ - -/obj/effect/rune - name = "rune" - var/cultist_name = "basic rune" - desc = "An odd collection of symbols drawn in what seems to be blood." - var/cultist_desc = "A basic rune with no function." //This is shown to cultists who examine the rune in order to determine its true purpose. - anchored = 1 - icon = 'icons/obj/rune.dmi' - icon_state = "1" - unacidable = 1 - layer = TURF_LAYER - color = rgb(255,0,0) - mouse_opacity = 2 - - var/invocation = "Aiy ele-mayo!" //This is said by cultists when the rune is invoked. - var/req_cultists = 1 //The amount of cultists required around the rune to invoke it. If only 1, any cultist can invoke it. - var/rune_in_use = 0 // Used for some runes, this is for when you want a rune to not be usable when in use. - - var/req_pylons = 0 - var/req_forges = 0 - var/req_archives = 0 - var/req_altars = 0 - - var/req_keyword = 0 //If the rune requires a keyword - go figure amirite - var/keyword //The actual keyword for the rune - -/obj/effect/rune/examine(mob/user) - ..() - if(iscultist(user) || user.stat == DEAD) //If they're a cultist or a ghost, tell them the effects - user << "Name: [cultist_name]" - user << "Effects: [cultist_desc]" - user << "Required Acolytes: [req_cultists]" - if(req_keyword && keyword) - user << "Keyword: [keyword]" - -/obj/effect/rune/attackby(obj/I, mob/user, params) - if(istype(I, /obj/item/weapon/tome) && iscultist(user)) - user << "You carefully erase [src]." - qdel(src) - return - else if(istype(I, /obj/item/weapon/nullrod)) - user << "You disrupt the magic of [src] with [I]." - qdel(src) - return - return - -/obj/effect/rune/attack_hand(mob/living/user) - if(!iscultist(user)) - user << "You aren't able to understand the words of [src]." - return - if(can_invoke(user)) - invoke(user) - var/oldtransform = transform - animate(src, transform = matrix()*2, alpha = 0, time = 5) //fade out - animate(transform = oldtransform, alpha = 255, time = 0) - else - fail_invoke(user) - -/* - -There are a few different procs each rune runs through when a cultist activates it. -can_invoke() is called when a cultist activates the rune with an empty hand. If there are multiple cultists, this rune determines if the required amount is nearby. -invoke() is the rune's actual effects. -fail_invoke() is called when the rune fails, via not enough people around or otherwise. Typically this just has a generic 'fizzle' effect. -structure_check() searches for nearby cultist structures required for the invocation. Proper structures are pylons, forges, archives, and altars. - -*/ - -/obj/effect/rune/proc/can_invoke(var/mob/living/user) - //This proc determines if the rune can be invoked at the time. If there are multiple required cultists, it will find all nearby cultists. - if(!structure_check(req_pylons, req_forges, req_archives, req_altars)) - return 0 - if(!keyword && req_keyword) - return 0 - if(req_cultists <= 1) - if(invocation) - user.say(invocation) - return 1 - else - var/cultists_in_range = 0 - for(var/mob/living/L in range(1, src)) - if(iscultist(L)) - var/mob/living/carbon/human/H = L - if(!istype(H)) - if(istype(L, /mob/living/simple_animal/hostile/construct)) - if(invocation) - L.say(invocation) - cultists_in_range++ - continue - if(L.stat || (H.disabilities & MUTE) || H.silent) - continue - if(invocation) - L.say(invocation) - cultists_in_range++ - if(cultists_in_range >= req_cultists) - return 1 - else - return 0 - -/obj/effect/rune/proc/invoke(var/mob/living/user) - //This proc contains the effects of the rune as well as things that happen afterwards. If you want it to spawn an object and then delete itself, have both here. - -/obj/effect/rune/proc/fail_invoke(var/mob/living/user) - //This proc contains the effects of a rune if it is not invoked correctly, through either invalid wording or not enough cultists. By default, it's just a basic fizzle. - visible_message("The markings pulse with a small flash of red light, then fall dark.") - -/obj/effect/rune/proc/structure_check(var/rpylons, var/rforges, var/rarchives, var/raltars) - var/pylons = 0 - var/forges = 0 - var/archives = 0 - var/altars = 0 - for(var/obj/structure/cult/pylon in orange(3,src)) - pylons++ - for(var/obj/structure/cult/forge in orange(3,src)) - forges++ - for(var/obj/structure/cult/tome in orange(3,src)) - archives++ - for(var/obj/structure/cult/talisman in orange(3,src)) - altars++ - if(pylons >= rpylons && forges >= rforges && archives >= rarchives && altars >= raltars) - return 1 - return 0 - - -//Malformed Rune: This forms if a rune is not drawn correctly. Invoking it does nothing but hurt the user. -/obj/effect/rune/malformed - cultist_name = "malformed rune" - cultist_desc = "A senseless rune written in gibberish. No good can come from invoking this." - invocation = "Ra'sha yoka!" - -/obj/effect/rune/malformed/New() - ..() - icon_state = "[rand(1,6)]" - color = rgb(rand(0,255), rand(0,255), rand(0,255)) - -/obj/effect/rune/malformed/invoke(mob/living/user) - user << "You feel your life force draining. The Geometer is displeased." - user.apply_damage(30, BRUTE) - qdel(src) - -/mob/proc/null_rod_check() //The null rod, if equipped, will protect the holder from the effects of most runes - var/obj/item/weapon/nullrod/N = locate() in src - if(N) - return 1 - return 0 - -var/list/teleport_runes = list() -//Rite of Translocation: Warps the user to a random teleport rune with the same keyword. -/obj/effect/rune/teleport - cultist_name = "Teleport" - cultist_desc = "Warps the user to a random rune of the same keyword." - invocation = "Sas'so c'arta forbici!" - icon_state = "2" - color = rgb(0, 0, 255) - req_keyword = 1 - -/obj/effect/rune/teleport/New() - ..() - teleport_runes.Add(src) - -/obj/effect/rune/teleport/Destroy() - teleport_runes.Remove(src) - ..() - -/obj/effect/rune/teleport/invoke(mob/living/user) - var/list/potential_runes = list() - for(var/obj/effect/rune/teleport/T in teleport_runes) - if(T.keyword == src.keyword && T != src) - potential_runes.Add(T) - - if(!potential_runes.len) - user << "There are no runes with the same keyword!" - fail_invoke() - log_game("Teleport rune failed - no candidates with matching keyword") - return - - var/obj/effect/rune/selected_rune = pick(potential_runes) - if(user.buckled) - user.buckled.unbuckle_mob() - user.visible_message("[user] vanishes in a flash of red light!", \ - "Your vision blurs, and you suddenly appear somewhere else.") - user.forceMove(get_turf(selected_rune)) - - -var/list/teleport_other_runes = list() -//Rite of Forced Translocation: Warps the target to a random teleport rune with the same keyword. -/obj/effect/rune/teleport_other - cultist_name = "Teleport Other" - cultist_desc = "Warps the target to a random rune of the same keyword." - invocation = "Sas'so c'arta forbica!" - icon_state = "1" - color = rgb(200, 0, 0) - req_keyword = 1 - -/obj/effect/rune/teleport_other/New() - ..() - teleport_other_runes.Add(src) - -/obj/effect/rune/teleport_other/Destroy() - teleport_other_runes.Remove(src) - ..() - -/obj/effect/rune/teleport_other/invoke(mob/living/user) - var/list/potential_runes = list() - for(var/obj/effect/rune/teleport_other/T in teleport_other_runes) - if(T.keyword == src.keyword && T != src) - potential_runes.Add(T) - - if(!potential_runes.len) - user << "There are no runes with the same keyword!" - fail_invoke() - log_game("Teleport Other rune failed - no candidates with matching keyword") - return - - var/obj/effect/rune/selected_rune = pick(potential_runes) - var/mob/living/target - - var/list/targets = list() - for(var/mob/living/L in get_turf(src)) - if(L != user) - targets.Add(L) - if(!targets.len) - user << "There are no targets standing on the rune!" - fail_invoke() - log_game("Teleport Other rune failed - no targets on rune") - return - if(targets.len > 1) - target = input(user, "Choose a person to teleport.", "Rite of Forced Translocation") as null|anything in targets - user - if(!target) - fail_invoke() - return - else - target = targets[targets.len] - if(target.buckled) - target.buckled.unbuckle_mob() - target.visible_message("[target] vanishes in a flash of red light!", \ - "Your vision blurs, and you suddenly appear somewhere else.") - target.forceMove(get_turf(selected_rune)) - -//Rite of Tribute: Sacrifices a crew member to Nar-Sie. Places them into a soul shard if they're in their body. -/obj/effect/rune/sacrifice - cultist_name = "Sacrifice" - cultist_desc = "Sacrifices a crew member to the Geometer. May place them into a soul shard if their spirit remains in their body." - icon_state = "3" - invocation = "Barhah hra zar'garis!" - color = rgb(255, 255, 255) - rune_in_use = 0 - -/obj/effect/rune/sacrifice/New() - ..() - icon_state = "[rand(1,6)]" - -/obj/effect/rune/sacrifice/invoke(mob/living/user) - if(rune_in_use) - return - rune_in_use = 1 - var/turf/T = get_turf(src) - var/list/possible_targets = list() - for(var/mob/living/M in T.contents) - if(!iscultist(M)) - possible_targets.Add(M) - var/mob/offering - if(possible_targets.len > 1) //If there's more than one target, allow choice - offering = input(user, "Choose an offering to sacrifice.", "Unholy Tribute") as null|anything in possible_targets - else if(possible_targets.len) //Otherwise, if there's a target at all, pick the only one - offering = possible_targets[possible_targets.len] - if(!offering) - rune_in_use = 0 - return - if(offering.null_rod_check()) - user << "Something is blocking the Geometer's magic!" - log_game("Sacrifice rune failed - target has null rod") - fail_invoke() - rune_in_use = 0 - return - if(((ishuman(offering) || isrobot(offering)) && offering.stat != DEAD)) //Requires two people to sacrifice living targets - var/cultists_nearby = 1 - for(var/mob/living/M in range(1,src)) - if(iscultist(M) && M != user) - cultists_nearby++ - M.say(invocation) - if(cultists_nearby < 2) - user << "[offering] is too greatly linked to the world! You need more acolytes!" - fail_invoke() - log_game("Sacrifice rune failed - not enough acolytes and target is living") - rune_in_use = 0 - return - visible_message("[src] pulses blood red!") - color = rgb(126, 23, 23) - spawn(5) - color = initial(color) - sac(offering) - -/obj/effect/rune/sacrifice/proc/sac(mob/living/T) - if(T) - PoolOrNew(/obj/effect/overlay/temp/cult/sac, src.loc) - for(var/mob/living/M in range(3,src)) - if(iscultist(M)) - if(ishuman(T) || isrobot(T)) - M << "\"I accept this sacrifice.\"" - else - M << "\"I accept this meager sacrifice.\"" - if(T.mind) - if(ishuman(T) || isrobot(T)) - new /obj/item/summoning_orb(get_turf(src)) - var/obj/item/device/soulstone/stone = new /obj/item/device/soulstone(get_turf(src)) - stone.invisibility = INVISIBILITY_MAXIMUM //so it's not picked up during transfer_soul() - if(!stone.transfer_soul("FORCE", T, usr)) //If it cannot be added - qdel(stone) - if(stone) - stone.invisibility = 0 - if(!T) - rune_in_use = 0 - return - if(isrobot(T)) - playsound(T, 'sound/magic/Disable_Tech.ogg', 100, 1) - T.dust() //To prevent the MMI from remaining - else - playsound(T, 'sound/magic/Disintegrate.ogg', 100, 1) - T.gib() - rune_in_use = 0 - - -//Rite of Resurrection: Requires two corpses. Revives one and gibs the other. -/obj/effect/rune/raise_dead - cultist_name = "Raise Dead" - cultist_desc = "Requires two corpses. The one placed upon the rune is brought to life, the other is turned to ash." - invocation = null //Depends on the name of the user - see below - icon_state = "1" - color = rgb(255, 0, 0) - -/obj/effect/rune/raise_dead/invoke(mob/living/user) - var/turf/T = get_turf(src) - var/mob/living/mob_to_sacrifice - var/mob/living/mob_to_revive - var/list/potential_sacrifice_mobs = list() - var/list/potential_revive_mobs = list() - if(rune_in_use) - return - for(var/mob/living/M in orange(1,src)) - if(!(M in T.contents) && M.stat == DEAD) - potential_sacrifice_mobs.Add(M) - if(!potential_sacrifice_mobs.len) - user << "There are no eligible sacrifices nearby!" - log_game("Raise Dead rune failed - no catalyst corpse") - return - mob_to_sacrifice = input(user, "Choose a corpse to sacrifice.", "Corpse to Sacrifice") as null|anything in potential_sacrifice_mobs - for(var/mob/living/M in T.contents) - if(M.stat == DEAD) - potential_revive_mobs.Add(M) - if(rune_in_use) - return - if(!potential_revive_mobs.len) - user << "There is no eligible revival target on the rune!" - log_game("Raise Dead rune failed - no corpse to revived") - return - mob_to_revive = input(user, "Choose a corpse to revive.", "Corpse to Revive") as null|anything in potential_revive_mobs - revive(mob_to_revive, mob_to_sacrifice, user, T) - - //Begin revival -/obj/effect/rune/raise_dead/proc/revive(mob/living/mob_to_revive, mob/living/mob_to_sacrifice, mob/living/user, turf/T) - if(rune_in_use) - return - if(!in_range(mob_to_sacrifice,src)) - user << "The sacrificial target has been moved!" - fail_invoke() - log_game("Raise Dead rune failed - catalyst corpse moved") - return - if(!(mob_to_revive in T.contents)) - user << "The corpse to revive has been moved!" - fail_invoke() - log_game("Raise Dead rune failed - revival target moved") - return - if(mob_to_sacrifice.stat != DEAD) - user << "The sacrificial target must be dead!" - fail_invoke() - log_game("Raise Dead rune failed - catalyst corpse is not dead") - return - rune_in_use = 1 - if(user.name == "Herbert West") - user.say("To life, to life, I bring them!") - else - user.say("Pasnar val'keriam usinar. Savrae ines amutan. Yam'toth remium il'tarat!") - mob_to_sacrifice.visible_message("[mob_to_sacrifice]'s body rises into the air, connected to [mob_to_revive] by a glowing tendril!") - mob_to_revive.Beam(mob_to_sacrifice,icon_state="sendbeam",icon='icons/effects/effects.dmi',time=20) - sleep(20) - if(!mob_to_sacrifice || !in_range(mob_to_sacrifice, src)) - return - mob_to_sacrifice.visible_message("[mob_to_sacrifice] disintegrates into a pile of bones[mob_to_revive ? ", the glowing tendril sinking into [mob_to_revive]'s body":""].") - mob_to_sacrifice.dust() - if(!mob_to_revive || mob_to_revive.stat != DEAD) - visible_message("The glowing tendril snaps against the rune with a shocking crack.") - rune_in_use = 0 - return - mob_to_revive.revive() //This does remove disabilities and such, but the rune might actually see some use because of it! - mob_to_revive << "\"PASNAR SAVRAE YAM'TOTH. Arise.\"" - mob_to_revive.visible_message("[mob_to_revive] draws in a huge breath, red light shining from their eyes.", \ - "You awaken suddenly from the void. You're alive!") - rune_in_use = 0 - - -/obj/effect/rune/raise_dead/fail_invoke() - for(var/mob/living/M in orange(1,src)) - if(M.stat == DEAD) - M.visible_message("[M] twitches.") - - -//Rite of Obscurity: Turns all runes within a 3-tile radius invisible or reveals them again. -/obj/effect/rune/hide_runes - cultist_name = "Veil Runes" - cultist_desc = "Turns nearby runes invisible. They can be revealed by using this rune again." - invocation = "Kla'atu barada nikt'o!" - icon_state = "1" - color = rgb(0,0,255) - -/obj/effect/rune/hide_runes/invoke(mob/living/user) - visible_message("[src] darkens to black and vanishes.") - for(var/obj/effect/rune/R in orange(3,src)) - if(R.invisibility == INVISIBILITY_OBSERVER) - R.invisibility = 0 - R.alpha = initial(R.alpha) - else - R.visible_message("[R] fades away.") - R.invisibility = INVISIBILITY_OBSERVER - R.alpha = 100 //To help ghosts distinguish hidden runes - qdel(src) - - -//Rite of Disruption: Emits an EMP blast. -/obj/effect/rune/emp - cultist_name = "Electromagnetic Disruption" - cultist_desc = "Emits a large electromagnetic pulse, hindering electronics and disabling silicons." - invocation = "Ta'gh fara'qha fel d'amar det!" - icon_state = "5" - color = rgb(255, 0, 0) - -/obj/effect/rune/emp/invoke(mob/living/user) - visible_message("[src] glows blue for a moment before vanishing.") - for(var/mob/living/carbon/C in orange(1,src)) - C << "You feel a minute vibration pass through you!" - playsound(get_turf(src), 'sound/items/Welder2.ogg', 25, 1) - empulse(src, 4, 8) //A bit less than an EMP grenade - qdel(src) - - -//Rite of Astral Communion: Separates one's spirit from their body. They will take damage while it is active. -/obj/effect/rune/astral - cultist_name = "Astral Communion" - cultist_desc = "Severs the link between one's spirit and body. This effect is taxing and one's physical body will take damage while this is active." - invocation = "Fwe'sh mah erl nyag r'ya!" - icon_state = "6" - color = rgb(126, 23, 23) - rune_in_use = 0 //One at a time, please! - var/mob/living/affecting = null - -/obj/effect/rune/astral/examine(mob/user) - ..() - if(affecting) - user << "A translucent field encases [user] above the rune!" - -/obj/effect/rune/astral/invoke(mob/living/user) - if(rune_in_use) - user << "[src] cannot support more than one body!" - fail_invoke() - log_game("Astral Communion rune failed - more than one user") - return - var/turf/T = get_turf(src) - if(!user in T.contents) - user << "You must be standing on top of [src]!" - fail_invoke() - log_game("Astral Communion rune failed - user not standing on rune") - return - rune_in_use = 1 - affecting = user - user.color = "#7e1717" - user.visible_message("[user] freezes statue-still, glowing an unearthly red.", \ - "You see what lies beyond. All is revealed. While this is a wondrous experience, your physical form will waste away in this state. Hurry...") - user.ghostize(1) - while(user) - if(!affecting) - visible_message("[src] pulses gently before falling dark.") - affecting = null //In case it's assigned to a number or something - rune_in_use = 0 - return - affecting.apply_damage(1, BRUTE) - if(!(user in T.contents)) - user.visible_message("A spectral tendril wraps around [user] and pulls them back to the rune!") - Beam(user,icon_state="drainbeam",icon='icons/effects/effects.dmi',time=2) - user.forceMove(get_turf(src)) //NO ESCAPE :^) - if(user.key) - user.visible_message("[user] slowly relaxes, the glow around them dimming.", \ - "You are re-united with your physical form. [src] releases its hold over you.") - user.color = initial(user.color) - user.Weaken(3) - rune_in_use = 0 - affecting = null - return - if(user.stat == UNCONSCIOUS) - if(prob(10)) - var/mob/dead/observer/G = user.get_ghost() - if(G) - G << "You feel the link between you and your body weakening... you must hurry!" - if(user.stat == DEAD) - user.color = initial(user.color) - rune_in_use = 0 - affecting = null - var/mob/dead/observer/G = user.get_ghost() - if(G) - G << "You suddenly feel your physical form pass on. [src]'s exertion has killed you!" - return - sleep(10) - rune_in_use = 0 - - -//Rite of the Corporeal Shield: When invoked, becomes solid and cannot be passed. Invoke again to undo. -/obj/effect/rune/wall - cultist_name = "Form Shield" - cultist_desc = "When invoked, makes the rune block passage. Can be invoked again to reverse this." - invocation = "Khari'd! Eske'te tannin!" - icon_state = "1" - color = rgb(255, 0, 0) - -/obj/effect/rune/wall/examine(mob/user) - ..() - if(density) - user << "There is a barely perceptible shimmering of the air above [src]." - -/obj/effect/rune/wall/invoke(mob/living/user) - density = !density - user.visible_message("[user] places their hands on [src], and [density ? "the air above it begins to shimmer" : "the shimmer above it fades"].", \ - "You channel your life energy into [src], [density ? "preventing" : "allowing"] passage above it.") - if(iscarbon(user)) - var/mob/living/carbon/C = user - C.apply_damage(2, BRUTE, pick("l_arm", "r_arm")) - - -//Rite of the Shadowed Mind: Deafens, blinds and mutes all non-cultists nearby. -/obj/effect/rune/deafen - cultist_name = "Debilitate" - cultist_desc = "Causes all non-followers nearby to lose their hearing, sight and voice." - invocation = "Sti kaliedir!" - color = rgb(0, 255, 0) - icon_state = "4" - -/obj/effect/rune/deafen/invoke(mob/living/user) - visible_message("[src] emits a blinding red flash!") - for(var/mob/living/carbon/C in viewers(src)) - if(!iscultist(C) && !C.null_rod_check()) - C << "You feel oily shadows cover your senses." - C.adjustEarDamage(0,50) - C.flash_eyes(1, 1) - C.eye_blurry += 50 - C.eye_blind += 20 - C.silent += 10 - qdel(src) - -//Rite of Disorientation: Stuns and mutes all non-cultists nearby for a brief time -/obj/effect/rune/stun - cultist_name = "Stun" - cultist_desc = "Stuns and mutes all nearby non-followers for a brief time." - invocation = "Fuu ma'jin!" - icon_state = "2" - color = rgb(100, 0, 100) - -/obj/effect/rune/stun/invoke(mob/living/user) - visible_message("[src] explodes in a bright flash!") - for(var/mob/living/M in viewers(src)) - stun_he(M) - qdel(src) - -/obj/effect/rune/stun/Crossed(atom/movable/AM) - ..() - if(isliving(AM)) - stun_he(AM) - -/obj/effect/rune/stun/proc/stun_he(mob/living/M) - if(!iscultist(M) && !M.null_rod_check()) - M << "You are disoriented by [src]!" - M.Weaken(3) - M.Stun(3) - M.flash_eyes(1,1) - if(iscarbon(M)) - var/mob/living/carbon/C = M - C.silent += 3 - -//Rite of Joined Souls: Summons a single cultist. -/obj/effect/rune/summon - cultist_name = "Summon Cultist" - cultist_desc = "Summons a single cultist to the rune." - invocation = "N'ath reth sh'yro eth d'rekkathnor!" - icon_state = "5" - color = rgb(0, 255, 0) - -/obj/effect/rune/summon/invoke(mob/living/user) - var/list/cultists = list() - for(var/datum/mind/M in ticker.mode.cult) - cultists.Add(M.current) - var/mob/living/cultist_to_summon = input("Who do you wish to call to [src]?", "Followers of the Geometer") as null|anything in (cultists - user) - if(!cultist_to_summon) - user << "You require a summoning target!" - fail_invoke() - log_game("Summon Cultist rune failed - no target") - return - if(!iscultist(cultist_to_summon)) - user << "[cultist_to_summon] is not a follower of the Geometer!" - fail_invoke() - log_game("Summon Cultist rune failed - no target") - return - if(cultist_to_summon.buckled) - cultist_to_summon.buckled.unbuckle_mob() - cultist_to_summon.visible_message("[cultist_to_summon] suddenly disappears in a flash of red light!", \ - "Overwhelming vertigo consumes you as you are hurled through the air!") - visible_message("A foggy shape materializes atop [src] and solidifes into [cultist_to_summon]!") - user.apply_damage(10, BRUTE, "head") - cultist_to_summon.loc = get_turf(src) - qdel(src) - - -//Rite of Binding: Turns a nearby rune and a paper on top of the rune to a talisman, if both are valid. -/obj/effect/rune/imbue - cultist_name = "Bind Talisman" - cultist_desc = "Transforms papers and valid runes into talismans." - invocation = null //no talisman made, no invocation. - icon_state = "3" - color = rgb(0, 0, 255) - -/obj/effect/rune/imbue/invoke(mob/living/user) - var/turf/T = get_turf(src) - var/list/papers_on_rune = list() - for(var/obj/item/weapon/paper/P in T) - if(!P.info) - papers_on_rune.Add(P) - if(!papers_on_rune.len) - user << "There must be a blank paper on top of [src]!" - fail_invoke() - log_game("Talisman Imbue rune failed - no blank papers on rune") - return - var/obj/item/weapon/paper/paper_to_imbue = pick(papers_on_rune) - var/list/nearby_runes = list() - for(var/obj/effect/rune/R in orange(1,src)) - nearby_runes.Add(R) - if(!nearby_runes.len) - user << "There are no runes near [src]!" - fail_invoke() - log_game("Talisman Imbue rune failed - no nearby runes") - return - var/obj/effect/rune/picked_rune = pick(nearby_runes) - var/list/split_rune_type = text2list("[picked_rune.type]", "/") - var/imbue_type = split_rune_type[split_rune_type.len] - var/talisman_type = text2path("/obj/item/weapon/paper/talisman/[imbue_type]") - if(ispath(talisman_type)) - user.say("H'drak v'loso, mir'kanas verbot!") - var/obj/item/weapon/paper/talisman/TA = new talisman_type(get_turf(src)) - if(istype(picked_rune, /obj/effect/rune/teleport)) - var/obj/effect/rune/teleport/TR = picked_rune - var/obj/item/weapon/paper/talisman/teleport/TT = TA - TT.keyword = TR.keyword - else - user << "The chosen rune was not a valid target!" - fail_invoke() - log_game("Talisman Imbue rune failed - chosen rune invalid") - return - visible_message("[picked_rune] crumbles to dust, and bloody images form themselves on [paper_to_imbue].") - qdel(paper_to_imbue) - qdel(picked_rune) - - -//Rite of Fabrication: Creates a construct shell out of 5 metal sheets. -/obj/effect/rune/construct_shell - cultist_name = "Fabricate Shell" - cultist_desc = "Turns five plasteel sheets into an empty construct shell, suitable for containing a soul shard." - invocation = null //see below; doesn't say the invocation unless there's enough sheets. - icon_state = "5" - color = rgb(150, 150, 150) - -/obj/effect/rune/construct_shell/invoke(mob/living/user) - var/turf/T = get_turf(src) - for(var/obj/item/stack/sheet/S in T) - if(istype(S, /obj/item/stack/sheet/plasteel)) - var/obj/item/stack/sheet/plasteel/M = S - if(M.amount >= 5) - user.say("Ethra p'ni dedol!") - new /obj/structure/constructshell(T) - M.visible_message("[M] bends and twists into a humanoid shell!") - M.amount -= 5 - if(M.amount <= 0) - qdel(M) - qdel(src) - return - else - user << "There must be at least five sheets of plasteel on [src]!" - fail_invoke() - log_game("Construct Shell rune failed - not enough plasteel sheets") - return - - -//Rite of Arming: Creates cult robes, a trophy rack, and a cult sword on the rune. -/obj/effect/rune/armor - cultist_name = "Summon Armaments" - cultist_desc = "Equips the user with robes, shoes, a backpack, and a longsword. Items that cannot be equipped will not be summoned." - invocation = "N'ath reth sh'yro eth draggathnor!" - icon_state = "4" - color = rgb(255, 0, 0) - -/obj/effect/rune/armor/invoke(mob/living/user) - visible_message("With the sound of clanging metal, [src] crumbles to dust!") - user.equip_to_slot_or_del(new /obj/item/clothing/head/culthood/alt(user), slot_head) - user.equip_to_slot_or_del(new /obj/item/clothing/suit/cultrobes/alt(user), slot_wear_suit) - user.equip_to_slot_or_del(new /obj/item/clothing/shoes/cult/alt(user), slot_shoes) - user.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/cultpack(user), slot_back) - user.put_in_hands(new /obj/item/weapon/melee/cultblade(user)) - qdel(src) - - -//Rite of Leeching: Deals brute damage to the target and heals the same amount to the invoker. -/obj/effect/rune/leeching - cultist_name = "Drain Life" - cultist_desc = "Drains the life of the target on the rune, restoring it to the user." - invocation = null //see below; doesn't say the invocation if it has no targets. - icon_state = "2" - color = rgb(255, 0, 0) - -/obj/effect/rune/leeching/invoke(mob/living/user) - var/turf/T = get_turf(src) - var/list/potential_targets = list() - for(var/mob/living/carbon/M in T) - if(M.stat != DEAD && M != user) - potential_targets.Add(M) - if(!potential_targets.len) - user << "There must be a valid target on the rune!" - fail_invoke() - log_game("Leeching rune failed - no valid targets") - return - var/mob/living/carbon/target = pick(potential_targets) - var/drained_amount = rand(5,25) - user.say("Yu'gular faras desdae. Umathar uf'kal thenar!") - target.apply_damage(drained_amount, BRUTE, "chest") - user.adjustBruteLoss(-drained_amount) - user.Beam(target,icon_state="drainbeam",icon='icons/effects/effects.dmi',time=3) - target << "You feel extremely weak." - user.visible_message("Blood flows from the rune into [user]!", \ - "[target]'s blood flows into you, healing your wounds and revitalizing your spirit.") - - -//Rite of Boiling Blood: Deals extremely high amounts of damage to non-cultists nearby -/obj/effect/rune/blood_boil - cultist_name = "Boil Blood" - cultist_desc = "Boils the blood of non-believers who can see the rune, dealing extreme amounts of damage." - invocation = "Dedo ol'btoh!" - icon_state = "4" - color = rgb(255, 0, 0) - req_cultists = 2 - -/obj/effect/rune/blood_boil/invoke(mob/living/user) - visible_message("[src] briefly bubbles before exploding!") - for(var/mob/living/carbon/C in viewers(src)) - if(!iscultist(C)) - if(C.null_rod_check()) - C << "The null rod suddenly burns hotly before returning to normal!" - continue - C << "Your blood boils in your veins!" - C.take_overall_damage(51,51) - for(var/mob/living/carbon/M in range(1,src)) - if(iscultist(M)) - M.apply_damage(15, BRUTE, pick("l_arm", "r_arm")) - M << "[src] saps your strength!" - explosion(get_turf(src), -1, 0, 1, 5) - qdel(src) - - -//Rite of the Cleansing Flame: Sets all non-cultists on fire. -/obj/effect/rune/flame - cultist_name = "Immolate" - cultist_desc = "Sets any non-believers who can see the rune on fire." - invocation = "Dedo va'batoh!" - icon_state = "2" - color = rgb(255, 0, 0) - -/obj/effect/rune/flame/invoke(mob/living/user) - visible_message("[src] burns away, scorching the floor below!") - for(var/mob/living/carbon/C in viewers(src)) - if(!iscultist(C) && !C.null_rod_check()) - C << "You feel your skin crisp as you burst into flames!" - C.fire_act() - user.apply_damage(15, BURN, pick("l_arm", "r_arm")) - user << "[src] burns your arms!" - var/turf/simulated/T = get_turf(src) - T.burn_tile() - qdel(src) - - -//Rite of Spectral Manifestation: Summons a ghost on top of the rune as a cultist human with no items. User must stand on the rune at all times, and takes damage for each summoned ghost. -/obj/effect/rune/manifest - cultist_name = "Manifest Spirit" - cultist_desc = "Manifests a spirit as a servant of the Geometer. The invoker must not move from atop the rune, and will take damage for each summoned spirit." - invocation = "Gal'h'rfikk harfrandid mud'gib!" //how the fuck do you pronounce this - icon_state = "6" - color = rgb(255, 0, 0) - -/obj/effect/rune/manifest/invoke(mob/living/user) - if(!(user in get_turf(src))) - user << "You must be standing on [src]!" - fail_invoke() - log_game("Manifest rune failed - user not standing on rune") - return - var/list/ghosts_on_rune = list() - for(var/mob/dead/observer/O in get_turf(src)) - if(O.client) - ghosts_on_rune.Add(O) - if(!ghosts_on_rune.len) - user << "There are no spirits near [src]!" - fail_invoke() - log_game("Manifest rune failed - no nearby ghosts") - return - var/mob/dead/observer/ghost_to_spawn = pick(ghosts_on_rune) - var/mob/living/carbon/human/new_human = new(get_turf(src)) - new_human.real_name = ghost_to_spawn.real_name - new_human.alpha = 150 //Makes them translucent - visible_message("A cloud of red mist forms above [src], and from within steps... a man.") - user << "Your blood begins flowing into [src]. You must remain in place and conscious to maintain the forms of those summoned. This will hurt you slowly but surely..." - new_human.key = ghost_to_spawn.key - ticker.mode.add_cultist(new_human.mind) - new_human << "You are a servant of the Geometer. You have been made semi-corporeal by the cult of Nar-Sie, and you are to serve them at all costs." - - while(user in get_turf(src)) - if(user.stat) - break - user.apply_damage(1, BRUTE) - sleep(30) - - if(new_human) - new_human.visible_message("[new_human] suddenly dissolves into bones and ashes.", \ - "Your link to the world fades. Your form breaks apart.") - for(var/obj/I in new_human) - new_human.unEquip(I) - new_human.dust() - -//Rite of Dimensional Corruption: Stops time around the rune for all non-cultists. -/obj/effect/rune/timestop - cultist_name = "Time Stop" - cultist_desc = "Stops time around the rune for all non-cultists." - invocation = "T'ak ot'marzah oahr'du!" - icon_state = "2" - color = rgb(0, 0, 255) - req_cultists = 2 - -/obj/effect/rune/timestop/invoke(mob/living/user) - visible_message("[src] flares up for a moment, and then disappears into itself!") - new /obj/effect/timestop/cult(get_turf(src)) - for(var/mob/living/carbon/M in range(1,src)) - if(iscultist(M)) - M.apply_damage(20, BRUTE, "chest") - M << "[src] temporarily stops your heart from beating!" - qdel(src) - -/obj/effect/timestop/cult - name = "Dimensional Corruption" - desc = "Something not from this world has corrupted the spacetime continuum in this exact spot." - icon = 'icons/effects/96x96.dmi' - icon_state = "rune_large" - pixel_x = -32 - pixel_y = -32 - duration = 50 - alpha = 0 - color = rgb(255, 0, 0) - -/obj/effect/timestop/cult/timestop() - animate(src, alpha = 255, time = 5, loop = -1) - SpinAnimation(speed = 5) - for(var/mob/living/M in player_list) - if(iscultist(M) || M.null_rod_check()) - immune |= M - ..() +/* + +This file contains runes. +Runes are used by the cult to cause many different effects and are paramount to their success. +They are drawn with an arcane tome in blood, and are distinguishable to cultists and normal crew by examining. +Fake runes can be drawn in crayon to fool people. +Runes can either be invoked by one's self or with many different cultists. Each rune has a specific incantation that the cultists will say when invoking it. + +To draw a rune, use an arcane tome. + +*/ + +/obj/effect/rune + name = "rune" + var/cultist_name = "basic rune" + desc = "An odd collection of symbols drawn in what seems to be blood." + var/cultist_desc = "A basic rune with no function." //This is shown to cultists who examine the rune in order to determine its true purpose. + anchored = 1 + icon = 'icons/obj/rune.dmi' + icon_state = "1" + unacidable = 1 + layer = TURF_LAYER + color = rgb(255,0,0) + mouse_opacity = 2 + + var/invocation = "Aiy ele-mayo!" //This is said by cultists when the rune is invoked. + var/req_cultists = 1 //The amount of cultists required around the rune to invoke it. If only 1, any cultist can invoke it. + var/rune_in_use = 0 // Used for some runes, this is for when you want a rune to not be usable when in use. + + var/req_pylons = 0 + var/req_forges = 0 + var/req_archives = 0 + var/req_altars = 0 + + var/req_keyword = 0 //If the rune requires a keyword - go figure amirite + var/keyword //The actual keyword for the rune + +/obj/effect/rune/examine(mob/user) + ..() + if(iscultist(user) || user.stat == DEAD) //If they're a cultist or a ghost, tell them the effects + user << "Name: [cultist_name]" + user << "Effects: [cultist_desc]" + user << "Required Acolytes: [req_cultists]" + if(req_keyword && keyword) + user << "Keyword: [keyword]" + +/obj/effect/rune/attackby(obj/I, mob/user, params) + if(istype(I, /obj/item/weapon/tome) && iscultist(user)) + user << "You carefully erase [src]." + qdel(src) + return + else if(istype(I, /obj/item/weapon/nullrod)) + user << "You disrupt the magic of [src] with [I]." + qdel(src) + return + return + +/obj/effect/rune/attack_hand(mob/living/user) + if(!iscultist(user)) + user << "You aren't able to understand the words of [src]." + return + if(can_invoke(user)) + invoke(user) + var/oldtransform = transform + animate(src, transform = matrix()*2, alpha = 0, time = 5) //fade out + animate(transform = oldtransform, alpha = 255, time = 0) + else + fail_invoke(user) + +/* + +There are a few different procs each rune runs through when a cultist activates it. +can_invoke() is called when a cultist activates the rune with an empty hand. If there are multiple cultists, this rune determines if the required amount is nearby. +invoke() is the rune's actual effects. +fail_invoke() is called when the rune fails, via not enough people around or otherwise. Typically this just has a generic 'fizzle' effect. +structure_check() searches for nearby cultist structures required for the invocation. Proper structures are pylons, forges, archives, and altars. + +*/ + +/obj/effect/rune/proc/can_invoke(var/mob/living/user) + //This proc determines if the rune can be invoked at the time. If there are multiple required cultists, it will find all nearby cultists. + if(!structure_check(req_pylons, req_forges, req_archives, req_altars)) + return 0 + if(!keyword && req_keyword) + return 0 + if(req_cultists <= 1) + if(invocation) + user.say(invocation) + return 1 + else + var/cultists_in_range = 0 + for(var/mob/living/L in range(1, src)) + if(iscultist(L)) + var/mob/living/carbon/human/H = L + if(!istype(H)) + if(istype(L, /mob/living/simple_animal/hostile/construct)) + if(invocation) + L.say(invocation) + cultists_in_range++ + continue + if(L.stat || (H.disabilities & MUTE) || H.silent) + continue + if(invocation) + L.say(invocation) + cultists_in_range++ + if(cultists_in_range >= req_cultists) + return 1 + else + return 0 + +/obj/effect/rune/proc/invoke(var/mob/living/user) + //This proc contains the effects of the rune as well as things that happen afterwards. If you want it to spawn an object and then delete itself, have both here. + +/obj/effect/rune/proc/fail_invoke(var/mob/living/user) + //This proc contains the effects of a rune if it is not invoked correctly, through either invalid wording or not enough cultists. By default, it's just a basic fizzle. + visible_message("The markings pulse with a small flash of red light, then fall dark.") + +/obj/effect/rune/proc/structure_check(var/rpylons, var/rforges, var/rarchives, var/raltars) + var/pylons = 0 + var/forges = 0 + var/archives = 0 + var/altars = 0 + for(var/obj/structure/cult/pylon in orange(3,src)) + pylons++ + for(var/obj/structure/cult/forge in orange(3,src)) + forges++ + for(var/obj/structure/cult/tome in orange(3,src)) + archives++ + for(var/obj/structure/cult/talisman in orange(3,src)) + altars++ + if(pylons >= rpylons && forges >= rforges && archives >= rarchives && altars >= raltars) + return 1 + return 0 + + +//Malformed Rune: This forms if a rune is not drawn correctly. Invoking it does nothing but hurt the user. +/obj/effect/rune/malformed + cultist_name = "malformed rune" + cultist_desc = "A senseless rune written in gibberish. No good can come from invoking this." + invocation = "Ra'sha yoka!" + +/obj/effect/rune/malformed/New() + ..() + icon_state = "[rand(1,6)]" + color = rgb(rand(0,255), rand(0,255), rand(0,255)) + +/obj/effect/rune/malformed/invoke(mob/living/user) + user << "You feel your life force draining. The Geometer is displeased." + user.apply_damage(30, BRUTE) + qdel(src) + +/mob/proc/null_rod_check() //The null rod, if equipped, will protect the holder from the effects of most runes + var/obj/item/weapon/nullrod/N = locate() in src + if(N) + return 1 + return 0 + +var/list/teleport_runes = list() +//Rite of Translocation: Warps the user to a random teleport rune with the same keyword. +/obj/effect/rune/teleport + cultist_name = "Teleport" + cultist_desc = "Warps the user to a random rune of the same keyword." + invocation = "Sas'so c'arta forbici!" + icon_state = "2" + color = rgb(0, 0, 255) + req_keyword = 1 + +/obj/effect/rune/teleport/New() + ..() + teleport_runes.Add(src) + +/obj/effect/rune/teleport/Destroy() + teleport_runes.Remove(src) + ..() + +/obj/effect/rune/teleport/invoke(mob/living/user) + var/list/potential_runes = list() + for(var/obj/effect/rune/teleport/T in teleport_runes) + if(T.keyword == src.keyword && T != src) + potential_runes.Add(T) + + if(!potential_runes.len) + user << "There are no runes with the same keyword!" + fail_invoke() + log_game("Teleport rune failed - no candidates with matching keyword") + return + + var/obj/effect/rune/selected_rune = pick(potential_runes) + if(user.buckled) + user.buckled.unbuckle_mob() + user.visible_message("[user] vanishes in a flash of red light!", \ + "Your vision blurs, and you suddenly appear somewhere else.") + user.forceMove(get_turf(selected_rune)) + + +var/list/teleport_other_runes = list() +//Rite of Forced Translocation: Warps the target to a random teleport rune with the same keyword. +/obj/effect/rune/teleport_other + cultist_name = "Teleport Other" + cultist_desc = "Warps the target to a random rune of the same keyword." + invocation = "Sas'so c'arta forbica!" + icon_state = "1" + color = rgb(200, 0, 0) + req_keyword = 1 + +/obj/effect/rune/teleport_other/New() + ..() + teleport_other_runes.Add(src) + +/obj/effect/rune/teleport_other/Destroy() + teleport_other_runes.Remove(src) + ..() + +/obj/effect/rune/teleport_other/invoke(mob/living/user) + var/list/potential_runes = list() + for(var/obj/effect/rune/teleport_other/T in teleport_other_runes) + if(T.keyword == src.keyword && T != src) + potential_runes.Add(T) + + if(!potential_runes.len) + user << "There are no runes with the same keyword!" + fail_invoke() + log_game("Teleport Other rune failed - no candidates with matching keyword") + return + + var/obj/effect/rune/selected_rune = pick(potential_runes) + var/mob/living/target + + var/list/targets = list() + for(var/mob/living/L in get_turf(src)) + if(L != user) + targets.Add(L) + if(!targets.len) + user << "There are no targets standing on the rune!" + fail_invoke() + log_game("Teleport Other rune failed - no targets on rune") + return + if(targets.len > 1) + target = input(user, "Choose a person to teleport.", "Rite of Forced Translocation") as null|anything in targets - user + if(!target) + fail_invoke() + return + else + target = targets[targets.len] + if(target.buckled) + target.buckled.unbuckle_mob() + target.visible_message("[target] vanishes in a flash of red light!", \ + "Your vision blurs, and you suddenly appear somewhere else.") + target.forceMove(get_turf(selected_rune)) + +//Rite of Tribute: Sacrifices a crew member to Nar-Sie. Places them into a soul shard if they're in their body. +/obj/effect/rune/sacrifice + cultist_name = "Sacrifice" + cultist_desc = "Sacrifices a crew member to the Geometer. May place them into a soul shard if their spirit remains in their body." + icon_state = "3" + invocation = "Barhah hra zar'garis!" + color = rgb(255, 255, 255) + rune_in_use = 0 + +/obj/effect/rune/sacrifice/New() + ..() + icon_state = "[rand(1,6)]" + +/obj/effect/rune/sacrifice/invoke(mob/living/user) + if(rune_in_use) + return + rune_in_use = 1 + var/turf/T = get_turf(src) + var/list/possible_targets = list() + for(var/mob/living/M in T.contents) + if(!iscultist(M)) + possible_targets.Add(M) + var/mob/offering + if(possible_targets.len > 1) //If there's more than one target, allow choice + offering = input(user, "Choose an offering to sacrifice.", "Unholy Tribute") as null|anything in possible_targets + else if(possible_targets.len) //Otherwise, if there's a target at all, pick the only one + offering = possible_targets[possible_targets.len] + if(!offering) + rune_in_use = 0 + return + if(offering.null_rod_check()) + user << "Something is blocking the Geometer's magic!" + log_game("Sacrifice rune failed - target has null rod") + fail_invoke() + rune_in_use = 0 + return + if(((ishuman(offering) || isrobot(offering)) && offering.stat != DEAD)) //Requires two people to sacrifice living targets + var/cultists_nearby = 1 + for(var/mob/living/M in range(1,src)) + if(iscultist(M) && M != user) + cultists_nearby++ + M.say(invocation) + if(cultists_nearby < 2) + user << "[offering] is too greatly linked to the world! You need more acolytes!" + fail_invoke() + log_game("Sacrifice rune failed - not enough acolytes and target is living") + rune_in_use = 0 + return + visible_message("[src] pulses blood red!") + color = rgb(126, 23, 23) + spawn(5) + color = initial(color) + sac(offering) + +/obj/effect/rune/sacrifice/proc/sac(mob/living/T) + if(T) + PoolOrNew(/obj/effect/overlay/temp/cult/sac, src.loc) + for(var/mob/living/M in range(3,src)) + if(iscultist(M)) + if(ishuman(T) || isrobot(T)) + M << "\"I accept this sacrifice.\"" + else + M << "\"I accept this meager sacrifice.\"" + if(T.mind) + if(ishuman(T) || isrobot(T)) + new /obj/item/summoning_orb(get_turf(src)) + var/obj/item/device/soulstone/stone = new /obj/item/device/soulstone(get_turf(src)) + stone.invisibility = INVISIBILITY_MAXIMUM //so it's not picked up during transfer_soul() + if(!stone.transfer_soul("FORCE", T, usr)) //If it cannot be added + qdel(stone) + if(stone) + stone.invisibility = 0 + if(!T) + rune_in_use = 0 + return + if(isrobot(T)) + playsound(T, 'sound/magic/Disable_Tech.ogg', 100, 1) + T.dust() //To prevent the MMI from remaining + else + playsound(T, 'sound/magic/Disintegrate.ogg', 100, 1) + T.gib() + rune_in_use = 0 + + +//Rite of Resurrection: Requires two corpses. Revives one and gibs the other. +/obj/effect/rune/raise_dead + cultist_name = "Raise Dead" + cultist_desc = "Requires two corpses. The one placed upon the rune is brought to life, the other is turned to ash." + invocation = null //Depends on the name of the user - see below + icon_state = "1" + color = rgb(255, 0, 0) + +/obj/effect/rune/raise_dead/invoke(mob/living/user) + var/turf/T = get_turf(src) + var/mob/living/mob_to_sacrifice + var/mob/living/mob_to_revive + var/list/potential_sacrifice_mobs = list() + var/list/potential_revive_mobs = list() + if(rune_in_use) + return + for(var/mob/living/M in orange(1,src)) + if(!(M in T.contents) && M.stat == DEAD) + potential_sacrifice_mobs.Add(M) + if(!potential_sacrifice_mobs.len) + user << "There are no eligible sacrifices nearby!" + log_game("Raise Dead rune failed - no catalyst corpse") + return + mob_to_sacrifice = input(user, "Choose a corpse to sacrifice.", "Corpse to Sacrifice") as null|anything in potential_sacrifice_mobs + for(var/mob/living/M in T.contents) + if(M.stat == DEAD) + potential_revive_mobs.Add(M) + if(rune_in_use) + return + if(!potential_revive_mobs.len) + user << "There is no eligible revival target on the rune!" + log_game("Raise Dead rune failed - no corpse to revived") + return + mob_to_revive = input(user, "Choose a corpse to revive.", "Corpse to Revive") as null|anything in potential_revive_mobs + revive(mob_to_revive, mob_to_sacrifice, user, T) + + //Begin revival +/obj/effect/rune/raise_dead/proc/revive(mob/living/mob_to_revive, mob/living/mob_to_sacrifice, mob/living/user, turf/T) + if(rune_in_use) + return + if(!in_range(mob_to_sacrifice,src)) + user << "The sacrificial target has been moved!" + fail_invoke() + log_game("Raise Dead rune failed - catalyst corpse moved") + return + if(!(mob_to_revive in T.contents)) + user << "The corpse to revive has been moved!" + fail_invoke() + log_game("Raise Dead rune failed - revival target moved") + return + if(mob_to_sacrifice.stat != DEAD) + user << "The sacrificial target must be dead!" + fail_invoke() + log_game("Raise Dead rune failed - catalyst corpse is not dead") + return + rune_in_use = 1 + if(user.name == "Herbert West") + user.say("To life, to life, I bring them!") + else + user.say("Pasnar val'keriam usinar. Savrae ines amutan. Yam'toth remium il'tarat!") + mob_to_sacrifice.visible_message("[mob_to_sacrifice]'s body rises into the air, connected to [mob_to_revive] by a glowing tendril!") + mob_to_revive.Beam(mob_to_sacrifice,icon_state="sendbeam",icon='icons/effects/effects.dmi',time=20) + sleep(20) + if(!mob_to_sacrifice || !in_range(mob_to_sacrifice, src)) + return + mob_to_sacrifice.visible_message("[mob_to_sacrifice] disintegrates into a pile of bones[mob_to_revive ? ", the glowing tendril sinking into [mob_to_revive]'s body":""].") + mob_to_sacrifice.dust() + if(!mob_to_revive || mob_to_revive.stat != DEAD) + visible_message("The glowing tendril snaps against the rune with a shocking crack.") + rune_in_use = 0 + return + mob_to_revive.revive() //This does remove disabilities and such, but the rune might actually see some use because of it! + mob_to_revive << "\"PASNAR SAVRAE YAM'TOTH. Arise.\"" + mob_to_revive.visible_message("[mob_to_revive] draws in a huge breath, red light shining from their eyes.", \ + "You awaken suddenly from the void. You're alive!") + rune_in_use = 0 + + +/obj/effect/rune/raise_dead/fail_invoke() + for(var/mob/living/M in orange(1,src)) + if(M.stat == DEAD) + M.visible_message("[M] twitches.") + + +//Rite of Obscurity: Turns all runes within a 3-tile radius invisible or reveals them again. +/obj/effect/rune/hide_runes + cultist_name = "Veil Runes" + cultist_desc = "Turns nearby runes invisible. They can be revealed by using this rune again." + invocation = "Kla'atu barada nikt'o!" + icon_state = "1" + color = rgb(0,0,255) + +/obj/effect/rune/hide_runes/invoke(mob/living/user) + visible_message("[src] darkens to black and vanishes.") + for(var/obj/effect/rune/R in orange(3,src)) + if(R.invisibility == INVISIBILITY_OBSERVER) + R.invisibility = 0 + R.alpha = initial(R.alpha) + else + R.visible_message("[R] fades away.") + R.invisibility = INVISIBILITY_OBSERVER + R.alpha = 100 //To help ghosts distinguish hidden runes + qdel(src) + + +//Rite of Disruption: Emits an EMP blast. +/obj/effect/rune/emp + cultist_name = "Electromagnetic Disruption" + cultist_desc = "Emits a large electromagnetic pulse, hindering electronics and disabling silicons." + invocation = "Ta'gh fara'qha fel d'amar det!" + icon_state = "5" + color = rgb(255, 0, 0) + +/obj/effect/rune/emp/invoke(mob/living/user) + visible_message("[src] glows blue for a moment before vanishing.") + for(var/mob/living/carbon/C in orange(1,src)) + C << "You feel a minute vibration pass through you!" + playsound(get_turf(src), 'sound/items/Welder2.ogg', 25, 1) + empulse(src, 4, 8) //A bit less than an EMP grenade + qdel(src) + + +//Rite of Astral Communion: Separates one's spirit from their body. They will take damage while it is active. +/obj/effect/rune/astral + cultist_name = "Astral Communion" + cultist_desc = "Severs the link between one's spirit and body. This effect is taxing and one's physical body will take damage while this is active." + invocation = "Fwe'sh mah erl nyag r'ya!" + icon_state = "6" + color = rgb(126, 23, 23) + rune_in_use = 0 //One at a time, please! + var/mob/living/affecting = null + +/obj/effect/rune/astral/examine(mob/user) + ..() + if(affecting) + user << "A translucent field encases [user] above the rune!" + +/obj/effect/rune/astral/invoke(mob/living/user) + if(rune_in_use) + user << "[src] cannot support more than one body!" + fail_invoke() + log_game("Astral Communion rune failed - more than one user") + return + var/turf/T = get_turf(src) + if(!user in T.contents) + user << "You must be standing on top of [src]!" + fail_invoke() + log_game("Astral Communion rune failed - user not standing on rune") + return + rune_in_use = 1 + affecting = user + user.color = "#7e1717" + user.visible_message("[user] freezes statue-still, glowing an unearthly red.", \ + "You see what lies beyond. All is revealed. While this is a wondrous experience, your physical form will waste away in this state. Hurry...") + user.ghostize(1) + while(user) + if(!affecting) + visible_message("[src] pulses gently before falling dark.") + affecting = null //In case it's assigned to a number or something + rune_in_use = 0 + return + affecting.apply_damage(1, BRUTE) + if(!(user in T.contents)) + user.visible_message("A spectral tendril wraps around [user] and pulls them back to the rune!") + Beam(user,icon_state="drainbeam",icon='icons/effects/effects.dmi',time=2) + user.forceMove(get_turf(src)) //NO ESCAPE :^) + if(user.key) + user.visible_message("[user] slowly relaxes, the glow around them dimming.", \ + "You are re-united with your physical form. [src] releases its hold over you.") + user.color = initial(user.color) + user.Weaken(3) + rune_in_use = 0 + affecting = null + return + if(user.stat == UNCONSCIOUS) + if(prob(10)) + var/mob/dead/observer/G = user.get_ghost() + if(G) + G << "You feel the link between you and your body weakening... you must hurry!" + if(user.stat == DEAD) + user.color = initial(user.color) + rune_in_use = 0 + affecting = null + var/mob/dead/observer/G = user.get_ghost() + if(G) + G << "You suddenly feel your physical form pass on. [src]'s exertion has killed you!" + return + sleep(10) + rune_in_use = 0 + + +//Rite of the Corporeal Shield: When invoked, becomes solid and cannot be passed. Invoke again to undo. +/obj/effect/rune/wall + cultist_name = "Form Shield" + cultist_desc = "When invoked, makes the rune block passage. Can be invoked again to reverse this." + invocation = "Khari'd! Eske'te tannin!" + icon_state = "1" + color = rgb(255, 0, 0) + +/obj/effect/rune/wall/examine(mob/user) + ..() + if(density) + user << "There is a barely perceptible shimmering of the air above [src]." + +/obj/effect/rune/wall/invoke(mob/living/user) + density = !density + user.visible_message("[user] places their hands on [src], and [density ? "the air above it begins to shimmer" : "the shimmer above it fades"].", \ + "You channel your life energy into [src], [density ? "preventing" : "allowing"] passage above it.") + if(iscarbon(user)) + var/mob/living/carbon/C = user + C.apply_damage(2, BRUTE, pick("l_arm", "r_arm")) + + +//Rite of the Shadowed Mind: Deafens, blinds and mutes all non-cultists nearby. +/obj/effect/rune/deafen + cultist_name = "Debilitate" + cultist_desc = "Causes all non-followers nearby to lose their hearing, sight and voice." + invocation = "Sti kaliedir!" + color = rgb(0, 255, 0) + icon_state = "4" + +/obj/effect/rune/deafen/invoke(mob/living/user) + visible_message("[src] emits a blinding red flash!") + for(var/mob/living/carbon/C in viewers(src)) + if(!iscultist(C) && !C.null_rod_check()) + C << "You feel oily shadows cover your senses." + C.adjustEarDamage(0,50) + C.flash_eyes(1, 1) + C.eye_blurry += 50 + C.eye_blind += 20 + C.silent += 10 + qdel(src) + +//Rite of Disorientation: Stuns and mutes all non-cultists nearby for a brief time +/obj/effect/rune/stun + cultist_name = "Stun" + cultist_desc = "Stuns and mutes all nearby non-followers for a brief time." + invocation = "Fuu ma'jin!" + icon_state = "2" + color = rgb(100, 0, 100) + +/obj/effect/rune/stun/invoke(mob/living/user) + visible_message("[src] explodes in a bright flash!") + for(var/mob/living/M in viewers(src)) + stun_he(M) + qdel(src) + +/obj/effect/rune/stun/Crossed(atom/movable/AM) + ..() + if(isliving(AM)) + stun_he(AM) + +/obj/effect/rune/stun/proc/stun_he(mob/living/M) + if(!iscultist(M) && !M.null_rod_check()) + M << "You are disoriented by [src]!" + M.Weaken(3) + M.Stun(3) + M.flash_eyes(1,1) + if(iscarbon(M)) + var/mob/living/carbon/C = M + C.silent += 3 + +//Rite of Joined Souls: Summons a single cultist. +/obj/effect/rune/summon + cultist_name = "Summon Cultist" + cultist_desc = "Summons a single cultist to the rune." + invocation = "N'ath reth sh'yro eth d'rekkathnor!" + icon_state = "5" + color = rgb(0, 255, 0) + +/obj/effect/rune/summon/invoke(mob/living/user) + var/list/cultists = list() + for(var/datum/mind/M in ticker.mode.cult) + cultists.Add(M.current) + var/mob/living/cultist_to_summon = input("Who do you wish to call to [src]?", "Followers of the Geometer") as null|anything in (cultists - user) + if(!cultist_to_summon) + user << "You require a summoning target!" + fail_invoke() + log_game("Summon Cultist rune failed - no target") + return + if(!iscultist(cultist_to_summon)) + user << "[cultist_to_summon] is not a follower of the Geometer!" + fail_invoke() + log_game("Summon Cultist rune failed - no target") + return + if(cultist_to_summon.buckled) + cultist_to_summon.buckled.unbuckle_mob() + cultist_to_summon.visible_message("[cultist_to_summon] suddenly disappears in a flash of red light!", \ + "Overwhelming vertigo consumes you as you are hurled through the air!") + visible_message("A foggy shape materializes atop [src] and solidifes into [cultist_to_summon]!") + user.apply_damage(10, BRUTE, "head") + cultist_to_summon.loc = get_turf(src) + qdel(src) + + +//Rite of Binding: Turns a nearby rune and a paper on top of the rune to a talisman, if both are valid. +/obj/effect/rune/imbue + cultist_name = "Bind Talisman" + cultist_desc = "Transforms papers and valid runes into talismans." + invocation = null //no talisman made, no invocation. + icon_state = "3" + color = rgb(0, 0, 255) + +/obj/effect/rune/imbue/invoke(mob/living/user) + var/turf/T = get_turf(src) + var/list/papers_on_rune = list() + for(var/obj/item/weapon/paper/P in T) + if(!P.info) + papers_on_rune.Add(P) + if(!papers_on_rune.len) + user << "There must be a blank paper on top of [src]!" + fail_invoke() + log_game("Talisman Imbue rune failed - no blank papers on rune") + return + var/obj/item/weapon/paper/paper_to_imbue = pick(papers_on_rune) + var/list/nearby_runes = list() + for(var/obj/effect/rune/R in orange(1,src)) + nearby_runes.Add(R) + if(!nearby_runes.len) + user << "There are no runes near [src]!" + fail_invoke() + log_game("Talisman Imbue rune failed - no nearby runes") + return + var/obj/effect/rune/picked_rune = pick(nearby_runes) + var/list/split_rune_type = text2list("[picked_rune.type]", "/") + var/imbue_type = split_rune_type[split_rune_type.len] + var/talisman_type = text2path("/obj/item/weapon/paper/talisman/[imbue_type]") + if(ispath(talisman_type)) + user.say("H'drak v'loso, mir'kanas verbot!") + var/obj/item/weapon/paper/talisman/TA = new talisman_type(get_turf(src)) + if(istype(picked_rune, /obj/effect/rune/teleport)) + var/obj/effect/rune/teleport/TR = picked_rune + var/obj/item/weapon/paper/talisman/teleport/TT = TA + TT.keyword = TR.keyword + else + user << "The chosen rune was not a valid target!" + fail_invoke() + log_game("Talisman Imbue rune failed - chosen rune invalid") + return + visible_message("[picked_rune] crumbles to dust, and bloody images form themselves on [paper_to_imbue].") + qdel(paper_to_imbue) + qdel(picked_rune) + + +//Rite of Fabrication: Creates a construct shell out of 5 metal sheets. +/obj/effect/rune/construct_shell + cultist_name = "Fabricate Shell" + cultist_desc = "Turns five plasteel sheets into an empty construct shell, suitable for containing a soul shard." + invocation = null //see below; doesn't say the invocation unless there's enough sheets. + icon_state = "5" + color = rgb(150, 150, 150) + +/obj/effect/rune/construct_shell/invoke(mob/living/user) + var/turf/T = get_turf(src) + for(var/obj/item/stack/sheet/S in T) + if(istype(S, /obj/item/stack/sheet/plasteel)) + var/obj/item/stack/sheet/plasteel/M = S + if(M.amount >= 5) + user.say("Ethra p'ni dedol!") + new /obj/structure/constructshell(T) + M.visible_message("[M] bends and twists into a humanoid shell!") + M.amount -= 5 + if(M.amount <= 0) + qdel(M) + qdel(src) + return + else + user << "There must be at least five sheets of plasteel on [src]!" + fail_invoke() + log_game("Construct Shell rune failed - not enough plasteel sheets") + return + + +//Rite of Arming: Creates cult robes, a trophy rack, and a cult sword on the rune. +/obj/effect/rune/armor + cultist_name = "Summon Armaments" + cultist_desc = "Equips the user with robes, shoes, a backpack, and a longsword. Items that cannot be equipped will not be summoned." + invocation = "N'ath reth sh'yro eth draggathnor!" + icon_state = "4" + color = rgb(255, 0, 0) + +/obj/effect/rune/armor/invoke(mob/living/user) + visible_message("With the sound of clanging metal, [src] crumbles to dust!") + user.equip_to_slot_or_del(new /obj/item/clothing/head/culthood/alt(user), slot_head) + user.equip_to_slot_or_del(new /obj/item/clothing/suit/cultrobes/alt(user), slot_wear_suit) + user.equip_to_slot_or_del(new /obj/item/clothing/shoes/cult/alt(user), slot_shoes) + user.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/cultpack(user), slot_back) + user.put_in_hands(new /obj/item/weapon/melee/cultblade(user)) + qdel(src) + + +//Rite of Leeching: Deals brute damage to the target and heals the same amount to the invoker. +/obj/effect/rune/leeching + cultist_name = "Drain Life" + cultist_desc = "Drains the life of the target on the rune, restoring it to the user." + invocation = null //see below; doesn't say the invocation if it has no targets. + icon_state = "2" + color = rgb(255, 0, 0) + +/obj/effect/rune/leeching/invoke(mob/living/user) + var/turf/T = get_turf(src) + var/list/potential_targets = list() + for(var/mob/living/carbon/M in T) + if(M.stat != DEAD && M != user) + potential_targets.Add(M) + if(!potential_targets.len) + user << "There must be a valid target on the rune!" + fail_invoke() + log_game("Leeching rune failed - no valid targets") + return + var/mob/living/carbon/target = pick(potential_targets) + var/drained_amount = rand(5,25) + user.say("Yu'gular faras desdae. Umathar uf'kal thenar!") + target.apply_damage(drained_amount, BRUTE, "chest") + user.adjustBruteLoss(-drained_amount) + user.Beam(target,icon_state="drainbeam",icon='icons/effects/effects.dmi',time=3) + target << "You feel extremely weak." + user.visible_message("Blood flows from the rune into [user]!", \ + "[target]'s blood flows into you, healing your wounds and revitalizing your spirit.") + + +//Rite of Boiling Blood: Deals extremely high amounts of damage to non-cultists nearby +/obj/effect/rune/blood_boil + cultist_name = "Boil Blood" + cultist_desc = "Boils the blood of non-believers who can see the rune, dealing extreme amounts of damage." + invocation = "Dedo ol'btoh!" + icon_state = "4" + color = rgb(255, 0, 0) + req_cultists = 2 + +/obj/effect/rune/blood_boil/invoke(mob/living/user) + visible_message("[src] briefly bubbles before exploding!") + for(var/mob/living/carbon/C in viewers(src)) + if(!iscultist(C)) + if(C.null_rod_check()) + C << "The null rod suddenly burns hotly before returning to normal!" + continue + C << "Your blood boils in your veins!" + C.take_overall_damage(51,51) + for(var/mob/living/carbon/M in range(1,src)) + if(iscultist(M)) + M.apply_damage(15, BRUTE, pick("l_arm", "r_arm")) + M << "[src] saps your strength!" + explosion(get_turf(src), -1, 0, 1, 5) + qdel(src) + + +//Rite of the Cleansing Flame: Sets all non-cultists on fire. +/obj/effect/rune/flame + cultist_name = "Immolate" + cultist_desc = "Sets any non-believers who can see the rune on fire." + invocation = "Dedo va'batoh!" + icon_state = "2" + color = rgb(255, 0, 0) + +/obj/effect/rune/flame/invoke(mob/living/user) + visible_message("[src] burns away, scorching the floor below!") + for(var/mob/living/carbon/C in viewers(src)) + if(!iscultist(C) && !C.null_rod_check()) + C << "You feel your skin crisp as you burst into flames!" + C.fire_act() + user.apply_damage(15, BURN, pick("l_arm", "r_arm")) + user << "[src] burns your arms!" + var/turf/simulated/T = get_turf(src) + T.burn_tile() + qdel(src) + + +//Rite of Spectral Manifestation: Summons a ghost on top of the rune as a cultist human with no items. User must stand on the rune at all times, and takes damage for each summoned ghost. +/obj/effect/rune/manifest + cultist_name = "Manifest Spirit" + cultist_desc = "Manifests a spirit as a servant of the Geometer. The invoker must not move from atop the rune, and will take damage for each summoned spirit." + invocation = "Gal'h'rfikk harfrandid mud'gib!" //how the fuck do you pronounce this + icon_state = "6" + color = rgb(255, 0, 0) + +/obj/effect/rune/manifest/invoke(mob/living/user) + if(!(user in get_turf(src))) + user << "You must be standing on [src]!" + fail_invoke() + log_game("Manifest rune failed - user not standing on rune") + return + var/list/ghosts_on_rune = list() + for(var/mob/dead/observer/O in get_turf(src)) + if(O.client) + ghosts_on_rune.Add(O) + if(!ghosts_on_rune.len) + user << "There are no spirits near [src]!" + fail_invoke() + log_game("Manifest rune failed - no nearby ghosts") + return + var/mob/dead/observer/ghost_to_spawn = pick(ghosts_on_rune) + var/mob/living/carbon/human/new_human = new(get_turf(src)) + new_human.real_name = ghost_to_spawn.real_name + new_human.alpha = 150 //Makes them translucent + visible_message("A cloud of red mist forms above [src], and from within steps... a man.") + user << "Your blood begins flowing into [src]. You must remain in place and conscious to maintain the forms of those summoned. This will hurt you slowly but surely..." + new_human.key = ghost_to_spawn.key + ticker.mode.add_cultist(new_human.mind) + new_human << "You are a servant of the Geometer. You have been made semi-corporeal by the cult of Nar-Sie, and you are to serve them at all costs." + + while(user in get_turf(src)) + if(user.stat) + break + user.apply_damage(1, BRUTE) + sleep(30) + + if(new_human) + new_human.visible_message("[new_human] suddenly dissolves into bones and ashes.", \ + "Your link to the world fades. Your form breaks apart.") + for(var/obj/I in new_human) + new_human.unEquip(I) + new_human.dust() + +//Rite of Dimensional Corruption: Stops time around the rune for all non-cultists. +/obj/effect/rune/timestop + cultist_name = "Time Stop" + cultist_desc = "Stops time around the rune for all non-cultists." + invocation = "T'ak ot'marzah oahr'du!" + icon_state = "2" + color = rgb(0, 0, 255) + req_cultists = 2 + +/obj/effect/rune/timestop/invoke(mob/living/user) + visible_message("[src] flares up for a moment, and then disappears into itself!") + new /obj/effect/timestop/cult(get_turf(src)) + for(var/mob/living/carbon/M in range(1,src)) + if(iscultist(M)) + M.apply_damage(20, BRUTE, "chest") + M << "[src] temporarily stops your heart from beating!" + qdel(src) + +/obj/effect/timestop/cult + name = "Dimensional Corruption" + desc = "Something not from this world has corrupted the spacetime continuum in this exact spot." + icon = 'icons/effects/96x96.dmi' + icon_state = "rune_large" + pixel_x = -32 + pixel_y = -32 + duration = 50 + alpha = 0 + color = rgb(255, 0, 0) + +/obj/effect/timestop/cult/timestop() + animate(src, alpha = 255, time = 5, loop = -1) + SpinAnimation(speed = 5) + for(var/mob/living/M in player_list) + if(iscultist(M) || M.null_rod_check()) + immune |= M + ..() diff --git a/code/game/gamemodes/cult/talisman.dm b/code/game/gamemodes/cult/talisman.dm index ed9b106e3ce..f42d4aca999 100644 --- a/code/game/gamemodes/cult/talisman.dm +++ b/code/game/gamemodes/cult/talisman.dm @@ -1,218 +1,218 @@ -/* - -Talismans are portable versions of runes that resemble blank sheets of paper. They may have different effects than their parent runes and are created by using a Rite of Binding with a paper on top and -a compatible rune somewhere nearby. A list of compatible runes can found below. - -Basic Runes: -Rite of Translocation -Rite of Knowledge -Rite of Obscurity -Rite of True Sight -Rite of False Truths -Rite of Disruption -Rite of Disorientation - -*/ - -/obj/item/weapon/paper/talisman - var/cultist_name = "talisman" - var/cultist_desc = "A basic talisman. It serves no purpose." - var/invocation = "Naise meam!" - var/uses = 1 - var/health_cost = 0 //The amount of health taken from the user when invoking the talisman - -/obj/item/weapon/paper/talisman/examine(mob/user) - if(iscultist(user) || user.stat == DEAD) - user << "Name: [cultist_name]" - user << "Effect: [cultist_desc]" - user << "Uses Remaining: [uses]" - else - user << "There are indecipherable images scrawled on the paper in what looks to be... blood?" - -/obj/item/weapon/paper/talisman/attack_self(mob/living/user) - if(!iscultist(user)) - user << "There are indecipherable images scrawled on the paper in what looks to be... blood?" - return - if(invocation) - user.whisper(invocation) - src.invoke(user) - uses-- - if(uses <= 0) - user.drop_item() - qdel(src) - -/obj/item/weapon/paper/talisman/proc/invoke(mob/living/user) - if(health_cost && iscarbon(user)) - var/mob/living/carbon/C = user - C.apply_damage(health_cost, BRUTE, pick("l_arm", "r_arm")) - -//Malformed Talisman: If something goes wrong. -/obj/item/weapon/paper/talisman/malformed - cultist_name = "malformed talisman" - cultist_desc = "A talisman with gibberish scrawlings. No good can come from invoking this." - invocation = "Ra'sha yoka!" - -/obj/item/weapon/paper/talisman/malformed/invoke(mob/living/user) - user << "You feel a pain in your head. The Geometer is displeased." - if(iscarbon(user)) - var/mob/living/carbon/C = user - C.apply_damage(10, BRUTE, "head") - - -//Supply Talisman: Has a few unique effects. Granted only to starter cultists. -/obj/item/weapon/paper/talisman/supply - cultist_name = "Supply Talisman" - cultist_desc = "A multi-use talisman that can create various objects. Intended to increase the cult's strength early on." - invocation = null - uses = 3 - -/obj/item/weapon/paper/talisman/supply/invoke(mob/living/user) - var/dat = "There are [uses] bloody runes on the parchment.
" - dat += "Please choose the chant to be imbued into the fabric of reality.
" - dat += "
" - dat += "Sas'so c'arta forbici! - Allows you to move to a Rite of Dislocation with the keyword of \"veri\".
" - dat += "Ta'gh fara'qha fel d'amar det! - Allows you to destroy technology in a short range.
" - dat += "Kla'atu barada nikt'o! - Allows you to conceal nearby runes, or reveal previously concealed runes.
" - dat += "Dedo va'batoh! - Allows you to set nearby non-believers on fire.
" - dat += "Barhah hra zar'garis! - A sacrifice rune appears under your feet, ready to be invoked in the name of Nar-sie.
" - dat += "Kal'om neth! - Summons a soul stone, used to capure the spirits of dead or dying humans.
" - dat += "Daa'ig osk! - Summons a construct shell for use with captured souls. It is too large to carry on your person.
" - var/datum/browser/popup = new(user, "talisman", "", 400, 400) - popup.set_content(dat) - popup.open() - uses++ //To prevent uses being consumed just by opening it - return 1 - -/obj/item/weapon/paper/talisman/supply/Topic(href, href_list) - if(!src || usr.stat || usr.restrained() || !in_range(src, usr)) - return - if(href_list["rune"]) - switch(href_list["rune"]) - if("teleport") - var/obj/item/weapon/paper/talisman/teleport/T = new(usr) - T.keyword = "veri" - usr.put_in_hands(T) - if("emp") - var/obj/item/weapon/paper/talisman/emp/T = new(usr) - usr.put_in_hands(T) - if("conceal") - var/obj/item/weapon/paper/talisman/hide_runes/T = new(usr) - usr.put_in_hands(T) - if("flame") - var/obj/item/weapon/paper/talisman/flame/T = new(usr) - usr.put_in_hands(T) - if("sacrune") - new /obj/effect/rune/sacrifice(get_turf(usr)) - if("soulstone") - var/obj/item/device/soulstone/T = new(usr) - usr.put_in_hands(T) - if("construct") - new /obj/structure/constructshell(get_turf(usr)) - src.uses-- - if(src.uses <= 0) - if(iscarbon(usr)) - var/mob/living/carbon/C = usr - C.drop_item() - visible_message("[src] crumbles to dust.") - qdel(src) - -//Rite of Translocation: Same as rune -/obj/item/weapon/paper/talisman/teleport - cultist_name = "Talisman of Teleportation" - cultist_desc = "A single-use talisman that will teleport a user to a random rune of the same keyword." - invocation = "Sas'so c'arta forbici!" - health_cost = 5 - var/keyword = "ire" - -/obj/item/weapon/paper/talisman/teleport/invoke(mob/living/user) - var/list/possible_runes = list() - for(var/obj/effect/rune/teleport/R in teleport_runes) - if(R.keyword == src.keyword) - possible_runes.Add(R) - if(!possible_runes.len) - user << "There are no Teleport runes with the same keyword!" - log_game("Teleportation talisman failed - no teleport runes of the same keyword") - uses++ //To prevent deletion - return - var/chosen_rune = pick(possible_runes) - user.visible_message("Dust flows from [user]'s hand, and they disappear in a flash of red light!", \ - "You speak the words of the talisman and find yourself somewhere else!") - if(user.buckled) - user.buckled.unbuckle_mob() - user.loc = get_turf(chosen_rune) - -/obj/item/weapon/paper/talisman/teleport/New() - ..() - spawn(1) //To give the keyword time to change from the imbue rune - info += keyword - -/obj/item/weapon/paper/talisman/teleport/examine(mob/user) - ..() - if(iscultist(user) && keyword) - user << "Keyword: [keyword]" - - -//Talisman of Obscurity: Same as rune -/obj/item/weapon/paper/talisman/hide_runes - cultist_name = "Talisman of Veiling" - cultist_desc = "A talisman that will make all runes within a small radius invisible, or make invisible runes visible again." - invocation = "Kla'atu barada nikt'o!" - health_cost = 1 - -/obj/item/weapon/paper/talisman/hide_runes/invoke(mob/living/user) - user.visible_message("Dust flows from [user]'s hand.", \ - "You speak the words of the talisman, veiling nearby runes.") - for(var/obj/effect/rune/R in orange(3,src)) - if(R.invisibility == INVISIBILITY_OBSERVER) - R.invisibility = 0 - R.alpha = initial(R.alpha) - else - R.visible_message("[R] fades away.") - R.invisibility = INVISIBILITY_OBSERVER - R.alpha = 100 //To help ghosts distinguish hidden runes - - -//Rite of Disruption: Same as rune -/obj/item/weapon/paper/talisman/emp - cultist_name = "Talisman of Electromagnetic Pulse" - cultist_desc = "A talisman that will cause a moderately-sized electromagnetic pulse." - invocation = "Ta'gh fara'qha fel d'amar det!" - health_cost = 5 - -/obj/item/weapon/paper/talisman/emp/invoke(mob/living/user) - user.visible_message("[user]'s hand flashes a bright blue!", \ - "You speak the words of the talisman, emitting an EMP blast.") - empulse(src, 4, 8) - - -//Rite of the Cleansing Flame: Same as rune -/obj/item/weapon/paper/talisman/flame - cultist_name = "Talisman of Immolation" - cultist_desc = "A talisman that sets any non-believers who can see you on fire." - invocation = "Dedo va'batoh!" - health_cost = 10 - -/obj/item/weapon/paper/talisman/flame/invoke(mob/living/user) - user.visible_message("\The [src] in [user]'s hand suddenly burns away in a red flash!", \ - "You speak the words of the talisman, setting your enemies on fire.") - for(var/mob/living/carbon/C in viewers(user)) - if(!iscultist(C) && !C.null_rod_check()) - C << "You feel your skin crisp as you burst into flames!" - C.fire_act() - - -//Rite of Arming: Equips cultist armor on the user, where available -/obj/item/weapon/paper/talisman/armor - cultist_name = "Talisman of Arming" - cultist_desc = "A talisman that will equip the invoker with cultist equipment if there is a slot to equip it to." - invocation = "N'ath reth sh'yro eth draggathnor!" - health_cost = 3 - -/obj/item/weapon/paper/talisman/armor/invoke(mob/living/user) - user.visible_message("Otherworldly armor suddenly appears on [user]!", \ - "You speak the words of the talisman, arming yourself!") - user.equip_to_slot_or_del(new /obj/item/clothing/head/culthood/alt(user), slot_head) - user.equip_to_slot_or_del(new /obj/item/clothing/suit/cultrobes/alt(user), slot_wear_suit) - user.equip_to_slot_or_del(new /obj/item/clothing/shoes/cult/alt(user), slot_shoes) - user.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/cultpack(user), slot_back) - user.put_in_hands(new /obj/item/weapon/melee/cultblade(user)) +/* + +Talismans are portable versions of runes that resemble blank sheets of paper. They may have different effects than their parent runes and are created by using a Rite of Binding with a paper on top and +a compatible rune somewhere nearby. A list of compatible runes can found below. + +Basic Runes: +Rite of Translocation +Rite of Knowledge +Rite of Obscurity +Rite of True Sight +Rite of False Truths +Rite of Disruption +Rite of Disorientation + +*/ + +/obj/item/weapon/paper/talisman + var/cultist_name = "talisman" + var/cultist_desc = "A basic talisman. It serves no purpose." + var/invocation = "Naise meam!" + var/uses = 1 + var/health_cost = 0 //The amount of health taken from the user when invoking the talisman + +/obj/item/weapon/paper/talisman/examine(mob/user) + if(iscultist(user) || user.stat == DEAD) + user << "Name: [cultist_name]" + user << "Effect: [cultist_desc]" + user << "Uses Remaining: [uses]" + else + user << "There are indecipherable images scrawled on the paper in what looks to be... blood?" + +/obj/item/weapon/paper/talisman/attack_self(mob/living/user) + if(!iscultist(user)) + user << "There are indecipherable images scrawled on the paper in what looks to be... blood?" + return + if(invocation) + user.whisper(invocation) + src.invoke(user) + uses-- + if(uses <= 0) + user.drop_item() + qdel(src) + +/obj/item/weapon/paper/talisman/proc/invoke(mob/living/user) + if(health_cost && iscarbon(user)) + var/mob/living/carbon/C = user + C.apply_damage(health_cost, BRUTE, pick("l_arm", "r_arm")) + +//Malformed Talisman: If something goes wrong. +/obj/item/weapon/paper/talisman/malformed + cultist_name = "malformed talisman" + cultist_desc = "A talisman with gibberish scrawlings. No good can come from invoking this." + invocation = "Ra'sha yoka!" + +/obj/item/weapon/paper/talisman/malformed/invoke(mob/living/user) + user << "You feel a pain in your head. The Geometer is displeased." + if(iscarbon(user)) + var/mob/living/carbon/C = user + C.apply_damage(10, BRUTE, "head") + + +//Supply Talisman: Has a few unique effects. Granted only to starter cultists. +/obj/item/weapon/paper/talisman/supply + cultist_name = "Supply Talisman" + cultist_desc = "A multi-use talisman that can create various objects. Intended to increase the cult's strength early on." + invocation = null + uses = 3 + +/obj/item/weapon/paper/talisman/supply/invoke(mob/living/user) + var/dat = "There are [uses] bloody runes on the parchment.
" + dat += "Please choose the chant to be imbued into the fabric of reality.
" + dat += "
" + dat += "Sas'so c'arta forbici! - Allows you to move to a Rite of Dislocation with the keyword of \"veri\".
" + dat += "Ta'gh fara'qha fel d'amar det! - Allows you to destroy technology in a short range.
" + dat += "Kla'atu barada nikt'o! - Allows you to conceal nearby runes, or reveal previously concealed runes.
" + dat += "Dedo va'batoh! - Allows you to set nearby non-believers on fire.
" + dat += "Barhah hra zar'garis! - A sacrifice rune appears under your feet, ready to be invoked in the name of Nar-sie.
" + dat += "Kal'om neth! - Summons a soul stone, used to capure the spirits of dead or dying humans.
" + dat += "Daa'ig osk! - Summons a construct shell for use with captured souls. It is too large to carry on your person.
" + var/datum/browser/popup = new(user, "talisman", "", 400, 400) + popup.set_content(dat) + popup.open() + uses++ //To prevent uses being consumed just by opening it + return 1 + +/obj/item/weapon/paper/talisman/supply/Topic(href, href_list) + if(!src || usr.stat || usr.restrained() || !in_range(src, usr)) + return + if(href_list["rune"]) + switch(href_list["rune"]) + if("teleport") + var/obj/item/weapon/paper/talisman/teleport/T = new(usr) + T.keyword = "veri" + usr.put_in_hands(T) + if("emp") + var/obj/item/weapon/paper/talisman/emp/T = new(usr) + usr.put_in_hands(T) + if("conceal") + var/obj/item/weapon/paper/talisman/hide_runes/T = new(usr) + usr.put_in_hands(T) + if("flame") + var/obj/item/weapon/paper/talisman/flame/T = new(usr) + usr.put_in_hands(T) + if("sacrune") + new /obj/effect/rune/sacrifice(get_turf(usr)) + if("soulstone") + var/obj/item/device/soulstone/T = new(usr) + usr.put_in_hands(T) + if("construct") + new /obj/structure/constructshell(get_turf(usr)) + src.uses-- + if(src.uses <= 0) + if(iscarbon(usr)) + var/mob/living/carbon/C = usr + C.drop_item() + visible_message("[src] crumbles to dust.") + qdel(src) + +//Rite of Translocation: Same as rune +/obj/item/weapon/paper/talisman/teleport + cultist_name = "Talisman of Teleportation" + cultist_desc = "A single-use talisman that will teleport a user to a random rune of the same keyword." + invocation = "Sas'so c'arta forbici!" + health_cost = 5 + var/keyword = "ire" + +/obj/item/weapon/paper/talisman/teleport/invoke(mob/living/user) + var/list/possible_runes = list() + for(var/obj/effect/rune/teleport/R in teleport_runes) + if(R.keyword == src.keyword) + possible_runes.Add(R) + if(!possible_runes.len) + user << "There are no Teleport runes with the same keyword!" + log_game("Teleportation talisman failed - no teleport runes of the same keyword") + uses++ //To prevent deletion + return + var/chosen_rune = pick(possible_runes) + user.visible_message("Dust flows from [user]'s hand, and they disappear in a flash of red light!", \ + "You speak the words of the talisman and find yourself somewhere else!") + if(user.buckled) + user.buckled.unbuckle_mob() + user.loc = get_turf(chosen_rune) + +/obj/item/weapon/paper/talisman/teleport/New() + ..() + spawn(1) //To give the keyword time to change from the imbue rune + info += keyword + +/obj/item/weapon/paper/talisman/teleport/examine(mob/user) + ..() + if(iscultist(user) && keyword) + user << "Keyword: [keyword]" + + +//Talisman of Obscurity: Same as rune +/obj/item/weapon/paper/talisman/hide_runes + cultist_name = "Talisman of Veiling" + cultist_desc = "A talisman that will make all runes within a small radius invisible, or make invisible runes visible again." + invocation = "Kla'atu barada nikt'o!" + health_cost = 1 + +/obj/item/weapon/paper/talisman/hide_runes/invoke(mob/living/user) + user.visible_message("Dust flows from [user]'s hand.", \ + "You speak the words of the talisman, veiling nearby runes.") + for(var/obj/effect/rune/R in orange(3,src)) + if(R.invisibility == INVISIBILITY_OBSERVER) + R.invisibility = 0 + R.alpha = initial(R.alpha) + else + R.visible_message("[R] fades away.") + R.invisibility = INVISIBILITY_OBSERVER + R.alpha = 100 //To help ghosts distinguish hidden runes + + +//Rite of Disruption: Same as rune +/obj/item/weapon/paper/talisman/emp + cultist_name = "Talisman of Electromagnetic Pulse" + cultist_desc = "A talisman that will cause a moderately-sized electromagnetic pulse." + invocation = "Ta'gh fara'qha fel d'amar det!" + health_cost = 5 + +/obj/item/weapon/paper/talisman/emp/invoke(mob/living/user) + user.visible_message("[user]'s hand flashes a bright blue!", \ + "You speak the words of the talisman, emitting an EMP blast.") + empulse(src, 4, 8) + + +//Rite of the Cleansing Flame: Same as rune +/obj/item/weapon/paper/talisman/flame + cultist_name = "Talisman of Immolation" + cultist_desc = "A talisman that sets any non-believers who can see you on fire." + invocation = "Dedo va'batoh!" + health_cost = 10 + +/obj/item/weapon/paper/talisman/flame/invoke(mob/living/user) + user.visible_message("\The [src] in [user]'s hand suddenly burns away in a red flash!", \ + "You speak the words of the talisman, setting your enemies on fire.") + for(var/mob/living/carbon/C in viewers(user)) + if(!iscultist(C) && !C.null_rod_check()) + C << "You feel your skin crisp as you burst into flames!" + C.fire_act() + + +//Rite of Arming: Equips cultist armor on the user, where available +/obj/item/weapon/paper/talisman/armor + cultist_name = "Talisman of Arming" + cultist_desc = "A talisman that will equip the invoker with cultist equipment if there is a slot to equip it to." + invocation = "N'ath reth sh'yro eth draggathnor!" + health_cost = 3 + +/obj/item/weapon/paper/talisman/armor/invoke(mob/living/user) + user.visible_message("Otherworldly armor suddenly appears on [user]!", \ + "You speak the words of the talisman, arming yourself!") + user.equip_to_slot_or_del(new /obj/item/clothing/head/culthood/alt(user), slot_head) + user.equip_to_slot_or_del(new /obj/item/clothing/suit/cultrobes/alt(user), slot_wear_suit) + user.equip_to_slot_or_del(new /obj/item/clothing/shoes/cult/alt(user), slot_shoes) + user.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/cultpack(user), slot_back) + user.put_in_hands(new /obj/item/weapon/melee/cultblade(user)) diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm index f696f271a83..1b49780d386 100644 --- a/code/game/gamemodes/nuclear/nuclear.dm +++ b/code/game/gamemodes/nuclear/nuclear.dm @@ -1,351 +1,351 @@ -/datum/game_mode - var/list/datum/mind/syndicates = list() - - -/datum/game_mode/nuclear - name = "nuclear emergency" - config_tag = "nuclear" - required_players = 20 // 20 players - 5 players to be the nuke ops = 15 players remaining - required_enemies = 5 - recommended_enemies = 5 - antag_flag = ROLE_OPERATIVE - enemy_minimum_age = 14 - var/const/agents_possible = 5 //If we ever need more syndicate agents. - - var/nukes_left = 1 // Call 3714-PRAY right now and order more nukes! Limited offer! - var/nuke_off_station = 0 //Used for tracking if the syndies actually haul the nuke to the station - var/syndies_didnt_escape = 0 //Used for tracking if the syndies got the shuttle off of the z-level - -/datum/game_mode/nuclear/announce() - world << "The current game mode is - Nuclear Emergency!" - world << "A [syndicate_name()] Strike Force is approaching [station_name()]!" - world << "A nuclear explosive was being transported by Nanotrasen to a military base. The transport ship mysteriously lost contact with Space Traffic Control (STC). About that time a strange disk was discovered around [station_name()]. It was identified by Nanotrasen as a nuclear auth. disk and now Syndicate Operatives have arrived to retake the disk and detonate SS13! Also, most likely Syndicate star ships are in the vicinity so take care not to lose the disk!\nSyndicate: Reclaim the disk and detonate the nuclear bomb anywhere on SS13.\nPersonnel: Hold the disk and escape with the disk on the shuttle!" - -/datum/game_mode/nuclear/pre_setup() - var/agent_number = 0 - if(antag_candidates.len > agents_possible) - agent_number = agents_possible - else - agent_number = antag_candidates.len - - var/n_players = num_players() - if(agent_number > n_players) - agent_number = n_players/2 - - while(agent_number > 0) - var/datum/mind/new_syndicate = pick(antag_candidates) - syndicates += new_syndicate - antag_candidates -= new_syndicate //So it doesn't pick the same guy each time. - agent_number-- - - for(var/datum/mind/synd_mind in syndicates) - synd_mind.assigned_role = "Syndicate" - synd_mind.special_role = "Syndicate"//So they actually have a special role/N - log_game("[synd_mind.key] (ckey) has been selected as a nuclear operative") - if(ishuman(synd_mind.current))//don't want operatives burning to death instantly. - var/mob/living/carbon/human/human = synd_mind.current - if(human.dna && human.dna.species.dangerous_existence) - human.set_species(/datum/species/human) - - return 1 - - -//////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////// -/datum/game_mode/proc/update_synd_icons_added(datum/mind/synd_mind) - var/datum/atom_hud/antag/opshud = huds[ANTAG_HUD_OPS] - opshud.join_hud(synd_mind.current) - set_antag_hud(synd_mind.current, "synd") - -/datum/game_mode/proc/update_synd_icons_removed(datum/mind/synd_mind) - var/datum/atom_hud/antag/opshud = huds[ANTAG_HUD_OPS] - opshud.leave_hud(synd_mind.current) - set_antag_hud(synd_mind.current, null) - -//////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////// - -/datum/game_mode/nuclear/post_setup() - - var/list/turf/synd_spawn = list() - - for(var/obj/effect/landmark/A in landmarks_list) - if(A.name == "Syndicate-Spawn") - synd_spawn += get_turf(A) - continue - - var/nuke_code = "[rand(10000, 99999)]" - var/leader_selected = 0 - var/agent_number = 1 - var/spawnpos = 1 - - for(var/datum/mind/synd_mind in syndicates) - if(spawnpos > synd_spawn.len) - spawnpos = 2 - synd_mind.current.loc = synd_spawn[spawnpos] - - forge_syndicate_objectives(synd_mind) - greet_syndicate(synd_mind) - equip_syndicate(synd_mind.current) - - if (nuke_code) - synd_mind.store_memory("Syndicate Nuclear Bomb Code: [nuke_code]", 0, 0) - synd_mind.current << "The nuclear authorization code is: [nuke_code]" - - if(!leader_selected) - prepare_syndicate_leader(synd_mind, nuke_code) - leader_selected = 1 - else - synd_mind.current.real_name = "[syndicate_name()] Operative #[agent_number]" - agent_number++ - spawnpos++ - update_synd_icons_added(synd_mind) - var/obj/machinery/nuclearbomb/nuke = locate("syndienuke") in nuke_list - if(nuke) - nuke.r_code = nuke_code - return ..() - - -/datum/game_mode/proc/prepare_syndicate_leader(datum/mind/synd_mind, nuke_code) - var/leader_title = pick("Czar", "Boss", "Commander", "Chief", "Kingpin", "Director", "Overlord") - spawn(1) - NukeNameAssign(nukelastname(synd_mind.current),syndicates) //allows time for the rest of the syndies to be chosen - synd_mind.current.real_name = "[syndicate_name()] [leader_title]" - synd_mind.current << "You are the Syndicate [leader_title] for this mission. You are responsible for the distribution of telecrystals and your ID is the only one who can open the launch bay doors." - synd_mind.current << "If you feel you are not up to this task, give your ID to another operative." - synd_mind.current << "In your hand you will find a special item capable of triggering a greater challenge for your team. Examine it carefully and consult with your fellow operatives before activating it." - - var/obj/item/device/nuclear_challenge/challenge = new /obj/item/device/nuclear_challenge - synd_mind.current.equip_to_slot_or_del(challenge, slot_r_hand) - - var/list/foundIDs = synd_mind.current.search_contents_for(/obj/item/weapon/card/id) - if(foundIDs.len) - for(var/obj/item/weapon/card/id/ID in foundIDs) - ID.name = "lead agent card" - ID.access += access_syndicate_leader - else - message_admins("Warning: Nuke Ops spawned without access to leave their spawn area!") - - if (nuke_code) - var/obj/item/weapon/paper/P = new - P.info = "The nuclear authorization code is: [nuke_code]" - P.name = "nuclear bomb code" - var/mob/living/carbon/human/H = synd_mind.current - P.loc = H.loc - H.equip_to_slot_or_del(P, slot_r_hand, 0) - H.update_icons() - else - nuke_code = "code will be provided later" - return - - - -/datum/game_mode/proc/forge_syndicate_objectives(datum/mind/syndicate) - var/datum/objective/nuclear/syndobj = new - syndobj.owner = syndicate - syndicate.objectives += syndobj - - -/datum/game_mode/proc/greet_syndicate(datum/mind/syndicate, you_are=1) - if (you_are) - syndicate.current << "You are a [syndicate_name()] agent!" - var/obj_count = 1 - for(var/datum/objective/objective in syndicate.objectives) - syndicate.current << "Objective #[obj_count]: [objective.explanation_text]" - obj_count++ - return - - -/datum/game_mode/proc/random_radio_frequency() - return 1337 // WHY??? -- Doohl - - -/datum/game_mode/proc/equip_syndicate(mob/living/carbon/human/synd_mob) - synd_mob.equipOutfit(/datum/outfit/syndicate) - return 1 - - -/datum/game_mode/nuclear/check_win() - if (nukes_left == 0) - return 1 - return ..() - - -/datum/game_mode/proc/are_operatives_dead() - for(var/datum/mind/operative_mind in syndicates) - if (istype(operative_mind.current,/mob/living/carbon/human) && (operative_mind.current.stat!=2)) - return 0 - return 1 - -/datum/game_mode/nuclear/check_finished() //to be called by ticker - if(replacementmode && round_converted == 2) - return replacementmode.check_finished() - if(SSshuttle.emergency.mode >= SHUTTLE_ENDGAME || station_was_nuked) - return 1 - if(are_operatives_dead()) - if(bomb_set) //snaaaaaaaaaake! It's not over yet! - return 0 - ..() - -/datum/game_mode/nuclear/declare_completion() - var/disk_rescued = 1 - for(var/obj/item/weapon/disk/nuclear/D in poi_list) - if(!D.onCentcom()) - disk_rescued = 0 - break - var/crew_evacuated = (SSshuttle.emergency.mode >= SHUTTLE_ENDGAME) - //var/operatives_are_dead = is_operatives_are_dead() - - - //nukes_left - //station_was_nuked - //derp //Used for tracking if the syndies actually haul the nuke to the station //no - //herp //Used for tracking if the syndies got the shuttle off of the z-level //NO, DON'T FUCKING NAME VARS LIKE THIS - - if (!disk_rescued && station_was_nuked && !syndies_didnt_escape) - feedback_set_details("round_end_result","win - syndicate nuke") - world << "Syndicate Major Victory!" - world << "[syndicate_name()] operatives have destroyed [station_name()]!" - - else if (!disk_rescued && station_was_nuked && syndies_didnt_escape) - feedback_set_details("round_end_result","halfwin - syndicate nuke - did not evacuate in time") - world << "Total Annihilation" - world << "[syndicate_name()] operatives destroyed [station_name()] but did not leave the area in time and got caught in the explosion. Next time, don't lose the disk!" - - else if (!disk_rescued && !station_was_nuked && nuke_off_station && !syndies_didnt_escape) - feedback_set_details("round_end_result","halfwin - blew wrong station") - world << "Crew Minor Victory" - world << "[syndicate_name()] operatives secured the authentication disk but blew up something that wasn't [station_name()]. Next time, don't lose the disk!" - - else if (!disk_rescued && !station_was_nuked && nuke_off_station && syndies_didnt_escape) - feedback_set_details("round_end_result","halfwin - blew wrong station - did not evacuate in time") - world << "[syndicate_name()] operatives have earned Darwin Award!" - world << "[syndicate_name()] operatives blew up something that wasn't [station_name()] and got caught in the explosion. Next time, don't lose the disk!" - - else if ((disk_rescued || SSshuttle.emergency.mode < SHUTTLE_ENDGAME) && are_operatives_dead()) - feedback_set_details("round_end_result","loss - evacuation - disk secured - syndi team dead") - world << "Crew Major Victory!" - world << "The Research Staff has saved the disc and killed the [syndicate_name()] Operatives" - - else if ( disk_rescued ) - feedback_set_details("round_end_result","loss - evacuation - disk secured") - world << "Crew Major Victory" - world << "The Research Staff has saved the disc and stopped the [syndicate_name()] Operatives!" - - else if (!disk_rescued && are_operatives_dead()) - feedback_set_details("round_end_result","loss - evacuation - disk not secured") - world << "Syndicate Minor Victory!" - world << "The Research Staff failed to secure the authentication disk but did manage to kill most of the [syndicate_name()] Operatives!" - - else if (!disk_rescued && crew_evacuated) - feedback_set_details("round_end_result","halfwin - detonation averted") - world << "Syndicate Minor Victory!" - world << "[syndicate_name()] operatives recovered the abandoned authentication disk but detonation of [station_name()] was averted. Next time, don't lose the disk!" - - else if (!disk_rescued && !crew_evacuated) - feedback_set_details("round_end_result","halfwin - interrupted") - world << "Neutral Victory" - world << "Round was mysteriously interrupted!" - - ..() - return - - -/datum/game_mode/proc/auto_declare_completion_nuclear() - if( syndicates.len || (ticker && istype(ticker.mode,/datum/game_mode/nuclear)) ) - var/text = "
The syndicate operatives were:" - var/purchases = "" - var/TC_uses = 0 - for(var/datum/mind/syndicate in syndicates) - text += printplayer(syndicate) - for(var/obj/item/device/uplink/H in world_uplinks) - if(H && H.uplink_owner && H.uplink_owner==syndicate.key) - TC_uses += H.used_TC - purchases += H.purchase_log - text += "
" - text += "(Syndicates used [TC_uses] TC) [purchases]" - if(TC_uses==0 && station_was_nuked && !are_operatives_dead()) - text += "" - world << text - return 1 - - -/proc/nukelastname(mob/M) //--All praise goes to NEO|Phyte, all blame goes to DH, and it was Cindi-Kate's idea. Also praise Urist for copypasta ho. - var/randomname = pick(last_names) - var/newname = copytext(sanitize(input(M,"You are the nuke operative [pick("Czar", "Boss", "Commander", "Chief", "Kingpin", "Director", "Overlord")]. Please choose a last name for your family.", "Name change",randomname)),1,MAX_NAME_LEN) - - if (!newname) - newname = randomname - - else - if (newname == "Unknown" || newname == "floor" || newname == "wall" || newname == "rwall" || newname == "_") - M << "That name is reserved." - return nukelastname(M) - - return capitalize(newname) - -/proc/NukeNameAssign(lastname,list/syndicates) - for(var/datum/mind/synd_mind in syndicates) - var/mob/living/carbon/human/H = synd_mind.current - synd_mind.name = H.dna.species.random_name(H.gender,0,lastname) - synd_mind.current.real_name = synd_mind.name - return - -/datum/outfit/syndicate - name = "Syndicate Operative - Basic" - - uniform = /obj/item/clothing/under/syndicate - shoes = /obj/item/clothing/shoes/combat - gloves = /obj/item/clothing/gloves/combat - back = /obj/item/weapon/storage/backpack - ears = /obj/item/device/radio/headset/syndicate/alt - id = /obj/item/weapon/card/id/syndicate - belt = /obj/item/weapon/gun/projectile/automatic/pistol - backpack_contents = list(/obj/item/weapon/storage/box/engineer=1) - - var/tc = 20 - -/datum/outfit/syndicate/post_equip(mob/living/carbon/human/H) - var/obj/item/device/radio/R = H.ears - R.set_frequency(SYND_FREQ) - R.freqlock = 1 - - var/obj/item/device/radio/uplink/U = new /obj/item/device/radio/uplink(H) - U.hidden_uplink.uplink_owner="[H.key]" - U.hidden_uplink.uses = tc - U.hidden_uplink.mode_override = /datum/game_mode/nuclear //Goodies - H.equip_to_slot_or_del(U, slot_in_backpack) - - var/obj/item/weapon/implant/weapons_auth/W = new/obj/item/weapon/implant/weapons_auth(H) - W.implant(H) - var/obj/item/weapon/implant/explosive/E = new/obj/item/weapon/implant/explosive(H) - E.implant(H) - H.faction |= "syndicate" - H.update_icons() - -/datum/outfit/syndicate/full - name = "Syndicate Operative - Full Kit" - - glasses = /obj/item/clothing/glasses/night - mask = /obj/item/clothing/mask/gas/syndicate - suit = /obj/item/clothing/suit/space/hardsuit/syndi - l_pocket = /obj/item/weapon/tank/internals/emergency_oxygen/engi - r_pocket = /obj/item/weapon/gun/projectile/automatic/pistol - belt = /obj/item/weapon/storage/belt/military - r_hand = /obj/item/weapon/gun/projectile/automatic/shotgun/bulldog - backpack_contents = list(/obj/item/weapon/storage/box/engineer=1,\ - /obj/item/weapon/tank/jetpack/oxygen/harness=1,\ - /obj/item/weapon/pinpointer/nukeop=1) - - tc = 30 - -/datum/outfit/syndicate/full/post_equip(mob/living/carbon/human/H) - ..() - - - var/obj/item/clothing/suit/space/hardsuit/syndi/suit = H.wear_suit - suit.ToggleHelmet() - var/obj/item/clothing/head/helmet/space/hardsuit/syndi/helmet = H.head - helmet.attack_self(H) - +/datum/game_mode + var/list/datum/mind/syndicates = list() + + +/datum/game_mode/nuclear + name = "nuclear emergency" + config_tag = "nuclear" + required_players = 20 // 20 players - 5 players to be the nuke ops = 15 players remaining + required_enemies = 5 + recommended_enemies = 5 + antag_flag = ROLE_OPERATIVE + enemy_minimum_age = 14 + var/const/agents_possible = 5 //If we ever need more syndicate agents. + + var/nukes_left = 1 // Call 3714-PRAY right now and order more nukes! Limited offer! + var/nuke_off_station = 0 //Used for tracking if the syndies actually haul the nuke to the station + var/syndies_didnt_escape = 0 //Used for tracking if the syndies got the shuttle off of the z-level + +/datum/game_mode/nuclear/announce() + world << "The current game mode is - Nuclear Emergency!" + world << "A [syndicate_name()] Strike Force is approaching [station_name()]!" + world << "A nuclear explosive was being transported by Nanotrasen to a military base. The transport ship mysteriously lost contact with Space Traffic Control (STC). About that time a strange disk was discovered around [station_name()]. It was identified by Nanotrasen as a nuclear auth. disk and now Syndicate Operatives have arrived to retake the disk and detonate SS13! Also, most likely Syndicate star ships are in the vicinity so take care not to lose the disk!\nSyndicate: Reclaim the disk and detonate the nuclear bomb anywhere on SS13.\nPersonnel: Hold the disk and escape with the disk on the shuttle!" + +/datum/game_mode/nuclear/pre_setup() + var/agent_number = 0 + if(antag_candidates.len > agents_possible) + agent_number = agents_possible + else + agent_number = antag_candidates.len + + var/n_players = num_players() + if(agent_number > n_players) + agent_number = n_players/2 + + while(agent_number > 0) + var/datum/mind/new_syndicate = pick(antag_candidates) + syndicates += new_syndicate + antag_candidates -= new_syndicate //So it doesn't pick the same guy each time. + agent_number-- + + for(var/datum/mind/synd_mind in syndicates) + synd_mind.assigned_role = "Syndicate" + synd_mind.special_role = "Syndicate"//So they actually have a special role/N + log_game("[synd_mind.key] (ckey) has been selected as a nuclear operative") + if(ishuman(synd_mind.current))//don't want operatives burning to death instantly. + var/mob/living/carbon/human/human = synd_mind.current + if(human.dna && human.dna.species.dangerous_existence) + human.set_species(/datum/species/human) + + return 1 + + +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// +/datum/game_mode/proc/update_synd_icons_added(datum/mind/synd_mind) + var/datum/atom_hud/antag/opshud = huds[ANTAG_HUD_OPS] + opshud.join_hud(synd_mind.current) + set_antag_hud(synd_mind.current, "synd") + +/datum/game_mode/proc/update_synd_icons_removed(datum/mind/synd_mind) + var/datum/atom_hud/antag/opshud = huds[ANTAG_HUD_OPS] + opshud.leave_hud(synd_mind.current) + set_antag_hud(synd_mind.current, null) + +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// + +/datum/game_mode/nuclear/post_setup() + + var/list/turf/synd_spawn = list() + + for(var/obj/effect/landmark/A in landmarks_list) + if(A.name == "Syndicate-Spawn") + synd_spawn += get_turf(A) + continue + + var/nuke_code = "[rand(10000, 99999)]" + var/leader_selected = 0 + var/agent_number = 1 + var/spawnpos = 1 + + for(var/datum/mind/synd_mind in syndicates) + if(spawnpos > synd_spawn.len) + spawnpos = 2 + synd_mind.current.loc = synd_spawn[spawnpos] + + forge_syndicate_objectives(synd_mind) + greet_syndicate(synd_mind) + equip_syndicate(synd_mind.current) + + if (nuke_code) + synd_mind.store_memory("Syndicate Nuclear Bomb Code: [nuke_code]", 0, 0) + synd_mind.current << "The nuclear authorization code is: [nuke_code]" + + if(!leader_selected) + prepare_syndicate_leader(synd_mind, nuke_code) + leader_selected = 1 + else + synd_mind.current.real_name = "[syndicate_name()] Operative #[agent_number]" + agent_number++ + spawnpos++ + update_synd_icons_added(synd_mind) + var/obj/machinery/nuclearbomb/nuke = locate("syndienuke") in nuke_list + if(nuke) + nuke.r_code = nuke_code + return ..() + + +/datum/game_mode/proc/prepare_syndicate_leader(datum/mind/synd_mind, nuke_code) + var/leader_title = pick("Czar", "Boss", "Commander", "Chief", "Kingpin", "Director", "Overlord") + spawn(1) + NukeNameAssign(nukelastname(synd_mind.current),syndicates) //allows time for the rest of the syndies to be chosen + synd_mind.current.real_name = "[syndicate_name()] [leader_title]" + synd_mind.current << "You are the Syndicate [leader_title] for this mission. You are responsible for the distribution of telecrystals and your ID is the only one who can open the launch bay doors." + synd_mind.current << "If you feel you are not up to this task, give your ID to another operative." + synd_mind.current << "In your hand you will find a special item capable of triggering a greater challenge for your team. Examine it carefully and consult with your fellow operatives before activating it." + + var/obj/item/device/nuclear_challenge/challenge = new /obj/item/device/nuclear_challenge + synd_mind.current.equip_to_slot_or_del(challenge, slot_r_hand) + + var/list/foundIDs = synd_mind.current.search_contents_for(/obj/item/weapon/card/id) + if(foundIDs.len) + for(var/obj/item/weapon/card/id/ID in foundIDs) + ID.name = "lead agent card" + ID.access += access_syndicate_leader + else + message_admins("Warning: Nuke Ops spawned without access to leave their spawn area!") + + if (nuke_code) + var/obj/item/weapon/paper/P = new + P.info = "The nuclear authorization code is: [nuke_code]" + P.name = "nuclear bomb code" + var/mob/living/carbon/human/H = synd_mind.current + P.loc = H.loc + H.equip_to_slot_or_del(P, slot_r_hand, 0) + H.update_icons() + else + nuke_code = "code will be provided later" + return + + + +/datum/game_mode/proc/forge_syndicate_objectives(datum/mind/syndicate) + var/datum/objective/nuclear/syndobj = new + syndobj.owner = syndicate + syndicate.objectives += syndobj + + +/datum/game_mode/proc/greet_syndicate(datum/mind/syndicate, you_are=1) + if (you_are) + syndicate.current << "You are a [syndicate_name()] agent!" + var/obj_count = 1 + for(var/datum/objective/objective in syndicate.objectives) + syndicate.current << "Objective #[obj_count]: [objective.explanation_text]" + obj_count++ + return + + +/datum/game_mode/proc/random_radio_frequency() + return 1337 // WHY??? -- Doohl + + +/datum/game_mode/proc/equip_syndicate(mob/living/carbon/human/synd_mob) + synd_mob.equipOutfit(/datum/outfit/syndicate) + return 1 + + +/datum/game_mode/nuclear/check_win() + if (nukes_left == 0) + return 1 + return ..() + + +/datum/game_mode/proc/are_operatives_dead() + for(var/datum/mind/operative_mind in syndicates) + if (istype(operative_mind.current,/mob/living/carbon/human) && (operative_mind.current.stat!=2)) + return 0 + return 1 + +/datum/game_mode/nuclear/check_finished() //to be called by ticker + if(replacementmode && round_converted == 2) + return replacementmode.check_finished() + if(SSshuttle.emergency.mode >= SHUTTLE_ENDGAME || station_was_nuked) + return 1 + if(are_operatives_dead()) + if(bomb_set) //snaaaaaaaaaake! It's not over yet! + return 0 + ..() + +/datum/game_mode/nuclear/declare_completion() + var/disk_rescued = 1 + for(var/obj/item/weapon/disk/nuclear/D in poi_list) + if(!D.onCentcom()) + disk_rescued = 0 + break + var/crew_evacuated = (SSshuttle.emergency.mode >= SHUTTLE_ENDGAME) + //var/operatives_are_dead = is_operatives_are_dead() + + + //nukes_left + //station_was_nuked + //derp //Used for tracking if the syndies actually haul the nuke to the station //no + //herp //Used for tracking if the syndies got the shuttle off of the z-level //NO, DON'T FUCKING NAME VARS LIKE THIS + + if (!disk_rescued && station_was_nuked && !syndies_didnt_escape) + feedback_set_details("round_end_result","win - syndicate nuke") + world << "Syndicate Major Victory!" + world << "[syndicate_name()] operatives have destroyed [station_name()]!" + + else if (!disk_rescued && station_was_nuked && syndies_didnt_escape) + feedback_set_details("round_end_result","halfwin - syndicate nuke - did not evacuate in time") + world << "Total Annihilation" + world << "[syndicate_name()] operatives destroyed [station_name()] but did not leave the area in time and got caught in the explosion. Next time, don't lose the disk!" + + else if (!disk_rescued && !station_was_nuked && nuke_off_station && !syndies_didnt_escape) + feedback_set_details("round_end_result","halfwin - blew wrong station") + world << "Crew Minor Victory" + world << "[syndicate_name()] operatives secured the authentication disk but blew up something that wasn't [station_name()]. Next time, don't lose the disk!" + + else if (!disk_rescued && !station_was_nuked && nuke_off_station && syndies_didnt_escape) + feedback_set_details("round_end_result","halfwin - blew wrong station - did not evacuate in time") + world << "[syndicate_name()] operatives have earned Darwin Award!" + world << "[syndicate_name()] operatives blew up something that wasn't [station_name()] and got caught in the explosion. Next time, don't lose the disk!" + + else if ((disk_rescued || SSshuttle.emergency.mode < SHUTTLE_ENDGAME) && are_operatives_dead()) + feedback_set_details("round_end_result","loss - evacuation - disk secured - syndi team dead") + world << "Crew Major Victory!" + world << "The Research Staff has saved the disc and killed the [syndicate_name()] Operatives" + + else if ( disk_rescued ) + feedback_set_details("round_end_result","loss - evacuation - disk secured") + world << "Crew Major Victory" + world << "The Research Staff has saved the disc and stopped the [syndicate_name()] Operatives!" + + else if (!disk_rescued && are_operatives_dead()) + feedback_set_details("round_end_result","loss - evacuation - disk not secured") + world << "Syndicate Minor Victory!" + world << "The Research Staff failed to secure the authentication disk but did manage to kill most of the [syndicate_name()] Operatives!" + + else if (!disk_rescued && crew_evacuated) + feedback_set_details("round_end_result","halfwin - detonation averted") + world << "Syndicate Minor Victory!" + world << "[syndicate_name()] operatives recovered the abandoned authentication disk but detonation of [station_name()] was averted. Next time, don't lose the disk!" + + else if (!disk_rescued && !crew_evacuated) + feedback_set_details("round_end_result","halfwin - interrupted") + world << "Neutral Victory" + world << "Round was mysteriously interrupted!" + + ..() + return + + +/datum/game_mode/proc/auto_declare_completion_nuclear() + if( syndicates.len || (ticker && istype(ticker.mode,/datum/game_mode/nuclear)) ) + var/text = "
The syndicate operatives were:" + var/purchases = "" + var/TC_uses = 0 + for(var/datum/mind/syndicate in syndicates) + text += printplayer(syndicate) + for(var/obj/item/device/uplink/H in world_uplinks) + if(H && H.uplink_owner && H.uplink_owner==syndicate.key) + TC_uses += H.used_TC + purchases += H.purchase_log + text += "
" + text += "(Syndicates used [TC_uses] TC) [purchases]" + if(TC_uses==0 && station_was_nuked && !are_operatives_dead()) + text += "" + world << text + return 1 + + +/proc/nukelastname(mob/M) //--All praise goes to NEO|Phyte, all blame goes to DH, and it was Cindi-Kate's idea. Also praise Urist for copypasta ho. + var/randomname = pick(last_names) + var/newname = copytext(sanitize(input(M,"You are the nuke operative [pick("Czar", "Boss", "Commander", "Chief", "Kingpin", "Director", "Overlord")]. Please choose a last name for your family.", "Name change",randomname)),1,MAX_NAME_LEN) + + if (!newname) + newname = randomname + + else + if (newname == "Unknown" || newname == "floor" || newname == "wall" || newname == "rwall" || newname == "_") + M << "That name is reserved." + return nukelastname(M) + + return capitalize(newname) + +/proc/NukeNameAssign(lastname,list/syndicates) + for(var/datum/mind/synd_mind in syndicates) + var/mob/living/carbon/human/H = synd_mind.current + synd_mind.name = H.dna.species.random_name(H.gender,0,lastname) + synd_mind.current.real_name = synd_mind.name + return + +/datum/outfit/syndicate + name = "Syndicate Operative - Basic" + + uniform = /obj/item/clothing/under/syndicate + shoes = /obj/item/clothing/shoes/combat + gloves = /obj/item/clothing/gloves/combat + back = /obj/item/weapon/storage/backpack + ears = /obj/item/device/radio/headset/syndicate/alt + id = /obj/item/weapon/card/id/syndicate + belt = /obj/item/weapon/gun/projectile/automatic/pistol + backpack_contents = list(/obj/item/weapon/storage/box/engineer=1) + + var/tc = 20 + +/datum/outfit/syndicate/post_equip(mob/living/carbon/human/H) + var/obj/item/device/radio/R = H.ears + R.set_frequency(SYND_FREQ) + R.freqlock = 1 + + var/obj/item/device/radio/uplink/U = new /obj/item/device/radio/uplink(H) + U.hidden_uplink.uplink_owner="[H.key]" + U.hidden_uplink.uses = tc + U.hidden_uplink.mode_override = /datum/game_mode/nuclear //Goodies + H.equip_to_slot_or_del(U, slot_in_backpack) + + var/obj/item/weapon/implant/weapons_auth/W = new/obj/item/weapon/implant/weapons_auth(H) + W.implant(H) + var/obj/item/weapon/implant/explosive/E = new/obj/item/weapon/implant/explosive(H) + E.implant(H) + H.faction |= "syndicate" + H.update_icons() + +/datum/outfit/syndicate/full + name = "Syndicate Operative - Full Kit" + + glasses = /obj/item/clothing/glasses/night + mask = /obj/item/clothing/mask/gas/syndicate + suit = /obj/item/clothing/suit/space/hardsuit/syndi + l_pocket = /obj/item/weapon/tank/internals/emergency_oxygen/engi + r_pocket = /obj/item/weapon/gun/projectile/automatic/pistol + belt = /obj/item/weapon/storage/belt/military + r_hand = /obj/item/weapon/gun/projectile/automatic/shotgun/bulldog + backpack_contents = list(/obj/item/weapon/storage/box/engineer=1,\ + /obj/item/weapon/tank/jetpack/oxygen/harness=1,\ + /obj/item/weapon/pinpointer/nukeop=1) + + tc = 30 + +/datum/outfit/syndicate/full/post_equip(mob/living/carbon/human/H) + ..() + + + var/obj/item/clothing/suit/space/hardsuit/syndi/suit = H.wear_suit + suit.ToggleHelmet() + var/obj/item/clothing/head/helmet/space/hardsuit/syndi/helmet = H.head + helmet.attack_self(H) + H.internal = H.l_store \ No newline at end of file diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm index c62f3597066..fedb33a4d11 100644 --- a/code/game/gamemodes/objective.dm +++ b/code/game/gamemodes/objective.dm @@ -1,838 +1,838 @@ -/datum/objective - var/datum/mind/owner = null //Who owns the objective. - var/explanation_text = "Nothing" //What that person is supposed to do. - var/datum/mind/target = null //If they are focused on a particular person. - var/target_amount = 0 //If they are focused on a particular number. Steal objectives have their own counter. - var/completed = 0 //currently only used for custom objectives. - var/dangerrating = 0 //How hard the objective is, essentially. Used for dishing out objectives and checking overall victory. - var/martyr_compatible = 0 //If the objective is compatible with martyr objective, i.e. if you can still do it while dead. - -/datum/objective/New(var/text) - if(text) - explanation_text = text - -/datum/objective/proc/check_completion() - return completed - -/datum/objective/proc/is_unique_objective(possible_target) - for(var/datum/objective/O in owner.objectives) - if(istype(O, type) && O.get_target() == possible_target) - return 0 - return 1 - -/datum/objective/proc/get_target() - return target - -/datum/objective/proc/find_target() - var/list/possible_targets = list() - for(var/datum/mind/possible_target in ticker.minds) - if(possible_target != owner && ishuman(possible_target.current) && (possible_target.current.stat != 2) && is_unique_objective(possible_target)) - possible_targets += possible_target - if(possible_targets.len > 0) - target = pick(possible_targets) - update_explanation_text() - return target - -/datum/objective/proc/find_target_by_role(role, role_type=0, invert=0)//Option sets either to check assigned role or special role. Default to assigned., invert inverts the check, eg: "Don't choose a Ling" - for(var/datum/mind/possible_target in ticker.minds) - if((possible_target != owner) && ishuman(possible_target.current)) - var/is_role = 0 - if(role_type) - if(possible_target.special_role == role) - is_role++ - else - if(possible_target.assigned_role == role) - is_role++ - - if(invert) - if(is_role) - continue - target = possible_target - break - else if(is_role) - target = possible_target - break - - update_explanation_text() - -/datum/objective/proc/update_explanation_text() - //Default does nothing, override where needed - -/datum/objective/proc/give_special_equipment() - -/datum/objective/assassinate - var/target_role_type=0 - dangerrating = 10 - martyr_compatible = 1 - -/datum/objective/assassinate/find_target_by_role(role, role_type=0, invert=0) - if(!invert) - target_role_type = role_type - ..() - return target - -/datum/objective/assassinate/check_completion() - if(target && target.current) - if(target.current.stat == DEAD || issilicon(target.current) || isbrain(target.current) || target.current.z > 6 || !target.current.ckey) //Borgs/brains/AIs count as dead for traitor objectives. --NeoFite - return 1 - return 0 - return 1 - -/datum/objective/assassinate/update_explanation_text() - ..() - if(target && target.current) - explanation_text = "Assassinate [target.name], the [!target_role_type ? target.assigned_role : target.special_role]." - else - explanation_text = "Free Objective" - - -/datum/objective/mutiny - var/target_role_type=0 - martyr_compatible = 1 - -/datum/objective/mutiny/find_target_by_role(role, role_type=0,invert=0) - if(!invert) - target_role_type = role_type - ..() - return target - -/datum/objective/mutiny/check_completion() - if(target && target.current) - if(target.current.stat == DEAD || !ishuman(target.current) || !target.current.ckey || !target.current.client) - return 1 - var/turf/T = get_turf(target.current) - if(T && (T.z > ZLEVEL_STATION) || target.current.client.is_afk()) //If they leave the station or go afk they count as dead for this - return 2 - return 0 - return 1 - -/datum/objective/mutiny/update_explanation_text() - ..() - if(target && target.current) - explanation_text = "Assassinate or exile [target.name], the [!target_role_type ? target.assigned_role : target.special_role]." - else - explanation_text = "Free Objective" - - - -/datum/objective/maroon - var/target_role_type=0 - dangerrating = 5 - martyr_compatible = 1 - -/datum/objective/maroon/find_target_by_role(role, role_type=0, invert=0) - if(!invert) - target_role_type = role_type - ..() - return target - -/datum/objective/maroon/check_completion() - if(target && target.current) - if(target.current.stat == DEAD || issilicon(target.current) || isbrain(target.current) || target.current.z > 6 || !target.current.ckey) //Borgs/brains/AIs count as dead for traitor objectives. --NeoFite - return 1 - if(target.current.onCentcom() || target.current.onSyndieBase()) - return 0 - return 1 - -/datum/objective/maroon/update_explanation_text() - if(target && target.current) - explanation_text = "Prevent [target.name], the [!target_role_type ? target.assigned_role : target.special_role], from escaping alive." - else - explanation_text = "Free Objective" - - - -/datum/objective/debrain//I want braaaainssss - var/target_role_type=0 - dangerrating = 20 - -/datum/objective/debrain/find_target_by_role(role, role_type=0, invert=0) - if(!invert) - target_role_type = role_type - ..() - return target - -/datum/objective/debrain/check_completion() - if(!target)//If it's a free objective. - return 1 - if( !owner.current || owner.current.stat==DEAD )//If you're otherwise dead. - return 0 - if( !target.current || !isbrain(target.current) ) - return 0 - var/atom/A = target.current - while(A.loc) //check to see if the brainmob is on our person - A = A.loc - if(A == owner.current) - return 1 - return 0 - -/datum/objective/debrain/update_explanation_text() - ..() - if(target && target.current) - explanation_text = "Steal the brain of [target.name], the [!target_role_type ? target.assigned_role : target.special_role]." - else - explanation_text = "Free Objective" - - - -/datum/objective/protect//The opposite of killing a dude. - var/target_role_type=0 - dangerrating = 10 - martyr_compatible = 1 - -/datum/objective/protect/find_target_by_role(role, role_type=0, invert=0) - if(!invert) - target_role_type = role_type - ..() - return target - -/datum/objective/protect/check_completion() - if(!target) //If it's a free objective. - return 1 - if(target.current) - if(target.current.stat == DEAD || issilicon(target.current) || isbrain(target.current)) - return 0 - return 1 - return 0 - -/datum/objective/protect/update_explanation_text() - ..() - if(target && target.current) - explanation_text = "Protect [target.name], the [!target_role_type ? target.assigned_role : target.special_role]." - else - explanation_text = "Free Objective" - - - -/datum/objective/hijack - explanation_text = "Hijack the shuttle to ensure no loyalist Nanotrasen crew escape alive and out of custody." - dangerrating = 25 - martyr_compatible = 0 //Technically you won't get both anyway. - -/datum/objective/hijack/check_completion() - if(!owner.current || owner.current.stat) - return 0 - if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) - return 0 - if(issilicon(owner.current)) - return 0 - - var/area/A = get_area(owner.current) - if(SSshuttle.emergency.areaInstance != A) - return 0 - - for(var/mob/living/player in player_list) - if(player.mind && player.mind != owner) - if(player.stat != DEAD) - if(istype(player, /mob/living/silicon)) //Borgs are technically dead anyways - continue - if(get_area(player) == A) - if(!player.mind.special_role && !istype(get_turf(player.mind.current), /turf/simulated/floor/plasteel/shuttle/red)) - return 0 - return 1 - -/datum/objective/hijackclone - explanation_text = "Hijack the emergency shuttle by ensuring only you (or your copies) escape." - dangerrating = 25 - martyr_compatible = 0 - -/datum/objective/hijackclone/check_completion() - if(!owner.current) - return 0 - if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) - return 0 - - var/area/A = SSshuttle.emergency.areaInstance - - for(var/mob/living/player in player_list) //Make sure nobody else is onboard - if(player.mind && player.mind != owner) - if(player.stat != DEAD) - if(istype(player, /mob/living/silicon)) - continue - if(get_area(player) == A) - if(player.real_name != owner.current.real_name && !istype(get_turf(player.mind.current), /turf/simulated/floor/plasteel/shuttle/red)) - return 0 - - for(var/mob/living/player in player_list) //Make sure at least one of you is onboard - if(player.mind && player.mind != owner) - if(player.stat != DEAD) - if(istype(player, /mob/living/silicon)) - continue - if(get_area(player) == A) - if(player.real_name == owner.current.real_name && !istype(get_turf(player.mind.current), /turf/simulated/floor/plasteel/shuttle/red)) - return 1 - return 0 - -/datum/objective/block - explanation_text = "Do not allow any organic lifeforms to escape on the shuttle alive." - dangerrating = 25 - martyr_compatible = 1 - -/datum/objective/block/check_completion() - if(!istype(owner.current, /mob/living/silicon)) - return 0 - if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) - return 1 - - var/area/A = SSshuttle.emergency.areaInstance - - for(var/mob/living/player in player_list) - if(istype(player, /mob/living/silicon)) - continue - if(player.mind) - if(player.stat != DEAD) - if(get_area(player) == A) - return 0 - - return 1 - - -/datum/objective/escape - explanation_text = "Escape on the shuttle or an escape pod alive and without being in custody." - dangerrating = 5 - -/datum/objective/escape/check_completion() - if(issilicon(owner.current)) - return 0 - if(isbrain(owner.current)) - return 0 - if(!owner.current || owner.current.stat == DEAD) - return 0 - if(ticker.force_ending) //This one isn't their fault, so lets just assume good faith - return 1 - if(ticker.mode.station_was_nuked) //If they escaped the blast somehow, let them win - return 1 - if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) - return 0 - var/turf/location = get_turf(owner.current) - if(!location) - return 0 - - if(istype(location, /turf/simulated/floor/plasteel/shuttle/red)) // Fails traitors if they are in the shuttle brig -- Polymorph - return 0 - - if(location.onCentcom() || location.onSyndieBase()) - return 1 - - return 0 - -/datum/objective/escape/escape_with_identity - dangerrating = 10 - var/target_real_name // Has to be stored because the target's real_name can change over the course of the round - var/target_missing_id - -/datum/objective/escape/escape_with_identity/find_target() - target = ..() - update_explanation_text() - -/datum/objective/escape/escape_with_identity/update_explanation_text() - if(target && target.current) - target_real_name = target.current.real_name - explanation_text = "Escape on the shuttle or an escape pod with the identity of [target_real_name], the [target.assigned_role]" - var/mob/living/carbon/human/H - if(ishuman(target.current)) - H = target.current - if(H && H.get_id_name() != target_real_name) - target_missing_id = 1 - else - explanation_text += " while wearing their identification card" - explanation_text += "." //Proper punctuation is important! - - else - explanation_text = "Free Objective." - -/datum/objective/escape/escape_with_identity/check_completion() - if(!target_real_name) - return 1 - if(!ishuman(owner.current)) - return 0 - var/mob/living/carbon/human/H = owner.current - if(..()) - if(H.dna.real_name == target_real_name) - if(H.get_id_name()== target_real_name || target_missing_id) - return 1 - return 0 - - -/datum/objective/survive - explanation_text = "Stay alive until the end." - dangerrating = 3 - -/datum/objective/survive/check_completion() - if(!owner.current || owner.current.stat == DEAD || isbrain(owner.current)) - return 0 //Brains no longer win survive objectives. --NEO - if(!is_special_character(owner.current)) //This fails borg'd traitors - return 0 - return 1 - - -/datum/objective/martyr - explanation_text = "Die a glorious death." - dangerrating = 1 - -/datum/objective/martyr/check_completion() - if(!owner.current) //Gibbed, etc. - return 1 - if(owner.current && owner.current.stat == DEAD) //You're dead! Yay! - return 1 - return 0 - - -/datum/objective/nuclear - explanation_text = "Destroy the station with a nuclear device." - martyr_compatible = 1 - -/datum/objective/nuclear/check_completion() - if(ticker && ticker.mode && ticker.mode.station_was_nuked) - return 1 - return 0 - - -var/global/list/possible_items = list() -/datum/objective/steal - var/datum/objective_item/targetinfo = null //Save the chosen item datum so we can access it later. - var/obj/item/steal_target = null //Needed for custom objectives (they're just items, not datums). - dangerrating = 5 //Overridden by the individual item's difficulty, but defaults to 5 for custom objectives. - martyr_compatible = 0 - -/datum/objective/steal/get_target() - return steal_target - -/datum/objective/steal/New() - ..() - if(!possible_items.len)//Only need to fill the list when it's needed. - init_subtypes(/datum/objective_item/steal,possible_items) - -/datum/objective/steal/find_target() - var/approved_targets = list() - for(var/datum/objective_item/possible_item in possible_items) - if(is_unique_objective(possible_item.targetitem) && !(owner.current.mind.assigned_role in possible_item.excludefromjob)) - approved_targets += possible_item - return set_target(safepick(approved_targets)) - -/datum/objective/steal/proc/set_target(datum/objective_item/item) - if(item) - targetinfo = item - - steal_target = targetinfo.targetitem - explanation_text = "Steal [targetinfo.name]." - dangerrating = targetinfo.difficulty - give_special_equipment() - return steal_target - else - explanation_text = "Free objective" - return - -/datum/objective/steal/proc/select_target() //For admins setting objectives manually. - var/list/possible_items_all = possible_items+"custom" - var/new_target = input("Select target:", "Objective target", steal_target) as null|anything in possible_items_all - if (!new_target) return - - if (new_target == "custom") //Can set custom items. - var/obj/item/custom_target = input("Select type:","Type") as null|anything in typesof(/obj/item) - if (!custom_target) return - var/tmp_obj = new custom_target - var/custom_name = tmp_obj:name - qdel(tmp_obj) - custom_name = stripped_input("Enter target name:", "Objective target", custom_name) - if (!custom_name) return - steal_target = custom_target - explanation_text = "Steal [custom_name]." - - else - set_target(new_target) - return steal_target - -/datum/objective/steal/check_completion() - if(!steal_target) return 1 - if(!isliving(owner.current)) return 0 - var/list/all_items = owner.current.GetAllContents() //this should get things in cheesewheels, books, etc. - - for(var/obj/I in all_items) //Check for items - if(istype(I, steal_target)) - if(targetinfo && targetinfo.check_special_completion(I))//Returns 1 by default. Items with special checks will return 1 if the conditions are fulfilled. - return 1 - else //If there's no targetinfo, then that means it was a custom objective. At this point, we know you have the item, so return 1. - return 1 - - if(targetinfo && I.type in targetinfo.altitems) //Ok, so you don't have the item. Do you have an alternative, at least? - if(targetinfo.check_special_completion(I))//Yeah, we do! Don't return 0 if we don't though - then you could fail if you had 1 item that didn't pass and got checked first! - return 1 - return 0 - -/datum/objective/steal/give_special_equipment() - if(owner && owner.current && targetinfo) - if(istype(owner.current, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = owner.current - var/list/slots = list ("backpack" = slot_in_backpack) - for(var/eq_path in targetinfo.special_equipment) - var/obj/O = new eq_path - H.equip_in_one_of_slots(O, slots) - H.update_icons() - -var/global/list/possible_items_special = list() -/datum/objective/steal/special //ninjas are so special they get their own subtype good for them - -/datum/objective/steal/special/New() - ..() - if(!possible_items_special.len) - init_subtypes(/datum/objective_item/special,possible_items) - init_subtypes(/datum/objective_item/stack,possible_items) - -/datum/objective/steal/special/find_target() - return set_target(pick(possible_items_special)) - - - -/datum/objective/steal/exchange - dangerrating = 10 - martyr_compatible = 0 - -/datum/objective/steal/exchange/proc/set_faction(faction,otheragent) - target = otheragent - if(faction == "red") - targetinfo = new/datum/objective_item/unique/docs_blue - else if(faction == "blue") - targetinfo = new/datum/objective_item/unique/docs_red - explanation_text = "Acquire [targetinfo.name] held by [target.current.real_name], the [target.assigned_role] and syndicate agent" - steal_target = targetinfo.targetitem - - -/datum/objective/steal/exchange/update_explanation_text() - ..() - if(target && target.current) - explanation_text = "Acquire [targetinfo.name] held by [target.name], the [target.assigned_role] and syndicate agent" - else - explanation_text = "Free Objective" - - -/datum/objective/steal/exchange/backstab - dangerrating = 3 - -/datum/objective/steal/exchange/backstab/set_faction(faction) - if(faction == "red") - targetinfo = new/datum/objective_item/unique/docs_red - else if(faction == "blue") - targetinfo = new/datum/objective_item/unique/docs_blue - explanation_text = "Do not give up or lose [targetinfo.name]." - steal_target = targetinfo.targetitem - - -/datum/objective/download - dangerrating = 10 - -/datum/objective/download/proc/gen_amount_goal() - target_amount = rand(10,20) - explanation_text = "Download [target_amount] research level\s." - return target_amount - -/datum/objective/download/check_completion()//NINJACODE - if(!ishuman(owner.current)) - return 0 - - var/mob/living/carbon/human/H = owner.current - if(!H || H.stat == DEAD) - return 0 - - if(!istype(H.wear_suit, /obj/item/clothing/suit/space/space_ninja)) - return 0 - - var/obj/item/clothing/suit/space/space_ninja/SN = H.wear_suit - if(!SN.s_initialized) - return 0 - - var/current_amount - if(!SN.stored_research.len) - return 0 - else - for(var/datum/tech/current_data in SN.stored_research) - if(current_data.level) - current_amount += (current_data.level-1) - if(current_amount= target_amount)) - return 1 - else - return 0 - - - -/datum/objective/destroy - dangerrating = 10 - martyr_compatible = 1 - -/datum/objective/destroy/find_target() - var/list/possible_targets = active_ais(1) - var/mob/living/silicon/ai/target_ai = pick(possible_targets) - target = target_ai.mind - update_explanation_text() - return target - -/datum/objective/destroy/check_completion() - if(target && target.current) - if(target.current.stat == DEAD || target.current.z > 6 || !target.current.ckey) //Borgs/brains/AIs count as dead for traitor objectives. --NeoFite - return 1 - return 0 - return 1 - -/datum/objective/destroy/update_explanation_text() - ..() - if(target && target.current) - explanation_text = "Destroy [target.name], the experimental AI." - else - explanation_text = "Free Objective" - -/datum/objective/summon_guns - explanation_text = "Steal at least five guns!" - -/datum/objective/summon_guns/check_completion() - if(!isliving(owner.current)) return 0 - var/guncount = 0 - var/list/all_items = owner.current.GetAllContents() //this should get things in cheesewheels, books, etc. - for(var/obj/I in all_items) //Check for guns - if(istype(I, /obj/item/weapon/gun)) - guncount++ - if(guncount >= 5) - return 1 - else - return 0 - return 0 - - - -//////////////////////////////// -// Changeling team objectives // -//////////////////////////////// - -/datum/objective/changeling_team_objective //Abstract type - martyr_compatible = 0 //Suicide is not teamwork! - explanation_text = "Changeling Friendship!" - var/min_lings = 3 //Minimum amount of lings for this team objective to be possible - var/escape_objective_compatible = FALSE - - -//Impersonate department -//Picks as many people as it can from a department (Security,Engineer,Medical,Science) -//and tasks the lings with killing and replacing them -/datum/objective/changeling_team_objective/impersonate_department - explanation_text = "Ensure X derpartment are killed, impersonated, and replaced by Changelings" - var/command_staff_only = FALSE //if this is true, it picks command staff instead - var/list/department_minds = list() - var/list/department_real_names = list() - var/department_string = "" - - -/datum/objective/changeling_team_objective/impersonate_department/proc/get_department_staff() - department_minds = list() - department_real_names = list() - - var/list/departments = list("Head of Security","Research Director","Chief Engineer","Chief Medical Officer") - var/department_head = pick(departments) - switch(department_head) - if("Head of Security") - department_string = "security" - if("Research Director") - department_string = "science" - if("Chief Engineer") - department_string = "engineering" - if("Chief Medical Officer") - department_string = "medical" - - var/ling_count = ticker.mode.changelings - - for(var/datum/mind/M in ticker.minds) - if(M in ticker.mode.changelings) - continue - if(department_head in get_department_heads(M.assigned_role)) - if(ling_count) - ling_count-- - department_minds += M - department_real_names += M.current.real_name - else - break - - if(!department_minds.len) - log_game("[type] has failed to find department staff, and has removed itself. the round will continue normally") - owner.objectives -= src - qdel(src) - return - - -/datum/objective/changeling_team_objective/impersonate_department/proc/get_heads() - department_minds = list() - department_real_names = list() - - //Needed heads is between min_lings and the maximum possible amount of command roles - //So at the time of writing, rand(3,6), it's also capped by the amount of lings there are - //Because you can't fill 6 head roles with 3 lings - - var/needed_heads = rand(min_lings,command_positions.len) - needed_heads = min(ticker.mode.changelings.len,needed_heads) - - var/list/heads = ticker.mode.get_living_heads() - for(var/datum/mind/head in heads) - if(head in ticker.mode.changelings) //Looking at you HoP. - continue - if(needed_heads) - department_minds += head - department_real_names += head.current.real_name - needed_heads-- - else - break - - if(!department_minds.len) - log_game("[type] has failed to find department heads, and has removed itself. the round will continue normally") - owner.objectives -= src - qdel(src) - return - - -/datum/objective/changeling_team_objective/impersonate_department/New(var/text) - ..() - if(command_staff_only) - get_heads() - else - get_department_staff() - - update_explanation_text() - - -/datum/objective/changeling_team_objective/impersonate_department/update_explanation_text() - ..() - if(!department_real_names.len || !department_minds.len) - explanation_text = "Free Objective" - return //Something fucked up, give them a win - - if(command_staff_only) - explanation_text = "Ensure changelings impersonate and escape as the following heads of staff: " - else - explanation_text = "Ensure changelings impersonate and escape as the following members of \the [department_string] department: " - - var/first = 1 - for(var/datum/mind/M in department_minds) - var/string = "[M.name] the [M.assigned_role]" - if(!first) - string = ", [M.name] the [M.assigned_role]" - else - first-- - explanation_text += string - - if(command_staff_only) - explanation_text += ", while the real heads are dead. This is a team objective." - else - explanation_text += ", while the real members are dead. This is a team objective." - - -/datum/objective/changeling_team_objective/impersonate_department/check_completion() - if(!department_real_names.len || !department_minds.len) - return 1 //Something fucked up, give them a win - - var/list/check_names = department_real_names.Copy() - - //Check each department member's mind to see if any of them made it to centcomm alive, if they did it's an automatic fail - for(var/datum/mind/M in department_minds) - if(M in ticker.mode.changelings) //Lings aren't picked for this, but let's be safe - continue - - if(M.current) - var/turf/mloc = get_turf(M.current) - if(mloc.onCentcom() && (M.current.stat != DEAD)) - return 0 //A Non-ling living target got to centcomm, fail - - //Check each staff member has been replaced, by cross referencing changeling minds, changeling current dna, the staff minds and their original DNA names - var/success = 0 - changelings: - for(var/datum/mind/changeling in ticker.mode.changelings) - if(success >= department_minds.len) //We did it, stop here! - return 1 - if(ishuman(changeling.current)) - var/mob/living/carbon/human/H = changeling.current - var/turf/cloc = get_turf(changeling.current) - if(cloc && cloc.onCentcom() && (changeling.current.stat != DEAD)) //Living changeling on centcomm.... - for(var/name in check_names) //Is he (disguised as) one of the staff? - if(H.dna.real_name == name) - check_names -= name //This staff member is accounted for, remove them, so the team don't succeed by escape as 7 of the same engineer - success++ //A living changeling staff member made it to centcomm - continue changelings - - if(success >= department_minds.len) - return 1 - return 0 - - - - -//A subtype of impersonate_derpartment -//This subtype always picks as many command staff as it can (HoS,HoP,Cap,CE,CMO,RD) -//and tasks the lings with killing and replacing them -/datum/objective/changeling_team_objective/impersonate_department/impersonate_heads - explanation_text = "Have X or more heads of staff escape on the shuttle disguised as heads, while the real heads are dead" - command_staff_only = TRUE - - - +/datum/objective + var/datum/mind/owner = null //Who owns the objective. + var/explanation_text = "Nothing" //What that person is supposed to do. + var/datum/mind/target = null //If they are focused on a particular person. + var/target_amount = 0 //If they are focused on a particular number. Steal objectives have their own counter. + var/completed = 0 //currently only used for custom objectives. + var/dangerrating = 0 //How hard the objective is, essentially. Used for dishing out objectives and checking overall victory. + var/martyr_compatible = 0 //If the objective is compatible with martyr objective, i.e. if you can still do it while dead. + +/datum/objective/New(var/text) + if(text) + explanation_text = text + +/datum/objective/proc/check_completion() + return completed + +/datum/objective/proc/is_unique_objective(possible_target) + for(var/datum/objective/O in owner.objectives) + if(istype(O, type) && O.get_target() == possible_target) + return 0 + return 1 + +/datum/objective/proc/get_target() + return target + +/datum/objective/proc/find_target() + var/list/possible_targets = list() + for(var/datum/mind/possible_target in ticker.minds) + if(possible_target != owner && ishuman(possible_target.current) && (possible_target.current.stat != 2) && is_unique_objective(possible_target)) + possible_targets += possible_target + if(possible_targets.len > 0) + target = pick(possible_targets) + update_explanation_text() + return target + +/datum/objective/proc/find_target_by_role(role, role_type=0, invert=0)//Option sets either to check assigned role or special role. Default to assigned., invert inverts the check, eg: "Don't choose a Ling" + for(var/datum/mind/possible_target in ticker.minds) + if((possible_target != owner) && ishuman(possible_target.current)) + var/is_role = 0 + if(role_type) + if(possible_target.special_role == role) + is_role++ + else + if(possible_target.assigned_role == role) + is_role++ + + if(invert) + if(is_role) + continue + target = possible_target + break + else if(is_role) + target = possible_target + break + + update_explanation_text() + +/datum/objective/proc/update_explanation_text() + //Default does nothing, override where needed + +/datum/objective/proc/give_special_equipment() + +/datum/objective/assassinate + var/target_role_type=0 + dangerrating = 10 + martyr_compatible = 1 + +/datum/objective/assassinate/find_target_by_role(role, role_type=0, invert=0) + if(!invert) + target_role_type = role_type + ..() + return target + +/datum/objective/assassinate/check_completion() + if(target && target.current) + if(target.current.stat == DEAD || issilicon(target.current) || isbrain(target.current) || target.current.z > 6 || !target.current.ckey) //Borgs/brains/AIs count as dead for traitor objectives. --NeoFite + return 1 + return 0 + return 1 + +/datum/objective/assassinate/update_explanation_text() + ..() + if(target && target.current) + explanation_text = "Assassinate [target.name], the [!target_role_type ? target.assigned_role : target.special_role]." + else + explanation_text = "Free Objective" + + +/datum/objective/mutiny + var/target_role_type=0 + martyr_compatible = 1 + +/datum/objective/mutiny/find_target_by_role(role, role_type=0,invert=0) + if(!invert) + target_role_type = role_type + ..() + return target + +/datum/objective/mutiny/check_completion() + if(target && target.current) + if(target.current.stat == DEAD || !ishuman(target.current) || !target.current.ckey || !target.current.client) + return 1 + var/turf/T = get_turf(target.current) + if(T && (T.z > ZLEVEL_STATION) || target.current.client.is_afk()) //If they leave the station or go afk they count as dead for this + return 2 + return 0 + return 1 + +/datum/objective/mutiny/update_explanation_text() + ..() + if(target && target.current) + explanation_text = "Assassinate or exile [target.name], the [!target_role_type ? target.assigned_role : target.special_role]." + else + explanation_text = "Free Objective" + + + +/datum/objective/maroon + var/target_role_type=0 + dangerrating = 5 + martyr_compatible = 1 + +/datum/objective/maroon/find_target_by_role(role, role_type=0, invert=0) + if(!invert) + target_role_type = role_type + ..() + return target + +/datum/objective/maroon/check_completion() + if(target && target.current) + if(target.current.stat == DEAD || issilicon(target.current) || isbrain(target.current) || target.current.z > 6 || !target.current.ckey) //Borgs/brains/AIs count as dead for traitor objectives. --NeoFite + return 1 + if(target.current.onCentcom() || target.current.onSyndieBase()) + return 0 + return 1 + +/datum/objective/maroon/update_explanation_text() + if(target && target.current) + explanation_text = "Prevent [target.name], the [!target_role_type ? target.assigned_role : target.special_role], from escaping alive." + else + explanation_text = "Free Objective" + + + +/datum/objective/debrain//I want braaaainssss + var/target_role_type=0 + dangerrating = 20 + +/datum/objective/debrain/find_target_by_role(role, role_type=0, invert=0) + if(!invert) + target_role_type = role_type + ..() + return target + +/datum/objective/debrain/check_completion() + if(!target)//If it's a free objective. + return 1 + if( !owner.current || owner.current.stat==DEAD )//If you're otherwise dead. + return 0 + if( !target.current || !isbrain(target.current) ) + return 0 + var/atom/A = target.current + while(A.loc) //check to see if the brainmob is on our person + A = A.loc + if(A == owner.current) + return 1 + return 0 + +/datum/objective/debrain/update_explanation_text() + ..() + if(target && target.current) + explanation_text = "Steal the brain of [target.name], the [!target_role_type ? target.assigned_role : target.special_role]." + else + explanation_text = "Free Objective" + + + +/datum/objective/protect//The opposite of killing a dude. + var/target_role_type=0 + dangerrating = 10 + martyr_compatible = 1 + +/datum/objective/protect/find_target_by_role(role, role_type=0, invert=0) + if(!invert) + target_role_type = role_type + ..() + return target + +/datum/objective/protect/check_completion() + if(!target) //If it's a free objective. + return 1 + if(target.current) + if(target.current.stat == DEAD || issilicon(target.current) || isbrain(target.current)) + return 0 + return 1 + return 0 + +/datum/objective/protect/update_explanation_text() + ..() + if(target && target.current) + explanation_text = "Protect [target.name], the [!target_role_type ? target.assigned_role : target.special_role]." + else + explanation_text = "Free Objective" + + + +/datum/objective/hijack + explanation_text = "Hijack the shuttle to ensure no loyalist Nanotrasen crew escape alive and out of custody." + dangerrating = 25 + martyr_compatible = 0 //Technically you won't get both anyway. + +/datum/objective/hijack/check_completion() + if(!owner.current || owner.current.stat) + return 0 + if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) + return 0 + if(issilicon(owner.current)) + return 0 + + var/area/A = get_area(owner.current) + if(SSshuttle.emergency.areaInstance != A) + return 0 + + for(var/mob/living/player in player_list) + if(player.mind && player.mind != owner) + if(player.stat != DEAD) + if(istype(player, /mob/living/silicon)) //Borgs are technically dead anyways + continue + if(get_area(player) == A) + if(!player.mind.special_role && !istype(get_turf(player.mind.current), /turf/simulated/floor/plasteel/shuttle/red)) + return 0 + return 1 + +/datum/objective/hijackclone + explanation_text = "Hijack the emergency shuttle by ensuring only you (or your copies) escape." + dangerrating = 25 + martyr_compatible = 0 + +/datum/objective/hijackclone/check_completion() + if(!owner.current) + return 0 + if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) + return 0 + + var/area/A = SSshuttle.emergency.areaInstance + + for(var/mob/living/player in player_list) //Make sure nobody else is onboard + if(player.mind && player.mind != owner) + if(player.stat != DEAD) + if(istype(player, /mob/living/silicon)) + continue + if(get_area(player) == A) + if(player.real_name != owner.current.real_name && !istype(get_turf(player.mind.current), /turf/simulated/floor/plasteel/shuttle/red)) + return 0 + + for(var/mob/living/player in player_list) //Make sure at least one of you is onboard + if(player.mind && player.mind != owner) + if(player.stat != DEAD) + if(istype(player, /mob/living/silicon)) + continue + if(get_area(player) == A) + if(player.real_name == owner.current.real_name && !istype(get_turf(player.mind.current), /turf/simulated/floor/plasteel/shuttle/red)) + return 1 + return 0 + +/datum/objective/block + explanation_text = "Do not allow any organic lifeforms to escape on the shuttle alive." + dangerrating = 25 + martyr_compatible = 1 + +/datum/objective/block/check_completion() + if(!istype(owner.current, /mob/living/silicon)) + return 0 + if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) + return 1 + + var/area/A = SSshuttle.emergency.areaInstance + + for(var/mob/living/player in player_list) + if(istype(player, /mob/living/silicon)) + continue + if(player.mind) + if(player.stat != DEAD) + if(get_area(player) == A) + return 0 + + return 1 + + +/datum/objective/escape + explanation_text = "Escape on the shuttle or an escape pod alive and without being in custody." + dangerrating = 5 + +/datum/objective/escape/check_completion() + if(issilicon(owner.current)) + return 0 + if(isbrain(owner.current)) + return 0 + if(!owner.current || owner.current.stat == DEAD) + return 0 + if(ticker.force_ending) //This one isn't their fault, so lets just assume good faith + return 1 + if(ticker.mode.station_was_nuked) //If they escaped the blast somehow, let them win + return 1 + if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) + return 0 + var/turf/location = get_turf(owner.current) + if(!location) + return 0 + + if(istype(location, /turf/simulated/floor/plasteel/shuttle/red)) // Fails traitors if they are in the shuttle brig -- Polymorph + return 0 + + if(location.onCentcom() || location.onSyndieBase()) + return 1 + + return 0 + +/datum/objective/escape/escape_with_identity + dangerrating = 10 + var/target_real_name // Has to be stored because the target's real_name can change over the course of the round + var/target_missing_id + +/datum/objective/escape/escape_with_identity/find_target() + target = ..() + update_explanation_text() + +/datum/objective/escape/escape_with_identity/update_explanation_text() + if(target && target.current) + target_real_name = target.current.real_name + explanation_text = "Escape on the shuttle or an escape pod with the identity of [target_real_name], the [target.assigned_role]" + var/mob/living/carbon/human/H + if(ishuman(target.current)) + H = target.current + if(H && H.get_id_name() != target_real_name) + target_missing_id = 1 + else + explanation_text += " while wearing their identification card" + explanation_text += "." //Proper punctuation is important! + + else + explanation_text = "Free Objective." + +/datum/objective/escape/escape_with_identity/check_completion() + if(!target_real_name) + return 1 + if(!ishuman(owner.current)) + return 0 + var/mob/living/carbon/human/H = owner.current + if(..()) + if(H.dna.real_name == target_real_name) + if(H.get_id_name()== target_real_name || target_missing_id) + return 1 + return 0 + + +/datum/objective/survive + explanation_text = "Stay alive until the end." + dangerrating = 3 + +/datum/objective/survive/check_completion() + if(!owner.current || owner.current.stat == DEAD || isbrain(owner.current)) + return 0 //Brains no longer win survive objectives. --NEO + if(!is_special_character(owner.current)) //This fails borg'd traitors + return 0 + return 1 + + +/datum/objective/martyr + explanation_text = "Die a glorious death." + dangerrating = 1 + +/datum/objective/martyr/check_completion() + if(!owner.current) //Gibbed, etc. + return 1 + if(owner.current && owner.current.stat == DEAD) //You're dead! Yay! + return 1 + return 0 + + +/datum/objective/nuclear + explanation_text = "Destroy the station with a nuclear device." + martyr_compatible = 1 + +/datum/objective/nuclear/check_completion() + if(ticker && ticker.mode && ticker.mode.station_was_nuked) + return 1 + return 0 + + +var/global/list/possible_items = list() +/datum/objective/steal + var/datum/objective_item/targetinfo = null //Save the chosen item datum so we can access it later. + var/obj/item/steal_target = null //Needed for custom objectives (they're just items, not datums). + dangerrating = 5 //Overridden by the individual item's difficulty, but defaults to 5 for custom objectives. + martyr_compatible = 0 + +/datum/objective/steal/get_target() + return steal_target + +/datum/objective/steal/New() + ..() + if(!possible_items.len)//Only need to fill the list when it's needed. + init_subtypes(/datum/objective_item/steal,possible_items) + +/datum/objective/steal/find_target() + var/approved_targets = list() + for(var/datum/objective_item/possible_item in possible_items) + if(is_unique_objective(possible_item.targetitem) && !(owner.current.mind.assigned_role in possible_item.excludefromjob)) + approved_targets += possible_item + return set_target(safepick(approved_targets)) + +/datum/objective/steal/proc/set_target(datum/objective_item/item) + if(item) + targetinfo = item + + steal_target = targetinfo.targetitem + explanation_text = "Steal [targetinfo.name]." + dangerrating = targetinfo.difficulty + give_special_equipment() + return steal_target + else + explanation_text = "Free objective" + return + +/datum/objective/steal/proc/select_target() //For admins setting objectives manually. + var/list/possible_items_all = possible_items+"custom" + var/new_target = input("Select target:", "Objective target", steal_target) as null|anything in possible_items_all + if (!new_target) return + + if (new_target == "custom") //Can set custom items. + var/obj/item/custom_target = input("Select type:","Type") as null|anything in typesof(/obj/item) + if (!custom_target) return + var/tmp_obj = new custom_target + var/custom_name = tmp_obj:name + qdel(tmp_obj) + custom_name = stripped_input("Enter target name:", "Objective target", custom_name) + if (!custom_name) return + steal_target = custom_target + explanation_text = "Steal [custom_name]." + + else + set_target(new_target) + return steal_target + +/datum/objective/steal/check_completion() + if(!steal_target) return 1 + if(!isliving(owner.current)) return 0 + var/list/all_items = owner.current.GetAllContents() //this should get things in cheesewheels, books, etc. + + for(var/obj/I in all_items) //Check for items + if(istype(I, steal_target)) + if(targetinfo && targetinfo.check_special_completion(I))//Returns 1 by default. Items with special checks will return 1 if the conditions are fulfilled. + return 1 + else //If there's no targetinfo, then that means it was a custom objective. At this point, we know you have the item, so return 1. + return 1 + + if(targetinfo && I.type in targetinfo.altitems) //Ok, so you don't have the item. Do you have an alternative, at least? + if(targetinfo.check_special_completion(I))//Yeah, we do! Don't return 0 if we don't though - then you could fail if you had 1 item that didn't pass and got checked first! + return 1 + return 0 + +/datum/objective/steal/give_special_equipment() + if(owner && owner.current && targetinfo) + if(istype(owner.current, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = owner.current + var/list/slots = list ("backpack" = slot_in_backpack) + for(var/eq_path in targetinfo.special_equipment) + var/obj/O = new eq_path + H.equip_in_one_of_slots(O, slots) + H.update_icons() + +var/global/list/possible_items_special = list() +/datum/objective/steal/special //ninjas are so special they get their own subtype good for them + +/datum/objective/steal/special/New() + ..() + if(!possible_items_special.len) + init_subtypes(/datum/objective_item/special,possible_items) + init_subtypes(/datum/objective_item/stack,possible_items) + +/datum/objective/steal/special/find_target() + return set_target(pick(possible_items_special)) + + + +/datum/objective/steal/exchange + dangerrating = 10 + martyr_compatible = 0 + +/datum/objective/steal/exchange/proc/set_faction(faction,otheragent) + target = otheragent + if(faction == "red") + targetinfo = new/datum/objective_item/unique/docs_blue + else if(faction == "blue") + targetinfo = new/datum/objective_item/unique/docs_red + explanation_text = "Acquire [targetinfo.name] held by [target.current.real_name], the [target.assigned_role] and syndicate agent" + steal_target = targetinfo.targetitem + + +/datum/objective/steal/exchange/update_explanation_text() + ..() + if(target && target.current) + explanation_text = "Acquire [targetinfo.name] held by [target.name], the [target.assigned_role] and syndicate agent" + else + explanation_text = "Free Objective" + + +/datum/objective/steal/exchange/backstab + dangerrating = 3 + +/datum/objective/steal/exchange/backstab/set_faction(faction) + if(faction == "red") + targetinfo = new/datum/objective_item/unique/docs_red + else if(faction == "blue") + targetinfo = new/datum/objective_item/unique/docs_blue + explanation_text = "Do not give up or lose [targetinfo.name]." + steal_target = targetinfo.targetitem + + +/datum/objective/download + dangerrating = 10 + +/datum/objective/download/proc/gen_amount_goal() + target_amount = rand(10,20) + explanation_text = "Download [target_amount] research level\s." + return target_amount + +/datum/objective/download/check_completion()//NINJACODE + if(!ishuman(owner.current)) + return 0 + + var/mob/living/carbon/human/H = owner.current + if(!H || H.stat == DEAD) + return 0 + + if(!istype(H.wear_suit, /obj/item/clothing/suit/space/space_ninja)) + return 0 + + var/obj/item/clothing/suit/space/space_ninja/SN = H.wear_suit + if(!SN.s_initialized) + return 0 + + var/current_amount + if(!SN.stored_research.len) + return 0 + else + for(var/datum/tech/current_data in SN.stored_research) + if(current_data.level) + current_amount += (current_data.level-1) + if(current_amount= target_amount)) + return 1 + else + return 0 + + + +/datum/objective/destroy + dangerrating = 10 + martyr_compatible = 1 + +/datum/objective/destroy/find_target() + var/list/possible_targets = active_ais(1) + var/mob/living/silicon/ai/target_ai = pick(possible_targets) + target = target_ai.mind + update_explanation_text() + return target + +/datum/objective/destroy/check_completion() + if(target && target.current) + if(target.current.stat == DEAD || target.current.z > 6 || !target.current.ckey) //Borgs/brains/AIs count as dead for traitor objectives. --NeoFite + return 1 + return 0 + return 1 + +/datum/objective/destroy/update_explanation_text() + ..() + if(target && target.current) + explanation_text = "Destroy [target.name], the experimental AI." + else + explanation_text = "Free Objective" + +/datum/objective/summon_guns + explanation_text = "Steal at least five guns!" + +/datum/objective/summon_guns/check_completion() + if(!isliving(owner.current)) return 0 + var/guncount = 0 + var/list/all_items = owner.current.GetAllContents() //this should get things in cheesewheels, books, etc. + for(var/obj/I in all_items) //Check for guns + if(istype(I, /obj/item/weapon/gun)) + guncount++ + if(guncount >= 5) + return 1 + else + return 0 + return 0 + + + +//////////////////////////////// +// Changeling team objectives // +//////////////////////////////// + +/datum/objective/changeling_team_objective //Abstract type + martyr_compatible = 0 //Suicide is not teamwork! + explanation_text = "Changeling Friendship!" + var/min_lings = 3 //Minimum amount of lings for this team objective to be possible + var/escape_objective_compatible = FALSE + + +//Impersonate department +//Picks as many people as it can from a department (Security,Engineer,Medical,Science) +//and tasks the lings with killing and replacing them +/datum/objective/changeling_team_objective/impersonate_department + explanation_text = "Ensure X derpartment are killed, impersonated, and replaced by Changelings" + var/command_staff_only = FALSE //if this is true, it picks command staff instead + var/list/department_minds = list() + var/list/department_real_names = list() + var/department_string = "" + + +/datum/objective/changeling_team_objective/impersonate_department/proc/get_department_staff() + department_minds = list() + department_real_names = list() + + var/list/departments = list("Head of Security","Research Director","Chief Engineer","Chief Medical Officer") + var/department_head = pick(departments) + switch(department_head) + if("Head of Security") + department_string = "security" + if("Research Director") + department_string = "science" + if("Chief Engineer") + department_string = "engineering" + if("Chief Medical Officer") + department_string = "medical" + + var/ling_count = ticker.mode.changelings + + for(var/datum/mind/M in ticker.minds) + if(M in ticker.mode.changelings) + continue + if(department_head in get_department_heads(M.assigned_role)) + if(ling_count) + ling_count-- + department_minds += M + department_real_names += M.current.real_name + else + break + + if(!department_minds.len) + log_game("[type] has failed to find department staff, and has removed itself. the round will continue normally") + owner.objectives -= src + qdel(src) + return + + +/datum/objective/changeling_team_objective/impersonate_department/proc/get_heads() + department_minds = list() + department_real_names = list() + + //Needed heads is between min_lings and the maximum possible amount of command roles + //So at the time of writing, rand(3,6), it's also capped by the amount of lings there are + //Because you can't fill 6 head roles with 3 lings + + var/needed_heads = rand(min_lings,command_positions.len) + needed_heads = min(ticker.mode.changelings.len,needed_heads) + + var/list/heads = ticker.mode.get_living_heads() + for(var/datum/mind/head in heads) + if(head in ticker.mode.changelings) //Looking at you HoP. + continue + if(needed_heads) + department_minds += head + department_real_names += head.current.real_name + needed_heads-- + else + break + + if(!department_minds.len) + log_game("[type] has failed to find department heads, and has removed itself. the round will continue normally") + owner.objectives -= src + qdel(src) + return + + +/datum/objective/changeling_team_objective/impersonate_department/New(var/text) + ..() + if(command_staff_only) + get_heads() + else + get_department_staff() + + update_explanation_text() + + +/datum/objective/changeling_team_objective/impersonate_department/update_explanation_text() + ..() + if(!department_real_names.len || !department_minds.len) + explanation_text = "Free Objective" + return //Something fucked up, give them a win + + if(command_staff_only) + explanation_text = "Ensure changelings impersonate and escape as the following heads of staff: " + else + explanation_text = "Ensure changelings impersonate and escape as the following members of \the [department_string] department: " + + var/first = 1 + for(var/datum/mind/M in department_minds) + var/string = "[M.name] the [M.assigned_role]" + if(!first) + string = ", [M.name] the [M.assigned_role]" + else + first-- + explanation_text += string + + if(command_staff_only) + explanation_text += ", while the real heads are dead. This is a team objective." + else + explanation_text += ", while the real members are dead. This is a team objective." + + +/datum/objective/changeling_team_objective/impersonate_department/check_completion() + if(!department_real_names.len || !department_minds.len) + return 1 //Something fucked up, give them a win + + var/list/check_names = department_real_names.Copy() + + //Check each department member's mind to see if any of them made it to centcomm alive, if they did it's an automatic fail + for(var/datum/mind/M in department_minds) + if(M in ticker.mode.changelings) //Lings aren't picked for this, but let's be safe + continue + + if(M.current) + var/turf/mloc = get_turf(M.current) + if(mloc.onCentcom() && (M.current.stat != DEAD)) + return 0 //A Non-ling living target got to centcomm, fail + + //Check each staff member has been replaced, by cross referencing changeling minds, changeling current dna, the staff minds and their original DNA names + var/success = 0 + changelings: + for(var/datum/mind/changeling in ticker.mode.changelings) + if(success >= department_minds.len) //We did it, stop here! + return 1 + if(ishuman(changeling.current)) + var/mob/living/carbon/human/H = changeling.current + var/turf/cloc = get_turf(changeling.current) + if(cloc && cloc.onCentcom() && (changeling.current.stat != DEAD)) //Living changeling on centcomm.... + for(var/name in check_names) //Is he (disguised as) one of the staff? + if(H.dna.real_name == name) + check_names -= name //This staff member is accounted for, remove them, so the team don't succeed by escape as 7 of the same engineer + success++ //A living changeling staff member made it to centcomm + continue changelings + + if(success >= department_minds.len) + return 1 + return 0 + + + + +//A subtype of impersonate_derpartment +//This subtype always picks as many command staff as it can (HoS,HoP,Cap,CE,CMO,RD) +//and tasks the lings with killing and replacing them +/datum/objective/changeling_team_objective/impersonate_department/impersonate_heads + explanation_text = "Have X or more heads of staff escape on the shuttle disguised as heads, while the real heads are dead" + command_staff_only = TRUE + + + diff --git a/code/game/gamemodes/revolution/revolution.dm b/code/game/gamemodes/revolution/revolution.dm index 43d2b2f6ae7..277027ca37a 100644 --- a/code/game/gamemodes/revolution/revolution.dm +++ b/code/game/gamemodes/revolution/revolution.dm @@ -1,371 +1,371 @@ -// To add a rev to the list of revolutionaries, make sure it's rev (with if(ticker.mode.name == "revolution)), -// then call ticker.mode:add_revolutionary(_THE_PLAYERS_MIND_) -// nothing else needs to be done, as that proc will check if they are a valid target. -// Just make sure the converter is a head before you call it! -// To remove a rev (from brainwashing or w/e), call ticker.mode:remove_revolutionary(_THE_PLAYERS_MIND_), -// this will also check they're not a head, so it can just be called freely -// If the game somtimes isn't registering a win properly, then ticker.mode.check_win() isn't being called somewhere. - -/datum/game_mode - var/list/datum/mind/head_revolutionaries = list() - var/list/datum/mind/revolutionaries = list() - -/datum/game_mode/revolution - name = "revolution" - config_tag = "revolution" - antag_flag = ROLE_REV - restricted_jobs = list("Security Officer", "Warden", "Detective", "AI", "Cyborg","Captain", "Head of Personnel", "Head of Security", "Chief Engineer", "Research Director", "Chief Medical Officer") - required_players = 20 - required_enemies = 1 - recommended_enemies = 3 - enemy_minimum_age = 14 - - var/finished = 0 - var/check_counter = 0 - var/max_headrevs = 3 - var/list/datum/mind/heads_to_kill = list() - -/////////////////////////// -//Announces the game type// -/////////////////////////// -/datum/game_mode/revolution/announce() - world << "The current game mode is - Revolution!" - world << "Some crewmembers are attempting to start a revolution!
\nRevolutionaries - Kill the Captain, HoP, HoS, CE, RD and CMO. Convert other crewmembers (excluding the heads of staff, and security officers) to your cause by flashing them. Protect your leaders.
\nPersonnel - Protect the heads of staff. Kill the leaders of the revolution, and brainwash the other revolutionaries (by beating them in the head).
" - - -/////////////////////////////////////////////////////////////////////////////// -//Gets the round setup, cancelling if there's not enough players at the start// -/////////////////////////////////////////////////////////////////////////////// -/datum/game_mode/revolution/pre_setup() - - if(config.protect_roles_from_antagonist) - restricted_jobs += protected_jobs - - if(config.protect_assistant_from_antagonist) - restricted_jobs += "Assistant" - - for (var/i=1 to max_headrevs) - if (antag_candidates.len==0) - break - var/datum/mind/lenin = pick(antag_candidates) - antag_candidates -= lenin - head_revolutionaries += lenin - lenin.restricted_roles = restricted_jobs - - if(head_revolutionaries.len < required_enemies) - return 0 - - return 1 - - -/datum/game_mode/revolution/post_setup() - var/list/heads = get_living_heads() - var/list/sec = get_living_sec() - var/weighted_score = min(max(round(heads.len - ((8 - sec.len) / 3)),1),max_headrevs) - - while(weighted_score < head_revolutionaries.len) //das vi danya - var/datum/mind/trotsky = pick(head_revolutionaries) - antag_candidates += trotsky - head_revolutionaries -= trotsky - update_rev_icons_removed(trotsky) - - for(var/datum/mind/rev_mind in head_revolutionaries) - log_game("[rev_mind.key] (ckey) has been selected as a head rev") - for(var/datum/mind/head_mind in heads) - mark_for_death(rev_mind, head_mind) - - spawn(rand(10,100)) - // equip_traitor(rev_mind.current, 1) //changing how revs get assigned their uplink so they can get PDA uplinks. --NEO - // Removing revolutionary uplinks. -Pete - equip_revolutionary(rev_mind.current) - - for(var/datum/mind/rev_mind in head_revolutionaries) - greet_revolutionary(rev_mind) - modePlayer += head_revolutionaries - SSshuttle.emergencyNoEscape = 1 - ..() - - -/datum/game_mode/revolution/process() - check_counter++ - if(check_counter >= 5) - if(!finished) - check_heads() - ticker.mode.check_win() - check_counter = 0 - return 0 - - -/datum/game_mode/proc/forge_revolutionary_objectives(datum/mind/rev_mind) - var/list/heads = get_living_heads() - for(var/datum/mind/head_mind in heads) - var/datum/objective/mutiny/rev_obj = new - rev_obj.owner = rev_mind - rev_obj.target = head_mind - rev_obj.explanation_text = "Assassinate or exile [head_mind.name], the [head_mind.assigned_role]." - rev_mind.objectives += rev_obj - -/datum/game_mode/proc/greet_revolutionary(datum/mind/rev_mind, you_are=1) - var/obj_count = 1 - update_rev_icons_added(rev_mind) - if (you_are) - rev_mind.current << "You are a member of the revolutionaries' leadership!" - for(var/datum/objective/objective in rev_mind.objectives) - rev_mind.current << "Objective #[obj_count]: [objective.explanation_text]" - rev_mind.special_role = "Head Revolutionary" - obj_count++ - -///////////////////////////////////////////////////////////////////////////////// -//This are equips the rev heads with their gear, and makes the clown not clumsy// -///////////////////////////////////////////////////////////////////////////////// -/datum/game_mode/proc/equip_revolutionary(mob/living/carbon/human/mob) - if(!istype(mob)) - return - - if (mob.mind) - if (mob.mind.assigned_role == "Clown") - mob << "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself." - mob.dna.remove_mutation(CLOWNMUT) - - - var/obj/item/device/assembly/flash/T = new(mob) - var/obj/item/toy/crayon/spraycan/R = new(mob) - var/obj/item/clothing/glasses/hud/security/chameleon/C = new(mob) - - var/list/slots = list ( - "backpack" = slot_in_backpack, - "left pocket" = slot_l_store, - "right pocket" = slot_r_store, - "left hand" = slot_l_hand, - "right hand" = slot_r_hand, - ) - var/where = mob.equip_in_one_of_slots(T, slots) - var/where2 = mob.equip_in_one_of_slots(C, slots) - mob.equip_in_one_of_slots(R,slots) - - mob.update_icons() - - if (!where2) - mob << "The Syndicate were unfortunately unable to get you a chameleon security HUD." - else - mob << "The chameleon security HUD in your [where2] will help you keep track of who is loyalty-implanted, and unable to be recruited." - - if (!where) - mob << "The Syndicate were unfortunately unable to get you a flash." - else - mob << "The flash in your [where] will help you to persuade the crew to join your cause." - return 1 - -///////////////////////////////// -//Gives head revs their targets// -///////////////////////////////// -/datum/game_mode/revolution/proc/mark_for_death(datum/mind/rev_mind, datum/mind/head_mind) - var/datum/objective/mutiny/rev_obj = new - rev_obj.owner = rev_mind - rev_obj.target = head_mind - rev_obj.explanation_text = "Assassinate [head_mind.name], the [head_mind.assigned_role]." - rev_mind.objectives += rev_obj - heads_to_kill += head_mind - -//////////////////////////////////////////// -//Checks if new heads have joined midround// -//////////////////////////////////////////// -/datum/game_mode/revolution/proc/check_heads() - var/list/heads = get_all_heads() - var/list/sec = get_all_sec() - if(heads_to_kill.len < heads.len) - var/list/new_heads = heads - heads_to_kill - for(var/datum/mind/head_mind in new_heads) - for(var/datum/mind/rev_mind in head_revolutionaries) - mark_for_death(rev_mind, head_mind) - - if(head_revolutionaries.len < max_headrevs && head_revolutionaries.len < round(heads.len - ((8 - sec.len) / 3))) - latejoin_headrev() - -/////////////////////////////// -//Adds a new headrev midround// -/////////////////////////////// -/datum/game_mode/revolution/proc/latejoin_headrev() - if(revolutionaries) //Head Revs are not in this list - var/list/promotable_revs = list() - for(var/datum/mind/khrushchev in revolutionaries) - if(khrushchev.current && khrushchev.current.client && khrushchev.current.stat != DEAD) - if(ROLE_REV in khrushchev.current.client.prefs.be_special) - promotable_revs += khrushchev - if(promotable_revs.len) - var/datum/mind/stalin = pick(promotable_revs) - revolutionaries -= stalin - head_revolutionaries += stalin - log_game("[stalin.key] (ckey) has been promoted to a head rev") - equip_revolutionary(stalin.current) - forge_revolutionary_objectives(stalin) - greet_revolutionary(stalin) - -////////////////////////////////////// -//Checks if the revs have won or not// -////////////////////////////////////// -/datum/game_mode/revolution/check_win() - if(check_rev_victory()) - finished = 1 - else if(check_heads_victory()) - finished = 2 - return - -/////////////////////////////// -//Checks if the round is over// -/////////////////////////////// -/datum/game_mode/revolution/check_finished() - if(config.continuous["revolution"]) - if(finished != 0) - SSshuttle.emergencyNoEscape = 0 - if(SSshuttle.emergency.mode == SHUTTLE_STRANDED) - SSshuttle.emergency.mode = SHUTTLE_DOCKED - SSshuttle.emergency.timer = world.time - priority_announce("Hostile enviroment resolved. You have 3 minutes to board the Emergency Shuttle.", null, 'sound/AI/shuttledock.ogg', "Priority") - return ..() - if(finished != 0) - return 1 - else - return ..() - -/////////////////////////////////////////////////// -//Deals with converting players to the revolution// -/////////////////////////////////////////////////// -/datum/game_mode/proc/add_revolutionary(datum/mind/rev_mind) - if(rev_mind.assigned_role in command_positions) - return 0 - var/mob/living/carbon/human/H = rev_mind.current//Check to see if the potential rev is implanted - if(isloyal(H)) - return 0 - if((rev_mind in revolutionaries) || (rev_mind in head_revolutionaries)) - return 0 - revolutionaries += rev_mind - if(iscarbon(rev_mind.current)) - var/mob/living/carbon/carbon_mob = rev_mind.current - carbon_mob.silent = max(carbon_mob.silent, 5) - carbon_mob.flash_eyes(1, 1) - rev_mind.current.Stun(5) - rev_mind.current << " You are now a revolutionary! Help your cause. Do not harm your fellow freedom fighters. You can identify your comrades by the red \"R\" icons, and your leaders by the blue \"R\" icons. Help them kill the heads to win the revolution!" - rev_mind.current.attack_log += "\[[time_stamp()]\] Has been converted to the revolution!" - rev_mind.special_role = "Revolutionary" - update_rev_icons_added(rev_mind) - if(jobban_isbanned(rev_mind.current, ROLE_REV)) - replace_jobbaned_player(rev_mind.current, ROLE_REV, ROLE_REV) - return 1 -////////////////////////////////////////////////////////////////////////////// -//Deals with players being converted from the revolution (Not a rev anymore)// // Modified to handle borged MMIs. Accepts another var if the target is being borged at the time -- Polymorph. -////////////////////////////////////////////////////////////////////////////// -/datum/game_mode/proc/remove_revolutionary(datum/mind/rev_mind , beingborged) - var/remove_head = 0 - if(beingborged && (rev_mind in head_revolutionaries)) - head_revolutionaries -= rev_mind - remove_head = 1 - - if((rev_mind in revolutionaries) || remove_head) - revolutionaries -= rev_mind - rev_mind.special_role = null - rev_mind.current.attack_log += "\[[time_stamp()]\] Has renounced the revolution!" - - if(beingborged) - rev_mind.current << "The frame's firmware detects and deletes your neural reprogramming! You remember nothing[remove_head ? "." : " but the name of the one who flashed you."]" - message_admins("[key_name_admin(rev_mind.current)] ? (FLW) has been borged while being a [remove_head ? "leader" : " member"] of the revolution.") - - else - rev_mind.current.Paralyse(5) - rev_mind.current << "You have been brainwashed! You are no longer a revolutionary! Your memory is hazy from the time you were a rebel...the only thing you remember is the name of the one who brainwashed you..." - - update_rev_icons_removed(rev_mind) - for(var/mob/living/M in view(rev_mind.current)) - if(beingborged) - M << "The frame beeps contentedly, purging the hostile memory engram from the MMI before initalizing it." - - else - M << "[rev_mind.current] looks like they just remembered their real allegiance!" - -///////////////////////////////////// -//Adds the rev hud to a new convert// -///////////////////////////////////// -/datum/game_mode/proc/update_rev_icons_added(datum/mind/rev_mind) - var/datum/atom_hud/antag/revhud = huds[ANTAG_HUD_REV] - revhud.join_hud(rev_mind.current) - set_antag_hud(rev_mind.current, ((rev_mind in head_revolutionaries) ? "rev_head" : "rev")) - -///////////////////////////////////////// -//Removes the hud from deconverted revs// -///////////////////////////////////////// -/datum/game_mode/proc/update_rev_icons_removed(datum/mind/rev_mind) - var/datum/atom_hud/antag/revhud = huds[ANTAG_HUD_REV] - revhud.leave_hud(rev_mind.current) - set_antag_hud(rev_mind.current, null) - -////////////////////////// -//Checks for rev victory// -////////////////////////// -/datum/game_mode/revolution/proc/check_rev_victory() - for(var/datum/mind/rev_mind in head_revolutionaries) - for(var/datum/objective/mutiny/objective in rev_mind.objectives) - if(!(objective.check_completion())) - return 0 - - return 1 - -///////////////////////////// -//Checks for a head victory// -///////////////////////////// -/datum/game_mode/revolution/proc/check_heads_victory() - for(var/datum/mind/rev_mind in head_revolutionaries) - var/turf/T = get_turf(rev_mind.current) - if((rev_mind) && (rev_mind.current) && (rev_mind.current.stat != 2) && rev_mind.current.client && T && (T.z == ZLEVEL_STATION)) - if(ishuman(rev_mind.current)) - return 0 - return 1 - -////////////////////////////////////////////////////////////////////// -//Announces the end of the game with all relavent information stated// -////////////////////////////////////////////////////////////////////// -/datum/game_mode/revolution/declare_completion() - if(finished == 1) - feedback_set_details("round_end_result","win - heads killed") - world << "The heads of staff were killed or exiled! The revolutionaries win!" - else if(finished == 2) - feedback_set_details("round_end_result","loss - rev heads killed") - world << "The heads of staff managed to stop the revolution!" - ..() - return 1 - -/datum/game_mode/proc/auto_declare_completion_revolution() - var/list/targets = list() - if(head_revolutionaries.len || istype(ticker.mode,/datum/game_mode/revolution)) - var/num_revs = 0 - var/num_survivors = 0 - for(var/mob/living/carbon/survivor in living_mob_list) - if(survivor.ckey) - num_survivors++ - if(survivor.mind) - if((survivor.mind in head_revolutionaries) || (survivor.mind in revolutionaries)) - num_revs++ - if(num_survivors) - world << "[TAB]Command's Approval Rating: [100 - round((num_revs/num_survivors)*100, 0.1)]%" // % of loyal crew - var/text = "
The head revolutionaries were:" - for(var/datum/mind/headrev in head_revolutionaries) - text += printplayer(headrev, 1) - text += "
" - world << text - - if(revolutionaries.len || istype(ticker.mode,/datum/game_mode/revolution)) - var/text = "
The revolutionaries were:" - for(var/datum/mind/rev in revolutionaries) - text += printplayer(rev, 1) - text += "
" - world << text - - if( head_revolutionaries.len || revolutionaries.len || istype(ticker.mode,/datum/game_mode/revolution) ) - var/text = "
The heads of staff were:" - var/list/heads = get_all_heads() - for(var/datum/mind/head in heads) - var/target = (head in targets) - if(target) - text += "Target" - text += printplayer(head, 1) - text += "
" - world << text +// To add a rev to the list of revolutionaries, make sure it's rev (with if(ticker.mode.name == "revolution)), +// then call ticker.mode:add_revolutionary(_THE_PLAYERS_MIND_) +// nothing else needs to be done, as that proc will check if they are a valid target. +// Just make sure the converter is a head before you call it! +// To remove a rev (from brainwashing or w/e), call ticker.mode:remove_revolutionary(_THE_PLAYERS_MIND_), +// this will also check they're not a head, so it can just be called freely +// If the game somtimes isn't registering a win properly, then ticker.mode.check_win() isn't being called somewhere. + +/datum/game_mode + var/list/datum/mind/head_revolutionaries = list() + var/list/datum/mind/revolutionaries = list() + +/datum/game_mode/revolution + name = "revolution" + config_tag = "revolution" + antag_flag = ROLE_REV + restricted_jobs = list("Security Officer", "Warden", "Detective", "AI", "Cyborg","Captain", "Head of Personnel", "Head of Security", "Chief Engineer", "Research Director", "Chief Medical Officer") + required_players = 20 + required_enemies = 1 + recommended_enemies = 3 + enemy_minimum_age = 14 + + var/finished = 0 + var/check_counter = 0 + var/max_headrevs = 3 + var/list/datum/mind/heads_to_kill = list() + +/////////////////////////// +//Announces the game type// +/////////////////////////// +/datum/game_mode/revolution/announce() + world << "The current game mode is - Revolution!" + world << "Some crewmembers are attempting to start a revolution!
\nRevolutionaries - Kill the Captain, HoP, HoS, CE, RD and CMO. Convert other crewmembers (excluding the heads of staff, and security officers) to your cause by flashing them. Protect your leaders.
\nPersonnel - Protect the heads of staff. Kill the leaders of the revolution, and brainwash the other revolutionaries (by beating them in the head).
" + + +/////////////////////////////////////////////////////////////////////////////// +//Gets the round setup, cancelling if there's not enough players at the start// +/////////////////////////////////////////////////////////////////////////////// +/datum/game_mode/revolution/pre_setup() + + if(config.protect_roles_from_antagonist) + restricted_jobs += protected_jobs + + if(config.protect_assistant_from_antagonist) + restricted_jobs += "Assistant" + + for (var/i=1 to max_headrevs) + if (antag_candidates.len==0) + break + var/datum/mind/lenin = pick(antag_candidates) + antag_candidates -= lenin + head_revolutionaries += lenin + lenin.restricted_roles = restricted_jobs + + if(head_revolutionaries.len < required_enemies) + return 0 + + return 1 + + +/datum/game_mode/revolution/post_setup() + var/list/heads = get_living_heads() + var/list/sec = get_living_sec() + var/weighted_score = min(max(round(heads.len - ((8 - sec.len) / 3)),1),max_headrevs) + + while(weighted_score < head_revolutionaries.len) //das vi danya + var/datum/mind/trotsky = pick(head_revolutionaries) + antag_candidates += trotsky + head_revolutionaries -= trotsky + update_rev_icons_removed(trotsky) + + for(var/datum/mind/rev_mind in head_revolutionaries) + log_game("[rev_mind.key] (ckey) has been selected as a head rev") + for(var/datum/mind/head_mind in heads) + mark_for_death(rev_mind, head_mind) + + spawn(rand(10,100)) + // equip_traitor(rev_mind.current, 1) //changing how revs get assigned their uplink so they can get PDA uplinks. --NEO + // Removing revolutionary uplinks. -Pete + equip_revolutionary(rev_mind.current) + + for(var/datum/mind/rev_mind in head_revolutionaries) + greet_revolutionary(rev_mind) + modePlayer += head_revolutionaries + SSshuttle.emergencyNoEscape = 1 + ..() + + +/datum/game_mode/revolution/process() + check_counter++ + if(check_counter >= 5) + if(!finished) + check_heads() + ticker.mode.check_win() + check_counter = 0 + return 0 + + +/datum/game_mode/proc/forge_revolutionary_objectives(datum/mind/rev_mind) + var/list/heads = get_living_heads() + for(var/datum/mind/head_mind in heads) + var/datum/objective/mutiny/rev_obj = new + rev_obj.owner = rev_mind + rev_obj.target = head_mind + rev_obj.explanation_text = "Assassinate or exile [head_mind.name], the [head_mind.assigned_role]." + rev_mind.objectives += rev_obj + +/datum/game_mode/proc/greet_revolutionary(datum/mind/rev_mind, you_are=1) + var/obj_count = 1 + update_rev_icons_added(rev_mind) + if (you_are) + rev_mind.current << "You are a member of the revolutionaries' leadership!" + for(var/datum/objective/objective in rev_mind.objectives) + rev_mind.current << "Objective #[obj_count]: [objective.explanation_text]" + rev_mind.special_role = "Head Revolutionary" + obj_count++ + +///////////////////////////////////////////////////////////////////////////////// +//This are equips the rev heads with their gear, and makes the clown not clumsy// +///////////////////////////////////////////////////////////////////////////////// +/datum/game_mode/proc/equip_revolutionary(mob/living/carbon/human/mob) + if(!istype(mob)) + return + + if (mob.mind) + if (mob.mind.assigned_role == "Clown") + mob << "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself." + mob.dna.remove_mutation(CLOWNMUT) + + + var/obj/item/device/assembly/flash/T = new(mob) + var/obj/item/toy/crayon/spraycan/R = new(mob) + var/obj/item/clothing/glasses/hud/security/chameleon/C = new(mob) + + var/list/slots = list ( + "backpack" = slot_in_backpack, + "left pocket" = slot_l_store, + "right pocket" = slot_r_store, + "left hand" = slot_l_hand, + "right hand" = slot_r_hand, + ) + var/where = mob.equip_in_one_of_slots(T, slots) + var/where2 = mob.equip_in_one_of_slots(C, slots) + mob.equip_in_one_of_slots(R,slots) + + mob.update_icons() + + if (!where2) + mob << "The Syndicate were unfortunately unable to get you a chameleon security HUD." + else + mob << "The chameleon security HUD in your [where2] will help you keep track of who is loyalty-implanted, and unable to be recruited." + + if (!where) + mob << "The Syndicate were unfortunately unable to get you a flash." + else + mob << "The flash in your [where] will help you to persuade the crew to join your cause." + return 1 + +///////////////////////////////// +//Gives head revs their targets// +///////////////////////////////// +/datum/game_mode/revolution/proc/mark_for_death(datum/mind/rev_mind, datum/mind/head_mind) + var/datum/objective/mutiny/rev_obj = new + rev_obj.owner = rev_mind + rev_obj.target = head_mind + rev_obj.explanation_text = "Assassinate [head_mind.name], the [head_mind.assigned_role]." + rev_mind.objectives += rev_obj + heads_to_kill += head_mind + +//////////////////////////////////////////// +//Checks if new heads have joined midround// +//////////////////////////////////////////// +/datum/game_mode/revolution/proc/check_heads() + var/list/heads = get_all_heads() + var/list/sec = get_all_sec() + if(heads_to_kill.len < heads.len) + var/list/new_heads = heads - heads_to_kill + for(var/datum/mind/head_mind in new_heads) + for(var/datum/mind/rev_mind in head_revolutionaries) + mark_for_death(rev_mind, head_mind) + + if(head_revolutionaries.len < max_headrevs && head_revolutionaries.len < round(heads.len - ((8 - sec.len) / 3))) + latejoin_headrev() + +/////////////////////////////// +//Adds a new headrev midround// +/////////////////////////////// +/datum/game_mode/revolution/proc/latejoin_headrev() + if(revolutionaries) //Head Revs are not in this list + var/list/promotable_revs = list() + for(var/datum/mind/khrushchev in revolutionaries) + if(khrushchev.current && khrushchev.current.client && khrushchev.current.stat != DEAD) + if(ROLE_REV in khrushchev.current.client.prefs.be_special) + promotable_revs += khrushchev + if(promotable_revs.len) + var/datum/mind/stalin = pick(promotable_revs) + revolutionaries -= stalin + head_revolutionaries += stalin + log_game("[stalin.key] (ckey) has been promoted to a head rev") + equip_revolutionary(stalin.current) + forge_revolutionary_objectives(stalin) + greet_revolutionary(stalin) + +////////////////////////////////////// +//Checks if the revs have won or not// +////////////////////////////////////// +/datum/game_mode/revolution/check_win() + if(check_rev_victory()) + finished = 1 + else if(check_heads_victory()) + finished = 2 + return + +/////////////////////////////// +//Checks if the round is over// +/////////////////////////////// +/datum/game_mode/revolution/check_finished() + if(config.continuous["revolution"]) + if(finished != 0) + SSshuttle.emergencyNoEscape = 0 + if(SSshuttle.emergency.mode == SHUTTLE_STRANDED) + SSshuttle.emergency.mode = SHUTTLE_DOCKED + SSshuttle.emergency.timer = world.time + priority_announce("Hostile enviroment resolved. You have 3 minutes to board the Emergency Shuttle.", null, 'sound/AI/shuttledock.ogg', "Priority") + return ..() + if(finished != 0) + return 1 + else + return ..() + +/////////////////////////////////////////////////// +//Deals with converting players to the revolution// +/////////////////////////////////////////////////// +/datum/game_mode/proc/add_revolutionary(datum/mind/rev_mind) + if(rev_mind.assigned_role in command_positions) + return 0 + var/mob/living/carbon/human/H = rev_mind.current//Check to see if the potential rev is implanted + if(isloyal(H)) + return 0 + if((rev_mind in revolutionaries) || (rev_mind in head_revolutionaries)) + return 0 + revolutionaries += rev_mind + if(iscarbon(rev_mind.current)) + var/mob/living/carbon/carbon_mob = rev_mind.current + carbon_mob.silent = max(carbon_mob.silent, 5) + carbon_mob.flash_eyes(1, 1) + rev_mind.current.Stun(5) + rev_mind.current << " You are now a revolutionary! Help your cause. Do not harm your fellow freedom fighters. You can identify your comrades by the red \"R\" icons, and your leaders by the blue \"R\" icons. Help them kill the heads to win the revolution!" + rev_mind.current.attack_log += "\[[time_stamp()]\] Has been converted to the revolution!" + rev_mind.special_role = "Revolutionary" + update_rev_icons_added(rev_mind) + if(jobban_isbanned(rev_mind.current, ROLE_REV)) + replace_jobbaned_player(rev_mind.current, ROLE_REV, ROLE_REV) + return 1 +////////////////////////////////////////////////////////////////////////////// +//Deals with players being converted from the revolution (Not a rev anymore)// // Modified to handle borged MMIs. Accepts another var if the target is being borged at the time -- Polymorph. +////////////////////////////////////////////////////////////////////////////// +/datum/game_mode/proc/remove_revolutionary(datum/mind/rev_mind , beingborged) + var/remove_head = 0 + if(beingborged && (rev_mind in head_revolutionaries)) + head_revolutionaries -= rev_mind + remove_head = 1 + + if((rev_mind in revolutionaries) || remove_head) + revolutionaries -= rev_mind + rev_mind.special_role = null + rev_mind.current.attack_log += "\[[time_stamp()]\] Has renounced the revolution!" + + if(beingborged) + rev_mind.current << "The frame's firmware detects and deletes your neural reprogramming! You remember nothing[remove_head ? "." : " but the name of the one who flashed you."]" + message_admins("[key_name_admin(rev_mind.current)] ? (FLW) has been borged while being a [remove_head ? "leader" : " member"] of the revolution.") + + else + rev_mind.current.Paralyse(5) + rev_mind.current << "You have been brainwashed! You are no longer a revolutionary! Your memory is hazy from the time you were a rebel...the only thing you remember is the name of the one who brainwashed you..." + + update_rev_icons_removed(rev_mind) + for(var/mob/living/M in view(rev_mind.current)) + if(beingborged) + M << "The frame beeps contentedly, purging the hostile memory engram from the MMI before initalizing it." + + else + M << "[rev_mind.current] looks like they just remembered their real allegiance!" + +///////////////////////////////////// +//Adds the rev hud to a new convert// +///////////////////////////////////// +/datum/game_mode/proc/update_rev_icons_added(datum/mind/rev_mind) + var/datum/atom_hud/antag/revhud = huds[ANTAG_HUD_REV] + revhud.join_hud(rev_mind.current) + set_antag_hud(rev_mind.current, ((rev_mind in head_revolutionaries) ? "rev_head" : "rev")) + +///////////////////////////////////////// +//Removes the hud from deconverted revs// +///////////////////////////////////////// +/datum/game_mode/proc/update_rev_icons_removed(datum/mind/rev_mind) + var/datum/atom_hud/antag/revhud = huds[ANTAG_HUD_REV] + revhud.leave_hud(rev_mind.current) + set_antag_hud(rev_mind.current, null) + +////////////////////////// +//Checks for rev victory// +////////////////////////// +/datum/game_mode/revolution/proc/check_rev_victory() + for(var/datum/mind/rev_mind in head_revolutionaries) + for(var/datum/objective/mutiny/objective in rev_mind.objectives) + if(!(objective.check_completion())) + return 0 + + return 1 + +///////////////////////////// +//Checks for a head victory// +///////////////////////////// +/datum/game_mode/revolution/proc/check_heads_victory() + for(var/datum/mind/rev_mind in head_revolutionaries) + var/turf/T = get_turf(rev_mind.current) + if((rev_mind) && (rev_mind.current) && (rev_mind.current.stat != 2) && rev_mind.current.client && T && (T.z == ZLEVEL_STATION)) + if(ishuman(rev_mind.current)) + return 0 + return 1 + +////////////////////////////////////////////////////////////////////// +//Announces the end of the game with all relavent information stated// +////////////////////////////////////////////////////////////////////// +/datum/game_mode/revolution/declare_completion() + if(finished == 1) + feedback_set_details("round_end_result","win - heads killed") + world << "The heads of staff were killed or exiled! The revolutionaries win!" + else if(finished == 2) + feedback_set_details("round_end_result","loss - rev heads killed") + world << "The heads of staff managed to stop the revolution!" + ..() + return 1 + +/datum/game_mode/proc/auto_declare_completion_revolution() + var/list/targets = list() + if(head_revolutionaries.len || istype(ticker.mode,/datum/game_mode/revolution)) + var/num_revs = 0 + var/num_survivors = 0 + for(var/mob/living/carbon/survivor in living_mob_list) + if(survivor.ckey) + num_survivors++ + if(survivor.mind) + if((survivor.mind in head_revolutionaries) || (survivor.mind in revolutionaries)) + num_revs++ + if(num_survivors) + world << "[TAB]Command's Approval Rating: [100 - round((num_revs/num_survivors)*100, 0.1)]%" // % of loyal crew + var/text = "
The head revolutionaries were:" + for(var/datum/mind/headrev in head_revolutionaries) + text += printplayer(headrev, 1) + text += "
" + world << text + + if(revolutionaries.len || istype(ticker.mode,/datum/game_mode/revolution)) + var/text = "
The revolutionaries were:" + for(var/datum/mind/rev in revolutionaries) + text += printplayer(rev, 1) + text += "
" + world << text + + if( head_revolutionaries.len || revolutionaries.len || istype(ticker.mode,/datum/game_mode/revolution) ) + var/text = "
The heads of staff were:" + var/list/heads = get_all_heads() + for(var/datum/mind/head in heads) + var/target = (head in targets) + if(target) + text += "Target" + text += printplayer(head, 1) + text += "
" + world << text diff --git a/code/game/gamemodes/wizard/soulstone.dm b/code/game/gamemodes/wizard/soulstone.dm index d6fd311404b..4217fcc7bf8 100644 --- a/code/game/gamemodes/wizard/soulstone.dm +++ b/code/game/gamemodes/wizard/soulstone.dm @@ -1,274 +1,274 @@ -/obj/item/device/soulstone - name = "soulstone shard" - icon = 'icons/obj/wizard.dmi' - icon_state = "soulstone" - item_state = "electronic" - desc = "A fragment of the legendary treasure known simply as the 'Soul Stone'. The shard still flickers with a fraction of the full artefacts power." - w_class = 1 - slot_flags = SLOT_BELT - origin_tech = "bluespace=4;materials=4" - var/imprinted = "empty" - var/usability = 0 - -/obj/item/device/soulstone/anybody - usability = 1 - -/obj/item/device/soulstone/pickup(mob/living/user) - if(!iscultist(user) && !iswizard(user) && !usability) - user << "An overwhelming feeling of dread comes over you as you pick up the soulstone. It would be wise to be rid of this quickly." - user.Dizzy(120) - -//////////////////////////////Capturing//////////////////////////////////////////////////////// - -/obj/item/device/soulstone/attack(mob/living/carbon/human/M, mob/user) - if(!iscultist(user) && !iswizard(user) && !usability) - user.Paralyse(5) - user << "Your body is wracked with debilitating pain!" - return - if(!istype(M, /mob/living/carbon/human))//If target is not a human. - return ..() - if(istype(M, /mob/living/carbon/human/dummy)) - return ..() - add_logs(user, M, "captured [M.name]'s soul", src) - - if(iscultist(user) && M && M.mind) - new /obj/item/summoning_orb(get_turf(M)) - - transfer_soul("VICTIM", M, user) - return - -///////////////////Options for using captured souls/////////////////////////////////////// - -/obj/item/device/soulstone/attack_self(mob/user) - if (!in_range(src, user)) - return - if(!iscultist(user) && !iswizard(user) && !usability) - user.Paralyse(5) - user << "Your body is wracked with debilitating pain!" - return - user.set_machine(src) - var/dat = "Soul Stone
" - for(var/mob/living/simple_animal/shade/A in src) - dat += "Captured Soul: [A.name]
" - dat += {"Summon Shade"} - dat += "
" - dat += {"Close"} - user << browse(dat, "window=aicard") - onclose(user, "aicard") - return - - -/obj/item/device/soulstone/Topic(href, href_list) - var/mob/U = usr - if (!in_range(src, U)||U.machine!=src) - U << browse(null, "window=aicard") - U.unset_machine() - return - - add_fingerprint(U) - U.set_machine(src) - - switch(href_list["choice"])//Now we switch based on choice. - if ("Close") - U << browse(null, "window=aicard") - U.unset_machine() - return - - if ("Summon") - for(var/mob/living/simple_animal/shade/A in src) - A.status_flags &= ~GODMODE - A.canmove = 1 - A.loc = U.loc - A.cancel_camera() - src.icon_state = "soulstone" - if(iswizard(U) || usability) - A << "You have been released from your prison, but you are still bound to [U.name]'s will. Help them suceed in their goals at all costs." - else if(iscultist(U)) - A << "You have been released from your prison, but you are still bound to the cult's will. Help them suceed in their goals at all costs." - - attack_self(U) - -///////////////////////////Transferring to constructs///////////////////////////////////////////////////// -/obj/structure/constructshell - name = "empty shell" - icon = 'icons/obj/wizard.dmi' - icon_state = "construct" - desc = "A wicked machine used by those skilled in magical arts. It is inactive" - -/obj/structure/constructshell/attackby(obj/item/O, mob/user, params) - if(istype(O, /obj/item/device/soulstone)) - var/obj/item/device/soulstone/SS = O - SS.transfer_soul("CONSTRUCT",src,user) - - -////////////////////////////Proc for moving soul in and out off stone////////////////////////////////////// - - -/obj/item/device/soulstone/proc/transfer_soul(choice as text, target, mob/user). - switch(choice) - if("FORCE") - if(!iscarbon(target)) //TODO: Add sacrifice stoning for non-organics, just because you have no body doesnt mean you dont have a soul - return 0 - if(contents.len) - return 0 - var/mob/living/carbon/T = target - if(T.client != null) - for(var/obj/item/W in T) - T.unEquip(W) - init_shade(src, T, user) - return 1 - else - user << "Capture failed!: The soul has already fled it's mortal frame. You attempt to bring it back..." - return getCultGhost(src,T,user) - - if("VICTIM") - var/mob/living/carbon/human/T = target - if(imprinted != "empty") - user << "Capture failed!: The soul stone has already been imprinted with [imprinted]'s mind!" - else - if (T.stat == 0) - user << "Capture failed!: Kill or maim the victim first!" - else - if(T.client == null) - user << "Capture failed!: The soul has already fled it's mortal frame. You attempt to bring it back..." - getCultGhost(src,T,user) - else - if(contents.len) - user << "Capture failed!: The soul stone is full! Use or free an existing soul to make room." - else - for(var/obj/item/W in T) - T.unEquip(W) - init_shade(src, T, user, vic = 1) - qdel(T) - if("SHADE") - var/mob/living/simple_animal/shade/T = target - if (T.stat == DEAD) - user << "Capture failed!: The shade has already been banished!" - else - if(contents.len) - user << "Capture failed!: The soul stone is full! Use or free an existing soul to make room." - else - if(T.name != imprinted) - user << "Capture failed!: The soul stone has already been imprinted with [imprinted]'s mind!" - else - T.loc = src //put shade in stone - T.status_flags |= GODMODE - T.canmove = 0 - T.health = T.maxHealth - icon_state = "soulstone2" - T << "Your soul has been recaptured by the soul stone, its arcane energies are reknitting your ethereal form" - if(user != T) - user << "Capture successful!: [T.name]'s has been recaptured and stored within the soul stone." - if("CONSTRUCT") - var/obj/structure/constructshell/T = target - var/mob/living/simple_animal/shade/A = locate() in src - if(A) - var/construct_class = alert(user, "Please choose which type of construct you wish to create.",,"Juggernaut","Wraith","Artificer") - if(!T || !T.loc) - return - switch(construct_class) - if("Juggernaut") - makeNewConstruct(/mob/living/simple_animal/hostile/construct/armored, A, user, 0, T.loc) - - if("Wraith") - makeNewConstruct(/mob/living/simple_animal/hostile/construct/wraith, A, user, 0, T.loc) - - if("Artificer") - makeNewConstruct(/mob/living/simple_animal/hostile/construct/builder, A, user, 0, T.loc) - - qdel(T) - qdel(src) - else - user << "Creation failed!: The soul stone is empty! Go kill someone!" - return - - -/proc/makeNewConstruct(mob/living/simple_animal/hostile/construct/ctype, mob/target, mob/stoner = null, cultoverride = 0, loc_override = null) - var/mob/living/simple_animal/hostile/construct/newstruct = new ctype((loc_override) ? (loc_override) : (get_turf(target))) - newstruct.faction |= "\ref[stoner]" - newstruct.key = target.key - if(stoner && iscultist(stoner) || cultoverride) - if(ticker.mode.name == "cult") - ticker.mode:add_cultist(newstruct.mind) - else - ticker.mode.cult+=newstruct.mind - ticker.mode.update_cult_icons_added(newstruct.mind) - newstruct << newstruct.playstyle_string - if(stoner && iswizard(stoner)) - newstruct << "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs." - else if(stoner && iscultist(stoner)) - newstruct << "You are still bound to serve the cult, follow their orders and help them summon the Geometer at all costs." - else newstruct << "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs." - newstruct.cancel_camera() - - -/obj/item/device/soulstone/proc/init_shade(obj/item/device/soulstone/C, mob/living/carbon/human/T, mob/U, vic = 0) - new /obj/effect/decal/remains/human(T.loc) //Spawns a skeleton - T.invisibility = 101 - var/atom/movable/overlay/animation = new /atom/movable/overlay( T.loc ) - animation.icon_state = "blank" - animation.icon = 'icons/mob/mob.dmi' - animation.master = T - flick("dust-h", animation) - qdel(animation) - var/mob/living/simple_animal/shade/S = new /mob/living/simple_animal/shade( T.loc ) - S.loc = C //put shade in stone - S.status_flags |= GODMODE //So they won't die inside the stone somehow - S.canmove = 0//Can't move out of the soul stone - S.name = "Shade of [T.real_name]" - S.real_name = "Shade of [T.real_name]" - S.key = T.key - S.faction |= "\ref[U]" //Add the master as a faction, allowing inter-mob cooperation - if(iscultist(U)) - ticker.mode.add_cultist(S.mind,2) - S.cancel_camera() - C.icon_state = "soulstone2" - C.name = "Soul Stone: [S.real_name]" - if(iswizard(U) || usability) - S << "Your soul has been captured! You are now bound to [U.name]'s will, help them suceed in their goals at all costs." - else if(iscultist(U)) - S << "Your soul has been captured! You are now bound to the cult's will, help them suceed in their goals at all costs." - C.imprinted = "[S.name]" - if(vic) - U << "Capture successful!: [T.real_name]'s soul has been ripped from their body and stored within the soul stone." - U << "The soulstone has been imprinted with [S.real_name]'s mind, it will no longer react to other souls." - - -/obj/item/device/soulstone/proc/getCultGhost(obj/item/device/soulstone/C, mob/living/carbon/human/T, mob/U) - var/list/candidates = get_candidates(ROLE_CULTIST) - - shuffle(candidates) - - var/time_passed = world.time - var/list/consenting_candidates = list() - - for(var/candidate in candidates) - - spawn(0) - switch(alert(candidate, "Would you like to play as a Shade? Please choose quickly!","Confirmation","Yes","No")) - if("Yes") - if((world.time-time_passed)>=50 || !src) - return - consenting_candidates += candidate - - sleep(50) - - if(!T) //target mob got soulstoned or gibbed during sleep(50) - return 0 - listclearnulls(consenting_candidates) //some candidates might have left during sleep(50) - - if(consenting_candidates.len) - var/client/ghost = null - ghost = pick(consenting_candidates) - if(C.contents.len) //If they used the soulstone on someone else in the meantime - return 0 - if(!T.client) //If the original returns in the alloted time - T.client = ghost - for(var/obj/item/W in T) - T.unEquip(W) - init_shade(C, T, U) - qdel(T) - return 1 - else - U << "The ghost has fled beyond your grasp." - return 0 +/obj/item/device/soulstone + name = "soulstone shard" + icon = 'icons/obj/wizard.dmi' + icon_state = "soulstone" + item_state = "electronic" + desc = "A fragment of the legendary treasure known simply as the 'Soul Stone'. The shard still flickers with a fraction of the full artefacts power." + w_class = 1 + slot_flags = SLOT_BELT + origin_tech = "bluespace=4;materials=4" + var/imprinted = "empty" + var/usability = 0 + +/obj/item/device/soulstone/anybody + usability = 1 + +/obj/item/device/soulstone/pickup(mob/living/user) + if(!iscultist(user) && !iswizard(user) && !usability) + user << "An overwhelming feeling of dread comes over you as you pick up the soulstone. It would be wise to be rid of this quickly." + user.Dizzy(120) + +//////////////////////////////Capturing//////////////////////////////////////////////////////// + +/obj/item/device/soulstone/attack(mob/living/carbon/human/M, mob/user) + if(!iscultist(user) && !iswizard(user) && !usability) + user.Paralyse(5) + user << "Your body is wracked with debilitating pain!" + return + if(!istype(M, /mob/living/carbon/human))//If target is not a human. + return ..() + if(istype(M, /mob/living/carbon/human/dummy)) + return ..() + add_logs(user, M, "captured [M.name]'s soul", src) + + if(iscultist(user) && M && M.mind) + new /obj/item/summoning_orb(get_turf(M)) + + transfer_soul("VICTIM", M, user) + return + +///////////////////Options for using captured souls/////////////////////////////////////// + +/obj/item/device/soulstone/attack_self(mob/user) + if (!in_range(src, user)) + return + if(!iscultist(user) && !iswizard(user) && !usability) + user.Paralyse(5) + user << "Your body is wracked with debilitating pain!" + return + user.set_machine(src) + var/dat = "Soul Stone
" + for(var/mob/living/simple_animal/shade/A in src) + dat += "Captured Soul: [A.name]
" + dat += {"Summon Shade"} + dat += "
" + dat += {"Close"} + user << browse(dat, "window=aicard") + onclose(user, "aicard") + return + + +/obj/item/device/soulstone/Topic(href, href_list) + var/mob/U = usr + if (!in_range(src, U)||U.machine!=src) + U << browse(null, "window=aicard") + U.unset_machine() + return + + add_fingerprint(U) + U.set_machine(src) + + switch(href_list["choice"])//Now we switch based on choice. + if ("Close") + U << browse(null, "window=aicard") + U.unset_machine() + return + + if ("Summon") + for(var/mob/living/simple_animal/shade/A in src) + A.status_flags &= ~GODMODE + A.canmove = 1 + A.loc = U.loc + A.cancel_camera() + src.icon_state = "soulstone" + if(iswizard(U) || usability) + A << "You have been released from your prison, but you are still bound to [U.name]'s will. Help them suceed in their goals at all costs." + else if(iscultist(U)) + A << "You have been released from your prison, but you are still bound to the cult's will. Help them suceed in their goals at all costs." + + attack_self(U) + +///////////////////////////Transferring to constructs///////////////////////////////////////////////////// +/obj/structure/constructshell + name = "empty shell" + icon = 'icons/obj/wizard.dmi' + icon_state = "construct" + desc = "A wicked machine used by those skilled in magical arts. It is inactive" + +/obj/structure/constructshell/attackby(obj/item/O, mob/user, params) + if(istype(O, /obj/item/device/soulstone)) + var/obj/item/device/soulstone/SS = O + SS.transfer_soul("CONSTRUCT",src,user) + + +////////////////////////////Proc for moving soul in and out off stone////////////////////////////////////// + + +/obj/item/device/soulstone/proc/transfer_soul(choice as text, target, mob/user). + switch(choice) + if("FORCE") + if(!iscarbon(target)) //TODO: Add sacrifice stoning for non-organics, just because you have no body doesnt mean you dont have a soul + return 0 + if(contents.len) + return 0 + var/mob/living/carbon/T = target + if(T.client != null) + for(var/obj/item/W in T) + T.unEquip(W) + init_shade(src, T, user) + return 1 + else + user << "Capture failed!: The soul has already fled it's mortal frame. You attempt to bring it back..." + return getCultGhost(src,T,user) + + if("VICTIM") + var/mob/living/carbon/human/T = target + if(imprinted != "empty") + user << "Capture failed!: The soul stone has already been imprinted with [imprinted]'s mind!" + else + if (T.stat == 0) + user << "Capture failed!: Kill or maim the victim first!" + else + if(T.client == null) + user << "Capture failed!: The soul has already fled it's mortal frame. You attempt to bring it back..." + getCultGhost(src,T,user) + else + if(contents.len) + user << "Capture failed!: The soul stone is full! Use or free an existing soul to make room." + else + for(var/obj/item/W in T) + T.unEquip(W) + init_shade(src, T, user, vic = 1) + qdel(T) + if("SHADE") + var/mob/living/simple_animal/shade/T = target + if (T.stat == DEAD) + user << "Capture failed!: The shade has already been banished!" + else + if(contents.len) + user << "Capture failed!: The soul stone is full! Use or free an existing soul to make room." + else + if(T.name != imprinted) + user << "Capture failed!: The soul stone has already been imprinted with [imprinted]'s mind!" + else + T.loc = src //put shade in stone + T.status_flags |= GODMODE + T.canmove = 0 + T.health = T.maxHealth + icon_state = "soulstone2" + T << "Your soul has been recaptured by the soul stone, its arcane energies are reknitting your ethereal form" + if(user != T) + user << "Capture successful!: [T.name]'s has been recaptured and stored within the soul stone." + if("CONSTRUCT") + var/obj/structure/constructshell/T = target + var/mob/living/simple_animal/shade/A = locate() in src + if(A) + var/construct_class = alert(user, "Please choose which type of construct you wish to create.",,"Juggernaut","Wraith","Artificer") + if(!T || !T.loc) + return + switch(construct_class) + if("Juggernaut") + makeNewConstruct(/mob/living/simple_animal/hostile/construct/armored, A, user, 0, T.loc) + + if("Wraith") + makeNewConstruct(/mob/living/simple_animal/hostile/construct/wraith, A, user, 0, T.loc) + + if("Artificer") + makeNewConstruct(/mob/living/simple_animal/hostile/construct/builder, A, user, 0, T.loc) + + qdel(T) + qdel(src) + else + user << "Creation failed!: The soul stone is empty! Go kill someone!" + return + + +/proc/makeNewConstruct(mob/living/simple_animal/hostile/construct/ctype, mob/target, mob/stoner = null, cultoverride = 0, loc_override = null) + var/mob/living/simple_animal/hostile/construct/newstruct = new ctype((loc_override) ? (loc_override) : (get_turf(target))) + newstruct.faction |= "\ref[stoner]" + newstruct.key = target.key + if(stoner && iscultist(stoner) || cultoverride) + if(ticker.mode.name == "cult") + ticker.mode:add_cultist(newstruct.mind) + else + ticker.mode.cult+=newstruct.mind + ticker.mode.update_cult_icons_added(newstruct.mind) + newstruct << newstruct.playstyle_string + if(stoner && iswizard(stoner)) + newstruct << "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs." + else if(stoner && iscultist(stoner)) + newstruct << "You are still bound to serve the cult, follow their orders and help them summon the Geometer at all costs." + else newstruct << "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs." + newstruct.cancel_camera() + + +/obj/item/device/soulstone/proc/init_shade(obj/item/device/soulstone/C, mob/living/carbon/human/T, mob/U, vic = 0) + new /obj/effect/decal/remains/human(T.loc) //Spawns a skeleton + T.invisibility = 101 + var/atom/movable/overlay/animation = new /atom/movable/overlay( T.loc ) + animation.icon_state = "blank" + animation.icon = 'icons/mob/mob.dmi' + animation.master = T + flick("dust-h", animation) + qdel(animation) + var/mob/living/simple_animal/shade/S = new /mob/living/simple_animal/shade( T.loc ) + S.loc = C //put shade in stone + S.status_flags |= GODMODE //So they won't die inside the stone somehow + S.canmove = 0//Can't move out of the soul stone + S.name = "Shade of [T.real_name]" + S.real_name = "Shade of [T.real_name]" + S.key = T.key + S.faction |= "\ref[U]" //Add the master as a faction, allowing inter-mob cooperation + if(iscultist(U)) + ticker.mode.add_cultist(S.mind,2) + S.cancel_camera() + C.icon_state = "soulstone2" + C.name = "Soul Stone: [S.real_name]" + if(iswizard(U) || usability) + S << "Your soul has been captured! You are now bound to [U.name]'s will, help them suceed in their goals at all costs." + else if(iscultist(U)) + S << "Your soul has been captured! You are now bound to the cult's will, help them suceed in their goals at all costs." + C.imprinted = "[S.name]" + if(vic) + U << "Capture successful!: [T.real_name]'s soul has been ripped from their body and stored within the soul stone." + U << "The soulstone has been imprinted with [S.real_name]'s mind, it will no longer react to other souls." + + +/obj/item/device/soulstone/proc/getCultGhost(obj/item/device/soulstone/C, mob/living/carbon/human/T, mob/U) + var/list/candidates = get_candidates(ROLE_CULTIST) + + shuffle(candidates) + + var/time_passed = world.time + var/list/consenting_candidates = list() + + for(var/candidate in candidates) + + spawn(0) + switch(alert(candidate, "Would you like to play as a Shade? Please choose quickly!","Confirmation","Yes","No")) + if("Yes") + if((world.time-time_passed)>=50 || !src) + return + consenting_candidates += candidate + + sleep(50) + + if(!T) //target mob got soulstoned or gibbed during sleep(50) + return 0 + listclearnulls(consenting_candidates) //some candidates might have left during sleep(50) + + if(consenting_candidates.len) + var/client/ghost = null + ghost = pick(consenting_candidates) + if(C.contents.len) //If they used the soulstone on someone else in the meantime + return 0 + if(!T.client) //If the original returns in the alloted time + T.client = ghost + for(var/obj/item/W in T) + T.unEquip(W) + init_shade(C, T, U) + qdel(T) + return 1 + else + U << "The ghost has fled beyond your grasp." + return 0 diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm index 673dfc06d2c..a28dfac40fc 100644 --- a/code/game/machinery/alarm.dm +++ b/code/game/machinery/alarm.dm @@ -1,1187 +1,1187 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 -// A datum for dealing with threshold limit values -// used in /obj/machinery/alarm -/datum/tlv - var/min2 - var/min1 - var/max1 - var/max2 - -/datum/tlv/New(_min2 as num, _min1 as num, _max1 as num, _max2 as num) - min2 = _min2 - min1 = _min1 - max1 = _max1 - max2 = _max2 - -/datum/tlv/proc/get_danger_level(curval as num) - if (max2 >=0 && curval>=max2) - return 2 - if (min2 >=0 && curval<=min2) - return 2 - if (max1 >=0 && curval>=max1) - return 1 - if (min1 >=0 && curval<=min1) - return 1 - return 0 - -/datum/tlv/proc/CopyFrom(datum/tlv/other) - min2 = other.min2 - min1 = other.min1 - max1 = other.max1 - max2 = other.max2 - -#define AALARM_MODE_SCRUBBING 1 -#define AALARM_MODE_VENTING 2 //makes draught -#define AALARM_MODE_PANIC 3 //like siphon, but stronger (enables widenet) -#define AALARM_MODE_REPLACEMENT 4 //sucks off all air, then refill and swithes to scrubbing -#define AALARM_MODE_OFF 5 -#define AALARM_MODE_FLOOD 6 //Emagged mode; turns off scrubbers and pressure checks on vents -#define AALARM_MODE_SIPHON 7 //Scrubbers suck air -#define AALARM_MODE_CONTAMINATED 8 //Turns on all filtering and widenet scrubbing. -#define AALARM_MODE_REFILL 9 //just like normal, but with triple the air output - -#define AALARM_SCREEN_MAIN 1 -#define AALARM_SCREEN_VENT 2 -#define AALARM_SCREEN_SCRUB 3 -#define AALARM_SCREEN_MODE 4 -#define AALARM_SCREEN_SENSORS 5 - -#define AALARM_REPORT_TIMEOUT 100 - -/obj/machinery/alarm - name = "alarm" - desc = "A machine that monitors atmosphere levels. Goes off if the area is dangerous." - icon = 'icons/obj/monitors.dmi' - icon_state = "alarm0" - anchored = 1 - use_power = 1 - idle_power_usage = 4 - active_power_usage = 8 - power_channel = ENVIRON - req_access = list(access_atmospherics) - var/frequency = 1439 - //var/skipprocess = 0 //Experimenting - var/alarm_frequency = 1437 - - var/datum/radio_frequency/radio_connection - var/locked = 1 - var/datum/wires/alarm/wires = null - var/aidisabled = 0 - var/shorted = 0 - var/buildstage = 2 // 2 = complete, 1 = no wires, 0 = circuit gone - - - var/mode = AALARM_MODE_SCRUBBING - - var/screen = AALARM_SCREEN_MAIN - var/area_uid - var/area/alarm_area - var/danger_level = 0 - - // breathable air according to human/Life() - var/list/TLV = list( - "oxygen" = new/datum/tlv(16,19,135,140), // Partial pressure, kpa - "nitrogen" = new/datum/tlv(-1,-1,1000,1000), // Partial pressure, kpa - "carbon dioxide" = new/datum/tlv(-1,-1,5,10), // Partial pressure, kpa - "plasma" = new/datum/tlv(-1,-1,0.2,0.5), // Partial pressure, kpa - "other" = new/datum/tlv(-1,-1,0.5,1), // Partial pressure, kpa - "pressure" = new/datum/tlv(ONE_ATMOSPHERE*0.80,ONE_ATMOSPHERE*0.90,ONE_ATMOSPHERE*1.10,ONE_ATMOSPHERE*1.20), // kPa - "temperature" = new/datum/tlv(T0C,T0C+10,T0C+40,T0C+66), // K - ) - -/* - // breathable air according to wikipedia - "oxygen" = new/datum/tlv( 9, 12, 158, 296), // Partial pressure, kpa - "carbon dioxide" = new/datum/tlv(-1,-1, 0.5, 1), // Partial pressure, kpa -*/ -/obj/machinery/alarm/server - //req_access = list(access_rd) //no, let departaments to work together - TLV = list( - "oxygen" = new/datum/tlv(-1,-1,-1,-1), // Partial pressure, kpa - "nitrogen" = new/datum/tlv(-1,-1,-1,-1), // Partial pressure, kpa - "carbon dioxide" = new/datum/tlv(-1,-1,-1,-1), // Partial pressure, kpa - "plasma" = new/datum/tlv(-1,-1,-1,-1), // Partial pressure, kpa - "other" = new/datum/tlv(-1,-1,-1,-1), // Partial pressure, kpa - "pressure" = new/datum/tlv(-1,-1,-1,-1), /* kpa */ - "temperature" = new/datum/tlv(-1,-1,-1,-1), // K - ) - -/obj/machinery/alarm/kitchen_cold_room - TLV = list( - "oxygen" = new/datum/tlv(16,19,135,140), // Partial pressure, kpa - "nitrogen" = new/datum/tlv(-1,-1,1000,1000), // Partial pressure, kpa - "carbon dioxide" = new/datum/tlv(-1,-1,5,10), // Partial pressure, kpa - "plasma" = new/datum/tlv(-1,-1,0.2,0.5), // Partial pressure, kpa - "other" = new/datum/tlv(-1,-1,0.5,1), // Partial pressure, kpa - "pressure" = new/datum/tlv(ONE_ATMOSPHERE*0.80,ONE_ATMOSPHERE*0.90,ONE_ATMOSPHERE*1.10,ONE_ATMOSPHERE*1.20), // kPa - "temperature" = new/datum/tlv(200,210,273.15,283.15), // K - ) - -//all air alarms in area are connected via magic -/area - var/obj/machinery/alarm/master_air_alarm - var/list/air_vent_names = list() - var/list/air_scrub_names = list() - var/list/air_vent_info = list() - var/list/air_scrub_info = list() - -/obj/machinery/alarm/New(loc, ndir, nbuild) - ..() - wires = new(src) - if(ndir) - dir = ndir - - if(nbuild) - buildstage = 0 - panel_open = 1 - pixel_x = (dir & 3)? 0 : (dir == 4 ? -24 : 24) - pixel_y = (dir & 3)? (dir ==1 ? -24 : 24) : 0 - - alarm_area = get_area(loc) - if (alarm_area.master) - alarm_area = alarm_area.master - area_uid = alarm_area.uid - if (name == "alarm") - name = "[alarm_area.name] Air Alarm" - - update_icon() - if(ticker && ticker.current_state == 3)//if the game is running - src.initialize() - -/obj/machinery/alarm/Destroy() - if(SSradio) - SSradio.remove_object(src, frequency) - qdel(wires) - wires = null - return ..() - -/obj/machinery/alarm/initialize() - set_frequency(frequency) - if (!master_is_operating()) - elect_master() - -/obj/machinery/alarm/proc/master_is_operating() - return alarm_area.master_air_alarm && !(alarm_area.master_air_alarm.stat & (NOPOWER|BROKEN)) - -/obj/machinery/alarm/proc/elect_master() - for (var/area/A in alarm_area.related) - for (var/obj/machinery/alarm/AA in A) - if (!(AA.stat & (NOPOWER|BROKEN))) - alarm_area.master_air_alarm = AA - return 1 - return 0 - -/obj/machinery/alarm/attack_hand(mob/user) - if (..() || !user) return - if (buildstage != 2) return - - interact(user) - -/obj/machinery/alarm/interact(mob/user) - if (user.has_unlimited_silicon_privilege && src.aidisabled) - user << "AI control for this Air Alarm interface has been disabled." - return - - if(panel_open && !istype(user, /mob/living/silicon/ai)) - wires.Interact(user) - else if (!shorted) - ui_interact(user) - -/obj/machinery/alarm/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 0) - ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open) - if (!ui) - ui = new(user, src, ui_key, "air_alarm", name, 480, 660) - ui.open() - -/obj/machinery/alarm/get_ui_data(mob/user) - var/data = list() - data["locked"] = locked - data["siliconUser"] = user.has_unlimited_silicon_privilege - data["screen"] = screen - data["dangerous"] = emagged - populate_status(data) - if (!locked || user.has_unlimited_silicon_privilege) - populate_controls(data) - return data - -/obj/machinery/alarm/proc/shock(mob/user, prb) - if((stat & (NOPOWER))) // unpowered, no shock - return 0 - if(!prob(prb)) - return 0 //you lucked out, no shock for you - var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread - s.set_up(5, 1, src) - s.start() //sparks always. - if (electrocute_mob(user, get_area(src), src)) - return 1 - else - return 0 - -/obj/machinery/alarm/proc/refresh_all() - for(var/id_tag in alarm_area.air_vent_names) - var/list/I = alarm_area.air_vent_info[id_tag] - if (I && I["timestamp"]+AALARM_REPORT_TIMEOUT/2 > world.time) - continue - send_signal(id_tag, list("status") ) - for(var/id_tag in alarm_area.air_scrub_names) - var/list/I = alarm_area.air_scrub_info[id_tag] - if (I && I["timestamp"]+AALARM_REPORT_TIMEOUT/2 > world.time) - continue - send_signal(id_tag, list("status") ) - -/obj/machinery/alarm/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - radio_connection = SSradio.add_object(src, frequency, RADIO_TO_AIRALARM) - -/obj/machinery/alarm/proc/send_signal(target, list/command)//sends signal 'command' to 'target'. Returns 0 if no radio connection, 1 otherwise - if(!radio_connection) - return 0 - - var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal - signal.source = src - - signal.data = command - signal.data["tag"] = target - signal.data["sigtype"] = "command" - - radio_connection.post_signal(src, signal, RADIO_FROM_AIRALARM) -// world << text("Signal [] Broadcasted to []", command, target) - - return 1 - -/obj/machinery/alarm/proc/populate_status(list/data) - var/turf/location = get_turf(src) - if(!location) - return - var/datum/gas_mixture/environment = location.return_air() - var/total = environment.oxygen + environment.nitrogen + environment.carbon_dioxide + environment.toxins - - var/list/environment_data = list() - data["atmos_alarm"] = alarm_area.atmosalm - data["fire_alarm"] = (alarm_area.fire != null && alarm_area.fire) - data["danger_level"] = danger_level - if(total) - var/datum/tlv/cur_tlv - var/partial_pressure = R_IDEAL_GAS_EQUATION*environment.temperature/environment.volume - - cur_tlv = TLV["pressure"] - var/pressure = environment.return_pressure() - var/pressure_danger = cur_tlv.get_danger_level(pressure) - environment_data += list(list("name" = "Pressure", "value" = pressure, "unit" = "kPa", "danger_level" = pressure_danger)) - - cur_tlv = TLV["oxygen"] - var/oxygen_danger = cur_tlv.get_danger_level(environment.oxygen*partial_pressure) - environment_data += list(list("name" = "Oxygen", "value" = environment.oxygen / total * 100, "unit" = "%", "danger_level" = oxygen_danger)) - - cur_tlv = TLV["nitrogen"] - var/nitrogen_danger = cur_tlv.get_danger_level(environment.nitrogen*partial_pressure) - environment_data += list(list("name" = "Nitrogen", "value" = environment.nitrogen / total * 100, "unit" = "%", "danger_level" = nitrogen_danger)) - - cur_tlv = TLV["carbon dioxide"] - var/carbon_dioxide_danger = cur_tlv.get_danger_level(environment.carbon_dioxide*partial_pressure) - environment_data += list(list("name" = "Carbon Dioxide", "value" = environment.carbon_dioxide / total * 100, "unit" = "%", "danger_level" = carbon_dioxide_danger)) - - cur_tlv = TLV["plasma"] - var/plasma_danger = cur_tlv.get_danger_level(environment.toxins*partial_pressure) - environment_data += list(list("name" = "Toxins", "value" = environment.toxins / total * 100, "unit" = "%", "danger_level" = plasma_danger)) - - cur_tlv = TLV["other"] - var/other_moles = 0 - for(var/datum/gas/G in environment.trace_gases) - other_moles+=G.moles - var/other_danger = cur_tlv.get_danger_level(other_moles*partial_pressure) - environment_data += list(list("name" = "Other", "value" = other_moles / total * 100, "unit" = "%", "danger_level" = other_danger)) - - cur_tlv = TLV["temperature"] - var/temperature_danger = cur_tlv.get_danger_level(environment.temperature) - environment_data += list(list("name" = "Temperature", "value" = environment.temperature, "unit" = "K ([round(environment.temperature - T0C, 0.1)]C)", "danger_level" = temperature_danger)) - - data["environment_data"] = environment_data - -/obj/machinery/alarm/proc/populate_controls(list/data) - switch(screen) - if(AALARM_SCREEN_MAIN) - data["mode"] = mode - if(AALARM_SCREEN_VENT) - data["vents"] = list() - for(var/id_tag in alarm_area.air_vent_names) - var/long_name = alarm_area.air_vent_names[id_tag] - var/list/info = alarm_area.air_vent_info[id_tag] - if(!info) - continue - data["vents"] += list(list( - "id_tag" = id_tag, - "long_name" = sanitize(long_name), - "power" = info["power"], - "checks" = info["checks"], - "excheck" = info["checks"]&1, - "incheck" = info["checks"]&2, - "direction" = info["direction"], - "external" = info["external"], - "extdefault"= (info["external"] == ONE_ATMOSPHERE) - )) - if(AALARM_SCREEN_SCRUB) - data["scrubbers"] = list() - for(var/id_tag in alarm_area.air_scrub_names) - var/long_name = alarm_area.air_scrub_names[id_tag] - var/list/info = alarm_area.air_scrub_info[id_tag] - if(!info) - continue - data["scrubbers"] += list(list( - "id_tag" = id_tag, - "long_name" = sanitize(long_name), - "power" = info["power"], - "scrubbing" = info["scrubbing"], - "widenet" = info["widenet"], - "filter_co2" = info["filter_co2"], - "filter_toxins" = info["filter_toxins"], - "filter_n2o" = info["filter_n2o"] - )) - if(AALARM_SCREEN_MODE) - data["mode"] = mode - data["modes"] = list() - data["modes"] += list(list("name" = "Filtering - Scrubs out contaminants", "mode" = AALARM_MODE_SCRUBBING, "selected" = mode == AALARM_MODE_SCRUBBING, "danger" = 0)) - data["modes"] += list(list("name" = "Contaminated - Scrubs out ALL contaminants quickly","mode" = AALARM_MODE_CONTAMINATED, "selected" = mode == AALARM_MODE_CONTAMINATED, "danger" = 0)) - data["modes"] += list(list("name" = "Draught - Siphons out air while replacing", "mode" = AALARM_MODE_VENTING, "selected" = mode == AALARM_MODE_VENTING, "danger" = 0)) - data["modes"] += list(list("name" = "Refill - Triple vent output", "mode" = AALARM_MODE_REFILL, "selected" = mode == AALARM_MODE_REFILL, "danger" = 0)) - data["modes"] += list(list("name" = "Cycle - Siphons air before replacing", "mode" = AALARM_MODE_REPLACEMENT, "selected" = mode == AALARM_MODE_REPLACEMENT, "danger" = 1)) - data["modes"] += list(list("name" = "Siphon - Siphons air out of the room", "mode" = AALARM_MODE_SIPHON, "selected" = mode == AALARM_MODE_SIPHON, "danger" = 1)) - data["modes"] += list(list("name" = "Panic Siphon - Siphons air out of the room quickly","mode" = AALARM_MODE_PANIC, "selected" = mode == AALARM_MODE_PANIC, "danger" = 1)) - data["modes"] += list(list("name" = "Off - Shuts off vents and scrubbers", "mode" = AALARM_MODE_OFF, "selected" = mode == AALARM_MODE_OFF, "danger" = 0)) - if (src.emagged) - data["modes"] += list(list("name" = "Flood - Shuts off scrubbers and opens vents", "mode" = AALARM_MODE_FLOOD, "selected" = mode == AALARM_MODE_FLOOD, "danger" = 1)) - if(AALARM_SCREEN_SENSORS) - var/datum/tlv/selected - var/list/thresholds = list() - - var/list/gas_names = list( - "oxygen" = "O2", - "nitrogen" = "N2", - "carbon dioxide" = "CO2", - "plasma" = "Toxin", - "other" = "Other") - for (var/g in gas_names) - thresholds += list(list("name" = gas_names[g], "settings" = list())) - selected = TLV[g] - thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = "min2", "selected" = selected.min2)) - thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = "min1", "selected" = selected.min1)) - thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = "max1", "selected" = selected.max1)) - thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = "max2", "selected" = selected.max2)) - - selected = TLV["pressure"] - thresholds += list(list("name" = "Pressure", "settings" = list())) - thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "min2", "selected" = selected.min2)) - thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "min1", "selected" = selected.min1)) - thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "max1", "selected" = selected.max1)) - thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "max2", "selected" = selected.max2)) - - selected = TLV["temperature"] - thresholds += list(list("name" = "Temperature", "settings" = list())) - thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "min2", "selected" = selected.min2)) - thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "min1", "selected" = selected.min1)) - thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "max1", "selected" = selected.max1)) - thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "max2", "selected" = selected.max2)) - - - data["thresholds"] = thresholds - -/obj/machinery/alarm/ui_act(action, params) - if(..()) - return - - if (buildstage != 2) - return - - if (locked && !usr.has_unlimited_silicon_privilege) - return - - if (usr.has_unlimited_silicon_privilege && src.aidisabled) - return - - switch(action) - if("toggleaccess") - if(usr.has_unlimited_silicon_privilege && !wires.IsIndexCut(AALARM_WIRE_IDSCAN)) - locked = !locked - if("adjust") - var/device_id = params["id_tag"] - switch(params["command"]) - if("set_external_pressure") - var/input_pressure = input("Enter target pressure:", "Pressure Controls") as num|null - if(isnum(input_pressure)) - send_signal(device_id, list(params["command"] = input_pressure)) - if("reset_external_pressure") - send_signal(device_id, list("set_external_pressure" = ONE_ATMOSPHERE)) - if( - "power", - "adjust_external_pressure", - "co2_scrub", - "tox_scrub", - "n2o_scrub", - "widenet", - "scrubbing" - ) - send_signal(device_id, list (params["command"] = text2num(params["val"]))) - if ("excheck") - send_signal(device_id, list ("checks" = text2num(params["val"])^1)) - if ("incheck") - send_signal(device_id, list ("checks" = text2num(params["val"])^2)) - if("set_threshold") - var/env = params["env"] - var/varname = params["var"] - var/datum/tlv/tlv = TLV[env] - var/newval = input("Enter [varname] for [env]:", "Alarm Triggers", tlv.vars[varname]) as num|null - if (isnull(newval)) - return - if (newval<0) - tlv.vars[varname] = -1 - else if (env=="temperature" && newval>5000) - tlv.vars[varname] = 5000 - else if (env=="pressure" && newval>50*ONE_ATMOSPHERE) - tlv.vars[varname] = 50*ONE_ATMOSPHERE - else if (env!="temperature" && env!="pressure" && newval>200) - tlv.vars[varname] = 200 - else - newval = round(newval,0.01) - tlv.vars[varname] = newval - if("screen") - screen = text2num(params["screen"]) - if("mode") - mode = text2num(params["mode"]) - apply_mode() - if("alarm") - if(alarm_area.atmosalert(2, src)) - post_alert(2) - update_icon() - if("reset") - if(alarm_area.atmosalert(0, src)) - post_alert(0) - update_icon() - return 1 - -/obj/machinery/alarm/proc/apply_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, - "tox_scrub"= 0, - "n2o_scrub"= 0, - "scrubbing"= 1, - "widenet"= 0, - )) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list( - "power"= 1, - "checks"= 1, - "set_external_pressure"= ONE_ATMOSPHERE - )) - if(AALARM_MODE_CONTAMINATED) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list( - "power"= 1, - "co2_scrub"= 1, - "tox_scrub"= 1, - "n2o_scrub"= 1, - "scrubbing"= 1, - "widenet"= 1, - )) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list( - "power"= 1, - "checks"= 1, - "set_external_pressure"= ONE_ATMOSPHERE - )) - if(AALARM_MODE_VENTING) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list( - "power"= 1, - "widenet"= 0, - "scrubbing"= 0 - )) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list( - "power"= 1, - "checks"= 1, - "set_external_pressure" = ONE_ATMOSPHERE*2 - )) - if(AALARM_MODE_REFILL) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list( - "power"= 1, - "co2_scrub"= 1, - "tox_scrub"= 0, - "n2o_scrub"= 0, - "scrubbing"= 1, - "widenet"= 0, - )) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list( - "power"= 1, - "checks"= 1, - "set_external_pressure" = ONE_ATMOSPHERE*3 - )) - if( - AALARM_MODE_PANIC, - AALARM_MODE_REPLACEMENT - ) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list( - "power"= 1, - "widenet"= 1, - "scrubbing"= 0 - )) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list( - "power"= 0 - )) - if( - AALARM_MODE_SIPHON - ) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list( - "power"= 1, - "widenet"= 0, - "scrubbing"= 0 - )) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list( - "power"= 0 - )) - - if(AALARM_MODE_OFF) - 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"= 0 - )) - if(AALARM_MODE_FLOOD) - 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"= 0, - )) - -/obj/machinery/alarm/update_icon() - if(panel_open) - switch(buildstage) - if(2) - icon_state = "alarmx" - if(1) - icon_state = "alarm_b2" - if(0) - icon_state = "alarm_b1" - return - - if((stat & (NOPOWER|BROKEN)) || shorted) - icon_state = "alarmp" - return - switch(max(danger_level, alarm_area.atmosalm)) - if (0) - src.icon_state = "alarm0" - if (1) - src.icon_state = "alarm2" //yes, alarm2 is yellow alarm - if (2) - src.icon_state = "alarm1" - -/obj/machinery/alarm/process() - if((stat & (NOPOWER|BROKEN)) || shorted) - return - - var/turf/simulated/location = src.loc - if (!istype(location)) - return 0 - - var/datum/gas_mixture/environment = location.return_air() - - var/datum/tlv/cur_tlv - var/GET_PP = R_IDEAL_GAS_EQUATION*environment.temperature/environment.volume - - cur_tlv = TLV["pressure"] - var/environment_pressure = environment.return_pressure() - var/pressure_dangerlevel = cur_tlv.get_danger_level(environment_pressure) - - cur_tlv = TLV["oxygen"] - var/oxygen_dangerlevel = cur_tlv.get_danger_level(environment.oxygen*GET_PP) - - cur_tlv = TLV["carbon dioxide"] - var/co2_dangerlevel = cur_tlv.get_danger_level(environment.carbon_dioxide*GET_PP) - - cur_tlv = TLV["plasma"] - var/plasma_dangerlevel = cur_tlv.get_danger_level(environment.toxins*GET_PP) - - cur_tlv = TLV["other"] - var/other_moles = 0 - for(var/datum/gas/G in environment.trace_gases) - other_moles+=G.moles - var/other_dangerlevel = cur_tlv.get_danger_level(other_moles*GET_PP) - - cur_tlv = TLV["temperature"] - var/temperature_dangerlevel = cur_tlv.get_danger_level(environment.temperature) - - var/old_danger_level = danger_level - danger_level = max( - pressure_dangerlevel, - oxygen_dangerlevel, - co2_dangerlevel, - plasma_dangerlevel, - other_dangerlevel, - temperature_dangerlevel - ) - if (old_danger_level!=danger_level) - apply_danger_level() - - if (mode==AALARM_MODE_REPLACEMENT && environment_pressureYou cut the final wires.
" - var/obj/item/stack/cable_coil/cable = new /obj/item/stack/cable_coil( src.loc ) - cable.amount = 5 - buildstage = 1 - update_icon() - return - - if(istype(W, /obj/item/weapon/screwdriver)) // Opening that Air Alarm up. - playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1) - panel_open = !panel_open - user << "The wires have been [panel_open ? "exposed" : "unexposed"]." - update_icon() - return - - if (panel_open && ((istype(W, /obj/item/device/multitool) || istype(W, /obj/item/weapon/wirecutters)))) - return src.attack_hand(user) - else if (istype(W, /obj/item/weapon/card/id) || istype(W, /obj/item/device/pda))// trying to unlock the interface with an ID card - if(stat & (NOPOWER|BROKEN)) - user << "It does nothing!" - else - if(src.allowed(usr) && !wires.IsIndexCut(AALARM_WIRE_IDSCAN)) - locked = !locked - user << "You [ locked ? "lock" : "unlock"] the air alarm interface." - src.updateUsrDialog() - else - user << "Access denied." - return - if(1) - if(istype(W, /obj/item/weapon/crowbar)) - user.visible_message("[user.name] removes the electronics from [src.name].",\ - "You start prying out the circuit...") - playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1) - if (do_after(user, 20/W.toolspeed, target = src)) - if (buildstage == 1) - user <<"You remove the air alarm electronics." - new /obj/item/weapon/electronics/airalarm( src.loc ) - playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) - buildstage = 0 - update_icon() - return - - if(istype(W, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/cable = W - if(cable.get_amount() < 5) - user << "You need five lengths of cable to wire the fire alarm!" - return - user.visible_message("[user.name] wires the air alarm.", \ - "You start wiring the air alarm...") - if (do_after(user, 20, target = src)) - if (cable.get_amount() >= 5 && buildstage == 1) - cable.use(5) - user << "You wire the air alarm." - wires.wires_status = 0 - aidisabled = 0 - locked = 1 - mode = 1 - shorted = 0 - post_alert(0) - buildstage = 2 - update_icon() - return - if(0) - if(istype(W, /obj/item/weapon/electronics/airalarm)) - if(user.unEquip(W)) - user << "You insert the circuit." - buildstage = 1 - update_icon() - qdel(W) - return - - if(istype(W, /obj/item/weapon/wrench)) - user << "You detach \the [src] from the wall." - playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1) - new /obj/item/wallframe/alarm( user.loc ) - qdel(src) - return - - return ..() - -/obj/machinery/alarm/power_change() - if(powered(power_channel)) - stat &= ~NOPOWER - else - stat |= NOPOWER - spawn(rand(0,15)) - if(loc) - update_icon() - - -/obj/machinery/alarm/emag_act(mob/user) - if(!emagged) - src.emagged = 1 - if(user) - user.visible_message("Sparks fly out of the [src]!", "You emag the [src], disabling its safeties.") - playsound(src.loc, 'sound/effects/sparks4.ogg', 50, 1) - return - - -/* -AIR ALARM CIRCUIT -Just a object used in constructing air alarms -*/ -/obj/item/weapon/electronics/airalarm - name = "air alarm electronics" - icon_state = "airalarm_electronics" - -/* -AIR ALARM ITEM -Handheld air alarm frame, for placing on walls -*/ -/obj/item/wallframe/alarm - name = "air alarm frame" - desc = "Used for building Air Alarms" - icon = 'icons/obj/monitors.dmi' - icon_state = "alarm_bitem" - result_path = /obj/machinery/alarm - - -/* -FIRE ALARM -*/ -/obj/machinery/firealarm - name = "fire alarm" - desc = "\"Pull this in case of emergency\". Thus, keep pulling it forever." - icon = 'icons/obj/monitors.dmi' - icon_state = "fire0" - var/detecting = 1 - var/time = 10 - var/timing = 0 - var/lockdownbyai = 0 - anchored = 1 - use_power = 1 - idle_power_usage = 2 - active_power_usage = 6 - power_channel = ENVIRON - var/last_process = 0 - var/buildstage = 2 // 2 = complete, 1 = no wires, 0 = circuit gone - -/obj/machinery/firealarm/update_icon() - src.overlays = list() - - var/area/A = src.loc - A = A.loc - - if(panel_open) - switch(buildstage) - if(0) - icon_state="fire_b0" - return - if(1) - icon_state="fire_b1" - return - if(2) - icon_state="fire_b2" - - if((stat & BROKEN) || (stat & NOPOWER)) - return - - overlays += "overlay_[security_level]" - return - - if(stat & BROKEN) - icon_state = "firex" - return - - icon_state = "fire0" - - if(stat & NOPOWER) - return - - overlays += "overlay_[security_level]" - - if(!src.detecting) - overlays += "overlay_fire" - else - overlays += "overlay_[A.fire ? "fire" : "clear"]" - - - -/obj/machinery/firealarm/emag_act(mob/user) - if(!emagged) - src.emagged = 1 - if(user) - user.visible_message("Sparks fly out of the [src]!", "You emag the [src], disabling its thermal sensors.") - playsound(src.loc, 'sound/effects/sparks4.ogg', 50, 1) - return - - -/obj/machinery/firealarm/temperature_expose(datum/gas_mixture/air, temperature, volume) - if(src.detecting) - if(temperature > T0C+200) - if(!emagged) //Doesn't give off alarm when emagged - src.alarm() // added check of detector status here - return - -/obj/machinery/firealarm/attack_ai(mob/user as mob) - return src.attack_hand(user) - -/obj/machinery/firealarm/bullet_act(BLAH) - return src.alarm() - -/obj/machinery/firealarm/attack_paw(mob/user) - return src.attack_hand(user) - -/obj/machinery/firealarm/emp_act(severity) - if(prob(50/severity)) alarm() - ..() - -/obj/machinery/firealarm/attackby(obj/item/W, mob/user, params) - add_fingerprint(user) - - if(istype(W, /obj/item/weapon/screwdriver) && buildstage == 2) - playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1) - panel_open = !panel_open - user << "The wires have been [panel_open ? "exposed" : "unexposed"]." - update_icon() - return - - if(panel_open) - switch(buildstage) - if(2) - if(istype(W, /obj/item/device/multitool)) - src.detecting = !( src.detecting ) - if (src.detecting) - user.visible_message("[user] has reconnected [src]'s detecting unit!", "You reconnect [src]'s detecting unit.") - else - user.visible_message("[user] has disconnected [src]'s detecting unit!", "You disconnect [src]'s detecting unit.") - return - - else if (istype(W, /obj/item/weapon/wirecutters)) - buildstage = 1 - playsound(src.loc, 'sound/items/Wirecutter.ogg', 50, 1) - var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil() - coil.amount = 5 - coil.loc = user.loc - user << "You cut the wires from \the [src]." - update_icon() - return - if(1) - if(istype(W, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/coil = W - if(coil.get_amount() < 5) - user << "You need more cable for this!" - else - coil.use(5) - buildstage = 2 - user << "You wire \the [src]." - update_icon() - return - - else if(istype(W, /obj/item/weapon/crowbar)) - playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1) - user.visible_message("[user.name] removes the electronics from [src.name].", \ - "You start prying out the circuit...") - if(do_after(user, 20/W.toolspeed, target = src)) - if(buildstage == 1) - if(stat & BROKEN) - user << "You remove the destroyed circuit." - else - user << "You pry out the circuit." - new /obj/item/weapon/electronics/firealarm(user.loc) - buildstage = 0 - update_icon() - return - if(0) - if(istype(W, /obj/item/weapon/electronics/firealarm)) - user << "You insert the circuit." - qdel(W) - buildstage = 1 - update_icon() - return - - else if(istype(W, /obj/item/weapon/wrench)) - user.visible_message("[user] removes the fire alarm assembly from the wall.", \ - "You remove the fire alarm assembly from the wall.") - var/obj/item/wallframe/firealarm/frame = new /obj/item/wallframe/firealarm() - frame.loc = user.loc - playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1) - qdel(src) - return - return ..() - -/obj/machinery/firealarm/process()//Note: this processing was mostly phased out due to other code, and only runs when needed - if(stat & (NOPOWER|BROKEN)) - return - - if(src.timing) - if(src.time > 0) - src.time = src.time - ((world.timeofday - last_process)/10) - else - src.alarm() - src.time = 0 - src.timing = 0 - SSobj.processing.Remove(src) - src.updateDialog() - last_process = world.timeofday - return - -/obj/machinery/firealarm/power_change() - if(powered(ENVIRON)) - stat &= ~NOPOWER - else - stat |= NOPOWER - spawn(rand(0,15)) - if(loc) - update_icon() - -/obj/machinery/firealarm/attack_hand(mob/user) - if((user.stat && !IsAdminGhost(user)) || stat & (NOPOWER|BROKEN)) - return - - if (buildstage != 2) - return - - user.set_machine(src) - var/area/A = src.loc - var/safety_warning - var/d1 - var/d2 - var/dat = "" - if (istype(user, /mob/living/carbon/human) || user.has_unlimited_silicon_privilege) - A = A.loc - if (src.emagged) - safety_warning = text("NOTICE: Thermal sensors nonfunctional. Device will not report or recognize high temperatures.") - else - safety_warning = text("Safety measures functioning properly.") - if (A.fire) - d1 = text("Reset - Lockdown", src) - else - d1 = text("Alarm - Lockdown", src) - if (src.timing) - d2 = text("Stop Time Lock", src) - else - d2 = text("Initiate Time Lock", src) - var/second = round(src.time) % 60 - var/minute = (round(src.time) - second) / 60 - dat = "[safety_warning]

[d1]
The current alert level is: [get_security_level()]

Timer System: [d2]
Time Left: - - [(minute ? "[minute]:" : null)][second] + +" - //user << browse(dat, "window=firealarm") - //onclose(user, "firealarm") - else - A = A.loc - if (A.fire) - d1 = text("[]", src, stars("Reset - Lockdown")) - else - d1 = text("[]", src, stars("Alarm - Lockdown")) - if (src.timing) - d2 = text("[]", src, stars("Stop Time Lock")) - else - d2 = text("[]", src, stars("Initiate Time Lock")) - var/second = round(src.time) % 60 - var/minute = (round(src.time) - second) / 60 - dat = "[d1]
The current alert level is: [stars(get_security_level())]

Timer System: [d2]
Time Left: - - [(minute ? text("[]:", minute) : null)][second] + +" - //user << browse(dat, "window=firealarm") - //onclose(user, "firealarm") - var/datum/browser/popup = new(user, "firealarm", "Fire Alarm") - popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) - popup.open() - return - -/obj/machinery/firealarm/Topic(href, href_list) - if(..()) - return - - if (buildstage != 2) - return - - usr.set_machine(src) - if (href_list["reset"]) - src.reset() - else if (href_list["alarm"]) - src.alarm() - else if (href_list["time"]) - src.timing = text2num(href_list["time"]) - last_process = world.timeofday - SSobj.processing |= src - else if (href_list["tp"]) - var/tp = text2num(href_list["tp"]) - src.time += tp - src.time = min(max(round(src.time), 0), 120) - - src.updateUsrDialog() - -/obj/machinery/firealarm/proc/reset() - if (stat & (NOPOWER|BROKEN)) // can't reset alarm if it's unpowered or broken. - return - var/area/A = get_area(src) - A.firereset(src) - return - -/obj/machinery/firealarm/proc/alarm() - if (stat & (NOPOWER|BROKEN)) // can't activate alarm if it's unpowered or broken. - return - var/area/A = get_area(src) - if(!A.fire) - A.firealert(src) - //playsound(src.loc, 'sound/ambience/signal.ogg', 75, 0) - return - -/obj/machinery/firealarm/New(loc, ndir, building) - ..() - - if(ndir) - src.dir = ndir - - if(building) - buildstage = 0 - panel_open = 1 - pixel_x = (dir & 3)? 0 : (dir == 4 ? -24 : 24) - pixel_y = (dir & 3)? (dir ==1 ? -24 : 24) : 0 - - if(z == 1) - if(security_level) - src.overlays += image('icons/obj/monitors.dmi', "overlay_[get_security_level()]") - else - src.overlays += image('icons/obj/monitors.dmi', "overlay_green") - - update_icon() - -/* -FIRE ALARM CIRCUIT -Just a object used in constructing fire alarms -*/ -/obj/item/weapon/electronics/firealarm - name = "fire alarm electronics" - desc = "A circuit. It has a label on it, it says \"Can handle heat levels up to 40 degrees celsius!\"" - - -/* -FIRE ALARM ITEM -Handheld fire alarm frame, for placing on walls -*/ -/obj/item/wallframe/firealarm - name = "fire alarm frame" - desc = "Used for building Fire Alarms" - icon = 'icons/obj/monitors.dmi' - icon_state = "fire_bitem" - result_path = /obj/machinery/firealarm - -/* - * Party button - */ - -/obj/machinery/firealarm/partyalarm - name = "\improper PARTY BUTTON" - desc = "Cuban Pete is in the house!" - -/obj/machinery/firealarm/partyalarm/attack_hand(mob/user) - if((user.stat && !IsAdminGhost(user)) || stat & (NOPOWER|BROKEN)) - return - - if (buildstage != 2) - return - - user.set_machine(src) - var/area/A = src.loc - var/d1 - var/dat - if (istype(user, /mob/living/carbon/human) || user.has_unlimited_silicon_privilege) - A = A.loc - - if (A.party) - d1 = text("No Party :(", src) - else - d1 = text("PARTY!!!", src) - dat = text("Party Button []", d1) - - else - A = A.loc - if (A.fire) - d1 = text("[]", src, stars("No Party :(")) - else - d1 = text("[]", src, stars("PARTY!!!")) - dat = text("[] []", stars("Party Button"), d1) - - var/datum/browser/popup = new(user, "firealarm", "Party Alarm") - popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) - popup.open() - return - -/obj/machinery/firealarm/partyalarm/reset() - if (stat & (NOPOWER|BROKEN)) - return - var/area/A = src.loc - A = A.loc - if (!( istype(A, /area) )) - return - for(var/area/RA in A.related) - RA.partyreset() - return - -/obj/machinery/firealarm/partyalarm/alarm() - if (stat & (NOPOWER|BROKEN)) - return - var/area/A = src.loc - A = A.loc - if (!( istype(A, /area) )) - return - for(var/area/RA in A.related) - RA.partyalert() - return +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 +// A datum for dealing with threshold limit values +// used in /obj/machinery/alarm +/datum/tlv + var/min2 + var/min1 + var/max1 + var/max2 + +/datum/tlv/New(_min2 as num, _min1 as num, _max1 as num, _max2 as num) + min2 = _min2 + min1 = _min1 + max1 = _max1 + max2 = _max2 + +/datum/tlv/proc/get_danger_level(curval as num) + if (max2 >=0 && curval>=max2) + return 2 + if (min2 >=0 && curval<=min2) + return 2 + if (max1 >=0 && curval>=max1) + return 1 + if (min1 >=0 && curval<=min1) + return 1 + return 0 + +/datum/tlv/proc/CopyFrom(datum/tlv/other) + min2 = other.min2 + min1 = other.min1 + max1 = other.max1 + max2 = other.max2 + +#define AALARM_MODE_SCRUBBING 1 +#define AALARM_MODE_VENTING 2 //makes draught +#define AALARM_MODE_PANIC 3 //like siphon, but stronger (enables widenet) +#define AALARM_MODE_REPLACEMENT 4 //sucks off all air, then refill and swithes to scrubbing +#define AALARM_MODE_OFF 5 +#define AALARM_MODE_FLOOD 6 //Emagged mode; turns off scrubbers and pressure checks on vents +#define AALARM_MODE_SIPHON 7 //Scrubbers suck air +#define AALARM_MODE_CONTAMINATED 8 //Turns on all filtering and widenet scrubbing. +#define AALARM_MODE_REFILL 9 //just like normal, but with triple the air output + +#define AALARM_SCREEN_MAIN 1 +#define AALARM_SCREEN_VENT 2 +#define AALARM_SCREEN_SCRUB 3 +#define AALARM_SCREEN_MODE 4 +#define AALARM_SCREEN_SENSORS 5 + +#define AALARM_REPORT_TIMEOUT 100 + +/obj/machinery/alarm + name = "alarm" + desc = "A machine that monitors atmosphere levels. Goes off if the area is dangerous." + icon = 'icons/obj/monitors.dmi' + icon_state = "alarm0" + anchored = 1 + use_power = 1 + idle_power_usage = 4 + active_power_usage = 8 + power_channel = ENVIRON + req_access = list(access_atmospherics) + var/frequency = 1439 + //var/skipprocess = 0 //Experimenting + var/alarm_frequency = 1437 + + var/datum/radio_frequency/radio_connection + var/locked = 1 + var/datum/wires/alarm/wires = null + var/aidisabled = 0 + var/shorted = 0 + var/buildstage = 2 // 2 = complete, 1 = no wires, 0 = circuit gone + + + var/mode = AALARM_MODE_SCRUBBING + + var/screen = AALARM_SCREEN_MAIN + var/area_uid + var/area/alarm_area + var/danger_level = 0 + + // breathable air according to human/Life() + var/list/TLV = list( + "oxygen" = new/datum/tlv(16,19,135,140), // Partial pressure, kpa + "nitrogen" = new/datum/tlv(-1,-1,1000,1000), // Partial pressure, kpa + "carbon dioxide" = new/datum/tlv(-1,-1,5,10), // Partial pressure, kpa + "plasma" = new/datum/tlv(-1,-1,0.2,0.5), // Partial pressure, kpa + "other" = new/datum/tlv(-1,-1,0.5,1), // Partial pressure, kpa + "pressure" = new/datum/tlv(ONE_ATMOSPHERE*0.80,ONE_ATMOSPHERE*0.90,ONE_ATMOSPHERE*1.10,ONE_ATMOSPHERE*1.20), // kPa + "temperature" = new/datum/tlv(T0C,T0C+10,T0C+40,T0C+66), // K + ) + +/* + // breathable air according to wikipedia + "oxygen" = new/datum/tlv( 9, 12, 158, 296), // Partial pressure, kpa + "carbon dioxide" = new/datum/tlv(-1,-1, 0.5, 1), // Partial pressure, kpa +*/ +/obj/machinery/alarm/server + //req_access = list(access_rd) //no, let departaments to work together + TLV = list( + "oxygen" = new/datum/tlv(-1,-1,-1,-1), // Partial pressure, kpa + "nitrogen" = new/datum/tlv(-1,-1,-1,-1), // Partial pressure, kpa + "carbon dioxide" = new/datum/tlv(-1,-1,-1,-1), // Partial pressure, kpa + "plasma" = new/datum/tlv(-1,-1,-1,-1), // Partial pressure, kpa + "other" = new/datum/tlv(-1,-1,-1,-1), // Partial pressure, kpa + "pressure" = new/datum/tlv(-1,-1,-1,-1), /* kpa */ + "temperature" = new/datum/tlv(-1,-1,-1,-1), // K + ) + +/obj/machinery/alarm/kitchen_cold_room + TLV = list( + "oxygen" = new/datum/tlv(16,19,135,140), // Partial pressure, kpa + "nitrogen" = new/datum/tlv(-1,-1,1000,1000), // Partial pressure, kpa + "carbon dioxide" = new/datum/tlv(-1,-1,5,10), // Partial pressure, kpa + "plasma" = new/datum/tlv(-1,-1,0.2,0.5), // Partial pressure, kpa + "other" = new/datum/tlv(-1,-1,0.5,1), // Partial pressure, kpa + "pressure" = new/datum/tlv(ONE_ATMOSPHERE*0.80,ONE_ATMOSPHERE*0.90,ONE_ATMOSPHERE*1.10,ONE_ATMOSPHERE*1.20), // kPa + "temperature" = new/datum/tlv(200,210,273.15,283.15), // K + ) + +//all air alarms in area are connected via magic +/area + var/obj/machinery/alarm/master_air_alarm + var/list/air_vent_names = list() + var/list/air_scrub_names = list() + var/list/air_vent_info = list() + var/list/air_scrub_info = list() + +/obj/machinery/alarm/New(loc, ndir, nbuild) + ..() + wires = new(src) + if(ndir) + dir = ndir + + if(nbuild) + buildstage = 0 + panel_open = 1 + pixel_x = (dir & 3)? 0 : (dir == 4 ? -24 : 24) + pixel_y = (dir & 3)? (dir ==1 ? -24 : 24) : 0 + + alarm_area = get_area(loc) + if (alarm_area.master) + alarm_area = alarm_area.master + area_uid = alarm_area.uid + if (name == "alarm") + name = "[alarm_area.name] Air Alarm" + + update_icon() + if(ticker && ticker.current_state == 3)//if the game is running + src.initialize() + +/obj/machinery/alarm/Destroy() + if(SSradio) + SSradio.remove_object(src, frequency) + qdel(wires) + wires = null + return ..() + +/obj/machinery/alarm/initialize() + set_frequency(frequency) + if (!master_is_operating()) + elect_master() + +/obj/machinery/alarm/proc/master_is_operating() + return alarm_area.master_air_alarm && !(alarm_area.master_air_alarm.stat & (NOPOWER|BROKEN)) + +/obj/machinery/alarm/proc/elect_master() + for (var/area/A in alarm_area.related) + for (var/obj/machinery/alarm/AA in A) + if (!(AA.stat & (NOPOWER|BROKEN))) + alarm_area.master_air_alarm = AA + return 1 + return 0 + +/obj/machinery/alarm/attack_hand(mob/user) + if (..() || !user) return + if (buildstage != 2) return + + interact(user) + +/obj/machinery/alarm/interact(mob/user) + if (user.has_unlimited_silicon_privilege && src.aidisabled) + user << "AI control for this Air Alarm interface has been disabled." + return + + if(panel_open && !istype(user, /mob/living/silicon/ai)) + wires.Interact(user) + else if (!shorted) + ui_interact(user) + +/obj/machinery/alarm/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 0) + ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open) + if (!ui) + ui = new(user, src, ui_key, "air_alarm", name, 480, 660) + ui.open() + +/obj/machinery/alarm/get_ui_data(mob/user) + var/data = list() + data["locked"] = locked + data["siliconUser"] = user.has_unlimited_silicon_privilege + data["screen"] = screen + data["dangerous"] = emagged + populate_status(data) + if (!locked || user.has_unlimited_silicon_privilege) + populate_controls(data) + return data + +/obj/machinery/alarm/proc/shock(mob/user, prb) + if((stat & (NOPOWER))) // unpowered, no shock + return 0 + if(!prob(prb)) + return 0 //you lucked out, no shock for you + var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread + s.set_up(5, 1, src) + s.start() //sparks always. + if (electrocute_mob(user, get_area(src), src)) + return 1 + else + return 0 + +/obj/machinery/alarm/proc/refresh_all() + for(var/id_tag in alarm_area.air_vent_names) + var/list/I = alarm_area.air_vent_info[id_tag] + if (I && I["timestamp"]+AALARM_REPORT_TIMEOUT/2 > world.time) + continue + send_signal(id_tag, list("status") ) + for(var/id_tag in alarm_area.air_scrub_names) + var/list/I = alarm_area.air_scrub_info[id_tag] + if (I && I["timestamp"]+AALARM_REPORT_TIMEOUT/2 > world.time) + continue + send_signal(id_tag, list("status") ) + +/obj/machinery/alarm/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + radio_connection = SSradio.add_object(src, frequency, RADIO_TO_AIRALARM) + +/obj/machinery/alarm/proc/send_signal(target, list/command)//sends signal 'command' to 'target'. Returns 0 if no radio connection, 1 otherwise + if(!radio_connection) + return 0 + + var/datum/signal/signal = new + signal.transmission_method = 1 //radio signal + signal.source = src + + signal.data = command + signal.data["tag"] = target + signal.data["sigtype"] = "command" + + radio_connection.post_signal(src, signal, RADIO_FROM_AIRALARM) +// world << text("Signal [] Broadcasted to []", command, target) + + return 1 + +/obj/machinery/alarm/proc/populate_status(list/data) + var/turf/location = get_turf(src) + if(!location) + return + var/datum/gas_mixture/environment = location.return_air() + var/total = environment.oxygen + environment.nitrogen + environment.carbon_dioxide + environment.toxins + + var/list/environment_data = list() + data["atmos_alarm"] = alarm_area.atmosalm + data["fire_alarm"] = (alarm_area.fire != null && alarm_area.fire) + data["danger_level"] = danger_level + if(total) + var/datum/tlv/cur_tlv + var/partial_pressure = R_IDEAL_GAS_EQUATION*environment.temperature/environment.volume + + cur_tlv = TLV["pressure"] + var/pressure = environment.return_pressure() + var/pressure_danger = cur_tlv.get_danger_level(pressure) + environment_data += list(list("name" = "Pressure", "value" = pressure, "unit" = "kPa", "danger_level" = pressure_danger)) + + cur_tlv = TLV["oxygen"] + var/oxygen_danger = cur_tlv.get_danger_level(environment.oxygen*partial_pressure) + environment_data += list(list("name" = "Oxygen", "value" = environment.oxygen / total * 100, "unit" = "%", "danger_level" = oxygen_danger)) + + cur_tlv = TLV["nitrogen"] + var/nitrogen_danger = cur_tlv.get_danger_level(environment.nitrogen*partial_pressure) + environment_data += list(list("name" = "Nitrogen", "value" = environment.nitrogen / total * 100, "unit" = "%", "danger_level" = nitrogen_danger)) + + cur_tlv = TLV["carbon dioxide"] + var/carbon_dioxide_danger = cur_tlv.get_danger_level(environment.carbon_dioxide*partial_pressure) + environment_data += list(list("name" = "Carbon Dioxide", "value" = environment.carbon_dioxide / total * 100, "unit" = "%", "danger_level" = carbon_dioxide_danger)) + + cur_tlv = TLV["plasma"] + var/plasma_danger = cur_tlv.get_danger_level(environment.toxins*partial_pressure) + environment_data += list(list("name" = "Toxins", "value" = environment.toxins / total * 100, "unit" = "%", "danger_level" = plasma_danger)) + + cur_tlv = TLV["other"] + var/other_moles = 0 + for(var/datum/gas/G in environment.trace_gases) + other_moles+=G.moles + var/other_danger = cur_tlv.get_danger_level(other_moles*partial_pressure) + environment_data += list(list("name" = "Other", "value" = other_moles / total * 100, "unit" = "%", "danger_level" = other_danger)) + + cur_tlv = TLV["temperature"] + var/temperature_danger = cur_tlv.get_danger_level(environment.temperature) + environment_data += list(list("name" = "Temperature", "value" = environment.temperature, "unit" = "K ([round(environment.temperature - T0C, 0.1)]C)", "danger_level" = temperature_danger)) + + data["environment_data"] = environment_data + +/obj/machinery/alarm/proc/populate_controls(list/data) + switch(screen) + if(AALARM_SCREEN_MAIN) + data["mode"] = mode + if(AALARM_SCREEN_VENT) + data["vents"] = list() + for(var/id_tag in alarm_area.air_vent_names) + var/long_name = alarm_area.air_vent_names[id_tag] + var/list/info = alarm_area.air_vent_info[id_tag] + if(!info) + continue + data["vents"] += list(list( + "id_tag" = id_tag, + "long_name" = sanitize(long_name), + "power" = info["power"], + "checks" = info["checks"], + "excheck" = info["checks"]&1, + "incheck" = info["checks"]&2, + "direction" = info["direction"], + "external" = info["external"], + "extdefault"= (info["external"] == ONE_ATMOSPHERE) + )) + if(AALARM_SCREEN_SCRUB) + data["scrubbers"] = list() + for(var/id_tag in alarm_area.air_scrub_names) + var/long_name = alarm_area.air_scrub_names[id_tag] + var/list/info = alarm_area.air_scrub_info[id_tag] + if(!info) + continue + data["scrubbers"] += list(list( + "id_tag" = id_tag, + "long_name" = sanitize(long_name), + "power" = info["power"], + "scrubbing" = info["scrubbing"], + "widenet" = info["widenet"], + "filter_co2" = info["filter_co2"], + "filter_toxins" = info["filter_toxins"], + "filter_n2o" = info["filter_n2o"] + )) + if(AALARM_SCREEN_MODE) + data["mode"] = mode + data["modes"] = list() + data["modes"] += list(list("name" = "Filtering - Scrubs out contaminants", "mode" = AALARM_MODE_SCRUBBING, "selected" = mode == AALARM_MODE_SCRUBBING, "danger" = 0)) + data["modes"] += list(list("name" = "Contaminated - Scrubs out ALL contaminants quickly","mode" = AALARM_MODE_CONTAMINATED, "selected" = mode == AALARM_MODE_CONTAMINATED, "danger" = 0)) + data["modes"] += list(list("name" = "Draught - Siphons out air while replacing", "mode" = AALARM_MODE_VENTING, "selected" = mode == AALARM_MODE_VENTING, "danger" = 0)) + data["modes"] += list(list("name" = "Refill - Triple vent output", "mode" = AALARM_MODE_REFILL, "selected" = mode == AALARM_MODE_REFILL, "danger" = 0)) + data["modes"] += list(list("name" = "Cycle - Siphons air before replacing", "mode" = AALARM_MODE_REPLACEMENT, "selected" = mode == AALARM_MODE_REPLACEMENT, "danger" = 1)) + data["modes"] += list(list("name" = "Siphon - Siphons air out of the room", "mode" = AALARM_MODE_SIPHON, "selected" = mode == AALARM_MODE_SIPHON, "danger" = 1)) + data["modes"] += list(list("name" = "Panic Siphon - Siphons air out of the room quickly","mode" = AALARM_MODE_PANIC, "selected" = mode == AALARM_MODE_PANIC, "danger" = 1)) + data["modes"] += list(list("name" = "Off - Shuts off vents and scrubbers", "mode" = AALARM_MODE_OFF, "selected" = mode == AALARM_MODE_OFF, "danger" = 0)) + if (src.emagged) + data["modes"] += list(list("name" = "Flood - Shuts off scrubbers and opens vents", "mode" = AALARM_MODE_FLOOD, "selected" = mode == AALARM_MODE_FLOOD, "danger" = 1)) + if(AALARM_SCREEN_SENSORS) + var/datum/tlv/selected + var/list/thresholds = list() + + var/list/gas_names = list( + "oxygen" = "O2", + "nitrogen" = "N2", + "carbon dioxide" = "CO2", + "plasma" = "Toxin", + "other" = "Other") + for (var/g in gas_names) + thresholds += list(list("name" = gas_names[g], "settings" = list())) + selected = TLV[g] + thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = "min2", "selected" = selected.min2)) + thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = "min1", "selected" = selected.min1)) + thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = "max1", "selected" = selected.max1)) + thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = "max2", "selected" = selected.max2)) + + selected = TLV["pressure"] + thresholds += list(list("name" = "Pressure", "settings" = list())) + thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "min2", "selected" = selected.min2)) + thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "min1", "selected" = selected.min1)) + thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "max1", "selected" = selected.max1)) + thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "max2", "selected" = selected.max2)) + + selected = TLV["temperature"] + thresholds += list(list("name" = "Temperature", "settings" = list())) + thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "min2", "selected" = selected.min2)) + thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "min1", "selected" = selected.min1)) + thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "max1", "selected" = selected.max1)) + thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "max2", "selected" = selected.max2)) + + + data["thresholds"] = thresholds + +/obj/machinery/alarm/ui_act(action, params) + if(..()) + return + + if (buildstage != 2) + return + + if (locked && !usr.has_unlimited_silicon_privilege) + return + + if (usr.has_unlimited_silicon_privilege && src.aidisabled) + return + + switch(action) + if("toggleaccess") + if(usr.has_unlimited_silicon_privilege && !wires.IsIndexCut(AALARM_WIRE_IDSCAN)) + locked = !locked + if("adjust") + var/device_id = params["id_tag"] + switch(params["command"]) + if("set_external_pressure") + var/input_pressure = input("Enter target pressure:", "Pressure Controls") as num|null + if(isnum(input_pressure)) + send_signal(device_id, list(params["command"] = input_pressure)) + if("reset_external_pressure") + send_signal(device_id, list("set_external_pressure" = ONE_ATMOSPHERE)) + if( + "power", + "adjust_external_pressure", + "co2_scrub", + "tox_scrub", + "n2o_scrub", + "widenet", + "scrubbing" + ) + send_signal(device_id, list (params["command"] = text2num(params["val"]))) + if ("excheck") + send_signal(device_id, list ("checks" = text2num(params["val"])^1)) + if ("incheck") + send_signal(device_id, list ("checks" = text2num(params["val"])^2)) + if("set_threshold") + var/env = params["env"] + var/varname = params["var"] + var/datum/tlv/tlv = TLV[env] + var/newval = input("Enter [varname] for [env]:", "Alarm Triggers", tlv.vars[varname]) as num|null + if (isnull(newval)) + return + if (newval<0) + tlv.vars[varname] = -1 + else if (env=="temperature" && newval>5000) + tlv.vars[varname] = 5000 + else if (env=="pressure" && newval>50*ONE_ATMOSPHERE) + tlv.vars[varname] = 50*ONE_ATMOSPHERE + else if (env!="temperature" && env!="pressure" && newval>200) + tlv.vars[varname] = 200 + else + newval = round(newval,0.01) + tlv.vars[varname] = newval + if("screen") + screen = text2num(params["screen"]) + if("mode") + mode = text2num(params["mode"]) + apply_mode() + if("alarm") + if(alarm_area.atmosalert(2, src)) + post_alert(2) + update_icon() + if("reset") + if(alarm_area.atmosalert(0, src)) + post_alert(0) + update_icon() + return 1 + +/obj/machinery/alarm/proc/apply_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, + "tox_scrub"= 0, + "n2o_scrub"= 0, + "scrubbing"= 1, + "widenet"= 0, + )) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list( + "power"= 1, + "checks"= 1, + "set_external_pressure"= ONE_ATMOSPHERE + )) + if(AALARM_MODE_CONTAMINATED) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list( + "power"= 1, + "co2_scrub"= 1, + "tox_scrub"= 1, + "n2o_scrub"= 1, + "scrubbing"= 1, + "widenet"= 1, + )) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list( + "power"= 1, + "checks"= 1, + "set_external_pressure"= ONE_ATMOSPHERE + )) + if(AALARM_MODE_VENTING) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list( + "power"= 1, + "widenet"= 0, + "scrubbing"= 0 + )) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list( + "power"= 1, + "checks"= 1, + "set_external_pressure" = ONE_ATMOSPHERE*2 + )) + if(AALARM_MODE_REFILL) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list( + "power"= 1, + "co2_scrub"= 1, + "tox_scrub"= 0, + "n2o_scrub"= 0, + "scrubbing"= 1, + "widenet"= 0, + )) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list( + "power"= 1, + "checks"= 1, + "set_external_pressure" = ONE_ATMOSPHERE*3 + )) + if( + AALARM_MODE_PANIC, + AALARM_MODE_REPLACEMENT + ) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list( + "power"= 1, + "widenet"= 1, + "scrubbing"= 0 + )) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list( + "power"= 0 + )) + if( + AALARM_MODE_SIPHON + ) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list( + "power"= 1, + "widenet"= 0, + "scrubbing"= 0 + )) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list( + "power"= 0 + )) + + if(AALARM_MODE_OFF) + 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"= 0 + )) + if(AALARM_MODE_FLOOD) + 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"= 0, + )) + +/obj/machinery/alarm/update_icon() + if(panel_open) + switch(buildstage) + if(2) + icon_state = "alarmx" + if(1) + icon_state = "alarm_b2" + if(0) + icon_state = "alarm_b1" + return + + if((stat & (NOPOWER|BROKEN)) || shorted) + icon_state = "alarmp" + return + switch(max(danger_level, alarm_area.atmosalm)) + if (0) + src.icon_state = "alarm0" + if (1) + src.icon_state = "alarm2" //yes, alarm2 is yellow alarm + if (2) + src.icon_state = "alarm1" + +/obj/machinery/alarm/process() + if((stat & (NOPOWER|BROKEN)) || shorted) + return + + var/turf/simulated/location = src.loc + if (!istype(location)) + return 0 + + var/datum/gas_mixture/environment = location.return_air() + + var/datum/tlv/cur_tlv + var/GET_PP = R_IDEAL_GAS_EQUATION*environment.temperature/environment.volume + + cur_tlv = TLV["pressure"] + var/environment_pressure = environment.return_pressure() + var/pressure_dangerlevel = cur_tlv.get_danger_level(environment_pressure) + + cur_tlv = TLV["oxygen"] + var/oxygen_dangerlevel = cur_tlv.get_danger_level(environment.oxygen*GET_PP) + + cur_tlv = TLV["carbon dioxide"] + var/co2_dangerlevel = cur_tlv.get_danger_level(environment.carbon_dioxide*GET_PP) + + cur_tlv = TLV["plasma"] + var/plasma_dangerlevel = cur_tlv.get_danger_level(environment.toxins*GET_PP) + + cur_tlv = TLV["other"] + var/other_moles = 0 + for(var/datum/gas/G in environment.trace_gases) + other_moles+=G.moles + var/other_dangerlevel = cur_tlv.get_danger_level(other_moles*GET_PP) + + cur_tlv = TLV["temperature"] + var/temperature_dangerlevel = cur_tlv.get_danger_level(environment.temperature) + + var/old_danger_level = danger_level + danger_level = max( + pressure_dangerlevel, + oxygen_dangerlevel, + co2_dangerlevel, + plasma_dangerlevel, + other_dangerlevel, + temperature_dangerlevel + ) + if (old_danger_level!=danger_level) + apply_danger_level() + + if (mode==AALARM_MODE_REPLACEMENT && environment_pressureYou cut the final wires." + var/obj/item/stack/cable_coil/cable = new /obj/item/stack/cable_coil( src.loc ) + cable.amount = 5 + buildstage = 1 + update_icon() + return + + if(istype(W, /obj/item/weapon/screwdriver)) // Opening that Air Alarm up. + playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1) + panel_open = !panel_open + user << "The wires have been [panel_open ? "exposed" : "unexposed"]." + update_icon() + return + + if (panel_open && ((istype(W, /obj/item/device/multitool) || istype(W, /obj/item/weapon/wirecutters)))) + return src.attack_hand(user) + else if (istype(W, /obj/item/weapon/card/id) || istype(W, /obj/item/device/pda))// trying to unlock the interface with an ID card + if(stat & (NOPOWER|BROKEN)) + user << "It does nothing!" + else + if(src.allowed(usr) && !wires.IsIndexCut(AALARM_WIRE_IDSCAN)) + locked = !locked + user << "You [ locked ? "lock" : "unlock"] the air alarm interface." + src.updateUsrDialog() + else + user << "Access denied." + return + if(1) + if(istype(W, /obj/item/weapon/crowbar)) + user.visible_message("[user.name] removes the electronics from [src.name].",\ + "You start prying out the circuit...") + playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1) + if (do_after(user, 20/W.toolspeed, target = src)) + if (buildstage == 1) + user <<"You remove the air alarm electronics." + new /obj/item/weapon/electronics/airalarm( src.loc ) + playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) + buildstage = 0 + update_icon() + return + + if(istype(W, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/cable = W + if(cable.get_amount() < 5) + user << "You need five lengths of cable to wire the fire alarm!" + return + user.visible_message("[user.name] wires the air alarm.", \ + "You start wiring the air alarm...") + if (do_after(user, 20, target = src)) + if (cable.get_amount() >= 5 && buildstage == 1) + cable.use(5) + user << "You wire the air alarm." + wires.wires_status = 0 + aidisabled = 0 + locked = 1 + mode = 1 + shorted = 0 + post_alert(0) + buildstage = 2 + update_icon() + return + if(0) + if(istype(W, /obj/item/weapon/electronics/airalarm)) + if(user.unEquip(W)) + user << "You insert the circuit." + buildstage = 1 + update_icon() + qdel(W) + return + + if(istype(W, /obj/item/weapon/wrench)) + user << "You detach \the [src] from the wall." + playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1) + new /obj/item/wallframe/alarm( user.loc ) + qdel(src) + return + + return ..() + +/obj/machinery/alarm/power_change() + if(powered(power_channel)) + stat &= ~NOPOWER + else + stat |= NOPOWER + spawn(rand(0,15)) + if(loc) + update_icon() + + +/obj/machinery/alarm/emag_act(mob/user) + if(!emagged) + src.emagged = 1 + if(user) + user.visible_message("Sparks fly out of the [src]!", "You emag the [src], disabling its safeties.") + playsound(src.loc, 'sound/effects/sparks4.ogg', 50, 1) + return + + +/* +AIR ALARM CIRCUIT +Just a object used in constructing air alarms +*/ +/obj/item/weapon/electronics/airalarm + name = "air alarm electronics" + icon_state = "airalarm_electronics" + +/* +AIR ALARM ITEM +Handheld air alarm frame, for placing on walls +*/ +/obj/item/wallframe/alarm + name = "air alarm frame" + desc = "Used for building Air Alarms" + icon = 'icons/obj/monitors.dmi' + icon_state = "alarm_bitem" + result_path = /obj/machinery/alarm + + +/* +FIRE ALARM +*/ +/obj/machinery/firealarm + name = "fire alarm" + desc = "\"Pull this in case of emergency\". Thus, keep pulling it forever." + icon = 'icons/obj/monitors.dmi' + icon_state = "fire0" + var/detecting = 1 + var/time = 10 + var/timing = 0 + var/lockdownbyai = 0 + anchored = 1 + use_power = 1 + idle_power_usage = 2 + active_power_usage = 6 + power_channel = ENVIRON + var/last_process = 0 + var/buildstage = 2 // 2 = complete, 1 = no wires, 0 = circuit gone + +/obj/machinery/firealarm/update_icon() + src.overlays = list() + + var/area/A = src.loc + A = A.loc + + if(panel_open) + switch(buildstage) + if(0) + icon_state="fire_b0" + return + if(1) + icon_state="fire_b1" + return + if(2) + icon_state="fire_b2" + + if((stat & BROKEN) || (stat & NOPOWER)) + return + + overlays += "overlay_[security_level]" + return + + if(stat & BROKEN) + icon_state = "firex" + return + + icon_state = "fire0" + + if(stat & NOPOWER) + return + + overlays += "overlay_[security_level]" + + if(!src.detecting) + overlays += "overlay_fire" + else + overlays += "overlay_[A.fire ? "fire" : "clear"]" + + + +/obj/machinery/firealarm/emag_act(mob/user) + if(!emagged) + src.emagged = 1 + if(user) + user.visible_message("Sparks fly out of the [src]!", "You emag the [src], disabling its thermal sensors.") + playsound(src.loc, 'sound/effects/sparks4.ogg', 50, 1) + return + + +/obj/machinery/firealarm/temperature_expose(datum/gas_mixture/air, temperature, volume) + if(src.detecting) + if(temperature > T0C+200) + if(!emagged) //Doesn't give off alarm when emagged + src.alarm() // added check of detector status here + return + +/obj/machinery/firealarm/attack_ai(mob/user as mob) + return src.attack_hand(user) + +/obj/machinery/firealarm/bullet_act(BLAH) + return src.alarm() + +/obj/machinery/firealarm/attack_paw(mob/user) + return src.attack_hand(user) + +/obj/machinery/firealarm/emp_act(severity) + if(prob(50/severity)) alarm() + ..() + +/obj/machinery/firealarm/attackby(obj/item/W, mob/user, params) + add_fingerprint(user) + + if(istype(W, /obj/item/weapon/screwdriver) && buildstage == 2) + playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1) + panel_open = !panel_open + user << "The wires have been [panel_open ? "exposed" : "unexposed"]." + update_icon() + return + + if(panel_open) + switch(buildstage) + if(2) + if(istype(W, /obj/item/device/multitool)) + src.detecting = !( src.detecting ) + if (src.detecting) + user.visible_message("[user] has reconnected [src]'s detecting unit!", "You reconnect [src]'s detecting unit.") + else + user.visible_message("[user] has disconnected [src]'s detecting unit!", "You disconnect [src]'s detecting unit.") + return + + else if (istype(W, /obj/item/weapon/wirecutters)) + buildstage = 1 + playsound(src.loc, 'sound/items/Wirecutter.ogg', 50, 1) + var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil() + coil.amount = 5 + coil.loc = user.loc + user << "You cut the wires from \the [src]." + update_icon() + return + if(1) + if(istype(W, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/coil = W + if(coil.get_amount() < 5) + user << "You need more cable for this!" + else + coil.use(5) + buildstage = 2 + user << "You wire \the [src]." + update_icon() + return + + else if(istype(W, /obj/item/weapon/crowbar)) + playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1) + user.visible_message("[user.name] removes the electronics from [src.name].", \ + "You start prying out the circuit...") + if(do_after(user, 20/W.toolspeed, target = src)) + if(buildstage == 1) + if(stat & BROKEN) + user << "You remove the destroyed circuit." + else + user << "You pry out the circuit." + new /obj/item/weapon/electronics/firealarm(user.loc) + buildstage = 0 + update_icon() + return + if(0) + if(istype(W, /obj/item/weapon/electronics/firealarm)) + user << "You insert the circuit." + qdel(W) + buildstage = 1 + update_icon() + return + + else if(istype(W, /obj/item/weapon/wrench)) + user.visible_message("[user] removes the fire alarm assembly from the wall.", \ + "You remove the fire alarm assembly from the wall.") + var/obj/item/wallframe/firealarm/frame = new /obj/item/wallframe/firealarm() + frame.loc = user.loc + playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1) + qdel(src) + return + return ..() + +/obj/machinery/firealarm/process()//Note: this processing was mostly phased out due to other code, and only runs when needed + if(stat & (NOPOWER|BROKEN)) + return + + if(src.timing) + if(src.time > 0) + src.time = src.time - ((world.timeofday - last_process)/10) + else + src.alarm() + src.time = 0 + src.timing = 0 + SSobj.processing.Remove(src) + src.updateDialog() + last_process = world.timeofday + return + +/obj/machinery/firealarm/power_change() + if(powered(ENVIRON)) + stat &= ~NOPOWER + else + stat |= NOPOWER + spawn(rand(0,15)) + if(loc) + update_icon() + +/obj/machinery/firealarm/attack_hand(mob/user) + if((user.stat && !IsAdminGhost(user)) || stat & (NOPOWER|BROKEN)) + return + + if (buildstage != 2) + return + + user.set_machine(src) + var/area/A = src.loc + var/safety_warning + var/d1 + var/d2 + var/dat = "" + if (istype(user, /mob/living/carbon/human) || user.has_unlimited_silicon_privilege) + A = A.loc + if (src.emagged) + safety_warning = text("NOTICE: Thermal sensors nonfunctional. Device will not report or recognize high temperatures.") + else + safety_warning = text("Safety measures functioning properly.") + if (A.fire) + d1 = text("Reset - Lockdown", src) + else + d1 = text("Alarm - Lockdown", src) + if (src.timing) + d2 = text("Stop Time Lock", src) + else + d2 = text("Initiate Time Lock", src) + var/second = round(src.time) % 60 + var/minute = (round(src.time) - second) / 60 + dat = "[safety_warning]

[d1]
The current alert level is: [get_security_level()]

Timer System: [d2]
Time Left: - - [(minute ? "[minute]:" : null)][second] + +" + //user << browse(dat, "window=firealarm") + //onclose(user, "firealarm") + else + A = A.loc + if (A.fire) + d1 = text("[]", src, stars("Reset - Lockdown")) + else + d1 = text("[]", src, stars("Alarm - Lockdown")) + if (src.timing) + d2 = text("[]", src, stars("Stop Time Lock")) + else + d2 = text("[]", src, stars("Initiate Time Lock")) + var/second = round(src.time) % 60 + var/minute = (round(src.time) - second) / 60 + dat = "[d1]
The current alert level is: [stars(get_security_level())]

Timer System: [d2]
Time Left: - - [(minute ? text("[]:", minute) : null)][second] + +" + //user << browse(dat, "window=firealarm") + //onclose(user, "firealarm") + var/datum/browser/popup = new(user, "firealarm", "Fire Alarm") + popup.set_content(dat) + popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) + popup.open() + return + +/obj/machinery/firealarm/Topic(href, href_list) + if(..()) + return + + if (buildstage != 2) + return + + usr.set_machine(src) + if (href_list["reset"]) + src.reset() + else if (href_list["alarm"]) + src.alarm() + else if (href_list["time"]) + src.timing = text2num(href_list["time"]) + last_process = world.timeofday + SSobj.processing |= src + else if (href_list["tp"]) + var/tp = text2num(href_list["tp"]) + src.time += tp + src.time = min(max(round(src.time), 0), 120) + + src.updateUsrDialog() + +/obj/machinery/firealarm/proc/reset() + if (stat & (NOPOWER|BROKEN)) // can't reset alarm if it's unpowered or broken. + return + var/area/A = get_area(src) + A.firereset(src) + return + +/obj/machinery/firealarm/proc/alarm() + if (stat & (NOPOWER|BROKEN)) // can't activate alarm if it's unpowered or broken. + return + var/area/A = get_area(src) + if(!A.fire) + A.firealert(src) + //playsound(src.loc, 'sound/ambience/signal.ogg', 75, 0) + return + +/obj/machinery/firealarm/New(loc, ndir, building) + ..() + + if(ndir) + src.dir = ndir + + if(building) + buildstage = 0 + panel_open = 1 + pixel_x = (dir & 3)? 0 : (dir == 4 ? -24 : 24) + pixel_y = (dir & 3)? (dir ==1 ? -24 : 24) : 0 + + if(z == 1) + if(security_level) + src.overlays += image('icons/obj/monitors.dmi', "overlay_[get_security_level()]") + else + src.overlays += image('icons/obj/monitors.dmi', "overlay_green") + + update_icon() + +/* +FIRE ALARM CIRCUIT +Just a object used in constructing fire alarms +*/ +/obj/item/weapon/electronics/firealarm + name = "fire alarm electronics" + desc = "A circuit. It has a label on it, it says \"Can handle heat levels up to 40 degrees celsius!\"" + + +/* +FIRE ALARM ITEM +Handheld fire alarm frame, for placing on walls +*/ +/obj/item/wallframe/firealarm + name = "fire alarm frame" + desc = "Used for building Fire Alarms" + icon = 'icons/obj/monitors.dmi' + icon_state = "fire_bitem" + result_path = /obj/machinery/firealarm + +/* + * Party button + */ + +/obj/machinery/firealarm/partyalarm + name = "\improper PARTY BUTTON" + desc = "Cuban Pete is in the house!" + +/obj/machinery/firealarm/partyalarm/attack_hand(mob/user) + if((user.stat && !IsAdminGhost(user)) || stat & (NOPOWER|BROKEN)) + return + + if (buildstage != 2) + return + + user.set_machine(src) + var/area/A = src.loc + var/d1 + var/dat + if (istype(user, /mob/living/carbon/human) || user.has_unlimited_silicon_privilege) + A = A.loc + + if (A.party) + d1 = text("No Party :(", src) + else + d1 = text("PARTY!!!", src) + dat = text("Party Button []", d1) + + else + A = A.loc + if (A.fire) + d1 = text("[]", src, stars("No Party :(")) + else + d1 = text("[]", src, stars("PARTY!!!")) + dat = text("[] []", stars("Party Button"), d1) + + var/datum/browser/popup = new(user, "firealarm", "Party Alarm") + popup.set_content(dat) + popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) + popup.open() + return + +/obj/machinery/firealarm/partyalarm/reset() + if (stat & (NOPOWER|BROKEN)) + return + var/area/A = src.loc + A = A.loc + if (!( istype(A, /area) )) + return + for(var/area/RA in A.related) + RA.partyreset() + return + +/obj/machinery/firealarm/partyalarm/alarm() + if (stat & (NOPOWER|BROKEN)) + return + var/area/A = src.loc + A = A.loc + if (!( istype(A, /area) )) + return + for(var/area/RA in A.related) + RA.partyalert() + return diff --git a/code/game/machinery/atmoalter/canister.dm b/code/game/machinery/atmoalter/canister.dm index f201c663091..3d0cb1bd520 100644 --- a/code/game/machinery/atmoalter/canister.dm +++ b/code/game/machinery/atmoalter/canister.dm @@ -1,433 +1,433 @@ -#define CAN_MAX_RELEASE_PRESSURE (ONE_ATMOSPHERE*10) -#define CAN_MIN_RELEASE_PRESSURE (ONE_ATMOSPHERE/10) -#define CAN_DEFAULT_RELEASE_PRESSURE (ONE_ATMOSPHERE) - -/obj/machinery/portable_atmospherics/canister - name = "canister" - desc = "A canister for the storage of gas." - icon = 'icons/obj/atmos.dmi' - icon_state = "yellow" - density = 1 - var/health = 100 - - var/valve_open = 0 - var/release_pressure = ONE_ATMOSPHERE - - var/canister_color = "yellow" - var/can_label = 1 - var/filled = 0.5 - pressure_resistance = 7*ONE_ATMOSPHERE - var/temperature_resistance = 1000 + T0C - volume = 1000 - use_power = 0 - var/release_log = "" - var/update_flag = 0 - - -/obj/machinery/portable_atmospherics/canister/sleeping_agent - name = "canister: \[N2O\]" - desc = "Nitrous oxide gas. Known to cause drowsiness." - icon_state = "redws" - canister_color = "redws" - can_label = 0 -/obj/machinery/portable_atmospherics/canister/nitrogen - name = "canister: \[N2\]" - desc = "Nitrogen gas. Reportedly useful for something." - icon_state = "red" - canister_color = "red" - can_label = 0 -/obj/machinery/portable_atmospherics/canister/oxygen - name = "canister: \[O2\]" - desc = "Oxygen. Necessary for human life." - icon_state = "blue" - canister_color = "blue" - can_label = 0 -/obj/machinery/portable_atmospherics/canister/toxins - name = "canister \[Plasma\]" - desc = "Plasma gas. The reason YOU are here. Highly toxic." - icon_state = "orange" - canister_color = "orange" - can_label = 0 -/obj/machinery/portable_atmospherics/canister/carbon_dioxide - name = "canister \[CO2\]" - desc = "Carbon dioxide. What the fuck is carbon dioxide?" - icon_state = "black" - canister_color = "black" - can_label = 0 -/obj/machinery/portable_atmospherics/canister/air - name = "canister \[Air\]" - desc = "Pre-mixed air." - icon_state = "grey" - canister_color = "grey" - can_label = 0 - -/obj/machinery/portable_atmospherics/canister/proc/check_change() - var/old_flag = update_flag - update_flag = 0 - if(holding) - update_flag |= 1 - if(connected_port) - update_flag |= 2 - - var/tank_pressure = air_contents.return_pressure() - if(tank_pressure < 10) - update_flag |= 4 - else if(tank_pressure < ONE_ATMOSPHERE) - update_flag |= 8 - else if(tank_pressure < 15*ONE_ATMOSPHERE) - update_flag |= 16 - else - update_flag |= 32 - - if(update_flag == old_flag) - return 1 - else - return 0 - -/obj/machinery/portable_atmospherics/canister/update_icon() -/* -update_flag -1 = holding -2 = connected_port -4 = tank_pressure < 10 -8 = tank_pressure < ONE_ATMOS -16 = tank_pressure < 15*ONE_ATMOS -32 = tank_pressure go boom. -*/ - - if (destroyed) - overlays = 0 - icon_state = text("[]-1", canister_color) - return - - if(icon_state != "[canister_color]") - icon_state = "[canister_color]" - - if(check_change()) //Returns 1 if no change needed to icons. - return - - src.overlays = 0 - - if(update_flag & 1) - overlays += "can-open" - if(update_flag & 2) - overlays += "can-connector" - if(update_flag & 4) - overlays += "can-o0" - if(update_flag & 8) - overlays += "can-o1" - else if(update_flag & 16) - overlays += "can-o2" - else if(update_flag & 32) - overlays += "can-o3" - return - - -/obj/machinery/portable_atmospherics/canister/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) - if(exposed_temperature > temperature_resistance) - health -= 5 - healthcheck() - -/obj/machinery/portable_atmospherics/canister/proc/healthcheck() - if(destroyed) - return 1 - - if (src.health <= 10) - var/atom/location = src.loc - location.assume_air(air_contents) - air_update_turf() - - src.destroyed = 1 - playsound(src.loc, 'sound/effects/spray.ogg', 10, 1, -3) - src.density = 0 - update_icon() - investigate_log("was destroyed by heat/gunfire.", "atmos") - - if (src.holding) - src.holding.loc = src.loc - src.holding = null - - return 1 - else - return 1 - -/obj/machinery/portable_atmospherics/canister/process_atmos() - if (destroyed) - return PROCESS_KILL - - ..() - - if(valve_open) - var/datum/gas_mixture/environment - if(holding) - environment = holding.air_contents - else - environment = loc.return_air() - - var/env_pressure = environment.return_pressure() - var/pressure_delta = min(release_pressure - env_pressure, (air_contents.return_pressure() - env_pressure)/2) - //Can not have a pressure delta that would cause environment pressure > tank pressure - - var/transfer_moles = 0 - if((air_contents.temperature > 0) && (pressure_delta > 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) - air_update_turf() - src.update_icon() - - - if(air_contents.return_pressure() < 1) - can_label = 1 - else - can_label = 0 - -/obj/machinery/portable_atmospherics/canister/process() - src.updateDialog() - return ..() - -/obj/machinery/portable_atmospherics/canister/return_air() - return air_contents - -/obj/machinery/portable_atmospherics/canister/proc/return_temperature() - var/datum/gas_mixture/GM = src.return_air() - if(GM && GM.volume>0) - return GM.temperature - return 0 - -/obj/machinery/portable_atmospherics/canister/proc/return_pressure() - var/datum/gas_mixture/GM = src.return_air() - if(GM && GM.volume>0) - return GM.return_pressure() - return 0 - -/obj/machinery/portable_atmospherics/canister/blob_act() - src.health -= 200 - healthcheck() - return - -/obj/machinery/portable_atmospherics/canister/bullet_act(obj/item/projectile/Proj) - if((Proj.damage_type == BRUTE || Proj.damage_type == BURN)) - if(Proj.damage) - src.health -= round(Proj.damage / 2) - healthcheck() - ..() - -/obj/machinery/portable_atmospherics/canister/ex_act(severity, target) - switch(severity) - if(1) - if(destroyed || prob(30)) - qdel(src) - else - src.health = 0 - healthcheck() - return - if(2) - if(destroyed) - qdel(src) - else - src.health -= rand(40, 100) - healthcheck() - return - if(3) - src.health -= rand(15,40) - healthcheck() - return - return - -/obj/machinery/portable_atmospherics/canister/attackby(obj/item/weapon/W, mob/user, params) - if(!istype(W, /obj/item/weapon/wrench) && !istype(W, /obj/item/weapon/tank) && !istype(W, /obj/item/device/analyzer) && !istype(W, /obj/item/device/pda)) - investigate_log("was smacked with \a [W] by [key_name(user)]", "atmos") - health -= W.force - add_fingerprint(user) - healthcheck() - - if(istype(user, /mob/living/silicon/robot) && istype(W, /obj/item/weapon/tank/jetpack)) - var/datum/gas_mixture/thejetpack = W:air_contents - var/env_pressure = thejetpack.return_pressure() - var/pressure_delta = min(10*ONE_ATMOSPHERE - env_pressure, (air_contents.return_pressure() - env_pressure)/2) - //Can not have a pressure delta that would cause environment pressure > tank pressure - var/transfer_moles = 0 - if((air_contents.temperature > 0) && (pressure_delta > 0)) - transfer_moles = pressure_delta*thejetpack.volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION)//Actually transfer the gas - var/datum/gas_mixture/removed = air_contents.remove(transfer_moles) - thejetpack.merge(removed) - user << "You pulse-pressurize your jetpack from the tank." - return - - ..() - -/obj/machinery/portable_atmospherics/canister/attack_hand(mob/user) - if (!user) - return - interact(user) - -/obj/machinery/portable_atmospherics/canister/interact(mob/user) - if (src.destroyed) - return - ui_interact(user) - -/obj/machinery/portable_atmospherics/canister/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 0) - ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open) - if (!ui) - ui = new(user, src, ui_key, "canister", name, 470, 470, state = physical_state) - ui.open() - -/obj/machinery/portable_atmospherics/canister/get_ui_data() - var/data = list() - data["name"] = name - data["canLabel"] = can_label ? 1 : 0 - data["portConnected"] = connected_port ? 1 : 0 - data["tankPressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0) - data["releasePressure"] = round(release_pressure ? release_pressure : 0) - data["defaultReleasePressure"] = round(CAN_DEFAULT_RELEASE_PRESSURE) - data["minReleasePressure"] = round(CAN_MIN_RELEASE_PRESSURE) - data["maxReleasePressure"] = round(CAN_MAX_RELEASE_PRESSURE) - data["valveOpen"] = valve_open ? 1 : 0 - - data["hasHoldingTank"] = holding ? 1 : 0 - if (holding) - data["holdingTank"] = list() - data["holdingTank"]["name"] = holding.name - data["holdingTank"]["tankPressure"] = round(holding.air_contents.return_pressure()) - return data - -/obj/machinery/portable_atmospherics/canister/ui_act(action, params) - if(..()) - return - - switch(action) - if("relabel") - if (can_label) - var/list/colors = list(\ - "\[N2O\]" = "redws", \ - "\[N2\]" = "red", \ - "\[O2\]" = "blue", \ - "\[Plasma\]" = "orange", \ - "\[CO2\]" = "black", \ - "\[Air\]" = "grey", \ - "\[CAUTION\]" = "yellow", \ - ) - var/label = input("Label canister:", "Gas Canister") as null|anything in colors - if(label) - src.canister_color = colors[label] - src.icon_state = colors[label] - src.name = "canister: [label]" - if("pressure") - switch(params["set"]) - if("custom") - var/custom = input(usr, "What rate do you set the regulator to? The dial reads from [CAN_MIN_RELEASE_PRESSURE] to [CAN_MAX_RELEASE_PRESSURE].") as null|num - if(custom) - release_pressure = custom - if("reset") - release_pressure = CAN_DEFAULT_RELEASE_PRESSURE - if("min") - release_pressure = CAN_MIN_RELEASE_PRESSURE - if("max") - release_pressure = CAN_MAX_RELEASE_PRESSURE - release_pressure = Clamp(round(release_pressure), CAN_MIN_RELEASE_PRESSURE, CAN_MAX_RELEASE_PRESSURE) - if("valve") - var/logmsg - if (valve_open) - if (holding) - logmsg = "Valve was closed by [key_name(usr)], stopping the transfer into the [holding]
" - else - logmsg = "Valve was closed by [key_name(usr)], stopping the transfer into the air
" - else - if (holding) - logmsg = "Valve was opened by [key_name(usr)], starting the transfer into the [holding]
" - else - logmsg = "Valve was opened by [key_name(usr)], starting the transfer into the air
" - if(air_contents.toxins > 0) - message_admins("[key_name_admin(usr)] (?) (FLW) opened a canister that contains plasma! (JMP)") - log_admin("[key_name(usr)] opened a canister that contains plasma at [x], [y], [z]") - var/datum/gas/sleeping_agent = locate(/datum/gas/sleeping_agent) in air_contents.trace_gases - if(sleeping_agent && (sleeping_agent.moles > 1)) - message_admins("[key_name_admin(usr)] (?) (FLW) opened a canister that contains N2O! (JMP)") - log_admin("[key_name(usr)] opened a canister that contains N2O at [x], [y], [z]") - investigate_log(logmsg, "atmos") - release_log += logmsg - valve_open = !valve_open - if("eject") - if(holding) - if (valve_open) - investigate_log("[key_name(usr)] removed the [holding], leaving the valve open and transfering into the air
", "atmos") - holding.loc = loc - holding = null - 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) - - src.update_icon() - return 1 - -/obj/machinery/portable_atmospherics/canister/oxygen/New() - ..() - - src.air_contents.oxygen = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - - src.update_icon() - return 1 - -/obj/machinery/portable_atmospherics/canister/sleeping_agent/New() - ..() - - 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) - - src.update_icon() - return 1 - -//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 - spawn(10) - var/turf/simulated/location = src.loc - if (istype(src.loc)) - while (!location.air) - sleep(10) - location.assume_air(air_contents) - air_contents = new - return 1 - -/obj/machinery/portable_atmospherics/canister/nitrogen/New() - - ..() - - src.air_contents.nitrogen = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - - src.update_icon() - return 1 - -/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) - - src.update_icon() - 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) - - src.update_icon() - return 1 +#define CAN_MAX_RELEASE_PRESSURE (ONE_ATMOSPHERE*10) +#define CAN_MIN_RELEASE_PRESSURE (ONE_ATMOSPHERE/10) +#define CAN_DEFAULT_RELEASE_PRESSURE (ONE_ATMOSPHERE) + +/obj/machinery/portable_atmospherics/canister + name = "canister" + desc = "A canister for the storage of gas." + icon = 'icons/obj/atmos.dmi' + icon_state = "yellow" + density = 1 + var/health = 100 + + var/valve_open = 0 + var/release_pressure = ONE_ATMOSPHERE + + var/canister_color = "yellow" + var/can_label = 1 + var/filled = 0.5 + pressure_resistance = 7*ONE_ATMOSPHERE + var/temperature_resistance = 1000 + T0C + volume = 1000 + use_power = 0 + var/release_log = "" + var/update_flag = 0 + + +/obj/machinery/portable_atmospherics/canister/sleeping_agent + name = "canister: \[N2O\]" + desc = "Nitrous oxide gas. Known to cause drowsiness." + icon_state = "redws" + canister_color = "redws" + can_label = 0 +/obj/machinery/portable_atmospherics/canister/nitrogen + name = "canister: \[N2\]" + desc = "Nitrogen gas. Reportedly useful for something." + icon_state = "red" + canister_color = "red" + can_label = 0 +/obj/machinery/portable_atmospherics/canister/oxygen + name = "canister: \[O2\]" + desc = "Oxygen. Necessary for human life." + icon_state = "blue" + canister_color = "blue" + can_label = 0 +/obj/machinery/portable_atmospherics/canister/toxins + name = "canister \[Plasma\]" + desc = "Plasma gas. The reason YOU are here. Highly toxic." + icon_state = "orange" + canister_color = "orange" + can_label = 0 +/obj/machinery/portable_atmospherics/canister/carbon_dioxide + name = "canister \[CO2\]" + desc = "Carbon dioxide. What the fuck is carbon dioxide?" + icon_state = "black" + canister_color = "black" + can_label = 0 +/obj/machinery/portable_atmospherics/canister/air + name = "canister \[Air\]" + desc = "Pre-mixed air." + icon_state = "grey" + canister_color = "grey" + can_label = 0 + +/obj/machinery/portable_atmospherics/canister/proc/check_change() + var/old_flag = update_flag + update_flag = 0 + if(holding) + update_flag |= 1 + if(connected_port) + update_flag |= 2 + + var/tank_pressure = air_contents.return_pressure() + if(tank_pressure < 10) + update_flag |= 4 + else if(tank_pressure < ONE_ATMOSPHERE) + update_flag |= 8 + else if(tank_pressure < 15*ONE_ATMOSPHERE) + update_flag |= 16 + else + update_flag |= 32 + + if(update_flag == old_flag) + return 1 + else + return 0 + +/obj/machinery/portable_atmospherics/canister/update_icon() +/* +update_flag +1 = holding +2 = connected_port +4 = tank_pressure < 10 +8 = tank_pressure < ONE_ATMOS +16 = tank_pressure < 15*ONE_ATMOS +32 = tank_pressure go boom. +*/ + + if (destroyed) + overlays = 0 + icon_state = text("[]-1", canister_color) + return + + if(icon_state != "[canister_color]") + icon_state = "[canister_color]" + + if(check_change()) //Returns 1 if no change needed to icons. + return + + src.overlays = 0 + + if(update_flag & 1) + overlays += "can-open" + if(update_flag & 2) + overlays += "can-connector" + if(update_flag & 4) + overlays += "can-o0" + if(update_flag & 8) + overlays += "can-o1" + else if(update_flag & 16) + overlays += "can-o2" + else if(update_flag & 32) + overlays += "can-o3" + return + + +/obj/machinery/portable_atmospherics/canister/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) + if(exposed_temperature > temperature_resistance) + health -= 5 + healthcheck() + +/obj/machinery/portable_atmospherics/canister/proc/healthcheck() + if(destroyed) + return 1 + + if (src.health <= 10) + var/atom/location = src.loc + location.assume_air(air_contents) + air_update_turf() + + src.destroyed = 1 + playsound(src.loc, 'sound/effects/spray.ogg', 10, 1, -3) + src.density = 0 + update_icon() + investigate_log("was destroyed by heat/gunfire.", "atmos") + + if (src.holding) + src.holding.loc = src.loc + src.holding = null + + return 1 + else + return 1 + +/obj/machinery/portable_atmospherics/canister/process_atmos() + if (destroyed) + return PROCESS_KILL + + ..() + + if(valve_open) + var/datum/gas_mixture/environment + if(holding) + environment = holding.air_contents + else + environment = loc.return_air() + + var/env_pressure = environment.return_pressure() + var/pressure_delta = min(release_pressure - env_pressure, (air_contents.return_pressure() - env_pressure)/2) + //Can not have a pressure delta that would cause environment pressure > tank pressure + + var/transfer_moles = 0 + if((air_contents.temperature > 0) && (pressure_delta > 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) + air_update_turf() + src.update_icon() + + + if(air_contents.return_pressure() < 1) + can_label = 1 + else + can_label = 0 + +/obj/machinery/portable_atmospherics/canister/process() + src.updateDialog() + return ..() + +/obj/machinery/portable_atmospherics/canister/return_air() + return air_contents + +/obj/machinery/portable_atmospherics/canister/proc/return_temperature() + var/datum/gas_mixture/GM = src.return_air() + if(GM && GM.volume>0) + return GM.temperature + return 0 + +/obj/machinery/portable_atmospherics/canister/proc/return_pressure() + var/datum/gas_mixture/GM = src.return_air() + if(GM && GM.volume>0) + return GM.return_pressure() + return 0 + +/obj/machinery/portable_atmospherics/canister/blob_act() + src.health -= 200 + healthcheck() + return + +/obj/machinery/portable_atmospherics/canister/bullet_act(obj/item/projectile/Proj) + if((Proj.damage_type == BRUTE || Proj.damage_type == BURN)) + if(Proj.damage) + src.health -= round(Proj.damage / 2) + healthcheck() + ..() + +/obj/machinery/portable_atmospherics/canister/ex_act(severity, target) + switch(severity) + if(1) + if(destroyed || prob(30)) + qdel(src) + else + src.health = 0 + healthcheck() + return + if(2) + if(destroyed) + qdel(src) + else + src.health -= rand(40, 100) + healthcheck() + return + if(3) + src.health -= rand(15,40) + healthcheck() + return + return + +/obj/machinery/portable_atmospherics/canister/attackby(obj/item/weapon/W, mob/user, params) + if(!istype(W, /obj/item/weapon/wrench) && !istype(W, /obj/item/weapon/tank) && !istype(W, /obj/item/device/analyzer) && !istype(W, /obj/item/device/pda)) + investigate_log("was smacked with \a [W] by [key_name(user)]", "atmos") + health -= W.force + add_fingerprint(user) + healthcheck() + + if(istype(user, /mob/living/silicon/robot) && istype(W, /obj/item/weapon/tank/jetpack)) + var/datum/gas_mixture/thejetpack = W:air_contents + var/env_pressure = thejetpack.return_pressure() + var/pressure_delta = min(10*ONE_ATMOSPHERE - env_pressure, (air_contents.return_pressure() - env_pressure)/2) + //Can not have a pressure delta that would cause environment pressure > tank pressure + var/transfer_moles = 0 + if((air_contents.temperature > 0) && (pressure_delta > 0)) + transfer_moles = pressure_delta*thejetpack.volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION)//Actually transfer the gas + var/datum/gas_mixture/removed = air_contents.remove(transfer_moles) + thejetpack.merge(removed) + user << "You pulse-pressurize your jetpack from the tank." + return + + ..() + +/obj/machinery/portable_atmospherics/canister/attack_hand(mob/user) + if (!user) + return + interact(user) + +/obj/machinery/portable_atmospherics/canister/interact(mob/user) + if (src.destroyed) + return + ui_interact(user) + +/obj/machinery/portable_atmospherics/canister/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 0) + ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open) + if (!ui) + ui = new(user, src, ui_key, "canister", name, 470, 470, state = physical_state) + ui.open() + +/obj/machinery/portable_atmospherics/canister/get_ui_data() + var/data = list() + data["name"] = name + data["canLabel"] = can_label ? 1 : 0 + data["portConnected"] = connected_port ? 1 : 0 + data["tankPressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0) + data["releasePressure"] = round(release_pressure ? release_pressure : 0) + data["defaultReleasePressure"] = round(CAN_DEFAULT_RELEASE_PRESSURE) + data["minReleasePressure"] = round(CAN_MIN_RELEASE_PRESSURE) + data["maxReleasePressure"] = round(CAN_MAX_RELEASE_PRESSURE) + data["valveOpen"] = valve_open ? 1 : 0 + + data["hasHoldingTank"] = holding ? 1 : 0 + if (holding) + data["holdingTank"] = list() + data["holdingTank"]["name"] = holding.name + data["holdingTank"]["tankPressure"] = round(holding.air_contents.return_pressure()) + return data + +/obj/machinery/portable_atmospherics/canister/ui_act(action, params) + if(..()) + return + + switch(action) + if("relabel") + if (can_label) + var/list/colors = list(\ + "\[N2O\]" = "redws", \ + "\[N2\]" = "red", \ + "\[O2\]" = "blue", \ + "\[Plasma\]" = "orange", \ + "\[CO2\]" = "black", \ + "\[Air\]" = "grey", \ + "\[CAUTION\]" = "yellow", \ + ) + var/label = input("Label canister:", "Gas Canister") as null|anything in colors + if(label) + src.canister_color = colors[label] + src.icon_state = colors[label] + src.name = "canister: [label]" + if("pressure") + switch(params["set"]) + if("custom") + var/custom = input(usr, "What rate do you set the regulator to? The dial reads from [CAN_MIN_RELEASE_PRESSURE] to [CAN_MAX_RELEASE_PRESSURE].") as null|num + if(custom) + release_pressure = custom + if("reset") + release_pressure = CAN_DEFAULT_RELEASE_PRESSURE + if("min") + release_pressure = CAN_MIN_RELEASE_PRESSURE + if("max") + release_pressure = CAN_MAX_RELEASE_PRESSURE + release_pressure = Clamp(round(release_pressure), CAN_MIN_RELEASE_PRESSURE, CAN_MAX_RELEASE_PRESSURE) + if("valve") + var/logmsg + if (valve_open) + if (holding) + logmsg = "Valve was closed by [key_name(usr)], stopping the transfer into the [holding]
" + else + logmsg = "Valve was closed by [key_name(usr)], stopping the transfer into the air
" + else + if (holding) + logmsg = "Valve was opened by [key_name(usr)], starting the transfer into the [holding]
" + else + logmsg = "Valve was opened by [key_name(usr)], starting the transfer into the air
" + if(air_contents.toxins > 0) + message_admins("[key_name_admin(usr)] (?) (FLW) opened a canister that contains plasma! (JMP)") + log_admin("[key_name(usr)] opened a canister that contains plasma at [x], [y], [z]") + var/datum/gas/sleeping_agent = locate(/datum/gas/sleeping_agent) in air_contents.trace_gases + if(sleeping_agent && (sleeping_agent.moles > 1)) + message_admins("[key_name_admin(usr)] (?) (FLW) opened a canister that contains N2O! (JMP)") + log_admin("[key_name(usr)] opened a canister that contains N2O at [x], [y], [z]") + investigate_log(logmsg, "atmos") + release_log += logmsg + valve_open = !valve_open + if("eject") + if(holding) + if (valve_open) + investigate_log("[key_name(usr)] removed the [holding], leaving the valve open and transfering into the air
", "atmos") + holding.loc = loc + holding = null + 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) + + src.update_icon() + return 1 + +/obj/machinery/portable_atmospherics/canister/oxygen/New() + ..() + + src.air_contents.oxygen = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + + src.update_icon() + return 1 + +/obj/machinery/portable_atmospherics/canister/sleeping_agent/New() + ..() + + 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) + + src.update_icon() + return 1 + +//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 + spawn(10) + var/turf/simulated/location = src.loc + if (istype(src.loc)) + while (!location.air) + sleep(10) + location.assume_air(air_contents) + air_contents = new + return 1 + +/obj/machinery/portable_atmospherics/canister/nitrogen/New() + + ..() + + src.air_contents.nitrogen = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + + src.update_icon() + return 1 + +/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) + + src.update_icon() + 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) + + src.update_icon() + return 1 diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index 9528ce6311b..5f3bbb89368 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -1,378 +1,378 @@ -#define CAMERA_UPGRADE_XRAY 1 -#define CAMERA_UPGRADE_EMP_PROOF 2 -#define CAMERA_UPGRADE_MOTION 4 - -/obj/machinery/camera - name = "security camera" - desc = "It's used to monitor rooms." - icon = 'icons/obj/monitors.dmi' - icon_state = "camera" - use_power = 2 - idle_power_usage = 5 - active_power_usage = 10 - layer = 5 - - var/health = 50 - var/list/network = list("SS13") - var/c_tag = null - var/c_tag_order = 999 - var/status = 1 - anchored = 1 - var/start_active = 0 //If it ignores the random chance to start broken on round start - var/invuln = null - var/obj/item/device/camera_bug/bug = null - var/obj/machinery/camera_assembly/assembly = null - - //OTHER - - var/view_range = 7 - var/short_range = 2 - - var/light_disabled = 0 - var/alarm_on = 0 - var/busy = 0 - var/emped = 0 //Number of consecutive EMP's on this camera - - // Upgrades bitflag - var/upgrades = 0 - -/obj/machinery/camera/New() - ..() - assembly = new(src) - assembly.state = 4 - cameranet.cameras += src - cameranet.addCamera(src) - /* // Use this to look for cameras that have the same c_tag. - for(var/obj/machinery/camera/C in cameranet.cameras) - var/list/tempnetwork = C.network&src.network - if(C != src && C.c_tag == src.c_tag && tempnetwork.len) - world.log << "[src.c_tag] [src.x] [src.y] [src.z] conflicts with [C.c_tag] [C.x] [C.y] [C.z]" - */ - -/obj/machinery/camera/initialize() - if(z == 1 && prob(3) && !start_active) - deactivate() - -/obj/machinery/camera/Destroy() - deactivate(null, 0) //kick anyone viewing out - if(assembly) - qdel(assembly) - assembly = null - if(istype(bug)) - bug.bugged_cameras -= src.c_tag - if(bug.current == src) - bug.current = null - bug = null - cameranet.removeCamera(src) //Will handle removal from the camera network and the chunks, so we don't need to worry about that - cameranet.cameras -= src - cameranet.removeCamera(src) - return ..() - -/obj/machinery/camera/emp_act(severity) - if(!isEmpProof()) - if(prob(150/severity)) - icon_state = "[initial(icon_state)]emp" - var/list/previous_network = network - network = list() - cameranet.removeCamera(src) - stat |= EMPED - SetLuminosity(0) - emped = emped+1 //Increase the number of consecutive EMP's - var/thisemp = emped //Take note of which EMP this proc is for - spawn(900) - if(loc) //qdel limbo - triggerCameraAlarm() //camera alarm triggers even if multiple EMPs are in effect. - if(emped == thisemp) //Only fix it if the camera hasn't been EMP'd again - network = previous_network - icon_state = initial(icon_state) - stat &= ~EMPED - if(can_use()) - cameranet.addCamera(src) - emped = 0 //Resets the consecutive EMP count - spawn(100) - if(!qdeleted(src)) - cancelCameraAlarm() - for(var/mob/O in mob_list) - if (O.client && O.client.eye == src) - O.unset_machine() - O.reset_view(null) - O << "The screen bursts into static." - ..() - - -/obj/machinery/camera/ex_act(severity, target) - if(src.invuln) - return - else - ..() - return - -/obj/machinery/camera/proc/setViewRange(num = 7) - src.view_range = num - cameranet.updateVisibility(src, 0) - -/obj/machinery/camera/proc/shock(mob/living/user) - if(!istype(user)) - return - user.electrocute_act(10, src) - -/obj/machinery/camera/attack_paw(mob/living/carbon/alien/humanoid/user) - if(!istype(user)) - return - user.do_attack_animation(src) - add_hiddenprint(user) - visible_message("\The [user] slashes at [src]!") - playsound(src.loc, 'sound/weapons/slash.ogg', 100, 1) - health = max(0, health - 30) - if(!health && status) - deactivate(user, 0) - -/obj/machinery/camera/attackby(obj/W, mob/living/user, params) - var/msg = "You attach [W] into the assembly's inner circuits." - var/msg2 = "[src] already has that upgrade!" - - // DECONSTRUCTION - if(istype(W, /obj/item/weapon/screwdriver)) - panel_open = !panel_open - user << "You screw the camera's panel [panel_open ? "open" : "closed"]." - playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1) - return - - if(panel_open) - if(istype(W, /obj/item/weapon/wirecutters)) //enable/disable the camera - deactivate(user, 1) - health = initial(health) //this is a pretty simplistic way to heal the camera, but there's no reason for this to be complex. - - else if(istype(W, /obj/item/device/multitool)) //change focus - setViewRange((view_range == initial(view_range)) ? short_range : initial(view_range)) - user << "You [(view_range == initial(view_range)) ? "restore" : "mess up"] the camera's focus." - - else if(istype(W, /obj/item/weapon/weldingtool)) - if(weld(W, user)) - visible_message("[user] unwelds [src], leaving it as just a frame screwed to the wall.", "You unweld [src], leaving it as just a frame screwed to the wall") - if(!assembly) - assembly = new() - assembly.loc = src.loc - assembly.state = 1 - assembly.dir = src.dir - assembly = null - qdel(src) - return - - else if(istype(W, /obj/item/device/analyzer)) - if(!isXRay()) - upgradeXRay() - qdel(W) - user << "[msg]" - else - user << "[msg2]" - - else if(istype(W, /obj/item/stack/sheet/mineral/plasma)) - if(!isEmpProof()) - upgradeEmpProof() - user << "[msg]" - qdel(W) - else - user << "[msg2]" - else if(istype(W, /obj/item/device/assembly/prox_sensor)) - if(!isMotion()) - upgradeMotion() - user << "[msg]" - qdel(W) - else - user << "[msg2]" - - // OTHER - if((istype(W, /obj/item/weapon/paper) || istype(W, /obj/item/device/pda)) && isliving(user)) - var/mob/living/U = user - var/obj/item/weapon/paper/X = null - var/obj/item/device/pda/P = null - - var/itemname = "" - var/info = "" - if(istype(W, /obj/item/weapon/paper)) - X = W - itemname = X.name - info = X.info - else - P = W - itemname = P.name - info = P.notehtml - U << "You hold \the [itemname] up to the camera..." - U.changeNext_move(CLICK_CD_MELEE) - for(var/mob/O in player_list) - if(istype(O, /mob/living/silicon/ai)) - var/mob/living/silicon/ai/AI = O - if(AI.control_disabled || (AI.stat == DEAD)) - return - if(U.name == "Unknown") - AI << "[U] holds \a [itemname] up to one of your cameras ..." - else - AI << "[U] holds \a [itemname] up to one of your cameras ..." - AI.last_paper_seen = "[itemname][info]" - else if (O.client && O.client.eye == src) - O << "[U] holds \a [itemname] up to one of the cameras ..." - O << browse(text("[][]", itemname, info), text("window=[]", itemname)) - - else if (istype(W, /obj/item/device/camera_bug)) - if (!src.can_use()) - user << "Camera non-functional." - return - if(istype(src.bug)) - user << "Camera bug removed." - src.bug.bugged_cameras -= src.c_tag - src.bug = null - else - user << "Camera bugged." - src.bug = W - src.bug.bugged_cameras[src.c_tag] = src - - else if(istype(W, /obj/item/device/laser_pointer)) - var/obj/item/device/laser_pointer/L = W - L.laser_act(src, user) - - else - if(W.force >= 10) //fairly simplistic, but will do for now. - user.changeNext_move(CLICK_CD_MELEE) - visible_message("[user] hits [src] with [W]!", "You hit [src] with [W]!") - health = max(0, health - W.force) - user.do_attack_animation(src) - if(!health && status) - triggerCameraAlarm() - deactivate(user, 1) - return - -/obj/machinery/camera/proc/deactivate(mob/user, displaymessage = 1) //this should be called toggle() but doing a find and replace for this would be ass - status = !status - if(can_use()) - cameranet.addCamera(src) - else - SetLuminosity(0) - cameranet.removeCamera(src) - cameranet.updateChunk(x, y, z) - var/change_msg = "deactivates" - if(!status) - icon_state = "[initial(icon_state)]1" - else - icon_state = initial(icon_state) - change_msg = "reactivates" - triggerCameraAlarm() - spawn(100) - if(!qdeleted(src)) - cancelCameraAlarm() - if(displaymessage) - if(user) - visible_message("[user] [change_msg] [src]!") - add_hiddenprint(user) - else - visible_message("\The [src] [change_msg]!") - - playsound(src.loc, 'sound/items/Wirecutter.ogg', 100, 1) - - // 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? - for(var/mob/O in player_list) - if (O.client && O.client.eye == src) - O.unset_machine() - O.reset_view(null) - O << "The screen bursts into static." - -/obj/machinery/camera/proc/triggerCameraAlarm() - alarm_on = 1 - for(var/mob/living/silicon/S in mob_list) - S.triggerAlarm("Camera", get_area(src), list(src), src) - -/obj/machinery/camera/proc/cancelCameraAlarm() - alarm_on = 0 - for(var/mob/living/silicon/S in mob_list) - S.cancelAlarm("Camera", get_area(src), src) - -/obj/machinery/camera/proc/can_use() - if(!status) - return 0 - if(stat & EMPED) - return 0 - return 1 - -/obj/machinery/camera/proc/can_see() - var/list/see = null - var/turf/pos = get_turf(src) - if(isXRay()) - see = range(view_range, pos) - else - see = get_hear(view_range, pos) - return see - -/atom/proc/auto_turn() - //Automatically turns based on nearby walls. - var/turf/simulated/wall/T = null - for(var/i = 1, i <= 8; i += i) - T = get_ranged_target_turf(src, i, 1) - if(istype(T)) - //If someone knows a better way to do this, let me know. -Giacom - switch(i) - if(NORTH) - src.dir = SOUTH - if(SOUTH) - src.dir = NORTH - if(WEST) - src.dir = EAST - if(EAST) - src.dir = WEST - break - -//Return a working camera that can see a given mob -//or null if none -/proc/seen_by_camera(var/mob/M) - for(var/obj/machinery/camera/C in oview(4, M)) - if(C.can_use()) // check if camera disabled - return C - break - return null - -/proc/near_range_camera(var/mob/M) - for(var/obj/machinery/camera/C in range(4, M)) - if(C.can_use()) // check if camera disabled - return C - break - - return null - -/obj/machinery/camera/proc/weld(obj/item/weapon/weldingtool/WT, mob/living/user) - if(busy) - return 0 - if(!WT.remove_fuel(0, user)) - return 0 - - user << "You start to weld [src]..." - playsound(src.loc, 'sound/items/Welder.ogg', 50, 1) - busy = 1 - if(do_after(user, 100, target = src)) - busy = 0 - if(!WT.isOn()) - return 0 - return 1 - busy = 0 - return 0 - -/obj/machinery/camera/bullet_act(obj/item/projectile/proj) - if(proj.damage_type == BRUTE) - health = max(0, health - proj.damage) - if(!health && status) - triggerCameraAlarm() - deactivate(null, 1) - -/obj/machinery/camera/portable //Cameras which are placed inside of things, such as helmets. - var/turf/prev_turf - -/obj/machinery/camera/portable/New() - ..() - assembly.state = 0 //These cameras are portable, and so shall be in the portable state if removed. - assembly.anchored = 0 - assembly.update_icon() - -/obj/machinery/camera/portable/process() //Updates whenever the camera is moved. - if(cameranet && get_turf(src) != prev_turf) - cameranet.updatePortableCamera(src) - prev_turf = get_turf(src) +#define CAMERA_UPGRADE_XRAY 1 +#define CAMERA_UPGRADE_EMP_PROOF 2 +#define CAMERA_UPGRADE_MOTION 4 + +/obj/machinery/camera + name = "security camera" + desc = "It's used to monitor rooms." + icon = 'icons/obj/monitors.dmi' + icon_state = "camera" + use_power = 2 + idle_power_usage = 5 + active_power_usage = 10 + layer = 5 + + var/health = 50 + var/list/network = list("SS13") + var/c_tag = null + var/c_tag_order = 999 + var/status = 1 + anchored = 1 + var/start_active = 0 //If it ignores the random chance to start broken on round start + var/invuln = null + var/obj/item/device/camera_bug/bug = null + var/obj/machinery/camera_assembly/assembly = null + + //OTHER + + var/view_range = 7 + var/short_range = 2 + + var/light_disabled = 0 + var/alarm_on = 0 + var/busy = 0 + var/emped = 0 //Number of consecutive EMP's on this camera + + // Upgrades bitflag + var/upgrades = 0 + +/obj/machinery/camera/New() + ..() + assembly = new(src) + assembly.state = 4 + cameranet.cameras += src + cameranet.addCamera(src) + /* // Use this to look for cameras that have the same c_tag. + for(var/obj/machinery/camera/C in cameranet.cameras) + var/list/tempnetwork = C.network&src.network + if(C != src && C.c_tag == src.c_tag && tempnetwork.len) + world.log << "[src.c_tag] [src.x] [src.y] [src.z] conflicts with [C.c_tag] [C.x] [C.y] [C.z]" + */ + +/obj/machinery/camera/initialize() + if(z == 1 && prob(3) && !start_active) + deactivate() + +/obj/machinery/camera/Destroy() + deactivate(null, 0) //kick anyone viewing out + if(assembly) + qdel(assembly) + assembly = null + if(istype(bug)) + bug.bugged_cameras -= src.c_tag + if(bug.current == src) + bug.current = null + bug = null + cameranet.removeCamera(src) //Will handle removal from the camera network and the chunks, so we don't need to worry about that + cameranet.cameras -= src + cameranet.removeCamera(src) + return ..() + +/obj/machinery/camera/emp_act(severity) + if(!isEmpProof()) + if(prob(150/severity)) + icon_state = "[initial(icon_state)]emp" + var/list/previous_network = network + network = list() + cameranet.removeCamera(src) + stat |= EMPED + SetLuminosity(0) + emped = emped+1 //Increase the number of consecutive EMP's + var/thisemp = emped //Take note of which EMP this proc is for + spawn(900) + if(loc) //qdel limbo + triggerCameraAlarm() //camera alarm triggers even if multiple EMPs are in effect. + if(emped == thisemp) //Only fix it if the camera hasn't been EMP'd again + network = previous_network + icon_state = initial(icon_state) + stat &= ~EMPED + if(can_use()) + cameranet.addCamera(src) + emped = 0 //Resets the consecutive EMP count + spawn(100) + if(!qdeleted(src)) + cancelCameraAlarm() + for(var/mob/O in mob_list) + if (O.client && O.client.eye == src) + O.unset_machine() + O.reset_view(null) + O << "The screen bursts into static." + ..() + + +/obj/machinery/camera/ex_act(severity, target) + if(src.invuln) + return + else + ..() + return + +/obj/machinery/camera/proc/setViewRange(num = 7) + src.view_range = num + cameranet.updateVisibility(src, 0) + +/obj/machinery/camera/proc/shock(mob/living/user) + if(!istype(user)) + return + user.electrocute_act(10, src) + +/obj/machinery/camera/attack_paw(mob/living/carbon/alien/humanoid/user) + if(!istype(user)) + return + user.do_attack_animation(src) + add_hiddenprint(user) + visible_message("\The [user] slashes at [src]!") + playsound(src.loc, 'sound/weapons/slash.ogg', 100, 1) + health = max(0, health - 30) + if(!health && status) + deactivate(user, 0) + +/obj/machinery/camera/attackby(obj/W, mob/living/user, params) + var/msg = "You attach [W] into the assembly's inner circuits." + var/msg2 = "[src] already has that upgrade!" + + // DECONSTRUCTION + if(istype(W, /obj/item/weapon/screwdriver)) + panel_open = !panel_open + user << "You screw the camera's panel [panel_open ? "open" : "closed"]." + playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1) + return + + if(panel_open) + if(istype(W, /obj/item/weapon/wirecutters)) //enable/disable the camera + deactivate(user, 1) + health = initial(health) //this is a pretty simplistic way to heal the camera, but there's no reason for this to be complex. + + else if(istype(W, /obj/item/device/multitool)) //change focus + setViewRange((view_range == initial(view_range)) ? short_range : initial(view_range)) + user << "You [(view_range == initial(view_range)) ? "restore" : "mess up"] the camera's focus." + + else if(istype(W, /obj/item/weapon/weldingtool)) + if(weld(W, user)) + visible_message("[user] unwelds [src], leaving it as just a frame screwed to the wall.", "You unweld [src], leaving it as just a frame screwed to the wall") + if(!assembly) + assembly = new() + assembly.loc = src.loc + assembly.state = 1 + assembly.dir = src.dir + assembly = null + qdel(src) + return + + else if(istype(W, /obj/item/device/analyzer)) + if(!isXRay()) + upgradeXRay() + qdel(W) + user << "[msg]" + else + user << "[msg2]" + + else if(istype(W, /obj/item/stack/sheet/mineral/plasma)) + if(!isEmpProof()) + upgradeEmpProof() + user << "[msg]" + qdel(W) + else + user << "[msg2]" + else if(istype(W, /obj/item/device/assembly/prox_sensor)) + if(!isMotion()) + upgradeMotion() + user << "[msg]" + qdel(W) + else + user << "[msg2]" + + // OTHER + if((istype(W, /obj/item/weapon/paper) || istype(W, /obj/item/device/pda)) && isliving(user)) + var/mob/living/U = user + var/obj/item/weapon/paper/X = null + var/obj/item/device/pda/P = null + + var/itemname = "" + var/info = "" + if(istype(W, /obj/item/weapon/paper)) + X = W + itemname = X.name + info = X.info + else + P = W + itemname = P.name + info = P.notehtml + U << "You hold \the [itemname] up to the camera..." + U.changeNext_move(CLICK_CD_MELEE) + for(var/mob/O in player_list) + if(istype(O, /mob/living/silicon/ai)) + var/mob/living/silicon/ai/AI = O + if(AI.control_disabled || (AI.stat == DEAD)) + return + if(U.name == "Unknown") + AI << "[U] holds \a [itemname] up to one of your cameras ..." + else + AI << "[U] holds \a [itemname] up to one of your cameras ..." + AI.last_paper_seen = "[itemname][info]" + else if (O.client && O.client.eye == src) + O << "[U] holds \a [itemname] up to one of the cameras ..." + O << browse(text("[][]", itemname, info), text("window=[]", itemname)) + + else if (istype(W, /obj/item/device/camera_bug)) + if (!src.can_use()) + user << "Camera non-functional." + return + if(istype(src.bug)) + user << "Camera bug removed." + src.bug.bugged_cameras -= src.c_tag + src.bug = null + else + user << "Camera bugged." + src.bug = W + src.bug.bugged_cameras[src.c_tag] = src + + else if(istype(W, /obj/item/device/laser_pointer)) + var/obj/item/device/laser_pointer/L = W + L.laser_act(src, user) + + else + if(W.force >= 10) //fairly simplistic, but will do for now. + user.changeNext_move(CLICK_CD_MELEE) + visible_message("[user] hits [src] with [W]!", "You hit [src] with [W]!") + health = max(0, health - W.force) + user.do_attack_animation(src) + if(!health && status) + triggerCameraAlarm() + deactivate(user, 1) + return + +/obj/machinery/camera/proc/deactivate(mob/user, displaymessage = 1) //this should be called toggle() but doing a find and replace for this would be ass + status = !status + if(can_use()) + cameranet.addCamera(src) + else + SetLuminosity(0) + cameranet.removeCamera(src) + cameranet.updateChunk(x, y, z) + var/change_msg = "deactivates" + if(!status) + icon_state = "[initial(icon_state)]1" + else + icon_state = initial(icon_state) + change_msg = "reactivates" + triggerCameraAlarm() + spawn(100) + if(!qdeleted(src)) + cancelCameraAlarm() + if(displaymessage) + if(user) + visible_message("[user] [change_msg] [src]!") + add_hiddenprint(user) + else + visible_message("\The [src] [change_msg]!") + + playsound(src.loc, 'sound/items/Wirecutter.ogg', 100, 1) + + // 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? + for(var/mob/O in player_list) + if (O.client && O.client.eye == src) + O.unset_machine() + O.reset_view(null) + O << "The screen bursts into static." + +/obj/machinery/camera/proc/triggerCameraAlarm() + alarm_on = 1 + for(var/mob/living/silicon/S in mob_list) + S.triggerAlarm("Camera", get_area(src), list(src), src) + +/obj/machinery/camera/proc/cancelCameraAlarm() + alarm_on = 0 + for(var/mob/living/silicon/S in mob_list) + S.cancelAlarm("Camera", get_area(src), src) + +/obj/machinery/camera/proc/can_use() + if(!status) + return 0 + if(stat & EMPED) + return 0 + return 1 + +/obj/machinery/camera/proc/can_see() + var/list/see = null + var/turf/pos = get_turf(src) + if(isXRay()) + see = range(view_range, pos) + else + see = get_hear(view_range, pos) + return see + +/atom/proc/auto_turn() + //Automatically turns based on nearby walls. + var/turf/simulated/wall/T = null + for(var/i = 1, i <= 8; i += i) + T = get_ranged_target_turf(src, i, 1) + if(istype(T)) + //If someone knows a better way to do this, let me know. -Giacom + switch(i) + if(NORTH) + src.dir = SOUTH + if(SOUTH) + src.dir = NORTH + if(WEST) + src.dir = EAST + if(EAST) + src.dir = WEST + break + +//Return a working camera that can see a given mob +//or null if none +/proc/seen_by_camera(var/mob/M) + for(var/obj/machinery/camera/C in oview(4, M)) + if(C.can_use()) // check if camera disabled + return C + break + return null + +/proc/near_range_camera(var/mob/M) + for(var/obj/machinery/camera/C in range(4, M)) + if(C.can_use()) // check if camera disabled + return C + break + + return null + +/obj/machinery/camera/proc/weld(obj/item/weapon/weldingtool/WT, mob/living/user) + if(busy) + return 0 + if(!WT.remove_fuel(0, user)) + return 0 + + user << "You start to weld [src]..." + playsound(src.loc, 'sound/items/Welder.ogg', 50, 1) + busy = 1 + if(do_after(user, 100, target = src)) + busy = 0 + if(!WT.isOn()) + return 0 + return 1 + busy = 0 + return 0 + +/obj/machinery/camera/bullet_act(obj/item/projectile/proj) + if(proj.damage_type == BRUTE) + health = max(0, health - proj.damage) + if(!health && status) + triggerCameraAlarm() + deactivate(null, 1) + +/obj/machinery/camera/portable //Cameras which are placed inside of things, such as helmets. + var/turf/prev_turf + +/obj/machinery/camera/portable/New() + ..() + assembly.state = 0 //These cameras are portable, and so shall be in the portable state if removed. + assembly.anchored = 0 + assembly.update_icon() + +/obj/machinery/camera/portable/process() //Updates whenever the camera is moved. + if(cameranet && get_turf(src) != prev_turf) + cameranet.updatePortableCamera(src) + prev_turf = get_turf(src) diff --git a/code/game/machinery/computer/syndicate_shuttle.dm b/code/game/machinery/computer/syndicate_shuttle.dm index 2f397908c84..8146a02e92c 100644 --- a/code/game/machinery/computer/syndicate_shuttle.dm +++ b/code/game/machinery/computer/syndicate_shuttle.dm @@ -1,24 +1,24 @@ -#define SYNDICATE_CHALLENGE_TIMER 12000 //20 minutes - -/obj/machinery/computer/shuttle/syndicate - name = "syndicate shuttle terminal" - icon_screen = "syndishuttle" - icon_keyboard = "syndie_key" - req_access = list(access_syndicate) - shuttleId = "syndicate" - possible_destinations = "syndicate_away;syndicate_z5;syndicate_ne;syndicate_nw;syndicate_n;syndicate_se;syndicate_sw;syndicate_s" - var/challenge = FALSE - -/obj/machinery/computer/shuttle/syndicate/recall - name = "syndicate shuttle recall terminal" - possible_destinations = "syndicate_away" - - -/obj/machinery/computer/shuttle/syndicate/Topic(href, href_list) - if(href_list["move"]) - if(challenge && world.time < SYNDICATE_CHALLENGE_TIMER) - usr << "You've issued a combat challenge to the station! You've got to give them at least [round(((SYNDICATE_CHALLENGE_TIMER - world.time) / 10) / 60)] more minutes to allow them to prepare." - return 0 - ..() - +#define SYNDICATE_CHALLENGE_TIMER 12000 //20 minutes + +/obj/machinery/computer/shuttle/syndicate + name = "syndicate shuttle terminal" + icon_screen = "syndishuttle" + icon_keyboard = "syndie_key" + req_access = list(access_syndicate) + shuttleId = "syndicate" + possible_destinations = "syndicate_away;syndicate_z5;syndicate_ne;syndicate_nw;syndicate_n;syndicate_se;syndicate_sw;syndicate_s" + var/challenge = FALSE + +/obj/machinery/computer/shuttle/syndicate/recall + name = "syndicate shuttle recall terminal" + possible_destinations = "syndicate_away" + + +/obj/machinery/computer/shuttle/syndicate/Topic(href, href_list) + if(href_list["move"]) + if(challenge && world.time < SYNDICATE_CHALLENGE_TIMER) + usr << "You've issued a combat challenge to the station! You've got to give them at least [round(((SYNDICATE_CHALLENGE_TIMER - world.time) / 10) / 60)] more minutes to allow them to prepare." + return 0 + ..() + #undef SYNDICATE_CHALLENGE_TIMER \ No newline at end of file diff --git a/code/game/machinery/deployable.dm b/code/game/machinery/deployable.dm index 551954caeb0..d56706d2e09 100644 --- a/code/game/machinery/deployable.dm +++ b/code/game/machinery/deployable.dm @@ -1,185 +1,185 @@ -#define SINGLE "single" -#define VERTICAL "vertical" -#define HORIZONTAL "horizontal" - -#define METAL 1 -#define WOOD 2 -#define SAND 3 - -//Barricades/cover - -/obj/structure/barricade - name = "chest high wall" - desc = "Looks like this would make good cover." - anchored = 1 - density = 1 - var/health = 100 - var/maxhealth = 100 - var/proj_pass_rate = 50 //How many projectiles will pass the cover. Lower means stronger cover - var/ranged_damage_modifier = 1 //Multiply for ranged damage - var/material = METAL - var/debris_type - - -/obj/structure/barricade/proc/take_damage(damage, leave_debris=1, message) - health -= damage - if(health <= 0) - if(message) - visible_message(message) - else - visible_message("\The [src] is smashed apart!") - if(leave_debris && debris_type) - new debris_type(get_turf(src), 3) - qdel(src) - - -/obj/structure/barricade/attack_animal(mob/living/simple_animal/M) - M.changeNext_move(CLICK_CD_MELEE) - M.do_attack_animation(src) - if(M.melee_damage_upper == 0 || (M.melee_damage_type != BRUTE && M.melee_damage_type != BURN)) - return - visible_message("[M] [M.attacktext] [src]!") - add_logs(M, src, "attacked") - take_damage(M.melee_damage_upper) - -/obj/structure/barricade/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/weapon/weldingtool) && user.a_intent == "help" && material == METAL) - var/obj/item/weapon/weldingtool/WT = I - if(health < maxhealth) - if(WT.remove_fuel(0,user)) - user << "You begin repairing [src]..." - playsound(loc, 'sound/items/Welder.ogg', 40, 1) - if(do_after(user, 40/I.toolspeed, target = src)) - health = Clamp(health + 20, 0, maxhealth) - return - - else - user.changeNext_move(CLICK_CD_MELEE) - visible_message("[user] hits [src] with [I]!", "You hit [src] with [I]!") - take_damage(I.force) - user.do_attack_animation(src) - -/obj/structure/barricade/bullet_act(var/obj/item/projectile/P) - if(P) - ..() - take_damage(P.damage*ranged_damage_modifier) - visible_message("\The [src] is hit by [P]!") - -/obj/structure/barricade/ex_act(severity, target) - switch(severity) - if(1) - visible_message("\The [src] is blown apart!") - qdel(src) - if(2) - take_damage(25, message = "\The [src] is blown apart!") - -/obj/structure/barricade/blob_act() - take_damage(25, leave_debris = 0, message = "The blob eats through \the [src]!") - - -/obj/structure/barricade/CanPass(atom/movable/mover, turf/target, height=0)//So bullets will fly over and stuff. - if(height==0) - return 1 - if(istype(mover, /obj/item/projectile)) - if(!anchored) - return 1 - var/obj/item/projectile/proj = mover - if(proj.firer && Adjacent(proj.firer)) - return 1 - if(prob(proj_pass_rate)) - return 1 - return 0 - else - return 0 - - - - - -/////BARRICADE TYPES/////// - -/obj/structure/barricade/wooden - name = "wooden barricade" - desc = "This space is blocked off by a wooden barricade." - icon = 'icons/obj/structures.dmi' - icon_state = "woodenbarricade" - material = WOOD - - -/obj/structure/barricade/security - name = "security barrier" - desc = "A deployable barrier. Provides good cover in fire fights." - icon = 'icons/obj/objects.dmi' - icon_state = "barrier0" - density = 0 - anchored = 0 - health = 180 - maxhealth = 180 - proj_pass_rate = 20 - ranged_damage_modifier = 0.5 - - -/obj/structure/barricade/security/New() - ..() - spawn(40) - icon_state = "barrier1" - density = 1 - anchored = 1 - visible_message("[src] deploys!") - - -/obj/item/weapon/grenade/barrier - name = "barrier grenade" - desc = "Instant cover. Alt+click to toggle modes." - icon = 'icons/obj/grenade.dmi' - icon_state = "flashbang" - item_state = "flashbang" - action_button_name = "Toggle Barrier Spread" - var/mode = SINGLE - -/obj/item/weapon/grenade/barrier/AltClick(mob/user) - toggle_mode(user) - -/obj/item/weapon/grenade/barrier/proc/toggle_mode(mob/user) - switch(mode) - if(SINGLE) - mode = VERTICAL - if(VERTICAL) - mode = HORIZONTAL - if(HORIZONTAL) - mode = SINGLE - - user << "[src] is now in [mode] mode." - -/obj/item/weapon/grenade/barrier/prime() - new /obj/structure/barricade/security(get_turf(src.loc)) - switch(mode) - if(VERTICAL) - var/target_turf = get_step(src, NORTH) - if(!(is_blocked_turf(target_turf))) - new /obj/structure/barricade/security(target_turf) - - var/target_turf2 = get_step(src, SOUTH) - if(!(is_blocked_turf(target_turf2))) - new /obj/structure/barricade/security(target_turf2) - if(HORIZONTAL) - var/target_turf = get_step(src, EAST) - if(!(is_blocked_turf(target_turf))) - new /obj/structure/barricade/security(target_turf) - - var/target_turf2 = get_step(src, WEST) - if(!(is_blocked_turf(target_turf2))) - new /obj/structure/barricade/security(target_turf2) - qdel(src) - -/obj/item/weapon/grenade/barrier/ui_action_click() - toggle_mode(usr) - - -#undef SINGLE -#undef VERTICAL -#undef HORIZONTAL - -#undef METAL -#undef WOOD -#undef SAND +#define SINGLE "single" +#define VERTICAL "vertical" +#define HORIZONTAL "horizontal" + +#define METAL 1 +#define WOOD 2 +#define SAND 3 + +//Barricades/cover + +/obj/structure/barricade + name = "chest high wall" + desc = "Looks like this would make good cover." + anchored = 1 + density = 1 + var/health = 100 + var/maxhealth = 100 + var/proj_pass_rate = 50 //How many projectiles will pass the cover. Lower means stronger cover + var/ranged_damage_modifier = 1 //Multiply for ranged damage + var/material = METAL + var/debris_type + + +/obj/structure/barricade/proc/take_damage(damage, leave_debris=1, message) + health -= damage + if(health <= 0) + if(message) + visible_message(message) + else + visible_message("\The [src] is smashed apart!") + if(leave_debris && debris_type) + new debris_type(get_turf(src), 3) + qdel(src) + + +/obj/structure/barricade/attack_animal(mob/living/simple_animal/M) + M.changeNext_move(CLICK_CD_MELEE) + M.do_attack_animation(src) + if(M.melee_damage_upper == 0 || (M.melee_damage_type != BRUTE && M.melee_damage_type != BURN)) + return + visible_message("[M] [M.attacktext] [src]!") + add_logs(M, src, "attacked") + take_damage(M.melee_damage_upper) + +/obj/structure/barricade/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/weapon/weldingtool) && user.a_intent == "help" && material == METAL) + var/obj/item/weapon/weldingtool/WT = I + if(health < maxhealth) + if(WT.remove_fuel(0,user)) + user << "You begin repairing [src]..." + playsound(loc, 'sound/items/Welder.ogg', 40, 1) + if(do_after(user, 40/I.toolspeed, target = src)) + health = Clamp(health + 20, 0, maxhealth) + return + + else + user.changeNext_move(CLICK_CD_MELEE) + visible_message("[user] hits [src] with [I]!", "You hit [src] with [I]!") + take_damage(I.force) + user.do_attack_animation(src) + +/obj/structure/barricade/bullet_act(var/obj/item/projectile/P) + if(P) + ..() + take_damage(P.damage*ranged_damage_modifier) + visible_message("\The [src] is hit by [P]!") + +/obj/structure/barricade/ex_act(severity, target) + switch(severity) + if(1) + visible_message("\The [src] is blown apart!") + qdel(src) + if(2) + take_damage(25, message = "\The [src] is blown apart!") + +/obj/structure/barricade/blob_act() + take_damage(25, leave_debris = 0, message = "The blob eats through \the [src]!") + + +/obj/structure/barricade/CanPass(atom/movable/mover, turf/target, height=0)//So bullets will fly over and stuff. + if(height==0) + return 1 + if(istype(mover, /obj/item/projectile)) + if(!anchored) + return 1 + var/obj/item/projectile/proj = mover + if(proj.firer && Adjacent(proj.firer)) + return 1 + if(prob(proj_pass_rate)) + return 1 + return 0 + else + return 0 + + + + + +/////BARRICADE TYPES/////// + +/obj/structure/barricade/wooden + name = "wooden barricade" + desc = "This space is blocked off by a wooden barricade." + icon = 'icons/obj/structures.dmi' + icon_state = "woodenbarricade" + material = WOOD + + +/obj/structure/barricade/security + name = "security barrier" + desc = "A deployable barrier. Provides good cover in fire fights." + icon = 'icons/obj/objects.dmi' + icon_state = "barrier0" + density = 0 + anchored = 0 + health = 180 + maxhealth = 180 + proj_pass_rate = 20 + ranged_damage_modifier = 0.5 + + +/obj/structure/barricade/security/New() + ..() + spawn(40) + icon_state = "barrier1" + density = 1 + anchored = 1 + visible_message("[src] deploys!") + + +/obj/item/weapon/grenade/barrier + name = "barrier grenade" + desc = "Instant cover. Alt+click to toggle modes." + icon = 'icons/obj/grenade.dmi' + icon_state = "flashbang" + item_state = "flashbang" + action_button_name = "Toggle Barrier Spread" + var/mode = SINGLE + +/obj/item/weapon/grenade/barrier/AltClick(mob/user) + toggle_mode(user) + +/obj/item/weapon/grenade/barrier/proc/toggle_mode(mob/user) + switch(mode) + if(SINGLE) + mode = VERTICAL + if(VERTICAL) + mode = HORIZONTAL + if(HORIZONTAL) + mode = SINGLE + + user << "[src] is now in [mode] mode." + +/obj/item/weapon/grenade/barrier/prime() + new /obj/structure/barricade/security(get_turf(src.loc)) + switch(mode) + if(VERTICAL) + var/target_turf = get_step(src, NORTH) + if(!(is_blocked_turf(target_turf))) + new /obj/structure/barricade/security(target_turf) + + var/target_turf2 = get_step(src, SOUTH) + if(!(is_blocked_turf(target_turf2))) + new /obj/structure/barricade/security(target_turf2) + if(HORIZONTAL) + var/target_turf = get_step(src, EAST) + if(!(is_blocked_turf(target_turf))) + new /obj/structure/barricade/security(target_turf) + + var/target_turf2 = get_step(src, WEST) + if(!(is_blocked_turf(target_turf2))) + new /obj/structure/barricade/security(target_turf2) + qdel(src) + +/obj/item/weapon/grenade/barrier/ui_action_click() + toggle_mode(usr) + + +#undef SINGLE +#undef VERTICAL +#undef HORIZONTAL + +#undef METAL +#undef WOOD +#undef SAND diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index a1817bc15f0..db68793ce1c 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -1,1176 +1,1176 @@ -/* - New methods: - pulse - sends a pulse into a wire for hacking purposes - cut - cuts a wire and makes any necessary state changes - mend - mends a wire and makes any necessary state changes - canAIControl - 1 if the AI can control the airlock, 0 if not (then check canAIHack to see if it can hack in) - canAIHack - 1 if the AI can hack into the airlock to recover control, 0 if not. Also returns 0 if the AI does not *need* to hack it. - hasPower - 1 if the main or backup power are functioning, 0 if not. - requiresIDs - 1 if the airlock is requiring IDs, 0 if not - isAllPowerCut - 1 if the main and backup power both have cut wires. - regainMainPower - handles the effect of main power coming back on. - loseMainPower - handles the effect of main power going offline. Usually (if one isn't already running) spawn a thread to count down how long it will be offline - counting down won't happen if main power was completely cut along with backup power, though, the thread will just sleep. - loseBackupPower - handles the effect of backup power going offline. - regainBackupPower - handles the effect of main power coming back on. - shock - has a chance of electrocuting its target. -*/ - -// Wires for the airlock are located in the datum folder, inside the wires datum folder. - -#define AIRLOCK_CLOSED 1 -#define AIRLOCK_CLOSING 2 -#define AIRLOCK_OPEN 3 -#define AIRLOCK_OPENING 4 -#define AIRLOCK_DENY 5 -#define AIRLOCK_EMAG 6 -var/list/airlock_overlays = list() - -/obj/machinery/door/airlock - name = "airlock" - icon = 'icons/obj/doors/airlocks/station/public.dmi' - icon_state = "closed" - - 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. - var/secondsBackupPowerLost = 0 //The number of seconds until power is restored. - var/spawnPowerRestoreRunning = 0 - var/welded = null - var/locked = 0 - var/lights = 1 // bolt lights show by default - var/datum/wires/airlock/wires = null - secondsElectrified = 0 //How many seconds remain until the door is no longer electrified. -1 if it is permanently electrified until someone fixes it. - var/aiDisabledIdScanner = 0 - var/aiHacking = 0 - var/obj/machinery/door/airlock/closeOther = null - var/closeOtherId = null - var/lockdownbyai = 0 - var/doortype = /obj/structure/door_assembly/door_assembly_0 - var/justzap = 0 - var/safe = 1 - normalspeed = 1 - var/obj/item/weapon/electronics/airlock/electronics = null - var/hasShocked = 0 //Prevents multiple shocks from happening - var/autoclose = 1 - var/obj/item/device/doorCharge/charge = null //If applied, causes an explosion upon opening the door - var/detonated = 0 - - var/airlock_material = null //material of inner filling; if its an airlock with glass, this should be set to "glass" - var/overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' - - var/image/old_frame_overlay //keep those in order to prevent unnecessary updating - var/image/old_filling_overlay - var/image/old_lights_overlay - var/image/old_panel_overlay - var/image/old_weld_overlay - var/image/old_sparks_overlay - - explosion_block = 1 - -/obj/machinery/door/airlock/New() - ..() - wires = new(src) - if(src.closeOtherId != null) - spawn (5) - for (var/obj/machinery/door/airlock/A in airlocks) - if(A.closeOtherId == src.closeOtherId && A != src) - src.closeOther = A - break - if(glass) - airlock_material = "glass" - update_icon() - -/* -About the new airlock wires panel: -* An airlock wire dialog can be accessed by the normal way or by using wirecutters or a multitool on the door while the wire-panel is open. This would show the following wires, which you can either wirecut/mend or send a multitool pulse through. There are 9 wires. -* one wire from the ID scanner. Sending a pulse through this flashes the red light on the door (if the door has power). If you cut this wire, the door will stop recognizing valid IDs. (If the door has 0000 access, it still opens and closes, though) -* two wires for power. 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). 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 \red open, but bolts-raising will not work. Cutting these wires may electrocute the user. -* one wire for door bolts. Sending a pulse through this drops door bolts (whether the door is powered or not) or raises them (if it is). Cutting this wire also drops the door bolts, and mending it does not raise them. If the wire is cut, trying to raise the door bolts will not work. -* 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). Cutting either one disables the backup door power (allowing it to be crowbarred open, but disabling bolts-raising), but may electocute the user. -* one wire for opening the door. Sending a pulse through this while the door has power makes it open the door if no access is required. -* one wire for AI control. Sending a pulse through this blocks AI control for a second or so (which is enough to see the AI control light on the panel dialog go off and back on again). Cutting this prevents the AI from controlling the door unless it has hacked the door through the power connection (which takes about a minute). If both main and backup power are cut, as well as this wire, then the AI cannot operate or hack the door at all. -* one wire for electrifying the door. Sending a pulse through this electrifies the door for 30 seconds. Cutting this wire electrifies the door, so that the next person to touch the door without insulated gloves gets electrocuted. (Currently it is also STAYING electrified until someone mends the wire) -* one wire for controling door safetys. When active, door does not close on someone. When cut, door will ruin someone's shit. When pulsed, door will immedately ruin someone's shit. -* one wire for controlling door speed. When active, dor closes at normal rate. When cut, door does not close manually. When pulsed, door attempts to close every tick. -*/ -// You can find code for the airlock wires in the wire datum folder. - -/obj/machinery/door/airlock/proc/bolt() - if(locked) - return - locked = 1 - update_icon() - -/obj/machinery/door/airlock/proc/unbolt() - if(!locked) - return - locked = 0 - update_icon() - -/obj/machinery/door/airlock/Destroy() - qdel(wires) - wires = null - if(id_tag) - for(var/obj/machinery/doorButtons/D in machines) - D.removeMe(src) - return ..() - -/obj/machinery/door/airlock/bumpopen(mob/living/user) //Airlocks now zap you when you 'bump' them open when they're electrified. --NeoFite - if(!issilicon(usr)) - if(src.isElectrified()) - if(!src.justzap) - if(src.shock(user, 100)) - src.justzap = 1 - spawn (10) - src.justzap = 0 - return - else /*if(src.justzap)*/ - return - else if(user.hallucination > 50 && prob(10) && src.operating == 0) - user << "You feel a powerful shock course through your body!" - user.staminaloss += 50 - user.stunned += 5 - return - ..(user) - -/obj/machinery/door/airlock/bumpopen(mob/living/simple_animal/user) - ..(user) - -/obj/machinery/door/airlock/proc/isElectrified() - if(src.secondsElectrified != 0) - return 1 - return 0 - -/obj/machinery/door/airlock/proc/isWireCut(wireIndex) - // You can find the wires in the datum folder. - return wires.IsIndexCut(wireIndex) - -/obj/machinery/door/airlock/proc/canAIControl() - return ((src.aiControlDisabled!=1) && (!src.isAllPowerCut())); - -/obj/machinery/door/airlock/proc/canAIHack() - return ((src.aiControlDisabled==1) && (!hackProof) && (!src.isAllPowerCut())); - -/obj/machinery/door/airlock/hasPower() - return ((src.secondsMainPowerLost==0 || src.secondsBackupPowerLost==0) && !(stat & NOPOWER)) - -/obj/machinery/door/airlock/requiresID() - return !(src.isWireCut(AIRLOCK_WIRE_IDSCAN) || aiDisabledIdScanner) - -/obj/machinery/door/airlock/proc/isAllPowerCut() - var/retval=0 - 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 - -/obj/machinery/door/airlock/proc/regainMainPower() - if(src.secondsMainPowerLost > 0) - src.secondsMainPowerLost = 0 - -/obj/machinery/door/airlock/proc/loseMainPower() - if(src.secondsMainPowerLost <= 0) - src.secondsMainPowerLost = 60 - if(src.secondsBackupPowerLost < 10) - src.secondsBackupPowerLost = 10 - if(!src.spawnPowerRestoreRunning) - src.spawnPowerRestoreRunning = 1 - spawn(0) - var/cont = 1 - while (cont) - sleep(10) - if(qdeleted(src)) - return - cont = 0 - if(src.secondsMainPowerLost>0) - if((!src.isWireCut(AIRLOCK_WIRE_MAIN_POWER1)) && (!src.isWireCut(AIRLOCK_WIRE_MAIN_POWER2))) - src.secondsMainPowerLost -= 1 - src.updateDialog() - cont = 1 - - if(src.secondsBackupPowerLost>0) - if((!src.isWireCut(AIRLOCK_WIRE_BACKUP_POWER1)) && (!src.isWireCut(AIRLOCK_WIRE_BACKUP_POWER2))) - src.secondsBackupPowerLost -= 1 - src.updateDialog() - cont = 1 - src.spawnPowerRestoreRunning = 0 - src.updateDialog() - -/obj/machinery/door/airlock/proc/loseBackupPower() - if(src.secondsBackupPowerLost < 60) - src.secondsBackupPowerLost = 60 - -/obj/machinery/door/airlock/proc/regainBackupPower() - if(src.secondsBackupPowerLost > 0) - src.secondsBackupPowerLost = 0 - -// shock user with probability prb (if all connections & power are working) -// returns 1 if shocked, 0 otherwise -// The preceding comment was borrowed from the grille's shock script -/obj/machinery/door/airlock/proc/shock(mob/user, prb) - if(!hasPower()) // unpowered, no shock - return 0 - if(hasShocked) - return 0 //Already shocked someone recently? - if(!prob(prb)) - return 0 //you lucked out, no shock for you - var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread - s.set_up(5, 1, src) - s.start() //sparks always. - if(electrocute_mob(user, get_area(src), src)) - hasShocked = 1 - spawn(10) - hasShocked = 0 - return 1 - else - return 0 - -/obj/machinery/door/airlock/update_icon(state=0, override=0) - if(operating && !override) - return - switch(state) - if(0) - if(density) - state = AIRLOCK_CLOSED - else - state = AIRLOCK_OPEN - icon_state = "" - if(AIRLOCK_OPEN, AIRLOCK_CLOSED) - icon_state = "" - if(AIRLOCK_DENY, AIRLOCK_OPENING, AIRLOCK_CLOSING, AIRLOCK_EMAG) - icon_state = "nonexistenticonstate" //MADNESS - set_airlock_overlays(state) - -/obj/machinery/door/airlock/proc/set_airlock_overlays(state) - var/image/frame_overlay - var/image/filling_overlay - var/image/lights_overlay - var/image/panel_overlay - var/image/weld_overlay - var/image/sparks_overlay - - switch(state) - if(AIRLOCK_CLOSED) - frame_overlay = get_airlock_overlay("closed", icon) - if(airlock_material) - filling_overlay = get_airlock_overlay("[airlock_material]_closed", overlays_file) - else - filling_overlay = get_airlock_overlay("fill_closed", icon) - if(p_open) - panel_overlay = get_airlock_overlay("panel_closed", overlays_file) - if(welded) - weld_overlay = get_airlock_overlay("welded", overlays_file) - if(lights) - if(locked) - lights_overlay = get_airlock_overlay("lights_bolts", overlays_file) - else if(emergency) - lights_overlay = get_airlock_overlay("lights_emergency", overlays_file) - - if(AIRLOCK_DENY) - frame_overlay = get_airlock_overlay("closed", icon) - if(airlock_material) - filling_overlay = get_airlock_overlay("[airlock_material]_closed", overlays_file) - else - filling_overlay = get_airlock_overlay("fill_closed", icon) - if(p_open) - panel_overlay = get_airlock_overlay("panel_closed", overlays_file) - if(welded) - weld_overlay = get_airlock_overlay("welded", overlays_file) - lights_overlay = get_airlock_overlay("lights_denied", overlays_file) - - if(AIRLOCK_EMAG) - frame_overlay = get_airlock_overlay("closed", icon) - sparks_overlay = get_airlock_overlay("sparks", overlays_file) - if(airlock_material) - filling_overlay = get_airlock_overlay("[airlock_material]_closed", overlays_file) - else - filling_overlay = get_airlock_overlay("fill_closed", icon) - if(p_open) - panel_overlay = get_airlock_overlay("panel_closed", overlays_file) - if(welded) - weld_overlay = get_airlock_overlay("welded", overlays_file) - - if(AIRLOCK_CLOSING) - frame_overlay = get_airlock_overlay("closing", icon) - if(airlock_material) - filling_overlay = get_airlock_overlay("[airlock_material]_closing", overlays_file) - else - filling_overlay = get_airlock_overlay("fill_closing", icon) - if(lights) - lights_overlay = get_airlock_overlay("lights_closing", overlays_file) - if(p_open) - panel_overlay = get_airlock_overlay("panel_closing", overlays_file) - - if(AIRLOCK_OPEN) - frame_overlay = get_airlock_overlay("open", icon) - if(airlock_material) - filling_overlay = get_airlock_overlay("[airlock_material]_open", overlays_file) - else - filling_overlay = get_airlock_overlay("fill_open", icon) - if(p_open) - panel_overlay = get_airlock_overlay("panel_open", overlays_file) - - if(AIRLOCK_OPENING) - frame_overlay = get_airlock_overlay("opening", icon) - if(airlock_material) - filling_overlay = get_airlock_overlay("[airlock_material]_opening", overlays_file) - else - filling_overlay = get_airlock_overlay("fill_opening", icon) - if(lights) - lights_overlay = get_airlock_overlay("lights_opening", overlays_file) - if(p_open) - panel_overlay = get_airlock_overlay("panel_opening", overlays_file) - - //doesn't use overlays.Cut() for performance reasons - if(frame_overlay != old_frame_overlay) - overlays -= old_frame_overlay - overlays += frame_overlay - old_frame_overlay = frame_overlay - if(filling_overlay != old_filling_overlay) - overlays -= old_filling_overlay - overlays += filling_overlay - old_filling_overlay = filling_overlay - if(lights_overlay != old_lights_overlay) - overlays -= old_lights_overlay - overlays += lights_overlay - old_lights_overlay = lights_overlay - if(panel_overlay != old_panel_overlay) - overlays -= old_panel_overlay - overlays += panel_overlay - old_panel_overlay = panel_overlay - if(weld_overlay != old_weld_overlay) - overlays -= old_weld_overlay - overlays += weld_overlay - old_weld_overlay = weld_overlay - if(sparks_overlay != old_sparks_overlay) - overlays -= old_sparks_overlay - overlays += sparks_overlay - old_sparks_overlay = sparks_overlay - -/proc/get_airlock_overlay(icon_state, icon_file) - var/iconkey = "[icon_state][icon_file]" - if(airlock_overlays[iconkey]) - return airlock_overlays[iconkey] - airlock_overlays[iconkey] = image(icon_file, icon_state) - return airlock_overlays[iconkey] - -/obj/machinery/door/airlock/do_animate(animation) - switch(animation) - if("opening") - update_icon(AIRLOCK_OPENING) - if("closing") - update_icon(AIRLOCK_CLOSING) - if("deny") - update_icon(AIRLOCK_DENY) - sleep(6) - update_icon(AIRLOCK_CLOSED) - icon_state = "closed" - -/obj/machinery/door/airlock/examine(mob/user) - ..() - if(charge && !p_open && in_range(user, src)) - user << "The maintenance panel seems haphazardly fastened." - if(charge && p_open) - user << "Something is wired up to the airlock's electronics!" - -/obj/machinery/door/airlock/attack_ai(mob/user) - 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(emagged) - user << "Unable to interface: Airlock is unresponsive." - return - if(detonated) - user << "Unable to interface. Airlock control panel damaged." - return - - //Separate interface for the AI. - user.set_machine(src) - var/t1 = text("Airlock Control
\n") - if(src.secondsMainPowerLost > 0) - if((!src.isWireCut(AIRLOCK_WIRE_MAIN_POWER1)) && (!src.isWireCut(AIRLOCK_WIRE_MAIN_POWER2))) - t1 += text("Main power is offline for [] seconds.
\n", src.secondsMainPowerLost) - else - t1 += text("Main power is offline indefinitely.
\n") - else - t1 += text("Main power is online.") - - if(src.secondsBackupPowerLost > 0) - if((!src.isWireCut(AIRLOCK_WIRE_BACKUP_POWER1)) && (!src.isWireCut(AIRLOCK_WIRE_BACKUP_POWER2))) - t1 += text("Backup power is offline for [] seconds.
\n", src.secondsBackupPowerLost) - else - t1 += text("Backup power is offline indefinitely.
\n") - else if(src.secondsMainPowerLost > 0) - t1 += text("Backup power is online.") - else - t1 += text("Backup power is offline, but will turn on if main power fails.") - t1 += "
\n" - - if(src.isWireCut(AIRLOCK_WIRE_IDSCAN)) - t1 += text("IdScan wire is cut.
\n") - else if(src.aiDisabledIdScanner) - t1 += text("IdScan disabled. Enable?
\n", src) - else - t1 += text("IdScan enabled. Disable?
\n", src) - - if(src.emergency) - t1 += text("Emergency Access Override is enabled. Disable?
\n", src) - else - t1 += text("Emergency Access Override is disabled. Enable?
\n", src) - - if(src.isWireCut(AIRLOCK_WIRE_MAIN_POWER1)) - t1 += text("Main Power Input wire is cut.
\n") - if(src.isWireCut(AIRLOCK_WIRE_MAIN_POWER2)) - t1 += text("Main Power Output wire is cut.
\n") - if(src.secondsMainPowerLost == 0) - t1 += text("Temporarily disrupt main power?.
\n", src) - if(src.secondsBackupPowerLost == 0) - t1 += text("Temporarily disrupt backup power?.
\n", src) - - if(src.isWireCut(AIRLOCK_WIRE_BACKUP_POWER1)) - t1 += text("Backup Power Input wire is cut.
\n") - if(src.isWireCut(AIRLOCK_WIRE_BACKUP_POWER2)) - 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") - else if(!src.locked) - t1 += text("Door bolts are up. Drop them?
\n", src) - else - t1 += text("Door bolts are down.") - if(src.hasPower()) - t1 += text(" Raise?
\n", src) - else - t1 += text(" Cannot raise door bolts due to power failure.
\n") - - if(src.isWireCut(AIRLOCK_WIRE_LIGHT)) - t1 += text("Door bolt lights wire is cut.
\n") - else if(!src.lights) - t1 += text("Door bolt lights are off. Enable?
\n", src) - else - t1 += text("Door bolt lights are on. Disable?
\n", src) - - if(src.isWireCut(AIRLOCK_WIRE_ELECTRIFY)) - t1 += text("Electrification wire is cut.
\n") - if(src.secondsElectrified==-1) - t1 += text("Door is electrified indefinitely. Un-electrify it?
\n", src) - else if(src.secondsElectrified>0) - t1 += text("Door is electrified temporarily ([] seconds). Un-electrify it?
\n", src.secondsElectrified, src) - else - t1 += text("Door is not electrified. Electrify it for 30 seconds? Or, Electrify it indefinitely until someone cancels the electrification?
\n", src, src) - - if(src.isWireCut(AIRLOCK_WIRE_SAFETY)) - t1 += text("Door force sensors not responding.
\n") - else if(src.safe) - t1 += text("Door safeties operating normally. Override?
\n",src) - else - t1 += text("Danger. Door safeties disabled. Restore?
\n",src) - - if(src.isWireCut(AIRLOCK_WIRE_SPEED)) - t1 += text("Door timing circuitry not responding.
\n") - else if(src.normalspeed) - t1 += text("Door timing circuitry operating normally. Override?
\n",src) - else - t1 += text("Warning. Door timing circuitry operating abnormally. Restore?
\n",src) - - - - - if(src.welded) - t1 += text("Door appears to have been welded shut.
\n") - else if(!src.locked) - if(src.density) - t1 += text("Open door
\n", src) - else - t1 += text("Close door
\n", src) - - t1 += text("

Close

\n", src) - user << browse(t1, "window=airlock") - onclose(user, "airlock") - -//aiDisable - 1 idscan, 2 disrupt main power, 3 disrupt backup power, 4 drop door bolts, 5 un-electrify door, 7 close door, 11 lift access override -//aiEnable - 1 idscan, 4 raise door bolts, 5 electrify door for 30 seconds, 6 electrify door indefinitely, 7 open door, 11 enable access override - - -/obj/machinery/door/airlock/proc/hack(mob/user) - if(src.aiHacking==0) - src.aiHacking=1 - spawn(20) - //TODO: Make this take a minute - user << "Airlock AI control has been blocked. Beginning fault-detection." - sleep(50) - if(src.canAIControl()) - user << "Alert cancelled. Airlock control has been restored without our assistance." - src.aiHacking=0 - return - else if(!src.canAIHack()) - user << "We've lost our connection! Unable to hack airlock." - src.aiHacking=0 - return - user << "Fault confirmed: airlock control wire disabled or cut." - sleep(20) - user << "Attempting to hack into airlock. This may take some time." - sleep(200) - if(src.canAIControl()) - user << "Alert cancelled. Airlock control has been restored without our assistance." - src.aiHacking=0 - return - else if(!src.canAIHack()) - user << "We've lost our connection! Unable to hack airlock." - src.aiHacking=0 - return - user << "Upload access confirmed. Loading control program into airlock software." - sleep(170) - if(src.canAIControl()) - user << "Alert cancelled. Airlock control has been restored without our assistance." - src.aiHacking=0 - return - else if(!src.canAIHack()) - user << "We've lost our connection! Unable to hack airlock." - src.aiHacking=0 - return - user << "Transfer complete. Forcing airlock to execute program." - sleep(50) - //disable blocked control - src.aiControlDisabled = 2 - user << "Receiving control information from airlock." - sleep(10) - //bring up airlock dialog - src.aiHacking = 0 - if (user) - src.attack_ai(user) - - -/obj/machinery/door/airlock/attack_paw(mob/user) - return src.attack_hand(user) - -/obj/machinery/door/airlock/attack_hand(mob/user) - if(!(istype(user, /mob/living/silicon) || IsAdminGhost(user))) - if(src.isElectrified()) - if(src.shock(user, 100)) - return - - if(ishuman(user) && prob(40) && src.density) - var/mob/living/carbon/human/H = user - if(H.getBrainLoss() >= 60) - playsound(src.loc, 'sound/effects/bang.ogg', 25, 1) - if(!istype(H.head, /obj/item/clothing/head/helmet)) - H.visible_message("[user] headbutts the airlock.", \ - "You headbutt the airlock!") - var/obj/item/organ/limb/affecting = H.get_organ("head") - H.Stun(5) - H.Weaken(5) - if(affecting.take_damage(10, 0)) - H.update_damage_overlays(0) - else - visible_message("[user] headbutts the airlock. Good thing they're wearing a helmet.") - return - - if(src.p_open) - wires.Interact(user) - else - ..(user) - return - - -/obj/machinery/door/airlock/Topic(href, href_list, var/nowindow = 0) - // If you add an if(..()) check you must first remove the var/nowindow parameter. - // Otherwise it will runtime with this kind of error: null.Topic() - if(!nowindow) - ..() - if((usr.stat || usr.restrained()) && !IsAdminGhost(usr)) - return - add_fingerprint(usr) - if(href_list["close"]) - usr << browse(null, "window=airlock") - if(usr.machine==src) - usr.unset_machine() - return - - if((in_range(src, usr) && istype(src.loc, /turf)) && src.p_open) - usr.set_machine(src) - - - - if((istype(usr, /mob/living/silicon) && src.canAIControl()) || IsAdminGhost(usr)) - //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, 11 emergency access - //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, 11 emergency access - if(href_list["aiDisable"]) - var/code = text2num(href_list["aiDisable"]) - switch (code) - 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." - else if(src.aiDisabledIdScanner) - usr << "You've already disabled the IdScan feature." - else - src.aiDisabledIdScanner = 1 - if(2) - //disrupt main power - if(src.secondsMainPowerLost == 0) - src.loseMainPower() - update_icon() - else - usr << "Main power is already offline." - if(3) - //disrupt backup power - if(src.secondsBackupPowerLost == 0) - src.loseBackupPower() - update_icon() - else - usr << "Backup power is already offline." - 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 - bolt() - 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) - 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) - usr << text("The airlock has been welded shut!") - else if(src.locked) - usr << text("The door bolts are down!") - else if(!src.density) - close() - else - open() - - if(10) - // Bolt lights - if(src.isWireCut(AIRLOCK_WIRE_LIGHT)) - usr << text("Control to door bolt lights has been severed.") - else if (src.lights) - lights = 0 - update_icon() - else - usr << text("Door bolt lights are already disabled!") - - if(11) - // Emergency access - if (src.emergency) - emergency = 0 - update_icon() - else - usr << text("Emergency access is already disabled!") - - - else if(href_list["aiEnable"]) - var/code = text2num(href_list["aiEnable"]) - switch (code) - if(1) - //enable idscan - if(src.isWireCut(AIRLOCK_WIRE_IDSCAN)) - usr << "You can't enable IdScan - The IdScan wire has been cut." - else if(src.aiDisabledIdScanner) - src.aiDisabledIdScanner = 0 - else - usr << "The IdScan feature is not disabled." - 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") - else if(!src.locked) - usr << text("The door bolts are already up.
\n") - else - if(src.hasPower()) - unbolt() - else - usr << text("Cannot raise door bolts due to power failure.
\n") - - if(5) - //electrify door for 30 seconds - if(src.isWireCut(AIRLOCK_WIRE_ELECTRIFY)) - usr << text("The electrification wire has been cut.
\n") - 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") - else - shockedby += text("\[[time_stamp()]\][usr](ckey:[usr.ckey])") - add_logs(usr, src, "electrified", addition="at [x],[y],[z]") - 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 - if(src.isWireCut(AIRLOCK_WIRE_ELECTRIFY)) - usr << text("The electrification wire has been cut.
\n") - else if(src.secondsElectrified==-1) - usr << text("The door is already indefinitely electrified.
\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") - else - shockedby += text("\[[time_stamp()]\][usr](ckey:[usr.ckey])") - add_logs(usr, src, "electrified", addition="at [x],[y],[z]") - 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) - usr << text("The airlock has been welded shut!") - else if(src.locked) - usr << text("The door bolts are down!") - else if(src.density) - open() - else - close() - - if(10) - // Bolt lights - if(src.isWireCut(AIRLOCK_WIRE_LIGHT)) - usr << text("Control to door bolt lights has been severed.") - else if (!src.lights) - lights = 1 - update_icon() - src.updateUsrDialog() - else - usr << text("Door bolt lights are already enabled!") - - if(11) - // Emergency access - if (!src.emergency) - emergency = 1 - update_icon() - else - usr << text("Emergency access is already enabled!") - - add_fingerprint(usr) - if(!nowindow) - updateUsrDialog() - return - -/obj/machinery/door/airlock/attackby(obj/item/C, mob/user, params) - if(!(istype(usr, /mob/living/silicon) || IsAdminGhost(user))) - if(src.isElectrified()) - if(src.shock(user, 75)) - return - if(istype(C, /obj/item/device/detective_scanner)) - return - - if(istype(C, /obj/item/weapon/card/emag)) - return - - src.add_fingerprint(user) - if((istype(C, /obj/item/weapon/weldingtool) && !( src.operating ) && src.density)) - var/obj/item/weapon/weldingtool/W = C - if(W.remove_fuel(0,user)) - user.visible_message("[user] is [welded ? "unwelding":"welding"] the airlock.", \ - "You begin [welded ? "unwelding":"welding"] the airlock...", \ - "You hear welding.") - playsound(loc, 'sound/items/Welder.ogg', 40, 1) - if(do_after(user,40/C.toolspeed,5,1, target = src)) - if(density && !operating)//Door must be closed to weld. - if( !istype(src, /obj/machinery/door/airlock) || !user || !W || !W.isOn() || !user.loc ) - return - playsound(loc, 'sound/items/Welder2.ogg', 50, 1) - welded = !welded - user.visible_message("[user.name] has [welded? "welded shut":"unwelded"] [src].", \ - "You [welded ? "weld the airlock shut":"unweld the airlock"].") - update_icon() - return - else if(istype(C, /obj/item/weapon/screwdriver)) - if(p_open && detonated) - user << "[src] has no maintenance panel!" - return - src.p_open = !( src.p_open ) - user << "You [p_open ? "open":"close"] the maintenance panel of the airlock." - src.update_icon() - else if(wires.IsInteractionTool(C)) - return src.attack_hand(user) - else if(istype(C, /obj/item/weapon/pai_cable)) - var/obj/item/weapon/pai_cable/cable = C - cable.plugin(src, user) - else if(istype(C, /obj/item/weapon/crowbar) || istype(C, /obj/item/weapon/twohanded/fireaxe) ) - var/beingcrowbarred = null - if(istype(C, /obj/item/weapon/crowbar) ) - beingcrowbarred = 1 //derp, Agouri - else - beingcrowbarred = 0 - if(p_open && charge) - user << "You carefully start removing [charge] from [src]..." - playsound(get_turf(src), 'sound/items/Crowbar.ogg', 50, 1) - if(!do_after(user, 150/C.toolspeed, target = src)) - user << "You slip and [charge] detonates!" - charge.ex_act(1) - user.Weaken(3) - return - user.visible_message("[user] removes [charge] from [src].", \ - "You gently pry out [charge] from [src] and unhook its wires.") - charge.loc = get_turf(user) - charge = null - return - if( beingcrowbarred && (density && welded && !operating && src.p_open && (!hasPower()) && !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/C.toolspeed, target = src)) - if(src.loc) - if(src.doortype) - var/obj/structure/door_assembly/A = new src.doortype(src.loc) - A.heat_proof_finished = src.heat_proof //tracks whether there's rglass in - else - new /obj/structure/door_assembly/door_assembly_0(src.loc) - //If you come across a null doortype, it will produce the default assembly instead of disintegrating. - - if(emagged) - user << "You discard the damaged electronics." - qdel(src) - return - user << "You remove the airlock electronics." - - var/obj/item/weapon/electronics/airlock/ae - if(!electronics) - ae = new/obj/item/weapon/electronics/airlock( src.loc ) - if(req_one_access) - ae.one_access = 1 - ae.accesses = src.req_one_access - else - ae.accesses = src.req_access - else - ae = electronics - electronics = null - ae.loc = src.loc - - qdel(src) - return - else if(hasPower()) - user << "The airlock's motors resist your efforts to force it!" - else if(locked) - user << "The airlock's bolts prevent it from being forced!" - else if( !welded && !operating) - if(density) - if(beingcrowbarred == 0) //being fireaxe'd - var/obj/item/weapon/twohanded/fireaxe/F = C - if(F:wielded) - spawn(0) - open(2) - else - user << "You need to be wielding the fire axe to do that!" - else - spawn(0) - open(2) - else - if(beingcrowbarred == 0) - var/obj/item/weapon/twohanded/fireaxe/F = C - if(F:wielded) - spawn(0) - close(2) - else - user << "You need to be wielding the fire axe to do that!" - else - spawn(0) - close(2) - - else if(istype(C, /obj/item/weapon/airlock_painter)) - change_paintjob(C, user) - else if(istype(C, /obj/item/device/doorCharge) && p_open) - if(emagged) - return - if(charge && !detonated) - user << "There's already a charge hooked up to this door!" - return - if(detonated) - user << "The maintenance panel is destroyed!" - return - user << "You apply [C]. Next time someone opens the door, it will explode." - user.drop_item() - p_open = 0 - update_icon() - var/obj/item/device/doorCharge/newCharge = C //This is necessary, for some reason - newCharge.loc = src - charge = newCharge - return - else if(istype(C, /obj/item/weapon/rcd)&& istype(loc, /turf/simulated)) //Do not attack the airlock if the user is holding an RCD - return - else - ..() - return - -/obj/machinery/door/airlock/plasma/attackby(obj/item/C, mob/user, params) - if(C.is_hot() > 300)//If the temperature of the object is over 300, then ignite - message_admins("Plasma airlock ignited by [key_name_admin(user)](?) (FLW) in ([x],[y],[z] - JMP)") - log_game("Plasma wall ignited by [key_name(user)] in ([x],[y],[z])") - ignite(C.is_hot()) - return - ..() - -/obj/machinery/door/airlock/open(forced=0) - if( operating || welded || locked ) - return 0 - if(!forced) - if( !hasPower() || isWireCut(AIRLOCK_WIRE_OPEN_DOOR) ) - return 0 - if(charge && !detonated) - p_open = 1 - update_icon(AIRLOCK_OPENING) - visible_message("[src]'s panel is blown off in a spray of deadly shrapnel!") - charge.loc = get_turf(src) - charge.ex_act(1) - detonated = 1 - charge = null - for(var/mob/living/carbon/human/H in orange(1,src)) - H.Paralyse(8) - H.adjust_fire_stacks(1) - H.IgniteMob() //Guaranteed knockout and ignition for nearby people - H.apply_damage(20, BRUTE, "chest") - return - if(forced < 2) - if(emagged) - return 0 - use_power(50) - 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) - src.closeOther.close() - else - playsound(src.loc, 'sound/machines/airlockforced.ogg', 30, 1) - - if(autoclose && normalspeed) - spawn(150) - autoclose() - else if(autoclose && !normalspeed) - spawn(11) - autoclose() - - if(!density) - return 1 - if(!ticker || !ticker.mode) - return 0 - operating = 1 - update_icon(AIRLOCK_OPENING, 1) - src.SetOpacity(0) - sleep(5) - src.density = 0 - sleep(9) - src.layer = 2.7 - update_icon(AIRLOCK_OPEN, 1) - SetOpacity(0) - operating = 0 - air_update_turf(1) - update_freelook_sight() - return 1 - - -/obj/machinery/door/airlock/close(forced=0) - if(operating || welded || locked) - return - if(!forced) - if( !hasPower() || isWireCut(AIRLOCK_WIRE_DOOR_BOLTS) ) - return - if(safe) - for(var/atom/movable/M in get_turf(src)) - if(M.density && M != src) //something is blocking the door - spawn (60) - autoclose() - return - - if(forced < 2) - if(emagged) - return - use_power(50) - 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) - else - playsound(src.loc, 'sound/machines/airlockforced.ogg', 30, 1) - - var/obj/structure/window/killthis = (locate(/obj/structure/window) in get_turf(src)) - if(killthis) - killthis.ex_act(2)//Smashin windows - - if(density) - return 1 - operating = 1 - update_icon(AIRLOCK_CLOSING, 1) - src.layer = 3.1 - sleep(5) - src.density = 1 - if(!safe) - crush() - sleep(9) - update_icon(AIRLOCK_CLOSED, 1) - if(visible && !glass) - SetOpacity(1) - operating = 0 - air_update_turf(1) - update_freelook_sight() - if(safe) - if(locate(/mob/living) in get_turf(src)) - sleep(1) - open() - return 1 - -/obj/machinery/door/airlock/proc/prison_open() - if(emagged) return - src.locked = 0 - src.open() - src.locked = 1 - return - - -/obj/machinery/door/airlock/proc/autoclose() - if(!qdeleted(src) && !density && !operating && !locked && !welded && autoclose) - close() - -/obj/machinery/door/airlock/proc/change_paintjob(obj/item/C, mob/user) - var/obj/item/weapon/airlock_painter/W - if(istype(C, /obj/item/weapon/airlock_painter)) - W = C - else - user << "If you see this, it means airlock/change_paintjob() was called with something other than an airlock painter. Check your code!" - return - - if(!W.can_use(user)) - return - - var/list/optionlist - if(airlock_material == "glass") - optionlist = list("Public", "Public2", "Engineering", "Atmospherics", "Security", "Command", "Medical", "Research", "Mining", "Maintenance") - else - optionlist = list("Public", "Engineering", "Atmospherics", "Security", "Command", "Medical", "Research", "Mining", "Maintenance", "External", "High Security") - - var/paintjob = input(user, "Please select a paintjob for this airlock.") in optionlist - if((!in_range(src, usr) && src.loc != usr) || !W.use(user)) return - switch(paintjob) - if("Public") - icon = 'icons/obj/doors/airlocks/station/public.dmi' - overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' - doortype = /obj/structure/door_assembly/door_assembly_0 - if("Public2") - icon = 'icons/obj/doors/airlocks/station2/glass.dmi' - overlays_file = 'icons/obj/doors/airlocks/station2/overlays.dmi' - doortype = /obj/structure/door_assembly/door_assembly_glass - if("Engineering") - icon = 'icons/obj/doors/airlocks/station/engineering.dmi' - overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' - doortype = /obj/structure/door_assembly/door_assembly_eng - if("Atmospherics") - icon = 'icons/obj/doors/airlocks/station/atmos.dmi' - overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' - doortype = /obj/structure/door_assembly/door_assembly_atmo - if("Security") - icon = 'icons/obj/doors/airlocks/station/security.dmi' - overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' - doortype = /obj/structure/door_assembly/door_assembly_sec - if("Command") - icon = 'icons/obj/doors/airlocks/station/command.dmi' - overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' - doortype = /obj/structure/door_assembly/door_assembly_com - if("Medical") - icon = 'icons/obj/doors/airlocks/station/medical.dmi' - overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' - doortype = /obj/structure/door_assembly/door_assembly_med - if("Research") - icon = 'icons/obj/doors/airlocks/station/research.dmi' - overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' - doortype = /obj/structure/door_assembly/door_assembly_research - if("Mining") - icon = 'icons/obj/doors/airlocks/station/mining.dmi' - overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' - doortype = /obj/structure/door_assembly/door_assembly_min - if("Maintenance") - icon = 'icons/obj/doors/airlocks/station/maintenance.dmi' - overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' - doortype = /obj/structure/door_assembly/door_assembly_mai - if("External") - icon = 'icons/obj/doors/airlocks/external/external.dmi' - overlays_file = 'icons/obj/doors/airlocks/external/overlays.dmi' - doortype = /obj/structure/door_assembly/door_assembly_ext - if("High Security") - icon = 'icons/obj/doors/airlocks/highsec/highsec.dmi' - overlays_file = 'icons/obj/doors/airlocks/highsec/overlays.dmi' - doortype = /obj/structure/door_assembly/door_assembly_highsecurity - update_icon() - -/obj/machinery/door/airlock/CanAStarPass(obj/item/weapon/card/id/ID) -//Airlock is passable if it is open (!density), bot has access, and is not bolted shut or powered off) - return !density || (check_access(ID) && !locked && hasPower()) - -/obj/machinery/door/airlock/HasProximity(atom/movable/AM as mob|obj) - for (var/obj/A in contents) - A.HasProximity(AM) - return - -/obj/machinery/door/airlock/emag_act(mob/user) - if(!operating && density && hasPower() && !emagged) - operating = 1 - update_icon(AIRLOCK_EMAG) - sleep(6) - if(qdeleted(src)) - return - operating = 0 - if(!open()) - update_icon(AIRLOCK_CLOSED) - emagged = 1 - desc = "Its access panel is smoking slightly." - lights = 0 - locked = 1 - loseMainPower() - loseBackupPower() +/* + New methods: + pulse - sends a pulse into a wire for hacking purposes + cut - cuts a wire and makes any necessary state changes + mend - mends a wire and makes any necessary state changes + canAIControl - 1 if the AI can control the airlock, 0 if not (then check canAIHack to see if it can hack in) + canAIHack - 1 if the AI can hack into the airlock to recover control, 0 if not. Also returns 0 if the AI does not *need* to hack it. + hasPower - 1 if the main or backup power are functioning, 0 if not. + requiresIDs - 1 if the airlock is requiring IDs, 0 if not + isAllPowerCut - 1 if the main and backup power both have cut wires. + regainMainPower - handles the effect of main power coming back on. + loseMainPower - handles the effect of main power going offline. Usually (if one isn't already running) spawn a thread to count down how long it will be offline - counting down won't happen if main power was completely cut along with backup power, though, the thread will just sleep. + loseBackupPower - handles the effect of backup power going offline. + regainBackupPower - handles the effect of main power coming back on. + shock - has a chance of electrocuting its target. +*/ + +// Wires for the airlock are located in the datum folder, inside the wires datum folder. + +#define AIRLOCK_CLOSED 1 +#define AIRLOCK_CLOSING 2 +#define AIRLOCK_OPEN 3 +#define AIRLOCK_OPENING 4 +#define AIRLOCK_DENY 5 +#define AIRLOCK_EMAG 6 +var/list/airlock_overlays = list() + +/obj/machinery/door/airlock + name = "airlock" + icon = 'icons/obj/doors/airlocks/station/public.dmi' + icon_state = "closed" + + 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. + var/secondsBackupPowerLost = 0 //The number of seconds until power is restored. + var/spawnPowerRestoreRunning = 0 + var/welded = null + var/locked = 0 + var/lights = 1 // bolt lights show by default + var/datum/wires/airlock/wires = null + secondsElectrified = 0 //How many seconds remain until the door is no longer electrified. -1 if it is permanently electrified until someone fixes it. + var/aiDisabledIdScanner = 0 + var/aiHacking = 0 + var/obj/machinery/door/airlock/closeOther = null + var/closeOtherId = null + var/lockdownbyai = 0 + var/doortype = /obj/structure/door_assembly/door_assembly_0 + var/justzap = 0 + var/safe = 1 + normalspeed = 1 + var/obj/item/weapon/electronics/airlock/electronics = null + var/hasShocked = 0 //Prevents multiple shocks from happening + var/autoclose = 1 + var/obj/item/device/doorCharge/charge = null //If applied, causes an explosion upon opening the door + var/detonated = 0 + + var/airlock_material = null //material of inner filling; if its an airlock with glass, this should be set to "glass" + var/overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' + + var/image/old_frame_overlay //keep those in order to prevent unnecessary updating + var/image/old_filling_overlay + var/image/old_lights_overlay + var/image/old_panel_overlay + var/image/old_weld_overlay + var/image/old_sparks_overlay + + explosion_block = 1 + +/obj/machinery/door/airlock/New() + ..() + wires = new(src) + if(src.closeOtherId != null) + spawn (5) + for (var/obj/machinery/door/airlock/A in airlocks) + if(A.closeOtherId == src.closeOtherId && A != src) + src.closeOther = A + break + if(glass) + airlock_material = "glass" + update_icon() + +/* +About the new airlock wires panel: +* An airlock wire dialog can be accessed by the normal way or by using wirecutters or a multitool on the door while the wire-panel is open. This would show the following wires, which you can either wirecut/mend or send a multitool pulse through. There are 9 wires. +* one wire from the ID scanner. Sending a pulse through this flashes the red light on the door (if the door has power). If you cut this wire, the door will stop recognizing valid IDs. (If the door has 0000 access, it still opens and closes, though) +* two wires for power. 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). 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 \red open, but bolts-raising will not work. Cutting these wires may electrocute the user. +* one wire for door bolts. Sending a pulse through this drops door bolts (whether the door is powered or not) or raises them (if it is). Cutting this wire also drops the door bolts, and mending it does not raise them. If the wire is cut, trying to raise the door bolts will not work. +* 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). Cutting either one disables the backup door power (allowing it to be crowbarred open, but disabling bolts-raising), but may electocute the user. +* one wire for opening the door. Sending a pulse through this while the door has power makes it open the door if no access is required. +* one wire for AI control. Sending a pulse through this blocks AI control for a second or so (which is enough to see the AI control light on the panel dialog go off and back on again). Cutting this prevents the AI from controlling the door unless it has hacked the door through the power connection (which takes about a minute). If both main and backup power are cut, as well as this wire, then the AI cannot operate or hack the door at all. +* one wire for electrifying the door. Sending a pulse through this electrifies the door for 30 seconds. Cutting this wire electrifies the door, so that the next person to touch the door without insulated gloves gets electrocuted. (Currently it is also STAYING electrified until someone mends the wire) +* one wire for controling door safetys. When active, door does not close on someone. When cut, door will ruin someone's shit. When pulsed, door will immedately ruin someone's shit. +* one wire for controlling door speed. When active, dor closes at normal rate. When cut, door does not close manually. When pulsed, door attempts to close every tick. +*/ +// You can find code for the airlock wires in the wire datum folder. + +/obj/machinery/door/airlock/proc/bolt() + if(locked) + return + locked = 1 + update_icon() + +/obj/machinery/door/airlock/proc/unbolt() + if(!locked) + return + locked = 0 + update_icon() + +/obj/machinery/door/airlock/Destroy() + qdel(wires) + wires = null + if(id_tag) + for(var/obj/machinery/doorButtons/D in machines) + D.removeMe(src) + return ..() + +/obj/machinery/door/airlock/bumpopen(mob/living/user) //Airlocks now zap you when you 'bump' them open when they're electrified. --NeoFite + if(!issilicon(usr)) + if(src.isElectrified()) + if(!src.justzap) + if(src.shock(user, 100)) + src.justzap = 1 + spawn (10) + src.justzap = 0 + return + else /*if(src.justzap)*/ + return + else if(user.hallucination > 50 && prob(10) && src.operating == 0) + user << "You feel a powerful shock course through your body!" + user.staminaloss += 50 + user.stunned += 5 + return + ..(user) + +/obj/machinery/door/airlock/bumpopen(mob/living/simple_animal/user) + ..(user) + +/obj/machinery/door/airlock/proc/isElectrified() + if(src.secondsElectrified != 0) + return 1 + return 0 + +/obj/machinery/door/airlock/proc/isWireCut(wireIndex) + // You can find the wires in the datum folder. + return wires.IsIndexCut(wireIndex) + +/obj/machinery/door/airlock/proc/canAIControl() + return ((src.aiControlDisabled!=1) && (!src.isAllPowerCut())); + +/obj/machinery/door/airlock/proc/canAIHack() + return ((src.aiControlDisabled==1) && (!hackProof) && (!src.isAllPowerCut())); + +/obj/machinery/door/airlock/hasPower() + return ((src.secondsMainPowerLost==0 || src.secondsBackupPowerLost==0) && !(stat & NOPOWER)) + +/obj/machinery/door/airlock/requiresID() + return !(src.isWireCut(AIRLOCK_WIRE_IDSCAN) || aiDisabledIdScanner) + +/obj/machinery/door/airlock/proc/isAllPowerCut() + var/retval=0 + 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 + +/obj/machinery/door/airlock/proc/regainMainPower() + if(src.secondsMainPowerLost > 0) + src.secondsMainPowerLost = 0 + +/obj/machinery/door/airlock/proc/loseMainPower() + if(src.secondsMainPowerLost <= 0) + src.secondsMainPowerLost = 60 + if(src.secondsBackupPowerLost < 10) + src.secondsBackupPowerLost = 10 + if(!src.spawnPowerRestoreRunning) + src.spawnPowerRestoreRunning = 1 + spawn(0) + var/cont = 1 + while (cont) + sleep(10) + if(qdeleted(src)) + return + cont = 0 + if(src.secondsMainPowerLost>0) + if((!src.isWireCut(AIRLOCK_WIRE_MAIN_POWER1)) && (!src.isWireCut(AIRLOCK_WIRE_MAIN_POWER2))) + src.secondsMainPowerLost -= 1 + src.updateDialog() + cont = 1 + + if(src.secondsBackupPowerLost>0) + if((!src.isWireCut(AIRLOCK_WIRE_BACKUP_POWER1)) && (!src.isWireCut(AIRLOCK_WIRE_BACKUP_POWER2))) + src.secondsBackupPowerLost -= 1 + src.updateDialog() + cont = 1 + src.spawnPowerRestoreRunning = 0 + src.updateDialog() + +/obj/machinery/door/airlock/proc/loseBackupPower() + if(src.secondsBackupPowerLost < 60) + src.secondsBackupPowerLost = 60 + +/obj/machinery/door/airlock/proc/regainBackupPower() + if(src.secondsBackupPowerLost > 0) + src.secondsBackupPowerLost = 0 + +// shock user with probability prb (if all connections & power are working) +// returns 1 if shocked, 0 otherwise +// The preceding comment was borrowed from the grille's shock script +/obj/machinery/door/airlock/proc/shock(mob/user, prb) + if(!hasPower()) // unpowered, no shock + return 0 + if(hasShocked) + return 0 //Already shocked someone recently? + if(!prob(prb)) + return 0 //you lucked out, no shock for you + var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread + s.set_up(5, 1, src) + s.start() //sparks always. + if(electrocute_mob(user, get_area(src), src)) + hasShocked = 1 + spawn(10) + hasShocked = 0 + return 1 + else + return 0 + +/obj/machinery/door/airlock/update_icon(state=0, override=0) + if(operating && !override) + return + switch(state) + if(0) + if(density) + state = AIRLOCK_CLOSED + else + state = AIRLOCK_OPEN + icon_state = "" + if(AIRLOCK_OPEN, AIRLOCK_CLOSED) + icon_state = "" + if(AIRLOCK_DENY, AIRLOCK_OPENING, AIRLOCK_CLOSING, AIRLOCK_EMAG) + icon_state = "nonexistenticonstate" //MADNESS + set_airlock_overlays(state) + +/obj/machinery/door/airlock/proc/set_airlock_overlays(state) + var/image/frame_overlay + var/image/filling_overlay + var/image/lights_overlay + var/image/panel_overlay + var/image/weld_overlay + var/image/sparks_overlay + + switch(state) + if(AIRLOCK_CLOSED) + frame_overlay = get_airlock_overlay("closed", icon) + if(airlock_material) + filling_overlay = get_airlock_overlay("[airlock_material]_closed", overlays_file) + else + filling_overlay = get_airlock_overlay("fill_closed", icon) + if(p_open) + panel_overlay = get_airlock_overlay("panel_closed", overlays_file) + if(welded) + weld_overlay = get_airlock_overlay("welded", overlays_file) + if(lights) + if(locked) + lights_overlay = get_airlock_overlay("lights_bolts", overlays_file) + else if(emergency) + lights_overlay = get_airlock_overlay("lights_emergency", overlays_file) + + if(AIRLOCK_DENY) + frame_overlay = get_airlock_overlay("closed", icon) + if(airlock_material) + filling_overlay = get_airlock_overlay("[airlock_material]_closed", overlays_file) + else + filling_overlay = get_airlock_overlay("fill_closed", icon) + if(p_open) + panel_overlay = get_airlock_overlay("panel_closed", overlays_file) + if(welded) + weld_overlay = get_airlock_overlay("welded", overlays_file) + lights_overlay = get_airlock_overlay("lights_denied", overlays_file) + + if(AIRLOCK_EMAG) + frame_overlay = get_airlock_overlay("closed", icon) + sparks_overlay = get_airlock_overlay("sparks", overlays_file) + if(airlock_material) + filling_overlay = get_airlock_overlay("[airlock_material]_closed", overlays_file) + else + filling_overlay = get_airlock_overlay("fill_closed", icon) + if(p_open) + panel_overlay = get_airlock_overlay("panel_closed", overlays_file) + if(welded) + weld_overlay = get_airlock_overlay("welded", overlays_file) + + if(AIRLOCK_CLOSING) + frame_overlay = get_airlock_overlay("closing", icon) + if(airlock_material) + filling_overlay = get_airlock_overlay("[airlock_material]_closing", overlays_file) + else + filling_overlay = get_airlock_overlay("fill_closing", icon) + if(lights) + lights_overlay = get_airlock_overlay("lights_closing", overlays_file) + if(p_open) + panel_overlay = get_airlock_overlay("panel_closing", overlays_file) + + if(AIRLOCK_OPEN) + frame_overlay = get_airlock_overlay("open", icon) + if(airlock_material) + filling_overlay = get_airlock_overlay("[airlock_material]_open", overlays_file) + else + filling_overlay = get_airlock_overlay("fill_open", icon) + if(p_open) + panel_overlay = get_airlock_overlay("panel_open", overlays_file) + + if(AIRLOCK_OPENING) + frame_overlay = get_airlock_overlay("opening", icon) + if(airlock_material) + filling_overlay = get_airlock_overlay("[airlock_material]_opening", overlays_file) + else + filling_overlay = get_airlock_overlay("fill_opening", icon) + if(lights) + lights_overlay = get_airlock_overlay("lights_opening", overlays_file) + if(p_open) + panel_overlay = get_airlock_overlay("panel_opening", overlays_file) + + //doesn't use overlays.Cut() for performance reasons + if(frame_overlay != old_frame_overlay) + overlays -= old_frame_overlay + overlays += frame_overlay + old_frame_overlay = frame_overlay + if(filling_overlay != old_filling_overlay) + overlays -= old_filling_overlay + overlays += filling_overlay + old_filling_overlay = filling_overlay + if(lights_overlay != old_lights_overlay) + overlays -= old_lights_overlay + overlays += lights_overlay + old_lights_overlay = lights_overlay + if(panel_overlay != old_panel_overlay) + overlays -= old_panel_overlay + overlays += panel_overlay + old_panel_overlay = panel_overlay + if(weld_overlay != old_weld_overlay) + overlays -= old_weld_overlay + overlays += weld_overlay + old_weld_overlay = weld_overlay + if(sparks_overlay != old_sparks_overlay) + overlays -= old_sparks_overlay + overlays += sparks_overlay + old_sparks_overlay = sparks_overlay + +/proc/get_airlock_overlay(icon_state, icon_file) + var/iconkey = "[icon_state][icon_file]" + if(airlock_overlays[iconkey]) + return airlock_overlays[iconkey] + airlock_overlays[iconkey] = image(icon_file, icon_state) + return airlock_overlays[iconkey] + +/obj/machinery/door/airlock/do_animate(animation) + switch(animation) + if("opening") + update_icon(AIRLOCK_OPENING) + if("closing") + update_icon(AIRLOCK_CLOSING) + if("deny") + update_icon(AIRLOCK_DENY) + sleep(6) + update_icon(AIRLOCK_CLOSED) + icon_state = "closed" + +/obj/machinery/door/airlock/examine(mob/user) + ..() + if(charge && !p_open && in_range(user, src)) + user << "The maintenance panel seems haphazardly fastened." + if(charge && p_open) + user << "Something is wired up to the airlock's electronics!" + +/obj/machinery/door/airlock/attack_ai(mob/user) + 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(emagged) + user << "Unable to interface: Airlock is unresponsive." + return + if(detonated) + user << "Unable to interface. Airlock control panel damaged." + return + + //Separate interface for the AI. + user.set_machine(src) + var/t1 = text("Airlock Control
\n") + if(src.secondsMainPowerLost > 0) + if((!src.isWireCut(AIRLOCK_WIRE_MAIN_POWER1)) && (!src.isWireCut(AIRLOCK_WIRE_MAIN_POWER2))) + t1 += text("Main power is offline for [] seconds.
\n", src.secondsMainPowerLost) + else + t1 += text("Main power is offline indefinitely.
\n") + else + t1 += text("Main power is online.") + + if(src.secondsBackupPowerLost > 0) + if((!src.isWireCut(AIRLOCK_WIRE_BACKUP_POWER1)) && (!src.isWireCut(AIRLOCK_WIRE_BACKUP_POWER2))) + t1 += text("Backup power is offline for [] seconds.
\n", src.secondsBackupPowerLost) + else + t1 += text("Backup power is offline indefinitely.
\n") + else if(src.secondsMainPowerLost > 0) + t1 += text("Backup power is online.") + else + t1 += text("Backup power is offline, but will turn on if main power fails.") + t1 += "
\n" + + if(src.isWireCut(AIRLOCK_WIRE_IDSCAN)) + t1 += text("IdScan wire is cut.
\n") + else if(src.aiDisabledIdScanner) + t1 += text("IdScan disabled. Enable?
\n", src) + else + t1 += text("IdScan enabled. Disable?
\n", src) + + if(src.emergency) + t1 += text("Emergency Access Override is enabled. Disable?
\n", src) + else + t1 += text("Emergency Access Override is disabled. Enable?
\n", src) + + if(src.isWireCut(AIRLOCK_WIRE_MAIN_POWER1)) + t1 += text("Main Power Input wire is cut.
\n") + if(src.isWireCut(AIRLOCK_WIRE_MAIN_POWER2)) + t1 += text("Main Power Output wire is cut.
\n") + if(src.secondsMainPowerLost == 0) + t1 += text("Temporarily disrupt main power?.
\n", src) + if(src.secondsBackupPowerLost == 0) + t1 += text("Temporarily disrupt backup power?.
\n", src) + + if(src.isWireCut(AIRLOCK_WIRE_BACKUP_POWER1)) + t1 += text("Backup Power Input wire is cut.
\n") + if(src.isWireCut(AIRLOCK_WIRE_BACKUP_POWER2)) + 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") + else if(!src.locked) + t1 += text("Door bolts are up. Drop them?
\n", src) + else + t1 += text("Door bolts are down.") + if(src.hasPower()) + t1 += text(" Raise?
\n", src) + else + t1 += text(" Cannot raise door bolts due to power failure.
\n") + + if(src.isWireCut(AIRLOCK_WIRE_LIGHT)) + t1 += text("Door bolt lights wire is cut.
\n") + else if(!src.lights) + t1 += text("Door bolt lights are off. Enable?
\n", src) + else + t1 += text("Door bolt lights are on. Disable?
\n", src) + + if(src.isWireCut(AIRLOCK_WIRE_ELECTRIFY)) + t1 += text("Electrification wire is cut.
\n") + if(src.secondsElectrified==-1) + t1 += text("Door is electrified indefinitely. Un-electrify it?
\n", src) + else if(src.secondsElectrified>0) + t1 += text("Door is electrified temporarily ([] seconds). Un-electrify it?
\n", src.secondsElectrified, src) + else + t1 += text("Door is not electrified. Electrify it for 30 seconds? Or, Electrify it indefinitely until someone cancels the electrification?
\n", src, src) + + if(src.isWireCut(AIRLOCK_WIRE_SAFETY)) + t1 += text("Door force sensors not responding.
\n") + else if(src.safe) + t1 += text("Door safeties operating normally. Override?
\n",src) + else + t1 += text("Danger. Door safeties disabled. Restore?
\n",src) + + if(src.isWireCut(AIRLOCK_WIRE_SPEED)) + t1 += text("Door timing circuitry not responding.
\n") + else if(src.normalspeed) + t1 += text("Door timing circuitry operating normally. Override?
\n",src) + else + t1 += text("Warning. Door timing circuitry operating abnormally. Restore?
\n",src) + + + + + if(src.welded) + t1 += text("Door appears to have been welded shut.
\n") + else if(!src.locked) + if(src.density) + t1 += text("Open door
\n", src) + else + t1 += text("Close door
\n", src) + + t1 += text("

Close

\n", src) + user << browse(t1, "window=airlock") + onclose(user, "airlock") + +//aiDisable - 1 idscan, 2 disrupt main power, 3 disrupt backup power, 4 drop door bolts, 5 un-electrify door, 7 close door, 11 lift access override +//aiEnable - 1 idscan, 4 raise door bolts, 5 electrify door for 30 seconds, 6 electrify door indefinitely, 7 open door, 11 enable access override + + +/obj/machinery/door/airlock/proc/hack(mob/user) + if(src.aiHacking==0) + src.aiHacking=1 + spawn(20) + //TODO: Make this take a minute + user << "Airlock AI control has been blocked. Beginning fault-detection." + sleep(50) + if(src.canAIControl()) + user << "Alert cancelled. Airlock control has been restored without our assistance." + src.aiHacking=0 + return + else if(!src.canAIHack()) + user << "We've lost our connection! Unable to hack airlock." + src.aiHacking=0 + return + user << "Fault confirmed: airlock control wire disabled or cut." + sleep(20) + user << "Attempting to hack into airlock. This may take some time." + sleep(200) + if(src.canAIControl()) + user << "Alert cancelled. Airlock control has been restored without our assistance." + src.aiHacking=0 + return + else if(!src.canAIHack()) + user << "We've lost our connection! Unable to hack airlock." + src.aiHacking=0 + return + user << "Upload access confirmed. Loading control program into airlock software." + sleep(170) + if(src.canAIControl()) + user << "Alert cancelled. Airlock control has been restored without our assistance." + src.aiHacking=0 + return + else if(!src.canAIHack()) + user << "We've lost our connection! Unable to hack airlock." + src.aiHacking=0 + return + user << "Transfer complete. Forcing airlock to execute program." + sleep(50) + //disable blocked control + src.aiControlDisabled = 2 + user << "Receiving control information from airlock." + sleep(10) + //bring up airlock dialog + src.aiHacking = 0 + if (user) + src.attack_ai(user) + + +/obj/machinery/door/airlock/attack_paw(mob/user) + return src.attack_hand(user) + +/obj/machinery/door/airlock/attack_hand(mob/user) + if(!(istype(user, /mob/living/silicon) || IsAdminGhost(user))) + if(src.isElectrified()) + if(src.shock(user, 100)) + return + + if(ishuman(user) && prob(40) && src.density) + var/mob/living/carbon/human/H = user + if(H.getBrainLoss() >= 60) + playsound(src.loc, 'sound/effects/bang.ogg', 25, 1) + if(!istype(H.head, /obj/item/clothing/head/helmet)) + H.visible_message("[user] headbutts the airlock.", \ + "You headbutt the airlock!") + var/obj/item/organ/limb/affecting = H.get_organ("head") + H.Stun(5) + H.Weaken(5) + if(affecting.take_damage(10, 0)) + H.update_damage_overlays(0) + else + visible_message("[user] headbutts the airlock. Good thing they're wearing a helmet.") + return + + if(src.p_open) + wires.Interact(user) + else + ..(user) + return + + +/obj/machinery/door/airlock/Topic(href, href_list, var/nowindow = 0) + // If you add an if(..()) check you must first remove the var/nowindow parameter. + // Otherwise it will runtime with this kind of error: null.Topic() + if(!nowindow) + ..() + if((usr.stat || usr.restrained()) && !IsAdminGhost(usr)) + return + add_fingerprint(usr) + if(href_list["close"]) + usr << browse(null, "window=airlock") + if(usr.machine==src) + usr.unset_machine() + return + + if((in_range(src, usr) && istype(src.loc, /turf)) && src.p_open) + usr.set_machine(src) + + + + if((istype(usr, /mob/living/silicon) && src.canAIControl()) || IsAdminGhost(usr)) + //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, 11 emergency access + //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, 11 emergency access + if(href_list["aiDisable"]) + var/code = text2num(href_list["aiDisable"]) + switch (code) + 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." + else if(src.aiDisabledIdScanner) + usr << "You've already disabled the IdScan feature." + else + src.aiDisabledIdScanner = 1 + if(2) + //disrupt main power + if(src.secondsMainPowerLost == 0) + src.loseMainPower() + update_icon() + else + usr << "Main power is already offline." + if(3) + //disrupt backup power + if(src.secondsBackupPowerLost == 0) + src.loseBackupPower() + update_icon() + else + usr << "Backup power is already offline." + 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 + bolt() + 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) + 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) + usr << text("The airlock has been welded shut!") + else if(src.locked) + usr << text("The door bolts are down!") + else if(!src.density) + close() + else + open() + + if(10) + // Bolt lights + if(src.isWireCut(AIRLOCK_WIRE_LIGHT)) + usr << text("Control to door bolt lights has been severed.") + else if (src.lights) + lights = 0 + update_icon() + else + usr << text("Door bolt lights are already disabled!") + + if(11) + // Emergency access + if (src.emergency) + emergency = 0 + update_icon() + else + usr << text("Emergency access is already disabled!") + + + else if(href_list["aiEnable"]) + var/code = text2num(href_list["aiEnable"]) + switch (code) + if(1) + //enable idscan + if(src.isWireCut(AIRLOCK_WIRE_IDSCAN)) + usr << "You can't enable IdScan - The IdScan wire has been cut." + else if(src.aiDisabledIdScanner) + src.aiDisabledIdScanner = 0 + else + usr << "The IdScan feature is not disabled." + 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") + else if(!src.locked) + usr << text("The door bolts are already up.
\n") + else + if(src.hasPower()) + unbolt() + else + usr << text("Cannot raise door bolts due to power failure.
\n") + + if(5) + //electrify door for 30 seconds + if(src.isWireCut(AIRLOCK_WIRE_ELECTRIFY)) + usr << text("The electrification wire has been cut.
\n") + 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") + else + shockedby += text("\[[time_stamp()]\][usr](ckey:[usr.ckey])") + add_logs(usr, src, "electrified", addition="at [x],[y],[z]") + 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 + if(src.isWireCut(AIRLOCK_WIRE_ELECTRIFY)) + usr << text("The electrification wire has been cut.
\n") + else if(src.secondsElectrified==-1) + usr << text("The door is already indefinitely electrified.
\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") + else + shockedby += text("\[[time_stamp()]\][usr](ckey:[usr.ckey])") + add_logs(usr, src, "electrified", addition="at [x],[y],[z]") + 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) + usr << text("The airlock has been welded shut!") + else if(src.locked) + usr << text("The door bolts are down!") + else if(src.density) + open() + else + close() + + if(10) + // Bolt lights + if(src.isWireCut(AIRLOCK_WIRE_LIGHT)) + usr << text("Control to door bolt lights has been severed.") + else if (!src.lights) + lights = 1 + update_icon() + src.updateUsrDialog() + else + usr << text("Door bolt lights are already enabled!") + + if(11) + // Emergency access + if (!src.emergency) + emergency = 1 + update_icon() + else + usr << text("Emergency access is already enabled!") + + add_fingerprint(usr) + if(!nowindow) + updateUsrDialog() + return + +/obj/machinery/door/airlock/attackby(obj/item/C, mob/user, params) + if(!(istype(usr, /mob/living/silicon) || IsAdminGhost(user))) + if(src.isElectrified()) + if(src.shock(user, 75)) + return + if(istype(C, /obj/item/device/detective_scanner)) + return + + if(istype(C, /obj/item/weapon/card/emag)) + return + + src.add_fingerprint(user) + if((istype(C, /obj/item/weapon/weldingtool) && !( src.operating ) && src.density)) + var/obj/item/weapon/weldingtool/W = C + if(W.remove_fuel(0,user)) + user.visible_message("[user] is [welded ? "unwelding":"welding"] the airlock.", \ + "You begin [welded ? "unwelding":"welding"] the airlock...", \ + "You hear welding.") + playsound(loc, 'sound/items/Welder.ogg', 40, 1) + if(do_after(user,40/C.toolspeed,5,1, target = src)) + if(density && !operating)//Door must be closed to weld. + if( !istype(src, /obj/machinery/door/airlock) || !user || !W || !W.isOn() || !user.loc ) + return + playsound(loc, 'sound/items/Welder2.ogg', 50, 1) + welded = !welded + user.visible_message("[user.name] has [welded? "welded shut":"unwelded"] [src].", \ + "You [welded ? "weld the airlock shut":"unweld the airlock"].") + update_icon() + return + else if(istype(C, /obj/item/weapon/screwdriver)) + if(p_open && detonated) + user << "[src] has no maintenance panel!" + return + src.p_open = !( src.p_open ) + user << "You [p_open ? "open":"close"] the maintenance panel of the airlock." + src.update_icon() + else if(wires.IsInteractionTool(C)) + return src.attack_hand(user) + else if(istype(C, /obj/item/weapon/pai_cable)) + var/obj/item/weapon/pai_cable/cable = C + cable.plugin(src, user) + else if(istype(C, /obj/item/weapon/crowbar) || istype(C, /obj/item/weapon/twohanded/fireaxe) ) + var/beingcrowbarred = null + if(istype(C, /obj/item/weapon/crowbar) ) + beingcrowbarred = 1 //derp, Agouri + else + beingcrowbarred = 0 + if(p_open && charge) + user << "You carefully start removing [charge] from [src]..." + playsound(get_turf(src), 'sound/items/Crowbar.ogg', 50, 1) + if(!do_after(user, 150/C.toolspeed, target = src)) + user << "You slip and [charge] detonates!" + charge.ex_act(1) + user.Weaken(3) + return + user.visible_message("[user] removes [charge] from [src].", \ + "You gently pry out [charge] from [src] and unhook its wires.") + charge.loc = get_turf(user) + charge = null + return + if( beingcrowbarred && (density && welded && !operating && src.p_open && (!hasPower()) && !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/C.toolspeed, target = src)) + if(src.loc) + if(src.doortype) + var/obj/structure/door_assembly/A = new src.doortype(src.loc) + A.heat_proof_finished = src.heat_proof //tracks whether there's rglass in + else + new /obj/structure/door_assembly/door_assembly_0(src.loc) + //If you come across a null doortype, it will produce the default assembly instead of disintegrating. + + if(emagged) + user << "You discard the damaged electronics." + qdel(src) + return + user << "You remove the airlock electronics." + + var/obj/item/weapon/electronics/airlock/ae + if(!electronics) + ae = new/obj/item/weapon/electronics/airlock( src.loc ) + if(req_one_access) + ae.one_access = 1 + ae.accesses = src.req_one_access + else + ae.accesses = src.req_access + else + ae = electronics + electronics = null + ae.loc = src.loc + + qdel(src) + return + else if(hasPower()) + user << "The airlock's motors resist your efforts to force it!" + else if(locked) + user << "The airlock's bolts prevent it from being forced!" + else if( !welded && !operating) + if(density) + if(beingcrowbarred == 0) //being fireaxe'd + var/obj/item/weapon/twohanded/fireaxe/F = C + if(F:wielded) + spawn(0) + open(2) + else + user << "You need to be wielding the fire axe to do that!" + else + spawn(0) + open(2) + else + if(beingcrowbarred == 0) + var/obj/item/weapon/twohanded/fireaxe/F = C + if(F:wielded) + spawn(0) + close(2) + else + user << "You need to be wielding the fire axe to do that!" + else + spawn(0) + close(2) + + else if(istype(C, /obj/item/weapon/airlock_painter)) + change_paintjob(C, user) + else if(istype(C, /obj/item/device/doorCharge) && p_open) + if(emagged) + return + if(charge && !detonated) + user << "There's already a charge hooked up to this door!" + return + if(detonated) + user << "The maintenance panel is destroyed!" + return + user << "You apply [C]. Next time someone opens the door, it will explode." + user.drop_item() + p_open = 0 + update_icon() + var/obj/item/device/doorCharge/newCharge = C //This is necessary, for some reason + newCharge.loc = src + charge = newCharge + return + else if(istype(C, /obj/item/weapon/rcd)&& istype(loc, /turf/simulated)) //Do not attack the airlock if the user is holding an RCD + return + else + ..() + return + +/obj/machinery/door/airlock/plasma/attackby(obj/item/C, mob/user, params) + if(C.is_hot() > 300)//If the temperature of the object is over 300, then ignite + message_admins("Plasma airlock ignited by [key_name_admin(user)](?) (FLW) in ([x],[y],[z] - JMP)") + log_game("Plasma wall ignited by [key_name(user)] in ([x],[y],[z])") + ignite(C.is_hot()) + return + ..() + +/obj/machinery/door/airlock/open(forced=0) + if( operating || welded || locked ) + return 0 + if(!forced) + if( !hasPower() || isWireCut(AIRLOCK_WIRE_OPEN_DOOR) ) + return 0 + if(charge && !detonated) + p_open = 1 + update_icon(AIRLOCK_OPENING) + visible_message("[src]'s panel is blown off in a spray of deadly shrapnel!") + charge.loc = get_turf(src) + charge.ex_act(1) + detonated = 1 + charge = null + for(var/mob/living/carbon/human/H in orange(1,src)) + H.Paralyse(8) + H.adjust_fire_stacks(1) + H.IgniteMob() //Guaranteed knockout and ignition for nearby people + H.apply_damage(20, BRUTE, "chest") + return + if(forced < 2) + if(emagged) + return 0 + use_power(50) + 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) + src.closeOther.close() + else + playsound(src.loc, 'sound/machines/airlockforced.ogg', 30, 1) + + if(autoclose && normalspeed) + spawn(150) + autoclose() + else if(autoclose && !normalspeed) + spawn(11) + autoclose() + + if(!density) + return 1 + if(!ticker || !ticker.mode) + return 0 + operating = 1 + update_icon(AIRLOCK_OPENING, 1) + src.SetOpacity(0) + sleep(5) + src.density = 0 + sleep(9) + src.layer = 2.7 + update_icon(AIRLOCK_OPEN, 1) + SetOpacity(0) + operating = 0 + air_update_turf(1) + update_freelook_sight() + return 1 + + +/obj/machinery/door/airlock/close(forced=0) + if(operating || welded || locked) + return + if(!forced) + if( !hasPower() || isWireCut(AIRLOCK_WIRE_DOOR_BOLTS) ) + return + if(safe) + for(var/atom/movable/M in get_turf(src)) + if(M.density && M != src) //something is blocking the door + spawn (60) + autoclose() + return + + if(forced < 2) + if(emagged) + return + use_power(50) + 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) + else + playsound(src.loc, 'sound/machines/airlockforced.ogg', 30, 1) + + var/obj/structure/window/killthis = (locate(/obj/structure/window) in get_turf(src)) + if(killthis) + killthis.ex_act(2)//Smashin windows + + if(density) + return 1 + operating = 1 + update_icon(AIRLOCK_CLOSING, 1) + src.layer = 3.1 + sleep(5) + src.density = 1 + if(!safe) + crush() + sleep(9) + update_icon(AIRLOCK_CLOSED, 1) + if(visible && !glass) + SetOpacity(1) + operating = 0 + air_update_turf(1) + update_freelook_sight() + if(safe) + if(locate(/mob/living) in get_turf(src)) + sleep(1) + open() + return 1 + +/obj/machinery/door/airlock/proc/prison_open() + if(emagged) return + src.locked = 0 + src.open() + src.locked = 1 + return + + +/obj/machinery/door/airlock/proc/autoclose() + if(!qdeleted(src) && !density && !operating && !locked && !welded && autoclose) + close() + +/obj/machinery/door/airlock/proc/change_paintjob(obj/item/C, mob/user) + var/obj/item/weapon/airlock_painter/W + if(istype(C, /obj/item/weapon/airlock_painter)) + W = C + else + user << "If you see this, it means airlock/change_paintjob() was called with something other than an airlock painter. Check your code!" + return + + if(!W.can_use(user)) + return + + var/list/optionlist + if(airlock_material == "glass") + optionlist = list("Public", "Public2", "Engineering", "Atmospherics", "Security", "Command", "Medical", "Research", "Mining", "Maintenance") + else + optionlist = list("Public", "Engineering", "Atmospherics", "Security", "Command", "Medical", "Research", "Mining", "Maintenance", "External", "High Security") + + var/paintjob = input(user, "Please select a paintjob for this airlock.") in optionlist + if((!in_range(src, usr) && src.loc != usr) || !W.use(user)) return + switch(paintjob) + if("Public") + icon = 'icons/obj/doors/airlocks/station/public.dmi' + overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' + doortype = /obj/structure/door_assembly/door_assembly_0 + if("Public2") + icon = 'icons/obj/doors/airlocks/station2/glass.dmi' + overlays_file = 'icons/obj/doors/airlocks/station2/overlays.dmi' + doortype = /obj/structure/door_assembly/door_assembly_glass + if("Engineering") + icon = 'icons/obj/doors/airlocks/station/engineering.dmi' + overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' + doortype = /obj/structure/door_assembly/door_assembly_eng + if("Atmospherics") + icon = 'icons/obj/doors/airlocks/station/atmos.dmi' + overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' + doortype = /obj/structure/door_assembly/door_assembly_atmo + if("Security") + icon = 'icons/obj/doors/airlocks/station/security.dmi' + overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' + doortype = /obj/structure/door_assembly/door_assembly_sec + if("Command") + icon = 'icons/obj/doors/airlocks/station/command.dmi' + overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' + doortype = /obj/structure/door_assembly/door_assembly_com + if("Medical") + icon = 'icons/obj/doors/airlocks/station/medical.dmi' + overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' + doortype = /obj/structure/door_assembly/door_assembly_med + if("Research") + icon = 'icons/obj/doors/airlocks/station/research.dmi' + overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' + doortype = /obj/structure/door_assembly/door_assembly_research + if("Mining") + icon = 'icons/obj/doors/airlocks/station/mining.dmi' + overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' + doortype = /obj/structure/door_assembly/door_assembly_min + if("Maintenance") + icon = 'icons/obj/doors/airlocks/station/maintenance.dmi' + overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' + doortype = /obj/structure/door_assembly/door_assembly_mai + if("External") + icon = 'icons/obj/doors/airlocks/external/external.dmi' + overlays_file = 'icons/obj/doors/airlocks/external/overlays.dmi' + doortype = /obj/structure/door_assembly/door_assembly_ext + if("High Security") + icon = 'icons/obj/doors/airlocks/highsec/highsec.dmi' + overlays_file = 'icons/obj/doors/airlocks/highsec/overlays.dmi' + doortype = /obj/structure/door_assembly/door_assembly_highsecurity + update_icon() + +/obj/machinery/door/airlock/CanAStarPass(obj/item/weapon/card/id/ID) +//Airlock is passable if it is open (!density), bot has access, and is not bolted shut or powered off) + return !density || (check_access(ID) && !locked && hasPower()) + +/obj/machinery/door/airlock/HasProximity(atom/movable/AM as mob|obj) + for (var/obj/A in contents) + A.HasProximity(AM) + return + +/obj/machinery/door/airlock/emag_act(mob/user) + if(!operating && density && hasPower() && !emagged) + operating = 1 + update_icon(AIRLOCK_EMAG) + sleep(6) + if(qdeleted(src)) + return + operating = 0 + if(!open()) + update_icon(AIRLOCK_CLOSED) + emagged = 1 + desc = "Its access panel is smoking slightly." + lights = 0 + locked = 1 + loseMainPower() + loseBackupPower() diff --git a/code/game/machinery/doors/airlock_electronics.dm b/code/game/machinery/doors/airlock_electronics.dm index 59e312eaf45..8282a682489 100644 --- a/code/game/machinery/doors/airlock_electronics.dm +++ b/code/game/machinery/doors/airlock_electronics.dm @@ -1,59 +1,59 @@ -/obj/item/weapon/electronics/airlock - name = "airlock electronics" - req_access = list(access_maint_tunnels) - - var/list/accesses = list() - var/one_access = 0 - -/obj/item/weapon/electronics/airlock/attack_self(mob/user) - if (!user) return - interact(user) - -/obj/item/weapon/electronics/airlock/interact(mob/user) - add_fingerprint(user) - ui_interact(user) - -/obj/item/weapon/electronics/airlock/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, force_open = 0) - SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open) - if (!ui) - ui = new(user, src, ui_key, "airlock_electronics", name, 975, 415, state = hands_state) - ui.open() - -/obj/item/weapon/electronics/airlock/get_ui_data() - var/list/data = list() - var/list/regions = list() - - for(var/i in 1 to 7) - var/list/region = list() - var/list/accesses = list() - for(var/j in get_region_accesses(i)) - var/list/access = list() - access["name"] = get_access_desc(j) - access["id"] = j - access["req"] = (j in src.accesses) - accesses[++accesses.len] = access - region["name"] = get_region_accesses_name(i) - region["accesses"] = accesses - regions[++regions.len] = region - data["regions"] = regions - data["oneAccess"] = one_access - - return data - -/obj/item/weapon/electronics/airlock/ui_act(action, params) - if(..()) - return - - switch(action) - if("clear") - accesses = list() - one_access = 0 - if("one_access") - one_access = !one_access - if("set") - var/access = text2num(params["access"]) - if (!(access in accesses)) - accesses += access - else - accesses -= access - return 1 +/obj/item/weapon/electronics/airlock + name = "airlock electronics" + req_access = list(access_maint_tunnels) + + var/list/accesses = list() + var/one_access = 0 + +/obj/item/weapon/electronics/airlock/attack_self(mob/user) + if (!user) return + interact(user) + +/obj/item/weapon/electronics/airlock/interact(mob/user) + add_fingerprint(user) + ui_interact(user) + +/obj/item/weapon/electronics/airlock/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, force_open = 0) + SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open) + if (!ui) + ui = new(user, src, ui_key, "airlock_electronics", name, 975, 415, state = hands_state) + ui.open() + +/obj/item/weapon/electronics/airlock/get_ui_data() + var/list/data = list() + var/list/regions = list() + + for(var/i in 1 to 7) + var/list/region = list() + var/list/accesses = list() + for(var/j in get_region_accesses(i)) + var/list/access = list() + access["name"] = get_access_desc(j) + access["id"] = j + access["req"] = (j in src.accesses) + accesses[++accesses.len] = access + region["name"] = get_region_accesses_name(i) + region["accesses"] = accesses + regions[++regions.len] = region + data["regions"] = regions + data["oneAccess"] = one_access + + return data + +/obj/item/weapon/electronics/airlock/ui_act(action, params) + if(..()) + return + + switch(action) + if("clear") + accesses = list() + one_access = 0 + if("one_access") + one_access = !one_access + if("set") + var/access = text2num(params["access"]) + if (!(access in accesses)) + accesses += access + else + accesses -= access + return 1 diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm index 7f081902b51..7d79c1fe7be 100644 --- a/code/game/machinery/machinery.dm +++ b/code/game/machinery/machinery.dm @@ -1,408 +1,408 @@ -/* -Overview: - Used to create objects that need a per step proc call. Default definition of 'New()' - stores a reference to src machine in global 'machines list'. Default definition - of 'Del' removes reference to src machine in global 'machines list'. - -Class Variables: - use_power (num) - current state of auto power use. - Possible Values: - 0 -- no auto power use - 1 -- machine is using power at its idle power level - 2 -- machine is using power at its active power level - - active_power_usage (num) - Value for the amount of power to use when in active power mode - - idle_power_usage (num) - Value for the amount of power to use when in idle power mode - - power_channel (num) - What channel to draw from when drawing power for power mode - Possible Values: - EQUIP:0 -- Equipment Channel - LIGHT:2 -- Lighting Channel - ENVIRON:3 -- Environment Channel - - component_parts (list) - A list of component parts of machine used by frame based machines. - - uid (num) - Unique id of machine across all machines. - - gl_uid (global num) - Next uid value in sequence - - stat (bitflag) - Machine status bit flags. - Possible bit flags: - BROKEN:1 -- Machine is broken - NOPOWER:2 -- No power is being supplied to machine. - POWEROFF:4 -- tbd - MAINT:8 -- machine is currently under going maintenance. - EMPED:16 -- temporary broken by EMP pulse - -Class Procs: - New() 'game/machinery/machine.dm' - - Destroy() 'game/machinery/machine.dm' - - auto_use_power() 'game/machinery/machine.dm' - This proc determines how power mode power is deducted by the machine. - 'auto_use_power()' is called by the 'master_controller' game_controller every - tick. - - Return Value: - return:1 -- if object is powered - return:0 -- if object is not powered. - - Default definition uses 'use_power', 'power_channel', 'active_power_usage', - 'idle_power_usage', 'powered()', and 'use_power()' implement behavior. - - powered(chan = EQUIP) 'modules/power/power.dm' - Checks to see if area that contains the object has power available for power - channel given in 'chan'. - - use_power(amount, chan=EQUIP) 'modules/power/power.dm' - Deducts 'amount' from the power channel 'chan' of the area that contains the object. - - power_change() 'modules/power/power.dm' - Called by the area that contains the object when ever that area under goes a - power state change (area runs out of power, or area channel is turned off). - - RefreshParts() 'game/machinery/machine.dm' - Called to refresh the variables in the machine that are contributed to by parts - contained in the component_parts list. (example: glass and material amounts for - the autolathe) - - Default definition does nothing. - - assign_uid() 'game/machinery/machine.dm' - Called by machine to assign a value to the uid variable. - - process() 'game/machinery/machine.dm' - Called by the 'machinery subsystem' once per machinery tick for each machine that is listed in its 'machines' list. - - process_atmos() - Called by the 'air subsystem' once per atmos tick for each machine that is listed in its 'atmos_machines' list. - - is_operational() - Returns 0 if the machine is unpowered, broken or undergoing maintenance, something else if not - - Compiled by Aygar -*/ - -/obj/machinery - name = "machinery" - icon = 'icons/obj/stationobjs.dmi' - verb_yell = "blares" - pressure_resistance = 10 - var/stat = 0 - var/emagged = 0 - var/use_power = 1 - //0 = dont run the auto - //1 = run auto, use idle - //2 = run auto, use active - var/idle_power_usage = 0 - 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/uid - var/global/gl_uid = 1 - var/panel_open = 0 - var/state_open = 0 - var/mob/living/occupant = null - var/unsecuring_tool = /obj/item/weapon/wrench - var/interact_offline = 0 // Can the machine be interacted with while de-powered. - -/obj/machinery/New() - ..() - machines += src - SSmachine.processing += src - power_change() - -/obj/machinery/Destroy() - machines.Remove(src) - SSmachine.processing -= src - if(occupant) - dropContents() - return ..() - -/obj/machinery/attackby(obj/item/weapon/W, mob/user, params) - user.changeNext_move(CLICK_CD_MELEE) - ..() - -/obj/machinery/proc/locate_machinery() - return - -/obj/machinery/process()//If you dont use process or power why are you here - return PROCESS_KILL - -/obj/machinery/proc/process_atmos()//If you dont use process why are you here - return PROCESS_KILL - -/obj/machinery/emp_act(severity) - if(use_power && stat == 0) - use_power(7500/severity) - - PoolOrNew(/obj/effect/overlay/temp/emp, src.loc) - ..() - -/obj/machinery/proc/open_machine() - state_open = 1 - density = 0 - dropContents() - update_icon() - updateUsrDialog() - -/obj/machinery/proc/dropContents() - var/turf/T = get_turf(src) - T.contents += contents - if(occupant) - if(occupant.client) - occupant.client.eye = occupant - occupant.client.perspective = MOB_PERSPECTIVE - occupant = null - -/obj/machinery/proc/close_machine(mob/living/target = null) - state_open = 0 - density = 1 - if(!target) - for(var/mob/living/carbon/C in loc) - if(C.buckled || C.buckled_mob) - continue - else - target = C - if(target && !target.buckled && !target.buckled_mob) - if(target.client) - target.client.perspective = EYE_PERSPECTIVE - target.client.eye = src - occupant = target - target.loc = src - target.stop_pulling() - if(target.pulledby) - target.pulledby.stop_pulling() - updateUsrDialog() - update_icon() - -/obj/machinery/blob_act() - if(!density) - qdel(src) - if(prob(75)) - qdel(src) - -/obj/machinery/proc/auto_use_power() - if(!powered(power_channel)) - return 0 - if(src.use_power == 1) - use_power(idle_power_usage,power_channel) - else if(src.use_power >= 2) - use_power(active_power_usage,power_channel) - return 1 - -/obj/machinery/Topic(href, href_list) - ..() - if(!can_be_used_by(usr)) - return 1 - add_fingerprint(usr) - return 0 - -/obj/machinery/ui_act(action, params) - ..() - if(!can_be_used_by(usr)) - return 1 - add_fingerprint(usr) - return 0 - -/obj/machinery/proc/can_be_used_by(mob/user) - if(!interact_offline && stat & (NOPOWER|BROKEN)) - return 0 - if(!user.canUseTopic(src)) - return 0 - return 1 - -/obj/machinery/proc/is_operational() - return !(stat & (NOPOWER|BROKEN|MAINT)) - - -//////////////////////////////////////////////////////////////////////////////////////////// - - -/obj/machinery/attack_ai(mob/user) - if(isrobot(user)) - // For some reason attack_robot doesn't work - // This is to stop robots from using cameras to remotely control machines. - if(user.client && user.client.eye == user) - return src.attack_hand(user) - else - return src.attack_hand(user) - -/obj/machinery/attack_paw(mob/user) - return src.attack_hand(user) - -//set_machine must be 0 if clicking the machinery doesn't bring up a dialog -/obj/machinery/attack_hand(mob/user, check_power = 1, set_machine = 1) - if(..())// unbuckling etc - return 1 - if((user.lying || user.stat) && !IsAdminGhost(user)) - return 1 - if(!user.IsAdvancedToolUser() && !IsAdminGhost(user)) - usr << "You don't have the dexterity to do this!" - return 1 - if (ishuman(user)) - var/mob/living/carbon/human/H = user - if(H.getBrainLoss() >= 60) - visible_message("[H] stares cluelessly at [src] and drools.") - return 1 - else if(prob(H.getBrainLoss())) - user << "You momentarily forget how to use [src]!" - return 1 - if(panel_open) - src.add_fingerprint(user) - return 0 - if(check_power && stat & NOPOWER) - user << "\The [src] seems unpowered." - return 1 - if(!interact_offline && stat & (BROKEN|MAINT)) - user << "\The [src] seems broken." - return 1 - src.add_fingerprint(user) - if(set_machine) - user.set_machine(src) - return 0 - -/obj/machinery/CheckParts() - RefreshParts() - return - -/obj/machinery/proc/RefreshParts() //Placeholder proc for machines that are built using frames. - return - -/obj/machinery/proc/assign_uid() - uid = gl_uid - gl_uid++ - -/obj/machinery/proc/default_pry_open(obj/item/weapon/crowbar/C) - . = !(state_open || panel_open || is_operational() || (flags & NODECONSTRUCT)) && istype(C) - if(.) - playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1) - visible_message("[usr] pry open \the [src].", "You pry open \the [src].") - open_machine() - return 1 - -/obj/machinery/proc/default_deconstruction_crowbar(obj/item/weapon/crowbar/C, ignore_panel = 0) - . = istype(C) && (panel_open || ignore_panel) && !(flags & NODECONSTRUCT) - if(.) - deconstruction() - 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) - transfer_fingerprints_to(M) - M.state = 2 - M.icon_state = "box_1" - for(var/obj/item/I in component_parts) - if(I.reliability != 100 && crit_fail) - I.crit_fail = 1 - I.loc = src.loc - qdel(src) - -/obj/machinery/proc/default_deconstruction_screwdriver(mob/user, icon_state_open, icon_state_closed, obj/item/weapon/screwdriver/S) - if(istype(S) && !(flags & NODECONSTRUCT)) - playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1) - if(!panel_open) - panel_open = 1 - icon_state = icon_state_open - user << "You open the maintenance hatch of [src]." - else - panel_open = 0 - icon_state = icon_state_closed - user << "You close the maintenance hatch of [src]." - return 1 - return 0 - -/obj/machinery/proc/default_change_direction_wrench(mob/user, obj/item/weapon/wrench/W) - if(panel_open && istype(W)) - playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1) - dir = turn(dir,-90) - user << "You rotate [src]." - return 1 - return 0 - -/obj/proc/default_unfasten_wrench(mob/user, obj/item/weapon/wrench/W, time = 20) - if(istype(W) && !(flags & NODECONSTRUCT)) - user << "You begin [anchored ? "un" : ""]securing [name]..." - playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1) - if(do_after(user, time/W.toolspeed, target = src)) - user << "You [anchored ? "un" : ""]secure [name]." - anchored = !anchored - playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) - return 1 - return 0 - -/obj/machinery/proc/exchange_parts(mob/user, obj/item/weapon/storage/part_replacer/W) - if(flags & NODECONSTRUCT) - return - var/shouldplaysound = 0 - if(istype(W) && component_parts) - if(panel_open || W.works_from_distance) - var/obj/item/weapon/circuitboard/CB = locate(/obj/item/weapon/circuitboard) in component_parts - var/P - if(W.works_from_distance) - display_parts(user) - for(var/obj/item/weapon/stock_parts/A in component_parts) - for(var/D in CB.req_components) - if(ispath(A.type, D)) - P = D - break - for(var/obj/item/weapon/stock_parts/B in W.contents) - if(istype(B, P) && istype(A, P)) - if(B.rating > A.rating) - W.remove_from_storage(B, src) - W.handle_item_insertion(A, 1) - component_parts -= A - component_parts += B - B.loc = null - user << "[A.name] replaced with [B.name]." - shouldplaysound = 1 //Only play the sound when parts are actually replaced! - break - RefreshParts() - else - display_parts(user) - if(shouldplaysound) - W.play_rped_sound() - return 1 - return 0 - -/obj/machinery/proc/display_parts(mob/user) - user << "Following parts detected in the machine:" - for(var/obj/item/C in component_parts) - user << "\icon[C] [C.name]" - -/obj/machinery/examine(mob/user) - ..(user) - if(user.research_scanner && component_parts) - display_parts(user) - -//called on machinery construction (i.e from frame to machinery) but not on initialization -/obj/machinery/proc/construction() - return - -//called on deconstruction before the final deletion -/obj/machinery/proc/deconstruction() - return - -/obj/machinery/allow_drop() - return 0 - -// Hook for html_interface module to prevent updates to clients who don't have this as their active machine. -/obj/machinery/proc/hiIsValidClient(datum/html_interface_client/hclient, datum/html_interface/hi) - if (hclient.client.mob && (hclient.client.mob.stat == 0 || IsAdminGhost(hclient.client.mob))) - if (isAI(hclient.client.mob) || IsAdminGhost(hclient.client.mob)) return TRUE - else return hclient.client.mob.machine == src && src.Adjacent(hclient.client.mob) - else - return FALSE - -// Hook for html_interface module to unset the active machine when the window is closed by the player. -/obj/machinery/proc/hiOnHide(datum/html_interface_client/hclient) - if (hclient.client.mob && hclient.client.mob.machine == src) hclient.client.mob.unset_machine() +/* +Overview: + Used to create objects that need a per step proc call. Default definition of 'New()' + stores a reference to src machine in global 'machines list'. Default definition + of 'Del' removes reference to src machine in global 'machines list'. + +Class Variables: + use_power (num) + current state of auto power use. + Possible Values: + 0 -- no auto power use + 1 -- machine is using power at its idle power level + 2 -- machine is using power at its active power level + + active_power_usage (num) + Value for the amount of power to use when in active power mode + + idle_power_usage (num) + Value for the amount of power to use when in idle power mode + + power_channel (num) + What channel to draw from when drawing power for power mode + Possible Values: + EQUIP:0 -- Equipment Channel + LIGHT:2 -- Lighting Channel + ENVIRON:3 -- Environment Channel + + component_parts (list) + A list of component parts of machine used by frame based machines. + + uid (num) + Unique id of machine across all machines. + + gl_uid (global num) + Next uid value in sequence + + stat (bitflag) + Machine status bit flags. + Possible bit flags: + BROKEN:1 -- Machine is broken + NOPOWER:2 -- No power is being supplied to machine. + POWEROFF:4 -- tbd + MAINT:8 -- machine is currently under going maintenance. + EMPED:16 -- temporary broken by EMP pulse + +Class Procs: + New() 'game/machinery/machine.dm' + + Destroy() 'game/machinery/machine.dm' + + auto_use_power() 'game/machinery/machine.dm' + This proc determines how power mode power is deducted by the machine. + 'auto_use_power()' is called by the 'master_controller' game_controller every + tick. + + Return Value: + return:1 -- if object is powered + return:0 -- if object is not powered. + + Default definition uses 'use_power', 'power_channel', 'active_power_usage', + 'idle_power_usage', 'powered()', and 'use_power()' implement behavior. + + powered(chan = EQUIP) 'modules/power/power.dm' + Checks to see if area that contains the object has power available for power + channel given in 'chan'. + + use_power(amount, chan=EQUIP) 'modules/power/power.dm' + Deducts 'amount' from the power channel 'chan' of the area that contains the object. + + power_change() 'modules/power/power.dm' + Called by the area that contains the object when ever that area under goes a + power state change (area runs out of power, or area channel is turned off). + + RefreshParts() 'game/machinery/machine.dm' + Called to refresh the variables in the machine that are contributed to by parts + contained in the component_parts list. (example: glass and material amounts for + the autolathe) + + Default definition does nothing. + + assign_uid() 'game/machinery/machine.dm' + Called by machine to assign a value to the uid variable. + + process() 'game/machinery/machine.dm' + Called by the 'machinery subsystem' once per machinery tick for each machine that is listed in its 'machines' list. + + process_atmos() + Called by the 'air subsystem' once per atmos tick for each machine that is listed in its 'atmos_machines' list. + + is_operational() + Returns 0 if the machine is unpowered, broken or undergoing maintenance, something else if not + + Compiled by Aygar +*/ + +/obj/machinery + name = "machinery" + icon = 'icons/obj/stationobjs.dmi' + verb_yell = "blares" + pressure_resistance = 10 + var/stat = 0 + var/emagged = 0 + var/use_power = 1 + //0 = dont run the auto + //1 = run auto, use idle + //2 = run auto, use active + var/idle_power_usage = 0 + 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/uid + var/global/gl_uid = 1 + var/panel_open = 0 + var/state_open = 0 + var/mob/living/occupant = null + var/unsecuring_tool = /obj/item/weapon/wrench + var/interact_offline = 0 // Can the machine be interacted with while de-powered. + +/obj/machinery/New() + ..() + machines += src + SSmachine.processing += src + power_change() + +/obj/machinery/Destroy() + machines.Remove(src) + SSmachine.processing -= src + if(occupant) + dropContents() + return ..() + +/obj/machinery/attackby(obj/item/weapon/W, mob/user, params) + user.changeNext_move(CLICK_CD_MELEE) + ..() + +/obj/machinery/proc/locate_machinery() + return + +/obj/machinery/process()//If you dont use process or power why are you here + return PROCESS_KILL + +/obj/machinery/proc/process_atmos()//If you dont use process why are you here + return PROCESS_KILL + +/obj/machinery/emp_act(severity) + if(use_power && stat == 0) + use_power(7500/severity) + + PoolOrNew(/obj/effect/overlay/temp/emp, src.loc) + ..() + +/obj/machinery/proc/open_machine() + state_open = 1 + density = 0 + dropContents() + update_icon() + updateUsrDialog() + +/obj/machinery/proc/dropContents() + var/turf/T = get_turf(src) + T.contents += contents + if(occupant) + if(occupant.client) + occupant.client.eye = occupant + occupant.client.perspective = MOB_PERSPECTIVE + occupant = null + +/obj/machinery/proc/close_machine(mob/living/target = null) + state_open = 0 + density = 1 + if(!target) + for(var/mob/living/carbon/C in loc) + if(C.buckled || C.buckled_mob) + continue + else + target = C + if(target && !target.buckled && !target.buckled_mob) + if(target.client) + target.client.perspective = EYE_PERSPECTIVE + target.client.eye = src + occupant = target + target.loc = src + target.stop_pulling() + if(target.pulledby) + target.pulledby.stop_pulling() + updateUsrDialog() + update_icon() + +/obj/machinery/blob_act() + if(!density) + qdel(src) + if(prob(75)) + qdel(src) + +/obj/machinery/proc/auto_use_power() + if(!powered(power_channel)) + return 0 + if(src.use_power == 1) + use_power(idle_power_usage,power_channel) + else if(src.use_power >= 2) + use_power(active_power_usage,power_channel) + return 1 + +/obj/machinery/Topic(href, href_list) + ..() + if(!can_be_used_by(usr)) + return 1 + add_fingerprint(usr) + return 0 + +/obj/machinery/ui_act(action, params) + ..() + if(!can_be_used_by(usr)) + return 1 + add_fingerprint(usr) + return 0 + +/obj/machinery/proc/can_be_used_by(mob/user) + if(!interact_offline && stat & (NOPOWER|BROKEN)) + return 0 + if(!user.canUseTopic(src)) + return 0 + return 1 + +/obj/machinery/proc/is_operational() + return !(stat & (NOPOWER|BROKEN|MAINT)) + + +//////////////////////////////////////////////////////////////////////////////////////////// + + +/obj/machinery/attack_ai(mob/user) + if(isrobot(user)) + // For some reason attack_robot doesn't work + // This is to stop robots from using cameras to remotely control machines. + if(user.client && user.client.eye == user) + return src.attack_hand(user) + else + return src.attack_hand(user) + +/obj/machinery/attack_paw(mob/user) + return src.attack_hand(user) + +//set_machine must be 0 if clicking the machinery doesn't bring up a dialog +/obj/machinery/attack_hand(mob/user, check_power = 1, set_machine = 1) + if(..())// unbuckling etc + return 1 + if((user.lying || user.stat) && !IsAdminGhost(user)) + return 1 + if(!user.IsAdvancedToolUser() && !IsAdminGhost(user)) + usr << "You don't have the dexterity to do this!" + return 1 + if (ishuman(user)) + var/mob/living/carbon/human/H = user + if(H.getBrainLoss() >= 60) + visible_message("[H] stares cluelessly at [src] and drools.") + return 1 + else if(prob(H.getBrainLoss())) + user << "You momentarily forget how to use [src]!" + return 1 + if(panel_open) + src.add_fingerprint(user) + return 0 + if(check_power && stat & NOPOWER) + user << "\The [src] seems unpowered." + return 1 + if(!interact_offline && stat & (BROKEN|MAINT)) + user << "\The [src] seems broken." + return 1 + src.add_fingerprint(user) + if(set_machine) + user.set_machine(src) + return 0 + +/obj/machinery/CheckParts() + RefreshParts() + return + +/obj/machinery/proc/RefreshParts() //Placeholder proc for machines that are built using frames. + return + +/obj/machinery/proc/assign_uid() + uid = gl_uid + gl_uid++ + +/obj/machinery/proc/default_pry_open(obj/item/weapon/crowbar/C) + . = !(state_open || panel_open || is_operational() || (flags & NODECONSTRUCT)) && istype(C) + if(.) + playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1) + visible_message("[usr] pry open \the [src].", "You pry open \the [src].") + open_machine() + return 1 + +/obj/machinery/proc/default_deconstruction_crowbar(obj/item/weapon/crowbar/C, ignore_panel = 0) + . = istype(C) && (panel_open || ignore_panel) && !(flags & NODECONSTRUCT) + if(.) + deconstruction() + 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) + transfer_fingerprints_to(M) + M.state = 2 + M.icon_state = "box_1" + for(var/obj/item/I in component_parts) + if(I.reliability != 100 && crit_fail) + I.crit_fail = 1 + I.loc = src.loc + qdel(src) + +/obj/machinery/proc/default_deconstruction_screwdriver(mob/user, icon_state_open, icon_state_closed, obj/item/weapon/screwdriver/S) + if(istype(S) && !(flags & NODECONSTRUCT)) + playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1) + if(!panel_open) + panel_open = 1 + icon_state = icon_state_open + user << "You open the maintenance hatch of [src]." + else + panel_open = 0 + icon_state = icon_state_closed + user << "You close the maintenance hatch of [src]." + return 1 + return 0 + +/obj/machinery/proc/default_change_direction_wrench(mob/user, obj/item/weapon/wrench/W) + if(panel_open && istype(W)) + playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1) + dir = turn(dir,-90) + user << "You rotate [src]." + return 1 + return 0 + +/obj/proc/default_unfasten_wrench(mob/user, obj/item/weapon/wrench/W, time = 20) + if(istype(W) && !(flags & NODECONSTRUCT)) + user << "You begin [anchored ? "un" : ""]securing [name]..." + playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1) + if(do_after(user, time/W.toolspeed, target = src)) + user << "You [anchored ? "un" : ""]secure [name]." + anchored = !anchored + playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) + return 1 + return 0 + +/obj/machinery/proc/exchange_parts(mob/user, obj/item/weapon/storage/part_replacer/W) + if(flags & NODECONSTRUCT) + return + var/shouldplaysound = 0 + if(istype(W) && component_parts) + if(panel_open || W.works_from_distance) + var/obj/item/weapon/circuitboard/CB = locate(/obj/item/weapon/circuitboard) in component_parts + var/P + if(W.works_from_distance) + display_parts(user) + for(var/obj/item/weapon/stock_parts/A in component_parts) + for(var/D in CB.req_components) + if(ispath(A.type, D)) + P = D + break + for(var/obj/item/weapon/stock_parts/B in W.contents) + if(istype(B, P) && istype(A, P)) + if(B.rating > A.rating) + W.remove_from_storage(B, src) + W.handle_item_insertion(A, 1) + component_parts -= A + component_parts += B + B.loc = null + user << "[A.name] replaced with [B.name]." + shouldplaysound = 1 //Only play the sound when parts are actually replaced! + break + RefreshParts() + else + display_parts(user) + if(shouldplaysound) + W.play_rped_sound() + return 1 + return 0 + +/obj/machinery/proc/display_parts(mob/user) + user << "Following parts detected in the machine:" + for(var/obj/item/C in component_parts) + user << "\icon[C] [C.name]" + +/obj/machinery/examine(mob/user) + ..(user) + if(user.research_scanner && component_parts) + display_parts(user) + +//called on machinery construction (i.e from frame to machinery) but not on initialization +/obj/machinery/proc/construction() + return + +//called on deconstruction before the final deletion +/obj/machinery/proc/deconstruction() + return + +/obj/machinery/allow_drop() + return 0 + +// Hook for html_interface module to prevent updates to clients who don't have this as their active machine. +/obj/machinery/proc/hiIsValidClient(datum/html_interface_client/hclient, datum/html_interface/hi) + if (hclient.client.mob && (hclient.client.mob.stat == 0 || IsAdminGhost(hclient.client.mob))) + if (isAI(hclient.client.mob) || IsAdminGhost(hclient.client.mob)) return TRUE + else return hclient.client.mob.machine == src && src.Adjacent(hclient.client.mob) + else + return FALSE + +// Hook for html_interface module to unset the active machine when the window is closed by the player. +/obj/machinery/proc/hiOnHide(datum/html_interface_client/hclient) + if (hclient.client.mob && hclient.client.mob.machine == src) hclient.client.mob.unset_machine() diff --git a/code/game/machinery/spaceheater.dm b/code/game/machinery/spaceheater.dm index d5e58202f1c..698983bc823 100644 --- a/code/game/machinery/spaceheater.dm +++ b/code/game/machinery/spaceheater.dm @@ -1,261 +1,261 @@ -#define HEATER_MODE_STANDBY "standby" -#define HEATER_MODE_HEAT "heat" -#define HEATER_MODE_COOL "cool" - -/obj/machinery/space_heater - anchored = 0 - density = 1 - icon = 'icons/obj/atmos.dmi' - icon_state = "sheater-off" - name = "space heater" - desc = "Made by Space Amish using traditional space techniques, this heater/cooler is guaranteed not to set the station on fire." - var/obj/item/weapon/stock_parts/cell/cell - var/on = FALSE - var/mode = HEATER_MODE_STANDBY - var/setMode = "auto" // Anything other than "heat" or "cool" is considered auto. - var/targetTemperature = T20C - var/heatingPower = 40000 - var/efficiency = 20000 - var/temperatureTolerance = 1 - var/settableTemperatureMedian = 30 + T0C - var/settableTemperatureRange = 30 - - -/obj/machinery/space_heater/New() - ..() - cell = new(src) - component_parts = list() - component_parts += new /obj/item/weapon/circuitboard/space_heater(null) - component_parts += new /obj/item/weapon/stock_parts/capacitor(null) - component_parts += new /obj/item/weapon/stock_parts/micro_laser(null) - component_parts += new /obj/item/stack/cable_coil(null, 3) - RefreshParts() - update_icon() - -/obj/machinery/space_heater/construction() - qdel(cell) - cell = null - panel_open = TRUE - update_icon() - return ..() - -/obj/machinery/space_heater/deconstruction() - if(cell) - component_parts += cell - cell = null - return ..() - -/obj/machinery/space_heater/update_icon() - if(on) - icon_state = "sheater-[mode]" - else - icon_state = "sheater-off" - - overlays.Cut() - if(panel_open) - overlays += "sheater-open" - -/obj/machinery/space_heater/examine(mob/user) - ..() - user << "\The [src] is [on ? "on" : "off"], and the hatch is [panel_open ? "open" : "closed"]." - if(cell) - user << "The charge meter reads [cell ? round(cell.percent(), 1) : 0]%." - else - user << "There is no power cell installed." - -/obj/machinery/space_heater/RefreshParts() - var/laser = 0 - var/cap = 0 - for(var/obj/item/weapon/stock_parts/micro_laser/M in component_parts) - laser += M.rating - for(var/obj/item/weapon/stock_parts/capacitor/M in component_parts) - cap += M.rating - - heatingPower = laser * 40000 - - settableTemperatureRange = cap * 30 - efficiency = (cap + 1) * 10000 - - var/minTemp = max(settableTemperatureMedian - settableTemperatureRange, TCMB) - var/maxTemp = settableTemperatureMedian + settableTemperatureRange - targetTemperature = dd_range(minTemp, maxTemp, targetTemperature) - -/obj/machinery/space_heater/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - if(cell) - cell.emp_act(severity) - ..(severity) - -/obj/machinery/space_heater/get_ui_data() - var/list/data = list() - data["open"] = panel_open - data["on"] = on - data["mode"] = setMode - data["hasPowercell"] = !!cell - if(cell) - data["powerLevel"] = round(cell.percent(), 1) - data["targetTemp"] = round(targetTemperature - T0C, 1) - data["minTemp"] = max(settableTemperatureMedian - settableTemperatureRange - T0C, TCMB) - data["maxTemp"] = settableTemperatureMedian + settableTemperatureRange - T0C - - var/turf/simulated/L = get_turf(loc) - var/curTemp - if(istype(L)) - var/datum/gas_mixture/env = L.return_air() - curTemp = env.temperature - else if(isturf(L)) - curTemp = L.temperature - - if(isnull(curTemp)) - data["currentTemp"] = "N/A" - else - data["currentTemp"] = round(curTemp - T0C, 1) - return data - -/obj/machinery/space_heater/attackby(obj/item/I, mob/user, params) - add_fingerprint(user) - if(istype(I, /obj/item/weapon/stock_parts/cell)) - if(panel_open) - if(cell) - user << "There is already a power cell inside!" - return - else - // insert cell - var/obj/item/weapon/stock_parts/cell/C = usr.get_active_hand() - if(istype(C)) - if(!user.drop_item()) - return - cell = C - C.loc = src - C.add_fingerprint(usr) - - user.visible_message("\The [user] inserts a power cell into \the [src].", "You insert the power cell into \the [src].") - SSnano.update_uis(src) - else - user << "The hatch must be open to insert a power cell!" - return - else if(istype(I, /obj/item/weapon/screwdriver)) - panel_open = !panel_open - user.visible_message("\The [user] [panel_open ? "opens" : "closes"] the hatch on \the [src].", "You [panel_open ? "open" : "close"] the hatch on \the [src].") - update_icon() - if(panel_open) - interact(user) - else if(exchange_parts(user, I) || default_deconstruction_crowbar(I)) - return - else - ..() - -/obj/machinery/space_heater/attack_hand(mob/user) - interact(user) - -/obj/machinery/space_heater/attack_paw(mob/user) - interact(user) - -/obj/machinery/space_heater/interact(mob/user) - ui_interact(user) - -/obj/machinery/space_heater/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 0) - ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open) - if (!ui) - ui = new(user, src, ui_key, "space_heater", name, 490, 340, state = physical_state) - ui.open() - -/obj/machinery/space_heater/ui_act(action, params) - if(..()) - return - - switch(action) - if("power") - on = !on - mode = HEATER_MODE_STANDBY - usr.visible_message("[usr] switches [on ? "on" : "off"] \the [src].", "You switch [on ? "on" : "off"] \the [src].") - update_icon() - if("mode") - setMode = params["mode"] - if("temp") - if(panel_open) - var/value - if(params["set"] == "custom") - value = input("Please input the target temperature", name) as num|null - if(isnull(value)) - return - value += T0C - else - value = targetTemperature + text2num(params["set"]) - - var/minTemp = max(settableTemperatureMedian - settableTemperatureRange, TCMB) - var/maxTemp = settableTemperatureMedian + settableTemperatureRange - targetTemperature = dd_range(minTemp, maxTemp, round(value, 1)) - if("ejectcell") - if(panel_open && cell) - if(usr.get_active_hand()) - usr << "You need an empty hand to remove \the [cell]!" - return - cell.updateicon() - usr.put_in_hands(cell) - cell.add_fingerprint(usr) - usr.visible_message("\The [usr] removes \the [cell] from \the [src].", "You remove \the [cell] from \the [src].") - cell = null - if("installcell") - if(panel_open && !cell) - var/obj/item/weapon/stock_parts/cell/C = usr.get_active_hand() - if(istype(C)) - if(!usr.drop_item()) - return - cell = C - C.loc = src - C.add_fingerprint(usr) - usr.visible_message("\The [usr] inserts \a [C] into \the [src].", "You insert \the [C] into \the [src].") - add_fingerprint(usr) - return 1 - -/obj/machinery/space_heater/process() - if(!on || (stat & BROKEN)) - return - - if(cell && cell.charge > 0) - var/turf/simulated/L = loc - if(!istype(L)) - if(mode != HEATER_MODE_STANDBY) - mode = HEATER_MODE_STANDBY - update_icon() - return - - var/datum/gas_mixture/env = L.return_air() - - var/newMode = HEATER_MODE_STANDBY - if(setMode != HEATER_MODE_COOL && env.temperature < targetTemperature - temperatureTolerance) - newMode = HEATER_MODE_HEAT - else if(setMode != HEATER_MODE_HEAT && env.temperature > targetTemperature + temperatureTolerance) - newMode = HEATER_MODE_COOL - - if(mode != newMode) - mode = newMode - update_icon() - - if(mode == HEATER_MODE_STANDBY) - return - - var/heat_capacity = env.heat_capacity() - var/requiredPower = abs(env.temperature - targetTemperature) * heat_capacity - requiredPower = min(requiredPower, heatingPower) - - if(requiredPower < 1) - return - - var/deltaTemperature = requiredPower / heat_capacity - if(mode == HEATER_MODE_COOL) - deltaTemperature *= -1 - if(deltaTemperature) - env.temperature += deltaTemperature - air_update_turf() - cell.use(requiredPower / efficiency) - else - on = FALSE - update_icon() - -#undef HEATER_MODE_STANDBY -#undef HEATER_MODE_HEAT -#undef HEATER_MODE_COOL +#define HEATER_MODE_STANDBY "standby" +#define HEATER_MODE_HEAT "heat" +#define HEATER_MODE_COOL "cool" + +/obj/machinery/space_heater + anchored = 0 + density = 1 + icon = 'icons/obj/atmos.dmi' + icon_state = "sheater-off" + name = "space heater" + desc = "Made by Space Amish using traditional space techniques, this heater/cooler is guaranteed not to set the station on fire." + var/obj/item/weapon/stock_parts/cell/cell + var/on = FALSE + var/mode = HEATER_MODE_STANDBY + var/setMode = "auto" // Anything other than "heat" or "cool" is considered auto. + var/targetTemperature = T20C + var/heatingPower = 40000 + var/efficiency = 20000 + var/temperatureTolerance = 1 + var/settableTemperatureMedian = 30 + T0C + var/settableTemperatureRange = 30 + + +/obj/machinery/space_heater/New() + ..() + cell = new(src) + component_parts = list() + component_parts += new /obj/item/weapon/circuitboard/space_heater(null) + component_parts += new /obj/item/weapon/stock_parts/capacitor(null) + component_parts += new /obj/item/weapon/stock_parts/micro_laser(null) + component_parts += new /obj/item/stack/cable_coil(null, 3) + RefreshParts() + update_icon() + +/obj/machinery/space_heater/construction() + qdel(cell) + cell = null + panel_open = TRUE + update_icon() + return ..() + +/obj/machinery/space_heater/deconstruction() + if(cell) + component_parts += cell + cell = null + return ..() + +/obj/machinery/space_heater/update_icon() + if(on) + icon_state = "sheater-[mode]" + else + icon_state = "sheater-off" + + overlays.Cut() + if(panel_open) + overlays += "sheater-open" + +/obj/machinery/space_heater/examine(mob/user) + ..() + user << "\The [src] is [on ? "on" : "off"], and the hatch is [panel_open ? "open" : "closed"]." + if(cell) + user << "The charge meter reads [cell ? round(cell.percent(), 1) : 0]%." + else + user << "There is no power cell installed." + +/obj/machinery/space_heater/RefreshParts() + var/laser = 0 + var/cap = 0 + for(var/obj/item/weapon/stock_parts/micro_laser/M in component_parts) + laser += M.rating + for(var/obj/item/weapon/stock_parts/capacitor/M in component_parts) + cap += M.rating + + heatingPower = laser * 40000 + + settableTemperatureRange = cap * 30 + efficiency = (cap + 1) * 10000 + + var/minTemp = max(settableTemperatureMedian - settableTemperatureRange, TCMB) + var/maxTemp = settableTemperatureMedian + settableTemperatureRange + targetTemperature = dd_range(minTemp, maxTemp, targetTemperature) + +/obj/machinery/space_heater/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + if(cell) + cell.emp_act(severity) + ..(severity) + +/obj/machinery/space_heater/get_ui_data() + var/list/data = list() + data["open"] = panel_open + data["on"] = on + data["mode"] = setMode + data["hasPowercell"] = !!cell + if(cell) + data["powerLevel"] = round(cell.percent(), 1) + data["targetTemp"] = round(targetTemperature - T0C, 1) + data["minTemp"] = max(settableTemperatureMedian - settableTemperatureRange - T0C, TCMB) + data["maxTemp"] = settableTemperatureMedian + settableTemperatureRange - T0C + + var/turf/simulated/L = get_turf(loc) + var/curTemp + if(istype(L)) + var/datum/gas_mixture/env = L.return_air() + curTemp = env.temperature + else if(isturf(L)) + curTemp = L.temperature + + if(isnull(curTemp)) + data["currentTemp"] = "N/A" + else + data["currentTemp"] = round(curTemp - T0C, 1) + return data + +/obj/machinery/space_heater/attackby(obj/item/I, mob/user, params) + add_fingerprint(user) + if(istype(I, /obj/item/weapon/stock_parts/cell)) + if(panel_open) + if(cell) + user << "There is already a power cell inside!" + return + else + // insert cell + var/obj/item/weapon/stock_parts/cell/C = usr.get_active_hand() + if(istype(C)) + if(!user.drop_item()) + return + cell = C + C.loc = src + C.add_fingerprint(usr) + + user.visible_message("\The [user] inserts a power cell into \the [src].", "You insert the power cell into \the [src].") + SSnano.update_uis(src) + else + user << "The hatch must be open to insert a power cell!" + return + else if(istype(I, /obj/item/weapon/screwdriver)) + panel_open = !panel_open + user.visible_message("\The [user] [panel_open ? "opens" : "closes"] the hatch on \the [src].", "You [panel_open ? "open" : "close"] the hatch on \the [src].") + update_icon() + if(panel_open) + interact(user) + else if(exchange_parts(user, I) || default_deconstruction_crowbar(I)) + return + else + ..() + +/obj/machinery/space_heater/attack_hand(mob/user) + interact(user) + +/obj/machinery/space_heater/attack_paw(mob/user) + interact(user) + +/obj/machinery/space_heater/interact(mob/user) + ui_interact(user) + +/obj/machinery/space_heater/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 0) + ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open) + if (!ui) + ui = new(user, src, ui_key, "space_heater", name, 490, 340, state = physical_state) + ui.open() + +/obj/machinery/space_heater/ui_act(action, params) + if(..()) + return + + switch(action) + if("power") + on = !on + mode = HEATER_MODE_STANDBY + usr.visible_message("[usr] switches [on ? "on" : "off"] \the [src].", "You switch [on ? "on" : "off"] \the [src].") + update_icon() + if("mode") + setMode = params["mode"] + if("temp") + if(panel_open) + var/value + if(params["set"] == "custom") + value = input("Please input the target temperature", name) as num|null + if(isnull(value)) + return + value += T0C + else + value = targetTemperature + text2num(params["set"]) + + var/minTemp = max(settableTemperatureMedian - settableTemperatureRange, TCMB) + var/maxTemp = settableTemperatureMedian + settableTemperatureRange + targetTemperature = dd_range(minTemp, maxTemp, round(value, 1)) + if("ejectcell") + if(panel_open && cell) + if(usr.get_active_hand()) + usr << "You need an empty hand to remove \the [cell]!" + return + cell.updateicon() + usr.put_in_hands(cell) + cell.add_fingerprint(usr) + usr.visible_message("\The [usr] removes \the [cell] from \the [src].", "You remove \the [cell] from \the [src].") + cell = null + if("installcell") + if(panel_open && !cell) + var/obj/item/weapon/stock_parts/cell/C = usr.get_active_hand() + if(istype(C)) + if(!usr.drop_item()) + return + cell = C + C.loc = src + C.add_fingerprint(usr) + usr.visible_message("\The [usr] inserts \a [C] into \the [src].", "You insert \the [C] into \the [src].") + add_fingerprint(usr) + return 1 + +/obj/machinery/space_heater/process() + if(!on || (stat & BROKEN)) + return + + if(cell && cell.charge > 0) + var/turf/simulated/L = loc + if(!istype(L)) + if(mode != HEATER_MODE_STANDBY) + mode = HEATER_MODE_STANDBY + update_icon() + return + + var/datum/gas_mixture/env = L.return_air() + + var/newMode = HEATER_MODE_STANDBY + if(setMode != HEATER_MODE_COOL && env.temperature < targetTemperature - temperatureTolerance) + newMode = HEATER_MODE_HEAT + else if(setMode != HEATER_MODE_HEAT && env.temperature > targetTemperature + temperatureTolerance) + newMode = HEATER_MODE_COOL + + if(mode != newMode) + mode = newMode + update_icon() + + if(mode == HEATER_MODE_STANDBY) + return + + var/heat_capacity = env.heat_capacity() + var/requiredPower = abs(env.temperature - targetTemperature) * heat_capacity + requiredPower = min(requiredPower, heatingPower) + + if(requiredPower < 1) + return + + var/deltaTemperature = requiredPower / heat_capacity + if(mode == HEATER_MODE_COOL) + deltaTemperature *= -1 + if(deltaTemperature) + env.temperature += deltaTemperature + air_update_turf() + cell.use(requiredPower / efficiency) + else + on = FALSE + update_icon() + +#undef HEATER_MODE_STANDBY +#undef HEATER_MODE_HEAT +#undef HEATER_MODE_COOL diff --git a/code/game/machinery/telecomms/machine_interactions.dm b/code/game/machinery/telecomms/machine_interactions.dm index 9d5169e5781..64bcd14a150 100644 --- a/code/game/machinery/telecomms/machine_interactions.dm +++ b/code/game/machinery/telecomms/machine_interactions.dm @@ -1,327 +1,327 @@ - -/* - - All telecommunications interactions: - -*/ - -#define STATION_Z 1 -#define TELECOMM_Z 3 - -/obj/machinery/telecomms - var/temp = "" // output message - - -/obj/machinery/telecomms/attackby(obj/item/P, mob/user, params) - - var/icon_closed = initial(icon_state) - var/icon_open = "[initial(icon_state)]_o" - if(!on) - icon_closed = "[initial(icon_state)]_off" - icon_open = "[initial(icon_state)]_o_off" - - if(default_deconstruction_screwdriver(user, icon_open, icon_closed, P)) - updateUsrDialog() - return - - if(exchange_parts(user, P)) - return - - // Using a multitool lets you access the receiver's interface - if(istype(P, /obj/item/device/multitool)) - attack_hand(user) - - default_deconstruction_crowbar(P) - - -/obj/machinery/telecomms/attack_ai(mob/user) - attack_hand(user) - -/obj/machinery/telecomms/attack_hand(mob/user) - - // You need a multitool to use this, or be silicon - if(!issilicon(user)) - // istype returns false if the value is null - if(!istype(user.get_active_hand(), /obj/item/device/multitool)) - return - - if(stat & (BROKEN|NOPOWER)) - return - - var/obj/item/device/multitool/P = get_multitool(user) - - user.set_machine(src) - var/dat - dat = "[src.name]

[src.name] Access

" - dat += "
[temp]
" - dat += "
Power Status: [src.toggled ? "On" : "Off"]" - if(on && toggled) - if(id != "" && id) - dat += "
Identification String: [id]" - else - dat += "
Identification String: NULL" - dat += "
Network: [network]" - dat += "
Prefabrication: [autolinkers.len ? "TRUE" : "FALSE"]" - if(hide) dat += "
Shadow Link: ACTIVE" - - //Show additional options for certain machines. - dat += Options_Menu() - - dat += "
Linked Network Entities:
    " - - var/i = 0 - for(var/obj/machinery/telecomms/T in links) - i++ - if(T.hide && !src.hide) - continue - dat += "
  1. \ref[T] [T.name] ([T.id]) \[X\]
  2. " - dat += "
" - - dat += "
Filtering Frequencies: " - - i = 0 - if(length(freq_listening)) - for(var/x in freq_listening) - i++ - if(i < length(freq_listening)) - dat += "[format_frequency(x)] GHz\[X\]; " - else - dat += "[format_frequency(x)] GHz\[X\]" - else - dat += "NONE" - - dat += "
\[Add Filter\]" - dat += "
" - - if(P) - var/obj/machinery/telecomms/T = P.buffer - if(istype(T)) - dat += "

MULTITOOL BUFFER: [T] ([T.id]) \[Link\] \[Flush\]" - else - dat += "

MULTITOOL BUFFER:
\[Add Machine\]" - - dat += "
" - temp = "" - user << browse(dat, "window=tcommachine;size=520x500;can_resize=0") - onclose(user, "dormitory") - - -// Off-Site Relays -// -// You are able to send/receive signals from the station's z level (changeable in the STATION_Z #define) if -// the relay is on the telecomm satellite (changable in the TELECOMM_Z #define) - - -/obj/machinery/telecomms/relay/proc/toggle_level() - - var/turf/position = get_turf(src) - - // Toggle on/off getting signals from the station or the current Z level - if(src.listening_level == STATION_Z) // equals the station - src.listening_level = position.z - return 1 - else if(position.z == TELECOMM_Z) - src.listening_level = STATION_Z - return 1 - return 0 - -// Returns a multitool from a user depending on their mobtype. - -/obj/machinery/telecomms/proc/get_multitool(mob/user) - - var/obj/item/device/multitool/P = null - // Let's double check - if(!issilicon(user) && istype(user.get_active_hand(), /obj/item/device/multitool)) - P = user.get_active_hand() - else if(isAI(user)) - var/mob/living/silicon/ai/U = user - P = U.aiMulti - else if(isrobot(user) && in_range(user, src)) - if(istype(user.get_active_hand(), /obj/item/device/multitool)) - P = user.get_active_hand() - return P - -// Additional Options for certain machines. Use this when you want to add an option to a specific machine. -// Example of how to use below. - -/obj/machinery/telecomms/proc/Options_Menu() - return "" - -// The topic for Additional Options. Use this for checking href links for your specific option. -// Example of how to use below. -/obj/machinery/telecomms/proc/Options_Topic(href, href_list) - return - -// RELAY - -/obj/machinery/telecomms/relay/Options_Menu() - var/dat = "" - if(src.z == TELECOMM_Z) - dat += "
Signal Locked to Station: [listening_level == STATION_Z ? "TRUE" : "FALSE"]" - dat += "
Broadcasting: [broadcasting ? "YES" : "NO"]" - dat += "
Receiving: [receiving ? "YES" : "NO"]" - return dat - -/obj/machinery/telecomms/relay/Options_Topic(href, href_list) - - if(href_list["receive"]) - receiving = !receiving - temp = "-% Receiving mode changed. %-" - if(href_list["broadcast"]) - broadcasting = !broadcasting - temp = "-% Broadcasting mode changed. %-" - if(href_list["change_listening"]) - //Lock to the station OR lock to the current position! - //You need at least two receivers and two broadcasters for this to work, this includes the machine. - var/result = toggle_level() - if(result) - temp = "-% [src]'s signal has been successfully changed." - else - temp = "-% [src] could not lock it's signal onto the station. Two broadcasters or receivers required." - -// BUS - -/obj/machinery/telecomms/bus/Options_Menu() - var/dat = "
Change Signal Frequency: [change_frequency ? "YES ([change_frequency])" : "NO"]" - return dat - -/obj/machinery/telecomms/bus/Options_Topic(href, href_list) - - if(href_list["change_freq"]) - - var/newfreq = input(usr, "Specify a new frequency for new signals to change to. Enter null to turn off frequency changing. Decimals assigned automatically.", src, network) as null|num - if(canAccess(usr)) - if(newfreq) - if(findtext(num2text(newfreq), ".")) - newfreq *= 10 // shift the decimal one place - if(newfreq < 10000) - change_frequency = newfreq - temp = "-% New frequency to change to assigned: \"[newfreq] GHz\" %-" - else - change_frequency = 0 - temp = "-% Frequency changing deactivated %-" - - -/obj/machinery/telecomms/Topic(href, href_list) - if(..()) - return - - if(!issilicon(usr)) - if(!istype(usr.get_active_hand(), /obj/item/device/multitool)) - return - - var/obj/item/device/multitool/P = get_multitool(usr) - - if(href_list["input"]) - switch(href_list["input"]) - - if("toggle") - - src.toggled = !src.toggled - temp = "-% [src] has been [src.toggled ? "activated" : "deactivated"]." - update_power() - - /* - if("hide") - src.hide = !hide - temp = "-% Shadow Link has been [src.hide ? "activated" : "deactivated"]." - */ - - if("id") - var/newid = copytext(reject_bad_text(input(usr, "Specify the new ID for this machine", src, id) as null|text),1,MAX_MESSAGE_LEN) - if(newid && canAccess(usr)) - id = newid - temp = "-% New ID assigned: \"[id]\" %-" - - if("network") - var/newnet = stripped_input(usr, "Specify the new network for this machine. This will break all current links.", src, network) - if(newnet && canAccess(usr)) - - if(length(newnet) > 15) - temp = "-% Too many characters in new network tag %-" - - else - for(var/obj/machinery/telecomms/T in links) - T.links.Remove(src) - - network = newnet - links = list() - temp = "-% New network tag assigned: \"[network]\" %-" - - - if("freq") - var/newfreq = input(usr, "Specify a new frequency to filter (GHz). Decimals assigned automatically.", src, network) as null|num - if(newfreq && canAccess(usr)) - if(findtext(num2text(newfreq), ".")) - newfreq *= 10 // shift the decimal one place - if(newfreq == SYND_FREQ) - temp = "-% Error: Interference preventing filtering frequency: \"[newfreq] GHz\" %-" - else - if(!(newfreq in freq_listening) && newfreq < 10000) - freq_listening.Add(newfreq) - temp = "-% New frequency filter assigned: \"[newfreq] GHz\" %-" - - if(href_list["delete"]) - - // changed the layout about to workaround a pesky runtime -- Doohl - - var/x = text2num(href_list["delete"]) - temp = "-% Removed frequency filter [x] %-" - freq_listening.Remove(x) - - if(href_list["unlink"]) - - if(text2num(href_list["unlink"]) <= length(links)) - var/obj/machinery/telecomms/T = links[text2num(href_list["unlink"])] - if(T) - temp = "-% Removed \ref[T] [T.name] from linked entities. %-" - - // Remove link entries from both T and src. - - if(T.links) - T.links.Remove(src) - links.Remove(T) - - else - temp = "-% Unable to locate machine to unlink from, try again. %-" - - if(href_list["link"]) - - if(P) - var/obj/machinery/telecomms/T = P.buffer - if(istype(T) && T != src) - if(!(src in T.links)) - T.links.Add(src) - - if(!(T in src.links)) - src.links.Add(T) - - temp = "-% Successfully linked with \ref[T] [T.name] %-" - - else - temp = "-% Unable to acquire buffer %-" - - if(href_list["buffer"]) - - P.buffer = src - temp = "-% Successfully stored \ref[P.buffer] [P.buffer.name] in buffer %-" - - - if(href_list["flush"]) - - temp = "-% Buffer successfully flushed. %-" - P.buffer = null - - src.Options_Topic(href, href_list) - - usr.set_machine(src) - - updateUsrDialog() - -/obj/machinery/telecomms/proc/canAccess(mob/user) - if(issilicon(user) || in_range(user, src)) - return 1 - return 0 - -#undef TELECOMM_Z -#undef STATION_Z + +/* + + All telecommunications interactions: + +*/ + +#define STATION_Z 1 +#define TELECOMM_Z 3 + +/obj/machinery/telecomms + var/temp = "" // output message + + +/obj/machinery/telecomms/attackby(obj/item/P, mob/user, params) + + var/icon_closed = initial(icon_state) + var/icon_open = "[initial(icon_state)]_o" + if(!on) + icon_closed = "[initial(icon_state)]_off" + icon_open = "[initial(icon_state)]_o_off" + + if(default_deconstruction_screwdriver(user, icon_open, icon_closed, P)) + updateUsrDialog() + return + + if(exchange_parts(user, P)) + return + + // Using a multitool lets you access the receiver's interface + if(istype(P, /obj/item/device/multitool)) + attack_hand(user) + + default_deconstruction_crowbar(P) + + +/obj/machinery/telecomms/attack_ai(mob/user) + attack_hand(user) + +/obj/machinery/telecomms/attack_hand(mob/user) + + // You need a multitool to use this, or be silicon + if(!issilicon(user)) + // istype returns false if the value is null + if(!istype(user.get_active_hand(), /obj/item/device/multitool)) + return + + if(stat & (BROKEN|NOPOWER)) + return + + var/obj/item/device/multitool/P = get_multitool(user) + + user.set_machine(src) + var/dat + dat = "[src.name]

[src.name] Access

" + dat += "
[temp]
" + dat += "
Power Status: [src.toggled ? "On" : "Off"]" + if(on && toggled) + if(id != "" && id) + dat += "
Identification String: [id]" + else + dat += "
Identification String: NULL" + dat += "
Network: [network]" + dat += "
Prefabrication: [autolinkers.len ? "TRUE" : "FALSE"]" + if(hide) dat += "
Shadow Link: ACTIVE" + + //Show additional options for certain machines. + dat += Options_Menu() + + dat += "
Linked Network Entities:
    " + + var/i = 0 + for(var/obj/machinery/telecomms/T in links) + i++ + if(T.hide && !src.hide) + continue + dat += "
  1. \ref[T] [T.name] ([T.id]) \[X\]
  2. " + dat += "
" + + dat += "
Filtering Frequencies: " + + i = 0 + if(length(freq_listening)) + for(var/x in freq_listening) + i++ + if(i < length(freq_listening)) + dat += "[format_frequency(x)] GHz\[X\]; " + else + dat += "[format_frequency(x)] GHz\[X\]" + else + dat += "NONE" + + dat += "
\[Add Filter\]" + dat += "
" + + if(P) + var/obj/machinery/telecomms/T = P.buffer + if(istype(T)) + dat += "

MULTITOOL BUFFER: [T] ([T.id]) \[Link\] \[Flush\]" + else + dat += "

MULTITOOL BUFFER:
\[Add Machine\]" + + dat += "
" + temp = "" + user << browse(dat, "window=tcommachine;size=520x500;can_resize=0") + onclose(user, "dormitory") + + +// Off-Site Relays +// +// You are able to send/receive signals from the station's z level (changeable in the STATION_Z #define) if +// the relay is on the telecomm satellite (changable in the TELECOMM_Z #define) + + +/obj/machinery/telecomms/relay/proc/toggle_level() + + var/turf/position = get_turf(src) + + // Toggle on/off getting signals from the station or the current Z level + if(src.listening_level == STATION_Z) // equals the station + src.listening_level = position.z + return 1 + else if(position.z == TELECOMM_Z) + src.listening_level = STATION_Z + return 1 + return 0 + +// Returns a multitool from a user depending on their mobtype. + +/obj/machinery/telecomms/proc/get_multitool(mob/user) + + var/obj/item/device/multitool/P = null + // Let's double check + if(!issilicon(user) && istype(user.get_active_hand(), /obj/item/device/multitool)) + P = user.get_active_hand() + else if(isAI(user)) + var/mob/living/silicon/ai/U = user + P = U.aiMulti + else if(isrobot(user) && in_range(user, src)) + if(istype(user.get_active_hand(), /obj/item/device/multitool)) + P = user.get_active_hand() + return P + +// Additional Options for certain machines. Use this when you want to add an option to a specific machine. +// Example of how to use below. + +/obj/machinery/telecomms/proc/Options_Menu() + return "" + +// The topic for Additional Options. Use this for checking href links for your specific option. +// Example of how to use below. +/obj/machinery/telecomms/proc/Options_Topic(href, href_list) + return + +// RELAY + +/obj/machinery/telecomms/relay/Options_Menu() + var/dat = "" + if(src.z == TELECOMM_Z) + dat += "
Signal Locked to Station: [listening_level == STATION_Z ? "TRUE" : "FALSE"]" + dat += "
Broadcasting: [broadcasting ? "YES" : "NO"]" + dat += "
Receiving: [receiving ? "YES" : "NO"]" + return dat + +/obj/machinery/telecomms/relay/Options_Topic(href, href_list) + + if(href_list["receive"]) + receiving = !receiving + temp = "-% Receiving mode changed. %-" + if(href_list["broadcast"]) + broadcasting = !broadcasting + temp = "-% Broadcasting mode changed. %-" + if(href_list["change_listening"]) + //Lock to the station OR lock to the current position! + //You need at least two receivers and two broadcasters for this to work, this includes the machine. + var/result = toggle_level() + if(result) + temp = "-% [src]'s signal has been successfully changed." + else + temp = "-% [src] could not lock it's signal onto the station. Two broadcasters or receivers required." + +// BUS + +/obj/machinery/telecomms/bus/Options_Menu() + var/dat = "
Change Signal Frequency: [change_frequency ? "YES ([change_frequency])" : "NO"]" + return dat + +/obj/machinery/telecomms/bus/Options_Topic(href, href_list) + + if(href_list["change_freq"]) + + var/newfreq = input(usr, "Specify a new frequency for new signals to change to. Enter null to turn off frequency changing. Decimals assigned automatically.", src, network) as null|num + if(canAccess(usr)) + if(newfreq) + if(findtext(num2text(newfreq), ".")) + newfreq *= 10 // shift the decimal one place + if(newfreq < 10000) + change_frequency = newfreq + temp = "-% New frequency to change to assigned: \"[newfreq] GHz\" %-" + else + change_frequency = 0 + temp = "-% Frequency changing deactivated %-" + + +/obj/machinery/telecomms/Topic(href, href_list) + if(..()) + return + + if(!issilicon(usr)) + if(!istype(usr.get_active_hand(), /obj/item/device/multitool)) + return + + var/obj/item/device/multitool/P = get_multitool(usr) + + if(href_list["input"]) + switch(href_list["input"]) + + if("toggle") + + src.toggled = !src.toggled + temp = "-% [src] has been [src.toggled ? "activated" : "deactivated"]." + update_power() + + /* + if("hide") + src.hide = !hide + temp = "-% Shadow Link has been [src.hide ? "activated" : "deactivated"]." + */ + + if("id") + var/newid = copytext(reject_bad_text(input(usr, "Specify the new ID for this machine", src, id) as null|text),1,MAX_MESSAGE_LEN) + if(newid && canAccess(usr)) + id = newid + temp = "-% New ID assigned: \"[id]\" %-" + + if("network") + var/newnet = stripped_input(usr, "Specify the new network for this machine. This will break all current links.", src, network) + if(newnet && canAccess(usr)) + + if(length(newnet) > 15) + temp = "-% Too many characters in new network tag %-" + + else + for(var/obj/machinery/telecomms/T in links) + T.links.Remove(src) + + network = newnet + links = list() + temp = "-% New network tag assigned: \"[network]\" %-" + + + if("freq") + var/newfreq = input(usr, "Specify a new frequency to filter (GHz). Decimals assigned automatically.", src, network) as null|num + if(newfreq && canAccess(usr)) + if(findtext(num2text(newfreq), ".")) + newfreq *= 10 // shift the decimal one place + if(newfreq == SYND_FREQ) + temp = "-% Error: Interference preventing filtering frequency: \"[newfreq] GHz\" %-" + else + if(!(newfreq in freq_listening) && newfreq < 10000) + freq_listening.Add(newfreq) + temp = "-% New frequency filter assigned: \"[newfreq] GHz\" %-" + + if(href_list["delete"]) + + // changed the layout about to workaround a pesky runtime -- Doohl + + var/x = text2num(href_list["delete"]) + temp = "-% Removed frequency filter [x] %-" + freq_listening.Remove(x) + + if(href_list["unlink"]) + + if(text2num(href_list["unlink"]) <= length(links)) + var/obj/machinery/telecomms/T = links[text2num(href_list["unlink"])] + if(T) + temp = "-% Removed \ref[T] [T.name] from linked entities. %-" + + // Remove link entries from both T and src. + + if(T.links) + T.links.Remove(src) + links.Remove(T) + + else + temp = "-% Unable to locate machine to unlink from, try again. %-" + + if(href_list["link"]) + + if(P) + var/obj/machinery/telecomms/T = P.buffer + if(istype(T) && T != src) + if(!(src in T.links)) + T.links.Add(src) + + if(!(T in src.links)) + src.links.Add(T) + + temp = "-% Successfully linked with \ref[T] [T.name] %-" + + else + temp = "-% Unable to acquire buffer %-" + + if(href_list["buffer"]) + + P.buffer = src + temp = "-% Successfully stored \ref[P.buffer] [P.buffer.name] in buffer %-" + + + if(href_list["flush"]) + + temp = "-% Buffer successfully flushed. %-" + P.buffer = null + + src.Options_Topic(href, href_list) + + usr.set_machine(src) + + updateUsrDialog() + +/obj/machinery/telecomms/proc/canAccess(mob/user) + if(issilicon(user) || in_range(user, src)) + return 1 + return 0 + +#undef TELECOMM_Z +#undef STATION_Z diff --git a/code/game/machinery/teleporter.dm b/code/game/machinery/teleporter.dm index ecaecfae372..cb6f866a93d 100644 --- a/code/game/machinery/teleporter.dm +++ b/code/game/machinery/teleporter.dm @@ -1,464 +1,464 @@ -/obj/machinery/computer/teleporter - name = "Teleporter Control Console" - desc = "Used to control a linked teleportation Hub and Station." - icon_screen = "teleport" - icon_keyboard = "teleport_key" - circuit = "/obj/item/weapon/circuitboard/teleporter" - var/obj/item/device/gps/locked = null - var/regime_set = "Teleporter" - var/id = null - var/obj/machinery/teleport/station/power_station - var/calibrating - var/turf/target //Used for one-time-use teleport cards (such as clown planet coordinates.) - //Setting this to 1 will set src.locked to null after a player enters the portal and will not allow hand-teles to open portals to that location. - -/obj/machinery/computer/teleporter/New() - src.id = "[rand(1000, 9999)]" - link_power_station() - ..() - return - -/obj/machinery/computer/teleporter/initialize() - link_power_station() - -/obj/machinery/computer/teleporter/Destroy() - if (power_station) - power_station.teleporter_console = null - power_station = null - return ..() - -/obj/machinery/computer/teleporter/proc/link_power_station() - if(power_station) - return - for(dir in list(NORTH,EAST,SOUTH,WEST)) - power_station = locate(/obj/machinery/teleport/station, get_step(src, dir)) - if(power_station) - break - return power_station - -/obj/machinery/computer/teleporter/attackby(obj/I, mob/living/user, params) - if(istype(I, /obj/item/device/gps)) - var/obj/item/device/gps/L = I - if(L.locked_location && !(stat & (NOPOWER|BROKEN))) - if(!user.unEquip(L)) - user << "\the [I] is stuck to your hand, you cannot put it in \the [src]!" - return - L.loc = src - locked = L - user << "You insert the GPS device into the [name]'s slot." - else - ..() - return - -/obj/machinery/computer/teleporter/attack_ai(mob/user) - src.attack_hand(user) - -/obj/machinery/computer/teleporter/attack_hand(mob/user) - if(..()) - return - interact(user) - -/obj/machinery/computer/teleporter/interact(mob/user) - var/data = "

Teleporter Status

" - if(!power_station) - data += "
No power station linked.
" - else if(!power_station.teleporter_hub) - data += "
No hub linked.
" - else - data += "
Current regime: [regime_set]
" - data += "Current target: [(!target) ? "None" : "[get_area(target)] [(regime_set != "Gate") ? "" : "Teleporter"]"]
" - if(calibrating) - data += "Calibration: In Progress" - else if(power_station.teleporter_hub.calibrated || power_station.teleporter_hub.accurate >= 3) - data += "Calibration: Optimal" - else - data += "Calibration: Sub-Optimal" - data += "

" - - data += "Change regime
" - data += "Set target
" - if(locked) - data += "
Get target from memory
" - data += "Eject GPS device
" - else - data += "
Get target from memory
" - data += "Eject GPS device
" - - data += "
Calibrate Hub" - - var/datum/browser/popup = new(user, "teleporter", name, 400, 400) - popup.set_content(data) - popup.open() - return - -/obj/machinery/computer/teleporter/Topic(href, href_list) - if(..()) - return - - if(href_list["eject"]) - eject() - updateDialog() - return - - if(!check_hub_connection()) - say("Error: Unable to detect hub.") - return - if(calibrating) - say("Error: Calibration in progress. Stand by.") - return - - if(href_list["regimeset"]) - power_station.engaged = 0 - power_station.teleporter_hub.update_icon() - power_station.teleporter_hub.calibrated = 0 - reset_regime() - if(href_list["settarget"]) - power_station.engaged = 0 - power_station.teleporter_hub.update_icon() - power_station.teleporter_hub.calibrated = 0 - set_target(usr) - if(href_list["locked"]) - power_station.engaged = 0 - power_station.teleporter_hub.update_icon() - power_station.teleporter_hub.calibrated = 0 - target = get_turf(locked.locked_location) - if(href_list["calibrate"]) - if(!target) - say("Error: No target set to calibrate to.") - return - if(power_station.teleporter_hub.calibrated || power_station.teleporter_hub.accurate >= 3) - say("Hub is already calibrated!") - return - say("Processing hub calibration to target...") - - calibrating = 1 - spawn(50 * (3 - power_station.teleporter_hub.accurate)) //Better parts mean faster calibration - calibrating = 0 - if(check_hub_connection()) - power_station.teleporter_hub.calibrated = 1 - say("Calibration complete.") - else - say("Error: Unable to detect hub.") - updateDialog() - - updateDialog() - -/obj/machinery/computer/teleporter/proc/check_hub_connection() - if(!power_station) - return - if(!power_station.teleporter_hub) - return - return 1 - -/obj/machinery/computer/teleporter/proc/reset_regime() - target = null - if(regime_set == "Teleporter") - regime_set = "Gate" - else - regime_set = "Teleporter" - -/obj/machinery/computer/teleporter/proc/eject() - if(locked) - locked.loc = loc - locked = null - -/obj/machinery/computer/teleporter/proc/set_target(mob/user) - if(regime_set == "Teleporter") - var/list/L = list() - var/list/areaindex = list() - - for(var/obj/item/device/radio/beacon/R in world) - var/turf/T = get_turf(R) - if (!T) - continue - if(T.z == ZLEVEL_CENTCOM || T.z > ZLEVEL_SPACEMAX) - continue - var/tmpname = T.loc.name - if(areaindex[tmpname]) - tmpname = "[tmpname] ([++areaindex[tmpname]])" - else - areaindex[tmpname] = 1 - L[tmpname] = R - - for (var/obj/item/weapon/implant/tracking/I in tracked_implants) - if (!I.implanted || !ismob(I.loc)) - continue - else - var/mob/M = I.loc - if (M.stat == 2) - if (M.timeofdeath + 6000 < world.time) - continue - var/turf/T = get_turf(M) - if(!T) continue - if(T.z == ZLEVEL_CENTCOM) continue - var/tmpname = M.real_name - if(areaindex[tmpname]) - tmpname = "[tmpname] ([++areaindex[tmpname]])" - else - areaindex[tmpname] = 1 - L[tmpname] = I - - var/desc = input("Please select a location to lock in.", "Locking Computer") in L - target = L[desc] - - else - var/list/L = list() - var/list/areaindex = list() - var/list/S = power_station.linked_stations - if(!S.len) - user << "No connected stations located." - return - for(var/obj/machinery/teleport/station/R in S) - var/turf/T = get_turf(R) - if (!T || !R.teleporter_hub || !R.teleporter_console) - continue - if(T.z == ZLEVEL_CENTCOM || T.z > ZLEVEL_SPACEMAX) - continue - var/tmpname = T.loc.name - if(areaindex[tmpname]) - tmpname = "[tmpname] ([++areaindex[tmpname]])" - else - areaindex[tmpname] = 1 - L[tmpname] = R - var/desc = input("Please select a station to lock in.", "Locking Computer") in L - target = L[desc] - if(target) - var/obj/machinery/teleport/station/trg = target - trg.linked_stations |= power_station - trg.stat &= ~NOPOWER - if(trg.teleporter_hub) - trg.teleporter_hub.stat &= ~NOPOWER - trg.teleporter_hub.update_icon() - if(trg.teleporter_console) - trg.teleporter_console.stat &= ~NOPOWER - trg.teleporter_console.update_icon() - return - -/obj/machinery/teleport - name = "teleport" - icon = 'icons/obj/stationobjs.dmi' - density = 1 - anchored = 1 - -/obj/machinery/teleport/hub - name = "teleporter hub" - desc = "It's the hub of a teleporting machine." - icon_state = "tele0" - var/accurate = 0 - use_power = 1 - idle_power_usage = 10 - active_power_usage = 2000 - var/obj/machinery/teleport/station/power_station - var/calibrated //Calibration prevents mutation - -/obj/machinery/teleport/hub/New() - ..() - link_power_station() - component_parts = list() - component_parts += new /obj/item/weapon/circuitboard/teleporter_hub(null) - component_parts += new /obj/item/weapon/ore/bluespace_crystal/artificial(null) - component_parts += new /obj/item/weapon/ore/bluespace_crystal/artificial(null) - component_parts += new /obj/item/weapon/ore/bluespace_crystal/artificial(null) - component_parts += new /obj/item/weapon/stock_parts/matter_bin(null) - RefreshParts() - -/obj/machinery/teleport/hub/initialize() - link_power_station() - -/obj/machinery/teleport/hub/Destroy() - if (power_station) - power_station.teleporter_hub = null - power_station = null - return ..() - -/obj/machinery/teleport/hub/RefreshParts() - var/A = 0 - for(var/obj/item/weapon/stock_parts/matter_bin/M in component_parts) - A += M.rating - accurate = A - -/obj/machinery/teleport/hub/proc/link_power_station() - if(power_station) - return - for(dir in list(NORTH,EAST,SOUTH,WEST)) - power_station = locate(/obj/machinery/teleport/station, get_step(src, dir)) - if(power_station) - break - return power_station - -/obj/machinery/teleport/hub/Bumped(M as mob|obj) - if(z == ZLEVEL_CENTCOM) - M << "You can't use this here." - if(power_station && power_station.engaged && !panel_open) - teleport(M) - use_power(5000) - return - -/obj/machinery/teleport/hub/attackby(obj/item/W, mob/user, params) - if(default_deconstruction_screwdriver(user, "tele-o", "tele0", W)) - return - - if(exchange_parts(user, W)) - return - - default_deconstruction_crowbar(W) - -/obj/machinery/teleport/hub/proc/teleport(atom/movable/M as mob|obj, turf/T) - var/obj/machinery/computer/teleporter/com = power_station.teleporter_console - if (!com) - return - if (!com.target) - visible_message("Cannot authenticate locked on coordinates. Please reinstate coordinate matrix.") - return - if (istype(M, /atom/movable)) - if(do_teleport(M, com.target)) - if(!calibrated && prob(30 - ((accurate) * 10))) //oh dear a problem - if(ishuman(M))//don't remove people from the round randomly you jerks - var/mob/living/carbon/human/human = M - if(human.dna && human.dna.species.id == "human") - M << "You hear a buzzing in your ears." - human.set_species(/datum/species/fly) - - human.apply_effect((rand(120 - accurate * 40, 180 - accurate * 60)), IRRADIATE, 0) - calibrated = 0 - return - -/obj/machinery/teleport/hub/update_icon() - if(panel_open) - icon_state = "tele-o" - else if(power_station && power_station.engaged) - icon_state = "tele1" - else - icon_state = "tele0" - -/obj/machinery/teleport/hub/syndicate/New() - ..() - component_parts += new /obj/item/weapon/stock_parts/matter_bin/super(null) - RefreshParts() - - -/obj/machinery/teleport/station - name = "station" - desc = "The power control station for a bluespace teleporter. Used for toggling power, and can activate a test-fire to prevent malfunctions." - icon_state = "controller" - var/engaged = 0 - use_power = 1 - idle_power_usage = 10 - active_power_usage = 2000 - var/obj/machinery/computer/teleporter/teleporter_console - var/obj/machinery/teleport/hub/teleporter_hub - var/list/linked_stations = list() - var/efficiency = 0 - -/obj/machinery/teleport/station/New() - ..() - component_parts = list() - component_parts += new /obj/item/weapon/circuitboard/teleporter_station(null) - component_parts += new /obj/item/weapon/ore/bluespace_crystal/artificial(null) - component_parts += new /obj/item/weapon/ore/bluespace_crystal/artificial(null) - component_parts += new /obj/item/weapon/stock_parts/capacitor(null) - component_parts += new /obj/item/weapon/stock_parts/capacitor(null) - component_parts += new /obj/item/weapon/stock_parts/console_screen(null) - RefreshParts() - link_console_and_hub() - -/obj/machinery/teleport/station/initialize() - link_console_and_hub() - -/obj/machinery/teleport/station/RefreshParts() - var/E - for(var/obj/item/weapon/stock_parts/capacitor/C in component_parts) - E += C.rating - efficiency = E - 1 - -/obj/machinery/teleport/station/proc/link_console_and_hub() - for(dir in list(NORTH,EAST,SOUTH,WEST)) - teleporter_hub = locate(/obj/machinery/teleport/hub, get_step(src, dir)) - if(teleporter_hub) - teleporter_hub.link_power_station() - break - for(dir in list(NORTH,EAST,SOUTH,WEST)) - teleporter_console = locate(/obj/machinery/computer/teleporter, get_step(src, dir)) - if(teleporter_console) - teleporter_console.link_power_station() - break - return teleporter_hub && teleporter_console - - -/obj/machinery/teleport/station/Destroy() - if(teleporter_hub) - teleporter_hub.power_station = null - teleporter_hub.update_icon() - teleporter_hub = null - if (teleporter_console) - teleporter_console.power_station = null - teleporter_console = null - return ..() - -/obj/machinery/teleport/station/attackby(obj/item/weapon/W, mob/user, params) - if(istype(W, /obj/item/device/multitool) && !panel_open) - var/obj/item/device/multitool/M = W - if(M.buffer && istype(M.buffer, /obj/machinery/teleport/station) && M.buffer != src) - if(linked_stations.len < efficiency) - linked_stations.Add(M.buffer) - M.buffer = null - user << "You upload the data from the [W.name]'s buffer." - else - user << "This station can't hold more information, try to use better parts." - if(default_deconstruction_screwdriver(user, "controller-o", "controller", W)) - update_icon() - return - - if(exchange_parts(user, W)) - return - - default_deconstruction_crowbar(W) - - if(panel_open) - if(istype(W, /obj/item/device/multitool)) - var/obj/item/device/multitool/M = W - M.buffer = src - user << "You download the data to the [W.name]'s buffer." - return - if(istype(W, /obj/item/weapon/wirecutters)) - link_console_and_hub() - user << "You reconnect the station to nearby machinery." - return - -/obj/machinery/teleport/station/attack_paw() - src.attack_hand() - -/obj/machinery/teleport/station/attack_ai() - src.attack_hand() - -/obj/machinery/teleport/station/attack_hand(mob/user) - if(!panel_open) - toggle(user) - -/obj/machinery/teleport/station/proc/toggle(mob/user) - if(stat & (BROKEN|NOPOWER) || !teleporter_hub || !teleporter_console ) - return - if (teleporter_console.target) - src.engaged = !src.engaged - use_power(5000) - visible_message("Teleporter [engaged ? "" : "dis"]engaged!") - else - visible_message("No target detected.") - src.engaged = 0 - teleporter_hub.update_icon() - src.add_fingerprint(user) - return - -/obj/machinery/teleport/station/power_change() - ..() - update_icon() - if(teleporter_hub) - teleporter_hub.update_icon() - -/obj/machinery/teleport/station/update_icon() - if(panel_open) - icon_state = "controller-o" - else if(stat & NOPOWER) - icon_state = "controller-p" - else - icon_state = "controller" +/obj/machinery/computer/teleporter + name = "Teleporter Control Console" + desc = "Used to control a linked teleportation Hub and Station." + icon_screen = "teleport" + icon_keyboard = "teleport_key" + circuit = "/obj/item/weapon/circuitboard/teleporter" + var/obj/item/device/gps/locked = null + var/regime_set = "Teleporter" + var/id = null + var/obj/machinery/teleport/station/power_station + var/calibrating + var/turf/target //Used for one-time-use teleport cards (such as clown planet coordinates.) + //Setting this to 1 will set src.locked to null after a player enters the portal and will not allow hand-teles to open portals to that location. + +/obj/machinery/computer/teleporter/New() + src.id = "[rand(1000, 9999)]" + link_power_station() + ..() + return + +/obj/machinery/computer/teleporter/initialize() + link_power_station() + +/obj/machinery/computer/teleporter/Destroy() + if (power_station) + power_station.teleporter_console = null + power_station = null + return ..() + +/obj/machinery/computer/teleporter/proc/link_power_station() + if(power_station) + return + for(dir in list(NORTH,EAST,SOUTH,WEST)) + power_station = locate(/obj/machinery/teleport/station, get_step(src, dir)) + if(power_station) + break + return power_station + +/obj/machinery/computer/teleporter/attackby(obj/I, mob/living/user, params) + if(istype(I, /obj/item/device/gps)) + var/obj/item/device/gps/L = I + if(L.locked_location && !(stat & (NOPOWER|BROKEN))) + if(!user.unEquip(L)) + user << "\the [I] is stuck to your hand, you cannot put it in \the [src]!" + return + L.loc = src + locked = L + user << "You insert the GPS device into the [name]'s slot." + else + ..() + return + +/obj/machinery/computer/teleporter/attack_ai(mob/user) + src.attack_hand(user) + +/obj/machinery/computer/teleporter/attack_hand(mob/user) + if(..()) + return + interact(user) + +/obj/machinery/computer/teleporter/interact(mob/user) + var/data = "

Teleporter Status

" + if(!power_station) + data += "
No power station linked.
" + else if(!power_station.teleporter_hub) + data += "
No hub linked.
" + else + data += "
Current regime: [regime_set]
" + data += "Current target: [(!target) ? "None" : "[get_area(target)] [(regime_set != "Gate") ? "" : "Teleporter"]"]
" + if(calibrating) + data += "Calibration: In Progress" + else if(power_station.teleporter_hub.calibrated || power_station.teleporter_hub.accurate >= 3) + data += "Calibration: Optimal" + else + data += "Calibration: Sub-Optimal" + data += "

" + + data += "Change regime
" + data += "Set target
" + if(locked) + data += "
Get target from memory
" + data += "Eject GPS device
" + else + data += "
Get target from memory
" + data += "Eject GPS device
" + + data += "
Calibrate Hub" + + var/datum/browser/popup = new(user, "teleporter", name, 400, 400) + popup.set_content(data) + popup.open() + return + +/obj/machinery/computer/teleporter/Topic(href, href_list) + if(..()) + return + + if(href_list["eject"]) + eject() + updateDialog() + return + + if(!check_hub_connection()) + say("Error: Unable to detect hub.") + return + if(calibrating) + say("Error: Calibration in progress. Stand by.") + return + + if(href_list["regimeset"]) + power_station.engaged = 0 + power_station.teleporter_hub.update_icon() + power_station.teleporter_hub.calibrated = 0 + reset_regime() + if(href_list["settarget"]) + power_station.engaged = 0 + power_station.teleporter_hub.update_icon() + power_station.teleporter_hub.calibrated = 0 + set_target(usr) + if(href_list["locked"]) + power_station.engaged = 0 + power_station.teleporter_hub.update_icon() + power_station.teleporter_hub.calibrated = 0 + target = get_turf(locked.locked_location) + if(href_list["calibrate"]) + if(!target) + say("Error: No target set to calibrate to.") + return + if(power_station.teleporter_hub.calibrated || power_station.teleporter_hub.accurate >= 3) + say("Hub is already calibrated!") + return + say("Processing hub calibration to target...") + + calibrating = 1 + spawn(50 * (3 - power_station.teleporter_hub.accurate)) //Better parts mean faster calibration + calibrating = 0 + if(check_hub_connection()) + power_station.teleporter_hub.calibrated = 1 + say("Calibration complete.") + else + say("Error: Unable to detect hub.") + updateDialog() + + updateDialog() + +/obj/machinery/computer/teleporter/proc/check_hub_connection() + if(!power_station) + return + if(!power_station.teleporter_hub) + return + return 1 + +/obj/machinery/computer/teleporter/proc/reset_regime() + target = null + if(regime_set == "Teleporter") + regime_set = "Gate" + else + regime_set = "Teleporter" + +/obj/machinery/computer/teleporter/proc/eject() + if(locked) + locked.loc = loc + locked = null + +/obj/machinery/computer/teleporter/proc/set_target(mob/user) + if(regime_set == "Teleporter") + var/list/L = list() + var/list/areaindex = list() + + for(var/obj/item/device/radio/beacon/R in world) + var/turf/T = get_turf(R) + if (!T) + continue + if(T.z == ZLEVEL_CENTCOM || T.z > ZLEVEL_SPACEMAX) + continue + var/tmpname = T.loc.name + if(areaindex[tmpname]) + tmpname = "[tmpname] ([++areaindex[tmpname]])" + else + areaindex[tmpname] = 1 + L[tmpname] = R + + for (var/obj/item/weapon/implant/tracking/I in tracked_implants) + if (!I.implanted || !ismob(I.loc)) + continue + else + var/mob/M = I.loc + if (M.stat == 2) + if (M.timeofdeath + 6000 < world.time) + continue + var/turf/T = get_turf(M) + if(!T) continue + if(T.z == ZLEVEL_CENTCOM) continue + var/tmpname = M.real_name + if(areaindex[tmpname]) + tmpname = "[tmpname] ([++areaindex[tmpname]])" + else + areaindex[tmpname] = 1 + L[tmpname] = I + + var/desc = input("Please select a location to lock in.", "Locking Computer") in L + target = L[desc] + + else + var/list/L = list() + var/list/areaindex = list() + var/list/S = power_station.linked_stations + if(!S.len) + user << "No connected stations located." + return + for(var/obj/machinery/teleport/station/R in S) + var/turf/T = get_turf(R) + if (!T || !R.teleporter_hub || !R.teleporter_console) + continue + if(T.z == ZLEVEL_CENTCOM || T.z > ZLEVEL_SPACEMAX) + continue + var/tmpname = T.loc.name + if(areaindex[tmpname]) + tmpname = "[tmpname] ([++areaindex[tmpname]])" + else + areaindex[tmpname] = 1 + L[tmpname] = R + var/desc = input("Please select a station to lock in.", "Locking Computer") in L + target = L[desc] + if(target) + var/obj/machinery/teleport/station/trg = target + trg.linked_stations |= power_station + trg.stat &= ~NOPOWER + if(trg.teleporter_hub) + trg.teleporter_hub.stat &= ~NOPOWER + trg.teleporter_hub.update_icon() + if(trg.teleporter_console) + trg.teleporter_console.stat &= ~NOPOWER + trg.teleporter_console.update_icon() + return + +/obj/machinery/teleport + name = "teleport" + icon = 'icons/obj/stationobjs.dmi' + density = 1 + anchored = 1 + +/obj/machinery/teleport/hub + name = "teleporter hub" + desc = "It's the hub of a teleporting machine." + icon_state = "tele0" + var/accurate = 0 + use_power = 1 + idle_power_usage = 10 + active_power_usage = 2000 + var/obj/machinery/teleport/station/power_station + var/calibrated //Calibration prevents mutation + +/obj/machinery/teleport/hub/New() + ..() + link_power_station() + component_parts = list() + component_parts += new /obj/item/weapon/circuitboard/teleporter_hub(null) + component_parts += new /obj/item/weapon/ore/bluespace_crystal/artificial(null) + component_parts += new /obj/item/weapon/ore/bluespace_crystal/artificial(null) + component_parts += new /obj/item/weapon/ore/bluespace_crystal/artificial(null) + component_parts += new /obj/item/weapon/stock_parts/matter_bin(null) + RefreshParts() + +/obj/machinery/teleport/hub/initialize() + link_power_station() + +/obj/machinery/teleport/hub/Destroy() + if (power_station) + power_station.teleporter_hub = null + power_station = null + return ..() + +/obj/machinery/teleport/hub/RefreshParts() + var/A = 0 + for(var/obj/item/weapon/stock_parts/matter_bin/M in component_parts) + A += M.rating + accurate = A + +/obj/machinery/teleport/hub/proc/link_power_station() + if(power_station) + return + for(dir in list(NORTH,EAST,SOUTH,WEST)) + power_station = locate(/obj/machinery/teleport/station, get_step(src, dir)) + if(power_station) + break + return power_station + +/obj/machinery/teleport/hub/Bumped(M as mob|obj) + if(z == ZLEVEL_CENTCOM) + M << "You can't use this here." + if(power_station && power_station.engaged && !panel_open) + teleport(M) + use_power(5000) + return + +/obj/machinery/teleport/hub/attackby(obj/item/W, mob/user, params) + if(default_deconstruction_screwdriver(user, "tele-o", "tele0", W)) + return + + if(exchange_parts(user, W)) + return + + default_deconstruction_crowbar(W) + +/obj/machinery/teleport/hub/proc/teleport(atom/movable/M as mob|obj, turf/T) + var/obj/machinery/computer/teleporter/com = power_station.teleporter_console + if (!com) + return + if (!com.target) + visible_message("Cannot authenticate locked on coordinates. Please reinstate coordinate matrix.") + return + if (istype(M, /atom/movable)) + if(do_teleport(M, com.target)) + if(!calibrated && prob(30 - ((accurate) * 10))) //oh dear a problem + if(ishuman(M))//don't remove people from the round randomly you jerks + var/mob/living/carbon/human/human = M + if(human.dna && human.dna.species.id == "human") + M << "You hear a buzzing in your ears." + human.set_species(/datum/species/fly) + + human.apply_effect((rand(120 - accurate * 40, 180 - accurate * 60)), IRRADIATE, 0) + calibrated = 0 + return + +/obj/machinery/teleport/hub/update_icon() + if(panel_open) + icon_state = "tele-o" + else if(power_station && power_station.engaged) + icon_state = "tele1" + else + icon_state = "tele0" + +/obj/machinery/teleport/hub/syndicate/New() + ..() + component_parts += new /obj/item/weapon/stock_parts/matter_bin/super(null) + RefreshParts() + + +/obj/machinery/teleport/station + name = "station" + desc = "The power control station for a bluespace teleporter. Used for toggling power, and can activate a test-fire to prevent malfunctions." + icon_state = "controller" + var/engaged = 0 + use_power = 1 + idle_power_usage = 10 + active_power_usage = 2000 + var/obj/machinery/computer/teleporter/teleporter_console + var/obj/machinery/teleport/hub/teleporter_hub + var/list/linked_stations = list() + var/efficiency = 0 + +/obj/machinery/teleport/station/New() + ..() + component_parts = list() + component_parts += new /obj/item/weapon/circuitboard/teleporter_station(null) + component_parts += new /obj/item/weapon/ore/bluespace_crystal/artificial(null) + component_parts += new /obj/item/weapon/ore/bluespace_crystal/artificial(null) + component_parts += new /obj/item/weapon/stock_parts/capacitor(null) + component_parts += new /obj/item/weapon/stock_parts/capacitor(null) + component_parts += new /obj/item/weapon/stock_parts/console_screen(null) + RefreshParts() + link_console_and_hub() + +/obj/machinery/teleport/station/initialize() + link_console_and_hub() + +/obj/machinery/teleport/station/RefreshParts() + var/E + for(var/obj/item/weapon/stock_parts/capacitor/C in component_parts) + E += C.rating + efficiency = E - 1 + +/obj/machinery/teleport/station/proc/link_console_and_hub() + for(dir in list(NORTH,EAST,SOUTH,WEST)) + teleporter_hub = locate(/obj/machinery/teleport/hub, get_step(src, dir)) + if(teleporter_hub) + teleporter_hub.link_power_station() + break + for(dir in list(NORTH,EAST,SOUTH,WEST)) + teleporter_console = locate(/obj/machinery/computer/teleporter, get_step(src, dir)) + if(teleporter_console) + teleporter_console.link_power_station() + break + return teleporter_hub && teleporter_console + + +/obj/machinery/teleport/station/Destroy() + if(teleporter_hub) + teleporter_hub.power_station = null + teleporter_hub.update_icon() + teleporter_hub = null + if (teleporter_console) + teleporter_console.power_station = null + teleporter_console = null + return ..() + +/obj/machinery/teleport/station/attackby(obj/item/weapon/W, mob/user, params) + if(istype(W, /obj/item/device/multitool) && !panel_open) + var/obj/item/device/multitool/M = W + if(M.buffer && istype(M.buffer, /obj/machinery/teleport/station) && M.buffer != src) + if(linked_stations.len < efficiency) + linked_stations.Add(M.buffer) + M.buffer = null + user << "You upload the data from the [W.name]'s buffer." + else + user << "This station can't hold more information, try to use better parts." + if(default_deconstruction_screwdriver(user, "controller-o", "controller", W)) + update_icon() + return + + if(exchange_parts(user, W)) + return + + default_deconstruction_crowbar(W) + + if(panel_open) + if(istype(W, /obj/item/device/multitool)) + var/obj/item/device/multitool/M = W + M.buffer = src + user << "You download the data to the [W.name]'s buffer." + return + if(istype(W, /obj/item/weapon/wirecutters)) + link_console_and_hub() + user << "You reconnect the station to nearby machinery." + return + +/obj/machinery/teleport/station/attack_paw() + src.attack_hand() + +/obj/machinery/teleport/station/attack_ai() + src.attack_hand() + +/obj/machinery/teleport/station/attack_hand(mob/user) + if(!panel_open) + toggle(user) + +/obj/machinery/teleport/station/proc/toggle(mob/user) + if(stat & (BROKEN|NOPOWER) || !teleporter_hub || !teleporter_console ) + return + if (teleporter_console.target) + src.engaged = !src.engaged + use_power(5000) + visible_message("Teleporter [engaged ? "" : "dis"]engaged!") + else + visible_message("No target detected.") + src.engaged = 0 + teleporter_hub.update_icon() + src.add_fingerprint(user) + return + +/obj/machinery/teleport/station/power_change() + ..() + update_icon() + if(teleporter_hub) + teleporter_hub.update_icon() + +/obj/machinery/teleport/station/update_icon() + if(panel_open) + icon_state = "controller-o" + else if(stat & NOPOWER) + icon_state = "controller-p" + else + icon_state = "controller" diff --git a/code/game/objects/items/devices/traitordevices.dm b/code/game/objects/items/devices/traitordevices.dm index c4c0845d2b4..f217989a655 100644 --- a/code/game/objects/items/devices/traitordevices.dm +++ b/code/game/objects/items/devices/traitordevices.dm @@ -1,204 +1,204 @@ -/* - -Miscellaneous traitor devices - -BATTERER - -RADIOACTIVE MICROLASER - -*/ - -/* - -The Batterer, like a flashbang but 50% chance to knock people over. Can be either very -effective or pretty fucking useless. - -*/ - -/obj/item/device/batterer - name = "mind batterer" - desc = "A strange device with twin antennas." - icon_state = "batterer" - throwforce = 5 - w_class = 1 - throw_speed = 3 - throw_range = 7 - flags = CONDUCT - item_state = "electronic" - origin_tech = "magnets=3;combat=3;syndicate=3" - - var/times_used = 0 //Number of times it's been used. - var/max_uses = 2 - - -/obj/item/device/batterer/attack_self(mob/living/carbon/user, flag = 0, emp = 0) - if(!user) return - if(times_used >= max_uses) - user << "The mind batterer has been burnt out!" - return - - add_logs(user, null, "knocked down people in the area", src) - - for(var/mob/living/carbon/human/M in ultra_range(10, user, 1)) - spawn() - if(prob(50)) - - M.Weaken(rand(10,20)) - if(prob(25)) - M.Stun(rand(5,10)) - M << "You feel a tremendous, paralyzing wave flood your mind." - - else - M << "You feel a sudden, electric jolt travel through your head." - - playsound(src.loc, 'sound/misc/interference.ogg', 50, 1) - user << "You trigger [src]." - times_used += 1 - if(times_used >= max_uses) - icon_state = "battererburnt" - -/* - The radioactive microlaser, a device disguised as a health analyzer used to irradiate people. - - The strength of the radiation is determined by the 'intensity' setting, while the delay between - the scan and the irradiation kicking in is determined by the wavelength. - - Each scan will cause the microlaser to have a brief cooldown period. Higher intensity will increase - the cooldown, while higher wavelength will decrease it. - - Wavelength is also slightly increased by the intensity as well. -*/ - -/obj/item/device/rad_laser - name = "health analyzer" - icon_state = "health" - item_state = "analyzer" - desc = "A hand-held body scanner able to distinguish vital signs of the subject. A strange microlaser is hooked on to the scanning end." - flags = CONDUCT - slot_flags = SLOT_BELT - throwforce = 3 - w_class = 1 - throw_speed = 3 - throw_range = 7 - materials = list(MAT_METAL=400) - origin_tech = "magnets=3;biotech=5;syndicate=3" - var/intensity = 5 // how much damage the radiation does - var/wavelength = 10 // time it takes for the radiation to kick in, in seconds - var/used = 0 // is it cooling down? - -/obj/item/device/rad_laser/attack(mob/living/M, mob/living/user) - if(!used) - add_logs(user, M, "irradiated", src) - user.visible_message("[user] has analyzed [M]'s vitals.") - var/cooldown = round(max(100,(((intensity*8)-(wavelength/2))+(intensity*2))*10)) - used = 1 - icon_state = "health1" - handle_cooldown(cooldown) // splits off to handle the cooldown while handling wavelength - spawn((wavelength+(intensity*4))*10) - if(M) - if(intensity >= 5) - M.apply_effect(round(intensity/1.5), PARALYZE) - M.rad_act(intensity*10) - else - user << "The radioactive microlaser is still recharging." - -/obj/item/device/rad_laser/proc/handle_cooldown(cooldown) - spawn(cooldown) - used = 0 - icon_state = "health" - -/obj/item/device/rad_laser/attack_self(mob/user) - ..() - interact(user) - -/obj/item/device/rad_laser/interact(mob/user) - user.set_machine(src) - - var/cooldown = round(max(10,((intensity*8)-(wavelength/2))+(intensity*2))) - var/dat = {" - Radiation Intensity: -- [intensity] ++
- Radiation Wavelength: -- [(wavelength+(intensity*4))] ++
- Laser Cooldown: [cooldown] Seconds
- "} - - var/datum/browser/popup = new(user, "radlaser", "Radioactive Microlaser Interface", 400, 240) - popup.set_content(dat) - popup.open() - -/obj/item/device/rad_laser/Topic(href, href_list) - if(!usr.canUseTopic(src)) - return 1 - - usr.set_machine(src) - - if(href_list["radint"]) - var/amount = text2num(href_list["radint"]) - amount += intensity - intensity = max(1,(min(10,amount))) - - else if(href_list["radwav"]) - var/amount = text2num(href_list["radwav"]) - amount += wavelength - wavelength = max(1,(min(120,amount))) - - attack_self(usr) - add_fingerprint(usr) - return - -/obj/item/device/shadowcloak - name = "cloaker belt" - desc = "Makes you invisible for short periods of time. Recharges in darkness." - icon = 'icons/obj/clothing/belts.dmi' - icon_state = "utilitybelt" - item_state = "utility" - slot_flags = SLOT_BELT - attack_verb = list("whipped", "lashed", "disciplined") - - var/mob/living/carbon/human/user = null - var/charge = 300 - var/max_charge = 300 - var/on = 0 - var/old_alpha = 0 - action_button_name = "Toggle Cloaker" - -/obj/item/device/shadowcloak/ui_action_click() - if(usr.get_item_by_slot(slot_belt) == src) - if(!on) - Activate(usr) - else - Deactivate() - return - -/obj/item/device/shadowcloak/proc/Activate(mob/living/carbon/human/user) - if(!user) - return - user << "You activate [src]." - src.user = user - SSobj.processing |= src - old_alpha = user.alpha - on = 1 - -/obj/item/device/shadowcloak/proc/Deactivate() - user << "You deactivate [src]." - SSobj.processing.Remove(src) - if(user) - user.alpha = old_alpha - on = 0 - user = null - -/obj/item/device/shadowcloak/dropped(mob/user) - if(user && user.get_item_by_slot(slot_belt) != src) - Deactivate() - -/obj/item/device/shadowcloak/process() - if(user.get_item_by_slot(slot_belt) != src) - Deactivate() - return - var/turf/simulated/T = get_turf(src) - if(on) - var/lumcount = T.get_lumcount() - if(lumcount > 3) - charge = max(0,charge - 25)//Quick decrease in light - else - charge = min(max_charge,charge + 50) //Charge in the dark - animate(user,alpha = Clamp(255 - charge,0,255),time = 10) +/* + +Miscellaneous traitor devices + +BATTERER + +RADIOACTIVE MICROLASER + +*/ + +/* + +The Batterer, like a flashbang but 50% chance to knock people over. Can be either very +effective or pretty fucking useless. + +*/ + +/obj/item/device/batterer + name = "mind batterer" + desc = "A strange device with twin antennas." + icon_state = "batterer" + throwforce = 5 + w_class = 1 + throw_speed = 3 + throw_range = 7 + flags = CONDUCT + item_state = "electronic" + origin_tech = "magnets=3;combat=3;syndicate=3" + + var/times_used = 0 //Number of times it's been used. + var/max_uses = 2 + + +/obj/item/device/batterer/attack_self(mob/living/carbon/user, flag = 0, emp = 0) + if(!user) return + if(times_used >= max_uses) + user << "The mind batterer has been burnt out!" + return + + add_logs(user, null, "knocked down people in the area", src) + + for(var/mob/living/carbon/human/M in ultra_range(10, user, 1)) + spawn() + if(prob(50)) + + M.Weaken(rand(10,20)) + if(prob(25)) + M.Stun(rand(5,10)) + M << "You feel a tremendous, paralyzing wave flood your mind." + + else + M << "You feel a sudden, electric jolt travel through your head." + + playsound(src.loc, 'sound/misc/interference.ogg', 50, 1) + user << "You trigger [src]." + times_used += 1 + if(times_used >= max_uses) + icon_state = "battererburnt" + +/* + The radioactive microlaser, a device disguised as a health analyzer used to irradiate people. + + The strength of the radiation is determined by the 'intensity' setting, while the delay between + the scan and the irradiation kicking in is determined by the wavelength. + + Each scan will cause the microlaser to have a brief cooldown period. Higher intensity will increase + the cooldown, while higher wavelength will decrease it. + + Wavelength is also slightly increased by the intensity as well. +*/ + +/obj/item/device/rad_laser + name = "health analyzer" + icon_state = "health" + item_state = "analyzer" + desc = "A hand-held body scanner able to distinguish vital signs of the subject. A strange microlaser is hooked on to the scanning end." + flags = CONDUCT + slot_flags = SLOT_BELT + throwforce = 3 + w_class = 1 + throw_speed = 3 + throw_range = 7 + materials = list(MAT_METAL=400) + origin_tech = "magnets=3;biotech=5;syndicate=3" + var/intensity = 5 // how much damage the radiation does + var/wavelength = 10 // time it takes for the radiation to kick in, in seconds + var/used = 0 // is it cooling down? + +/obj/item/device/rad_laser/attack(mob/living/M, mob/living/user) + if(!used) + add_logs(user, M, "irradiated", src) + user.visible_message("[user] has analyzed [M]'s vitals.") + var/cooldown = round(max(100,(((intensity*8)-(wavelength/2))+(intensity*2))*10)) + used = 1 + icon_state = "health1" + handle_cooldown(cooldown) // splits off to handle the cooldown while handling wavelength + spawn((wavelength+(intensity*4))*10) + if(M) + if(intensity >= 5) + M.apply_effect(round(intensity/1.5), PARALYZE) + M.rad_act(intensity*10) + else + user << "The radioactive microlaser is still recharging." + +/obj/item/device/rad_laser/proc/handle_cooldown(cooldown) + spawn(cooldown) + used = 0 + icon_state = "health" + +/obj/item/device/rad_laser/attack_self(mob/user) + ..() + interact(user) + +/obj/item/device/rad_laser/interact(mob/user) + user.set_machine(src) + + var/cooldown = round(max(10,((intensity*8)-(wavelength/2))+(intensity*2))) + var/dat = {" + Radiation Intensity: -- [intensity] ++
+ Radiation Wavelength: -- [(wavelength+(intensity*4))] ++
+ Laser Cooldown: [cooldown] Seconds
+ "} + + var/datum/browser/popup = new(user, "radlaser", "Radioactive Microlaser Interface", 400, 240) + popup.set_content(dat) + popup.open() + +/obj/item/device/rad_laser/Topic(href, href_list) + if(!usr.canUseTopic(src)) + return 1 + + usr.set_machine(src) + + if(href_list["radint"]) + var/amount = text2num(href_list["radint"]) + amount += intensity + intensity = max(1,(min(10,amount))) + + else if(href_list["radwav"]) + var/amount = text2num(href_list["radwav"]) + amount += wavelength + wavelength = max(1,(min(120,amount))) + + attack_self(usr) + add_fingerprint(usr) + return + +/obj/item/device/shadowcloak + name = "cloaker belt" + desc = "Makes you invisible for short periods of time. Recharges in darkness." + icon = 'icons/obj/clothing/belts.dmi' + icon_state = "utilitybelt" + item_state = "utility" + slot_flags = SLOT_BELT + attack_verb = list("whipped", "lashed", "disciplined") + + var/mob/living/carbon/human/user = null + var/charge = 300 + var/max_charge = 300 + var/on = 0 + var/old_alpha = 0 + action_button_name = "Toggle Cloaker" + +/obj/item/device/shadowcloak/ui_action_click() + if(usr.get_item_by_slot(slot_belt) == src) + if(!on) + Activate(usr) + else + Deactivate() + return + +/obj/item/device/shadowcloak/proc/Activate(mob/living/carbon/human/user) + if(!user) + return + user << "You activate [src]." + src.user = user + SSobj.processing |= src + old_alpha = user.alpha + on = 1 + +/obj/item/device/shadowcloak/proc/Deactivate() + user << "You deactivate [src]." + SSobj.processing.Remove(src) + if(user) + user.alpha = old_alpha + on = 0 + user = null + +/obj/item/device/shadowcloak/dropped(mob/user) + if(user && user.get_item_by_slot(slot_belt) != src) + Deactivate() + +/obj/item/device/shadowcloak/process() + if(user.get_item_by_slot(slot_belt) != src) + Deactivate() + return + var/turf/simulated/T = get_turf(src) + if(on) + var/lumcount = T.get_lumcount() + if(lumcount > 3) + charge = max(0,charge - 25)//Quick decrease in light + else + charge = min(max_charge,charge + 50) //Charge in the dark + animate(user,alpha = Clamp(255 - charge,0,255),time = 10) diff --git a/code/game/objects/items/stacks/sheets/mineral.dm b/code/game/objects/items/stacks/sheets/mineral.dm index 3a378065040..a976498469c 100644 --- a/code/game/objects/items/stacks/sheets/mineral.dm +++ b/code/game/objects/items/stacks/sheets/mineral.dm @@ -1,311 +1,311 @@ -/* -Mineral Sheets - Contains: - - Sandstone - - Diamond - - Snow - - Uranium - - Plasma - - Gold - - Silver - - Clown - Others: - - Adamantine - - Mythril - - Enriched Uranium -*/ - -/* - * Sandstone - */ - -/obj/item/stack/sheet/mineral - icon = 'icons/obj/mining.dmi' - -/obj/item/stack/sheet/mineral/sandstone - name = "sandstone brick" - desc = "This appears to be a combination of both sand and stone." - singular_name = "sandstone brick" - icon_state = "sheet-sandstone" - throw_speed = 3 - throw_range = 5 - origin_tech = "materials=1" - materials = list(MAT_GLASS=MINERAL_MATERIAL_AMOUNT) - sheettype = "sandstone" - -var/global/list/datum/stack_recipe/sandstone_recipes = list ( \ - new/datum/stack_recipe("pile of dirt", /obj/machinery/hydroponics/soil, 3, time = 10, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("sandstone door", /obj/structure/mineral_door/sandstone, 10, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("Assistant Statue", /obj/structure/statue/sandstone/assistant, 5, one_per_turf = 1, on_floor = 1), \ -/* new/datum/stack_recipe("sandstone wall", ???), \ - new/datum/stack_recipe("sandstone floor", ???),\ */ - ) - -/obj/item/stack/sheet/mineral/sandstone/New(var/loc, var/amount=null) - recipes = sandstone_recipes - pixel_x = rand(0,4)-4 - pixel_y = rand(0,4)-4 - ..() - -/* - * Diamond - */ -/obj/item/stack/sheet/mineral/diamond - name = "diamond" - icon_state = "sheet-diamond" - singular_name = "diamond" - force = 5 - throwforce = 5 - w_class = 3 - throw_range = 3 - origin_tech = "materials=6" - sheettype = "diamond" - materials = list(MAT_DIAMOND=MINERAL_MATERIAL_AMOUNT) - -var/global/list/datum/stack_recipe/diamond_recipes = list ( \ - new/datum/stack_recipe("diamond door", /obj/structure/mineral_door/transparent/diamond, 10, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("diamond tile", /obj/item/stack/tile/mineral/diamond, 1, 4, 20), \ - new/datum/stack_recipe("Captain Statue", /obj/structure/statue/diamond/captain, 5, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("AI Hologram Statue", /obj/structure/statue/diamond/ai1, 5, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("AI Core Statue", /obj/structure/statue/diamond/ai2, 5, one_per_turf = 1, on_floor = 1), \ - ) - -/obj/item/stack/sheet/mineral/diamond/New(var/loc, var/amount=null) - recipes = diamond_recipes - pixel_x = rand(0,4)-4 - pixel_y = rand(0,4)-4 - ..() - -/* - * Uranium - */ -/obj/item/stack/sheet/mineral/uranium - name = "uranium" - icon_state = "sheet-uranium" - singular_name = "uranium sheet" - force = 5 - throwforce = 5 - w_class = 3 - throw_speed = 1 - throw_range = 3 - origin_tech = "materials=5" - sheettype = "uranium" - materials = list(MAT_URANIUM=MINERAL_MATERIAL_AMOUNT) - -var/global/list/datum/stack_recipe/uranium_recipes = list ( \ - new/datum/stack_recipe("uranium door", /obj/structure/mineral_door/uranium, 10, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("uranium tile", /obj/item/stack/tile/mineral/uranium, 1, 4, 20), \ - new/datum/stack_recipe("Nuke Statue", /obj/structure/statue/uranium/nuke, 5, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("Engineer Statue", /obj/structure/statue/uranium/eng, 5, one_per_turf = 1, on_floor = 1), \ - ) - -/obj/item/stack/sheet/mineral/uranium/New(var/loc, var/amount=null) - recipes = uranium_recipes - pixel_x = rand(0,4)-4 - pixel_y = rand(0,4)-4 - ..() - -/* - * Plasma - */ -/obj/item/stack/sheet/mineral/plasma - name = "solid plasma" - icon_state = "sheet-plasma" - singular_name = "plasma sheet" - force = 5 - throwforce = 5 - w_class = 3 - throw_speed = 1 - throw_range = 3 - origin_tech = "plasmatech=2;materials=2" - sheettype = "plasma" - burn_state = FLAMMABLE - burntime = 5 - materials = list(MAT_PLASMA=MINERAL_MATERIAL_AMOUNT) - -var/global/list/datum/stack_recipe/plasma_recipes = list ( \ - new/datum/stack_recipe("plasma door", /obj/structure/mineral_door/transparent/plasma, 10, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("plasma tile", /obj/item/stack/tile/mineral/plasma, 1, 4, 20), \ - new/datum/stack_recipe("Scientist Statue", /obj/structure/statue/plasma/scientist, 5, one_per_turf = 1, on_floor = 1), \ - ) - -/obj/item/stack/sheet/mineral/plasma/New(var/loc, var/amount=null) - recipes = plasma_recipes - pixel_x = rand(0,4)-4 - pixel_y = rand(0,4)-4 - ..() - -/obj/item/stack/sheet/mineral/plasma/attackby(obj/item/weapon/W as obj, mob/user as mob, params) - if(W.is_hot() > 300)//If the temperature of the object is over 300, then ignite - message_admins("Plasma sheets ignited by [key_name_admin(user)](?) (FLW) in ([x],[y],[z] - JMP)",0,1) - log_game("Plasma sheets ignited by [key_name(user)] in ([x],[y],[z])") - fire_act() - else - ..() - -/obj/item/stack/sheet/mineral/plasma/fire_act() - atmos_spawn_air(SPAWN_HEAT | SPAWN_TOXINS, amount*10) - qdel(src) - -/* - * Gold - */ -/obj/item/stack/sheet/mineral/gold - name = "gold" - icon_state = "sheet-gold" - singular_name = "gold bar" - force = 5 - throwforce = 5 - w_class = 3 - throw_speed = 1 - throw_range = 3 - origin_tech = "materials=4" - sheettype = "gold" - materials = list(MAT_GOLD=MINERAL_MATERIAL_AMOUNT) - -var/global/list/datum/stack_recipe/gold_recipes = list ( \ - new/datum/stack_recipe("golden door", /obj/structure/mineral_door/gold, 10, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("gold tile", /obj/item/stack/tile/mineral/gold, 1, 4, 20), \ - new/datum/stack_recipe("HoS Statue", /obj/structure/statue/gold/hos, 5, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("HoP Statue", /obj/structure/statue/gold/hop, 5, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("CE Statue", /obj/structure/statue/gold/ce, 5, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("RD Statue", /obj/structure/statue/gold/rd, 5, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("CMO Statue", /obj/structure/statue/gold/cmo, 5, one_per_turf = 1, on_floor = 1), \ - ) - -/obj/item/stack/sheet/mineral/gold/New(var/loc, var/amount=null) - recipes = gold_recipes - pixel_x = rand(0,4)-4 - pixel_y = rand(0,4)-4 - ..() - -/* - * Silver - */ -/obj/item/stack/sheet/mineral/silver - name = "silver" - icon_state = "sheet-silver" - singular_name = "silver bar" - force = 5 - throwforce = 5 - w_class = 3 - throw_speed = 1 - throw_range = 3 - origin_tech = "materials=3" - sheettype = "silver" - materials = list(MAT_SILVER=MINERAL_MATERIAL_AMOUNT) - -var/global/list/datum/stack_recipe/silver_recipes = list ( \ - new/datum/stack_recipe("silver door", /obj/structure/mineral_door/silver, 10, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("silver tile", /obj/item/stack/tile/mineral/silver, 1, 4, 20), \ - new/datum/stack_recipe("Med Officer Statue", /obj/structure/statue/silver/md, 5, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("Janitor Statue", /obj/structure/statue/silver/janitor, 5, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("Sec Officer Statue", /obj/structure/statue/silver/sec, 5, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("Sec Borg Statue", /obj/structure/statue/silver/secborg, 5, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("Med Borg Statue", /obj/structure/statue/silver/medborg, 5, one_per_turf = 1, on_floor = 1), \ - ) - -/obj/item/stack/sheet/mineral/silver/New(var/loc, var/amount=null) - recipes = silver_recipes - pixel_x = rand(0,4)-4 - pixel_y = rand(0,4)-4 - ..() - -/* - * Clown - */ -/obj/item/stack/sheet/mineral/bananium - name = "bananium" - icon_state = "sheet-clown" - singular_name = "bananium sheet" - force = 5 - throwforce = 5 - w_class = 3 - throw_speed = 1 - throw_range = 3 - origin_tech = "materials=4" - sheettype = "clown" - materials = list(MAT_BANANIUM=MINERAL_MATERIAL_AMOUNT) - -var/global/list/datum/stack_recipe/clown_recipes = list ( \ - new/datum/stack_recipe("bananium tile", /obj/item/stack/tile/mineral/bananium, 1, 4, 20), \ - new/datum/stack_recipe("Clown Statue", /obj/structure/statue/bananium/clown, 5, one_per_turf = 1, on_floor = 1), \ - ) - -/obj/item/stack/sheet/mineral/bananium/New(var/loc, var/amount=null) - recipes = clown_recipes - pixel_x = rand(0,4)-4 - pixel_y = rand(0,4)-4 - ..() - -/* - * Snow - */ -/obj/item/stack/sheet/mineral/snow - name = "snow" - icon_state = "sheet-snow" - singular_name = "snow block" - force = 1 - throwforce = 2 - w_class = 3 - throw_speed = 1 - throw_range = 3 - origin_tech = "materials=1" - sheettype = "snow" - -var/global/list/datum/stack_recipe/snow_recipes = list ( \ - new/datum/stack_recipe("Snow Wall",/turf/simulated/wall/mineral/snow, 5, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("Snowman", /obj/structure/statue/snow/snowman, 5, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("Snowball", /obj/item/toy/snowball, 1), \ - ) - -/obj/item/stack/sheet/mineral/snow/New(var/loc, var/amount=null) - recipes = snow_recipes - pixel_x = rand(0,4)-4 - pixel_y = rand(0,4)-4 - ..() - -/****************************** Others ****************************/ - -/* - * Enriched Uranium - */ -/obj/item/stack/sheet/mineral/enruranium - name = "enriched uranium" - icon_state = "sheet-enruranium" - singular_name = "enriched uranium sheet" - force = 5 - throwforce = 5 - w_class = 3 - throw_speed = 1 - throw_range = 3 - origin_tech = "materials=6" - materials = list(MAT_URANIUM=3000) - -/* - * Adamantine - */ -/obj/item/stack/sheet/mineral/adamantine - name = "adamantine" - icon_state = "sheet-adamantine" - singular_name = "adamantine sheet" - force = 5 - throwforce = 5 - w_class = 3 - throw_speed = 1 - throw_range = 3 - origin_tech = "materials=4" - -/* - * Mythril - */ -/obj/item/stack/sheet/mineral/mythril - name = "mythril" - icon_state = "sheet-mythril" - singular_name = "mythril sheet" - force = 5 - throwforce = 5 - w_class = 3 - throw_speed = 1 - throw_range = 3 +/* +Mineral Sheets + Contains: + - Sandstone + - Diamond + - Snow + - Uranium + - Plasma + - Gold + - Silver + - Clown + Others: + - Adamantine + - Mythril + - Enriched Uranium +*/ + +/* + * Sandstone + */ + +/obj/item/stack/sheet/mineral + icon = 'icons/obj/mining.dmi' + +/obj/item/stack/sheet/mineral/sandstone + name = "sandstone brick" + desc = "This appears to be a combination of both sand and stone." + singular_name = "sandstone brick" + icon_state = "sheet-sandstone" + throw_speed = 3 + throw_range = 5 + origin_tech = "materials=1" + materials = list(MAT_GLASS=MINERAL_MATERIAL_AMOUNT) + sheettype = "sandstone" + +var/global/list/datum/stack_recipe/sandstone_recipes = list ( \ + new/datum/stack_recipe("pile of dirt", /obj/machinery/hydroponics/soil, 3, time = 10, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("sandstone door", /obj/structure/mineral_door/sandstone, 10, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("Assistant Statue", /obj/structure/statue/sandstone/assistant, 5, one_per_turf = 1, on_floor = 1), \ +/* new/datum/stack_recipe("sandstone wall", ???), \ + new/datum/stack_recipe("sandstone floor", ???),\ */ + ) + +/obj/item/stack/sheet/mineral/sandstone/New(var/loc, var/amount=null) + recipes = sandstone_recipes + pixel_x = rand(0,4)-4 + pixel_y = rand(0,4)-4 + ..() + +/* + * Diamond + */ +/obj/item/stack/sheet/mineral/diamond + name = "diamond" + icon_state = "sheet-diamond" + singular_name = "diamond" + force = 5 + throwforce = 5 + w_class = 3 + throw_range = 3 + origin_tech = "materials=6" + sheettype = "diamond" + materials = list(MAT_DIAMOND=MINERAL_MATERIAL_AMOUNT) + +var/global/list/datum/stack_recipe/diamond_recipes = list ( \ + new/datum/stack_recipe("diamond door", /obj/structure/mineral_door/transparent/diamond, 10, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("diamond tile", /obj/item/stack/tile/mineral/diamond, 1, 4, 20), \ + new/datum/stack_recipe("Captain Statue", /obj/structure/statue/diamond/captain, 5, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("AI Hologram Statue", /obj/structure/statue/diamond/ai1, 5, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("AI Core Statue", /obj/structure/statue/diamond/ai2, 5, one_per_turf = 1, on_floor = 1), \ + ) + +/obj/item/stack/sheet/mineral/diamond/New(var/loc, var/amount=null) + recipes = diamond_recipes + pixel_x = rand(0,4)-4 + pixel_y = rand(0,4)-4 + ..() + +/* + * Uranium + */ +/obj/item/stack/sheet/mineral/uranium + name = "uranium" + icon_state = "sheet-uranium" + singular_name = "uranium sheet" + force = 5 + throwforce = 5 + w_class = 3 + throw_speed = 1 + throw_range = 3 + origin_tech = "materials=5" + sheettype = "uranium" + materials = list(MAT_URANIUM=MINERAL_MATERIAL_AMOUNT) + +var/global/list/datum/stack_recipe/uranium_recipes = list ( \ + new/datum/stack_recipe("uranium door", /obj/structure/mineral_door/uranium, 10, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("uranium tile", /obj/item/stack/tile/mineral/uranium, 1, 4, 20), \ + new/datum/stack_recipe("Nuke Statue", /obj/structure/statue/uranium/nuke, 5, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("Engineer Statue", /obj/structure/statue/uranium/eng, 5, one_per_turf = 1, on_floor = 1), \ + ) + +/obj/item/stack/sheet/mineral/uranium/New(var/loc, var/amount=null) + recipes = uranium_recipes + pixel_x = rand(0,4)-4 + pixel_y = rand(0,4)-4 + ..() + +/* + * Plasma + */ +/obj/item/stack/sheet/mineral/plasma + name = "solid plasma" + icon_state = "sheet-plasma" + singular_name = "plasma sheet" + force = 5 + throwforce = 5 + w_class = 3 + throw_speed = 1 + throw_range = 3 + origin_tech = "plasmatech=2;materials=2" + sheettype = "plasma" + burn_state = FLAMMABLE + burntime = 5 + materials = list(MAT_PLASMA=MINERAL_MATERIAL_AMOUNT) + +var/global/list/datum/stack_recipe/plasma_recipes = list ( \ + new/datum/stack_recipe("plasma door", /obj/structure/mineral_door/transparent/plasma, 10, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("plasma tile", /obj/item/stack/tile/mineral/plasma, 1, 4, 20), \ + new/datum/stack_recipe("Scientist Statue", /obj/structure/statue/plasma/scientist, 5, one_per_turf = 1, on_floor = 1), \ + ) + +/obj/item/stack/sheet/mineral/plasma/New(var/loc, var/amount=null) + recipes = plasma_recipes + pixel_x = rand(0,4)-4 + pixel_y = rand(0,4)-4 + ..() + +/obj/item/stack/sheet/mineral/plasma/attackby(obj/item/weapon/W as obj, mob/user as mob, params) + if(W.is_hot() > 300)//If the temperature of the object is over 300, then ignite + message_admins("Plasma sheets ignited by [key_name_admin(user)](?) (FLW) in ([x],[y],[z] - JMP)",0,1) + log_game("Plasma sheets ignited by [key_name(user)] in ([x],[y],[z])") + fire_act() + else + ..() + +/obj/item/stack/sheet/mineral/plasma/fire_act() + atmos_spawn_air(SPAWN_HEAT | SPAWN_TOXINS, amount*10) + qdel(src) + +/* + * Gold + */ +/obj/item/stack/sheet/mineral/gold + name = "gold" + icon_state = "sheet-gold" + singular_name = "gold bar" + force = 5 + throwforce = 5 + w_class = 3 + throw_speed = 1 + throw_range = 3 + origin_tech = "materials=4" + sheettype = "gold" + materials = list(MAT_GOLD=MINERAL_MATERIAL_AMOUNT) + +var/global/list/datum/stack_recipe/gold_recipes = list ( \ + new/datum/stack_recipe("golden door", /obj/structure/mineral_door/gold, 10, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("gold tile", /obj/item/stack/tile/mineral/gold, 1, 4, 20), \ + new/datum/stack_recipe("HoS Statue", /obj/structure/statue/gold/hos, 5, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("HoP Statue", /obj/structure/statue/gold/hop, 5, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("CE Statue", /obj/structure/statue/gold/ce, 5, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("RD Statue", /obj/structure/statue/gold/rd, 5, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("CMO Statue", /obj/structure/statue/gold/cmo, 5, one_per_turf = 1, on_floor = 1), \ + ) + +/obj/item/stack/sheet/mineral/gold/New(var/loc, var/amount=null) + recipes = gold_recipes + pixel_x = rand(0,4)-4 + pixel_y = rand(0,4)-4 + ..() + +/* + * Silver + */ +/obj/item/stack/sheet/mineral/silver + name = "silver" + icon_state = "sheet-silver" + singular_name = "silver bar" + force = 5 + throwforce = 5 + w_class = 3 + throw_speed = 1 + throw_range = 3 + origin_tech = "materials=3" + sheettype = "silver" + materials = list(MAT_SILVER=MINERAL_MATERIAL_AMOUNT) + +var/global/list/datum/stack_recipe/silver_recipes = list ( \ + new/datum/stack_recipe("silver door", /obj/structure/mineral_door/silver, 10, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("silver tile", /obj/item/stack/tile/mineral/silver, 1, 4, 20), \ + new/datum/stack_recipe("Med Officer Statue", /obj/structure/statue/silver/md, 5, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("Janitor Statue", /obj/structure/statue/silver/janitor, 5, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("Sec Officer Statue", /obj/structure/statue/silver/sec, 5, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("Sec Borg Statue", /obj/structure/statue/silver/secborg, 5, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("Med Borg Statue", /obj/structure/statue/silver/medborg, 5, one_per_turf = 1, on_floor = 1), \ + ) + +/obj/item/stack/sheet/mineral/silver/New(var/loc, var/amount=null) + recipes = silver_recipes + pixel_x = rand(0,4)-4 + pixel_y = rand(0,4)-4 + ..() + +/* + * Clown + */ +/obj/item/stack/sheet/mineral/bananium + name = "bananium" + icon_state = "sheet-clown" + singular_name = "bananium sheet" + force = 5 + throwforce = 5 + w_class = 3 + throw_speed = 1 + throw_range = 3 + origin_tech = "materials=4" + sheettype = "clown" + materials = list(MAT_BANANIUM=MINERAL_MATERIAL_AMOUNT) + +var/global/list/datum/stack_recipe/clown_recipes = list ( \ + new/datum/stack_recipe("bananium tile", /obj/item/stack/tile/mineral/bananium, 1, 4, 20), \ + new/datum/stack_recipe("Clown Statue", /obj/structure/statue/bananium/clown, 5, one_per_turf = 1, on_floor = 1), \ + ) + +/obj/item/stack/sheet/mineral/bananium/New(var/loc, var/amount=null) + recipes = clown_recipes + pixel_x = rand(0,4)-4 + pixel_y = rand(0,4)-4 + ..() + +/* + * Snow + */ +/obj/item/stack/sheet/mineral/snow + name = "snow" + icon_state = "sheet-snow" + singular_name = "snow block" + force = 1 + throwforce = 2 + w_class = 3 + throw_speed = 1 + throw_range = 3 + origin_tech = "materials=1" + sheettype = "snow" + +var/global/list/datum/stack_recipe/snow_recipes = list ( \ + new/datum/stack_recipe("Snow Wall",/turf/simulated/wall/mineral/snow, 5, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("Snowman", /obj/structure/statue/snow/snowman, 5, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("Snowball", /obj/item/toy/snowball, 1), \ + ) + +/obj/item/stack/sheet/mineral/snow/New(var/loc, var/amount=null) + recipes = snow_recipes + pixel_x = rand(0,4)-4 + pixel_y = rand(0,4)-4 + ..() + +/****************************** Others ****************************/ + +/* + * Enriched Uranium + */ +/obj/item/stack/sheet/mineral/enruranium + name = "enriched uranium" + icon_state = "sheet-enruranium" + singular_name = "enriched uranium sheet" + force = 5 + throwforce = 5 + w_class = 3 + throw_speed = 1 + throw_range = 3 + origin_tech = "materials=6" + materials = list(MAT_URANIUM=3000) + +/* + * Adamantine + */ +/obj/item/stack/sheet/mineral/adamantine + name = "adamantine" + icon_state = "sheet-adamantine" + singular_name = "adamantine sheet" + force = 5 + throwforce = 5 + w_class = 3 + throw_speed = 1 + throw_range = 3 + origin_tech = "materials=4" + +/* + * Mythril + */ +/obj/item/stack/sheet/mineral/mythril + name = "mythril" + icon_state = "sheet-mythril" + singular_name = "mythril sheet" + force = 5 + throwforce = 5 + w_class = 3 + throw_speed = 1 + throw_range = 3 origin_tech = "materials=4" \ No newline at end of file diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index 06f2122fc3c..61e55c97791 100755 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -1,1454 +1,1454 @@ -/* Toys! - * Contains - * Balloons - * Fake singularity - * Toy gun - * Toy crossbow - * Toy swords - * Crayons - * Snap pops - * Mech prizes - * AI core prizes - * Cards - * Toy nuke - * Fake meteor - * Carp plushie - * Foam armblade - * Toy big red button - * Beach ball - * Toy xeno - * Kitty toys! - * Snowballs - */ - - -/obj/item/toy - throwforce = 0 - throw_speed = 3 - throw_range = 7 - force = 0 - - -/* - * Balloons - */ -/obj/item/toy/balloon - name = "water balloon" - desc = "A translucent balloon. There's nothing in it." - icon = 'icons/obj/toy.dmi' - icon_state = "waterballoon-e" - item_state = "balloon-empty" - -/obj/item/toy/balloon/New() - create_reagents(10) - -/obj/item/toy/balloon/attack(mob/living/carbon/human/M, mob/user) - return - -/obj/item/toy/balloon/afterattack(atom/A as mob|obj, mob/user, proximity) - if(!proximity) return - if (istype(A, /obj/structure/reagent_dispensers)) - var/obj/structure/reagent_dispensers/RD = A - if(RD.reagents.total_volume <= 0) - user << "[RD] is empty." - else if(reagents.total_volume >= 10) - user << "[src] is full." - else - A.reagents.trans_to(src, 10) - user << "You fill the balloon with the contents of [A]." - desc = "A translucent balloon with some form of liquid sloshing around in it." - update_icon() - -/obj/item/toy/balloon/attackby(obj/O, mob/user, params) - if(istype(O, /obj/item/weapon/reagent_containers/glass)) - if(O.reagents) - if(O.reagents.total_volume <= 0) - user << "[O] is empty." - else if(reagents.total_volume >= 10) - user << "[src] is full." - else - desc = "A translucent balloon with some form of liquid sloshing around in it." - user << "You fill the balloon with the contents of [O]." - O.reagents.trans_to(src, 10) - update_icon() - -/obj/item/toy/balloon/throw_impact(atom/hit_atom) - if(!..()) //was it caught by a mob? - if(reagents.total_volume >= 1) - visible_message("[src] bursts!","You hear a pop and a splash.") - reagents.reaction(get_turf(hit_atom)) - for(var/atom/A in get_turf(hit_atom)) - reagents.reaction(A) - icon_state = "burst" - qdel(src) - -/obj/item/toy/balloon/update_icon() - if(src.reagents.total_volume >= 1) - icon_state = "waterballoon" - item_state = "balloon" - else - icon_state = "waterballoon-e" - item_state = "balloon-empty" - -/obj/item/toy/syndicateballoon - name = "syndicate balloon" - desc = "There is a tag on the back that reads \"FUK NT!11!\"." - throwforce = 0 - throw_speed = 3 - throw_range = 7 - force = 0 - icon = 'icons/obj/weapons.dmi' - icon_state = "syndballoon" - item_state = "syndballoon" - w_class = 4 - -/* - * Fake singularity - */ -/obj/item/toy/spinningtoy - name = "gravitational singularity" - desc = "\"Singulo\" brand spinning toy." - icon = 'icons/obj/singularity.dmi' - icon_state = "singularity_s1" - -/* - * Toy gun: Why isnt this an /obj/item/weapon/gun? - */ -/obj/item/toy/gun - name = "cap gun" - desc = "Looks almost like the real thing! Ages 8 and up. Please recycle in an autolathe when you're out of caps." - icon = 'icons/obj/guns/projectile.dmi' - icon_state = "revolver" - item_state = "gun" - lefthand_file = 'icons/mob/inhands/guns_lefthand.dmi' - righthand_file = 'icons/mob/inhands/guns_righthand.dmi' - flags = CONDUCT - slot_flags = SLOT_BELT - w_class = 3 - materials = list(MAT_METAL=10, MAT_GLASS=10) - attack_verb = list("struck", "pistol whipped", "hit", "bashed") - var/bullets = 7 - -/obj/item/toy/gun/examine(mob/user) - ..() - user << "There [bullets == 1 ? "is" : "are"] [bullets] cap\s left." - -/obj/item/toy/gun/attackby(obj/item/toy/ammo/gun/A, mob/user, params) - - if (istype(A, /obj/item/toy/ammo/gun)) - if (src.bullets >= 7) - user << "It's already fully loaded!" - return 1 - if (A.amount_left <= 0) - user << "There are no more caps!" - return 1 - if (A.amount_left < (7 - src.bullets)) - src.bullets += A.amount_left - user << text("You reload [] cap\s.", A.amount_left) - A.amount_left = 0 - else - user << text("You reload [] cap\s.", 7 - src.bullets) - A.amount_left -= 7 - src.bullets - src.bullets = 7 - A.update_icon() - return 1 - return - -/obj/item/toy/gun/afterattack(atom/target as mob|obj|turf|area, mob/user, flag) - if (flag) - return - if (!(istype(usr, /mob/living/carbon/human) || ticker) && ticker.mode.name != "monkey") - usr << "You don't have the dexterity to do this!" - return - src.add_fingerprint(user) - if (src.bullets < 1) - user.show_message("*click*", 2) - playsound(user, 'sound/weapons/empty.ogg', 100, 1) - return - playsound(user, 'sound/weapons/Gunshot.ogg', 100, 1) - src.bullets-- - user.visible_message("[user] fires [src] at [target]!", \ - "You fire [src] at [target]!", \ - "You hear a gunshot!") - -/obj/item/toy/ammo/gun - name = "capgun ammo" - desc = "Make sure to recyle the box in an autolathe when it gets empty." - icon = 'icons/obj/ammo.dmi' - icon_state = "357OLD-7" - w_class = 1 - materials = list(MAT_METAL=10, MAT_GLASS=10) - var/amount_left = 7 - -/obj/item/toy/ammo/gun/update_icon() - src.icon_state = text("357OLD-[]", src.amount_left) - -/obj/item/toy/ammo/gun/examine(mob/user) - ..() - user << "There [amount_left == 1 ? "is" : "are"] [amount_left] cap\s left." - -/* - * Toy swords - */ -/obj/item/toy/sword - name = "toy sword" - desc = "A cheap, plastic replica of an energy sword. Realistic sounds! Ages 8 and up." - icon = 'icons/obj/weapons.dmi' - icon_state = "sword0" - item_state = "sword0" - var/active = 0 - w_class = 2 - flags = NOSHIELD - attack_verb = list("attacked", "struck", "hit") - var/hacked = 0 - -/obj/item/toy/sword/attack_self(mob/user) - active = !( active ) - if (active) - user << "You extend the plastic blade with a quick flick of your wrist." - playsound(user, 'sound/weapons/saberon.ogg', 20, 1) - if(hacked) - icon_state = "swordrainbow" - item_state = "swordrainbow" - else - icon_state = "swordblue" - item_state = "swordblue" - w_class = 4 - else - user << "You push the plastic blade back down into the handle." - playsound(user, 'sound/weapons/saberoff.ogg', 20, 1) - icon_state = "sword0" - item_state = "sword0" - w_class = 2 - add_fingerprint(user) - return - -// Copied from /obj/item/weapon/melee/energy/sword/attackby -/obj/item/toy/sword/attackby(obj/item/weapon/W, mob/living/user, params) - ..() - if(istype(W, /obj/item/toy/sword)) - if(W == src) - user << "You try to attach the end of the plastic sword to... itself. You're not very smart, are you?" - if(ishuman(user)) - user.adjustBrainLoss(10) - else if((W.flags & NODROP) || (flags & NODROP)) - user << "\the [flags & NODROP ? src : W] is stuck to your hand, you can't attach it to \the [flags & NODROP ? W : src]!" - else - user << "You attach the ends of the two plastic swords, making a single double-bladed toy! You're fake-cool." - var/obj/item/weapon/twohanded/dualsaber/toy/newSaber = new /obj/item/weapon/twohanded/dualsaber/toy(user.loc) - if(hacked) // That's right, we'll only check the "original" "sword". - newSaber.hacked = 1 - newSaber.item_color = "rainbow" - user.unEquip(W) - user.unEquip(src) - qdel(W) - qdel(src) - else if(istype(W, /obj/item/device/multitool)) - if(hacked == 0) - hacked = 1 - item_color = "rainbow" - user << "RNBW_ENGAGE" - - if(active) - icon_state = "swordrainbow" - // Updating overlays, copied from welder code. - // I tried calling attack_self twice, which looked cool, except it somehow didn't update the overlays!! - if(user.r_hand == src) - user.update_inv_r_hand(0) - else if(user.l_hand == src) - user.update_inv_l_hand(0) - else - user << "It's already fabulous!" - -/* - * Foam armblade - */ -/obj/item/toy/foamblade - name = "foam armblade" - desc = "it says \"Sternside Changs #1 fan\" on it. " - icon = 'icons/obj/toy.dmi' - icon_state = "foamblade" - item_state = "arm_blade" - attack_verb = list("pricked", "absorbed", "gored") - w_class = 2 - burn_state = FLAMMABLE - - -/* - * Subtype of Double-Bladed Energy Swords - */ -/obj/item/weapon/twohanded/dualsaber/toy - name = "double-bladed toy sword" - desc = "A cheap, plastic replica of TWO energy swords. Double the fun!" - force = 0 - throwforce = 0 - throw_speed = 3 - throw_range = 5 - force_unwielded = 0 - force_wielded = 0 - origin_tech = null - attack_verb = list("attacked", "struck", "hit") - -/obj/item/weapon/twohanded/dualsaber/toy/hit_reaction() - return 0 - -/obj/item/weapon/twohanded/dualsaber/toy/IsReflect()//Stops Toy Dualsabers from reflecting energy projectiles - return 0 - -/obj/item/toy/katana - name = "replica katana" - desc = "Woefully underpowered in D20." - icon = 'icons/obj/weapons.dmi' - icon_state = "katana" - item_state = "katana" - flags = CONDUCT - slot_flags = SLOT_BELT | SLOT_BACK - force = 5 - throwforce = 5 - w_class = 3 - attack_verb = list("attacked", "slashed", "stabbed", "sliced") - hitsound = 'sound/weapons/bladeslice.ogg' - -/* - * Crayons - */ - -/obj/item/toy/crayon - name = "crayon" - desc = "A colourful crayon. Looks tasty. Mmmm..." - icon = 'icons/obj/crayons.dmi' - icon_state = "crayonred" - w_class = 1 - attack_verb = list("attacked", "coloured") - var/paint_color = "#FF0000" //RGB - var/drawtype = "rune" - var/list/graffiti = list("amyjon","face","matt","revolution","engie","guy","end","dwarf","uboa","body","cyka","arrow","star","poseur tag") - var/list/letters = 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") - var/list/numerals = list("0","1","2","3","4","5","6","7","8","9") - var/list/oriented = list("arrow","body") // These turn to face the same way as the drawer - var/uses = 30 //-1 or less for unlimited uses - var/instant = 0 - var/colourName = "red" //for updateIcon purposes - var/dat - var/list/validSurfaces = list(/turf/simulated/floor) - var/gang = 0 //For marking territory - var/edible = 1 - -/obj/item/toy/crayon/suicide_act(mob/user) - user.visible_message("[user] is jamming the [src.name] up \his nose and into \his brain. It looks like \he's trying to commit suicide.") - return (BRUTELOSS|OXYLOSS) - -/obj/item/toy/crayon/New() - ..() - name = "[colourName] crayon" //Makes crayons identifiable in things like grinders - drawtype = pick(pick(graffiti), pick(letters), "rune[rand(1,6)]") - if(config) - if(config.mutant_races == 1) - graffiti |= "antilizard" - graffiti |= "prolizard" - -/obj/item/toy/crayon/initialize() - if(config.mutant_races == 1) - graffiti |= "antilizard" - graffiti |= "prolizard" - -/obj/item/toy/crayon/attack_self(mob/living/user) - update_window(user) - -/obj/item/toy/crayon/proc/update_window(mob/living/user) - dat += "

Currently selected: [drawtype]


" - dat += "Random letterPick letter/number" - dat += "
" - dat += "

Runes:


" - dat += "Random rune" - for(var/i = 1; i <= 6; i++) - dat += "Rune[i]" - if(!((i + 1) % 3)) //3 buttons in a row - dat += "
" - dat += "
" - graffiti.Find() - dat += "

Graffiti:


" - dat += "Random graffiti" - var/c = 1 - for(var/T in graffiti) - dat += "[T]" - if(!((c + 1) % 3)) //3 buttons in a row - dat += "
" - c++ - dat += "
" - var/datum/browser/popup = new(user, "crayon", name, 300, 500) - popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) - popup.open() - dat = "" - -/obj/item/toy/crayon/Topic(href, href_list, hsrc) - var/temp = "a" - switch(href_list["type"]) - if("random_letter") - temp = pick(letters) - if("letter") - temp = input("Choose what to write.", "Scribbles") in (letters|numerals) - if("random_rune") - temp = "rune[rand(1,6)]" - if("random_graffiti") - temp = pick(graffiti) - else - temp = href_list["type"] - if ((usr.restrained() || usr.stat || usr.get_active_hand() != src)) - return - drawtype = temp - update_window(usr) - -/obj/item/toy/crayon/afterattack(atom/target, mob/user, proximity) - if(!proximity || !check_allowed_items(target)) return - if(!uses) - user << "There is no more of [src.name] left!" - if(!instant) - qdel(src) - return - if(istype(target, /obj/effect/decal/cleanable)) - target = target.loc - if(is_type_in_list(target,validSurfaces)) - var/temp = "rune" - if(letters.Find(drawtype)) - temp = "letter" - else if(graffiti.Find(drawtype)) - temp = "graffiti" - else if(numerals.Find(drawtype)) - temp = "number" - - ////////////////////////// GANG FUNCTIONS - var/area/territory - var/gangID - if(gang) - //Determine gang affiliation - gangID = user.mind.gang_datum - - //Check area validity. Reject space, player-created areas, and non-station z-levels. - if(gangID) - territory = get_area(target) - if(territory && (territory.z == ZLEVEL_STATION) && territory.valid_territory) - //Check if this area is already tagged by a gang - if(!(locate(/obj/effect/decal/cleanable/crayon/gang) in target)) //Ignore the check if the tile being sprayed has a gang tag - if(territory_claimed(territory, user)) - return - if(locate(/obj/machinery/power/apc) in (user.loc.contents | target.contents)) - user << "You cannot tag here." - return - else - user << "[territory] is unsuitable for tagging." - return - ///////////////////////////////////////// - - var/graf_rot - if(oriented.Find(drawtype)) - switch(user.dir) - if(EAST) - graf_rot = 90 - if(SOUTH) - graf_rot = 180 - if(WEST) - graf_rot = 270 - else - graf_rot = 0 - - user << "You start [instant ? "spraying" : "drawing"] a [temp] on the [target.name]..." - if(instant) - playsound(user.loc, 'sound/effects/spray.ogg', 5, 1, 5) - if((instant>0) || do_after(user, 50, target = target)) - - //Gang functions - if(gangID) - //Delete any old markings on this tile, including other gang tags - if(!(locate(/obj/effect/decal/cleanable/crayon/gang) in target)) //Ignore the check if the tile being sprayed has a gang tag - if(territory_claimed(territory, user)) - return - for(var/obj/effect/decal/cleanable/crayon/old_marking in target) - qdel(old_marking) - new /obj/effect/decal/cleanable/crayon/gang(target,gangID,"graffiti",graf_rot) - user << "You tagged [territory] for your gang!" - - else - new /obj/effect/decal/cleanable/crayon(target,paint_color,drawtype,temp,graf_rot) - - user << "You finish [instant ? "spraying" : "drawing"] \the [temp]." - if(instant<0) - playsound(user.loc, 'sound/effects/spray.ogg', 5, 1, 5) - if(uses < 0) - return - uses = max(0,uses-1) - if(!uses) - user << "There is no more of [src.name] left!" - if(!instant) - qdel(src) - return - -/obj/item/toy/crayon/attack(mob/M, mob/user) - if(edible && (M == user)) - user << "You take a bite of the [src.name]. Delicious!" - user.nutrition += 5 - if(uses < 0) - return - uses = max(0,uses-5) - if(!uses) - user << "There is no more of [src.name] left!" - qdel(src) - else - ..() - -/obj/item/toy/crayon/proc/territory_claimed(area/territory,mob/user) - var/occupying_gang - for(var/datum/gang/G in ticker.mode.gangs) - if(territory.type in (G.territory|G.territory_new)) - occupying_gang = G.name - break - if(occupying_gang) - user << "[territory] has already been tagged by the [occupying_gang] gang! You must get rid of or spray over the old tag first!" - return 1 - return 0 - -/* - * Snap pops - */ - -/obj/item/toy/snappop - name = "snap pop" - desc = "Wow!" - icon = 'icons/obj/toy.dmi' - icon_state = "snappop" - w_class = 1 - -/obj/item/toy/snappop/proc/pop_burst() - var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread - s.set_up(3, 1, src) - s.start() - new /obj/effect/decal/cleanable/ash(loc) - visible_message("The [src.name] explodes!","You hear a snap!") - playsound(src, 'sound/effects/snap.ogg', 50, 1) - qdel(src) - -/obj/item/toy/snappop/fire_act() - pop_burst() - return - -/obj/item/toy/snappop/throw_impact(atom/hit_atom) - if(!..()) - pop_burst() - - -/obj/item/toy/snappop/Crossed(H as mob|obj) - if((ishuman(H))) //i guess carp and shit shouldn't set them off - var/mob/living/carbon/M = H - if(M.m_intent == "run") - M << "You step on the snap pop!" - - var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread - s.set_up(2, 0, src) - s.start() - new /obj/effect/decal/cleanable/ash(src.loc) - src.visible_message("The [src.name] explodes!","You hear a snap!") - playsound(src, 'sound/effects/snap.ogg', 50, 1) - qdel(src) - -/* - * Mech prizes - */ -/obj/item/toy/prize - icon = 'icons/obj/toy.dmi' - icon_state = "ripleytoy" - var/cooldown = 0 - var/quiet = 0 - -//all credit to skasi for toy mech fun ideas -/obj/item/toy/prize/attack_self(mob/user) - if(!cooldown) - user << "You play with [src]." - cooldown = 1 - spawn(30) cooldown = 0 - if (!quiet) - playsound(user, 'sound/mecha/mechstep.ogg', 20, 1) - return - ..() - -/obj/item/toy/prize/attack_hand(mob/user) - if(loc == user) - if(!cooldown) - user << "You play with [src]." - cooldown = 1 - spawn(30) cooldown = 0 - if (!quiet) - playsound(user, 'sound/mecha/mechturn.ogg', 20, 1) - return - ..() - -/obj/item/toy/prize/ripley - name = "toy Ripley" - desc = "Mini-Mecha action figure! Collect them all! 1/12." - -/obj/item/toy/prize/fireripley - name = "toy firefighting Ripley" - desc = "Mini-Mecha action figure! Collect them all! 2/12." - icon_state = "fireripleytoy" - -/obj/item/toy/prize/deathripley - name = "toy deathsquad Ripley" - desc = "Mini-Mecha action figure! Collect them all! 3/12." - icon_state = "deathripleytoy" - -/obj/item/toy/prize/gygax - name = "toy Gygax" - desc = "Mini-Mecha action figure! Collect them all! 4/12." - icon_state = "gygaxtoy" - -/obj/item/toy/prize/durand - name = "toy Durand" - desc = "Mini-Mecha action figure! Collect them all! 5/12." - icon_state = "durandprize" - -/obj/item/toy/prize/honk - name = "toy H.O.N.K." - desc = "Mini-Mecha action figure! Collect them all! 6/12." - icon_state = "honkprize" - -/obj/item/toy/prize/marauder - name = "toy Marauder" - desc = "Mini-Mecha action figure! Collect them all! 7/12." - icon_state = "marauderprize" - -/obj/item/toy/prize/seraph - name = "toy Seraph" - desc = "Mini-Mecha action figure! Collect them all! 8/12." - icon_state = "seraphprize" - -/obj/item/toy/prize/mauler - name = "toy Mauler" - desc = "Mini-Mecha action figure! Collect them all! 9/12." - icon_state = "maulerprize" - -/obj/item/toy/prize/odysseus - name = "toy Odysseus" - desc = "Mini-Mecha action figure! Collect them all! 10/12." - icon_state = "odysseusprize" - -/obj/item/toy/prize/phazon - name = "toy Phazon" - desc = "Mini-Mecha action figure! Collect them all! 11/12." - icon_state = "phazonprize" - -/obj/item/toy/prize/reticence - name = "toy Reticence" - desc = "Mini-Mecha action figure! Collect them all! 12/12." - icon_state = "reticenceprize" - quiet = 1 - -/* - * AI core prizes - */ -/obj/item/toy/AI - name = "toy AI" - desc = "A little toy model AI core with real law announcing action!" - icon = 'icons/obj/toy.dmi' - icon_state = "AI" - w_class = 2 - var/cooldown = 0 - -/obj/item/toy/AI/attack_self(mob/user) - if(!cooldown) //for the sanity of everyone - var/message = generate_ion_law() - user << "You press the button on [src]." - playsound(user, 'sound/machines/click.ogg', 20, 1) - src.loc.visible_message("\icon[src] [message]") - cooldown = 1 - spawn(30) cooldown = 0 - return - ..() - -/obj/item/toy/owl - name = "owl action figure" - desc = "An action figure modeled after 'The Owl', defender of justice." - icon = 'icons/obj/toy.dmi' - icon_state = "owlprize" - w_class = 2 - var/cooldown = 0 - -/obj/item/toy/owl/attack_self(mob/user) - if(!cooldown) //for the sanity of everyone - var/message = pick("You won't get away this time, Griffin!", "Stop right there, criminal!", "Hoot! Hoot!", "I am the night!") - user << "You pull the string on the [src]." - playsound(user, 'sound/machines/click.ogg', 20, 1) - src.loc.visible_message("\icon[src] [message]") - cooldown = 1 - spawn(30) cooldown = 0 - return - ..() - -/obj/item/toy/griffin - name = "griffin action figure" - desc = "An action figure modeled after 'The Griffin', criminal mastermind." - icon = 'icons/obj/toy.dmi' - icon_state = "griffinprize" - w_class = 2 - var/cooldown = 0 - -/obj/item/toy/griffin/attack_self(mob/user) - if(!cooldown) //for the sanity of everyone - var/message = pick("You can't stop me, Owl!", "My plan is flawless! The vault is mine!", "Caaaawwww!", "You will never catch me!") - user << "You pull the string on the [src]." - playsound(user, 'sound/machines/click.ogg', 20, 1) - src.loc.visible_message("\icon[src] [message]") - cooldown = 1 - spawn(30) cooldown = 0 - return - ..() - - -/* -|| A Deck of Cards for playing various games of chance || -*/ - - - -/obj/item/toy/cards - burn_state = FLAMMABLE - burntime = 5 - var/parentdeck = null - var/deckstyle = "nanotrasen" - var/card_hitsound = null - var/card_force = 0 - var/card_throwforce = 0 - var/card_throw_speed = 3 - var/card_throw_range = 7 - var/list/card_attack_verb = list("attacked") - -/obj/item/toy/cards/New() - ..() - -/obj/item/toy/cards/proc/apply_card_vars(obj/item/toy/cards/newobj, obj/item/toy/cards/sourceobj) // Applies variables for supporting multiple types of card deck - if(!istype(sourceobj)) - return - -/obj/item/toy/cards/deck - name = "deck of cards" - desc = "A deck of space-grade playing cards." - icon = 'icons/obj/toy.dmi' - deckstyle = "nanotrasen" - icon_state = "deck_nanotrasen_full" - w_class = 2 - var/cooldown = 0 - var/obj/machinery/computer/holodeck/holo = null // Holodeck cards should not be infinite - var/list/cards = list() - -/obj/item/toy/cards/deck/New() - ..() - icon_state = "deck_[deckstyle]_full" - for(var/i = 2; i <= 10; i++) - cards += "[i] of Hearts" - cards += "[i] of Spades" - cards += "[i] of Clubs" - cards += "[i] of Diamonds" - cards += "King of Hearts" - cards += "King of Spades" - cards += "King of Clubs" - cards += "King of Diamonds" - cards += "Queen of Hearts" - cards += "Queen of Spades" - cards += "Queen of Clubs" - cards += "Queen of Diamonds" - cards += "Jack of Hearts" - cards += "Jack of Spades" - cards += "Jack of Clubs" - cards += "Jack of Diamonds" - cards += "Ace of Hearts" - cards += "Ace of Spades" - cards += "Ace of Clubs" - cards += "Ace of Diamonds" - - -/obj/item/toy/cards/deck/attack_hand(mob/user) - if(user.lying) - return - var/choice = null - if(cards.len == 0) - src.icon_state = "deck_[deckstyle]_empty" - user << "There are no more cards to draw!" - return - var/obj/item/toy/cards/singlecard/H = new/obj/item/toy/cards/singlecard(user.loc) - if(holo) - holo.spawned += H // track them leaving the holodeck - choice = cards[1] - H.cardname = choice - H.parentdeck = src - var/O = src - H.apply_card_vars(H,O) - src.cards -= choice - H.pickup(user) - user.put_in_active_hand(H) - user.visible_message("[user] draws a card from the deck.", "You draw a card from the deck.") - if(cards.len > 26) - src.icon_state = "deck_[deckstyle]_full" - else if(cards.len > 10) - src.icon_state = "deck_[deckstyle]_half" - else if(cards.len > 1) - src.icon_state = "deck_[deckstyle]_low" - -/obj/item/toy/cards/deck/attack_self(mob/user) - if(cooldown < world.time - 50) - cards = shuffle(cards) - playsound(user, 'sound/items/cardshuffle.ogg', 50, 1) - user.visible_message("[user] shuffles the deck.", "You shuffle the deck.") - cooldown = world.time - -/obj/item/toy/cards/deck/attackby(obj/item/toy/cards/singlecard/C, mob/living/user, params) - ..() - if(istype(C)) - if(C.parentdeck == src) - if(!user.unEquip(C)) - user << "The card is stuck to your hand, you can't add it to the deck!" - return - src.cards += C.cardname - user.visible_message("[user] adds a card to the bottom of the deck.","You add the card to the bottom of the deck.") - qdel(C) - else - user << "You can't mix cards from other decks!" - if(cards.len > 26) - src.icon_state = "deck_[deckstyle]_full" - else if(cards.len > 10) - src.icon_state = "deck_[deckstyle]_half" - else if(cards.len > 1) - src.icon_state = "deck_[deckstyle]_low" - - -/obj/item/toy/cards/deck/attackby(obj/item/toy/cards/cardhand/C, mob/living/user, params) - ..() - if(istype(C)) - if(C.parentdeck == src) - if(!user.unEquip(C)) - user << "The hand of cards is stuck to your hand, you can't add it to the deck!" - return - src.cards += C.currenthand - user.visible_message("[user] puts their hand of cards in the deck.", "You put the hand of cards in the deck.") - qdel(C) - else - user << "You can't mix cards from other decks!" - if(cards.len > 26) - src.icon_state = "deck_[deckstyle]_full" - else if(cards.len > 10) - src.icon_state = "deck_[deckstyle]_half" - else if(cards.len > 1) - src.icon_state = "deck_[deckstyle]_low" - -/obj/item/toy/cards/deck/MouseDrop(atom/over_object) - var/mob/M = usr - if(!ishuman(usr) || usr.incapacitated() || usr.lying) - return - if(Adjacent(usr)) - if(over_object == M && loc != M) - M.put_in_hands(src) - usr << "You pick up the deck." - - else if(istype(over_object, /obj/screen)) - switch(over_object.name) - if("l_hand") - if(!remove_item_from_storage(M)) - M.unEquip(src) - M.put_in_l_hand(src) - else if("r_hand") - if(!remove_item_from_storage(M)) - M.unEquip(src) - M.put_in_r_hand(src) - usr << "You pick up the deck." - else - usr << "You can't reach it from here!" - - - -/obj/item/toy/cards/cardhand - name = "hand of cards" - desc = "A number of cards not in a deck, customarily held in ones hand." - icon = 'icons/obj/toy.dmi' - icon_state = "nanotrasen_hand2" - w_class = 1 - var/list/currenthand = list() - var/choice = null - - -/obj/item/toy/cards/cardhand/attack_self(mob/user) - user.set_machine(src) - interact(user) - -/obj/item/toy/cards/cardhand/interact(mob/user) - var/dat = "You have:
" - for(var/t in currenthand) - dat += "A [t].
" - dat += "Which card will you remove next?" - var/datum/browser/popup = new(user, "cardhand", "Hand of Cards", 400, 240) - popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) - popup.set_content(dat) - popup.open() - - -/obj/item/toy/cards/cardhand/Topic(href, href_list) - if(..()) - return - if(usr.stat || !ishuman(usr) || !usr.canmove) - return - var/mob/living/carbon/human/cardUser = usr - var/O = src - if(href_list["pick"]) - if (cardUser.get_item_by_slot(slot_l_hand) == src || cardUser.get_item_by_slot(slot_r_hand) == src) - var/choice = href_list["pick"] - var/obj/item/toy/cards/singlecard/C = new/obj/item/toy/cards/singlecard(cardUser.loc) - src.currenthand -= choice - C.parentdeck = src.parentdeck - C.cardname = choice - C.apply_card_vars(C,O) - C.pickup(cardUser) - cardUser.put_in_any_hand_if_possible(C) - cardUser.visible_message("[cardUser] draws a card from \his hand.", "You take the [C.cardname] from your hand.") - - interact(cardUser) - if(src.currenthand.len < 3) - src.icon_state = "[deckstyle]_hand2" - else if(src.currenthand.len < 4) - src.icon_state = "[deckstyle]_hand3" - else if(src.currenthand.len < 5) - src.icon_state = "[deckstyle]_hand4" - if(src.currenthand.len == 1) - var/obj/item/toy/cards/singlecard/N = new/obj/item/toy/cards/singlecard(src.loc) - N.parentdeck = src.parentdeck - N.cardname = src.currenthand[1] - N.apply_card_vars(N,O) - cardUser.unEquip(src) - N.pickup(cardUser) - cardUser.put_in_any_hand_if_possible(N) - cardUser << "You also take [currenthand[1]] and hold it." - cardUser << browse(null, "window=cardhand") - qdel(src) - return - -/obj/item/toy/cards/cardhand/attackby(obj/item/toy/cards/singlecard/C, mob/living/user, params) - if(istype(C)) - if(C.parentdeck == src.parentdeck) - src.currenthand += C.cardname - user.unEquip(C) - user.visible_message("[user] adds a card to their hand.", "You add the [C.cardname] to your hand.") - interact(user) - if(currenthand.len > 4) - src.icon_state = "[deckstyle]_hand5" - else if(currenthand.len > 3) - src.icon_state = "[deckstyle]_hand4" - else if(currenthand.len > 2) - src.icon_state = "[deckstyle]_hand3" - qdel(C) - else - user << "You can't mix cards from other decks!" - -/obj/item/toy/cards/cardhand/apply_card_vars(obj/item/toy/cards/newobj,obj/item/toy/cards/sourceobj) - ..() - newobj.deckstyle = sourceobj.deckstyle - newobj.icon_state = "[deckstyle]_hand2" // Another dumb hack, without this the hand is invisible (or has the default deckstyle) until another card is added. - newobj.card_hitsound = sourceobj.card_hitsound - newobj.card_force = sourceobj.card_force - newobj.card_throwforce = sourceobj.card_throwforce - newobj.card_throw_speed = sourceobj.card_throw_speed - newobj.card_throw_range = sourceobj.card_throw_range - newobj.card_attack_verb = sourceobj.card_attack_verb - if(sourceobj.burn_state == FIRE_PROOF) - newobj.burn_state = FIRE_PROOF - -/obj/item/toy/cards/singlecard - name = "card" - desc = "a card" - icon = 'icons/obj/toy.dmi' - icon_state = "singlecard_nanotrasen_down" - w_class = 1 - var/cardname = null - var/flipped = 0 - pixel_x = -5 - - -/obj/item/toy/cards/singlecard/examine(mob/user) - if(ishuman(user)) - var/mob/living/carbon/human/cardUser = user - if(cardUser.get_item_by_slot(slot_l_hand) == src || cardUser.get_item_by_slot(slot_r_hand) == src) - cardUser.visible_message("[cardUser] checks \his card.", "The card reads: [src.cardname]") - else - cardUser << "You need to have the card in your hand to check it!" - - -/obj/item/toy/cards/singlecard/verb/Flip() - set name = "Flip Card" - set category = "Object" - set src in range(1) - if(usr.stat || !ishuman(usr) || !usr.canmove || usr.restrained()) - return - if(!flipped) - src.flipped = 1 - if (cardname) - src.icon_state = "sc_[cardname]_[deckstyle]" - src.name = src.cardname - else - src.icon_state = "sc_Ace of Spades_[deckstyle]" - src.name = "What Card" - src.pixel_x = 5 - else if(flipped) - src.flipped = 0 - src.icon_state = "singlecard_down_[deckstyle]" - src.name = "card" - src.pixel_x = -5 - -/obj/item/toy/cards/singlecard/attackby(obj/item/I, mob/living/user, params) - if(istype(I, /obj/item/toy/cards/singlecard/)) - var/obj/item/toy/cards/singlecard/C = I - if(C.parentdeck == src.parentdeck) - var/obj/item/toy/cards/cardhand/H = new/obj/item/toy/cards/cardhand(user.loc) - H.currenthand += C.cardname - H.currenthand += src.cardname - H.parentdeck = C.parentdeck - H.apply_card_vars(H,C) - user.unEquip(C) - H.pickup(user) - user.put_in_active_hand(H) - user << "You combine the [C.cardname] and the [src.cardname] into a hand." - qdel(C) - qdel(src) - else - user << "You can't mix cards from other decks!" - - if(istype(I, /obj/item/toy/cards/cardhand/)) - var/obj/item/toy/cards/cardhand/H = I - if(H.parentdeck == parentdeck) - H.currenthand += cardname - user.unEquip(src) - user.visible_message("[user] adds a card to \his hand.", "You add the [cardname] to your hand.") - H.interact(user) - if(H.currenthand.len > 4) - H.icon_state = "[deckstyle]_hand5" - else if(H.currenthand.len > 3) - H.icon_state = "[deckstyle]_hand4" - else if(H.currenthand.len > 2) - H.icon_state = "[deckstyle]_hand3" - qdel(src) - else - user << "You can't mix cards from other decks!" - - -/obj/item/toy/cards/singlecard/attack_self(mob/user) - if(usr.stat || !ishuman(usr) || !usr.canmove || usr.restrained()) - return - Flip() - -/obj/item/toy/cards/singlecard/apply_card_vars(obj/item/toy/cards/singlecard/newobj,obj/item/toy/cards/sourceobj) - ..() - newobj.deckstyle = sourceobj.deckstyle - newobj.icon_state = "singlecard_down_[deckstyle]" // Without this the card is invisible until flipped. It's an ugly hack, but it works. - newobj.card_hitsound = sourceobj.card_hitsound - newobj.hitsound = newobj.card_hitsound - newobj.card_force = sourceobj.card_force - newobj.force = newobj.card_force - newobj.card_throwforce = sourceobj.card_throwforce - newobj.throwforce = newobj.card_throwforce - newobj.card_throw_speed = sourceobj.card_throw_speed - newobj.throw_speed = newobj.card_throw_speed - newobj.card_throw_range = sourceobj.card_throw_range - newobj.throw_range = newobj.card_throw_range - newobj.card_attack_verb = sourceobj.card_attack_verb - newobj.attack_verb = newobj.card_attack_verb - - -/* -|| Syndicate playing cards, for pretending you're Gambit and playing poker for the nuke disk. || -*/ - -/obj/item/toy/cards/deck/syndicate - name = "suspicious looking deck of cards" - desc = "A deck of space-grade playing cards. They seem unusually rigid." - deckstyle = "syndicate" - card_hitsound = 'sound/weapons/bladeslice.ogg' - card_force = 5 - card_throwforce = 10 - card_throw_speed = 3 - card_throw_range = 7 - card_attack_verb = list("attacked", "sliced", "diced", "slashed", "cut") - burn_state = FIRE_PROOF - -/* - * Fake nuke - */ - -/obj/item/toy/nuke - name = "\improper Nuclear Fission Explosive toy" - desc = "A plastic model of a Nuclear Fission Explosive." - icon = 'icons/obj/toy.dmi' - icon_state = "nuketoyidle" - w_class = 2 - var/cooldown = 0 - -/obj/item/toy/nuke/attack_self(mob/user) - if (cooldown < world.time) - cooldown = world.time + 1800 //3 minutes - user.visible_message("[user] presses a button on [src].", "You activate [src], it plays a loud noise!", "You hear the click of a button.") - spawn(5) //gia said so - icon_state = "nuketoy" - playsound(src, 'sound/machines/Alarm.ogg', 100, 0, surround = 0) - sleep(135) - icon_state = "nuketoycool" - sleep(cooldown - world.time) - icon_state = "nuketoyidle" - else - var/timeleft = (cooldown - world.time) - user << "Nothing happens, and '[round(timeleft/10)]' appears on a small display." - -/* - * Fake meteor - */ - -/obj/item/toy/minimeteor - name = "\improper Mini-Meteor" - desc = "Relive the excitement of a meteor shower! SweetMeat-eor. Co is not responsible for any injuries, headaches or hearing loss caused by Mini-Meteor?" - icon = 'icons/obj/toy.dmi' - icon_state = "minimeteor" - w_class = 2 - -/obj/item/toy/minimeteor/throw_impact(atom/hit_atom) - if(!..()) - playsound(src, 'sound/effects/meteorimpact.ogg', 40, 1) - for(var/mob/M in ultra_range(10, src)) - if(!M.stat && !istype(M, /mob/living/silicon/ai))\ - shake_camera(M, 3, 1) - qdel(src) - -/* - * Carp plushie - */ - -/obj/item/toy/carpplushie - name = "space carp plushie" - desc = "An adorable stuffed toy that resembles a space carp." - icon = 'icons/obj/toy.dmi' - icon_state = "carpplushie" - item_state = "carp_plushie" - w_class = 2 - attack_verb = list("bitten", "eaten", "fin slapped") - burn_state = FLAMMABLE - var/bitesound = 'sound/weapons/bite.ogg' - -//Attack mob -/obj/item/toy/carpplushie/attack(mob/M, mob/user) - playsound(loc, bitesound, 20, 1) //Play bite sound in local area - return ..() - -//Attack self -/obj/item/toy/carpplushie/attack_self(mob/user) - playsound(src.loc, bitesound, 20, 1) - user << "You pet [src]. D'awww." - return ..() - -/* - * Toy big red button - */ -/obj/item/toy/redbutton - name = "big red button" - desc = "A big, plastic red button. Reads 'From HonkCo Pranks?' on the back." - icon = 'icons/obj/assemblies.dmi' - icon_state = "bigred" - w_class = 2 - var/cooldown = 0 - -/obj/item/toy/redbutton/attack_self(mob/user) - if (cooldown < world.time) - cooldown = (world.time + 300) // Sets cooldown at 30 seconds - user.visible_message("[user] presses the big red button.", "You press the button, it plays a loud noise!", "The button clicks loudly.") - playsound(src, 'sound/effects/explosionfar.ogg', 50, 0, surround = 0) - for(var/mob/M in ultra_range(10, src)) // Checks range - if(!M.stat && !istype(M, /mob/living/silicon/ai)) // Checks to make sure whoever's getting shaken is alive/not the AI - sleep(8) // Short delay to match up with the explosion sound - shake_camera(M, 2, 1) // Shakes player camera 2 squares for 1 second. - - else - user << "Nothing happens." - -/* - * Snowballs - */ - -/obj/item/toy/snowball - name = "snowball" - desc = "A compact ball of snow. Good for throwing at people." - icon = 'icons/obj/toy.dmi' - icon_state = "snowball" - throwforce = 12 //pelt your enemies to death with lumps of snow - -/obj/item/toy/snowball/afterattack(atom/target as mob|obj|turf|area, mob/user) - user.drop_item() - src.throw_at(target, throw_range, throw_speed) - -/obj/item/toy/snowball/throw_impact(atom/hit_atom) - if(!..()) - playsound(src, 'sound/effects/pop.ogg', 20, 1) - qdel(src) - -/* - * Beach ball - */ -/obj/item/toy/beach_ball - icon = 'icons/misc/beach.dmi' - icon_state = "ball" - name = "beach ball" - item_state = "beachball" - w_class = 4 //Stops people from hiding it in their bags/pockets - -/obj/item/toy/beach_ball/afterattack(atom/target as mob|obj|turf|area, mob/user) - user.drop_item() - src.throw_at(target, throw_range, throw_speed) - -/* - * Xenomorph action figure - */ - -/obj/item/toy/toy_xeno - icon = 'icons/obj/toy.dmi' - icon_state = "toy_xeno" - name = "xenomorph action figure" - desc = "MEGA presents the new Xenos Isolated action figure! Comes complete with realistic sounds! Pull back string to use." - w_class = 2 - var/cooldown = 0 - -/obj/item/toy/toy_xeno/attack_self(mob/user) - if(cooldown <= world.time) - cooldown = (world.time + 50) //5 second cooldown - user.visible_message("[user] pulls back the string on [src].") - icon_state = "[initial(icon_state)]_used" - sleep(5) - audible_message("\icon[src] Hiss!") - var/list/possible_sounds = list('sound/voice/hiss1.ogg', 'sound/voice/hiss2.ogg', 'sound/voice/hiss3.ogg', 'sound/voice/hiss4.ogg') - var/chosen_sound = pick(possible_sounds) - playsound(get_turf(src), chosen_sound, 50, 1) - spawn(45) - if(src) - icon_state = "[initial(icon_state)]" - else - user << "The string on [src] hasn't rewound all the way!" - return - -// TOY MOUSEYS :3 :3 :3 - -/obj/item/toy/cattoy - name = "toy mouse" - desc = "A colorful toy mouse!" - icon = 'icons/obj/toy.dmi' - icon_state = "toy_mouse" - w_class = 2.0 - var/cooldown = 0 - burn_state = FLAMMABLE - - -/* - * Action Figures - */ - -/obj/item/toy/figure - name = "Non-Specific Action Figure action figure" - desc = null - icon = 'icons/obj/toy.dmi' - icon_state = "nuketoy" - var/cooldown = 0 - var/toysay = "What the fuck did you do?" - var/toysound = 'sound/machines/click.ogg' - -/obj/item/toy/figure/New() - desc = "A \"Space Life\" brand [src]." - -/obj/item/toy/figure/attack_self(mob/user as mob) - if(cooldown <= world.time) - cooldown = world.time + 50 - user << "The [src] says \"[toysay]\"" - playsound(user, toysound, 20, 1) - -/obj/item/toy/figure/cmo - name = "Chief Medical Officer action figure" - icon_state = "cmo" - toysay = "Suit sensors!" - -/obj/item/toy/figure/assistant - name = "Assistant action figure" - icon_state = "assistant" - toysay = "Grey tide world wide!" - -/obj/item/toy/figure/atmos - name = "Atmospheric Technician action figure" - icon_state = "atmos" - toysay = "Glory to Atmosia!" - -/obj/item/toy/figure/bartender - name = "Bartender action figure" - icon_state = "bartender" - toysay = "Where is Pun Pun?" - -/obj/item/toy/figure/borg - name = "Cyborg action figure" - icon_state = "borg" - toysay = "I. LIVE. AGAIN." - toysound = 'sound/voice/liveagain.ogg' - -/obj/item/toy/figure/botanist - name = "Botanist action figure" - icon_state = "botanist" - toysay = "Dude, I see colors..." - -/obj/item/toy/figure/captain - name = "Captain action figure" - icon_state = "captain" - toysay = "Any heads of staff?" - -/obj/item/toy/figure/cargotech - name = "Cargo Technician action figure" - icon_state = "cargotech" - toysay = "For Cargonia!" - -/obj/item/toy/figure/ce - name = "Chief Engineer action figure" - icon_state = "ce" - toysay = "Wire the solars!" - -/obj/item/toy/figure/chaplain - name = "Chaplain action figure" - icon_state = "chaplain" - toysay = "Praise Space Jesus!" - -/obj/item/toy/figure/chef - name = "Chef action figure" - icon_state = "chef" - toysay = "Pun-Pun is a tasty burger." - -/obj/item/toy/figure/chemist - name = "Chemist action figure" - icon_state = "chemist" - toysay = "Get your pills!" - -/obj/item/toy/figure/clown - name = "Clown action figure" - icon_state = "clown" - toysay = "Honk!" - toysound = 'sound/items/bikehorn.ogg' - -/obj/item/toy/figure/ian - name = "Ian action figure" - icon_state = "ian" - toysay = "Arf!" - -/obj/item/toy/figure/detective - name = "Detective action figure" - icon_state = "detective" - toysay = "This airlock has grey jumpsuit and insulated glove fibers on it." - -/obj/item/toy/figure/dsquad - name = "Death Squad Officer action figure" - icon_state = "dsquad" - toysay = "Eliminate all threats!" - -/obj/item/toy/figure/engineer - name = "Engineer action figure" - icon_state = "engineer" - toysay = "Oh god, the singularity is loose!" - -/obj/item/toy/figure/geneticist - name = "Geneticist action figure" - icon_state = "geneticist" - toysay = "Smash!" - -/obj/item/toy/figure/hop - name = "Head of Personel action figure" - icon_state = "hop" - toysay = "Giving out all access!" - -/obj/item/toy/figure/hos - name = "Head of Security action figure" - icon_state = "hos" - toysay = "Get the justice chamber ready, I think we got a joker here." - -/obj/item/toy/figure/qm - name = "Quartermaster action figure" - icon_state = "qm" - toysay = "Please sign this form in triplicate and we will see about geting you a welding mask within 3 business days." - -/obj/item/toy/figure/janitor - name = "Janitor action figure" - icon_state = "janitor" - toysay = "Look at the signs, you idiot." - -/obj/item/toy/figure/lawyer - name = "Lawyer action figure" - icon_state = "lawyer" - toysay = "My client is a dirty traitor!" - -/obj/item/toy/figure/librarian - name = "Librarian action figure" - icon_state = "librarian" - toysay = "One day while..." - -/obj/item/toy/figure/md - name = "Medical Doctor action figure" - icon_state = "md" - toysay = "The patient is already dead!" - -/obj/item/toy/figure/mime - name = "Mime action figure" - icon_state = "mime" - toysay = "..." - toysound = null - -/obj/item/toy/figure/miner - name = "Shaft Miner action figure" - icon_state = "miner" - toysay = "Oh god it's eating my intestines!" - -/obj/item/toy/figure/ninja - name = "Ninja action figure" - icon_state = "ninja" - toysay = "Oh god! Stop shooting, I'm friendly!" - -/obj/item/toy/figure/wizard - name = "Wizard action figure" - icon_state = "wizard" - toysay = "Ei Nath!" - toysound = 'sound/magic/Disintegrate.ogg' - -/obj/item/toy/figure/rd - name = "Research Director action figure" - icon_state = "rd" - toysay = "Blowing all of the borgs!" - -/obj/item/toy/figure/roboticist - name = "Roboticist action figure" - icon_state = "roboticist" - toysay = "Big stompy mechs!" - toysound = 'sound/mecha/mechstep.ogg' - -/obj/item/toy/figure/scientist - name = "Scientist action figure" - icon_state = "scientist" - toysay = "For science!" - toysound = 'sound/effects/explosionfar.ogg' - -/obj/item/toy/figure/syndie - name = "Nuclear Operative action figure" - icon_state = "syndie" - toysay = "Get that fucking disk!" - -/obj/item/toy/figure/secofficer - name = "Security Officer action figure" - icon_state = "secofficer" - toysay = "I am the law!" - toysound = 'sound/voice/complionator/dredd.ogg' - -/obj/item/toy/figure/virologist - name = "Virologist action figure" - icon_state = "virologist" - toysay = "The cure is potassium!" - -/obj/item/toy/figure/warden - name = "Warden action figure" - icon_state = "warden" - toysay = "Seventeen minutes for coughing at an officer!" +/* Toys! + * Contains + * Balloons + * Fake singularity + * Toy gun + * Toy crossbow + * Toy swords + * Crayons + * Snap pops + * Mech prizes + * AI core prizes + * Cards + * Toy nuke + * Fake meteor + * Carp plushie + * Foam armblade + * Toy big red button + * Beach ball + * Toy xeno + * Kitty toys! + * Snowballs + */ + + +/obj/item/toy + throwforce = 0 + throw_speed = 3 + throw_range = 7 + force = 0 + + +/* + * Balloons + */ +/obj/item/toy/balloon + name = "water balloon" + desc = "A translucent balloon. There's nothing in it." + icon = 'icons/obj/toy.dmi' + icon_state = "waterballoon-e" + item_state = "balloon-empty" + +/obj/item/toy/balloon/New() + create_reagents(10) + +/obj/item/toy/balloon/attack(mob/living/carbon/human/M, mob/user) + return + +/obj/item/toy/balloon/afterattack(atom/A as mob|obj, mob/user, proximity) + if(!proximity) return + if (istype(A, /obj/structure/reagent_dispensers)) + var/obj/structure/reagent_dispensers/RD = A + if(RD.reagents.total_volume <= 0) + user << "[RD] is empty." + else if(reagents.total_volume >= 10) + user << "[src] is full." + else + A.reagents.trans_to(src, 10) + user << "You fill the balloon with the contents of [A]." + desc = "A translucent balloon with some form of liquid sloshing around in it." + update_icon() + +/obj/item/toy/balloon/attackby(obj/O, mob/user, params) + if(istype(O, /obj/item/weapon/reagent_containers/glass)) + if(O.reagents) + if(O.reagents.total_volume <= 0) + user << "[O] is empty." + else if(reagents.total_volume >= 10) + user << "[src] is full." + else + desc = "A translucent balloon with some form of liquid sloshing around in it." + user << "You fill the balloon with the contents of [O]." + O.reagents.trans_to(src, 10) + update_icon() + +/obj/item/toy/balloon/throw_impact(atom/hit_atom) + if(!..()) //was it caught by a mob? + if(reagents.total_volume >= 1) + visible_message("[src] bursts!","You hear a pop and a splash.") + reagents.reaction(get_turf(hit_atom)) + for(var/atom/A in get_turf(hit_atom)) + reagents.reaction(A) + icon_state = "burst" + qdel(src) + +/obj/item/toy/balloon/update_icon() + if(src.reagents.total_volume >= 1) + icon_state = "waterballoon" + item_state = "balloon" + else + icon_state = "waterballoon-e" + item_state = "balloon-empty" + +/obj/item/toy/syndicateballoon + name = "syndicate balloon" + desc = "There is a tag on the back that reads \"FUK NT!11!\"." + throwforce = 0 + throw_speed = 3 + throw_range = 7 + force = 0 + icon = 'icons/obj/weapons.dmi' + icon_state = "syndballoon" + item_state = "syndballoon" + w_class = 4 + +/* + * Fake singularity + */ +/obj/item/toy/spinningtoy + name = "gravitational singularity" + desc = "\"Singulo\" brand spinning toy." + icon = 'icons/obj/singularity.dmi' + icon_state = "singularity_s1" + +/* + * Toy gun: Why isnt this an /obj/item/weapon/gun? + */ +/obj/item/toy/gun + name = "cap gun" + desc = "Looks almost like the real thing! Ages 8 and up. Please recycle in an autolathe when you're out of caps." + icon = 'icons/obj/guns/projectile.dmi' + icon_state = "revolver" + item_state = "gun" + lefthand_file = 'icons/mob/inhands/guns_lefthand.dmi' + righthand_file = 'icons/mob/inhands/guns_righthand.dmi' + flags = CONDUCT + slot_flags = SLOT_BELT + w_class = 3 + materials = list(MAT_METAL=10, MAT_GLASS=10) + attack_verb = list("struck", "pistol whipped", "hit", "bashed") + var/bullets = 7 + +/obj/item/toy/gun/examine(mob/user) + ..() + user << "There [bullets == 1 ? "is" : "are"] [bullets] cap\s left." + +/obj/item/toy/gun/attackby(obj/item/toy/ammo/gun/A, mob/user, params) + + if (istype(A, /obj/item/toy/ammo/gun)) + if (src.bullets >= 7) + user << "It's already fully loaded!" + return 1 + if (A.amount_left <= 0) + user << "There are no more caps!" + return 1 + if (A.amount_left < (7 - src.bullets)) + src.bullets += A.amount_left + user << text("You reload [] cap\s.", A.amount_left) + A.amount_left = 0 + else + user << text("You reload [] cap\s.", 7 - src.bullets) + A.amount_left -= 7 - src.bullets + src.bullets = 7 + A.update_icon() + return 1 + return + +/obj/item/toy/gun/afterattack(atom/target as mob|obj|turf|area, mob/user, flag) + if (flag) + return + if (!(istype(usr, /mob/living/carbon/human) || ticker) && ticker.mode.name != "monkey") + usr << "You don't have the dexterity to do this!" + return + src.add_fingerprint(user) + if (src.bullets < 1) + user.show_message("*click*", 2) + playsound(user, 'sound/weapons/empty.ogg', 100, 1) + return + playsound(user, 'sound/weapons/Gunshot.ogg', 100, 1) + src.bullets-- + user.visible_message("[user] fires [src] at [target]!", \ + "You fire [src] at [target]!", \ + "You hear a gunshot!") + +/obj/item/toy/ammo/gun + name = "capgun ammo" + desc = "Make sure to recyle the box in an autolathe when it gets empty." + icon = 'icons/obj/ammo.dmi' + icon_state = "357OLD-7" + w_class = 1 + materials = list(MAT_METAL=10, MAT_GLASS=10) + var/amount_left = 7 + +/obj/item/toy/ammo/gun/update_icon() + src.icon_state = text("357OLD-[]", src.amount_left) + +/obj/item/toy/ammo/gun/examine(mob/user) + ..() + user << "There [amount_left == 1 ? "is" : "are"] [amount_left] cap\s left." + +/* + * Toy swords + */ +/obj/item/toy/sword + name = "toy sword" + desc = "A cheap, plastic replica of an energy sword. Realistic sounds! Ages 8 and up." + icon = 'icons/obj/weapons.dmi' + icon_state = "sword0" + item_state = "sword0" + var/active = 0 + w_class = 2 + flags = NOSHIELD + attack_verb = list("attacked", "struck", "hit") + var/hacked = 0 + +/obj/item/toy/sword/attack_self(mob/user) + active = !( active ) + if (active) + user << "You extend the plastic blade with a quick flick of your wrist." + playsound(user, 'sound/weapons/saberon.ogg', 20, 1) + if(hacked) + icon_state = "swordrainbow" + item_state = "swordrainbow" + else + icon_state = "swordblue" + item_state = "swordblue" + w_class = 4 + else + user << "You push the plastic blade back down into the handle." + playsound(user, 'sound/weapons/saberoff.ogg', 20, 1) + icon_state = "sword0" + item_state = "sword0" + w_class = 2 + add_fingerprint(user) + return + +// Copied from /obj/item/weapon/melee/energy/sword/attackby +/obj/item/toy/sword/attackby(obj/item/weapon/W, mob/living/user, params) + ..() + if(istype(W, /obj/item/toy/sword)) + if(W == src) + user << "You try to attach the end of the plastic sword to... itself. You're not very smart, are you?" + if(ishuman(user)) + user.adjustBrainLoss(10) + else if((W.flags & NODROP) || (flags & NODROP)) + user << "\the [flags & NODROP ? src : W] is stuck to your hand, you can't attach it to \the [flags & NODROP ? W : src]!" + else + user << "You attach the ends of the two plastic swords, making a single double-bladed toy! You're fake-cool." + var/obj/item/weapon/twohanded/dualsaber/toy/newSaber = new /obj/item/weapon/twohanded/dualsaber/toy(user.loc) + if(hacked) // That's right, we'll only check the "original" "sword". + newSaber.hacked = 1 + newSaber.item_color = "rainbow" + user.unEquip(W) + user.unEquip(src) + qdel(W) + qdel(src) + else if(istype(W, /obj/item/device/multitool)) + if(hacked == 0) + hacked = 1 + item_color = "rainbow" + user << "RNBW_ENGAGE" + + if(active) + icon_state = "swordrainbow" + // Updating overlays, copied from welder code. + // I tried calling attack_self twice, which looked cool, except it somehow didn't update the overlays!! + if(user.r_hand == src) + user.update_inv_r_hand(0) + else if(user.l_hand == src) + user.update_inv_l_hand(0) + else + user << "It's already fabulous!" + +/* + * Foam armblade + */ +/obj/item/toy/foamblade + name = "foam armblade" + desc = "it says \"Sternside Changs #1 fan\" on it. " + icon = 'icons/obj/toy.dmi' + icon_state = "foamblade" + item_state = "arm_blade" + attack_verb = list("pricked", "absorbed", "gored") + w_class = 2 + burn_state = FLAMMABLE + + +/* + * Subtype of Double-Bladed Energy Swords + */ +/obj/item/weapon/twohanded/dualsaber/toy + name = "double-bladed toy sword" + desc = "A cheap, plastic replica of TWO energy swords. Double the fun!" + force = 0 + throwforce = 0 + throw_speed = 3 + throw_range = 5 + force_unwielded = 0 + force_wielded = 0 + origin_tech = null + attack_verb = list("attacked", "struck", "hit") + +/obj/item/weapon/twohanded/dualsaber/toy/hit_reaction() + return 0 + +/obj/item/weapon/twohanded/dualsaber/toy/IsReflect()//Stops Toy Dualsabers from reflecting energy projectiles + return 0 + +/obj/item/toy/katana + name = "replica katana" + desc = "Woefully underpowered in D20." + icon = 'icons/obj/weapons.dmi' + icon_state = "katana" + item_state = "katana" + flags = CONDUCT + slot_flags = SLOT_BELT | SLOT_BACK + force = 5 + throwforce = 5 + w_class = 3 + attack_verb = list("attacked", "slashed", "stabbed", "sliced") + hitsound = 'sound/weapons/bladeslice.ogg' + +/* + * Crayons + */ + +/obj/item/toy/crayon + name = "crayon" + desc = "A colourful crayon. Looks tasty. Mmmm..." + icon = 'icons/obj/crayons.dmi' + icon_state = "crayonred" + w_class = 1 + attack_verb = list("attacked", "coloured") + var/paint_color = "#FF0000" //RGB + var/drawtype = "rune" + var/list/graffiti = list("amyjon","face","matt","revolution","engie","guy","end","dwarf","uboa","body","cyka","arrow","star","poseur tag") + var/list/letters = 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") + var/list/numerals = list("0","1","2","3","4","5","6","7","8","9") + var/list/oriented = list("arrow","body") // These turn to face the same way as the drawer + var/uses = 30 //-1 or less for unlimited uses + var/instant = 0 + var/colourName = "red" //for updateIcon purposes + var/dat + var/list/validSurfaces = list(/turf/simulated/floor) + var/gang = 0 //For marking territory + var/edible = 1 + +/obj/item/toy/crayon/suicide_act(mob/user) + user.visible_message("[user] is jamming the [src.name] up \his nose and into \his brain. It looks like \he's trying to commit suicide.") + return (BRUTELOSS|OXYLOSS) + +/obj/item/toy/crayon/New() + ..() + name = "[colourName] crayon" //Makes crayons identifiable in things like grinders + drawtype = pick(pick(graffiti), pick(letters), "rune[rand(1,6)]") + if(config) + if(config.mutant_races == 1) + graffiti |= "antilizard" + graffiti |= "prolizard" + +/obj/item/toy/crayon/initialize() + if(config.mutant_races == 1) + graffiti |= "antilizard" + graffiti |= "prolizard" + +/obj/item/toy/crayon/attack_self(mob/living/user) + update_window(user) + +/obj/item/toy/crayon/proc/update_window(mob/living/user) + dat += "

Currently selected: [drawtype]


" + dat += "Random letterPick letter/number" + dat += "
" + dat += "

Runes:


" + dat += "Random rune" + for(var/i = 1; i <= 6; i++) + dat += "Rune[i]" + if(!((i + 1) % 3)) //3 buttons in a row + dat += "
" + dat += "
" + graffiti.Find() + dat += "

Graffiti:


" + dat += "Random graffiti" + var/c = 1 + for(var/T in graffiti) + dat += "[T]" + if(!((c + 1) % 3)) //3 buttons in a row + dat += "
" + c++ + dat += "
" + var/datum/browser/popup = new(user, "crayon", name, 300, 500) + popup.set_content(dat) + popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) + popup.open() + dat = "" + +/obj/item/toy/crayon/Topic(href, href_list, hsrc) + var/temp = "a" + switch(href_list["type"]) + if("random_letter") + temp = pick(letters) + if("letter") + temp = input("Choose what to write.", "Scribbles") in (letters|numerals) + if("random_rune") + temp = "rune[rand(1,6)]" + if("random_graffiti") + temp = pick(graffiti) + else + temp = href_list["type"] + if ((usr.restrained() || usr.stat || usr.get_active_hand() != src)) + return + drawtype = temp + update_window(usr) + +/obj/item/toy/crayon/afterattack(atom/target, mob/user, proximity) + if(!proximity || !check_allowed_items(target)) return + if(!uses) + user << "There is no more of [src.name] left!" + if(!instant) + qdel(src) + return + if(istype(target, /obj/effect/decal/cleanable)) + target = target.loc + if(is_type_in_list(target,validSurfaces)) + var/temp = "rune" + if(letters.Find(drawtype)) + temp = "letter" + else if(graffiti.Find(drawtype)) + temp = "graffiti" + else if(numerals.Find(drawtype)) + temp = "number" + + ////////////////////////// GANG FUNCTIONS + var/area/territory + var/gangID + if(gang) + //Determine gang affiliation + gangID = user.mind.gang_datum + + //Check area validity. Reject space, player-created areas, and non-station z-levels. + if(gangID) + territory = get_area(target) + if(territory && (territory.z == ZLEVEL_STATION) && territory.valid_territory) + //Check if this area is already tagged by a gang + if(!(locate(/obj/effect/decal/cleanable/crayon/gang) in target)) //Ignore the check if the tile being sprayed has a gang tag + if(territory_claimed(territory, user)) + return + if(locate(/obj/machinery/power/apc) in (user.loc.contents | target.contents)) + user << "You cannot tag here." + return + else + user << "[territory] is unsuitable for tagging." + return + ///////////////////////////////////////// + + var/graf_rot + if(oriented.Find(drawtype)) + switch(user.dir) + if(EAST) + graf_rot = 90 + if(SOUTH) + graf_rot = 180 + if(WEST) + graf_rot = 270 + else + graf_rot = 0 + + user << "You start [instant ? "spraying" : "drawing"] a [temp] on the [target.name]..." + if(instant) + playsound(user.loc, 'sound/effects/spray.ogg', 5, 1, 5) + if((instant>0) || do_after(user, 50, target = target)) + + //Gang functions + if(gangID) + //Delete any old markings on this tile, including other gang tags + if(!(locate(/obj/effect/decal/cleanable/crayon/gang) in target)) //Ignore the check if the tile being sprayed has a gang tag + if(territory_claimed(territory, user)) + return + for(var/obj/effect/decal/cleanable/crayon/old_marking in target) + qdel(old_marking) + new /obj/effect/decal/cleanable/crayon/gang(target,gangID,"graffiti",graf_rot) + user << "You tagged [territory] for your gang!" + + else + new /obj/effect/decal/cleanable/crayon(target,paint_color,drawtype,temp,graf_rot) + + user << "You finish [instant ? "spraying" : "drawing"] \the [temp]." + if(instant<0) + playsound(user.loc, 'sound/effects/spray.ogg', 5, 1, 5) + if(uses < 0) + return + uses = max(0,uses-1) + if(!uses) + user << "There is no more of [src.name] left!" + if(!instant) + qdel(src) + return + +/obj/item/toy/crayon/attack(mob/M, mob/user) + if(edible && (M == user)) + user << "You take a bite of the [src.name]. Delicious!" + user.nutrition += 5 + if(uses < 0) + return + uses = max(0,uses-5) + if(!uses) + user << "There is no more of [src.name] left!" + qdel(src) + else + ..() + +/obj/item/toy/crayon/proc/territory_claimed(area/territory,mob/user) + var/occupying_gang + for(var/datum/gang/G in ticker.mode.gangs) + if(territory.type in (G.territory|G.territory_new)) + occupying_gang = G.name + break + if(occupying_gang) + user << "[territory] has already been tagged by the [occupying_gang] gang! You must get rid of or spray over the old tag first!" + return 1 + return 0 + +/* + * Snap pops + */ + +/obj/item/toy/snappop + name = "snap pop" + desc = "Wow!" + icon = 'icons/obj/toy.dmi' + icon_state = "snappop" + w_class = 1 + +/obj/item/toy/snappop/proc/pop_burst() + var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread + s.set_up(3, 1, src) + s.start() + new /obj/effect/decal/cleanable/ash(loc) + visible_message("The [src.name] explodes!","You hear a snap!") + playsound(src, 'sound/effects/snap.ogg', 50, 1) + qdel(src) + +/obj/item/toy/snappop/fire_act() + pop_burst() + return + +/obj/item/toy/snappop/throw_impact(atom/hit_atom) + if(!..()) + pop_burst() + + +/obj/item/toy/snappop/Crossed(H as mob|obj) + if((ishuman(H))) //i guess carp and shit shouldn't set them off + var/mob/living/carbon/M = H + if(M.m_intent == "run") + M << "You step on the snap pop!" + + var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread + s.set_up(2, 0, src) + s.start() + new /obj/effect/decal/cleanable/ash(src.loc) + src.visible_message("The [src.name] explodes!","You hear a snap!") + playsound(src, 'sound/effects/snap.ogg', 50, 1) + qdel(src) + +/* + * Mech prizes + */ +/obj/item/toy/prize + icon = 'icons/obj/toy.dmi' + icon_state = "ripleytoy" + var/cooldown = 0 + var/quiet = 0 + +//all credit to skasi for toy mech fun ideas +/obj/item/toy/prize/attack_self(mob/user) + if(!cooldown) + user << "You play with [src]." + cooldown = 1 + spawn(30) cooldown = 0 + if (!quiet) + playsound(user, 'sound/mecha/mechstep.ogg', 20, 1) + return + ..() + +/obj/item/toy/prize/attack_hand(mob/user) + if(loc == user) + if(!cooldown) + user << "You play with [src]." + cooldown = 1 + spawn(30) cooldown = 0 + if (!quiet) + playsound(user, 'sound/mecha/mechturn.ogg', 20, 1) + return + ..() + +/obj/item/toy/prize/ripley + name = "toy Ripley" + desc = "Mini-Mecha action figure! Collect them all! 1/12." + +/obj/item/toy/prize/fireripley + name = "toy firefighting Ripley" + desc = "Mini-Mecha action figure! Collect them all! 2/12." + icon_state = "fireripleytoy" + +/obj/item/toy/prize/deathripley + name = "toy deathsquad Ripley" + desc = "Mini-Mecha action figure! Collect them all! 3/12." + icon_state = "deathripleytoy" + +/obj/item/toy/prize/gygax + name = "toy Gygax" + desc = "Mini-Mecha action figure! Collect them all! 4/12." + icon_state = "gygaxtoy" + +/obj/item/toy/prize/durand + name = "toy Durand" + desc = "Mini-Mecha action figure! Collect them all! 5/12." + icon_state = "durandprize" + +/obj/item/toy/prize/honk + name = "toy H.O.N.K." + desc = "Mini-Mecha action figure! Collect them all! 6/12." + icon_state = "honkprize" + +/obj/item/toy/prize/marauder + name = "toy Marauder" + desc = "Mini-Mecha action figure! Collect them all! 7/12." + icon_state = "marauderprize" + +/obj/item/toy/prize/seraph + name = "toy Seraph" + desc = "Mini-Mecha action figure! Collect them all! 8/12." + icon_state = "seraphprize" + +/obj/item/toy/prize/mauler + name = "toy Mauler" + desc = "Mini-Mecha action figure! Collect them all! 9/12." + icon_state = "maulerprize" + +/obj/item/toy/prize/odysseus + name = "toy Odysseus" + desc = "Mini-Mecha action figure! Collect them all! 10/12." + icon_state = "odysseusprize" + +/obj/item/toy/prize/phazon + name = "toy Phazon" + desc = "Mini-Mecha action figure! Collect them all! 11/12." + icon_state = "phazonprize" + +/obj/item/toy/prize/reticence + name = "toy Reticence" + desc = "Mini-Mecha action figure! Collect them all! 12/12." + icon_state = "reticenceprize" + quiet = 1 + +/* + * AI core prizes + */ +/obj/item/toy/AI + name = "toy AI" + desc = "A little toy model AI core with real law announcing action!" + icon = 'icons/obj/toy.dmi' + icon_state = "AI" + w_class = 2 + var/cooldown = 0 + +/obj/item/toy/AI/attack_self(mob/user) + if(!cooldown) //for the sanity of everyone + var/message = generate_ion_law() + user << "You press the button on [src]." + playsound(user, 'sound/machines/click.ogg', 20, 1) + src.loc.visible_message("\icon[src] [message]") + cooldown = 1 + spawn(30) cooldown = 0 + return + ..() + +/obj/item/toy/owl + name = "owl action figure" + desc = "An action figure modeled after 'The Owl', defender of justice." + icon = 'icons/obj/toy.dmi' + icon_state = "owlprize" + w_class = 2 + var/cooldown = 0 + +/obj/item/toy/owl/attack_self(mob/user) + if(!cooldown) //for the sanity of everyone + var/message = pick("You won't get away this time, Griffin!", "Stop right there, criminal!", "Hoot! Hoot!", "I am the night!") + user << "You pull the string on the [src]." + playsound(user, 'sound/machines/click.ogg', 20, 1) + src.loc.visible_message("\icon[src] [message]") + cooldown = 1 + spawn(30) cooldown = 0 + return + ..() + +/obj/item/toy/griffin + name = "griffin action figure" + desc = "An action figure modeled after 'The Griffin', criminal mastermind." + icon = 'icons/obj/toy.dmi' + icon_state = "griffinprize" + w_class = 2 + var/cooldown = 0 + +/obj/item/toy/griffin/attack_self(mob/user) + if(!cooldown) //for the sanity of everyone + var/message = pick("You can't stop me, Owl!", "My plan is flawless! The vault is mine!", "Caaaawwww!", "You will never catch me!") + user << "You pull the string on the [src]." + playsound(user, 'sound/machines/click.ogg', 20, 1) + src.loc.visible_message("\icon[src] [message]") + cooldown = 1 + spawn(30) cooldown = 0 + return + ..() + + +/* +|| A Deck of Cards for playing various games of chance || +*/ + + + +/obj/item/toy/cards + burn_state = FLAMMABLE + burntime = 5 + var/parentdeck = null + var/deckstyle = "nanotrasen" + var/card_hitsound = null + var/card_force = 0 + var/card_throwforce = 0 + var/card_throw_speed = 3 + var/card_throw_range = 7 + var/list/card_attack_verb = list("attacked") + +/obj/item/toy/cards/New() + ..() + +/obj/item/toy/cards/proc/apply_card_vars(obj/item/toy/cards/newobj, obj/item/toy/cards/sourceobj) // Applies variables for supporting multiple types of card deck + if(!istype(sourceobj)) + return + +/obj/item/toy/cards/deck + name = "deck of cards" + desc = "A deck of space-grade playing cards." + icon = 'icons/obj/toy.dmi' + deckstyle = "nanotrasen" + icon_state = "deck_nanotrasen_full" + w_class = 2 + var/cooldown = 0 + var/obj/machinery/computer/holodeck/holo = null // Holodeck cards should not be infinite + var/list/cards = list() + +/obj/item/toy/cards/deck/New() + ..() + icon_state = "deck_[deckstyle]_full" + for(var/i = 2; i <= 10; i++) + cards += "[i] of Hearts" + cards += "[i] of Spades" + cards += "[i] of Clubs" + cards += "[i] of Diamonds" + cards += "King of Hearts" + cards += "King of Spades" + cards += "King of Clubs" + cards += "King of Diamonds" + cards += "Queen of Hearts" + cards += "Queen of Spades" + cards += "Queen of Clubs" + cards += "Queen of Diamonds" + cards += "Jack of Hearts" + cards += "Jack of Spades" + cards += "Jack of Clubs" + cards += "Jack of Diamonds" + cards += "Ace of Hearts" + cards += "Ace of Spades" + cards += "Ace of Clubs" + cards += "Ace of Diamonds" + + +/obj/item/toy/cards/deck/attack_hand(mob/user) + if(user.lying) + return + var/choice = null + if(cards.len == 0) + src.icon_state = "deck_[deckstyle]_empty" + user << "There are no more cards to draw!" + return + var/obj/item/toy/cards/singlecard/H = new/obj/item/toy/cards/singlecard(user.loc) + if(holo) + holo.spawned += H // track them leaving the holodeck + choice = cards[1] + H.cardname = choice + H.parentdeck = src + var/O = src + H.apply_card_vars(H,O) + src.cards -= choice + H.pickup(user) + user.put_in_active_hand(H) + user.visible_message("[user] draws a card from the deck.", "You draw a card from the deck.") + if(cards.len > 26) + src.icon_state = "deck_[deckstyle]_full" + else if(cards.len > 10) + src.icon_state = "deck_[deckstyle]_half" + else if(cards.len > 1) + src.icon_state = "deck_[deckstyle]_low" + +/obj/item/toy/cards/deck/attack_self(mob/user) + if(cooldown < world.time - 50) + cards = shuffle(cards) + playsound(user, 'sound/items/cardshuffle.ogg', 50, 1) + user.visible_message("[user] shuffles the deck.", "You shuffle the deck.") + cooldown = world.time + +/obj/item/toy/cards/deck/attackby(obj/item/toy/cards/singlecard/C, mob/living/user, params) + ..() + if(istype(C)) + if(C.parentdeck == src) + if(!user.unEquip(C)) + user << "The card is stuck to your hand, you can't add it to the deck!" + return + src.cards += C.cardname + user.visible_message("[user] adds a card to the bottom of the deck.","You add the card to the bottom of the deck.") + qdel(C) + else + user << "You can't mix cards from other decks!" + if(cards.len > 26) + src.icon_state = "deck_[deckstyle]_full" + else if(cards.len > 10) + src.icon_state = "deck_[deckstyle]_half" + else if(cards.len > 1) + src.icon_state = "deck_[deckstyle]_low" + + +/obj/item/toy/cards/deck/attackby(obj/item/toy/cards/cardhand/C, mob/living/user, params) + ..() + if(istype(C)) + if(C.parentdeck == src) + if(!user.unEquip(C)) + user << "The hand of cards is stuck to your hand, you can't add it to the deck!" + return + src.cards += C.currenthand + user.visible_message("[user] puts their hand of cards in the deck.", "You put the hand of cards in the deck.") + qdel(C) + else + user << "You can't mix cards from other decks!" + if(cards.len > 26) + src.icon_state = "deck_[deckstyle]_full" + else if(cards.len > 10) + src.icon_state = "deck_[deckstyle]_half" + else if(cards.len > 1) + src.icon_state = "deck_[deckstyle]_low" + +/obj/item/toy/cards/deck/MouseDrop(atom/over_object) + var/mob/M = usr + if(!ishuman(usr) || usr.incapacitated() || usr.lying) + return + if(Adjacent(usr)) + if(over_object == M && loc != M) + M.put_in_hands(src) + usr << "You pick up the deck." + + else if(istype(over_object, /obj/screen)) + switch(over_object.name) + if("l_hand") + if(!remove_item_from_storage(M)) + M.unEquip(src) + M.put_in_l_hand(src) + else if("r_hand") + if(!remove_item_from_storage(M)) + M.unEquip(src) + M.put_in_r_hand(src) + usr << "You pick up the deck." + else + usr << "You can't reach it from here!" + + + +/obj/item/toy/cards/cardhand + name = "hand of cards" + desc = "A number of cards not in a deck, customarily held in ones hand." + icon = 'icons/obj/toy.dmi' + icon_state = "nanotrasen_hand2" + w_class = 1 + var/list/currenthand = list() + var/choice = null + + +/obj/item/toy/cards/cardhand/attack_self(mob/user) + user.set_machine(src) + interact(user) + +/obj/item/toy/cards/cardhand/interact(mob/user) + var/dat = "You have:
" + for(var/t in currenthand) + dat += "A [t].
" + dat += "Which card will you remove next?" + var/datum/browser/popup = new(user, "cardhand", "Hand of Cards", 400, 240) + popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) + popup.set_content(dat) + popup.open() + + +/obj/item/toy/cards/cardhand/Topic(href, href_list) + if(..()) + return + if(usr.stat || !ishuman(usr) || !usr.canmove) + return + var/mob/living/carbon/human/cardUser = usr + var/O = src + if(href_list["pick"]) + if (cardUser.get_item_by_slot(slot_l_hand) == src || cardUser.get_item_by_slot(slot_r_hand) == src) + var/choice = href_list["pick"] + var/obj/item/toy/cards/singlecard/C = new/obj/item/toy/cards/singlecard(cardUser.loc) + src.currenthand -= choice + C.parentdeck = src.parentdeck + C.cardname = choice + C.apply_card_vars(C,O) + C.pickup(cardUser) + cardUser.put_in_any_hand_if_possible(C) + cardUser.visible_message("[cardUser] draws a card from \his hand.", "You take the [C.cardname] from your hand.") + + interact(cardUser) + if(src.currenthand.len < 3) + src.icon_state = "[deckstyle]_hand2" + else if(src.currenthand.len < 4) + src.icon_state = "[deckstyle]_hand3" + else if(src.currenthand.len < 5) + src.icon_state = "[deckstyle]_hand4" + if(src.currenthand.len == 1) + var/obj/item/toy/cards/singlecard/N = new/obj/item/toy/cards/singlecard(src.loc) + N.parentdeck = src.parentdeck + N.cardname = src.currenthand[1] + N.apply_card_vars(N,O) + cardUser.unEquip(src) + N.pickup(cardUser) + cardUser.put_in_any_hand_if_possible(N) + cardUser << "You also take [currenthand[1]] and hold it." + cardUser << browse(null, "window=cardhand") + qdel(src) + return + +/obj/item/toy/cards/cardhand/attackby(obj/item/toy/cards/singlecard/C, mob/living/user, params) + if(istype(C)) + if(C.parentdeck == src.parentdeck) + src.currenthand += C.cardname + user.unEquip(C) + user.visible_message("[user] adds a card to their hand.", "You add the [C.cardname] to your hand.") + interact(user) + if(currenthand.len > 4) + src.icon_state = "[deckstyle]_hand5" + else if(currenthand.len > 3) + src.icon_state = "[deckstyle]_hand4" + else if(currenthand.len > 2) + src.icon_state = "[deckstyle]_hand3" + qdel(C) + else + user << "You can't mix cards from other decks!" + +/obj/item/toy/cards/cardhand/apply_card_vars(obj/item/toy/cards/newobj,obj/item/toy/cards/sourceobj) + ..() + newobj.deckstyle = sourceobj.deckstyle + newobj.icon_state = "[deckstyle]_hand2" // Another dumb hack, without this the hand is invisible (or has the default deckstyle) until another card is added. + newobj.card_hitsound = sourceobj.card_hitsound + newobj.card_force = sourceobj.card_force + newobj.card_throwforce = sourceobj.card_throwforce + newobj.card_throw_speed = sourceobj.card_throw_speed + newobj.card_throw_range = sourceobj.card_throw_range + newobj.card_attack_verb = sourceobj.card_attack_verb + if(sourceobj.burn_state == FIRE_PROOF) + newobj.burn_state = FIRE_PROOF + +/obj/item/toy/cards/singlecard + name = "card" + desc = "a card" + icon = 'icons/obj/toy.dmi' + icon_state = "singlecard_nanotrasen_down" + w_class = 1 + var/cardname = null + var/flipped = 0 + pixel_x = -5 + + +/obj/item/toy/cards/singlecard/examine(mob/user) + if(ishuman(user)) + var/mob/living/carbon/human/cardUser = user + if(cardUser.get_item_by_slot(slot_l_hand) == src || cardUser.get_item_by_slot(slot_r_hand) == src) + cardUser.visible_message("[cardUser] checks \his card.", "The card reads: [src.cardname]") + else + cardUser << "You need to have the card in your hand to check it!" + + +/obj/item/toy/cards/singlecard/verb/Flip() + set name = "Flip Card" + set category = "Object" + set src in range(1) + if(usr.stat || !ishuman(usr) || !usr.canmove || usr.restrained()) + return + if(!flipped) + src.flipped = 1 + if (cardname) + src.icon_state = "sc_[cardname]_[deckstyle]" + src.name = src.cardname + else + src.icon_state = "sc_Ace of Spades_[deckstyle]" + src.name = "What Card" + src.pixel_x = 5 + else if(flipped) + src.flipped = 0 + src.icon_state = "singlecard_down_[deckstyle]" + src.name = "card" + src.pixel_x = -5 + +/obj/item/toy/cards/singlecard/attackby(obj/item/I, mob/living/user, params) + if(istype(I, /obj/item/toy/cards/singlecard/)) + var/obj/item/toy/cards/singlecard/C = I + if(C.parentdeck == src.parentdeck) + var/obj/item/toy/cards/cardhand/H = new/obj/item/toy/cards/cardhand(user.loc) + H.currenthand += C.cardname + H.currenthand += src.cardname + H.parentdeck = C.parentdeck + H.apply_card_vars(H,C) + user.unEquip(C) + H.pickup(user) + user.put_in_active_hand(H) + user << "You combine the [C.cardname] and the [src.cardname] into a hand." + qdel(C) + qdel(src) + else + user << "You can't mix cards from other decks!" + + if(istype(I, /obj/item/toy/cards/cardhand/)) + var/obj/item/toy/cards/cardhand/H = I + if(H.parentdeck == parentdeck) + H.currenthand += cardname + user.unEquip(src) + user.visible_message("[user] adds a card to \his hand.", "You add the [cardname] to your hand.") + H.interact(user) + if(H.currenthand.len > 4) + H.icon_state = "[deckstyle]_hand5" + else if(H.currenthand.len > 3) + H.icon_state = "[deckstyle]_hand4" + else if(H.currenthand.len > 2) + H.icon_state = "[deckstyle]_hand3" + qdel(src) + else + user << "You can't mix cards from other decks!" + + +/obj/item/toy/cards/singlecard/attack_self(mob/user) + if(usr.stat || !ishuman(usr) || !usr.canmove || usr.restrained()) + return + Flip() + +/obj/item/toy/cards/singlecard/apply_card_vars(obj/item/toy/cards/singlecard/newobj,obj/item/toy/cards/sourceobj) + ..() + newobj.deckstyle = sourceobj.deckstyle + newobj.icon_state = "singlecard_down_[deckstyle]" // Without this the card is invisible until flipped. It's an ugly hack, but it works. + newobj.card_hitsound = sourceobj.card_hitsound + newobj.hitsound = newobj.card_hitsound + newobj.card_force = sourceobj.card_force + newobj.force = newobj.card_force + newobj.card_throwforce = sourceobj.card_throwforce + newobj.throwforce = newobj.card_throwforce + newobj.card_throw_speed = sourceobj.card_throw_speed + newobj.throw_speed = newobj.card_throw_speed + newobj.card_throw_range = sourceobj.card_throw_range + newobj.throw_range = newobj.card_throw_range + newobj.card_attack_verb = sourceobj.card_attack_verb + newobj.attack_verb = newobj.card_attack_verb + + +/* +|| Syndicate playing cards, for pretending you're Gambit and playing poker for the nuke disk. || +*/ + +/obj/item/toy/cards/deck/syndicate + name = "suspicious looking deck of cards" + desc = "A deck of space-grade playing cards. They seem unusually rigid." + deckstyle = "syndicate" + card_hitsound = 'sound/weapons/bladeslice.ogg' + card_force = 5 + card_throwforce = 10 + card_throw_speed = 3 + card_throw_range = 7 + card_attack_verb = list("attacked", "sliced", "diced", "slashed", "cut") + burn_state = FIRE_PROOF + +/* + * Fake nuke + */ + +/obj/item/toy/nuke + name = "\improper Nuclear Fission Explosive toy" + desc = "A plastic model of a Nuclear Fission Explosive." + icon = 'icons/obj/toy.dmi' + icon_state = "nuketoyidle" + w_class = 2 + var/cooldown = 0 + +/obj/item/toy/nuke/attack_self(mob/user) + if (cooldown < world.time) + cooldown = world.time + 1800 //3 minutes + user.visible_message("[user] presses a button on [src].", "You activate [src], it plays a loud noise!", "You hear the click of a button.") + spawn(5) //gia said so + icon_state = "nuketoy" + playsound(src, 'sound/machines/Alarm.ogg', 100, 0, surround = 0) + sleep(135) + icon_state = "nuketoycool" + sleep(cooldown - world.time) + icon_state = "nuketoyidle" + else + var/timeleft = (cooldown - world.time) + user << "Nothing happens, and '[round(timeleft/10)]' appears on a small display." + +/* + * Fake meteor + */ + +/obj/item/toy/minimeteor + name = "\improper Mini-Meteor" + desc = "Relive the excitement of a meteor shower! SweetMeat-eor. Co is not responsible for any injuries, headaches or hearing loss caused by Mini-Meteor?" + icon = 'icons/obj/toy.dmi' + icon_state = "minimeteor" + w_class = 2 + +/obj/item/toy/minimeteor/throw_impact(atom/hit_atom) + if(!..()) + playsound(src, 'sound/effects/meteorimpact.ogg', 40, 1) + for(var/mob/M in ultra_range(10, src)) + if(!M.stat && !istype(M, /mob/living/silicon/ai))\ + shake_camera(M, 3, 1) + qdel(src) + +/* + * Carp plushie + */ + +/obj/item/toy/carpplushie + name = "space carp plushie" + desc = "An adorable stuffed toy that resembles a space carp." + icon = 'icons/obj/toy.dmi' + icon_state = "carpplushie" + item_state = "carp_plushie" + w_class = 2 + attack_verb = list("bitten", "eaten", "fin slapped") + burn_state = FLAMMABLE + var/bitesound = 'sound/weapons/bite.ogg' + +//Attack mob +/obj/item/toy/carpplushie/attack(mob/M, mob/user) + playsound(loc, bitesound, 20, 1) //Play bite sound in local area + return ..() + +//Attack self +/obj/item/toy/carpplushie/attack_self(mob/user) + playsound(src.loc, bitesound, 20, 1) + user << "You pet [src]. D'awww." + return ..() + +/* + * Toy big red button + */ +/obj/item/toy/redbutton + name = "big red button" + desc = "A big, plastic red button. Reads 'From HonkCo Pranks?' on the back." + icon = 'icons/obj/assemblies.dmi' + icon_state = "bigred" + w_class = 2 + var/cooldown = 0 + +/obj/item/toy/redbutton/attack_self(mob/user) + if (cooldown < world.time) + cooldown = (world.time + 300) // Sets cooldown at 30 seconds + user.visible_message("[user] presses the big red button.", "You press the button, it plays a loud noise!", "The button clicks loudly.") + playsound(src, 'sound/effects/explosionfar.ogg', 50, 0, surround = 0) + for(var/mob/M in ultra_range(10, src)) // Checks range + if(!M.stat && !istype(M, /mob/living/silicon/ai)) // Checks to make sure whoever's getting shaken is alive/not the AI + sleep(8) // Short delay to match up with the explosion sound + shake_camera(M, 2, 1) // Shakes player camera 2 squares for 1 second. + + else + user << "Nothing happens." + +/* + * Snowballs + */ + +/obj/item/toy/snowball + name = "snowball" + desc = "A compact ball of snow. Good for throwing at people." + icon = 'icons/obj/toy.dmi' + icon_state = "snowball" + throwforce = 12 //pelt your enemies to death with lumps of snow + +/obj/item/toy/snowball/afterattack(atom/target as mob|obj|turf|area, mob/user) + user.drop_item() + src.throw_at(target, throw_range, throw_speed) + +/obj/item/toy/snowball/throw_impact(atom/hit_atom) + if(!..()) + playsound(src, 'sound/effects/pop.ogg', 20, 1) + qdel(src) + +/* + * Beach ball + */ +/obj/item/toy/beach_ball + icon = 'icons/misc/beach.dmi' + icon_state = "ball" + name = "beach ball" + item_state = "beachball" + w_class = 4 //Stops people from hiding it in their bags/pockets + +/obj/item/toy/beach_ball/afterattack(atom/target as mob|obj|turf|area, mob/user) + user.drop_item() + src.throw_at(target, throw_range, throw_speed) + +/* + * Xenomorph action figure + */ + +/obj/item/toy/toy_xeno + icon = 'icons/obj/toy.dmi' + icon_state = "toy_xeno" + name = "xenomorph action figure" + desc = "MEGA presents the new Xenos Isolated action figure! Comes complete with realistic sounds! Pull back string to use." + w_class = 2 + var/cooldown = 0 + +/obj/item/toy/toy_xeno/attack_self(mob/user) + if(cooldown <= world.time) + cooldown = (world.time + 50) //5 second cooldown + user.visible_message("[user] pulls back the string on [src].") + icon_state = "[initial(icon_state)]_used" + sleep(5) + audible_message("\icon[src] Hiss!") + var/list/possible_sounds = list('sound/voice/hiss1.ogg', 'sound/voice/hiss2.ogg', 'sound/voice/hiss3.ogg', 'sound/voice/hiss4.ogg') + var/chosen_sound = pick(possible_sounds) + playsound(get_turf(src), chosen_sound, 50, 1) + spawn(45) + if(src) + icon_state = "[initial(icon_state)]" + else + user << "The string on [src] hasn't rewound all the way!" + return + +// TOY MOUSEYS :3 :3 :3 + +/obj/item/toy/cattoy + name = "toy mouse" + desc = "A colorful toy mouse!" + icon = 'icons/obj/toy.dmi' + icon_state = "toy_mouse" + w_class = 2.0 + var/cooldown = 0 + burn_state = FLAMMABLE + + +/* + * Action Figures + */ + +/obj/item/toy/figure + name = "Non-Specific Action Figure action figure" + desc = null + icon = 'icons/obj/toy.dmi' + icon_state = "nuketoy" + var/cooldown = 0 + var/toysay = "What the fuck did you do?" + var/toysound = 'sound/machines/click.ogg' + +/obj/item/toy/figure/New() + desc = "A \"Space Life\" brand [src]." + +/obj/item/toy/figure/attack_self(mob/user as mob) + if(cooldown <= world.time) + cooldown = world.time + 50 + user << "The [src] says \"[toysay]\"" + playsound(user, toysound, 20, 1) + +/obj/item/toy/figure/cmo + name = "Chief Medical Officer action figure" + icon_state = "cmo" + toysay = "Suit sensors!" + +/obj/item/toy/figure/assistant + name = "Assistant action figure" + icon_state = "assistant" + toysay = "Grey tide world wide!" + +/obj/item/toy/figure/atmos + name = "Atmospheric Technician action figure" + icon_state = "atmos" + toysay = "Glory to Atmosia!" + +/obj/item/toy/figure/bartender + name = "Bartender action figure" + icon_state = "bartender" + toysay = "Where is Pun Pun?" + +/obj/item/toy/figure/borg + name = "Cyborg action figure" + icon_state = "borg" + toysay = "I. LIVE. AGAIN." + toysound = 'sound/voice/liveagain.ogg' + +/obj/item/toy/figure/botanist + name = "Botanist action figure" + icon_state = "botanist" + toysay = "Dude, I see colors..." + +/obj/item/toy/figure/captain + name = "Captain action figure" + icon_state = "captain" + toysay = "Any heads of staff?" + +/obj/item/toy/figure/cargotech + name = "Cargo Technician action figure" + icon_state = "cargotech" + toysay = "For Cargonia!" + +/obj/item/toy/figure/ce + name = "Chief Engineer action figure" + icon_state = "ce" + toysay = "Wire the solars!" + +/obj/item/toy/figure/chaplain + name = "Chaplain action figure" + icon_state = "chaplain" + toysay = "Praise Space Jesus!" + +/obj/item/toy/figure/chef + name = "Chef action figure" + icon_state = "chef" + toysay = "Pun-Pun is a tasty burger." + +/obj/item/toy/figure/chemist + name = "Chemist action figure" + icon_state = "chemist" + toysay = "Get your pills!" + +/obj/item/toy/figure/clown + name = "Clown action figure" + icon_state = "clown" + toysay = "Honk!" + toysound = 'sound/items/bikehorn.ogg' + +/obj/item/toy/figure/ian + name = "Ian action figure" + icon_state = "ian" + toysay = "Arf!" + +/obj/item/toy/figure/detective + name = "Detective action figure" + icon_state = "detective" + toysay = "This airlock has grey jumpsuit and insulated glove fibers on it." + +/obj/item/toy/figure/dsquad + name = "Death Squad Officer action figure" + icon_state = "dsquad" + toysay = "Eliminate all threats!" + +/obj/item/toy/figure/engineer + name = "Engineer action figure" + icon_state = "engineer" + toysay = "Oh god, the singularity is loose!" + +/obj/item/toy/figure/geneticist + name = "Geneticist action figure" + icon_state = "geneticist" + toysay = "Smash!" + +/obj/item/toy/figure/hop + name = "Head of Personel action figure" + icon_state = "hop" + toysay = "Giving out all access!" + +/obj/item/toy/figure/hos + name = "Head of Security action figure" + icon_state = "hos" + toysay = "Get the justice chamber ready, I think we got a joker here." + +/obj/item/toy/figure/qm + name = "Quartermaster action figure" + icon_state = "qm" + toysay = "Please sign this form in triplicate and we will see about geting you a welding mask within 3 business days." + +/obj/item/toy/figure/janitor + name = "Janitor action figure" + icon_state = "janitor" + toysay = "Look at the signs, you idiot." + +/obj/item/toy/figure/lawyer + name = "Lawyer action figure" + icon_state = "lawyer" + toysay = "My client is a dirty traitor!" + +/obj/item/toy/figure/librarian + name = "Librarian action figure" + icon_state = "librarian" + toysay = "One day while..." + +/obj/item/toy/figure/md + name = "Medical Doctor action figure" + icon_state = "md" + toysay = "The patient is already dead!" + +/obj/item/toy/figure/mime + name = "Mime action figure" + icon_state = "mime" + toysay = "..." + toysound = null + +/obj/item/toy/figure/miner + name = "Shaft Miner action figure" + icon_state = "miner" + toysay = "Oh god it's eating my intestines!" + +/obj/item/toy/figure/ninja + name = "Ninja action figure" + icon_state = "ninja" + toysay = "Oh god! Stop shooting, I'm friendly!" + +/obj/item/toy/figure/wizard + name = "Wizard action figure" + icon_state = "wizard" + toysay = "Ei Nath!" + toysound = 'sound/magic/Disintegrate.ogg' + +/obj/item/toy/figure/rd + name = "Research Director action figure" + icon_state = "rd" + toysay = "Blowing all of the borgs!" + +/obj/item/toy/figure/roboticist + name = "Roboticist action figure" + icon_state = "roboticist" + toysay = "Big stompy mechs!" + toysound = 'sound/mecha/mechstep.ogg' + +/obj/item/toy/figure/scientist + name = "Scientist action figure" + icon_state = "scientist" + toysay = "For science!" + toysound = 'sound/effects/explosionfar.ogg' + +/obj/item/toy/figure/syndie + name = "Nuclear Operative action figure" + icon_state = "syndie" + toysay = "Get that fucking disk!" + +/obj/item/toy/figure/secofficer + name = "Security Officer action figure" + icon_state = "secofficer" + toysay = "I am the law!" + toysound = 'sound/voice/complionator/dredd.ogg' + +/obj/item/toy/figure/virologist + name = "Virologist action figure" + icon_state = "virologist" + toysay = "The cure is potassium!" + +/obj/item/toy/figure/warden + name = "Warden action figure" + icon_state = "warden" + toysay = "Seventeen minutes for coughing at an officer!" diff --git a/code/game/objects/items/weapons/gift_wrappaper.dm b/code/game/objects/items/weapons/gift_wrappaper.dm index bde9fa7fa01..aa786407444 100644 --- a/code/game/objects/items/weapons/gift_wrappaper.dm +++ b/code/game/objects/items/weapons/gift_wrappaper.dm @@ -1,94 +1,94 @@ -/* Gifts and wrapping paper - * Contains: - * Gifts - * Wrapping Paper - */ - -/* - * Gifts - */ -/obj/item/weapon/a_gift - name = "gift" - desc = "PRESENTS!!!! eek!" - icon = 'icons/obj/storage.dmi' - icon_state = "giftcrate3" - item_state = "gift1" - burn_state = FLAMMABLE - -/obj/item/weapon/a_gift/New() - ..() - pixel_x = rand(-10,10) - pixel_y = rand(-10,10) - icon_state = "giftcrate[rand(1,5)]" - -/obj/item/weapon/a_gift/attack_self(mob/M) - if(M && M.mind && M.mind.special_role == "Santa") - M << "You're supposed to be spreading gifts, not opening them yourself!" - return - - var/gift_type_list = list(/obj/item/weapon/sord, - /obj/item/weapon/storage/wallet, - /obj/item/weapon/storage/photo_album, - /obj/item/weapon/storage/box/snappops, - /obj/item/weapon/storage/crayons, - /obj/item/weapon/storage/backpack/holding, - /obj/item/weapon/storage/belt/champion, - /obj/item/weapon/soap/deluxe, - /obj/item/weapon/pickaxe/diamond, - /obj/item/weapon/pen/invisible, - /obj/item/weapon/lipstick/random, - /obj/item/weapon/grenade/smokebomb, - /obj/item/weapon/grown/corncob, - /obj/item/weapon/poster/contraband, - /obj/item/weapon/poster/legit, - /obj/item/weapon/book/manual/barman_recipes, - /obj/item/weapon/book/manual/chef_recipes, - /obj/item/weapon/bikehorn, - /obj/item/toy/beach_ball, - /obj/item/toy/beach_ball/holoball, - /obj/item/weapon/banhammer, - /obj/item/weapon/reagent_containers/food/snacks/grown/ambrosia/deus, - /obj/item/weapon/reagent_containers/food/snacks/grown/ambrosia/vulgaris, - /obj/item/device/paicard, - /obj/item/device/instrument/violin, - /obj/item/device/instrument/guitar, - /obj/item/weapon/storage/belt/utility/full, - /obj/item/clothing/tie/horrible, - /obj/item/clothing/suit/jacket/leather, - /obj/item/clothing/suit/jacket/leather/overcoat, - /obj/item/clothing/suit/poncho, - /obj/item/clothing/suit/poncho/green, - /obj/item/clothing/suit/poncho/red, - /obj/item/clothing/suit/snowman, - /obj/item/clothing/head/snowman) - - gift_type_list += subtypesof(/obj/item/clothing/head/collectable) - gift_type_list += subtypesof(/obj/item/toy) - (((typesof(/obj/item/toy/cards) - /obj/item/toy/cards/deck) + /obj/item/toy/figure + /obj/item/toy/ammo)) //All toys, except for abstract types and syndicate cards. - - var/gift_type = pick(gift_type_list) - - if(!ispath(gift_type,/obj/item)) return - - var/obj/item/I = new gift_type(M) - M.unEquip(src, 1) - M.put_in_hands(I) - I.add_fingerprint(M) - qdel(src) - return - - -/* - * Wrapping Paper - */ -/obj/item/stack/wrapping_paper - name = "wrapping paper" - desc = "You can use this to wrap items in." - icon = 'icons/obj/items.dmi' - icon_state = "wrap_paper" - flags = NOBLUDGEON - amount = 25 - max_amount = 25 - burn_state = FLAMMABLE - -/obj/item/stack/wrapping_paper/attack_self(mob/user) - user << "You need to use it on a package that has already been wrapped!" +/* Gifts and wrapping paper + * Contains: + * Gifts + * Wrapping Paper + */ + +/* + * Gifts + */ +/obj/item/weapon/a_gift + name = "gift" + desc = "PRESENTS!!!! eek!" + icon = 'icons/obj/storage.dmi' + icon_state = "giftcrate3" + item_state = "gift1" + burn_state = FLAMMABLE + +/obj/item/weapon/a_gift/New() + ..() + pixel_x = rand(-10,10) + pixel_y = rand(-10,10) + icon_state = "giftcrate[rand(1,5)]" + +/obj/item/weapon/a_gift/attack_self(mob/M) + if(M && M.mind && M.mind.special_role == "Santa") + M << "You're supposed to be spreading gifts, not opening them yourself!" + return + + var/gift_type_list = list(/obj/item/weapon/sord, + /obj/item/weapon/storage/wallet, + /obj/item/weapon/storage/photo_album, + /obj/item/weapon/storage/box/snappops, + /obj/item/weapon/storage/crayons, + /obj/item/weapon/storage/backpack/holding, + /obj/item/weapon/storage/belt/champion, + /obj/item/weapon/soap/deluxe, + /obj/item/weapon/pickaxe/diamond, + /obj/item/weapon/pen/invisible, + /obj/item/weapon/lipstick/random, + /obj/item/weapon/grenade/smokebomb, + /obj/item/weapon/grown/corncob, + /obj/item/weapon/poster/contraband, + /obj/item/weapon/poster/legit, + /obj/item/weapon/book/manual/barman_recipes, + /obj/item/weapon/book/manual/chef_recipes, + /obj/item/weapon/bikehorn, + /obj/item/toy/beach_ball, + /obj/item/toy/beach_ball/holoball, + /obj/item/weapon/banhammer, + /obj/item/weapon/reagent_containers/food/snacks/grown/ambrosia/deus, + /obj/item/weapon/reagent_containers/food/snacks/grown/ambrosia/vulgaris, + /obj/item/device/paicard, + /obj/item/device/instrument/violin, + /obj/item/device/instrument/guitar, + /obj/item/weapon/storage/belt/utility/full, + /obj/item/clothing/tie/horrible, + /obj/item/clothing/suit/jacket/leather, + /obj/item/clothing/suit/jacket/leather/overcoat, + /obj/item/clothing/suit/poncho, + /obj/item/clothing/suit/poncho/green, + /obj/item/clothing/suit/poncho/red, + /obj/item/clothing/suit/snowman, + /obj/item/clothing/head/snowman) + + gift_type_list += subtypesof(/obj/item/clothing/head/collectable) + gift_type_list += subtypesof(/obj/item/toy) - (((typesof(/obj/item/toy/cards) - /obj/item/toy/cards/deck) + /obj/item/toy/figure + /obj/item/toy/ammo)) //All toys, except for abstract types and syndicate cards. + + var/gift_type = pick(gift_type_list) + + if(!ispath(gift_type,/obj/item)) return + + var/obj/item/I = new gift_type(M) + M.unEquip(src, 1) + M.put_in_hands(I) + I.add_fingerprint(M) + qdel(src) + return + + +/* + * Wrapping Paper + */ +/obj/item/stack/wrapping_paper + name = "wrapping paper" + desc = "You can use this to wrap items in." + icon = 'icons/obj/items.dmi' + icon_state = "wrap_paper" + flags = NOBLUDGEON + amount = 25 + max_amount = 25 + burn_state = FLAMMABLE + +/obj/item/stack/wrapping_paper/attack_self(mob/user) + user << "You need to use it on a package that has already been wrapped!" diff --git a/code/game/objects/items/weapons/tanks/tanks.dm b/code/game/objects/items/weapons/tanks/tanks.dm index 5a8bb1245ff..f3d5ead1b88 100644 --- a/code/game/objects/items/weapons/tanks/tanks.dm +++ b/code/game/objects/items/weapons/tanks/tanks.dm @@ -1,280 +1,280 @@ -#define TANK_MAX_RELEASE_PRESSURE (ONE_ATMOSPHERE*3) -#define TANK_MIN_RELEASE_PRESSURE 0 -#define TANK_DEFAULT_RELEASE_PRESSURE (ONE_ATMOSPHERE*O2STANDARD) - -/obj/item/weapon/tank - name = "tank" - icon = 'icons/obj/tank.dmi' - flags = CONDUCT - slot_flags = SLOT_BACK - hitsound = 'sound/weapons/smash.ogg' - - pressure_resistance = ONE_ATMOSPHERE*5 - - force = 5 - throwforce = 10 - throw_speed = 1 - throw_range = 4 - - var/datum/gas_mixture/air_contents = null - var/distribute_pressure = ONE_ATMOSPHERE - var/integrity = 3 - var/volume = 70 - -/obj/item/weapon/tank/suicide_act(mob/user) - var/mob/living/carbon/human/H = user - user.visible_message("[user] is putting the [src]'s valve to their lips! I don't think they're gonna stop!") - playsound(loc, 'sound/effects/spray.ogg', 10, 1, -3) - if (H && !qdeleted(H)) - for(var/obj/item/W in H) - H.unEquip(W) - if(prob(50)) - step(W, pick(alldirs)) - H.hair_style = "Bald" - H.update_hair() - H.blood_max = 5 - gibs(H.loc, H.viruses, H.dna) - H.adjustBruteLoss(1000) //to make the body super-bloody - - return (BRUTELOSS) - -/obj/item/weapon/tank/New() - ..() - - src.air_contents = new /datum/gas_mixture() - src.air_contents.volume = volume //liters - src.air_contents.temperature = T20C - - SSobj.processing |= src - - return - -/obj/item/weapon/tank/Destroy() - if(air_contents) - qdel(air_contents) - - SSobj.processing.Remove(src) - - return ..() - -/obj/item/weapon/tank/examine(mob/user) - var/obj/icon = src - ..() - if (istype(src.loc, /obj/item/assembly)) - icon = src.loc - if (!in_range(src, user)) - if (icon == src) user << "If you want any more information you'll need to get closer." - return - - user << "The pressure gauge reads [src.air_contents.return_pressure()] kPa." - - var/celsius_temperature = src.air_contents.temperature-T0C - var/descriptive - - if (celsius_temperature < 20) - descriptive = "cold" - else if (celsius_temperature < 40) - descriptive = "room temperature" - else if (celsius_temperature < 80) - descriptive = "lukewarm" - else if (celsius_temperature < 100) - descriptive = "warm" - else if (celsius_temperature < 300) - descriptive = "hot" - else - descriptive = "furiously hot" - - user << "It feels [descriptive]." - -/obj/item/weapon/tank/blob_act() - if(prob(50)) - var/turf/location = src.loc - if (!( istype(location, /turf) )) - qdel(src) - - if(src.air_contents) - location.assume_air(air_contents) - - qdel(src) - -/obj/item/weapon/tank/attackby(obj/item/weapon/W, mob/user, params) - ..() - - add_fingerprint(user) - if (istype(src.loc, /obj/item/assembly)) - icon = src.loc - - if ((istype(W, /obj/item/device/analyzer)) && get_dist(user, src) <= 1) - atmosanalyzer_scan(air_contents, user) - - if(istype(W, /obj/item/device/assembly_holder)) - bomb_assemble(W,user) - -/obj/item/weapon/tank/attack_self(mob/user) - if (!user) - return - interact(user) - -/obj/item/weapon/tank/interact(mob/user) - add_fingerprint(user) - ui_interact(user) - -/obj/item/weapon/tank/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, force_open = 0) - ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open) - if (!ui) - ui = new(user, src, ui_key, "tanks", name, 525, 210, state = inventory_state) - ui.open() - -/obj/item/weapon/tank/get_ui_data() - var/mob/living/carbon/location = null - - if(istype(loc, /mob/living/carbon)) - location = loc - else if(istype(loc.loc, /mob/living/carbon)) - location = loc.loc - - var/data = list() - data["tankPressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0) - data["releasePressure"] = round(distribute_pressure ? distribute_pressure : 0) - data["defaultReleasePressure"] = round(TANK_DEFAULT_RELEASE_PRESSURE) - data["minReleasePressure"] = round(TANK_MIN_RELEASE_PRESSURE) - data["maxReleasePressure"] = round(TANK_MAX_RELEASE_PRESSURE) - data["valveOpen"] = 0 - data["maskConnected"] = 0 - - if(istype(location)) - var/mask_check = 0 - - if(location.internal == src) // if tank is current internal - mask_check = 1 - data["valveOpen"] = 1 - else if(src in location) // or if tank is in the mobs possession - if(!location.internal) // and they do not have any active internals - mask_check = 1 - - if(mask_check) - if(location.wear_mask && (location.wear_mask.flags & MASKINTERNALS)) - data["maskConnected"] = 1 - return data - -/obj/item/weapon/tank/ui_act(action, params) - if (..()) - return - - switch(action) - if("pressure") - switch(params["set"]) - if("custom") - var/custom = input(usr, "What rate do you set the regulator to? The dial reads from 0 to [TANK_MAX_RELEASE_PRESSURE].") as null|num - if(isnum(custom)) - distribute_pressure = custom - if("reset") - distribute_pressure = TANK_DEFAULT_RELEASE_PRESSURE - if("min") - distribute_pressure = TANK_MIN_RELEASE_PRESSURE - if("max") - distribute_pressure = TANK_MAX_RELEASE_PRESSURE - distribute_pressure = Clamp(round(distribute_pressure), TANK_MIN_RELEASE_PRESSURE, TANK_MAX_RELEASE_PRESSURE) - if("valve") - if(istype(loc,/mob/living/carbon)) - var/mob/living/carbon/location = loc - if(location.internal == src) - location.internal = null - location.internals.icon_state = "internal0" - usr << "You close the tank release valve." - if (location.internals) - location.internals.icon_state = "internal0" - else - if(location.wear_mask && (location.wear_mask.flags & MASKINTERNALS)) - location.internal = src - usr << "You open \the [src] valve." - if (location.internals) - location.internals.icon_state = "internal1" - else - usr << "You need something to connect to \the [src]!" - return 1 - - -/obj/item/weapon/tank/remove_air(amount) - return air_contents.remove(amount) - -/obj/item/weapon/tank/return_air() - return air_contents - -/obj/item/weapon/tank/assume_air(datum/gas_mixture/giver) - air_contents.merge(giver) - - check_status() - return 1 - -/obj/item/weapon/tank/proc/remove_air_volume(volume_to_return) - if(!air_contents) - return null - - var/tank_pressure = air_contents.return_pressure() - if(tank_pressure < distribute_pressure) - distribute_pressure = tank_pressure - - var/moles_needed = distribute_pressure*volume_to_return/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - - return remove_air(moles_needed) - -/obj/item/weapon/tank/process() - //Allow for reactions - air_contents.react() - check_status() - - -/obj/item/weapon/tank/proc/check_status() - //Handle exploding, leaking, and rupturing of the tank - - if(!air_contents) - return 0 - - var/pressure = air_contents.return_pressure() - if(pressure > TANK_FRAGMENT_PRESSURE) - if(!istype(src.loc,/obj/item/device/transfer_valve)) - message_admins("Explosive tank rupture! Last key to touch the tank was [src.fingerprintslast].") - log_game("Explosive tank rupture! Last key to touch the tank was [src.fingerprintslast].") - //world << "\blue[x],[y] tank is exploding: [pressure] kPa" - //Give the gas a chance to build up more pressure through reacting - air_contents.react() - air_contents.react() - air_contents.react() - pressure = air_contents.return_pressure() - var/range = (pressure-TANK_FRAGMENT_PRESSURE)/TANK_FRAGMENT_SCALE - var/turf/epicenter = get_turf(loc) - - //world << "\blue Exploding Pressure: [pressure] kPa, intensity: [range]" - - explosion(epicenter, round(range*0.25), round(range*0.5), round(range), round(range*1.5)) - if(istype(src.loc,/obj/item/device/transfer_valve)) - qdel(src.loc) - else - qdel(src) - - else if(pressure > TANK_RUPTURE_PRESSURE) - //world << "\blue[x],[y] tank is rupturing: [pressure] kPa, integrity [integrity]" - if(integrity <= 0) - var/turf/simulated/T = get_turf(src) - if(!T) - return - T.assume_air(air_contents) - playsound(src.loc, 'sound/effects/spray.ogg', 10, 1, -3) - qdel(src) - else - integrity-- - - else if(pressure > TANK_LEAK_PRESSURE) - //world << "\blue[x],[y] tank is leaking: [pressure] kPa, integrity [integrity]" - if(integrity <= 0) - var/turf/simulated/T = get_turf(src) - if(!T) - return - var/datum/gas_mixture/leaked_gas = air_contents.remove_ratio(0.25) - T.assume_air(leaked_gas) - else - integrity-- - - else if(integrity < 3) - integrity++ +#define TANK_MAX_RELEASE_PRESSURE (ONE_ATMOSPHERE*3) +#define TANK_MIN_RELEASE_PRESSURE 0 +#define TANK_DEFAULT_RELEASE_PRESSURE (ONE_ATMOSPHERE*O2STANDARD) + +/obj/item/weapon/tank + name = "tank" + icon = 'icons/obj/tank.dmi' + flags = CONDUCT + slot_flags = SLOT_BACK + hitsound = 'sound/weapons/smash.ogg' + + pressure_resistance = ONE_ATMOSPHERE*5 + + force = 5 + throwforce = 10 + throw_speed = 1 + throw_range = 4 + + var/datum/gas_mixture/air_contents = null + var/distribute_pressure = ONE_ATMOSPHERE + var/integrity = 3 + var/volume = 70 + +/obj/item/weapon/tank/suicide_act(mob/user) + var/mob/living/carbon/human/H = user + user.visible_message("[user] is putting the [src]'s valve to their lips! I don't think they're gonna stop!") + playsound(loc, 'sound/effects/spray.ogg', 10, 1, -3) + if (H && !qdeleted(H)) + for(var/obj/item/W in H) + H.unEquip(W) + if(prob(50)) + step(W, pick(alldirs)) + H.hair_style = "Bald" + H.update_hair() + H.blood_max = 5 + gibs(H.loc, H.viruses, H.dna) + H.adjustBruteLoss(1000) //to make the body super-bloody + + return (BRUTELOSS) + +/obj/item/weapon/tank/New() + ..() + + src.air_contents = new /datum/gas_mixture() + src.air_contents.volume = volume //liters + src.air_contents.temperature = T20C + + SSobj.processing |= src + + return + +/obj/item/weapon/tank/Destroy() + if(air_contents) + qdel(air_contents) + + SSobj.processing.Remove(src) + + return ..() + +/obj/item/weapon/tank/examine(mob/user) + var/obj/icon = src + ..() + if (istype(src.loc, /obj/item/assembly)) + icon = src.loc + if (!in_range(src, user)) + if (icon == src) user << "If you want any more information you'll need to get closer." + return + + user << "The pressure gauge reads [src.air_contents.return_pressure()] kPa." + + var/celsius_temperature = src.air_contents.temperature-T0C + var/descriptive + + if (celsius_temperature < 20) + descriptive = "cold" + else if (celsius_temperature < 40) + descriptive = "room temperature" + else if (celsius_temperature < 80) + descriptive = "lukewarm" + else if (celsius_temperature < 100) + descriptive = "warm" + else if (celsius_temperature < 300) + descriptive = "hot" + else + descriptive = "furiously hot" + + user << "It feels [descriptive]." + +/obj/item/weapon/tank/blob_act() + if(prob(50)) + var/turf/location = src.loc + if (!( istype(location, /turf) )) + qdel(src) + + if(src.air_contents) + location.assume_air(air_contents) + + qdel(src) + +/obj/item/weapon/tank/attackby(obj/item/weapon/W, mob/user, params) + ..() + + add_fingerprint(user) + if (istype(src.loc, /obj/item/assembly)) + icon = src.loc + + if ((istype(W, /obj/item/device/analyzer)) && get_dist(user, src) <= 1) + atmosanalyzer_scan(air_contents, user) + + if(istype(W, /obj/item/device/assembly_holder)) + bomb_assemble(W,user) + +/obj/item/weapon/tank/attack_self(mob/user) + if (!user) + return + interact(user) + +/obj/item/weapon/tank/interact(mob/user) + add_fingerprint(user) + ui_interact(user) + +/obj/item/weapon/tank/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, force_open = 0) + ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open) + if (!ui) + ui = new(user, src, ui_key, "tanks", name, 525, 210, state = inventory_state) + ui.open() + +/obj/item/weapon/tank/get_ui_data() + var/mob/living/carbon/location = null + + if(istype(loc, /mob/living/carbon)) + location = loc + else if(istype(loc.loc, /mob/living/carbon)) + location = loc.loc + + var/data = list() + data["tankPressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0) + data["releasePressure"] = round(distribute_pressure ? distribute_pressure : 0) + data["defaultReleasePressure"] = round(TANK_DEFAULT_RELEASE_PRESSURE) + data["minReleasePressure"] = round(TANK_MIN_RELEASE_PRESSURE) + data["maxReleasePressure"] = round(TANK_MAX_RELEASE_PRESSURE) + data["valveOpen"] = 0 + data["maskConnected"] = 0 + + if(istype(location)) + var/mask_check = 0 + + if(location.internal == src) // if tank is current internal + mask_check = 1 + data["valveOpen"] = 1 + else if(src in location) // or if tank is in the mobs possession + if(!location.internal) // and they do not have any active internals + mask_check = 1 + + if(mask_check) + if(location.wear_mask && (location.wear_mask.flags & MASKINTERNALS)) + data["maskConnected"] = 1 + return data + +/obj/item/weapon/tank/ui_act(action, params) + if (..()) + return + + switch(action) + if("pressure") + switch(params["set"]) + if("custom") + var/custom = input(usr, "What rate do you set the regulator to? The dial reads from 0 to [TANK_MAX_RELEASE_PRESSURE].") as null|num + if(isnum(custom)) + distribute_pressure = custom + if("reset") + distribute_pressure = TANK_DEFAULT_RELEASE_PRESSURE + if("min") + distribute_pressure = TANK_MIN_RELEASE_PRESSURE + if("max") + distribute_pressure = TANK_MAX_RELEASE_PRESSURE + distribute_pressure = Clamp(round(distribute_pressure), TANK_MIN_RELEASE_PRESSURE, TANK_MAX_RELEASE_PRESSURE) + if("valve") + if(istype(loc,/mob/living/carbon)) + var/mob/living/carbon/location = loc + if(location.internal == src) + location.internal = null + location.internals.icon_state = "internal0" + usr << "You close the tank release valve." + if (location.internals) + location.internals.icon_state = "internal0" + else + if(location.wear_mask && (location.wear_mask.flags & MASKINTERNALS)) + location.internal = src + usr << "You open \the [src] valve." + if (location.internals) + location.internals.icon_state = "internal1" + else + usr << "You need something to connect to \the [src]!" + return 1 + + +/obj/item/weapon/tank/remove_air(amount) + return air_contents.remove(amount) + +/obj/item/weapon/tank/return_air() + return air_contents + +/obj/item/weapon/tank/assume_air(datum/gas_mixture/giver) + air_contents.merge(giver) + + check_status() + return 1 + +/obj/item/weapon/tank/proc/remove_air_volume(volume_to_return) + if(!air_contents) + return null + + var/tank_pressure = air_contents.return_pressure() + if(tank_pressure < distribute_pressure) + distribute_pressure = tank_pressure + + var/moles_needed = distribute_pressure*volume_to_return/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + + return remove_air(moles_needed) + +/obj/item/weapon/tank/process() + //Allow for reactions + air_contents.react() + check_status() + + +/obj/item/weapon/tank/proc/check_status() + //Handle exploding, leaking, and rupturing of the tank + + if(!air_contents) + return 0 + + var/pressure = air_contents.return_pressure() + if(pressure > TANK_FRAGMENT_PRESSURE) + if(!istype(src.loc,/obj/item/device/transfer_valve)) + message_admins("Explosive tank rupture! Last key to touch the tank was [src.fingerprintslast].") + log_game("Explosive tank rupture! Last key to touch the tank was [src.fingerprintslast].") + //world << "\blue[x],[y] tank is exploding: [pressure] kPa" + //Give the gas a chance to build up more pressure through reacting + air_contents.react() + air_contents.react() + air_contents.react() + pressure = air_contents.return_pressure() + var/range = (pressure-TANK_FRAGMENT_PRESSURE)/TANK_FRAGMENT_SCALE + var/turf/epicenter = get_turf(loc) + + //world << "\blue Exploding Pressure: [pressure] kPa, intensity: [range]" + + explosion(epicenter, round(range*0.25), round(range*0.5), round(range), round(range*1.5)) + if(istype(src.loc,/obj/item/device/transfer_valve)) + qdel(src.loc) + else + qdel(src) + + else if(pressure > TANK_RUPTURE_PRESSURE) + //world << "\blue[x],[y] tank is rupturing: [pressure] kPa, integrity [integrity]" + if(integrity <= 0) + var/turf/simulated/T = get_turf(src) + if(!T) + return + T.assume_air(air_contents) + playsound(src.loc, 'sound/effects/spray.ogg', 10, 1, -3) + qdel(src) + else + integrity-- + + else if(pressure > TANK_LEAK_PRESSURE) + //world << "\blue[x],[y] tank is leaking: [pressure] kPa, integrity [integrity]" + if(integrity <= 0) + var/turf/simulated/T = get_turf(src) + if(!T) + return + var/datum/gas_mixture/leaked_gas = air_contents.remove_ratio(0.25) + T.assume_air(leaked_gas) + else + integrity-- + + else if(integrity < 3) + integrity++ diff --git a/code/game/objects/items/weapons/tanks/watertank.dm b/code/game/objects/items/weapons/tanks/watertank.dm index 742f96d48ae..52a6bfac865 100644 --- a/code/game/objects/items/weapons/tanks/watertank.dm +++ b/code/game/objects/items/weapons/tanks/watertank.dm @@ -1,433 +1,433 @@ -//Hydroponics tank and base code -/obj/item/weapon/watertank - name = "backpack water tank" - desc = "A S.U.N.S.H.I.N.E. brand watertank backpack with nozzle to water plants." - icon = 'icons/obj/hydroponics/equipment.dmi' - icon_state = "waterbackpack" - item_state = "waterbackpack" - w_class = 4 - slot_flags = SLOT_BACK - slowdown = 1 - action_button_name = "Toggle Mister" - - var/obj/item/weapon/noz - var/on = 0 - var/volume = 500 - -/obj/item/weapon/watertank/New() - ..() - create_reagents(volume) - noz = make_noz() - -/obj/item/weapon/watertank/ui_action_click() - toggle_mister() - -/obj/item/weapon/watertank/verb/toggle_mister() - set name = "Toggle Mister" - set category = "Object" - if (usr.get_item_by_slot(usr.getWatertankSlot()) != src) - usr << "The watertank must be worn properly to use!" - return - if(usr.incapacitated()) - return - on = !on - - var/mob/living/carbon/human/user = usr - if(on) - if(noz == null) - noz = make_noz() - - //Detach the nozzle into the user's hands - if(!user.put_in_hands(noz)) - on = 0 - user << "You need a free hand to hold the mister!" - return - noz.loc = user - else - //Remove from their hands and put back "into" the tank - remove_noz() - return - -/obj/item/weapon/watertank/proc/make_noz() - return new /obj/item/weapon/reagent_containers/spray/mister(src) - -/obj/item/weapon/watertank/equipped(mob/user, slot) - if (slot != slot_back) - remove_noz() - -/obj/item/weapon/watertank/proc/remove_noz() - if(ismob(noz.loc)) - var/mob/M = noz.loc - M.unEquip(noz, 1) - return - -/obj/item/weapon/watertank/Destroy() - if (on) - remove_noz() - qdel(noz) - noz = null - return ..() - -/obj/item/weapon/watertank/attack_hand(mob/user) - if(src.loc == user) - ui_action_click() - return - ..() - -/obj/item/weapon/watertank/MouseDrop(obj/over_object) - var/mob/H = src.loc - if(istype(H)) - switch(over_object.name) - if("r_hand") - if(H.r_hand) - return - if(!H.unEquip(src)) - return - H.put_in_r_hand(src) - if("l_hand") - if(H.l_hand) - return - if(!H.unEquip(src)) - return - H.put_in_l_hand(src) - return - -/obj/item/weapon/watertank/attackby(obj/item/W, mob/user, params) - if(W == noz) - remove_noz() - return - ..() - -/mob/proc/getWatertankSlot() - return slot_back - -/mob/living/simple_animal/drone/getWatertankSlot() - return slot_drone_storage - -// This mister item is intended as an extension of the watertank and always attached to it. -// Therefore, it's designed to be "locked" to the player's hands or extended back onto -// the watertank backpack. Allowing it to be placed elsewhere or created without a parent -// watertank object will likely lead to weird behaviour or runtimes. -/obj/item/weapon/reagent_containers/spray/mister - name = "water mister" - desc = "A mister nozzle attached to a water tank." - icon = 'icons/obj/hydroponics/equipment.dmi' - icon_state = "mister" - item_state = "mister" - w_class = 4 - amount_per_transfer_from_this = 50 - possible_transfer_amounts = list(25,50,100) - volume = 500 - flags = NODROP | OPENCONTAINER | NOBLUDGEON - - var/obj/item/weapon/watertank/tank - -/obj/item/weapon/reagent_containers/spray/mister/New(parent_tank) - ..() - if(check_tank_exists(parent_tank, src)) - tank = parent_tank - reagents = tank.reagents //This mister is really just a proxy for the tank's reagents - loc = tank - return - -/obj/item/weapon/reagent_containers/spray/mister/dropped(mob/user) - user << "The mister snaps back onto the watertank." - tank.on = 0 - loc = tank - -/obj/item/weapon/reagent_containers/spray/mister/attack_self() - return - -/proc/check_tank_exists(parent_tank, mob/living/carbon/human/M, obj/O) - if (!parent_tank || !istype(parent_tank, /obj/item/weapon/watertank)) //To avoid weird issues from admin spawns - M.unEquip(O) - qdel(0) - return 0 - else - return 1 - -/obj/item/weapon/reagent_containers/spray/mister/Move() - ..() - if(loc != tank.loc) - loc = tank.loc - -/obj/item/weapon/reagent_containers/spray/mister/afterattack(obj/target, mob/user, proximity) - if(target.loc == loc || target == tank) //Safety check so you don't fill your mister with mutagen or something and then blast yourself in the face with it putting it away - return - ..() - -//Janitor tank -/obj/item/weapon/watertank/janitor - name = "backpack water tank" - desc = "A janitorial watertank backpack with nozzle to clean dirt and graffiti." - icon_state = "waterbackpackjani" - item_state = "waterbackpackjani" - -/obj/item/weapon/watertank/janitor/New() - ..() - reagents.add_reagent("cleaner", 500) - -/obj/item/weapon/reagent_containers/spray/mister/janitor - name = "janitor spray nozzle" - desc = "A janitorial spray nozzle attached to a watertank, designed to clean up large messes." - icon = 'icons/obj/hydroponics/equipment.dmi' - icon_state = "misterjani" - item_state = "misterjani" - amount_per_transfer_from_this = 5 - possible_transfer_amounts = list() - -/obj/item/weapon/watertank/janitor/make_noz() - return new /obj/item/weapon/reagent_containers/spray/mister/janitor(src) - -/obj/item/weapon/reagent_containers/spray/mister/janitor/attack_self(var/mob/user) - amount_per_transfer_from_this = (amount_per_transfer_from_this == 10 ? 5 : 10) - user << "You [amount_per_transfer_from_this == 10 ? "remove" : "fix"] the nozzle. You'll now use [amount_per_transfer_from_this] units per spray." - -//ATMOS FIRE FIGHTING BACKPACK - -#define EXTINGUISHER 0 -#define NANOFROST 1 -#define METAL_FOAM 2 - -/obj/item/weapon/watertank/atmos - name = "backpack firefighter tank" - desc = "A refridgerated and pressurized backpack tank with extinguisher nozzle, intended to fight fires. Swaps between extinguisher, nanofrost launcher, and metal foam dispenser for breaches. Nanofrost converts plasma in the air to nitrogen, but only if it is combusting at the time." - icon_state = "waterbackpackatmos" - item_state = "waterbackpackatmos" - volume = 200 - -/obj/item/weapon/watertank/atmos/New() - ..() - reagents.add_reagent("water", 200) - -/obj/item/weapon/watertank/atmos/make_noz() - return new /obj/item/weapon/extinguisher/mini/nozzle(src) - -/obj/item/weapon/watertank/atmos/dropped(mob/user) - icon_state = "waterbackpackatmos" - if(istype(noz, /obj/item/weapon/extinguisher/mini/nozzle)) - var/obj/item/weapon/extinguisher/mini/nozzle/N = noz - N.nozzle_mode = 0 - -/obj/item/weapon/extinguisher/mini/nozzle - name = "extinguisher nozzle" - desc = "A heavy duty nozzle attached to a firefighter's backpack tank." - icon = 'icons/obj/hydroponics/equipment.dmi' - icon_state = "atmos_nozzle" - item_state = "nozzleatmos" - safety = 0 - max_water = 200 - power = 8 - precision = 1 - cooling_power = 5 - w_class = 5 - flags = NODROP //Necessary to ensure that the nozzle and tank never seperate - var/obj/item/weapon/watertank/tank - var/nozzle_mode = 0 - var/metal_synthesis_cooldown = 0 - var/nanofrost_cooldown = 0 - -/obj/item/weapon/extinguisher/mini/nozzle/New(parent_tank) - if(check_tank_exists(parent_tank, src)) - tank = parent_tank - reagents = tank.reagents - max_water = tank.volume - loc = tank - return - -/obj/item/weapon/extinguisher/mini/nozzle/Move() - ..() - if(loc != tank.loc) - loc = tank - return - -/obj/item/weapon/extinguisher/mini/nozzle/attack_self(mob/user) - switch(nozzle_mode) - if(EXTINGUISHER) - nozzle_mode = NANOFROST - tank.icon_state = "waterbackpackatmos_1" - user << "Swapped to nanofrost launcher" - return - if(NANOFROST) - nozzle_mode = METAL_FOAM - tank.icon_state = "waterbackpackatmos_2" - user << "Swapped to metal foam synthesizer" - return - if(METAL_FOAM) - nozzle_mode = EXTINGUISHER - tank.icon_state = "waterbackpackatmos_0" - user << "Swapped to water extinguisher" - return - return - -/obj/item/weapon/extinguisher/mini/nozzle/dropped(mob/user) - user << "The nozzle snaps back onto the tank!" - tank.on = 0 - loc = tank - -/obj/item/weapon/extinguisher/mini/nozzle/afterattack(atom/target, mob/user) - if(nozzle_mode == EXTINGUISHER) - ..() - return - var/Adj = user.Adjacent(target) - if(Adj) - AttemptRefill(target, user) - if(nozzle_mode == NANOFROST) - if(Adj) - return //Safety check so you don't blast yourself trying to refill your tank - var/datum/reagents/R = reagents - if(R.total_volume < 100) - user << "You need at least 100 units of water to use the nanofrost launcher!" - return - if(nanofrost_cooldown) - user << "Nanofrost launcher is still recharging..." - return - nanofrost_cooldown = 1 - R.remove_any(100) - var/obj/effect/nanofrost_container/A = new /obj/effect/nanofrost_container(get_turf(src)) - log_game("[user.ckey] ([user.name]) used Nanofrost at [get_area(user)] ([user.x], [user.y], [user.z]).") - playsound(src,'sound/items/syringeproj.ogg',40,1) - for(var/a=0, a<5, a++) - step_towards(A, target) - sleep(2) - A.Smoke() - spawn(100) - if(src) - nanofrost_cooldown = 0 - return - if(nozzle_mode == METAL_FOAM) - if(!Adj|| !istype(target, /turf)) - return - if(metal_synthesis_cooldown < 5) - var/obj/effect/particle_effect/foam/metal/F = PoolOrNew(/obj/effect/particle_effect/foam/metal, get_turf(target)) - F.amount = 0 - metal_synthesis_cooldown++ - spawn(100) - metal_synthesis_cooldown-- - else - user << "Metal foam mix is still being synthesized..." - return - -/obj/effect/nanofrost_container - name = "nanofrost container" - desc = "A frozen shell of ice containing nanofrost that freezes the surrounding area after activation." - icon = 'icons/effects/effects.dmi' - icon_state = "frozen_smoke_capsule" - mouse_opacity = 0 - pass_flags = PASSTABLE - -/obj/effect/nanofrost_container/proc/Smoke() - var/datum/effect_system/smoke_spread/freezing/S = new - S.set_up(2, src.loc, blasting=1) - S.start() - var/obj/effect/decal/cleanable/flour/F = new /obj/effect/decal/cleanable/flour(src.loc) - F.color = "#B2FFFF" - F.name = "nanofrost residue" - F.desc = "Residue left behind from a nanofrost detonation. Perhaps there was a fire here?" - playsound(src,'sound/effects/bamf.ogg',100,1) - qdel(src) - -#undef EXTINGUISHER -#undef NANOFROST -#undef METAL_FOAM - -/obj/item/weapon/reagent_containers/chemtank - name = "backpack chemical injector" - desc = "A chemical autoinjector that can be carried on your back." - icon = 'icons/obj/hydroponics/equipment.dmi' - icon_state = "waterbackpackatmos" - item_state = "waterbackpackatmos" - w_class = 4 - slot_flags = SLOT_BACK - slowdown = 1 - action_button_name = "Activate Injector" - - var/on = 0 - volume = 300 - var/usage_ratio = 5 //5 unit added per 1 removed - var/injection_amount = 1 - amount_per_transfer_from_this = 5 - flags = OPENCONTAINER - spillable = 0 - possible_transfer_amounts = list(5,10,15) - -/obj/item/weapon/reagent_containers/chemtank/ui_action_click() - toggle_injection() - -/obj/item/weapon/reagent_containers/chemtank/proc/toggle_injection() - var/mob/living/carbon/human/user = usr - if(!istype(user)) - return - if (user.get_item_by_slot(slot_back) != src) - user << "The chemtank needs to be on your back before you can activate it!" - return - if(on) - turn_off() - else - turn_on() - -//Todo : cache these. -/obj/item/weapon/reagent_containers/chemtank/proc/update_filling() - overlays.Cut() - - if(reagents.total_volume) - var/image/filling = image('icons/obj/reagentfillings.dmi',icon_state = "backpack-10") - - var/percent = round((reagents.total_volume / volume) * 100) - switch(percent) - if(0 to 15) filling.icon_state = "backpack-10" - if(16 to 60) filling.icon_state = "backpack50" - if(61 to INFINITY) filling.icon_state = "backpack100" - - filling.color = mix_color_from_reagents(reagents.reagent_list) - overlays += filling - -/obj/item/weapon/reagent_containers/chemtank/worn_overlays(var/isinhands = FALSE) //apply chemcolor and level - . = list() - //inhands + reagent_filling - if(!isinhands && reagents.total_volume) - var/image/filling = image('icons/obj/reagentfillings.dmi',icon_state = "backpackmob-10") - - var/percent = round((reagents.total_volume / volume) * 100) - switch(percent) - if(0 to 15) filling.icon_state = "backpackmob-10" - if(16 to 60) filling.icon_state = "backpackmob50" - if(61 to INFINITY) filling.icon_state = "backpackmob100" - - filling.color = mix_color_from_reagents(reagents.reagent_list) - . += filling - -/obj/item/weapon/reagent_containers/chemtank/proc/turn_on() - on = 1 - SSobj.processing |= src - if(ismob(loc)) - loc << "[src] turns on." - -/obj/item/weapon/reagent_containers/chemtank/proc/turn_off() - on = 0 - SSobj.processing.Remove(src) - if(ismob(loc)) - loc << "[src] turns off." - -/obj/item/weapon/reagent_containers/chemtank/process() - if(!istype(loc,/mob/living/carbon/human)) - turn_off() - return - if(!reagents.total_volume) - turn_off() - return - var/mob/living/carbon/human/user = loc - if(user.back != src) - turn_off() - return - - var/used_amount = injection_amount/usage_ratio - reagents.reaction(user, INJECT,injection_amount,0) - reagents.trans_to(user,used_amount,multiplier=usage_ratio) - update_filling() - user.update_inv_back() //for overlays update - -/obj/item/weapon/reagent_containers/chemtank/stim/New() - ..() - reagents.add_reagent("stimulants_longterm", 300) +//Hydroponics tank and base code +/obj/item/weapon/watertank + name = "backpack water tank" + desc = "A S.U.N.S.H.I.N.E. brand watertank backpack with nozzle to water plants." + icon = 'icons/obj/hydroponics/equipment.dmi' + icon_state = "waterbackpack" + item_state = "waterbackpack" + w_class = 4 + slot_flags = SLOT_BACK + slowdown = 1 + action_button_name = "Toggle Mister" + + var/obj/item/weapon/noz + var/on = 0 + var/volume = 500 + +/obj/item/weapon/watertank/New() + ..() + create_reagents(volume) + noz = make_noz() + +/obj/item/weapon/watertank/ui_action_click() + toggle_mister() + +/obj/item/weapon/watertank/verb/toggle_mister() + set name = "Toggle Mister" + set category = "Object" + if (usr.get_item_by_slot(usr.getWatertankSlot()) != src) + usr << "The watertank must be worn properly to use!" + return + if(usr.incapacitated()) + return + on = !on + + var/mob/living/carbon/human/user = usr + if(on) + if(noz == null) + noz = make_noz() + + //Detach the nozzle into the user's hands + if(!user.put_in_hands(noz)) + on = 0 + user << "You need a free hand to hold the mister!" + return + noz.loc = user + else + //Remove from their hands and put back "into" the tank + remove_noz() + return + +/obj/item/weapon/watertank/proc/make_noz() + return new /obj/item/weapon/reagent_containers/spray/mister(src) + +/obj/item/weapon/watertank/equipped(mob/user, slot) + if (slot != slot_back) + remove_noz() + +/obj/item/weapon/watertank/proc/remove_noz() + if(ismob(noz.loc)) + var/mob/M = noz.loc + M.unEquip(noz, 1) + return + +/obj/item/weapon/watertank/Destroy() + if (on) + remove_noz() + qdel(noz) + noz = null + return ..() + +/obj/item/weapon/watertank/attack_hand(mob/user) + if(src.loc == user) + ui_action_click() + return + ..() + +/obj/item/weapon/watertank/MouseDrop(obj/over_object) + var/mob/H = src.loc + if(istype(H)) + switch(over_object.name) + if("r_hand") + if(H.r_hand) + return + if(!H.unEquip(src)) + return + H.put_in_r_hand(src) + if("l_hand") + if(H.l_hand) + return + if(!H.unEquip(src)) + return + H.put_in_l_hand(src) + return + +/obj/item/weapon/watertank/attackby(obj/item/W, mob/user, params) + if(W == noz) + remove_noz() + return + ..() + +/mob/proc/getWatertankSlot() + return slot_back + +/mob/living/simple_animal/drone/getWatertankSlot() + return slot_drone_storage + +// This mister item is intended as an extension of the watertank and always attached to it. +// Therefore, it's designed to be "locked" to the player's hands or extended back onto +// the watertank backpack. Allowing it to be placed elsewhere or created without a parent +// watertank object will likely lead to weird behaviour or runtimes. +/obj/item/weapon/reagent_containers/spray/mister + name = "water mister" + desc = "A mister nozzle attached to a water tank." + icon = 'icons/obj/hydroponics/equipment.dmi' + icon_state = "mister" + item_state = "mister" + w_class = 4 + amount_per_transfer_from_this = 50 + possible_transfer_amounts = list(25,50,100) + volume = 500 + flags = NODROP | OPENCONTAINER | NOBLUDGEON + + var/obj/item/weapon/watertank/tank + +/obj/item/weapon/reagent_containers/spray/mister/New(parent_tank) + ..() + if(check_tank_exists(parent_tank, src)) + tank = parent_tank + reagents = tank.reagents //This mister is really just a proxy for the tank's reagents + loc = tank + return + +/obj/item/weapon/reagent_containers/spray/mister/dropped(mob/user) + user << "The mister snaps back onto the watertank." + tank.on = 0 + loc = tank + +/obj/item/weapon/reagent_containers/spray/mister/attack_self() + return + +/proc/check_tank_exists(parent_tank, mob/living/carbon/human/M, obj/O) + if (!parent_tank || !istype(parent_tank, /obj/item/weapon/watertank)) //To avoid weird issues from admin spawns + M.unEquip(O) + qdel(0) + return 0 + else + return 1 + +/obj/item/weapon/reagent_containers/spray/mister/Move() + ..() + if(loc != tank.loc) + loc = tank.loc + +/obj/item/weapon/reagent_containers/spray/mister/afterattack(obj/target, mob/user, proximity) + if(target.loc == loc || target == tank) //Safety check so you don't fill your mister with mutagen or something and then blast yourself in the face with it putting it away + return + ..() + +//Janitor tank +/obj/item/weapon/watertank/janitor + name = "backpack water tank" + desc = "A janitorial watertank backpack with nozzle to clean dirt and graffiti." + icon_state = "waterbackpackjani" + item_state = "waterbackpackjani" + +/obj/item/weapon/watertank/janitor/New() + ..() + reagents.add_reagent("cleaner", 500) + +/obj/item/weapon/reagent_containers/spray/mister/janitor + name = "janitor spray nozzle" + desc = "A janitorial spray nozzle attached to a watertank, designed to clean up large messes." + icon = 'icons/obj/hydroponics/equipment.dmi' + icon_state = "misterjani" + item_state = "misterjani" + amount_per_transfer_from_this = 5 + possible_transfer_amounts = list() + +/obj/item/weapon/watertank/janitor/make_noz() + return new /obj/item/weapon/reagent_containers/spray/mister/janitor(src) + +/obj/item/weapon/reagent_containers/spray/mister/janitor/attack_self(var/mob/user) + amount_per_transfer_from_this = (amount_per_transfer_from_this == 10 ? 5 : 10) + user << "You [amount_per_transfer_from_this == 10 ? "remove" : "fix"] the nozzle. You'll now use [amount_per_transfer_from_this] units per spray." + +//ATMOS FIRE FIGHTING BACKPACK + +#define EXTINGUISHER 0 +#define NANOFROST 1 +#define METAL_FOAM 2 + +/obj/item/weapon/watertank/atmos + name = "backpack firefighter tank" + desc = "A refridgerated and pressurized backpack tank with extinguisher nozzle, intended to fight fires. Swaps between extinguisher, nanofrost launcher, and metal foam dispenser for breaches. Nanofrost converts plasma in the air to nitrogen, but only if it is combusting at the time." + icon_state = "waterbackpackatmos" + item_state = "waterbackpackatmos" + volume = 200 + +/obj/item/weapon/watertank/atmos/New() + ..() + reagents.add_reagent("water", 200) + +/obj/item/weapon/watertank/atmos/make_noz() + return new /obj/item/weapon/extinguisher/mini/nozzle(src) + +/obj/item/weapon/watertank/atmos/dropped(mob/user) + icon_state = "waterbackpackatmos" + if(istype(noz, /obj/item/weapon/extinguisher/mini/nozzle)) + var/obj/item/weapon/extinguisher/mini/nozzle/N = noz + N.nozzle_mode = 0 + +/obj/item/weapon/extinguisher/mini/nozzle + name = "extinguisher nozzle" + desc = "A heavy duty nozzle attached to a firefighter's backpack tank." + icon = 'icons/obj/hydroponics/equipment.dmi' + icon_state = "atmos_nozzle" + item_state = "nozzleatmos" + safety = 0 + max_water = 200 + power = 8 + precision = 1 + cooling_power = 5 + w_class = 5 + flags = NODROP //Necessary to ensure that the nozzle and tank never seperate + var/obj/item/weapon/watertank/tank + var/nozzle_mode = 0 + var/metal_synthesis_cooldown = 0 + var/nanofrost_cooldown = 0 + +/obj/item/weapon/extinguisher/mini/nozzle/New(parent_tank) + if(check_tank_exists(parent_tank, src)) + tank = parent_tank + reagents = tank.reagents + max_water = tank.volume + loc = tank + return + +/obj/item/weapon/extinguisher/mini/nozzle/Move() + ..() + if(loc != tank.loc) + loc = tank + return + +/obj/item/weapon/extinguisher/mini/nozzle/attack_self(mob/user) + switch(nozzle_mode) + if(EXTINGUISHER) + nozzle_mode = NANOFROST + tank.icon_state = "waterbackpackatmos_1" + user << "Swapped to nanofrost launcher" + return + if(NANOFROST) + nozzle_mode = METAL_FOAM + tank.icon_state = "waterbackpackatmos_2" + user << "Swapped to metal foam synthesizer" + return + if(METAL_FOAM) + nozzle_mode = EXTINGUISHER + tank.icon_state = "waterbackpackatmos_0" + user << "Swapped to water extinguisher" + return + return + +/obj/item/weapon/extinguisher/mini/nozzle/dropped(mob/user) + user << "The nozzle snaps back onto the tank!" + tank.on = 0 + loc = tank + +/obj/item/weapon/extinguisher/mini/nozzle/afterattack(atom/target, mob/user) + if(nozzle_mode == EXTINGUISHER) + ..() + return + var/Adj = user.Adjacent(target) + if(Adj) + AttemptRefill(target, user) + if(nozzle_mode == NANOFROST) + if(Adj) + return //Safety check so you don't blast yourself trying to refill your tank + var/datum/reagents/R = reagents + if(R.total_volume < 100) + user << "You need at least 100 units of water to use the nanofrost launcher!" + return + if(nanofrost_cooldown) + user << "Nanofrost launcher is still recharging..." + return + nanofrost_cooldown = 1 + R.remove_any(100) + var/obj/effect/nanofrost_container/A = new /obj/effect/nanofrost_container(get_turf(src)) + log_game("[user.ckey] ([user.name]) used Nanofrost at [get_area(user)] ([user.x], [user.y], [user.z]).") + playsound(src,'sound/items/syringeproj.ogg',40,1) + for(var/a=0, a<5, a++) + step_towards(A, target) + sleep(2) + A.Smoke() + spawn(100) + if(src) + nanofrost_cooldown = 0 + return + if(nozzle_mode == METAL_FOAM) + if(!Adj|| !istype(target, /turf)) + return + if(metal_synthesis_cooldown < 5) + var/obj/effect/particle_effect/foam/metal/F = PoolOrNew(/obj/effect/particle_effect/foam/metal, get_turf(target)) + F.amount = 0 + metal_synthesis_cooldown++ + spawn(100) + metal_synthesis_cooldown-- + else + user << "Metal foam mix is still being synthesized..." + return + +/obj/effect/nanofrost_container + name = "nanofrost container" + desc = "A frozen shell of ice containing nanofrost that freezes the surrounding area after activation." + icon = 'icons/effects/effects.dmi' + icon_state = "frozen_smoke_capsule" + mouse_opacity = 0 + pass_flags = PASSTABLE + +/obj/effect/nanofrost_container/proc/Smoke() + var/datum/effect_system/smoke_spread/freezing/S = new + S.set_up(2, src.loc, blasting=1) + S.start() + var/obj/effect/decal/cleanable/flour/F = new /obj/effect/decal/cleanable/flour(src.loc) + F.color = "#B2FFFF" + F.name = "nanofrost residue" + F.desc = "Residue left behind from a nanofrost detonation. Perhaps there was a fire here?" + playsound(src,'sound/effects/bamf.ogg',100,1) + qdel(src) + +#undef EXTINGUISHER +#undef NANOFROST +#undef METAL_FOAM + +/obj/item/weapon/reagent_containers/chemtank + name = "backpack chemical injector" + desc = "A chemical autoinjector that can be carried on your back." + icon = 'icons/obj/hydroponics/equipment.dmi' + icon_state = "waterbackpackatmos" + item_state = "waterbackpackatmos" + w_class = 4 + slot_flags = SLOT_BACK + slowdown = 1 + action_button_name = "Activate Injector" + + var/on = 0 + volume = 300 + var/usage_ratio = 5 //5 unit added per 1 removed + var/injection_amount = 1 + amount_per_transfer_from_this = 5 + flags = OPENCONTAINER + spillable = 0 + possible_transfer_amounts = list(5,10,15) + +/obj/item/weapon/reagent_containers/chemtank/ui_action_click() + toggle_injection() + +/obj/item/weapon/reagent_containers/chemtank/proc/toggle_injection() + var/mob/living/carbon/human/user = usr + if(!istype(user)) + return + if (user.get_item_by_slot(slot_back) != src) + user << "The chemtank needs to be on your back before you can activate it!" + return + if(on) + turn_off() + else + turn_on() + +//Todo : cache these. +/obj/item/weapon/reagent_containers/chemtank/proc/update_filling() + overlays.Cut() + + if(reagents.total_volume) + var/image/filling = image('icons/obj/reagentfillings.dmi',icon_state = "backpack-10") + + var/percent = round((reagents.total_volume / volume) * 100) + switch(percent) + if(0 to 15) filling.icon_state = "backpack-10" + if(16 to 60) filling.icon_state = "backpack50" + if(61 to INFINITY) filling.icon_state = "backpack100" + + filling.color = mix_color_from_reagents(reagents.reagent_list) + overlays += filling + +/obj/item/weapon/reagent_containers/chemtank/worn_overlays(var/isinhands = FALSE) //apply chemcolor and level + . = list() + //inhands + reagent_filling + if(!isinhands && reagents.total_volume) + var/image/filling = image('icons/obj/reagentfillings.dmi',icon_state = "backpackmob-10") + + var/percent = round((reagents.total_volume / volume) * 100) + switch(percent) + if(0 to 15) filling.icon_state = "backpackmob-10" + if(16 to 60) filling.icon_state = "backpackmob50" + if(61 to INFINITY) filling.icon_state = "backpackmob100" + + filling.color = mix_color_from_reagents(reagents.reagent_list) + . += filling + +/obj/item/weapon/reagent_containers/chemtank/proc/turn_on() + on = 1 + SSobj.processing |= src + if(ismob(loc)) + loc << "[src] turns on." + +/obj/item/weapon/reagent_containers/chemtank/proc/turn_off() + on = 0 + SSobj.processing.Remove(src) + if(ismob(loc)) + loc << "[src] turns off." + +/obj/item/weapon/reagent_containers/chemtank/process() + if(!istype(loc,/mob/living/carbon/human)) + turn_off() + return + if(!reagents.total_volume) + turn_off() + return + var/mob/living/carbon/human/user = loc + if(user.back != src) + turn_off() + return + + var/used_amount = injection_amount/usage_ratio + reagents.reaction(user, INJECT,injection_amount,0) + reagents.trans_to(user,used_amount,multiplier=usage_ratio) + update_filling() + user.update_inv_back() //for overlays update + +/obj/item/weapon/reagent_containers/chemtank/stim/New() + ..() + reagents.add_reagent("stimulants_longterm", 300) update_filling() \ No newline at end of file diff --git a/code/game/objects/items/weapons/twohanded.dm b/code/game/objects/items/weapons/twohanded.dm index 889d629d926..054dcf76376 100644 --- a/code/game/objects/items/weapons/twohanded.dm +++ b/code/game/objects/items/weapons/twohanded.dm @@ -1,413 +1,413 @@ -/* Two-handed Weapons - * Contains: - * Twohanded - * Fireaxe - * Double-Bladed Energy Swords - * Spears - * CHAINSAWS - */ - -/*################################################################## -##################### TWO HANDED WEAPONS BE HERE~ -Agouri :3 ######## -####################################################################*/ - -//Rewrote TwoHanded weapons stuff and put it all here. Just copypasta fireaxe to make new ones ~Carn -//This rewrite means we don't have two variables for EVERY item which are used only by a few weapons. -//It also tidies stuff up elsewhere. - - - - -/* - * Twohanded - */ -/obj/item/weapon/twohanded - var/wielded = 0 - var/force_unwielded = 0 - var/force_wielded = 0 - var/wieldsound = null - var/unwieldsound = null - -/obj/item/weapon/twohanded/proc/unwield(mob/living/carbon/user) - if(!wielded || !user) return - wielded = 0 - if(force_unwielded) - force = force_unwielded - var/sf = findtext(name," (Wielded)") - if(sf) - name = copytext(name,1,sf) - else //something wrong - name = "[initial(name)]" - update_icon() - if(isrobot(user)) - user << "You free up your module." - else if(istype(src, /obj/item/weapon/twohanded/required)) - user << "You drop \the [name]." - else - user << "You are now carrying the [name] with one hand." - if(unwieldsound) - playsound(loc, unwieldsound, 50, 1) - var/obj/item/weapon/twohanded/offhand/O = user.get_inactive_hand() - if(O && istype(O)) - O.unwield() - return - -/obj/item/weapon/twohanded/proc/wield(mob/living/carbon/user) - if(wielded) return - if(istype(user,/mob/living/carbon/monkey) ) - user << "It's too heavy for you to wield fully." - return - if(user.get_inactive_hand()) - user << "You need your other hand to be empty!" - return - wielded = 1 - if(force_wielded) - force = force_wielded - name = "[name] (Wielded)" - update_icon() - if(isrobot(user)) - user << "You dedicate your module to [name]." - else - user << "You grab the [name] with both hands." - if (wieldsound) - playsound(loc, wieldsound, 50, 1) - var/obj/item/weapon/twohanded/offhand/O = new(user) ////Let's reserve his other hand~ - O.name = "[name] - offhand" - O.desc = "Your second grip on the [name]" - user.put_in_inactive_hand(O) - return - -/obj/item/weapon/twohanded/mob_can_equip(mob/M, slot) - //Cannot equip wielded items. - if(wielded) - M << "Unwield the [name] first!" - return 0 - return ..() - -/obj/item/weapon/twohanded/dropped(mob/user) - //handles unwielding a twohanded weapon when dropped as well as clearing up the offhand - if(user) - var/obj/item/weapon/twohanded/O = user.get_inactive_hand() - if(istype(O)) - O.unwield(user) - return unwield(user) - -/obj/item/weapon/twohanded/update_icon() - return - -/obj/item/weapon/twohanded/attack_self(mob/user) - ..() - if(wielded) //Trying to unwield it - unwield(user) - else //Trying to wield it - wield(user) - -///////////OFFHAND/////////////// -/obj/item/weapon/twohanded/offhand - name = "offhand" - icon_state = "offhand" - w_class = 5 - flags = ABSTRACT - -/obj/item/weapon/twohanded/offhand/unwield() - qdel(src) - -/obj/item/weapon/twohanded/offhand/wield() - qdel(src) - -/obj/item/weapon/twohanded/offhand/hit_reaction()//if the actual twohanded weapon is a shield, we count as a shield too! - var/mob/user = loc - if(!istype(user)) - return 0 - var/obj/item/I = user.get_active_hand() - if(I == src) - I = user.get_inactive_hand() - if(!I) - return 0 - return I.hit_reaction() - -///////////Two hand required objects/////////////// -//This is for objects that require two hands to even pick up -/obj/item/weapon/twohanded/required/ - w_class = 5 - -/obj/item/weapon/twohanded/required/attack_self() - return - -/obj/item/weapon/twohanded/required/mob_can_equip(mob/M, slot) - if(wielded) - M << "\The [src] is too cumbersome to carry with anything but your hands!" - return 0 - return ..() - -/obj/item/weapon/twohanded/required/attack_hand(mob/user)//Can't even pick it up without both hands empty - var/obj/item/weapon/twohanded/required/H = user.get_inactive_hand() - if(get_dist(src,user) > 1) - return 0 - if(H != null) - user << "\The [src] is too cumbersome to carry in one hand!" - return - wield(user) - ..() - - -/obj/item/weapon/twohanded/ - -/* - * Fireaxe - */ -/obj/item/weapon/twohanded/fireaxe // DEM AXES MAN, marker -Agouri - icon_state = "fireaxe0" - name = "fire axe" - desc = "Truly, the weapon of a madman. Who would think to fight fire with an axe?" - force = 5 - throwforce = 15 - w_class = 4 - slot_flags = SLOT_BACK - force_unwielded = 5 - force_wielded = 24 // Was 18, Buffed - RobRichards/RR - attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut") - hitsound = 'sound/weapons/bladeslice.ogg' - sharpness = IS_SHARP - -/obj/item/weapon/twohanded/fireaxe/update_icon() //Currently only here to fuck with the on-mob icons. - icon_state = "fireaxe[wielded]" - return - -/obj/item/weapon/twohanded/fireaxe/suicide_act(mob/user) - user.visible_message("[user] axes \himself from head to toe! It looks like \he's trying to commit suicide..") - return (BRUTELOSS) - -/obj/item/weapon/twohanded/fireaxe/afterattack(atom/A as mob|obj|turf|area, mob/user, proximity) - if(!proximity) return - if(wielded) //destroys windows and grilles in one hit - if(istype(A,/obj/structure/window)) - var/obj/structure/window/W = A - W.spawnfragments() // this will qdel and spawn shards - else if(istype(A,/obj/structure/grille)) - var/obj/structure/grille/G = A - G.health = -6 - G.destroyed += prob(25) // If this is set, healthcheck will completely remove the grille - G.healthcheck() - - -/* - * Double-Bladed Energy Swords - Cheridan - */ -/obj/item/weapon/twohanded/dualsaber - icon_state = "dualsaber0" - name = "double-bladed energy sword" - desc = "Handle with care." - force = 3 - throwforce = 5 - throw_speed = 3 - throw_range = 5 - w_class = 2 - force_unwielded = 3 - force_wielded = 34 - wieldsound = 'sound/weapons/saberon.ogg' - unwieldsound = 'sound/weapons/saberoff.ogg' - hitsound = "swing_hit" - flags = NOSHIELD - origin_tech = "magnets=3;syndicate=4" - item_color = "green" - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - block_chance = 50 - var/hacked = 0 - -/obj/item/weapon/twohanded/dualsaber/New() - item_color = pick("red", "blue", "green", "purple") - -/obj/item/weapon/twohanded/dualsaber/update_icon() - if(wielded) - icon_state = "dualsaber[item_color][wielded]" - else - icon_state = "dualsaber0" - clean_blood()//blood overlays get weird otherwise, because the sprite changes. - return - -/obj/item/weapon/twohanded/dualsaber/attack(mob/target, mob/living/carbon/human/user) - ..() - if(user.disabilities & CLUMSY && (wielded) && prob(40)) - impale(user) - return - if((wielded) && prob(50)) - spawn(0) - for(var/i in list(1,2,4,8,4,2,1,2,4,8,4,2)) - user.dir = i - if(i == 8) - user.emote("flip") - sleep(1) - -/obj/item/weapon/twohanded/dualsaber/proc/impale(mob/living/user) - user << "You twirl around a bit before losing your balance and impaling yourself on \the [src]." - if (force_wielded) - user.take_organ_damage(20,25) - else - user.adjustStaminaLoss(25) - -/obj/item/weapon/twohanded/dualsaber/hit_reaction(mob/living/carbon/human/owner, attack_text, final_block_chance) - if(wielded) - return ..() - return 0 - -/obj/item/weapon/twohanded/dualsaber/attack_hulk(mob/living/carbon/human/user) //In case thats just so happens that it is still activated on the groud, prevents hulk from picking it up - if(wielded) - user << "You can't pick up such dangerous item with your meaty hands without losing fingers, better not to!" - return 1 - -/obj/item/weapon/twohanded/dualsaber/wield(mob/living/carbon/M) //Specific wield () hulk checks due to reflection chance for balance issues and switches hitsounds. - if(M.has_dna()) - if(M.dna.check_mutation(HULK)) - M << "You lack the grace to wield this!" - return - ..() - hitsound = 'sound/weapons/blade1.ogg' - -/obj/item/weapon/twohanded/dualsaber/unwield() //Specific unwield () to switch hitsounds. - ..() - hitsound = "swing_hit" - -/obj/item/weapon/twohanded/dualsaber/IsReflect() - if(wielded) - return 1 - -/obj/item/weapon/twohanded/dualsaber/green - New() - item_color = "green" - -/obj/item/weapon/twohanded/dualsaber/red - New() - item_color = "red" - -/obj/item/weapon/twohanded/dualsaber/attackby(obj/item/weapon/W, mob/user, params) - ..() - if(istype(W, /obj/item/device/multitool)) - if(hacked == 0) - hacked = 1 - user << "2XRNBW_ENGAGE" - item_color = "rainbow" - update_icon() - else - user << "It's starting to look like a triple rainbow - no, nevermind." - - -//spears -/obj/item/weapon/twohanded/spear - icon_state = "spearglass0" - name = "spear" - desc = "A haphazardly-constructed yet still deadly weapon of ancient design." - force = 10 - w_class = 4 - slot_flags = SLOT_BACK - force_unwielded = 10 - force_wielded = 18 - throwforce = 20 - throw_speed = 4 - embedded_impact_pain_multiplier = 3 - flags = NOSHIELD - hitsound = 'sound/weapons/bladeslice.ogg' - attack_verb = list("attacked", "poked", "jabbed", "torn", "gored") - sharpness = IS_SHARP - var/obj/item/weapon/grenade/explosive = null - var/war_cry = "AAAAARGH!!!" - -/obj/item/weapon/twohanded/spear/update_icon() - if(explosive) - icon_state = "spearbomb[wielded]" - else - icon_state = "spearglass[wielded]" - -/obj/item/weapon/twohanded/spear/afterattack(atom/movable/AM, mob/user, proximity) - if(!proximity) - return - if(istype(AM, /turf/simulated/floor)) //So you can actually melee with it - return - if(istype(AM, /turf/space)) //So you can actually melee with it - return - if(explosive && wielded) - user.say("[war_cry]") - explosive.loc = AM - explosive.prime() - qdel(src) - - //THIS MIGHT BE UNBALANCED SO I DUNNO -/obj/item/weapon/twohanded/spear/throw_impact(atom/target) - . = ..() - if(explosive) - explosive.prime() - qdel(src) - - -/obj/item/weapon/twohanded/spear/AltClick() - ..() - if(!explosive) - return - if(ismob(loc)) - var/mob/M = loc - var/input = stripped_input(M,"What do you want your war cry to be? You will shout it when you hit someone in melee.", ,"", 50) - if(input) - src.war_cry = input - -//Placeholder C4 "grenade" for use on this spear -/obj/item/weapon/grenade/C4 - name = "C-4" - desc = "A brick of C-4." - -/obj/item/weapon/grenade/C4/prime() - update_mob() - explosion(src.loc,-1,1,3) - qdel(src) - -/obj/item/weapon/twohanded/spear/CheckParts() - if(explosive) - explosive.loc = get_turf(src.loc) - explosive = null - var/obj/item/weapon/grenade/G = locate() in contents - if(G) - explosive = G - name = "explosive lance" - desc = "A makeshift spear with [G] attached to it. Alt+click on the spear to set your war cry!" - return - var/obj/item/weapon/c4/C4 = locate() in contents - if(C4) - var /obj/item/weapon/grenade/C4/C42 = new /obj/item/weapon/grenade/C4(src) - qdel(C4) - explosive = C42 - desc = "A makeshift spear with [C42] attached to it. Alt+click on the spear to set your war cry!" - update_icon() - -// CHAINSAW -/obj/item/weapon/twohanded/required/chainsaw - name = "chainsaw" - desc = "A versatile power tool. Useful for limbing trees and delimbing humans." - icon_state = "chainsaw_off" - flags = CONDUCT - force = 13 - w_class = 5 - throwforce = 13 - throw_speed = 2 - throw_range = 4 - materials = list(MAT_METAL=13000) - origin_tech = "materials=2;engineering=2;combat=2" - attack_verb = list("sawed", "torn", "cut", "chopped", "diced") - hitsound = "swing_hit" - sharpness = IS_SHARP - action_button_name = "Pull the starting cord" - var/on = 0 - -/obj/item/weapon/twohanded/required/chainsaw/attack_self(mob/user) - on = !on - user << "As you pull the starting cord dangling from \the [src], [on ? "it begins to whirr." : "the chain stops moving."]" - force = on ? 21 : 13 - throwforce = on ? 21 : 13 - icon_state = "chainsaw_[on ? "on" : "off"]" - - if(hitsound == "swing_hit") - hitsound = 'sound/weapons/chainsawhit.ogg' - else - hitsound = "swing_hit" - - if(src == user.get_active_hand()) //update inhands - user.update_inv_l_hand() - user.update_inv_r_hand() +/* Two-handed Weapons + * Contains: + * Twohanded + * Fireaxe + * Double-Bladed Energy Swords + * Spears + * CHAINSAWS + */ + +/*################################################################## +##################### TWO HANDED WEAPONS BE HERE~ -Agouri :3 ######## +####################################################################*/ + +//Rewrote TwoHanded weapons stuff and put it all here. Just copypasta fireaxe to make new ones ~Carn +//This rewrite means we don't have two variables for EVERY item which are used only by a few weapons. +//It also tidies stuff up elsewhere. + + + + +/* + * Twohanded + */ +/obj/item/weapon/twohanded + var/wielded = 0 + var/force_unwielded = 0 + var/force_wielded = 0 + var/wieldsound = null + var/unwieldsound = null + +/obj/item/weapon/twohanded/proc/unwield(mob/living/carbon/user) + if(!wielded || !user) return + wielded = 0 + if(force_unwielded) + force = force_unwielded + var/sf = findtext(name," (Wielded)") + if(sf) + name = copytext(name,1,sf) + else //something wrong + name = "[initial(name)]" + update_icon() + if(isrobot(user)) + user << "You free up your module." + else if(istype(src, /obj/item/weapon/twohanded/required)) + user << "You drop \the [name]." + else + user << "You are now carrying the [name] with one hand." + if(unwieldsound) + playsound(loc, unwieldsound, 50, 1) + var/obj/item/weapon/twohanded/offhand/O = user.get_inactive_hand() + if(O && istype(O)) + O.unwield() + return + +/obj/item/weapon/twohanded/proc/wield(mob/living/carbon/user) + if(wielded) return + if(istype(user,/mob/living/carbon/monkey) ) + user << "It's too heavy for you to wield fully." + return + if(user.get_inactive_hand()) + user << "You need your other hand to be empty!" + return + wielded = 1 + if(force_wielded) + force = force_wielded + name = "[name] (Wielded)" + update_icon() + if(isrobot(user)) + user << "You dedicate your module to [name]." + else + user << "You grab the [name] with both hands." + if (wieldsound) + playsound(loc, wieldsound, 50, 1) + var/obj/item/weapon/twohanded/offhand/O = new(user) ////Let's reserve his other hand~ + O.name = "[name] - offhand" + O.desc = "Your second grip on the [name]" + user.put_in_inactive_hand(O) + return + +/obj/item/weapon/twohanded/mob_can_equip(mob/M, slot) + //Cannot equip wielded items. + if(wielded) + M << "Unwield the [name] first!" + return 0 + return ..() + +/obj/item/weapon/twohanded/dropped(mob/user) + //handles unwielding a twohanded weapon when dropped as well as clearing up the offhand + if(user) + var/obj/item/weapon/twohanded/O = user.get_inactive_hand() + if(istype(O)) + O.unwield(user) + return unwield(user) + +/obj/item/weapon/twohanded/update_icon() + return + +/obj/item/weapon/twohanded/attack_self(mob/user) + ..() + if(wielded) //Trying to unwield it + unwield(user) + else //Trying to wield it + wield(user) + +///////////OFFHAND/////////////// +/obj/item/weapon/twohanded/offhand + name = "offhand" + icon_state = "offhand" + w_class = 5 + flags = ABSTRACT + +/obj/item/weapon/twohanded/offhand/unwield() + qdel(src) + +/obj/item/weapon/twohanded/offhand/wield() + qdel(src) + +/obj/item/weapon/twohanded/offhand/hit_reaction()//if the actual twohanded weapon is a shield, we count as a shield too! + var/mob/user = loc + if(!istype(user)) + return 0 + var/obj/item/I = user.get_active_hand() + if(I == src) + I = user.get_inactive_hand() + if(!I) + return 0 + return I.hit_reaction() + +///////////Two hand required objects/////////////// +//This is for objects that require two hands to even pick up +/obj/item/weapon/twohanded/required/ + w_class = 5 + +/obj/item/weapon/twohanded/required/attack_self() + return + +/obj/item/weapon/twohanded/required/mob_can_equip(mob/M, slot) + if(wielded) + M << "\The [src] is too cumbersome to carry with anything but your hands!" + return 0 + return ..() + +/obj/item/weapon/twohanded/required/attack_hand(mob/user)//Can't even pick it up without both hands empty + var/obj/item/weapon/twohanded/required/H = user.get_inactive_hand() + if(get_dist(src,user) > 1) + return 0 + if(H != null) + user << "\The [src] is too cumbersome to carry in one hand!" + return + wield(user) + ..() + + +/obj/item/weapon/twohanded/ + +/* + * Fireaxe + */ +/obj/item/weapon/twohanded/fireaxe // DEM AXES MAN, marker -Agouri + icon_state = "fireaxe0" + name = "fire axe" + desc = "Truly, the weapon of a madman. Who would think to fight fire with an axe?" + force = 5 + throwforce = 15 + w_class = 4 + slot_flags = SLOT_BACK + force_unwielded = 5 + force_wielded = 24 // Was 18, Buffed - RobRichards/RR + attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut") + hitsound = 'sound/weapons/bladeslice.ogg' + sharpness = IS_SHARP + +/obj/item/weapon/twohanded/fireaxe/update_icon() //Currently only here to fuck with the on-mob icons. + icon_state = "fireaxe[wielded]" + return + +/obj/item/weapon/twohanded/fireaxe/suicide_act(mob/user) + user.visible_message("[user] axes \himself from head to toe! It looks like \he's trying to commit suicide..") + return (BRUTELOSS) + +/obj/item/weapon/twohanded/fireaxe/afterattack(atom/A as mob|obj|turf|area, mob/user, proximity) + if(!proximity) return + if(wielded) //destroys windows and grilles in one hit + if(istype(A,/obj/structure/window)) + var/obj/structure/window/W = A + W.spawnfragments() // this will qdel and spawn shards + else if(istype(A,/obj/structure/grille)) + var/obj/structure/grille/G = A + G.health = -6 + G.destroyed += prob(25) // If this is set, healthcheck will completely remove the grille + G.healthcheck() + + +/* + * Double-Bladed Energy Swords - Cheridan + */ +/obj/item/weapon/twohanded/dualsaber + icon_state = "dualsaber0" + name = "double-bladed energy sword" + desc = "Handle with care." + force = 3 + throwforce = 5 + throw_speed = 3 + throw_range = 5 + w_class = 2 + force_unwielded = 3 + force_wielded = 34 + wieldsound = 'sound/weapons/saberon.ogg' + unwieldsound = 'sound/weapons/saberoff.ogg' + hitsound = "swing_hit" + flags = NOSHIELD + origin_tech = "magnets=3;syndicate=4" + item_color = "green" + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + block_chance = 50 + var/hacked = 0 + +/obj/item/weapon/twohanded/dualsaber/New() + item_color = pick("red", "blue", "green", "purple") + +/obj/item/weapon/twohanded/dualsaber/update_icon() + if(wielded) + icon_state = "dualsaber[item_color][wielded]" + else + icon_state = "dualsaber0" + clean_blood()//blood overlays get weird otherwise, because the sprite changes. + return + +/obj/item/weapon/twohanded/dualsaber/attack(mob/target, mob/living/carbon/human/user) + ..() + if(user.disabilities & CLUMSY && (wielded) && prob(40)) + impale(user) + return + if((wielded) && prob(50)) + spawn(0) + for(var/i in list(1,2,4,8,4,2,1,2,4,8,4,2)) + user.dir = i + if(i == 8) + user.emote("flip") + sleep(1) + +/obj/item/weapon/twohanded/dualsaber/proc/impale(mob/living/user) + user << "You twirl around a bit before losing your balance and impaling yourself on \the [src]." + if (force_wielded) + user.take_organ_damage(20,25) + else + user.adjustStaminaLoss(25) + +/obj/item/weapon/twohanded/dualsaber/hit_reaction(mob/living/carbon/human/owner, attack_text, final_block_chance) + if(wielded) + return ..() + return 0 + +/obj/item/weapon/twohanded/dualsaber/attack_hulk(mob/living/carbon/human/user) //In case thats just so happens that it is still activated on the groud, prevents hulk from picking it up + if(wielded) + user << "You can't pick up such dangerous item with your meaty hands without losing fingers, better not to!" + return 1 + +/obj/item/weapon/twohanded/dualsaber/wield(mob/living/carbon/M) //Specific wield () hulk checks due to reflection chance for balance issues and switches hitsounds. + if(M.has_dna()) + if(M.dna.check_mutation(HULK)) + M << "You lack the grace to wield this!" + return + ..() + hitsound = 'sound/weapons/blade1.ogg' + +/obj/item/weapon/twohanded/dualsaber/unwield() //Specific unwield () to switch hitsounds. + ..() + hitsound = "swing_hit" + +/obj/item/weapon/twohanded/dualsaber/IsReflect() + if(wielded) + return 1 + +/obj/item/weapon/twohanded/dualsaber/green + New() + item_color = "green" + +/obj/item/weapon/twohanded/dualsaber/red + New() + item_color = "red" + +/obj/item/weapon/twohanded/dualsaber/attackby(obj/item/weapon/W, mob/user, params) + ..() + if(istype(W, /obj/item/device/multitool)) + if(hacked == 0) + hacked = 1 + user << "2XRNBW_ENGAGE" + item_color = "rainbow" + update_icon() + else + user << "It's starting to look like a triple rainbow - no, nevermind." + + +//spears +/obj/item/weapon/twohanded/spear + icon_state = "spearglass0" + name = "spear" + desc = "A haphazardly-constructed yet still deadly weapon of ancient design." + force = 10 + w_class = 4 + slot_flags = SLOT_BACK + force_unwielded = 10 + force_wielded = 18 + throwforce = 20 + throw_speed = 4 + embedded_impact_pain_multiplier = 3 + flags = NOSHIELD + hitsound = 'sound/weapons/bladeslice.ogg' + attack_verb = list("attacked", "poked", "jabbed", "torn", "gored") + sharpness = IS_SHARP + var/obj/item/weapon/grenade/explosive = null + var/war_cry = "AAAAARGH!!!" + +/obj/item/weapon/twohanded/spear/update_icon() + if(explosive) + icon_state = "spearbomb[wielded]" + else + icon_state = "spearglass[wielded]" + +/obj/item/weapon/twohanded/spear/afterattack(atom/movable/AM, mob/user, proximity) + if(!proximity) + return + if(istype(AM, /turf/simulated/floor)) //So you can actually melee with it + return + if(istype(AM, /turf/space)) //So you can actually melee with it + return + if(explosive && wielded) + user.say("[war_cry]") + explosive.loc = AM + explosive.prime() + qdel(src) + + //THIS MIGHT BE UNBALANCED SO I DUNNO +/obj/item/weapon/twohanded/spear/throw_impact(atom/target) + . = ..() + if(explosive) + explosive.prime() + qdel(src) + + +/obj/item/weapon/twohanded/spear/AltClick() + ..() + if(!explosive) + return + if(ismob(loc)) + var/mob/M = loc + var/input = stripped_input(M,"What do you want your war cry to be? You will shout it when you hit someone in melee.", ,"", 50) + if(input) + src.war_cry = input + +//Placeholder C4 "grenade" for use on this spear +/obj/item/weapon/grenade/C4 + name = "C-4" + desc = "A brick of C-4." + +/obj/item/weapon/grenade/C4/prime() + update_mob() + explosion(src.loc,-1,1,3) + qdel(src) + +/obj/item/weapon/twohanded/spear/CheckParts() + if(explosive) + explosive.loc = get_turf(src.loc) + explosive = null + var/obj/item/weapon/grenade/G = locate() in contents + if(G) + explosive = G + name = "explosive lance" + desc = "A makeshift spear with [G] attached to it. Alt+click on the spear to set your war cry!" + return + var/obj/item/weapon/c4/C4 = locate() in contents + if(C4) + var /obj/item/weapon/grenade/C4/C42 = new /obj/item/weapon/grenade/C4(src) + qdel(C4) + explosive = C42 + desc = "A makeshift spear with [C42] attached to it. Alt+click on the spear to set your war cry!" + update_icon() + +// CHAINSAW +/obj/item/weapon/twohanded/required/chainsaw + name = "chainsaw" + desc = "A versatile power tool. Useful for limbing trees and delimbing humans." + icon_state = "chainsaw_off" + flags = CONDUCT + force = 13 + w_class = 5 + throwforce = 13 + throw_speed = 2 + throw_range = 4 + materials = list(MAT_METAL=13000) + origin_tech = "materials=2;engineering=2;combat=2" + attack_verb = list("sawed", "torn", "cut", "chopped", "diced") + hitsound = "swing_hit" + sharpness = IS_SHARP + action_button_name = "Pull the starting cord" + var/on = 0 + +/obj/item/weapon/twohanded/required/chainsaw/attack_self(mob/user) + on = !on + user << "As you pull the starting cord dangling from \the [src], [on ? "it begins to whirr." : "the chain stops moving."]" + force = on ? 21 : 13 + throwforce = on ? 21 : 13 + icon_state = "chainsaw_[on ? "on" : "off"]" + + if(hitsound == "swing_hit") + hitsound = 'sound/weapons/chainsawhit.ogg' + else + hitsound = "swing_hit" + + if(src == user.get_active_hand()) //update inhands + user.update_inv_l_hand() + user.update_inv_r_hand() diff --git a/code/game/objects/structures/door_assembly.dm b/code/game/objects/structures/door_assembly.dm index 2054d013079..94ea8e37317 100644 --- a/code/game/objects/structures/door_assembly.dm +++ b/code/game/objects/structures/door_assembly.dm @@ -1,667 +1,667 @@ -/obj/structure/door_assembly - name = "airlock assembly" - icon = 'icons/obj/doors/airlocks/station/public.dmi' - icon_state = "construction" - var/overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' - anchored = 0 - density = 1 - var/state = 0 - var/mineral = null - var/typetext = "" - var/icontext = "" - var/obj/item/weapon/electronics/airlock/electronics = null - var/airlock_type = /obj/machinery/door/airlock //the type path of the airlock once completed - var/glass_type = /obj/machinery/door/airlock/glass - var/created_name = null - var/heat_proof_finished = 0 //whether to heat-proof the finished airlock - var/material = null //icon state logic - -/obj/structure/door_assembly/New() - update_icon() - -/obj/structure/door_assembly/door_assembly_0 - name = "airlock assembly" - airlock_type = /obj/machinery/door/airlock - anchored = 1 - state = 1 - -/obj/structure/door_assembly/door_assembly_com - name = "command airlock assembly" - icon = 'icons/obj/doors/airlocks/station/command.dmi' - typetext = "command" - icontext = "com" - glass_type = /obj/machinery/door/airlock/glass_command - airlock_type = /obj/machinery/door/airlock/command - anchored = 1 - state = 1 - -/obj/structure/door_assembly/door_assembly_com/glass - mineral = "glass" - material = "glass" - -/obj/structure/door_assembly/door_assembly_sec - name = "security airlock assembly" - icon = 'icons/obj/doors/airlocks/station/security.dmi' - typetext = "security" - icontext = "sec" - glass_type = /obj/machinery/door/airlock/glass_security - airlock_type = /obj/machinery/door/airlock/security - anchored = 1 - state = 1 - -/obj/structure/door_assembly/door_assembly_sec/glass - mineral = "glass" - material = "glass" - -/obj/structure/door_assembly/door_assembly_eng - name = "engineering airlock assembly" - icon = 'icons/obj/doors/airlocks/station/engineering.dmi' - typetext = "engineering" - icontext = "eng" - glass_type = /obj/machinery/door/airlock/glass_engineering - airlock_type = /obj/machinery/door/airlock/engineering - anchored = 1 - state = 1 - -/obj/structure/door_assembly/door_assembly_eng/glass - mineral = "glass" - material = "glass" - -/obj/structure/door_assembly/door_assembly_min - name = "mining airlock assembly" - icon = 'icons/obj/doors/airlocks/station/mining.dmi' - typetext = "mining" - icontext = "min" - glass_type = /obj/machinery/door/airlock/glass_mining - airlock_type = /obj/machinery/door/airlock/mining - anchored = 1 - state = 1 - -/obj/structure/door_assembly/door_assembly_min/glass - mineral = "glass" - material = "glass" - -/obj/structure/door_assembly/door_assembly_atmo - name = "atmospherics airlock assembly" - icon = 'icons/obj/doors/airlocks/station/atmos.dmi' - typetext = "atmos" - icontext = "atmo" - glass_type = /obj/machinery/door/airlock/glass_atmos - airlock_type = /obj/machinery/door/airlock/atmos - anchored = 1 - state = 1 - -/obj/structure/door_assembly/door_assembly_atmo/glass - mineral = "glass" - material = "glass" - -/obj/structure/door_assembly/door_assembly_research - name = "research airlock assembly" - icon = 'icons/obj/doors/airlocks/station/research.dmi' - typetext = "research" - icontext = "res" - glass_type = /obj/machinery/door/airlock/glass_research - airlock_type = /obj/machinery/door/airlock/research - anchored = 1 - state = 1 - -/obj/structure/door_assembly/door_assembly_research/glass - mineral = "glass" - material = "glass" - -/obj/structure/door_assembly/door_assembly_science - name = "science airlock assembly" - icon = 'icons/obj/doors/airlocks/station/science.dmi' - typetext = "science" - icontext = "sci" - glass_type = /obj/machinery/door/airlock/glass_science - airlock_type = /obj/machinery/door/airlock/science - anchored = 1 - state = 1 - -/obj/structure/door_assembly/door_assembly_science/glass - mineral = "glass" - material = "glass" - -/obj/structure/door_assembly/door_assembly_med - name = "medical airlock assembly" - icon = 'icons/obj/doors/airlocks/station/medical.dmi' - typetext = "medical" - icontext = "med" - glass_type = /obj/machinery/door/airlock/glass_medical - airlock_type = /obj/machinery/door/airlock/medical - anchored = 1 - state = 1 - -/obj/structure/door_assembly/door_assembly_med/glass - mineral = "glass" - material = "glass" - -/obj/structure/door_assembly/door_assembly_mai - name = "maintenance airlock assembly" - icon = 'icons/obj/doors/airlocks/station/maintenance.dmi' - typetext = "maintenance" - icontext = "mai" - glass_type = /obj/machinery/door/airlock/glass_maintenance - airlock_type = /obj/machinery/door/airlock/maintenance - anchored = 1 - state = 1 - -/obj/structure/door_assembly/door_assembly_mai/glass - mineral = "glass" - material = "glass" - -/obj/structure/door_assembly/door_assembly_ext - name = "external airlock assembly" - icon = 'icons/obj/doors/airlocks/external/external.dmi' - overlays_file = 'icons/obj/doors/airlocks/external/overlays.dmi' - typetext = "external" - icontext = "ext" - glass_type = /obj/machinery/door/airlock/glass_external - airlock_type = /obj/machinery/door/airlock/external - anchored = 1 - state = 1 - -/obj/structure/door_assembly/door_assembly_ext/glass - mineral = "glass" - material = "glass" - -/obj/structure/door_assembly/door_assembly_fre - name = "freezer airlock assembly" - icon = 'icons/obj/doors/airlocks/station/freezer.dmi' - typetext = "freezer" - icontext = "fre" - airlock_type = /obj/machinery/door/airlock/freezer - anchored = 1 - state = 1 - -/obj/structure/door_assembly/door_assembly_hatch - name = "airtight hatch assembly" - icon = 'icons/obj/doors/airlocks/hatch/centcom.dmi' - overlays_file = 'icons/obj/doors/airlocks/hatch/overlays.dmi' - typetext = "hatch" - icontext = "hatch" - airlock_type = /obj/machinery/door/airlock/hatch - anchored = 1 - state = 1 - -/obj/structure/door_assembly/door_assembly_mhatch - name = "maintenance hatch assembly" - icon = 'icons/obj/doors/airlocks/hatch/maintenance.dmi' - overlays_file = 'icons/obj/doors/airlocks/hatch/overlays.dmi' - typetext = "maintenance_hatch" - icontext = "mhatch" - airlock_type = /obj/machinery/door/airlock/maintenance_hatch - anchored = 1 - state = 1 - -/obj/structure/door_assembly/door_assembly_glass - name = "glass airlock assembly" - icon = 'icons/obj/doors/airlocks/station2/glass.dmi' - overlays_file = 'icons/obj/doors/airlocks/station2/overlays.dmi' - airlock_type = /obj/machinery/door/airlock/glass - anchored = 1 - state = 1 - mineral = "glass" - material = "glass" - -/obj/structure/door_assembly/door_assembly_gold - name = "gold airlock assembly" - icon = 'icons/obj/doors/airlocks/station/gold.dmi' - airlock_type = /obj/machinery/door/airlock/gold - anchored = 1 - state = 1 - mineral = "gold" - -/obj/structure/door_assembly/door_assembly_silver - name = "silver airlock assembly" - icon = 'icons/obj/doors/airlocks/station/silver.dmi' - airlock_type = /obj/machinery/door/airlock/silver - anchored = 1 - state = 1 - mineral = "silver" - -/obj/structure/door_assembly/door_assembly_diamond - name = "diamond airlock assembly" - icon = 'icons/obj/doors/airlocks/station/diamond.dmi' - airlock_type = /obj/machinery/door/airlock/diamond - anchored = 1 - state = 1 - mineral = "diamond" - -/obj/structure/door_assembly/door_assembly_uranium - name = "uranium airlock assembly" - icon = 'icons/obj/doors/airlocks/station/uranium.dmi' - airlock_type = /obj/machinery/door/airlock/uranium - anchored = 1 - state = 1 - mineral = "uranium" - -/obj/structure/door_assembly/door_assembly_plasma - name = "plasma airlock assembly" - icon = 'icons/obj/doors/airlocks/station/plasma.dmi' - airlock_type = /obj/machinery/door/airlock/plasma - anchored = 1 - state = 1 - mineral = "plasma" - -/obj/structure/door_assembly/door_assembly_clown - name = "bananium airlock assembly" - desc = "Honk" - icon = 'icons/obj/doors/airlocks/station/bananium.dmi' - airlock_type = /obj/machinery/door/airlock/clown - anchored = 1 - state = 1 - mineral = "bananium" - -/obj/structure/door_assembly/door_assembly_sandstone - name = "sandstone airlock assembly" - icon = 'icons/obj/doors/airlocks/station/sandstone.dmi' - airlock_type = /obj/machinery/door/airlock/sandstone - anchored = 1 - state = 1 - mineral = "sandstone" - -/obj/structure/door_assembly/door_assembly_highsecurity // Borrowing this until WJohnston makes sprites for the assembly - name = "high security airlock assembly" - icon = 'icons/obj/doors/airlocks/highsec/highsec.dmi' - overlays_file = 'icons/obj/doors/airlocks/highsec/overlays.dmi' - typetext = "highsecurity" - icontext = "highsec" - airlock_type = /obj/machinery/door/airlock/highsecurity - anchored = 1 - state = 1 - -/obj/structure/door_assembly/door_assembly_vault - name = "vault door assembly" - icon = 'icons/obj/doors/airlocks/vault/vault.dmi' - overlays_file = 'icons/obj/doors/airlocks/vault/overlays.dmi' - typetext = "vault" - icontext = "vault" - airlock_type = /obj/machinery/door/airlock/vault - anchored = 1 - state = 1 - -/obj/structure/door_assembly/door_assembly_shuttle - name = "shuttle airlock assembly" - icon = 'icons/obj/doors/airlocks/shuttle/shuttle.dmi' - overlays_file = 'icons/obj/doors/airlocks/shuttle/overlays.dmi' - typetext = "shuttle" - icontext = "shuttle" - airlock_type = /obj/machinery/door/airlock/shuttle - anchored = 1 - state = 1 - -/obj/structure/door_assembly/door_assembly_wood - name = "wooden airlock assembly" - icon = 'icons/obj/doors/airlocks/station/wood.dmi' - airlock_type = /obj/machinery/door/airlock/wood - anchored = 1 - state = 1 - mineral = "wood" - -/obj/structure/door_assembly/door_assembly_viro - name = "virology airlock assembly" - icon = 'icons/obj/doors/airlocks/station/virology.dmi' - typetext = "virology" - icontext = "viro" - glass_type = /obj/machinery/door/airlock/glass_virology - airlock_type = /obj/machinery/door/airlock/virology - anchored = 1 - state = 1 - -/obj/structure/door_assembly/door_assembly_viro/glass - mineral = "glass" - material = "glass" - -/obj/structure/door_assembly/door_assembly_centcom - typetext = "centcom" - icon = 'icons/obj/doors/airlocks/centcom/centcom.dmi' - overlays_file = 'icons/obj/doors/airlocks/centcom/overlays.dmi' - icontext = "ele" - airlock_type = /obj/machinery/door/airlock/centcom - anchored = 1 - state = 1 - -/obj/structure/door_assembly/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/weapon/pen)) - var/t = stripped_input(user, "Enter the name for the door.", src.name, src.created_name,MAX_NAME_LEN) - if(!t) - return - if(!in_range(src, usr) && src.loc != usr) - return - created_name = t - return - - else if(istype(W, /obj/item/weapon/airlock_painter)) // |- Ricotez - //INFORMATION ABOUT ADDING A NEW AIRLOCK TO THE PAINT LIST: - //If your airlock has a regular version, add it to the list with regular versions. - //If your airlock has a glass version, add it to the list with glass versions. - //Don't forget to also set has_solid and has_glass to the proper value. - //Do NOT add your airlock to a list if it does not have a version for that list, - // or you will get broken icons. - var/obj/item/weapon/airlock_painter/WT = W - if(WT.can_use(user)) - var/icontype - var/optionlist - if(mineral && mineral == "glass") - //These airlocks have a glass version. - optionlist = list("Public", "Public2", "Engineering", "Atmospherics", "Security", "Command", "Medical", "Research", "Science", "Mining") - else - //These airlocks have a regular version. - optionlist = list("Public", "Engineering", "Atmospherics", "Security", "Command", "Medical", "Research", "Science", "Mining", "Maintenance", "External", "High Security") - - - icontype = input(user, "Please select a paintjob for this airlock.") in optionlist - if((!in_range(src, usr) && src.loc != usr) || !WT.use(user)) - return - var/has_solid = 0 - var/has_glass = 0 - switch(icontype) - if("Public") - icon = 'icons/obj/doors/airlocks/station/public.dmi' - overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' - typetext = "" - icontext = "" - has_solid = 1 - has_glass = 1 - if("Public2") - icon = 'icons/obj/doors/airlocks/station2/glass.dmi' - overlays_file = 'icons/obj/doors/airlocks/station2/overlays.dmi' - typetext = "" - icontext = "" - has_solid = 1 - has_glass = 1 - if("Engineering") - icon = 'icons/obj/doors/airlocks/station/engineering.dmi' - overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' - typetext = "engineering" - icontext = "eng" - has_solid = 1 - has_glass = 1 - if("Atmospherics") - icon = 'icons/obj/doors/airlocks/station/atmos.dmi' - overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' - typetext = "atmos" - icontext = "atmo" - has_solid = 1 - has_glass = 1 - if("Security") - icon = 'icons/obj/doors/airlocks/station/security.dmi' - overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' - typetext = "security" - icontext = "sec" - has_solid = 1 - has_glass = 1 - if("Command") - icon = 'icons/obj/doors/airlocks/station/command.dmi' - overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' - typetext = "command" - icontext = "com" - has_solid = 1 - has_glass = 1 - if("Medical") - icon = 'icons/obj/doors/airlocks/station/medical.dmi' - overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' - typetext = "medical" - icontext = "med" - has_solid = 1 - has_glass = 1 - if("Research") - icon = 'icons/obj/doors/airlocks/station/research.dmi' - overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' - typetext = "research" - icontext = "res" - has_solid = 1 - has_glass = 1 - if("Science") - icon = 'icons/obj/doors/airlocks/station/science.dmi' - overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' - typetext = "research" - icontext = "res" - has_solid = 1 - has_glass = 1 - if("Mining") - icon = 'icons/obj/doors/airlocks/station/mining.dmi' - overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' - typetext = "mining" - icontext = "min" - has_solid = 1 - has_glass = 1 - if("Maintenance") - icon = 'icons/obj/doors/airlocks/station/maintenance.dmi' - overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' - typetext = "maintenance" - icontext = "mai" - has_solid = 1 - has_glass = 0 - if("External") - icon = 'icons/obj/doors/airlocks/external/external.dmi' - overlays_file = 'icons/obj/doors/airlocks/external/overlays.dmi' - typetext = "external" - icontext = "ext" - has_solid = 1 - has_glass = 0 - if("High Security") - icon = 'icons/obj/doors/airlocks/highsec/highsec.dmi' - overlays_file = 'icons/obj/doors/airlocks/highsec/overlays.dmi' - typetext = "highsecurity" - icontext = "highsec" - has_solid = 1 - has_glass = 0 - if(has_solid) - airlock_type = text2path("/obj/machinery/door/airlock/[typetext]") - else - airlock_type = /obj/machinery/door/airlock - - if(has_glass) - glass_type = text2path("/obj/machinery/door/airlock/glass_[typetext]") - else - glass_type = /obj/machinery/door/airlock/glass - - if(mineral && mineral != "glass") - mineral = null //I know this is stupid, but until we change glass to a boolean it's how this code works. - user << "You change the paintjob on the airlock assembly." - - else if(istype(W, /obj/item/weapon/weldingtool) && !anchored ) - var/obj/item/weapon/weldingtool/WT = W - if(WT.remove_fuel(0,user)) - user.visible_message("[user] disassembles the airlock assembly.", \ - "You start to disassemble the airlock assembly...") - playsound(src.loc, 'sound/items/Welder2.ogg', 50, 1) - - if(do_after(user, 40/W.toolspeed, target = src)) - if( !WT.isOn() ) - return - user << "You disassemble the airlock assembly." - new /obj/item/stack/sheet/metal(get_turf(src), 4) - if (mineral) - if (mineral == "glass") - if (heat_proof_finished) - new /obj/item/stack/sheet/rglass(get_turf(src)) - else - new /obj/item/stack/sheet/glass(get_turf(src)) - else - var/M = text2path("/obj/item/stack/sheet/mineral/[mineral]") - new M(get_turf(src)) - new M(get_turf(src)) - qdel(src) - else - return - - else if(istype(W, /obj/item/weapon/wrench) && !anchored ) - var/door_check = 1 - for(var/obj/machinery/door/D in loc) - if(!D.sub_door) - door_check = 0 - break - - if(door_check) - playsound(src.loc, 'sound/items/Ratchet.ogg', 100, 1) - user.visible_message("[user] secures the airlock assembly to the floor.", \ - "You start to secure the airlock assembly to the floor...", \ - "You hear wrenching.") - - if(do_after(user, 40/W.toolspeed, target = src)) - if( src.anchored ) - return - user << "You secure the airlock assembly." - src.name = "secured airlock assembly" - src.anchored = 1 - else - user << "There is another door here!" - - else if(istype(W, /obj/item/weapon/wrench) && anchored ) - playsound(src.loc, 'sound/items/Ratchet.ogg', 100, 1) - user.visible_message("[user] unsecures the airlock assembly from the floor.", \ - "You start to unsecure the airlock assembly from the floor...", \ - "You hear wrenching.") - if(do_after(user, 40/W.toolspeed, target = src)) - if( !src.anchored ) - return - user << "You unsecure the airlock assembly." - src.name = "airlock assembly" - src.anchored = 0 - - else if(istype(W, /obj/item/stack/cable_coil) && state == 0 && anchored ) - var/obj/item/stack/cable_coil/C = W - if (C.get_amount() < 1) - user << "You need one length of cable to wire the airlock assembly!" - return - user.visible_message("[user] wires the airlock assembly.", \ - "You start to wire the airlock assembly...") - if(do_after(user, 40, target = src)) - if(C.get_amount() < 1 || state != 0) return - C.use(1) - src.state = 1 - user << "You wire the airlock assembly." - src.name = "wired airlock assembly" - - else if(istype(W, /obj/item/weapon/wirecutters) && state == 1 ) - playsound(src.loc, 'sound/items/Wirecutter.ogg', 100, 1) - user.visible_message("[user] cuts the wires from the airlock assembly.", \ - "You start to cut the wires from the airlock assembly...") - - if(do_after(user, 40/W.toolspeed, target = src)) - if( src.state != 1 ) - return - user << "You cut the wires from the airlock assembly." - new/obj/item/stack/cable_coil(get_turf(user), 1) - src.state = 0 - src.name = "secured airlock assembly" - - else if(istype(W, /obj/item/weapon/electronics/airlock) && state == 1 ) - playsound(src.loc, 'sound/items/Screwdriver.ogg', 100, 1) - user.visible_message("[user] installs the electronics into the airlock assembly.", \ - "You start to install electronics into the airlock assembly...") - if(do_after(user, 40, target = src)) - if( src.state != 1 ) - return - if(!user.drop_item()) - return - - W.loc = src - user << "You install the airlock electronics." - src.state = 2 - src.name = "near finished airlock assembly" - src.electronics = W - - - else if(istype(W, /obj/item/weapon/crowbar) && state == 2 ) - 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/W.toolspeed, target = src)) - if( src.state != 2 ) - return - user << "You remove the airlock electronics." - src.state = 1 - src.name = "wired airlock assembly" - var/obj/item/weapon/electronics/airlock/ae - if (!electronics) - ae = new/obj/item/weapon/electronics/airlock( src.loc ) - else - ae = electronics - electronics = null - ae.loc = src.loc - else if(istype(W, /obj/item/stack/sheet) && !mineral) - var/obj/item/stack/sheet/G = W - if(G) - if(G.get_amount() >= 1) - if(istype(G, /obj/item/stack/sheet/rglass) || istype(G, /obj/item/stack/sheet/glass)) - playsound(src.loc, 'sound/items/Crowbar.ogg', 100, 1) - user.visible_message("[user] adds [G.name] to the airlock assembly.", \ - "You start to install [G.name] into the airlock assembly...") - if(do_after(user, 40, target = src)) - if(G.get_amount() < 1 || mineral) return - if (G.type == /obj/item/stack/sheet/rglass) - user << "You install reinforced glass windows into the airlock assembly." - heat_proof_finished = 1 //reinforced glass makes the airlock heat-proof - name = "near finished heat-proofed window airlock assembly" - else - user << "You install regular glass windows into the airlock assembly." - name = "near finished window airlock assembly" - G.use(1) - mineral = "glass" - material = "glass" - //This list contains the airlock paintjobs that have a glass version: - if(icontext in list("eng", "atmo", "sec", "com", "med", "res", "min")) - src.airlock_type = text2path("/obj/machinery/door/airlock/[typetext]") - src.glass_type = text2path("/obj/machinery/door/airlock/glass_[typetext]") - else - //This airlock is default or does not have a glass version, so we revert to the default glass airlock. |- Ricotez - airlock_type = /obj/machinery/door/airlock - glass_type = /obj/machinery/door/airlock/glass - typetext = "" - icontext = "" - else if(istype(G, /obj/item/stack/sheet/mineral)) - var/M = G.sheettype - if(G.get_amount() >= 2) - playsound(src.loc, 'sound/items/Crowbar.ogg', 100, 1) - user.visible_message("[user] adds [G.name] to the airlock assembly.", \ - "You start to install [G.name] into the airlock assembly...") - if(do_after(user, 40, target = src)) - if(G.get_amount() < 2 || mineral) return - user << "You install [M] plating into the airlock assembly." - G.use(2) - mineral = "[M]" - name = "near finished [M] airlock assembly" - airlock_type = text2path ("/obj/machinery/door/airlock/[M]") - glass_type = /obj/machinery/door/airlock/glass - - else if(istype(W, /obj/item/weapon/screwdriver) && state == 2 ) - playsound(src.loc, 'sound/items/Screwdriver.ogg', 100, 1) - user.visible_message("[user] finishes the airlock.", \ - "You start finishing the airlock...") - - if(do_after(user, 40/W.toolspeed, target = src)) - if(src.loc && state == 2) - user << "You finish the airlock." - var/obj/machinery/door/airlock/door - if(mineral == "glass") - door = new src.glass_type( src.loc ) - else - door = new src.airlock_type( src.loc ) - //door.req_access = src.req_access - door.electronics = src.electronics - door.heat_proof = src.heat_proof_finished - if(src.electronics.one_access) - door.req_one_access = src.electronics.accesses - else - door.req_access = src.electronics.accesses - if(created_name) - door.name = created_name - src.electronics.loc = door - qdel(src) - else - ..() - update_icon() - -/obj/structure/door_assembly/update_icon() - overlays.Cut() - if(!material) - overlays += get_airlock_overlay("fill_construction", icon) - else - overlays += get_airlock_overlay("[material]_construction", overlays_file) - overlays += get_airlock_overlay("panel_c[state+1]", overlays_file) +/obj/structure/door_assembly + name = "airlock assembly" + icon = 'icons/obj/doors/airlocks/station/public.dmi' + icon_state = "construction" + var/overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' + anchored = 0 + density = 1 + var/state = 0 + var/mineral = null + var/typetext = "" + var/icontext = "" + var/obj/item/weapon/electronics/airlock/electronics = null + var/airlock_type = /obj/machinery/door/airlock //the type path of the airlock once completed + var/glass_type = /obj/machinery/door/airlock/glass + var/created_name = null + var/heat_proof_finished = 0 //whether to heat-proof the finished airlock + var/material = null //icon state logic + +/obj/structure/door_assembly/New() + update_icon() + +/obj/structure/door_assembly/door_assembly_0 + name = "airlock assembly" + airlock_type = /obj/machinery/door/airlock + anchored = 1 + state = 1 + +/obj/structure/door_assembly/door_assembly_com + name = "command airlock assembly" + icon = 'icons/obj/doors/airlocks/station/command.dmi' + typetext = "command" + icontext = "com" + glass_type = /obj/machinery/door/airlock/glass_command + airlock_type = /obj/machinery/door/airlock/command + anchored = 1 + state = 1 + +/obj/structure/door_assembly/door_assembly_com/glass + mineral = "glass" + material = "glass" + +/obj/structure/door_assembly/door_assembly_sec + name = "security airlock assembly" + icon = 'icons/obj/doors/airlocks/station/security.dmi' + typetext = "security" + icontext = "sec" + glass_type = /obj/machinery/door/airlock/glass_security + airlock_type = /obj/machinery/door/airlock/security + anchored = 1 + state = 1 + +/obj/structure/door_assembly/door_assembly_sec/glass + mineral = "glass" + material = "glass" + +/obj/structure/door_assembly/door_assembly_eng + name = "engineering airlock assembly" + icon = 'icons/obj/doors/airlocks/station/engineering.dmi' + typetext = "engineering" + icontext = "eng" + glass_type = /obj/machinery/door/airlock/glass_engineering + airlock_type = /obj/machinery/door/airlock/engineering + anchored = 1 + state = 1 + +/obj/structure/door_assembly/door_assembly_eng/glass + mineral = "glass" + material = "glass" + +/obj/structure/door_assembly/door_assembly_min + name = "mining airlock assembly" + icon = 'icons/obj/doors/airlocks/station/mining.dmi' + typetext = "mining" + icontext = "min" + glass_type = /obj/machinery/door/airlock/glass_mining + airlock_type = /obj/machinery/door/airlock/mining + anchored = 1 + state = 1 + +/obj/structure/door_assembly/door_assembly_min/glass + mineral = "glass" + material = "glass" + +/obj/structure/door_assembly/door_assembly_atmo + name = "atmospherics airlock assembly" + icon = 'icons/obj/doors/airlocks/station/atmos.dmi' + typetext = "atmos" + icontext = "atmo" + glass_type = /obj/machinery/door/airlock/glass_atmos + airlock_type = /obj/machinery/door/airlock/atmos + anchored = 1 + state = 1 + +/obj/structure/door_assembly/door_assembly_atmo/glass + mineral = "glass" + material = "glass" + +/obj/structure/door_assembly/door_assembly_research + name = "research airlock assembly" + icon = 'icons/obj/doors/airlocks/station/research.dmi' + typetext = "research" + icontext = "res" + glass_type = /obj/machinery/door/airlock/glass_research + airlock_type = /obj/machinery/door/airlock/research + anchored = 1 + state = 1 + +/obj/structure/door_assembly/door_assembly_research/glass + mineral = "glass" + material = "glass" + +/obj/structure/door_assembly/door_assembly_science + name = "science airlock assembly" + icon = 'icons/obj/doors/airlocks/station/science.dmi' + typetext = "science" + icontext = "sci" + glass_type = /obj/machinery/door/airlock/glass_science + airlock_type = /obj/machinery/door/airlock/science + anchored = 1 + state = 1 + +/obj/structure/door_assembly/door_assembly_science/glass + mineral = "glass" + material = "glass" + +/obj/structure/door_assembly/door_assembly_med + name = "medical airlock assembly" + icon = 'icons/obj/doors/airlocks/station/medical.dmi' + typetext = "medical" + icontext = "med" + glass_type = /obj/machinery/door/airlock/glass_medical + airlock_type = /obj/machinery/door/airlock/medical + anchored = 1 + state = 1 + +/obj/structure/door_assembly/door_assembly_med/glass + mineral = "glass" + material = "glass" + +/obj/structure/door_assembly/door_assembly_mai + name = "maintenance airlock assembly" + icon = 'icons/obj/doors/airlocks/station/maintenance.dmi' + typetext = "maintenance" + icontext = "mai" + glass_type = /obj/machinery/door/airlock/glass_maintenance + airlock_type = /obj/machinery/door/airlock/maintenance + anchored = 1 + state = 1 + +/obj/structure/door_assembly/door_assembly_mai/glass + mineral = "glass" + material = "glass" + +/obj/structure/door_assembly/door_assembly_ext + name = "external airlock assembly" + icon = 'icons/obj/doors/airlocks/external/external.dmi' + overlays_file = 'icons/obj/doors/airlocks/external/overlays.dmi' + typetext = "external" + icontext = "ext" + glass_type = /obj/machinery/door/airlock/glass_external + airlock_type = /obj/machinery/door/airlock/external + anchored = 1 + state = 1 + +/obj/structure/door_assembly/door_assembly_ext/glass + mineral = "glass" + material = "glass" + +/obj/structure/door_assembly/door_assembly_fre + name = "freezer airlock assembly" + icon = 'icons/obj/doors/airlocks/station/freezer.dmi' + typetext = "freezer" + icontext = "fre" + airlock_type = /obj/machinery/door/airlock/freezer + anchored = 1 + state = 1 + +/obj/structure/door_assembly/door_assembly_hatch + name = "airtight hatch assembly" + icon = 'icons/obj/doors/airlocks/hatch/centcom.dmi' + overlays_file = 'icons/obj/doors/airlocks/hatch/overlays.dmi' + typetext = "hatch" + icontext = "hatch" + airlock_type = /obj/machinery/door/airlock/hatch + anchored = 1 + state = 1 + +/obj/structure/door_assembly/door_assembly_mhatch + name = "maintenance hatch assembly" + icon = 'icons/obj/doors/airlocks/hatch/maintenance.dmi' + overlays_file = 'icons/obj/doors/airlocks/hatch/overlays.dmi' + typetext = "maintenance_hatch" + icontext = "mhatch" + airlock_type = /obj/machinery/door/airlock/maintenance_hatch + anchored = 1 + state = 1 + +/obj/structure/door_assembly/door_assembly_glass + name = "glass airlock assembly" + icon = 'icons/obj/doors/airlocks/station2/glass.dmi' + overlays_file = 'icons/obj/doors/airlocks/station2/overlays.dmi' + airlock_type = /obj/machinery/door/airlock/glass + anchored = 1 + state = 1 + mineral = "glass" + material = "glass" + +/obj/structure/door_assembly/door_assembly_gold + name = "gold airlock assembly" + icon = 'icons/obj/doors/airlocks/station/gold.dmi' + airlock_type = /obj/machinery/door/airlock/gold + anchored = 1 + state = 1 + mineral = "gold" + +/obj/structure/door_assembly/door_assembly_silver + name = "silver airlock assembly" + icon = 'icons/obj/doors/airlocks/station/silver.dmi' + airlock_type = /obj/machinery/door/airlock/silver + anchored = 1 + state = 1 + mineral = "silver" + +/obj/structure/door_assembly/door_assembly_diamond + name = "diamond airlock assembly" + icon = 'icons/obj/doors/airlocks/station/diamond.dmi' + airlock_type = /obj/machinery/door/airlock/diamond + anchored = 1 + state = 1 + mineral = "diamond" + +/obj/structure/door_assembly/door_assembly_uranium + name = "uranium airlock assembly" + icon = 'icons/obj/doors/airlocks/station/uranium.dmi' + airlock_type = /obj/machinery/door/airlock/uranium + anchored = 1 + state = 1 + mineral = "uranium" + +/obj/structure/door_assembly/door_assembly_plasma + name = "plasma airlock assembly" + icon = 'icons/obj/doors/airlocks/station/plasma.dmi' + airlock_type = /obj/machinery/door/airlock/plasma + anchored = 1 + state = 1 + mineral = "plasma" + +/obj/structure/door_assembly/door_assembly_clown + name = "bananium airlock assembly" + desc = "Honk" + icon = 'icons/obj/doors/airlocks/station/bananium.dmi' + airlock_type = /obj/machinery/door/airlock/clown + anchored = 1 + state = 1 + mineral = "bananium" + +/obj/structure/door_assembly/door_assembly_sandstone + name = "sandstone airlock assembly" + icon = 'icons/obj/doors/airlocks/station/sandstone.dmi' + airlock_type = /obj/machinery/door/airlock/sandstone + anchored = 1 + state = 1 + mineral = "sandstone" + +/obj/structure/door_assembly/door_assembly_highsecurity // Borrowing this until WJohnston makes sprites for the assembly + name = "high security airlock assembly" + icon = 'icons/obj/doors/airlocks/highsec/highsec.dmi' + overlays_file = 'icons/obj/doors/airlocks/highsec/overlays.dmi' + typetext = "highsecurity" + icontext = "highsec" + airlock_type = /obj/machinery/door/airlock/highsecurity + anchored = 1 + state = 1 + +/obj/structure/door_assembly/door_assembly_vault + name = "vault door assembly" + icon = 'icons/obj/doors/airlocks/vault/vault.dmi' + overlays_file = 'icons/obj/doors/airlocks/vault/overlays.dmi' + typetext = "vault" + icontext = "vault" + airlock_type = /obj/machinery/door/airlock/vault + anchored = 1 + state = 1 + +/obj/structure/door_assembly/door_assembly_shuttle + name = "shuttle airlock assembly" + icon = 'icons/obj/doors/airlocks/shuttle/shuttle.dmi' + overlays_file = 'icons/obj/doors/airlocks/shuttle/overlays.dmi' + typetext = "shuttle" + icontext = "shuttle" + airlock_type = /obj/machinery/door/airlock/shuttle + anchored = 1 + state = 1 + +/obj/structure/door_assembly/door_assembly_wood + name = "wooden airlock assembly" + icon = 'icons/obj/doors/airlocks/station/wood.dmi' + airlock_type = /obj/machinery/door/airlock/wood + anchored = 1 + state = 1 + mineral = "wood" + +/obj/structure/door_assembly/door_assembly_viro + name = "virology airlock assembly" + icon = 'icons/obj/doors/airlocks/station/virology.dmi' + typetext = "virology" + icontext = "viro" + glass_type = /obj/machinery/door/airlock/glass_virology + airlock_type = /obj/machinery/door/airlock/virology + anchored = 1 + state = 1 + +/obj/structure/door_assembly/door_assembly_viro/glass + mineral = "glass" + material = "glass" + +/obj/structure/door_assembly/door_assembly_centcom + typetext = "centcom" + icon = 'icons/obj/doors/airlocks/centcom/centcom.dmi' + overlays_file = 'icons/obj/doors/airlocks/centcom/overlays.dmi' + icontext = "ele" + airlock_type = /obj/machinery/door/airlock/centcom + anchored = 1 + state = 1 + +/obj/structure/door_assembly/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/weapon/pen)) + var/t = stripped_input(user, "Enter the name for the door.", src.name, src.created_name,MAX_NAME_LEN) + if(!t) + return + if(!in_range(src, usr) && src.loc != usr) + return + created_name = t + return + + else if(istype(W, /obj/item/weapon/airlock_painter)) // |- Ricotez + //INFORMATION ABOUT ADDING A NEW AIRLOCK TO THE PAINT LIST: + //If your airlock has a regular version, add it to the list with regular versions. + //If your airlock has a glass version, add it to the list with glass versions. + //Don't forget to also set has_solid and has_glass to the proper value. + //Do NOT add your airlock to a list if it does not have a version for that list, + // or you will get broken icons. + var/obj/item/weapon/airlock_painter/WT = W + if(WT.can_use(user)) + var/icontype + var/optionlist + if(mineral && mineral == "glass") + //These airlocks have a glass version. + optionlist = list("Public", "Public2", "Engineering", "Atmospherics", "Security", "Command", "Medical", "Research", "Science", "Mining") + else + //These airlocks have a regular version. + optionlist = list("Public", "Engineering", "Atmospherics", "Security", "Command", "Medical", "Research", "Science", "Mining", "Maintenance", "External", "High Security") + + + icontype = input(user, "Please select a paintjob for this airlock.") in optionlist + if((!in_range(src, usr) && src.loc != usr) || !WT.use(user)) + return + var/has_solid = 0 + var/has_glass = 0 + switch(icontype) + if("Public") + icon = 'icons/obj/doors/airlocks/station/public.dmi' + overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' + typetext = "" + icontext = "" + has_solid = 1 + has_glass = 1 + if("Public2") + icon = 'icons/obj/doors/airlocks/station2/glass.dmi' + overlays_file = 'icons/obj/doors/airlocks/station2/overlays.dmi' + typetext = "" + icontext = "" + has_solid = 1 + has_glass = 1 + if("Engineering") + icon = 'icons/obj/doors/airlocks/station/engineering.dmi' + overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' + typetext = "engineering" + icontext = "eng" + has_solid = 1 + has_glass = 1 + if("Atmospherics") + icon = 'icons/obj/doors/airlocks/station/atmos.dmi' + overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' + typetext = "atmos" + icontext = "atmo" + has_solid = 1 + has_glass = 1 + if("Security") + icon = 'icons/obj/doors/airlocks/station/security.dmi' + overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' + typetext = "security" + icontext = "sec" + has_solid = 1 + has_glass = 1 + if("Command") + icon = 'icons/obj/doors/airlocks/station/command.dmi' + overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' + typetext = "command" + icontext = "com" + has_solid = 1 + has_glass = 1 + if("Medical") + icon = 'icons/obj/doors/airlocks/station/medical.dmi' + overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' + typetext = "medical" + icontext = "med" + has_solid = 1 + has_glass = 1 + if("Research") + icon = 'icons/obj/doors/airlocks/station/research.dmi' + overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' + typetext = "research" + icontext = "res" + has_solid = 1 + has_glass = 1 + if("Science") + icon = 'icons/obj/doors/airlocks/station/science.dmi' + overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' + typetext = "research" + icontext = "res" + has_solid = 1 + has_glass = 1 + if("Mining") + icon = 'icons/obj/doors/airlocks/station/mining.dmi' + overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' + typetext = "mining" + icontext = "min" + has_solid = 1 + has_glass = 1 + if("Maintenance") + icon = 'icons/obj/doors/airlocks/station/maintenance.dmi' + overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' + typetext = "maintenance" + icontext = "mai" + has_solid = 1 + has_glass = 0 + if("External") + icon = 'icons/obj/doors/airlocks/external/external.dmi' + overlays_file = 'icons/obj/doors/airlocks/external/overlays.dmi' + typetext = "external" + icontext = "ext" + has_solid = 1 + has_glass = 0 + if("High Security") + icon = 'icons/obj/doors/airlocks/highsec/highsec.dmi' + overlays_file = 'icons/obj/doors/airlocks/highsec/overlays.dmi' + typetext = "highsecurity" + icontext = "highsec" + has_solid = 1 + has_glass = 0 + if(has_solid) + airlock_type = text2path("/obj/machinery/door/airlock/[typetext]") + else + airlock_type = /obj/machinery/door/airlock + + if(has_glass) + glass_type = text2path("/obj/machinery/door/airlock/glass_[typetext]") + else + glass_type = /obj/machinery/door/airlock/glass + + if(mineral && mineral != "glass") + mineral = null //I know this is stupid, but until we change glass to a boolean it's how this code works. + user << "You change the paintjob on the airlock assembly." + + else if(istype(W, /obj/item/weapon/weldingtool) && !anchored ) + var/obj/item/weapon/weldingtool/WT = W + if(WT.remove_fuel(0,user)) + user.visible_message("[user] disassembles the airlock assembly.", \ + "You start to disassemble the airlock assembly...") + playsound(src.loc, 'sound/items/Welder2.ogg', 50, 1) + + if(do_after(user, 40/W.toolspeed, target = src)) + if( !WT.isOn() ) + return + user << "You disassemble the airlock assembly." + new /obj/item/stack/sheet/metal(get_turf(src), 4) + if (mineral) + if (mineral == "glass") + if (heat_proof_finished) + new /obj/item/stack/sheet/rglass(get_turf(src)) + else + new /obj/item/stack/sheet/glass(get_turf(src)) + else + var/M = text2path("/obj/item/stack/sheet/mineral/[mineral]") + new M(get_turf(src)) + new M(get_turf(src)) + qdel(src) + else + return + + else if(istype(W, /obj/item/weapon/wrench) && !anchored ) + var/door_check = 1 + for(var/obj/machinery/door/D in loc) + if(!D.sub_door) + door_check = 0 + break + + if(door_check) + playsound(src.loc, 'sound/items/Ratchet.ogg', 100, 1) + user.visible_message("[user] secures the airlock assembly to the floor.", \ + "You start to secure the airlock assembly to the floor...", \ + "You hear wrenching.") + + if(do_after(user, 40/W.toolspeed, target = src)) + if( src.anchored ) + return + user << "You secure the airlock assembly." + src.name = "secured airlock assembly" + src.anchored = 1 + else + user << "There is another door here!" + + else if(istype(W, /obj/item/weapon/wrench) && anchored ) + playsound(src.loc, 'sound/items/Ratchet.ogg', 100, 1) + user.visible_message("[user] unsecures the airlock assembly from the floor.", \ + "You start to unsecure the airlock assembly from the floor...", \ + "You hear wrenching.") + if(do_after(user, 40/W.toolspeed, target = src)) + if( !src.anchored ) + return + user << "You unsecure the airlock assembly." + src.name = "airlock assembly" + src.anchored = 0 + + else if(istype(W, /obj/item/stack/cable_coil) && state == 0 && anchored ) + var/obj/item/stack/cable_coil/C = W + if (C.get_amount() < 1) + user << "You need one length of cable to wire the airlock assembly!" + return + user.visible_message("[user] wires the airlock assembly.", \ + "You start to wire the airlock assembly...") + if(do_after(user, 40, target = src)) + if(C.get_amount() < 1 || state != 0) return + C.use(1) + src.state = 1 + user << "You wire the airlock assembly." + src.name = "wired airlock assembly" + + else if(istype(W, /obj/item/weapon/wirecutters) && state == 1 ) + playsound(src.loc, 'sound/items/Wirecutter.ogg', 100, 1) + user.visible_message("[user] cuts the wires from the airlock assembly.", \ + "You start to cut the wires from the airlock assembly...") + + if(do_after(user, 40/W.toolspeed, target = src)) + if( src.state != 1 ) + return + user << "You cut the wires from the airlock assembly." + new/obj/item/stack/cable_coil(get_turf(user), 1) + src.state = 0 + src.name = "secured airlock assembly" + + else if(istype(W, /obj/item/weapon/electronics/airlock) && state == 1 ) + playsound(src.loc, 'sound/items/Screwdriver.ogg', 100, 1) + user.visible_message("[user] installs the electronics into the airlock assembly.", \ + "You start to install electronics into the airlock assembly...") + if(do_after(user, 40, target = src)) + if( src.state != 1 ) + return + if(!user.drop_item()) + return + + W.loc = src + user << "You install the airlock electronics." + src.state = 2 + src.name = "near finished airlock assembly" + src.electronics = W + + + else if(istype(W, /obj/item/weapon/crowbar) && state == 2 ) + 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/W.toolspeed, target = src)) + if( src.state != 2 ) + return + user << "You remove the airlock electronics." + src.state = 1 + src.name = "wired airlock assembly" + var/obj/item/weapon/electronics/airlock/ae + if (!electronics) + ae = new/obj/item/weapon/electronics/airlock( src.loc ) + else + ae = electronics + electronics = null + ae.loc = src.loc + else if(istype(W, /obj/item/stack/sheet) && !mineral) + var/obj/item/stack/sheet/G = W + if(G) + if(G.get_amount() >= 1) + if(istype(G, /obj/item/stack/sheet/rglass) || istype(G, /obj/item/stack/sheet/glass)) + playsound(src.loc, 'sound/items/Crowbar.ogg', 100, 1) + user.visible_message("[user] adds [G.name] to the airlock assembly.", \ + "You start to install [G.name] into the airlock assembly...") + if(do_after(user, 40, target = src)) + if(G.get_amount() < 1 || mineral) return + if (G.type == /obj/item/stack/sheet/rglass) + user << "You install reinforced glass windows into the airlock assembly." + heat_proof_finished = 1 //reinforced glass makes the airlock heat-proof + name = "near finished heat-proofed window airlock assembly" + else + user << "You install regular glass windows into the airlock assembly." + name = "near finished window airlock assembly" + G.use(1) + mineral = "glass" + material = "glass" + //This list contains the airlock paintjobs that have a glass version: + if(icontext in list("eng", "atmo", "sec", "com", "med", "res", "min")) + src.airlock_type = text2path("/obj/machinery/door/airlock/[typetext]") + src.glass_type = text2path("/obj/machinery/door/airlock/glass_[typetext]") + else + //This airlock is default or does not have a glass version, so we revert to the default glass airlock. |- Ricotez + airlock_type = /obj/machinery/door/airlock + glass_type = /obj/machinery/door/airlock/glass + typetext = "" + icontext = "" + else if(istype(G, /obj/item/stack/sheet/mineral)) + var/M = G.sheettype + if(G.get_amount() >= 2) + playsound(src.loc, 'sound/items/Crowbar.ogg', 100, 1) + user.visible_message("[user] adds [G.name] to the airlock assembly.", \ + "You start to install [G.name] into the airlock assembly...") + if(do_after(user, 40, target = src)) + if(G.get_amount() < 2 || mineral) return + user << "You install [M] plating into the airlock assembly." + G.use(2) + mineral = "[M]" + name = "near finished [M] airlock assembly" + airlock_type = text2path ("/obj/machinery/door/airlock/[M]") + glass_type = /obj/machinery/door/airlock/glass + + else if(istype(W, /obj/item/weapon/screwdriver) && state == 2 ) + playsound(src.loc, 'sound/items/Screwdriver.ogg', 100, 1) + user.visible_message("[user] finishes the airlock.", \ + "You start finishing the airlock...") + + if(do_after(user, 40/W.toolspeed, target = src)) + if(src.loc && state == 2) + user << "You finish the airlock." + var/obj/machinery/door/airlock/door + if(mineral == "glass") + door = new src.glass_type( src.loc ) + else + door = new src.airlock_type( src.loc ) + //door.req_access = src.req_access + door.electronics = src.electronics + door.heat_proof = src.heat_proof_finished + if(src.electronics.one_access) + door.req_one_access = src.electronics.accesses + else + door.req_access = src.electronics.accesses + if(created_name) + door.name = created_name + src.electronics.loc = door + qdel(src) + else + ..() + update_icon() + +/obj/structure/door_assembly/update_icon() + overlays.Cut() + if(!material) + overlays += get_airlock_overlay("fill_construction", icon) + else + overlays += get_airlock_overlay("[material]_construction", overlays_file) + overlays += get_airlock_overlay("panel_c[state+1]", overlays_file) diff --git a/code/game/objects/structures/janicart.dm b/code/game/objects/structures/janicart.dm index 12545718b8d..d9333d6e123 100644 --- a/code/game/objects/structures/janicart.dm +++ b/code/game/objects/structures/janicart.dm @@ -1,373 +1,373 @@ -/obj/structure/janitorialcart - name = "janitorial cart" - desc = "This is the alpha and omega of sanitation." - icon = 'icons/obj/janitor.dmi' - icon_state = "cart" - anchored = 0 - density = 1 - flags = OPENCONTAINER - //copypaste sorry - var/amount_per_transfer_from_this = 5 //shit I dunno, adding this so syringes stop runtime erroring. --NeoFite - var/obj/item/weapon/storage/bag/trash/mybag = null - var/obj/item/weapon/mop/mymop = null - var/obj/item/weapon/reagent_containers/spray/cleaner/myspray = null - var/obj/item/device/lightreplacer/myreplacer = null - var/signs = 0 - var/const/max_signs = 4 - - -/obj/structure/janitorialcart/New() - create_reagents(100) - - -/obj/structure/janitorialcart/proc/wet_mop(obj/item/weapon/mop, mob/user) - if(reagents.total_volume < 1) - user << "[src] is out of water!" - else - reagents.trans_to(mop, 5) // - user << "You wet [mop] in [src]." - playsound(loc, 'sound/effects/slosh.ogg', 25, 1) - -/obj/structure/janitorialcart/proc/put_in_cart(obj/item/I, mob/user) - if(!user.drop_item()) - return - I.loc = src - updateUsrDialog() - user << "You put [I] into [src]." - return - - -/obj/structure/janitorialcart/attackby(obj/item/I, mob/user, params) - var/fail_msg = "There is already one of those in [src]!" - - if(istype(I, /obj/item/weapon/mop)) - var/obj/item/weapon/mop/m=I - if(m.reagents.total_volume < m.reagents.maximum_volume) - wet_mop(m, user) - return - if(!mymop) - m.janicart_insert(user, src) - else - user << fail_msg - - else if(istype(I, /obj/item/weapon/storage/bag/trash)) - if(!mybag) - var/obj/item/weapon/storage/bag/trash/t=I - t.janicart_insert(user, src) - else - user << fail_msg - else if(istype(I, /obj/item/weapon/reagent_containers/spray/cleaner)) - if(!myspray) - put_in_cart(I, user) - myspray=I - update_icon() - else - user << fail_msg - else if(istype(I, /obj/item/device/lightreplacer)) - if(!myreplacer) - var/obj/item/device/lightreplacer/l=I - l.janicart_insert(user,src) - else - user << fail_msg - else if(istype(I, /obj/item/weapon/caution)) - if(signs < max_signs) - put_in_cart(I, user) - signs++ - update_icon() - else - user << "[src] can't hold any more signs!" - else if(mybag) - mybag.attackby(I, user) - else if(istype(I, /obj/item/weapon/crowbar)) - user.visible_message("[user] begins to empty the contents of [src].", "You begin to empty the contents of [src]...") - if(do_after(user, 30/I.toolspeed, target = src)) - usr << "You empty the contents of [src]'s bucket onto the floor." - reagents.reaction(src.loc) - src.reagents.clear_reagents() - -/obj/structure/janitorialcart/attack_hand(mob/user) - user.set_machine(src) - var/dat - if(mybag) - dat += "[mybag.name]
" - if(mymop) - dat += "[mymop.name]
" - if(myspray) - dat += "[myspray.name]
" - if(myreplacer) - dat += "[myreplacer.name]
" - if(signs) - dat += "[signs] sign\s
" - var/datum/browser/popup = new(user, "janicart", name, 240, 160) - popup.set_content(dat) - popup.open() - - -/obj/structure/janitorialcart/Topic(href, href_list) - if(!in_range(src, usr)) - return - if(!isliving(usr)) - return - var/mob/living/user = usr - if(href_list["garbage"]) - if(mybag) - user.put_in_hands(mybag) - user << "You take [mybag] from [src]." - mybag = null - if(href_list["mop"]) - if(mymop) - user.put_in_hands(mymop) - user << "You take [mymop] from [src]." - mymop = null - if(href_list["spray"]) - if(myspray) - user.put_in_hands(myspray) - user << "You take [myspray] from [src]." - myspray = null - if(href_list["replacer"]) - if(myreplacer) - user.put_in_hands(myreplacer) - user << "You take [myreplacer] from [src]." - myreplacer = null - if(href_list["sign"]) - if(signs) - var/obj/item/weapon/caution/Sign = locate() in src - if(Sign) - user.put_in_hands(Sign) - user << "You take \a [Sign] from [src]." - signs-- - else - WARNING("Signs ([signs]) didn't match contents") - signs = 0 - - update_icon() - updateUsrDialog() - - -/obj/structure/janitorialcart/update_icon() - overlays.Cut() - if(mybag) - overlays += "cart_garbage" - if(mymop) - overlays += "cart_mop" - if(myspray) - overlays += "cart_spray" - if(myreplacer) - overlays += "cart_replacer" - if(signs) - overlays += "cart_sign[signs]" - - -//old style PIMP-CART -/obj/structure/bed/chair/janicart - name = "janicart" - desc = "A brave janitor cyborg gave its life to produce such an amazing combination of speed and utility." - icon = 'icons/obj/vehicles.dmi' - icon_state = "pussywagon" - anchored = 0 - density = 1 - var/obj/item/weapon/storage/bag/trash/mybag = null - var/callme = "pimpin' ride" //how do people refer to it? - var/move_delay = 0 - var/floorbuffer = 0 - var/keytype = /obj/item/key/janitor - -/obj/structure/bed/chair/janicart/New() - handle_rotation() - -/obj/structure/bed/chair/janicart/Move(a, b, flag) - ..() - if(floorbuffer) - var/turf/tile = loc - if(isturf(tile)) - tile.clean_blood() - for(var/A in tile) - if(istype(A, /obj/effect)) - if(is_cleanable(A)) - qdel(A) - -/obj/structure/bed/chair/janicart/examine(mob/user) - ..() - if(floorbuffer) - user << "It has been upgraded with a floor buffer." - - -/obj/structure/bed/chair/janicart/attackby(obj/item/I, mob/user, params) - if(istype(I, keytype)) - user << "Hold [I] in one of your hands while you drive this [callme]." - else if(istype(I, /obj/item/weapon/storage/bag/trash)) - if(keytype == /obj/item/key/janitor) - if(!user.drop_item()) - return - user << "You hook the trashbag onto the [callme]." - I.loc = src - mybag = I - else if(istype(I, /obj/item/janiupgrade)) - if(keytype == /obj/item/key/janitor) - floorbuffer = 1 - qdel(I) - user << "You upgrade the [callme] with the floor buffer." - update_icon() - -/obj/structure/bed/chair/janicart/update_icon() - overlays.Cut() - if(mybag) - overlays += "cart_garbage" - if(floorbuffer) - overlays += "cart_buffer" - -/obj/structure/bed/chair/janicart/attack_hand(mob/user) - if(..()) - return 1 - else if(mybag) - mybag.loc = get_turf(user) - user.put_in_hands(mybag) - mybag = null - update_icon() - - -/obj/structure/bed/chair/janicart/relaymove(mob/user, direction) - if(user.stat || user.stunned || user.weakened || user.paralysis) - unbuckle_mob() - if(istype(user.l_hand, keytype) || istype(user.r_hand, keytype)) - if(!Process_Spacemove(direction) || !has_gravity(src.loc) || move_delay || !isturf(loc)) - return - step(src, direction) - update_mob() - handle_rotation() - move_delay = 1 - spawn(2) - move_delay = 0 - else - user << "You'll need the keys in one of your hands to drive this [callme]." - -/obj/structure/bed/chair/janicart/user_buckle_mob(mob/living/M, mob/user) - if(user.incapacitated()) //user can't move the mob on the janicart's turf if incapacitated - return - for(var/atom/movable/A in get_turf(src)) //we check for obstacles on the turf. - if(A.density) - if(A != src && A != M) - return - M.loc = loc //we move the mob on the janicart's turf before checking if we can buckle. - ..() - update_mob() - -/obj/structure/bed/chair/janicart/unbuckle_mob(force = 0) - if(buckled_mob) - buckled_mob.pixel_x = 0 - buckled_mob.pixel_y = 0 - . = ..() - -/obj/structure/bed/chair/janicart/handle_rotation() - if((dir == SOUTH) || (dir == WEST) || (dir == EAST)) - layer = FLY_LAYER - else - layer = OBJ_LAYER - - if(buckled_mob) - if(buckled_mob.loc != loc) - buckled_mob.buckled = null //Temporary, so Move() succeeds. - buckled_mob.buckled = src //Restoring - - update_mob() - -/obj/structure/bed/chair/janicart/Bump(atom/movable/M) - . = ..() - if(istype(M, /obj/machinery/door) && buckled_mob) - M.Bumped(buckled_mob) - -/obj/structure/bed/chair/janicart/proc/update_mob() - if(buckled_mob) - buckled_mob.dir = dir - switch(dir) - if(SOUTH) - buckled_mob.pixel_x = 0 - buckled_mob.pixel_y = 7 - if(WEST) - buckled_mob.pixel_x = 12 - buckled_mob.pixel_y = 7 - if(NORTH) - buckled_mob.pixel_x = 0 - buckled_mob.pixel_y = 4 - if(EAST) - buckled_mob.pixel_x = -12 - buckled_mob.pixel_y = 7 - -//keys+attachables// - -/obj/item/key - name = "key" - desc = "A small grey key." - icon = 'icons/obj/vehicles.dmi' - icon_state = "key" - w_class = 1 - -/obj/item/key/janitor - desc = "A keyring with a small steel key, and a pink fob reading \"Pussy Wagon\"." - icon_state = "keyjanitor" - -/obj/item/key/security - desc = "A keyring with a small steel key, and a rubber stun baton accessory." - icon_state = "keysec" - -/obj/item/janiupgrade - name = "floor buffer upgrade" - desc = "An upgrade for mobile janicarts." - icon = 'icons/obj/vehicles.dmi' - icon_state = "upgrade" - -//secway// - -/obj/structure/bed/chair/janicart/secway - name = "secway" - desc = "A brave security cyborg gave its life to help you look like a complete tool." - icon = 'icons/obj/vehicles.dmi' - icon_state = "secway" - callme = "secway" - keytype = /obj/item/key/security - -/obj/structure/bed/chair/janicart/secway/update_mob() - if(buckled_mob) - buckled_mob.dir = dir - buckled_mob.pixel_y = 4 - -//atv// - -/obj/structure/bed/chair/janicart/atv - name = "atv" - desc = "An all-terrain vehicle built for traversing rough terrain with ease. One of the few old-earth technologies that are still relevant on most planet-bound outposts." - icon = 'icons/obj/vehicles.dmi' - icon_state = "atv" - callme = "All-terrain vehicle" - keytype = /obj/item/key - var/image/atvcover = null - -/obj/structure/bed/chair/janicart/atv/New() - atvcover = image("icons/obj/vehicles.dmi", "atvcover") - atvcover.layer = MOB_LAYER + 0.1 - return ..() - -obj/structure/bed/chair/janicart/atv/post_buckle_mob(mob/living/M) - if(buckled_mob) - overlays += atvcover - else - overlays -= atvcover - -/obj/structure/bed/chair/janicart/atv/update_mob() - if(buckled_mob) - buckled_mob.dir = dir - buckled_mob.pixel_x = 0 - buckled_mob.pixel_y = 4 - -/obj/structure/bed/chair/janicart/atv/handle_rotation() - if(dir == SOUTH) - layer = FLY_LAYER - else - layer = OBJ_LAYER - - if(buckled_mob) - if(buckled_mob.loc != loc) - buckled_mob.buckled = null //Temporary, so Move() succeeds. - buckled_mob.buckled = src //Restoring - - update_mob() +/obj/structure/janitorialcart + name = "janitorial cart" + desc = "This is the alpha and omega of sanitation." + icon = 'icons/obj/janitor.dmi' + icon_state = "cart" + anchored = 0 + density = 1 + flags = OPENCONTAINER + //copypaste sorry + var/amount_per_transfer_from_this = 5 //shit I dunno, adding this so syringes stop runtime erroring. --NeoFite + var/obj/item/weapon/storage/bag/trash/mybag = null + var/obj/item/weapon/mop/mymop = null + var/obj/item/weapon/reagent_containers/spray/cleaner/myspray = null + var/obj/item/device/lightreplacer/myreplacer = null + var/signs = 0 + var/const/max_signs = 4 + + +/obj/structure/janitorialcart/New() + create_reagents(100) + + +/obj/structure/janitorialcart/proc/wet_mop(obj/item/weapon/mop, mob/user) + if(reagents.total_volume < 1) + user << "[src] is out of water!" + else + reagents.trans_to(mop, 5) // + user << "You wet [mop] in [src]." + playsound(loc, 'sound/effects/slosh.ogg', 25, 1) + +/obj/structure/janitorialcart/proc/put_in_cart(obj/item/I, mob/user) + if(!user.drop_item()) + return + I.loc = src + updateUsrDialog() + user << "You put [I] into [src]." + return + + +/obj/structure/janitorialcart/attackby(obj/item/I, mob/user, params) + var/fail_msg = "There is already one of those in [src]!" + + if(istype(I, /obj/item/weapon/mop)) + var/obj/item/weapon/mop/m=I + if(m.reagents.total_volume < m.reagents.maximum_volume) + wet_mop(m, user) + return + if(!mymop) + m.janicart_insert(user, src) + else + user << fail_msg + + else if(istype(I, /obj/item/weapon/storage/bag/trash)) + if(!mybag) + var/obj/item/weapon/storage/bag/trash/t=I + t.janicart_insert(user, src) + else + user << fail_msg + else if(istype(I, /obj/item/weapon/reagent_containers/spray/cleaner)) + if(!myspray) + put_in_cart(I, user) + myspray=I + update_icon() + else + user << fail_msg + else if(istype(I, /obj/item/device/lightreplacer)) + if(!myreplacer) + var/obj/item/device/lightreplacer/l=I + l.janicart_insert(user,src) + else + user << fail_msg + else if(istype(I, /obj/item/weapon/caution)) + if(signs < max_signs) + put_in_cart(I, user) + signs++ + update_icon() + else + user << "[src] can't hold any more signs!" + else if(mybag) + mybag.attackby(I, user) + else if(istype(I, /obj/item/weapon/crowbar)) + user.visible_message("[user] begins to empty the contents of [src].", "You begin to empty the contents of [src]...") + if(do_after(user, 30/I.toolspeed, target = src)) + usr << "You empty the contents of [src]'s bucket onto the floor." + reagents.reaction(src.loc) + src.reagents.clear_reagents() + +/obj/structure/janitorialcart/attack_hand(mob/user) + user.set_machine(src) + var/dat + if(mybag) + dat += "[mybag.name]
" + if(mymop) + dat += "[mymop.name]
" + if(myspray) + dat += "[myspray.name]
" + if(myreplacer) + dat += "[myreplacer.name]
" + if(signs) + dat += "[signs] sign\s
" + var/datum/browser/popup = new(user, "janicart", name, 240, 160) + popup.set_content(dat) + popup.open() + + +/obj/structure/janitorialcart/Topic(href, href_list) + if(!in_range(src, usr)) + return + if(!isliving(usr)) + return + var/mob/living/user = usr + if(href_list["garbage"]) + if(mybag) + user.put_in_hands(mybag) + user << "You take [mybag] from [src]." + mybag = null + if(href_list["mop"]) + if(mymop) + user.put_in_hands(mymop) + user << "You take [mymop] from [src]." + mymop = null + if(href_list["spray"]) + if(myspray) + user.put_in_hands(myspray) + user << "You take [myspray] from [src]." + myspray = null + if(href_list["replacer"]) + if(myreplacer) + user.put_in_hands(myreplacer) + user << "You take [myreplacer] from [src]." + myreplacer = null + if(href_list["sign"]) + if(signs) + var/obj/item/weapon/caution/Sign = locate() in src + if(Sign) + user.put_in_hands(Sign) + user << "You take \a [Sign] from [src]." + signs-- + else + WARNING("Signs ([signs]) didn't match contents") + signs = 0 + + update_icon() + updateUsrDialog() + + +/obj/structure/janitorialcart/update_icon() + overlays.Cut() + if(mybag) + overlays += "cart_garbage" + if(mymop) + overlays += "cart_mop" + if(myspray) + overlays += "cart_spray" + if(myreplacer) + overlays += "cart_replacer" + if(signs) + overlays += "cart_sign[signs]" + + +//old style PIMP-CART +/obj/structure/bed/chair/janicart + name = "janicart" + desc = "A brave janitor cyborg gave its life to produce such an amazing combination of speed and utility." + icon = 'icons/obj/vehicles.dmi' + icon_state = "pussywagon" + anchored = 0 + density = 1 + var/obj/item/weapon/storage/bag/trash/mybag = null + var/callme = "pimpin' ride" //how do people refer to it? + var/move_delay = 0 + var/floorbuffer = 0 + var/keytype = /obj/item/key/janitor + +/obj/structure/bed/chair/janicart/New() + handle_rotation() + +/obj/structure/bed/chair/janicart/Move(a, b, flag) + ..() + if(floorbuffer) + var/turf/tile = loc + if(isturf(tile)) + tile.clean_blood() + for(var/A in tile) + if(istype(A, /obj/effect)) + if(is_cleanable(A)) + qdel(A) + +/obj/structure/bed/chair/janicart/examine(mob/user) + ..() + if(floorbuffer) + user << "It has been upgraded with a floor buffer." + + +/obj/structure/bed/chair/janicart/attackby(obj/item/I, mob/user, params) + if(istype(I, keytype)) + user << "Hold [I] in one of your hands while you drive this [callme]." + else if(istype(I, /obj/item/weapon/storage/bag/trash)) + if(keytype == /obj/item/key/janitor) + if(!user.drop_item()) + return + user << "You hook the trashbag onto the [callme]." + I.loc = src + mybag = I + else if(istype(I, /obj/item/janiupgrade)) + if(keytype == /obj/item/key/janitor) + floorbuffer = 1 + qdel(I) + user << "You upgrade the [callme] with the floor buffer." + update_icon() + +/obj/structure/bed/chair/janicart/update_icon() + overlays.Cut() + if(mybag) + overlays += "cart_garbage" + if(floorbuffer) + overlays += "cart_buffer" + +/obj/structure/bed/chair/janicart/attack_hand(mob/user) + if(..()) + return 1 + else if(mybag) + mybag.loc = get_turf(user) + user.put_in_hands(mybag) + mybag = null + update_icon() + + +/obj/structure/bed/chair/janicart/relaymove(mob/user, direction) + if(user.stat || user.stunned || user.weakened || user.paralysis) + unbuckle_mob() + if(istype(user.l_hand, keytype) || istype(user.r_hand, keytype)) + if(!Process_Spacemove(direction) || !has_gravity(src.loc) || move_delay || !isturf(loc)) + return + step(src, direction) + update_mob() + handle_rotation() + move_delay = 1 + spawn(2) + move_delay = 0 + else + user << "You'll need the keys in one of your hands to drive this [callme]." + +/obj/structure/bed/chair/janicart/user_buckle_mob(mob/living/M, mob/user) + if(user.incapacitated()) //user can't move the mob on the janicart's turf if incapacitated + return + for(var/atom/movable/A in get_turf(src)) //we check for obstacles on the turf. + if(A.density) + if(A != src && A != M) + return + M.loc = loc //we move the mob on the janicart's turf before checking if we can buckle. + ..() + update_mob() + +/obj/structure/bed/chair/janicart/unbuckle_mob(force = 0) + if(buckled_mob) + buckled_mob.pixel_x = 0 + buckled_mob.pixel_y = 0 + . = ..() + +/obj/structure/bed/chair/janicart/handle_rotation() + if((dir == SOUTH) || (dir == WEST) || (dir == EAST)) + layer = FLY_LAYER + else + layer = OBJ_LAYER + + if(buckled_mob) + if(buckled_mob.loc != loc) + buckled_mob.buckled = null //Temporary, so Move() succeeds. + buckled_mob.buckled = src //Restoring + + update_mob() + +/obj/structure/bed/chair/janicart/Bump(atom/movable/M) + . = ..() + if(istype(M, /obj/machinery/door) && buckled_mob) + M.Bumped(buckled_mob) + +/obj/structure/bed/chair/janicart/proc/update_mob() + if(buckled_mob) + buckled_mob.dir = dir + switch(dir) + if(SOUTH) + buckled_mob.pixel_x = 0 + buckled_mob.pixel_y = 7 + if(WEST) + buckled_mob.pixel_x = 12 + buckled_mob.pixel_y = 7 + if(NORTH) + buckled_mob.pixel_x = 0 + buckled_mob.pixel_y = 4 + if(EAST) + buckled_mob.pixel_x = -12 + buckled_mob.pixel_y = 7 + +//keys+attachables// + +/obj/item/key + name = "key" + desc = "A small grey key." + icon = 'icons/obj/vehicles.dmi' + icon_state = "key" + w_class = 1 + +/obj/item/key/janitor + desc = "A keyring with a small steel key, and a pink fob reading \"Pussy Wagon\"." + icon_state = "keyjanitor" + +/obj/item/key/security + desc = "A keyring with a small steel key, and a rubber stun baton accessory." + icon_state = "keysec" + +/obj/item/janiupgrade + name = "floor buffer upgrade" + desc = "An upgrade for mobile janicarts." + icon = 'icons/obj/vehicles.dmi' + icon_state = "upgrade" + +//secway// + +/obj/structure/bed/chair/janicart/secway + name = "secway" + desc = "A brave security cyborg gave its life to help you look like a complete tool." + icon = 'icons/obj/vehicles.dmi' + icon_state = "secway" + callme = "secway" + keytype = /obj/item/key/security + +/obj/structure/bed/chair/janicart/secway/update_mob() + if(buckled_mob) + buckled_mob.dir = dir + buckled_mob.pixel_y = 4 + +//atv// + +/obj/structure/bed/chair/janicart/atv + name = "atv" + desc = "An all-terrain vehicle built for traversing rough terrain with ease. One of the few old-earth technologies that are still relevant on most planet-bound outposts." + icon = 'icons/obj/vehicles.dmi' + icon_state = "atv" + callme = "All-terrain vehicle" + keytype = /obj/item/key + var/image/atvcover = null + +/obj/structure/bed/chair/janicart/atv/New() + atvcover = image("icons/obj/vehicles.dmi", "atvcover") + atvcover.layer = MOB_LAYER + 0.1 + return ..() + +obj/structure/bed/chair/janicart/atv/post_buckle_mob(mob/living/M) + if(buckled_mob) + overlays += atvcover + else + overlays -= atvcover + +/obj/structure/bed/chair/janicart/atv/update_mob() + if(buckled_mob) + buckled_mob.dir = dir + buckled_mob.pixel_x = 0 + buckled_mob.pixel_y = 4 + +/obj/structure/bed/chair/janicart/atv/handle_rotation() + if(dir == SOUTH) + layer = FLY_LAYER + else + layer = OBJ_LAYER + + if(buckled_mob) + if(buckled_mob.loc != loc) + buckled_mob.buckled = null //Temporary, so Move() succeeds. + buckled_mob.buckled = src //Restoring + + update_mob() diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index 96603ab4f51..0e995f815f0 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -1,504 +1,504 @@ -/obj/structure/window - name = "window" - desc = "A window." - icon_state = "window" - density = 1 - layer = 3.2//Just above doors - pressure_resistance = 4*ONE_ATMOSPHERE - anchored = 1 //initially is 0 for tile smoothing - flags = ON_BORDER - var/maxhealth = 25 - var/health = 0 - var/ini_dir = null - var/state = 0 - var/reinf = 0 - var/disassembled = 0 - var/wtype = "glass" - var/fulltile = 0 - var/list/storeditems = list() -// var/silicate = 0 // number of units of silicate -// var/icon/silicateIcon = null // the silicated icon - var/image/crack_overlay - can_be_unanchored = 1 - -/obj/structure/window/examine(mob/user) - ..() - user << "Alt-click to rotate it clockwise." - -/obj/structure/window/New(Loc,re=0) - ..() - health = maxhealth - if(re) - reinf = re - if(reinf) - state = 2*anchored - - spawn(5) // The NODECONSTRUCT flag gets added immediately by the holodeck (but not immediately enough) - if(!(flags & NODECONSTRUCT)) - storeditems.Add(new/obj/item/weapon/shard(src)) - if(fulltile) - storeditems.Add(new/obj/item/weapon/shard(src)) - if(reinf) - var/obj/item/stack/rods/R = new/obj/item/stack/rods(src) - storeditems.Add(R) - if(fulltile) - R.add(1) - - ini_dir = dir - air_update_turf(1) - - return - -/obj/structure/window/bullet_act(obj/item/projectile/Proj) - if((Proj.damage_type == BRUTE || Proj.damage_type == BURN)) - health -= Proj.damage - update_nearby_icons() - ..() - if(health <= 0) - spawnfragments() - return - - -/obj/structure/window/ex_act(severity, target) - switch(severity) - if(1) - qdel(src) - return - if(2) - spawnfragments() - return - if(3) - if(prob(50)) - spawnfragments() - return - - -/obj/structure/window/blob_act() - spawnfragments() - -/obj/structure/window/singularity_pull(S, current_size) - if(current_size >= STAGE_FIVE) - spawnfragments() - -/obj/structure/window/CanPass(atom/movable/mover, turf/target, height=0) - if(istype(mover) && mover.checkpass(PASSGLASS)) - return 1 - if(dir == SOUTHWEST || dir == SOUTHEAST || dir == NORTHWEST || dir == NORTHEAST) - return 0 //full tile window, you can't move into it! - if(get_dir(loc, target) == dir) - return !density - else - return 1 - - -/obj/structure/window/CheckExit(atom/movable/O as mob|obj, target) - if(istype(O) && O.checkpass(PASSGLASS)) - return 1 - if(get_dir(O.loc, target) == dir) - return 0 - return 1 - - -/obj/structure/window/hitby(AM as mob|obj) - ..() - var/tforce = 0 - if(ismob(AM)) - tforce = 40 - - else if(isobj(AM)) - var/obj/item/I = AM - tforce = I.throwforce - - if(reinf) - tforce *= 0.25 - - playsound(loc, 'sound/effects/Glasshit.ogg', 100, 1) - health = max(0, health - tforce) - if(health <= 7 && !reinf) - anchored = 0 - update_nearby_icons() - step(src, get_dir(AM, src)) - - if(health <= 0) - spawnfragments() - update_nearby_icons() - -/obj/structure/window/attack_tk(mob/user) - user.changeNext_move(CLICK_CD_MELEE) - user.visible_message("Something knocks on [src].") - add_fingerprint(user) - playsound(loc, 'sound/effects/Glassknock.ogg', 50, 1) - -/obj/structure/window/attack_hulk(mob/living/carbon/human/user) - if(!can_be_reached(user)) - return - ..(user, 1) - user.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!")) - user.visible_message("[user] smashes through [src]!") - add_fingerprint(user) - hit(50) - return 1 - -/obj/structure/window/attack_hand(mob/user) - if(!can_be_reached(user)) - return - user.changeNext_move(CLICK_CD_MELEE) - user.visible_message("[user] knocks on [src].") - add_fingerprint(user) - playsound(loc, 'sound/effects/Glassknock.ogg', 50, 1) - -/obj/structure/window/attack_paw(mob/user) - return attack_hand(user) - - -/obj/structure/window/proc/attack_generic(mob/user, damage = 0) //used by attack_alien, attack_animal, and attack_slime - if(!can_be_reached(user)) - return - user.changeNext_move(CLICK_CD_MELEE) - health -= damage - if(health <= 0) - user.visible_message("[user] smashes through [src]!") - spawnfragments() - else //for nicer text~ - user.visible_message("[user] smashes into [src]!") - playsound(loc, 'sound/effects/Glasshit.ogg', 100, 1) - - -/obj/structure/window/attack_alien(mob/living/user) - user.do_attack_animation(src) - if(islarva(user)) return - attack_generic(user, 15) - update_nearby_icons() - -/obj/structure/window/attack_animal(mob/living/user) - if(!isanimal(user)) - return - - var/mob/living/simple_animal/M = user - M.do_attack_animation(src) - if(M.melee_damage_upper <= 0 || (M.melee_damage_type != BRUTE && M.melee_damage_type != BURN)) - return - - attack_generic(M, M.melee_damage_upper) - update_nearby_icons() - -/obj/structure/window/attack_slime(mob/living/simple_animal/slime/user) - user.do_attack_animation(src) - if(!user.is_adult) - return - - attack_generic(user, rand(10, 15)) - update_nearby_icons() - -/obj/structure/window/attackby(obj/item/I, mob/living/user, params) - if(!can_be_reached(user)) - return 1 //skip the afterattack - - add_fingerprint(user) - if(istype(I, /obj/item/weapon/weldingtool) && user.a_intent == "help") - var/obj/item/weapon/weldingtool/WT = I - if(health < maxhealth) - if(WT.remove_fuel(0,user)) - user << "You begin repairing [src]..." - playsound(loc, 'sound/items/Welder.ogg', 40, 1) - if(do_after(user, 40/I.toolspeed, target = src)) - health = maxhealth - playsound(loc, 'sound/items/Welder2.ogg', 50, 1) - update_nearby_icons() - else - user << "[src] is already in good condition!" - return - - - if(!(flags&NODECONSTRUCT)) - if(istype(I, /obj/item/weapon/screwdriver)) - playsound(loc, 'sound/items/Screwdriver.ogg', 75, 1) - if(reinf && (state == 2 || state == 1)) - user << (state == 2 ? "You begin to unscrew the window from the frame..." : "You begin to screw the window to the frame...") - else if(reinf && state == 0) - user << (anchored ? "You begin to unscrew the frame from the floor..." : "You begin to screw the frame to the floor...") - else if(!reinf) - user << (anchored ? "You begin to unscrew the window from the floor..." : "You begin to screw the window to the floor...") - - if(do_after(user, 40/I.toolspeed, target = src)) - if(reinf && (state == 1 || state == 2)) - //If state was unfastened, fasten it, else do the reverse - state = (state == 1 ? 2 : 1) - user << (state == 1 ? "You unfasten the window from the frame." : "You fasten the window to the frame.") - else if(reinf && state == 0) - anchored = !anchored - update_nearby_icons() - user << (anchored ? "You fasten the frame to the floor." : "You unfasten the frame from the floor.") - else if(!reinf) - anchored = !anchored - update_nearby_icons() - user << (anchored ? "You fasten the window to the floor." : "You unfasten the window.") - return - - else if (istype(I, /obj/item/weapon/crowbar) && reinf && (state == 0 || state == 1)) - user << (state == 0 ? "You begin to lever the window into the frame..." : "You begin to lever the window out of the frame...") - playsound(loc, 'sound/items/Crowbar.ogg', 75, 1) - if(do_after(user, 40/I.toolspeed, target = src)) - //If state was out of frame, put into frame, else do the reverse - state = (state == 0 ? 1 : 0) - user << (state == 1 ? "You pry the window into the frame." : "You pry the window out of the frame.") - return - - else if(istype(I, /obj/item/weapon/wrench) && !anchored) - playsound(loc, 'sound/items/Ratchet.ogg', 75, 1) - user << " You begin to disassemble [src]..." - if(do_after(user, 40/I.toolspeed, target = src)) - if(disassembled) - return //Prevents multiple deconstruction attempts - - if(reinf) - var/obj/item/stack/sheet/rglass/RG = new (user.loc) - RG.add_fingerprint(user) - if(fulltile) //fulltiles drop two panes - RG = new (user.loc) - RG.add_fingerprint(user) - - else - var/obj/item/stack/sheet/glass/G = new (user.loc) - G.add_fingerprint(user) - if(fulltile) - G = new (user.loc) - G.add_fingerprint(user) - - playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) - disassembled = 1 - user << "You successfully disassemble [src]." - qdel(src) - return - - if(istype(I, /obj/item/weapon/rcd)) //Do not attack the window if the user is holding an RCD - return - - if(I.damtype == BRUTE || I.damtype == BURN) - user.changeNext_move(CLICK_CD_MELEE) - hit(I.force) - else - playsound(loc, 'sound/effects/Glasshit.ogg', 75, 1) - ..() - return - -/obj/structure/window/mech_melee_attack(obj/mecha/M) - if(..()) - hit(M.force, 1) - - -/obj/structure/window/proc/can_be_reached(mob/user) - if(!fulltile) - if(get_dir(user,src) & dir) - for(var/obj/O in loc) - if(!O.CanPass(user, user.loc, 1)) - return 0 - return 1 - -/obj/structure/window/proc/hit(damage, sound_effect = 1) - if(reinf) - damage *= 0.5 - health = max(0, health - damage) - update_nearby_icons() - if(sound_effect) - playsound(loc, 'sound/effects/Glasshit.ogg', 75, 1) - if(health <= 0) - spawnfragments() - return - -/obj/structure/window/proc/spawnfragments() - if(!loc) //if already qdel'd somehow, we do nothing - return - var/turf/T = loc - for(var/obj/item/I in storeditems) - I.loc = T - transfer_fingerprints_to(I) - qdel(src) - update_nearby_icons() - -/obj/structure/window/verb/rotate() - set name = "Rotate Window Counter-Clockwise" - set category = "Object" - set src in oview(1) - - if(usr.stat || !usr.canmove || usr.restrained()) - return - - if(anchored) - usr << "It is fastened to the floor therefore you can't rotate it!" - return 0 - - dir = turn(dir, 90) -// updateSilicate() - air_update_turf(1) - ini_dir = dir - add_fingerprint(usr) - return - - -/obj/structure/window/verb/revrotate() - set name = "Rotate Window Clockwise" - set category = "Object" - set src in oview(1) - - if(usr.stat || !usr.canmove || usr.restrained()) - return - - if(anchored) - usr << "It is fastened to the floor therefore you can't rotate it!" - return 0 - - dir = turn(dir, 270) -// updateSilicate() - air_update_turf(1) - ini_dir = dir - add_fingerprint(usr) - return - -/obj/structure/window/AltClick(mob/user) - ..() - if(user.incapacitated()) - user << "You can't do that right now!" - return - if(!in_range(src, user)) - return - else - revrotate() - -/* -/obj/structure/window/proc/updateSilicate() what do you call a syndicate silicon? - if(silicateIcon && silicate) - icon = initial(icon) - - var/icon/I = icon(icon,icon_state,dir) - - var/r = (silicate / 100) + 1 - var/g = (silicate / 70) + 1 - var/b = (silicate / 50) + 1 - I.SetIntensity(r,g,b) - icon = I - silicateIcon = I -*/ - -/obj/structure/window/Destroy() - density = 0 - air_update_turf(1) - if(!disassembled && !(flags&NODECONSTRUCT)) - playsound(src, "shatter", 70, 1) - update_nearby_icons() - return ..() - - -/obj/structure/window/Move() - var/turf/T = loc - ..() - dir = ini_dir - move_update_air(T) - -/obj/structure/window/CanAtmosPass(turf/T) - if(get_dir(loc, T) == dir) - return !density - if(dir == SOUTHWEST || dir == SOUTHEAST || dir == NORTHWEST || dir == NORTHEAST) - return !density - return 1 - -//This proc is used to update the icons of nearby windows. -/obj/structure/window/proc/update_nearby_icons() - update_icon() - if(smooth) - smooth_icon_neighbors(src) - -//merges adjacent full-tile windows into one (blatant ripoff from game/smoothwall.dm) -/obj/structure/window/update_icon() - //A little cludge here, since I don't know how it will work with slim windows. Most likely VERY wrong. - //this way it will only update full-tile ones - //This spawn is here so windows get properly updated when one gets deleted. - spawn(2) - if(!src) return - if(!fulltile) - return - - var/ratio = health / maxhealth - ratio = Ceiling(ratio*4) * 25 - - if(smooth) - smooth_icon(src) - - overlays -= crack_overlay - if(ratio > 75) - return - crack_overlay = image('icons/obj/structures.dmi',"damage[ratio]",-(layer+0.1)) - overlays += crack_overlay - -/obj/structure/window/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) - if(exposed_temperature > T0C + (reinf ? 1600 : 800)) - hit(round(exposed_volume / 100), 0) - ..() - -/obj/structure/window/storage_contents_dump_act(obj/item/weapon/storage/src_object, mob/user) - return 0 - -/obj/structure/window/reinforced - name = "reinforced window" - icon_state = "rwindow" - reinf = 1 - maxhealth = 50 - explosion_block = 1 - -/obj/structure/window/reinforced/tinted - name = "tinted window" - icon_state = "twindow" - opacity = 1 - -/obj/structure/window/reinforced/tinted/frosted - name = "frosted window" - icon_state = "fwindow" - - -/* Full Tile Windows (more health) */ - -/obj/structure/window/fulltile - icon = 'icons/obj/smooth_structures/window.dmi' - icon_state = "window" - dir = 5 - maxhealth = 50 - fulltile = 1 - smooth = SMOOTH_TRUE - canSmoothWith = list(/obj/structure/window/fulltile, /obj/structure/window/reinforced/fulltile, /obj/structure/window/reinforced/tinted/fulltile) - -/obj/structure/window/reinforced/fulltile - icon = 'icons/obj/smooth_structures/reinforced_window.dmi' - icon_state = "r_window" - dir = 5 - maxhealth = 100 - fulltile = 1 - smooth = SMOOTH_TRUE - canSmoothWith = list(/obj/structure/window/fulltile, /obj/structure/window/reinforced/fulltile, /obj/structure/window/reinforced/tinted/fulltile) - -/obj/structure/window/reinforced/tinted/fulltile - icon = 'icons/obj/smooth_structures/tinted_window.dmi' - icon_state = "tinted_window" - dir = 5 - fulltile = 1 - smooth = SMOOTH_TRUE - canSmoothWith = list(/obj/structure/window/fulltile, /obj/structure/window/reinforced/fulltile, /obj/structure/window/reinforced/tinted/fulltile/) - -/obj/structure/window/reinforced/fulltile/ice - icon = 'icons/obj/smooth_structures/rice_window.dmi' - icon_state = "ice_window" - maxhealth = 150 - canSmoothWith = list(/obj/structure/window/fulltile, /obj/structure/window/reinforced/fulltile, /obj/structure/window/reinforced/tinted/fulltile, /obj/structure/window/reinforced/fulltile/ice) - -/obj/structure/window/shuttle - name = "shuttle window" - desc = "A reinforced, air-locked pod window." - icon = 'icons/obj/smooth_structures/shuttle_window.dmi' - icon_state = "shuttle_window" - dir = 5 - maxhealth = 100 - wtype = "shuttle" - fulltile = 1 - reinf = 1 - smooth = SMOOTH_TRUE - canSmoothWith = null - explosion_block = 1 +/obj/structure/window + name = "window" + desc = "A window." + icon_state = "window" + density = 1 + layer = 3.2//Just above doors + pressure_resistance = 4*ONE_ATMOSPHERE + anchored = 1 //initially is 0 for tile smoothing + flags = ON_BORDER + var/maxhealth = 25 + var/health = 0 + var/ini_dir = null + var/state = 0 + var/reinf = 0 + var/disassembled = 0 + var/wtype = "glass" + var/fulltile = 0 + var/list/storeditems = list() +// var/silicate = 0 // number of units of silicate +// var/icon/silicateIcon = null // the silicated icon + var/image/crack_overlay + can_be_unanchored = 1 + +/obj/structure/window/examine(mob/user) + ..() + user << "Alt-click to rotate it clockwise." + +/obj/structure/window/New(Loc,re=0) + ..() + health = maxhealth + if(re) + reinf = re + if(reinf) + state = 2*anchored + + spawn(5) // The NODECONSTRUCT flag gets added immediately by the holodeck (but not immediately enough) + if(!(flags & NODECONSTRUCT)) + storeditems.Add(new/obj/item/weapon/shard(src)) + if(fulltile) + storeditems.Add(new/obj/item/weapon/shard(src)) + if(reinf) + var/obj/item/stack/rods/R = new/obj/item/stack/rods(src) + storeditems.Add(R) + if(fulltile) + R.add(1) + + ini_dir = dir + air_update_turf(1) + + return + +/obj/structure/window/bullet_act(obj/item/projectile/Proj) + if((Proj.damage_type == BRUTE || Proj.damage_type == BURN)) + health -= Proj.damage + update_nearby_icons() + ..() + if(health <= 0) + spawnfragments() + return + + +/obj/structure/window/ex_act(severity, target) + switch(severity) + if(1) + qdel(src) + return + if(2) + spawnfragments() + return + if(3) + if(prob(50)) + spawnfragments() + return + + +/obj/structure/window/blob_act() + spawnfragments() + +/obj/structure/window/singularity_pull(S, current_size) + if(current_size >= STAGE_FIVE) + spawnfragments() + +/obj/structure/window/CanPass(atom/movable/mover, turf/target, height=0) + if(istype(mover) && mover.checkpass(PASSGLASS)) + return 1 + if(dir == SOUTHWEST || dir == SOUTHEAST || dir == NORTHWEST || dir == NORTHEAST) + return 0 //full tile window, you can't move into it! + if(get_dir(loc, target) == dir) + return !density + else + return 1 + + +/obj/structure/window/CheckExit(atom/movable/O as mob|obj, target) + if(istype(O) && O.checkpass(PASSGLASS)) + return 1 + if(get_dir(O.loc, target) == dir) + return 0 + return 1 + + +/obj/structure/window/hitby(AM as mob|obj) + ..() + var/tforce = 0 + if(ismob(AM)) + tforce = 40 + + else if(isobj(AM)) + var/obj/item/I = AM + tforce = I.throwforce + + if(reinf) + tforce *= 0.25 + + playsound(loc, 'sound/effects/Glasshit.ogg', 100, 1) + health = max(0, health - tforce) + if(health <= 7 && !reinf) + anchored = 0 + update_nearby_icons() + step(src, get_dir(AM, src)) + + if(health <= 0) + spawnfragments() + update_nearby_icons() + +/obj/structure/window/attack_tk(mob/user) + user.changeNext_move(CLICK_CD_MELEE) + user.visible_message("Something knocks on [src].") + add_fingerprint(user) + playsound(loc, 'sound/effects/Glassknock.ogg', 50, 1) + +/obj/structure/window/attack_hulk(mob/living/carbon/human/user) + if(!can_be_reached(user)) + return + ..(user, 1) + user.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!")) + user.visible_message("[user] smashes through [src]!") + add_fingerprint(user) + hit(50) + return 1 + +/obj/structure/window/attack_hand(mob/user) + if(!can_be_reached(user)) + return + user.changeNext_move(CLICK_CD_MELEE) + user.visible_message("[user] knocks on [src].") + add_fingerprint(user) + playsound(loc, 'sound/effects/Glassknock.ogg', 50, 1) + +/obj/structure/window/attack_paw(mob/user) + return attack_hand(user) + + +/obj/structure/window/proc/attack_generic(mob/user, damage = 0) //used by attack_alien, attack_animal, and attack_slime + if(!can_be_reached(user)) + return + user.changeNext_move(CLICK_CD_MELEE) + health -= damage + if(health <= 0) + user.visible_message("[user] smashes through [src]!") + spawnfragments() + else //for nicer text~ + user.visible_message("[user] smashes into [src]!") + playsound(loc, 'sound/effects/Glasshit.ogg', 100, 1) + + +/obj/structure/window/attack_alien(mob/living/user) + user.do_attack_animation(src) + if(islarva(user)) return + attack_generic(user, 15) + update_nearby_icons() + +/obj/structure/window/attack_animal(mob/living/user) + if(!isanimal(user)) + return + + var/mob/living/simple_animal/M = user + M.do_attack_animation(src) + if(M.melee_damage_upper <= 0 || (M.melee_damage_type != BRUTE && M.melee_damage_type != BURN)) + return + + attack_generic(M, M.melee_damage_upper) + update_nearby_icons() + +/obj/structure/window/attack_slime(mob/living/simple_animal/slime/user) + user.do_attack_animation(src) + if(!user.is_adult) + return + + attack_generic(user, rand(10, 15)) + update_nearby_icons() + +/obj/structure/window/attackby(obj/item/I, mob/living/user, params) + if(!can_be_reached(user)) + return 1 //skip the afterattack + + add_fingerprint(user) + if(istype(I, /obj/item/weapon/weldingtool) && user.a_intent == "help") + var/obj/item/weapon/weldingtool/WT = I + if(health < maxhealth) + if(WT.remove_fuel(0,user)) + user << "You begin repairing [src]..." + playsound(loc, 'sound/items/Welder.ogg', 40, 1) + if(do_after(user, 40/I.toolspeed, target = src)) + health = maxhealth + playsound(loc, 'sound/items/Welder2.ogg', 50, 1) + update_nearby_icons() + else + user << "[src] is already in good condition!" + return + + + if(!(flags&NODECONSTRUCT)) + if(istype(I, /obj/item/weapon/screwdriver)) + playsound(loc, 'sound/items/Screwdriver.ogg', 75, 1) + if(reinf && (state == 2 || state == 1)) + user << (state == 2 ? "You begin to unscrew the window from the frame..." : "You begin to screw the window to the frame...") + else if(reinf && state == 0) + user << (anchored ? "You begin to unscrew the frame from the floor..." : "You begin to screw the frame to the floor...") + else if(!reinf) + user << (anchored ? "You begin to unscrew the window from the floor..." : "You begin to screw the window to the floor...") + + if(do_after(user, 40/I.toolspeed, target = src)) + if(reinf && (state == 1 || state == 2)) + //If state was unfastened, fasten it, else do the reverse + state = (state == 1 ? 2 : 1) + user << (state == 1 ? "You unfasten the window from the frame." : "You fasten the window to the frame.") + else if(reinf && state == 0) + anchored = !anchored + update_nearby_icons() + user << (anchored ? "You fasten the frame to the floor." : "You unfasten the frame from the floor.") + else if(!reinf) + anchored = !anchored + update_nearby_icons() + user << (anchored ? "You fasten the window to the floor." : "You unfasten the window.") + return + + else if (istype(I, /obj/item/weapon/crowbar) && reinf && (state == 0 || state == 1)) + user << (state == 0 ? "You begin to lever the window into the frame..." : "You begin to lever the window out of the frame...") + playsound(loc, 'sound/items/Crowbar.ogg', 75, 1) + if(do_after(user, 40/I.toolspeed, target = src)) + //If state was out of frame, put into frame, else do the reverse + state = (state == 0 ? 1 : 0) + user << (state == 1 ? "You pry the window into the frame." : "You pry the window out of the frame.") + return + + else if(istype(I, /obj/item/weapon/wrench) && !anchored) + playsound(loc, 'sound/items/Ratchet.ogg', 75, 1) + user << " You begin to disassemble [src]..." + if(do_after(user, 40/I.toolspeed, target = src)) + if(disassembled) + return //Prevents multiple deconstruction attempts + + if(reinf) + var/obj/item/stack/sheet/rglass/RG = new (user.loc) + RG.add_fingerprint(user) + if(fulltile) //fulltiles drop two panes + RG = new (user.loc) + RG.add_fingerprint(user) + + else + var/obj/item/stack/sheet/glass/G = new (user.loc) + G.add_fingerprint(user) + if(fulltile) + G = new (user.loc) + G.add_fingerprint(user) + + playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) + disassembled = 1 + user << "You successfully disassemble [src]." + qdel(src) + return + + if(istype(I, /obj/item/weapon/rcd)) //Do not attack the window if the user is holding an RCD + return + + if(I.damtype == BRUTE || I.damtype == BURN) + user.changeNext_move(CLICK_CD_MELEE) + hit(I.force) + else + playsound(loc, 'sound/effects/Glasshit.ogg', 75, 1) + ..() + return + +/obj/structure/window/mech_melee_attack(obj/mecha/M) + if(..()) + hit(M.force, 1) + + +/obj/structure/window/proc/can_be_reached(mob/user) + if(!fulltile) + if(get_dir(user,src) & dir) + for(var/obj/O in loc) + if(!O.CanPass(user, user.loc, 1)) + return 0 + return 1 + +/obj/structure/window/proc/hit(damage, sound_effect = 1) + if(reinf) + damage *= 0.5 + health = max(0, health - damage) + update_nearby_icons() + if(sound_effect) + playsound(loc, 'sound/effects/Glasshit.ogg', 75, 1) + if(health <= 0) + spawnfragments() + return + +/obj/structure/window/proc/spawnfragments() + if(!loc) //if already qdel'd somehow, we do nothing + return + var/turf/T = loc + for(var/obj/item/I in storeditems) + I.loc = T + transfer_fingerprints_to(I) + qdel(src) + update_nearby_icons() + +/obj/structure/window/verb/rotate() + set name = "Rotate Window Counter-Clockwise" + set category = "Object" + set src in oview(1) + + if(usr.stat || !usr.canmove || usr.restrained()) + return + + if(anchored) + usr << "It is fastened to the floor therefore you can't rotate it!" + return 0 + + dir = turn(dir, 90) +// updateSilicate() + air_update_turf(1) + ini_dir = dir + add_fingerprint(usr) + return + + +/obj/structure/window/verb/revrotate() + set name = "Rotate Window Clockwise" + set category = "Object" + set src in oview(1) + + if(usr.stat || !usr.canmove || usr.restrained()) + return + + if(anchored) + usr << "It is fastened to the floor therefore you can't rotate it!" + return 0 + + dir = turn(dir, 270) +// updateSilicate() + air_update_turf(1) + ini_dir = dir + add_fingerprint(usr) + return + +/obj/structure/window/AltClick(mob/user) + ..() + if(user.incapacitated()) + user << "You can't do that right now!" + return + if(!in_range(src, user)) + return + else + revrotate() + +/* +/obj/structure/window/proc/updateSilicate() what do you call a syndicate silicon? + if(silicateIcon && silicate) + icon = initial(icon) + + var/icon/I = icon(icon,icon_state,dir) + + var/r = (silicate / 100) + 1 + var/g = (silicate / 70) + 1 + var/b = (silicate / 50) + 1 + I.SetIntensity(r,g,b) + icon = I + silicateIcon = I +*/ + +/obj/structure/window/Destroy() + density = 0 + air_update_turf(1) + if(!disassembled && !(flags&NODECONSTRUCT)) + playsound(src, "shatter", 70, 1) + update_nearby_icons() + return ..() + + +/obj/structure/window/Move() + var/turf/T = loc + ..() + dir = ini_dir + move_update_air(T) + +/obj/structure/window/CanAtmosPass(turf/T) + if(get_dir(loc, T) == dir) + return !density + if(dir == SOUTHWEST || dir == SOUTHEAST || dir == NORTHWEST || dir == NORTHEAST) + return !density + return 1 + +//This proc is used to update the icons of nearby windows. +/obj/structure/window/proc/update_nearby_icons() + update_icon() + if(smooth) + smooth_icon_neighbors(src) + +//merges adjacent full-tile windows into one (blatant ripoff from game/smoothwall.dm) +/obj/structure/window/update_icon() + //A little cludge here, since I don't know how it will work with slim windows. Most likely VERY wrong. + //this way it will only update full-tile ones + //This spawn is here so windows get properly updated when one gets deleted. + spawn(2) + if(!src) return + if(!fulltile) + return + + var/ratio = health / maxhealth + ratio = Ceiling(ratio*4) * 25 + + if(smooth) + smooth_icon(src) + + overlays -= crack_overlay + if(ratio > 75) + return + crack_overlay = image('icons/obj/structures.dmi',"damage[ratio]",-(layer+0.1)) + overlays += crack_overlay + +/obj/structure/window/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) + if(exposed_temperature > T0C + (reinf ? 1600 : 800)) + hit(round(exposed_volume / 100), 0) + ..() + +/obj/structure/window/storage_contents_dump_act(obj/item/weapon/storage/src_object, mob/user) + return 0 + +/obj/structure/window/reinforced + name = "reinforced window" + icon_state = "rwindow" + reinf = 1 + maxhealth = 50 + explosion_block = 1 + +/obj/structure/window/reinforced/tinted + name = "tinted window" + icon_state = "twindow" + opacity = 1 + +/obj/structure/window/reinforced/tinted/frosted + name = "frosted window" + icon_state = "fwindow" + + +/* Full Tile Windows (more health) */ + +/obj/structure/window/fulltile + icon = 'icons/obj/smooth_structures/window.dmi' + icon_state = "window" + dir = 5 + maxhealth = 50 + fulltile = 1 + smooth = SMOOTH_TRUE + canSmoothWith = list(/obj/structure/window/fulltile, /obj/structure/window/reinforced/fulltile, /obj/structure/window/reinforced/tinted/fulltile) + +/obj/structure/window/reinforced/fulltile + icon = 'icons/obj/smooth_structures/reinforced_window.dmi' + icon_state = "r_window" + dir = 5 + maxhealth = 100 + fulltile = 1 + smooth = SMOOTH_TRUE + canSmoothWith = list(/obj/structure/window/fulltile, /obj/structure/window/reinforced/fulltile, /obj/structure/window/reinforced/tinted/fulltile) + +/obj/structure/window/reinforced/tinted/fulltile + icon = 'icons/obj/smooth_structures/tinted_window.dmi' + icon_state = "tinted_window" + dir = 5 + fulltile = 1 + smooth = SMOOTH_TRUE + canSmoothWith = list(/obj/structure/window/fulltile, /obj/structure/window/reinforced/fulltile, /obj/structure/window/reinforced/tinted/fulltile/) + +/obj/structure/window/reinforced/fulltile/ice + icon = 'icons/obj/smooth_structures/rice_window.dmi' + icon_state = "ice_window" + maxhealth = 150 + canSmoothWith = list(/obj/structure/window/fulltile, /obj/structure/window/reinforced/fulltile, /obj/structure/window/reinforced/tinted/fulltile, /obj/structure/window/reinforced/fulltile/ice) + +/obj/structure/window/shuttle + name = "shuttle window" + desc = "A reinforced, air-locked pod window." + icon = 'icons/obj/smooth_structures/shuttle_window.dmi' + icon_state = "shuttle_window" + dir = 5 + maxhealth = 100 + wtype = "shuttle" + fulltile = 1 + reinf = 1 + smooth = SMOOTH_TRUE + canSmoothWith = null + explosion_block = 1 diff --git a/code/game/turfs/simulated.dm b/code/game/turfs/simulated.dm index 8cdf2c11b4f..d49d0ca0a33 100644 --- a/code/game/turfs/simulated.dm +++ b/code/game/turfs/simulated.dm @@ -1,62 +1,62 @@ -/turf/simulated - name = "station" - var/wet = 0 - var/image/wet_overlay = null - - var/thermite = 0 - oxygen = MOLES_O2STANDARD - nitrogen = MOLES_N2STANDARD - var/to_be_destroyed = 0 //Used for fire, if a melting temperature was reached, it will be destroyed - var/max_fire_temperature_sustained = 0 //The max temperature of the fire which it was subjected to - - - -/turf/simulated/proc/burn_tile() - -/turf/simulated/proc/MakeSlippery(wet_setting = TURF_WET_WATER) // 1 = Water, 2 = Lube, 3 = Ice - if(wet >= wet_setting) - return - wet = wet_setting - if(wet_setting != TURF_DRY) - if(wet_overlay) - overlays -= wet_overlay - wet_overlay = null - var/turf/simulated/floor/F = src - if(istype(F)) - wet_overlay = image('icons/effects/water.dmi', src, "wet_floor_static") - else - wet_overlay = image('icons/effects/water.dmi', src, "wet_static") - overlays += wet_overlay - - spawn(rand(790, 820)) // Purely so for visual effect - if(!istype(src, /turf/simulated)) //Because turfs don't get deleted, they change, adapt, transform, evolve and deform. they are one and they are all. - return - MakeDry(wet_setting) - -/turf/simulated/proc/MakeDry(wet_setting = TURF_WET_WATER) - if(wet > wet_setting) - return - wet = TURF_DRY - if(wet_overlay) - overlays -= wet_overlay - -/turf/simulated/Entered(atom/A, atom/OL) - ..() - if (istype(A,/mob/living/carbon)) - var/mob/living/carbon/M = A - switch(wet) - if(TURF_WET_WATER) - if(!M.slip(3, 1, null, NO_SLIP_WHEN_WALKING)) - M.inertia_dir = 0 - return - if(TURF_WET_LUBE) - M.slip(0, 7, null, (SLIDE|GALOSHES_DONT_HELP)) - return - if(TURF_WET_ICE) - M.slip(0, 4, null, (SLIDE|NO_SLIP_WHEN_WALKING)) - return - - -/turf/simulated/ChangeTurf(var/path) - . = ..() - smooth_icon_neighbors(src) +/turf/simulated + name = "station" + var/wet = 0 + var/image/wet_overlay = null + + var/thermite = 0 + oxygen = MOLES_O2STANDARD + nitrogen = MOLES_N2STANDARD + var/to_be_destroyed = 0 //Used for fire, if a melting temperature was reached, it will be destroyed + var/max_fire_temperature_sustained = 0 //The max temperature of the fire which it was subjected to + + + +/turf/simulated/proc/burn_tile() + +/turf/simulated/proc/MakeSlippery(wet_setting = TURF_WET_WATER) // 1 = Water, 2 = Lube, 3 = Ice + if(wet >= wet_setting) + return + wet = wet_setting + if(wet_setting != TURF_DRY) + if(wet_overlay) + overlays -= wet_overlay + wet_overlay = null + var/turf/simulated/floor/F = src + if(istype(F)) + wet_overlay = image('icons/effects/water.dmi', src, "wet_floor_static") + else + wet_overlay = image('icons/effects/water.dmi', src, "wet_static") + overlays += wet_overlay + + spawn(rand(790, 820)) // Purely so for visual effect + if(!istype(src, /turf/simulated)) //Because turfs don't get deleted, they change, adapt, transform, evolve and deform. they are one and they are all. + return + MakeDry(wet_setting) + +/turf/simulated/proc/MakeDry(wet_setting = TURF_WET_WATER) + if(wet > wet_setting) + return + wet = TURF_DRY + if(wet_overlay) + overlays -= wet_overlay + +/turf/simulated/Entered(atom/A, atom/OL) + ..() + if (istype(A,/mob/living/carbon)) + var/mob/living/carbon/M = A + switch(wet) + if(TURF_WET_WATER) + if(!M.slip(3, 1, null, NO_SLIP_WHEN_WALKING)) + M.inertia_dir = 0 + return + if(TURF_WET_LUBE) + M.slip(0, 7, null, (SLIDE|GALOSHES_DONT_HELP)) + return + if(TURF_WET_ICE) + M.slip(0, 4, null, (SLIDE|NO_SLIP_WHEN_WALKING)) + return + + +/turf/simulated/ChangeTurf(var/path) + . = ..() + smooth_icon_neighbors(src) diff --git a/code/game/turfs/simulated/floor/misc_floor.dm b/code/game/turfs/simulated/floor/misc_floor.dm index 6c24fc97642..71ea4b52c76 100644 --- a/code/game/turfs/simulated/floor/misc_floor.dm +++ b/code/game/turfs/simulated/floor/misc_floor.dm @@ -1,95 +1,95 @@ -/turf/simulated/floor/goonplaque - name = "Commemorative Plaque" - icon_state = "plaque" - desc = "\"This is a plaque in honour of our comrades on the G4407 Stations. Hopefully TG4407 model can live up to your fame and fortune.\" Scratched in beneath that is a crude image of a meteor and a spaceman. The spaceman is laughing. The meteor is exploding." - floor_tile = /obj/item/stack/tile/plasteel - -/turf/simulated/floor/vault - icon_state = "rockvault" - floor_tile = /obj/item/stack/tile/plasteel - -/turf/simulated/floor/bluegrid - icon = 'icons/turf/floors.dmi' - icon_state = "bcircuit" - floor_tile = /obj/item/stack/tile/plasteel - -/turf/simulated/floor/greengrid - icon = 'icons/turf/floors.dmi' - icon_state = "gcircuit" - floor_tile = /obj/item/stack/tile/plasteel - -/turf/simulated/floor/plating/beach - name = "Beach" - icon = 'icons/misc/beach.dmi' - -/turf/simulated/floor/plating/beach/ex_act(severity, target) - contents_explosion(severity, target) - -/turf/simulated/floor/plating/beach/sand - name = "Sand" - icon_state = "sand" - -/turf/simulated/floor/plating/beach/coastline - name = "Coastline" - icon = 'icons/misc/beach2.dmi' - icon_state = "sandwater" - -/turf/simulated/floor/plating/beach/water - name = "Water" - icon_state = "water" - -/turf/simulated/floor/plating/ironsand/New() - ..() - name = "Iron Sand" - icon_state = "ironsand[rand(1,15)]" - -/turf/simulated/floor/plating/snow - name = "snow" - desc = "Looks cold." - icon = 'icons/turf/snow.dmi' - icon_state = "snow" - temperature = 180 - baseturf = /turf/simulated/floor/plating/snow - slowdown = 2 - -/turf/simulated/floor/plating/snow/break_tile() - return - -/turf/simulated/floor/plating/snow/burn_tile() - return - -/turf/simulated/floor/plating/snow/gravsnow - icon_state = "snow_dug" - slowdown = 0 - -/turf/simulated/floor/plating/snow/gravsnow/corner - icon_state = "gravsnow_corner" - -/turf/simulated/floor/plating/snow/gravsnow/surround - icon_state = "gravsnow_surround" - -/turf/simulated/floor/plating/ice - name = "ice sheet" - desc = "A sheet of solid ice. Looks slippery." - icon = 'icons/turf/snow.dmi' - icon_state = "ice" - baseturf = /turf/simulated/floor/plating/snow - slowdown = 1 - wet = TURF_WET_ICE - -/turf/simulated/floor/plating/ice/break_tile() - return - -/turf/simulated/floor/plating/snow/burn_tile() - return - -/turf/simulated/floor/noslip - name = "high-traction floor" - icon_state = "noslip" - floor_tile = /obj/item/stack/tile/noslip - broken_states = list("noslip-damaged1","noslip-damaged2","noslip-damaged3") - burnt_states = list("noslip-scorched1","noslip-scorched2") - slowdown = -0.3 - -/turf/simulated/floor/noslip/MakeSlippery() - return +/turf/simulated/floor/goonplaque + name = "Commemorative Plaque" + icon_state = "plaque" + desc = "\"This is a plaque in honour of our comrades on the G4407 Stations. Hopefully TG4407 model can live up to your fame and fortune.\" Scratched in beneath that is a crude image of a meteor and a spaceman. The spaceman is laughing. The meteor is exploding." + floor_tile = /obj/item/stack/tile/plasteel + +/turf/simulated/floor/vault + icon_state = "rockvault" + floor_tile = /obj/item/stack/tile/plasteel + +/turf/simulated/floor/bluegrid + icon = 'icons/turf/floors.dmi' + icon_state = "bcircuit" + floor_tile = /obj/item/stack/tile/plasteel + +/turf/simulated/floor/greengrid + icon = 'icons/turf/floors.dmi' + icon_state = "gcircuit" + floor_tile = /obj/item/stack/tile/plasteel + +/turf/simulated/floor/plating/beach + name = "Beach" + icon = 'icons/misc/beach.dmi' + +/turf/simulated/floor/plating/beach/ex_act(severity, target) + contents_explosion(severity, target) + +/turf/simulated/floor/plating/beach/sand + name = "Sand" + icon_state = "sand" + +/turf/simulated/floor/plating/beach/coastline + name = "Coastline" + icon = 'icons/misc/beach2.dmi' + icon_state = "sandwater" + +/turf/simulated/floor/plating/beach/water + name = "Water" + icon_state = "water" + +/turf/simulated/floor/plating/ironsand/New() + ..() + name = "Iron Sand" + icon_state = "ironsand[rand(1,15)]" + +/turf/simulated/floor/plating/snow + name = "snow" + desc = "Looks cold." + icon = 'icons/turf/snow.dmi' + icon_state = "snow" + temperature = 180 + baseturf = /turf/simulated/floor/plating/snow + slowdown = 2 + +/turf/simulated/floor/plating/snow/break_tile() + return + +/turf/simulated/floor/plating/snow/burn_tile() + return + +/turf/simulated/floor/plating/snow/gravsnow + icon_state = "snow_dug" + slowdown = 0 + +/turf/simulated/floor/plating/snow/gravsnow/corner + icon_state = "gravsnow_corner" + +/turf/simulated/floor/plating/snow/gravsnow/surround + icon_state = "gravsnow_surround" + +/turf/simulated/floor/plating/ice + name = "ice sheet" + desc = "A sheet of solid ice. Looks slippery." + icon = 'icons/turf/snow.dmi' + icon_state = "ice" + baseturf = /turf/simulated/floor/plating/snow + slowdown = 1 + wet = TURF_WET_ICE + +/turf/simulated/floor/plating/ice/break_tile() + return + +/turf/simulated/floor/plating/snow/burn_tile() + return + +/turf/simulated/floor/noslip + name = "high-traction floor" + icon_state = "noslip" + floor_tile = /obj/item/stack/tile/noslip + broken_states = list("noslip-damaged1","noslip-damaged2","noslip-damaged3") + burnt_states = list("noslip-scorched1","noslip-scorched2") + slowdown = -0.3 + +/turf/simulated/floor/noslip/MakeSlippery() + return diff --git a/code/game/turfs/simulated/floor/plating.dm b/code/game/turfs/simulated/floor/plating.dm index ab4bac63354..4aa16f11ec1 100644 --- a/code/game/turfs/simulated/floor/plating.dm +++ b/code/game/turfs/simulated/floor/plating.dm @@ -1,244 +1,244 @@ -/* In this file: - * - * Plating - * Airless - * Airless plating - * Engine floor - */ -// note that plating and engine floor do not call their parent attackby, unlike other flooring -// this is done in order to avoid inheriting the crowbar attackby - -/turf/simulated/floor/plating - name = "plating" - icon_state = "plating" - intact = 0 - broken_states = list("platingdmg1", "platingdmg2", "platingdmg3") - burnt_states = list("panelscorched") - -/turf/simulated/floor/plating/New() - ..() - icon_plating = icon_state - -/turf/simulated/floor/plating/update_icon() - if(!..()) - return - if(!broken && !burnt) - icon_state = icon_plating //Because asteroids are 'platings' too. - -/turf/simulated/floor/plating/attackby(obj/item/C, mob/user, params) - if(..()) - return - if(istype(C, /obj/item/stack/rods)) - if(broken || burnt) - user << "Repair the plating first!" - return - var/obj/item/stack/rods/R = C - if (R.get_amount() < 2) - user << "You need two rods to make a reinforced floor!" - return - else - user << "You begin reinforcing the floor..." - if(do_after(user, 30, target = src)) - if (R.get_amount() >= 2 && !istype(src, /turf/simulated/floor/engine)) - ChangeTurf(/turf/simulated/floor/engine) - playsound(src, 'sound/items/Deconstruct.ogg', 80, 1) - R.use(2) - user << "You reinforce the floor." - return - else if(istype(C, /obj/item/stack/tile)) - if(!broken && !burnt) - var/obj/item/stack/tile/W = C - if(!W.use(1)) - return - var/turf/simulated/floor/T = ChangeTurf(W.turf_type) - if(istype(W,/obj/item/stack/tile/light)) //TODO: get rid of this ugly check somehow - var/obj/item/stack/tile/light/L = W - var/turf/simulated/floor/light/F = T - F.state = L.state - playsound(src, 'sound/weapons/Genhit.ogg', 50, 1) - else - user << "This section is too damaged to support a tile! Use a welder to fix the damage." - else if(istype(C, /obj/item/weapon/weldingtool)) - var/obj/item/weapon/weldingtool/welder = C - if( welder.isOn() && (broken || burnt) ) - if(welder.remove_fuel(0,user)) - user << "You fix some dents on the broken plating." - playsound(src, 'sound/items/Welder.ogg', 80, 1) - icon_state = icon_plating - burnt = 0 - broken = 0 - -/turf/simulated/floor/plating/airless - icon_state = "plating" - oxygen = 0 - nitrogen = 0 - temperature = TCMB - -/turf/simulated/floor/engine - name = "reinforced floor" - icon_state = "engine" - thermal_conductivity = 0.025 - heat_capacity = 325000 - floor_tile = /obj/item/stack/rods - -/turf/simulated/floor/engine/break_tile() - return //unbreakable - -/turf/simulated/floor/engine/burn_tile() - return //unburnable - -/turf/simulated/floor/engine/make_plating(force = 0) - if(force) - ..() - return //unplateable - -/turf/simulated/floor/engine/attackby(obj/item/weapon/C, mob/user, params) - if(!C || !user) - return - if(istype(C, /obj/item/weapon/wrench)) - user << "You begin removing rods..." - playsound(src, 'sound/items/Ratchet.ogg', 80, 1) - if(do_after(user, 30/C.toolspeed, target = src)) - if(!istype(src, /turf/simulated/floor/engine)) - return - new /obj/item/stack/rods(src, 2) - ChangeTurf(/turf/simulated/floor/plating) - return - - -/turf/simulated/floor/engine/ex_act(severity,target) - switch(severity) - if(1) - if(prob(80)) - ReplaceWithLattice() - else if(prob(50)) - qdel(src) - else - make_plating(1) - if(2) - if(prob(50)) - make_plating(1) - - -/turf/simulated/floor/engine/cult - name = "engraved floor" - icon_state = "cult" - -/turf/simulated/floor/engine/cult/New() - PoolOrNew(/obj/effect/overlay/temp/cult/turf/floor, src) - ..() - -/turf/simulated/floor/engine/cult/narsie_act() - return - -/turf/simulated/floor/engine/n20/New() - ..() - var/datum/gas_mixture/adding = new - var/datum/gas/sleeping_agent/trace_gas = new - - trace_gas.moles = 6000 - adding.trace_gases += trace_gas - adding.temperature = T20C - - assume_air(adding) - -/turf/simulated/floor/engine/singularity_pull(S, current_size) - if(current_size >= STAGE_FIVE) - if(builtin_tile) - if(prob(30)) - builtin_tile.loc = src - make_plating() - else if(prob(30)) - ReplaceWithLattice() - -/turf/simulated/floor/engine/vacuum - name = "vacuum floor" - icon_state = "engine" - oxygen = 0 - nitrogen = 0 - temperature = TCMB - -/turf/simulated/floor/plasteel/airless - oxygen = 0 - nitrogen = 0 - temperature = TCMB - -/turf/simulated/floor/plating/abductor - name = "alien floor" - icon_state = "alienpod1" - -/turf/simulated/floor/plating/abductor/New() - ..() - icon_state = "alienpod[rand(1,9)]" - -///LAVA - -/turf/simulated/floor/plating/lava - name = "lava" - icon_state = "lava" - baseturf = /turf/simulated/floor/plating/lava //lava all the way down - slowdown = 2 - var/processing = 0 - luminosity = 1 - -/turf/simulated/floor/plating/lava/airless - oxygen = 0 - nitrogen = 0 - temperature = TCMB - -/turf/simulated/floor/plating/lava/Entered(atom/movable/AM) - burn_stuff() - if(!processing) - processing = 1 - SSobj.processing |= src - -/turf/simulated/floor/plating/lava/process() - if(!contents) - processing = 0 - SSobj.processing.Remove(src) - return - burn_stuff() - -/turf/simulated/floor/plating/lava/proc/burn_stuff() - for(var/atom/movable/AM in contents) - if(!istype(AM)) - return - if(istype(AM, /obj)) - var/obj/O = AM - if(istype(O, /obj/effect/decal/cleanable/ash)) //So we don't get stuck burning the same ash pile forever - qdel(O) - return - if(O.burn_state == FIRE_PROOF) - O.burn_state = FLAMMABLE //Even fireproof things burn up in lava - O.fire_act() - else if (istype(AM, /mob/living)) - var/mob/living/L = AM - L.adjustFireLoss(20) - if(L) //mobs turning into object corpses could get deleted here. - L.adjust_fire_stacks(20) - L.IgniteMob() - -/turf/simulated/floor/plating/lava/attackby(obj/item/C, mob/user, params) //Lava isn't a good foundation to build on - return - -/turf/simulated/floor/plating/lava/break_tile() - return - -/turf/simulated/floor/plating/lava/burn_tile() - return - -/turf/simulated/floor/plating/lava/attackby(obj/item/C, mob/user, params) //Lava isn't a good foundation to build on - return - -/turf/simulated/floor/plating/lava/smooth - name = "lava" - baseturf = /turf/simulated/floor/plating/lava/smooth - smooth = SMOOTH_TRUE - icon = 'icons/turf/floors/lava.dmi' - icon_state = "smooth" - canSmoothWith = list(/turf/simulated/wall, /turf/simulated/mineral, /turf/simulated/floor/plating/lava/smooth) - -/turf/simulated/floor/plating/lava/smooth/airless - oxygen = 0 - nitrogen = 0 +/* In this file: + * + * Plating + * Airless + * Airless plating + * Engine floor + */ +// note that plating and engine floor do not call their parent attackby, unlike other flooring +// this is done in order to avoid inheriting the crowbar attackby + +/turf/simulated/floor/plating + name = "plating" + icon_state = "plating" + intact = 0 + broken_states = list("platingdmg1", "platingdmg2", "platingdmg3") + burnt_states = list("panelscorched") + +/turf/simulated/floor/plating/New() + ..() + icon_plating = icon_state + +/turf/simulated/floor/plating/update_icon() + if(!..()) + return + if(!broken && !burnt) + icon_state = icon_plating //Because asteroids are 'platings' too. + +/turf/simulated/floor/plating/attackby(obj/item/C, mob/user, params) + if(..()) + return + if(istype(C, /obj/item/stack/rods)) + if(broken || burnt) + user << "Repair the plating first!" + return + var/obj/item/stack/rods/R = C + if (R.get_amount() < 2) + user << "You need two rods to make a reinforced floor!" + return + else + user << "You begin reinforcing the floor..." + if(do_after(user, 30, target = src)) + if (R.get_amount() >= 2 && !istype(src, /turf/simulated/floor/engine)) + ChangeTurf(/turf/simulated/floor/engine) + playsound(src, 'sound/items/Deconstruct.ogg', 80, 1) + R.use(2) + user << "You reinforce the floor." + return + else if(istype(C, /obj/item/stack/tile)) + if(!broken && !burnt) + var/obj/item/stack/tile/W = C + if(!W.use(1)) + return + var/turf/simulated/floor/T = ChangeTurf(W.turf_type) + if(istype(W,/obj/item/stack/tile/light)) //TODO: get rid of this ugly check somehow + var/obj/item/stack/tile/light/L = W + var/turf/simulated/floor/light/F = T + F.state = L.state + playsound(src, 'sound/weapons/Genhit.ogg', 50, 1) + else + user << "This section is too damaged to support a tile! Use a welder to fix the damage." + else if(istype(C, /obj/item/weapon/weldingtool)) + var/obj/item/weapon/weldingtool/welder = C + if( welder.isOn() && (broken || burnt) ) + if(welder.remove_fuel(0,user)) + user << "You fix some dents on the broken plating." + playsound(src, 'sound/items/Welder.ogg', 80, 1) + icon_state = icon_plating + burnt = 0 + broken = 0 + +/turf/simulated/floor/plating/airless + icon_state = "plating" + oxygen = 0 + nitrogen = 0 + temperature = TCMB + +/turf/simulated/floor/engine + name = "reinforced floor" + icon_state = "engine" + thermal_conductivity = 0.025 + heat_capacity = 325000 + floor_tile = /obj/item/stack/rods + +/turf/simulated/floor/engine/break_tile() + return //unbreakable + +/turf/simulated/floor/engine/burn_tile() + return //unburnable + +/turf/simulated/floor/engine/make_plating(force = 0) + if(force) + ..() + return //unplateable + +/turf/simulated/floor/engine/attackby(obj/item/weapon/C, mob/user, params) + if(!C || !user) + return + if(istype(C, /obj/item/weapon/wrench)) + user << "You begin removing rods..." + playsound(src, 'sound/items/Ratchet.ogg', 80, 1) + if(do_after(user, 30/C.toolspeed, target = src)) + if(!istype(src, /turf/simulated/floor/engine)) + return + new /obj/item/stack/rods(src, 2) + ChangeTurf(/turf/simulated/floor/plating) + return + + +/turf/simulated/floor/engine/ex_act(severity,target) + switch(severity) + if(1) + if(prob(80)) + ReplaceWithLattice() + else if(prob(50)) + qdel(src) + else + make_plating(1) + if(2) + if(prob(50)) + make_plating(1) + + +/turf/simulated/floor/engine/cult + name = "engraved floor" + icon_state = "cult" + +/turf/simulated/floor/engine/cult/New() + PoolOrNew(/obj/effect/overlay/temp/cult/turf/floor, src) + ..() + +/turf/simulated/floor/engine/cult/narsie_act() + return + +/turf/simulated/floor/engine/n20/New() + ..() + var/datum/gas_mixture/adding = new + var/datum/gas/sleeping_agent/trace_gas = new + + trace_gas.moles = 6000 + adding.trace_gases += trace_gas + adding.temperature = T20C + + assume_air(adding) + +/turf/simulated/floor/engine/singularity_pull(S, current_size) + if(current_size >= STAGE_FIVE) + if(builtin_tile) + if(prob(30)) + builtin_tile.loc = src + make_plating() + else if(prob(30)) + ReplaceWithLattice() + +/turf/simulated/floor/engine/vacuum + name = "vacuum floor" + icon_state = "engine" + oxygen = 0 + nitrogen = 0 + temperature = TCMB + +/turf/simulated/floor/plasteel/airless + oxygen = 0 + nitrogen = 0 + temperature = TCMB + +/turf/simulated/floor/plating/abductor + name = "alien floor" + icon_state = "alienpod1" + +/turf/simulated/floor/plating/abductor/New() + ..() + icon_state = "alienpod[rand(1,9)]" + +///LAVA + +/turf/simulated/floor/plating/lava + name = "lava" + icon_state = "lava" + baseturf = /turf/simulated/floor/plating/lava //lava all the way down + slowdown = 2 + var/processing = 0 + luminosity = 1 + +/turf/simulated/floor/plating/lava/airless + oxygen = 0 + nitrogen = 0 + temperature = TCMB + +/turf/simulated/floor/plating/lava/Entered(atom/movable/AM) + burn_stuff() + if(!processing) + processing = 1 + SSobj.processing |= src + +/turf/simulated/floor/plating/lava/process() + if(!contents) + processing = 0 + SSobj.processing.Remove(src) + return + burn_stuff() + +/turf/simulated/floor/plating/lava/proc/burn_stuff() + for(var/atom/movable/AM in contents) + if(!istype(AM)) + return + if(istype(AM, /obj)) + var/obj/O = AM + if(istype(O, /obj/effect/decal/cleanable/ash)) //So we don't get stuck burning the same ash pile forever + qdel(O) + return + if(O.burn_state == FIRE_PROOF) + O.burn_state = FLAMMABLE //Even fireproof things burn up in lava + O.fire_act() + else if (istype(AM, /mob/living)) + var/mob/living/L = AM + L.adjustFireLoss(20) + if(L) //mobs turning into object corpses could get deleted here. + L.adjust_fire_stacks(20) + L.IgniteMob() + +/turf/simulated/floor/plating/lava/attackby(obj/item/C, mob/user, params) //Lava isn't a good foundation to build on + return + +/turf/simulated/floor/plating/lava/break_tile() + return + +/turf/simulated/floor/plating/lava/burn_tile() + return + +/turf/simulated/floor/plating/lava/attackby(obj/item/C, mob/user, params) //Lava isn't a good foundation to build on + return + +/turf/simulated/floor/plating/lava/smooth + name = "lava" + baseturf = /turf/simulated/floor/plating/lava/smooth + smooth = SMOOTH_TRUE + icon = 'icons/turf/floors/lava.dmi' + icon_state = "smooth" + canSmoothWith = list(/turf/simulated/wall, /turf/simulated/mineral, /turf/simulated/floor/plating/lava/smooth) + +/turf/simulated/floor/plating/lava/smooth/airless + oxygen = 0 + nitrogen = 0 temperature = TCMB \ 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 fbe720974e9..7333c428b75 100644 --- a/code/game/turfs/simulated/walls_mineral.dm +++ b/code/game/turfs/simulated/walls_mineral.dm @@ -1,176 +1,176 @@ -/turf/simulated/wall/mineral - name = "mineral wall" - desc = "This shouldn't exist" - icon_state = "" - var/last_event = 0 - var/active = null - canSmoothWith = null - smooth = SMOOTH_TRUE - -/turf/simulated/wall/mineral/gold - name = "gold wall" - desc = "A wall with gold plating. Swag!" - icon = 'icons/turf/walls/gold_wall.dmi' - icon_state = "gold" - walltype = "gold" - mineral = "gold" - sheet_type = /obj/item/stack/sheet/mineral/gold - //var/electro = 1 - //var/shocked = null - explosion_block = 0 //gold is a soft metal you dingus. - canSmoothWith = list(/turf/simulated/wall/mineral/gold, /obj/structure/falsewall/gold) - -/turf/simulated/wall/mineral/silver - name = "silver wall" - desc = "A wall with silver plating. Shiny!" - icon = 'icons/turf/walls/silver_wall.dmi' - icon_state = "silver" - walltype = "silver" - mineral = "silver" - sheet_type = /obj/item/stack/sheet/mineral/silver - //var/electro = 0.75 - //var/shocked = null - canSmoothWith = list(/turf/simulated/wall/mineral/silver, /obj/structure/falsewall/silver) - -/turf/simulated/wall/mineral/diamond - name = "diamond wall" - desc = "A wall with diamond plating. You monster." - icon = 'icons/turf/walls/diamond_wall.dmi' - icon_state = "diamond" - walltype = "diamond" - mineral = "diamond" - sheet_type = /obj/item/stack/sheet/mineral/diamond - slicing_duration = 200 //diamond wall takes twice as much time to slice - explosion_block = 3 - canSmoothWith = list(/turf/simulated/wall/mineral/diamond, /obj/structure/falsewall/diamond) - -/turf/simulated/wall/mineral/diamond/thermitemelt(mob/user) - return - -/turf/simulated/wall/mineral/clown - name = "bananium wall" - desc = "A wall with bananium plating. Honk!" - icon = 'icons/turf/walls/bananium_wall.dmi' - icon_state = "bananium" - walltype = "bananium" - mineral = "bananium" - sheet_type = /obj/item/stack/sheet/mineral/bananium - canSmoothWith = list(/turf/simulated/wall/mineral/clown, /obj/structure/falsewall/clown) - -/turf/simulated/wall/mineral/sandstone - name = "sandstone wall" - desc = "A wall with sandstone plating. Rough." - icon = 'icons/turf/walls/sandstone_wall.dmi' - icon_state = "sandstone" - walltype = "sandstone" - mineral = "sandstone" - sheet_type = /obj/item/stack/sheet/mineral/sandstone - explosion_block = 0 - canSmoothWith = list(/turf/simulated/wall/mineral/sandstone, /obj/structure/falsewall/sandstone) - -/turf/simulated/wall/mineral/uranium - name = "uranium wall" - desc = "A wall with uranium plating. This is probably a bad idea." - icon = 'icons/turf/walls/uranium_wall.dmi' - icon_state = "uranium" - walltype = "uranium" - mineral = "uranium" - sheet_type = /obj/item/stack/sheet/mineral/uranium - canSmoothWith = list(/turf/simulated/wall/mineral/uranium, /obj/structure/falsewall/uranium) - -/turf/simulated/wall/mineral/uranium/proc/radiate() - if(!active) - if(world.time > last_event+15) - active = 1 - radiation_pulse(get_turf(src), 3, 3, 4, 0) - for(var/turf/simulated/wall/mineral/uranium/T in orange(1,src)) - T.radiate() - last_event = world.time - active = null - return - return - -/turf/simulated/wall/mineral/uranium/attack_hand(mob/user) - radiate() - ..() - -/turf/simulated/wall/mineral/uranium/attackby(obj/item/weapon/W, mob/user, params) - radiate() - ..() - -/turf/simulated/wall/mineral/uranium/Bumped(AM as mob|obj) - radiate() - ..() - -/turf/simulated/wall/mineral/plasma - name = "plasma wall" - desc = "A wall with plasma plating. This is definitely a bad idea." - icon = 'icons/turf/walls/plasma_wall.dmi' - icon_state = "plasma" - walltype = "plasma" - mineral = "plasma" - sheet_type = /obj/item/stack/sheet/mineral/plasma - thermal_conductivity = 0.04 - canSmoothWith = list(/turf/simulated/wall/mineral/plasma, /obj/structure/falsewall/plasma) - -/turf/simulated/wall/mineral/plasma/attackby(obj/item/weapon/W, mob/user, params) - if(W.is_hot() > 300)//If the temperature of the object is over 300, then ignite - message_admins("Plasma wall ignited by [key_name_admin(user)](?) (FLW) in ([x],[y],[z] - JMP)",0,1) - log_game("Plasma wall ignited by [key_name(user)] in ([x],[y],[z])") - ignite(W.is_hot()) - return - ..() - -/turf/simulated/wall/mineral/plasma/proc/PlasmaBurn(temperature) - new /obj/structure/girder(src) - src.ChangeTurf(/turf/simulated/floor/plasteel) - atmos_spawn_air(SPAWN_HEAT | SPAWN_TOXINS, 400) - -/turf/simulated/wall/mineral/plasma/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume)//Doesn't fucking work because walls don't interact with air :( - if(exposed_temperature > 300) - PlasmaBurn(exposed_temperature) - -/turf/simulated/wall/mineral/plasma/proc/ignite(exposed_temperature) - if(exposed_temperature > 300) - PlasmaBurn(exposed_temperature) - -/turf/simulated/wall/mineral/plasma/bullet_act(var/obj/item/projectile/Proj) - if(istype(Proj,/obj/item/projectile/beam)) - PlasmaBurn(2500) - else if(istype(Proj,/obj/item/projectile/ion)) - PlasmaBurn(500) - ..() - - -/turf/simulated/wall/mineral/wood - name = "wooden wall" - desc = "A wall with wooden plating. Stiff." - icon = 'icons/turf/walls/wood_wall.dmi' - icon_state = "wood" - walltype = "wood" - mineral = "wood" - sheet_type = /obj/item/stack/sheet/mineral/wood - hardness = 70 - explosion_block = 0 - canSmoothWith = list(/turf/simulated/wall/mineral/wood, /obj/structure/falsewall/wood) - -/turf/simulated/wall/mineral/iron - name = "rough metal wall" - desc = "A wall with rough metal plating." - icon = 'icons/turf/walls/iron_wall.dmi' - icon_state = "iron" - walltype = "iron" - mineral = "rods" - sheet_type = /obj/item/stack/rods - canSmoothWith = list(/turf/simulated/wall/mineral/iron, /obj/structure/falsewall/iron) - -/turf/simulated/wall/mineral/snow - name = "packed snow wall" - desc = "A wall made of densely packed snow blocks." - icon = 'icons/turf/walls/snow_wall.dmi' - icon_state = "snow" - walltype = "snow" - mineral = "snow" - hardness = 80 - sheet_type = /obj/item/stack/sheet/mineral/snow +/turf/simulated/wall/mineral + name = "mineral wall" + desc = "This shouldn't exist" + icon_state = "" + var/last_event = 0 + var/active = null + canSmoothWith = null + smooth = SMOOTH_TRUE + +/turf/simulated/wall/mineral/gold + name = "gold wall" + desc = "A wall with gold plating. Swag!" + icon = 'icons/turf/walls/gold_wall.dmi' + icon_state = "gold" + walltype = "gold" + mineral = "gold" + sheet_type = /obj/item/stack/sheet/mineral/gold + //var/electro = 1 + //var/shocked = null + explosion_block = 0 //gold is a soft metal you dingus. + canSmoothWith = list(/turf/simulated/wall/mineral/gold, /obj/structure/falsewall/gold) + +/turf/simulated/wall/mineral/silver + name = "silver wall" + desc = "A wall with silver plating. Shiny!" + icon = 'icons/turf/walls/silver_wall.dmi' + icon_state = "silver" + walltype = "silver" + mineral = "silver" + sheet_type = /obj/item/stack/sheet/mineral/silver + //var/electro = 0.75 + //var/shocked = null + canSmoothWith = list(/turf/simulated/wall/mineral/silver, /obj/structure/falsewall/silver) + +/turf/simulated/wall/mineral/diamond + name = "diamond wall" + desc = "A wall with diamond plating. You monster." + icon = 'icons/turf/walls/diamond_wall.dmi' + icon_state = "diamond" + walltype = "diamond" + mineral = "diamond" + sheet_type = /obj/item/stack/sheet/mineral/diamond + slicing_duration = 200 //diamond wall takes twice as much time to slice + explosion_block = 3 + canSmoothWith = list(/turf/simulated/wall/mineral/diamond, /obj/structure/falsewall/diamond) + +/turf/simulated/wall/mineral/diamond/thermitemelt(mob/user) + return + +/turf/simulated/wall/mineral/clown + name = "bananium wall" + desc = "A wall with bananium plating. Honk!" + icon = 'icons/turf/walls/bananium_wall.dmi' + icon_state = "bananium" + walltype = "bananium" + mineral = "bananium" + sheet_type = /obj/item/stack/sheet/mineral/bananium + canSmoothWith = list(/turf/simulated/wall/mineral/clown, /obj/structure/falsewall/clown) + +/turf/simulated/wall/mineral/sandstone + name = "sandstone wall" + desc = "A wall with sandstone plating. Rough." + icon = 'icons/turf/walls/sandstone_wall.dmi' + icon_state = "sandstone" + walltype = "sandstone" + mineral = "sandstone" + sheet_type = /obj/item/stack/sheet/mineral/sandstone + explosion_block = 0 + canSmoothWith = list(/turf/simulated/wall/mineral/sandstone, /obj/structure/falsewall/sandstone) + +/turf/simulated/wall/mineral/uranium + name = "uranium wall" + desc = "A wall with uranium plating. This is probably a bad idea." + icon = 'icons/turf/walls/uranium_wall.dmi' + icon_state = "uranium" + walltype = "uranium" + mineral = "uranium" + sheet_type = /obj/item/stack/sheet/mineral/uranium + canSmoothWith = list(/turf/simulated/wall/mineral/uranium, /obj/structure/falsewall/uranium) + +/turf/simulated/wall/mineral/uranium/proc/radiate() + if(!active) + if(world.time > last_event+15) + active = 1 + radiation_pulse(get_turf(src), 3, 3, 4, 0) + for(var/turf/simulated/wall/mineral/uranium/T in orange(1,src)) + T.radiate() + last_event = world.time + active = null + return + return + +/turf/simulated/wall/mineral/uranium/attack_hand(mob/user) + radiate() + ..() + +/turf/simulated/wall/mineral/uranium/attackby(obj/item/weapon/W, mob/user, params) + radiate() + ..() + +/turf/simulated/wall/mineral/uranium/Bumped(AM as mob|obj) + radiate() + ..() + +/turf/simulated/wall/mineral/plasma + name = "plasma wall" + desc = "A wall with plasma plating. This is definitely a bad idea." + icon = 'icons/turf/walls/plasma_wall.dmi' + icon_state = "plasma" + walltype = "plasma" + mineral = "plasma" + sheet_type = /obj/item/stack/sheet/mineral/plasma + thermal_conductivity = 0.04 + canSmoothWith = list(/turf/simulated/wall/mineral/plasma, /obj/structure/falsewall/plasma) + +/turf/simulated/wall/mineral/plasma/attackby(obj/item/weapon/W, mob/user, params) + if(W.is_hot() > 300)//If the temperature of the object is over 300, then ignite + message_admins("Plasma wall ignited by [key_name_admin(user)](?) (FLW) in ([x],[y],[z] - JMP)",0,1) + log_game("Plasma wall ignited by [key_name(user)] in ([x],[y],[z])") + ignite(W.is_hot()) + return + ..() + +/turf/simulated/wall/mineral/plasma/proc/PlasmaBurn(temperature) + new /obj/structure/girder(src) + src.ChangeTurf(/turf/simulated/floor/plasteel) + atmos_spawn_air(SPAWN_HEAT | SPAWN_TOXINS, 400) + +/turf/simulated/wall/mineral/plasma/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume)//Doesn't fucking work because walls don't interact with air :( + if(exposed_temperature > 300) + PlasmaBurn(exposed_temperature) + +/turf/simulated/wall/mineral/plasma/proc/ignite(exposed_temperature) + if(exposed_temperature > 300) + PlasmaBurn(exposed_temperature) + +/turf/simulated/wall/mineral/plasma/bullet_act(var/obj/item/projectile/Proj) + if(istype(Proj,/obj/item/projectile/beam)) + PlasmaBurn(2500) + else if(istype(Proj,/obj/item/projectile/ion)) + PlasmaBurn(500) + ..() + + +/turf/simulated/wall/mineral/wood + name = "wooden wall" + desc = "A wall with wooden plating. Stiff." + icon = 'icons/turf/walls/wood_wall.dmi' + icon_state = "wood" + walltype = "wood" + mineral = "wood" + sheet_type = /obj/item/stack/sheet/mineral/wood + hardness = 70 + explosion_block = 0 + canSmoothWith = list(/turf/simulated/wall/mineral/wood, /obj/structure/falsewall/wood) + +/turf/simulated/wall/mineral/iron + name = "rough metal wall" + desc = "A wall with rough metal plating." + icon = 'icons/turf/walls/iron_wall.dmi' + icon_state = "iron" + walltype = "iron" + mineral = "rods" + sheet_type = /obj/item/stack/rods + canSmoothWith = list(/turf/simulated/wall/mineral/iron, /obj/structure/falsewall/iron) + +/turf/simulated/wall/mineral/snow + name = "packed snow wall" + desc = "A wall made of densely packed snow blocks." + icon = 'icons/turf/walls/snow_wall.dmi' + icon_state = "snow" + walltype = "snow" + mineral = "snow" + hardness = 80 + sheet_type = /obj/item/stack/sheet/mineral/snow canSmoothWith = null \ No newline at end of file diff --git a/code/game/turfs/simulated/walls_misc.dm b/code/game/turfs/simulated/walls_misc.dm index 9c39655a875..78ee7ffce45 100644 --- a/code/game/turfs/simulated/walls_misc.dm +++ b/code/game/turfs/simulated/walls_misc.dm @@ -1,87 +1,87 @@ -/turf/simulated/wall/cult - name = "wall" - desc = "The patterns engraved on the wall seem to shift as you try to focus on them. You feel sick." - icon = 'icons/turf/walls/cult_wall.dmi' - icon_state = "cult" - walltype = "cult" - builtin_sheet = null - canSmoothWith = null - -/turf/simulated/wall/cult/New() - PoolOrNew(/obj/effect/overlay/temp/cult/turf, src) - ..() - -/turf/simulated/wall/cult/break_wall() - new /obj/effect/decal/cleanable/blood(src) - return (new /obj/structure/cultgirder(src)) - -/turf/simulated/wall/cult/devastate_wall() - new /obj/effect/decal/cleanable/blood(src) - new /obj/effect/decal/remains/human(src) - -/turf/simulated/wall/cult/narsie_act() - return - -/turf/simulated/wall/vault - icon = 'icons/turf/walls.dmi' - icon_state = "rockvault" - -/turf/simulated/wall/ice - icon = 'icons/turf/walls/icedmetal_wall.dmi' - icon_state = "iced" - desc = "A wall covered in a thick sheet of ice." - walltype = "iced" - canSmoothWith = null - hardness = 35 - slicing_duration = 150 //welding through the ice+metal - -/turf/simulated/wall/rust - name = "rusted wall" - desc = "A rusted metal wall." - icon = 'icons/turf/walls/rusty_wall.dmi' - icon_state = "arust" - walltype = "arust" - hardness = 45 - -/turf/simulated/wall/r_wall/rust - name = "rusted reinforced wall" - desc = "A huge chunk of rusted reinforced metal." - icon = 'icons/turf/walls/rusty_reinforced_wall.dmi' - icon_state = "rrust" - walltype = "rrust" - hardness = 15 - -/turf/simulated/wall/shuttle - name = "wall" - icon = 'icons/turf/shuttle.dmi' - icon_state = "wall1" - walltype = "shuttle" - smooth = SMOOTH_FALSE - -//sub-type to be used for interior shuttle walls -//won't get an underlay of the destination turf on shuttle move -/turf/simulated/wall/shuttle/interior/copyTurf(turf/T) - if(T.type != type) - T.ChangeTurf(type) - if(underlays.len) - T.underlays = underlays - if(T.icon_state != icon_state) - T.icon_state = icon_state - if(T.icon != icon) - T.icon = icon - if(T.color != color) - T.color = color - if(T.dir != dir) - T.dir = dir - T.transform = transform - return T - -/turf/simulated/wall/shuttle/copyTurf(turf/T) - . = ..() - T.transform = transform - -//why don't shuttle walls habe smoothwall? now i gotta do rotation the dirty way -/turf/simulated/wall/shuttle/shuttleRotate(rotation) - var/matrix/M = transform - M.Turn(rotation) +/turf/simulated/wall/cult + name = "wall" + desc = "The patterns engraved on the wall seem to shift as you try to focus on them. You feel sick." + icon = 'icons/turf/walls/cult_wall.dmi' + icon_state = "cult" + walltype = "cult" + builtin_sheet = null + canSmoothWith = null + +/turf/simulated/wall/cult/New() + PoolOrNew(/obj/effect/overlay/temp/cult/turf, src) + ..() + +/turf/simulated/wall/cult/break_wall() + new /obj/effect/decal/cleanable/blood(src) + return (new /obj/structure/cultgirder(src)) + +/turf/simulated/wall/cult/devastate_wall() + new /obj/effect/decal/cleanable/blood(src) + new /obj/effect/decal/remains/human(src) + +/turf/simulated/wall/cult/narsie_act() + return + +/turf/simulated/wall/vault + icon = 'icons/turf/walls.dmi' + icon_state = "rockvault" + +/turf/simulated/wall/ice + icon = 'icons/turf/walls/icedmetal_wall.dmi' + icon_state = "iced" + desc = "A wall covered in a thick sheet of ice." + walltype = "iced" + canSmoothWith = null + hardness = 35 + slicing_duration = 150 //welding through the ice+metal + +/turf/simulated/wall/rust + name = "rusted wall" + desc = "A rusted metal wall." + icon = 'icons/turf/walls/rusty_wall.dmi' + icon_state = "arust" + walltype = "arust" + hardness = 45 + +/turf/simulated/wall/r_wall/rust + name = "rusted reinforced wall" + desc = "A huge chunk of rusted reinforced metal." + icon = 'icons/turf/walls/rusty_reinforced_wall.dmi' + icon_state = "rrust" + walltype = "rrust" + hardness = 15 + +/turf/simulated/wall/shuttle + name = "wall" + icon = 'icons/turf/shuttle.dmi' + icon_state = "wall1" + walltype = "shuttle" + smooth = SMOOTH_FALSE + +//sub-type to be used for interior shuttle walls +//won't get an underlay of the destination turf on shuttle move +/turf/simulated/wall/shuttle/interior/copyTurf(turf/T) + if(T.type != type) + T.ChangeTurf(type) + if(underlays.len) + T.underlays = underlays + if(T.icon_state != icon_state) + T.icon_state = icon_state + if(T.icon != icon) + T.icon = icon + if(T.color != color) + T.color = color + if(T.dir != dir) + T.dir = dir + T.transform = transform + return T + +/turf/simulated/wall/shuttle/copyTurf(turf/T) + . = ..() + T.transform = transform + +//why don't shuttle walls habe smoothwall? now i gotta do rotation the dirty way +/turf/simulated/wall/shuttle/shuttleRotate(rotation) + var/matrix/M = transform + M.Turn(rotation) transform = M \ No newline at end of file diff --git a/code/game/turfs/space/transit.dm b/code/game/turfs/space/transit.dm index 60c82e3399f..9023e546854 100644 --- a/code/game/turfs/space/transit.dm +++ b/code/game/turfs/space/transit.dm @@ -1,71 +1,71 @@ -/turf/space/transit - icon_state = "black" - dir = SOUTH - baseturf = /turf/space/transit - -/turf/space/transit/horizontal - dir = WEST - -/turf/space/transit/Entered(atom/movable/AM, atom/OldLoc) - if(!AM) - return - var/max = world.maxx-TRANSITIONEDGE - var/min = 1+TRANSITIONEDGE - - var/_z = rand(ZLEVEL_SPACEMIN,ZLEVEL_SPACEMAX) //select a random space zlevel - - //now select coordinates for a border turf - var/_x - var/_y - switch(dir) - if(SOUTH) - _x = rand(min,max) - _y = max - if(WEST) - _x = max - _y = rand(min,max) - if(EAST) - _x = min - _y = rand(min,max) - else - _x = rand(min,max) - _y = min - - var/turf/T = locate(_x, _y, _z) - AM.loc = T - AM.newtonian_move(dir) - - - - -//Overwrite because we dont want people building rods in space. -/turf/space/transit/attackby() - return - -/turf/space/transit/New() - update_icon() - ..() - -/turf/space/transit/proc/update_icon() - var/p = 9 - var/angle = 0 - var/state = 1 - switch(dir) - if(NORTH) - angle = 180 - state = ((-p*x+y) % 15) + 1 - if(state < 1) - state += 15 - if(EAST) - angle = 90 - state = ((x+p*y) % 15) + 1 - if(WEST) - angle = -90 - state = ((x-p*y) % 15) + 1 - if(state < 1) - state += 15 - else - state = ((p*x+y) % 15) + 1 - - icon_state = "speedspace_ns_[state]" - transform = turn(matrix(), angle) +/turf/space/transit + icon_state = "black" + dir = SOUTH + baseturf = /turf/space/transit + +/turf/space/transit/horizontal + dir = WEST + +/turf/space/transit/Entered(atom/movable/AM, atom/OldLoc) + if(!AM) + return + var/max = world.maxx-TRANSITIONEDGE + var/min = 1+TRANSITIONEDGE + + var/_z = rand(ZLEVEL_SPACEMIN,ZLEVEL_SPACEMAX) //select a random space zlevel + + //now select coordinates for a border turf + var/_x + var/_y + switch(dir) + if(SOUTH) + _x = rand(min,max) + _y = max + if(WEST) + _x = max + _y = rand(min,max) + if(EAST) + _x = min + _y = rand(min,max) + else + _x = rand(min,max) + _y = min + + var/turf/T = locate(_x, _y, _z) + AM.loc = T + AM.newtonian_move(dir) + + + + +//Overwrite because we dont want people building rods in space. +/turf/space/transit/attackby() + return + +/turf/space/transit/New() + update_icon() + ..() + +/turf/space/transit/proc/update_icon() + var/p = 9 + var/angle = 0 + var/state = 1 + switch(dir) + if(NORTH) + angle = 180 + state = ((-p*x+y) % 15) + 1 + if(state < 1) + state += 15 + if(EAST) + angle = 90 + state = ((x+p*y) % 15) + 1 + if(WEST) + angle = -90 + state = ((x-p*y) % 15) + 1 + if(state < 1) + state += 15 + else + state = ((p*x+y) % 15) + 1 + + icon_state = "speedspace_ns_[state]" + transform = turn(matrix(), angle) diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 82e6ae05742..34c8ce714e0 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -1,344 +1,344 @@ -/turf - icon = 'icons/turf/floors.dmi' - level = 1 - - var/slowdown = 0 //negative for faster, positive for slower - var/intact = 1 - var/baseturf = /turf/space - - //Properties for open tiles (/floor) - var/oxygen = 0 - var/carbon_dioxide = 0 - var/nitrogen = 0 - var/toxins = 0 - - - //Properties for airtight tiles (/wall) - var/thermal_conductivity = 0.05 - var/heat_capacity = 1 - - //Properties for both - var/temperature = T20C - - var/blocks_air = 0 - - var/PathNode/PNode = null //associated PathNode in the A* algorithm - - flags = 0 - - var/image/obscured //camerachunks - -/turf/New() - ..() - for(var/atom/movable/AM in src) - Entered(AM) - -/turf/Destroy() - // Adds the adjacent turfs to the current atmos processing - for(var/direction in cardinal) - if(atmos_adjacent_turfs & direction) - var/turf/simulated/T = get_step(src, direction) - if(istype(T)) - SSair.add_to_active(T) - ..() - return QDEL_HINT_HARDDEL_NOW - -/turf/attack_hand(mob/user) - user.Move_Pulled(src) - -/turf/attackby(obj/item/C, mob/user, params) - if(can_lay_cable() && istype(C, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/coil = C - for(var/obj/structure/cable/LC in src) - if((LC.d1==0)||(LC.d2==0)) - LC.attackby(C,user) - return - coil.place_turf(src, user) - return 1 - - return 0 - -/turf/Enter(atom/movable/mover as mob|obj, atom/forget as mob|obj|turf|area) - if (!mover) - return 1 - // First, make sure it can leave its square - if(isturf(mover.loc)) - // Nothing but border objects stop you from leaving a tile, only one loop is needed - for(var/obj/obstacle in mover.loc) - if(!obstacle.CheckExit(mover, src) && obstacle != mover && obstacle != forget) - mover.Bump(obstacle, 1) - return 0 - - var/list/large_dense = list() - //Next, check objects to block entry that are on the border - for(var/atom/movable/border_obstacle in src) - if(border_obstacle.flags&ON_BORDER) - if(!border_obstacle.CanPass(mover, mover.loc, 1) && (forget != border_obstacle)) - mover.Bump(border_obstacle, 1) - return 0 - else - large_dense += border_obstacle - - //Then, check the turf itself - if (!src.CanPass(mover, src)) - mover.Bump(src, 1) - return 0 - - //Finally, check objects/mobs to block entry that are not on the border - for(var/atom/movable/obstacle in large_dense) - if(!obstacle.CanPass(mover, mover.loc, 1) && (forget != obstacle)) - mover.Bump(obstacle, 1) - return 0 - return 1 //Nothing found to block so return success! - -/turf/Entered(atom/movable/M) - var/loopsanity = 100 - for(var/atom/A in range(1)) - if(loopsanity == 0) - break - loopsanity-- - A.HasProximity(M, 1) - -/turf/proc/is_plasteel_floor() - return 0 - -/turf/proc/levelupdate() - for(var/obj/O in src) - if(O.level == 1) - O.hide(src.intact) - -// override for space turfs, since they should never hide anything -/turf/space/levelupdate() - for(var/obj/O in src) - if(O.level == 1) - O.hide(0) - -// Removes all signs of lattice on the pos of the turf -Donkieyo -/turf/proc/RemoveLattice() - var/obj/structure/lattice/L = locate(/obj/structure/lattice, src) - if(L) - qdel(L) - -//Creates a new turf -/turf/proc/ChangeTurf(path) - if(!path) return - if(path == type) return src - - SSair.remove_from_active(src) - - var/turf/W = new path(src) - if(istype(W, /turf/simulated)) - W:Assimilate_Air() - W.RemoveLattice() - W.levelupdate() - W.CalculateAdjacentTurfs() - - if(!can_have_cabling()) - for(var/obj/structure/cable/C in contents) - C.Deconstruct() - return W - -//////Assimilate Air////// -/turf/simulated/proc/Assimilate_Air() - if(air) - var/aoxy = 0//Holders to assimilate air from nearby turfs - var/anitro = 0 - var/aco = 0 - var/atox = 0 - var/atemp = 0 - var/turf_count = 0 - - for(var/direction in cardinal)//Only use cardinals to cut down on lag - var/turf/T = get_step(src,direction) - if(istype(T,/turf/space))//Counted as no air - turf_count++//Considered a valid turf for air calcs - continue - else if(istype(T,/turf/simulated/floor)) - var/turf/simulated/S = T - if(S.air)//Add the air's contents to the holders - aoxy += S.air.oxygen - anitro += S.air.nitrogen - aco += S.air.carbon_dioxide - atox += S.air.toxins - atemp += S.air.temperature - turf_count ++ - air.oxygen = (aoxy/max(turf_count,1))//Averages contents of the turfs, ignoring walls and the like - air.nitrogen = (anitro/max(turf_count,1)) - air.carbon_dioxide = (aco/max(turf_count,1)) - air.toxins = (atox/max(turf_count,1)) - air.temperature = (atemp/max(turf_count,1))//Trace gases can get bant - SSair.add_to_active(src) - -/turf/proc/ReplaceWithLattice() - src.ChangeTurf(src.baseturf) - new /obj/structure/lattice(locate(src.x, src.y, src.z) ) - -/turf/proc/ReplaceWithCatwalk() - src.ChangeTurf(src.baseturf) - new /obj/structure/lattice/catwalk(locate(src.x, src.y, src.z) ) - -/turf/proc/phase_damage_creatures(damage,mob/U = null)//>Ninja Code. Hurts and knocks out creatures on this turf //NINJACODE - for(var/mob/living/M in src) - if(M==U) - continue//Will not harm U. Since null != M, can be excluded to kill everyone. - M.adjustBruteLoss(damage) - M.Paralyse(damage/5) - for(var/obj/mecha/M in src) - M.take_damage(damage*2, "brute") - -/turf/proc/Bless() - flags |= NOJAUNT - -/turf/storage_contents_dump_act(obj/item/weapon/storage/src_object, mob/user) - if(src_object.contents.len) - usr << "You start dumping out the contents..." - if(!do_after(usr,20,target=src_object)) - return 0 - for(var/obj/item/I in src_object) - if(user.s_active != src_object) - if(I.on_found(user)) - return - src_object.remove_from_storage(I, src) //No check needed, put everything inside - return 1 - -////////////////////////////// -//Distance procs -////////////////////////////// - -//Distance associates with all directions movement -/turf/proc/Distance(var/turf/T) - return get_dist(src,T) - -// This Distance proc assumes that only cardinal movement is -// possible. It results in more efficient (CPU-wise) pathing -// for bots and anything else that only moves in cardinal dirs. -/turf/proc/Distance_cardinal(turf/T) - if(!src || !T) return 0 - return abs(src.x - T.x) + abs(src.y - T.y) - -//////////////////////////////////////////////////// - -/turf/handle_fall(mob/faller, forced) - faller.lying = pick(90, 270) - if(!forced) - return - if(has_gravity(src)) - playsound(src, "bodyfall", 50, 1) - -/turf/handle_slip(mob/living/carbon/C, s_amount, w_amount, obj/O, lube) - if(has_gravity(src)) - var/obj/buckled_obj - var/oldlying = C.lying - if(C.buckled) - buckled_obj = C.buckled - if(!(lube&GALOSHES_DONT_HELP)) //can't slip while buckled unless it's lube. - return 0 - else - if(C.lying || !(C.status_flags & CANWEAKEN)) // can't slip unbuckled mob if they're lying or can't fall. - return 0 - if(C.m_intent=="walk" && (lube&NO_SLIP_WHEN_WALKING)) - return 0 - - C << "You slipped[ O ? " on the [O.name]" : ""]!" - - C.attack_log += "\[[time_stamp()]\] Slipped[O ? " on the [O.name]" : ""][(lube&SLIDE)? " (LUBE)" : ""]!" - playsound(C.loc, 'sound/misc/slip.ogg', 50, 1, -3) - - C.accident(C.l_hand) - C.accident(C.r_hand) - - var/olddir = C.dir - C.Stun(s_amount) - C.Weaken(w_amount) - C.stop_pulling() - if(buckled_obj) - buckled_obj.unbuckle_mob() - step(buckled_obj, olddir) - else if(lube&SLIDE) - for(var/i=1, i<5, i++) - spawn (i) - step(C, olddir) - C.spin(1,1) - if(C.lying != oldlying && lube) //did we actually fall? - var/dam_zone = pick("chest", "l_hand", "r_hand", "l_leg", "r_leg") - C.apply_damage(5, BRUTE, dam_zone) - return 1 - -/turf/singularity_act() - if(intact) - for(var/obj/O in contents) //this is for deleting things like wires contained in the turf - if(O.level != 1) - continue - if(O.invisibility == 101) - O.singularity_act() - ChangeTurf(src.baseturf) - return(2) - -/turf/proc/can_have_cabling() - return 1 - -/turf/proc/can_lay_cable() - return can_have_cabling() & !intact - -/turf/proc/visibilityChanged() - if(ticker) - cameranet.updateVisibility(src) - -/turf/indestructible - name = "wall" - icon = 'icons/turf/walls.dmi' - density = 1 - blocks_air = 1 - opacity = 1 - explosion_block = 50 - -/turf/indestructible/splashscreen - name = "Space Station 13" - icon = 'icons/misc/fullscreen.dmi' - icon_state = "title" - layer = FLY_LAYER - -/turf/indestructible/riveted - icon_state = "riveted" - -/turf/indestructible/riveted/New() - ..() - if(smooth) - smooth_icon(src) - icon_state = "" - -/turf/indestructible/riveted/uranium - icon = 'icons/turf/walls/uranium_wall.dmi' - icon_state = "uranium" - smooth = SMOOTH_TRUE - -/turf/indestructible/abductor - icon_state = "alien1" - -/turf/indestructible/fakeglass - name = "window" - icon_state = "fakewindows" - opacity = 0 - -/turf/indestructible/fakedoor - name = "Centcom Access" - icon = 'icons/obj/doors/airlocks/centcom/centcom.dmi' - icon_state = "fake_door" - -/turf/indestructible/rock - name = "dense rock" - desc = "An extremely densely-packed rock, most mining tools or explosives would never get through this." - icon = 'icons/turf/mining.dmi' - icon_state = "rock" - -/turf/indestructible/rock/snow - name = "mountainside" - desc = "An extremely densely-packed rock, sheeted over with centuries worth of ice and snow." - icon = 'icons/turf/walls.dmi' - icon_state = "snowrock" - -/turf/indestructible/rock/snow/ice - name = "iced rock" - desc = "Extremely densely-packed sheets of ice and rock, forged over the years of the harsh cold." - icon = 'icons/turf/walls.dmi' +/turf + icon = 'icons/turf/floors.dmi' + level = 1 + + var/slowdown = 0 //negative for faster, positive for slower + var/intact = 1 + var/baseturf = /turf/space + + //Properties for open tiles (/floor) + var/oxygen = 0 + var/carbon_dioxide = 0 + var/nitrogen = 0 + var/toxins = 0 + + + //Properties for airtight tiles (/wall) + var/thermal_conductivity = 0.05 + var/heat_capacity = 1 + + //Properties for both + var/temperature = T20C + + var/blocks_air = 0 + + var/PathNode/PNode = null //associated PathNode in the A* algorithm + + flags = 0 + + var/image/obscured //camerachunks + +/turf/New() + ..() + for(var/atom/movable/AM in src) + Entered(AM) + +/turf/Destroy() + // Adds the adjacent turfs to the current atmos processing + for(var/direction in cardinal) + if(atmos_adjacent_turfs & direction) + var/turf/simulated/T = get_step(src, direction) + if(istype(T)) + SSair.add_to_active(T) + ..() + return QDEL_HINT_HARDDEL_NOW + +/turf/attack_hand(mob/user) + user.Move_Pulled(src) + +/turf/attackby(obj/item/C, mob/user, params) + if(can_lay_cable() && istype(C, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/coil = C + for(var/obj/structure/cable/LC in src) + if((LC.d1==0)||(LC.d2==0)) + LC.attackby(C,user) + return + coil.place_turf(src, user) + return 1 + + return 0 + +/turf/Enter(atom/movable/mover as mob|obj, atom/forget as mob|obj|turf|area) + if (!mover) + return 1 + // First, make sure it can leave its square + if(isturf(mover.loc)) + // Nothing but border objects stop you from leaving a tile, only one loop is needed + for(var/obj/obstacle in mover.loc) + if(!obstacle.CheckExit(mover, src) && obstacle != mover && obstacle != forget) + mover.Bump(obstacle, 1) + return 0 + + var/list/large_dense = list() + //Next, check objects to block entry that are on the border + for(var/atom/movable/border_obstacle in src) + if(border_obstacle.flags&ON_BORDER) + if(!border_obstacle.CanPass(mover, mover.loc, 1) && (forget != border_obstacle)) + mover.Bump(border_obstacle, 1) + return 0 + else + large_dense += border_obstacle + + //Then, check the turf itself + if (!src.CanPass(mover, src)) + mover.Bump(src, 1) + return 0 + + //Finally, check objects/mobs to block entry that are not on the border + for(var/atom/movable/obstacle in large_dense) + if(!obstacle.CanPass(mover, mover.loc, 1) && (forget != obstacle)) + mover.Bump(obstacle, 1) + return 0 + return 1 //Nothing found to block so return success! + +/turf/Entered(atom/movable/M) + var/loopsanity = 100 + for(var/atom/A in range(1)) + if(loopsanity == 0) + break + loopsanity-- + A.HasProximity(M, 1) + +/turf/proc/is_plasteel_floor() + return 0 + +/turf/proc/levelupdate() + for(var/obj/O in src) + if(O.level == 1) + O.hide(src.intact) + +// override for space turfs, since they should never hide anything +/turf/space/levelupdate() + for(var/obj/O in src) + if(O.level == 1) + O.hide(0) + +// Removes all signs of lattice on the pos of the turf -Donkieyo +/turf/proc/RemoveLattice() + var/obj/structure/lattice/L = locate(/obj/structure/lattice, src) + if(L) + qdel(L) + +//Creates a new turf +/turf/proc/ChangeTurf(path) + if(!path) return + if(path == type) return src + + SSair.remove_from_active(src) + + var/turf/W = new path(src) + if(istype(W, /turf/simulated)) + W:Assimilate_Air() + W.RemoveLattice() + W.levelupdate() + W.CalculateAdjacentTurfs() + + if(!can_have_cabling()) + for(var/obj/structure/cable/C in contents) + C.Deconstruct() + return W + +//////Assimilate Air////// +/turf/simulated/proc/Assimilate_Air() + if(air) + var/aoxy = 0//Holders to assimilate air from nearby turfs + var/anitro = 0 + var/aco = 0 + var/atox = 0 + var/atemp = 0 + var/turf_count = 0 + + for(var/direction in cardinal)//Only use cardinals to cut down on lag + var/turf/T = get_step(src,direction) + if(istype(T,/turf/space))//Counted as no air + turf_count++//Considered a valid turf for air calcs + continue + else if(istype(T,/turf/simulated/floor)) + var/turf/simulated/S = T + if(S.air)//Add the air's contents to the holders + aoxy += S.air.oxygen + anitro += S.air.nitrogen + aco += S.air.carbon_dioxide + atox += S.air.toxins + atemp += S.air.temperature + turf_count ++ + air.oxygen = (aoxy/max(turf_count,1))//Averages contents of the turfs, ignoring walls and the like + air.nitrogen = (anitro/max(turf_count,1)) + air.carbon_dioxide = (aco/max(turf_count,1)) + air.toxins = (atox/max(turf_count,1)) + air.temperature = (atemp/max(turf_count,1))//Trace gases can get bant + SSair.add_to_active(src) + +/turf/proc/ReplaceWithLattice() + src.ChangeTurf(src.baseturf) + new /obj/structure/lattice(locate(src.x, src.y, src.z) ) + +/turf/proc/ReplaceWithCatwalk() + src.ChangeTurf(src.baseturf) + new /obj/structure/lattice/catwalk(locate(src.x, src.y, src.z) ) + +/turf/proc/phase_damage_creatures(damage,mob/U = null)//>Ninja Code. Hurts and knocks out creatures on this turf //NINJACODE + for(var/mob/living/M in src) + if(M==U) + continue//Will not harm U. Since null != M, can be excluded to kill everyone. + M.adjustBruteLoss(damage) + M.Paralyse(damage/5) + for(var/obj/mecha/M in src) + M.take_damage(damage*2, "brute") + +/turf/proc/Bless() + flags |= NOJAUNT + +/turf/storage_contents_dump_act(obj/item/weapon/storage/src_object, mob/user) + if(src_object.contents.len) + usr << "You start dumping out the contents..." + if(!do_after(usr,20,target=src_object)) + return 0 + for(var/obj/item/I in src_object) + if(user.s_active != src_object) + if(I.on_found(user)) + return + src_object.remove_from_storage(I, src) //No check needed, put everything inside + return 1 + +////////////////////////////// +//Distance procs +////////////////////////////// + +//Distance associates with all directions movement +/turf/proc/Distance(var/turf/T) + return get_dist(src,T) + +// This Distance proc assumes that only cardinal movement is +// possible. It results in more efficient (CPU-wise) pathing +// for bots and anything else that only moves in cardinal dirs. +/turf/proc/Distance_cardinal(turf/T) + if(!src || !T) return 0 + return abs(src.x - T.x) + abs(src.y - T.y) + +//////////////////////////////////////////////////// + +/turf/handle_fall(mob/faller, forced) + faller.lying = pick(90, 270) + if(!forced) + return + if(has_gravity(src)) + playsound(src, "bodyfall", 50, 1) + +/turf/handle_slip(mob/living/carbon/C, s_amount, w_amount, obj/O, lube) + if(has_gravity(src)) + var/obj/buckled_obj + var/oldlying = C.lying + if(C.buckled) + buckled_obj = C.buckled + if(!(lube&GALOSHES_DONT_HELP)) //can't slip while buckled unless it's lube. + return 0 + else + if(C.lying || !(C.status_flags & CANWEAKEN)) // can't slip unbuckled mob if they're lying or can't fall. + return 0 + if(C.m_intent=="walk" && (lube&NO_SLIP_WHEN_WALKING)) + return 0 + + C << "You slipped[ O ? " on the [O.name]" : ""]!" + + C.attack_log += "\[[time_stamp()]\] Slipped[O ? " on the [O.name]" : ""][(lube&SLIDE)? " (LUBE)" : ""]!" + playsound(C.loc, 'sound/misc/slip.ogg', 50, 1, -3) + + C.accident(C.l_hand) + C.accident(C.r_hand) + + var/olddir = C.dir + C.Stun(s_amount) + C.Weaken(w_amount) + C.stop_pulling() + if(buckled_obj) + buckled_obj.unbuckle_mob() + step(buckled_obj, olddir) + else if(lube&SLIDE) + for(var/i=1, i<5, i++) + spawn (i) + step(C, olddir) + C.spin(1,1) + if(C.lying != oldlying && lube) //did we actually fall? + var/dam_zone = pick("chest", "l_hand", "r_hand", "l_leg", "r_leg") + C.apply_damage(5, BRUTE, dam_zone) + return 1 + +/turf/singularity_act() + if(intact) + for(var/obj/O in contents) //this is for deleting things like wires contained in the turf + if(O.level != 1) + continue + if(O.invisibility == 101) + O.singularity_act() + ChangeTurf(src.baseturf) + return(2) + +/turf/proc/can_have_cabling() + return 1 + +/turf/proc/can_lay_cable() + return can_have_cabling() & !intact + +/turf/proc/visibilityChanged() + if(ticker) + cameranet.updateVisibility(src) + +/turf/indestructible + name = "wall" + icon = 'icons/turf/walls.dmi' + density = 1 + blocks_air = 1 + opacity = 1 + explosion_block = 50 + +/turf/indestructible/splashscreen + name = "Space Station 13" + icon = 'icons/misc/fullscreen.dmi' + icon_state = "title" + layer = FLY_LAYER + +/turf/indestructible/riveted + icon_state = "riveted" + +/turf/indestructible/riveted/New() + ..() + if(smooth) + smooth_icon(src) + icon_state = "" + +/turf/indestructible/riveted/uranium + icon = 'icons/turf/walls/uranium_wall.dmi' + icon_state = "uranium" + smooth = SMOOTH_TRUE + +/turf/indestructible/abductor + icon_state = "alien1" + +/turf/indestructible/fakeglass + name = "window" + icon_state = "fakewindows" + opacity = 0 + +/turf/indestructible/fakedoor + name = "Centcom Access" + icon = 'icons/obj/doors/airlocks/centcom/centcom.dmi' + icon_state = "fake_door" + +/turf/indestructible/rock + name = "dense rock" + desc = "An extremely densely-packed rock, most mining tools or explosives would never get through this." + icon = 'icons/turf/mining.dmi' + icon_state = "rock" + +/turf/indestructible/rock/snow + name = "mountainside" + desc = "An extremely densely-packed rock, sheeted over with centuries worth of ice and snow." + icon = 'icons/turf/walls.dmi' + icon_state = "snowrock" + +/turf/indestructible/rock/snow/ice + name = "iced rock" + desc = "Extremely densely-packed sheets of ice and rock, forged over the years of the harsh cold." + icon = 'icons/turf/walls.dmi' icon_state = "icerock" \ No newline at end of file diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index bfb2d195477..366889c8df8 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -1,638 +1,638 @@ -//admin verb groups - They can overlap if you so wish. Only one of each verb will exist in the verbs list regardless -var/list/admin_verbs_default = list( - /client/proc/toggleadminhelpsound, /*toggles whether we hear a sound when adminhelps/PMs are used*/ - /client/proc/toggleannouncelogin, /*toggles if an admin's login is announced during a round*/ - /client/proc/deadmin_self, /*destroys our own admin datum so we can play as a regular player*/ - /client/proc/cmd_admin_say, /*admin-only ooc chat*/ - /client/proc/hide_verbs, /*hides all our adminverbs*/ - /client/proc/hide_most_verbs, /*hides all our hideable adminverbs*/ - /client/proc/debug_variables, /*allows us to -see- the variables of any instance in the game. +VAREDIT needed to modify*/ - /client/proc/admin_memo, /*admin memo system. show/delete/write. +SERVER needed to delete admin memos of others*/ - /client/proc/deadchat, /*toggles deadchat on/off*/ - /client/proc/dsay, /*talk in deadchat using our ckey/fakekey*/ - /client/proc/toggleprayers, /*toggles prayers on/off*/ - /client/verb/toggleprayersounds, /*Toggles prayer sounds (HALLELUJAH!)*/ - /client/proc/toggle_hear_radio, /*toggles whether we hear the radio*/ - /client/proc/investigate_show, /*various admintools for investigation. Such as a singulo grief-log*/ - /client/proc/secrets, - /client/proc/reload_admins, - /client/proc/reestablish_db_connection,/*reattempt a connection to the database*/ - /client/proc/cmd_admin_pm_context, /*right-click adminPM interface*/ - /client/proc/cmd_admin_pm_panel, /*admin-pm list*/ - /client/proc/stop_sounds - ) -var/list/admin_verbs_admin = list( - /client/proc/player_panel_new, /*shows an interface for all players, with links to various panels*/ - /client/proc/invisimin, /*allows our mob to go invisible/visible*/ -// /datum/admins/proc/show_traitor_panel, /*interface which shows a mob's mind*/ -Removed due to rare practical use. Moved to debug verbs ~Errorage - /datum/admins/proc/show_player_panel, /*shows an interface for individual players, with various links (links require additional flags*/ - /client/proc/game_panel, /*game panel, allows to change game-mode etc*/ - /client/proc/check_ai_laws, /*shows AI and borg laws*/ - /datum/admins/proc/toggleooc, /*toggles ooc on/off for everyone*/ - /datum/admins/proc/toggleoocdead, /*toggles ooc on/off for everyone who is dead*/ - /datum/admins/proc/toggleenter, /*toggles whether people can join the current game*/ - /datum/admins/proc/toggleguests, /*toggles whether guests can join the current game*/ - /datum/admins/proc/announce, /*priority announce something to all clients.*/ - /datum/admins/proc/set_admin_notice,/*announcement all clients see when joining the server.*/ - /client/proc/admin_ghost, /*allows us to ghost/reenter body at will*/ - /client/proc/toggle_view_range, /*changes how far we can see*/ - /datum/admins/proc/view_txt_log, /*shows the server log (diary) for today*/ - /datum/admins/proc/view_atk_log, /*shows the server combat-log, doesn't do anything presently*/ - /client/proc/cmd_admin_subtle_message, /*send an message to somebody as a 'voice in their head'*/ - /client/proc/cmd_admin_delete, /*delete an instance/object/mob/etc*/ - /client/proc/cmd_admin_check_contents, /*displays the contents of an instance*/ - /client/proc/check_antagonists, /*shows all antags*/ - /datum/admins/proc/access_news_network, /*allows access of newscasters*/ - /client/proc/giveruntimelog, /*allows us to give access to runtime logs to somebody*/ - /client/proc/getruntimelog, /*allows us to access runtime logs to somebody*/ - /client/proc/getserverlog, /*allows us to fetch server logs (diary) for other days*/ - /client/proc/jumptocoord, /*we ghost and jump to a coordinate*/ - /client/proc/Getmob, /*teleports a mob to our location*/ - /client/proc/Getkey, /*teleports a mob with a certain ckey to our location*/ -// /client/proc/sendmob, /*sends a mob somewhere*/ -Removed due to it needing two sorting procs to work, which were executed every time an admin right-clicked. ~Errorage - /client/proc/jumptoarea, - /client/proc/jumptokey, /*allows us to jump to the location of a mob with a certain ckey*/ - /client/proc/jumptomob, /*allows us to jump to a specific mob*/ - /client/proc/jumptoturf, /*allows us to jump to a specific turf*/ - /client/proc/admin_call_shuttle, /*allows us to call the emergency shuttle*/ - /client/proc/admin_cancel_shuttle, /*allows us to cancel the emergency shuttle, sending it back to centcom*/ - /client/proc/cmd_admin_direct_narrate, /*send text directly to a player with no padding. Useful for narratives and fluff-text*/ - /client/proc/cmd_admin_world_narrate, /*sends text to all players with no padding*/ - /client/proc/cmd_admin_local_narrate, /*sends text to all mobs within view of atom*/ - /client/proc/cmd_admin_create_centcom_report, - /client/proc/toggle_antag_hud /*toggle display of the admin antag hud*/ - ) -var/list/admin_verbs_ban = list( - /client/proc/unban_panel, - /client/proc/DB_ban_panel, - /client/proc/stickybanpanel - ) -var/list/admin_verbs_sounds = list( - /client/proc/play_local_sound, - /client/proc/play_sound, - /client/proc/set_round_end_sound, - ) -var/list/admin_verbs_fun = list( - /client/proc/cmd_admin_dress, - /client/proc/cmd_admin_gib_self, - /client/proc/drop_bomb, - /client/proc/cinematic, - /client/proc/one_click_antag, - /client/proc/send_space_ninja, - /client/proc/cmd_admin_add_freeform_ai_law, - /client/proc/cmd_admin_add_random_ai_law, - /client/proc/object_say, - /client/proc/toggle_random_events, - /client/proc/set_ooc, - /client/proc/reset_ooc, - /client/proc/forceEvent, - /client/proc/bluespace_artillery, - /client/proc/admin_change_sec_level, - /client/proc/toggle_nuke - ) -var/list/admin_verbs_spawn = list( - /datum/admins/proc/spawn_atom, /*allows us to spawn instances*/ - /client/proc/respawn_character - ) -var/list/admin_verbs_server = list( - /datum/admins/proc/startnow, - /datum/admins/proc/restart, - /datum/admins/proc/end_round, - /datum/admins/proc/delay, - /datum/admins/proc/toggleaban, - /client/proc/toggle_log_hrefs, - /client/proc/everyone_random, - /datum/admins/proc/toggleAI, - /client/proc/cmd_admin_delete, /*delete an instance/object/mob/etc*/ - /client/proc/cmd_debug_del_all, - /client/proc/toggle_random_events, -#if SERVERTOOLS - /client/proc/forcerandomrotate, - /client/proc/adminchangemap, -#endif - /client/proc/panicbunker - - ) -var/list/admin_verbs_debug = list( - /client/proc/restart_controller, - /client/proc/cmd_admin_list_open_jobs, - /client/proc/Debug2, - /client/proc/cmd_debug_make_powernets, - /client/proc/cmd_debug_mob_lists, - /client/proc/cmd_admin_delete, - /client/proc/cmd_debug_del_all, - /client/proc/restart_controller, - /client/proc/enable_debug_verbs, - /client/proc/callproc, - /client/proc/callproc_datum, - /client/proc/SDQL2_query, - /client/proc/test_movable_UI, - /client/proc/test_snap_UI, - /client/proc/debugNatureMapGenerator, - /client/proc/check_bomb_impacts, - /proc/machine_upgrade, - /client/proc/populate_world, - /client/proc/cmd_display_del_log, - /client/proc/reset_latejoin_spawns, - /client/proc/create_outfits, - /client/proc/debug_huds - ) -var/list/admin_verbs_possess = list( - /proc/possess, - /proc/release - ) -var/list/admin_verbs_permissions = list( - /client/proc/edit_admin_permissions, - /client/proc/create_poll - ) -var/list/admin_verbs_rejuv = list( - /client/proc/respawn_character - ) - -//verbs which can be hidden - needs work -var/list/admin_verbs_hideable = list( - /client/proc/set_ooc, - /client/proc/reset_ooc, - /client/proc/deadmin_self, - /client/proc/deadchat, - /client/proc/toggleprayers, - /client/proc/toggle_hear_radio, - /datum/admins/proc/show_traitor_panel, - /datum/admins/proc/toggleenter, - /datum/admins/proc/toggleguests, - /datum/admins/proc/announce, - /datum/admins/proc/set_admin_notice, - /client/proc/admin_ghost, - /client/proc/toggle_view_range, - /datum/admins/proc/view_txt_log, - /datum/admins/proc/view_atk_log, - /client/proc/cmd_admin_subtle_message, - /client/proc/cmd_admin_check_contents, - /datum/admins/proc/access_news_network, - /client/proc/admin_call_shuttle, - /client/proc/admin_cancel_shuttle, - /client/proc/cmd_admin_direct_narrate, - /client/proc/cmd_admin_world_narrate, - /client/proc/cmd_admin_local_narrate, - /client/proc/play_local_sound, - /client/proc/play_sound, - /client/proc/set_round_end_sound, - /client/proc/cmd_admin_dress, - /client/proc/cmd_admin_gib_self, - /client/proc/drop_bomb, - /client/proc/cinematic, - /client/proc/send_space_ninja, - /client/proc/cmd_admin_add_freeform_ai_law, - /client/proc/cmd_admin_add_random_ai_law, - /client/proc/cmd_admin_create_centcom_report, - /client/proc/object_say, - /client/proc/toggle_random_events, - /client/proc/cmd_admin_add_random_ai_law, - /datum/admins/proc/startnow, - /datum/admins/proc/restart, - /datum/admins/proc/delay, - /datum/admins/proc/toggleaban, - /client/proc/toggle_log_hrefs, - /client/proc/everyone_random, - /datum/admins/proc/toggleAI, - /client/proc/restart_controller, - /client/proc/cmd_admin_list_open_jobs, - /client/proc/callproc, - /client/proc/callproc_datum, - /client/proc/Debug2, - /client/proc/reload_admins, - /client/proc/cmd_debug_make_powernets, - /client/proc/startSinglo, - /client/proc/cmd_debug_mob_lists, - /client/proc/cmd_debug_del_all, - /client/proc/enable_debug_verbs, - /proc/possess, - /proc/release, - /client/proc/reload_admins, - /client/proc/panicbunker, - /client/proc/admin_change_sec_level, - /client/proc/toggle_nuke, - /client/proc/cmd_display_del_log, - /client/proc/toggle_antag_hud, - /client/proc/debug_huds - ) - -/client/proc/add_admin_verbs() - if(holder) - control_freak = CONTROL_FREAK_SKIN | CONTROL_FREAK_MACROS - - var/rights = holder.rank.rights - verbs += admin_verbs_default - if(rights & R_BUILDMODE) verbs += /client/proc/togglebuildmodeself - if(rights & R_ADMIN) verbs += admin_verbs_admin - if(rights & R_BAN) verbs += admin_verbs_ban - if(rights & R_FUN) verbs += admin_verbs_fun - if(rights & R_SERVER) verbs += admin_verbs_server - if(rights & R_DEBUG) verbs += admin_verbs_debug - if(rights & R_POSSESS) verbs += admin_verbs_possess - if(rights & R_PERMISSIONS) verbs += admin_verbs_permissions - if(rights & R_STEALTH) verbs += /client/proc/stealth - if(rights & R_REJUVINATE) verbs += admin_verbs_rejuv - if(rights & R_SOUNDS) verbs += admin_verbs_sounds - if(rights & R_SPAWN) verbs += admin_verbs_spawn - - for(var/path in holder.rank.adds) - verbs += path - for(var/path in holder.rank.subs) - verbs -= path - -/client/proc/remove_admin_verbs() - verbs.Remove( - admin_verbs_default, - /client/proc/togglebuildmodeself, - admin_verbs_admin, - admin_verbs_ban, - admin_verbs_fun, - admin_verbs_server, - admin_verbs_debug, - admin_verbs_possess, - admin_verbs_permissions, - /client/proc/stealth, - admin_verbs_rejuv, - admin_verbs_sounds, - admin_verbs_spawn, - /*Debug verbs added by "show debug verbs"*/ - /client/proc/Cell, - /client/proc/do_not_use_these, - /client/proc/camera_view, - /client/proc/sec_camera_report, - /client/proc/intercom_view, - /client/proc/air_status, - /client/proc/atmosscan, - /client/proc/powerdebug, - /client/proc/count_objects_on_z_level, - /client/proc/count_objects_all, - /client/proc/cmd_assume_direct_control, - /client/proc/startSinglo, - /client/proc/fps, - /client/proc/cmd_admin_grantfullaccess, - /client/proc/cmd_admin_areatest, - /client/proc/readmin - ) - if(holder) - verbs.Remove(holder.rank.adds) - -/client/proc/hide_most_verbs()//Allows you to keep some functionality while hiding some verbs - set name = "Adminverbs - Hide Most" - set category = "Admin" - - verbs.Remove(/client/proc/hide_most_verbs, admin_verbs_hideable) - verbs += /client/proc/show_verbs - - src << "Most of your adminverbs have been hidden." - feedback_add_details("admin_verb","HMV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/hide_verbs() - set name = "Adminverbs - Hide All" - set category = "Admin" - - remove_admin_verbs() - verbs += /client/proc/show_verbs - - src << "Almost all of your adminverbs have been hidden." - feedback_add_details("admin_verb","TAVVH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/show_verbs() - set name = "Adminverbs - Show" - set category = "Admin" - - verbs -= /client/proc/show_verbs - add_admin_verbs() - - src << "All of your adminverbs are now visible." - feedback_add_details("admin_verb","TAVVS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - - - -/client/proc/admin_ghost() - set category = "Admin" - set name = "Aghost" - if(!holder) return - if(istype(mob,/mob/dead/observer)) - //re-enter - var/mob/dead/observer/ghost = mob - if (!ghost.can_reenter_corpse) - log_admin("[key_name(usr)] re-entered corpse") - message_admins("[key_name_admin(usr)] re-entered corpse") - ghost.can_reenter_corpse = 1 //just in-case. - ghost.reenter_corpse() - feedback_add_details("admin_verb","P") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - else if(istype(mob,/mob/new_player)) - src << "Error: Aghost: Can't admin-ghost whilst in the lobby. Join or Observe first." - else - //ghostize - log_admin("[key_name(usr)] admin ghosted") - message_admins("[key_name_admin(usr)] admin ghosted") - var/mob/body = mob - body.ghostize(1) - if(body && !body.key) - body.key = "@[key]" //Haaaaaaaack. But the people have spoken. If it breaks; blame adminbus - feedback_add_details("admin_verb","O") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/client/proc/invisimin() - set name = "Invisimin" - set category = "Admin" - set desc = "Toggles ghost-like invisibility (Don't abuse this)" - if(holder && mob) - if(mob.invisibility == INVISIBILITY_OBSERVER) - mob.invisibility = initial(mob.invisibility) - mob << "Invisimin off. Invisibility reset." - else - mob.invisibility = INVISIBILITY_OBSERVER - mob << "Invisimin on. You are now as invisible as a ghost." - -/client/proc/player_panel_new() - set name = "Player Panel" - set category = "Admin" - if(holder) - holder.player_panel_new() - feedback_add_details("admin_verb","PPN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/check_antagonists() - set name = "Check Antagonists" - set category = "Admin" - if(holder) - holder.check_antagonists() - log_admin("[key_name(usr)] checked antagonists.") //for tsar~ - feedback_add_details("admin_verb","CHA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/unban_panel() - set name = "Unban Panel" - set category = "Admin" - if(holder) - if(config.ban_legacy_system) - holder.unbanpanel() - else - holder.DB_ban_panel() - feedback_add_details("admin_verb","UBP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/game_panel() - set name = "Game Panel" - set category = "Admin" - if(holder) - holder.Game() - feedback_add_details("admin_verb","GP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/secrets() - set name = "Secrets" - set category = "Admin" - if (holder) - holder.Secrets() - feedback_add_details("admin_verb","S") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - - -/client/proc/findStealthKey(txt) - if(txt) - for(var/P in stealthminID) - if(stealthminID[P] == txt) - return P - txt = stealthminID[ckey] - return txt - -/client/proc/createStealthKey() - var/num = (rand(0,1000)) - var/i = 0 - while(i == 0) - i = 1 - for(var/P in stealthminID) - if(num == stealthminID[P]) - num++ - i = 0 - stealthminID["[ckey]"] = "@[num2text(num)]" - -/client/proc/stealth() - set category = "Admin" - set name = "Stealth Mode" - if(holder) - if(holder.fakekey) - holder.fakekey = null - else - var/new_key = ckeyEx(input("Enter your desired display name.", "Fake Key", key) as text|null) - if(!new_key) return - if(length(new_key) >= 26) - new_key = copytext(new_key, 1, 26) - holder.fakekey = new_key - createStealthKey() - log_admin("[key_name(usr)] has turned stealth mode [holder.fakekey ? "ON" : "OFF"]") - message_admins("[key_name_admin(usr)] has turned stealth mode [holder.fakekey ? "ON" : "OFF"]") - feedback_add_details("admin_verb","SM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/drop_bomb() - set category = "Special Verbs" - set name = "Drop Bomb" - set desc = "Cause an explosion of varying strength at your location." - - var/turf/epicenter = mob.loc - var/list/choices = list("Small Bomb", "Medium Bomb", "Big Bomb", "Custom Bomb") - var/choice = input("What size explosion would you like to produce?") in choices - switch(choice) - if(null) - return 0 - if("Small Bomb") - explosion(epicenter, 1, 2, 3, 3) - if("Medium Bomb") - explosion(epicenter, 2, 3, 4, 4) - if("Big Bomb") - explosion(epicenter, 3, 5, 7, 5) - if("Custom Bomb") - var/devastation_range = input("Devastation range (in tiles):") as null|num - if(devastation_range == null) - return - var/heavy_impact_range = input("Heavy impact range (in tiles):") as null|num - if(heavy_impact_range == null) - return - var/light_impact_range = input("Light impact range (in tiles):") as null|num - if(light_impact_range == null) - return - var/flash_range = input("Flash range (in tiles):") as null|num - if(flash_range == null) - return - explosion(epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range) - message_admins("[ckey] creating an admin explosion at [epicenter.loc].") - feedback_add_details("admin_verb","DB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/give_spell(mob/T in mob_list) - set category = "Fun" - set name = "Give Spell" - set desc = "Gives a spell to a mob." - - var/list/spell_list = list() - var/type_length = length("/obj/effect/proc_holder/spell") + 2 - for(var/A in spells) - spell_list[copytext("[A]", type_length)] = A - var/obj/effect/proc_holder/spell/S = input("Choose the spell to give to that guy", "ABRAKADABRA") as null|anything in spell_list - if(!S) - return - S = spell_list[S] - feedback_add_details("admin_verb","GS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] gave [key_name(T)] the spell [S].") - message_admins("[key_name_admin(usr)] gave [key_name(T)] the spell [S].") - if(T.mind) - T.mind.AddSpell(new S) - else - T.AddSpell(new S) - message_admins("Spells given to mindless mobs will not be transferred in mindswap or cloning!") - - -/client/proc/give_disease(mob/T in mob_list) - set category = "Fun" - set name = "Give Disease" - set desc = "Gives a Disease to a mob." - var/datum/disease/D = input("Choose the disease to give to that guy", "ACHOO") as null|anything in diseases - if(!D) return - T.ForceContractDisease(new D) - feedback_add_details("admin_verb","GD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] gave [key_name(T)] the disease [D].") - message_admins("[key_name_admin(usr)] gave [key_name(T)] the disease [D].") - -/client/proc/object_say(obj/O in world) - set category = "Special Verbs" - set name = "OSay" - set desc = "Makes an object say something." - var/message = input(usr, "What do you want the message to be?", "Make Sound") as text | null - if(!message) - return - var/templanguages = O.languages - O.languages |= ALL - O.say(message) - O.languages = templanguages - log_admin("[key_name(usr)] made [O] at [O.x], [O.y], [O.z] say \"[message]\"") - message_admins("[key_name_admin(usr)] made [O] at [O.x], [O.y], [O.z]. say \"[message]\"") - feedback_add_details("admin_verb","OS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! -/client/proc/togglebuildmodeself() - set name = "Toggle Build Mode Self" - set category = "Special Verbs" - if(src.mob) - togglebuildmode(src.mob) - feedback_add_details("admin_verb","TBMS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/deadmin_self() - set name = "De-admin self" - set category = "Admin" - - if(holder) - log_admin("[src] deadmined themself.") - message_admins("[src] deadmined themself.") - deadmin() - verbs += /client/proc/readmin - deadmins += ckey - src << "You are now a normal player." - feedback_add_details("admin_verb","DAS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/toggle_log_hrefs() - set name = "Toggle href logging" - set category = "Server" - if(!holder) return - if(config) - if(config.log_hrefs) - config.log_hrefs = 0 - src << "Stopped logging hrefs" - else - config.log_hrefs = 1 - src << "Started logging hrefs" - -/client/proc/check_ai_laws() - set name = "Check AI Laws" - set category = "Admin" - if(holder) - src.holder.output_ai_laws() - -/client/proc/readmin() - set name = "Re-admin self" - set category = "Admin" - set desc = "Regain your admin powers." - var/list/rank_names = list() - for(var/datum/admin_rank/R in admin_ranks) - rank_names[R.name] = R - var/datum/admins/D = admin_datums[ckey] - var/rank = null - if(config.admin_legacy_system) - //load text from file - var/list/Lines = file2list("config/admins.txt") - for(var/line in Lines) - var/list/splitline = text2list(line, " = ") - if(ckey(splitline[1]) == ckey) - if(splitline.len >= 2) - rank = ckeyEx(splitline[2]) - break - continue - else - if(!dbcon.IsConnected()) - message_admins("Warning, mysql database is not connected.") - src << "Warning, mysql database is not connected." - return - var/sql_ckey = sanitizeSQL(ckey) - var/DBQuery/query = dbcon.NewQuery("SELECT rank FROM [format_table_name("admin")] WHERE ckey = '[sql_ckey]'") - query.Execute() - while(query.NextRow()) - rank = ckeyEx(query.item[1]) - if(!D) - if(rank_names[rank] == null) - var/error_extra = "" - if(!config.admin_legacy_system) - error_extra = " Check mysql DB connection." - src << "Error while re-adminning, admin rank ([rank]) does not exist.[error_extra]" - WARNING("Error while re-adminning [src], admin rank ([rank]) does not exist.[error_extra]") - return - D = new(rank_names[rank],ckey) - var/client/C = directory[ckey] - D.associate(C) - message_admins("[src] re-adminned themselves.") - log_admin("[src] re-adminned themselves.") - deadmins -= ckey - feedback_add_details("admin_verb","RAS") - return - else - src << "You are already an admin." - verbs -= /client/proc/readmin - deadmins -= ckey - return - -/client/proc/populate_world(amount = 50 as num) - set name = "Populate World" - set category = "Debug" - set desc = "(\"Amount of mobs to create\") Populate the world with test mobs." - - if (amount > 0) - var/area/area - var/list/candidates - var/turf/simulated/floor/tile - var/j,k - var/mob/living/carbon/human/mob - - for (var/i = 1 to amount) - j = 100 - - do - area = pick(the_station_areas) - - if (area) - - candidates = get_area_turfs(area) - - if (candidates.len) - k = 100 - - do - tile = pick(candidates) - while ((!tile || !istype(tile)) && --k > 0) - - if (tile) - mob = new/mob/living/carbon/human/interactive(tile) - - testing("Spawned test mob with name \"[mob.name]\" at [tile.x],[tile.y],[tile.z]") - while (!area && --j > 0) +//admin verb groups - They can overlap if you so wish. Only one of each verb will exist in the verbs list regardless +var/list/admin_verbs_default = list( + /client/proc/toggleadminhelpsound, /*toggles whether we hear a sound when adminhelps/PMs are used*/ + /client/proc/toggleannouncelogin, /*toggles if an admin's login is announced during a round*/ + /client/proc/deadmin_self, /*destroys our own admin datum so we can play as a regular player*/ + /client/proc/cmd_admin_say, /*admin-only ooc chat*/ + /client/proc/hide_verbs, /*hides all our adminverbs*/ + /client/proc/hide_most_verbs, /*hides all our hideable adminverbs*/ + /client/proc/debug_variables, /*allows us to -see- the variables of any instance in the game. +VAREDIT needed to modify*/ + /client/proc/admin_memo, /*admin memo system. show/delete/write. +SERVER needed to delete admin memos of others*/ + /client/proc/deadchat, /*toggles deadchat on/off*/ + /client/proc/dsay, /*talk in deadchat using our ckey/fakekey*/ + /client/proc/toggleprayers, /*toggles prayers on/off*/ + /client/verb/toggleprayersounds, /*Toggles prayer sounds (HALLELUJAH!)*/ + /client/proc/toggle_hear_radio, /*toggles whether we hear the radio*/ + /client/proc/investigate_show, /*various admintools for investigation. Such as a singulo grief-log*/ + /client/proc/secrets, + /client/proc/reload_admins, + /client/proc/reestablish_db_connection,/*reattempt a connection to the database*/ + /client/proc/cmd_admin_pm_context, /*right-click adminPM interface*/ + /client/proc/cmd_admin_pm_panel, /*admin-pm list*/ + /client/proc/stop_sounds + ) +var/list/admin_verbs_admin = list( + /client/proc/player_panel_new, /*shows an interface for all players, with links to various panels*/ + /client/proc/invisimin, /*allows our mob to go invisible/visible*/ +// /datum/admins/proc/show_traitor_panel, /*interface which shows a mob's mind*/ -Removed due to rare practical use. Moved to debug verbs ~Errorage + /datum/admins/proc/show_player_panel, /*shows an interface for individual players, with various links (links require additional flags*/ + /client/proc/game_panel, /*game panel, allows to change game-mode etc*/ + /client/proc/check_ai_laws, /*shows AI and borg laws*/ + /datum/admins/proc/toggleooc, /*toggles ooc on/off for everyone*/ + /datum/admins/proc/toggleoocdead, /*toggles ooc on/off for everyone who is dead*/ + /datum/admins/proc/toggleenter, /*toggles whether people can join the current game*/ + /datum/admins/proc/toggleguests, /*toggles whether guests can join the current game*/ + /datum/admins/proc/announce, /*priority announce something to all clients.*/ + /datum/admins/proc/set_admin_notice,/*announcement all clients see when joining the server.*/ + /client/proc/admin_ghost, /*allows us to ghost/reenter body at will*/ + /client/proc/toggle_view_range, /*changes how far we can see*/ + /datum/admins/proc/view_txt_log, /*shows the server log (diary) for today*/ + /datum/admins/proc/view_atk_log, /*shows the server combat-log, doesn't do anything presently*/ + /client/proc/cmd_admin_subtle_message, /*send an message to somebody as a 'voice in their head'*/ + /client/proc/cmd_admin_delete, /*delete an instance/object/mob/etc*/ + /client/proc/cmd_admin_check_contents, /*displays the contents of an instance*/ + /client/proc/check_antagonists, /*shows all antags*/ + /datum/admins/proc/access_news_network, /*allows access of newscasters*/ + /client/proc/giveruntimelog, /*allows us to give access to runtime logs to somebody*/ + /client/proc/getruntimelog, /*allows us to access runtime logs to somebody*/ + /client/proc/getserverlog, /*allows us to fetch server logs (diary) for other days*/ + /client/proc/jumptocoord, /*we ghost and jump to a coordinate*/ + /client/proc/Getmob, /*teleports a mob to our location*/ + /client/proc/Getkey, /*teleports a mob with a certain ckey to our location*/ +// /client/proc/sendmob, /*sends a mob somewhere*/ -Removed due to it needing two sorting procs to work, which were executed every time an admin right-clicked. ~Errorage + /client/proc/jumptoarea, + /client/proc/jumptokey, /*allows us to jump to the location of a mob with a certain ckey*/ + /client/proc/jumptomob, /*allows us to jump to a specific mob*/ + /client/proc/jumptoturf, /*allows us to jump to a specific turf*/ + /client/proc/admin_call_shuttle, /*allows us to call the emergency shuttle*/ + /client/proc/admin_cancel_shuttle, /*allows us to cancel the emergency shuttle, sending it back to centcom*/ + /client/proc/cmd_admin_direct_narrate, /*send text directly to a player with no padding. Useful for narratives and fluff-text*/ + /client/proc/cmd_admin_world_narrate, /*sends text to all players with no padding*/ + /client/proc/cmd_admin_local_narrate, /*sends text to all mobs within view of atom*/ + /client/proc/cmd_admin_create_centcom_report, + /client/proc/toggle_antag_hud /*toggle display of the admin antag hud*/ + ) +var/list/admin_verbs_ban = list( + /client/proc/unban_panel, + /client/proc/DB_ban_panel, + /client/proc/stickybanpanel + ) +var/list/admin_verbs_sounds = list( + /client/proc/play_local_sound, + /client/proc/play_sound, + /client/proc/set_round_end_sound, + ) +var/list/admin_verbs_fun = list( + /client/proc/cmd_admin_dress, + /client/proc/cmd_admin_gib_self, + /client/proc/drop_bomb, + /client/proc/cinematic, + /client/proc/one_click_antag, + /client/proc/send_space_ninja, + /client/proc/cmd_admin_add_freeform_ai_law, + /client/proc/cmd_admin_add_random_ai_law, + /client/proc/object_say, + /client/proc/toggle_random_events, + /client/proc/set_ooc, + /client/proc/reset_ooc, + /client/proc/forceEvent, + /client/proc/bluespace_artillery, + /client/proc/admin_change_sec_level, + /client/proc/toggle_nuke + ) +var/list/admin_verbs_spawn = list( + /datum/admins/proc/spawn_atom, /*allows us to spawn instances*/ + /client/proc/respawn_character + ) +var/list/admin_verbs_server = list( + /datum/admins/proc/startnow, + /datum/admins/proc/restart, + /datum/admins/proc/end_round, + /datum/admins/proc/delay, + /datum/admins/proc/toggleaban, + /client/proc/toggle_log_hrefs, + /client/proc/everyone_random, + /datum/admins/proc/toggleAI, + /client/proc/cmd_admin_delete, /*delete an instance/object/mob/etc*/ + /client/proc/cmd_debug_del_all, + /client/proc/toggle_random_events, +#if SERVERTOOLS + /client/proc/forcerandomrotate, + /client/proc/adminchangemap, +#endif + /client/proc/panicbunker + + ) +var/list/admin_verbs_debug = list( + /client/proc/restart_controller, + /client/proc/cmd_admin_list_open_jobs, + /client/proc/Debug2, + /client/proc/cmd_debug_make_powernets, + /client/proc/cmd_debug_mob_lists, + /client/proc/cmd_admin_delete, + /client/proc/cmd_debug_del_all, + /client/proc/restart_controller, + /client/proc/enable_debug_verbs, + /client/proc/callproc, + /client/proc/callproc_datum, + /client/proc/SDQL2_query, + /client/proc/test_movable_UI, + /client/proc/test_snap_UI, + /client/proc/debugNatureMapGenerator, + /client/proc/check_bomb_impacts, + /proc/machine_upgrade, + /client/proc/populate_world, + /client/proc/cmd_display_del_log, + /client/proc/reset_latejoin_spawns, + /client/proc/create_outfits, + /client/proc/debug_huds + ) +var/list/admin_verbs_possess = list( + /proc/possess, + /proc/release + ) +var/list/admin_verbs_permissions = list( + /client/proc/edit_admin_permissions, + /client/proc/create_poll + ) +var/list/admin_verbs_rejuv = list( + /client/proc/respawn_character + ) + +//verbs which can be hidden - needs work +var/list/admin_verbs_hideable = list( + /client/proc/set_ooc, + /client/proc/reset_ooc, + /client/proc/deadmin_self, + /client/proc/deadchat, + /client/proc/toggleprayers, + /client/proc/toggle_hear_radio, + /datum/admins/proc/show_traitor_panel, + /datum/admins/proc/toggleenter, + /datum/admins/proc/toggleguests, + /datum/admins/proc/announce, + /datum/admins/proc/set_admin_notice, + /client/proc/admin_ghost, + /client/proc/toggle_view_range, + /datum/admins/proc/view_txt_log, + /datum/admins/proc/view_atk_log, + /client/proc/cmd_admin_subtle_message, + /client/proc/cmd_admin_check_contents, + /datum/admins/proc/access_news_network, + /client/proc/admin_call_shuttle, + /client/proc/admin_cancel_shuttle, + /client/proc/cmd_admin_direct_narrate, + /client/proc/cmd_admin_world_narrate, + /client/proc/cmd_admin_local_narrate, + /client/proc/play_local_sound, + /client/proc/play_sound, + /client/proc/set_round_end_sound, + /client/proc/cmd_admin_dress, + /client/proc/cmd_admin_gib_self, + /client/proc/drop_bomb, + /client/proc/cinematic, + /client/proc/send_space_ninja, + /client/proc/cmd_admin_add_freeform_ai_law, + /client/proc/cmd_admin_add_random_ai_law, + /client/proc/cmd_admin_create_centcom_report, + /client/proc/object_say, + /client/proc/toggle_random_events, + /client/proc/cmd_admin_add_random_ai_law, + /datum/admins/proc/startnow, + /datum/admins/proc/restart, + /datum/admins/proc/delay, + /datum/admins/proc/toggleaban, + /client/proc/toggle_log_hrefs, + /client/proc/everyone_random, + /datum/admins/proc/toggleAI, + /client/proc/restart_controller, + /client/proc/cmd_admin_list_open_jobs, + /client/proc/callproc, + /client/proc/callproc_datum, + /client/proc/Debug2, + /client/proc/reload_admins, + /client/proc/cmd_debug_make_powernets, + /client/proc/startSinglo, + /client/proc/cmd_debug_mob_lists, + /client/proc/cmd_debug_del_all, + /client/proc/enable_debug_verbs, + /proc/possess, + /proc/release, + /client/proc/reload_admins, + /client/proc/panicbunker, + /client/proc/admin_change_sec_level, + /client/proc/toggle_nuke, + /client/proc/cmd_display_del_log, + /client/proc/toggle_antag_hud, + /client/proc/debug_huds + ) + +/client/proc/add_admin_verbs() + if(holder) + control_freak = CONTROL_FREAK_SKIN | CONTROL_FREAK_MACROS + + var/rights = holder.rank.rights + verbs += admin_verbs_default + if(rights & R_BUILDMODE) verbs += /client/proc/togglebuildmodeself + if(rights & R_ADMIN) verbs += admin_verbs_admin + if(rights & R_BAN) verbs += admin_verbs_ban + if(rights & R_FUN) verbs += admin_verbs_fun + if(rights & R_SERVER) verbs += admin_verbs_server + if(rights & R_DEBUG) verbs += admin_verbs_debug + if(rights & R_POSSESS) verbs += admin_verbs_possess + if(rights & R_PERMISSIONS) verbs += admin_verbs_permissions + if(rights & R_STEALTH) verbs += /client/proc/stealth + if(rights & R_REJUVINATE) verbs += admin_verbs_rejuv + if(rights & R_SOUNDS) verbs += admin_verbs_sounds + if(rights & R_SPAWN) verbs += admin_verbs_spawn + + for(var/path in holder.rank.adds) + verbs += path + for(var/path in holder.rank.subs) + verbs -= path + +/client/proc/remove_admin_verbs() + verbs.Remove( + admin_verbs_default, + /client/proc/togglebuildmodeself, + admin_verbs_admin, + admin_verbs_ban, + admin_verbs_fun, + admin_verbs_server, + admin_verbs_debug, + admin_verbs_possess, + admin_verbs_permissions, + /client/proc/stealth, + admin_verbs_rejuv, + admin_verbs_sounds, + admin_verbs_spawn, + /*Debug verbs added by "show debug verbs"*/ + /client/proc/Cell, + /client/proc/do_not_use_these, + /client/proc/camera_view, + /client/proc/sec_camera_report, + /client/proc/intercom_view, + /client/proc/air_status, + /client/proc/atmosscan, + /client/proc/powerdebug, + /client/proc/count_objects_on_z_level, + /client/proc/count_objects_all, + /client/proc/cmd_assume_direct_control, + /client/proc/startSinglo, + /client/proc/fps, + /client/proc/cmd_admin_grantfullaccess, + /client/proc/cmd_admin_areatest, + /client/proc/readmin + ) + if(holder) + verbs.Remove(holder.rank.adds) + +/client/proc/hide_most_verbs()//Allows you to keep some functionality while hiding some verbs + set name = "Adminverbs - Hide Most" + set category = "Admin" + + verbs.Remove(/client/proc/hide_most_verbs, admin_verbs_hideable) + verbs += /client/proc/show_verbs + + src << "Most of your adminverbs have been hidden." + feedback_add_details("admin_verb","HMV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/hide_verbs() + set name = "Adminverbs - Hide All" + set category = "Admin" + + remove_admin_verbs() + verbs += /client/proc/show_verbs + + src << "Almost all of your adminverbs have been hidden." + feedback_add_details("admin_verb","TAVVH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/show_verbs() + set name = "Adminverbs - Show" + set category = "Admin" + + verbs -= /client/proc/show_verbs + add_admin_verbs() + + src << "All of your adminverbs are now visible." + feedback_add_details("admin_verb","TAVVS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + + + +/client/proc/admin_ghost() + set category = "Admin" + set name = "Aghost" + if(!holder) return + if(istype(mob,/mob/dead/observer)) + //re-enter + var/mob/dead/observer/ghost = mob + if (!ghost.can_reenter_corpse) + log_admin("[key_name(usr)] re-entered corpse") + message_admins("[key_name_admin(usr)] re-entered corpse") + ghost.can_reenter_corpse = 1 //just in-case. + ghost.reenter_corpse() + feedback_add_details("admin_verb","P") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + else if(istype(mob,/mob/new_player)) + src << "Error: Aghost: Can't admin-ghost whilst in the lobby. Join or Observe first." + else + //ghostize + log_admin("[key_name(usr)] admin ghosted") + message_admins("[key_name_admin(usr)] admin ghosted") + var/mob/body = mob + body.ghostize(1) + if(body && !body.key) + body.key = "@[key]" //Haaaaaaaack. But the people have spoken. If it breaks; blame adminbus + feedback_add_details("admin_verb","O") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/client/proc/invisimin() + set name = "Invisimin" + set category = "Admin" + set desc = "Toggles ghost-like invisibility (Don't abuse this)" + if(holder && mob) + if(mob.invisibility == INVISIBILITY_OBSERVER) + mob.invisibility = initial(mob.invisibility) + mob << "Invisimin off. Invisibility reset." + else + mob.invisibility = INVISIBILITY_OBSERVER + mob << "Invisimin on. You are now as invisible as a ghost." + +/client/proc/player_panel_new() + set name = "Player Panel" + set category = "Admin" + if(holder) + holder.player_panel_new() + feedback_add_details("admin_verb","PPN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/check_antagonists() + set name = "Check Antagonists" + set category = "Admin" + if(holder) + holder.check_antagonists() + log_admin("[key_name(usr)] checked antagonists.") //for tsar~ + feedback_add_details("admin_verb","CHA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/unban_panel() + set name = "Unban Panel" + set category = "Admin" + if(holder) + if(config.ban_legacy_system) + holder.unbanpanel() + else + holder.DB_ban_panel() + feedback_add_details("admin_verb","UBP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/game_panel() + set name = "Game Panel" + set category = "Admin" + if(holder) + holder.Game() + feedback_add_details("admin_verb","GP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/secrets() + set name = "Secrets" + set category = "Admin" + if (holder) + holder.Secrets() + feedback_add_details("admin_verb","S") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + + +/client/proc/findStealthKey(txt) + if(txt) + for(var/P in stealthminID) + if(stealthminID[P] == txt) + return P + txt = stealthminID[ckey] + return txt + +/client/proc/createStealthKey() + var/num = (rand(0,1000)) + var/i = 0 + while(i == 0) + i = 1 + for(var/P in stealthminID) + if(num == stealthminID[P]) + num++ + i = 0 + stealthminID["[ckey]"] = "@[num2text(num)]" + +/client/proc/stealth() + set category = "Admin" + set name = "Stealth Mode" + if(holder) + if(holder.fakekey) + holder.fakekey = null + else + var/new_key = ckeyEx(input("Enter your desired display name.", "Fake Key", key) as text|null) + if(!new_key) return + if(length(new_key) >= 26) + new_key = copytext(new_key, 1, 26) + holder.fakekey = new_key + createStealthKey() + log_admin("[key_name(usr)] has turned stealth mode [holder.fakekey ? "ON" : "OFF"]") + message_admins("[key_name_admin(usr)] has turned stealth mode [holder.fakekey ? "ON" : "OFF"]") + feedback_add_details("admin_verb","SM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/drop_bomb() + set category = "Special Verbs" + set name = "Drop Bomb" + set desc = "Cause an explosion of varying strength at your location." + + var/turf/epicenter = mob.loc + var/list/choices = list("Small Bomb", "Medium Bomb", "Big Bomb", "Custom Bomb") + var/choice = input("What size explosion would you like to produce?") in choices + switch(choice) + if(null) + return 0 + if("Small Bomb") + explosion(epicenter, 1, 2, 3, 3) + if("Medium Bomb") + explosion(epicenter, 2, 3, 4, 4) + if("Big Bomb") + explosion(epicenter, 3, 5, 7, 5) + if("Custom Bomb") + var/devastation_range = input("Devastation range (in tiles):") as null|num + if(devastation_range == null) + return + var/heavy_impact_range = input("Heavy impact range (in tiles):") as null|num + if(heavy_impact_range == null) + return + var/light_impact_range = input("Light impact range (in tiles):") as null|num + if(light_impact_range == null) + return + var/flash_range = input("Flash range (in tiles):") as null|num + if(flash_range == null) + return + explosion(epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range) + message_admins("[ckey] creating an admin explosion at [epicenter.loc].") + feedback_add_details("admin_verb","DB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/give_spell(mob/T in mob_list) + set category = "Fun" + set name = "Give Spell" + set desc = "Gives a spell to a mob." + + var/list/spell_list = list() + var/type_length = length("/obj/effect/proc_holder/spell") + 2 + for(var/A in spells) + spell_list[copytext("[A]", type_length)] = A + var/obj/effect/proc_holder/spell/S = input("Choose the spell to give to that guy", "ABRAKADABRA") as null|anything in spell_list + if(!S) + return + S = spell_list[S] + feedback_add_details("admin_verb","GS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_admin("[key_name(usr)] gave [key_name(T)] the spell [S].") + message_admins("[key_name_admin(usr)] gave [key_name(T)] the spell [S].") + if(T.mind) + T.mind.AddSpell(new S) + else + T.AddSpell(new S) + message_admins("Spells given to mindless mobs will not be transferred in mindswap or cloning!") + + +/client/proc/give_disease(mob/T in mob_list) + set category = "Fun" + set name = "Give Disease" + set desc = "Gives a Disease to a mob." + var/datum/disease/D = input("Choose the disease to give to that guy", "ACHOO") as null|anything in diseases + if(!D) return + T.ForceContractDisease(new D) + feedback_add_details("admin_verb","GD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_admin("[key_name(usr)] gave [key_name(T)] the disease [D].") + message_admins("[key_name_admin(usr)] gave [key_name(T)] the disease [D].") + +/client/proc/object_say(obj/O in world) + set category = "Special Verbs" + set name = "OSay" + set desc = "Makes an object say something." + var/message = input(usr, "What do you want the message to be?", "Make Sound") as text | null + if(!message) + return + var/templanguages = O.languages + O.languages |= ALL + O.say(message) + O.languages = templanguages + log_admin("[key_name(usr)] made [O] at [O.x], [O.y], [O.z] say \"[message]\"") + message_admins("[key_name_admin(usr)] made [O] at [O.x], [O.y], [O.z]. say \"[message]\"") + feedback_add_details("admin_verb","OS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +/client/proc/togglebuildmodeself() + set name = "Toggle Build Mode Self" + set category = "Special Verbs" + if(src.mob) + togglebuildmode(src.mob) + feedback_add_details("admin_verb","TBMS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/deadmin_self() + set name = "De-admin self" + set category = "Admin" + + if(holder) + log_admin("[src] deadmined themself.") + message_admins("[src] deadmined themself.") + deadmin() + verbs += /client/proc/readmin + deadmins += ckey + src << "You are now a normal player." + feedback_add_details("admin_verb","DAS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/toggle_log_hrefs() + set name = "Toggle href logging" + set category = "Server" + if(!holder) return + if(config) + if(config.log_hrefs) + config.log_hrefs = 0 + src << "Stopped logging hrefs" + else + config.log_hrefs = 1 + src << "Started logging hrefs" + +/client/proc/check_ai_laws() + set name = "Check AI Laws" + set category = "Admin" + if(holder) + src.holder.output_ai_laws() + +/client/proc/readmin() + set name = "Re-admin self" + set category = "Admin" + set desc = "Regain your admin powers." + var/list/rank_names = list() + for(var/datum/admin_rank/R in admin_ranks) + rank_names[R.name] = R + var/datum/admins/D = admin_datums[ckey] + var/rank = null + if(config.admin_legacy_system) + //load text from file + var/list/Lines = file2list("config/admins.txt") + for(var/line in Lines) + var/list/splitline = text2list(line, " = ") + if(ckey(splitline[1]) == ckey) + if(splitline.len >= 2) + rank = ckeyEx(splitline[2]) + break + continue + else + if(!dbcon.IsConnected()) + message_admins("Warning, mysql database is not connected.") + src << "Warning, mysql database is not connected." + return + var/sql_ckey = sanitizeSQL(ckey) + var/DBQuery/query = dbcon.NewQuery("SELECT rank FROM [format_table_name("admin")] WHERE ckey = '[sql_ckey]'") + query.Execute() + while(query.NextRow()) + rank = ckeyEx(query.item[1]) + if(!D) + if(rank_names[rank] == null) + var/error_extra = "" + if(!config.admin_legacy_system) + error_extra = " Check mysql DB connection." + src << "Error while re-adminning, admin rank ([rank]) does not exist.[error_extra]" + WARNING("Error while re-adminning [src], admin rank ([rank]) does not exist.[error_extra]") + return + D = new(rank_names[rank],ckey) + var/client/C = directory[ckey] + D.associate(C) + message_admins("[src] re-adminned themselves.") + log_admin("[src] re-adminned themselves.") + deadmins -= ckey + feedback_add_details("admin_verb","RAS") + return + else + src << "You are already an admin." + verbs -= /client/proc/readmin + deadmins -= ckey + return + +/client/proc/populate_world(amount = 50 as num) + set name = "Populate World" + set category = "Debug" + set desc = "(\"Amount of mobs to create\") Populate the world with test mobs." + + if (amount > 0) + var/area/area + var/list/candidates + var/turf/simulated/floor/tile + var/j,k + var/mob/living/carbon/human/mob + + for (var/i = 1 to amount) + j = 100 + + do + area = pick(the_station_areas) + + if (area) + + candidates = get_area_turfs(area) + + if (candidates.len) + k = 100 + + do + tile = pick(candidates) + while ((!tile || !istype(tile)) && --k > 0) + + if (tile) + mob = new/mob/living/carbon/human/interactive(tile) + + testing("Spawned test mob with name \"[mob.name]\" at [tile.x],[tile.y],[tile.z]") + while (!area && --j > 0) diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index 02de8309c59..69192126ea0 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -1,687 +1,687 @@ -/client/proc/Debug2() - set category = "Debug" - set name = "Debug-Game" - if(!check_rights(R_DEBUG)) return - - if(Debug2) - Debug2 = 0 - message_admins("[key_name(src)] toggled debugging off.") - log_admin("[key_name(src)] toggled debugging off.") - else - Debug2 = 1 - message_admins("[key_name(src)] toggled debugging on.") - log_admin("[key_name(src)] toggled debugging on.") - - feedback_add_details("admin_verb","DG2") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - - -/* 21st Sept 2010 -Updated by Skie -- Still not perfect but better! -Stuff you can't do: -Call proc /mob/proc/Dizzy() for some player -Because if you select a player mob as owner it tries to do the proc for -/mob/living/carbon/human/ instead. And that gives a run-time error. -But you can call procs that are of type /mob/living/carbon/human/proc/ for that player. -*/ - -/client/proc/callproc() - set category = "Debug" - set name = "Advanced ProcCall" - - if(!check_rights(R_DEBUG)) return - - spawn(0) - var/target = null - var/targetselected = 0 - var/returnval = null - var/class = null - - switch(alert("Proc owned by something?",,"Yes","No")) - if("Yes") - targetselected = 1 - if(src.holder && src.holder.marked_datum) - class = input("Proc owned by...","Owner",null) as null|anything in list("Obj","Mob","Area or Turf","Client","Marked datum ([holder.marked_datum.type])") - if(class == "Marked datum ([holder.marked_datum.type])") - class = "Marked datum" - else - class = input("Proc owned by...","Owner",null) as null|anything in list("Obj","Mob","Area or Turf","Client") - switch(class) - if("Obj") - target = input("Enter target:","Target",usr) as obj in world - if("Mob") - target = input("Enter target:","Target",usr) as mob in world - if("Area or Turf") - target = input("Enter target:","Target",usr.loc) as area|turf in world - if("Client") - var/list/keys = list() - for(var/client/C) - keys += C - target = input("Please, select a player!", "Selection", null, null) as null|anything in keys - if("Marked datum") - target = holder.marked_datum - else - return - if("No") - target = null - targetselected = 0 - - var/procname = input("Proc path, eg: /proc/fake_blood","Path:", null) as text|null - if(!procname) return - if(targetselected && !hascall(target,procname)) - usr << "Error: callproc(): target has no such call [procname]." - return - var/list/lst = get_callproc_args() - if(!lst) - return - - if(targetselected) - if(!target) - usr << "Error: callproc(): owner of proc no longer exists." - return - log_admin("[key_name(src)] called [target]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].") - returnval = call(target,procname)(arglist(lst)) // Pass the lst as an argument list to the proc - else - //this currently has no hascall protection. wasn't able to get it working. - log_admin("[key_name(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].") - returnval = call(procname)(arglist(lst)) // Pass the lst as an argument list to the proc - - usr << "[procname] returned: [returnval ? returnval : "null"]" - feedback_add_details("admin_verb","APC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/callproc_datum(A as null|area|mob|obj|turf) - set category = "Debug" - set name = "Atom ProcCall" - - if(!check_rights(R_DEBUG)) - return - - var/procname = input("Proc name, eg: fake_blood","Proc:", null) as text|null - if(!procname) - return - if(!hascall(A,procname)) - usr << "Error: callproc_datum(): target has no such call [procname]." - return - var/list/lst = get_callproc_args() - if(!lst) - return - - if(!A || !IsValidSrc(A)) - usr << "Error: callproc_datum(): owner of proc no longer exists." - return - log_admin("[key_name(src)] called [A]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].") - - spawn() - var/returnval = call(A,procname)(arglist(lst)) // Pass the lst as an argument list to the proc - usr << "[procname] returned: [returnval ? returnval : "null"]" - - feedback_add_details("admin_verb","DPC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/get_callproc_args() - var/argnum = input("Number of arguments","Number:",0) as num|null - if(!argnum && (argnum!=0)) return - - var/list/lst = list() - //TODO: make a list to store whether each argument was initialised as null. - //Reason: So we can abort the proccall if say, one of our arguments was a mob which no longer exists - //this will protect us from a fair few errors ~Carn - - while(argnum--) - var/class = null - // Make a list with each index containing one variable, to be given to the proc - if(src.holder && src.holder.marked_datum) - class = input("What kind of variable?","Variable Type") in list("text","num","type","reference","mob reference","icon","file","client","mob's area","Marked datum ([holder.marked_datum.type])","CANCEL") - if(holder.marked_datum && class == "Marked datum ([holder.marked_datum.type])") - class = "Marked datum" - else - class = input("What kind of variable?","Variable Type") in list("text","num","type","reference","mob reference","icon","file","client","mob's area","CANCEL") - switch(class) - if("CANCEL") - return null - - if("text") - lst += input("Enter new text:","Text",null) as text - - if("num") - lst += input("Enter new number:","Num",0) as num - - if("type") - lst += input("Enter type:","Type") in typesof(/obj,/mob,/area,/turf) - - if("reference") - lst += input("Select reference:","Reference",src) as mob|obj|turf|area in world - - if("mob reference") - lst += input("Select reference:","Reference",usr) as mob in world - - if("file") - lst += input("Pick file:","File") as file - - if("icon") - lst += input("Pick icon:","Icon") as icon - - if("client") - var/list/keys = list() - for(var/mob/M in world) - keys += M.client - lst += input("Please, select a player!", "Selection", null, null) as null|anything in keys - - if("mob's area") - var/mob/temp = input("Select mob", "Selection", usr) as mob in world - lst += temp.loc - if("Marked datum") - lst += holder.marked_datum - return lst - - -/client/proc/Cell() - set category = "Debug" - set name = "Air Status in Location" - if(!mob) - return - var/turf/T = mob.loc - - if (!( istype(T, /turf) )) - return - - 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" - - 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! - -/client/proc/cmd_admin_robotize(mob/M in mob_list) - set category = "Fun" - set name = "Make Robot" - - if(!ticker || !ticker.mode) - alert("Wait until the game starts") - return - if(istype(M, /mob/living/carbon/human)) - log_admin("[key_name(src)] has robotized [M.key].") - var/mob/living/carbon/human/H = M - spawn(10) - H.Robotize() - - else - alert("Invalid mob") - -/client/proc/cmd_admin_blobize(mob/M in mob_list) - set category = "Fun" - set name = "Make Blob" - - if(!ticker || !ticker.mode) - alert("Wait until the game starts") - return - if(istype(M, /mob/living/carbon/human)) - log_admin("[key_name(src)] has blobized [M.key].") - var/mob/living/carbon/human/H = M - spawn(10) - H.Blobize() - - else - alert("Invalid mob") - - -/client/proc/cmd_admin_animalize(mob/M in mob_list) - set category = "Fun" - set name = "Make Simple Animal" - - if(!ticker || !ticker.mode) - alert("Wait until the game starts") - return - - if(!M) - alert("That mob doesn't seem to exist, close the panel and try again.") - return - - if(istype(M, /mob/new_player)) - alert("The mob must not be a new_player.") - return - - log_admin("[key_name(src)] has animalized [M.key].") - spawn(10) - M.Animalize() - - -/client/proc/makepAI(turf/T in mob_list) - set category = "Fun" - set name = "Make pAI" - set desc = "Specify a location to spawn a pAI device, then specify a key to play that pAI" - - var/list/available = list() - for(var/mob/C in mob_list) - if(C.key) - available.Add(C) - var/mob/choice = input("Choose a player to play the pAI", "Spawn pAI") in available - if(!choice) - return 0 - if(!istype(choice, /mob/dead/observer)) - var/confirm = input("[choice.key] isn't ghosting right now. Are you sure you want to yank him out of them out of their body and place them in this pAI?", "Spawn pAI Confirmation", "No") in list("Yes", "No") - if(confirm != "Yes") - return 0 - var/obj/item/device/paicard/card = new(T) - var/mob/living/silicon/pai/pai = new(card) - pai.name = input(choice, "Enter your pAI name:", "pAI Name", "Personal AI") as text - pai.real_name = pai.name - pai.key = choice.key - card.setPersonality(pai) - for(var/datum/paiCandidate/candidate in SSpai.candidates) - if(candidate.key == choice.key) - SSpai.candidates.Remove(candidate) - feedback_add_details("admin_verb","MPAI") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_alienize(mob/M in mob_list) - set category = "Fun" - set name = "Make Alien" - - if(!ticker || !ticker.mode) - alert("Wait until the game starts") - return - if(ishuman(M)) - log_admin("[key_name(src)] has alienized [M.key].") - spawn(10) - M:Alienize() - feedback_add_details("admin_verb","MKAL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] made [key_name(M)] into an alien.") - message_admins("[key_name_admin(usr)] made [key_name(M)] into an alien.") - else - alert("Invalid mob") - -/client/proc/cmd_admin_slimeize(mob/M in mob_list) - set category = "Fun" - set name = "Make slime" - - if(!ticker || !ticker.mode) - alert("Wait until the game starts") - return - if(ishuman(M)) - log_admin("[key_name(src)] has slimeized [M.key].") - spawn(10) - M:slimeize() - feedback_add_details("admin_verb","MKMET") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] made [key_name(M)] into a slime.") - message_admins("[key_name_admin(usr)] made [key_name(M)] into a slime.") - else - alert("Invalid mob") - -var/list/TYPES_SHORTCUTS = list( - /obj/effect/decal/cleanable = "CLEANABLE", - /obj/item/device/radio/headset = "HEADSET", - /obj/item/clothing/head/helmet/space = "SPESSHELMET", - /obj/item/weapon/book/manual = "MANUAL", - /obj/item/weapon/reagent_containers/food/drinks = "DRINK", //longest paths comes first - /obj/item/weapon/reagent_containers/food = "FOOD", - /obj/item/weapon/reagent_containers = "REAGENT_CONTAINERS", - /obj/machinery/atmospherics = "ATMOS", - /obj/machinery/portable_atmospherics = "PORT_ATMOS", - /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/missile_rack = "MECHA_MISSILE_RACK", - /obj/item/mecha_parts/mecha_equipment = "MECHA_EQUIP", - /obj/item/organ/internal = "ORGAN_INT", -) - -var/global/list/g_fancy_list_of_types = null -/proc/get_fancy_list_of_types() - if (isnull(g_fancy_list_of_types)) //init - var/list/temp = sortList(subtypesof(/atom) - typesof(/area) - /atom/movable) - g_fancy_list_of_types = new(temp.len) - for(var/type in temp) - var/typename = "[type]" - for (var/tn in TYPES_SHORTCUTS) - if (copytext(typename,1, length("[tn]/")+1)=="[tn]/" /*findtextEx(typename,"[tn]/",1,2)*/ ) - typename = TYPES_SHORTCUTS[tn]+copytext(typename,length("[tn]/")) - break - g_fancy_list_of_types[typename] = type - return g_fancy_list_of_types - -/proc/filter_fancy_list(list/L, filter as text) - var/list/matches = new - for(var/key in L) - var/value = L[key] - if(findtext("[key]", filter) || findtext("[value]", filter)) - matches[key] = value - return matches - -//TODO: merge the vievars version into this or something maybe mayhaps -/client/proc/cmd_debug_del_all(object as text) - set category = "Debug" - set name = "Del-All" - - var/list/matches = get_fancy_list_of_types() - if (!isnull(object) && object!="") - matches = filter_fancy_list(matches, object) - - if(matches.len==0) - return - var/hsbitem = input(usr, "Choose an object to delete.", "Delete:") as null|anything in matches - if(hsbitem) - hsbitem = matches[hsbitem] - var/counter = 0 - for(var/atom/O in world) - if(istype(O, hsbitem)) - counter++ - qdel(O) - log_admin("[key_name(src)] has deleted all ([counter]) instances of [hsbitem].") - message_admins("[key_name_admin(src)] has deleted all ([counter]) instances of [hsbitem].", 0) - feedback_add_details("admin_verb","DELA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/client/proc/cmd_debug_make_powernets() - set category = "Debug" - set name = "Make Powernets" - SSmachine.makepowernets() - log_admin("[key_name(src)] has remade the powernet. makepowernets() called.") - message_admins("[key_name_admin(src)] has remade the powernets. makepowernets() called.", 0) - feedback_add_details("admin_verb","MPWN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_grantfullaccess(mob/M in mob_list) - set category = "Admin" - set name = "Grant Full Access" - - if(!ticker || !ticker.mode) - alert("Wait until the game starts") - return - if (istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - var/obj/item/worn = H.wear_id - var/obj/item/weapon/card/id/id = null - if(worn) - id = worn.GetID() - if(id) - id.icon_state = "gold" - id.access = get_all_accesses()+get_all_centcom_access()+get_all_syndicate_access() - else - id = new /obj/item/weapon/card/id/gold(H.loc) - id.access = get_all_accesses()+get_all_centcom_access()+get_all_syndicate_access() - id.registered_name = H.real_name - id.assignment = "Captain" - id.update_label() - - if(worn) - if(istype(worn,/obj/item/device/pda)) - worn:id = id - id.loc = worn - else if(istype(worn,/obj/item/weapon/storage/wallet)) - worn:front_id = id - id.loc = worn - worn.update_icon() - else - H.equip_to_slot(id,slot_wear_id) - - else - alert("Invalid mob") - feedback_add_details("admin_verb","GFA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(src)] has granted [M.key] full access.") - message_admins("[key_name_admin(usr)] has granted [M.key] full access.") - -/client/proc/cmd_assume_direct_control(mob/M in mob_list) - set category = "Admin" - set name = "Assume direct control" - set desc = "Direct intervention" - - if(M.ckey) - if(alert("This mob is being controlled by [M.ckey]. Are you sure you wish to assume control of it? [M.ckey] will be made a ghost.",,"Yes","No") != "Yes") - return - else - var/mob/dead/observer/ghost = new/mob/dead/observer(M,1) - ghost.ckey = M.ckey - message_admins("[key_name_admin(usr)] assumed direct control of [M].") - log_admin("[key_name(usr)] assumed direct control of [M].") - var/mob/adminmob = src.mob - M.ckey = src.ckey - if( isobserver(adminmob) ) - qdel(adminmob) - feedback_add_details("admin_verb","ADC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_areatest() - set category = "Mapping" - set name = "Test areas" - - var/list/areas_all = list() - var/list/areas_with_APC = list() - var/list/areas_with_air_alarm = list() - var/list/areas_with_RC = list() - var/list/areas_with_light = list() - var/list/areas_with_LS = list() - var/list/areas_with_intercom = list() - var/list/areas_with_camera = list() - - for(var/area/A in world) - if(!(A.type in areas_all)) - areas_all.Add(A.type) - - for(var/obj/machinery/power/apc/APC in apcs_list) - var/area/A = get_area(APC) - if(!(A.type in areas_with_APC)) - areas_with_APC.Add(A.type) - - for(var/obj/machinery/alarm/alarm in machines) - var/area/A = get_area(alarm) - if(!(A.type in areas_with_air_alarm)) - areas_with_air_alarm.Add(A.type) - - for(var/obj/machinery/requests_console/RC in machines) - var/area/A = get_area(RC) - if(!(A.type in areas_with_RC)) - areas_with_RC.Add(A.type) - - for(var/obj/machinery/light/L in machines) - var/area/A = get_area(L) - if(!(A.type in areas_with_light)) - areas_with_light.Add(A.type) - - for(var/obj/machinery/light_switch/LS in machines) - var/area/A = get_area(LS) - if(!(A.type in areas_with_LS)) - areas_with_LS.Add(A.type) - - for(var/obj/item/device/radio/intercom/I in machines) - var/area/A = get_area(I) - if(!(A.type in areas_with_intercom)) - areas_with_intercom.Add(A.type) - - for(var/obj/machinery/camera/C in machines) - var/area/A = get_area(C) - if(!(A.type in areas_with_camera)) - areas_with_camera.Add(A.type) - - var/list/areas_without_APC = areas_all - areas_with_APC - var/list/areas_without_air_alarm = areas_all - areas_with_air_alarm - var/list/areas_without_RC = areas_all - areas_with_RC - var/list/areas_without_light = areas_all - areas_with_light - var/list/areas_without_LS = areas_all - areas_with_LS - var/list/areas_without_intercom = areas_all - areas_with_intercom - var/list/areas_without_camera = areas_all - areas_with_camera - - world << "AREAS WITHOUT AN APC:" - for(var/areatype in areas_without_APC) - world << "* [areatype]" - - world << "AREAS WITHOUT AN AIR ALARM:" - for(var/areatype in areas_without_air_alarm) - world << "* [areatype]" - - world << "AREAS WITHOUT A REQUEST CONSOLE:" - for(var/areatype in areas_without_RC) - world << "* [areatype]" - - world << "AREAS WITHOUT ANY LIGHTS:" - for(var/areatype in areas_without_light) - world << "* [areatype]" - - world << "AREAS WITHOUT A LIGHT SWITCH:" - for(var/areatype in areas_without_LS) - world << "* [areatype]" - - world << "AREAS WITHOUT ANY INTERCOMS:" - for(var/areatype in areas_without_intercom) - world << "* [areatype]" - - world << "AREAS WITHOUT ANY CAMERAS:" - for(var/areatype in areas_without_camera) - world << "* [areatype]" - -/client/proc/cmd_admin_dress(mob/living/carbon/human/M in mob_list) - set category = "Fun" - set name = "Select equipment" - if(!ishuman(M)) - alert("Invalid mob") - return - //log_admin("[key_name(src)] has alienized [M.key].") - - - var/list/outfits = list("Naked","Custom","As Job...") - var/list/paths = subtypesof(/datum/outfit) - typesof(/datum/outfit/job) - for(var/path in paths) - var/datum/outfit/O = path //not much to initalize here but whatever - outfits[initial(O.name)] = path - - - var/dresscode = input("Select dress for [M]", "Robust quick dress shop") as null|anything in outfits - if (isnull(dresscode)) - return - - var/datum/job/jobdatum - if (dresscode == "As Job...") - var/jobname = input("Select job", "Robust quick dress shop") as null|anything in get_all_jobs() - if(isnull(jobname)) - return - jobdatum = SSjob.GetJob(jobname) - - - var/datum/outfit/custom = null - if (dresscode == "Custom") - var/list/custom_names = list() - for(var/datum/outfit/D in custom_outfits) - custom_names[D.name] = D - var/selected_name = input("Select outfit", "Robust quick dress shop") as null|anything in custom_names - custom = custom_names[selected_name] - if(isnull(custom)) - return - - feedback_add_details("admin_verb","SEQ") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - for (var/obj/item/I in M) - if (istype(I, /obj/item/weapon/implant)) - continue - qdel(I) - switch(dresscode) - if ("Naked") - //do nothing - if ("Custom") - //use custom one - M.equipOutfit(custom) - if ("As Job...") - if(jobdatum) - dresscode = jobdatum.title - M.job = jobdatum.title - jobdatum.equip(M) - - else - M.equipOutfit(outfits[dresscode]) - - - M.regenerate_icons() - - log_admin("[key_name(usr)] changed the equipment of [key_name(M)] to [dresscode].") - message_admins("[key_name_admin(usr)] changed the equipment of [key_name_admin(M)] to [dresscode]..") - return - -/client/proc/startSinglo() - - set category = "Debug" - set name = "Start Singularity" - set desc = "Sets up the singularity and all machines to get power flowing through the station" - - if(alert("Are you sure? This will start up the engine. Should only be used during debug!",,"Yes","No") != "Yes") - return - - for(var/obj/machinery/power/emitter/E in machines) - if(E.anchored) - E.active = 1 - - for(var/obj/machinery/field/generator/F in machines) - if(F.anchored) - F.Varedit_start = 1 - spawn(30) - for(var/obj/machinery/the_singularitygen/G in machines) - if(G.anchored) - var/obj/singularity/S = new /obj/singularity(get_turf(G), 50) -// qdel(G) - S.energy = 1750 - S.current_size = 7 - S.icon = 'icons/effects/224x224.dmi' - S.icon_state = "singularity_s7" - S.pixel_x = -96 - S.pixel_y = -96 - S.grav_pull = 0 - //S.consume_range = 3 - S.dissipate = 0 - //S.dissipate_delay = 10 - //S.dissipate_track = 0 - //S.dissipate_strength = 10 - - for(var/obj/machinery/power/rad_collector/Rad in machines) - if(Rad.anchored) - if(!Rad.P) - var/obj/item/weapon/tank/internals/plasma/Plasma = new/obj/item/weapon/tank/internals/plasma(Rad) - Plasma.air_contents.toxins = 70 - Rad.drainratio = 0 - Rad.P = Plasma - Plasma.loc = Rad - - if(!Rad.active) - Rad.toggle_power() - - for(var/obj/machinery/power/smes/SMES in machines) - if(SMES.anchored) - SMES.input_attempt = 1 - -/client/proc/cmd_debug_mob_lists() - set category = "Debug" - set name = "Debug Mob Lists" - set desc = "For when you just gotta know" - - switch(input("Which list?") in list("Players","Admins","Mobs","Living Mobs","Dead Mobs","Clients","Joined Clients")) - if("Players") - usr << list2text(player_list,",") - if("Admins") - usr << list2text(admins,",") - if("Mobs") - usr << list2text(mob_list,",") - if("Living Mobs") - usr << list2text(living_mob_list,",") - if("Dead Mobs") - usr << list2text(dead_mob_list,",") - if("Clients") - usr << list2text(clients,",") - if("Joined Clients") - usr << list2text(joined_player_list,",") - -/client/proc/cmd_display_del_log() - set category = "Debug" - set name = "Display del() Log" - set desc = "Displays a list of things that have failed to GC this round" - - var/dat = "List of things that failed to GC this round

" - - for(var/path in SSgarbage.didntgc) - dat += "[path] - [SSgarbage.didntgc[path]] times
" - - dat += "List of paths that did not return a qdel hint in Destroy()

" - for(var/path in SSgarbage.noqdelhint) - dat += "[path]
" - - usr << browse(dat, "window=dellog") - -/client/proc/debug_huds(i as num) - set category = "Debug" - set name = "Debug HUDs" - set desc = "Debug the data or antag HUDs" - - if(!holder) return - debug_variables(huds[i]) +/client/proc/Debug2() + set category = "Debug" + set name = "Debug-Game" + if(!check_rights(R_DEBUG)) return + + if(Debug2) + Debug2 = 0 + message_admins("[key_name(src)] toggled debugging off.") + log_admin("[key_name(src)] toggled debugging off.") + else + Debug2 = 1 + message_admins("[key_name(src)] toggled debugging on.") + log_admin("[key_name(src)] toggled debugging on.") + + feedback_add_details("admin_verb","DG2") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + + +/* 21st Sept 2010 +Updated by Skie -- Still not perfect but better! +Stuff you can't do: +Call proc /mob/proc/Dizzy() for some player +Because if you select a player mob as owner it tries to do the proc for +/mob/living/carbon/human/ instead. And that gives a run-time error. +But you can call procs that are of type /mob/living/carbon/human/proc/ for that player. +*/ + +/client/proc/callproc() + set category = "Debug" + set name = "Advanced ProcCall" + + if(!check_rights(R_DEBUG)) return + + spawn(0) + var/target = null + var/targetselected = 0 + var/returnval = null + var/class = null + + switch(alert("Proc owned by something?",,"Yes","No")) + if("Yes") + targetselected = 1 + if(src.holder && src.holder.marked_datum) + class = input("Proc owned by...","Owner",null) as null|anything in list("Obj","Mob","Area or Turf","Client","Marked datum ([holder.marked_datum.type])") + if(class == "Marked datum ([holder.marked_datum.type])") + class = "Marked datum" + else + class = input("Proc owned by...","Owner",null) as null|anything in list("Obj","Mob","Area or Turf","Client") + switch(class) + if("Obj") + target = input("Enter target:","Target",usr) as obj in world + if("Mob") + target = input("Enter target:","Target",usr) as mob in world + if("Area or Turf") + target = input("Enter target:","Target",usr.loc) as area|turf in world + if("Client") + var/list/keys = list() + for(var/client/C) + keys += C + target = input("Please, select a player!", "Selection", null, null) as null|anything in keys + if("Marked datum") + target = holder.marked_datum + else + return + if("No") + target = null + targetselected = 0 + + var/procname = input("Proc path, eg: /proc/fake_blood","Path:", null) as text|null + if(!procname) return + if(targetselected && !hascall(target,procname)) + usr << "Error: callproc(): target has no such call [procname]." + return + var/list/lst = get_callproc_args() + if(!lst) + return + + if(targetselected) + if(!target) + usr << "Error: callproc(): owner of proc no longer exists." + return + log_admin("[key_name(src)] called [target]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].") + returnval = call(target,procname)(arglist(lst)) // Pass the lst as an argument list to the proc + else + //this currently has no hascall protection. wasn't able to get it working. + log_admin("[key_name(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].") + returnval = call(procname)(arglist(lst)) // Pass the lst as an argument list to the proc + + usr << "[procname] returned: [returnval ? returnval : "null"]" + feedback_add_details("admin_verb","APC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/callproc_datum(A as null|area|mob|obj|turf) + set category = "Debug" + set name = "Atom ProcCall" + + if(!check_rights(R_DEBUG)) + return + + var/procname = input("Proc name, eg: fake_blood","Proc:", null) as text|null + if(!procname) + return + if(!hascall(A,procname)) + usr << "Error: callproc_datum(): target has no such call [procname]." + return + var/list/lst = get_callproc_args() + if(!lst) + return + + if(!A || !IsValidSrc(A)) + usr << "Error: callproc_datum(): owner of proc no longer exists." + return + log_admin("[key_name(src)] called [A]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].") + + spawn() + var/returnval = call(A,procname)(arglist(lst)) // Pass the lst as an argument list to the proc + usr << "[procname] returned: [returnval ? returnval : "null"]" + + feedback_add_details("admin_verb","DPC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/get_callproc_args() + var/argnum = input("Number of arguments","Number:",0) as num|null + if(!argnum && (argnum!=0)) return + + var/list/lst = list() + //TODO: make a list to store whether each argument was initialised as null. + //Reason: So we can abort the proccall if say, one of our arguments was a mob which no longer exists + //this will protect us from a fair few errors ~Carn + + while(argnum--) + var/class = null + // Make a list with each index containing one variable, to be given to the proc + if(src.holder && src.holder.marked_datum) + class = input("What kind of variable?","Variable Type") in list("text","num","type","reference","mob reference","icon","file","client","mob's area","Marked datum ([holder.marked_datum.type])","CANCEL") + if(holder.marked_datum && class == "Marked datum ([holder.marked_datum.type])") + class = "Marked datum" + else + class = input("What kind of variable?","Variable Type") in list("text","num","type","reference","mob reference","icon","file","client","mob's area","CANCEL") + switch(class) + if("CANCEL") + return null + + if("text") + lst += input("Enter new text:","Text",null) as text + + if("num") + lst += input("Enter new number:","Num",0) as num + + if("type") + lst += input("Enter type:","Type") in typesof(/obj,/mob,/area,/turf) + + if("reference") + lst += input("Select reference:","Reference",src) as mob|obj|turf|area in world + + if("mob reference") + lst += input("Select reference:","Reference",usr) as mob in world + + if("file") + lst += input("Pick file:","File") as file + + if("icon") + lst += input("Pick icon:","Icon") as icon + + if("client") + var/list/keys = list() + for(var/mob/M in world) + keys += M.client + lst += input("Please, select a player!", "Selection", null, null) as null|anything in keys + + if("mob's area") + var/mob/temp = input("Select mob", "Selection", usr) as mob in world + lst += temp.loc + if("Marked datum") + lst += holder.marked_datum + return lst + + +/client/proc/Cell() + set category = "Debug" + set name = "Air Status in Location" + if(!mob) + return + var/turf/T = mob.loc + + if (!( istype(T, /turf) )) + return + + 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" + + 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! + +/client/proc/cmd_admin_robotize(mob/M in mob_list) + set category = "Fun" + set name = "Make Robot" + + if(!ticker || !ticker.mode) + alert("Wait until the game starts") + return + if(istype(M, /mob/living/carbon/human)) + log_admin("[key_name(src)] has robotized [M.key].") + var/mob/living/carbon/human/H = M + spawn(10) + H.Robotize() + + else + alert("Invalid mob") + +/client/proc/cmd_admin_blobize(mob/M in mob_list) + set category = "Fun" + set name = "Make Blob" + + if(!ticker || !ticker.mode) + alert("Wait until the game starts") + return + if(istype(M, /mob/living/carbon/human)) + log_admin("[key_name(src)] has blobized [M.key].") + var/mob/living/carbon/human/H = M + spawn(10) + H.Blobize() + + else + alert("Invalid mob") + + +/client/proc/cmd_admin_animalize(mob/M in mob_list) + set category = "Fun" + set name = "Make Simple Animal" + + if(!ticker || !ticker.mode) + alert("Wait until the game starts") + return + + if(!M) + alert("That mob doesn't seem to exist, close the panel and try again.") + return + + if(istype(M, /mob/new_player)) + alert("The mob must not be a new_player.") + return + + log_admin("[key_name(src)] has animalized [M.key].") + spawn(10) + M.Animalize() + + +/client/proc/makepAI(turf/T in mob_list) + set category = "Fun" + set name = "Make pAI" + set desc = "Specify a location to spawn a pAI device, then specify a key to play that pAI" + + var/list/available = list() + for(var/mob/C in mob_list) + if(C.key) + available.Add(C) + var/mob/choice = input("Choose a player to play the pAI", "Spawn pAI") in available + if(!choice) + return 0 + if(!istype(choice, /mob/dead/observer)) + var/confirm = input("[choice.key] isn't ghosting right now. Are you sure you want to yank him out of them out of their body and place them in this pAI?", "Spawn pAI Confirmation", "No") in list("Yes", "No") + if(confirm != "Yes") + return 0 + var/obj/item/device/paicard/card = new(T) + var/mob/living/silicon/pai/pai = new(card) + pai.name = input(choice, "Enter your pAI name:", "pAI Name", "Personal AI") as text + pai.real_name = pai.name + pai.key = choice.key + card.setPersonality(pai) + for(var/datum/paiCandidate/candidate in SSpai.candidates) + if(candidate.key == choice.key) + SSpai.candidates.Remove(candidate) + feedback_add_details("admin_verb","MPAI") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_alienize(mob/M in mob_list) + set category = "Fun" + set name = "Make Alien" + + if(!ticker || !ticker.mode) + alert("Wait until the game starts") + return + if(ishuman(M)) + log_admin("[key_name(src)] has alienized [M.key].") + spawn(10) + M:Alienize() + feedback_add_details("admin_verb","MKAL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_admin("[key_name(usr)] made [key_name(M)] into an alien.") + message_admins("[key_name_admin(usr)] made [key_name(M)] into an alien.") + else + alert("Invalid mob") + +/client/proc/cmd_admin_slimeize(mob/M in mob_list) + set category = "Fun" + set name = "Make slime" + + if(!ticker || !ticker.mode) + alert("Wait until the game starts") + return + if(ishuman(M)) + log_admin("[key_name(src)] has slimeized [M.key].") + spawn(10) + M:slimeize() + feedback_add_details("admin_verb","MKMET") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_admin("[key_name(usr)] made [key_name(M)] into a slime.") + message_admins("[key_name_admin(usr)] made [key_name(M)] into a slime.") + else + alert("Invalid mob") + +var/list/TYPES_SHORTCUTS = list( + /obj/effect/decal/cleanable = "CLEANABLE", + /obj/item/device/radio/headset = "HEADSET", + /obj/item/clothing/head/helmet/space = "SPESSHELMET", + /obj/item/weapon/book/manual = "MANUAL", + /obj/item/weapon/reagent_containers/food/drinks = "DRINK", //longest paths comes first + /obj/item/weapon/reagent_containers/food = "FOOD", + /obj/item/weapon/reagent_containers = "REAGENT_CONTAINERS", + /obj/machinery/atmospherics = "ATMOS", + /obj/machinery/portable_atmospherics = "PORT_ATMOS", + /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/missile_rack = "MECHA_MISSILE_RACK", + /obj/item/mecha_parts/mecha_equipment = "MECHA_EQUIP", + /obj/item/organ/internal = "ORGAN_INT", +) + +var/global/list/g_fancy_list_of_types = null +/proc/get_fancy_list_of_types() + if (isnull(g_fancy_list_of_types)) //init + var/list/temp = sortList(subtypesof(/atom) - typesof(/area) - /atom/movable) + g_fancy_list_of_types = new(temp.len) + for(var/type in temp) + var/typename = "[type]" + for (var/tn in TYPES_SHORTCUTS) + if (copytext(typename,1, length("[tn]/")+1)=="[tn]/" /*findtextEx(typename,"[tn]/",1,2)*/ ) + typename = TYPES_SHORTCUTS[tn]+copytext(typename,length("[tn]/")) + break + g_fancy_list_of_types[typename] = type + return g_fancy_list_of_types + +/proc/filter_fancy_list(list/L, filter as text) + var/list/matches = new + for(var/key in L) + var/value = L[key] + if(findtext("[key]", filter) || findtext("[value]", filter)) + matches[key] = value + return matches + +//TODO: merge the vievars version into this or something maybe mayhaps +/client/proc/cmd_debug_del_all(object as text) + set category = "Debug" + set name = "Del-All" + + var/list/matches = get_fancy_list_of_types() + if (!isnull(object) && object!="") + matches = filter_fancy_list(matches, object) + + if(matches.len==0) + return + var/hsbitem = input(usr, "Choose an object to delete.", "Delete:") as null|anything in matches + if(hsbitem) + hsbitem = matches[hsbitem] + var/counter = 0 + for(var/atom/O in world) + if(istype(O, hsbitem)) + counter++ + qdel(O) + log_admin("[key_name(src)] has deleted all ([counter]) instances of [hsbitem].") + message_admins("[key_name_admin(src)] has deleted all ([counter]) instances of [hsbitem].", 0) + feedback_add_details("admin_verb","DELA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/client/proc/cmd_debug_make_powernets() + set category = "Debug" + set name = "Make Powernets" + SSmachine.makepowernets() + log_admin("[key_name(src)] has remade the powernet. makepowernets() called.") + message_admins("[key_name_admin(src)] has remade the powernets. makepowernets() called.", 0) + feedback_add_details("admin_verb","MPWN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_grantfullaccess(mob/M in mob_list) + set category = "Admin" + set name = "Grant Full Access" + + if(!ticker || !ticker.mode) + alert("Wait until the game starts") + return + if (istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + var/obj/item/worn = H.wear_id + var/obj/item/weapon/card/id/id = null + if(worn) + id = worn.GetID() + if(id) + id.icon_state = "gold" + id.access = get_all_accesses()+get_all_centcom_access()+get_all_syndicate_access() + else + id = new /obj/item/weapon/card/id/gold(H.loc) + id.access = get_all_accesses()+get_all_centcom_access()+get_all_syndicate_access() + id.registered_name = H.real_name + id.assignment = "Captain" + id.update_label() + + if(worn) + if(istype(worn,/obj/item/device/pda)) + worn:id = id + id.loc = worn + else if(istype(worn,/obj/item/weapon/storage/wallet)) + worn:front_id = id + id.loc = worn + worn.update_icon() + else + H.equip_to_slot(id,slot_wear_id) + + else + alert("Invalid mob") + feedback_add_details("admin_verb","GFA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_admin("[key_name(src)] has granted [M.key] full access.") + message_admins("[key_name_admin(usr)] has granted [M.key] full access.") + +/client/proc/cmd_assume_direct_control(mob/M in mob_list) + set category = "Admin" + set name = "Assume direct control" + set desc = "Direct intervention" + + if(M.ckey) + if(alert("This mob is being controlled by [M.ckey]. Are you sure you wish to assume control of it? [M.ckey] will be made a ghost.",,"Yes","No") != "Yes") + return + else + var/mob/dead/observer/ghost = new/mob/dead/observer(M,1) + ghost.ckey = M.ckey + message_admins("[key_name_admin(usr)] assumed direct control of [M].") + log_admin("[key_name(usr)] assumed direct control of [M].") + var/mob/adminmob = src.mob + M.ckey = src.ckey + if( isobserver(adminmob) ) + qdel(adminmob) + feedback_add_details("admin_verb","ADC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_areatest() + set category = "Mapping" + set name = "Test areas" + + var/list/areas_all = list() + var/list/areas_with_APC = list() + var/list/areas_with_air_alarm = list() + var/list/areas_with_RC = list() + var/list/areas_with_light = list() + var/list/areas_with_LS = list() + var/list/areas_with_intercom = list() + var/list/areas_with_camera = list() + + for(var/area/A in world) + if(!(A.type in areas_all)) + areas_all.Add(A.type) + + for(var/obj/machinery/power/apc/APC in apcs_list) + var/area/A = get_area(APC) + if(!(A.type in areas_with_APC)) + areas_with_APC.Add(A.type) + + for(var/obj/machinery/alarm/alarm in machines) + var/area/A = get_area(alarm) + if(!(A.type in areas_with_air_alarm)) + areas_with_air_alarm.Add(A.type) + + for(var/obj/machinery/requests_console/RC in machines) + var/area/A = get_area(RC) + if(!(A.type in areas_with_RC)) + areas_with_RC.Add(A.type) + + for(var/obj/machinery/light/L in machines) + var/area/A = get_area(L) + if(!(A.type in areas_with_light)) + areas_with_light.Add(A.type) + + for(var/obj/machinery/light_switch/LS in machines) + var/area/A = get_area(LS) + if(!(A.type in areas_with_LS)) + areas_with_LS.Add(A.type) + + for(var/obj/item/device/radio/intercom/I in machines) + var/area/A = get_area(I) + if(!(A.type in areas_with_intercom)) + areas_with_intercom.Add(A.type) + + for(var/obj/machinery/camera/C in machines) + var/area/A = get_area(C) + if(!(A.type in areas_with_camera)) + areas_with_camera.Add(A.type) + + var/list/areas_without_APC = areas_all - areas_with_APC + var/list/areas_without_air_alarm = areas_all - areas_with_air_alarm + var/list/areas_without_RC = areas_all - areas_with_RC + var/list/areas_without_light = areas_all - areas_with_light + var/list/areas_without_LS = areas_all - areas_with_LS + var/list/areas_without_intercom = areas_all - areas_with_intercom + var/list/areas_without_camera = areas_all - areas_with_camera + + world << "AREAS WITHOUT AN APC:" + for(var/areatype in areas_without_APC) + world << "* [areatype]" + + world << "AREAS WITHOUT AN AIR ALARM:" + for(var/areatype in areas_without_air_alarm) + world << "* [areatype]" + + world << "AREAS WITHOUT A REQUEST CONSOLE:" + for(var/areatype in areas_without_RC) + world << "* [areatype]" + + world << "AREAS WITHOUT ANY LIGHTS:" + for(var/areatype in areas_without_light) + world << "* [areatype]" + + world << "AREAS WITHOUT A LIGHT SWITCH:" + for(var/areatype in areas_without_LS) + world << "* [areatype]" + + world << "AREAS WITHOUT ANY INTERCOMS:" + for(var/areatype in areas_without_intercom) + world << "* [areatype]" + + world << "AREAS WITHOUT ANY CAMERAS:" + for(var/areatype in areas_without_camera) + world << "* [areatype]" + +/client/proc/cmd_admin_dress(mob/living/carbon/human/M in mob_list) + set category = "Fun" + set name = "Select equipment" + if(!ishuman(M)) + alert("Invalid mob") + return + //log_admin("[key_name(src)] has alienized [M.key].") + + + var/list/outfits = list("Naked","Custom","As Job...") + var/list/paths = subtypesof(/datum/outfit) - typesof(/datum/outfit/job) + for(var/path in paths) + var/datum/outfit/O = path //not much to initalize here but whatever + outfits[initial(O.name)] = path + + + var/dresscode = input("Select dress for [M]", "Robust quick dress shop") as null|anything in outfits + if (isnull(dresscode)) + return + + var/datum/job/jobdatum + if (dresscode == "As Job...") + var/jobname = input("Select job", "Robust quick dress shop") as null|anything in get_all_jobs() + if(isnull(jobname)) + return + jobdatum = SSjob.GetJob(jobname) + + + var/datum/outfit/custom = null + if (dresscode == "Custom") + var/list/custom_names = list() + for(var/datum/outfit/D in custom_outfits) + custom_names[D.name] = D + var/selected_name = input("Select outfit", "Robust quick dress shop") as null|anything in custom_names + custom = custom_names[selected_name] + if(isnull(custom)) + return + + feedback_add_details("admin_verb","SEQ") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + for (var/obj/item/I in M) + if (istype(I, /obj/item/weapon/implant)) + continue + qdel(I) + switch(dresscode) + if ("Naked") + //do nothing + if ("Custom") + //use custom one + M.equipOutfit(custom) + if ("As Job...") + if(jobdatum) + dresscode = jobdatum.title + M.job = jobdatum.title + jobdatum.equip(M) + + else + M.equipOutfit(outfits[dresscode]) + + + M.regenerate_icons() + + log_admin("[key_name(usr)] changed the equipment of [key_name(M)] to [dresscode].") + message_admins("[key_name_admin(usr)] changed the equipment of [key_name_admin(M)] to [dresscode]..") + return + +/client/proc/startSinglo() + + set category = "Debug" + set name = "Start Singularity" + set desc = "Sets up the singularity and all machines to get power flowing through the station" + + if(alert("Are you sure? This will start up the engine. Should only be used during debug!",,"Yes","No") != "Yes") + return + + for(var/obj/machinery/power/emitter/E in machines) + if(E.anchored) + E.active = 1 + + for(var/obj/machinery/field/generator/F in machines) + if(F.anchored) + F.Varedit_start = 1 + spawn(30) + for(var/obj/machinery/the_singularitygen/G in machines) + if(G.anchored) + var/obj/singularity/S = new /obj/singularity(get_turf(G), 50) +// qdel(G) + S.energy = 1750 + S.current_size = 7 + S.icon = 'icons/effects/224x224.dmi' + S.icon_state = "singularity_s7" + S.pixel_x = -96 + S.pixel_y = -96 + S.grav_pull = 0 + //S.consume_range = 3 + S.dissipate = 0 + //S.dissipate_delay = 10 + //S.dissipate_track = 0 + //S.dissipate_strength = 10 + + for(var/obj/machinery/power/rad_collector/Rad in machines) + if(Rad.anchored) + if(!Rad.P) + var/obj/item/weapon/tank/internals/plasma/Plasma = new/obj/item/weapon/tank/internals/plasma(Rad) + Plasma.air_contents.toxins = 70 + Rad.drainratio = 0 + Rad.P = Plasma + Plasma.loc = Rad + + if(!Rad.active) + Rad.toggle_power() + + for(var/obj/machinery/power/smes/SMES in machines) + if(SMES.anchored) + SMES.input_attempt = 1 + +/client/proc/cmd_debug_mob_lists() + set category = "Debug" + set name = "Debug Mob Lists" + set desc = "For when you just gotta know" + + switch(input("Which list?") in list("Players","Admins","Mobs","Living Mobs","Dead Mobs","Clients","Joined Clients")) + if("Players") + usr << list2text(player_list,",") + if("Admins") + usr << list2text(admins,",") + if("Mobs") + usr << list2text(mob_list,",") + if("Living Mobs") + usr << list2text(living_mob_list,",") + if("Dead Mobs") + usr << list2text(dead_mob_list,",") + if("Clients") + usr << list2text(clients,",") + if("Joined Clients") + usr << list2text(joined_player_list,",") + +/client/proc/cmd_display_del_log() + set category = "Debug" + set name = "Display del() Log" + set desc = "Displays a list of things that have failed to GC this round" + + var/dat = "List of things that failed to GC this round

" + + for(var/path in SSgarbage.didntgc) + dat += "[path] - [SSgarbage.didntgc[path]] times
" + + dat += "List of paths that did not return a qdel hint in Destroy()

" + for(var/path in SSgarbage.noqdelhint) + dat += "[path]
" + + usr << browse(dat, "window=dellog") + +/client/proc/debug_huds(i as num) + set category = "Debug" + set name = "Debug HUDs" + set desc = "Debug the data or antag HUDs" + + if(!holder) return + debug_variables(huds[i]) diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm index 122e85d711f..a9e6582cac6 100644 --- a/code/modules/admin/verbs/mapping.dm +++ b/code/modules/admin/verbs/mapping.dm @@ -1,261 +1,261 @@ -//- Are all the floors with or without air, as they should be? (regular or airless) -//- Does the area have an APC? -//- Does the area have an Air Alarm? -//- Does the area have a Request Console? -//- Does the area have lights? -//- Does the area have a light switch? -//- Does the area have enough intercoms? -//- Does the area have enough security cameras? (Use the 'Camera Range Display' verb under Debug) -//- Is the area connected to the scrubbers air loop? -//- Is the area connected to the vent air loop? (vent pumps) -//- Is everything wired properly? -//- Does the area have a fire alarm and firedoors? -//- Do all pod doors work properly? -//- Are accesses set properly on doors, pod buttons, etc. -//- Are all items placed properly? (not below vents, scrubbers, tables) -//- Does the disposal system work properly from all the disposal units in this room and all the units, the pipes of which pass through this room? -//- Check for any misplaced or stacked piece of pipe (air and disposal) -//- Check for any misplaced or stacked piece of wire -//- Identify how hard it is to break into the area and where the weak points are -//- Check if the area has too much empty space. If so, make it smaller and replace the rest with maintenance tunnels. -var/intercom_range_display_status = 0 - -/obj/effect/debugging/marker - icon = 'icons/turf/areas.dmi' - icon_state = "yellow" - -/obj/effect/debugging/marker/Move() - return 0 - -/client/proc/do_not_use_these() - set category = "Mapping" - set name = "-None of these are for ingame use!!" - - ..() - -/client/proc/camera_view() - set category = "Mapping" - set name = "Camera Range Display" - - var/on = 0 - for(var/turf/T in world) - if(T.maptext) - on = 1 - T.maptext = null - - if(!on) - var/list/seen = list() - for(var/obj/machinery/camera/C in cameranet.cameras) - for(var/turf/T in C.can_see()) - seen[T]++ - for(var/turf/T in seen) - T.maptext = "[seen[T]]" - feedback_add_details("admin_verb","mCRD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - - -/client/proc/sec_camera_report() - set category = "Mapping" - set name = "Camera Report" - - if(!Master) - alert(usr,"Master_controller not found.","Sec Camera Report") - return 0 - - var/list/obj/machinery/camera/CL = list() - - for(var/obj/machinery/camera/C in cameranet.cameras) - CL += C - - var/output = {"CAMERA ANNOMALITIES REPORT
-The following annomalities have been detected. The ones in red need immediate attention: Some of those in black may be intentional.
    "} - - for(var/obj/machinery/camera/C1 in CL) - for(var/obj/machinery/camera/C2 in CL) - if(C1 != C2) - if(C1.c_tag == C2.c_tag) - output += "
  • c_tag match for sec. cameras at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) and \[[C2.x], [C2.y], [C2.z]\] ([C2.loc.loc]) - c_tag is [C1.c_tag]
  • " - if(C1.loc == C2.loc && C1.dir == C2.dir && C1.pixel_x == C2.pixel_x && C1.pixel_y == C2.pixel_y) - output += "
  • FULLY overlapping sec. cameras at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) Networks: [C1.network] and [C2.network]
  • " - if(C1.loc == C2.loc) - output += "
  • overlapping sec. cameras at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) Networks: [C1.network] and [C2.network]
  • " - var/turf/T = get_step(C1,turn(C1.dir,180)) - if(!T || !isturf(T) || !T.density ) - if(!(locate(/obj/structure/grille,T))) - var/window_check = 0 - for(var/obj/structure/window/W in T) - if (W.dir == turn(C1.dir,180) || W.dir in list(5,6,9,10) ) - window_check = 1 - break - if(!window_check) - output += "
  • Camera not connected to wall at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) Network: [C1.network]
  • " - - output += "
" - usr << browse(output,"window=airreport;size=1000x500") - feedback_add_details("admin_verb","mCRP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/intercom_view() - set category = "Mapping" - set name = "Intercom Range Display" - - if(intercom_range_display_status) - intercom_range_display_status = 0 - else - intercom_range_display_status = 1 - - for(var/obj/effect/debugging/marker/M in world) - qdel(M) - - if(intercom_range_display_status) - for(var/obj/item/device/radio/intercom/I in world) - for(var/turf/T in orange(7,I)) - var/obj/effect/debugging/marker/F = new/obj/effect/debugging/marker(T) - if (!(F in view(7,I.loc))) - qdel(F) - feedback_add_details("admin_verb","mIRD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_show_at_list() - set category = "Mapping" - set name = "Show roundstart AT list" - set desc = "Displays a list of active turfs coordinates at roundstart" - - var/dat = {"Coordinate list of Active Turfs at Roundstart -
Real-time Active Turfs list you can see in Air Subsystem at active_turfs var
"} - - for(var/i=1; i<=active_turfs_startlist.len; i++) - dat += active_turfs_startlist[i] - dat += "
" - - usr << browse(dat, "window=at_list") - - feedback_add_details("admin_verb","mATL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/enable_debug_verbs() - set category = "Debug" - set name = "Debug verbs" - - if(!check_rights(R_DEBUG)) return - - src.verbs += /client/proc/do_not_use_these //-errorage - src.verbs += /client/proc/camera_view //-errorage - src.verbs += /client/proc/sec_camera_report //-errorage - src.verbs += /client/proc/intercom_view //-errorage - src.verbs += /client/proc/air_status //Air things - src.verbs += /client/proc/Cell //More air things - src.verbs += /client/proc/atmosscan //check plumbing - src.verbs += /client/proc/powerdebug //check power - src.verbs += /client/proc/count_objects_on_z_level - src.verbs += /client/proc/count_objects_all - src.verbs += /client/proc/cmd_assume_direct_control //-errorage - src.verbs += /client/proc/startSinglo - src.verbs += /client/proc/fps //allows you to set the ticklag. - src.verbs += /client/proc/cmd_admin_grantfullaccess - src.verbs += /client/proc/cmd_admin_areatest - src.verbs += /client/proc/cmd_admin_rejuvenate - src.verbs += /datum/admins/proc/show_traitor_panel - src.verbs += /client/proc/disable_communication - src.verbs += /client/proc/print_pointers - src.verbs += /client/proc/count_movable_instances - src.verbs += /client/proc/cmd_show_at_list - src.verbs += /client/proc/cmd_show_at_list - src.verbs += /client/proc/manipulate_organs - - feedback_add_details("admin_verb","mDV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/count_movable_instances() - set category = "Debug" - set name = "Count Movable Instances" - - var/count = 0; - - // Apparently there's a BYOND limit on the number of instances for non-turfs. - - for(var/thing in world) - if(isturf(thing)) - continue - count++; - usr << "There are [count]/[MAX_FLAG] instances of non-turfs in the world." - - -/client/proc/count_objects_on_z_level() - set category = "Mapping" - set name = "Count Objects On Level" - var/level = input("Which z-level?","Level?") as text - if(!level) return - var/num_level = text2num(level) - if(!num_level) return - if(!isnum(num_level)) return - - var/type_text = input("Which type path?","Path?") as text - if(!type_text) return - var/type_path = text2path(type_text) - if(!type_path) return - - var/count = 0 - - var/list/atom/atom_list = list() - - for(var/atom/A in world) - if(istype(A,type_path)) - var/atom/B = A - while(!(isturf(B.loc))) - if(B && B.loc) - B = B.loc - else - break - if(B) - if(B.z == num_level) - count++ - atom_list += A - /* - var/atom/temp_atom - for(var/i = 0; i <= (atom_list.len/10); i++) - var/line = "" - for(var/j = 1; j <= 10; j++) - if(i*10+j <= atom_list.len) - temp_atom = atom_list[i*10+j] - line += " no.[i+10+j]@\[[temp_atom.x], [temp_atom.y], [temp_atom.z]\]; " - world << line*/ - - world << "There are [count] objects of type [type_path] on z-level [num_level]" - feedback_add_details("admin_verb","mOBJZ") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/count_objects_all() - set category = "Mapping" - set name = "Count Objects All" - - var/type_text = input("Which type path?","") as text - if(!type_text) return - var/type_path = text2path(type_text) - if(!type_path) return - - var/count = 0 - - for(var/atom/A in world) - if(istype(A,type_path)) - count++ - /* - var/atom/temp_atom - for(var/i = 0; i <= (atom_list.len/10); i++) - var/line = "" - for(var/j = 1; j <= 10; j++) - if(i*10+j <= atom_list.len) - temp_atom = atom_list[i*10+j] - line += " no.[i+10+j]@\[[temp_atom.x], [temp_atom.y], [temp_atom.z]\]; " - world << line*/ - - world << "There are [count] objects of type [type_path] in the game world" - feedback_add_details("admin_verb","mOBJ") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -//This proc is intended to detect lag problems relating to communication procs -var/global/say_disabled = 0 -/client/proc/disable_communication() - set category = "Mapping" - set name = "Disable all communication verbs" - - say_disabled = !say_disabled - if(say_disabled) - message_admins("[src.ckey] used 'Disable all communication verbs', killing all communication methods.") - else - message_admins("[src.ckey] used 'Disable all communication verbs', restoring all communication methods.") +//- Are all the floors with or without air, as they should be? (regular or airless) +//- Does the area have an APC? +//- Does the area have an Air Alarm? +//- Does the area have a Request Console? +//- Does the area have lights? +//- Does the area have a light switch? +//- Does the area have enough intercoms? +//- Does the area have enough security cameras? (Use the 'Camera Range Display' verb under Debug) +//- Is the area connected to the scrubbers air loop? +//- Is the area connected to the vent air loop? (vent pumps) +//- Is everything wired properly? +//- Does the area have a fire alarm and firedoors? +//- Do all pod doors work properly? +//- Are accesses set properly on doors, pod buttons, etc. +//- Are all items placed properly? (not below vents, scrubbers, tables) +//- Does the disposal system work properly from all the disposal units in this room and all the units, the pipes of which pass through this room? +//- Check for any misplaced or stacked piece of pipe (air and disposal) +//- Check for any misplaced or stacked piece of wire +//- Identify how hard it is to break into the area and where the weak points are +//- Check if the area has too much empty space. If so, make it smaller and replace the rest with maintenance tunnels. +var/intercom_range_display_status = 0 + +/obj/effect/debugging/marker + icon = 'icons/turf/areas.dmi' + icon_state = "yellow" + +/obj/effect/debugging/marker/Move() + return 0 + +/client/proc/do_not_use_these() + set category = "Mapping" + set name = "-None of these are for ingame use!!" + + ..() + +/client/proc/camera_view() + set category = "Mapping" + set name = "Camera Range Display" + + var/on = 0 + for(var/turf/T in world) + if(T.maptext) + on = 1 + T.maptext = null + + if(!on) + var/list/seen = list() + for(var/obj/machinery/camera/C in cameranet.cameras) + for(var/turf/T in C.can_see()) + seen[T]++ + for(var/turf/T in seen) + T.maptext = "[seen[T]]" + feedback_add_details("admin_verb","mCRD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + + +/client/proc/sec_camera_report() + set category = "Mapping" + set name = "Camera Report" + + if(!Master) + alert(usr,"Master_controller not found.","Sec Camera Report") + return 0 + + var/list/obj/machinery/camera/CL = list() + + for(var/obj/machinery/camera/C in cameranet.cameras) + CL += C + + var/output = {"CAMERA ANNOMALITIES REPORT
+The following annomalities have been detected. The ones in red need immediate attention: Some of those in black may be intentional.
    "} + + for(var/obj/machinery/camera/C1 in CL) + for(var/obj/machinery/camera/C2 in CL) + if(C1 != C2) + if(C1.c_tag == C2.c_tag) + output += "
  • c_tag match for sec. cameras at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) and \[[C2.x], [C2.y], [C2.z]\] ([C2.loc.loc]) - c_tag is [C1.c_tag]
  • " + if(C1.loc == C2.loc && C1.dir == C2.dir && C1.pixel_x == C2.pixel_x && C1.pixel_y == C2.pixel_y) + output += "
  • FULLY overlapping sec. cameras at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) Networks: [C1.network] and [C2.network]
  • " + if(C1.loc == C2.loc) + output += "
  • overlapping sec. cameras at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) Networks: [C1.network] and [C2.network]
  • " + var/turf/T = get_step(C1,turn(C1.dir,180)) + if(!T || !isturf(T) || !T.density ) + if(!(locate(/obj/structure/grille,T))) + var/window_check = 0 + for(var/obj/structure/window/W in T) + if (W.dir == turn(C1.dir,180) || W.dir in list(5,6,9,10) ) + window_check = 1 + break + if(!window_check) + output += "
  • Camera not connected to wall at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) Network: [C1.network]
  • " + + output += "
" + usr << browse(output,"window=airreport;size=1000x500") + feedback_add_details("admin_verb","mCRP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/intercom_view() + set category = "Mapping" + set name = "Intercom Range Display" + + if(intercom_range_display_status) + intercom_range_display_status = 0 + else + intercom_range_display_status = 1 + + for(var/obj/effect/debugging/marker/M in world) + qdel(M) + + if(intercom_range_display_status) + for(var/obj/item/device/radio/intercom/I in world) + for(var/turf/T in orange(7,I)) + var/obj/effect/debugging/marker/F = new/obj/effect/debugging/marker(T) + if (!(F in view(7,I.loc))) + qdel(F) + feedback_add_details("admin_verb","mIRD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_show_at_list() + set category = "Mapping" + set name = "Show roundstart AT list" + set desc = "Displays a list of active turfs coordinates at roundstart" + + var/dat = {"Coordinate list of Active Turfs at Roundstart +
Real-time Active Turfs list you can see in Air Subsystem at active_turfs var
"} + + for(var/i=1; i<=active_turfs_startlist.len; i++) + dat += active_turfs_startlist[i] + dat += "
" + + usr << browse(dat, "window=at_list") + + feedback_add_details("admin_verb","mATL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/enable_debug_verbs() + set category = "Debug" + set name = "Debug verbs" + + if(!check_rights(R_DEBUG)) return + + src.verbs += /client/proc/do_not_use_these //-errorage + src.verbs += /client/proc/camera_view //-errorage + src.verbs += /client/proc/sec_camera_report //-errorage + src.verbs += /client/proc/intercom_view //-errorage + src.verbs += /client/proc/air_status //Air things + src.verbs += /client/proc/Cell //More air things + src.verbs += /client/proc/atmosscan //check plumbing + src.verbs += /client/proc/powerdebug //check power + src.verbs += /client/proc/count_objects_on_z_level + src.verbs += /client/proc/count_objects_all + src.verbs += /client/proc/cmd_assume_direct_control //-errorage + src.verbs += /client/proc/startSinglo + src.verbs += /client/proc/fps //allows you to set the ticklag. + src.verbs += /client/proc/cmd_admin_grantfullaccess + src.verbs += /client/proc/cmd_admin_areatest + src.verbs += /client/proc/cmd_admin_rejuvenate + src.verbs += /datum/admins/proc/show_traitor_panel + src.verbs += /client/proc/disable_communication + src.verbs += /client/proc/print_pointers + src.verbs += /client/proc/count_movable_instances + src.verbs += /client/proc/cmd_show_at_list + src.verbs += /client/proc/cmd_show_at_list + src.verbs += /client/proc/manipulate_organs + + feedback_add_details("admin_verb","mDV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/count_movable_instances() + set category = "Debug" + set name = "Count Movable Instances" + + var/count = 0; + + // Apparently there's a BYOND limit on the number of instances for non-turfs. + + for(var/thing in world) + if(isturf(thing)) + continue + count++; + usr << "There are [count]/[MAX_FLAG] instances of non-turfs in the world." + + +/client/proc/count_objects_on_z_level() + set category = "Mapping" + set name = "Count Objects On Level" + var/level = input("Which z-level?","Level?") as text + if(!level) return + var/num_level = text2num(level) + if(!num_level) return + if(!isnum(num_level)) return + + var/type_text = input("Which type path?","Path?") as text + if(!type_text) return + var/type_path = text2path(type_text) + if(!type_path) return + + var/count = 0 + + var/list/atom/atom_list = list() + + for(var/atom/A in world) + if(istype(A,type_path)) + var/atom/B = A + while(!(isturf(B.loc))) + if(B && B.loc) + B = B.loc + else + break + if(B) + if(B.z == num_level) + count++ + atom_list += A + /* + var/atom/temp_atom + for(var/i = 0; i <= (atom_list.len/10); i++) + var/line = "" + for(var/j = 1; j <= 10; j++) + if(i*10+j <= atom_list.len) + temp_atom = atom_list[i*10+j] + line += " no.[i+10+j]@\[[temp_atom.x], [temp_atom.y], [temp_atom.z]\]; " + world << line*/ + + world << "There are [count] objects of type [type_path] on z-level [num_level]" + feedback_add_details("admin_verb","mOBJZ") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/count_objects_all() + set category = "Mapping" + set name = "Count Objects All" + + var/type_text = input("Which type path?","") as text + if(!type_text) return + var/type_path = text2path(type_text) + if(!type_path) return + + var/count = 0 + + for(var/atom/A in world) + if(istype(A,type_path)) + count++ + /* + var/atom/temp_atom + for(var/i = 0; i <= (atom_list.len/10); i++) + var/line = "" + for(var/j = 1; j <= 10; j++) + if(i*10+j <= atom_list.len) + temp_atom = atom_list[i*10+j] + line += " no.[i+10+j]@\[[temp_atom.x], [temp_atom.y], [temp_atom.z]\]; " + world << line*/ + + world << "There are [count] objects of type [type_path] in the game world" + feedback_add_details("admin_verb","mOBJ") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +//This proc is intended to detect lag problems relating to communication procs +var/global/say_disabled = 0 +/client/proc/disable_communication() + set category = "Mapping" + set name = "Disable all communication verbs" + + say_disabled = !say_disabled + if(say_disabled) + message_admins("[src.ckey] used 'Disable all communication verbs', killing all communication methods.") + else + message_admins("[src.ckey] used 'Disable all communication verbs', restoring all communication methods.") diff --git a/code/modules/admin/verbs/one_click_antag.dm b/code/modules/admin/verbs/one_click_antag.dm index be7c7f7c7cb..6e827501734 100644 --- a/code/modules/admin/verbs/one_click_antag.dm +++ b/code/modules/admin/verbs/one_click_antag.dm @@ -1,608 +1,608 @@ -/client/proc/one_click_antag() - set name = "Create Antagonist" - set desc = "Auto-create an antagonist of your choice" - set category = "Admin" - - if(holder) - holder.one_click_antag() - return - - -/datum/admins/proc/one_click_antag() - - var/dat = {" - Make Traitors
- Make Changelings
- Make Revs
- Make Cult
- Make Malf AI
- Make Blob
- Make Gangsters
- Make Shadowling
- Make Wizard (Requires Ghosts)
- Make Nuke Team (Requires Ghosts)
- Make Centcom Response Team (Requires Ghosts)
- Make Abductor Team (Requires Ghosts)
- Make Revenant (Requires Ghost)
- "} - - var/datum/browser/popup = new(usr, "oneclickantag", "Quick-Create Antagonist", 400, 400) - popup.set_content(dat) - popup.open() - - -/datum/admins/proc/makeMalfAImode() - - var/list/mob/living/silicon/AIs = list() - var/mob/living/silicon/malfAI = null - var/datum/mind/themind = null - - for(var/mob/living/silicon/ai/ai in player_list) - if(ai.client) - AIs += ai - - if(AIs.len) - malfAI = pick(AIs) - - if(malfAI) - themind = malfAI.mind - themind.make_AI_Malf() - return 1 - - return 0 - - -/datum/admins/proc/makeTraitors() - var/datum/game_mode/traitor/temp = new - - if(config.protect_roles_from_antagonist) - temp.restricted_jobs += temp.protected_jobs - - if(config.protect_assistant_from_antagonist) - temp.restricted_jobs += "Assistant" - - var/list/mob/living/carbon/human/candidates = list() - var/mob/living/carbon/human/H = null - - for(var/mob/living/carbon/human/applicant in player_list) - if(ROLE_TRAITOR in applicant.client.prefs.be_special) - if(!applicant.stat) - if(applicant.mind) - if (!applicant.mind.special_role) - if(!jobban_isbanned(applicant, ROLE_TRAITOR) && !jobban_isbanned(applicant, "Syndicate")) - if(temp.age_check(applicant.client)) - if(!(applicant.job in temp.restricted_jobs)) - candidates += applicant - - if(candidates.len) - var/numTraitors = min(candidates.len, 3) - - for(var/i = 0, i300)//If more than 30 game seconds passed. - return - candidates += G - if("No") - return - else - return - - sleep(300) - - if(candidates.len) - shuffle(candidates) - for(var/mob/i in candidates) - if(!i || !i.client) continue //Dont bother removing them from the list since we only grab one wizard - - theghost = i - break - - if(theghost) - var/mob/living/carbon/human/new_character=makeBody(theghost) - new_character.mind.make_Wizard() - return 1 - - return 0 - - -/datum/admins/proc/makeCult() - var/datum/game_mode/cult/temp = new - if(config.protect_roles_from_antagonist) - temp.restricted_jobs += temp.protected_jobs - - if(config.protect_assistant_from_antagonist) - temp.restricted_jobs += "Assistant" - - var/list/mob/living/carbon/human/candidates = list() - var/mob/living/carbon/human/H = null - - for(var/mob/living/carbon/human/applicant in player_list) - if(ROLE_CULTIST in applicant.client.prefs.be_special) - if(applicant.stat == CONSCIOUS) - if(applicant.mind) - if(!applicant.mind.special_role) - if(!jobban_isbanned(applicant, ROLE_CULTIST) && !jobban_isbanned(applicant, "Syndicate")) - if(temp.age_check(applicant.client)) - if(!(applicant.job in temp.restricted_jobs)) - candidates += applicant - - if(candidates.len) - var/numCultists = min(candidates.len, 5) - - for(var/i = 0, i synd_spawn.len) - spawnpos = 2 //Ran out of spawns. Let's loop back to the first non-leader position - var/mob/living/carbon/human/new_character=makeBody(c) - if(!leader_chosen) - leader_chosen = 1 - new_character.mind.make_Nuke(synd_spawn[spawnpos],nuke_code,1) - else - new_character.mind.make_Nuke(synd_spawn[spawnpos],nuke_code) - spawnpos++ - return 1 - else - return 0 - - - - - -/datum/admins/proc/makeAliens() - new /datum/round_event/alien_infestation{spawncount=3}() - return 1 - -/datum/admins/proc/makeSpaceNinja() - new /datum/round_event/ninja() - return 1 - -// DEATH SQUADS -/datum/admins/proc/makeDeathsquad() - var/mission = input("Assign a mission to the deathsquad", "Assign Mission", "Leave no witnesses.") - var/list/mob/dead/observer/candidates = pollCandidates("Do you wish to be considered for an elite Nanotrasen Strike Team?", "deathsquad", null) - var/squadSpawned = 0 - - if(candidates.len >= 2) //Minimum 2 to be considered a squad - //Pick the lucky players - var/numagents = min(5,candidates.len) //How many commandos to spawn - var/list/spawnpoints = emergencyresponseteamspawn - while(numagents && candidates.len) - if (numagents > spawnpoints.len) - numagents-- - continue // This guy's unlucky, not enough spawn points, we skip him. - var/spawnloc = spawnpoints[numagents] - var/mob/dead/observer/chosen_candidate = pick(candidates) - candidates -= chosen_candidate - if(!chosen_candidate.key) - continue - - //Spawn and equip the commando - var/mob/living/carbon/human/Commando = new(spawnloc) - chosen_candidate.client.prefs.copy_to(Commando) - if(numagents == 1) //If Squad Leader - Commando.real_name = "Officer [pick(commando_names)]" - Commando.equipOutfit(/datum/outfit/death_commando/officer) - else - Commando.real_name = "Trooper [pick(commando_names)]" - Commando.equipOutfit(/datum/outfit/death_commando) - Commando.dna.update_dna_identity() - Commando.key = chosen_candidate.key - Commando.mind.assigned_role = "Death Commando" - for(var/obj/machinery/door/poddoor/ert/door in airlocks) - spawn(0) - door.open() - - //Assign antag status and the mission - ticker.mode.traitors += Commando.mind - Commando.mind.special_role = "deathsquad" - var/datum/objective/missionobj = new - missionobj.owner = Commando.mind - missionobj.explanation_text = mission - missionobj.completed = 1 - Commando.mind.objectives += missionobj - - //Greet the commando - Commando << "You are the [numagents==1?"Deathsquad Officer":"Death Commando"]." - var/missiondesc = "Your squad is being sent on a mission to [station_name()] by Nanotrasen's Security Division." - if(numagents == 1) //If Squad Leader - missiondesc += " Lead your squad to ensure the completion of the mission. Board the shuttle when your team is ready." - else - missiondesc += " Follow orders given to you by your squad leader." - missiondesc += "
Your Mission: [mission]" - Commando << missiondesc - - if(config.enforce_human_authority) - Commando.set_species("human") - - //Logging and cleanup - if(numagents == 1) - message_admins("The deathsquad has spawned with the mission: [mission].") - log_game("[key_name(Commando)] has been selected as a Death Commando") - numagents-- - squadSpawned++ - - if (squadSpawned) - return 1 - else - return 0 - - return - - -/datum/admins/proc/makeGangsters() - - var/datum/game_mode/gang/temp = new - if(config.protect_roles_from_antagonist) - temp.restricted_jobs += temp.protected_jobs - - if(config.protect_assistant_from_antagonist) - temp.restricted_jobs += "Assistant" - - var/list/mob/living/carbon/human/candidates = list() - var/mob/living/carbon/human/H = null - - for(var/mob/living/carbon/human/applicant in player_list) - if(ROLE_GANG in applicant.client.prefs.be_special) - if(!applicant.stat) - if(applicant.mind) - if(!applicant.mind.special_role) - if(!jobban_isbanned(applicant, ROLE_GANG) && !jobban_isbanned(applicant, "Syndicate")) - if(temp.age_check(applicant.client)) - if(!(applicant.job in temp.restricted_jobs)) - candidates += applicant - - if(candidates.len >= 2) - for(var/needs_assigned=2,needs_assigned>0,needs_assigned--) - H = pick(candidates) - if(gang_colors_pool.len) - var/datum/gang/newgang = new() - ticker.mode.gangs += newgang - H.mind.make_Gang(newgang) - candidates.Remove(H) - else if(needs_assigned == 2) - return 0 - return 1 - - return 0 - - -/datum/admins/proc/makeOfficial() - var/mission = input("Assign a task for the official", "Assign Task", "Conduct a routine preformance review of [station_name()] and its Captain.") - var/list/mob/dead/observer/candidates = pollCandidates("Do you wish to be considered to be a Centcom Official?", "deathsquad") - - if(candidates.len) - var/mob/dead/observer/chosen_candidate = pick(candidates) - - //Create the official - var/mob/living/carbon/human/newmob = new (pick(emergencyresponseteamspawn)) - chosen_candidate.client.prefs.copy_to(newmob) - newmob.real_name = newmob.dna.species.random_name(newmob.gender,1) - newmob.dna.update_dna_identity() - newmob.key = chosen_candidate.key - newmob.mind.assigned_role = "Centcom Official" - newmob.equipOutfit(/datum/outfit/centcom_official) - - //Assign antag status and the mission - ticker.mode.traitors += newmob.mind - newmob.mind.special_role = "official" - var/datum/objective/missionobj = new - missionobj.owner = newmob.mind - missionobj.explanation_text = mission - missionobj.completed = 1 - newmob.mind.objectives += missionobj - - //Greet the official - newmob << "You are a Centcom Official." - newmob << "
Central Command is sending you to [station_name()] with the task: [mission]" - - //Logging and cleanup - message_admins("Centcom Official [key_name_admin(newmob)] has spawned with the task: [mission]") - log_game("[key_name(newmob)] has been selected as a Centcom Official") - - return 1 - - return 0 - -// CENTCOM RESPONSE TEAM -/datum/admins/proc/makeEmergencyresponseteam() - var/alert = input("Which team should we send?", "Select Response Level") as null|anything in list("Green: Centcom Official", "Blue: Light ERT (No Armoury Access)", "Amber: Full ERT (Armoury Access)", "Red: Elite ERT (Armoury Access + Pulse Weapons)", "Delta: Deathsquad") - if(!alert) - return - switch(alert) - if("Delta: Deathsquad") - return makeDeathsquad() - if("Red: Elite ERT (Armoury Access + Pulse Weapons)") - alert = "Red" - if("Amber: Full ERT (Armoury Access)") - alert = "Amber" - if("Blue: Light ERT (No Armoury Access)") - alert = "Blue" - if("Green: Centcom Official") - return makeOfficial() - var/teamsize = min(7,input("Maximum size of team? (7 max)", "Select Team Size",4) as null|num) - var/mission = input("Assign a mission to the Emergency Response Team", "Assign Mission", "Assist the station.") - var/list/mob/dead/observer/candidates = pollCandidates("Do you wish to be considered for a Code [alert] Nanotrasen Emergency Response Team?", "deathsquad", null) - var/teamSpawned = 0 - - if(candidates.len > 0) - //Pick the (un)lucky players - var/numagents = min(teamsize,candidates.len) //How many officers to spawn - var/redalert //If the ert gets super weapons - if (alert == "Red") - numagents = min(teamsize,candidates.len) - redalert = 1 - var/list/spawnpoints = emergencyresponseteamspawn - while(numagents && candidates.len) - if (numagents > spawnpoints.len) - numagents-- - continue // This guy's unlucky, not enough spawn points, we skip him. - var/spawnloc = spawnpoints[numagents] - var/mob/dead/observer/chosen_candidate = pick(candidates) - candidates -= chosen_candidate - if(!chosen_candidate.key) - continue - - //Spawn and equip the officer - var/mob/living/carbon/human/ERTOperative = new(spawnloc) - var/list/lastname = last_names - chosen_candidate.client.prefs.copy_to(ERTOperative) - var/ertname = pick(lastname) - switch(numagents) - if(1) - ERTOperative.real_name = "Commander [ertname]" - ERTOperative.equipOutfit(redalert ? /datum/outfit/ert/commander/alert : /datum/outfit/ert/commander) - if(2) - ERTOperative.real_name = "Security Officer [ertname]" - ERTOperative.equipOutfit(redalert ? /datum/outfit/ert/security/alert : /datum/outfit/ert/security) - if(3) - ERTOperative.real_name = "Medical Officer [ertname]" - ERTOperative.equipOutfit(redalert ? /datum/outfit/ert/medic/alert : /datum/outfit/ert/medic) - if(4) - ERTOperative.real_name = "Engineer [ertname]" - ERTOperative.equipOutfit(redalert ? /datum/outfit/ert/engineer/alert : /datum/outfit/ert/engineer) - if(5) - ERTOperative.real_name = "Security Officer [ertname]" - ERTOperative.equipOutfit(redalert ? /datum/outfit/ert/security/alert : /datum/outfit/ert/security) - if(6) - ERTOperative.real_name = "Medical Officer [ertname]" - ERTOperative.equipOutfit(redalert ? /datum/outfit/ert/medic/alert : /datum/outfit/ert/medic) - if(7) - ERTOperative.real_name = "Engineer [ertname]" - ERTOperative.equipOutfit(redalert ? /datum/outfit/ert/engineer/alert : /datum/outfit/ert/engineer) - ERTOperative.dna.update_dna_identity() - ERTOperative.key = chosen_candidate.key - ERTOperative.mind.assigned_role = "ERT" - - //Open the Armory doors - if(alert != "Blue") - for(var/obj/machinery/door/poddoor/ert/door in airlocks) - spawn(0) - door.open() - - //Assign antag status and the mission - ticker.mode.traitors += ERTOperative.mind - ERTOperative.mind.special_role = "ERT" - var/datum/objective/missionobj = new - missionobj.owner = ERTOperative.mind - missionobj.explanation_text = mission - missionobj.completed = 1 - ERTOperative.mind.objectives += missionobj - - //Greet the commando - ERTOperative << "You are [numagents==1?"the Emergency Response Team Commander":"an Emergency Response Officer"]." - var/missiondesc = "Your squad is being sent on a Code [alert] mission to [station_name()] by Nanotrasen's Security Division." - if(numagents == 1) //If Squad Leader - missiondesc += " Lead your squad to ensure the completion of the mission. Avoid civilian casualites when possible. Board the shuttle when your team is ready." - else - missiondesc += " Follow orders given to you by your commander. Avoid civilian casualites when possible." - missiondesc += "
Your Mission: [mission]" - ERTOperative << missiondesc - - if(config.enforce_human_authority) - ERTOperative.set_species("human") - - //Logging and cleanup - if(numagents == 1) - message_admins("A Code [alert] emergency response team has spawned with the mission: [mission]") - log_game("[key_name(ERTOperative)] has been selected as an Emergency Response Officer") - numagents-- - teamSpawned++ - - if (teamSpawned) - return 1 - else - return 0 - - return - -//Abductors -/datum/admins/proc/makeAbductorTeam() - new /datum/round_event/abductor - return 1 - -/datum/admins/proc/makeRevenant() - var/list/mob/dead/observer/candidates = pollCandidates("Do you wish to be considered for becoming a revenant?", "revenant", null) - if(candidates.len >= 1) - var/spook_op = pick(candidates) - var/mob/dead/observer/O = spook_op - candidates -= spook_op - var/mob/living/simple_animal/revenant/revvie = new /mob/living/simple_animal/revenant(get_turf(O)) - revvie.key = O.key - revvie.mind.assigned_role = "revenant" - revvie.mind.special_role = "Revenant" - return 1 - else - return - -//Shadowling -/datum/admins/proc/makeShadowling() - var/datum/game_mode/shadowling/temp = new - if(config.protect_roles_from_antagonist) - temp.restricted_jobs += temp.protected_jobs - if(config.protect_assistant_from_antagonist) - temp.restricted_jobs += "Assistant" - var/list/mob/living/carbon/human/candidates = list() - var/mob/living/carbon/human/H = null - for(var/mob/living/carbon/human/applicant in player_list) - if(ROLE_SHADOWLING in applicant.client.prefs.be_special) - if(!applicant.stat) - if(applicant.mind) - if(!applicant.mind.special_role) - if(!jobban_isbanned(applicant, "shadowling") && !jobban_isbanned(applicant, "Syndicate")) - if(temp.age_check(applicant.client)) - if(!(applicant.job in temp.restricted_jobs)) - if(!(is_shadow_or_thrall(applicant))) - candidates += applicant - - if(candidates.len) - H = pick(candidates) - ticker.mode.shadows += H.mind - H.mind.special_role = "shadowling" - H << "Something stirs in the space between worlds. A red light floods your mind, and suddenly you understand. Your human disguise has served you well, but it \ - is time you cast it away. You are a shadowling, and you are to ascend at all costs." - ticker.mode.finalize_shadowling(H.mind) - message_admins("[H] has been made into a shadowling.") - candidates.Remove(H) - return 1 - return 0 +/client/proc/one_click_antag() + set name = "Create Antagonist" + set desc = "Auto-create an antagonist of your choice" + set category = "Admin" + + if(holder) + holder.one_click_antag() + return + + +/datum/admins/proc/one_click_antag() + + var/dat = {" + Make Traitors
+ Make Changelings
+ Make Revs
+ Make Cult
+ Make Malf AI
+ Make Blob
+ Make Gangsters
+ Make Shadowling
+ Make Wizard (Requires Ghosts)
+ Make Nuke Team (Requires Ghosts)
+ Make Centcom Response Team (Requires Ghosts)
+ Make Abductor Team (Requires Ghosts)
+ Make Revenant (Requires Ghost)
+ "} + + var/datum/browser/popup = new(usr, "oneclickantag", "Quick-Create Antagonist", 400, 400) + popup.set_content(dat) + popup.open() + + +/datum/admins/proc/makeMalfAImode() + + var/list/mob/living/silicon/AIs = list() + var/mob/living/silicon/malfAI = null + var/datum/mind/themind = null + + for(var/mob/living/silicon/ai/ai in player_list) + if(ai.client) + AIs += ai + + if(AIs.len) + malfAI = pick(AIs) + + if(malfAI) + themind = malfAI.mind + themind.make_AI_Malf() + return 1 + + return 0 + + +/datum/admins/proc/makeTraitors() + var/datum/game_mode/traitor/temp = new + + if(config.protect_roles_from_antagonist) + temp.restricted_jobs += temp.protected_jobs + + if(config.protect_assistant_from_antagonist) + temp.restricted_jobs += "Assistant" + + var/list/mob/living/carbon/human/candidates = list() + var/mob/living/carbon/human/H = null + + for(var/mob/living/carbon/human/applicant in player_list) + if(ROLE_TRAITOR in applicant.client.prefs.be_special) + if(!applicant.stat) + if(applicant.mind) + if (!applicant.mind.special_role) + if(!jobban_isbanned(applicant, ROLE_TRAITOR) && !jobban_isbanned(applicant, "Syndicate")) + if(temp.age_check(applicant.client)) + if(!(applicant.job in temp.restricted_jobs)) + candidates += applicant + + if(candidates.len) + var/numTraitors = min(candidates.len, 3) + + for(var/i = 0, i300)//If more than 30 game seconds passed. + return + candidates += G + if("No") + return + else + return + + sleep(300) + + if(candidates.len) + shuffle(candidates) + for(var/mob/i in candidates) + if(!i || !i.client) continue //Dont bother removing them from the list since we only grab one wizard + + theghost = i + break + + if(theghost) + var/mob/living/carbon/human/new_character=makeBody(theghost) + new_character.mind.make_Wizard() + return 1 + + return 0 + + +/datum/admins/proc/makeCult() + var/datum/game_mode/cult/temp = new + if(config.protect_roles_from_antagonist) + temp.restricted_jobs += temp.protected_jobs + + if(config.protect_assistant_from_antagonist) + temp.restricted_jobs += "Assistant" + + var/list/mob/living/carbon/human/candidates = list() + var/mob/living/carbon/human/H = null + + for(var/mob/living/carbon/human/applicant in player_list) + if(ROLE_CULTIST in applicant.client.prefs.be_special) + if(applicant.stat == CONSCIOUS) + if(applicant.mind) + if(!applicant.mind.special_role) + if(!jobban_isbanned(applicant, ROLE_CULTIST) && !jobban_isbanned(applicant, "Syndicate")) + if(temp.age_check(applicant.client)) + if(!(applicant.job in temp.restricted_jobs)) + candidates += applicant + + if(candidates.len) + var/numCultists = min(candidates.len, 5) + + for(var/i = 0, i synd_spawn.len) + spawnpos = 2 //Ran out of spawns. Let's loop back to the first non-leader position + var/mob/living/carbon/human/new_character=makeBody(c) + if(!leader_chosen) + leader_chosen = 1 + new_character.mind.make_Nuke(synd_spawn[spawnpos],nuke_code,1) + else + new_character.mind.make_Nuke(synd_spawn[spawnpos],nuke_code) + spawnpos++ + return 1 + else + return 0 + + + + + +/datum/admins/proc/makeAliens() + new /datum/round_event/alien_infestation{spawncount=3}() + return 1 + +/datum/admins/proc/makeSpaceNinja() + new /datum/round_event/ninja() + return 1 + +// DEATH SQUADS +/datum/admins/proc/makeDeathsquad() + var/mission = input("Assign a mission to the deathsquad", "Assign Mission", "Leave no witnesses.") + var/list/mob/dead/observer/candidates = pollCandidates("Do you wish to be considered for an elite Nanotrasen Strike Team?", "deathsquad", null) + var/squadSpawned = 0 + + if(candidates.len >= 2) //Minimum 2 to be considered a squad + //Pick the lucky players + var/numagents = min(5,candidates.len) //How many commandos to spawn + var/list/spawnpoints = emergencyresponseteamspawn + while(numagents && candidates.len) + if (numagents > spawnpoints.len) + numagents-- + continue // This guy's unlucky, not enough spawn points, we skip him. + var/spawnloc = spawnpoints[numagents] + var/mob/dead/observer/chosen_candidate = pick(candidates) + candidates -= chosen_candidate + if(!chosen_candidate.key) + continue + + //Spawn and equip the commando + var/mob/living/carbon/human/Commando = new(spawnloc) + chosen_candidate.client.prefs.copy_to(Commando) + if(numagents == 1) //If Squad Leader + Commando.real_name = "Officer [pick(commando_names)]" + Commando.equipOutfit(/datum/outfit/death_commando/officer) + else + Commando.real_name = "Trooper [pick(commando_names)]" + Commando.equipOutfit(/datum/outfit/death_commando) + Commando.dna.update_dna_identity() + Commando.key = chosen_candidate.key + Commando.mind.assigned_role = "Death Commando" + for(var/obj/machinery/door/poddoor/ert/door in airlocks) + spawn(0) + door.open() + + //Assign antag status and the mission + ticker.mode.traitors += Commando.mind + Commando.mind.special_role = "deathsquad" + var/datum/objective/missionobj = new + missionobj.owner = Commando.mind + missionobj.explanation_text = mission + missionobj.completed = 1 + Commando.mind.objectives += missionobj + + //Greet the commando + Commando << "You are the [numagents==1?"Deathsquad Officer":"Death Commando"]." + var/missiondesc = "Your squad is being sent on a mission to [station_name()] by Nanotrasen's Security Division." + if(numagents == 1) //If Squad Leader + missiondesc += " Lead your squad to ensure the completion of the mission. Board the shuttle when your team is ready." + else + missiondesc += " Follow orders given to you by your squad leader." + missiondesc += "
Your Mission: [mission]" + Commando << missiondesc + + if(config.enforce_human_authority) + Commando.set_species("human") + + //Logging and cleanup + if(numagents == 1) + message_admins("The deathsquad has spawned with the mission: [mission].") + log_game("[key_name(Commando)] has been selected as a Death Commando") + numagents-- + squadSpawned++ + + if (squadSpawned) + return 1 + else + return 0 + + return + + +/datum/admins/proc/makeGangsters() + + var/datum/game_mode/gang/temp = new + if(config.protect_roles_from_antagonist) + temp.restricted_jobs += temp.protected_jobs + + if(config.protect_assistant_from_antagonist) + temp.restricted_jobs += "Assistant" + + var/list/mob/living/carbon/human/candidates = list() + var/mob/living/carbon/human/H = null + + for(var/mob/living/carbon/human/applicant in player_list) + if(ROLE_GANG in applicant.client.prefs.be_special) + if(!applicant.stat) + if(applicant.mind) + if(!applicant.mind.special_role) + if(!jobban_isbanned(applicant, ROLE_GANG) && !jobban_isbanned(applicant, "Syndicate")) + if(temp.age_check(applicant.client)) + if(!(applicant.job in temp.restricted_jobs)) + candidates += applicant + + if(candidates.len >= 2) + for(var/needs_assigned=2,needs_assigned>0,needs_assigned--) + H = pick(candidates) + if(gang_colors_pool.len) + var/datum/gang/newgang = new() + ticker.mode.gangs += newgang + H.mind.make_Gang(newgang) + candidates.Remove(H) + else if(needs_assigned == 2) + return 0 + return 1 + + return 0 + + +/datum/admins/proc/makeOfficial() + var/mission = input("Assign a task for the official", "Assign Task", "Conduct a routine preformance review of [station_name()] and its Captain.") + var/list/mob/dead/observer/candidates = pollCandidates("Do you wish to be considered to be a Centcom Official?", "deathsquad") + + if(candidates.len) + var/mob/dead/observer/chosen_candidate = pick(candidates) + + //Create the official + var/mob/living/carbon/human/newmob = new (pick(emergencyresponseteamspawn)) + chosen_candidate.client.prefs.copy_to(newmob) + newmob.real_name = newmob.dna.species.random_name(newmob.gender,1) + newmob.dna.update_dna_identity() + newmob.key = chosen_candidate.key + newmob.mind.assigned_role = "Centcom Official" + newmob.equipOutfit(/datum/outfit/centcom_official) + + //Assign antag status and the mission + ticker.mode.traitors += newmob.mind + newmob.mind.special_role = "official" + var/datum/objective/missionobj = new + missionobj.owner = newmob.mind + missionobj.explanation_text = mission + missionobj.completed = 1 + newmob.mind.objectives += missionobj + + //Greet the official + newmob << "You are a Centcom Official." + newmob << "
Central Command is sending you to [station_name()] with the task: [mission]" + + //Logging and cleanup + message_admins("Centcom Official [key_name_admin(newmob)] has spawned with the task: [mission]") + log_game("[key_name(newmob)] has been selected as a Centcom Official") + + return 1 + + return 0 + +// CENTCOM RESPONSE TEAM +/datum/admins/proc/makeEmergencyresponseteam() + var/alert = input("Which team should we send?", "Select Response Level") as null|anything in list("Green: Centcom Official", "Blue: Light ERT (No Armoury Access)", "Amber: Full ERT (Armoury Access)", "Red: Elite ERT (Armoury Access + Pulse Weapons)", "Delta: Deathsquad") + if(!alert) + return + switch(alert) + if("Delta: Deathsquad") + return makeDeathsquad() + if("Red: Elite ERT (Armoury Access + Pulse Weapons)") + alert = "Red" + if("Amber: Full ERT (Armoury Access)") + alert = "Amber" + if("Blue: Light ERT (No Armoury Access)") + alert = "Blue" + if("Green: Centcom Official") + return makeOfficial() + var/teamsize = min(7,input("Maximum size of team? (7 max)", "Select Team Size",4) as null|num) + var/mission = input("Assign a mission to the Emergency Response Team", "Assign Mission", "Assist the station.") + var/list/mob/dead/observer/candidates = pollCandidates("Do you wish to be considered for a Code [alert] Nanotrasen Emergency Response Team?", "deathsquad", null) + var/teamSpawned = 0 + + if(candidates.len > 0) + //Pick the (un)lucky players + var/numagents = min(teamsize,candidates.len) //How many officers to spawn + var/redalert //If the ert gets super weapons + if (alert == "Red") + numagents = min(teamsize,candidates.len) + redalert = 1 + var/list/spawnpoints = emergencyresponseteamspawn + while(numagents && candidates.len) + if (numagents > spawnpoints.len) + numagents-- + continue // This guy's unlucky, not enough spawn points, we skip him. + var/spawnloc = spawnpoints[numagents] + var/mob/dead/observer/chosen_candidate = pick(candidates) + candidates -= chosen_candidate + if(!chosen_candidate.key) + continue + + //Spawn and equip the officer + var/mob/living/carbon/human/ERTOperative = new(spawnloc) + var/list/lastname = last_names + chosen_candidate.client.prefs.copy_to(ERTOperative) + var/ertname = pick(lastname) + switch(numagents) + if(1) + ERTOperative.real_name = "Commander [ertname]" + ERTOperative.equipOutfit(redalert ? /datum/outfit/ert/commander/alert : /datum/outfit/ert/commander) + if(2) + ERTOperative.real_name = "Security Officer [ertname]" + ERTOperative.equipOutfit(redalert ? /datum/outfit/ert/security/alert : /datum/outfit/ert/security) + if(3) + ERTOperative.real_name = "Medical Officer [ertname]" + ERTOperative.equipOutfit(redalert ? /datum/outfit/ert/medic/alert : /datum/outfit/ert/medic) + if(4) + ERTOperative.real_name = "Engineer [ertname]" + ERTOperative.equipOutfit(redalert ? /datum/outfit/ert/engineer/alert : /datum/outfit/ert/engineer) + if(5) + ERTOperative.real_name = "Security Officer [ertname]" + ERTOperative.equipOutfit(redalert ? /datum/outfit/ert/security/alert : /datum/outfit/ert/security) + if(6) + ERTOperative.real_name = "Medical Officer [ertname]" + ERTOperative.equipOutfit(redalert ? /datum/outfit/ert/medic/alert : /datum/outfit/ert/medic) + if(7) + ERTOperative.real_name = "Engineer [ertname]" + ERTOperative.equipOutfit(redalert ? /datum/outfit/ert/engineer/alert : /datum/outfit/ert/engineer) + ERTOperative.dna.update_dna_identity() + ERTOperative.key = chosen_candidate.key + ERTOperative.mind.assigned_role = "ERT" + + //Open the Armory doors + if(alert != "Blue") + for(var/obj/machinery/door/poddoor/ert/door in airlocks) + spawn(0) + door.open() + + //Assign antag status and the mission + ticker.mode.traitors += ERTOperative.mind + ERTOperative.mind.special_role = "ERT" + var/datum/objective/missionobj = new + missionobj.owner = ERTOperative.mind + missionobj.explanation_text = mission + missionobj.completed = 1 + ERTOperative.mind.objectives += missionobj + + //Greet the commando + ERTOperative << "You are [numagents==1?"the Emergency Response Team Commander":"an Emergency Response Officer"]." + var/missiondesc = "Your squad is being sent on a Code [alert] mission to [station_name()] by Nanotrasen's Security Division." + if(numagents == 1) //If Squad Leader + missiondesc += " Lead your squad to ensure the completion of the mission. Avoid civilian casualites when possible. Board the shuttle when your team is ready." + else + missiondesc += " Follow orders given to you by your commander. Avoid civilian casualites when possible." + missiondesc += "
Your Mission: [mission]" + ERTOperative << missiondesc + + if(config.enforce_human_authority) + ERTOperative.set_species("human") + + //Logging and cleanup + if(numagents == 1) + message_admins("A Code [alert] emergency response team has spawned with the mission: [mission]") + log_game("[key_name(ERTOperative)] has been selected as an Emergency Response Officer") + numagents-- + teamSpawned++ + + if (teamSpawned) + return 1 + else + return 0 + + return + +//Abductors +/datum/admins/proc/makeAbductorTeam() + new /datum/round_event/abductor + return 1 + +/datum/admins/proc/makeRevenant() + var/list/mob/dead/observer/candidates = pollCandidates("Do you wish to be considered for becoming a revenant?", "revenant", null) + if(candidates.len >= 1) + var/spook_op = pick(candidates) + var/mob/dead/observer/O = spook_op + candidates -= spook_op + var/mob/living/simple_animal/revenant/revvie = new /mob/living/simple_animal/revenant(get_turf(O)) + revvie.key = O.key + revvie.mind.assigned_role = "revenant" + revvie.mind.special_role = "Revenant" + return 1 + else + return + +//Shadowling +/datum/admins/proc/makeShadowling() + var/datum/game_mode/shadowling/temp = new + if(config.protect_roles_from_antagonist) + temp.restricted_jobs += temp.protected_jobs + if(config.protect_assistant_from_antagonist) + temp.restricted_jobs += "Assistant" + var/list/mob/living/carbon/human/candidates = list() + var/mob/living/carbon/human/H = null + for(var/mob/living/carbon/human/applicant in player_list) + if(ROLE_SHADOWLING in applicant.client.prefs.be_special) + if(!applicant.stat) + if(applicant.mind) + if(!applicant.mind.special_role) + if(!jobban_isbanned(applicant, "shadowling") && !jobban_isbanned(applicant, "Syndicate")) + if(temp.age_check(applicant.client)) + if(!(applicant.job in temp.restricted_jobs)) + if(!(is_shadow_or_thrall(applicant))) + candidates += applicant + + if(candidates.len) + H = pick(candidates) + ticker.mode.shadows += H.mind + H.mind.special_role = "shadowling" + H << "Something stirs in the space between worlds. A red light floods your mind, and suddenly you understand. Your human disguise has served you well, but it \ + is time you cast it away. You are a shadowling, and you are to ascend at all costs." + ticker.mode.finalize_shadowling(H.mind) + message_admins("[H] has been made into a shadowling.") + candidates.Remove(H) + return 1 + return 0 diff --git a/code/modules/awaymissions/corpse.dm b/code/modules/awaymissions/corpse.dm index de416f2e304..bf87018e786 100644 --- a/code/modules/awaymissions/corpse.dm +++ b/code/modules/awaymissions/corpse.dm @@ -1,345 +1,345 @@ -//These are meant for spawning on maps, namely Away Missions. - -//If someone can do this in a neater way, be my guest-Kor - -//To do: Allow corpses to appear mangled, bloody, etc. Allow customizing the bodies appearance (they're all bald and white right now). - -/obj/effect/landmark/corpse - name = "Unknown" - var/mobname = "default" //Use for the ghost spawner variant, so they don't come out named "sleeper" - var/mobgender = MALE //Set to male by default due to the patriarchy. Other options include FEMALE and NEUTER - var/mob_species = null //Set to make them a mutant race such as lizard or skeleton. Uses the datum typepath instead of the ID. - var/corpseuniform = null //Set this to an object path to have the slot filled with said object on the corpse. - var/corpsesuit = null - var/corpseshoes = null - var/corpsegloves = null - var/corpseradio = null - var/corpseglasses = null - var/corpsemask = null - var/corpsehelmet = null - var/corpsebelt = null - var/corpsepocket1 = null - var/corpsepocket2 = null - var/corpseback = null - var/corpseid = 0 //Just set to 1 if you want them to have an ID - var/corpseidjob = null // Needs to be in quotes, such as "Clown" or "Chef." This just determines what the ID reads as, not their access - var/corpseidaccess = null //This is for access. See access.dm for which jobs give what access. Again, put in quotes. Use "Captain" if you want it to be all access. - var/corpseidicon = null //For setting it to be a gold, silver, centcom etc ID - var/corpsehusk = null - var/corpsebrute = null //set brute damage on the corpse - var/corpseoxy = null //set suffocation damage on the corpse - var/roundstart = TRUE - var/death = TRUE - var/flavour_text = "The mapper forgot to set this!" - var/faction = null - density = 1 - -/obj/effect/landmark/corpse/initialize() - if(roundstart) - createCorpse(death = src.death) - else - return - -/obj/effect/landmark/corpse/New() - ..() - invisibility = 0 - -/obj/effect/landmark/corpse/proc/createCorpse(death, ckey) //Creates a mob and checks for gear in each slot before attempting to equip it. - var/mob/living/carbon/human/M = new /mob/living/carbon/human (src.loc) - if(mobname != "default") - M.real_name = mobname - else - M.real_name = src.name - M.gender = src.mobgender - if(mob_species) - M.set_species(mob_species) - if(death) - M.death(1) //Kills the new mob - if(src.corpsehusk) - M.Drain() - if(faction) - M.faction = list(src.faction) - M.adjustBruteLoss(src.corpsebrute) - M.adjustOxyLoss(src.corpseoxy) - if(src.corpseuniform) - M.equip_to_slot_or_del(new src.corpseuniform(M), slot_w_uniform) - if(src.corpsesuit) - M.equip_to_slot_or_del(new src.corpsesuit(M), slot_wear_suit) - if(src.corpseshoes) - M.equip_to_slot_or_del(new src.corpseshoes(M), slot_shoes) - if(src.corpsegloves) - M.equip_to_slot_or_del(new src.corpsegloves(M), slot_gloves) - if(src.corpseradio) - M.equip_to_slot_or_del(new src.corpseradio(M), slot_ears) - if(src.corpseglasses) - M.equip_to_slot_or_del(new src.corpseglasses(M), slot_glasses) - if(src.corpsemask) - M.equip_to_slot_or_del(new src.corpsemask(M), slot_wear_mask) - if(src.corpsehelmet) - M.equip_to_slot_or_del(new src.corpsehelmet(M), slot_head) - if(src.corpsebelt) - M.equip_to_slot_or_del(new src.corpsebelt(M), slot_belt) - if(src.corpsepocket1) - M.equip_to_slot_or_del(new src.corpsepocket1(M), slot_r_store) - if(src.corpsepocket2) - M.equip_to_slot_or_del(new src.corpsepocket2(M), slot_l_store) - if(src.corpseback) - M.equip_to_slot_or_del(new src.corpseback(M), slot_back) - if(src.corpseid == 1) - var/obj/item/weapon/card/id/W = new(M) - var/datum/job/jobdatum - for(var/jobtype in typesof(/datum/job)) - var/datum/job/J = new jobtype - if(J.title == corpseidaccess) - jobdatum = J - break - if(src.corpseidicon) - W.icon_state = corpseidicon - if(src.corpseidaccess) - if(jobdatum) - W.access = jobdatum.get_access() - else - W.access = list() - if(corpseidjob) - W.assignment = corpseidjob - W.registered_name = M.real_name - W.update_label() - M.equip_to_slot_or_del(W, slot_wear_id) - if(ckey) - M.ckey = ckey - M << "[flavour_text]" - qdel(src) - -/obj/effect/landmark/corpse/AICorpse/createCorpse() //Creates a corrupted AI - var/A = locate(/mob/living/silicon/ai) in loc //variable A looks for an AI at the location of the landmark - if(A) //if variable A is true - return //stop executing the proc - var/L = new /datum/ai_laws/default/asimov/ //variable L is a new Asimov lawset - var/B = new /obj/item/device/mmi/ //variable B is a new MMI - var/mob/living/silicon/ai/M = new(src.loc, L, B, 1) //spawn new AI at landmark as var M - M.name = src.name - M.real_name = src.name - M.aiPDA.toff = 1 //turns the AI's PDA messenger off, stopping it showing up on player PDAs - M.death() //call the AI's death proc - qdel(src) - -/obj/effect/landmark/corpse/slimeCorpse - var/mobcolour = "grey" - icon = 'icons/mob/slimes.dmi' - icon_state = "grey baby slime" //sets the icon in the map editor - -/obj/effect/landmark/corpse/slimeCorpse/createCorpse() //proc creates a dead slime - var/A = locate(/mob/living/simple_animal/slime/) in loc //variable A looks for a slime at the location of the landmark - if(A) //if variable A is true - return //stop executing the proc - var/mob/living/simple_animal/slime/M = new(src.loc) //variable M is a new slime at the location of the landmark - M.colour = src.mobcolour //slime colour is set by landmark's mobcolour var - M.adjustToxLoss(9001) //kills the slime, death() doesn't update its icon correctly - qdel(src) - -/obj/effect/landmark/corpse/facehugCorpse/createCorpse() //Creates a squashed facehugger - var/obj/item/clothing/mask/facehugger/O = new(src.loc) //variable O is a new facehugger at the location of the landmark - O.name = src.name - O.Die() //call the facehugger's death proc - qdel(src) - - -// I'll work on making a list of corpses people request for maps, or that I think will be commonly used. Syndicate operatives for example. - -/obj/effect/landmark/corpse/syndicatesoldier - name = "Syndicate Operative" - corpseuniform = /obj/item/clothing/under/syndicate - corpsesuit = /obj/item/clothing/suit/armor/vest - corpseshoes = /obj/item/clothing/shoes/combat - corpsegloves = /obj/item/clothing/gloves/combat - corpseradio = /obj/item/device/radio/headset - corpsemask = /obj/item/clothing/mask/gas - corpsehelmet = /obj/item/clothing/head/helmet/swat - corpseback = /obj/item/weapon/storage/backpack - corpseid = 1 - corpseidjob = "Operative" - corpseidaccess = "Syndicate" - - - -/obj/effect/landmark/corpse/syndicatecommando - name = "Syndicate Commando" - corpseuniform = /obj/item/clothing/under/syndicate - corpsesuit = /obj/item/clothing/suit/space/hardsuit/syndi - corpseshoes = /obj/item/clothing/shoes/combat - corpsegloves = /obj/item/clothing/gloves/combat - corpseradio = /obj/item/device/radio/headset - corpsemask = /obj/item/clothing/mask/gas/syndicate - corpsehelmet = /obj/item/clothing/head/helmet/space/hardsuit/syndi - corpseback = /obj/item/weapon/tank/jetpack/oxygen - corpsepocket1 = /obj/item/weapon/tank/internals/emergency_oxygen - corpseid = 1 - corpseidjob = "Operative" - corpseidaccess = "Syndicate" - - - -///////////Civilians////////////////////// - -/obj/effect/landmark/corpse/cook - name = "Cook" - corpseuniform = /obj/item/clothing/under/rank/chef - corpsesuit = /obj/item/clothing/suit/apron/chef - corpseshoes = /obj/item/clothing/shoes/sneakers/black - corpsehelmet = /obj/item/clothing/head/chefhat - corpseback = /obj/item/weapon/storage/backpack - corpseradio = /obj/item/device/radio/headset - corpseid = 1 - corpseidjob = "Cook" - corpseidaccess = "Cook" - - -/obj/effect/landmark/corpse/doctor - name = "Doctor" - corpseradio = /obj/item/device/radio/headset/headset_med - corpseuniform = /obj/item/clothing/under/rank/medical - corpsesuit = /obj/item/clothing/suit/toggle/labcoat - corpseback = /obj/item/weapon/storage/backpack/medic - corpsepocket1 = /obj/item/device/flashlight/pen - corpseshoes = /obj/item/clothing/shoes/sneakers/black - corpseid = 1 - corpseidjob = "Medical Doctor" - corpseidaccess = "Medical Doctor" - -/obj/effect/landmark/corpse/engineer - name = "Engineer" - corpseradio = /obj/item/device/radio/headset/headset_eng - corpseuniform = /obj/item/clothing/under/rank/engineer - corpseback = /obj/item/weapon/storage/backpack/industrial - corpseshoes = /obj/item/clothing/shoes/sneakers/orange - corpsebelt = /obj/item/weapon/storage/belt/utility/full - corpsegloves = /obj/item/clothing/gloves/color/yellow - corpsehelmet = /obj/item/clothing/head/hardhat - corpseid = 1 - corpseidjob = "Station Engineer" - corpseidaccess = "Station Engineer" - -/obj/effect/landmark/corpse/engineer/rig - corpsesuit = /obj/item/clothing/suit/space/hardsuit/engine - corpsemask = /obj/item/clothing/mask/breath - -/obj/effect/landmark/corpse/clown - name = "Clown" - corpseuniform = /obj/item/clothing/under/rank/clown - corpseshoes = /obj/item/clothing/shoes/clown_shoes - corpseradio = /obj/item/device/radio/headset - corpsemask = /obj/item/clothing/mask/gas/clown_hat - corpsepocket1 = /obj/item/weapon/bikehorn - corpseback = /obj/item/weapon/storage/backpack/clown - corpseid = 1 - corpseidjob = "Clown" - corpseidaccess = "Clown" - -/obj/effect/landmark/corpse/scientist - name = "Scientist" - corpseradio = /obj/item/device/radio/headset/headset_sci - corpseuniform = /obj/item/clothing/under/rank/scientist - corpsesuit = /obj/item/clothing/suit/toggle/labcoat/science - corpseback = /obj/item/weapon/storage/backpack - corpseshoes = /obj/item/clothing/shoes/sneakers/white - corpseid = 1 - corpseidjob = "Scientist" - corpseidaccess = "Scientist" - -/obj/effect/landmark/corpse/miner - corpseradio = /obj/item/device/radio/headset/headset_cargo - corpseuniform = /obj/item/clothing/under/rank/miner - corpsegloves = /obj/item/clothing/gloves/fingerless - corpseback = /obj/item/weapon/storage/backpack/industrial - corpseshoes = /obj/item/clothing/shoes/sneakers/black - corpseid = 1 - corpseidjob = "Shaft Miner" - corpseidaccess = "Shaft Miner" - -/obj/effect/landmark/corpse/miner/rig - corpsesuit = /obj/item/clothing/suit/space/hardsuit/mining - corpsemask = /obj/item/clothing/mask/breath - - -/obj/effect/landmark/corpse/plasmaman - mob_species = /datum/species/plasmaman - corpsehelmet = /obj/item/clothing/head/helmet/space/plasmaman - corpseuniform = /obj/item/clothing/under/plasmaman - corpseback = /obj/item/weapon/tank/internals/plasmaman/full - corpsemask = /obj/item/clothing/mask/breath - - - -/////////////////Officers////////////////////// - -/obj/effect/landmark/corpse/bridgeofficer - name = "Bridge Officer" - corpseradio = /obj/item/device/radio/headset/heads/hop - corpseuniform = /obj/item/clothing/under/rank/centcom_officer - corpsesuit = /obj/item/clothing/suit/armor/bulletproof - corpseshoes = /obj/item/clothing/shoes/sneakers/black - corpseglasses = /obj/item/clothing/glasses/sunglasses - corpseid = 1 - corpseidjob = "Bridge Officer" - corpseidaccess = "Captain" - -/obj/effect/landmark/corpse/commander - name = "Commander" - corpseuniform = /obj/item/clothing/under/rank/centcom_commander - corpsesuit = /obj/item/clothing/suit/armor - corpseradio = /obj/item/device/radio/headset/heads/captain - corpseglasses = /obj/item/clothing/glasses/eyepatch - corpsemask = /obj/item/clothing/mask/cigarette/cigar/cohiba - corpsehelmet = /obj/item/clothing/head/centhat - corpsegloves = /obj/item/clothing/gloves/combat - corpseshoes = /obj/item/clothing/shoes/combat/swat - corpsepocket1 = /obj/item/weapon/lighter - corpseid = 1 - corpseidjob = "Commander" - corpseidaccess = "Captain" - -/obj/effect/landmark/corpse/commander/alive - death = FALSE - roundstart = FALSE - mobname = "Nanotrasen Commander" - name = "sleeper" - icon = 'icons/obj/Cryogenic2.dmi' - icon_state = "sleeper" - flavour_text = "You are a Nanotrasen Commander!" - -/obj/effect/landmark/corpse/attack_ghost(mob/user) - if(ticker.current_state != GAME_STATE_PLAYING) - return - var/ghost_role = alert("Become [mobname]? (Warning, You can no longer be cloned!)",,"Yes","No") - if(ghost_role == "No") - return - createCorpse(death = src.death, ckey = user.ckey) - -/////////////////Spooky Undead////////////////////// - -/obj/effect/landmark/corpse/skeleton - name = "skeletal remains" - mobname = "skeleton" - mob_species = /datum/species/skeleton - mobgender = NEUTER - - -/obj/effect/landmark/corpse/skeleton/alive - death = FALSE - roundstart = FALSE - icon = 'icons/effects/blood.dmi' - icon_state = "remains" - flavour_text = "By unknown powers, your skeletal remains have been reanimated! Walk this mortal plain and terrorize all living adventurers who dare cross your path." - - -/obj/effect/landmark/corpse/zombie - name = "rotting corpse" - mobname = "zombie" - mob_species = /datum/species/zombie - -/obj/effect/landmark/corpse/zombie/alive - death = FALSE - roundstart = FALSE - icon = 'icons/effects/blood.dmi' - icon_state = "remains" - flavour_text = "By unknown powers, your rotting remains have been resurrected! Walk this mortal plain and terrorize all living adventurers who dare cross your path." +//These are meant for spawning on maps, namely Away Missions. + +//If someone can do this in a neater way, be my guest-Kor + +//To do: Allow corpses to appear mangled, bloody, etc. Allow customizing the bodies appearance (they're all bald and white right now). + +/obj/effect/landmark/corpse + name = "Unknown" + var/mobname = "default" //Use for the ghost spawner variant, so they don't come out named "sleeper" + var/mobgender = MALE //Set to male by default due to the patriarchy. Other options include FEMALE and NEUTER + var/mob_species = null //Set to make them a mutant race such as lizard or skeleton. Uses the datum typepath instead of the ID. + var/corpseuniform = null //Set this to an object path to have the slot filled with said object on the corpse. + var/corpsesuit = null + var/corpseshoes = null + var/corpsegloves = null + var/corpseradio = null + var/corpseglasses = null + var/corpsemask = null + var/corpsehelmet = null + var/corpsebelt = null + var/corpsepocket1 = null + var/corpsepocket2 = null + var/corpseback = null + var/corpseid = 0 //Just set to 1 if you want them to have an ID + var/corpseidjob = null // Needs to be in quotes, such as "Clown" or "Chef." This just determines what the ID reads as, not their access + var/corpseidaccess = null //This is for access. See access.dm for which jobs give what access. Again, put in quotes. Use "Captain" if you want it to be all access. + var/corpseidicon = null //For setting it to be a gold, silver, centcom etc ID + var/corpsehusk = null + var/corpsebrute = null //set brute damage on the corpse + var/corpseoxy = null //set suffocation damage on the corpse + var/roundstart = TRUE + var/death = TRUE + var/flavour_text = "The mapper forgot to set this!" + var/faction = null + density = 1 + +/obj/effect/landmark/corpse/initialize() + if(roundstart) + createCorpse(death = src.death) + else + return + +/obj/effect/landmark/corpse/New() + ..() + invisibility = 0 + +/obj/effect/landmark/corpse/proc/createCorpse(death, ckey) //Creates a mob and checks for gear in each slot before attempting to equip it. + var/mob/living/carbon/human/M = new /mob/living/carbon/human (src.loc) + if(mobname != "default") + M.real_name = mobname + else + M.real_name = src.name + M.gender = src.mobgender + if(mob_species) + M.set_species(mob_species) + if(death) + M.death(1) //Kills the new mob + if(src.corpsehusk) + M.Drain() + if(faction) + M.faction = list(src.faction) + M.adjustBruteLoss(src.corpsebrute) + M.adjustOxyLoss(src.corpseoxy) + if(src.corpseuniform) + M.equip_to_slot_or_del(new src.corpseuniform(M), slot_w_uniform) + if(src.corpsesuit) + M.equip_to_slot_or_del(new src.corpsesuit(M), slot_wear_suit) + if(src.corpseshoes) + M.equip_to_slot_or_del(new src.corpseshoes(M), slot_shoes) + if(src.corpsegloves) + M.equip_to_slot_or_del(new src.corpsegloves(M), slot_gloves) + if(src.corpseradio) + M.equip_to_slot_or_del(new src.corpseradio(M), slot_ears) + if(src.corpseglasses) + M.equip_to_slot_or_del(new src.corpseglasses(M), slot_glasses) + if(src.corpsemask) + M.equip_to_slot_or_del(new src.corpsemask(M), slot_wear_mask) + if(src.corpsehelmet) + M.equip_to_slot_or_del(new src.corpsehelmet(M), slot_head) + if(src.corpsebelt) + M.equip_to_slot_or_del(new src.corpsebelt(M), slot_belt) + if(src.corpsepocket1) + M.equip_to_slot_or_del(new src.corpsepocket1(M), slot_r_store) + if(src.corpsepocket2) + M.equip_to_slot_or_del(new src.corpsepocket2(M), slot_l_store) + if(src.corpseback) + M.equip_to_slot_or_del(new src.corpseback(M), slot_back) + if(src.corpseid == 1) + var/obj/item/weapon/card/id/W = new(M) + var/datum/job/jobdatum + for(var/jobtype in typesof(/datum/job)) + var/datum/job/J = new jobtype + if(J.title == corpseidaccess) + jobdatum = J + break + if(src.corpseidicon) + W.icon_state = corpseidicon + if(src.corpseidaccess) + if(jobdatum) + W.access = jobdatum.get_access() + else + W.access = list() + if(corpseidjob) + W.assignment = corpseidjob + W.registered_name = M.real_name + W.update_label() + M.equip_to_slot_or_del(W, slot_wear_id) + if(ckey) + M.ckey = ckey + M << "[flavour_text]" + qdel(src) + +/obj/effect/landmark/corpse/AICorpse/createCorpse() //Creates a corrupted AI + var/A = locate(/mob/living/silicon/ai) in loc //variable A looks for an AI at the location of the landmark + if(A) //if variable A is true + return //stop executing the proc + var/L = new /datum/ai_laws/default/asimov/ //variable L is a new Asimov lawset + var/B = new /obj/item/device/mmi/ //variable B is a new MMI + var/mob/living/silicon/ai/M = new(src.loc, L, B, 1) //spawn new AI at landmark as var M + M.name = src.name + M.real_name = src.name + M.aiPDA.toff = 1 //turns the AI's PDA messenger off, stopping it showing up on player PDAs + M.death() //call the AI's death proc + qdel(src) + +/obj/effect/landmark/corpse/slimeCorpse + var/mobcolour = "grey" + icon = 'icons/mob/slimes.dmi' + icon_state = "grey baby slime" //sets the icon in the map editor + +/obj/effect/landmark/corpse/slimeCorpse/createCorpse() //proc creates a dead slime + var/A = locate(/mob/living/simple_animal/slime/) in loc //variable A looks for a slime at the location of the landmark + if(A) //if variable A is true + return //stop executing the proc + var/mob/living/simple_animal/slime/M = new(src.loc) //variable M is a new slime at the location of the landmark + M.colour = src.mobcolour //slime colour is set by landmark's mobcolour var + M.adjustToxLoss(9001) //kills the slime, death() doesn't update its icon correctly + qdel(src) + +/obj/effect/landmark/corpse/facehugCorpse/createCorpse() //Creates a squashed facehugger + var/obj/item/clothing/mask/facehugger/O = new(src.loc) //variable O is a new facehugger at the location of the landmark + O.name = src.name + O.Die() //call the facehugger's death proc + qdel(src) + + +// I'll work on making a list of corpses people request for maps, or that I think will be commonly used. Syndicate operatives for example. + +/obj/effect/landmark/corpse/syndicatesoldier + name = "Syndicate Operative" + corpseuniform = /obj/item/clothing/under/syndicate + corpsesuit = /obj/item/clothing/suit/armor/vest + corpseshoes = /obj/item/clothing/shoes/combat + corpsegloves = /obj/item/clothing/gloves/combat + corpseradio = /obj/item/device/radio/headset + corpsemask = /obj/item/clothing/mask/gas + corpsehelmet = /obj/item/clothing/head/helmet/swat + corpseback = /obj/item/weapon/storage/backpack + corpseid = 1 + corpseidjob = "Operative" + corpseidaccess = "Syndicate" + + + +/obj/effect/landmark/corpse/syndicatecommando + name = "Syndicate Commando" + corpseuniform = /obj/item/clothing/under/syndicate + corpsesuit = /obj/item/clothing/suit/space/hardsuit/syndi + corpseshoes = /obj/item/clothing/shoes/combat + corpsegloves = /obj/item/clothing/gloves/combat + corpseradio = /obj/item/device/radio/headset + corpsemask = /obj/item/clothing/mask/gas/syndicate + corpsehelmet = /obj/item/clothing/head/helmet/space/hardsuit/syndi + corpseback = /obj/item/weapon/tank/jetpack/oxygen + corpsepocket1 = /obj/item/weapon/tank/internals/emergency_oxygen + corpseid = 1 + corpseidjob = "Operative" + corpseidaccess = "Syndicate" + + + +///////////Civilians////////////////////// + +/obj/effect/landmark/corpse/cook + name = "Cook" + corpseuniform = /obj/item/clothing/under/rank/chef + corpsesuit = /obj/item/clothing/suit/apron/chef + corpseshoes = /obj/item/clothing/shoes/sneakers/black + corpsehelmet = /obj/item/clothing/head/chefhat + corpseback = /obj/item/weapon/storage/backpack + corpseradio = /obj/item/device/radio/headset + corpseid = 1 + corpseidjob = "Cook" + corpseidaccess = "Cook" + + +/obj/effect/landmark/corpse/doctor + name = "Doctor" + corpseradio = /obj/item/device/radio/headset/headset_med + corpseuniform = /obj/item/clothing/under/rank/medical + corpsesuit = /obj/item/clothing/suit/toggle/labcoat + corpseback = /obj/item/weapon/storage/backpack/medic + corpsepocket1 = /obj/item/device/flashlight/pen + corpseshoes = /obj/item/clothing/shoes/sneakers/black + corpseid = 1 + corpseidjob = "Medical Doctor" + corpseidaccess = "Medical Doctor" + +/obj/effect/landmark/corpse/engineer + name = "Engineer" + corpseradio = /obj/item/device/radio/headset/headset_eng + corpseuniform = /obj/item/clothing/under/rank/engineer + corpseback = /obj/item/weapon/storage/backpack/industrial + corpseshoes = /obj/item/clothing/shoes/sneakers/orange + corpsebelt = /obj/item/weapon/storage/belt/utility/full + corpsegloves = /obj/item/clothing/gloves/color/yellow + corpsehelmet = /obj/item/clothing/head/hardhat + corpseid = 1 + corpseidjob = "Station Engineer" + corpseidaccess = "Station Engineer" + +/obj/effect/landmark/corpse/engineer/rig + corpsesuit = /obj/item/clothing/suit/space/hardsuit/engine + corpsemask = /obj/item/clothing/mask/breath + +/obj/effect/landmark/corpse/clown + name = "Clown" + corpseuniform = /obj/item/clothing/under/rank/clown + corpseshoes = /obj/item/clothing/shoes/clown_shoes + corpseradio = /obj/item/device/radio/headset + corpsemask = /obj/item/clothing/mask/gas/clown_hat + corpsepocket1 = /obj/item/weapon/bikehorn + corpseback = /obj/item/weapon/storage/backpack/clown + corpseid = 1 + corpseidjob = "Clown" + corpseidaccess = "Clown" + +/obj/effect/landmark/corpse/scientist + name = "Scientist" + corpseradio = /obj/item/device/radio/headset/headset_sci + corpseuniform = /obj/item/clothing/under/rank/scientist + corpsesuit = /obj/item/clothing/suit/toggle/labcoat/science + corpseback = /obj/item/weapon/storage/backpack + corpseshoes = /obj/item/clothing/shoes/sneakers/white + corpseid = 1 + corpseidjob = "Scientist" + corpseidaccess = "Scientist" + +/obj/effect/landmark/corpse/miner + corpseradio = /obj/item/device/radio/headset/headset_cargo + corpseuniform = /obj/item/clothing/under/rank/miner + corpsegloves = /obj/item/clothing/gloves/fingerless + corpseback = /obj/item/weapon/storage/backpack/industrial + corpseshoes = /obj/item/clothing/shoes/sneakers/black + corpseid = 1 + corpseidjob = "Shaft Miner" + corpseidaccess = "Shaft Miner" + +/obj/effect/landmark/corpse/miner/rig + corpsesuit = /obj/item/clothing/suit/space/hardsuit/mining + corpsemask = /obj/item/clothing/mask/breath + + +/obj/effect/landmark/corpse/plasmaman + mob_species = /datum/species/plasmaman + corpsehelmet = /obj/item/clothing/head/helmet/space/plasmaman + corpseuniform = /obj/item/clothing/under/plasmaman + corpseback = /obj/item/weapon/tank/internals/plasmaman/full + corpsemask = /obj/item/clothing/mask/breath + + + +/////////////////Officers////////////////////// + +/obj/effect/landmark/corpse/bridgeofficer + name = "Bridge Officer" + corpseradio = /obj/item/device/radio/headset/heads/hop + corpseuniform = /obj/item/clothing/under/rank/centcom_officer + corpsesuit = /obj/item/clothing/suit/armor/bulletproof + corpseshoes = /obj/item/clothing/shoes/sneakers/black + corpseglasses = /obj/item/clothing/glasses/sunglasses + corpseid = 1 + corpseidjob = "Bridge Officer" + corpseidaccess = "Captain" + +/obj/effect/landmark/corpse/commander + name = "Commander" + corpseuniform = /obj/item/clothing/under/rank/centcom_commander + corpsesuit = /obj/item/clothing/suit/armor + corpseradio = /obj/item/device/radio/headset/heads/captain + corpseglasses = /obj/item/clothing/glasses/eyepatch + corpsemask = /obj/item/clothing/mask/cigarette/cigar/cohiba + corpsehelmet = /obj/item/clothing/head/centhat + corpsegloves = /obj/item/clothing/gloves/combat + corpseshoes = /obj/item/clothing/shoes/combat/swat + corpsepocket1 = /obj/item/weapon/lighter + corpseid = 1 + corpseidjob = "Commander" + corpseidaccess = "Captain" + +/obj/effect/landmark/corpse/commander/alive + death = FALSE + roundstart = FALSE + mobname = "Nanotrasen Commander" + name = "sleeper" + icon = 'icons/obj/Cryogenic2.dmi' + icon_state = "sleeper" + flavour_text = "You are a Nanotrasen Commander!" + +/obj/effect/landmark/corpse/attack_ghost(mob/user) + if(ticker.current_state != GAME_STATE_PLAYING) + return + var/ghost_role = alert("Become [mobname]? (Warning, You can no longer be cloned!)",,"Yes","No") + if(ghost_role == "No") + return + createCorpse(death = src.death, ckey = user.ckey) + +/////////////////Spooky Undead////////////////////// + +/obj/effect/landmark/corpse/skeleton + name = "skeletal remains" + mobname = "skeleton" + mob_species = /datum/species/skeleton + mobgender = NEUTER + + +/obj/effect/landmark/corpse/skeleton/alive + death = FALSE + roundstart = FALSE + icon = 'icons/effects/blood.dmi' + icon_state = "remains" + flavour_text = "By unknown powers, your skeletal remains have been reanimated! Walk this mortal plain and terrorize all living adventurers who dare cross your path." + + +/obj/effect/landmark/corpse/zombie + name = "rotting corpse" + mobname = "zombie" + mob_species = /datum/species/zombie + +/obj/effect/landmark/corpse/zombie/alive + death = FALSE + roundstart = FALSE + icon = 'icons/effects/blood.dmi' + icon_state = "remains" + flavour_text = "By unknown powers, your rotting remains have been resurrected! Walk this mortal plain and terrorize all living adventurers who dare cross your path." diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm index 76630a3dc1f..91198db5fe4 100644 --- a/code/modules/client/asset_cache.dm +++ b/code/modules/client/asset_cache.dm @@ -1,240 +1,240 @@ -/* -Asset cache quick users guide: - -Make a datum at the bottom of this file with your assets for your thing. -The simple subsystem will most like be of use for most cases. -Then call get_asset_datum() with the type of the datum you created and store the return -Then call .send(client) on that stored return value. - -You can set verify to TRUE if you want send() to sleep until the client has the assets. -*/ - - -// Amount of time(ds) MAX to send per asset, if this get exceeded we cancel the sleeping. -// This is doubled for the first asset, then added per asset after -#define ASSET_CACHE_SEND_TIMEOUT 7 - -//When sending mutiple assets, how many before we give the client a quaint little sending resources message -#define ASSET_CACHE_TELL_CLIENT_AMOUNT 8 - -/client - var/list/cache = list() // List of all assets sent to this client by the asset cache. - var/list/completed_asset_jobs = list() // List of all completed jobs, awaiting acknowledgement. - var/list/sending = list() - var/last_asset_job = 0 // Last job done. - -//This proc sends the asset to the client, but only if it needs it. -//This proc blocks(sleeps) unless verify is set to false -/proc/send_asset(var/client/client, var/asset_name, var/verify = TRUE) - if(!istype(client)) - if(ismob(client)) - var/mob/M = client - if(M.client) - client = M.client - - else - return 0 - - else - return 0 - - if(client.cache.Find(asset_name) || client.sending.Find(asset_name)) - return 0 - - client << browse_rsc(SSasset.cache[asset_name], asset_name) - if(!verify || !winexists(client, "asset_cache_browser")) // Can't access the asset cache browser, rip. - if (client) - client.cache += asset_name - return 1 - if (!client) - return 0 - - client.sending |= asset_name - var/job = ++client.last_asset_job - - client << browse({" - - "}, "window=asset_cache_browser") - - var/t = 0 - var/timeout_time = (ASSET_CACHE_SEND_TIMEOUT * client.sending.len) + ASSET_CACHE_SEND_TIMEOUT - while(client && !client.completed_asset_jobs.Find(job) && t < timeout_time) // Reception is handled in Topic() - sleep(1) // Lock up the caller until this is received. - t++ - - if(client) - client.sending -= asset_name - client.cache |= asset_name - client.completed_asset_jobs -= job - - return 1 - -//This proc blocks(sleeps) unless verify is set to false -/proc/send_asset_list(var/client/client, var/list/asset_list, var/verify = TRUE) - if(!istype(client)) - if(ismob(client)) - var/mob/M = client - if(M.client) - client = M.client - - else - return 0 - - else - return 0 - - var/list/unreceived = asset_list - (client.cache + client.sending) - if(!unreceived || !unreceived.len) - return 0 - if (unreceived.len >= ASSET_CACHE_TELL_CLIENT_AMOUNT) - client << "Sending Resources..." - for(var/asset in unreceived) - if (asset in SSasset.cache) - client << browse_rsc(SSasset.cache[asset], asset) - - if(!verify || !winexists(client, "asset_cache_browser")) // Can't access the asset cache browser, rip. - if (client) - client.cache += unreceived - return 1 - if (!client) - return 0 - client.sending |= unreceived - var/job = ++client.last_asset_job - - client << browse({" - - "}, "window=asset_cache_browser") - - var/t = 0 - var/timeout_time = ASSET_CACHE_SEND_TIMEOUT * client.sending.len - while(client && !client.completed_asset_jobs.Find(job) && t < timeout_time) // Reception is handled in Topic() - sleep(1) // Lock up the caller until this is received. - t++ - - if(client) - client.sending -= unreceived - client.cache |= unreceived - client.completed_asset_jobs -= job - - return 1 - -//This proc will download the files without clogging up the browse() queue, used for passively sending files on connection start. -//The proc calls procs that sleep for long times. -/proc/getFilesSlow(var/client/client, var/list/files, var/register_asset = TRUE) - for(var/file in files) - if (!client) - break - if (register_asset) - register_asset(file,files[file]) - send_asset(client,file) - sleep(-1) //queuing calls like this too quickly can cause issues in some client versions - -//This proc "registers" an asset, it adds it to the cache for further use, you cannot touch it from this point on or you'll fuck things up. -//if it's an icon or something be careful, you'll have to copy it before further use. -/proc/register_asset(var/asset_name, var/asset) - SSasset.cache[asset_name] = asset - -//These datums are used to populate the asset cache, the proc "register()" does this. - -//all of our asset datums, used for referring to these later -/var/global/list/asset_datums = list() - -//get a assetdatum or make a new one -/proc/get_asset_datum(var/type) - if (!(type in asset_datums)) - return new type() - return asset_datums[type] - -/datum/asset/New() - asset_datums[type] = src - -/datum/asset/proc/register() - return - -/datum/asset/proc/send(client) - return - -//If you don't need anything complicated. -/datum/asset/simple - var/assets = list() - var/verify = FALSE - -/datum/asset/simple/register() - for(var/asset_name in assets) - register_asset(asset_name, assets[asset_name]) -/datum/asset/simple/send(client) - send_asset_list(client,assets,verify) - - -//DEFINITIONS FOR ASSET DATUMS START HERE. - - -/datum/asset/simple/nanoui - assets = list( - "nanoui.lib.js" = 'nano/assets/nanoui.lib.js', - "nanoui.main.js" = 'nano/assets/nanoui.main.js', - "nanoui.templates.js" = 'nano/assets/nanoui.templates.js', - "nanoui.lib.css" = 'nano/assets/nanoui.lib.css', - "nanoui.common.css" = 'nano/assets/nanoui.common.css', - "nanoui.generic.css" = 'nano/assets/nanoui.generic.css', - "nanoui.nanotrasen.css" = 'nano/assets/nanoui.nanotrasen.css', - "fontawesome-webfont.eot" = 'nano/assets/fontawesome-webfont.eot', - "fontawesome-webfont.woff2" = 'nano/assets/fontawesome-webfont.woff2' - ) - -/datum/asset/simple/pda - assets = list( - "pda_atmos.png" = 'icons/pda_icons/pda_atmos.png', - "pda_back.png" = 'icons/pda_icons/pda_back.png', - "pda_bell.png" = 'icons/pda_icons/pda_bell.png', - "pda_blank.png" = 'icons/pda_icons/pda_blank.png', - "pda_boom.png" = 'icons/pda_icons/pda_boom.png', - "pda_bucket.png" = 'icons/pda_icons/pda_bucket.png', - "pda_medbot.png" = 'icons/pda_icons/pda_medbot.png', - "pda_floorbot.png" = 'icons/pda_icons/pda_floorbot.png', - "pda_cleanbot.png" = 'icons/pda_icons/pda_cleanbot.png', - "pda_crate.png" = 'icons/pda_icons/pda_crate.png', - "pda_cuffs.png" = 'icons/pda_icons/pda_cuffs.png', - "pda_eject.png" = 'icons/pda_icons/pda_eject.png', - "pda_exit.png" = 'icons/pda_icons/pda_exit.png', - "pda_flashlight.png" = 'icons/pda_icons/pda_flashlight.png', - "pda_honk.png" = 'icons/pda_icons/pda_honk.png', - "pda_mail.png" = 'icons/pda_icons/pda_mail.png', - "pda_medical.png" = 'icons/pda_icons/pda_medical.png', - "pda_menu.png" = 'icons/pda_icons/pda_menu.png', - "pda_mule.png" = 'icons/pda_icons/pda_mule.png', - "pda_notes.png" = 'icons/pda_icons/pda_notes.png', - "pda_power.png" = 'icons/pda_icons/pda_power.png', - "pda_rdoor.png" = 'icons/pda_icons/pda_rdoor.png', - "pda_reagent.png" = 'icons/pda_icons/pda_reagent.png', - "pda_refresh.png" = 'icons/pda_icons/pda_refresh.png', - "pda_scanner.png" = 'icons/pda_icons/pda_scanner.png', - "pda_signaler.png" = 'icons/pda_icons/pda_signaler.png', - "pda_status.png" = 'icons/pda_icons/pda_status.png' - ) - -/datum/asset/simple/paper - assets = list( - "large_stamp-clown.png" = 'icons/stamp_icons/large_stamp-clown.png', - "large_stamp-deny.png" = 'icons/stamp_icons/large_stamp-deny.png', - "large_stamp-ok.png" = 'icons/stamp_icons/large_stamp-ok.png', - "large_stamp-hop.png" = 'icons/stamp_icons/large_stamp-hop.png', - "large_stamp-cmo.png" = 'icons/stamp_icons/large_stamp-cmo.png', - "large_stamp-ce.png" = 'icons/stamp_icons/large_stamp-ce.png', - "large_stamp-hos.png" = 'icons/stamp_icons/large_stamp-hos.png', - "large_stamp-rd.png" = 'icons/stamp_icons/large_stamp-rd.png', - "large_stamp-cap.png" = 'icons/stamp_icons/large_stamp-cap.png', - "large_stamp-qm.png" = 'icons/stamp_icons/large_stamp-qm.png', - "large_stamp-law.png" = 'icons/stamp_icons/large_stamp-law.png' - ) - - -//Registers HTML Interface assets. -/datum/asset/HTML_interface/register() - for(var/path in typesof(/datum/html_interface)) - var/datum/html_interface/hi = new path() - hi.registerResources() +/* +Asset cache quick users guide: + +Make a datum at the bottom of this file with your assets for your thing. +The simple subsystem will most like be of use for most cases. +Then call get_asset_datum() with the type of the datum you created and store the return +Then call .send(client) on that stored return value. + +You can set verify to TRUE if you want send() to sleep until the client has the assets. +*/ + + +// Amount of time(ds) MAX to send per asset, if this get exceeded we cancel the sleeping. +// This is doubled for the first asset, then added per asset after +#define ASSET_CACHE_SEND_TIMEOUT 7 + +//When sending mutiple assets, how many before we give the client a quaint little sending resources message +#define ASSET_CACHE_TELL_CLIENT_AMOUNT 8 + +/client + var/list/cache = list() // List of all assets sent to this client by the asset cache. + var/list/completed_asset_jobs = list() // List of all completed jobs, awaiting acknowledgement. + var/list/sending = list() + var/last_asset_job = 0 // Last job done. + +//This proc sends the asset to the client, but only if it needs it. +//This proc blocks(sleeps) unless verify is set to false +/proc/send_asset(var/client/client, var/asset_name, var/verify = TRUE) + if(!istype(client)) + if(ismob(client)) + var/mob/M = client + if(M.client) + client = M.client + + else + return 0 + + else + return 0 + + if(client.cache.Find(asset_name) || client.sending.Find(asset_name)) + return 0 + + client << browse_rsc(SSasset.cache[asset_name], asset_name) + if(!verify || !winexists(client, "asset_cache_browser")) // Can't access the asset cache browser, rip. + if (client) + client.cache += asset_name + return 1 + if (!client) + return 0 + + client.sending |= asset_name + var/job = ++client.last_asset_job + + client << browse({" + + "}, "window=asset_cache_browser") + + var/t = 0 + var/timeout_time = (ASSET_CACHE_SEND_TIMEOUT * client.sending.len) + ASSET_CACHE_SEND_TIMEOUT + while(client && !client.completed_asset_jobs.Find(job) && t < timeout_time) // Reception is handled in Topic() + sleep(1) // Lock up the caller until this is received. + t++ + + if(client) + client.sending -= asset_name + client.cache |= asset_name + client.completed_asset_jobs -= job + + return 1 + +//This proc blocks(sleeps) unless verify is set to false +/proc/send_asset_list(var/client/client, var/list/asset_list, var/verify = TRUE) + if(!istype(client)) + if(ismob(client)) + var/mob/M = client + if(M.client) + client = M.client + + else + return 0 + + else + return 0 + + var/list/unreceived = asset_list - (client.cache + client.sending) + if(!unreceived || !unreceived.len) + return 0 + if (unreceived.len >= ASSET_CACHE_TELL_CLIENT_AMOUNT) + client << "Sending Resources..." + for(var/asset in unreceived) + if (asset in SSasset.cache) + client << browse_rsc(SSasset.cache[asset], asset) + + if(!verify || !winexists(client, "asset_cache_browser")) // Can't access the asset cache browser, rip. + if (client) + client.cache += unreceived + return 1 + if (!client) + return 0 + client.sending |= unreceived + var/job = ++client.last_asset_job + + client << browse({" + + "}, "window=asset_cache_browser") + + var/t = 0 + var/timeout_time = ASSET_CACHE_SEND_TIMEOUT * client.sending.len + while(client && !client.completed_asset_jobs.Find(job) && t < timeout_time) // Reception is handled in Topic() + sleep(1) // Lock up the caller until this is received. + t++ + + if(client) + client.sending -= unreceived + client.cache |= unreceived + client.completed_asset_jobs -= job + + return 1 + +//This proc will download the files without clogging up the browse() queue, used for passively sending files on connection start. +//The proc calls procs that sleep for long times. +/proc/getFilesSlow(var/client/client, var/list/files, var/register_asset = TRUE) + for(var/file in files) + if (!client) + break + if (register_asset) + register_asset(file,files[file]) + send_asset(client,file) + sleep(-1) //queuing calls like this too quickly can cause issues in some client versions + +//This proc "registers" an asset, it adds it to the cache for further use, you cannot touch it from this point on or you'll fuck things up. +//if it's an icon or something be careful, you'll have to copy it before further use. +/proc/register_asset(var/asset_name, var/asset) + SSasset.cache[asset_name] = asset + +//These datums are used to populate the asset cache, the proc "register()" does this. + +//all of our asset datums, used for referring to these later +/var/global/list/asset_datums = list() + +//get a assetdatum or make a new one +/proc/get_asset_datum(var/type) + if (!(type in asset_datums)) + return new type() + return asset_datums[type] + +/datum/asset/New() + asset_datums[type] = src + +/datum/asset/proc/register() + return + +/datum/asset/proc/send(client) + return + +//If you don't need anything complicated. +/datum/asset/simple + var/assets = list() + var/verify = FALSE + +/datum/asset/simple/register() + for(var/asset_name in assets) + register_asset(asset_name, assets[asset_name]) +/datum/asset/simple/send(client) + send_asset_list(client,assets,verify) + + +//DEFINITIONS FOR ASSET DATUMS START HERE. + + +/datum/asset/simple/nanoui + assets = list( + "nanoui.lib.js" = 'nano/assets/nanoui.lib.js', + "nanoui.main.js" = 'nano/assets/nanoui.main.js', + "nanoui.templates.js" = 'nano/assets/nanoui.templates.js', + "nanoui.lib.css" = 'nano/assets/nanoui.lib.css', + "nanoui.common.css" = 'nano/assets/nanoui.common.css', + "nanoui.generic.css" = 'nano/assets/nanoui.generic.css', + "nanoui.nanotrasen.css" = 'nano/assets/nanoui.nanotrasen.css', + "fontawesome-webfont.eot" = 'nano/assets/fontawesome-webfont.eot', + "fontawesome-webfont.woff2" = 'nano/assets/fontawesome-webfont.woff2' + ) + +/datum/asset/simple/pda + assets = list( + "pda_atmos.png" = 'icons/pda_icons/pda_atmos.png', + "pda_back.png" = 'icons/pda_icons/pda_back.png', + "pda_bell.png" = 'icons/pda_icons/pda_bell.png', + "pda_blank.png" = 'icons/pda_icons/pda_blank.png', + "pda_boom.png" = 'icons/pda_icons/pda_boom.png', + "pda_bucket.png" = 'icons/pda_icons/pda_bucket.png', + "pda_medbot.png" = 'icons/pda_icons/pda_medbot.png', + "pda_floorbot.png" = 'icons/pda_icons/pda_floorbot.png', + "pda_cleanbot.png" = 'icons/pda_icons/pda_cleanbot.png', + "pda_crate.png" = 'icons/pda_icons/pda_crate.png', + "pda_cuffs.png" = 'icons/pda_icons/pda_cuffs.png', + "pda_eject.png" = 'icons/pda_icons/pda_eject.png', + "pda_exit.png" = 'icons/pda_icons/pda_exit.png', + "pda_flashlight.png" = 'icons/pda_icons/pda_flashlight.png', + "pda_honk.png" = 'icons/pda_icons/pda_honk.png', + "pda_mail.png" = 'icons/pda_icons/pda_mail.png', + "pda_medical.png" = 'icons/pda_icons/pda_medical.png', + "pda_menu.png" = 'icons/pda_icons/pda_menu.png', + "pda_mule.png" = 'icons/pda_icons/pda_mule.png', + "pda_notes.png" = 'icons/pda_icons/pda_notes.png', + "pda_power.png" = 'icons/pda_icons/pda_power.png', + "pda_rdoor.png" = 'icons/pda_icons/pda_rdoor.png', + "pda_reagent.png" = 'icons/pda_icons/pda_reagent.png', + "pda_refresh.png" = 'icons/pda_icons/pda_refresh.png', + "pda_scanner.png" = 'icons/pda_icons/pda_scanner.png', + "pda_signaler.png" = 'icons/pda_icons/pda_signaler.png', + "pda_status.png" = 'icons/pda_icons/pda_status.png' + ) + +/datum/asset/simple/paper + assets = list( + "large_stamp-clown.png" = 'icons/stamp_icons/large_stamp-clown.png', + "large_stamp-deny.png" = 'icons/stamp_icons/large_stamp-deny.png', + "large_stamp-ok.png" = 'icons/stamp_icons/large_stamp-ok.png', + "large_stamp-hop.png" = 'icons/stamp_icons/large_stamp-hop.png', + "large_stamp-cmo.png" = 'icons/stamp_icons/large_stamp-cmo.png', + "large_stamp-ce.png" = 'icons/stamp_icons/large_stamp-ce.png', + "large_stamp-hos.png" = 'icons/stamp_icons/large_stamp-hos.png', + "large_stamp-rd.png" = 'icons/stamp_icons/large_stamp-rd.png', + "large_stamp-cap.png" = 'icons/stamp_icons/large_stamp-cap.png', + "large_stamp-qm.png" = 'icons/stamp_icons/large_stamp-qm.png', + "large_stamp-law.png" = 'icons/stamp_icons/large_stamp-law.png' + ) + + +//Registers HTML Interface assets. +/datum/asset/HTML_interface/register() + for(var/path in typesof(/datum/html_interface)) + var/datum/html_interface/hi = new path() + hi.registerResources() diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index bc8f011ac44..d0a2c68cb57 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -1,339 +1,339 @@ - //////////// - //SECURITY// - //////////// -#define UPLOAD_LIMIT 1048576 //Restricts client uploads to the server to 1MB //Could probably do with being lower. -#define MIN_CLIENT_VERSION 0 //Just an ambiguously low version for now, I don't want to suddenly stop people playing. - //I would just like the code ready should it ever need to be used. - /* - When somebody clicks a link in game, this Topic is called first. - It does the stuff in this proc and then is redirected to the Topic() proc for the src=[0xWhatever] - (if specified in the link). ie locate(hsrc).Topic() - - Such links can be spoofed. - - Because of this certain things MUST be considered whenever adding a Topic() for something: - - Can it be fed harmful values which could cause runtimes? - - Is the Topic call an admin-only thing? - - If so, does it have checks to see if the person who called it (usr.client) is an admin? - - Are the processes being called by Topic() particularly laggy? - - If so, is there any protection against somebody spam-clicking a link? - If you have any questions about this stuff feel free to ask. ~Carn - */ -/client/Topic(href, href_list, hsrc) - if(!usr || usr != mob) //stops us calling Topic for somebody else's client. Also helps prevent usr=null - return - // NanoUI - if(href_list["nano_error"]) - src << href_list["nano_error"] - throw EXCEPTION("NanoUI: [href_list["nano_error"]]") - if(href_list["nano_log"]) - src << href_list["nano_log"] - return - // asset_cache - if(href_list["asset_cache_confirm_arrival"]) - //src << "ASSET JOB [href_list["asset_cache_confirm_arrival"]] ARRIVED." - var/job = text2num(href_list["asset_cache_confirm_arrival"]) - completed_asset_jobs += job - return - //Admin PM - if(href_list["priv_msg"]) - if (href_list["ahelp_reply"]) - cmd_ahelp_reply(href_list["priv_msg"]) - return - cmd_admin_pm(href_list["priv_msg"],null) - return - - //Logs all hrefs - if(config && config.log_hrefs && href_logfile) - href_logfile << "[time2text(world.timeofday,"hh:mm")] [src] (usr:[usr]) || [hsrc ? "[hsrc] " : ""][href]
" - - switch(href_list["_src_"]) - if("holder") hsrc = holder - if("usr") hsrc = mob - if("prefs") return prefs.process_link(usr,href_list) - if("vars") return view_var_Topic(href,href_list,hsrc) - - ..() //redirect to hsrc.Topic() - -/client/proc/is_content_unlocked() - if(!prefs.unlock_content) - src << "Become a BYOND member to access member-perks and features, as well as support the engine that makes this game possible. Only 10 bucks for 3 months! Click Here to find out more." - return 0 - return 1 - -/client/proc/handle_spam_prevention(message, mute_type) - if(config.automute_on && !holder && src.last_message == message) - src.last_message_count++ - if(src.last_message_count >= SPAM_TRIGGER_AUTOMUTE) - src << "You have exceeded the spam filter limit for identical messages. An auto-mute was applied." - cmd_admin_mute(src, mute_type, 1) - return 1 - if(src.last_message_count >= SPAM_TRIGGER_WARNING) - src << "You are nearing the spam filter limit for identical messages." - return 0 - else - last_message = message - src.last_message_count = 0 - return 0 - -//This stops files larger than UPLOAD_LIMIT being sent from client to server via input(), client.Import() etc. -/client/AllowUpload(filename, filelength) - if(filelength > UPLOAD_LIMIT) - src << "Error: AllowUpload(): File Upload too large. Upload Limit: [UPLOAD_LIMIT/1024]KiB." - return 0 -/* //Don't need this at the moment. But it's here if it's needed later. - //Helps prevent multiple files being uploaded at once. Or right after eachother. - var/time_to_wait = fileaccess_timer - world.time - if(time_to_wait > 0) - src << "Error: AllowUpload(): Spam prevention. Please wait [round(time_to_wait/10)] seconds." - return 0 - fileaccess_timer = world.time + FTPDELAY */ - return 1 - - - /////////// - //CONNECT// - /////////// -#if (PRELOAD_RSC == 0) -var/list/external_rsc_urls -var/next_external_rsc = 0 -#endif - - -/client/New(TopicData) - - TopicData = null //Prevent calls to client.Topic from connect - - if(connection != "seeker" && connection != "web")//Invalid connection type. - return null - if(byond_version < MIN_CLIENT_VERSION) //Out of date client. - return null - -#if (PRELOAD_RSC == 0) - if(external_rsc_urls && external_rsc_urls.len) - next_external_rsc = Wrap(next_external_rsc+1, 1, external_rsc_urls.len+1) - preload_rsc = external_rsc_urls[next_external_rsc] -#endif - - clients += src - directory[ckey] = src - - //Admin Authorisation - if(protected_config.autoadmin) - if(!admin_datums[ckey]) - var/datum/admin_rank/autorank - for(var/datum/admin_rank/R in admin_ranks) - if(R.name == protected_config.autoadmin_rank) - autorank = R - break - if(!autorank) - world << "Autoadmin rank not found" - else - var/datum/admins/D = new(autorank, ckey) - admin_datums[ckey] = D - holder = admin_datums[ckey] - if(holder) - admins += src - holder.owner = src - - //preferences datum - also holds some persistant data for the client (because we may as well keep these datums to a minimum) - prefs = preferences_datums[ckey] - if(!prefs) - prefs = new /datum/preferences(src) - preferences_datums[ckey] = prefs - prefs.last_ip = address //these are gonna be used for banning - prefs.last_id = computer_id //these are gonna be used for banning - - . = ..() //calls mob.Login() - - if (connection == "web") - if (!config.allowwebclient) - src << "Web client is disabled" - del(src) - return 0 - if (config.webclientmembersonly && !IsByondMember()) - src << "Sorry, but the web client is restricted to byond members only." - del(src) - return 0 - - if( (world.address == address || !address) && !host ) - host = key - world.update_status() - - if(holder) - add_admin_verbs() - admin_memo_output("Show") - adminGreet() - if((global.comms_key == "default_pwd" || length(global.comms_key) <= 6) && global.comms_allowed) //It's the default value or less than 6 characters long, but it somehow didn't disable comms. - src << "The server's API key is either too short or is the default value! Consider changing it immediately!" - - add_verbs_from_config() - set_client_age_from_db() - - if (isnum(player_age) && player_age == -1) //first connection - if (config.panic_bunker && !holder && !(ckey in deadmins)) - log_access("Failed Login: [key] - New account attempting to connect during panic bunker") - message_admins("Failed Login: [key] - New account attempting to connect during panic bunker") - src << "Sorry but the server is currently not accepting connections from never before seen players." - del(src) - return 0 - - if (config.notify_new_player_age >= 0) - message_admins("New user: [key_name_admin(src)] is connecting here for the first time.") - if (config.irc_first_connection_alert) - send2irc_adminless_only("New-user", "[key_name(src)] is connecting for the first time!") - - player_age = 0 // set it from -1 to 0 so the job selection code doesn't have a panic attack - - else if (isnum(player_age) && player_age < config.notify_new_player_age) - message_admins("New user: [key_name_admin(src)] just connected with an age of [player_age] day[(player_age==1?"":"s")]") - - sync_client_with_db() - - send_resources() - - if(!void) - void = new() - - screen += void - - if(prefs.lastchangelog != changelog_hash) //bolds the changelog button on the interface so we know there are updates. - src << "You have unread updates in the changelog." - if(config.aggressive_changelog) - src.changes() - else - winset(src, "rpane.changelogb", "background-color=#eaeaea;font-style=bold") - - if (ckey in clientmessages) - for (var/message in clientmessages[ckey]) - src << message - clientmessages.Remove(ckey) - - if (config && config.autoconvert_notes) - convert_notes_sql(ckey) - - if(!winexists(src, "asset_cache_browser")) // The client is using a custom skin, tell them. - src << "Unable to access asset cache browser, if you are using a custom skin file, please allow DS to download the updated version, if you are not, then make a bug report. This is not a critical issue but can cause issues with resource downloading, as it is impossible to know when extra resources arrived to you." - - - //This is down here because of the browse() calls in tooltip/New() - if(!tooltips) - tooltips = new /datum/tooltip(src) - - -////////////// -//DISCONNECT// -////////////// - -/client/Del() - if(holder) - adminGreet(1) - holder.owner = null - admins -= src - directory -= ckey - clients -= src - return ..() - - -/client/proc/set_client_age_from_db() - if (IsGuestKey(src.key)) - return - - establish_db_connection() - if(!dbcon.IsConnected()) - return - - var/sql_ckey = sanitizeSQL(src.ckey) - - var/DBQuery/query = dbcon.NewQuery("SELECT id, datediff(Now(),firstseen) as age FROM [format_table_name("player")] WHERE ckey = '[sql_ckey]'") - if (!query.Execute()) - return - - while (query.NextRow()) - player_age = text2num(query.item[2]) - return - - //no match mark it as a first connection for use in client/New() - player_age = -1 - - -/client/proc/sync_client_with_db() - if (IsGuestKey(src.key)) - return - - establish_db_connection() - if (!dbcon.IsConnected()) - return - - var/sql_ckey = sanitizeSQL(src.ckey) - - var/DBQuery/query_ip = dbcon.NewQuery("SELECT ckey FROM [format_table_name("player")] WHERE ip = '[address]' AND ckey != '[sql_ckey]'") - query_ip.Execute() - related_accounts_ip = "" - while(query_ip.NextRow()) - related_accounts_ip += "[query_ip.item[1]], " - - var/DBQuery/query_cid = dbcon.NewQuery("SELECT ckey FROM [format_table_name("player")] WHERE computerid = '[computer_id]' AND ckey != '[sql_ckey]'") - query_cid.Execute() - related_accounts_cid = "" - while (query_cid.NextRow()) - related_accounts_cid += "[query_cid.item[1]], " - - var/watchreason = check_watchlist(sql_ckey) - if(watchreason) - message_admins("Notice: [key_name_admin(src)] is on the watchlist and has just connected - Reason: [watchreason]") - send2irc_adminless_only("Watchlist", "[key_name(src)] is on the watchlist and has just connected - Reason: [watchreason]") - - var/admin_rank = "Player" - if (src.holder && src.holder.rank) - admin_rank = src.holder.rank.name - - var/sql_ip = sanitizeSQL(src.address) - var/sql_computerid = sanitizeSQL(src.computer_id) - var/sql_admin_rank = sanitizeSQL(admin_rank) - - - var/DBQuery/query_insert = dbcon.NewQuery("INSERT INTO [format_table_name("player")] (id, ckey, firstseen, lastseen, ip, computerid, lastadminrank) VALUES (null, '[sql_ckey]', Now(), Now(), '[sql_ip]', '[sql_computerid]', '[sql_admin_rank]') ON DUPLICATE KEY UPDATE lastseen = VALUES(lastseen), ip = VALUES(ip), computerid = VALUES(computerid), lastadminrank = VALUES(lastadminrank)") - query_insert.Execute() - - //Logging player access - var/serverip = "[world.internet_address]:[world.port]" - var/DBQuery/query_accesslog = dbcon.NewQuery("INSERT INTO `[format_table_name("connection_log")]` (`id`,`datetime`,`serverip`,`ckey`,`ip`,`computerid`) VALUES(null,Now(),'[serverip]','[sql_ckey]','[sql_ip]','[sql_computerid]');") - query_accesslog.Execute() - -/client/proc/add_verbs_from_config() - if(config.see_own_notes) - verbs += /client/proc/self_notes - - -#undef TOPIC_SPAM_DELAY -#undef UPLOAD_LIMIT -#undef MIN_CLIENT_VERSION - -//checks if a client is afk -//3000 frames = 5 minutes -/client/proc/is_afk(duration=3000) - if(inactivity > duration) return inactivity - return 0 - -// Byond seemingly calls stat, each tick. -// Calling things each tick can get expensive real quick. -// So we slow this down a little. -// See: http://www.byond.com/docs/ref/info.html#/client/proc/Stat -/client/Stat() - . = ..() - sleep(1) - -//send resources to the client. It's here in its own proc so we can move it around easiliy if need be -/client/proc/send_resources() - //get the common files - getFiles( - 'html/search.js', - 'html/panels.css', - 'html/browser/common.css', - 'html/browser/scannernew.css', - 'html/browser/playeroptions.css', - ) - spawn (10) - //Precache the client with all other assets slowly, so as to not block other browse() calls - getFilesSlow(src, SSasset.cache, register_asset = FALSE) + //////////// + //SECURITY// + //////////// +#define UPLOAD_LIMIT 1048576 //Restricts client uploads to the server to 1MB //Could probably do with being lower. +#define MIN_CLIENT_VERSION 0 //Just an ambiguously low version for now, I don't want to suddenly stop people playing. + //I would just like the code ready should it ever need to be used. + /* + When somebody clicks a link in game, this Topic is called first. + It does the stuff in this proc and then is redirected to the Topic() proc for the src=[0xWhatever] + (if specified in the link). ie locate(hsrc).Topic() + + Such links can be spoofed. + + Because of this certain things MUST be considered whenever adding a Topic() for something: + - Can it be fed harmful values which could cause runtimes? + - Is the Topic call an admin-only thing? + - If so, does it have checks to see if the person who called it (usr.client) is an admin? + - Are the processes being called by Topic() particularly laggy? + - If so, is there any protection against somebody spam-clicking a link? + If you have any questions about this stuff feel free to ask. ~Carn + */ +/client/Topic(href, href_list, hsrc) + if(!usr || usr != mob) //stops us calling Topic for somebody else's client. Also helps prevent usr=null + return + // NanoUI + if(href_list["nano_error"]) + src << href_list["nano_error"] + throw EXCEPTION("NanoUI: [href_list["nano_error"]]") + if(href_list["nano_log"]) + src << href_list["nano_log"] + return + // asset_cache + if(href_list["asset_cache_confirm_arrival"]) + //src << "ASSET JOB [href_list["asset_cache_confirm_arrival"]] ARRIVED." + var/job = text2num(href_list["asset_cache_confirm_arrival"]) + completed_asset_jobs += job + return + //Admin PM + if(href_list["priv_msg"]) + if (href_list["ahelp_reply"]) + cmd_ahelp_reply(href_list["priv_msg"]) + return + cmd_admin_pm(href_list["priv_msg"],null) + return + + //Logs all hrefs + if(config && config.log_hrefs && href_logfile) + href_logfile << "[time2text(world.timeofday,"hh:mm")] [src] (usr:[usr]) || [hsrc ? "[hsrc] " : ""][href]
" + + switch(href_list["_src_"]) + if("holder") hsrc = holder + if("usr") hsrc = mob + if("prefs") return prefs.process_link(usr,href_list) + if("vars") return view_var_Topic(href,href_list,hsrc) + + ..() //redirect to hsrc.Topic() + +/client/proc/is_content_unlocked() + if(!prefs.unlock_content) + src << "Become a BYOND member to access member-perks and features, as well as support the engine that makes this game possible. Only 10 bucks for 3 months! Click Here to find out more." + return 0 + return 1 + +/client/proc/handle_spam_prevention(message, mute_type) + if(config.automute_on && !holder && src.last_message == message) + src.last_message_count++ + if(src.last_message_count >= SPAM_TRIGGER_AUTOMUTE) + src << "You have exceeded the spam filter limit for identical messages. An auto-mute was applied." + cmd_admin_mute(src, mute_type, 1) + return 1 + if(src.last_message_count >= SPAM_TRIGGER_WARNING) + src << "You are nearing the spam filter limit for identical messages." + return 0 + else + last_message = message + src.last_message_count = 0 + return 0 + +//This stops files larger than UPLOAD_LIMIT being sent from client to server via input(), client.Import() etc. +/client/AllowUpload(filename, filelength) + if(filelength > UPLOAD_LIMIT) + src << "Error: AllowUpload(): File Upload too large. Upload Limit: [UPLOAD_LIMIT/1024]KiB." + return 0 +/* //Don't need this at the moment. But it's here if it's needed later. + //Helps prevent multiple files being uploaded at once. Or right after eachother. + var/time_to_wait = fileaccess_timer - world.time + if(time_to_wait > 0) + src << "Error: AllowUpload(): Spam prevention. Please wait [round(time_to_wait/10)] seconds." + return 0 + fileaccess_timer = world.time + FTPDELAY */ + return 1 + + + /////////// + //CONNECT// + /////////// +#if (PRELOAD_RSC == 0) +var/list/external_rsc_urls +var/next_external_rsc = 0 +#endif + + +/client/New(TopicData) + + TopicData = null //Prevent calls to client.Topic from connect + + if(connection != "seeker" && connection != "web")//Invalid connection type. + return null + if(byond_version < MIN_CLIENT_VERSION) //Out of date client. + return null + +#if (PRELOAD_RSC == 0) + if(external_rsc_urls && external_rsc_urls.len) + next_external_rsc = Wrap(next_external_rsc+1, 1, external_rsc_urls.len+1) + preload_rsc = external_rsc_urls[next_external_rsc] +#endif + + clients += src + directory[ckey] = src + + //Admin Authorisation + if(protected_config.autoadmin) + if(!admin_datums[ckey]) + var/datum/admin_rank/autorank + for(var/datum/admin_rank/R in admin_ranks) + if(R.name == protected_config.autoadmin_rank) + autorank = R + break + if(!autorank) + world << "Autoadmin rank not found" + else + var/datum/admins/D = new(autorank, ckey) + admin_datums[ckey] = D + holder = admin_datums[ckey] + if(holder) + admins += src + holder.owner = src + + //preferences datum - also holds some persistant data for the client (because we may as well keep these datums to a minimum) + prefs = preferences_datums[ckey] + if(!prefs) + prefs = new /datum/preferences(src) + preferences_datums[ckey] = prefs + prefs.last_ip = address //these are gonna be used for banning + prefs.last_id = computer_id //these are gonna be used for banning + + . = ..() //calls mob.Login() + + if (connection == "web") + if (!config.allowwebclient) + src << "Web client is disabled" + del(src) + return 0 + if (config.webclientmembersonly && !IsByondMember()) + src << "Sorry, but the web client is restricted to byond members only." + del(src) + return 0 + + if( (world.address == address || !address) && !host ) + host = key + world.update_status() + + if(holder) + add_admin_verbs() + admin_memo_output("Show") + adminGreet() + if((global.comms_key == "default_pwd" || length(global.comms_key) <= 6) && global.comms_allowed) //It's the default value or less than 6 characters long, but it somehow didn't disable comms. + src << "The server's API key is either too short or is the default value! Consider changing it immediately!" + + add_verbs_from_config() + set_client_age_from_db() + + if (isnum(player_age) && player_age == -1) //first connection + if (config.panic_bunker && !holder && !(ckey in deadmins)) + log_access("Failed Login: [key] - New account attempting to connect during panic bunker") + message_admins("Failed Login: [key] - New account attempting to connect during panic bunker") + src << "Sorry but the server is currently not accepting connections from never before seen players." + del(src) + return 0 + + if (config.notify_new_player_age >= 0) + message_admins("New user: [key_name_admin(src)] is connecting here for the first time.") + if (config.irc_first_connection_alert) + send2irc_adminless_only("New-user", "[key_name(src)] is connecting for the first time!") + + player_age = 0 // set it from -1 to 0 so the job selection code doesn't have a panic attack + + else if (isnum(player_age) && player_age < config.notify_new_player_age) + message_admins("New user: [key_name_admin(src)] just connected with an age of [player_age] day[(player_age==1?"":"s")]") + + sync_client_with_db() + + send_resources() + + if(!void) + void = new() + + screen += void + + if(prefs.lastchangelog != changelog_hash) //bolds the changelog button on the interface so we know there are updates. + src << "You have unread updates in the changelog." + if(config.aggressive_changelog) + src.changes() + else + winset(src, "rpane.changelogb", "background-color=#eaeaea;font-style=bold") + + if (ckey in clientmessages) + for (var/message in clientmessages[ckey]) + src << message + clientmessages.Remove(ckey) + + if (config && config.autoconvert_notes) + convert_notes_sql(ckey) + + if(!winexists(src, "asset_cache_browser")) // The client is using a custom skin, tell them. + src << "Unable to access asset cache browser, if you are using a custom skin file, please allow DS to download the updated version, if you are not, then make a bug report. This is not a critical issue but can cause issues with resource downloading, as it is impossible to know when extra resources arrived to you." + + + //This is down here because of the browse() calls in tooltip/New() + if(!tooltips) + tooltips = new /datum/tooltip(src) + + +////////////// +//DISCONNECT// +////////////// + +/client/Del() + if(holder) + adminGreet(1) + holder.owner = null + admins -= src + directory -= ckey + clients -= src + return ..() + + +/client/proc/set_client_age_from_db() + if (IsGuestKey(src.key)) + return + + establish_db_connection() + if(!dbcon.IsConnected()) + return + + var/sql_ckey = sanitizeSQL(src.ckey) + + var/DBQuery/query = dbcon.NewQuery("SELECT id, datediff(Now(),firstseen) as age FROM [format_table_name("player")] WHERE ckey = '[sql_ckey]'") + if (!query.Execute()) + return + + while (query.NextRow()) + player_age = text2num(query.item[2]) + return + + //no match mark it as a first connection for use in client/New() + player_age = -1 + + +/client/proc/sync_client_with_db() + if (IsGuestKey(src.key)) + return + + establish_db_connection() + if (!dbcon.IsConnected()) + return + + var/sql_ckey = sanitizeSQL(src.ckey) + + var/DBQuery/query_ip = dbcon.NewQuery("SELECT ckey FROM [format_table_name("player")] WHERE ip = '[address]' AND ckey != '[sql_ckey]'") + query_ip.Execute() + related_accounts_ip = "" + while(query_ip.NextRow()) + related_accounts_ip += "[query_ip.item[1]], " + + var/DBQuery/query_cid = dbcon.NewQuery("SELECT ckey FROM [format_table_name("player")] WHERE computerid = '[computer_id]' AND ckey != '[sql_ckey]'") + query_cid.Execute() + related_accounts_cid = "" + while (query_cid.NextRow()) + related_accounts_cid += "[query_cid.item[1]], " + + var/watchreason = check_watchlist(sql_ckey) + if(watchreason) + message_admins("Notice: [key_name_admin(src)] is on the watchlist and has just connected - Reason: [watchreason]") + send2irc_adminless_only("Watchlist", "[key_name(src)] is on the watchlist and has just connected - Reason: [watchreason]") + + var/admin_rank = "Player" + if (src.holder && src.holder.rank) + admin_rank = src.holder.rank.name + + var/sql_ip = sanitizeSQL(src.address) + var/sql_computerid = sanitizeSQL(src.computer_id) + var/sql_admin_rank = sanitizeSQL(admin_rank) + + + var/DBQuery/query_insert = dbcon.NewQuery("INSERT INTO [format_table_name("player")] (id, ckey, firstseen, lastseen, ip, computerid, lastadminrank) VALUES (null, '[sql_ckey]', Now(), Now(), '[sql_ip]', '[sql_computerid]', '[sql_admin_rank]') ON DUPLICATE KEY UPDATE lastseen = VALUES(lastseen), ip = VALUES(ip), computerid = VALUES(computerid), lastadminrank = VALUES(lastadminrank)") + query_insert.Execute() + + //Logging player access + var/serverip = "[world.internet_address]:[world.port]" + var/DBQuery/query_accesslog = dbcon.NewQuery("INSERT INTO `[format_table_name("connection_log")]` (`id`,`datetime`,`serverip`,`ckey`,`ip`,`computerid`) VALUES(null,Now(),'[serverip]','[sql_ckey]','[sql_ip]','[sql_computerid]');") + query_accesslog.Execute() + +/client/proc/add_verbs_from_config() + if(config.see_own_notes) + verbs += /client/proc/self_notes + + +#undef TOPIC_SPAM_DELAY +#undef UPLOAD_LIMIT +#undef MIN_CLIENT_VERSION + +//checks if a client is afk +//3000 frames = 5 minutes +/client/proc/is_afk(duration=3000) + if(inactivity > duration) return inactivity + return 0 + +// Byond seemingly calls stat, each tick. +// Calling things each tick can get expensive real quick. +// So we slow this down a little. +// See: http://www.byond.com/docs/ref/info.html#/client/proc/Stat +/client/Stat() + . = ..() + sleep(1) + +//send resources to the client. It's here in its own proc so we can move it around easiliy if need be +/client/proc/send_resources() + //get the common files + getFiles( + 'html/search.js', + 'html/panels.css', + 'html/browser/common.css', + 'html/browser/scannernew.css', + 'html/browser/playeroptions.css', + ) + spawn (10) + //Precache the client with all other assets slowly, so as to not block other browse() calls + getFilesSlow(src, SSasset.cache, register_asset = FALSE) diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 9af05edd54f..24e42225941 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -1,1127 +1,1127 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:33 - -var/list/preferences_datums = list() - - - -/datum/preferences - //doohickeys for savefiles - var/path - var/default_slot = 1 //Holder so it doesn't default to slot 1, rather the last one used - var/max_save_slots = 3 - - //non-preference stuff - var/muted = 0 - var/last_ip - var/last_id - - //game-preferences - var/lastchangelog = "" //Saved changlog filesize to detect if there was a change - var/ooccolor = null - - //Antag preferences - var/list/be_special = list() //Special role selection - var/tmp/old_be_special = 0 //Bitflag version of be_special, used to update old savefiles and nothing more - //If it's 0, that's good, if it's anything but 0, the owner of this prefs file's antag choices were, - //autocorrected this round, not that you'd need to check that. - - - var/UI_style = "Midnight" - var/nanoui_fancy = TRUE - var/toggles = TOGGLES_DEFAULT - var/chat_toggles = TOGGLES_DEFAULT_CHAT - var/ghost_form = "ghost" - var/allow_midround_antag = 1 - var/preferred_map = null - - //character preferences - var/real_name //our character's name - var/be_random_name = 0 //whether we'll have a random name every round - var/be_random_body = 0 //whether we'll have a random body every round - var/gender = MALE //gender of character (well duh) - var/age = 30 //age of character - var/blood_type = "A+" //blood type (not-chooseable) - var/underwear = "Nude" //underwear type - var/undershirt = "Nude" //undershirt type - var/socks = "Nude" //socks type - var/backbag = 1 //backpack type - var/hair_style = "Bald" //Hair type - var/hair_color = "000" //Hair color - var/facial_hair_style = "Shaved" //Face hair type - var/facial_hair_color = "000" //Facial hair color - var/skin_tone = "caucasian1" //Skin color - var/eye_color = "000" //Eye color - var/datum/species/pref_species = new /datum/species/human() //Mutant race - var/list/features = list("mcolor" = "FFF", "tail_lizard" = "Smooth", "tail_human" = "None", "snout" = "Round", "horns" = "None", "ears" = "None", "frills" = "None", "spines" = "None", "body_markings" = "None") - - var/list/custom_names = list("clown", "mime", "ai", "cyborg", "religion", "deity") - - //Mob preview - var/icon/preview_icon = null - - //Jobs, uses bitflags - var/job_civilian_high = 0 - var/job_civilian_med = 0 - var/job_civilian_low = 0 - - var/job_medsci_high = 0 - var/job_medsci_med = 0 - var/job_medsci_low = 0 - - var/job_engsec_high = 0 - var/job_engsec_med = 0 - var/job_engsec_low = 0 - - // Want randomjob if preferences already filled - Donkie - var/userandomjob = 1 //defaults to 1 for fewer assistants - - // 0 = character settings, 1 = game preferences - var/current_tab = 0 - - // OOC Metadata: - var/metadata = "" - - var/unlock_content = 0 - - var/list/ignoring = list() - -/datum/preferences/New(client/C) - blood_type = random_blood_type() - custom_names["ai"] = pick(ai_names) - custom_names["cyborg"] = pick(ai_names) - custom_names["clown"] = pick(clown_names) - custom_names["mime"] = pick(mime_names) - if(istype(C)) - if(!IsGuestKey(C.key)) - load_path(C.ckey) - unlock_content = C.IsByondMember() - if(unlock_content) - max_save_slots = 8 - var/loaded_preferences_successfully = load_preferences() - if(loaded_preferences_successfully) - if(load_character()) - return - //we couldn't load character data so just randomize the character appearance + name - random_character() //let's create a random character then - rather than a fat, bald and naked man. - real_name = pref_species.random_name(gender,1) - if(!loaded_preferences_successfully) - save_preferences() - save_character() //let's save this new random character so it doesn't keep generating new ones. - return - - -/datum/preferences/proc/ShowChoices(mob/user) - if(!user || !user.client) return - update_preview_icon() - user << browse_rsc(preview_icon, "previewicon.png") - var/dat = "
" - - dat += "Character Settings " - dat += "Game Preferences" - - if(!path) - dat += "
Please create an account to save your preferences
" - - dat += "
" - - dat += "
" - - switch(current_tab) - if (0) // Character Settings# - if(path) - var/savefile/S = new /savefile(path) - if(S) - dat += "
" - 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!=1) dat += " | " - dat += "[name] " - dat += "
" - - dat += "

Occupation Choices

" - dat += "Set Occupation Preferences
" - dat += "

Identity

" - dat += "" - - - dat += "
" - if(appearance_isbanned(user)) - dat += "You are banned from using custom names and appearances. You can continue to adjust your characters, but you will be randomised once you join the game.
" - dat += "Random Name " - dat += "Always Random Name: [be_random_name ? "Yes" : "No"]
" - - dat += "Name: " - dat += "[real_name]
" - - dat += "Gender: [gender == MALE ? "Male" : "Female"]
" - dat += "Age: [age]
" - - dat += "Special Names:
" - dat += "Clown: [custom_names["clown"]] " - dat += "Mime:[custom_names["mime"]]
" - dat += "AI: [custom_names["ai"]] " - dat += "Cyborg: [custom_names["cyborg"]]
" - dat += "Chaplain religion: [custom_names["religion"]] " - dat += "Chaplain deity: [custom_names["deity"]]
" - - dat += "
" - - dat += "
" - - dat += "

Body

" - dat += "Random Body " - dat += "Always Random Body: [be_random_body ? "Yes" : "No"]
" - - dat += "" - - if(pref_species.use_skintones) - - dat += "" - - if(HAIR in pref_species.specflags) - - dat += "" - - if(EYECOLOR in pref_species.specflags) - - dat += "" - - if(config.mutant_races) //We don't allow mutant bodyparts for humans either unless this is true. - - if((MUTCOLORS in pref_species.specflags) || (MUTCOLORS_PARTSONLY in pref_species.specflags)) - - dat += "" - - if("tail_lizard" in pref_species.mutant_bodyparts) - dat += "" - - if("snout" in pref_species.mutant_bodyparts) - dat += "" - - if("horns" in pref_species.mutant_bodyparts) - dat += "" - - if("frills" in pref_species.mutant_bodyparts) - dat += "" - - if("spines" in pref_species.mutant_bodyparts) - dat += "" - - if("body_markings" in pref_species.mutant_bodyparts) - dat += "" - - if(config.mutant_humans) - - if("tail_human" in pref_species.mutant_bodyparts) - dat += "" - - if("ears" in pref_species.mutant_bodyparts) - dat += "" - - dat += "
" - - if(config.mutant_races) - dat += "Species:
[pref_species.name]
" - else - dat += "Species: Human
" - - dat += "Blood Type: [blood_type]
" - dat += "Underwear:
[underwear]
" - dat += "Undershirt:
[undershirt]
" - dat += "Socks:
[socks]
" - dat += "Backpack:
[backbaglist[backbag]]
" - - dat += "

Skin Tone

" - - dat += "[skin_tone]
" - - dat += "
" - - dat += "

Hair Style

" - - dat += "[hair_style]
" - dat += "< >
" - dat += "    Change
" - - - dat += "
" - - dat += "

Facial Hair Style

" - - dat += "[facial_hair_style]
" - dat += "< >
" - dat += "    Change
" - - dat += "
" - - dat += "

Eye Color

" - - dat += "    Change
" - - dat += "
" - - dat += "

Alien/Mutant Color

" - - dat += "    Change
" - - dat += "
" - - dat += "

Tail

" - - dat += "[features["tail_lizard"]]
" - - dat += "
" - - dat += "

Snout

" - - dat += "[features["snout"]]
" - - dat += "
" - - dat += "

Horns

" - - dat += "[features["horns"]]
" - - dat += "
" - - dat += "

Frills

" - - dat += "[features["frills"]]
" - - dat += "
" - - dat += "

Spines

" - - dat += "[features["spines"]]
" - - dat += "
" - - dat += "

Body Markings

" - - dat += "[features["body_markings"]]
" - - dat += "
" - - dat += "

Tail

" - - dat += "[features["tail_human"]]
" - - dat += "
" - - dat += "

Ears

" - - dat += "[features["ears"]]
" - - dat += "
" - - - if (1) // Game Preferences - dat += "
" - dat += "

General Settings

" - dat += "UI Style: [UI_style]
" - dat += "Fancy NanoUI: [(nanoui_fancy) ? "Yes" : "No"]
" - dat += "Play admin midis: [(toggles & SOUND_MIDI) ? "Yes" : "No"]
" - dat += "Play lobby music: [(toggles & SOUND_LOBBY) ? "Yes" : "No"]
" - dat += "Ghost ears: [(chat_toggles & CHAT_GHOSTEARS) ? "Nearest Creatures" : "All Speech"]
" - dat += "Ghost sight: [(chat_toggles & CHAT_GHOSTSIGHT) ? "Nearest Creatures" : "All Emotes"]
" - dat += "Ghost whispers: [(chat_toggles & CHAT_GHOSTWHISPER) ? "Nearest Creatures" : "All Speech"]
" - dat += "Ghost radio: [(chat_toggles & CHAT_GHOSTRADIO) ? "Yes" : "No"]
" - dat += "Ghost pda: [(chat_toggles & CHAT_GHOSTPDA) ? "Nearest Creatures" : "All Messages"]
" - dat += "Pull requests: [(chat_toggles & CHAT_PULLR) ? "Yes" : "No"]
" - dat += "Midround Antagonist: [(toggles & MIDROUND_ANTAG) ? "Yes" : "No"]
" - if(config.allow_Metadata) - dat += "OOC Notes: Edit
" - - if(user.client) - if(user.client.holder) - dat += "Adminhelp Sound: [(toggles & SOUND_ADMINHELP)?"On":"Off"]
" - dat += "Announce Login: [(toggles & ANNOUNCE_LOGIN)?"On":"Off"]
" - - if(unlock_content || check_rights_for(user.client, R_ADMIN)) - dat += "OOC:     Change
" - - if(unlock_content) - dat += "BYOND Membership Publicity: [(toggles & MEMBER_PUBLIC) ? "Public" : "Hidden"]
" - dat += "Ghost Form: [ghost_form]
" - - if (SERVERTOOLS && config.maprotation) - var/p_map = preferred_map - if (!p_map) - p_map = "Default" - if (config.defaultmap) - p_map += " ([config.defaultmap.friendlyname])" - else - if (p_map in config.maplist) - var/datum/votablemap/VM = config.maplist[p_map] - if (!VM) - p_map += " (No longer exists)" - else - p_map = VM.friendlyname - else - p_map += " (No longer exists)" - dat += "Preferred Map: [p_map]" - - dat += "
" - - dat += "

Special Role Settings

" - - if(jobban_isbanned(user, "Syndicate")) - dat += "You are banned from antagonist roles." - src.be_special = list() - - - for (var/i in special_roles) - if(jobban_isbanned(user, i)) - dat += "Be [capitalize(i)]: BANNED
" - else - var/days_remaining = null - if(config.use_age_restriction_for_jobs && ispath(special_roles[i])) //If it's a game mode antag, check if the player meets the minimum age - var/mode_path = special_roles[i] - var/datum/game_mode/temp_mode = new mode_path - days_remaining = temp_mode.get_remaining_days(user.client) - - if(days_remaining) - dat += "Be [capitalize(i)]: \[IN [days_remaining] DAYS]
" - else - dat += "Be [capitalize(i)]: [(i in be_special) ? "Yes" : "No"]
" - - dat += "
" - - dat += "
" - - if(!IsGuestKey(user.key)) - dat += "Undo " - dat += "Save Setup " - - dat += "Reset Setup" - dat += "
" - - //user << browse(dat, "window=preferences;size=560x560") - var/datum/browser/popup = new(user, "preferences", "
Character Setup
", 640, 750) - popup.set_content(dat) - popup.open(0) - -/datum/preferences/proc/SetChoices(mob/user, limit = 17, list/splitJobs = list("Chief Engineer"), widthPerColumn = 295, height = 620) - if(!SSjob) 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. - //widthPerColumn - Screen's width for every column. - //height - Screen's height. - - var/width = widthPerColumn - - var/HTML = "
" - HTML += "Choose occupation chances
" - HTML += "
Left-click to raise an occupation preference, right-click to lower it.
" - HTML += "
Done

" // Easier to press up here. - HTML += "" - 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 - - for(var/datum/job/job in SSjob.occupations) - - index += 1 - if((index >= limit) || (job.title in splitJobs)) - width += widthPerColumn - 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") && !jobban_isbanned(user, "Assistant")) - HTML += "[rank]" - continue - if(config.enforce_human_authority && !user.client.prefs.pref_species.qualifies_for_rank(rank, user.client.prefs.features)) - if(user.client.prefs.pref_species.id == "human") - HTML += "[rank]" - else - HTML += "[rank]" - continue - if((rank in command_positions) || (rank == "AI"))//Bold head jobs - HTML += "[rank]" - else - HTML += "[rank]" - - HTML += "" - continue - - HTML += "[prefLevelLabel]" - HTML += "" - - for(var/i = 1, i < (limit - index), i += 1) // Finish the column so it is even - HTML += "" - - HTML += "
" - var/rank = job.title - lastJob = job - if(jobban_isbanned(user, rank)) - HTML += "[rank] BANNED
\[IN [(available_in_days)] DAYS\]
\[MUTANT\]
\[NON-HUMAN\]
" - - var/prefLevelLabel = "ERROR" - var/prefLevelColor = "pink" - var/prefUpperLevel = -1 // level to assign on left click - var/prefLowerLevel = -1 // level to assign on right click - - if(GetJobDepartment(job, 1) & job.flag) - prefLevelLabel = "High" - prefLevelColor = "slateblue" - prefUpperLevel = 4 - prefLowerLevel = 2 - else if(GetJobDepartment(job, 2) & job.flag) - prefLevelLabel = "Medium" - prefLevelColor = "green" - prefUpperLevel = 1 - prefLowerLevel = 3 - else if(GetJobDepartment(job, 3) & job.flag) - prefLevelLabel = "Low" - prefLevelColor = "orange" - prefUpperLevel = 2 - prefLowerLevel = 4 - else - prefLevelLabel = "NEVER" - prefLevelColor = "red" - prefUpperLevel = 3 - prefLowerLevel = 1 - - - HTML += "" - - if(rank == "Assistant")//Assistant is special - if(job_civilian_low & ASSISTANT) - HTML += "Yes" - else - HTML += "No" - HTML += "
  
" - - HTML += "
" - - HTML += "

[userandomjob ? "Get random job if preferences unavailable" : "Be an Assistant if preference unavailable"]
" - HTML += "
Reset Preferences
" - - user << browse(null, "window=preferences") - //user << browse(HTML, "window=mob_occupation;size=[width]x[height]") - var/datum/browser/popup = new(user, "mob_occupation", "
Occupation Preferences
", width, height) - popup.set_window_options("can_close=0") - popup.set_content(HTML) - popup.open(0) - return - -/datum/preferences/proc/SetJobPreferenceLevel(datum/job/job, level) - if (!job) - return 0 - - if (level == 1) // to high - // remove any other job(s) set to high - job_civilian_med |= job_civilian_high - job_engsec_med |= job_engsec_high - job_medsci_med |= job_medsci_high - job_civilian_high = 0 - job_engsec_high = 0 - job_medsci_high = 0 - - if (job.department_flag == CIVILIAN) - job_civilian_low &= ~job.flag - job_civilian_med &= ~job.flag - job_civilian_high &= ~job.flag - - switch(level) - if (1) - job_civilian_high |= job.flag - if (2) - job_civilian_med |= job.flag - if (3) - job_civilian_low |= job.flag - - return 1 - else if (job.department_flag == ENGSEC) - job_engsec_low &= ~job.flag - job_engsec_med &= ~job.flag - job_engsec_high &= ~job.flag - - switch(level) - if (1) - job_engsec_high |= job.flag - if (2) - job_engsec_med |= job.flag - if (3) - job_engsec_low |= job.flag - - return 1 - else if (job.department_flag == MEDSCI) - job_medsci_low &= ~job.flag - job_medsci_med &= ~job.flag - job_medsci_high &= ~job.flag - - switch(level) - if (1) - job_medsci_high |= job.flag - if (2) - job_medsci_med |= job.flag - if (3) - job_medsci_low |= job.flag - - return 1 - - return 0 - -/datum/preferences/proc/UpdateJobPreference(mob/user, role, desiredLvl) - if(!SSjob) - return - var/datum/job/job = SSjob.GetJob(role) - - if(!job) - user << browse(null, "window=mob_occupation") - ShowChoices(user) - return - - if (!isnum(desiredLvl)) - user << "UpdateJobPreference - desired level was not a number. Please notify coders!" - 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 - - SetJobPreferenceLevel(job, desiredLvl) - SetChoices(user) - - return 1 - - -/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 - - -/datum/preferences/proc/GetJobDepartment(datum/job/job, 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/process_link(mob/user, list/href_list) - if(href_list["jobbancheck"]) - var/job = sanitizeSQL(href_list["jobbancheck"]) - var/sql_ckey = sanitizeSQL(user.ckey) - var/DBQuery/query_get_jobban = dbcon.NewQuery("SELECT reason, bantime, duration, expiration_time, a_ckey FROM [format_table_name("ban")] WHERE ckey = '[sql_ckey]' AND job = '[job]' AND (bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)") - if(!query_get_jobban.Execute()) - var/err = query_get_jobban.ErrorMsg() - log_game("SQL ERROR obtaining reason from ban table. Error : \[[err]\]\n") - return - if(query_get_jobban.NextRow()) - var/reason = query_get_jobban.item[1] - var/bantime = query_get_jobban.item[2] - var/duration = query_get_jobban.item[3] - var/expiration_time = query_get_jobban.item[4] - var/a_ckey = query_get_jobban.item[5] - var/text - text = "You, or another user of this computer, ([user.ckey]) is banned from playing [job]. The ban reason is:
[reason]
This ban was applied by [a_ckey] on [bantime]" - if(text2num(duration) > 0) - text += ". The ban is for [duration] minutes and expires on [expiration_time] (server time)" - text += ".
" - user << text - 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(jobban_isbanned(user, "Assistant")) - userandomjob = 1 - else - userandomjob = !userandomjob - SetChoices(user) - if("setJobLevel") - UpdateJobPreference(user, href_list["text"], text2num(href_list["level"])) - else - SetChoices(user) - return 1 - - switch(href_list["task"]) - if("random") - switch(href_list["preference"]) - if("name") - real_name = pref_species.random_name(gender,1) - if("age") - age = rand(AGE_MIN, AGE_MAX) - if("hair") - hair_color = random_short_color() - if("hair_style") - hair_style = random_hair_style(gender) - if("facial") - facial_hair_color = random_short_color() - if("facial_hair_style") - facial_hair_style = random_facial_hair_style(gender) - if("underwear") - underwear = random_underwear(gender) - if("undershirt") - undershirt = random_undershirt(gender) - if("socks") - socks = random_socks(gender) - if("eyes") - eye_color = random_eye_color() - if("s_tone") - skin_tone = random_skin_tone() - if("bag") - backbag = rand(1,2) - if("all") - random_character() - - if("input") - switch(href_list["preference"]) - if("ghostform") - if(unlock_content) - var/new_form = input(user, "Thanks for supporting BYOND - Choose your ghostly form:","Thanks for supporting BYOND",null) as null|anything in ghost_forms - if(new_form) - ghost_form = new_form - if("name") - var/new_name = reject_bad_name( input(user, "Choose your character's name:", "Character Preference") as text|null ) - 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("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("hair") - var/new_hair = input(user, "Choose your character's hair colour:", "Character Preference") as null|color - if(new_hair) - hair_color = sanitize_hexcolor(new_hair) - - - if("hair_style") - var/new_hair_style - if(gender == MALE) - new_hair_style = input(user, "Choose your character's hair style:", "Character Preference") as null|anything in hair_styles_male_list - else - new_hair_style = input(user, "Choose your character's hair style:", "Character Preference") as null|anything in hair_styles_female_list - if(new_hair_style) - hair_style = new_hair_style - - if("next_hair_style") - if (gender == MALE) - hair_style = next_list_item(hair_style, hair_styles_male_list) - else - hair_style = next_list_item(hair_style, hair_styles_female_list) - - if("previous_hair_style") - if (gender == MALE) - hair_style = previous_list_item(hair_style, hair_styles_male_list) - else - hair_style = previous_list_item(hair_style, hair_styles_female_list) - - if("facial") - var/new_facial = input(user, "Choose your character's facial-hair colour:", "Character Preference") as null|color - if(new_facial) - facial_hair_color = sanitize_hexcolor(new_facial) - - if("facial_hair_style") - var/new_facial_hair_style - if(gender == MALE) - new_facial_hair_style = input(user, "Choose your character's facial-hair style:", "Character Preference") as null|anything in facial_hair_styles_male_list - else - new_facial_hair_style = input(user, "Choose your character's facial-hair style:", "Character Preference") as null|anything in facial_hair_styles_female_list - if(new_facial_hair_style) - facial_hair_style = new_facial_hair_style - - if("next_facehair_style") - if (gender == MALE) - facial_hair_style = next_list_item(facial_hair_style, facial_hair_styles_male_list) - else - facial_hair_style = next_list_item(facial_hair_style, facial_hair_styles_female_list) - - if("previous_facehair_style") - if (gender == MALE) - facial_hair_style = previous_list_item(facial_hair_style, facial_hair_styles_male_list) - else - facial_hair_style = previous_list_item(facial_hair_style, facial_hair_styles_female_list) - - if("underwear") - var/new_underwear - if(gender == MALE) - new_underwear = input(user, "Choose your character's underwear:", "Character Preference") as null|anything in underwear_m - else - new_underwear = input(user, "Choose your character's underwear:", "Character Preference") as null|anything in underwear_f - if(new_underwear) - underwear = new_underwear - - if("undershirt") - var/new_undershirt - if(gender == MALE) - new_undershirt = input(user, "Choose your character's undershirt:", "Character Preference") as null|anything in undershirt_m - else - new_undershirt = input(user, "Choose your character's undershirt:", "Character Preference") as null|anything in undershirt_f - if(new_undershirt) - undershirt = new_undershirt - - if("socks") - var/new_socks - if(gender == MALE) - new_socks = input(user, "Choose your character's socks:", "Character Preference") as null|anything in socks_m - else - new_socks = input(user, "Choose your character's socks:", "Character Preference") as null|anything in socks_f - if(new_socks) - socks = new_socks - - if("eyes") - var/new_eyes = input(user, "Choose your character's eye colour:", "Character Preference") as color|null - if(new_eyes) - eye_color = sanitize_hexcolor(new_eyes) - - if("species") - - var/result = input(user, "Select a species", "Species Selection") as null|anything in roundstart_species - - if(result) - var/newtype = roundstart_species[result] - pref_species = new newtype() - //Now that we changed our species, we must verify that the mutant colour is still allowed. - var/temp_hsv = RGBtoHSV(features["mcolor"]) - if(features["mcolor"] == "#000" || (!(MUTCOLORS_PARTSONLY in pref_species.specflags) && ReadHSV(temp_hsv)[3] < ReadHSV("#7F7F7F")[3])) - features["mcolor"] = pref_species.default_color - if("mutant_color") - var/new_mutantcolor = input(user, "Choose your character's alien/mutant color:", "Character Preference") as color|null - if(new_mutantcolor) - var/temp_hsv = RGBtoHSV(new_mutantcolor) - if(new_mutantcolor == "#000000") - features["mcolor"] = pref_species.default_color - else if((MUTCOLORS_PARTSONLY in pref_species.specflags) || ReadHSV(temp_hsv)[3] >= ReadHSV("#7F7F7F")[3]) // mutantcolors must be bright, but only if they affect the skin - features["mcolor"] = sanitize_hexcolor(new_mutantcolor) - else - user << "Invalid color. Your color is not bright enough." - - if("tail_lizard") - var/new_tail - new_tail = input(user, "Choose your character's tail:", "Character Preference") as null|anything in tails_list_lizard - if(new_tail) - features["tail_lizard"] = new_tail - - if("tail_human") - var/new_tail - new_tail = input(user, "Choose your character's tail:", "Character Preference") as null|anything in tails_list_human - if(new_tail) - features["tail_human"] = new_tail - - if("snout") - var/new_snout - new_snout = input(user, "Choose your character's snout:", "Character Preference") as null|anything in snouts_list - if(new_snout) - features["snout"] = new_snout - - if("horns") - var/new_horns - new_horns = input(user, "Choose your character's horns:", "Character Preference") as null|anything in horns_list - if(new_horns) - features["horns"] = new_horns - - if("ears") - var/new_ears - new_ears = input(user, "Choose your character's ears:", "Character Preference") as null|anything in ears_list - if(new_ears) - features["ears"] = new_ears - - if("frills") - var/new_frills - new_frills = input(user, "Choose your character's frills:", "Character Preference") as null|anything in frills_list - if(new_frills) - features["frills"] = new_frills - - if("spines") - var/new_spines - new_spines = input(user, "Choose your character's spines:", "Character Preference") as null|anything in spines_list - if(new_spines) - features["spines"] = new_spines - - if("body_markings") - var/new_body_markings - new_body_markings = input(user, "Choose your character's body markings:", "Character Preference") as null|anything in body_markings_list - if(new_body_markings) - features["body_markings"] = new_body_markings - - if("s_tone") - var/new_s_tone = input(user, "Choose your character's skin-tone:", "Character Preference") as null|anything in skin_tones - if(new_s_tone) - skin_tone = new_s_tone - - if("ooccolor") - var/new_ooccolor = input(user, "Choose your OOC colour:", "Game Preference") as color|null - if(new_ooccolor) - ooccolor = sanitize_ooccolor(new_ooccolor) - - 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("clown_name") - var/new_clown_name = reject_bad_name( input(user, "Choose your character's clown name:", "Character Preference") as text|null ) - if(new_clown_name) - custom_names["clown"] = new_clown_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("mime_name") - var/new_mime_name = reject_bad_name( input(user, "Choose your character's mime name:", "Character Preference") as text|null ) - if(new_mime_name) - custom_names["mime"] = new_mime_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("ai_name") - var/new_ai_name = reject_bad_name( input(user, "Choose your character's AI name:", "Character Preference") as text|null, 1 ) - if(new_ai_name) - custom_names["ai"] = new_ai_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, 0-9, -, ' and ." - - if("cyborg_name") - var/new_cyborg_name = reject_bad_name( input(user, "Choose your character's cyborg name:", "Character Preference") as text|null, 1 ) - if(new_cyborg_name) - custom_names["cyborg"] = new_cyborg_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, 0-9, -, ' and ." - - if("religion_name") - var/new_religion_name = reject_bad_name( input(user, "Choose your character's religion:", "Character Preference") as text|null ) - if(new_religion_name) - custom_names["religion"] = new_religion_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("deity_name") - var/new_deity_name = reject_bad_name( input(user, "Choose your character's deity:", "Character Preference") as text|null ) - if(new_deity_name) - custom_names["deity"] = new_deity_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 ("preferred_map") - var/maplist = list() - var/default = "Default" - if (config.defaultmap) - default += " ([config.defaultmap.friendlyname])" - for (var/M in config.maplist) - var/datum/votablemap/VM = config.maplist[M] - var/friendlyname = "[VM.friendlyname] " - if (VM.voteweight <= 0) - friendlyname += " (disabled)" - maplist[friendlyname] = VM.name - maplist[default] = null - var/pickedmap = input(user, "Choose your preferred map. This will be used to help weight random map selection.", "Character Preference") as null|anything in maplist - if (pickedmap) - preferred_map = maplist[pickedmap] - - - else - switch(href_list["preference"]) - if("publicity") - if(unlock_content) - toggles ^= MEMBER_PUBLIC - if("gender") - if(gender == MALE) - gender = FEMALE - else - gender = MALE - underwear = random_underwear(gender) - undershirt = random_undershirt(gender) - socks = random_socks(gender) - facial_hair_style = random_facial_hair_style(gender) - hair_style = random_hair_style(gender) - - if("ui") - switch(UI_style) - if("Midnight") - UI_style = "Plasmafire" - if("Plasmafire") - UI_style = "Retro" - else - UI_style = "Midnight" - - if("nanoui") - nanoui_fancy = !nanoui_fancy - - if("hear_adminhelps") - toggles ^= SOUND_ADMINHELP - if("announce_login") - toggles ^= ANNOUNCE_LOGIN - - if("be_special") - var/be_special_type = href_list["be_special_type"] - if(be_special_type in be_special) - be_special -= be_special_type - else - be_special += be_special_type - - if("name") - be_random_name = !be_random_name - - if("all") - be_random_body = !be_random_body - - if("hear_midis") - toggles ^= SOUND_MIDI - - if("lobby_music") - toggles ^= SOUND_LOBBY - if(toggles & SOUND_LOBBY) - user << sound(ticker.login_music, repeat = 0, wait = 0, volume = 85, channel = 1) - else - user.stopLobbySound() - - if("ghost_ears") - chat_toggles ^= CHAT_GHOSTEARS - - if("ghost_sight") - chat_toggles ^= CHAT_GHOSTSIGHT - - if("ghost_whispers") - chat_toggles ^= CHAT_GHOSTWHISPER - - if("ghost_radio") - chat_toggles ^= CHAT_GHOSTRADIO - - if("ghost_pda") - chat_toggles ^= CHAT_GHOSTPDA - - if("pull_requests") - chat_toggles ^= CHAT_PULLR - - if("allow_midround_antag") - toggles ^= MIDROUND_ANTAG - - if("save") - save_preferences() - save_character() - - if("load") - load_preferences() - load_character() - - if("changeslot") - if(!load_character(text2num(href_list["num"]))) - random_character() - real_name = random_unique_name(gender) - save_character() - - if("tab") - if (href_list["tab"]) - current_tab = text2num(href_list["tab"]) - - ShowChoices(user) - return 1 - -/datum/preferences/proc/copy_to(mob/living/carbon/human/character, icon_updates = 1) - if(be_random_name) - real_name = pref_species.random_name(gender) - - if(be_random_body) - random_character(gender) - - if(config.humans_need_surnames) - var/firstspace = findtext(real_name, " ") - var/name_length = length(real_name) - if(!firstspace) //we need a surname - real_name += " [pick(last_names)]" - else if(firstspace == name_length) - real_name += "[pick(last_names)]" - - character.real_name = real_name - character.name = character.real_name - - character.gender = gender - character.age = age - - character.eye_color = eye_color - character.hair_color = hair_color - character.facial_hair_color = facial_hair_color - - character.skin_tone = skin_tone - character.hair_style = hair_style - character.facial_hair_style = facial_hair_style - character.underwear = underwear - character.undershirt = undershirt - character.socks = socks - - character.backbag = backbag - - character.dna.blood_type = blood_type - character.dna.features = features - character.dna.real_name = character.real_name - var/datum/species/chosen_species - if(pref_species != /datum/species/human && config.mutant_races) - chosen_species = pref_species.type - else - chosen_species = /datum/species/human - character.set_species(chosen_species, icon_update=0) - - if(icon_updates) - character.update_body() - character.update_hair() +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:33 + +var/list/preferences_datums = list() + + + +/datum/preferences + //doohickeys for savefiles + var/path + var/default_slot = 1 //Holder so it doesn't default to slot 1, rather the last one used + var/max_save_slots = 3 + + //non-preference stuff + var/muted = 0 + var/last_ip + var/last_id + + //game-preferences + var/lastchangelog = "" //Saved changlog filesize to detect if there was a change + var/ooccolor = null + + //Antag preferences + var/list/be_special = list() //Special role selection + var/tmp/old_be_special = 0 //Bitflag version of be_special, used to update old savefiles and nothing more + //If it's 0, that's good, if it's anything but 0, the owner of this prefs file's antag choices were, + //autocorrected this round, not that you'd need to check that. + + + var/UI_style = "Midnight" + var/nanoui_fancy = TRUE + var/toggles = TOGGLES_DEFAULT + var/chat_toggles = TOGGLES_DEFAULT_CHAT + var/ghost_form = "ghost" + var/allow_midround_antag = 1 + var/preferred_map = null + + //character preferences + var/real_name //our character's name + var/be_random_name = 0 //whether we'll have a random name every round + var/be_random_body = 0 //whether we'll have a random body every round + var/gender = MALE //gender of character (well duh) + var/age = 30 //age of character + var/blood_type = "A+" //blood type (not-chooseable) + var/underwear = "Nude" //underwear type + var/undershirt = "Nude" //undershirt type + var/socks = "Nude" //socks type + var/backbag = 1 //backpack type + var/hair_style = "Bald" //Hair type + var/hair_color = "000" //Hair color + var/facial_hair_style = "Shaved" //Face hair type + var/facial_hair_color = "000" //Facial hair color + var/skin_tone = "caucasian1" //Skin color + var/eye_color = "000" //Eye color + var/datum/species/pref_species = new /datum/species/human() //Mutant race + var/list/features = list("mcolor" = "FFF", "tail_lizard" = "Smooth", "tail_human" = "None", "snout" = "Round", "horns" = "None", "ears" = "None", "frills" = "None", "spines" = "None", "body_markings" = "None") + + var/list/custom_names = list("clown", "mime", "ai", "cyborg", "religion", "deity") + + //Mob preview + var/icon/preview_icon = null + + //Jobs, uses bitflags + var/job_civilian_high = 0 + var/job_civilian_med = 0 + var/job_civilian_low = 0 + + var/job_medsci_high = 0 + var/job_medsci_med = 0 + var/job_medsci_low = 0 + + var/job_engsec_high = 0 + var/job_engsec_med = 0 + var/job_engsec_low = 0 + + // Want randomjob if preferences already filled - Donkie + var/userandomjob = 1 //defaults to 1 for fewer assistants + + // 0 = character settings, 1 = game preferences + var/current_tab = 0 + + // OOC Metadata: + var/metadata = "" + + var/unlock_content = 0 + + var/list/ignoring = list() + +/datum/preferences/New(client/C) + blood_type = random_blood_type() + custom_names["ai"] = pick(ai_names) + custom_names["cyborg"] = pick(ai_names) + custom_names["clown"] = pick(clown_names) + custom_names["mime"] = pick(mime_names) + if(istype(C)) + if(!IsGuestKey(C.key)) + load_path(C.ckey) + unlock_content = C.IsByondMember() + if(unlock_content) + max_save_slots = 8 + var/loaded_preferences_successfully = load_preferences() + if(loaded_preferences_successfully) + if(load_character()) + return + //we couldn't load character data so just randomize the character appearance + name + random_character() //let's create a random character then - rather than a fat, bald and naked man. + real_name = pref_species.random_name(gender,1) + if(!loaded_preferences_successfully) + save_preferences() + save_character() //let's save this new random character so it doesn't keep generating new ones. + return + + +/datum/preferences/proc/ShowChoices(mob/user) + if(!user || !user.client) return + update_preview_icon() + user << browse_rsc(preview_icon, "previewicon.png") + var/dat = "
" + + dat += "Character Settings " + dat += "Game Preferences" + + if(!path) + dat += "
Please create an account to save your preferences
" + + dat += "
" + + dat += "
" + + switch(current_tab) + if (0) // Character Settings# + if(path) + var/savefile/S = new /savefile(path) + if(S) + dat += "
" + 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!=1) dat += " | " + dat += "[name] " + dat += "
" + + dat += "

Occupation Choices

" + dat += "Set Occupation Preferences
" + dat += "

Identity

" + dat += "" + + + dat += "
" + if(appearance_isbanned(user)) + dat += "You are banned from using custom names and appearances. You can continue to adjust your characters, but you will be randomised once you join the game.
" + dat += "Random Name " + dat += "Always Random Name: [be_random_name ? "Yes" : "No"]
" + + dat += "Name: " + dat += "[real_name]
" + + dat += "Gender: [gender == MALE ? "Male" : "Female"]
" + dat += "Age: [age]
" + + dat += "Special Names:
" + dat += "Clown: [custom_names["clown"]] " + dat += "Mime:[custom_names["mime"]]
" + dat += "AI: [custom_names["ai"]] " + dat += "Cyborg: [custom_names["cyborg"]]
" + dat += "Chaplain religion: [custom_names["religion"]] " + dat += "Chaplain deity: [custom_names["deity"]]
" + + dat += "
" + + dat += "
" + + dat += "

Body

" + dat += "Random Body " + dat += "Always Random Body: [be_random_body ? "Yes" : "No"]
" + + dat += "" + + if(pref_species.use_skintones) + + dat += "" + + if(HAIR in pref_species.specflags) + + dat += "" + + if(EYECOLOR in pref_species.specflags) + + dat += "" + + if(config.mutant_races) //We don't allow mutant bodyparts for humans either unless this is true. + + if((MUTCOLORS in pref_species.specflags) || (MUTCOLORS_PARTSONLY in pref_species.specflags)) + + dat += "" + + if("tail_lizard" in pref_species.mutant_bodyparts) + dat += "" + + if("snout" in pref_species.mutant_bodyparts) + dat += "" + + if("horns" in pref_species.mutant_bodyparts) + dat += "" + + if("frills" in pref_species.mutant_bodyparts) + dat += "" + + if("spines" in pref_species.mutant_bodyparts) + dat += "" + + if("body_markings" in pref_species.mutant_bodyparts) + dat += "" + + if(config.mutant_humans) + + if("tail_human" in pref_species.mutant_bodyparts) + dat += "" + + if("ears" in pref_species.mutant_bodyparts) + dat += "" + + dat += "
" + + if(config.mutant_races) + dat += "Species:
[pref_species.name]
" + else + dat += "Species: Human
" + + dat += "Blood Type: [blood_type]
" + dat += "Underwear:
[underwear]
" + dat += "Undershirt:
[undershirt]
" + dat += "Socks:
[socks]
" + dat += "Backpack:
[backbaglist[backbag]]
" + + dat += "

Skin Tone

" + + dat += "[skin_tone]
" + + dat += "
" + + dat += "

Hair Style

" + + dat += "[hair_style]
" + dat += "< >
" + dat += "    Change
" + + + dat += "
" + + dat += "

Facial Hair Style

" + + dat += "[facial_hair_style]
" + dat += "< >
" + dat += "    Change
" + + dat += "
" + + dat += "

Eye Color

" + + dat += "    Change
" + + dat += "
" + + dat += "

Alien/Mutant Color

" + + dat += "    Change
" + + dat += "
" + + dat += "

Tail

" + + dat += "[features["tail_lizard"]]
" + + dat += "
" + + dat += "

Snout

" + + dat += "[features["snout"]]
" + + dat += "
" + + dat += "

Horns

" + + dat += "[features["horns"]]
" + + dat += "
" + + dat += "

Frills

" + + dat += "[features["frills"]]
" + + dat += "
" + + dat += "

Spines

" + + dat += "[features["spines"]]
" + + dat += "
" + + dat += "

Body Markings

" + + dat += "[features["body_markings"]]
" + + dat += "
" + + dat += "

Tail

" + + dat += "[features["tail_human"]]
" + + dat += "
" + + dat += "

Ears

" + + dat += "[features["ears"]]
" + + dat += "
" + + + if (1) // Game Preferences + dat += "
" + dat += "

General Settings

" + dat += "UI Style: [UI_style]
" + dat += "Fancy NanoUI: [(nanoui_fancy) ? "Yes" : "No"]
" + dat += "Play admin midis: [(toggles & SOUND_MIDI) ? "Yes" : "No"]
" + dat += "Play lobby music: [(toggles & SOUND_LOBBY) ? "Yes" : "No"]
" + dat += "Ghost ears: [(chat_toggles & CHAT_GHOSTEARS) ? "Nearest Creatures" : "All Speech"]
" + dat += "Ghost sight: [(chat_toggles & CHAT_GHOSTSIGHT) ? "Nearest Creatures" : "All Emotes"]
" + dat += "Ghost whispers: [(chat_toggles & CHAT_GHOSTWHISPER) ? "Nearest Creatures" : "All Speech"]
" + dat += "Ghost radio: [(chat_toggles & CHAT_GHOSTRADIO) ? "Yes" : "No"]
" + dat += "Ghost pda: [(chat_toggles & CHAT_GHOSTPDA) ? "Nearest Creatures" : "All Messages"]
" + dat += "Pull requests: [(chat_toggles & CHAT_PULLR) ? "Yes" : "No"]
" + dat += "Midround Antagonist: [(toggles & MIDROUND_ANTAG) ? "Yes" : "No"]
" + if(config.allow_Metadata) + dat += "OOC Notes: Edit
" + + if(user.client) + if(user.client.holder) + dat += "Adminhelp Sound: [(toggles & SOUND_ADMINHELP)?"On":"Off"]
" + dat += "Announce Login: [(toggles & ANNOUNCE_LOGIN)?"On":"Off"]
" + + if(unlock_content || check_rights_for(user.client, R_ADMIN)) + dat += "OOC:     Change
" + + if(unlock_content) + dat += "BYOND Membership Publicity: [(toggles & MEMBER_PUBLIC) ? "Public" : "Hidden"]
" + dat += "Ghost Form: [ghost_form]
" + + if (SERVERTOOLS && config.maprotation) + var/p_map = preferred_map + if (!p_map) + p_map = "Default" + if (config.defaultmap) + p_map += " ([config.defaultmap.friendlyname])" + else + if (p_map in config.maplist) + var/datum/votablemap/VM = config.maplist[p_map] + if (!VM) + p_map += " (No longer exists)" + else + p_map = VM.friendlyname + else + p_map += " (No longer exists)" + dat += "Preferred Map: [p_map]" + + dat += "
" + + dat += "

Special Role Settings

" + + if(jobban_isbanned(user, "Syndicate")) + dat += "You are banned from antagonist roles." + src.be_special = list() + + + for (var/i in special_roles) + if(jobban_isbanned(user, i)) + dat += "Be [capitalize(i)]: BANNED
" + else + var/days_remaining = null + if(config.use_age_restriction_for_jobs && ispath(special_roles[i])) //If it's a game mode antag, check if the player meets the minimum age + var/mode_path = special_roles[i] + var/datum/game_mode/temp_mode = new mode_path + days_remaining = temp_mode.get_remaining_days(user.client) + + if(days_remaining) + dat += "Be [capitalize(i)]: \[IN [days_remaining] DAYS]
" + else + dat += "Be [capitalize(i)]: [(i in be_special) ? "Yes" : "No"]
" + + dat += "
" + + dat += "
" + + if(!IsGuestKey(user.key)) + dat += "Undo " + dat += "Save Setup " + + dat += "Reset Setup" + dat += "
" + + //user << browse(dat, "window=preferences;size=560x560") + var/datum/browser/popup = new(user, "preferences", "
Character Setup
", 640, 750) + popup.set_content(dat) + popup.open(0) + +/datum/preferences/proc/SetChoices(mob/user, limit = 17, list/splitJobs = list("Chief Engineer"), widthPerColumn = 295, height = 620) + if(!SSjob) 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. + //widthPerColumn - Screen's width for every column. + //height - Screen's height. + + var/width = widthPerColumn + + var/HTML = "
" + HTML += "Choose occupation chances
" + HTML += "
Left-click to raise an occupation preference, right-click to lower it.
" + HTML += "
Done

" // Easier to press up here. + HTML += "" + 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 + + for(var/datum/job/job in SSjob.occupations) + + index += 1 + if((index >= limit) || (job.title in splitJobs)) + width += widthPerColumn + 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") && !jobban_isbanned(user, "Assistant")) + HTML += "[rank]" + continue + if(config.enforce_human_authority && !user.client.prefs.pref_species.qualifies_for_rank(rank, user.client.prefs.features)) + if(user.client.prefs.pref_species.id == "human") + HTML += "[rank]" + else + HTML += "[rank]" + continue + if((rank in command_positions) || (rank == "AI"))//Bold head jobs + HTML += "[rank]" + else + HTML += "[rank]" + + HTML += "" + continue + + HTML += "[prefLevelLabel]" + HTML += "" + + for(var/i = 1, i < (limit - index), i += 1) // Finish the column so it is even + HTML += "" + + HTML += "
" + var/rank = job.title + lastJob = job + if(jobban_isbanned(user, rank)) + HTML += "[rank] BANNED
\[IN [(available_in_days)] DAYS\]
\[MUTANT\]
\[NON-HUMAN\]
" + + var/prefLevelLabel = "ERROR" + var/prefLevelColor = "pink" + var/prefUpperLevel = -1 // level to assign on left click + var/prefLowerLevel = -1 // level to assign on right click + + if(GetJobDepartment(job, 1) & job.flag) + prefLevelLabel = "High" + prefLevelColor = "slateblue" + prefUpperLevel = 4 + prefLowerLevel = 2 + else if(GetJobDepartment(job, 2) & job.flag) + prefLevelLabel = "Medium" + prefLevelColor = "green" + prefUpperLevel = 1 + prefLowerLevel = 3 + else if(GetJobDepartment(job, 3) & job.flag) + prefLevelLabel = "Low" + prefLevelColor = "orange" + prefUpperLevel = 2 + prefLowerLevel = 4 + else + prefLevelLabel = "NEVER" + prefLevelColor = "red" + prefUpperLevel = 3 + prefLowerLevel = 1 + + + HTML += "" + + if(rank == "Assistant")//Assistant is special + if(job_civilian_low & ASSISTANT) + HTML += "Yes" + else + HTML += "No" + HTML += "
  
" + + HTML += "
" + + HTML += "

[userandomjob ? "Get random job if preferences unavailable" : "Be an Assistant if preference unavailable"]
" + HTML += "
Reset Preferences
" + + user << browse(null, "window=preferences") + //user << browse(HTML, "window=mob_occupation;size=[width]x[height]") + var/datum/browser/popup = new(user, "mob_occupation", "
Occupation Preferences
", width, height) + popup.set_window_options("can_close=0") + popup.set_content(HTML) + popup.open(0) + return + +/datum/preferences/proc/SetJobPreferenceLevel(datum/job/job, level) + if (!job) + return 0 + + if (level == 1) // to high + // remove any other job(s) set to high + job_civilian_med |= job_civilian_high + job_engsec_med |= job_engsec_high + job_medsci_med |= job_medsci_high + job_civilian_high = 0 + job_engsec_high = 0 + job_medsci_high = 0 + + if (job.department_flag == CIVILIAN) + job_civilian_low &= ~job.flag + job_civilian_med &= ~job.flag + job_civilian_high &= ~job.flag + + switch(level) + if (1) + job_civilian_high |= job.flag + if (2) + job_civilian_med |= job.flag + if (3) + job_civilian_low |= job.flag + + return 1 + else if (job.department_flag == ENGSEC) + job_engsec_low &= ~job.flag + job_engsec_med &= ~job.flag + job_engsec_high &= ~job.flag + + switch(level) + if (1) + job_engsec_high |= job.flag + if (2) + job_engsec_med |= job.flag + if (3) + job_engsec_low |= job.flag + + return 1 + else if (job.department_flag == MEDSCI) + job_medsci_low &= ~job.flag + job_medsci_med &= ~job.flag + job_medsci_high &= ~job.flag + + switch(level) + if (1) + job_medsci_high |= job.flag + if (2) + job_medsci_med |= job.flag + if (3) + job_medsci_low |= job.flag + + return 1 + + return 0 + +/datum/preferences/proc/UpdateJobPreference(mob/user, role, desiredLvl) + if(!SSjob) + return + var/datum/job/job = SSjob.GetJob(role) + + if(!job) + user << browse(null, "window=mob_occupation") + ShowChoices(user) + return + + if (!isnum(desiredLvl)) + user << "UpdateJobPreference - desired level was not a number. Please notify coders!" + 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 + + SetJobPreferenceLevel(job, desiredLvl) + SetChoices(user) + + return 1 + + +/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 + + +/datum/preferences/proc/GetJobDepartment(datum/job/job, 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/process_link(mob/user, list/href_list) + if(href_list["jobbancheck"]) + var/job = sanitizeSQL(href_list["jobbancheck"]) + var/sql_ckey = sanitizeSQL(user.ckey) + var/DBQuery/query_get_jobban = dbcon.NewQuery("SELECT reason, bantime, duration, expiration_time, a_ckey FROM [format_table_name("ban")] WHERE ckey = '[sql_ckey]' AND job = '[job]' AND (bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)") + if(!query_get_jobban.Execute()) + var/err = query_get_jobban.ErrorMsg() + log_game("SQL ERROR obtaining reason from ban table. Error : \[[err]\]\n") + return + if(query_get_jobban.NextRow()) + var/reason = query_get_jobban.item[1] + var/bantime = query_get_jobban.item[2] + var/duration = query_get_jobban.item[3] + var/expiration_time = query_get_jobban.item[4] + var/a_ckey = query_get_jobban.item[5] + var/text + text = "You, or another user of this computer, ([user.ckey]) is banned from playing [job]. The ban reason is:
[reason]
This ban was applied by [a_ckey] on [bantime]" + if(text2num(duration) > 0) + text += ". The ban is for [duration] minutes and expires on [expiration_time] (server time)" + text += ".
" + user << text + 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(jobban_isbanned(user, "Assistant")) + userandomjob = 1 + else + userandomjob = !userandomjob + SetChoices(user) + if("setJobLevel") + UpdateJobPreference(user, href_list["text"], text2num(href_list["level"])) + else + SetChoices(user) + return 1 + + switch(href_list["task"]) + if("random") + switch(href_list["preference"]) + if("name") + real_name = pref_species.random_name(gender,1) + if("age") + age = rand(AGE_MIN, AGE_MAX) + if("hair") + hair_color = random_short_color() + if("hair_style") + hair_style = random_hair_style(gender) + if("facial") + facial_hair_color = random_short_color() + if("facial_hair_style") + facial_hair_style = random_facial_hair_style(gender) + if("underwear") + underwear = random_underwear(gender) + if("undershirt") + undershirt = random_undershirt(gender) + if("socks") + socks = random_socks(gender) + if("eyes") + eye_color = random_eye_color() + if("s_tone") + skin_tone = random_skin_tone() + if("bag") + backbag = rand(1,2) + if("all") + random_character() + + if("input") + switch(href_list["preference"]) + if("ghostform") + if(unlock_content) + var/new_form = input(user, "Thanks for supporting BYOND - Choose your ghostly form:","Thanks for supporting BYOND",null) as null|anything in ghost_forms + if(new_form) + ghost_form = new_form + if("name") + var/new_name = reject_bad_name( input(user, "Choose your character's name:", "Character Preference") as text|null ) + 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("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("hair") + var/new_hair = input(user, "Choose your character's hair colour:", "Character Preference") as null|color + if(new_hair) + hair_color = sanitize_hexcolor(new_hair) + + + if("hair_style") + var/new_hair_style + if(gender == MALE) + new_hair_style = input(user, "Choose your character's hair style:", "Character Preference") as null|anything in hair_styles_male_list + else + new_hair_style = input(user, "Choose your character's hair style:", "Character Preference") as null|anything in hair_styles_female_list + if(new_hair_style) + hair_style = new_hair_style + + if("next_hair_style") + if (gender == MALE) + hair_style = next_list_item(hair_style, hair_styles_male_list) + else + hair_style = next_list_item(hair_style, hair_styles_female_list) + + if("previous_hair_style") + if (gender == MALE) + hair_style = previous_list_item(hair_style, hair_styles_male_list) + else + hair_style = previous_list_item(hair_style, hair_styles_female_list) + + if("facial") + var/new_facial = input(user, "Choose your character's facial-hair colour:", "Character Preference") as null|color + if(new_facial) + facial_hair_color = sanitize_hexcolor(new_facial) + + if("facial_hair_style") + var/new_facial_hair_style + if(gender == MALE) + new_facial_hair_style = input(user, "Choose your character's facial-hair style:", "Character Preference") as null|anything in facial_hair_styles_male_list + else + new_facial_hair_style = input(user, "Choose your character's facial-hair style:", "Character Preference") as null|anything in facial_hair_styles_female_list + if(new_facial_hair_style) + facial_hair_style = new_facial_hair_style + + if("next_facehair_style") + if (gender == MALE) + facial_hair_style = next_list_item(facial_hair_style, facial_hair_styles_male_list) + else + facial_hair_style = next_list_item(facial_hair_style, facial_hair_styles_female_list) + + if("previous_facehair_style") + if (gender == MALE) + facial_hair_style = previous_list_item(facial_hair_style, facial_hair_styles_male_list) + else + facial_hair_style = previous_list_item(facial_hair_style, facial_hair_styles_female_list) + + if("underwear") + var/new_underwear + if(gender == MALE) + new_underwear = input(user, "Choose your character's underwear:", "Character Preference") as null|anything in underwear_m + else + new_underwear = input(user, "Choose your character's underwear:", "Character Preference") as null|anything in underwear_f + if(new_underwear) + underwear = new_underwear + + if("undershirt") + var/new_undershirt + if(gender == MALE) + new_undershirt = input(user, "Choose your character's undershirt:", "Character Preference") as null|anything in undershirt_m + else + new_undershirt = input(user, "Choose your character's undershirt:", "Character Preference") as null|anything in undershirt_f + if(new_undershirt) + undershirt = new_undershirt + + if("socks") + var/new_socks + if(gender == MALE) + new_socks = input(user, "Choose your character's socks:", "Character Preference") as null|anything in socks_m + else + new_socks = input(user, "Choose your character's socks:", "Character Preference") as null|anything in socks_f + if(new_socks) + socks = new_socks + + if("eyes") + var/new_eyes = input(user, "Choose your character's eye colour:", "Character Preference") as color|null + if(new_eyes) + eye_color = sanitize_hexcolor(new_eyes) + + if("species") + + var/result = input(user, "Select a species", "Species Selection") as null|anything in roundstart_species + + if(result) + var/newtype = roundstart_species[result] + pref_species = new newtype() + //Now that we changed our species, we must verify that the mutant colour is still allowed. + var/temp_hsv = RGBtoHSV(features["mcolor"]) + if(features["mcolor"] == "#000" || (!(MUTCOLORS_PARTSONLY in pref_species.specflags) && ReadHSV(temp_hsv)[3] < ReadHSV("#7F7F7F")[3])) + features["mcolor"] = pref_species.default_color + if("mutant_color") + var/new_mutantcolor = input(user, "Choose your character's alien/mutant color:", "Character Preference") as color|null + if(new_mutantcolor) + var/temp_hsv = RGBtoHSV(new_mutantcolor) + if(new_mutantcolor == "#000000") + features["mcolor"] = pref_species.default_color + else if((MUTCOLORS_PARTSONLY in pref_species.specflags) || ReadHSV(temp_hsv)[3] >= ReadHSV("#7F7F7F")[3]) // mutantcolors must be bright, but only if they affect the skin + features["mcolor"] = sanitize_hexcolor(new_mutantcolor) + else + user << "Invalid color. Your color is not bright enough." + + if("tail_lizard") + var/new_tail + new_tail = input(user, "Choose your character's tail:", "Character Preference") as null|anything in tails_list_lizard + if(new_tail) + features["tail_lizard"] = new_tail + + if("tail_human") + var/new_tail + new_tail = input(user, "Choose your character's tail:", "Character Preference") as null|anything in tails_list_human + if(new_tail) + features["tail_human"] = new_tail + + if("snout") + var/new_snout + new_snout = input(user, "Choose your character's snout:", "Character Preference") as null|anything in snouts_list + if(new_snout) + features["snout"] = new_snout + + if("horns") + var/new_horns + new_horns = input(user, "Choose your character's horns:", "Character Preference") as null|anything in horns_list + if(new_horns) + features["horns"] = new_horns + + if("ears") + var/new_ears + new_ears = input(user, "Choose your character's ears:", "Character Preference") as null|anything in ears_list + if(new_ears) + features["ears"] = new_ears + + if("frills") + var/new_frills + new_frills = input(user, "Choose your character's frills:", "Character Preference") as null|anything in frills_list + if(new_frills) + features["frills"] = new_frills + + if("spines") + var/new_spines + new_spines = input(user, "Choose your character's spines:", "Character Preference") as null|anything in spines_list + if(new_spines) + features["spines"] = new_spines + + if("body_markings") + var/new_body_markings + new_body_markings = input(user, "Choose your character's body markings:", "Character Preference") as null|anything in body_markings_list + if(new_body_markings) + features["body_markings"] = new_body_markings + + if("s_tone") + var/new_s_tone = input(user, "Choose your character's skin-tone:", "Character Preference") as null|anything in skin_tones + if(new_s_tone) + skin_tone = new_s_tone + + if("ooccolor") + var/new_ooccolor = input(user, "Choose your OOC colour:", "Game Preference") as color|null + if(new_ooccolor) + ooccolor = sanitize_ooccolor(new_ooccolor) + + 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("clown_name") + var/new_clown_name = reject_bad_name( input(user, "Choose your character's clown name:", "Character Preference") as text|null ) + if(new_clown_name) + custom_names["clown"] = new_clown_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("mime_name") + var/new_mime_name = reject_bad_name( input(user, "Choose your character's mime name:", "Character Preference") as text|null ) + if(new_mime_name) + custom_names["mime"] = new_mime_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("ai_name") + var/new_ai_name = reject_bad_name( input(user, "Choose your character's AI name:", "Character Preference") as text|null, 1 ) + if(new_ai_name) + custom_names["ai"] = new_ai_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, 0-9, -, ' and ." + + if("cyborg_name") + var/new_cyborg_name = reject_bad_name( input(user, "Choose your character's cyborg name:", "Character Preference") as text|null, 1 ) + if(new_cyborg_name) + custom_names["cyborg"] = new_cyborg_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, 0-9, -, ' and ." + + if("religion_name") + var/new_religion_name = reject_bad_name( input(user, "Choose your character's religion:", "Character Preference") as text|null ) + if(new_religion_name) + custom_names["religion"] = new_religion_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("deity_name") + var/new_deity_name = reject_bad_name( input(user, "Choose your character's deity:", "Character Preference") as text|null ) + if(new_deity_name) + custom_names["deity"] = new_deity_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 ("preferred_map") + var/maplist = list() + var/default = "Default" + if (config.defaultmap) + default += " ([config.defaultmap.friendlyname])" + for (var/M in config.maplist) + var/datum/votablemap/VM = config.maplist[M] + var/friendlyname = "[VM.friendlyname] " + if (VM.voteweight <= 0) + friendlyname += " (disabled)" + maplist[friendlyname] = VM.name + maplist[default] = null + var/pickedmap = input(user, "Choose your preferred map. This will be used to help weight random map selection.", "Character Preference") as null|anything in maplist + if (pickedmap) + preferred_map = maplist[pickedmap] + + + else + switch(href_list["preference"]) + if("publicity") + if(unlock_content) + toggles ^= MEMBER_PUBLIC + if("gender") + if(gender == MALE) + gender = FEMALE + else + gender = MALE + underwear = random_underwear(gender) + undershirt = random_undershirt(gender) + socks = random_socks(gender) + facial_hair_style = random_facial_hair_style(gender) + hair_style = random_hair_style(gender) + + if("ui") + switch(UI_style) + if("Midnight") + UI_style = "Plasmafire" + if("Plasmafire") + UI_style = "Retro" + else + UI_style = "Midnight" + + if("nanoui") + nanoui_fancy = !nanoui_fancy + + if("hear_adminhelps") + toggles ^= SOUND_ADMINHELP + if("announce_login") + toggles ^= ANNOUNCE_LOGIN + + if("be_special") + var/be_special_type = href_list["be_special_type"] + if(be_special_type in be_special) + be_special -= be_special_type + else + be_special += be_special_type + + if("name") + be_random_name = !be_random_name + + if("all") + be_random_body = !be_random_body + + if("hear_midis") + toggles ^= SOUND_MIDI + + if("lobby_music") + toggles ^= SOUND_LOBBY + if(toggles & SOUND_LOBBY) + user << sound(ticker.login_music, repeat = 0, wait = 0, volume = 85, channel = 1) + else + user.stopLobbySound() + + if("ghost_ears") + chat_toggles ^= CHAT_GHOSTEARS + + if("ghost_sight") + chat_toggles ^= CHAT_GHOSTSIGHT + + if("ghost_whispers") + chat_toggles ^= CHAT_GHOSTWHISPER + + if("ghost_radio") + chat_toggles ^= CHAT_GHOSTRADIO + + if("ghost_pda") + chat_toggles ^= CHAT_GHOSTPDA + + if("pull_requests") + chat_toggles ^= CHAT_PULLR + + if("allow_midround_antag") + toggles ^= MIDROUND_ANTAG + + if("save") + save_preferences() + save_character() + + if("load") + load_preferences() + load_character() + + if("changeslot") + if(!load_character(text2num(href_list["num"]))) + random_character() + real_name = random_unique_name(gender) + save_character() + + if("tab") + if (href_list["tab"]) + current_tab = text2num(href_list["tab"]) + + ShowChoices(user) + return 1 + +/datum/preferences/proc/copy_to(mob/living/carbon/human/character, icon_updates = 1) + if(be_random_name) + real_name = pref_species.random_name(gender) + + if(be_random_body) + random_character(gender) + + if(config.humans_need_surnames) + var/firstspace = findtext(real_name, " ") + var/name_length = length(real_name) + if(!firstspace) //we need a surname + real_name += " [pick(last_names)]" + else if(firstspace == name_length) + real_name += "[pick(last_names)]" + + character.real_name = real_name + character.name = character.real_name + + character.gender = gender + character.age = age + + character.eye_color = eye_color + character.hair_color = hair_color + character.facial_hair_color = facial_hair_color + + character.skin_tone = skin_tone + character.hair_style = hair_style + character.facial_hair_style = facial_hair_style + character.underwear = underwear + character.undershirt = undershirt + character.socks = socks + + character.backbag = backbag + + character.dna.blood_type = blood_type + character.dna.features = features + character.dna.real_name = character.real_name + var/datum/species/chosen_species + if(pref_species != /datum/species/human && config.mutant_races) + chosen_species = pref_species.type + else + chosen_species = /datum/species/human + character.set_species(chosen_species, icon_update=0) + + if(icon_updates) + character.update_body() + character.update_hair() character.update_mutcolor() \ No newline at end of file diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index 660ebbe3de5..5abcbd7b972 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -1,426 +1,426 @@ -//This is the lowest supported version, anything below this is completely obsolete and the entire savefile will be wiped. -#define SAVEFILE_VERSION_MIN 8 - -//This is the current version, anything below this will attempt to update (if it's not obsolete) -#define SAVEFILE_VERSION_MAX 12 -/* -SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Carn - This proc checks if the current directory of the savefile S needs updating - It is to be used by the load_character and load_preferences procs. - (S.cd=="/" is preferences, S.cd=="/character[integer]" is a character slot, etc) - - if the current directory's version is below SAVEFILE_VERSION_MIN it will simply wipe everything in that directory - (if we're at root "/" then it'll just wipe the entire savefile, for instance.) - - if its version is below SAVEFILE_VERSION_MAX but above the minimum, it will load data but later call the - respective update_preferences() or update_character() proc. - Those procs allow coders to specify format changes so users do not lose their setups and have to redo them again. - - Failing all that, the standard sanity checks are performed. They simply check the data is suitable, reverting to - initial() values if necessary. -*/ -/datum/preferences/proc/savefile_needs_update(savefile/S) - var/savefile_version - S["version"] >> savefile_version - - if(savefile_version < SAVEFILE_VERSION_MIN) - S.dir.Cut() - return -2 - if(savefile_version < SAVEFILE_VERSION_MAX) - return savefile_version - return -1 - - -/datum/preferences/proc/update_antagchoices(current_version) - if((!islist(be_special) || old_be_special ) && current_version < 12) - //Archived values of when antag pref defines were a bitfield+fitflags - var/B_traitor = 1 - var/B_operative = 2 - var/B_changeling = 4 - var/B_wizard = 8 - var/B_malf = 16 - var/B_rev = 32 - var/B_alien = 64 - var/B_pai = 128 - var/B_cultist = 256 - var/B_blob = 512 - var/B_ninja = 1024 - var/B_monkey = 2048 - var/B_gang = 4096 - var/B_shadowling = 8192 - var/B_abductor = 16384 - var/B_revenant = 32768 - - var/list/archived = list(B_traitor,B_operative,B_changeling,B_wizard,B_malf,B_rev,B_alien,B_pai,B_cultist,B_blob,B_ninja,B_monkey,B_gang,B_shadowling,B_abductor,B_revenant) - - be_special = list() - - for(var/flag in archived) - if(old_be_special & flag) - //this is shitty, but this proc should only be run once per player and then never again for the rest of eternity, - switch(flag) - if(1) //why aren't these the variables above? Good question, it's because byond complains the expression isn't constant, when it is. - be_special += ROLE_TRAITOR - if(2) - be_special += ROLE_OPERATIVE - if(4) - be_special += ROLE_CHANGELING - if(8) - be_special += ROLE_WIZARD - if(16) - be_special += ROLE_MALF - if(32) - be_special += ROLE_REV - if(64) - be_special += ROLE_ALIEN - if(128) - be_special += ROLE_PAI - if(256) - be_special += ROLE_CULTIST - if(512) - be_special += ROLE_BLOB - if(1024) - be_special += ROLE_NINJA - if(2048) - be_special += ROLE_MONKEY - if(4096) - be_special += ROLE_GANG - if(8192) - be_special += ROLE_SHADOWLING - if(16384) - be_special += ROLE_ABDUCTOR - if(32768) - be_special += ROLE_REVENANT - - -/datum/preferences/proc/update_preferences(current_version) - if(current_version < 10) - toggles |= MEMBER_PUBLIC - if(current_version < 11) - chat_toggles = TOGGLES_DEFAULT_CHAT - toggles = TOGGLES_DEFAULT - if(current_version < 12) - ignoring = list() - - -//should this proc get fairly long (say 3 versions long), -//just increase SAVEFILE_VERSION_MIN so it's not as far behind -//SAVEFILE_VERSION_MAX and then delete any obsolete if clauses -//from this proc. -//It's only really meant to avoid annoying frequent players -//if your savefile is 3 months out of date, then 'tough shit'. -/datum/preferences/proc/update_character(current_version) - if(current_version < 9) //an example, underwear were an index for a hardcoded list, converting to a string - if(gender == MALE) - switch(underwear) - if(1) underwear = "Mens White" - if(2) underwear = "Mens Grey" - if(3) underwear = "Mens Green" - if(4) underwear = "Mens Blue" - if(5) underwear = "Mens Black" - if(6) underwear = "Mankini" - if(7) underwear = "Mens Hearts Boxer" - if(8) underwear = "Mens Black Boxer" - if(9) underwear = "Mens Grey Boxer" - if(10) underwear = "Mens Striped Boxer" - if(11) underwear = "Mens Kinky" - if(12) underwear = "Mens Red" - if(13) underwear = "Nude" - else - switch(underwear) - if(1) underwear = "Ladies Red" - if(2) underwear = "Ladies White" - if(3) underwear = "Ladies Yellow" - if(4) underwear = "Ladies Blue" - if(5) underwear = "Ladies Black" - if(6) underwear = "Ladies Thong" - if(7) underwear = "Babydoll" - if(8) underwear = "Ladies Baby-Blue" - if(9) underwear = "Ladies Green" - if(10) underwear = "Ladies Pink" - if(11) underwear = "Ladies Kinky" - if(12) underwear = "Tankini" - if(13) underwear = "Nude" - if(!(pref_species in species_list)) - pref_species = new /datum/species/human() - return - -/datum/preferences/proc/load_path(ckey,filename="preferences.sav") - if(!ckey) return - path = "data/player_saves/[copytext(ckey,1,2)]/[ckey]/[filename]" - -/datum/preferences/proc/load_preferences() - if(!path) return 0 - if(!fexists(path)) return 0 - - var/savefile/S = new /savefile(path) - if(!S) return 0 - S.cd = "/" - - var/needs_update = savefile_needs_update(S) - if(needs_update == -2) //fatal, can't load any data - return 0 - - //general preferences - S["ooccolor"] >> ooccolor - S["lastchangelog"] >> lastchangelog - S["UI_style"] >> UI_style - S["nanoui_fancy"] >> nanoui_fancy - S["be_special"] >> be_special - - if(islist(S["be_special"])) - S["be_special"] >> be_special - else //force update and store the old bitflag version of be_special - needs_update = 11 - S["be_special"] >> old_be_special - - S["default_slot"] >> default_slot - S["chat_toggles"] >> chat_toggles - S["toggles"] >> toggles - S["ghost_form"] >> ghost_form - S["preferred_map"] >> preferred_map - S["ignoring"] >> ignoring - - //try to fix any outdated data if necessary - if(needs_update >= 0) - update_preferences(needs_update) //needs_update = savefile_version if we need an update (positive integer) - update_antagchoices(needs_update) - - //Sanitize - ooccolor = sanitize_ooccolor(sanitize_hexcolor(ooccolor, 6, 1, initial(ooccolor))) - lastchangelog = sanitize_text(lastchangelog, initial(lastchangelog)) - UI_style = sanitize_inlist(UI_style, list("Midnight", "Plasmafire", "Retro"), initial(UI_style)) - nanoui_fancy = sanitize_integer(nanoui_fancy, 0, 1, initial(nanoui_fancy)) - default_slot = sanitize_integer(default_slot, 1, max_save_slots, initial(default_slot)) - toggles = sanitize_integer(toggles, 0, 65535, initial(toggles)) - ghost_form = sanitize_inlist(ghost_form, ghost_forms, initial(ghost_form)) - - return 1 - -/datum/preferences/proc/save_preferences() - if(!path) return 0 - var/savefile/S = new /savefile(path) - if(!S) return 0 - S.cd = "/" - - S["version"] << SAVEFILE_VERSION_MAX //updates (or failing that the sanity checks) will ensure data is not invalid at load. Assume up-to-date - - //general preferences - S["ooccolor"] << ooccolor - S["lastchangelog"] << lastchangelog - S["UI_style"] << UI_style - S["nanoui_fancy"] << nanoui_fancy - S["be_special"] << be_special - S["default_slot"] << default_slot - S["toggles"] << toggles - S["chat_toggles"] << chat_toggles - S["ghost_form"] << ghost_form - S["preferred_map"] << preferred_map - S["ignoring"] << ignoring - - return 1 - -/datum/preferences/proc/load_character(slot) - if(!path) return 0 - if(!fexists(path)) return 0 - var/savefile/S = new /savefile(path) - if(!S) return 0 - S.cd = "/" - if(!slot) slot = default_slot - slot = sanitize_integer(slot, 1, max_save_slots, initial(default_slot)) - if(slot != default_slot) - default_slot = slot - S["default_slot"] << slot - - S.cd = "/character[slot]" - var/needs_update = savefile_needs_update(S) - if(needs_update == -2) //fatal, can't load any data - return 0 - - //Species - var/species_id - S["species"] >> species_id - if(config.mutant_races && species_id && (species_id in roundstart_species)) - var/newtype = roundstart_species[species_id] - pref_species = new newtype() - else - pref_species = new /datum/species/human() - - if(!S["features["mcolor"]"] || S["features["mcolor"]"] == "#000") - S["features["mcolor"]"] << "#FFF" - - //Character - S["OOC_Notes"] >> metadata - S["real_name"] >> real_name - S["name_is_always_random"] >> be_random_name - S["body_is_always_random"] >> be_random_body - S["gender"] >> gender - S["age"] >> age - S["hair_color"] >> hair_color - S["facial_hair_color"] >> facial_hair_color - S["eye_color"] >> eye_color - S["skin_tone"] >> skin_tone - S["hair_style_name"] >> hair_style - S["facial_style_name"] >> facial_hair_style - S["underwear"] >> underwear - S["undershirt"] >> undershirt - S["socks"] >> socks - S["backbag"] >> backbag - S["feature_mcolor"] >> features["mcolor"] - S["feature_lizard_tail"] >> features["tail_lizard"] - S["feature_lizard_snout"] >> features["snout"] - S["feature_lizard_horns"] >> features["horns"] - S["feature_lizard_frills"] >> features["frills"] - S["feature_lizard_spines"] >> features["spines"] - S["feature_lizard_body_markings"] >> features["body_markings"] - if(!config.mutant_humans) - features["tail_human"] = "none" - features["ears"] = "none" - else - S["feature_human_tail"] >> features["tail_human"] - S["feature_human_ears"] >> features["ears"] - S["clown_name"] >> custom_names["clown"] - S["mime_name"] >> custom_names["mime"] - S["ai_name"] >> custom_names["ai"] - S["cyborg_name"] >> custom_names["cyborg"] - S["religion_name"] >> custom_names["religion"] - S["deity_name"] >> custom_names["deity"] - - //Jobs - S["userandomjob"] >> userandomjob - S["job_civilian_high"] >> job_civilian_high - S["job_civilian_med"] >> job_civilian_med - S["job_civilian_low"] >> job_civilian_low - S["job_medsci_high"] >> job_medsci_high - S["job_medsci_med"] >> job_medsci_med - S["job_medsci_low"] >> job_medsci_low - S["job_engsec_high"] >> job_engsec_high - S["job_engsec_med"] >> job_engsec_med - S["job_engsec_low"] >> job_engsec_low - - //try to fix any outdated data if necessary - if(needs_update >= 0) - update_character(needs_update) //needs_update == savefile_version if we need an update (positive integer) - - //Sanitize - metadata = sanitize_text(metadata, initial(metadata)) - real_name = reject_bad_name(real_name) - if(!features["mcolor"] || features["mcolor"] == "#000") - features["mcolor"] = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F") - if(!real_name) real_name = random_unique_name(gender) - be_random_name = sanitize_integer(be_random_name, 0, 1, initial(be_random_name)) - be_random_body = sanitize_integer(be_random_body, 0, 1, initial(be_random_body)) - gender = sanitize_gender(gender) - if(gender == MALE) - hair_style = sanitize_inlist(hair_style, hair_styles_male_list) - facial_hair_style = sanitize_inlist(facial_hair_style, facial_hair_styles_male_list) - underwear = sanitize_inlist(underwear, underwear_m) - undershirt = sanitize_inlist(undershirt, undershirt_m) - socks = sanitize_inlist(socks, socks_m) - else - hair_style = sanitize_inlist(hair_style, hair_styles_female_list) - facial_hair_style = sanitize_inlist(facial_hair_style, facial_hair_styles_female_list) - underwear = sanitize_inlist(underwear, underwear_f) - undershirt = sanitize_inlist(undershirt, undershirt_f) - socks = sanitize_inlist(socks, socks_f) - - age = sanitize_integer(age, AGE_MIN, AGE_MAX, initial(age)) - hair_color = sanitize_hexcolor(hair_color, 3, 0) - facial_hair_color = sanitize_hexcolor(facial_hair_color, 3, 0) - eye_color = sanitize_hexcolor(eye_color, 3, 0) - skin_tone = sanitize_inlist(skin_tone, skin_tones) - backbag = sanitize_integer(backbag, 1, backbaglist.len, initial(backbag)) - features["mcolor"] = sanitize_hexcolor(features["mcolor"], 3, 0) - features["tail_lizard"] = sanitize_inlist(features["tail_lizard"], tails_list_lizard) - features["tail_human"] = sanitize_inlist(features["tail_human"], tails_list_human, "None") - features["snout"] = sanitize_inlist(features["snout"], snouts_list) - features["horns"] = sanitize_inlist(features["horns"], horns_list) - features["ears"] = sanitize_inlist(features["ears"], ears_list, "None") - features["frills"] = sanitize_inlist(features["frills"], frills_list) - features["spines"] = sanitize_inlist(features["spines"], spines_list) - features["body_markings"] = sanitize_inlist(features["body_markings"], body_markings_list) - - userandomjob = sanitize_integer(userandomjob, 0, 1, initial(userandomjob)) - job_civilian_high = sanitize_integer(job_civilian_high, 0, 65535, initial(job_civilian_high)) - job_civilian_med = sanitize_integer(job_civilian_med, 0, 65535, initial(job_civilian_med)) - job_civilian_low = sanitize_integer(job_civilian_low, 0, 65535, initial(job_civilian_low)) - job_medsci_high = sanitize_integer(job_medsci_high, 0, 65535, initial(job_medsci_high)) - job_medsci_med = sanitize_integer(job_medsci_med, 0, 65535, initial(job_medsci_med)) - job_medsci_low = sanitize_integer(job_medsci_low, 0, 65535, initial(job_medsci_low)) - job_engsec_high = sanitize_integer(job_engsec_high, 0, 65535, initial(job_engsec_high)) - job_engsec_med = sanitize_integer(job_engsec_med, 0, 65535, initial(job_engsec_med)) - job_engsec_low = sanitize_integer(job_engsec_low, 0, 65535, initial(job_engsec_low)) - - return 1 - -/datum/preferences/proc/save_character() - if(!path) return 0 - var/savefile/S = new /savefile(path) - if(!S) return 0 - S.cd = "/character[default_slot]" - - S["version"] << SAVEFILE_VERSION_MAX //load_character will sanitize any bad data, so assume up-to-date. - - //Character - S["OOC_Notes"] << metadata - S["real_name"] << real_name - S["name_is_always_random"] << be_random_name - S["body_is_always_random"] << be_random_body - S["gender"] << gender - S["age"] << age - S["hair_color"] << hair_color - S["facial_hair_color"] << facial_hair_color - S["eye_color"] << eye_color - S["skin_tone"] << skin_tone - S["hair_style_name"] << hair_style - S["facial_style_name"] << facial_hair_style - S["underwear"] << underwear - S["undershirt"] << undershirt - S["socks"] << socks - S["backbag"] << backbag - S["species"] << pref_species.id - S["feature_mcolor"] << features["mcolor"] - S["feature_lizard_tail"] << features["tail_lizard"] - S["feature_human_tail"] << features["tail_human"] - S["feature_lizard_snout"] << features["snout"] - S["feature_lizard_horns"] << features["horns"] - S["feature_human_ears"] << features["ears"] - S["feature_lizard_frills"] << features["frills"] - S["feature_lizard_spines"] << features["spines"] - S["feature_lizard_body_markings"] << features["body_markings"] - S["clown_name"] << custom_names["clown"] - S["mime_name"] << custom_names["mime"] - S["ai_name"] << custom_names["ai"] - S["cyborg_name"] << custom_names["cyborg"] - S["religion_name"] << custom_names["religion"] - S["deity_name"] << custom_names["deity"] - - //Jobs - S["userandomjob"] << userandomjob - S["job_civilian_high"] << job_civilian_high - S["job_civilian_med"] << job_civilian_med - S["job_civilian_low"] << job_civilian_low - S["job_medsci_high"] << job_medsci_high - S["job_medsci_med"] << job_medsci_med - S["job_medsci_low"] << job_medsci_low - S["job_engsec_high"] << job_engsec_high - S["job_engsec_med"] << job_engsec_med - S["job_engsec_low"] << job_engsec_low - - return 1 - - -#undef SAVEFILE_VERSION_MAX -#undef SAVEFILE_VERSION_MIN -/* -//DEBUG -//Some crude tools for testing savefiles -//path is the savefile path -/client/verb/savefile_export(path as text) - var/savefile/S = new /savefile(path) - S.ExportText("/",file("[path].txt")) -//path is the savefile path -/client/verb/savefile_import(path as text) - var/savefile/S = new /savefile(path) - S.ImportText("/",file("[path].txt")) -*/ +//This is the lowest supported version, anything below this is completely obsolete and the entire savefile will be wiped. +#define SAVEFILE_VERSION_MIN 8 + +//This is the current version, anything below this will attempt to update (if it's not obsolete) +#define SAVEFILE_VERSION_MAX 12 +/* +SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Carn + This proc checks if the current directory of the savefile S needs updating + It is to be used by the load_character and load_preferences procs. + (S.cd=="/" is preferences, S.cd=="/character[integer]" is a character slot, etc) + + if the current directory's version is below SAVEFILE_VERSION_MIN it will simply wipe everything in that directory + (if we're at root "/" then it'll just wipe the entire savefile, for instance.) + + if its version is below SAVEFILE_VERSION_MAX but above the minimum, it will load data but later call the + respective update_preferences() or update_character() proc. + Those procs allow coders to specify format changes so users do not lose their setups and have to redo them again. + + Failing all that, the standard sanity checks are performed. They simply check the data is suitable, reverting to + initial() values if necessary. +*/ +/datum/preferences/proc/savefile_needs_update(savefile/S) + var/savefile_version + S["version"] >> savefile_version + + if(savefile_version < SAVEFILE_VERSION_MIN) + S.dir.Cut() + return -2 + if(savefile_version < SAVEFILE_VERSION_MAX) + return savefile_version + return -1 + + +/datum/preferences/proc/update_antagchoices(current_version) + if((!islist(be_special) || old_be_special ) && current_version < 12) + //Archived values of when antag pref defines were a bitfield+fitflags + var/B_traitor = 1 + var/B_operative = 2 + var/B_changeling = 4 + var/B_wizard = 8 + var/B_malf = 16 + var/B_rev = 32 + var/B_alien = 64 + var/B_pai = 128 + var/B_cultist = 256 + var/B_blob = 512 + var/B_ninja = 1024 + var/B_monkey = 2048 + var/B_gang = 4096 + var/B_shadowling = 8192 + var/B_abductor = 16384 + var/B_revenant = 32768 + + var/list/archived = list(B_traitor,B_operative,B_changeling,B_wizard,B_malf,B_rev,B_alien,B_pai,B_cultist,B_blob,B_ninja,B_monkey,B_gang,B_shadowling,B_abductor,B_revenant) + + be_special = list() + + for(var/flag in archived) + if(old_be_special & flag) + //this is shitty, but this proc should only be run once per player and then never again for the rest of eternity, + switch(flag) + if(1) //why aren't these the variables above? Good question, it's because byond complains the expression isn't constant, when it is. + be_special += ROLE_TRAITOR + if(2) + be_special += ROLE_OPERATIVE + if(4) + be_special += ROLE_CHANGELING + if(8) + be_special += ROLE_WIZARD + if(16) + be_special += ROLE_MALF + if(32) + be_special += ROLE_REV + if(64) + be_special += ROLE_ALIEN + if(128) + be_special += ROLE_PAI + if(256) + be_special += ROLE_CULTIST + if(512) + be_special += ROLE_BLOB + if(1024) + be_special += ROLE_NINJA + if(2048) + be_special += ROLE_MONKEY + if(4096) + be_special += ROLE_GANG + if(8192) + be_special += ROLE_SHADOWLING + if(16384) + be_special += ROLE_ABDUCTOR + if(32768) + be_special += ROLE_REVENANT + + +/datum/preferences/proc/update_preferences(current_version) + if(current_version < 10) + toggles |= MEMBER_PUBLIC + if(current_version < 11) + chat_toggles = TOGGLES_DEFAULT_CHAT + toggles = TOGGLES_DEFAULT + if(current_version < 12) + ignoring = list() + + +//should this proc get fairly long (say 3 versions long), +//just increase SAVEFILE_VERSION_MIN so it's not as far behind +//SAVEFILE_VERSION_MAX and then delete any obsolete if clauses +//from this proc. +//It's only really meant to avoid annoying frequent players +//if your savefile is 3 months out of date, then 'tough shit'. +/datum/preferences/proc/update_character(current_version) + if(current_version < 9) //an example, underwear were an index for a hardcoded list, converting to a string + if(gender == MALE) + switch(underwear) + if(1) underwear = "Mens White" + if(2) underwear = "Mens Grey" + if(3) underwear = "Mens Green" + if(4) underwear = "Mens Blue" + if(5) underwear = "Mens Black" + if(6) underwear = "Mankini" + if(7) underwear = "Mens Hearts Boxer" + if(8) underwear = "Mens Black Boxer" + if(9) underwear = "Mens Grey Boxer" + if(10) underwear = "Mens Striped Boxer" + if(11) underwear = "Mens Kinky" + if(12) underwear = "Mens Red" + if(13) underwear = "Nude" + else + switch(underwear) + if(1) underwear = "Ladies Red" + if(2) underwear = "Ladies White" + if(3) underwear = "Ladies Yellow" + if(4) underwear = "Ladies Blue" + if(5) underwear = "Ladies Black" + if(6) underwear = "Ladies Thong" + if(7) underwear = "Babydoll" + if(8) underwear = "Ladies Baby-Blue" + if(9) underwear = "Ladies Green" + if(10) underwear = "Ladies Pink" + if(11) underwear = "Ladies Kinky" + if(12) underwear = "Tankini" + if(13) underwear = "Nude" + if(!(pref_species in species_list)) + pref_species = new /datum/species/human() + return + +/datum/preferences/proc/load_path(ckey,filename="preferences.sav") + if(!ckey) return + path = "data/player_saves/[copytext(ckey,1,2)]/[ckey]/[filename]" + +/datum/preferences/proc/load_preferences() + if(!path) return 0 + if(!fexists(path)) return 0 + + var/savefile/S = new /savefile(path) + if(!S) return 0 + S.cd = "/" + + var/needs_update = savefile_needs_update(S) + if(needs_update == -2) //fatal, can't load any data + return 0 + + //general preferences + S["ooccolor"] >> ooccolor + S["lastchangelog"] >> lastchangelog + S["UI_style"] >> UI_style + S["nanoui_fancy"] >> nanoui_fancy + S["be_special"] >> be_special + + if(islist(S["be_special"])) + S["be_special"] >> be_special + else //force update and store the old bitflag version of be_special + needs_update = 11 + S["be_special"] >> old_be_special + + S["default_slot"] >> default_slot + S["chat_toggles"] >> chat_toggles + S["toggles"] >> toggles + S["ghost_form"] >> ghost_form + S["preferred_map"] >> preferred_map + S["ignoring"] >> ignoring + + //try to fix any outdated data if necessary + if(needs_update >= 0) + update_preferences(needs_update) //needs_update = savefile_version if we need an update (positive integer) + update_antagchoices(needs_update) + + //Sanitize + ooccolor = sanitize_ooccolor(sanitize_hexcolor(ooccolor, 6, 1, initial(ooccolor))) + lastchangelog = sanitize_text(lastchangelog, initial(lastchangelog)) + UI_style = sanitize_inlist(UI_style, list("Midnight", "Plasmafire", "Retro"), initial(UI_style)) + nanoui_fancy = sanitize_integer(nanoui_fancy, 0, 1, initial(nanoui_fancy)) + default_slot = sanitize_integer(default_slot, 1, max_save_slots, initial(default_slot)) + toggles = sanitize_integer(toggles, 0, 65535, initial(toggles)) + ghost_form = sanitize_inlist(ghost_form, ghost_forms, initial(ghost_form)) + + return 1 + +/datum/preferences/proc/save_preferences() + if(!path) return 0 + var/savefile/S = new /savefile(path) + if(!S) return 0 + S.cd = "/" + + S["version"] << SAVEFILE_VERSION_MAX //updates (or failing that the sanity checks) will ensure data is not invalid at load. Assume up-to-date + + //general preferences + S["ooccolor"] << ooccolor + S["lastchangelog"] << lastchangelog + S["UI_style"] << UI_style + S["nanoui_fancy"] << nanoui_fancy + S["be_special"] << be_special + S["default_slot"] << default_slot + S["toggles"] << toggles + S["chat_toggles"] << chat_toggles + S["ghost_form"] << ghost_form + S["preferred_map"] << preferred_map + S["ignoring"] << ignoring + + return 1 + +/datum/preferences/proc/load_character(slot) + if(!path) return 0 + if(!fexists(path)) return 0 + var/savefile/S = new /savefile(path) + if(!S) return 0 + S.cd = "/" + if(!slot) slot = default_slot + slot = sanitize_integer(slot, 1, max_save_slots, initial(default_slot)) + if(slot != default_slot) + default_slot = slot + S["default_slot"] << slot + + S.cd = "/character[slot]" + var/needs_update = savefile_needs_update(S) + if(needs_update == -2) //fatal, can't load any data + return 0 + + //Species + var/species_id + S["species"] >> species_id + if(config.mutant_races && species_id && (species_id in roundstart_species)) + var/newtype = roundstart_species[species_id] + pref_species = new newtype() + else + pref_species = new /datum/species/human() + + if(!S["features["mcolor"]"] || S["features["mcolor"]"] == "#000") + S["features["mcolor"]"] << "#FFF" + + //Character + S["OOC_Notes"] >> metadata + S["real_name"] >> real_name + S["name_is_always_random"] >> be_random_name + S["body_is_always_random"] >> be_random_body + S["gender"] >> gender + S["age"] >> age + S["hair_color"] >> hair_color + S["facial_hair_color"] >> facial_hair_color + S["eye_color"] >> eye_color + S["skin_tone"] >> skin_tone + S["hair_style_name"] >> hair_style + S["facial_style_name"] >> facial_hair_style + S["underwear"] >> underwear + S["undershirt"] >> undershirt + S["socks"] >> socks + S["backbag"] >> backbag + S["feature_mcolor"] >> features["mcolor"] + S["feature_lizard_tail"] >> features["tail_lizard"] + S["feature_lizard_snout"] >> features["snout"] + S["feature_lizard_horns"] >> features["horns"] + S["feature_lizard_frills"] >> features["frills"] + S["feature_lizard_spines"] >> features["spines"] + S["feature_lizard_body_markings"] >> features["body_markings"] + if(!config.mutant_humans) + features["tail_human"] = "none" + features["ears"] = "none" + else + S["feature_human_tail"] >> features["tail_human"] + S["feature_human_ears"] >> features["ears"] + S["clown_name"] >> custom_names["clown"] + S["mime_name"] >> custom_names["mime"] + S["ai_name"] >> custom_names["ai"] + S["cyborg_name"] >> custom_names["cyborg"] + S["religion_name"] >> custom_names["religion"] + S["deity_name"] >> custom_names["deity"] + + //Jobs + S["userandomjob"] >> userandomjob + S["job_civilian_high"] >> job_civilian_high + S["job_civilian_med"] >> job_civilian_med + S["job_civilian_low"] >> job_civilian_low + S["job_medsci_high"] >> job_medsci_high + S["job_medsci_med"] >> job_medsci_med + S["job_medsci_low"] >> job_medsci_low + S["job_engsec_high"] >> job_engsec_high + S["job_engsec_med"] >> job_engsec_med + S["job_engsec_low"] >> job_engsec_low + + //try to fix any outdated data if necessary + if(needs_update >= 0) + update_character(needs_update) //needs_update == savefile_version if we need an update (positive integer) + + //Sanitize + metadata = sanitize_text(metadata, initial(metadata)) + real_name = reject_bad_name(real_name) + if(!features["mcolor"] || features["mcolor"] == "#000") + features["mcolor"] = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F") + if(!real_name) real_name = random_unique_name(gender) + be_random_name = sanitize_integer(be_random_name, 0, 1, initial(be_random_name)) + be_random_body = sanitize_integer(be_random_body, 0, 1, initial(be_random_body)) + gender = sanitize_gender(gender) + if(gender == MALE) + hair_style = sanitize_inlist(hair_style, hair_styles_male_list) + facial_hair_style = sanitize_inlist(facial_hair_style, facial_hair_styles_male_list) + underwear = sanitize_inlist(underwear, underwear_m) + undershirt = sanitize_inlist(undershirt, undershirt_m) + socks = sanitize_inlist(socks, socks_m) + else + hair_style = sanitize_inlist(hair_style, hair_styles_female_list) + facial_hair_style = sanitize_inlist(facial_hair_style, facial_hair_styles_female_list) + underwear = sanitize_inlist(underwear, underwear_f) + undershirt = sanitize_inlist(undershirt, undershirt_f) + socks = sanitize_inlist(socks, socks_f) + + age = sanitize_integer(age, AGE_MIN, AGE_MAX, initial(age)) + hair_color = sanitize_hexcolor(hair_color, 3, 0) + facial_hair_color = sanitize_hexcolor(facial_hair_color, 3, 0) + eye_color = sanitize_hexcolor(eye_color, 3, 0) + skin_tone = sanitize_inlist(skin_tone, skin_tones) + backbag = sanitize_integer(backbag, 1, backbaglist.len, initial(backbag)) + features["mcolor"] = sanitize_hexcolor(features["mcolor"], 3, 0) + features["tail_lizard"] = sanitize_inlist(features["tail_lizard"], tails_list_lizard) + features["tail_human"] = sanitize_inlist(features["tail_human"], tails_list_human, "None") + features["snout"] = sanitize_inlist(features["snout"], snouts_list) + features["horns"] = sanitize_inlist(features["horns"], horns_list) + features["ears"] = sanitize_inlist(features["ears"], ears_list, "None") + features["frills"] = sanitize_inlist(features["frills"], frills_list) + features["spines"] = sanitize_inlist(features["spines"], spines_list) + features["body_markings"] = sanitize_inlist(features["body_markings"], body_markings_list) + + userandomjob = sanitize_integer(userandomjob, 0, 1, initial(userandomjob)) + job_civilian_high = sanitize_integer(job_civilian_high, 0, 65535, initial(job_civilian_high)) + job_civilian_med = sanitize_integer(job_civilian_med, 0, 65535, initial(job_civilian_med)) + job_civilian_low = sanitize_integer(job_civilian_low, 0, 65535, initial(job_civilian_low)) + job_medsci_high = sanitize_integer(job_medsci_high, 0, 65535, initial(job_medsci_high)) + job_medsci_med = sanitize_integer(job_medsci_med, 0, 65535, initial(job_medsci_med)) + job_medsci_low = sanitize_integer(job_medsci_low, 0, 65535, initial(job_medsci_low)) + job_engsec_high = sanitize_integer(job_engsec_high, 0, 65535, initial(job_engsec_high)) + job_engsec_med = sanitize_integer(job_engsec_med, 0, 65535, initial(job_engsec_med)) + job_engsec_low = sanitize_integer(job_engsec_low, 0, 65535, initial(job_engsec_low)) + + return 1 + +/datum/preferences/proc/save_character() + if(!path) return 0 + var/savefile/S = new /savefile(path) + if(!S) return 0 + S.cd = "/character[default_slot]" + + S["version"] << SAVEFILE_VERSION_MAX //load_character will sanitize any bad data, so assume up-to-date. + + //Character + S["OOC_Notes"] << metadata + S["real_name"] << real_name + S["name_is_always_random"] << be_random_name + S["body_is_always_random"] << be_random_body + S["gender"] << gender + S["age"] << age + S["hair_color"] << hair_color + S["facial_hair_color"] << facial_hair_color + S["eye_color"] << eye_color + S["skin_tone"] << skin_tone + S["hair_style_name"] << hair_style + S["facial_style_name"] << facial_hair_style + S["underwear"] << underwear + S["undershirt"] << undershirt + S["socks"] << socks + S["backbag"] << backbag + S["species"] << pref_species.id + S["feature_mcolor"] << features["mcolor"] + S["feature_lizard_tail"] << features["tail_lizard"] + S["feature_human_tail"] << features["tail_human"] + S["feature_lizard_snout"] << features["snout"] + S["feature_lizard_horns"] << features["horns"] + S["feature_human_ears"] << features["ears"] + S["feature_lizard_frills"] << features["frills"] + S["feature_lizard_spines"] << features["spines"] + S["feature_lizard_body_markings"] << features["body_markings"] + S["clown_name"] << custom_names["clown"] + S["mime_name"] << custom_names["mime"] + S["ai_name"] << custom_names["ai"] + S["cyborg_name"] << custom_names["cyborg"] + S["religion_name"] << custom_names["religion"] + S["deity_name"] << custom_names["deity"] + + //Jobs + S["userandomjob"] << userandomjob + S["job_civilian_high"] << job_civilian_high + S["job_civilian_med"] << job_civilian_med + S["job_civilian_low"] << job_civilian_low + S["job_medsci_high"] << job_medsci_high + S["job_medsci_med"] << job_medsci_med + S["job_medsci_low"] << job_medsci_low + S["job_engsec_high"] << job_engsec_high + S["job_engsec_med"] << job_engsec_med + S["job_engsec_low"] << job_engsec_low + + return 1 + + +#undef SAVEFILE_VERSION_MAX +#undef SAVEFILE_VERSION_MIN +/* +//DEBUG +//Some crude tools for testing savefiles +//path is the savefile path +/client/verb/savefile_export(path as text) + var/savefile/S = new /savefile(path) + S.ExportText("/",file("[path].txt")) +//path is the savefile path +/client/verb/savefile_import(path as text) + var/savefile/S = new /savefile(path) + S.ImportText("/",file("[path].txt")) +*/ diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm index a9ce46ce9df..c7f42350d07 100644 --- a/code/modules/clothing/suits/armor.dm +++ b/code/modules/clothing/suits/armor.dm @@ -1,300 +1,300 @@ -/obj/item/clothing/suit/armor - allowed = list(/obj/item/weapon/gun/energy,/obj/item/weapon/reagent_containers/spray/pepper,/obj/item/weapon/gun/projectile,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/restraints/handcuffs,/obj/item/device/flashlight/seclite,/obj/item/weapon/melee/classic_baton/telescopic) - body_parts_covered = CHEST - cold_protection = CHEST|GROIN - min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT - heat_protection = CHEST|GROIN - max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT - strip_delay = 60 - put_on_delay = 40 - burn_state = FIRE_PROOF - -/obj/item/clothing/suit/armor/vest - name = "armor" - desc = "A slim armored vest that protects against most types of damage." - icon_state = "armor" - item_state = "armor" - blood_overlay_type = "armor" - armor = list(melee = 25, bullet = 15, laser = 25, energy = 10, bomb = 25, bio = 0, rad = 0) - -/obj/item/clothing/suit/armor/hos - name = "armored greatcoat" - desc = "A greatcoat enchanced with a special alloy for some protection and style for those with a commanding presence." - icon_state = "hos" - item_state = "greatcoat" - body_parts_covered = CHEST|GROIN|ARMS|LEGS - armor = list(melee = 30, bullet = 30, laser = 30, energy = 10, bomb = 25, bio = 0, rad = 0) - flags_inv = HIDEJUMPSUIT - cold_protection = CHEST|GROIN|LEGS|ARMS - heat_protection = CHEST|GROIN|LEGS|ARMS - strip_delay = 80 - -/obj/item/clothing/suit/armor/hos/trenchcoat - name = "armored trenchoat" - desc = "A trenchcoat enchanced with a special lightweight kevlar. The epitome of tactical plainclothes." - icon_state = "hostrench" - item_state = "hostrench" - flags_inv = 0 - strip_delay = 80 - -/obj/item/clothing/suit/armor/vest/warden - name = "warden's jacket" - desc = "A red jacket with silver rank pips and body armor strapped on top." - icon_state = "warden_jacket" - item_state = "armor" - body_parts_covered = CHEST|GROIN|ARMS - cold_protection = CHEST|GROIN|ARMS|HANDS - heat_protection = CHEST|GROIN|ARMS|HANDS - strip_delay = 70 - burn_state = FLAMMABLE - -/obj/item/clothing/suit/armor/vest/warden/alt - name = "warden's armored jacket" - desc = "A navy-blue armored jacket with blue shoulder designations and '/Warden/' stitched into one of the chest pockets." - icon_state = "warden_alt" - -/obj/item/clothing/suit/armor/vest/leather - name = "security overcoat" - desc = "Lightly armored leather overcoat meant as casual wear for high-ranking officers. Bears the crest of Nanotrasen Security." - icon_state = "leathercoat-sec" - item_state = "hostrench" - body_parts_covered = CHEST|GROIN|ARMS|LEGS - cold_protection = CHEST|GROIN|LEGS|ARMS - heat_protection = CHEST|GROIN|LEGS|ARMS - -/obj/item/clothing/suit/armor/vest/capcarapace - name = "captain's carapace" - desc = "An armored vest reinforced with ceramic plates and pauldrons to provide additional protection whilst still offering maximum mobility and flexibility. Issued only to the station's finest, although it does chafe your nipples." - icon_state = "capcarapace" - item_state = "armor" - body_parts_covered = CHEST|GROIN - armor = list(melee = 50, bullet = 40, laser = 50, energy = 10, bomb = 25, bio = 0, rad = 0) - -/obj/item/clothing/suit/armor/vest/capcarapace/alt - name = "captain's parade jacket" - desc = "For when an armoured vest isn't fashionable enough." - icon_state = "capformal" - item_state = "capspacesuit" - -/obj/item/clothing/suit/armor/riot - name = "riot suit" - desc = "A suit of armor with heavy padding to protect against melee attacks. Looks like it might impair movement." - icon_state = "riot" - item_state = "swat_suit" - body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS - cold_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS - heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS - armor = list(melee = 50, bullet = 10, laser = 10, energy = 10, bomb = 0, bio = 0, rad = 0) - flags_inv = HIDEJUMPSUIT - strip_delay = 80 - put_on_delay = 60 - -/obj/item/clothing/suit/armor/bulletproof - name = "bulletproof armor" - desc = "A bulletproof vest that excels in protecting the wearer against traditional projectile weaponry and explosives to a minor extent." - icon_state = "bulletproof" - item_state = "armor" - blood_overlay_type = "armor" - armor = list(melee = 15, bullet = 80, laser = 10, energy = 10, bomb = 40, bio = 0, rad = 0) - strip_delay = 70 - put_on_delay = 50 - -/obj/item/clothing/suit/armor/laserproof - name = "reflector vest" - desc = "A vest that excels in protecting the wearer against energy projectiles, as well as occasionally reflecting them." - icon_state = "armor_reflec" - item_state = "armor_reflec" - blood_overlay_type = "armor" - armor = list(melee = 10, bullet = 10, laser = 60, energy = 50, bomb = 0, bio = 0, rad = 0) - var/hit_reflect_chance = 40 - -/obj/item/clothing/suit/armor/laserproof/IsReflect(def_zone) - if(!(def_zone in list("chest", "groin"))) //If not shot where ablative is covering you, you don't get the reflection bonus! - return 0 - if (prob(hit_reflect_chance)) - return 1 - -/obj/item/clothing/suit/armor/vest/det_suit - name = "armor" - desc = "An armored vest with a detective's badge on it." - icon_state = "detective-armor" - allowed = list(/obj/item/weapon/tank/internals/emergency_oxygen,/obj/item/weapon/reagent_containers/spray/pepper,/obj/item/device/flashlight,/obj/item/weapon/gun/energy,/obj/item/weapon/gun/projectile,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/restraints/handcuffs,/obj/item/weapon/storage/fancy/cigarettes,/obj/item/weapon/lighter,/obj/item/device/detective_scanner,/obj/item/device/taperecorder) - burn_state = FLAMMABLE - - -//Reactive armor -//When the wearer gets hit, this armor will teleport the user a short distance away (to safety or to more danger, no one knows. That's the fun of it!) -/obj/item/clothing/suit/armor/reactive - name = "reactive teleport armor" - desc = "Someone seperated our Research Director from his own head!" - var/active = 0 - icon_state = "reactiveoff" - item_state = "reactiveoff" - blood_overlay_type = "armor" - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0) - action_button_name = "Toggle Armor" - unacidable = 1 - hit_reaction_chance = 50 - - -/obj/item/clothing/suit/armor/reactive/attack_self(mob/user) - src.active = !( src.active ) - if (src.active) - user << "[src] is now active." - src.icon_state = "reactive" - src.item_state = "reactive" - else - user << "[src] is now inactive." - src.icon_state = "reactiveoff" - src.item_state = "reactiveoff" - src.add_fingerprint(user) - return - -/obj/item/clothing/suit/armor/reactive/emp_act(severity) - active = 0 - src.icon_state = "reactiveoff" - src.item_state = "reactiveoff" - ..() - -/obj/item/clothing/suit/armor/reactive/hit_reaction(mob/living/carbon/human/owner, attack_text, final_block_chance) - if(!active) - return 0 - if(prob(hit_reaction_chance)) - var/mob/living/carbon/human/H = owner - owner.visible_message("The reactive teleport system flings [H] clear of [attack_text]!") - var/list/turfs = new/list() - for(var/turf/T in orange(6, H)) - if(T.density) - continue - if(T.x>world.maxx-6 || T.x<6) - continue - if(T.y>world.maxy-6 || T.y<6) - continue - turfs += T - if(!turfs.len) - turfs += pick(/turf in orange(6, H)) - var/turf/picked = pick(turfs) - if(!isturf(picked)) - return - if(H.buckled) - H.buckled.unbuckle_mob() - H.forceMove(picked) - return 1 - return 0 - -/obj/item/clothing/suit/armor/reactive/fire - name = "reactive incendiary armor" - - -/obj/item/clothing/suit/armor/reactive/fire/hit_reaction(mob/living/carbon/human/owner, attack_text) - if(!active) - return 0 - if(prob(hit_reaction_chance)) - owner.visible_message("The [src] blocks the [attack_text], sending out jets of flame!") - for(var/mob/living/carbon/C in range(6, owner)) - if(C != owner) - C.fire_stacks += 8 - C.IgniteMob() - owner.fire_stacks = -20 - return 1 - return 0 - - - - - - -/obj/item/clothing/suit/armor/reactive/stealth - name = "reactive stealth armor" - -/obj/item/clothing/suit/armor/reactive/stealth/hit_reaction(mob/living/carbon/human/owner, attack_text) - if(!active) - return 0 - if(prob(hit_reaction_chance)) - var/mob/living/simple_animal/hostile/illusion/escape/E = new(owner.loc) - E.Copy_Parent(owner, 50) - E.GiveTarget(owner) //so it starts running right away - E.Goto(owner, E.move_to_delay, E.minimum_distance) - owner.alpha = 0 - owner.visible_message("[owner] is hit by [attack_text] in the chest!") //We pretend to be hit, since blocking it would stop the message otherwise - spawn(40) - owner.alpha = initial(owner.alpha) - return 1 - - - - - -//All of the armor below is mostly unused - - -/obj/item/clothing/suit/armor/centcom - name = "\improper Centcom armor" - desc = "A suit that protects against some damage." - icon_state = "centcom" - item_state = "centcom" - w_class = 4//bulky item - body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS - allowed = list(/obj/item/weapon/gun/energy,/obj/item/weapon/melee/baton,/obj/item/weapon/restraints/handcuffs,/obj/item/weapon/tank/internals/emergency_oxygen) - flags = THICKMATERIAL - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT - cold_protection = CHEST | GROIN | LEGS | FEET | ARMS | HANDS - min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT - heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS - max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT - -/obj/item/clothing/suit/armor/heavy - name = "heavy armor" - desc = "A heavily armored suit that protects against moderate damage." - icon_state = "heavy" - item_state = "swat_suit" - w_class = 4//bulky item - gas_transfer_coefficient = 0.90 - flags = THICKMATERIAL - body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS - slowdown = 3 - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT - -/obj/item/clothing/suit/armor/tdome - body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT - flags = THICKMATERIAL - cold_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS - heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS - -/obj/item/clothing/suit/armor/tdome/red - name = "thunderdome suit" - desc = "Reddish armor." - icon_state = "tdred" - item_state = "tdred" - -/obj/item/clothing/suit/armor/tdome/green - name = "thunderdome suit" - desc = "Pukish armor." //classy. - icon_state = "tdgreen" - item_state = "tdgreen" - - -/obj/item/clothing/suit/armor/riot/knight - name = "plate armour" - desc = "A classic suit of plate armour, highly effective at stopping melee attacks." - icon_state = "knight_green" - item_state = "knight_green" - -/obj/item/clothing/suit/armor/riot/knight/yellow - icon_state = "knight_yellow" - item_state = "knight_yellow" - -/obj/item/clothing/suit/armor/riot/knight/blue - icon_state = "knight_blue" - item_state = "knight_blue" - -/obj/item/clothing/suit/armor/riot/knight/red - icon_state = "knight_red" - item_state = "knight_red" - -/obj/item/clothing/suit/armor/riot/knight/templar - name = "crusader armour" - desc = "God wills it!" - icon_state = "knight_templar" +/obj/item/clothing/suit/armor + allowed = list(/obj/item/weapon/gun/energy,/obj/item/weapon/reagent_containers/spray/pepper,/obj/item/weapon/gun/projectile,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/restraints/handcuffs,/obj/item/device/flashlight/seclite,/obj/item/weapon/melee/classic_baton/telescopic) + body_parts_covered = CHEST + cold_protection = CHEST|GROIN + min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT + heat_protection = CHEST|GROIN + max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT + strip_delay = 60 + put_on_delay = 40 + burn_state = FIRE_PROOF + +/obj/item/clothing/suit/armor/vest + name = "armor" + desc = "A slim armored vest that protects against most types of damage." + icon_state = "armor" + item_state = "armor" + blood_overlay_type = "armor" + armor = list(melee = 25, bullet = 15, laser = 25, energy = 10, bomb = 25, bio = 0, rad = 0) + +/obj/item/clothing/suit/armor/hos + name = "armored greatcoat" + desc = "A greatcoat enchanced with a special alloy for some protection and style for those with a commanding presence." + icon_state = "hos" + item_state = "greatcoat" + body_parts_covered = CHEST|GROIN|ARMS|LEGS + armor = list(melee = 30, bullet = 30, laser = 30, energy = 10, bomb = 25, bio = 0, rad = 0) + flags_inv = HIDEJUMPSUIT + cold_protection = CHEST|GROIN|LEGS|ARMS + heat_protection = CHEST|GROIN|LEGS|ARMS + strip_delay = 80 + +/obj/item/clothing/suit/armor/hos/trenchcoat + name = "armored trenchoat" + desc = "A trenchcoat enchanced with a special lightweight kevlar. The epitome of tactical plainclothes." + icon_state = "hostrench" + item_state = "hostrench" + flags_inv = 0 + strip_delay = 80 + +/obj/item/clothing/suit/armor/vest/warden + name = "warden's jacket" + desc = "A red jacket with silver rank pips and body armor strapped on top." + icon_state = "warden_jacket" + item_state = "armor" + body_parts_covered = CHEST|GROIN|ARMS + cold_protection = CHEST|GROIN|ARMS|HANDS + heat_protection = CHEST|GROIN|ARMS|HANDS + strip_delay = 70 + burn_state = FLAMMABLE + +/obj/item/clothing/suit/armor/vest/warden/alt + name = "warden's armored jacket" + desc = "A navy-blue armored jacket with blue shoulder designations and '/Warden/' stitched into one of the chest pockets." + icon_state = "warden_alt" + +/obj/item/clothing/suit/armor/vest/leather + name = "security overcoat" + desc = "Lightly armored leather overcoat meant as casual wear for high-ranking officers. Bears the crest of Nanotrasen Security." + icon_state = "leathercoat-sec" + item_state = "hostrench" + body_parts_covered = CHEST|GROIN|ARMS|LEGS + cold_protection = CHEST|GROIN|LEGS|ARMS + heat_protection = CHEST|GROIN|LEGS|ARMS + +/obj/item/clothing/suit/armor/vest/capcarapace + name = "captain's carapace" + desc = "An armored vest reinforced with ceramic plates and pauldrons to provide additional protection whilst still offering maximum mobility and flexibility. Issued only to the station's finest, although it does chafe your nipples." + icon_state = "capcarapace" + item_state = "armor" + body_parts_covered = CHEST|GROIN + armor = list(melee = 50, bullet = 40, laser = 50, energy = 10, bomb = 25, bio = 0, rad = 0) + +/obj/item/clothing/suit/armor/vest/capcarapace/alt + name = "captain's parade jacket" + desc = "For when an armoured vest isn't fashionable enough." + icon_state = "capformal" + item_state = "capspacesuit" + +/obj/item/clothing/suit/armor/riot + name = "riot suit" + desc = "A suit of armor with heavy padding to protect against melee attacks. Looks like it might impair movement." + icon_state = "riot" + item_state = "swat_suit" + body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS + cold_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS + heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS + armor = list(melee = 50, bullet = 10, laser = 10, energy = 10, bomb = 0, bio = 0, rad = 0) + flags_inv = HIDEJUMPSUIT + strip_delay = 80 + put_on_delay = 60 + +/obj/item/clothing/suit/armor/bulletproof + name = "bulletproof armor" + desc = "A bulletproof vest that excels in protecting the wearer against traditional projectile weaponry and explosives to a minor extent." + icon_state = "bulletproof" + item_state = "armor" + blood_overlay_type = "armor" + armor = list(melee = 15, bullet = 80, laser = 10, energy = 10, bomb = 40, bio = 0, rad = 0) + strip_delay = 70 + put_on_delay = 50 + +/obj/item/clothing/suit/armor/laserproof + name = "reflector vest" + desc = "A vest that excels in protecting the wearer against energy projectiles, as well as occasionally reflecting them." + icon_state = "armor_reflec" + item_state = "armor_reflec" + blood_overlay_type = "armor" + armor = list(melee = 10, bullet = 10, laser = 60, energy = 50, bomb = 0, bio = 0, rad = 0) + var/hit_reflect_chance = 40 + +/obj/item/clothing/suit/armor/laserproof/IsReflect(def_zone) + if(!(def_zone in list("chest", "groin"))) //If not shot where ablative is covering you, you don't get the reflection bonus! + return 0 + if (prob(hit_reflect_chance)) + return 1 + +/obj/item/clothing/suit/armor/vest/det_suit + name = "armor" + desc = "An armored vest with a detective's badge on it." + icon_state = "detective-armor" + allowed = list(/obj/item/weapon/tank/internals/emergency_oxygen,/obj/item/weapon/reagent_containers/spray/pepper,/obj/item/device/flashlight,/obj/item/weapon/gun/energy,/obj/item/weapon/gun/projectile,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/restraints/handcuffs,/obj/item/weapon/storage/fancy/cigarettes,/obj/item/weapon/lighter,/obj/item/device/detective_scanner,/obj/item/device/taperecorder) + burn_state = FLAMMABLE + + +//Reactive armor +//When the wearer gets hit, this armor will teleport the user a short distance away (to safety or to more danger, no one knows. That's the fun of it!) +/obj/item/clothing/suit/armor/reactive + name = "reactive teleport armor" + desc = "Someone seperated our Research Director from his own head!" + var/active = 0 + icon_state = "reactiveoff" + item_state = "reactiveoff" + blood_overlay_type = "armor" + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0) + action_button_name = "Toggle Armor" + unacidable = 1 + hit_reaction_chance = 50 + + +/obj/item/clothing/suit/armor/reactive/attack_self(mob/user) + src.active = !( src.active ) + if (src.active) + user << "[src] is now active." + src.icon_state = "reactive" + src.item_state = "reactive" + else + user << "[src] is now inactive." + src.icon_state = "reactiveoff" + src.item_state = "reactiveoff" + src.add_fingerprint(user) + return + +/obj/item/clothing/suit/armor/reactive/emp_act(severity) + active = 0 + src.icon_state = "reactiveoff" + src.item_state = "reactiveoff" + ..() + +/obj/item/clothing/suit/armor/reactive/hit_reaction(mob/living/carbon/human/owner, attack_text, final_block_chance) + if(!active) + return 0 + if(prob(hit_reaction_chance)) + var/mob/living/carbon/human/H = owner + owner.visible_message("The reactive teleport system flings [H] clear of [attack_text]!") + var/list/turfs = new/list() + for(var/turf/T in orange(6, H)) + if(T.density) + continue + if(T.x>world.maxx-6 || T.x<6) + continue + if(T.y>world.maxy-6 || T.y<6) + continue + turfs += T + if(!turfs.len) + turfs += pick(/turf in orange(6, H)) + var/turf/picked = pick(turfs) + if(!isturf(picked)) + return + if(H.buckled) + H.buckled.unbuckle_mob() + H.forceMove(picked) + return 1 + return 0 + +/obj/item/clothing/suit/armor/reactive/fire + name = "reactive incendiary armor" + + +/obj/item/clothing/suit/armor/reactive/fire/hit_reaction(mob/living/carbon/human/owner, attack_text) + if(!active) + return 0 + if(prob(hit_reaction_chance)) + owner.visible_message("The [src] blocks the [attack_text], sending out jets of flame!") + for(var/mob/living/carbon/C in range(6, owner)) + if(C != owner) + C.fire_stacks += 8 + C.IgniteMob() + owner.fire_stacks = -20 + return 1 + return 0 + + + + + + +/obj/item/clothing/suit/armor/reactive/stealth + name = "reactive stealth armor" + +/obj/item/clothing/suit/armor/reactive/stealth/hit_reaction(mob/living/carbon/human/owner, attack_text) + if(!active) + return 0 + if(prob(hit_reaction_chance)) + var/mob/living/simple_animal/hostile/illusion/escape/E = new(owner.loc) + E.Copy_Parent(owner, 50) + E.GiveTarget(owner) //so it starts running right away + E.Goto(owner, E.move_to_delay, E.minimum_distance) + owner.alpha = 0 + owner.visible_message("[owner] is hit by [attack_text] in the chest!") //We pretend to be hit, since blocking it would stop the message otherwise + spawn(40) + owner.alpha = initial(owner.alpha) + return 1 + + + + + +//All of the armor below is mostly unused + + +/obj/item/clothing/suit/armor/centcom + name = "\improper Centcom armor" + desc = "A suit that protects against some damage." + icon_state = "centcom" + item_state = "centcom" + w_class = 4//bulky item + body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS + allowed = list(/obj/item/weapon/gun/energy,/obj/item/weapon/melee/baton,/obj/item/weapon/restraints/handcuffs,/obj/item/weapon/tank/internals/emergency_oxygen) + flags = THICKMATERIAL + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT + cold_protection = CHEST | GROIN | LEGS | FEET | ARMS | HANDS + min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT + heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS + max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT + +/obj/item/clothing/suit/armor/heavy + name = "heavy armor" + desc = "A heavily armored suit that protects against moderate damage." + icon_state = "heavy" + item_state = "swat_suit" + w_class = 4//bulky item + gas_transfer_coefficient = 0.90 + flags = THICKMATERIAL + body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS + slowdown = 3 + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT + +/obj/item/clothing/suit/armor/tdome + body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT + flags = THICKMATERIAL + cold_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS + heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS + +/obj/item/clothing/suit/armor/tdome/red + name = "thunderdome suit" + desc = "Reddish armor." + icon_state = "tdred" + item_state = "tdred" + +/obj/item/clothing/suit/armor/tdome/green + name = "thunderdome suit" + desc = "Pukish armor." //classy. + icon_state = "tdgreen" + item_state = "tdgreen" + + +/obj/item/clothing/suit/armor/riot/knight + name = "plate armour" + desc = "A classic suit of plate armour, highly effective at stopping melee attacks." + icon_state = "knight_green" + item_state = "knight_green" + +/obj/item/clothing/suit/armor/riot/knight/yellow + icon_state = "knight_yellow" + item_state = "knight_yellow" + +/obj/item/clothing/suit/armor/riot/knight/blue + icon_state = "knight_blue" + item_state = "knight_blue" + +/obj/item/clothing/suit/armor/riot/knight/red + icon_state = "knight_red" + item_state = "knight_red" + +/obj/item/clothing/suit/armor/riot/knight/templar + name = "crusader armour" + desc = "God wills it!" + icon_state = "knight_templar" item_state = "knight_templar" \ No newline at end of file diff --git a/code/modules/mining/mine_turfs.dm b/code/modules/mining/mine_turfs.dm index fdb61522b30..284e1b66050 100644 --- a/code/modules/mining/mine_turfs.dm +++ b/code/modules/mining/mine_turfs.dm @@ -1,707 +1,707 @@ -/**********************Mineral deposits**************************/ - -var/global/list/rockTurfEdgeCache -#define NORTH_EDGING "north" -#define SOUTH_EDGING "south" -#define EAST_EDGING "east" -#define WEST_EDGING "west" - -/turf/simulated/mineral //wall piece - name = "rock" - icon = 'icons/turf/mining.dmi' - icon_state = "rock_nochance" - baseturf = /turf/simulated/floor/plating/asteroid/airless - oxygen = 0 - nitrogen = 0 - opacity = 1 - density = 1 - blocks_air = 1 - temperature = TCMB - var/environment_type = "asteroid" - var/turf/simulated/floor/plating/asteroid/turf_type = /turf/simulated/floor/plating/asteroid //For basalt vs normal asteroid - var/mineralType = null - var/mineralAmt = 3 - var/spread = 0 //will the seam spread? - var/spreadChance = 0 //the percentual chance of an ore spreading to the neighbouring tiles - var/last_act = 0 - var/scan_state = null //Holder for the image we display when we're pinged by a mining scanner - var/hidden = 1 - -/turf/simulated/mineral/volcanic - environment_type = "basalt" - turf_type = /turf/simulated/floor/plating/asteroid/basalt - baseturf = /turf/simulated/floor/plating/asteroid/basalt - -/turf/simulated/mineral/ex_act(severity, target) - ..() - switch(severity) - if(3) - if (prob(75)) - src.gets_drilled(null, 1) - if(2) - if (prob(90)) - src.gets_drilled(null, 1) - if(1) - src.gets_drilled(null, 1) - return - -/turf/simulated/mineral/New() - if(!rockTurfEdgeCache || !rockTurfEdgeCache.len) - rockTurfEdgeCache = list() - rockTurfEdgeCache.len = 4 - rockTurfEdgeCache[NORTH_EDGING] = image('icons/turf/mining.dmi', "rock_side_n", layer = 6) - rockTurfEdgeCache[SOUTH_EDGING] = image('icons/turf/mining.dmi', "rock_side_s") - rockTurfEdgeCache[EAST_EDGING] = image('icons/turf/mining.dmi', "rock_side_e", layer = 6) - rockTurfEdgeCache[WEST_EDGING] = image('icons/turf/mining.dmi', "rock_side_w", layer = 6) - - spawn(1) - var/turf/T - if((istype(get_step(src, NORTH), /turf/simulated/floor)) || (istype(get_step(src, NORTH), /turf/space))) - T = get_step(src, NORTH) - if (T) - T.overlays += rockTurfEdgeCache[SOUTH_EDGING] - if((istype(get_step(src, SOUTH), /turf/simulated/floor)) || (istype(get_step(src, SOUTH), /turf/space))) - T = get_step(src, SOUTH) - if (T) - T.overlays += rockTurfEdgeCache[NORTH_EDGING] - if((istype(get_step(src, EAST), /turf/simulated/floor)) || (istype(get_step(src, EAST), /turf/space))) - T = get_step(src, EAST) - if (T) - T.overlays += rockTurfEdgeCache[WEST_EDGING] - if((istype(get_step(src, WEST), /turf/simulated/floor)) || (istype(get_step(src, WEST), /turf/space))) - T = get_step(src, WEST) - if (T) - T.overlays += rockTurfEdgeCache[EAST_EDGING] - - if (mineralType && mineralAmt && spread && spreadChance) - for(var/dir in cardinal) - if(prob(spreadChance)) - var/turf/T = get_step(src, dir) - if(istype(T, /turf/simulated/mineral/random)) - Spread(T) - - HideRock() - return - -/turf/simulated/mineral/proc/HideRock() - if(hidden) - name = "rock" - icon_state = "rock" - return - -/turf/simulated/mineral/proc/Spread(turf/T) - new src.type(T) - -/turf/simulated/mineral/random - name = "rock" - icon_state = "rock" - var/mineralSpawnChanceList = list( - "Uranium" = 5, "Diamond" = 1, "Gold" = 10, - "Silver" = 12, "Plasma" = 20, "Iron" = 40, - "Gibtonite" = 4, "Cave" = 2, "BScrystal" = 1, - /*, "Adamantine" =5*/) - //Currently, Adamantine won't spawn as it has no uses. -Durandan - var/mineralChance = 13 - -/turf/simulated/mineral/random/New() - ..() - if (prob(mineralChance)) - var/mName = pickweight(mineralSpawnChanceList) //temp mineral name - - if (mName) - var/turf/simulated/mineral/M - switch(mName) - if("Uranium") - M = new/turf/simulated/mineral/uranium(src) - if("Iron") - M = new/turf/simulated/mineral/iron(src) - if("Diamond") - M = new/turf/simulated/mineral/diamond(src) - if("Gold") - M = new/turf/simulated/mineral/gold(src) - if("Silver") - M = new/turf/simulated/mineral/silver(src) - if("Plasma") - M = new/turf/simulated/mineral/plasma(src) - if("Cave") - new/turf/simulated/floor/plating/asteroid/airless/cave(src) - if("Gibtonite") - M = new/turf/simulated/mineral/gibtonite(src) - if("Bananium") - M = new/turf/simulated/mineral/clown(src) - if("BScrystal") - M = new/turf/simulated/mineral/bscrystal(src) - /*if("Adamantine") - M = new/turf/simulated/mineral/adamantine(src)*/ - if(M) - M.mineralAmt = rand(1, 5) - M.environment_type = src.environment_type - M.turf_type = src.turf_type - M.baseturf = src.baseturf - src = M - M.levelupdate() - return - -/turf/simulated/mineral/random/high_chance - icon_state = "rock_highchance" - mineralChance = 25 - mineralSpawnChanceList = list( - "Uranium" = 35, "Diamond" = 30, - "Gold" = 45, "Silver" = 50, "Plasma" = 50, - "BScrystal" = 20) - -/turf/simulated/mineral/random/high_chance/New() - icon_state = "rock" - ..() - -/turf/simulated/mineral/random/low_chance - icon_state = "rock_lowchance" - mineralChance = 6 - mineralSpawnChanceList = list( - "Uranium" = 2, "Diamond" = 1, "Gold" = 4, - "Silver" = 6, "Plasma" = 15, "Iron" = 40, - "Gibtonite" = 2, "BScrystal" = 1) - -/turf/simulated/mineral/random/low_chance/New() - icon_state = "rock" - ..() - -/turf/simulated/mineral/iron - name = "iron deposit" - icon_state = "rock_Iron" - mineralType = /obj/item/weapon/ore/iron - spreadChance = 20 - spread = 1 - hidden = 0 - -/turf/simulated/mineral/uranium - name = "uranium deposit" - mineralType = /obj/item/weapon/ore/uranium - spreadChance = 5 - spread = 1 - hidden = 1 - scan_state = "rock_Uranium" - -/turf/simulated/mineral/diamond - name = "diamond deposit" - mineralType = /obj/item/weapon/ore/diamond - spreadChance = 0 - spread = 1 - hidden = 1 - scan_state = "rock_Diamond" - -/turf/simulated/mineral/gold - name = "gold deposit" - mineralType = /obj/item/weapon/ore/gold - spreadChance = 5 - spread = 1 - hidden = 1 - scan_state = "rock_Gold" - -/turf/simulated/mineral/silver - name = "silver deposit" - mineralType = /obj/item/weapon/ore/silver - spreadChance = 5 - spread = 1 - hidden = 1 - scan_state = "rock_Silver" - -/turf/simulated/mineral/plasma - name = "plasma deposit" - icon_state = "rock_Plasma" - mineralType = /obj/item/weapon/ore/plasma - spreadChance = 8 - spread = 1 - hidden = 1 - scan_state = "rock_Plasma" - -/turf/simulated/mineral/clown - name = "bananium deposit" - icon_state = "rock_Clown" - mineralType = /obj/item/weapon/ore/bananium - mineralAmt = 3 - spreadChance = 0 - spread = 0 - hidden = 0 - -/turf/simulated/mineral/bscrystal - name = "bluespace crystal deposit" - icon_state = "rock_BScrystal" - mineralType = /obj/item/weapon/ore/bluespace_crystal - mineralAmt = 1 - spreadChance = 0 - spread = 0 - hidden = 1 - scan_state = "rock_BScrystal" - -////////////////////////////////Gibtonite -/turf/simulated/mineral/gibtonite - name = "gibtonite deposit" - icon_state = "rock_Gibtonite" - mineralAmt = 1 - spreadChance = 0 - spread = 0 - hidden = 1 - scan_state = "rock_Gibtonite" - var/det_time = 8 //Countdown till explosion, but also rewards the player for how close you were to detonation when you defuse it - var/stage = 0 //How far into the lifecycle of gibtonite we are, 0 is untouched, 1 is active and attempting to detonate, 2 is benign and ready for extraction - var/activated_ckey = null //These are to track who triggered the gibtonite deposit for logging purposes - var/activated_name = null - -/turf/simulated/mineral/gibtonite/New() - det_time = rand(8,10) //So you don't know exactly when the hot potato will explode - ..() - -/turf/simulated/mineral/gibtonite/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/device/mining_scanner) || istype(I, /obj/item/device/t_scanner/adv_mining_scanner) && stage == 1) - user.visible_message("You use [I] to locate where to cut off the chain reaction and attempt to stop it...") - defuse() - ..() - -/turf/simulated/mineral/gibtonite/proc/explosive_reaction(mob/user = null, triggered_by_explosion = 0) - if(stage == 0) - icon_state = "rock_Gibtonite_active" - name = "gibtonite deposit" - desc = "An active gibtonite reserve. Run!" - stage = 1 - visible_message("There was gibtonite inside! It's going to explode!") - var/turf/bombturf = get_turf(src) - var/area/A = get_area(bombturf) - - var/notify_admins = 0 - if(z != 5) - notify_admins = 1 - if(!triggered_by_explosion) - message_admins("[key_name_admin(user)]? (FLW) has triggered a gibtonite deposit reaction at [A.name] (JMP).") - else - message_admins("An explosion has triggered a gibtonite deposit reaction at [A.name] (JMP).") - - if(!triggered_by_explosion) - log_game("[key_name(user)] has triggered a gibtonite deposit reaction at [A.name] ([A.x], [A.y], [A.z]).") - else - log_game("An explosion has triggered a gibtonite deposit reaction at [A.name]([bombturf.x],[bombturf.y],[bombturf.z])") - - countdown(notify_admins) - -/turf/simulated/mineral/gibtonite/proc/countdown(notify_admins = 0) - spawn(0) - while(stage == 1 && det_time > 0 && mineralAmt >= 1) - det_time-- - sleep(5) - if(stage == 1 && det_time <= 0 && mineralAmt >= 1) - var/turf/bombturf = get_turf(src) - mineralAmt = 0 - explosion(bombturf,1,3,5, adminlog = notify_admins) - if(stage == 0 || stage == 2) - return - -/turf/simulated/mineral/gibtonite/proc/defuse() - if(stage == 1) - icon_state = "rock_Gibtonite_inactive" - desc = "An inactive gibtonite reserve. The ore can be extracted." - stage = 2 - if(det_time < 0) - det_time = 0 - visible_message("The chain reaction was stopped! The gibtonite had [src.det_time] reactions left till the explosion!") - -/turf/simulated/mineral/gibtonite/gets_drilled(mob/user, triggered_by_explosion = 0) - if(stage == 0 && mineralAmt >= 1) //Gibtonite deposit is activated - playsound(src,'sound/effects/hit_on_shattered_glass.ogg',50,1) - explosive_reaction(user, triggered_by_explosion) - return - if(stage == 1 && mineralAmt >= 1) //Gibtonite deposit goes kaboom - var/turf/bombturf = get_turf(src) - mineralAmt = 0 - explosion(bombturf,1,2,5, adminlog = 0) - if(stage == 2) //Gibtonite deposit is now benign and extractable. Depending on how close you were to it blowing up before defusing, you get better quality ore. - var/obj/item/weapon/twohanded/required/gibtonite/G = new /obj/item/weapon/twohanded/required/gibtonite/(src) - if(det_time <= 0) - G.quality = 3 - G.icon_state = "Gibtonite ore 3" - if(det_time >= 1 && det_time <= 2) - G.quality = 2 - G.icon_state = "Gibtonite ore 2" - var/turf/simulated/floor/plating/asteroid/G = ChangeTurf(turf_type) - G.fullUpdateMineralOverlays() - -////////////////////////////////End Gibtonite - -/turf/simulated/floor/plating/asteroid/airless/cave - var/length = 100 - var/mob_spawn_list = list("Goldgrub" = 1, "Goliath" = 5, "Basilisk" = 4, "Hivelord" = 3) - var/sanity = 1 - turf_type = /turf/simulated/floor/plating/asteroid/airless - -/turf/simulated/floor/plating/asteroid/airless/cave/New(loc, var/length, var/go_backwards = 1, var/exclude_dir = -1) - - // If length (arg2) isn't defined, get a random length; otherwise assign our length to the length arg. - if(!length) - src.length = rand(25, 50) - else - src.length = length - - // Get our directiosn - var/forward_cave_dir = pick(alldirs - exclude_dir) - // Get the opposite direction of our facing direction - var/backward_cave_dir = angle2dir(dir2angle(forward_cave_dir) + 180) - - // Make our tunnels - make_tunnel(forward_cave_dir) - if(go_backwards) - make_tunnel(backward_cave_dir) - // Kill ourselves by replacing ourselves with a normal floor. - SpawnFloor(src) - ..() - -/turf/simulated/floor/plating/asteroid/airless/cave/proc/make_tunnel(dir) - - var/turf/simulated/mineral/tunnel = src - var/next_angle = pick(45, -45) - - for(var/i = 0; i < length; i++) - if(!sanity) - break - - var/list/L = list(45) - if(IsOdd(dir2angle(dir))) // We're going at an angle and we want thick angled tunnels. - L += -45 - - // Expand the edges of our tunnel - for(var/edge_angle in L) - var/turf/simulated/mineral/edge = get_step(tunnel, angle2dir(dir2angle(dir) + edge_angle)) - if(istype(edge)) - SpawnFloor(edge) - - // Move our tunnel forward - tunnel = get_step(tunnel, dir) - - if(istype(tunnel)) - // Small chance to have forks in our tunnel; otherwise dig our tunnel. - if(i > 3 && prob(20)) - new src.type(tunnel, rand(10, 15), 0, dir) - else - SpawnFloor(tunnel) - else //if(!istype(tunnel, src.parent)) // We hit space/normal/wall, stop our tunnel. - break - - // Chance to change our direction left or right. - if(i > 2 && prob(33)) - // We can't go a full loop though - next_angle = -next_angle - dir = angle2dir(dir2angle(dir) + next_angle) - - -/turf/simulated/floor/plating/asteroid/airless/cave/proc/SpawnFloor(turf/T) - for(var/turf/S in range(2,T)) - if(istype(S, /turf/space) || istype(S.loc, /area/mine/explored)) - sanity = 0 - break - if(!sanity) - return - - SpawnMonster(T) - var/turf/simulated/floor/t = new turf_type(T) - spawn(2) - t.fullUpdateMineralOverlays() - -/turf/simulated/floor/plating/asteroid/airless/cave/proc/SpawnMonster(turf/T) - if(prob(30)) - if(istype(loc, /area/mine/explored)) - return - for(var/atom/A in ultra_range(15,T))//Lowers chance of mob clumps - if(istype(A, /mob/living/simple_animal/hostile/asteroid)) - return - var/randumb = pickweight(mob_spawn_list) - switch(randumb) - if("Goliath") - new /mob/living/simple_animal/hostile/asteroid/goliath(T) - if("Goldgrub") - new /mob/living/simple_animal/hostile/asteroid/goldgrub(T) - if("Basilisk") - new /mob/living/simple_animal/hostile/asteroid/basilisk(T) - if("Hivelord") - new /mob/living/simple_animal/hostile/asteroid/hivelord(T) - return - -/turf/simulated/mineral/attackby(obj/item/weapon/pickaxe/P, mob/user, params) - - if (!user.IsAdvancedToolUser()) - usr << "You don't have the dexterity to do this!" - return - - if (istype(P, /obj/item/weapon/pickaxe)) - var/turf/T = user.loc - if (!( istype(T, /turf) )) - return - - if(last_act+P.digspeed > world.time)//prevents message spam - return - last_act = world.time - user << "You start picking..." - P.playDigSound() - - if(do_after(user,P.digspeed, target = src)) - if(istype(src, /turf/simulated/mineral)) - user << "You finish cutting into the rock." - gets_drilled(user) - feedback_add_details("pick_used_mining","[P.type]") - else - return attack_hand(user) - return - -/turf/simulated/mineral/proc/gets_drilled() - if (mineralType && (src.mineralAmt > 0) && (src.mineralAmt < 11)) - var/i - for (i=0;i= 2) - gets_drilled() - ..() - -/turf/simulated/mineral/attack_alien(mob/living/carbon/alien/M) - M << "You start digging into the rock..." - playsound(src, 'sound/effects/break_stone.ogg', 50, 1) - if(do_after(M,40, target = src)) - M << "You tunnel into the rock." - gets_drilled(M) - -/turf/simulated/mineral/Bumped(AM as mob|obj) - ..() - if(istype(AM,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = AM - if((istype(H.l_hand,/obj/item/weapon/pickaxe)) && (!H.hand)) - src.attackby(H.l_hand,H) - else if((istype(H.r_hand,/obj/item/weapon/pickaxe)) && H.hand) - src.attackby(H.r_hand,H) - return - else if(istype(AM,/mob/living/silicon/robot)) - var/mob/living/silicon/robot/R = AM - if(istype(R.module_active,/obj/item/weapon/pickaxe)) - src.attackby(R.module_active,R) - return -/* else if(istype(AM,/obj/mecha)) - var/obj/mecha/M = AM - if(istype(M.selected,/obj/item/mecha_parts/mecha_equipment/drill)) - src.attackby(M.selected,M) - return*/ -//Aparantly mechs are just TOO COOL to call Bump(), so fuck em (for now) - else - return - -/**********************Asteroid**************************/ - -/turf/simulated/floor/plating/asteroid //floor piece - name = "Asteroid" - baseturf = /turf/simulated/floor/plating/asteroid - icon = 'icons/turf/floors.dmi' - icon_state = "asteroid" - icon_plating = "asteroid" - var/environment_type = "asteroid" - var/turf_type = /turf/simulated/floor/plating/asteroid //Because caves do whacky shit to revert to normal - var/dug = 0 //0 = has not yet been dug, 1 = has already been dug - -/turf/simulated/floor/plating/asteroid/airless - oxygen = 0.01 - nitrogen = 0.01 - turf_type = /turf/simulated/floor/plating/asteroid/airless - temperature = TCMB - -/turf/simulated/floor/plating/asteroid/basalt - name = "volcanic floor" - baseturf = /turf/simulated/floor/plating/asteroid/basalt - icon = 'icons/turf/floors.dmi' - icon_state = "basalt" - icon_plating = "basalt" - environment_type = "basalt" - -/turf/simulated/floor/plating/asteroid/basalt/lava //lava underneath - baseturf = /turf/simulated/floor/plating/lava/smooth - -/turf/simulated/floor/plating/asteroid/basalt/airless - oxygen = 0.01 - nitrogen = 0.01 - temperature = TCMB - -/turf/simulated/floor/plating/asteroid/snow - name = "snow" - desc = "Looks cold." - icon = 'icons/turf/snow.dmi' - baseturf = /turf/simulated/floor/plating/asteroid/snow - icon_state = "snow" - icon_plating = "snow" - temperature = 180 - slowdown = 2 - environment_type = "snow" - -/turf/simulated/floor/plating/asteroid/snow/airless - oxygen = 0.01 - nitrogen = 0.01 - temperature = TCMB - -/turf/simulated/floor/plating/asteroid/New() - var/proper_name = name - ..() - name = proper_name - if(prob(20)) - icon_state = "[environment_type][rand(0,12)]" - -/turf/simulated/floor/plating/asteroid/burn_tile() - return - -/turf/simulated/floor/plating/asteroid/ex_act(severity, target) - contents_explosion(severity, target) - switch(severity) - if(3) - return - if(2) - if (prob(20)) - src.gets_dug() - if(1) - src.gets_dug() - return - -/turf/simulated/floor/plating/asteroid/attackby(obj/item/weapon/W, mob/user, params) - //note that this proc does not call ..() - if(!W || !user) - return 0 - var/digging_speed = 0 - if (istype(W, /obj/item/weapon/shovel)) - var/obj/item/weapon/shovel/S = W - digging_speed = S.digspeed - else if (istype(W, /obj/item/weapon/pickaxe)) - var/obj/item/weapon/pickaxe/P = W - digging_speed = P.digspeed - if (digging_speed) - var/turf/T = user.loc - if (!( istype(T, /turf) )) - return - - if (dug) - user << "This area has already been dug!" - return - - user << "You start digging..." - playsound(src, 'sound/effects/shovel_dig.ogg', 50, 1) //FUCK YO RUSTLE I GOT'S THE DIGS SOUND HERE - - if(do_after(user, digging_speed, target = src)) - if(istype(src, /turf/simulated/floor/plating/asteroid)) - user << "You dig a hole." - gets_dug() - feedback_add_details("pick_used_mining","[W.type]") - - if(istype(W,/obj/item/weapon/storage/bag/ore)) - var/obj/item/weapon/storage/bag/ore/S = W - if(S.collection_mode == 1) - for(var/obj/item/weapon/ore/O in src.contents) - O.attackby(W,user) - return - - if(istype(W, /obj/item/stack/tile)) - var/obj/item/stack/tile/Z = W - if(!Z.use(1)) - return - var/turf/simulated/floor/T = ChangeTurf(Z.turf_type) - if(istype(Z,/obj/item/stack/tile/light)) //TODO: get rid of this ugly check somehow - var/obj/item/stack/tile/light/L = Z - var/turf/simulated/floor/light/F = T - F.state = L.state - playsound(src, 'sound/weapons/Genhit.ogg', 50, 1) - -/turf/simulated/floor/plating/asteroid/proc/gets_dug() - if(dug) - return - new/obj/item/weapon/ore/glass(src) - new/obj/item/weapon/ore/glass(src) - new/obj/item/weapon/ore/glass(src) - new/obj/item/weapon/ore/glass(src) - new/obj/item/weapon/ore/glass(src) - dug = 1 - icon_plating = "[environment_type]_dug" - icon_state = "[environment_type]_dug" - slowdown = 0 - return - -/turf/simulated/floor/plating/asteroid/singularity_act() - return - -/turf/simulated/floor/plating/asteroid/singularity_pull(S, current_size) - return - -/turf/proc/updateMineralOverlays() - src.overlays.Cut() - - if(istype(get_step(src, NORTH), /turf/simulated/mineral)) - src.overlays += rockTurfEdgeCache[NORTH_EDGING] - if(istype(get_step(src, SOUTH), /turf/simulated/mineral)) - src.overlays += rockTurfEdgeCache[SOUTH_EDGING] - if(istype(get_step(src, EAST), /turf/simulated/mineral)) - src.overlays += rockTurfEdgeCache[EAST_EDGING] - if(istype(get_step(src, WEST), /turf/simulated/mineral)) - src.overlays += rockTurfEdgeCache[WEST_EDGING] - -/turf/simulated/mineral/updateMineralOverlays() - return - -/turf/simulated/wall/updateMineralOverlays() - return - -/turf/proc/fullUpdateMineralOverlays() - for (var/turf/t in range(1,src)) - t.updateMineralOverlays() - - - - - - - - -//////////////CHASM////////////////// - -/turf/simulated/chasm - name = "chasm" - desc = "Watch your step." - baseturf = /turf/simulated/chasm - smooth = SMOOTH_TRUE - icon = 'icons/turf/floors/Chasms.dmi' - icon_state = "smooth" - var/drop_x = 1 - var/drop_y = 1 - var/drop_z = 1 - - -/turf/simulated/chasm/Entered(atom/movable/AM) - if(istype(AM, /obj/singularity) || istype(AM, /obj/item/projectile)) - return - drop(AM) - - -/turf/simulated/chasm/proc/drop(atom/movable/AM) - visible_message("[AM] falls into [src]!") - AM.Move(locate(drop_x, drop_y, drop_z)) - AM.visible_message("[AM] falls from above!") - if(istype(AM, /mob/living)) - var/mob/living/L = AM - L.adjustBruteLoss(30) - - - -/turf/simulated/chasm/straight_down/New() - ..() - drop_x = x - drop_y = y - if(z+1 <= world.maxz) - drop_z = z+1 - - -#undef NORTH_EDGING -#undef SOUTH_EDGING -#undef EAST_EDGING -#undef WEST_EDGING +/**********************Mineral deposits**************************/ + +var/global/list/rockTurfEdgeCache +#define NORTH_EDGING "north" +#define SOUTH_EDGING "south" +#define EAST_EDGING "east" +#define WEST_EDGING "west" + +/turf/simulated/mineral //wall piece + name = "rock" + icon = 'icons/turf/mining.dmi' + icon_state = "rock_nochance" + baseturf = /turf/simulated/floor/plating/asteroid/airless + oxygen = 0 + nitrogen = 0 + opacity = 1 + density = 1 + blocks_air = 1 + temperature = TCMB + var/environment_type = "asteroid" + var/turf/simulated/floor/plating/asteroid/turf_type = /turf/simulated/floor/plating/asteroid //For basalt vs normal asteroid + var/mineralType = null + var/mineralAmt = 3 + var/spread = 0 //will the seam spread? + var/spreadChance = 0 //the percentual chance of an ore spreading to the neighbouring tiles + var/last_act = 0 + var/scan_state = null //Holder for the image we display when we're pinged by a mining scanner + var/hidden = 1 + +/turf/simulated/mineral/volcanic + environment_type = "basalt" + turf_type = /turf/simulated/floor/plating/asteroid/basalt + baseturf = /turf/simulated/floor/plating/asteroid/basalt + +/turf/simulated/mineral/ex_act(severity, target) + ..() + switch(severity) + if(3) + if (prob(75)) + src.gets_drilled(null, 1) + if(2) + if (prob(90)) + src.gets_drilled(null, 1) + if(1) + src.gets_drilled(null, 1) + return + +/turf/simulated/mineral/New() + if(!rockTurfEdgeCache || !rockTurfEdgeCache.len) + rockTurfEdgeCache = list() + rockTurfEdgeCache.len = 4 + rockTurfEdgeCache[NORTH_EDGING] = image('icons/turf/mining.dmi', "rock_side_n", layer = 6) + rockTurfEdgeCache[SOUTH_EDGING] = image('icons/turf/mining.dmi', "rock_side_s") + rockTurfEdgeCache[EAST_EDGING] = image('icons/turf/mining.dmi', "rock_side_e", layer = 6) + rockTurfEdgeCache[WEST_EDGING] = image('icons/turf/mining.dmi', "rock_side_w", layer = 6) + + spawn(1) + var/turf/T + if((istype(get_step(src, NORTH), /turf/simulated/floor)) || (istype(get_step(src, NORTH), /turf/space))) + T = get_step(src, NORTH) + if (T) + T.overlays += rockTurfEdgeCache[SOUTH_EDGING] + if((istype(get_step(src, SOUTH), /turf/simulated/floor)) || (istype(get_step(src, SOUTH), /turf/space))) + T = get_step(src, SOUTH) + if (T) + T.overlays += rockTurfEdgeCache[NORTH_EDGING] + if((istype(get_step(src, EAST), /turf/simulated/floor)) || (istype(get_step(src, EAST), /turf/space))) + T = get_step(src, EAST) + if (T) + T.overlays += rockTurfEdgeCache[WEST_EDGING] + if((istype(get_step(src, WEST), /turf/simulated/floor)) || (istype(get_step(src, WEST), /turf/space))) + T = get_step(src, WEST) + if (T) + T.overlays += rockTurfEdgeCache[EAST_EDGING] + + if (mineralType && mineralAmt && spread && spreadChance) + for(var/dir in cardinal) + if(prob(spreadChance)) + var/turf/T = get_step(src, dir) + if(istype(T, /turf/simulated/mineral/random)) + Spread(T) + + HideRock() + return + +/turf/simulated/mineral/proc/HideRock() + if(hidden) + name = "rock" + icon_state = "rock" + return + +/turf/simulated/mineral/proc/Spread(turf/T) + new src.type(T) + +/turf/simulated/mineral/random + name = "rock" + icon_state = "rock" + var/mineralSpawnChanceList = list( + "Uranium" = 5, "Diamond" = 1, "Gold" = 10, + "Silver" = 12, "Plasma" = 20, "Iron" = 40, + "Gibtonite" = 4, "Cave" = 2, "BScrystal" = 1, + /*, "Adamantine" =5*/) + //Currently, Adamantine won't spawn as it has no uses. -Durandan + var/mineralChance = 13 + +/turf/simulated/mineral/random/New() + ..() + if (prob(mineralChance)) + var/mName = pickweight(mineralSpawnChanceList) //temp mineral name + + if (mName) + var/turf/simulated/mineral/M + switch(mName) + if("Uranium") + M = new/turf/simulated/mineral/uranium(src) + if("Iron") + M = new/turf/simulated/mineral/iron(src) + if("Diamond") + M = new/turf/simulated/mineral/diamond(src) + if("Gold") + M = new/turf/simulated/mineral/gold(src) + if("Silver") + M = new/turf/simulated/mineral/silver(src) + if("Plasma") + M = new/turf/simulated/mineral/plasma(src) + if("Cave") + new/turf/simulated/floor/plating/asteroid/airless/cave(src) + if("Gibtonite") + M = new/turf/simulated/mineral/gibtonite(src) + if("Bananium") + M = new/turf/simulated/mineral/clown(src) + if("BScrystal") + M = new/turf/simulated/mineral/bscrystal(src) + /*if("Adamantine") + M = new/turf/simulated/mineral/adamantine(src)*/ + if(M) + M.mineralAmt = rand(1, 5) + M.environment_type = src.environment_type + M.turf_type = src.turf_type + M.baseturf = src.baseturf + src = M + M.levelupdate() + return + +/turf/simulated/mineral/random/high_chance + icon_state = "rock_highchance" + mineralChance = 25 + mineralSpawnChanceList = list( + "Uranium" = 35, "Diamond" = 30, + "Gold" = 45, "Silver" = 50, "Plasma" = 50, + "BScrystal" = 20) + +/turf/simulated/mineral/random/high_chance/New() + icon_state = "rock" + ..() + +/turf/simulated/mineral/random/low_chance + icon_state = "rock_lowchance" + mineralChance = 6 + mineralSpawnChanceList = list( + "Uranium" = 2, "Diamond" = 1, "Gold" = 4, + "Silver" = 6, "Plasma" = 15, "Iron" = 40, + "Gibtonite" = 2, "BScrystal" = 1) + +/turf/simulated/mineral/random/low_chance/New() + icon_state = "rock" + ..() + +/turf/simulated/mineral/iron + name = "iron deposit" + icon_state = "rock_Iron" + mineralType = /obj/item/weapon/ore/iron + spreadChance = 20 + spread = 1 + hidden = 0 + +/turf/simulated/mineral/uranium + name = "uranium deposit" + mineralType = /obj/item/weapon/ore/uranium + spreadChance = 5 + spread = 1 + hidden = 1 + scan_state = "rock_Uranium" + +/turf/simulated/mineral/diamond + name = "diamond deposit" + mineralType = /obj/item/weapon/ore/diamond + spreadChance = 0 + spread = 1 + hidden = 1 + scan_state = "rock_Diamond" + +/turf/simulated/mineral/gold + name = "gold deposit" + mineralType = /obj/item/weapon/ore/gold + spreadChance = 5 + spread = 1 + hidden = 1 + scan_state = "rock_Gold" + +/turf/simulated/mineral/silver + name = "silver deposit" + mineralType = /obj/item/weapon/ore/silver + spreadChance = 5 + spread = 1 + hidden = 1 + scan_state = "rock_Silver" + +/turf/simulated/mineral/plasma + name = "plasma deposit" + icon_state = "rock_Plasma" + mineralType = /obj/item/weapon/ore/plasma + spreadChance = 8 + spread = 1 + hidden = 1 + scan_state = "rock_Plasma" + +/turf/simulated/mineral/clown + name = "bananium deposit" + icon_state = "rock_Clown" + mineralType = /obj/item/weapon/ore/bananium + mineralAmt = 3 + spreadChance = 0 + spread = 0 + hidden = 0 + +/turf/simulated/mineral/bscrystal + name = "bluespace crystal deposit" + icon_state = "rock_BScrystal" + mineralType = /obj/item/weapon/ore/bluespace_crystal + mineralAmt = 1 + spreadChance = 0 + spread = 0 + hidden = 1 + scan_state = "rock_BScrystal" + +////////////////////////////////Gibtonite +/turf/simulated/mineral/gibtonite + name = "gibtonite deposit" + icon_state = "rock_Gibtonite" + mineralAmt = 1 + spreadChance = 0 + spread = 0 + hidden = 1 + scan_state = "rock_Gibtonite" + var/det_time = 8 //Countdown till explosion, but also rewards the player for how close you were to detonation when you defuse it + var/stage = 0 //How far into the lifecycle of gibtonite we are, 0 is untouched, 1 is active and attempting to detonate, 2 is benign and ready for extraction + var/activated_ckey = null //These are to track who triggered the gibtonite deposit for logging purposes + var/activated_name = null + +/turf/simulated/mineral/gibtonite/New() + det_time = rand(8,10) //So you don't know exactly when the hot potato will explode + ..() + +/turf/simulated/mineral/gibtonite/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/device/mining_scanner) || istype(I, /obj/item/device/t_scanner/adv_mining_scanner) && stage == 1) + user.visible_message("You use [I] to locate where to cut off the chain reaction and attempt to stop it...") + defuse() + ..() + +/turf/simulated/mineral/gibtonite/proc/explosive_reaction(mob/user = null, triggered_by_explosion = 0) + if(stage == 0) + icon_state = "rock_Gibtonite_active" + name = "gibtonite deposit" + desc = "An active gibtonite reserve. Run!" + stage = 1 + visible_message("There was gibtonite inside! It's going to explode!") + var/turf/bombturf = get_turf(src) + var/area/A = get_area(bombturf) + + var/notify_admins = 0 + if(z != 5) + notify_admins = 1 + if(!triggered_by_explosion) + message_admins("[key_name_admin(user)]? (FLW) has triggered a gibtonite deposit reaction at [A.name] (JMP).") + else + message_admins("An explosion has triggered a gibtonite deposit reaction at [A.name] (JMP).") + + if(!triggered_by_explosion) + log_game("[key_name(user)] has triggered a gibtonite deposit reaction at [A.name] ([A.x], [A.y], [A.z]).") + else + log_game("An explosion has triggered a gibtonite deposit reaction at [A.name]([bombturf.x],[bombturf.y],[bombturf.z])") + + countdown(notify_admins) + +/turf/simulated/mineral/gibtonite/proc/countdown(notify_admins = 0) + spawn(0) + while(stage == 1 && det_time > 0 && mineralAmt >= 1) + det_time-- + sleep(5) + if(stage == 1 && det_time <= 0 && mineralAmt >= 1) + var/turf/bombturf = get_turf(src) + mineralAmt = 0 + explosion(bombturf,1,3,5, adminlog = notify_admins) + if(stage == 0 || stage == 2) + return + +/turf/simulated/mineral/gibtonite/proc/defuse() + if(stage == 1) + icon_state = "rock_Gibtonite_inactive" + desc = "An inactive gibtonite reserve. The ore can be extracted." + stage = 2 + if(det_time < 0) + det_time = 0 + visible_message("The chain reaction was stopped! The gibtonite had [src.det_time] reactions left till the explosion!") + +/turf/simulated/mineral/gibtonite/gets_drilled(mob/user, triggered_by_explosion = 0) + if(stage == 0 && mineralAmt >= 1) //Gibtonite deposit is activated + playsound(src,'sound/effects/hit_on_shattered_glass.ogg',50,1) + explosive_reaction(user, triggered_by_explosion) + return + if(stage == 1 && mineralAmt >= 1) //Gibtonite deposit goes kaboom + var/turf/bombturf = get_turf(src) + mineralAmt = 0 + explosion(bombturf,1,2,5, adminlog = 0) + if(stage == 2) //Gibtonite deposit is now benign and extractable. Depending on how close you were to it blowing up before defusing, you get better quality ore. + var/obj/item/weapon/twohanded/required/gibtonite/G = new /obj/item/weapon/twohanded/required/gibtonite/(src) + if(det_time <= 0) + G.quality = 3 + G.icon_state = "Gibtonite ore 3" + if(det_time >= 1 && det_time <= 2) + G.quality = 2 + G.icon_state = "Gibtonite ore 2" + var/turf/simulated/floor/plating/asteroid/G = ChangeTurf(turf_type) + G.fullUpdateMineralOverlays() + +////////////////////////////////End Gibtonite + +/turf/simulated/floor/plating/asteroid/airless/cave + var/length = 100 + var/mob_spawn_list = list("Goldgrub" = 1, "Goliath" = 5, "Basilisk" = 4, "Hivelord" = 3) + var/sanity = 1 + turf_type = /turf/simulated/floor/plating/asteroid/airless + +/turf/simulated/floor/plating/asteroid/airless/cave/New(loc, var/length, var/go_backwards = 1, var/exclude_dir = -1) + + // If length (arg2) isn't defined, get a random length; otherwise assign our length to the length arg. + if(!length) + src.length = rand(25, 50) + else + src.length = length + + // Get our directiosn + var/forward_cave_dir = pick(alldirs - exclude_dir) + // Get the opposite direction of our facing direction + var/backward_cave_dir = angle2dir(dir2angle(forward_cave_dir) + 180) + + // Make our tunnels + make_tunnel(forward_cave_dir) + if(go_backwards) + make_tunnel(backward_cave_dir) + // Kill ourselves by replacing ourselves with a normal floor. + SpawnFloor(src) + ..() + +/turf/simulated/floor/plating/asteroid/airless/cave/proc/make_tunnel(dir) + + var/turf/simulated/mineral/tunnel = src + var/next_angle = pick(45, -45) + + for(var/i = 0; i < length; i++) + if(!sanity) + break + + var/list/L = list(45) + if(IsOdd(dir2angle(dir))) // We're going at an angle and we want thick angled tunnels. + L += -45 + + // Expand the edges of our tunnel + for(var/edge_angle in L) + var/turf/simulated/mineral/edge = get_step(tunnel, angle2dir(dir2angle(dir) + edge_angle)) + if(istype(edge)) + SpawnFloor(edge) + + // Move our tunnel forward + tunnel = get_step(tunnel, dir) + + if(istype(tunnel)) + // Small chance to have forks in our tunnel; otherwise dig our tunnel. + if(i > 3 && prob(20)) + new src.type(tunnel, rand(10, 15), 0, dir) + else + SpawnFloor(tunnel) + else //if(!istype(tunnel, src.parent)) // We hit space/normal/wall, stop our tunnel. + break + + // Chance to change our direction left or right. + if(i > 2 && prob(33)) + // We can't go a full loop though + next_angle = -next_angle + dir = angle2dir(dir2angle(dir) + next_angle) + + +/turf/simulated/floor/plating/asteroid/airless/cave/proc/SpawnFloor(turf/T) + for(var/turf/S in range(2,T)) + if(istype(S, /turf/space) || istype(S.loc, /area/mine/explored)) + sanity = 0 + break + if(!sanity) + return + + SpawnMonster(T) + var/turf/simulated/floor/t = new turf_type(T) + spawn(2) + t.fullUpdateMineralOverlays() + +/turf/simulated/floor/plating/asteroid/airless/cave/proc/SpawnMonster(turf/T) + if(prob(30)) + if(istype(loc, /area/mine/explored)) + return + for(var/atom/A in ultra_range(15,T))//Lowers chance of mob clumps + if(istype(A, /mob/living/simple_animal/hostile/asteroid)) + return + var/randumb = pickweight(mob_spawn_list) + switch(randumb) + if("Goliath") + new /mob/living/simple_animal/hostile/asteroid/goliath(T) + if("Goldgrub") + new /mob/living/simple_animal/hostile/asteroid/goldgrub(T) + if("Basilisk") + new /mob/living/simple_animal/hostile/asteroid/basilisk(T) + if("Hivelord") + new /mob/living/simple_animal/hostile/asteroid/hivelord(T) + return + +/turf/simulated/mineral/attackby(obj/item/weapon/pickaxe/P, mob/user, params) + + if (!user.IsAdvancedToolUser()) + usr << "You don't have the dexterity to do this!" + return + + if (istype(P, /obj/item/weapon/pickaxe)) + var/turf/T = user.loc + if (!( istype(T, /turf) )) + return + + if(last_act+P.digspeed > world.time)//prevents message spam + return + last_act = world.time + user << "You start picking..." + P.playDigSound() + + if(do_after(user,P.digspeed, target = src)) + if(istype(src, /turf/simulated/mineral)) + user << "You finish cutting into the rock." + gets_drilled(user) + feedback_add_details("pick_used_mining","[P.type]") + else + return attack_hand(user) + return + +/turf/simulated/mineral/proc/gets_drilled() + if (mineralType && (src.mineralAmt > 0) && (src.mineralAmt < 11)) + var/i + for (i=0;i= 2) + gets_drilled() + ..() + +/turf/simulated/mineral/attack_alien(mob/living/carbon/alien/M) + M << "You start digging into the rock..." + playsound(src, 'sound/effects/break_stone.ogg', 50, 1) + if(do_after(M,40, target = src)) + M << "You tunnel into the rock." + gets_drilled(M) + +/turf/simulated/mineral/Bumped(AM as mob|obj) + ..() + if(istype(AM,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = AM + if((istype(H.l_hand,/obj/item/weapon/pickaxe)) && (!H.hand)) + src.attackby(H.l_hand,H) + else if((istype(H.r_hand,/obj/item/weapon/pickaxe)) && H.hand) + src.attackby(H.r_hand,H) + return + else if(istype(AM,/mob/living/silicon/robot)) + var/mob/living/silicon/robot/R = AM + if(istype(R.module_active,/obj/item/weapon/pickaxe)) + src.attackby(R.module_active,R) + return +/* else if(istype(AM,/obj/mecha)) + var/obj/mecha/M = AM + if(istype(M.selected,/obj/item/mecha_parts/mecha_equipment/drill)) + src.attackby(M.selected,M) + return*/ +//Aparantly mechs are just TOO COOL to call Bump(), so fuck em (for now) + else + return + +/**********************Asteroid**************************/ + +/turf/simulated/floor/plating/asteroid //floor piece + name = "Asteroid" + baseturf = /turf/simulated/floor/plating/asteroid + icon = 'icons/turf/floors.dmi' + icon_state = "asteroid" + icon_plating = "asteroid" + var/environment_type = "asteroid" + var/turf_type = /turf/simulated/floor/plating/asteroid //Because caves do whacky shit to revert to normal + var/dug = 0 //0 = has not yet been dug, 1 = has already been dug + +/turf/simulated/floor/plating/asteroid/airless + oxygen = 0.01 + nitrogen = 0.01 + turf_type = /turf/simulated/floor/plating/asteroid/airless + temperature = TCMB + +/turf/simulated/floor/plating/asteroid/basalt + name = "volcanic floor" + baseturf = /turf/simulated/floor/plating/asteroid/basalt + icon = 'icons/turf/floors.dmi' + icon_state = "basalt" + icon_plating = "basalt" + environment_type = "basalt" + +/turf/simulated/floor/plating/asteroid/basalt/lava //lava underneath + baseturf = /turf/simulated/floor/plating/lava/smooth + +/turf/simulated/floor/plating/asteroid/basalt/airless + oxygen = 0.01 + nitrogen = 0.01 + temperature = TCMB + +/turf/simulated/floor/plating/asteroid/snow + name = "snow" + desc = "Looks cold." + icon = 'icons/turf/snow.dmi' + baseturf = /turf/simulated/floor/plating/asteroid/snow + icon_state = "snow" + icon_plating = "snow" + temperature = 180 + slowdown = 2 + environment_type = "snow" + +/turf/simulated/floor/plating/asteroid/snow/airless + oxygen = 0.01 + nitrogen = 0.01 + temperature = TCMB + +/turf/simulated/floor/plating/asteroid/New() + var/proper_name = name + ..() + name = proper_name + if(prob(20)) + icon_state = "[environment_type][rand(0,12)]" + +/turf/simulated/floor/plating/asteroid/burn_tile() + return + +/turf/simulated/floor/plating/asteroid/ex_act(severity, target) + contents_explosion(severity, target) + switch(severity) + if(3) + return + if(2) + if (prob(20)) + src.gets_dug() + if(1) + src.gets_dug() + return + +/turf/simulated/floor/plating/asteroid/attackby(obj/item/weapon/W, mob/user, params) + //note that this proc does not call ..() + if(!W || !user) + return 0 + var/digging_speed = 0 + if (istype(W, /obj/item/weapon/shovel)) + var/obj/item/weapon/shovel/S = W + digging_speed = S.digspeed + else if (istype(W, /obj/item/weapon/pickaxe)) + var/obj/item/weapon/pickaxe/P = W + digging_speed = P.digspeed + if (digging_speed) + var/turf/T = user.loc + if (!( istype(T, /turf) )) + return + + if (dug) + user << "This area has already been dug!" + return + + user << "You start digging..." + playsound(src, 'sound/effects/shovel_dig.ogg', 50, 1) //FUCK YO RUSTLE I GOT'S THE DIGS SOUND HERE + + if(do_after(user, digging_speed, target = src)) + if(istype(src, /turf/simulated/floor/plating/asteroid)) + user << "You dig a hole." + gets_dug() + feedback_add_details("pick_used_mining","[W.type]") + + if(istype(W,/obj/item/weapon/storage/bag/ore)) + var/obj/item/weapon/storage/bag/ore/S = W + if(S.collection_mode == 1) + for(var/obj/item/weapon/ore/O in src.contents) + O.attackby(W,user) + return + + if(istype(W, /obj/item/stack/tile)) + var/obj/item/stack/tile/Z = W + if(!Z.use(1)) + return + var/turf/simulated/floor/T = ChangeTurf(Z.turf_type) + if(istype(Z,/obj/item/stack/tile/light)) //TODO: get rid of this ugly check somehow + var/obj/item/stack/tile/light/L = Z + var/turf/simulated/floor/light/F = T + F.state = L.state + playsound(src, 'sound/weapons/Genhit.ogg', 50, 1) + +/turf/simulated/floor/plating/asteroid/proc/gets_dug() + if(dug) + return + new/obj/item/weapon/ore/glass(src) + new/obj/item/weapon/ore/glass(src) + new/obj/item/weapon/ore/glass(src) + new/obj/item/weapon/ore/glass(src) + new/obj/item/weapon/ore/glass(src) + dug = 1 + icon_plating = "[environment_type]_dug" + icon_state = "[environment_type]_dug" + slowdown = 0 + return + +/turf/simulated/floor/plating/asteroid/singularity_act() + return + +/turf/simulated/floor/plating/asteroid/singularity_pull(S, current_size) + return + +/turf/proc/updateMineralOverlays() + src.overlays.Cut() + + if(istype(get_step(src, NORTH), /turf/simulated/mineral)) + src.overlays += rockTurfEdgeCache[NORTH_EDGING] + if(istype(get_step(src, SOUTH), /turf/simulated/mineral)) + src.overlays += rockTurfEdgeCache[SOUTH_EDGING] + if(istype(get_step(src, EAST), /turf/simulated/mineral)) + src.overlays += rockTurfEdgeCache[EAST_EDGING] + if(istype(get_step(src, WEST), /turf/simulated/mineral)) + src.overlays += rockTurfEdgeCache[WEST_EDGING] + +/turf/simulated/mineral/updateMineralOverlays() + return + +/turf/simulated/wall/updateMineralOverlays() + return + +/turf/proc/fullUpdateMineralOverlays() + for (var/turf/t in range(1,src)) + t.updateMineralOverlays() + + + + + + + + +//////////////CHASM////////////////// + +/turf/simulated/chasm + name = "chasm" + desc = "Watch your step." + baseturf = /turf/simulated/chasm + smooth = SMOOTH_TRUE + icon = 'icons/turf/floors/Chasms.dmi' + icon_state = "smooth" + var/drop_x = 1 + var/drop_y = 1 + var/drop_z = 1 + + +/turf/simulated/chasm/Entered(atom/movable/AM) + if(istype(AM, /obj/singularity) || istype(AM, /obj/item/projectile)) + return + drop(AM) + + +/turf/simulated/chasm/proc/drop(atom/movable/AM) + visible_message("[AM] falls into [src]!") + AM.Move(locate(drop_x, drop_y, drop_z)) + AM.visible_message("[AM] falls from above!") + if(istype(AM, /mob/living)) + var/mob/living/L = AM + L.adjustBruteLoss(30) + + + +/turf/simulated/chasm/straight_down/New() + ..() + drop_x = x + drop_y = y + if(z+1 <= world.maxz) + drop_z = z+1 + + +#undef NORTH_EDGING +#undef SOUTH_EDGING +#undef EAST_EDGING +#undef WEST_EDGING diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 3209daa1e8b..350ea9720d0 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -1,431 +1,431 @@ -var/list/image/ghost_darkness_images = list() //this is a list of images for things ghosts should still be able to see when they toggle darkness -/mob/dead/observer - name = "ghost" - desc = "It's a g-g-g-g-ghooooost!" //jinkies! - icon = 'icons/mob/mob.dmi' - icon_state = "ghost" - layer = MOB_LAYER + 1 - stat = DEAD - density = 0 - canmove = 0 - anchored = 1 // don't get pushed around - invisibility = INVISIBILITY_OBSERVER - languages = ALL - var/can_reenter_corpse - var/datum/hud/living/carbon/hud = null // hud - var/bootime = 0 - var/started_as_observer //This variable is set to 1 when you enter the game as an observer. - //If you died in the game and are a ghsot - this will remain as null. - //Note that this is not a reliable way to determine if admins started as observers, since they change mobs a lot. - var/atom/movable/following = null - var/fun_verbs = 0 - var/image/ghostimage = null //this mobs ghost image, for deleting and stuff - var/ghostvision = 1 //is the ghost able to see things humans can't? - var/seedarkness = 1 - var/ghost_hud_enabled = 1 //did this ghost disable the on-screen HUD? - var/data_hud_seen = 0 //this should one of the defines in __DEFINES/hud.dm - -/mob/dead/observer/New(mob/body) - sight |= SEE_TURFS | SEE_MOBS | SEE_OBJS | SEE_SELF - see_invisible = SEE_INVISIBLE_OBSERVER - see_in_dark = 100 - verbs += /mob/dead/observer/proc/dead_tele - stat = DEAD - - ghostimage = image(src.icon,src,src.icon_state) - ghost_darkness_images |= ghostimage - updateallghostimages() - var/turf/T - if(ismob(body)) - T = get_turf(body) //Where is the body located? - attack_log = body.attack_log //preserve our attack logs by copying them to our ghost - - gender = body.gender - if(body.mind && body.mind.name) - name = body.mind.name - else - if(body.real_name) - name = body.real_name - else - name = random_unique_name(gender) - - mind = body.mind //we don't transfer the mind but we keep a reference to it. - - if(!T) T = pick(latejoin) //Safety in case we cannot find the body's position - loc = T - - if(!name) //To prevent nameless ghosts - name = random_unique_name(gender) - real_name = name - - if(!fun_verbs) - verbs -= /mob/dead/observer/verb/boo - verbs -= /mob/dead/observer/verb/possess - - animate(src, pixel_y = 2, time = 10, loop = -1) - ..() - -/mob/dead/observer/Destroy() - if (ghostimage) - ghost_darkness_images -= ghostimage - qdel(ghostimage) - ghostimage = null - updateallghostimages() - return ..() - -/mob/dead/CanPass(atom/movable/mover, turf/target, height=0) - return 1 -/* -Transfer_mind is there to check if mob is being deleted/not going to have a body. -Works together with spawning an observer, noted above. -*/ - -/mob/proc/ghostize(can_reenter_corpse = 1) - if(key) - if(!cmptext(copytext(key,1,2),"@")) //aghost - var/mob/dead/observer/ghost = new(src) //Transfer safety to observer spawning proc. - ghost.can_reenter_corpse = can_reenter_corpse - ghost.key = key - return ghost - -/* -This is the proc mobs get to turn into a ghost. Forked from ghostize due to compatibility issues. -*/ -/mob/living/verb/ghost() - set category = "OOC" - set name = "Ghost" - set desc = "Relinquish your life and enter the land of the dead." - - if(stat != DEAD) - succumb() - if(stat == DEAD) - ghostize(1) - else - var/response = alert(src, "Are you -sure- you want to ghost?\n(You are alive. If you ghost whilst still alive you may not play again this round! 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 - ghostize(0) //0 parameter is so we can never re-enter our body, "Charlie, you can never come baaaack~" :3 - return - - -/mob/dead/observer/Move(NewLoc, direct) - if(NewLoc) - loc = NewLoc - for(var/obj/effect/step_trigger/S in NewLoc) - S.Crossed(src) - - return - loc = get_turf(src) //Get out of closets and such as a ghost - if((direct & NORTH) && y < world.maxy) - y++ - else if((direct & SOUTH) && y > 1) - y-- - if((direct & EAST) && x < world.maxx) - x++ - else if((direct & WEST) && x > 1) - x-- - - for(var/obj/effect/step_trigger/S in locate(x, y, z)) //<-- this is dumb - S.Crossed(src) - -/mob/dead/observer/is_active() - return 0 - -/mob/dead/observer/Stat() - ..() - if(statpanel("Status")) - stat(null, "Station Time: [worldtime2text()]") - if(ticker) - if(ticker.mode) - //world << "DEBUG: ticker not null" - if(ticker.mode.name == "AI malfunction") - var/datum/game_mode/malfunction/malf = ticker.mode - //world << "DEBUG: malf mode ticker test" - if(malf.malf_mode_declared && (malf.apcs > 0)) - stat(null, "Time left: [max(malf.AI_win_timeleft/malf.apcs, 0)]") - - for(var/datum/gang/G in ticker.mode.gangs) - if(isnum(G.dom_timer)) - stat(null, "[G.name] Gang Takeover: [max(G.dom_timer, 0)]") - -/mob/dead/observer/verb/reenter_corpse() - set category = "Ghost" - set name = "Re-enter Corpse" - if(!client) return - if(!(mind && mind.current)) - src << "You have no body." - return - if(!can_reenter_corpse) - src << "You cannot re-enter your body." - return - if(mind.current.key && copytext(mind.current.key,1,2)!="@") //makes sure we don't accidentally kick any clients - usr << "Another consciousness is in your body...It is resisting you." - return - mind.current.key = key - return 1 - -/mob/dead/observer/proc/notify_cloning(var/message, var/sound, var/atom/source) - if(message) - src << "[message]" - if(source) - var/obj/screen/alert/A = throw_alert("\ref[source]_notify_cloning", /obj/screen/alert/notify_cloning) - if(A) - A.desc = message - var/old_layer = source.layer - source.layer = FLOAT_LAYER - A.overlays += source - source.layer = old_layer - src << "(Click to re-enter)" - if(sound) - src << sound(sound) - -/mob/dead/observer/proc/dead_tele() - set category = "Ghost" - set name = "Teleport" - set desc= "Teleport to a location" - if(!istype(usr, /mob/dead/observer)) - usr << "Not when you're not dead!" - return - usr.verbs -= /mob/dead/observer/proc/dead_tele - spawn(30) - usr.verbs += /mob/dead/observer/proc/dead_tele - var/A - A = input("Area to jump to", "BOOYEA", A) as null|anything in sortedAreas - var/area/thearea = A - if(!thearea) return - - var/list/L = list() - for(var/turf/T in get_area_turfs(thearea.type)) - L+=T - - if(!L || !L.len) - usr << "No area available." - - usr.loc = pick(L) - -/mob/dead/observer/verb/follow() - set category = "Ghost" - set name = "Orbit" // "Haunt" - set desc = "Follow and orbit a mob." - - var/list/mobs = getpois(skip_mindless=1) - var/input = input("Please, select a mob!", "Haunt", null, null) as null|anything in mobs - var/mob/target = mobs[input] - ManualFollow(target) - -// This is the ghost's follow verb with an argument -/mob/dead/observer/proc/ManualFollow(atom/movable/target) - if (!istype(target)) - return - - var/icon/I = icon(target.icon,target.icon_state,target.dir) - - var/orbitsize = (I.Width()+I.Height())*0.5 - orbitsize -= (orbitsize/world.icon_size)*(world.icon_size*0.25) - - if(orbiting != target) - src << "Now orbiting [target]." - - orbit(target,orbitsize,0) - -/mob/dead/observer/orbit() - ..() - //restart our floating animation after orbit is done. - sleep 2 //orbit sets up a 2ds animation when it finishes, so we wait for that to end - if (!orbiting) //make sure another orbit hasn't started - pixel_y = 0 - animate(src, pixel_y = 2, time = 10, loop = -1) - -/mob/dead/observer/verb/jumptomob() //Moves the ghost instead of just changing the ghosts's eye -Nodrak - set category = "Ghost" - set name = "Jump to Mob" - set desc = "Teleport to a mob" - - if(istype(usr, /mob/dead/observer)) //Make sure they're an observer! - - - var/list/dest = list() //List of possible destinations (mobs) - var/target = null //Chosen target. - - dest += getpois(mobs_only=1) //Fill list, prompt user with list - target = input("Please, select a player!", "Jump to Mob", null, null) as null|anything in dest - - if (!target)//Make sure we actually have a target - return - else - var/mob/M = dest[target] //Destination mob - var/mob/A = src //Source mob - var/turf/T = get_turf(M) //Turf of the destination mob - - if(T && isturf(T)) //Make sure the turf exists, then move the source to that destination. - A.loc = T - else - A << "This mob is not located in the game world." - -/mob/dead/observer/verb/boo() - set category = "Ghost" - set name = "Boo!" - set desc= "Scare your crew members because of boredom!" - - if(bootime > world.time) return - var/obj/machinery/light/L = locate(/obj/machinery/light) in view(1, src) - if(L) - L.flicker() - bootime = world.time + 600 - return - //Maybe in the future we can add more spooky code here! - return - - -/mob/dead/observer/memory() - set hidden = 1 - src << "You are dead! You have no mind to store memory!" - -/mob/dead/observer/add_memory() - set hidden = 1 - src << "You are dead! You have no mind to store memory!" - -/mob/dead/observer/verb/toggle_ghostsee() - set name = "Toggle Ghost Vision" - set desc = "Toggles your ability to see things only ghosts can see, like other ghosts" - set category = "Ghost" - ghostvision = !(ghostvision) - updateghostsight() - usr << "You [(ghostvision?"now":"no longer")] have ghost vision." - -/mob/dead/observer/verb/toggle_darkness() - set name = "Toggle Darkness" - set category = "Ghost" - seedarkness = !(seedarkness) - updateghostsight() - -/mob/dead/observer/proc/updateghostsight() - if (!seedarkness) - see_invisible = SEE_INVISIBLE_OBSERVER_NOLIGHTING - else - see_invisible = SEE_INVISIBLE_OBSERVER - if (!ghostvision) - see_invisible = SEE_INVISIBLE_LIVING; - updateghostimages() - -/proc/updateallghostimages() - for (var/mob/dead/observer/O in player_list) - O.updateghostimages() - -/mob/dead/observer/proc/updateghostimages() - if (!client) - return - if (seedarkness || !ghostvision) - client.images -= ghost_darkness_images - else - //add images for the 60inv things ghosts can normally see when darkness is enabled so they can see them now - client.images |= ghost_darkness_images - if (ghostimage) - client.images -= ghostimage //remove ourself - -/mob/dead/observer/verb/possess() - set category = "Ghost" - set name = "Possess!" - set desc= "Take over the body of a mindless creature!" - - var/list/possessible = list() - for(var/mob/living/L in living_mob_list) - if(!(L in player_list) && !L.mind) - possessible += L - - var/mob/living/target = input("Your new life begins today!", "Possess Mob", null, null) as null|anything in possessible - - if(!target) - return 0 - if(can_reenter_corpse || (mind && mind.current)) - if(alert(src, "Your soul is still tied to your former life as [mind.current.name], if you go foward there is no going back to that life. Are you sure you wish to continue?", "Move On", "Yes", "No") == "No") - return 0 - if(target.key) - src << "Someone has taken this body while you were choosing!" - return 0 - - target.key = key - return 1 - - -//this is a mob verb instead of atom for performance reasons -//see /mob/verb/examinate() in mob.dm for more info -//overriden here and in /mob/living for different point span classes and sanity checks -/mob/dead/observer/pointed(atom/A as mob|obj|turf in view()) - if(!..()) - return 0 - usr.visible_message("[src] points to [A].") - return 1 - -/mob/dead/observer/verb/view_manifest() - set name = "View Crew Manifest" - set category = "Ghost" - - var/dat - dat += "

Crew Manifest

" - dat += data_core.get_manifest() - - src << browse(dat, "window=manifest;size=387x420;can_close=1") - -//this is called when a ghost is drag clicked to something. -/mob/dead/observer/MouseDrop(atom/over) - if(!usr || !over) return - if (isobserver(usr) && usr.client.holder && isliving(over)) - if (usr.client.holder.cmd_ghost_drag(src,over)) - return - - return ..() - -/mob/dead/observer/Topic(href, href_list) - ..() - if(usr == src) - if(href_list["follow"]) - var/atom/movable/target = locate(href_list["follow"]) - if(istype(target) && (target != src)) - ManualFollow(target) - if(href_list["reenter"]) - reenter_corpse() - -//We don't want to update the current var -//But we will still carry a mind. -/mob/dead/observer/mind_initialize() - return - -/mob/dead/observer/verb/toggle_ghosthud() - set name = "Toggle Ghost HUD" - set desc = "Toggles your ghost's on-screen HUD" - set category = "Ghost" - ghost_hud_enabled = !ghost_hud_enabled - hud_used.ghost_hud() - -/mob/dead/observer/proc/show_me_the_hud(hud_index) - var/datum/atom_hud/H = huds[hud_index] - H.add_hud_to(src) - data_hud_seen = hud_index - -/mob/dead/observer/verb/toggle_ghost_med_sec_diag_hud() - set name = "Toggle Sec/Med/Diag HUD" - set desc = "Toggles whether you see medical/security/diagnostic HUDs" - set category = "Ghost" - - if(data_hud_seen) //remove old huds - var/datum/atom_hud/H = huds[data_hud_seen] - H.remove_hud_from(src) - - switch(data_hud_seen) //give new huds - if(0) - show_me_the_hud(DATA_HUD_SECURITY_BASIC) - src << "Security HUD set." - if(DATA_HUD_SECURITY_BASIC) - show_me_the_hud(DATA_HUD_MEDICAL_ADVANCED) - src << "Medical HUD set." - if(DATA_HUD_MEDICAL_ADVANCED) - show_me_the_hud(DATA_HUD_DIAGNOSTIC) - src << "Diagnostic HUD set." - if(DATA_HUD_DIAGNOSTIC) - data_hud_seen = 0 - src << "HUDs disabled." - -/mob/dead/observer/canUseTopic() - if(check_rights(R_ADMIN, 0)) - return 1 +var/list/image/ghost_darkness_images = list() //this is a list of images for things ghosts should still be able to see when they toggle darkness +/mob/dead/observer + name = "ghost" + desc = "It's a g-g-g-g-ghooooost!" //jinkies! + icon = 'icons/mob/mob.dmi' + icon_state = "ghost" + layer = MOB_LAYER + 1 + stat = DEAD + density = 0 + canmove = 0 + anchored = 1 // don't get pushed around + invisibility = INVISIBILITY_OBSERVER + languages = ALL + var/can_reenter_corpse + var/datum/hud/living/carbon/hud = null // hud + var/bootime = 0 + var/started_as_observer //This variable is set to 1 when you enter the game as an observer. + //If you died in the game and are a ghsot - this will remain as null. + //Note that this is not a reliable way to determine if admins started as observers, since they change mobs a lot. + var/atom/movable/following = null + var/fun_verbs = 0 + var/image/ghostimage = null //this mobs ghost image, for deleting and stuff + var/ghostvision = 1 //is the ghost able to see things humans can't? + var/seedarkness = 1 + var/ghost_hud_enabled = 1 //did this ghost disable the on-screen HUD? + var/data_hud_seen = 0 //this should one of the defines in __DEFINES/hud.dm + +/mob/dead/observer/New(mob/body) + sight |= SEE_TURFS | SEE_MOBS | SEE_OBJS | SEE_SELF + see_invisible = SEE_INVISIBLE_OBSERVER + see_in_dark = 100 + verbs += /mob/dead/observer/proc/dead_tele + stat = DEAD + + ghostimage = image(src.icon,src,src.icon_state) + ghost_darkness_images |= ghostimage + updateallghostimages() + var/turf/T + if(ismob(body)) + T = get_turf(body) //Where is the body located? + attack_log = body.attack_log //preserve our attack logs by copying them to our ghost + + gender = body.gender + if(body.mind && body.mind.name) + name = body.mind.name + else + if(body.real_name) + name = body.real_name + else + name = random_unique_name(gender) + + mind = body.mind //we don't transfer the mind but we keep a reference to it. + + if(!T) T = pick(latejoin) //Safety in case we cannot find the body's position + loc = T + + if(!name) //To prevent nameless ghosts + name = random_unique_name(gender) + real_name = name + + if(!fun_verbs) + verbs -= /mob/dead/observer/verb/boo + verbs -= /mob/dead/observer/verb/possess + + animate(src, pixel_y = 2, time = 10, loop = -1) + ..() + +/mob/dead/observer/Destroy() + if (ghostimage) + ghost_darkness_images -= ghostimage + qdel(ghostimage) + ghostimage = null + updateallghostimages() + return ..() + +/mob/dead/CanPass(atom/movable/mover, turf/target, height=0) + return 1 +/* +Transfer_mind is there to check if mob is being deleted/not going to have a body. +Works together with spawning an observer, noted above. +*/ + +/mob/proc/ghostize(can_reenter_corpse = 1) + if(key) + if(!cmptext(copytext(key,1,2),"@")) //aghost + var/mob/dead/observer/ghost = new(src) //Transfer safety to observer spawning proc. + ghost.can_reenter_corpse = can_reenter_corpse + ghost.key = key + return ghost + +/* +This is the proc mobs get to turn into a ghost. Forked from ghostize due to compatibility issues. +*/ +/mob/living/verb/ghost() + set category = "OOC" + set name = "Ghost" + set desc = "Relinquish your life and enter the land of the dead." + + if(stat != DEAD) + succumb() + if(stat == DEAD) + ghostize(1) + else + var/response = alert(src, "Are you -sure- you want to ghost?\n(You are alive. If you ghost whilst still alive you may not play again this round! 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 + ghostize(0) //0 parameter is so we can never re-enter our body, "Charlie, you can never come baaaack~" :3 + return + + +/mob/dead/observer/Move(NewLoc, direct) + if(NewLoc) + loc = NewLoc + for(var/obj/effect/step_trigger/S in NewLoc) + S.Crossed(src) + + return + loc = get_turf(src) //Get out of closets and such as a ghost + if((direct & NORTH) && y < world.maxy) + y++ + else if((direct & SOUTH) && y > 1) + y-- + if((direct & EAST) && x < world.maxx) + x++ + else if((direct & WEST) && x > 1) + x-- + + for(var/obj/effect/step_trigger/S in locate(x, y, z)) //<-- this is dumb + S.Crossed(src) + +/mob/dead/observer/is_active() + return 0 + +/mob/dead/observer/Stat() + ..() + if(statpanel("Status")) + stat(null, "Station Time: [worldtime2text()]") + if(ticker) + if(ticker.mode) + //world << "DEBUG: ticker not null" + if(ticker.mode.name == "AI malfunction") + var/datum/game_mode/malfunction/malf = ticker.mode + //world << "DEBUG: malf mode ticker test" + if(malf.malf_mode_declared && (malf.apcs > 0)) + stat(null, "Time left: [max(malf.AI_win_timeleft/malf.apcs, 0)]") + + for(var/datum/gang/G in ticker.mode.gangs) + if(isnum(G.dom_timer)) + stat(null, "[G.name] Gang Takeover: [max(G.dom_timer, 0)]") + +/mob/dead/observer/verb/reenter_corpse() + set category = "Ghost" + set name = "Re-enter Corpse" + if(!client) return + if(!(mind && mind.current)) + src << "You have no body." + return + if(!can_reenter_corpse) + src << "You cannot re-enter your body." + return + if(mind.current.key && copytext(mind.current.key,1,2)!="@") //makes sure we don't accidentally kick any clients + usr << "Another consciousness is in your body...It is resisting you." + return + mind.current.key = key + return 1 + +/mob/dead/observer/proc/notify_cloning(var/message, var/sound, var/atom/source) + if(message) + src << "[message]" + if(source) + var/obj/screen/alert/A = throw_alert("\ref[source]_notify_cloning", /obj/screen/alert/notify_cloning) + if(A) + A.desc = message + var/old_layer = source.layer + source.layer = FLOAT_LAYER + A.overlays += source + source.layer = old_layer + src << "(Click to re-enter)" + if(sound) + src << sound(sound) + +/mob/dead/observer/proc/dead_tele() + set category = "Ghost" + set name = "Teleport" + set desc= "Teleport to a location" + if(!istype(usr, /mob/dead/observer)) + usr << "Not when you're not dead!" + return + usr.verbs -= /mob/dead/observer/proc/dead_tele + spawn(30) + usr.verbs += /mob/dead/observer/proc/dead_tele + var/A + A = input("Area to jump to", "BOOYEA", A) as null|anything in sortedAreas + var/area/thearea = A + if(!thearea) return + + var/list/L = list() + for(var/turf/T in get_area_turfs(thearea.type)) + L+=T + + if(!L || !L.len) + usr << "No area available." + + usr.loc = pick(L) + +/mob/dead/observer/verb/follow() + set category = "Ghost" + set name = "Orbit" // "Haunt" + set desc = "Follow and orbit a mob." + + var/list/mobs = getpois(skip_mindless=1) + var/input = input("Please, select a mob!", "Haunt", null, null) as null|anything in mobs + var/mob/target = mobs[input] + ManualFollow(target) + +// This is the ghost's follow verb with an argument +/mob/dead/observer/proc/ManualFollow(atom/movable/target) + if (!istype(target)) + return + + var/icon/I = icon(target.icon,target.icon_state,target.dir) + + var/orbitsize = (I.Width()+I.Height())*0.5 + orbitsize -= (orbitsize/world.icon_size)*(world.icon_size*0.25) + + if(orbiting != target) + src << "Now orbiting [target]." + + orbit(target,orbitsize,0) + +/mob/dead/observer/orbit() + ..() + //restart our floating animation after orbit is done. + sleep 2 //orbit sets up a 2ds animation when it finishes, so we wait for that to end + if (!orbiting) //make sure another orbit hasn't started + pixel_y = 0 + animate(src, pixel_y = 2, time = 10, loop = -1) + +/mob/dead/observer/verb/jumptomob() //Moves the ghost instead of just changing the ghosts's eye -Nodrak + set category = "Ghost" + set name = "Jump to Mob" + set desc = "Teleport to a mob" + + if(istype(usr, /mob/dead/observer)) //Make sure they're an observer! + + + var/list/dest = list() //List of possible destinations (mobs) + var/target = null //Chosen target. + + dest += getpois(mobs_only=1) //Fill list, prompt user with list + target = input("Please, select a player!", "Jump to Mob", null, null) as null|anything in dest + + if (!target)//Make sure we actually have a target + return + else + var/mob/M = dest[target] //Destination mob + var/mob/A = src //Source mob + var/turf/T = get_turf(M) //Turf of the destination mob + + if(T && isturf(T)) //Make sure the turf exists, then move the source to that destination. + A.loc = T + else + A << "This mob is not located in the game world." + +/mob/dead/observer/verb/boo() + set category = "Ghost" + set name = "Boo!" + set desc= "Scare your crew members because of boredom!" + + if(bootime > world.time) return + var/obj/machinery/light/L = locate(/obj/machinery/light) in view(1, src) + if(L) + L.flicker() + bootime = world.time + 600 + return + //Maybe in the future we can add more spooky code here! + return + + +/mob/dead/observer/memory() + set hidden = 1 + src << "You are dead! You have no mind to store memory!" + +/mob/dead/observer/add_memory() + set hidden = 1 + src << "You are dead! You have no mind to store memory!" + +/mob/dead/observer/verb/toggle_ghostsee() + set name = "Toggle Ghost Vision" + set desc = "Toggles your ability to see things only ghosts can see, like other ghosts" + set category = "Ghost" + ghostvision = !(ghostvision) + updateghostsight() + usr << "You [(ghostvision?"now":"no longer")] have ghost vision." + +/mob/dead/observer/verb/toggle_darkness() + set name = "Toggle Darkness" + set category = "Ghost" + seedarkness = !(seedarkness) + updateghostsight() + +/mob/dead/observer/proc/updateghostsight() + if (!seedarkness) + see_invisible = SEE_INVISIBLE_OBSERVER_NOLIGHTING + else + see_invisible = SEE_INVISIBLE_OBSERVER + if (!ghostvision) + see_invisible = SEE_INVISIBLE_LIVING; + updateghostimages() + +/proc/updateallghostimages() + for (var/mob/dead/observer/O in player_list) + O.updateghostimages() + +/mob/dead/observer/proc/updateghostimages() + if (!client) + return + if (seedarkness || !ghostvision) + client.images -= ghost_darkness_images + else + //add images for the 60inv things ghosts can normally see when darkness is enabled so they can see them now + client.images |= ghost_darkness_images + if (ghostimage) + client.images -= ghostimage //remove ourself + +/mob/dead/observer/verb/possess() + set category = "Ghost" + set name = "Possess!" + set desc= "Take over the body of a mindless creature!" + + var/list/possessible = list() + for(var/mob/living/L in living_mob_list) + if(!(L in player_list) && !L.mind) + possessible += L + + var/mob/living/target = input("Your new life begins today!", "Possess Mob", null, null) as null|anything in possessible + + if(!target) + return 0 + if(can_reenter_corpse || (mind && mind.current)) + if(alert(src, "Your soul is still tied to your former life as [mind.current.name], if you go foward there is no going back to that life. Are you sure you wish to continue?", "Move On", "Yes", "No") == "No") + return 0 + if(target.key) + src << "Someone has taken this body while you were choosing!" + return 0 + + target.key = key + return 1 + + +//this is a mob verb instead of atom for performance reasons +//see /mob/verb/examinate() in mob.dm for more info +//overriden here and in /mob/living for different point span classes and sanity checks +/mob/dead/observer/pointed(atom/A as mob|obj|turf in view()) + if(!..()) + return 0 + usr.visible_message("[src] points to [A].") + return 1 + +/mob/dead/observer/verb/view_manifest() + set name = "View Crew Manifest" + set category = "Ghost" + + var/dat + dat += "

Crew Manifest

" + dat += data_core.get_manifest() + + src << browse(dat, "window=manifest;size=387x420;can_close=1") + +//this is called when a ghost is drag clicked to something. +/mob/dead/observer/MouseDrop(atom/over) + if(!usr || !over) return + if (isobserver(usr) && usr.client.holder && isliving(over)) + if (usr.client.holder.cmd_ghost_drag(src,over)) + return + + return ..() + +/mob/dead/observer/Topic(href, href_list) + ..() + if(usr == src) + if(href_list["follow"]) + var/atom/movable/target = locate(href_list["follow"]) + if(istype(target) && (target != src)) + ManualFollow(target) + if(href_list["reenter"]) + reenter_corpse() + +//We don't want to update the current var +//But we will still carry a mind. +/mob/dead/observer/mind_initialize() + return + +/mob/dead/observer/verb/toggle_ghosthud() + set name = "Toggle Ghost HUD" + set desc = "Toggles your ghost's on-screen HUD" + set category = "Ghost" + ghost_hud_enabled = !ghost_hud_enabled + hud_used.ghost_hud() + +/mob/dead/observer/proc/show_me_the_hud(hud_index) + var/datum/atom_hud/H = huds[hud_index] + H.add_hud_to(src) + data_hud_seen = hud_index + +/mob/dead/observer/verb/toggle_ghost_med_sec_diag_hud() + set name = "Toggle Sec/Med/Diag HUD" + set desc = "Toggles whether you see medical/security/diagnostic HUDs" + set category = "Ghost" + + if(data_hud_seen) //remove old huds + var/datum/atom_hud/H = huds[data_hud_seen] + H.remove_hud_from(src) + + switch(data_hud_seen) //give new huds + if(0) + show_me_the_hud(DATA_HUD_SECURITY_BASIC) + src << "Security HUD set." + if(DATA_HUD_SECURITY_BASIC) + show_me_the_hud(DATA_HUD_MEDICAL_ADVANCED) + src << "Medical HUD set." + if(DATA_HUD_MEDICAL_ADVANCED) + show_me_the_hud(DATA_HUD_DIAGNOSTIC) + src << "Diagnostic HUD set." + if(DATA_HUD_DIAGNOSTIC) + data_hud_seen = 0 + src << "HUDs disabled." + +/mob/dead/observer/canUseTopic() + if(check_rights(R_ADMIN, 0)) + return 1 return \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/bear.dm b/code/modules/mob/living/simple_animal/hostile/bear.dm index be61ffa2fbb..498f39359bb 100644 --- a/code/modules/mob/living/simple_animal/hostile/bear.dm +++ b/code/modules/mob/living/simple_animal/hostile/bear.dm @@ -1,75 +1,75 @@ -//Space bears! -/mob/living/simple_animal/hostile/bear - name = "space bear" - desc = "You don't need to be faster than a space bear, you just need to outrun your crewmates." - icon_state = "bear" - icon_living = "bear" - icon_dead = "bear_dead" - icon_gib = "bear_gib" - speak = list("RAWR!","Rawr!","GRR!","Growl!") - speak_emote = list("growls", "roars") - emote_hear = list("rawrs.","grumbles.","grawls.") - emote_taunt = list("stares ferociously", "stomps") - speak_chance = 1 - taunt_chance = 25 - turns_per_move = 5 - see_in_dark = 6 - butcher_results = list(/obj/item/weapon/reagent_containers/food/snacks/meat/slab/bear = 5, /obj/item/clothing/head/bearpelt = 1) - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "hits" - maxHealth = 60 - health = 60 - - melee_damage_lower = 20 - melee_damage_upper = 30 - attacktext = "claws" - attack_sound = 'sound/weapons/bladeslice.ogg' - friendly = "bear hugs" - - //Space bears aren't affected by cold. - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - maxbodytemp = 1500 - - faction = list("russian") - gold_core_spawnable = 1 - -//SPACE BEARS! SQUEEEEEEEE~ OW! FUCK! IT BIT MY HAND OFF!! -/mob/living/simple_animal/hostile/bear/Hudson - name = "Hudson" - desc = "Feared outlaw, this guy is one bad news bear." //I'm sorry... - -/mob/living/simple_animal/hostile/bear/snow - name = "space polar bear" - icon_state = "snowbear" - icon_living = "snowbear" - icon_dead = "snowbear_dead" - desc = "It's a polar bear, in space, but not actually in space. " - -/mob/living/simple_animal/hostile/bear/Process_Spacemove(movement_dir = 0) - return 1 //No drifting in space for space bears! - - - - - - - - - - - - - - - - - - - - - - - - +//Space bears! +/mob/living/simple_animal/hostile/bear + name = "space bear" + desc = "You don't need to be faster than a space bear, you just need to outrun your crewmates." + icon_state = "bear" + icon_living = "bear" + icon_dead = "bear_dead" + icon_gib = "bear_gib" + speak = list("RAWR!","Rawr!","GRR!","Growl!") + speak_emote = list("growls", "roars") + emote_hear = list("rawrs.","grumbles.","grawls.") + emote_taunt = list("stares ferociously", "stomps") + speak_chance = 1 + taunt_chance = 25 + turns_per_move = 5 + see_in_dark = 6 + butcher_results = list(/obj/item/weapon/reagent_containers/food/snacks/meat/slab/bear = 5, /obj/item/clothing/head/bearpelt = 1) + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "hits" + maxHealth = 60 + health = 60 + + melee_damage_lower = 20 + melee_damage_upper = 30 + attacktext = "claws" + attack_sound = 'sound/weapons/bladeslice.ogg' + friendly = "bear hugs" + + //Space bears aren't affected by cold. + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + maxbodytemp = 1500 + + faction = list("russian") + gold_core_spawnable = 1 + +//SPACE BEARS! SQUEEEEEEEE~ OW! FUCK! IT BIT MY HAND OFF!! +/mob/living/simple_animal/hostile/bear/Hudson + name = "Hudson" + desc = "Feared outlaw, this guy is one bad news bear." //I'm sorry... + +/mob/living/simple_animal/hostile/bear/snow + name = "space polar bear" + icon_state = "snowbear" + icon_living = "snowbear" + icon_dead = "snowbear_dead" + desc = "It's a polar bear, in space, but not actually in space. " + +/mob/living/simple_animal/hostile/bear/Process_Spacemove(movement_dir = 0) + return 1 //No drifting in space for space bears! + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 1f9895bd796..d979ed6cbe9 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -1,519 +1,519 @@ -/mob/living/simple_animal - name = "animal" - icon = 'icons/mob/animal.dmi' - health = 20 - maxHealth = 20 - - status_flags = CANPUSH - - var/icon_living = "" - var/icon_dead = "" //icon when the animal is dead. Don't use animated icons for this. - var/icon_gib = null //We only try to show a gibbing animation if this exists. - - var/list/speak = list() - var/list/speak_emote = list()// Emotes while speaking IE: Ian [emote], [text] -- Ian barks, "WOOF!". Spoken text is generated from the speak variable. - var/speak_chance = 0 - var/list/emote_hear = list() //Hearable emotes - var/list/emote_see = list() //Unlike speak_emote, the list of things in this variable only show by themselves with no spoken text. IE: Ian barks, Ian yaps - - var/turns_per_move = 1 - var/turns_since_move = 0 - var/stop_automated_movement = 0 //Use this to temporarely stop random movement or to if you write special movement code for animals. - var/wander = 1 // Does the mob wander around when idle? - var/stop_automated_movement_when_pulled = 1 //When set to 1 this stops the animal from moving when someone is pulling it. - - //Interaction - var/response_help = "pokes" - var/response_disarm = "shoves" - var/response_harm = "hits" - var/harm_intent_damage = 3 - var/force_threshold = 0 //Minimum force required to deal any damage - - //Temperature effect - var/minbodytemp = 250 - var/maxbodytemp = 350 - - //Healable by medical stacks? Defaults to yes. - var/healable = 1 - - //Atmos effect - Yes, you can make creatures that require plasma or co2 to survive. N2O is a trace gas and handled separately, hence why it isn't here. It'd be hard to add it. Hard and me don't mix (Yes, yes make all the dick jokes you want with that.) - Errorage - var/list/atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0) //Leaving something at 0 means it's off - has no maximum - var/unsuitable_atmos_damage = 2 //This damage is taken when atmos doesn't fit all the requirements above - - //LETTING SIMPLE ANIMALS ATTACK? WHAT COULD GO WRONG. Defaults to zero so Ian can still be cuddly - var/melee_damage_lower = 0 - var/melee_damage_upper = 0 - var/armour_penetration = 0 //How much armour they ignore, as a flat reduction from the targets armour value - var/melee_damage_type = BRUTE //Damage type of a simple mob's melee attack, should it do damage. - var/list/damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, CLONE = 1, STAMINA = 0, OXY = 1) // 1 for full damage , 0 for none , -1 for 1:1 heal from that source - var/attacktext = "attacks" - var/attack_sound = null - var/friendly = "nuzzles" //If the mob does no damage with it's attack - var/environment_smash = 0 //Set to 1 to allow breaking of crates,lockers,racks,tables; 2 for walls; 3 for Rwalls - - var/speed = 1 //LETS SEE IF I CAN SET SPEEDS FOR SIMPLE MOBS WITHOUT DESTROYING EVERYTHING. Higher speed is slower, negative speed is faster - - //Hot simple_animal baby making vars - var/childtype = null - var/scan_ready = 1 - var/species //Sorry, no spider+corgi buttbabies. - - //simple_animal access - var/obj/item/weapon/card/id/access_card = null //innate access uses an internal ID card - var/flying = 0 //whether it's flying or touching the ground. - - var/buffed = 0 //In the event that you want to have a buffing effect on the mob, but don't want it to stack with other effects, any outside force that applies a buff to a simple mob should at least set this to 1, so we have something to check against - var/gold_core_spawnable = 0 //if 1 can be spawned by plasma with gold core, 2 are 'friendlies' spawned with blood - - var/mob/living/simple_animal/hostile/spawner/nest - - var/sentience_type = SENTIENCE_ORGANIC // Sentience type, for slime potions - -/mob/living/simple_animal/New() - ..() - verbs -= /mob/verb/observe - if(!real_name) - real_name = name - -/mob/living/simple_animal/Login() - if(src && src.client) - src.client.screen = list() - client.screen += client.void - ..() - -/mob/living/simple_animal/updatehealth() - ..() - health = Clamp(health, 0, maxHealth) - -/mob/living/simple_animal/Life() - if(..()) //alive - if(!ckey) - handle_automated_movement() - handle_automated_action() - handle_automated_speech() - return 1 - -/mob/living/simple_animal/handle_regular_status_updates() - if(..()) //alive - if(health < 1) - death() - return 0 - return 1 - -/mob/living/simple_animal/handle_disabilities() - //Eyes - if(disabilities & BLIND || stat) - eye_blind = max(eye_blind, 1) - else - if(eye_blind) - eye_blind = 0 - if(eye_blurry) - eye_blurry = 0 - if(eye_stat) - eye_stat = 0 - - //Ears - if(disabilities & DEAF) - setEarDamage(-1, max(ear_deaf, 1)) - else if(ear_damage < 100) - setEarDamage(0, 0) - -/mob/living/simple_animal/handle_status_effects() - ..() - if(stuttering) - stuttering = 0 - - if(druggy) - druggy = 0 - -/mob/living/simple_animal/proc/handle_automated_action() - return - -/mob/living/simple_animal/proc/handle_automated_movement() - if(!stop_automated_movement && wander) - if(isturf(src.loc) && !resting && !buckled && canmove) //This is so it only moves if it's not inside a closet, gentics machine, etc. - turns_since_move++ - if(turns_since_move >= turns_per_move) - if(!(stop_automated_movement_when_pulled && pulledby)) //Some animals don't move when pulled - var/anydir = pick(cardinal) - if(Process_Spacemove(anydir)) - Move(get_step(src, anydir), anydir) - turns_since_move = 0 - return 1 - -/mob/living/simple_animal/proc/handle_automated_speech() - if(speak_chance) - if(rand(0,200) < speak_chance) - if(speak && speak.len) - if((emote_hear && emote_hear.len) || (emote_see && emote_see.len)) - var/length = speak.len - if(emote_hear && emote_hear.len) - length += emote_hear.len - if(emote_see && emote_see.len) - length += emote_see.len - var/randomValue = rand(1,length) - if(randomValue <= speak.len) - say(pick(speak)) - else - randomValue -= speak.len - if(emote_see && randomValue <= emote_see.len) - emote("me", 1, pick(emote_see)) - else - emote("me", 2, pick(emote_hear)) - else - say(pick(speak)) - else - if(!(emote_hear && emote_hear.len) && (emote_see && emote_see.len)) - emote("me", 1, pick(emote_see)) - if((emote_hear && emote_hear.len) && !(emote_see && emote_see.len)) - emote("me", 2, pick(emote_hear)) - if((emote_hear && emote_hear.len) && (emote_see && emote_see.len)) - var/length = emote_hear.len + emote_see.len - var/pick = rand(1,length) - if(pick <= emote_see.len) - emote("me", 1, pick(emote_see)) - else - emote("me", 2, pick(emote_hear)) - - -/mob/living/simple_animal/handle_environment(datum/gas_mixture/environment) - var/atmos_suitable = 1 - - var/atom/A = src.loc - if(isturf(A)) - var/turf/T = A - var/areatemp = get_temperature(environment) - 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 - - if(atmos_requirements["min_oxy"] && oxy < atmos_requirements["min_oxy"]) - atmos_suitable = 0 - else if(atmos_requirements["max_oxy"] && oxy > atmos_requirements["max_oxy"]) - atmos_suitable = 0 - else if(atmos_requirements["min_tox"] && tox < atmos_requirements["min_tox"]) - atmos_suitable = 0 - else if(atmos_requirements["max_tox"] && tox > atmos_requirements["max_tox"]) - atmos_suitable = 0 - else if(atmos_requirements["min_n2"] && n2 < atmos_requirements["min_n2"]) - atmos_suitable = 0 - else if(atmos_requirements["max_n2"] && n2 > atmos_requirements["max_n2"]) - atmos_suitable = 0 - else if(atmos_requirements["min_co2"] && co2 < atmos_requirements["min_co2"]) - atmos_suitable = 0 - else if(atmos_requirements["max_co2"] && co2 > atmos_requirements["max_co2"]) - atmos_suitable = 0 - - if(!atmos_suitable) - adjustBruteLoss(unsuitable_atmos_damage) - - else - if(atmos_requirements["min_oxy"] || atmos_requirements["min_tox"] || atmos_requirements["min_n2"] || atmos_requirements["min_co2"]) - adjustBruteLoss(unsuitable_atmos_damage) - - handle_temperature_damage() - -/mob/living/simple_animal/proc/handle_temperature_damage() - if(bodytemperature < minbodytemp) - adjustBruteLoss(2) - else if(bodytemperature > maxbodytemp) - adjustBruteLoss(3) - -/mob/living/simple_animal/gib(animation = 0) - if(icon_gib) - flick(icon_gib, src) - if(butcher_results) - for(var/path in butcher_results) - for(var/i = 1; i <= butcher_results[path];i++) - new path(src.loc) - ..() - - -/mob/living/simple_animal/blob_act() - adjustBruteLoss(20) - return - -/mob/living/simple_animal/say_quote(input) - var/ending = copytext(input, length(input)) - if(speak_emote && speak_emote.len && ending != "?" && ending != "!") - var/emote = pick(speak_emote) - if(emote) - return "[emote], \"[input]\"" - return ..() - -/mob/living/simple_animal/emote(act, m_type=1, message = null) - if(stat) - return - if(act == "scream") - message = "makes a loud and pained whimper" //ugly hack to stop animals screaming when crushed :P - act = "me" - ..(act, m_type, message) - -/mob/living/simple_animal/attack_animal(mob/living/simple_animal/M) - if(..()) - var/damage = rand(M.melee_damage_lower, M.melee_damage_upper) - attack_threshold_check(damage,M.melee_damage_type) - return 1 - -/mob/living/simple_animal/bullet_act(obj/item/projectile/Proj) - if(!Proj) - return - apply_damage(Proj.damage, Proj.damage_type) - Proj.on_hit(src) - return 0 - -/mob/living/simple_animal/adjustBruteLoss(amount) - if(damage_coeff[BRUTE]) - ..(amount*damage_coeff[BRUTE]) - -/mob/living/simple_animal/adjustFireLoss(amount) - if(damage_coeff[BURN]) - adjustBruteLoss(amount*damage_coeff[BURN]) - -/mob/living/simple_animal/adjustOxyLoss(amount) - if(damage_coeff[OXY]) - adjustBruteLoss(amount*damage_coeff[OXY]) - -/mob/living/simple_animal/adjustToxLoss(amount) - if(damage_coeff[TOX]) - ..(amount*damage_coeff[TOX]) - -/mob/living/simple_animal/adjustCloneLoss(amount) - if(damage_coeff[CLONE]) - ..(amount*damage_coeff[CLONE]) - -/mob/living/simple_animal/adjustStaminaLoss(amount) - return - -/mob/living/simple_animal/attack_hand(mob/living/carbon/human/M) - ..() - switch(M.a_intent) - - if("help") - if (health > 0) - visible_message("[M] [response_help] [src].") - playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - - if("grab") - grabbedby(M) - - if("harm", "disarm") - M.do_attack_animation(src) - visible_message("[M] [response_harm] [src]!") - playsound(loc, "punch", 25, 1, -1) - attack_threshold_check(harm_intent_damage) - add_logs(M, src, "attacked") - updatehealth() - return 1 - -/mob/living/simple_animal/attack_paw(mob/living/carbon/monkey/M) - if(..()) //successful monkey bite. - if(stat != DEAD) - var/damage = rand(1, 3) - attack_threshold_check(damage) - return 1 - if (M.a_intent == "help") - if (health > 0) - visible_message("[M.name] [response_help] [src].") - playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - - return - -/mob/living/simple_animal/attack_alien(mob/living/carbon/alien/humanoid/M) - if(..()) //if harm or disarm intent. - if(M.a_intent == "disarm") - playsound(loc, 'sound/weapons/pierce.ogg', 25, 1, -1) - visible_message("[M] [response_disarm] [name]!", \ - "[M] [response_disarm] [name]!") - add_logs(M, src, "disarmed") - else - var/damage = rand(15, 30) - visible_message("[M] has slashed at [src]!", \ - "[M] has slashed at [src]!") - playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1) - attack_threshold_check(damage) - add_logs(M, src, "attacked") - return 1 - -/mob/living/simple_animal/attack_larva(mob/living/carbon/alien/larva/L) - if(..()) //successful larva bite - var/damage = rand(5, 10) - if(stat != DEAD) - L.amount_grown = min(L.amount_grown + damage, L.max_grown) - attack_threshold_check(damage) - return 1 - -/mob/living/simple_animal/attack_slime(mob/living/simple_animal/slime/M) - if(..()) //successful slime attack - var/damage = rand(15, 25) - if(M.is_adult) - damage = rand(20, 35) - attack_threshold_check(damage) - return 1 - -/mob/living/simple_animal/proc/attack_threshold_check(damage, damagetype = BRUTE) - if(damage <= force_threshold || !damage_coeff[damagetype]) - visible_message("[src] looks unharmed.") - else - adjustBruteLoss(damage) - updatehealth() - - -/mob/living/simple_animal/attackby(obj/item/O, mob/living/user, params) //Marker -Agouri - if(O.flags & NOBLUDGEON) - return - - ..() - -/mob/living/simple_animal/movement_delay() - . = ..() - - . = speed - - . += config.animal_delay - -/mob/living/simple_animal/Stat() - ..() - - if(statpanel("Status")) - stat(null, "Health: [round((health / maxHealth) * 100)]%") - return 1 - -/mob/living/simple_animal/death(gibbed) - health = 0 - icon_state = icon_dead - stat = DEAD - density = 0 - if(!gibbed) - visible_message("\the [src] stops moving...") - ..() - -/mob/living/simple_animal/ex_act(severity, target) - ..() - switch (severity) - if (1) - gib() - return - - if (2) - adjustBruteLoss(60) - - if(3) - adjustBruteLoss(30) - updatehealth() - -/mob/living/simple_animal/proc/CanAttack(atom/the_target) - if(see_invisible < the_target.invisibility) - return 0 - if (isliving(the_target)) - var/mob/living/L = the_target - if(L.stat != CONSCIOUS) - return 0 - if (istype(the_target, /obj/mecha)) - var/obj/mecha/M = the_target - if (M.occupant) - return 0 - return 1 - -/mob/living/simple_animal/handle_fire() - return - -/mob/living/simple_animal/update_fire() - return -/mob/living/simple_animal/IgniteMob() - return -/mob/living/simple_animal/ExtinguishMob() - return - -/mob/living/simple_animal/revive() - health = maxHealth - icon = initial(icon) - icon_state = icon_living - density = initial(density) - update_canmove() - ..() - -/mob/living/simple_animal/proc/make_babies() // <3 <3 <3 - if(gender != FEMALE || stat || !scan_ready || !childtype || !species) - return - scan_ready = 0 - spawn(400) - scan_ready = 1 - var/alone = 1 - var/mob/living/simple_animal/partner - var/children = 0 - for(var/mob/M in oview(7, src)) - if(M.stat != CONSCIOUS) //Check if it's concious FIRSTER. - continue - else if(istype(M, childtype)) //Check for children FIRST. - children++ - else if(istype(M, species)) - if(M.ckey) - continue - else if(!istype(M, childtype) && M.gender == MALE) //Better safe than sorry ;_; - partner = M - else if(istype(M, /mob/)) - alone = 0 - continue - if(alone && partner && children < 3) - new childtype(loc) - -/mob/living/simple_animal/stripPanelUnequip(obj/item/what, mob/who, where, child_override) - if(!child_override) - src << "You don't have the dexterity to do this!" - return - else - ..() - -/mob/living/simple_animal/stripPanelEquip(obj/item/what, mob/who, where, child_override) - if(!child_override) - src << "You don't have the dexterity to do this!" - return - else - ..() - -/mob/living/simple_animal/update_canmove() - if(paralysis || stunned || weakened || stat || resting) - drop_r_hand() - drop_l_hand() - canmove = 0 - else if(buckled) - canmove = 0 - else - canmove = 1 - update_transform() - return canmove - -/mob/living/simple_animal/update_transform() - var/matrix/ntransform = matrix(transform) //aka transform.Copy() - var/changed = 0 - - if(resize != RESIZE_DEFAULT_SIZE) - changed++ - ntransform.Scale(resize) - resize = RESIZE_DEFAULT_SIZE - - if(changed) - animate(src, transform = ntransform, time = 2, easing = EASE_IN|EASE_OUT) - - - -/mob/living/simple_animal/Destroy() - if(nest) - nest.spawned_mobs -= src - nest = null - return ..() - - -/mob/living/simple_animal/proc/sentience_act() //Called when a simple animal gains sentience via gold slime potion +/mob/living/simple_animal + name = "animal" + icon = 'icons/mob/animal.dmi' + health = 20 + maxHealth = 20 + + status_flags = CANPUSH + + var/icon_living = "" + var/icon_dead = "" //icon when the animal is dead. Don't use animated icons for this. + var/icon_gib = null //We only try to show a gibbing animation if this exists. + + var/list/speak = list() + var/list/speak_emote = list()// Emotes while speaking IE: Ian [emote], [text] -- Ian barks, "WOOF!". Spoken text is generated from the speak variable. + var/speak_chance = 0 + var/list/emote_hear = list() //Hearable emotes + var/list/emote_see = list() //Unlike speak_emote, the list of things in this variable only show by themselves with no spoken text. IE: Ian barks, Ian yaps + + var/turns_per_move = 1 + var/turns_since_move = 0 + var/stop_automated_movement = 0 //Use this to temporarely stop random movement or to if you write special movement code for animals. + var/wander = 1 // Does the mob wander around when idle? + var/stop_automated_movement_when_pulled = 1 //When set to 1 this stops the animal from moving when someone is pulling it. + + //Interaction + var/response_help = "pokes" + var/response_disarm = "shoves" + var/response_harm = "hits" + var/harm_intent_damage = 3 + var/force_threshold = 0 //Minimum force required to deal any damage + + //Temperature effect + var/minbodytemp = 250 + var/maxbodytemp = 350 + + //Healable by medical stacks? Defaults to yes. + var/healable = 1 + + //Atmos effect - Yes, you can make creatures that require plasma or co2 to survive. N2O is a trace gas and handled separately, hence why it isn't here. It'd be hard to add it. Hard and me don't mix (Yes, yes make all the dick jokes you want with that.) - Errorage + var/list/atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0) //Leaving something at 0 means it's off - has no maximum + var/unsuitable_atmos_damage = 2 //This damage is taken when atmos doesn't fit all the requirements above + + //LETTING SIMPLE ANIMALS ATTACK? WHAT COULD GO WRONG. Defaults to zero so Ian can still be cuddly + var/melee_damage_lower = 0 + var/melee_damage_upper = 0 + var/armour_penetration = 0 //How much armour they ignore, as a flat reduction from the targets armour value + var/melee_damage_type = BRUTE //Damage type of a simple mob's melee attack, should it do damage. + var/list/damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, CLONE = 1, STAMINA = 0, OXY = 1) // 1 for full damage , 0 for none , -1 for 1:1 heal from that source + var/attacktext = "attacks" + var/attack_sound = null + var/friendly = "nuzzles" //If the mob does no damage with it's attack + var/environment_smash = 0 //Set to 1 to allow breaking of crates,lockers,racks,tables; 2 for walls; 3 for Rwalls + + var/speed = 1 //LETS SEE IF I CAN SET SPEEDS FOR SIMPLE MOBS WITHOUT DESTROYING EVERYTHING. Higher speed is slower, negative speed is faster + + //Hot simple_animal baby making vars + var/childtype = null + var/scan_ready = 1 + var/species //Sorry, no spider+corgi buttbabies. + + //simple_animal access + var/obj/item/weapon/card/id/access_card = null //innate access uses an internal ID card + var/flying = 0 //whether it's flying or touching the ground. + + var/buffed = 0 //In the event that you want to have a buffing effect on the mob, but don't want it to stack with other effects, any outside force that applies a buff to a simple mob should at least set this to 1, so we have something to check against + var/gold_core_spawnable = 0 //if 1 can be spawned by plasma with gold core, 2 are 'friendlies' spawned with blood + + var/mob/living/simple_animal/hostile/spawner/nest + + var/sentience_type = SENTIENCE_ORGANIC // Sentience type, for slime potions + +/mob/living/simple_animal/New() + ..() + verbs -= /mob/verb/observe + if(!real_name) + real_name = name + +/mob/living/simple_animal/Login() + if(src && src.client) + src.client.screen = list() + client.screen += client.void + ..() + +/mob/living/simple_animal/updatehealth() + ..() + health = Clamp(health, 0, maxHealth) + +/mob/living/simple_animal/Life() + if(..()) //alive + if(!ckey) + handle_automated_movement() + handle_automated_action() + handle_automated_speech() + return 1 + +/mob/living/simple_animal/handle_regular_status_updates() + if(..()) //alive + if(health < 1) + death() + return 0 + return 1 + +/mob/living/simple_animal/handle_disabilities() + //Eyes + if(disabilities & BLIND || stat) + eye_blind = max(eye_blind, 1) + else + if(eye_blind) + eye_blind = 0 + if(eye_blurry) + eye_blurry = 0 + if(eye_stat) + eye_stat = 0 + + //Ears + if(disabilities & DEAF) + setEarDamage(-1, max(ear_deaf, 1)) + else if(ear_damage < 100) + setEarDamage(0, 0) + +/mob/living/simple_animal/handle_status_effects() + ..() + if(stuttering) + stuttering = 0 + + if(druggy) + druggy = 0 + +/mob/living/simple_animal/proc/handle_automated_action() + return + +/mob/living/simple_animal/proc/handle_automated_movement() + if(!stop_automated_movement && wander) + if(isturf(src.loc) && !resting && !buckled && canmove) //This is so it only moves if it's not inside a closet, gentics machine, etc. + turns_since_move++ + if(turns_since_move >= turns_per_move) + if(!(stop_automated_movement_when_pulled && pulledby)) //Some animals don't move when pulled + var/anydir = pick(cardinal) + if(Process_Spacemove(anydir)) + Move(get_step(src, anydir), anydir) + turns_since_move = 0 + return 1 + +/mob/living/simple_animal/proc/handle_automated_speech() + if(speak_chance) + if(rand(0,200) < speak_chance) + if(speak && speak.len) + if((emote_hear && emote_hear.len) || (emote_see && emote_see.len)) + var/length = speak.len + if(emote_hear && emote_hear.len) + length += emote_hear.len + if(emote_see && emote_see.len) + length += emote_see.len + var/randomValue = rand(1,length) + if(randomValue <= speak.len) + say(pick(speak)) + else + randomValue -= speak.len + if(emote_see && randomValue <= emote_see.len) + emote("me", 1, pick(emote_see)) + else + emote("me", 2, pick(emote_hear)) + else + say(pick(speak)) + else + if(!(emote_hear && emote_hear.len) && (emote_see && emote_see.len)) + emote("me", 1, pick(emote_see)) + if((emote_hear && emote_hear.len) && !(emote_see && emote_see.len)) + emote("me", 2, pick(emote_hear)) + if((emote_hear && emote_hear.len) && (emote_see && emote_see.len)) + var/length = emote_hear.len + emote_see.len + var/pick = rand(1,length) + if(pick <= emote_see.len) + emote("me", 1, pick(emote_see)) + else + emote("me", 2, pick(emote_hear)) + + +/mob/living/simple_animal/handle_environment(datum/gas_mixture/environment) + var/atmos_suitable = 1 + + var/atom/A = src.loc + if(isturf(A)) + var/turf/T = A + var/areatemp = get_temperature(environment) + 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 + + if(atmos_requirements["min_oxy"] && oxy < atmos_requirements["min_oxy"]) + atmos_suitable = 0 + else if(atmos_requirements["max_oxy"] && oxy > atmos_requirements["max_oxy"]) + atmos_suitable = 0 + else if(atmos_requirements["min_tox"] && tox < atmos_requirements["min_tox"]) + atmos_suitable = 0 + else if(atmos_requirements["max_tox"] && tox > atmos_requirements["max_tox"]) + atmos_suitable = 0 + else if(atmos_requirements["min_n2"] && n2 < atmos_requirements["min_n2"]) + atmos_suitable = 0 + else if(atmos_requirements["max_n2"] && n2 > atmos_requirements["max_n2"]) + atmos_suitable = 0 + else if(atmos_requirements["min_co2"] && co2 < atmos_requirements["min_co2"]) + atmos_suitable = 0 + else if(atmos_requirements["max_co2"] && co2 > atmos_requirements["max_co2"]) + atmos_suitable = 0 + + if(!atmos_suitable) + adjustBruteLoss(unsuitable_atmos_damage) + + else + if(atmos_requirements["min_oxy"] || atmos_requirements["min_tox"] || atmos_requirements["min_n2"] || atmos_requirements["min_co2"]) + adjustBruteLoss(unsuitable_atmos_damage) + + handle_temperature_damage() + +/mob/living/simple_animal/proc/handle_temperature_damage() + if(bodytemperature < minbodytemp) + adjustBruteLoss(2) + else if(bodytemperature > maxbodytemp) + adjustBruteLoss(3) + +/mob/living/simple_animal/gib(animation = 0) + if(icon_gib) + flick(icon_gib, src) + if(butcher_results) + for(var/path in butcher_results) + for(var/i = 1; i <= butcher_results[path];i++) + new path(src.loc) + ..() + + +/mob/living/simple_animal/blob_act() + adjustBruteLoss(20) + return + +/mob/living/simple_animal/say_quote(input) + var/ending = copytext(input, length(input)) + if(speak_emote && speak_emote.len && ending != "?" && ending != "!") + var/emote = pick(speak_emote) + if(emote) + return "[emote], \"[input]\"" + return ..() + +/mob/living/simple_animal/emote(act, m_type=1, message = null) + if(stat) + return + if(act == "scream") + message = "makes a loud and pained whimper" //ugly hack to stop animals screaming when crushed :P + act = "me" + ..(act, m_type, message) + +/mob/living/simple_animal/attack_animal(mob/living/simple_animal/M) + if(..()) + var/damage = rand(M.melee_damage_lower, M.melee_damage_upper) + attack_threshold_check(damage,M.melee_damage_type) + return 1 + +/mob/living/simple_animal/bullet_act(obj/item/projectile/Proj) + if(!Proj) + return + apply_damage(Proj.damage, Proj.damage_type) + Proj.on_hit(src) + return 0 + +/mob/living/simple_animal/adjustBruteLoss(amount) + if(damage_coeff[BRUTE]) + ..(amount*damage_coeff[BRUTE]) + +/mob/living/simple_animal/adjustFireLoss(amount) + if(damage_coeff[BURN]) + adjustBruteLoss(amount*damage_coeff[BURN]) + +/mob/living/simple_animal/adjustOxyLoss(amount) + if(damage_coeff[OXY]) + adjustBruteLoss(amount*damage_coeff[OXY]) + +/mob/living/simple_animal/adjustToxLoss(amount) + if(damage_coeff[TOX]) + ..(amount*damage_coeff[TOX]) + +/mob/living/simple_animal/adjustCloneLoss(amount) + if(damage_coeff[CLONE]) + ..(amount*damage_coeff[CLONE]) + +/mob/living/simple_animal/adjustStaminaLoss(amount) + return + +/mob/living/simple_animal/attack_hand(mob/living/carbon/human/M) + ..() + switch(M.a_intent) + + if("help") + if (health > 0) + visible_message("[M] [response_help] [src].") + playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) + + if("grab") + grabbedby(M) + + if("harm", "disarm") + M.do_attack_animation(src) + visible_message("[M] [response_harm] [src]!") + playsound(loc, "punch", 25, 1, -1) + attack_threshold_check(harm_intent_damage) + add_logs(M, src, "attacked") + updatehealth() + return 1 + +/mob/living/simple_animal/attack_paw(mob/living/carbon/monkey/M) + if(..()) //successful monkey bite. + if(stat != DEAD) + var/damage = rand(1, 3) + attack_threshold_check(damage) + return 1 + if (M.a_intent == "help") + if (health > 0) + visible_message("[M.name] [response_help] [src].") + playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) + + return + +/mob/living/simple_animal/attack_alien(mob/living/carbon/alien/humanoid/M) + if(..()) //if harm or disarm intent. + if(M.a_intent == "disarm") + playsound(loc, 'sound/weapons/pierce.ogg', 25, 1, -1) + visible_message("[M] [response_disarm] [name]!", \ + "[M] [response_disarm] [name]!") + add_logs(M, src, "disarmed") + else + var/damage = rand(15, 30) + visible_message("[M] has slashed at [src]!", \ + "[M] has slashed at [src]!") + playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1) + attack_threshold_check(damage) + add_logs(M, src, "attacked") + return 1 + +/mob/living/simple_animal/attack_larva(mob/living/carbon/alien/larva/L) + if(..()) //successful larva bite + var/damage = rand(5, 10) + if(stat != DEAD) + L.amount_grown = min(L.amount_grown + damage, L.max_grown) + attack_threshold_check(damage) + return 1 + +/mob/living/simple_animal/attack_slime(mob/living/simple_animal/slime/M) + if(..()) //successful slime attack + var/damage = rand(15, 25) + if(M.is_adult) + damage = rand(20, 35) + attack_threshold_check(damage) + return 1 + +/mob/living/simple_animal/proc/attack_threshold_check(damage, damagetype = BRUTE) + if(damage <= force_threshold || !damage_coeff[damagetype]) + visible_message("[src] looks unharmed.") + else + adjustBruteLoss(damage) + updatehealth() + + +/mob/living/simple_animal/attackby(obj/item/O, mob/living/user, params) //Marker -Agouri + if(O.flags & NOBLUDGEON) + return + + ..() + +/mob/living/simple_animal/movement_delay() + . = ..() + + . = speed + + . += config.animal_delay + +/mob/living/simple_animal/Stat() + ..() + + if(statpanel("Status")) + stat(null, "Health: [round((health / maxHealth) * 100)]%") + return 1 + +/mob/living/simple_animal/death(gibbed) + health = 0 + icon_state = icon_dead + stat = DEAD + density = 0 + if(!gibbed) + visible_message("\the [src] stops moving...") + ..() + +/mob/living/simple_animal/ex_act(severity, target) + ..() + switch (severity) + if (1) + gib() + return + + if (2) + adjustBruteLoss(60) + + if(3) + adjustBruteLoss(30) + updatehealth() + +/mob/living/simple_animal/proc/CanAttack(atom/the_target) + if(see_invisible < the_target.invisibility) + return 0 + if (isliving(the_target)) + var/mob/living/L = the_target + if(L.stat != CONSCIOUS) + return 0 + if (istype(the_target, /obj/mecha)) + var/obj/mecha/M = the_target + if (M.occupant) + return 0 + return 1 + +/mob/living/simple_animal/handle_fire() + return + +/mob/living/simple_animal/update_fire() + return +/mob/living/simple_animal/IgniteMob() + return +/mob/living/simple_animal/ExtinguishMob() + return + +/mob/living/simple_animal/revive() + health = maxHealth + icon = initial(icon) + icon_state = icon_living + density = initial(density) + update_canmove() + ..() + +/mob/living/simple_animal/proc/make_babies() // <3 <3 <3 + if(gender != FEMALE || stat || !scan_ready || !childtype || !species) + return + scan_ready = 0 + spawn(400) + scan_ready = 1 + var/alone = 1 + var/mob/living/simple_animal/partner + var/children = 0 + for(var/mob/M in oview(7, src)) + if(M.stat != CONSCIOUS) //Check if it's concious FIRSTER. + continue + else if(istype(M, childtype)) //Check for children FIRST. + children++ + else if(istype(M, species)) + if(M.ckey) + continue + else if(!istype(M, childtype) && M.gender == MALE) //Better safe than sorry ;_; + partner = M + else if(istype(M, /mob/)) + alone = 0 + continue + if(alone && partner && children < 3) + new childtype(loc) + +/mob/living/simple_animal/stripPanelUnequip(obj/item/what, mob/who, where, child_override) + if(!child_override) + src << "You don't have the dexterity to do this!" + return + else + ..() + +/mob/living/simple_animal/stripPanelEquip(obj/item/what, mob/who, where, child_override) + if(!child_override) + src << "You don't have the dexterity to do this!" + return + else + ..() + +/mob/living/simple_animal/update_canmove() + if(paralysis || stunned || weakened || stat || resting) + drop_r_hand() + drop_l_hand() + canmove = 0 + else if(buckled) + canmove = 0 + else + canmove = 1 + update_transform() + return canmove + +/mob/living/simple_animal/update_transform() + var/matrix/ntransform = matrix(transform) //aka transform.Copy() + var/changed = 0 + + if(resize != RESIZE_DEFAULT_SIZE) + changed++ + ntransform.Scale(resize) + resize = RESIZE_DEFAULT_SIZE + + if(changed) + animate(src, transform = ntransform, time = 2, easing = EASE_IN|EASE_OUT) + + + +/mob/living/simple_animal/Destroy() + if(nest) + nest.spawned_mobs -= src + nest = null + return ..() + + +/mob/living/simple_animal/proc/sentience_act() //Called when a simple animal gains sentience via gold slime potion return \ No newline at end of file diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index e09d129d9a3..43a04102eff 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -1,154 +1,154 @@ -/mob - density = 1 - layer = 4 - animate_movement = 2 - flags = HEAR - hud_possible = list(ANTAG_HUD) - pressure_resistance = 8 - var/datum/mind/mind - var/list/datum/action/actions = list() - - var/stat = 0 //Whether a mob is alive or dead. TODO: Move this to living - Nodrak - - var/obj/screen/flash = null - var/obj/screen/blind = null - var/obj/screen/hands = null - var/obj/screen/pullin = null - var/obj/screen/internals = null - var/obj/screen/i_select = null - var/obj/screen/m_select = null - var/obj/screen/healths = null - var/obj/screen/throw_icon = null - var/obj/screen/damageoverlay = null - /*A bunch of this stuff really needs to go under their own defines instead of being globally attached to mob. - A variable should only be globally attached to turfs/objects/whatever, when it is in fact needed as such. - The current method unnecessarily clusters up the variable list, especially for humans (although rearranging won't really clean it up a lot but the difference will be noticable for other mobs). - I'll make some notes on where certain variable defines should probably go. - Changing this around would probably require a good look-over the pre-existing code. - */ - var/obj/screen/zone_sel/zone_sel = null - var/obj/screen/leap_icon = null - var/obj/screen/healthdoll = null - - var/damageoverlaytemp = 0 - var/computer_id = null - var/lastattacker = null - var/lastattacked = null - var/attack_log = list( ) - var/obj/machinery/machine = null - var/other_mobs = null - var/memory = "" - var/disabilities = 0 //Carbon - var/atom/movable/pulling = null - var/next_move = null - var/notransform = null //Carbon - var/hand = null - var/eye_blind = 0 //Carbon - var/eye_blurry = 0 //Carbon - var/ear_deaf = 0 //Carbon - var/ear_damage = 0 //Carbon - var/stuttering = null //Carbon - var/slurring = 0 //Carbon - var/real_name = null - var/druggy = 0 //Carbon - var/confused = 0 //Carbon - var/sleeping = 0 //Carbon - var/resting = 0 //Carbon - var/lying = 0 - var/lying_prev = 0 - var/canmove = 1 - var/eye_stat = null//Living, potentially Carbon - var/lastpuke = 0 - - var/name_archive //For admin things like possession - - var/timeofdeath = 0//Living - var/cpr_time = 1//Carbon - - - var/bodytemperature = 310.055 //98.7 F - var/drowsyness = 0//Carbon - var/dizziness = 0//Carbon - var/jitteriness = 0//Carbon - var/nutrition = NUTRITION_LEVEL_FED + 50//Carbon - var/satiety = 0//Carbon - - var/overeatduration = 0 // How long this guy is overeating //Carbon - var/paralysis = 0 - var/stunned = 0 - var/weakened = 0 - var/losebreath = 0//Carbon - var/shakecamera = 0 - var/a_intent = "help"//Living - var/m_intent = "run"//Living - var/lastKnownIP = null - var/atom/movable/buckled = null//Living - var/obj/item/l_hand = null//Living - var/obj/item/r_hand = null//Living - var/obj/item/weapon/storage/s_active = null//Carbon - - var/see_override = 0 //0 for no override, sets see_invisible = see_override in mob life process - - var/datum/hud/hud_used = null - - var/research_scanner = 0 //For research scanner equipped mobs. Enable to show research data when examining. - var/datum/action/innate/scan_mode/scanner = new - - var/list/grabbed_by = list( ) - var/list/requests = list( ) - - var/list/mapobjs = list() - - var/in_throw_mode = 0 - - var/music_lastplayed = "null" - - var/job = null//Living - - var/radiation = 0//Carbon - - var/voice_name = "unidentifiable voice" - - var/list/faction = list("neutral") //A list of factions that this mob is currently in, for hostile mob targetting, amongst other things - var/move_on_shuttle = 1 // Can move on the shuttle. - -//The last mob/living/carbon to push/drag/grab this mob (mostly used by slimes friend recognition) - var/mob/living/carbon/LAssailant = null - - - var/list/mob_spell_list = list() //construct spells and mime spells. Spells that do not transfer from one mob to another and can not be lost in mindswap. - -//Changlings, but can be used in other modes -// var/obj/effect/proc_holder/changpower/list/power_list = list() - -//List of active diseases - - var/list/viruses = list() // replaces var/datum/disease/virus - var/list/resistances = list() - - mouse_drag_pointer = MOUSE_ACTIVE_POINTER - - - var/status_flags = CANSTUN|CANWEAKEN|CANPARALYSE|CANPUSH //bitflags defining which status effects can be inflicted (replaces canweaken, canstun, etc) - - var/digitalcamo = 0 // Can they be tracked by the AI? - var/digitalinvis = 0 //Are they ivisible to the AI? - var/image/digitaldisguise = null //what does the AI see instead of them? - - var/weakeyes = 0 //Are they vulnerable to flashes? - - var/has_unlimited_silicon_privilege = 0 // Can they interact with station electronics - - var/force_compose = 0 //If this is nonzero, the mob will always compose it's own hear message instead of using the one given in the arguments. - - var/obj/control_object //Used by admins to possess objects. All mobs should have this var - var/atom/movable/remote_control //Calls relaymove() to whatever it is - - var/remote_view = 0 // Set to 1 to prevent view resets on Life - - var/turf/listed_turf = null //the current turf being examined in the stat panel - - var/list/permanent_huds = list() - var/permanent_sight_flags = 0 - +/mob + density = 1 + layer = 4 + animate_movement = 2 + flags = HEAR + hud_possible = list(ANTAG_HUD) + pressure_resistance = 8 + var/datum/mind/mind + var/list/datum/action/actions = list() + + var/stat = 0 //Whether a mob is alive or dead. TODO: Move this to living - Nodrak + + var/obj/screen/flash = null + var/obj/screen/blind = null + var/obj/screen/hands = null + var/obj/screen/pullin = null + var/obj/screen/internals = null + var/obj/screen/i_select = null + var/obj/screen/m_select = null + var/obj/screen/healths = null + var/obj/screen/throw_icon = null + var/obj/screen/damageoverlay = null + /*A bunch of this stuff really needs to go under their own defines instead of being globally attached to mob. + A variable should only be globally attached to turfs/objects/whatever, when it is in fact needed as such. + The current method unnecessarily clusters up the variable list, especially for humans (although rearranging won't really clean it up a lot but the difference will be noticable for other mobs). + I'll make some notes on where certain variable defines should probably go. + Changing this around would probably require a good look-over the pre-existing code. + */ + var/obj/screen/zone_sel/zone_sel = null + var/obj/screen/leap_icon = null + var/obj/screen/healthdoll = null + + var/damageoverlaytemp = 0 + var/computer_id = null + var/lastattacker = null + var/lastattacked = null + var/attack_log = list( ) + var/obj/machinery/machine = null + var/other_mobs = null + var/memory = "" + var/disabilities = 0 //Carbon + var/atom/movable/pulling = null + var/next_move = null + var/notransform = null //Carbon + var/hand = null + var/eye_blind = 0 //Carbon + var/eye_blurry = 0 //Carbon + var/ear_deaf = 0 //Carbon + var/ear_damage = 0 //Carbon + var/stuttering = null //Carbon + var/slurring = 0 //Carbon + var/real_name = null + var/druggy = 0 //Carbon + var/confused = 0 //Carbon + var/sleeping = 0 //Carbon + var/resting = 0 //Carbon + var/lying = 0 + var/lying_prev = 0 + var/canmove = 1 + var/eye_stat = null//Living, potentially Carbon + var/lastpuke = 0 + + var/name_archive //For admin things like possession + + var/timeofdeath = 0//Living + var/cpr_time = 1//Carbon + + + var/bodytemperature = 310.055 //98.7 F + var/drowsyness = 0//Carbon + var/dizziness = 0//Carbon + var/jitteriness = 0//Carbon + var/nutrition = NUTRITION_LEVEL_FED + 50//Carbon + var/satiety = 0//Carbon + + var/overeatduration = 0 // How long this guy is overeating //Carbon + var/paralysis = 0 + var/stunned = 0 + var/weakened = 0 + var/losebreath = 0//Carbon + var/shakecamera = 0 + var/a_intent = "help"//Living + var/m_intent = "run"//Living + var/lastKnownIP = null + var/atom/movable/buckled = null//Living + var/obj/item/l_hand = null//Living + var/obj/item/r_hand = null//Living + var/obj/item/weapon/storage/s_active = null//Carbon + + var/see_override = 0 //0 for no override, sets see_invisible = see_override in mob life process + + var/datum/hud/hud_used = null + + var/research_scanner = 0 //For research scanner equipped mobs. Enable to show research data when examining. + var/datum/action/innate/scan_mode/scanner = new + + var/list/grabbed_by = list( ) + var/list/requests = list( ) + + var/list/mapobjs = list() + + var/in_throw_mode = 0 + + var/music_lastplayed = "null" + + var/job = null//Living + + var/radiation = 0//Carbon + + var/voice_name = "unidentifiable voice" + + var/list/faction = list("neutral") //A list of factions that this mob is currently in, for hostile mob targetting, amongst other things + var/move_on_shuttle = 1 // Can move on the shuttle. + +//The last mob/living/carbon to push/drag/grab this mob (mostly used by slimes friend recognition) + var/mob/living/carbon/LAssailant = null + + + var/list/mob_spell_list = list() //construct spells and mime spells. Spells that do not transfer from one mob to another and can not be lost in mindswap. + +//Changlings, but can be used in other modes +// var/obj/effect/proc_holder/changpower/list/power_list = list() + +//List of active diseases + + var/list/viruses = list() // replaces var/datum/disease/virus + var/list/resistances = list() + + mouse_drag_pointer = MOUSE_ACTIVE_POINTER + + + var/status_flags = CANSTUN|CANWEAKEN|CANPARALYSE|CANPUSH //bitflags defining which status effects can be inflicted (replaces canweaken, canstun, etc) + + var/digitalcamo = 0 // Can they be tracked by the AI? + var/digitalinvis = 0 //Are they ivisible to the AI? + var/image/digitaldisguise = null //what does the AI see instead of them? + + var/weakeyes = 0 //Are they vulnerable to flashes? + + var/has_unlimited_silicon_privilege = 0 // Can they interact with station electronics + + var/force_compose = 0 //If this is nonzero, the mob will always compose it's own hear message instead of using the one given in the arguments. + + var/obj/control_object //Used by admins to possess objects. All mobs should have this var + var/atom/movable/remote_control //Calls relaymove() to whatever it is + + var/remote_view = 0 // Set to 1 to prevent view resets on Life + + var/turf/listed_turf = null //the current turf being examined in the stat panel + + var/list/permanent_huds = list() + var/permanent_sight_flags = 0 + var/resize = 1 //Badminnery resize \ No newline at end of file diff --git a/code/modules/nano/nanoui.dm b/code/modules/nano/nanoui.dm index fa23b149b46..076d8baba53 100644 --- a/code/modules/nano/nanoui.dm +++ b/code/modules/nano/nanoui.dm @@ -1,405 +1,405 @@ - /** - * NanoUI - * - * Contains the NanoUI datum, and its procs. - * - * /tg/station user interface library - * thanks to baystation12 - * - * modified by neersighted - **/ - - /** - * NanoUI datum: - * - * Represents a NanoUI. - **/ -/datum/nanoui - var/mob/user // The mob who opened/is using the NanoUI. - var/atom/movable/src_object // The object which owns the NanoUI. - var/title // The title of te NanoUI. - var/ui_key // The ui_key of the NanoUI. This allows multiple UIs for one src_object. - var/window_id // The window_id for browse() and onclose(). - var/width = 0 // The window width. - var/height = 0 // The window height - var/window_options = list( // Extra options to winset(). - "focus" = 0, - "titlebar" = 1, - "can_resize" = 1, - "can_minimize" = 1, - "can_maximize" = 0, - "can_close" = 1, - "auto_format" = 0 - ) - var/atom/ref = null // An extra ref to call when the window is closed. - var/layout = "nanotrasen" // The layout to be used for this UI. - var/template // The template to be used for this UI. - var/auto_update = 1 // Update the NanoUI every MC tick. - var/list/initial_data // The data (and datastructure) used to initialize the NanoUI - var/status = NANO_INTERACTIVE // The status/visibility of the NanoUI. - var/datum/topic_state/state = null // Topic state used to determine status. Topic states are in interactions/. - var/datum/nanoui/master_ui // The parent NanoUI. - var/list/datum/nanoui/children = list() // Children of this NanoUI. - - /** - * public - * - * Create a new NanoUI. - * - * required user mob The mob who opened/is using the NanoUI. - * required src_object atom/movable The object which owns the NanoUI. - * required ui_key string The ui_key of the NanoUI. - * required template string The template to render the NanoUI content with. - * optional title string The title of the NanoUI. - * optional width int The window width. - * optional height int The window height. - * optional ref atom An extra ref to use when the window is closed. - * optional master_ui datum/nanoui The parent NanoUI. - * optional state datum/topic_state The state used to determine status. - * - * return datum/nanoui The requested NanoUI. - **/ -/datum/nanoui/New(mob/user, atom/movable/src_object, ui_key, template, \ - title = 0, width = 0, height = 0, \ - atom/ref = null, datum/nanoui/master_ui = null, \ - datum/topic_state/state = default_state) - src.user = user - src.src_object = src_object - src.ui_key = ui_key - src.window_id = "\ref[src_object]-[ui_key]" - - set_template(template) - - if (title) - src.title = sanitize(title) - if (width) - src.width = width - if (height) - src.height = height - - if (ref) - src.ref = ref - - src.master_ui = master_ui - if(master_ui) - master_ui.children += src - src.state = state - - var/datum/asset/assets = get_asset_datum(/datum/asset/simple/nanoui) - assets.send(user) - - - /** - * public - * - * Enable/disable auto-updating of the NanoUI. - * - * required state bool Enable/disable auto-updating. - **/ -/datum/nanoui/proc/set_auto_update(state = 1) - auto_update = state - - /** - * private - * - * Set the data to initialize the NanoUI with. - * The datastructure cannot be changed by subsequent updates. - * - * optional data list The data/datastructure to initialize the NanoUI with. - **/ -/datum/nanoui/proc/set_initial_data(list/data) - initial_data = data - - /** - * private - * - * Get the config data/datastructure to initialize the NanoUI with. - * - * return list The config data. - **/ -/datum/nanoui/proc/get_config_data() - var/list/config_data = list( - "title" = title, - "status" = status, - "ref" = "\ref[src]", - "window" = list( - "width" = width, - "height" = height, - "ref" = window_id - ), - "user" = list( - "name" = user.name, - "fancy" = user.client.prefs.nanoui_fancy, - "ref" = "\ref[user]" - ), - "srcObject" = list( - "name" = src_object.name, - "ref" = "\ref[src_object]" - ), - "templates" = list( - "layout" = "_[layout]", - "content" = "[template]" - ) - ) - return config_data - - /** - * private - * - * Package the data to send to the UI. - * This is the (regular) data and config data, bundled together. - * - * return list The packaged data. - **/ -/datum/nanoui/proc/get_send_data(list/data) - var/list/send_data = list() - - send_data["config"] = get_config_data() - if (!isnull(data)) - send_data["data"] = data - - return send_data - - /** - * public - * - * Sets the browse() window options for this NanoUI. - * - * required window_options list The window options to set. - **/ -/datum/nanoui/proc/set_window_options(list/window_options) - src.window_options = window_options - - /** - * public - * - * Set the layout for this NanoUI. - * This loads custom layout styles and templates for this NanoUI. - * - * required layout string The new UI layout. - **/ -/datum/nanoui/proc/set_layout(layout) - src.layout = lowertext(layout) - - /** - * public - * - * Set the template for this NanoUI. - * - * required template string The new UI template. - **/ -/datum/nanoui/proc/set_template(template) - src.template = lowertext(template) - - /** - * private - * - * Generate HTML for this NanoUI. - * - * return string NanoUI HTML output. - **/ -/datum/nanoui/proc/get_html() - // Generate - - - "} - var/stylesheet_html = {" - - - - - "} - - // Generate JSON. - var/list/send_data = get_send_data(initial_data) - var/initial_data_json = replacetext(JSON.stringify(send_data), "'", "\\'") - - // Generate the HTML document. - return {" - - - - - - [stylesheet_html] - [script_html] - - -
-
-
- Sending Resources... -
-
- - - - "} - - /** - * public - * - * Open this NanoUI (and initialize it with data). - * - * optional data list The data to intialize the UI with. - **/ -/datum/nanoui/proc/open(list/data = null) - if(!user.client) return - - if (!initial_data) - if (!data) // If we don't have initial_data and data was not passed, get data from the src_object. - data = src_object.get_ui_data(user) - set_initial_data(data) // Otherwise use the passed data. - - var/window_size = "" - if (width && height) // If we have a width and height, use them. - window_size = "size=[width]x[height];" - update_status(push = 0) // Update the window state. - if (status == NANO_CLOSE) - return // Bail if we should close. - - user << browse(get_html(), "window=[window_id];[window_size][list2params(window_options)]") // Open the window. - winset(user, window_id, "on-close=\"nanoclose \ref[src]\"") // Instruct the client to signal NanoUI when the window is closed. - SSnano.ui_opened(src) // Call the opened handler. - - /** - * public - * - * Reinitialize the NanoUI. - * (Possibly with a new template and/or data). - * - * optional template string The filename of the new template. - * optional data list The new initial data. - **/ -/datum/nanoui/proc/reinitialize(template, list/data) - if(template) - src.template = template // Set a new template. - if(data) - set_initial_data(data) // Replace the initial_data. - open() - - /** - * public - * - * Close the NanoUI, and all its children. - **/ -/datum/nanoui/proc/close() - set_auto_update(0) // Disable auto-updates. - user << browse(null, "window=[window_id]") // Close the window. - SSnano.ui_closed(src) // Call the closed handler. - for(var/datum/nanoui/child in children) // Loop through and close all children. - child.close() - - /** - * private - * - * Push data to an already open NanoUI. - * - * required data list The data to send. - * optional force bool If the update should be sent regardless of state. - **/ -/datum/nanoui/proc/push_data(data, force = 0) - update_status(push = 0) // Update the window state. - if (status == NANO_DISABLED && !force) - return // Cannot update UI, we have no visibility. - - var/list/send_data = get_send_data(data) // Get the data to send. - - // Send the new data to the recieveUpdate() Javascript function. - user << output(list2params(list(JSON.stringify(send_data))), "[window_id].browser:receiveUpdate") - - /** - * private - * - * Handle clicks from the NanoUI. - * Call the src_object's Topic() if status is NANO_INTERACTIVE. - * If the src_object's Topic() returns 1, update all UIs attacked to it. - **/ -/datum/nanoui/Topic(href, href_list) - update_status(push = 0) // Update the window state. - if (status != NANO_INTERACTIVE || user != usr) - return // If UI is not interactive or usr calling Topic is not the UI user. - - var/action = href_list["nano"] // Pull the action out. - href_list -= "nano" - - var/update = src_object.ui_act(action, href_list, state) // Call Topic() on the src_object. - - if (src_object && update) - SSnano.update_uis(src_object) // If we have a src_object and its Topic() told us to update. - - /** - * private - * - * Update the NanoUI. Only updates the contents/layout if update is true, - * otherwise only updates the status. - * - * optional force bool If the UI should be forced to update. - **/ -/datum/nanoui/process(force = 0) - if (!src_object || !user) // If the object or user died (or something else), abort. - close() - return - - if (status && (force || auto_update)) - update() // Update the UI if the status and update settings allow it. - else - update_status(push = 1) // Otherwise only update status. - - /** - * private - * - * Updates the UI by interacting with the src_object again, which will hopefully - * call try_ui_update on it. - * - * optional force_open bool If force_open should be passed to ui_interact. - **/ -/datum/nanoui/proc/update(force_open = 0) - src_object.ui_interact(user, ui_key, src, force_open, master_ui, state) - - /** - * private - * - * Set the status/visibility of the NanoUI. - * - * required state int The status to set (NANO_CLOSE/NANO_DISABLED/NANO_UPDATE/NANO_INTERACTIVE). - * optional push bool Push an update to the UI (an update is always sent for NANO_DISABLED). - **/ -/datum/nanoui/proc/set_status(state, push = 0) - if (state != status) // Only update if status has changed. - if (status == NANO_DISABLED) - status = state - if (push) - update() - else - status = state - if (push || status == 0) // Force an update if NANO_DISABLED. - push_data(null, 1) - - /** - * private - * - * Update the status/visibility of the NanoUI for its user. - * - * optional push bool Push an update to the UI (an update is always sent for NANO_DISABLED). - **/ -/datum/nanoui/proc/update_status(push = 0) - var/new_status = src_object.CanUseTopic(user, state) - if(master_ui) - new_status = min(new_status, master_ui.status) - - set_status(new_status, push) - if(new_status == NANO_CLOSE) + /** + * NanoUI + * + * Contains the NanoUI datum, and its procs. + * + * /tg/station user interface library + * thanks to baystation12 + * + * modified by neersighted + **/ + + /** + * NanoUI datum: + * + * Represents a NanoUI. + **/ +/datum/nanoui + var/mob/user // The mob who opened/is using the NanoUI. + var/atom/movable/src_object // The object which owns the NanoUI. + var/title // The title of te NanoUI. + var/ui_key // The ui_key of the NanoUI. This allows multiple UIs for one src_object. + var/window_id // The window_id for browse() and onclose(). + var/width = 0 // The window width. + var/height = 0 // The window height + var/window_options = list( // Extra options to winset(). + "focus" = 0, + "titlebar" = 1, + "can_resize" = 1, + "can_minimize" = 1, + "can_maximize" = 0, + "can_close" = 1, + "auto_format" = 0 + ) + var/atom/ref = null // An extra ref to call when the window is closed. + var/layout = "nanotrasen" // The layout to be used for this UI. + var/template // The template to be used for this UI. + var/auto_update = 1 // Update the NanoUI every MC tick. + var/list/initial_data // The data (and datastructure) used to initialize the NanoUI + var/status = NANO_INTERACTIVE // The status/visibility of the NanoUI. + var/datum/topic_state/state = null // Topic state used to determine status. Topic states are in interactions/. + var/datum/nanoui/master_ui // The parent NanoUI. + var/list/datum/nanoui/children = list() // Children of this NanoUI. + + /** + * public + * + * Create a new NanoUI. + * + * required user mob The mob who opened/is using the NanoUI. + * required src_object atom/movable The object which owns the NanoUI. + * required ui_key string The ui_key of the NanoUI. + * required template string The template to render the NanoUI content with. + * optional title string The title of the NanoUI. + * optional width int The window width. + * optional height int The window height. + * optional ref atom An extra ref to use when the window is closed. + * optional master_ui datum/nanoui The parent NanoUI. + * optional state datum/topic_state The state used to determine status. + * + * return datum/nanoui The requested NanoUI. + **/ +/datum/nanoui/New(mob/user, atom/movable/src_object, ui_key, template, \ + title = 0, width = 0, height = 0, \ + atom/ref = null, datum/nanoui/master_ui = null, \ + datum/topic_state/state = default_state) + src.user = user + src.src_object = src_object + src.ui_key = ui_key + src.window_id = "\ref[src_object]-[ui_key]" + + set_template(template) + + if (title) + src.title = sanitize(title) + if (width) + src.width = width + if (height) + src.height = height + + if (ref) + src.ref = ref + + src.master_ui = master_ui + if(master_ui) + master_ui.children += src + src.state = state + + var/datum/asset/assets = get_asset_datum(/datum/asset/simple/nanoui) + assets.send(user) + + + /** + * public + * + * Enable/disable auto-updating of the NanoUI. + * + * required state bool Enable/disable auto-updating. + **/ +/datum/nanoui/proc/set_auto_update(state = 1) + auto_update = state + + /** + * private + * + * Set the data to initialize the NanoUI with. + * The datastructure cannot be changed by subsequent updates. + * + * optional data list The data/datastructure to initialize the NanoUI with. + **/ +/datum/nanoui/proc/set_initial_data(list/data) + initial_data = data + + /** + * private + * + * Get the config data/datastructure to initialize the NanoUI with. + * + * return list The config data. + **/ +/datum/nanoui/proc/get_config_data() + var/list/config_data = list( + "title" = title, + "status" = status, + "ref" = "\ref[src]", + "window" = list( + "width" = width, + "height" = height, + "ref" = window_id + ), + "user" = list( + "name" = user.name, + "fancy" = user.client.prefs.nanoui_fancy, + "ref" = "\ref[user]" + ), + "srcObject" = list( + "name" = src_object.name, + "ref" = "\ref[src_object]" + ), + "templates" = list( + "layout" = "_[layout]", + "content" = "[template]" + ) + ) + return config_data + + /** + * private + * + * Package the data to send to the UI. + * This is the (regular) data and config data, bundled together. + * + * return list The packaged data. + **/ +/datum/nanoui/proc/get_send_data(list/data) + var/list/send_data = list() + + send_data["config"] = get_config_data() + if (!isnull(data)) + send_data["data"] = data + + return send_data + + /** + * public + * + * Sets the browse() window options for this NanoUI. + * + * required window_options list The window options to set. + **/ +/datum/nanoui/proc/set_window_options(list/window_options) + src.window_options = window_options + + /** + * public + * + * Set the layout for this NanoUI. + * This loads custom layout styles and templates for this NanoUI. + * + * required layout string The new UI layout. + **/ +/datum/nanoui/proc/set_layout(layout) + src.layout = lowertext(layout) + + /** + * public + * + * Set the template for this NanoUI. + * + * required template string The new UI template. + **/ +/datum/nanoui/proc/set_template(template) + src.template = lowertext(template) + + /** + * private + * + * Generate HTML for this NanoUI. + * + * return string NanoUI HTML output. + **/ +/datum/nanoui/proc/get_html() + // Generate + + + "} + var/stylesheet_html = {" + + + + + "} + + // Generate JSON. + var/list/send_data = get_send_data(initial_data) + var/initial_data_json = replacetext(JSON.stringify(send_data), "'", "\\'") + + // Generate the HTML document. + return {" + + + + + + [stylesheet_html] + [script_html] + + +
+
+
+ Sending Resources... +
+
+ + + + "} + + /** + * public + * + * Open this NanoUI (and initialize it with data). + * + * optional data list The data to intialize the UI with. + **/ +/datum/nanoui/proc/open(list/data = null) + if(!user.client) return + + if (!initial_data) + if (!data) // If we don't have initial_data and data was not passed, get data from the src_object. + data = src_object.get_ui_data(user) + set_initial_data(data) // Otherwise use the passed data. + + var/window_size = "" + if (width && height) // If we have a width and height, use them. + window_size = "size=[width]x[height];" + update_status(push = 0) // Update the window state. + if (status == NANO_CLOSE) + return // Bail if we should close. + + user << browse(get_html(), "window=[window_id];[window_size][list2params(window_options)]") // Open the window. + winset(user, window_id, "on-close=\"nanoclose \ref[src]\"") // Instruct the client to signal NanoUI when the window is closed. + SSnano.ui_opened(src) // Call the opened handler. + + /** + * public + * + * Reinitialize the NanoUI. + * (Possibly with a new template and/or data). + * + * optional template string The filename of the new template. + * optional data list The new initial data. + **/ +/datum/nanoui/proc/reinitialize(template, list/data) + if(template) + src.template = template // Set a new template. + if(data) + set_initial_data(data) // Replace the initial_data. + open() + + /** + * public + * + * Close the NanoUI, and all its children. + **/ +/datum/nanoui/proc/close() + set_auto_update(0) // Disable auto-updates. + user << browse(null, "window=[window_id]") // Close the window. + SSnano.ui_closed(src) // Call the closed handler. + for(var/datum/nanoui/child in children) // Loop through and close all children. + child.close() + + /** + * private + * + * Push data to an already open NanoUI. + * + * required data list The data to send. + * optional force bool If the update should be sent regardless of state. + **/ +/datum/nanoui/proc/push_data(data, force = 0) + update_status(push = 0) // Update the window state. + if (status == NANO_DISABLED && !force) + return // Cannot update UI, we have no visibility. + + var/list/send_data = get_send_data(data) // Get the data to send. + + // Send the new data to the recieveUpdate() Javascript function. + user << output(list2params(list(JSON.stringify(send_data))), "[window_id].browser:receiveUpdate") + + /** + * private + * + * Handle clicks from the NanoUI. + * Call the src_object's Topic() if status is NANO_INTERACTIVE. + * If the src_object's Topic() returns 1, update all UIs attacked to it. + **/ +/datum/nanoui/Topic(href, href_list) + update_status(push = 0) // Update the window state. + if (status != NANO_INTERACTIVE || user != usr) + return // If UI is not interactive or usr calling Topic is not the UI user. + + var/action = href_list["nano"] // Pull the action out. + href_list -= "nano" + + var/update = src_object.ui_act(action, href_list, state) // Call Topic() on the src_object. + + if (src_object && update) + SSnano.update_uis(src_object) // If we have a src_object and its Topic() told us to update. + + /** + * private + * + * Update the NanoUI. Only updates the contents/layout if update is true, + * otherwise only updates the status. + * + * optional force bool If the UI should be forced to update. + **/ +/datum/nanoui/process(force = 0) + if (!src_object || !user) // If the object or user died (or something else), abort. + close() + return + + if (status && (force || auto_update)) + update() // Update the UI if the status and update settings allow it. + else + update_status(push = 1) // Otherwise only update status. + + /** + * private + * + * Updates the UI by interacting with the src_object again, which will hopefully + * call try_ui_update on it. + * + * optional force_open bool If force_open should be passed to ui_interact. + **/ +/datum/nanoui/proc/update(force_open = 0) + src_object.ui_interact(user, ui_key, src, force_open, master_ui, state) + + /** + * private + * + * Set the status/visibility of the NanoUI. + * + * required state int The status to set (NANO_CLOSE/NANO_DISABLED/NANO_UPDATE/NANO_INTERACTIVE). + * optional push bool Push an update to the UI (an update is always sent for NANO_DISABLED). + **/ +/datum/nanoui/proc/set_status(state, push = 0) + if (state != status) // Only update if status has changed. + if (status == NANO_DISABLED) + status = state + if (push) + update() + else + status = state + if (push || status == 0) // Force an update if NANO_DISABLED. + push_data(null, 1) + + /** + * private + * + * Update the status/visibility of the NanoUI for its user. + * + * optional push bool Push an update to the UI (an update is always sent for NANO_DISABLED). + **/ +/datum/nanoui/proc/update_status(push = 0) + var/new_status = src_object.CanUseTopic(user, state) + if(master_ui) + new_status = min(new_status, master_ui.status) + + set_status(new_status, push) + if(new_status == NANO_CLOSE) close() \ No newline at end of file diff --git a/code/modules/nano/subsystem.dm b/code/modules/nano/subsystem.dm index 33092c353e8..7e6840198df 100644 --- a/code/modules/nano/subsystem.dm +++ b/code/modules/nano/subsystem.dm @@ -1,231 +1,231 @@ - /** - * NanoUI Subsystem - * - * Contains all NanoUI state and subsystem code. - * - * /tg/station user interface library - * thanks to baystation12 - * - * modified by neersighted - **/ - - /** - * public - * - * Get a open NanoUI given a user, src_object, and ui_key and try to update it with data. - * - * required user mob The mob who opened/is using the NanoUI. - * required src_object atom/movable The object which owns the NanoUI. - * required ui_key string The ui_key of the NanoUI. - * optional ui datum/nanoui The UI to be updated, if it exists. - * optional data list The data to update the UI with, if it exists. - * optional force_open bool If the UI should be re-opened instead of updated. - * - * return datum/nanoui The found NanoUI. - **/ -/datum/subsystem/nano/proc/try_update_ui(mob/user, atom/movable/src_object, ui_key, datum/nanoui/ui, \ - list/data = null, force_open = 0) - if (!data) - data = src_object.get_ui_data(user) - - if (isnull(ui)) // No NanoUI was passed, so look for one. - ui = get_open_ui(user, src_object, ui_key) - - if (!isnull(ui)) - if (!force_open) // UI is already open; update it. - ui.push_data(data) - else // Re-open it anyways. - ui.reinitialize(null, data) - return ui // We found the UI, return it. - else - return null // We couldn't find a UI. - /** - * private - * - * Get a open NanoUI given a user, src_object, and ui_key. - * - * required user mob The mob who opened/is using the NanoUI. - * required src_object atom/movable The object which owns the NanoUI. - * required ui_key string The ui_key of the NanoUI. - * - * return datum/nanoui The found NanoUI. - **/ -/datum/subsystem/nano/proc/get_open_ui(mob/user, atom/movable/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)) - return null // No UIs open. - else if (isnull(open_uis[src_object_key][ui_key]) || !istype(open_uis[src_object_key][ui_key], /list)) - return null // No UIs open for this object. - - for (var/datum/nanoui/ui in open_uis[src_object_key][ui_key]) // Find UIs for this object. - if (ui.user == user) // Make sure we have the right user - return ui - - return null // Couldn't find a UI! - - /** - * private - * - * Update all NanoUIs attached to src_object. - * - * required src_object atom/movable The object which owns the NanoUIs. - * - * return int The number of NanoUIs updated. - **/ -/datum/subsystem/nano/proc/update_uis(atom/movable/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 // Couldn't find any UIs for this object. - - var/update_count = 0 - for (var/ui_key in open_uis[src_object_key]) - for (var/datum/nanoui/ui in open_uis[src_object_key][ui_key]) - if(ui && ui.src_object && ui.user && ui.src_object.nano_host()) // Check the UI is valid. - ui.process(force = 1) // Update the UI. - update_count++ // Count each UI we update. - return update_count - - /** - * private - * - * Close all NanoUIs attached to src_object. - * - * required src_object atom/movable The object which owns the NanoUIs. - * - * return int The number of NanoUIs closed. - **/ -/datum/subsystem/nano/proc/close_uis(atom/movable/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 // Couldn't find any UIs for this object. - - var/close_count = 0 - for (var/ui_key in open_uis[src_object_key]) - for (var/datum/nanoui/ui in open_uis[src_object_key][ui_key]) - if(ui && ui.src_object && ui.user && ui.src_object.nano_host()) // Check the UI is valid. - ui.close() // Close the UI. - close_count++ // Count each UI we close. - return close_count - - /** - * private - * - * Update all NanoUIs belonging to a user. - * - * required user mob The mob who opened/is using the NanoUI. - * optional src_object atom/movable If provided, only update UIs belonging this atom. - * optional ui_key string If provided, only update UIs with this UI key. - * - * return int The number of NanoUIs updated. - **/ -/datum/subsystem/nano/proc/update_user_uis(mob/user, atom/movable/src_object = null, ui_key = null) - if (isnull(user.open_uis) || !istype(user.open_uis, /list) || open_uis.len == 0) - return 0 // Couldn't find any UIs for this user. - - var/update_count = 0 - for (var/datum/nanoui/ui in user.open_uis) - if ((isnull(src_object) || !isnull(src_object) && ui.src_object == src_object) && (isnull(ui_key) || !isnull(ui_key) && ui.ui_key == ui_key)) - ui.process(force = 1) // Update the UI. - update_count++ // Count each UI we upadte. - return update_count - - /** - * private - * - * Close all NanoUIs belonging to a user. - * - * required user mob The mob who opened/is using the NanoUI. - * optional src_object atom/movable If provided, only update UIs belonging this atom. - * optional ui_key string If provided, only update UIs with this UI key. - * - * return int The number of NanoUIs closed. - **/ -/datum/subsystem/nano/proc/close_user_uis(mob/user, atom/movable/src_object = null, ui_key = null) - if (isnull(user.open_uis) || !istype(user.open_uis, /list) || open_uis.len == 0) - return 0 // Couldn't find any UIs for this user. - - var/close_count = 0 - for (var/datum/nanoui/ui in user.open_uis) - if ((isnull(src_object) || !isnull(src_object) && ui.src_object == src_object) && (isnull(ui_key) || !isnull(ui_key) && ui.ui_key == ui_key)) - ui.close() // Close the UI. - close_count++ // Count each UI we close. - return close_count - - /** - * private - * - * Add a NanoUI to the list of open UIs. - * - * required ui datum/nanoui The UI to be added. - **/ -/datum/subsystem/nano/proc/ui_opened(datum/nanoui/ui) - var/src_object_key = "\ref[ui.src_object]" - if (isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list)) - open_uis[src_object_key] = list(ui.ui_key = list()) // Make a list for the ui_key and src_object. - else if (isnull(open_uis[src_object_key][ui.ui_key]) || !istype(open_uis[src_object_key][ui.ui_key], /list)) - open_uis[src_object_key][ui.ui_key] = list() // Make a list for the ui_key. - - // Append the UI to all the lists. - ui.user.open_uis |= ui - var/list/uis = open_uis[src_object_key][ui.ui_key] - uis |= ui - processing_uis |= ui - - /** - * private - * - * Remove a NanoUI from the list of open UIs. - * - * required ui datum/nanoui The UI to be removed. - * - * return bool If the UI was removed or not. - **/ -/datum/subsystem/nano/proc/ui_closed(datum/nanoui/ui) - var/src_object_key = "\ref[ui.src_object]" - if (isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list)) - return 0 // It wasn't open. - else if (isnull(open_uis[src_object_key][ui.ui_key]) || !istype(open_uis[src_object_key][ui.ui_key], /list)) - return 0 // It wasn't open. - - processing_uis.Remove(ui) // Remove it from the list of processing UIs. - if(ui.user) // If the user exists, remove it from them too. - ui.user.open_uis.Remove(ui) - var/list/uis = open_uis[src_object_key][ui.ui_key] // Remove it from the list of open UIs. - uis.Remove(ui) - return 1 // Let the caller know we did it. - - /** - * private - * - * Handle client logout, by closing all their NanoUIs. - * - * required user mob The mob which logged out. - * - * return int The number of NanoUIs closed. - **/ -/datum/subsystem/nano/proc/user_logout(mob/user) - return close_user_uis(user) - - /** - * private - * - * Handle clients switching mobs, by transfering their NanoUIs. - * - * required user oldMob The client's original mob. - * required user newMob The client's new mob. - * - * return bool If the NanoUIs were transferred. - **/ -/datum/subsystem/nano/proc/user_transferred(mob/oldMob, mob/newMob) - if (!oldMob || isnull(oldMob.open_uis) || !istype(oldMob.open_uis, /list) || open_uis.len == 0) - return 0 // The old mob had no open NanoUIs. - - if (isnull(newMob.open_uis) || !istype(newMob.open_uis, /list)) - newMob.open_uis = list() // Create a list for the new mob if needed. - - for (var/datum/nanoui/ui in oldMob.open_uis) - ui.user = newMob // Inform the UIs of their new owner. - newMob.open_uis.Add(ui) // Transfer all the NanoUIs. - - oldMob.open_uis.Cut() // Clear the old list. - return 1 // Let the caller know we did it. + /** + * NanoUI Subsystem + * + * Contains all NanoUI state and subsystem code. + * + * /tg/station user interface library + * thanks to baystation12 + * + * modified by neersighted + **/ + + /** + * public + * + * Get a open NanoUI given a user, src_object, and ui_key and try to update it with data. + * + * required user mob The mob who opened/is using the NanoUI. + * required src_object atom/movable The object which owns the NanoUI. + * required ui_key string The ui_key of the NanoUI. + * optional ui datum/nanoui The UI to be updated, if it exists. + * optional data list The data to update the UI with, if it exists. + * optional force_open bool If the UI should be re-opened instead of updated. + * + * return datum/nanoui The found NanoUI. + **/ +/datum/subsystem/nano/proc/try_update_ui(mob/user, atom/movable/src_object, ui_key, datum/nanoui/ui, \ + list/data = null, force_open = 0) + if (!data) + data = src_object.get_ui_data(user) + + if (isnull(ui)) // No NanoUI was passed, so look for one. + ui = get_open_ui(user, src_object, ui_key) + + if (!isnull(ui)) + if (!force_open) // UI is already open; update it. + ui.push_data(data) + else // Re-open it anyways. + ui.reinitialize(null, data) + return ui // We found the UI, return it. + else + return null // We couldn't find a UI. + /** + * private + * + * Get a open NanoUI given a user, src_object, and ui_key. + * + * required user mob The mob who opened/is using the NanoUI. + * required src_object atom/movable The object which owns the NanoUI. + * required ui_key string The ui_key of the NanoUI. + * + * return datum/nanoui The found NanoUI. + **/ +/datum/subsystem/nano/proc/get_open_ui(mob/user, atom/movable/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)) + return null // No UIs open. + else if (isnull(open_uis[src_object_key][ui_key]) || !istype(open_uis[src_object_key][ui_key], /list)) + return null // No UIs open for this object. + + for (var/datum/nanoui/ui in open_uis[src_object_key][ui_key]) // Find UIs for this object. + if (ui.user == user) // Make sure we have the right user + return ui + + return null // Couldn't find a UI! + + /** + * private + * + * Update all NanoUIs attached to src_object. + * + * required src_object atom/movable The object which owns the NanoUIs. + * + * return int The number of NanoUIs updated. + **/ +/datum/subsystem/nano/proc/update_uis(atom/movable/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 // Couldn't find any UIs for this object. + + var/update_count = 0 + for (var/ui_key in open_uis[src_object_key]) + for (var/datum/nanoui/ui in open_uis[src_object_key][ui_key]) + if(ui && ui.src_object && ui.user && ui.src_object.nano_host()) // Check the UI is valid. + ui.process(force = 1) // Update the UI. + update_count++ // Count each UI we update. + return update_count + + /** + * private + * + * Close all NanoUIs attached to src_object. + * + * required src_object atom/movable The object which owns the NanoUIs. + * + * return int The number of NanoUIs closed. + **/ +/datum/subsystem/nano/proc/close_uis(atom/movable/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 // Couldn't find any UIs for this object. + + var/close_count = 0 + for (var/ui_key in open_uis[src_object_key]) + for (var/datum/nanoui/ui in open_uis[src_object_key][ui_key]) + if(ui && ui.src_object && ui.user && ui.src_object.nano_host()) // Check the UI is valid. + ui.close() // Close the UI. + close_count++ // Count each UI we close. + return close_count + + /** + * private + * + * Update all NanoUIs belonging to a user. + * + * required user mob The mob who opened/is using the NanoUI. + * optional src_object atom/movable If provided, only update UIs belonging this atom. + * optional ui_key string If provided, only update UIs with this UI key. + * + * return int The number of NanoUIs updated. + **/ +/datum/subsystem/nano/proc/update_user_uis(mob/user, atom/movable/src_object = null, ui_key = null) + if (isnull(user.open_uis) || !istype(user.open_uis, /list) || open_uis.len == 0) + return 0 // Couldn't find any UIs for this user. + + var/update_count = 0 + for (var/datum/nanoui/ui in user.open_uis) + if ((isnull(src_object) || !isnull(src_object) && ui.src_object == src_object) && (isnull(ui_key) || !isnull(ui_key) && ui.ui_key == ui_key)) + ui.process(force = 1) // Update the UI. + update_count++ // Count each UI we upadte. + return update_count + + /** + * private + * + * Close all NanoUIs belonging to a user. + * + * required user mob The mob who opened/is using the NanoUI. + * optional src_object atom/movable If provided, only update UIs belonging this atom. + * optional ui_key string If provided, only update UIs with this UI key. + * + * return int The number of NanoUIs closed. + **/ +/datum/subsystem/nano/proc/close_user_uis(mob/user, atom/movable/src_object = null, ui_key = null) + if (isnull(user.open_uis) || !istype(user.open_uis, /list) || open_uis.len == 0) + return 0 // Couldn't find any UIs for this user. + + var/close_count = 0 + for (var/datum/nanoui/ui in user.open_uis) + if ((isnull(src_object) || !isnull(src_object) && ui.src_object == src_object) && (isnull(ui_key) || !isnull(ui_key) && ui.ui_key == ui_key)) + ui.close() // Close the UI. + close_count++ // Count each UI we close. + return close_count + + /** + * private + * + * Add a NanoUI to the list of open UIs. + * + * required ui datum/nanoui The UI to be added. + **/ +/datum/subsystem/nano/proc/ui_opened(datum/nanoui/ui) + var/src_object_key = "\ref[ui.src_object]" + if (isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list)) + open_uis[src_object_key] = list(ui.ui_key = list()) // Make a list for the ui_key and src_object. + else if (isnull(open_uis[src_object_key][ui.ui_key]) || !istype(open_uis[src_object_key][ui.ui_key], /list)) + open_uis[src_object_key][ui.ui_key] = list() // Make a list for the ui_key. + + // Append the UI to all the lists. + ui.user.open_uis |= ui + var/list/uis = open_uis[src_object_key][ui.ui_key] + uis |= ui + processing_uis |= ui + + /** + * private + * + * Remove a NanoUI from the list of open UIs. + * + * required ui datum/nanoui The UI to be removed. + * + * return bool If the UI was removed or not. + **/ +/datum/subsystem/nano/proc/ui_closed(datum/nanoui/ui) + var/src_object_key = "\ref[ui.src_object]" + if (isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list)) + return 0 // It wasn't open. + else if (isnull(open_uis[src_object_key][ui.ui_key]) || !istype(open_uis[src_object_key][ui.ui_key], /list)) + return 0 // It wasn't open. + + processing_uis.Remove(ui) // Remove it from the list of processing UIs. + if(ui.user) // If the user exists, remove it from them too. + ui.user.open_uis.Remove(ui) + var/list/uis = open_uis[src_object_key][ui.ui_key] // Remove it from the list of open UIs. + uis.Remove(ui) + return 1 // Let the caller know we did it. + + /** + * private + * + * Handle client logout, by closing all their NanoUIs. + * + * required user mob The mob which logged out. + * + * return int The number of NanoUIs closed. + **/ +/datum/subsystem/nano/proc/user_logout(mob/user) + return close_user_uis(user) + + /** + * private + * + * Handle clients switching mobs, by transfering their NanoUIs. + * + * required user oldMob The client's original mob. + * required user newMob The client's new mob. + * + * return bool If the NanoUIs were transferred. + **/ +/datum/subsystem/nano/proc/user_transferred(mob/oldMob, mob/newMob) + if (!oldMob || isnull(oldMob.open_uis) || !istype(oldMob.open_uis, /list) || open_uis.len == 0) + return 0 // The old mob had no open NanoUIs. + + if (isnull(newMob.open_uis) || !istype(newMob.open_uis, /list)) + newMob.open_uis = list() // Create a list for the new mob if needed. + + for (var/datum/nanoui/ui in oldMob.open_uis) + ui.user = newMob // Inform the UIs of their new owner. + newMob.open_uis.Add(ui) // Transfer all the NanoUIs. + + oldMob.open_uis.Cut() // Clear the old list. + return 1 // Let the caller know we did it. diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm index 8f913f964f2..4accdee71a2 100644 --- a/code/modules/paperwork/paper.dm +++ b/code/modules/paperwork/paper.dm @@ -1,402 +1,402 @@ -/* - * Paper - * also scraps of paper - * - * lipstick wiping is in code/game/objects/items/weapons/cosmetics.dm! - */ - -/obj/item/weapon/paper - name = "paper" - gender = NEUTER - icon = 'icons/obj/bureaucracy.dmi' - icon_state = "paper" - throwforce = 0 - w_class = 1 - throw_range = 1 - throw_speed = 1 - layer = 3 - pressure_resistance = 0 - slot_flags = SLOT_HEAD - body_parts_covered = HEAD - burn_state = FLAMMABLE - burntime = 5 - - var/info //What's actually written on the paper. - var/info_links //A different version of the paper which includes html links at fields and EOF - var/stamps //The (text for the) stamps on the paper. - var/fields //Amount of user created fields - var/list/stamped - var/rigged = 0 - var/spam_flag = 0 - - -/obj/item/weapon/paper/New() - ..() - pixel_y = rand(-8, 8) - pixel_x = rand(-9, 9) - spawn(2) - update_icon() - updateinfolinks() - - -/obj/item/weapon/paper/update_icon() - if(burn_state == ON_FIRE) - icon_state = "paper_onfire" - return - if(info) - icon_state = "paper_words" - return - icon_state = "paper" - - -/obj/item/weapon/paper/examine(mob/user) - ..() - var/datum/asset/assets = get_asset_datum(/datum/asset/simple/paper) - assets.send(user) - - if(in_range(user, src) || isobserver(user)) - if( !(ishuman(user) || isobserver(user) || issilicon(user)) ) - user << browse("[name][stars(info)]
[stamps]", "window=[name]") - onclose(user, "[name]") - else - user << browse("[name][info]
[stamps]", "window=[name]") - onclose(user, "[name]") - else - user << "It is too far away." - - -/obj/item/weapon/paper/verb/rename() - set name = "Rename paper" - set category = "Object" - set src in usr - - if(usr.stat || !usr.canmove || usr.restrained()) - return - - if(!ishuman(usr)) - return - var/mob/living/carbon/human/H = usr - if(H.disabilities & CLUMSY && prob(25)) - H << "You cut yourself on the paper! Ahhhh! Ahhhhh!" - H.damageoverlaytemp = 9001 - return - var/n_name = stripped_input(usr, "What would you like to label the paper?", "Paper Labelling", null, MAX_NAME_LEN) - if((loc == usr && usr.stat == 0)) - name = "paper[(n_name ? text("- '[n_name]'") : null)]" - add_fingerprint(usr) - -/obj/item/weapon/paper/suicide_act(mob/user) - user.visible_message("[user] scratches a grid on their wrist with the paper! It looks like \he's trying to commit sudoku..") - return (BRUTELOSS) - -/obj/item/weapon/paper/attack_self(mob/user) - user.examinate(src) - if(rigged && (SSevent.holidays && SSevent.holidays[APRIL_FOOLS])) - if(spam_flag == 0) - spam_flag = 1 - playsound(loc, 'sound/items/bikehorn.ogg', 50, 1) - spawn(20) - spam_flag = 0 - - -/obj/item/weapon/paper/attack_ai(mob/living/silicon/ai/user) - var/dist - if(istype(user) && user.current) //is AI - dist = get_dist(src, user.current) - else //cyborg or AI not seeing through a camera - dist = get_dist(src, user) - if(dist < 2) - usr << browse("[name][info]
[stamps]", "window=[name]") - onclose(usr, "[name]") - else - usr << browse("[name][stars(info)]
[stamps]", "window=[name]") - onclose(usr, "[name]") - - -/obj/item/weapon/paper/proc/addtofield(id, text, links = 0) - var/locid = 0 - var/laststart = 1 - var/textindex = 1 - while(1) //I know this can cause infinite loops and fuck up the whole server, but the if(istart==0) should be safe as fuck - var/istart = 0 - if(links) - istart = findtext(info_links, "", laststart) - else - istart = findtext(info, "", laststart) - - if(istart == 0) - return //No field found with matching id - - laststart = istart+1 - locid++ - if(locid == id) - var/iend = 1 - if(links) - iend = findtext(info_links, "", istart) - else - iend = findtext(info, "", istart) - - //textindex = istart+26 - textindex = iend - break - - if(links) - var/before = copytext(info_links, 1, textindex) - var/after = copytext(info_links, textindex) - info_links = before + text + after - else - var/before = copytext(info, 1, textindex) - var/after = copytext(info, textindex) - info = before + text + after - updateinfolinks() - - -/obj/item/weapon/paper/proc/updateinfolinks() - info_links = info - var/i = 0 - for(i=1,i<=fields,i++) - addtofield(i, "write", 1) - info_links = info_links + "write" - - -/obj/item/weapon/paper/proc/clearpaper() - info = null - stamps = null - stamped = list() - overlays.Cut() - updateinfolinks() - update_icon() - - -/obj/item/weapon/paper/proc/parsepencode(t, obj/item/weapon/pen/P, mob/user, iscrayon = 0) - if(length(t) < 1) //No input means nothing needs to be parsed - return - -// t = copytext(sanitize(t),1,MAX_MESSAGE_LEN) - - t = replacetext(t, "\[center\]", "
") - t = replacetext(t, "\[/center\]", "
") - t = replacetext(t, "\[br\]", "
") - t = replacetext(t, "\[b\]", "") - t = replacetext(t, "\[/b\]", "") - t = replacetext(t, "\[i\]", "") - t = replacetext(t, "\[/i\]", "") - t = replacetext(t, "\[u\]", "") - t = replacetext(t, "\[/u\]", "") - t = replacetext(t, "\[large\]", "") - t = replacetext(t, "\[/large\]", "") - t = replacetext(t, "\[sign\]", "[user.real_name]") - t = replacetext(t, "\[field\]", "") - - if(!iscrayon) - t = replacetext(t, "\[*\]", "
  • ") - t = replacetext(t, "\[hr\]", "
    ") - t = replacetext(t, "\[small\]", "") - t = replacetext(t, "\[/small\]", "") - t = replacetext(t, "\[list\]", "
      ") - t = replacetext(t, "\[/list\]", "
    ") - - t = "[t]" - else // If it is a crayon, and he still tries to use these, make them empty! - var/obj/item/toy/crayon/C = P - t = replacetext(t, "\[*\]", "") - t = replacetext(t, "\[hr\]", "") - t = replacetext(t, "\[small\]", "") - t = replacetext(t, "\[/small\]", "") - t = replacetext(t, "\[list\]", "") - t = replacetext(t, "\[/list\]", "") - - t = "[t]" - -// t = replacetext(t, "#", "") // Junk converted to nothing! - -//Count the fields - var/laststart = 1 - while(1) - var/i = findtext(t, "", laststart) - if(i == 0) - break - laststart = i+1 - fields++ - - return t - - -/obj/item/weapon/paper/proc/openhelp(mob/user) - user << browse({"Pen Help - -
    Crayon&Pen commands

    -
    - \[br\] : Creates a linebreak.
    - \[center\] - \[/center\] : Centers the text.
    - \[b\] - \[/b\] : Makes the text bold.
    - \[i\] - \[/i\] : Makes the text italic.
    - \[u\] - \[/u\] : Makes the text underlined.
    - \[large\] - \[/large\] : Increases the size of the text.
    - \[sign\] : Inserts a signature of your name in a foolproof way.
    - \[field\] : Inserts an invisible field which lets you start type from there. Useful for forms.
    -
    -
    Pen exclusive commands

    - \[small\] - \[/small\] : Decreases the size of the text.
    - \[list\] - \[/list\] : A list.
    - \[*\] : A dot used for lists.
    - \[hr\] : Adds a horizontal rule. - "}, "window=paper_help") - - -/obj/item/weapon/paper/Topic(href, href_list) - ..() - if(usr.stat || usr.restrained()) - return - - if(href_list["write"]) - var/id = href_list["write"] - var/t = stripped_multiline_input("Enter what you want to write:", "Write") - if(!t) - return - var/obj/item/i = usr.get_active_hand() //Check to see if he still got that darn pen, also check if he's using a crayon or pen. - var/iscrayon = 0 - if(!istype(i, /obj/item/weapon/pen)) - if(!istype(i, /obj/item/toy/crayon)) - return - 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 - return - - t = parsepencode(t, i, usr, iscrayon) // Encode everything from pencode to html - - if(t != null) //No input from the user means nothing needs to be added - if(id!="end") - addtofield(text2num(id), t) // He wants to edit a field, let him. - else - info += t // Oh, he wants to edit to the end of the file, let him. - updateinfolinks() - - usr << browse("[name][info_links]
    [stamps]", "window=[name]") // Update the window - update_icon() - - -/obj/item/weapon/paper/attackby(obj/item/weapon/P, mob/living/carbon/human/user, params) - ..() - - if(burn_state == ON_FIRE) - return - - if(is_blind(user)) - return - - if(istype(P, /obj/item/weapon/pen) || istype(P, /obj/item/toy/crayon)) - if(user.IsAdvancedToolUser()) - user << browse("[name][info_links]
    [stamps]", "window=[name]") - return - else - user << "You don't know how to read or write." - return - if(istype(src, /obj/item/weapon/paper/talisman/)) - user << "[P]'s ink fades away shortly after it is written." - return - - else if(istype(P, /obj/item/weapon/stamp)) - if(!in_range(src, usr) && loc != user && !istype(loc, /obj/item/weapon/clipboard) && loc.loc != user && user.get_active_hand() != P) - return - - stamps += "" - - var/image/stampoverlay = image('icons/obj/bureaucracy.dmi') - stampoverlay.pixel_x = rand(-2, 2) - stampoverlay.pixel_y = rand(-3, 2) - - stampoverlay.icon_state = "paper_[P.icon_state]" - - if(!stamped) - stamped = new - stamped += P.type - overlays += stampoverlay - - user << "You stamp the paper with your rubber stamp." - - if(P.is_hot()) - if(user.disabilities & CLUMSY && prob(10)) - user.visible_message("[user] accidentally ignites themselves!", \ - "You miss the paper and accidentally light yourself on fire!") - user.unEquip(P) - user.adjust_fire_stacks(1) - user.IgniteMob() - return - - if(!(in_range(user, src))) //to prevent issues as a result of telepathically lighting a paper - return - - user.unEquip(src) - user.visible_message("[user] lights [src] ablaze with [P]!", "You light [src] on fire!") - fire_act() - - - - add_fingerprint(user) - -/obj/item/weapon/paper/fire_act() - ..(0) - icon_state = "paper_onfire" - info = "[stars(info)]" - - -/obj/item/weapon/paper/extinguish() - ..() - update_icon() - -/* - * Premade paper - */ - -/obj/item/weapon/paper/Court - name = "paper- 'Judgement'" - info = "For crimes against the station, the offender is sentenced to:
    \n
    \n" - -/obj/item/weapon/paper/Toxin - name = "paper- 'Chemical Information'" - info = "Known Onboard Toxins:
    \n\tGrade A Semi-Liquid Plasma:
    \n\t\tHighly poisonous. You cannot sustain concentrations above 15 units.
    \n\t\tA gas mask fails to filter plasma after 50 units.
    \n\t\tWill attempt to diffuse like a gas.
    \n\t\tFiltered by scrubbers.
    \n\t\tThere is a bottled version which is very different
    \n\t\t\tfrom the version found in canisters!
    \n
    \n\t\tWARNING: Highly Flammable. Keep away from heat sources
    \n\t\texcept in a enclosed fire area!
    \n\t\tWARNING: It is a crime to use this without authorization.
    \nKnown Onboard Anti-Toxin:
    \n\tAnti-Toxin Type 01P: Works against Grade A Plasma.
    \n\t\tBest if injected directly into bloodstream.
    \n\t\tA full injection is in every regular Med-Kit.
    \n\t\tSpecial toxin Kits hold around 7.
    \n
    \nKnown Onboard Chemicals (other):
    \n\tRejuvenation T#001:
    \n\t\tEven 1 unit injected directly into the bloodstream
    \n\t\t\twill cure paralysis and sleep toxins.
    \n\t\tIf administered to a dying patient it will prevent
    \n\t\t\tfurther damage for about units*3 seconds.
    \n\t\t\tit will not cure them or allow them to be cured.
    \n\t\tIt can be administeredd to a non-dying patient
    \n\t\t\tbut the chemicals disappear just as fast.
    \n\tMorphine T#054:
    \n\t\t5 units wilkl induce precisely 1 minute of sleep.
    \n\t\t\tThe effect are cumulative.
    \n\t\tWARNING: It is a crime to use this without authorization" - -/obj/item/weapon/paper/courtroom - name = "paper- 'A Crash Course in Legal SOP on SS13'" - info = "Roles:
    \nThe Detective is basically the investigator and prosecutor.
    \nThe Staff Assistant can perform these functions with written authority from the Detective.
    \nThe Captain/HoP/Warden is ct as the judicial authority.
    \nThe Security Officers are responsible for executing warrants, security during trial, and prisoner transport.
    \n
    \nInvestigative Phase:
    \nAfter the crime has been committed the Detective's job is to gather evidence and try to ascertain not only who did it but what happened. He must take special care to catalogue everything and don't leave anything out. Write out all the evidence on paper. Make sure you take an appropriate number of fingerprints. IF he must ask someone questions he has permission to confront them. If the person refuses he can ask a judicial authority to write a subpoena for questioning. If again he fails to respond then that person is to be jailed as insubordinate and obstructing justice. Said person will be released after he cooperates.
    \n
    \nONCE the FT has a clear idea as to who the criminal is he is to write an arrest warrant on the piece of paper. IT MUST LIST THE CHARGES. The FT is to then go to the judicial authority and explain a small version of his case. If the case is moderately acceptable the authority should sign it. Security must then execute said warrant.
    \n
    \nPre-Pre-Trial Phase:
    \nNow a legal representative must be presented to the defendant if said defendant requests one. That person and the defendant are then to be given time to meet (in the jail IS ACCEPTABLE). The defendant and his lawyer are then to be given a copy of all the evidence that will be presented at trial (rewriting it all on paper is fine). THIS IS CALLED THE DISCOVERY PACK. With a few exceptions, THIS IS THE ONLY EVIDENCE BOTH SIDES MAY USE AT TRIAL. IF the prosecution will be seeking the death penalty it MUST be stated at this time. ALSO if the defense will be seeking not guilty by mental defect it must state this at this time to allow ample time for examination.
    \nNow at this time each side is to compile a list of witnesses. By default, the defendant is on both lists regardless of anything else. Also the defense and prosecution can compile more evidence beforehand BUT in order for it to be used the evidence MUST also be given to the other side.\nThe defense has time to compile motions against some evidence here.
    \nPossible Motions:
    \n1. Invalidate Evidence- Something with the evidence is wrong and the evidence is to be thrown out. This includes irrelevance or corrupt security.
    \n2. Free Movement- Basically the defendant is to be kept uncuffed before and during the trial.
    \n3. Subpoena Witness- If the defense presents god reasons for needing a witness but said person fails to cooperate then a subpoena is issued.
    \n4. Drop the Charges- Not enough evidence is there for a trial so the charges are to be dropped. The FT CAN RETRY but the judicial authority must carefully reexamine the new evidence.
    \n5. Declare Incompetent- Basically the defendant is insane. Once this is granted a medical official is to examine the patient. If he is indeed insane he is to be placed under care of the medical staff until he is deemed competent to stand trial.
    \n
    \nALL SIDES MOVE TO A COURTROOM
    \nPre-Trial Hearings:
    \nA judicial authority and the 2 sides are to meet in the trial room. NO ONE ELSE BESIDES A SECURITY DETAIL IS TO BE PRESENT. The defense submits a plea. If the plea is guilty then proceed directly to sentencing phase. Now the sides each present their motions to the judicial authority. He rules on them. Each side can debate each motion. Then the judicial authority gets a list of crew members. He first gets a chance to look at them all and pick out acceptable and available jurors. Those jurors are then called over. Each side can ask a few questions and dismiss jurors they find too biased. HOWEVER before dismissal the judicial authority MUST agree to the reasoning.
    \n
    \nThe Trial:
    \nThe trial has three phases.
    \n1. Opening Arguments- Each side can give a short speech. They may not present ANY evidence.
    \n2. Witness Calling/Evidence Presentation- The prosecution goes first and is able to call the witnesses on his approved list in any order. He can recall them if necessary. During the questioning the lawyer may use the evidence in the questions to help prove a point. After every witness the other side has a chance to cross-examine. After both sides are done questioning a witness the prosecution can present another or recall one (even the EXACT same one again!). After prosecution is done the defense can call witnesses. After the initial cases are presented both sides are free to call witnesses on either list.
    \nFINALLY once both sides are done calling witnesses we move onto the next phase.
    \n3. Closing Arguments- Same as opening.
    \nThe jury then deliberates IN PRIVATE. THEY MUST ALL AGREE on a verdict. REMEMBER: They mix between some charges being guilty and others not guilty (IE if you supposedly killed someone with a gun and you unfortunately picked up a gun without authorization then you CAN be found not guilty of murder BUT guilty of possession of illegal weaponry.). Once they have agreed they present their verdict. If unable to reach a verdict and feel they will never they call a deadlocked jury and we restart at Pre-Trial phase with an entirely new set of jurors.
    \n
    \nSentencing Phase:
    \nIf the death penalty was sought (you MUST have gone through a trial for death penalty) then skip to the second part.
    \nI. Each side can present more evidence/witnesses in any order. There is NO ban on emotional aspects or anything. The prosecution is to submit a suggested penalty. After all the sides are done then the judicial authority is to give a sentence.
    \nII. The jury stays and does the same thing as I. Their sole job is to determine if the death penalty is applicable. If NOT then the judge selects a sentence.
    \n
    \nTADA you're done. Security then executes the sentence and adds the applicable convictions to the person's record.
    \n" - -/obj/item/weapon/paper/hydroponics - name = "paper- 'Greetings from Billy Bob'" - info = "Hey fellow botanist!
    \n
    \nI didn't trust the station folk so I left
    \na couple of weeks ago. But here's some
    \ninstructions on how to operate things here.
    \nYou can grow plants and each iteration they become
    \nstronger, more potent and have better yield, if you
    \nknow which ones to pick. Use your botanist's analyzer
    \nfor that. You can turn harvested plants into seeds
    \nat the seed extractor, and replant them for better stuff!
    \nSometimes if the weed level gets high in the tray
    \nmutations into different mushroom or weed species have
    \nbeen witnessed. On the rare occassion even weeds mutate!
    \n
    \nEither way, have fun!
    \n
    \nBest regards,
    \nBilly Bob Johnson.
    \n
    \nPS.
    \nHere's a few tips:
    \nIn nettles, potency = damage
    \nIn amanitas, potency = deadliness + side effect
    \nIn Liberty caps, potency = drug power + effect
    \nIn chilis, potency = heat
    \nNutrients keep mushrooms alive!
    \nWater keeps weeds such as nettles alive!
    \nAll other plants need both." - -/obj/item/weapon/paper/djstation - name = "paper - 'DJ Listening Outpost'" - info = "Welcome new owner!

    You have purchased the latest in listening equipment. The telecommunication setup we created is the best in listening to common and private radio frequencies. Here is a step by step guide to start listening in on those saucy radio channels:
    1. Equip yourself with a multitool
    2. Use the multitool on the relay.
    3. Turn it on. It has already been configured for you to listen on.
    Simple as that. Now to listen to the private channels, you'll have to configure the intercoms. They are located on the front desk. Here is a list of frequencies for you to listen on.
    • 145.9 - Common Channel
    • 144.7 - Private AI Channel
    • 135.9 - Security Channel
    • 135.7 - Engineering Channel
    • 135.5 - Medical Channel
    • 135.3 - Command Channel
    • 135.1 - Science Channel
    • 134.9 - Service Channel
    • 134.7 - Supply Channel
    • " - -/obj/item/weapon/paper/jobs - name = "paper- 'Job Information'" - info = "Information on all formal jobs that can be assigned on Space Station 13 can be found on this document.
      \nThe data will be in the following form.
      \nGenerally lower ranking positions come first in this list.
      \n
      \nJob Name general access>lab access-engine access-systems access (atmosphere control)
      \n\tJob Description
      \nJob Duties (in no particular order)
      \nTips (where applicable)
      \n
      \nResearch Assistant 1>1-0-0
      \n\tThis is probably the lowest level position. Anyone who enters the space station after the initial job\nassignment will automatically receive this position. Access with this is restricted. Head of Personnel should\nappropriate the correct level of assistance.
      \n1. Assist the researchers.
      \n2. Clean up the labs.
      \n3. Prepare materials.
      \n
      \nStaff Assistant 2>0-0-0
      \n\tThis position assists the security officer in his duties. The staff assisstants should primarily br\npatrolling the ship waiting until they are needed to maintain ship safety.\n(Addendum: Updated/Elevated Security Protocols admit issuing of low level weapons to security personnel)
      \n1. Patrol ship/Guard key areas
      \n2. Assist security officer
      \n3. Perform other security duties.
      \n
      \nTechnical Assistant 1>0-0-1
      \n\tThis is yet another low level position. The technical assistant helps the engineer and the statian\ntechnician with the upkeep and maintenance of the station. This job is very important because it usually\ngets to be a heavy workload on station technician and these helpers will alleviate that.
      \n1. Assist Station technician and Engineers.
      \n2. Perform general maintenance of station.
      \n3. Prepare materials.
      \n
      \nMedical Assistant 1>1-0-0
      \n\tThis is the fourth position yet it is slightly less common. This position doesn't have much power\noutside of the med bay. Consider this position like a nurse who helps to upkeep medical records and the\nmaterials (filling syringes and checking vitals)
      \n1. Assist the medical personnel.
      \n2. Update medical files.
      \n3. Prepare materials for medical operations.
      \n
      \nResearch Technician 2>3-0-0
      \n\tThis job is primarily a step up from research assistant. These people generally do not get their own lab\nbut are more hands on in the experimentation process. At this level they are permitted to work as consultants to\nthe others formally.
      \n1. Inform superiors of research.
      \n2. Perform research alongside of official researchers.
      \n
      \nDetective 3>2-0-0
      \n\tThis job is in most cases slightly boring at best. Their sole duty is to\nperform investigations of crine scenes and analysis of the crime scene. This\nalleviates SOME of the burden from the security officer. This person's duty\nis to draw conclusions as to what happened and testify in court. Said person\nalso should stroe the evidence ly.
      \n1. Perform crime-scene investigations/draw conclusions.
      \n2. Store and catalogue evidence properly.
      \n3. Testify to superiors/inquieries on findings.
      \n
      \nStation Technician 2>0-2-3
      \n\tPeople assigned to this position must work to make sure all the systems aboard Space Station 13 are operable.\nThey should primarily work in the computer lab and repairing faulty equipment. They should work with the\natmospheric technician.
      \n1. Maintain SS13 systems.
      \n2. Repair equipment.
      \n
      \nAtmospheric Technician 3>0-0-4
      \n\tThese people should primarily work in the atmospheric control center and lab. They have the very important\njob of maintaining the delicate atmosphere on SS13.
      \n1. Maintain atmosphere on SS13
      \n2. Research atmospheres on the space station. (safely please!)
      \n
      \nEngineer 2>1-3-0
      \n\tPeople working as this should generally have detailed knowledge as to how the propulsion systems on SS13\nwork. They are one of the few classes that have unrestricted access to the engine area.
      \n1. Upkeep the engine.
      \n2. Prevent fires in the engine.
      \n3. Maintain a safe orbit.
      \n
      \nMedical Researcher 2>5-0-0
      \n\tThis position may need a little clarification. Their duty is to make sure that all experiments are safe and\nto conduct experiments that may help to improve the station. They will be generally idle until a new laboratory\nis constructed.
      \n1. Make sure the station is kept safe.
      \n2. Research medical properties of materials studied of Space Station 13.
      \n
      \nScientist 2>5-0-0
      \n\tThese people study the properties, particularly the toxic properties, of materials handled on SS13.\nTechnically they can also be called Plasma Technicians as plasma is the material they routinly handle.
      \n1. Research plasma
      \n2. Make sure all plasma is properly handled.
      \n
      \nMedical Doctor (Officer) 2>0-0-0
      \n\tPeople working this job should primarily stay in the medical area. They should make sure everyone goes to\nthe medical bay for treatment and examination. Also they should make sure that medical supplies are kept in\norder.
      \n1. Heal wounded people.
      \n2. Perform examinations of all personnel.
      \n3. Moniter usage of medical equipment.
      \n
      \nSecurity Officer 3>0-0-0
      \n\tThese people should attempt to keep the peace inside the station and make sure the station is kept safe. One\nside duty is to assist in repairing the station. They also work like general maintenance personnel. They are not\ngiven a weapon and must use their own resources.
      \n(Addendum: Updated/Elevated Security Protocols admit issuing of weapons to security personnel)
      \n1. Maintain order.
      \n2. Assist others.
      \n3. Repair structural problems.
      \n
      \nHead of Security 4>5-2-2
      \n\tPeople assigned as Head of Security should issue orders to the security staff. They should\nalso carefully moderate the usage of all security equipment. All security matters should be reported to this person.
      \n1. Oversee security.
      \n2. Assign patrol duties.
      \n3. Protect the station and staff.
      \n
      \nHead of Personnel 4>4-2-2
      \n\tPeople assigned as head of personnel will find themselves moderating all actions done by personnel. \nAlso they have the ability to assign jobs and access levels.
      \n1. Assign duties.
      \n2. Moderate personnel.
      \n3. Moderate research.
      \n
      \nCaptain 5>5-5-5 (unrestricted station wide access)
      \n\tThis is the highest position youi can aquire on Space Station 13. They are allowed anywhere inside the\nspace station and therefore should protect their ID card. They also have the ability to assign positions\nand access levels. They should not abuse their power.
      \n1. Assign all positions on SS13
      \n2. Inspect the station for any problems.
      \n3. Perform administrative duties.
      \n" - -/obj/item/weapon/paper/sop - name = "paper- 'Standard Operating Procedure'" - info = "Alert Levels:
      \nBlue- Emergency
      \n\t1. Caused by fire
      \n\t2. Caused by manual interaction
      \n\tAction:
      \n\t\tClose all fire doors. These can only be opened by reseting the alarm
      \nRed- Ejection/Self Destruct
      \n\t1. Caused by module operating computer.
      \n\tAction:
      \n\t\tAfter the specified time the module will eject completely.
      \n
      \nEngine Maintenance Instructions:
      \n\tShut off ignition systems:
      \n\tActivate internal power
      \n\tActivate orbital balance matrix
      \n\tRemove volatile liquids from area
      \n\tWear a fire suit
      \n
      \n\tAfter
      \n\t\tDecontaminate
      \n\t\tVisit medical examiner
      \n
      \nToxin Laboratory Procedure:
      \n\tWear a gas mask regardless
      \n\tGet an oxygen tank.
      \n\tActivate internal atmosphere
      \n
      \n\tAfter
      \n\t\tDecontaminate
      \n\t\tVisit medical examiner
      \n
      \nDisaster Procedure:
      \n\tFire:
      \n\t\tActivate sector fire alarm.
      \n\t\tMove to a safe area.
      \n\t\tGet a fire suit
      \n\t\tAfter:
      \n\t\t\tAssess Damage
      \n\t\t\tRepair damages
      \n\t\t\tIf needed, Evacuate
      \n\tMeteor Shower:
      \n\t\tActivate fire alarm
      \n\t\tMove to the back of ship
      \n\t\tAfter
      \n\t\t\tRepair damage
      \n\t\t\tIf needed, Evacuate
      \n\tAccidental Reentry:
      \n\t\tActivate fire alrms in front of ship.
      \n\t\tMove volatile matter to a fire proof area!
      \n\t\tGet a fire suit.
      \n\t\tStay secure until an emergency ship arrives.
      \n
      \n\t\tIf ship does not arrive-
      \n\t\t\tEvacuate to a nearby safe area!" - -/obj/item/weapon/paper/centcom - name = "paper- 'Official Bulletin'" - info = "
      Centcom Security
      Port Division
      Official Bulletin

      Inspector,
      There is an emergency shuttle arriving today.

      Approval is restricted to Nanotrasen employees only. Deny all other entrants.

      Centcom Port Commissioner" - -/obj/item/weapon/paper/range - name = "paper- Firing Range Instructions" - info = "Directions:
      First you'll want to make sure there is a target stake in the center of the magnetic platform. Next, take an aluminum target from the crates back there and slip it into the stake. Make sure it clicks! Next, there should be a control console mounted on the wall somewhere in the room.

      This control console dictates the behaviors of the magnetic platform, which can move your firing target around to simulate real-world combat situations. From here, you can turn off the magnets or adjust their electromagnetic levels and magnetic fields. The electricity level dictates the strength of the pull - you will usually want this to be the same value as the speed. The magnetic field level dictates how far the magnetic pull reaches.

      Speed and path are the next two settings. Speed is associated with how fast the machine loops through the designated path. Paths dictate where the magnetic field will be centered at what times. There should be a pre-fabricated path input already. You can enable moving to observe how the path affects the way the stake moves. To script your own path, look at the following key:


      N: North
      S: South
      E: East
      W: West
      C: Center
      R: Random (results may vary)
      ; or &: separators. They are not necessary but can make the path string better visible." - -/obj/item/weapon/paper/mining - name = "paper- Smelting Operations Closed" - info = "**NOTICE**

      Smelting operations moved on-station.

      Take your unrefined ore to the Redeption Machine in the Delivery Office to redeem points.

      --SS13 Command" - -/obj/item/weapon/paper/crumpled - name = "paper scrap" - icon_state = "scrap" - -/obj/item/weapon/paper/crumpled/update_icon() - return - - -/obj/item/weapon/paper/crumpled/bloody - icon_state = "scrap_bloodied" +/* + * Paper + * also scraps of paper + * + * lipstick wiping is in code/game/objects/items/weapons/cosmetics.dm! + */ + +/obj/item/weapon/paper + name = "paper" + gender = NEUTER + icon = 'icons/obj/bureaucracy.dmi' + icon_state = "paper" + throwforce = 0 + w_class = 1 + throw_range = 1 + throw_speed = 1 + layer = 3 + pressure_resistance = 0 + slot_flags = SLOT_HEAD + body_parts_covered = HEAD + burn_state = FLAMMABLE + burntime = 5 + + var/info //What's actually written on the paper. + var/info_links //A different version of the paper which includes html links at fields and EOF + var/stamps //The (text for the) stamps on the paper. + var/fields //Amount of user created fields + var/list/stamped + var/rigged = 0 + var/spam_flag = 0 + + +/obj/item/weapon/paper/New() + ..() + pixel_y = rand(-8, 8) + pixel_x = rand(-9, 9) + spawn(2) + update_icon() + updateinfolinks() + + +/obj/item/weapon/paper/update_icon() + if(burn_state == ON_FIRE) + icon_state = "paper_onfire" + return + if(info) + icon_state = "paper_words" + return + icon_state = "paper" + + +/obj/item/weapon/paper/examine(mob/user) + ..() + var/datum/asset/assets = get_asset_datum(/datum/asset/simple/paper) + assets.send(user) + + if(in_range(user, src) || isobserver(user)) + if( !(ishuman(user) || isobserver(user) || issilicon(user)) ) + user << browse("[name][stars(info)]
      [stamps]", "window=[name]") + onclose(user, "[name]") + else + user << browse("[name][info]
      [stamps]", "window=[name]") + onclose(user, "[name]") + else + user << "It is too far away." + + +/obj/item/weapon/paper/verb/rename() + set name = "Rename paper" + set category = "Object" + set src in usr + + if(usr.stat || !usr.canmove || usr.restrained()) + return + + if(!ishuman(usr)) + return + var/mob/living/carbon/human/H = usr + if(H.disabilities & CLUMSY && prob(25)) + H << "You cut yourself on the paper! Ahhhh! Ahhhhh!" + H.damageoverlaytemp = 9001 + return + var/n_name = stripped_input(usr, "What would you like to label the paper?", "Paper Labelling", null, MAX_NAME_LEN) + if((loc == usr && usr.stat == 0)) + name = "paper[(n_name ? text("- '[n_name]'") : null)]" + add_fingerprint(usr) + +/obj/item/weapon/paper/suicide_act(mob/user) + user.visible_message("[user] scratches a grid on their wrist with the paper! It looks like \he's trying to commit sudoku..") + return (BRUTELOSS) + +/obj/item/weapon/paper/attack_self(mob/user) + user.examinate(src) + if(rigged && (SSevent.holidays && SSevent.holidays[APRIL_FOOLS])) + if(spam_flag == 0) + spam_flag = 1 + playsound(loc, 'sound/items/bikehorn.ogg', 50, 1) + spawn(20) + spam_flag = 0 + + +/obj/item/weapon/paper/attack_ai(mob/living/silicon/ai/user) + var/dist + if(istype(user) && user.current) //is AI + dist = get_dist(src, user.current) + else //cyborg or AI not seeing through a camera + dist = get_dist(src, user) + if(dist < 2) + usr << browse("[name][info]
      [stamps]", "window=[name]") + onclose(usr, "[name]") + else + usr << browse("[name][stars(info)]
      [stamps]", "window=[name]") + onclose(usr, "[name]") + + +/obj/item/weapon/paper/proc/addtofield(id, text, links = 0) + var/locid = 0 + var/laststart = 1 + var/textindex = 1 + while(1) //I know this can cause infinite loops and fuck up the whole server, but the if(istart==0) should be safe as fuck + var/istart = 0 + if(links) + istart = findtext(info_links, "", laststart) + else + istart = findtext(info, "", laststart) + + if(istart == 0) + return //No field found with matching id + + laststart = istart+1 + locid++ + if(locid == id) + var/iend = 1 + if(links) + iend = findtext(info_links, "", istart) + else + iend = findtext(info, "", istart) + + //textindex = istart+26 + textindex = iend + break + + if(links) + var/before = copytext(info_links, 1, textindex) + var/after = copytext(info_links, textindex) + info_links = before + text + after + else + var/before = copytext(info, 1, textindex) + var/after = copytext(info, textindex) + info = before + text + after + updateinfolinks() + + +/obj/item/weapon/paper/proc/updateinfolinks() + info_links = info + var/i = 0 + for(i=1,i<=fields,i++) + addtofield(i, "write", 1) + info_links = info_links + "write" + + +/obj/item/weapon/paper/proc/clearpaper() + info = null + stamps = null + stamped = list() + overlays.Cut() + updateinfolinks() + update_icon() + + +/obj/item/weapon/paper/proc/parsepencode(t, obj/item/weapon/pen/P, mob/user, iscrayon = 0) + if(length(t) < 1) //No input means nothing needs to be parsed + return + +// t = copytext(sanitize(t),1,MAX_MESSAGE_LEN) + + t = replacetext(t, "\[center\]", "
      ") + t = replacetext(t, "\[/center\]", "
      ") + t = replacetext(t, "\[br\]", "
      ") + t = replacetext(t, "\[b\]", "") + t = replacetext(t, "\[/b\]", "") + t = replacetext(t, "\[i\]", "") + t = replacetext(t, "\[/i\]", "") + t = replacetext(t, "\[u\]", "") + t = replacetext(t, "\[/u\]", "") + t = replacetext(t, "\[large\]", "") + t = replacetext(t, "\[/large\]", "") + t = replacetext(t, "\[sign\]", "[user.real_name]") + t = replacetext(t, "\[field\]", "") + + if(!iscrayon) + t = replacetext(t, "\[*\]", "
    • ") + t = replacetext(t, "\[hr\]", "
      ") + t = replacetext(t, "\[small\]", "") + t = replacetext(t, "\[/small\]", "") + t = replacetext(t, "\[list\]", "
        ") + t = replacetext(t, "\[/list\]", "
      ") + + t = "[t]" + else // If it is a crayon, and he still tries to use these, make them empty! + var/obj/item/toy/crayon/C = P + t = replacetext(t, "\[*\]", "") + t = replacetext(t, "\[hr\]", "") + t = replacetext(t, "\[small\]", "") + t = replacetext(t, "\[/small\]", "") + t = replacetext(t, "\[list\]", "") + t = replacetext(t, "\[/list\]", "") + + t = "[t]" + +// t = replacetext(t, "#", "") // Junk converted to nothing! + +//Count the fields + var/laststart = 1 + while(1) + var/i = findtext(t, "", laststart) + if(i == 0) + break + laststart = i+1 + fields++ + + return t + + +/obj/item/weapon/paper/proc/openhelp(mob/user) + user << browse({"Pen Help + +
      Crayon&Pen commands

      +
      + \[br\] : Creates a linebreak.
      + \[center\] - \[/center\] : Centers the text.
      + \[b\] - \[/b\] : Makes the text bold.
      + \[i\] - \[/i\] : Makes the text italic.
      + \[u\] - \[/u\] : Makes the text underlined.
      + \[large\] - \[/large\] : Increases the size of the text.
      + \[sign\] : Inserts a signature of your name in a foolproof way.
      + \[field\] : Inserts an invisible field which lets you start type from there. Useful for forms.
      +
      +
      Pen exclusive commands

      + \[small\] - \[/small\] : Decreases the size of the text.
      + \[list\] - \[/list\] : A list.
      + \[*\] : A dot used for lists.
      + \[hr\] : Adds a horizontal rule. + "}, "window=paper_help") + + +/obj/item/weapon/paper/Topic(href, href_list) + ..() + if(usr.stat || usr.restrained()) + return + + if(href_list["write"]) + var/id = href_list["write"] + var/t = stripped_multiline_input("Enter what you want to write:", "Write") + if(!t) + return + var/obj/item/i = usr.get_active_hand() //Check to see if he still got that darn pen, also check if he's using a crayon or pen. + var/iscrayon = 0 + if(!istype(i, /obj/item/weapon/pen)) + if(!istype(i, /obj/item/toy/crayon)) + return + 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 + return + + t = parsepencode(t, i, usr, iscrayon) // Encode everything from pencode to html + + if(t != null) //No input from the user means nothing needs to be added + if(id!="end") + addtofield(text2num(id), t) // He wants to edit a field, let him. + else + info += t // Oh, he wants to edit to the end of the file, let him. + updateinfolinks() + + usr << browse("[name][info_links]
      [stamps]", "window=[name]") // Update the window + update_icon() + + +/obj/item/weapon/paper/attackby(obj/item/weapon/P, mob/living/carbon/human/user, params) + ..() + + if(burn_state == ON_FIRE) + return + + if(is_blind(user)) + return + + if(istype(P, /obj/item/weapon/pen) || istype(P, /obj/item/toy/crayon)) + if(user.IsAdvancedToolUser()) + user << browse("[name][info_links]
      [stamps]", "window=[name]") + return + else + user << "You don't know how to read or write." + return + if(istype(src, /obj/item/weapon/paper/talisman/)) + user << "[P]'s ink fades away shortly after it is written." + return + + else if(istype(P, /obj/item/weapon/stamp)) + if(!in_range(src, usr) && loc != user && !istype(loc, /obj/item/weapon/clipboard) && loc.loc != user && user.get_active_hand() != P) + return + + stamps += "" + + var/image/stampoverlay = image('icons/obj/bureaucracy.dmi') + stampoverlay.pixel_x = rand(-2, 2) + stampoverlay.pixel_y = rand(-3, 2) + + stampoverlay.icon_state = "paper_[P.icon_state]" + + if(!stamped) + stamped = new + stamped += P.type + overlays += stampoverlay + + user << "You stamp the paper with your rubber stamp." + + if(P.is_hot()) + if(user.disabilities & CLUMSY && prob(10)) + user.visible_message("[user] accidentally ignites themselves!", \ + "You miss the paper and accidentally light yourself on fire!") + user.unEquip(P) + user.adjust_fire_stacks(1) + user.IgniteMob() + return + + if(!(in_range(user, src))) //to prevent issues as a result of telepathically lighting a paper + return + + user.unEquip(src) + user.visible_message("[user] lights [src] ablaze with [P]!", "You light [src] on fire!") + fire_act() + + + + add_fingerprint(user) + +/obj/item/weapon/paper/fire_act() + ..(0) + icon_state = "paper_onfire" + info = "[stars(info)]" + + +/obj/item/weapon/paper/extinguish() + ..() + update_icon() + +/* + * Premade paper + */ + +/obj/item/weapon/paper/Court + name = "paper- 'Judgement'" + info = "For crimes against the station, the offender is sentenced to:
      \n
      \n" + +/obj/item/weapon/paper/Toxin + name = "paper- 'Chemical Information'" + info = "Known Onboard Toxins:
      \n\tGrade A Semi-Liquid Plasma:
      \n\t\tHighly poisonous. You cannot sustain concentrations above 15 units.
      \n\t\tA gas mask fails to filter plasma after 50 units.
      \n\t\tWill attempt to diffuse like a gas.
      \n\t\tFiltered by scrubbers.
      \n\t\tThere is a bottled version which is very different
      \n\t\t\tfrom the version found in canisters!
      \n
      \n\t\tWARNING: Highly Flammable. Keep away from heat sources
      \n\t\texcept in a enclosed fire area!
      \n\t\tWARNING: It is a crime to use this without authorization.
      \nKnown Onboard Anti-Toxin:
      \n\tAnti-Toxin Type 01P: Works against Grade A Plasma.
      \n\t\tBest if injected directly into bloodstream.
      \n\t\tA full injection is in every regular Med-Kit.
      \n\t\tSpecial toxin Kits hold around 7.
      \n
      \nKnown Onboard Chemicals (other):
      \n\tRejuvenation T#001:
      \n\t\tEven 1 unit injected directly into the bloodstream
      \n\t\t\twill cure paralysis and sleep toxins.
      \n\t\tIf administered to a dying patient it will prevent
      \n\t\t\tfurther damage for about units*3 seconds.
      \n\t\t\tit will not cure them or allow them to be cured.
      \n\t\tIt can be administeredd to a non-dying patient
      \n\t\t\tbut the chemicals disappear just as fast.
      \n\tMorphine T#054:
      \n\t\t5 units wilkl induce precisely 1 minute of sleep.
      \n\t\t\tThe effect are cumulative.
      \n\t\tWARNING: It is a crime to use this without authorization" + +/obj/item/weapon/paper/courtroom + name = "paper- 'A Crash Course in Legal SOP on SS13'" + info = "Roles:
      \nThe Detective is basically the investigator and prosecutor.
      \nThe Staff Assistant can perform these functions with written authority from the Detective.
      \nThe Captain/HoP/Warden is ct as the judicial authority.
      \nThe Security Officers are responsible for executing warrants, security during trial, and prisoner transport.
      \n
      \nInvestigative Phase:
      \nAfter the crime has been committed the Detective's job is to gather evidence and try to ascertain not only who did it but what happened. He must take special care to catalogue everything and don't leave anything out. Write out all the evidence on paper. Make sure you take an appropriate number of fingerprints. IF he must ask someone questions he has permission to confront them. If the person refuses he can ask a judicial authority to write a subpoena for questioning. If again he fails to respond then that person is to be jailed as insubordinate and obstructing justice. Said person will be released after he cooperates.
      \n
      \nONCE the FT has a clear idea as to who the criminal is he is to write an arrest warrant on the piece of paper. IT MUST LIST THE CHARGES. The FT is to then go to the judicial authority and explain a small version of his case. If the case is moderately acceptable the authority should sign it. Security must then execute said warrant.
      \n
      \nPre-Pre-Trial Phase:
      \nNow a legal representative must be presented to the defendant if said defendant requests one. That person and the defendant are then to be given time to meet (in the jail IS ACCEPTABLE). The defendant and his lawyer are then to be given a copy of all the evidence that will be presented at trial (rewriting it all on paper is fine). THIS IS CALLED THE DISCOVERY PACK. With a few exceptions, THIS IS THE ONLY EVIDENCE BOTH SIDES MAY USE AT TRIAL. IF the prosecution will be seeking the death penalty it MUST be stated at this time. ALSO if the defense will be seeking not guilty by mental defect it must state this at this time to allow ample time for examination.
      \nNow at this time each side is to compile a list of witnesses. By default, the defendant is on both lists regardless of anything else. Also the defense and prosecution can compile more evidence beforehand BUT in order for it to be used the evidence MUST also be given to the other side.\nThe defense has time to compile motions against some evidence here.
      \nPossible Motions:
      \n1. Invalidate Evidence- Something with the evidence is wrong and the evidence is to be thrown out. This includes irrelevance or corrupt security.
      \n2. Free Movement- Basically the defendant is to be kept uncuffed before and during the trial.
      \n3. Subpoena Witness- If the defense presents god reasons for needing a witness but said person fails to cooperate then a subpoena is issued.
      \n4. Drop the Charges- Not enough evidence is there for a trial so the charges are to be dropped. The FT CAN RETRY but the judicial authority must carefully reexamine the new evidence.
      \n5. Declare Incompetent- Basically the defendant is insane. Once this is granted a medical official is to examine the patient. If he is indeed insane he is to be placed under care of the medical staff until he is deemed competent to stand trial.
      \n
      \nALL SIDES MOVE TO A COURTROOM
      \nPre-Trial Hearings:
      \nA judicial authority and the 2 sides are to meet in the trial room. NO ONE ELSE BESIDES A SECURITY DETAIL IS TO BE PRESENT. The defense submits a plea. If the plea is guilty then proceed directly to sentencing phase. Now the sides each present their motions to the judicial authority. He rules on them. Each side can debate each motion. Then the judicial authority gets a list of crew members. He first gets a chance to look at them all and pick out acceptable and available jurors. Those jurors are then called over. Each side can ask a few questions and dismiss jurors they find too biased. HOWEVER before dismissal the judicial authority MUST agree to the reasoning.
      \n
      \nThe Trial:
      \nThe trial has three phases.
      \n1. Opening Arguments- Each side can give a short speech. They may not present ANY evidence.
      \n2. Witness Calling/Evidence Presentation- The prosecution goes first and is able to call the witnesses on his approved list in any order. He can recall them if necessary. During the questioning the lawyer may use the evidence in the questions to help prove a point. After every witness the other side has a chance to cross-examine. After both sides are done questioning a witness the prosecution can present another or recall one (even the EXACT same one again!). After prosecution is done the defense can call witnesses. After the initial cases are presented both sides are free to call witnesses on either list.
      \nFINALLY once both sides are done calling witnesses we move onto the next phase.
      \n3. Closing Arguments- Same as opening.
      \nThe jury then deliberates IN PRIVATE. THEY MUST ALL AGREE on a verdict. REMEMBER: They mix between some charges being guilty and others not guilty (IE if you supposedly killed someone with a gun and you unfortunately picked up a gun without authorization then you CAN be found not guilty of murder BUT guilty of possession of illegal weaponry.). Once they have agreed they present their verdict. If unable to reach a verdict and feel they will never they call a deadlocked jury and we restart at Pre-Trial phase with an entirely new set of jurors.
      \n
      \nSentencing Phase:
      \nIf the death penalty was sought (you MUST have gone through a trial for death penalty) then skip to the second part.
      \nI. Each side can present more evidence/witnesses in any order. There is NO ban on emotional aspects or anything. The prosecution is to submit a suggested penalty. After all the sides are done then the judicial authority is to give a sentence.
      \nII. The jury stays and does the same thing as I. Their sole job is to determine if the death penalty is applicable. If NOT then the judge selects a sentence.
      \n
      \nTADA you're done. Security then executes the sentence and adds the applicable convictions to the person's record.
      \n" + +/obj/item/weapon/paper/hydroponics + name = "paper- 'Greetings from Billy Bob'" + info = "Hey fellow botanist!
      \n
      \nI didn't trust the station folk so I left
      \na couple of weeks ago. But here's some
      \ninstructions on how to operate things here.
      \nYou can grow plants and each iteration they become
      \nstronger, more potent and have better yield, if you
      \nknow which ones to pick. Use your botanist's analyzer
      \nfor that. You can turn harvested plants into seeds
      \nat the seed extractor, and replant them for better stuff!
      \nSometimes if the weed level gets high in the tray
      \nmutations into different mushroom or weed species have
      \nbeen witnessed. On the rare occassion even weeds mutate!
      \n
      \nEither way, have fun!
      \n
      \nBest regards,
      \nBilly Bob Johnson.
      \n
      \nPS.
      \nHere's a few tips:
      \nIn nettles, potency = damage
      \nIn amanitas, potency = deadliness + side effect
      \nIn Liberty caps, potency = drug power + effect
      \nIn chilis, potency = heat
      \nNutrients keep mushrooms alive!
      \nWater keeps weeds such as nettles alive!
      \nAll other plants need both." + +/obj/item/weapon/paper/djstation + name = "paper - 'DJ Listening Outpost'" + info = "Welcome new owner!

      You have purchased the latest in listening equipment. The telecommunication setup we created is the best in listening to common and private radio frequencies. Here is a step by step guide to start listening in on those saucy radio channels:
      1. Equip yourself with a multitool
      2. Use the multitool on the relay.
      3. Turn it on. It has already been configured for you to listen on.
      Simple as that. Now to listen to the private channels, you'll have to configure the intercoms. They are located on the front desk. Here is a list of frequencies for you to listen on.
      • 145.9 - Common Channel
      • 144.7 - Private AI Channel
      • 135.9 - Security Channel
      • 135.7 - Engineering Channel
      • 135.5 - Medical Channel
      • 135.3 - Command Channel
      • 135.1 - Science Channel
      • 134.9 - Service Channel
      • 134.7 - Supply Channel
      • " + +/obj/item/weapon/paper/jobs + name = "paper- 'Job Information'" + info = "Information on all formal jobs that can be assigned on Space Station 13 can be found on this document.
        \nThe data will be in the following form.
        \nGenerally lower ranking positions come first in this list.
        \n
        \nJob Name general access>lab access-engine access-systems access (atmosphere control)
        \n\tJob Description
        \nJob Duties (in no particular order)
        \nTips (where applicable)
        \n
        \nResearch Assistant 1>1-0-0
        \n\tThis is probably the lowest level position. Anyone who enters the space station after the initial job\nassignment will automatically receive this position. Access with this is restricted. Head of Personnel should\nappropriate the correct level of assistance.
        \n1. Assist the researchers.
        \n2. Clean up the labs.
        \n3. Prepare materials.
        \n
        \nStaff Assistant 2>0-0-0
        \n\tThis position assists the security officer in his duties. The staff assisstants should primarily br\npatrolling the ship waiting until they are needed to maintain ship safety.\n(Addendum: Updated/Elevated Security Protocols admit issuing of low level weapons to security personnel)
        \n1. Patrol ship/Guard key areas
        \n2. Assist security officer
        \n3. Perform other security duties.
        \n
        \nTechnical Assistant 1>0-0-1
        \n\tThis is yet another low level position. The technical assistant helps the engineer and the statian\ntechnician with the upkeep and maintenance of the station. This job is very important because it usually\ngets to be a heavy workload on station technician and these helpers will alleviate that.
        \n1. Assist Station technician and Engineers.
        \n2. Perform general maintenance of station.
        \n3. Prepare materials.
        \n
        \nMedical Assistant 1>1-0-0
        \n\tThis is the fourth position yet it is slightly less common. This position doesn't have much power\noutside of the med bay. Consider this position like a nurse who helps to upkeep medical records and the\nmaterials (filling syringes and checking vitals)
        \n1. Assist the medical personnel.
        \n2. Update medical files.
        \n3. Prepare materials for medical operations.
        \n
        \nResearch Technician 2>3-0-0
        \n\tThis job is primarily a step up from research assistant. These people generally do not get their own lab\nbut are more hands on in the experimentation process. At this level they are permitted to work as consultants to\nthe others formally.
        \n1. Inform superiors of research.
        \n2. Perform research alongside of official researchers.
        \n
        \nDetective 3>2-0-0
        \n\tThis job is in most cases slightly boring at best. Their sole duty is to\nperform investigations of crine scenes and analysis of the crime scene. This\nalleviates SOME of the burden from the security officer. This person's duty\nis to draw conclusions as to what happened and testify in court. Said person\nalso should stroe the evidence ly.
        \n1. Perform crime-scene investigations/draw conclusions.
        \n2. Store and catalogue evidence properly.
        \n3. Testify to superiors/inquieries on findings.
        \n
        \nStation Technician 2>0-2-3
        \n\tPeople assigned to this position must work to make sure all the systems aboard Space Station 13 are operable.\nThey should primarily work in the computer lab and repairing faulty equipment. They should work with the\natmospheric technician.
        \n1. Maintain SS13 systems.
        \n2. Repair equipment.
        \n
        \nAtmospheric Technician 3>0-0-4
        \n\tThese people should primarily work in the atmospheric control center and lab. They have the very important\njob of maintaining the delicate atmosphere on SS13.
        \n1. Maintain atmosphere on SS13
        \n2. Research atmospheres on the space station. (safely please!)
        \n
        \nEngineer 2>1-3-0
        \n\tPeople working as this should generally have detailed knowledge as to how the propulsion systems on SS13\nwork. They are one of the few classes that have unrestricted access to the engine area.
        \n1. Upkeep the engine.
        \n2. Prevent fires in the engine.
        \n3. Maintain a safe orbit.
        \n
        \nMedical Researcher 2>5-0-0
        \n\tThis position may need a little clarification. Their duty is to make sure that all experiments are safe and\nto conduct experiments that may help to improve the station. They will be generally idle until a new laboratory\nis constructed.
        \n1. Make sure the station is kept safe.
        \n2. Research medical properties of materials studied of Space Station 13.
        \n
        \nScientist 2>5-0-0
        \n\tThese people study the properties, particularly the toxic properties, of materials handled on SS13.\nTechnically they can also be called Plasma Technicians as plasma is the material they routinly handle.
        \n1. Research plasma
        \n2. Make sure all plasma is properly handled.
        \n
        \nMedical Doctor (Officer) 2>0-0-0
        \n\tPeople working this job should primarily stay in the medical area. They should make sure everyone goes to\nthe medical bay for treatment and examination. Also they should make sure that medical supplies are kept in\norder.
        \n1. Heal wounded people.
        \n2. Perform examinations of all personnel.
        \n3. Moniter usage of medical equipment.
        \n
        \nSecurity Officer 3>0-0-0
        \n\tThese people should attempt to keep the peace inside the station and make sure the station is kept safe. One\nside duty is to assist in repairing the station. They also work like general maintenance personnel. They are not\ngiven a weapon and must use their own resources.
        \n(Addendum: Updated/Elevated Security Protocols admit issuing of weapons to security personnel)
        \n1. Maintain order.
        \n2. Assist others.
        \n3. Repair structural problems.
        \n
        \nHead of Security 4>5-2-2
        \n\tPeople assigned as Head of Security should issue orders to the security staff. They should\nalso carefully moderate the usage of all security equipment. All security matters should be reported to this person.
        \n1. Oversee security.
        \n2. Assign patrol duties.
        \n3. Protect the station and staff.
        \n
        \nHead of Personnel 4>4-2-2
        \n\tPeople assigned as head of personnel will find themselves moderating all actions done by personnel. \nAlso they have the ability to assign jobs and access levels.
        \n1. Assign duties.
        \n2. Moderate personnel.
        \n3. Moderate research.
        \n
        \nCaptain 5>5-5-5 (unrestricted station wide access)
        \n\tThis is the highest position youi can aquire on Space Station 13. They are allowed anywhere inside the\nspace station and therefore should protect their ID card. They also have the ability to assign positions\nand access levels. They should not abuse their power.
        \n1. Assign all positions on SS13
        \n2. Inspect the station for any problems.
        \n3. Perform administrative duties.
        \n" + +/obj/item/weapon/paper/sop + name = "paper- 'Standard Operating Procedure'" + info = "Alert Levels:
        \nBlue- Emergency
        \n\t1. Caused by fire
        \n\t2. Caused by manual interaction
        \n\tAction:
        \n\t\tClose all fire doors. These can only be opened by reseting the alarm
        \nRed- Ejection/Self Destruct
        \n\t1. Caused by module operating computer.
        \n\tAction:
        \n\t\tAfter the specified time the module will eject completely.
        \n
        \nEngine Maintenance Instructions:
        \n\tShut off ignition systems:
        \n\tActivate internal power
        \n\tActivate orbital balance matrix
        \n\tRemove volatile liquids from area
        \n\tWear a fire suit
        \n
        \n\tAfter
        \n\t\tDecontaminate
        \n\t\tVisit medical examiner
        \n
        \nToxin Laboratory Procedure:
        \n\tWear a gas mask regardless
        \n\tGet an oxygen tank.
        \n\tActivate internal atmosphere
        \n
        \n\tAfter
        \n\t\tDecontaminate
        \n\t\tVisit medical examiner
        \n
        \nDisaster Procedure:
        \n\tFire:
        \n\t\tActivate sector fire alarm.
        \n\t\tMove to a safe area.
        \n\t\tGet a fire suit
        \n\t\tAfter:
        \n\t\t\tAssess Damage
        \n\t\t\tRepair damages
        \n\t\t\tIf needed, Evacuate
        \n\tMeteor Shower:
        \n\t\tActivate fire alarm
        \n\t\tMove to the back of ship
        \n\t\tAfter
        \n\t\t\tRepair damage
        \n\t\t\tIf needed, Evacuate
        \n\tAccidental Reentry:
        \n\t\tActivate fire alrms in front of ship.
        \n\t\tMove volatile matter to a fire proof area!
        \n\t\tGet a fire suit.
        \n\t\tStay secure until an emergency ship arrives.
        \n
        \n\t\tIf ship does not arrive-
        \n\t\t\tEvacuate to a nearby safe area!" + +/obj/item/weapon/paper/centcom + name = "paper- 'Official Bulletin'" + info = "
        Centcom Security
        Port Division
        Official Bulletin

        Inspector,
        There is an emergency shuttle arriving today.

        Approval is restricted to Nanotrasen employees only. Deny all other entrants.

        Centcom Port Commissioner" + +/obj/item/weapon/paper/range + name = "paper- Firing Range Instructions" + info = "Directions:
        First you'll want to make sure there is a target stake in the center of the magnetic platform. Next, take an aluminum target from the crates back there and slip it into the stake. Make sure it clicks! Next, there should be a control console mounted on the wall somewhere in the room.

        This control console dictates the behaviors of the magnetic platform, which can move your firing target around to simulate real-world combat situations. From here, you can turn off the magnets or adjust their electromagnetic levels and magnetic fields. The electricity level dictates the strength of the pull - you will usually want this to be the same value as the speed. The magnetic field level dictates how far the magnetic pull reaches.

        Speed and path are the next two settings. Speed is associated with how fast the machine loops through the designated path. Paths dictate where the magnetic field will be centered at what times. There should be a pre-fabricated path input already. You can enable moving to observe how the path affects the way the stake moves. To script your own path, look at the following key:


        N: North
        S: South
        E: East
        W: West
        C: Center
        R: Random (results may vary)
        ; or &: separators. They are not necessary but can make the path string better visible." + +/obj/item/weapon/paper/mining + name = "paper- Smelting Operations Closed" + info = "**NOTICE**

        Smelting operations moved on-station.

        Take your unrefined ore to the Redeption Machine in the Delivery Office to redeem points.

        --SS13 Command" + +/obj/item/weapon/paper/crumpled + name = "paper scrap" + icon_state = "scrap" + +/obj/item/weapon/paper/crumpled/update_icon() + return + + +/obj/item/weapon/paper/crumpled/bloody + icon_state = "scrap_bloodied" diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index afbe1928d1e..315628d6bc5 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -1,1190 +1,1190 @@ -#define APC_WIRE_IDSCAN 1 -#define APC_WIRE_MAIN_POWER1 2 -#define APC_WIRE_MAIN_POWER2 3 -#define APC_WIRE_AI_CONTROL 4 - -//update_state -#define UPSTATE_CELL_IN 1 -#define UPSTATE_OPENED1 2 -#define UPSTATE_OPENED2 4 -#define UPSTATE_MAINT 8 -#define UPSTATE_BROKE 16 -#define UPSTATE_BLUESCREEN 32 -#define UPSTATE_WIREEXP 64 -#define UPSTATE_ALLGOOD 128 - -//update_overlay -#define APC_UPOVERLAY_CHARGEING0 1 -#define APC_UPOVERLAY_CHARGEING1 2 -#define APC_UPOVERLAY_CHARGEING2 4 -#define APC_UPOVERLAY_EQUIPMENT0 8 -#define APC_UPOVERLAY_EQUIPMENT1 16 -#define APC_UPOVERLAY_EQUIPMENT2 32 -#define APC_UPOVERLAY_LIGHTING0 64 -#define APC_UPOVERLAY_LIGHTING1 128 -#define APC_UPOVERLAY_LIGHTING2 256 -#define APC_UPOVERLAY_ENVIRON0 512 -#define APC_UPOVERLAY_ENVIRON1 1024 -#define APC_UPOVERLAY_ENVIRON2 2048 -#define APC_UPOVERLAY_LOCKED 4096 -#define APC_UPOVERLAY_OPERATING 8192 - - -#define APC_UPDATE_ICON_COOLDOWN 200 // 20 seconds - -// the Area Power Controller (APC), formerly Power Distribution Unit (PDU) -// one per area, needs wire conection to power network through a terminal - -// controls power to devices in that area -// may be opened to change power cell -// three different channels (lighting/equipment/environ) - may each be set to on, off, or auto - - -//NOTE: STUFF STOLEN FROM AIRLOCK.DM thx - - -/obj/machinery/power/apc - name = "area power controller" - desc = "A control terminal for the area electrical systems." - - icon_state = "apc0" - anchored = 1 - use_power = 0 - req_access = list(access_engine_equip) - var/area/area - var/areastring = null - var/obj/item/weapon/stock_parts/cell/cell - var/start_charge = 90 // initial cell charge % - var/cell_type = 2500 // 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 - var/equipment = 3 - var/environ = 3 - var/operating = 1 - var/charging = 0 - var/chargemode = 1 - var/chargecount = 0 - var/locked = 1 - var/coverlocked = 1 - var/aidisabled = 0 - var/tdir = null - var/obj/machinery/power/terminal/terminal = null - var/lastused_light = 0 - var/lastused_equip = 0 - var/lastused_environ = 0 - var/lastused_total = 0 - var/main_status = 0 - var/wiresexposed = 0 - powernet = 0 // set so that APCs aren't found as powernet nodes //Hackish, Horrible, was like this before I changed it :( - var/malfhack = 0 //New var for my changes to AI malf. --NeoFite - var/mob/living/silicon/ai/malfai = null //See above --NeoFite -// luminosity = 1 - var/has_electronics = 0 // 0 - none, 1 - plugged in, 2 - secured by screwdriver - var/overload = 1 //used for the Blackout malf module - var/beenhit = 0 // used for counting how many times it has been hit, used for Aliens at the moment - var/mob/living/silicon/ai/occupier = null - var/longtermpower = 10 - var/datum/wires/apc/wires = null - var/auto_name = 0 - var/update_state = -1 - var/update_overlay = -1 - var/global/status_overlays = 0 - var/updating_icon = 0 - var/global/list/status_overlays_lock - var/global/list/status_overlays_charging - var/global/list/status_overlays_equipment - var/global/list/status_overlays_lighting - var/global/list/status_overlays_environ - -/obj/machinery/power/apc/connect_to_network() - //Override because the APC does not directly connect to the network; it goes through a terminal. - //The terminal is what the power computer looks for anyway. - if(!terminal) - make_terminal() - if(terminal) - terminal.connect_to_network() - -/obj/machinery/power/apc/New(turf/loc, var/ndir, var/building=0) - ..() - apcs_list += src - - wires = new(src) - // offset 24 pixels in direction of dir - // this allows the APC to be embedded in a wall, yet still inside an area - if (building) - dir = ndir - src.tdir = dir // to fix Vars bug - dir = SOUTH - - if(auto_name) - name = "[get_area(src)] APC" - - pixel_x = (src.tdir & 3)? 0 : (src.tdir == 4 ? 24 : -24) - pixel_y = (src.tdir & 3)? (src.tdir ==1 ? 24 : -24) : 0 - if (building==0) - init() - else - area = src.loc.loc:master - opened = 1 - operating = 0 - name = "[area.name] APC" - stat |= MAINT - src.update_icon() - spawn(5) - src.update() - -/obj/machinery/power/apc/Destroy() - apcs_list -= src - - if(malfai && operating) - if (ticker.mode.config_tag == "malfunction") - if (src.z == ZLEVEL_STATION) //if (is_type_in_list(get_area(src), the_station_areas)) - ticker.mode:apcs-- - area.power_light = 0 - area.power_equip = 0 - area.power_environ = 0 - area.power_change() - if(occupier) - malfvacate(1) - qdel(wires) - wires = null - if(cell) - qdel(cell) - if(terminal) - disconnect_terminal() - return ..() - -/obj/machinery/power/apc/proc/make_terminal() - // create a terminal object at the same position as original turf loc - // wires will attach to this - terminal = new/obj/machinery/power/terminal(src.loc) - terminal.dir = tdir - terminal.master = src - -/obj/machinery/power/apc/proc/init() - 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/stock_parts/cell(src) - cell.maxcharge = cell_type // cell_type is maximum charge (old default was 1000 or 2500 (values one and two respectively) - cell.charge = start_charge * cell.maxcharge / 100 // (convert percentage to actual value) - - var/area/A = src.loc.loc - - //if area isn't specified use current - if(isarea(A) && src.areastring == null) - src.area = A - else - src.area = get_area_name(areastring) - update_icon() - - make_terminal() - - spawn(5) - src.update() - -/obj/machinery/power/apc/examine(mob/user) - ..() - if(stat & BROKEN) - user << "Looks broken." - return - if(opened) - if(has_electronics && terminal) - user << "The cover is [opened==2?"removed":"open"] and the power cell is [ cell ? "installed" : "missing"]." - else - user << "It's [!terminal?" not":""]wired up." - user << "The electronics are[!has_electronics?"n't":""] installed." - - else - if (stat & MAINT) - user << "The cover is closed. Something is wrong with it. It doesn't work." - else if (malfhack) - user << "The cover is broken. It may be hard to force it open." - else - user << "The cover is closed." - - -// update the APC icon to show the three base states -// also add overlays for indicator lights -/obj/machinery/power/apc/update_icon() - if (!status_overlays) - status_overlays = 1 - status_overlays_lock = new - status_overlays_charging = new - status_overlays_equipment = new - status_overlays_lighting = new - status_overlays_environ = new - - status_overlays_lock.len = 2 - status_overlays_charging.len = 3 - status_overlays_equipment.len = 4 - status_overlays_lighting.len = 4 - status_overlays_environ.len = 4 - - status_overlays_lock[1] = image(icon, "apcox-0") // 0=blue 1=red - status_overlays_lock[2] = image(icon, "apcox-1") - - status_overlays_charging[1] = image(icon, "apco3-0") - status_overlays_charging[2] = image(icon, "apco3-1") - status_overlays_charging[3] = image(icon, "apco3-2") - - status_overlays_equipment[1] = image(icon, "apco0-0") - status_overlays_equipment[2] = image(icon, "apco0-1") - status_overlays_equipment[3] = image(icon, "apco0-2") - status_overlays_equipment[4] = image(icon, "apco0-3") - - status_overlays_lighting[1] = image(icon, "apco1-0") - status_overlays_lighting[2] = image(icon, "apco1-1") - status_overlays_lighting[3] = image(icon, "apco1-2") - status_overlays_lighting[4] = image(icon, "apco1-3") - - status_overlays_environ[1] = image(icon, "apco2-0") - status_overlays_environ[2] = image(icon, "apco2-1") - status_overlays_environ[3] = image(icon, "apco2-2") - status_overlays_environ[4] = image(icon, "apco2-3") - - var/update = check_updates() //returns 0 if no need to update icons. - // 1 if we need to update the icon_state - // 2 if we need to update the overlays - if(!update) - return - - if(update & 1) // Updating the icon state - if(update_state & UPSTATE_ALLGOOD) - icon_state = "apc0" - else if(update_state & (UPSTATE_OPENED1|UPSTATE_OPENED2)) - var/basestate = "apc[ cell ? "2" : "1" ]" - if(update_state & UPSTATE_OPENED1) - if(update_state & (UPSTATE_MAINT|UPSTATE_BROKE)) - icon_state = "apcmaint" //disabled APC cannot hold cell - else - icon_state = basestate - else if(update_state & UPSTATE_OPENED2) - if (update_state & UPSTATE_BROKE || malfhack) - icon_state = "[basestate]-b-nocover" - else - icon_state = "[basestate]-nocover" - else if(update_state & UPSTATE_BROKE) - icon_state = "apc-b" - else if(update_state & UPSTATE_BLUESCREEN) - icon_state = "apcemag" - else if(update_state & UPSTATE_WIREEXP) - icon_state = "apcewires" - - if(!(update_state & UPSTATE_ALLGOOD)) - if(overlays.len) - overlays = 0 - - if(update & 2) - if(overlays.len) - overlays.len = 0 - if(!(stat & (BROKEN|MAINT)) && update_state & UPSTATE_ALLGOOD) - overlays += status_overlays_lock[locked+1] - overlays += status_overlays_charging[charging+1] - if(operating) - overlays += status_overlays_equipment[equipment+1] - overlays += status_overlays_lighting[lighting+1] - overlays += status_overlays_environ[environ+1] - - -/obj/machinery/power/apc/proc/check_updates() - - var/last_update_state = update_state - var/last_update_overlay = update_overlay - update_state = 0 - update_overlay = 0 - - if(cell) - update_state |= UPSTATE_CELL_IN - if(stat & BROKEN) - update_state |= UPSTATE_BROKE - if(stat & MAINT) - update_state |= UPSTATE_MAINT - if(opened) - if(opened==1) - update_state |= UPSTATE_OPENED1 - if(opened==2) - update_state |= UPSTATE_OPENED2 - else if(emagged || malfai) - update_state |= UPSTATE_BLUESCREEN - else if(wiresexposed) - update_state |= UPSTATE_WIREEXP - if(update_state <= 1) - update_state |= UPSTATE_ALLGOOD - - if(operating) - update_overlay |= APC_UPOVERLAY_OPERATING - - if(update_state & UPSTATE_ALLGOOD) - if(locked) - update_overlay |= APC_UPOVERLAY_LOCKED - - if(!charging) - update_overlay |= APC_UPOVERLAY_CHARGEING0 - else if(charging == 1) - update_overlay |= APC_UPOVERLAY_CHARGEING1 - else if(charging == 2) - update_overlay |= APC_UPOVERLAY_CHARGEING2 - - if (!equipment) - update_overlay |= APC_UPOVERLAY_EQUIPMENT0 - else if(equipment == 1) - update_overlay |= APC_UPOVERLAY_EQUIPMENT1 - else if(equipment == 2) - update_overlay |= APC_UPOVERLAY_EQUIPMENT2 - - if(!lighting) - update_overlay |= APC_UPOVERLAY_LIGHTING0 - else if(lighting == 1) - update_overlay |= APC_UPOVERLAY_LIGHTING1 - else if(lighting == 2) - update_overlay |= APC_UPOVERLAY_LIGHTING2 - - if(!environ) - update_overlay |= APC_UPOVERLAY_ENVIRON0 - else if(environ==1) - update_overlay |= APC_UPOVERLAY_ENVIRON1 - else if(environ==2) - update_overlay |= APC_UPOVERLAY_ENVIRON2 - - - var/results = 0 - if(last_update_state == update_state && last_update_overlay == update_overlay) - return 0 - if(last_update_state != update_state) - results += 1 - if(last_update_overlay != update_overlay) - results += 2 - return results - -// Used in process so it doesn't update the icon too much -/obj/machinery/power/apc/proc/queue_icon_update() - - if(!updating_icon) - updating_icon = 1 - // Start the update - spawn(APC_UPDATE_ICON_COOLDOWN) - update_icon() - updating_icon = 0 - -//attack with an item - open/close cover, insert cell, or (un)lock interface - -/obj/machinery/power/apc/attackby(obj/item/W, mob/living/user, params) - - if (istype(user, /mob/living/silicon) && get_dist(src,user)>1) - return src.attack_hand(user) - if (istype(W, /obj/item/weapon/crowbar) && opened) - if (has_electronics==1) - if (terminal) - user << "Disconnect the wires first!" - return - playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1) - user << "You are trying to remove the power control board..." //lpeters - fixed grammar issues - if(do_after(user, 50/W.toolspeed, target = src)) - if (has_electronics==1) - has_electronics = 0 - if ((stat & BROKEN) || malfhack) - user.visible_message(\ - "[user.name] has broken the power control board inside [src.name]!",\ - "You break the charred power control board and remove the remains.", - "You hear a crack.") - //ticker.mode:apcs-- //XSI said no and I agreed. -rastaf0 - else - user.visible_message(\ - "[user.name] has removed the power control board from [src.name]!",\ - "You remove the power control board.") - new /obj/item/weapon/electronics/apc(loc) - else if (opened!=2) //cover isn't removed - opened = 0 - update_icon() - else if (istype(W, /obj/item/weapon/crowbar) && !((stat & BROKEN) || malfhack) ) - if(coverlocked && !(stat & MAINT)) - user << "The cover is locked and cannot be opened!" - return - else - opened = 1 - update_icon() - else if (istype(W, /obj/item/weapon/stock_parts/cell) && opened) // trying to put a cell inside - if(cell) - user << "There is a power cell already installed!" - return - else - if (stat & MAINT) - user << "There is no connector for your power cell!" - return - if(!user.drop_item()) - return - W.loc = src - cell = W - user.visible_message(\ - "[user.name] has inserted the power cell to [src.name]!",\ - "You insert the power cell.") - chargecount = 0 - update_icon() - else if (istype(W, /obj/item/weapon/screwdriver)) // haxing - if(opened) - if (cell) - user << "Close the APC first!" //Less hints more mystery! - return - else - if (has_electronics==1 && terminal) - has_electronics = 2 - stat &= ~MAINT - playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1) - user << "You screw the circuit electronics into place." - else if (has_electronics==2) - has_electronics = 1 - stat |= MAINT - playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1) - user << "You unfasten the electronics." - else /* has_electronics==0 */ - user << "There is nothing to secure!" - return - update_icon() - else if(emagged) - user << "The interface is broken!" - else - wiresexposed = !wiresexposed - user << "The wires have been [wiresexposed ? "exposed" : "unexposed"]" - update_icon() - - else if (istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda)) // trying to unlock the interface with an ID card - if(emagged) - user << "The interface is broken!" - else if(opened) - user << "You must close the cover to swipe an ID card!" - else if(wiresexposed) - user << "You must close the panel!" - else if(stat & (BROKEN|MAINT)) - user << "Nothing happens!" - else - if(src.allowed(usr) && !isWireCut(APC_WIRE_IDSCAN)) - locked = !locked - user << "You [ locked ? "lock" : "unlock"] the APC interface." - update_icon() - else - user << "Access denied." - else if (istype(W, /obj/item/stack/cable_coil) && !terminal && opened && has_electronics!=2) - if (src.loc:intact) - user << "You must remove the floor plating in front of the APC first!" - return - var/obj/item/stack/cable_coil/C = W - if(C.get_amount() < 10) - user << "You need ten lengths of cable for APC!" - return - user.visible_message("[user.name] adds cables to the APC frame.", \ - "You start adding cables to the APC frame...") - playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) - if(do_after(user, 20, target = src)) - if (C.amount >= 10 && !terminal && opened && has_electronics != 2) - 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_system/spark_spread/s = new /datum/effect_system/spark_spread - s.set_up(5, 1, src) - s.start() - return - C.use(10) - user << "You add cables to the APC frame." - make_terminal() - terminal.connect_to_network() - else if (istype(W, /obj/item/weapon/wirecutters) && terminal && opened && has_electronics!=2) - terminal.dismantle(user) - - else if (istype(W, /obj/item/weapon/electronics/apc) && opened && has_electronics==0 && !((stat & BROKEN) || malfhack)) - user.visible_message("[user.name] inserts the power control board into [src].", \ - "You start to insert the power control board into the frame...") - playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) - if(do_after(user, 10, target = src)) - if(has_electronics==0) - has_electronics = 1 - user << "You place the power control board inside the frame." - qdel(W) - else if (istype(W, /obj/item/weapon/electronics/apc) && opened && has_electronics==0 && ((stat & BROKEN) || malfhack)) - user << "You cannot put the board inside, the frame is damaged!" - return - else if (istype(W, /obj/item/weapon/weldingtool) && opened && has_electronics==0 && !terminal) - var/obj/item/weapon/weldingtool/WT = W - if (WT.get_fuel() < 3) - user << "You need more welding fuel to complete this task!" - return - user.visible_message("[user.name] welds [src].", \ - "You start welding the APC frame...", \ - "You hear welding.") - playsound(src.loc, 'sound/items/Welder.ogg', 50, 1) - if(do_after(user, 50/W.toolspeed, target = src)) - if(!src || !WT.remove_fuel(3, user)) return - if (emagged || malfhack || (stat & BROKEN) || opened==2) - new /obj/item/stack/sheet/metal(loc) - user.visible_message(\ - "[user.name] has cut [src] apart with [W].",\ - "You disassembled the broken APC frame.") - else - new /obj/item/wallframe/apc(loc) - user.visible_message(\ - "[user.name] has cut [src] from the wall with [W].",\ - "You cut the APC frame from the wall.") - qdel(src) - return - else if (istype(W, /obj/item/wallframe/apc) && opened && emagged) - emagged = 0 - if (opened==2) - opened = 1 - user.visible_message(\ - "[user.name] has replaced the damaged APC frontal panel with a new one.",\ - "You replace the damaged APC frontal panel with a new one.") - qdel(W) - update_icon() - else if (istype(W, /obj/item/wallframe/apc) && opened && ((stat & BROKEN) || malfhack)) - if (has_electronics) - user << "You cannot repair this APC until you remove the electronics still inside!" - return - user.visible_message("[user.name] replaces the damaged APC frame with a new one.",\ - "You begin to replace the damaged APC frame...") - if(do_after(user, 50, target = src)) - user << "You replace the damaged APC frame with a new one." - qdel(W) - stat &= ~BROKEN - malfai = null - malfhack = 0 - if (opened==2) - opened = 1 - update_icon() - else - if((!opened && wiresexposed && wires.IsInteractionTool(W)) || (issilicon(user) && !(stat & BROKEN) &&!malfhack)) - return attack_hand(user) - - ..() - if( ((stat & BROKEN) || malfhack) && !opened && W.force >= 5 && W.w_class >= 3 && prob(20) ) - opened = 2 - user.visible_message("[user.name] has knocked down the APC cover with the [W.name].", \ - "You knock down the APC cover with your [W.name]!", \ - "You hear bang.") - update_icon() - -/obj/machinery/power/apc/emag_act(mob/user) - if(!emagged && !malfhack) - if(opened) - user << "You must close the cover to swipe an ID card!" - else if(wiresexposed) - user << "You must close the panel first!" - else if(stat & (BROKEN|MAINT)) - user << "Nothing happens!" - else - flick("apc-spark", src) - emagged = 1 - locked = 0 - user << "You emag the APC interface." - update_icon() - -// attack with hand - remove cell (if cover open) or interact with the APC - -/obj/machinery/power/apc/attack_hand(mob/user) - if (!user) return - add_fingerprint(user) - if(usr == user && opened && (!issilicon(user))) - if(cell) - user.put_in_hands(cell) - cell.add_fingerprint(user) - cell.updateicon() - - src.cell = null - user.visible_message("[user.name] removes the power cell from [src.name]!",\ - "You remove the power cell.") - //user << "You remove the power cell." - charging = 0 - src.update_icon() - return - interact(user) - -/obj/machinery/power/apc/attack_alien(mob/living/carbon/alien/humanoid/user) - if(!user) return - user.do_attack_animation(src) - user.visible_message("[user.name] slashes at the [src.name]!", "You slash at the [src.name]!") - playsound(src.loc, 'sound/weapons/slash.ogg', 100, 1) - - var/allcut = wires.IsAllCut() - - if(beenhit >= pick(3, 4) && wiresexposed != 1) - wiresexposed = 1 - src.update_icon() - src.visible_message("The [src.name]'s cover flies open, exposing the wires!") - - else if(wiresexposed == 1 && allcut == 0) - wires.CutAll() - src.update_icon() - src.visible_message("The [src.name]'s wires are shredded!") - else - beenhit += 1 - return - - -/obj/machinery/power/apc/interact(mob/user) - if(stat & (BROKEN|MAINT)) - return - if(wiresexposed && !istype(user, /mob/living/silicon/ai)) - wires.Interact(user) - else - ui_interact(user) - -/obj/machinery/power/apc/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, force_open = 0) - ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open) - if (!ui) - ui = new(user, src, ui_key, "apc", name, 550, 550) - ui.open() - -/obj/machinery/power/apc/get_ui_data(mob/user) - var/list/data = list( - "locked" = locked, - "isOperating" = operating, - "externalPower" = main_status, - "powerCellStatus" = cell ? cell.percent() : null, - "chargeMode" = chargemode, - "chargingStatus" = charging, - "totalLoad" = lastused_equip + lastused_light + lastused_environ, - "coverLocked" = coverlocked, - "siliconUser" = user.has_unlimited_silicon_privilege, - "malfStatus" = get_malf_status(user), - - "powerChannels" = list( - list( - "title" = "Equipment", - "powerLoad" = lastused_equip, - "status" = equipment, - "topicParams" = list( - "auto" = list("eqp" = 3), - "on" = list("eqp" = 2), - "off" = list("eqp" = 1) - ) - ), - list( - "title" = "Lighting", - "powerLoad" = lastused_light, - "status" = lighting, - "topicParams" = list( - "auto" = list("lgt" = 3), - "on" = list("lgt" = 2), - "off" = list("lgt" = 1) - ) - ), - list( - "title" = "Environment", - "powerLoad" = lastused_environ, - "status" = environ, - "topicParams" = list( - "auto" = list("env" = 3), - "on" = list("env" = 2), - "off" = list("env" = 1) - ) - ) - ) - ) - return data - - -/obj/machinery/power/apc/proc/get_malf_status(mob/user) - if (ticker && ticker.mode && (user.mind in ticker.mode.malf_ai) && istype(user, /mob/living/silicon/ai)) - if (src.malfai == (user:parent ? user:parent : user)) - if (src.occupier == user) - return 3 // 3 = User is shunted in this APC - else if (istype(user.loc, /obj/machinery/power/apc)) - return 4 // 4 = User is shunted in another APC - else - return 2 // 2 = APC hacked by user, and user is in its core. - else - return 1 // 1 = APC not hacked. - else - return 0 // 0 = User is not a Malf AI - -/obj/machinery/power/apc/proc/report() - return "[area.name] : [equipment]/[lighting]/[environ] ([lastused_equip+lastused_light+lastused_environ]) : [cell? cell.percent() : "N/C"] ([charging])" - -/obj/machinery/power/apc/proc/update() - if(operating && !shorted) - area.power_light = (lighting > 1) - area.power_equip = (equipment > 1) - area.power_environ = (environ > 1) -// if (area.name == "AI Chamber") -// spawn(10) -// world << " [area.name] [area.power_equip]" - else - area.power_light = 0 - area.power_equip = 0 - area.power_environ = 0 -// if (area.name == "AI Chamber") -// world << "[area.power_equip]" - area.power_change() - -/obj/machinery/power/apc/proc/isWireCut(wireIndex) - return wires.IsIndexCut(wireIndex) - - -/obj/machinery/power/apc/proc/can_use(mob/user, loud = 0) //used by attack_hand() and Topic() - if(IsAdminGhost(user)) - return 1 - if(user.has_unlimited_silicon_privilege) - var/mob/living/silicon/ai/AI = user - var/mob/living/silicon/robot/robot = user - if ( \ - src.aidisabled || \ - malfhack && istype(malfai) && \ - ( \ - (istype(AI) && (malfai!=AI && malfai != AI.parent)) || \ - (istype(robot) && (robot in malfai.connected_robots)) \ - ) \ - ) - if(!loud) - user << "\The [src] have AI control disabled!" - return 0 - else - if ((!in_range(src, user) || !istype(src.loc, /turf))) - return 0 - return 1 - -/obj/machinery/power/apc/ui_act(action, params) - if(..()) - return - - if(!can_use(usr, 1)) - return - - switch(action) - if("lock") - coverlocked = !coverlocked - if ("breaker") - toggle_breaker() - if("chargemode") - chargemode = !chargemode - if(!chargemode) - charging = 0 - update_icon() - if("channel") - if (params["eqp"]) - var/val = text2num(params["eqp"]) - equipment = setsubsystem(val) - update_icon() - update() - else if (params["lgt"]) - var/val = text2num(params["lgt"]) - lighting = setsubsystem(val) - update_icon() - update() - else if (params["env"]) - var/val = text2num(params["env"]) - environ = setsubsystem(val) - update_icon() - update() - if("toggleaccess") - if(usr.has_unlimited_silicon_privilege) - if(emagged || (stat & (BROKEN|MAINT))) - usr << "The APC does not respond to the command." - else - locked = !locked - update_icon() - if("overload") - if(usr.has_unlimited_silicon_privilege) - src.overload_lighting() - if("hack") - var/mob/living/silicon/ai/malfai = usr - if(get_malf_status(malfai)==1) - if (malfai.malfhacking) - malfai << "You are already hacking an APC." - return 1 - malfai << "Beginning override of APC systems. This takes some time, and you cannot perform other actions during the process." - malfai.malfhack = src - malfai.malfhacking = 1 - sleep(600) - if(src) - if (!src.aidisabled) - malfai.malfhack = null - malfai.malfhacking = 0 - locked = 1 - if (ticker.mode.config_tag == "malfunction") - if (src.z == ZLEVEL_STATION) //if (is_type_in_list(get_area(src), the_station_areas)) - ticker.mode:apcs++ - if(usr:parent) - src.malfai = usr:parent - else - src.malfai = usr - malfai << "Hack complete. The APC is now under your exclusive control." - update_icon() - if("occupy") - if(get_malf_status(usr)) - malfoccupy(usr) - if("deoccupy") - if(get_malf_status(usr)) - malfvacate() - return 1 - -/obj/machinery/power/apc/proc/toggle_breaker() - operating = !operating - - src.update() - update_icon() - -/obj/machinery/power/apc/proc/malfoccupy(mob/living/silicon/ai/malf) - if(!istype(malf)) - return - if(istype(malf.loc, /obj/machinery/power/apc)) // Already in an APC - malf << "You must evacuate your current APC first!" - return - if(!malf.can_shunt) - malf << "You cannot shunt!" - return - if(src.z != 1) - return - src.occupier = new /mob/living/silicon/ai(src,malf.laws,null,1) - src.occupier.adjustOxyLoss(malf.getOxyLoss()) - if(!findtext(src.occupier.name,"APC Copy")) - src.occupier.name = "[malf.name] APC Copy" - if(malf.parent) - src.occupier.parent = malf.parent - else - src.occupier.parent = malf - malf.shunted = 1 - malf.mind.transfer_to(src.occupier) - src.occupier.eyeobj.name = "[src.occupier.name] (AI Eye)" - if(malf.parent) - qdel(malf) - src.occupier.verbs += /mob/living/silicon/ai/proc/corereturn - src.occupier.verbs += /datum/game_mode/malfunction/proc/takeover - src.occupier.cancel_camera() - if (seclevel2num(get_security_level()) == SEC_LEVEL_DELTA) - for(var/obj/item/weapon/pinpointer/point in world) - point.the_disk = src //the pinpointer will detect the shunted AI - - -/obj/machinery/power/apc/proc/malfvacate(forced) - if(!src.occupier) - return - if(src.occupier.parent && src.occupier.parent.stat != 2) - src.occupier.mind.transfer_to(src.occupier.parent) - src.occupier.parent.shunted = 0 - src.occupier.parent.adjustOxyLoss(src.occupier.getOxyLoss()) - src.occupier.parent.cancel_camera() - qdel(src.occupier) - if (seclevel2num(get_security_level()) == SEC_LEVEL_DELTA) - for(var/obj/item/weapon/pinpointer/point in world) - for(var/datum/mind/AI_mind in ticker.mode.malf_ai) - var/mob/living/silicon/ai/A = AI_mind.current // the current mob the mind owns - if(A.stat != DEAD) - point.the_disk = A //The pinpointer tracks the AI back into its core. - - else - src.occupier << "Primary core damaged, unable to return core processes." - if(forced) - src.occupier.loc = src.loc - src.occupier.death() - src.occupier.gib() - for(var/obj/item/weapon/pinpointer/point in world) - point.the_disk = null //the pinpointer will go back to pointing at the nuke disc. - - -/obj/machinery/power/apc/proc/ion_act() - //intended to be exactly the same as an AI malf attack - if(!src.malfhack && src.z == ZLEVEL_STATION) - if(prob(3)) - src.locked = 1 - if (src.cell.charge > 0) -// world << "\red blew APC in [src.loc.loc]" - src.cell.charge = 0 - cell.corrupt() - src.malfhack = 1 - update_icon() - var/datum/effect_system/smoke_spread/smoke = new - smoke.set_up(1, src.loc) - smoke.attach(src) - smoke.start() - var/datum/effect_system/spark_spread/s = new - s.set_up(3, 1, src) - s.start() - visible_message("The [src.name] suddenly lets out a blast of smoke and some sparks!", \ - "You hear sizzling electronics.") - - -/obj/machinery/power/apc/surplus() - if(terminal) - return terminal.surplus() - else - return 0 - -/obj/machinery/power/apc/add_load(amount) - if(terminal && terminal.powernet) - terminal.powernet.load += amount - -/obj/machinery/power/apc/avail() - if(terminal) - return terminal.avail() - else - return 0 - -/obj/machinery/power/apc/process() - - if(stat & (BROKEN|MAINT)) - return - if(!area.requires_power) - return - - - /* - if (equipment > 1) // off=0, off auto=1, on=2, on auto=3 - use_power(src.equip_consumption, EQUIP) - if (lighting > 1) // off=0, off auto=1, on=2, on auto=3 - use_power(src.light_consumption, LIGHT) - if (environ > 1) // off=0, off auto=1, on=2, on auto=3 - use_power(src.environ_consumption, ENVIRON) - - area.calc_lighting() */ - lastused_light = area.usage(STATIC_LIGHT) - lastused_light += area.usage(LIGHT) - lastused_equip = area.usage(EQUIP) - lastused_equip += area.usage(STATIC_EQUIP) - lastused_environ = area.usage(ENVIRON) - lastused_environ += area.usage(STATIC_ENVIRON) - area.clear_usage() - - lastused_total = lastused_light + lastused_equip + lastused_environ - - //store states to update icon if any change - var/last_lt = lighting - var/last_eq = equipment - var/last_en = environ - var/last_ch = charging - - var/excess = surplus() - - if(!src.avail()) - main_status = 0 - else if(excess < 0) - main_status = 1 - else - main_status = 2 - - //if(debug) - // world.log << "Status: [main_status] - Excess: [excess] - Last Equip: [lastused_equip] - Last Light: [lastused_light] - Longterm: [longtermpower]" - - if(cell && !shorted) - - - // draw power from cell as before to power the area - var/cellused = min(cell.charge, CELLRATE * lastused_total) // clamp deduction to a max, amount left in cell - cell.use(cellused) - - if(excess > lastused_total) // if power excess recharge the cell - // by the same amount just used - cell.give(cellused) - add_load(cellused/CELLRATE) // add the load used to recharge the cell - - - else // no excess, and not enough per-apc - - if( (cell.charge/CELLRATE + excess) >= lastused_total) // can we draw enough from cell+grid to cover last usage? - - cell.charge = min(cell.maxcharge, cell.charge + CELLRATE * excess) //recharge with what we can - add_load(excess) // so draw what we can from the grid - charging = 0 - - else // 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) - lighting = autoset(lighting, 0) - environ = autoset(environ, 0) - - - // 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) - longtermpower += 1 - else if(longtermpower > -10) - longtermpower -= 2 - - if(cell.charge <= 0) // zero charge, turn all off - equipment = autoset(equipment, 0) - lighting = autoset(lighting, 0) - environ = autoset(environ, 0) - area.poweralert(0, src) - else if(cell.percent() < 15 && longtermpower < 0) // <15%, turn off lighting & equipment - equipment = autoset(equipment, 2) - lighting = autoset(lighting, 2) - environ = autoset(environ, 1) - area.poweralert(0, src) - else if(cell.percent() < 30 && longtermpower < 0) // <30%, turn off equipment - equipment = autoset(equipment, 2) - lighting = autoset(lighting, 1) - environ = autoset(environ, 1) - area.poweralert(0, src) - else // otherwise all can be on - equipment = autoset(equipment, 1) - lighting = autoset(lighting, 1) - environ = autoset(environ, 1) - area.poweralert(1, src) - if(cell.percent() > 75) - area.poweralert(1, src) - - // 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 capped to % per second constant - var/ch = min(excess*CELLRATE, cell.maxcharge*CHARGELEVEL) - add_load(ch/CELLRATE) // Removes the power we're taking from the grid - cell.give(ch) // actually recharge the cell - - else - charging = 0 // stop charging - chargecount = 0 - - // show cell as fully charged if so - if(cell.charge >= cell.maxcharge) - cell.charge = cell.maxcharge - charging = 2 - - if(chargemode) - if(!charging) - if(excess > cell.maxcharge*CHARGELEVEL) - chargecount++ - else - chargecount = 0 - - if(chargecount == 10) - - chargecount = 0 - charging = 1 - - else // chargemode off - charging = 0 - chargecount = 0 - - else // no cell, switch everything off - - charging = 0 - chargecount = 0 - equipment = autoset(equipment, 0) - lighting = autoset(lighting, 0) - environ = autoset(environ, 0) - area.poweralert(0, src) - - // update icon & area power if anything changed - - if(last_lt != lighting || last_eq != equipment || last_en != environ) - queue_icon_update() - update() - else if (last_ch != charging) - queue_icon_update() - -// val 0=off, 1=off(auto) 2=on 3=on(auto) -// on 0=off, 1=on, 2=autooff - -/obj/machinery/power/apc/proc/autoset(val, on) - if(on==0) - 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) - if(val==1) // if auto-off, return auto-on - return 3 - - else if(on==2) - if(val==3) // if auto-on, return auto-off - return 1 - - return val - - -// damage and destruction acts -/obj/machinery/power/apc/emp_act(severity) - if(cell) - cell.emp_act(severity) - if(occupier) - occupier.emp_act(severity) - lighting = 0 - equipment = 0 - environ = 0 - update_icon() - update() - spawn(600) - equipment = 3 - environ = 3 - update_icon() - update() - ..() - -/obj/machinery/power/apc/ex_act(severity, target) - ..() - if(!gc_destroyed) - switch(severity) - if(2) - if(prob(50)) - set_broken() - if(3) - if(prob(25)) - set_broken() - -/obj/machinery/power/apc/blob_act() - set_broken() - -/obj/machinery/power/apc/disconnect_terminal() - if(terminal) - terminal.master = null - terminal = null - -/obj/machinery/power/apc/proc/set_broken() - if(malfai && operating) - if (ticker.mode.config_tag == "malfunction") - if (src.z == ZLEVEL_STATION) //if (is_type_in_list(get_area(src), the_station_areas)) - ticker.mode:apcs-- - stat |= BROKEN - operating = 0 - if(occupier) - malfvacate(1) - update_icon() - update() - -// overload all the lights in this APC area - -/obj/machinery/power/apc/proc/overload_lighting() - if(/* !get_connection() || */ !operating || shorted) - return - if( cell && cell.charge>=20) - cell.use(20); - spawn(0) - for(var/area/A in area.related) - for(var/obj/machinery/light/L in A) - L.on = 1 - L.broken() - sleep(1) - -/obj/machinery/power/apc/proc/shock(mob/user, prb) - if(!prob(prb)) - return 0 - var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread - s.set_up(5, 1, src) - s.start() - if(isalien(user)) - return 0 - if (electrocute_mob(user, src, src)) - return 1 - else - return 0 - -/obj/machinery/power/apc/proc/setsubsystem(val) - if(cell && cell.charge > 0) - return (val==1) ? 0 : val - else if(val == 3) - return 1 - else - return 0 - -#undef APC_UPDATE_ICON_COOLDOWN - -/*Power module, used for APC construction*/ -/obj/item/weapon/electronics/apc - name = "power control module" - icon_state = "power_mod" - desc = "Heavy-duty switching circuits for power control." +#define APC_WIRE_IDSCAN 1 +#define APC_WIRE_MAIN_POWER1 2 +#define APC_WIRE_MAIN_POWER2 3 +#define APC_WIRE_AI_CONTROL 4 + +//update_state +#define UPSTATE_CELL_IN 1 +#define UPSTATE_OPENED1 2 +#define UPSTATE_OPENED2 4 +#define UPSTATE_MAINT 8 +#define UPSTATE_BROKE 16 +#define UPSTATE_BLUESCREEN 32 +#define UPSTATE_WIREEXP 64 +#define UPSTATE_ALLGOOD 128 + +//update_overlay +#define APC_UPOVERLAY_CHARGEING0 1 +#define APC_UPOVERLAY_CHARGEING1 2 +#define APC_UPOVERLAY_CHARGEING2 4 +#define APC_UPOVERLAY_EQUIPMENT0 8 +#define APC_UPOVERLAY_EQUIPMENT1 16 +#define APC_UPOVERLAY_EQUIPMENT2 32 +#define APC_UPOVERLAY_LIGHTING0 64 +#define APC_UPOVERLAY_LIGHTING1 128 +#define APC_UPOVERLAY_LIGHTING2 256 +#define APC_UPOVERLAY_ENVIRON0 512 +#define APC_UPOVERLAY_ENVIRON1 1024 +#define APC_UPOVERLAY_ENVIRON2 2048 +#define APC_UPOVERLAY_LOCKED 4096 +#define APC_UPOVERLAY_OPERATING 8192 + + +#define APC_UPDATE_ICON_COOLDOWN 200 // 20 seconds + +// the Area Power Controller (APC), formerly Power Distribution Unit (PDU) +// one per area, needs wire conection to power network through a terminal + +// controls power to devices in that area +// may be opened to change power cell +// three different channels (lighting/equipment/environ) - may each be set to on, off, or auto + + +//NOTE: STUFF STOLEN FROM AIRLOCK.DM thx + + +/obj/machinery/power/apc + name = "area power controller" + desc = "A control terminal for the area electrical systems." + + icon_state = "apc0" + anchored = 1 + use_power = 0 + req_access = list(access_engine_equip) + var/area/area + var/areastring = null + var/obj/item/weapon/stock_parts/cell/cell + var/start_charge = 90 // initial cell charge % + var/cell_type = 2500 // 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 + var/equipment = 3 + var/environ = 3 + var/operating = 1 + var/charging = 0 + var/chargemode = 1 + var/chargecount = 0 + var/locked = 1 + var/coverlocked = 1 + var/aidisabled = 0 + var/tdir = null + var/obj/machinery/power/terminal/terminal = null + var/lastused_light = 0 + var/lastused_equip = 0 + var/lastused_environ = 0 + var/lastused_total = 0 + var/main_status = 0 + var/wiresexposed = 0 + powernet = 0 // set so that APCs aren't found as powernet nodes //Hackish, Horrible, was like this before I changed it :( + var/malfhack = 0 //New var for my changes to AI malf. --NeoFite + var/mob/living/silicon/ai/malfai = null //See above --NeoFite +// luminosity = 1 + var/has_electronics = 0 // 0 - none, 1 - plugged in, 2 - secured by screwdriver + var/overload = 1 //used for the Blackout malf module + var/beenhit = 0 // used for counting how many times it has been hit, used for Aliens at the moment + var/mob/living/silicon/ai/occupier = null + var/longtermpower = 10 + var/datum/wires/apc/wires = null + var/auto_name = 0 + var/update_state = -1 + var/update_overlay = -1 + var/global/status_overlays = 0 + var/updating_icon = 0 + var/global/list/status_overlays_lock + var/global/list/status_overlays_charging + var/global/list/status_overlays_equipment + var/global/list/status_overlays_lighting + var/global/list/status_overlays_environ + +/obj/machinery/power/apc/connect_to_network() + //Override because the APC does not directly connect to the network; it goes through a terminal. + //The terminal is what the power computer looks for anyway. + if(!terminal) + make_terminal() + if(terminal) + terminal.connect_to_network() + +/obj/machinery/power/apc/New(turf/loc, var/ndir, var/building=0) + ..() + apcs_list += src + + wires = new(src) + // offset 24 pixels in direction of dir + // this allows the APC to be embedded in a wall, yet still inside an area + if (building) + dir = ndir + src.tdir = dir // to fix Vars bug + dir = SOUTH + + if(auto_name) + name = "[get_area(src)] APC" + + pixel_x = (src.tdir & 3)? 0 : (src.tdir == 4 ? 24 : -24) + pixel_y = (src.tdir & 3)? (src.tdir ==1 ? 24 : -24) : 0 + if (building==0) + init() + else + area = src.loc.loc:master + opened = 1 + operating = 0 + name = "[area.name] APC" + stat |= MAINT + src.update_icon() + spawn(5) + src.update() + +/obj/machinery/power/apc/Destroy() + apcs_list -= src + + if(malfai && operating) + if (ticker.mode.config_tag == "malfunction") + if (src.z == ZLEVEL_STATION) //if (is_type_in_list(get_area(src), the_station_areas)) + ticker.mode:apcs-- + area.power_light = 0 + area.power_equip = 0 + area.power_environ = 0 + area.power_change() + if(occupier) + malfvacate(1) + qdel(wires) + wires = null + if(cell) + qdel(cell) + if(terminal) + disconnect_terminal() + return ..() + +/obj/machinery/power/apc/proc/make_terminal() + // create a terminal object at the same position as original turf loc + // wires will attach to this + terminal = new/obj/machinery/power/terminal(src.loc) + terminal.dir = tdir + terminal.master = src + +/obj/machinery/power/apc/proc/init() + 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/stock_parts/cell(src) + cell.maxcharge = cell_type // cell_type is maximum charge (old default was 1000 or 2500 (values one and two respectively) + cell.charge = start_charge * cell.maxcharge / 100 // (convert percentage to actual value) + + var/area/A = src.loc.loc + + //if area isn't specified use current + if(isarea(A) && src.areastring == null) + src.area = A + else + src.area = get_area_name(areastring) + update_icon() + + make_terminal() + + spawn(5) + src.update() + +/obj/machinery/power/apc/examine(mob/user) + ..() + if(stat & BROKEN) + user << "Looks broken." + return + if(opened) + if(has_electronics && terminal) + user << "The cover is [opened==2?"removed":"open"] and the power cell is [ cell ? "installed" : "missing"]." + else + user << "It's [!terminal?" not":""]wired up." + user << "The electronics are[!has_electronics?"n't":""] installed." + + else + if (stat & MAINT) + user << "The cover is closed. Something is wrong with it. It doesn't work." + else if (malfhack) + user << "The cover is broken. It may be hard to force it open." + else + user << "The cover is closed." + + +// update the APC icon to show the three base states +// also add overlays for indicator lights +/obj/machinery/power/apc/update_icon() + if (!status_overlays) + status_overlays = 1 + status_overlays_lock = new + status_overlays_charging = new + status_overlays_equipment = new + status_overlays_lighting = new + status_overlays_environ = new + + status_overlays_lock.len = 2 + status_overlays_charging.len = 3 + status_overlays_equipment.len = 4 + status_overlays_lighting.len = 4 + status_overlays_environ.len = 4 + + status_overlays_lock[1] = image(icon, "apcox-0") // 0=blue 1=red + status_overlays_lock[2] = image(icon, "apcox-1") + + status_overlays_charging[1] = image(icon, "apco3-0") + status_overlays_charging[2] = image(icon, "apco3-1") + status_overlays_charging[3] = image(icon, "apco3-2") + + status_overlays_equipment[1] = image(icon, "apco0-0") + status_overlays_equipment[2] = image(icon, "apco0-1") + status_overlays_equipment[3] = image(icon, "apco0-2") + status_overlays_equipment[4] = image(icon, "apco0-3") + + status_overlays_lighting[1] = image(icon, "apco1-0") + status_overlays_lighting[2] = image(icon, "apco1-1") + status_overlays_lighting[3] = image(icon, "apco1-2") + status_overlays_lighting[4] = image(icon, "apco1-3") + + status_overlays_environ[1] = image(icon, "apco2-0") + status_overlays_environ[2] = image(icon, "apco2-1") + status_overlays_environ[3] = image(icon, "apco2-2") + status_overlays_environ[4] = image(icon, "apco2-3") + + var/update = check_updates() //returns 0 if no need to update icons. + // 1 if we need to update the icon_state + // 2 if we need to update the overlays + if(!update) + return + + if(update & 1) // Updating the icon state + if(update_state & UPSTATE_ALLGOOD) + icon_state = "apc0" + else if(update_state & (UPSTATE_OPENED1|UPSTATE_OPENED2)) + var/basestate = "apc[ cell ? "2" : "1" ]" + if(update_state & UPSTATE_OPENED1) + if(update_state & (UPSTATE_MAINT|UPSTATE_BROKE)) + icon_state = "apcmaint" //disabled APC cannot hold cell + else + icon_state = basestate + else if(update_state & UPSTATE_OPENED2) + if (update_state & UPSTATE_BROKE || malfhack) + icon_state = "[basestate]-b-nocover" + else + icon_state = "[basestate]-nocover" + else if(update_state & UPSTATE_BROKE) + icon_state = "apc-b" + else if(update_state & UPSTATE_BLUESCREEN) + icon_state = "apcemag" + else if(update_state & UPSTATE_WIREEXP) + icon_state = "apcewires" + + if(!(update_state & UPSTATE_ALLGOOD)) + if(overlays.len) + overlays = 0 + + if(update & 2) + if(overlays.len) + overlays.len = 0 + if(!(stat & (BROKEN|MAINT)) && update_state & UPSTATE_ALLGOOD) + overlays += status_overlays_lock[locked+1] + overlays += status_overlays_charging[charging+1] + if(operating) + overlays += status_overlays_equipment[equipment+1] + overlays += status_overlays_lighting[lighting+1] + overlays += status_overlays_environ[environ+1] + + +/obj/machinery/power/apc/proc/check_updates() + + var/last_update_state = update_state + var/last_update_overlay = update_overlay + update_state = 0 + update_overlay = 0 + + if(cell) + update_state |= UPSTATE_CELL_IN + if(stat & BROKEN) + update_state |= UPSTATE_BROKE + if(stat & MAINT) + update_state |= UPSTATE_MAINT + if(opened) + if(opened==1) + update_state |= UPSTATE_OPENED1 + if(opened==2) + update_state |= UPSTATE_OPENED2 + else if(emagged || malfai) + update_state |= UPSTATE_BLUESCREEN + else if(wiresexposed) + update_state |= UPSTATE_WIREEXP + if(update_state <= 1) + update_state |= UPSTATE_ALLGOOD + + if(operating) + update_overlay |= APC_UPOVERLAY_OPERATING + + if(update_state & UPSTATE_ALLGOOD) + if(locked) + update_overlay |= APC_UPOVERLAY_LOCKED + + if(!charging) + update_overlay |= APC_UPOVERLAY_CHARGEING0 + else if(charging == 1) + update_overlay |= APC_UPOVERLAY_CHARGEING1 + else if(charging == 2) + update_overlay |= APC_UPOVERLAY_CHARGEING2 + + if (!equipment) + update_overlay |= APC_UPOVERLAY_EQUIPMENT0 + else if(equipment == 1) + update_overlay |= APC_UPOVERLAY_EQUIPMENT1 + else if(equipment == 2) + update_overlay |= APC_UPOVERLAY_EQUIPMENT2 + + if(!lighting) + update_overlay |= APC_UPOVERLAY_LIGHTING0 + else if(lighting == 1) + update_overlay |= APC_UPOVERLAY_LIGHTING1 + else if(lighting == 2) + update_overlay |= APC_UPOVERLAY_LIGHTING2 + + if(!environ) + update_overlay |= APC_UPOVERLAY_ENVIRON0 + else if(environ==1) + update_overlay |= APC_UPOVERLAY_ENVIRON1 + else if(environ==2) + update_overlay |= APC_UPOVERLAY_ENVIRON2 + + + var/results = 0 + if(last_update_state == update_state && last_update_overlay == update_overlay) + return 0 + if(last_update_state != update_state) + results += 1 + if(last_update_overlay != update_overlay) + results += 2 + return results + +// Used in process so it doesn't update the icon too much +/obj/machinery/power/apc/proc/queue_icon_update() + + if(!updating_icon) + updating_icon = 1 + // Start the update + spawn(APC_UPDATE_ICON_COOLDOWN) + update_icon() + updating_icon = 0 + +//attack with an item - open/close cover, insert cell, or (un)lock interface + +/obj/machinery/power/apc/attackby(obj/item/W, mob/living/user, params) + + if (istype(user, /mob/living/silicon) && get_dist(src,user)>1) + return src.attack_hand(user) + if (istype(W, /obj/item/weapon/crowbar) && opened) + if (has_electronics==1) + if (terminal) + user << "Disconnect the wires first!" + return + playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1) + user << "You are trying to remove the power control board..." //lpeters - fixed grammar issues + if(do_after(user, 50/W.toolspeed, target = src)) + if (has_electronics==1) + has_electronics = 0 + if ((stat & BROKEN) || malfhack) + user.visible_message(\ + "[user.name] has broken the power control board inside [src.name]!",\ + "You break the charred power control board and remove the remains.", + "You hear a crack.") + //ticker.mode:apcs-- //XSI said no and I agreed. -rastaf0 + else + user.visible_message(\ + "[user.name] has removed the power control board from [src.name]!",\ + "You remove the power control board.") + new /obj/item/weapon/electronics/apc(loc) + else if (opened!=2) //cover isn't removed + opened = 0 + update_icon() + else if (istype(W, /obj/item/weapon/crowbar) && !((stat & BROKEN) || malfhack) ) + if(coverlocked && !(stat & MAINT)) + user << "The cover is locked and cannot be opened!" + return + else + opened = 1 + update_icon() + else if (istype(W, /obj/item/weapon/stock_parts/cell) && opened) // trying to put a cell inside + if(cell) + user << "There is a power cell already installed!" + return + else + if (stat & MAINT) + user << "There is no connector for your power cell!" + return + if(!user.drop_item()) + return + W.loc = src + cell = W + user.visible_message(\ + "[user.name] has inserted the power cell to [src.name]!",\ + "You insert the power cell.") + chargecount = 0 + update_icon() + else if (istype(W, /obj/item/weapon/screwdriver)) // haxing + if(opened) + if (cell) + user << "Close the APC first!" //Less hints more mystery! + return + else + if (has_electronics==1 && terminal) + has_electronics = 2 + stat &= ~MAINT + playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1) + user << "You screw the circuit electronics into place." + else if (has_electronics==2) + has_electronics = 1 + stat |= MAINT + playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1) + user << "You unfasten the electronics." + else /* has_electronics==0 */ + user << "There is nothing to secure!" + return + update_icon() + else if(emagged) + user << "The interface is broken!" + else + wiresexposed = !wiresexposed + user << "The wires have been [wiresexposed ? "exposed" : "unexposed"]" + update_icon() + + else if (istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda)) // trying to unlock the interface with an ID card + if(emagged) + user << "The interface is broken!" + else if(opened) + user << "You must close the cover to swipe an ID card!" + else if(wiresexposed) + user << "You must close the panel!" + else if(stat & (BROKEN|MAINT)) + user << "Nothing happens!" + else + if(src.allowed(usr) && !isWireCut(APC_WIRE_IDSCAN)) + locked = !locked + user << "You [ locked ? "lock" : "unlock"] the APC interface." + update_icon() + else + user << "Access denied." + else if (istype(W, /obj/item/stack/cable_coil) && !terminal && opened && has_electronics!=2) + if (src.loc:intact) + user << "You must remove the floor plating in front of the APC first!" + return + var/obj/item/stack/cable_coil/C = W + if(C.get_amount() < 10) + user << "You need ten lengths of cable for APC!" + return + user.visible_message("[user.name] adds cables to the APC frame.", \ + "You start adding cables to the APC frame...") + playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) + if(do_after(user, 20, target = src)) + if (C.amount >= 10 && !terminal && opened && has_electronics != 2) + 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_system/spark_spread/s = new /datum/effect_system/spark_spread + s.set_up(5, 1, src) + s.start() + return + C.use(10) + user << "You add cables to the APC frame." + make_terminal() + terminal.connect_to_network() + else if (istype(W, /obj/item/weapon/wirecutters) && terminal && opened && has_electronics!=2) + terminal.dismantle(user) + + else if (istype(W, /obj/item/weapon/electronics/apc) && opened && has_electronics==0 && !((stat & BROKEN) || malfhack)) + user.visible_message("[user.name] inserts the power control board into [src].", \ + "You start to insert the power control board into the frame...") + playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) + if(do_after(user, 10, target = src)) + if(has_electronics==0) + has_electronics = 1 + user << "You place the power control board inside the frame." + qdel(W) + else if (istype(W, /obj/item/weapon/electronics/apc) && opened && has_electronics==0 && ((stat & BROKEN) || malfhack)) + user << "You cannot put the board inside, the frame is damaged!" + return + else if (istype(W, /obj/item/weapon/weldingtool) && opened && has_electronics==0 && !terminal) + var/obj/item/weapon/weldingtool/WT = W + if (WT.get_fuel() < 3) + user << "You need more welding fuel to complete this task!" + return + user.visible_message("[user.name] welds [src].", \ + "You start welding the APC frame...", \ + "You hear welding.") + playsound(src.loc, 'sound/items/Welder.ogg', 50, 1) + if(do_after(user, 50/W.toolspeed, target = src)) + if(!src || !WT.remove_fuel(3, user)) return + if (emagged || malfhack || (stat & BROKEN) || opened==2) + new /obj/item/stack/sheet/metal(loc) + user.visible_message(\ + "[user.name] has cut [src] apart with [W].",\ + "You disassembled the broken APC frame.") + else + new /obj/item/wallframe/apc(loc) + user.visible_message(\ + "[user.name] has cut [src] from the wall with [W].",\ + "You cut the APC frame from the wall.") + qdel(src) + return + else if (istype(W, /obj/item/wallframe/apc) && opened && emagged) + emagged = 0 + if (opened==2) + opened = 1 + user.visible_message(\ + "[user.name] has replaced the damaged APC frontal panel with a new one.",\ + "You replace the damaged APC frontal panel with a new one.") + qdel(W) + update_icon() + else if (istype(W, /obj/item/wallframe/apc) && opened && ((stat & BROKEN) || malfhack)) + if (has_electronics) + user << "You cannot repair this APC until you remove the electronics still inside!" + return + user.visible_message("[user.name] replaces the damaged APC frame with a new one.",\ + "You begin to replace the damaged APC frame...") + if(do_after(user, 50, target = src)) + user << "You replace the damaged APC frame with a new one." + qdel(W) + stat &= ~BROKEN + malfai = null + malfhack = 0 + if (opened==2) + opened = 1 + update_icon() + else + if((!opened && wiresexposed && wires.IsInteractionTool(W)) || (issilicon(user) && !(stat & BROKEN) &&!malfhack)) + return attack_hand(user) + + ..() + if( ((stat & BROKEN) || malfhack) && !opened && W.force >= 5 && W.w_class >= 3 && prob(20) ) + opened = 2 + user.visible_message("[user.name] has knocked down the APC cover with the [W.name].", \ + "You knock down the APC cover with your [W.name]!", \ + "You hear bang.") + update_icon() + +/obj/machinery/power/apc/emag_act(mob/user) + if(!emagged && !malfhack) + if(opened) + user << "You must close the cover to swipe an ID card!" + else if(wiresexposed) + user << "You must close the panel first!" + else if(stat & (BROKEN|MAINT)) + user << "Nothing happens!" + else + flick("apc-spark", src) + emagged = 1 + locked = 0 + user << "You emag the APC interface." + update_icon() + +// attack with hand - remove cell (if cover open) or interact with the APC + +/obj/machinery/power/apc/attack_hand(mob/user) + if (!user) return + add_fingerprint(user) + if(usr == user && opened && (!issilicon(user))) + if(cell) + user.put_in_hands(cell) + cell.add_fingerprint(user) + cell.updateicon() + + src.cell = null + user.visible_message("[user.name] removes the power cell from [src.name]!",\ + "You remove the power cell.") + //user << "You remove the power cell." + charging = 0 + src.update_icon() + return + interact(user) + +/obj/machinery/power/apc/attack_alien(mob/living/carbon/alien/humanoid/user) + if(!user) return + user.do_attack_animation(src) + user.visible_message("[user.name] slashes at the [src.name]!", "You slash at the [src.name]!") + playsound(src.loc, 'sound/weapons/slash.ogg', 100, 1) + + var/allcut = wires.IsAllCut() + + if(beenhit >= pick(3, 4) && wiresexposed != 1) + wiresexposed = 1 + src.update_icon() + src.visible_message("The [src.name]'s cover flies open, exposing the wires!") + + else if(wiresexposed == 1 && allcut == 0) + wires.CutAll() + src.update_icon() + src.visible_message("The [src.name]'s wires are shredded!") + else + beenhit += 1 + return + + +/obj/machinery/power/apc/interact(mob/user) + if(stat & (BROKEN|MAINT)) + return + if(wiresexposed && !istype(user, /mob/living/silicon/ai)) + wires.Interact(user) + else + ui_interact(user) + +/obj/machinery/power/apc/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, force_open = 0) + ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open) + if (!ui) + ui = new(user, src, ui_key, "apc", name, 550, 550) + ui.open() + +/obj/machinery/power/apc/get_ui_data(mob/user) + var/list/data = list( + "locked" = locked, + "isOperating" = operating, + "externalPower" = main_status, + "powerCellStatus" = cell ? cell.percent() : null, + "chargeMode" = chargemode, + "chargingStatus" = charging, + "totalLoad" = lastused_equip + lastused_light + lastused_environ, + "coverLocked" = coverlocked, + "siliconUser" = user.has_unlimited_silicon_privilege, + "malfStatus" = get_malf_status(user), + + "powerChannels" = list( + list( + "title" = "Equipment", + "powerLoad" = lastused_equip, + "status" = equipment, + "topicParams" = list( + "auto" = list("eqp" = 3), + "on" = list("eqp" = 2), + "off" = list("eqp" = 1) + ) + ), + list( + "title" = "Lighting", + "powerLoad" = lastused_light, + "status" = lighting, + "topicParams" = list( + "auto" = list("lgt" = 3), + "on" = list("lgt" = 2), + "off" = list("lgt" = 1) + ) + ), + list( + "title" = "Environment", + "powerLoad" = lastused_environ, + "status" = environ, + "topicParams" = list( + "auto" = list("env" = 3), + "on" = list("env" = 2), + "off" = list("env" = 1) + ) + ) + ) + ) + return data + + +/obj/machinery/power/apc/proc/get_malf_status(mob/user) + if (ticker && ticker.mode && (user.mind in ticker.mode.malf_ai) && istype(user, /mob/living/silicon/ai)) + if (src.malfai == (user:parent ? user:parent : user)) + if (src.occupier == user) + return 3 // 3 = User is shunted in this APC + else if (istype(user.loc, /obj/machinery/power/apc)) + return 4 // 4 = User is shunted in another APC + else + return 2 // 2 = APC hacked by user, and user is in its core. + else + return 1 // 1 = APC not hacked. + else + return 0 // 0 = User is not a Malf AI + +/obj/machinery/power/apc/proc/report() + return "[area.name] : [equipment]/[lighting]/[environ] ([lastused_equip+lastused_light+lastused_environ]) : [cell? cell.percent() : "N/C"] ([charging])" + +/obj/machinery/power/apc/proc/update() + if(operating && !shorted) + area.power_light = (lighting > 1) + area.power_equip = (equipment > 1) + area.power_environ = (environ > 1) +// if (area.name == "AI Chamber") +// spawn(10) +// world << " [area.name] [area.power_equip]" + else + area.power_light = 0 + area.power_equip = 0 + area.power_environ = 0 +// if (area.name == "AI Chamber") +// world << "[area.power_equip]" + area.power_change() + +/obj/machinery/power/apc/proc/isWireCut(wireIndex) + return wires.IsIndexCut(wireIndex) + + +/obj/machinery/power/apc/proc/can_use(mob/user, loud = 0) //used by attack_hand() and Topic() + if(IsAdminGhost(user)) + return 1 + if(user.has_unlimited_silicon_privilege) + var/mob/living/silicon/ai/AI = user + var/mob/living/silicon/robot/robot = user + if ( \ + src.aidisabled || \ + malfhack && istype(malfai) && \ + ( \ + (istype(AI) && (malfai!=AI && malfai != AI.parent)) || \ + (istype(robot) && (robot in malfai.connected_robots)) \ + ) \ + ) + if(!loud) + user << "\The [src] have AI control disabled!" + return 0 + else + if ((!in_range(src, user) || !istype(src.loc, /turf))) + return 0 + return 1 + +/obj/machinery/power/apc/ui_act(action, params) + if(..()) + return + + if(!can_use(usr, 1)) + return + + switch(action) + if("lock") + coverlocked = !coverlocked + if ("breaker") + toggle_breaker() + if("chargemode") + chargemode = !chargemode + if(!chargemode) + charging = 0 + update_icon() + if("channel") + if (params["eqp"]) + var/val = text2num(params["eqp"]) + equipment = setsubsystem(val) + update_icon() + update() + else if (params["lgt"]) + var/val = text2num(params["lgt"]) + lighting = setsubsystem(val) + update_icon() + update() + else if (params["env"]) + var/val = text2num(params["env"]) + environ = setsubsystem(val) + update_icon() + update() + if("toggleaccess") + if(usr.has_unlimited_silicon_privilege) + if(emagged || (stat & (BROKEN|MAINT))) + usr << "The APC does not respond to the command." + else + locked = !locked + update_icon() + if("overload") + if(usr.has_unlimited_silicon_privilege) + src.overload_lighting() + if("hack") + var/mob/living/silicon/ai/malfai = usr + if(get_malf_status(malfai)==1) + if (malfai.malfhacking) + malfai << "You are already hacking an APC." + return 1 + malfai << "Beginning override of APC systems. This takes some time, and you cannot perform other actions during the process." + malfai.malfhack = src + malfai.malfhacking = 1 + sleep(600) + if(src) + if (!src.aidisabled) + malfai.malfhack = null + malfai.malfhacking = 0 + locked = 1 + if (ticker.mode.config_tag == "malfunction") + if (src.z == ZLEVEL_STATION) //if (is_type_in_list(get_area(src), the_station_areas)) + ticker.mode:apcs++ + if(usr:parent) + src.malfai = usr:parent + else + src.malfai = usr + malfai << "Hack complete. The APC is now under your exclusive control." + update_icon() + if("occupy") + if(get_malf_status(usr)) + malfoccupy(usr) + if("deoccupy") + if(get_malf_status(usr)) + malfvacate() + return 1 + +/obj/machinery/power/apc/proc/toggle_breaker() + operating = !operating + + src.update() + update_icon() + +/obj/machinery/power/apc/proc/malfoccupy(mob/living/silicon/ai/malf) + if(!istype(malf)) + return + if(istype(malf.loc, /obj/machinery/power/apc)) // Already in an APC + malf << "You must evacuate your current APC first!" + return + if(!malf.can_shunt) + malf << "You cannot shunt!" + return + if(src.z != 1) + return + src.occupier = new /mob/living/silicon/ai(src,malf.laws,null,1) + src.occupier.adjustOxyLoss(malf.getOxyLoss()) + if(!findtext(src.occupier.name,"APC Copy")) + src.occupier.name = "[malf.name] APC Copy" + if(malf.parent) + src.occupier.parent = malf.parent + else + src.occupier.parent = malf + malf.shunted = 1 + malf.mind.transfer_to(src.occupier) + src.occupier.eyeobj.name = "[src.occupier.name] (AI Eye)" + if(malf.parent) + qdel(malf) + src.occupier.verbs += /mob/living/silicon/ai/proc/corereturn + src.occupier.verbs += /datum/game_mode/malfunction/proc/takeover + src.occupier.cancel_camera() + if (seclevel2num(get_security_level()) == SEC_LEVEL_DELTA) + for(var/obj/item/weapon/pinpointer/point in world) + point.the_disk = src //the pinpointer will detect the shunted AI + + +/obj/machinery/power/apc/proc/malfvacate(forced) + if(!src.occupier) + return + if(src.occupier.parent && src.occupier.parent.stat != 2) + src.occupier.mind.transfer_to(src.occupier.parent) + src.occupier.parent.shunted = 0 + src.occupier.parent.adjustOxyLoss(src.occupier.getOxyLoss()) + src.occupier.parent.cancel_camera() + qdel(src.occupier) + if (seclevel2num(get_security_level()) == SEC_LEVEL_DELTA) + for(var/obj/item/weapon/pinpointer/point in world) + for(var/datum/mind/AI_mind in ticker.mode.malf_ai) + var/mob/living/silicon/ai/A = AI_mind.current // the current mob the mind owns + if(A.stat != DEAD) + point.the_disk = A //The pinpointer tracks the AI back into its core. + + else + src.occupier << "Primary core damaged, unable to return core processes." + if(forced) + src.occupier.loc = src.loc + src.occupier.death() + src.occupier.gib() + for(var/obj/item/weapon/pinpointer/point in world) + point.the_disk = null //the pinpointer will go back to pointing at the nuke disc. + + +/obj/machinery/power/apc/proc/ion_act() + //intended to be exactly the same as an AI malf attack + if(!src.malfhack && src.z == ZLEVEL_STATION) + if(prob(3)) + src.locked = 1 + if (src.cell.charge > 0) +// world << "\red blew APC in [src.loc.loc]" + src.cell.charge = 0 + cell.corrupt() + src.malfhack = 1 + update_icon() + var/datum/effect_system/smoke_spread/smoke = new + smoke.set_up(1, src.loc) + smoke.attach(src) + smoke.start() + var/datum/effect_system/spark_spread/s = new + s.set_up(3, 1, src) + s.start() + visible_message("The [src.name] suddenly lets out a blast of smoke and some sparks!", \ + "You hear sizzling electronics.") + + +/obj/machinery/power/apc/surplus() + if(terminal) + return terminal.surplus() + else + return 0 + +/obj/machinery/power/apc/add_load(amount) + if(terminal && terminal.powernet) + terminal.powernet.load += amount + +/obj/machinery/power/apc/avail() + if(terminal) + return terminal.avail() + else + return 0 + +/obj/machinery/power/apc/process() + + if(stat & (BROKEN|MAINT)) + return + if(!area.requires_power) + return + + + /* + if (equipment > 1) // off=0, off auto=1, on=2, on auto=3 + use_power(src.equip_consumption, EQUIP) + if (lighting > 1) // off=0, off auto=1, on=2, on auto=3 + use_power(src.light_consumption, LIGHT) + if (environ > 1) // off=0, off auto=1, on=2, on auto=3 + use_power(src.environ_consumption, ENVIRON) + + area.calc_lighting() */ + lastused_light = area.usage(STATIC_LIGHT) + lastused_light += area.usage(LIGHT) + lastused_equip = area.usage(EQUIP) + lastused_equip += area.usage(STATIC_EQUIP) + lastused_environ = area.usage(ENVIRON) + lastused_environ += area.usage(STATIC_ENVIRON) + area.clear_usage() + + lastused_total = lastused_light + lastused_equip + lastused_environ + + //store states to update icon if any change + var/last_lt = lighting + var/last_eq = equipment + var/last_en = environ + var/last_ch = charging + + var/excess = surplus() + + if(!src.avail()) + main_status = 0 + else if(excess < 0) + main_status = 1 + else + main_status = 2 + + //if(debug) + // world.log << "Status: [main_status] - Excess: [excess] - Last Equip: [lastused_equip] - Last Light: [lastused_light] - Longterm: [longtermpower]" + + if(cell && !shorted) + + + // draw power from cell as before to power the area + var/cellused = min(cell.charge, CELLRATE * lastused_total) // clamp deduction to a max, amount left in cell + cell.use(cellused) + + if(excess > lastused_total) // if power excess recharge the cell + // by the same amount just used + cell.give(cellused) + add_load(cellused/CELLRATE) // add the load used to recharge the cell + + + else // no excess, and not enough per-apc + + if( (cell.charge/CELLRATE + excess) >= lastused_total) // can we draw enough from cell+grid to cover last usage? + + cell.charge = min(cell.maxcharge, cell.charge + CELLRATE * excess) //recharge with what we can + add_load(excess) // so draw what we can from the grid + charging = 0 + + else // 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) + lighting = autoset(lighting, 0) + environ = autoset(environ, 0) + + + // 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) + longtermpower += 1 + else if(longtermpower > -10) + longtermpower -= 2 + + if(cell.charge <= 0) // zero charge, turn all off + equipment = autoset(equipment, 0) + lighting = autoset(lighting, 0) + environ = autoset(environ, 0) + area.poweralert(0, src) + else if(cell.percent() < 15 && longtermpower < 0) // <15%, turn off lighting & equipment + equipment = autoset(equipment, 2) + lighting = autoset(lighting, 2) + environ = autoset(environ, 1) + area.poweralert(0, src) + else if(cell.percent() < 30 && longtermpower < 0) // <30%, turn off equipment + equipment = autoset(equipment, 2) + lighting = autoset(lighting, 1) + environ = autoset(environ, 1) + area.poweralert(0, src) + else // otherwise all can be on + equipment = autoset(equipment, 1) + lighting = autoset(lighting, 1) + environ = autoset(environ, 1) + area.poweralert(1, src) + if(cell.percent() > 75) + area.poweralert(1, src) + + // 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 capped to % per second constant + var/ch = min(excess*CELLRATE, cell.maxcharge*CHARGELEVEL) + add_load(ch/CELLRATE) // Removes the power we're taking from the grid + cell.give(ch) // actually recharge the cell + + else + charging = 0 // stop charging + chargecount = 0 + + // show cell as fully charged if so + if(cell.charge >= cell.maxcharge) + cell.charge = cell.maxcharge + charging = 2 + + if(chargemode) + if(!charging) + if(excess > cell.maxcharge*CHARGELEVEL) + chargecount++ + else + chargecount = 0 + + if(chargecount == 10) + + chargecount = 0 + charging = 1 + + else // chargemode off + charging = 0 + chargecount = 0 + + else // no cell, switch everything off + + charging = 0 + chargecount = 0 + equipment = autoset(equipment, 0) + lighting = autoset(lighting, 0) + environ = autoset(environ, 0) + area.poweralert(0, src) + + // update icon & area power if anything changed + + if(last_lt != lighting || last_eq != equipment || last_en != environ) + queue_icon_update() + update() + else if (last_ch != charging) + queue_icon_update() + +// val 0=off, 1=off(auto) 2=on 3=on(auto) +// on 0=off, 1=on, 2=autooff + +/obj/machinery/power/apc/proc/autoset(val, on) + if(on==0) + 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) + if(val==1) // if auto-off, return auto-on + return 3 + + else if(on==2) + if(val==3) // if auto-on, return auto-off + return 1 + + return val + + +// damage and destruction acts +/obj/machinery/power/apc/emp_act(severity) + if(cell) + cell.emp_act(severity) + if(occupier) + occupier.emp_act(severity) + lighting = 0 + equipment = 0 + environ = 0 + update_icon() + update() + spawn(600) + equipment = 3 + environ = 3 + update_icon() + update() + ..() + +/obj/machinery/power/apc/ex_act(severity, target) + ..() + if(!gc_destroyed) + switch(severity) + if(2) + if(prob(50)) + set_broken() + if(3) + if(prob(25)) + set_broken() + +/obj/machinery/power/apc/blob_act() + set_broken() + +/obj/machinery/power/apc/disconnect_terminal() + if(terminal) + terminal.master = null + terminal = null + +/obj/machinery/power/apc/proc/set_broken() + if(malfai && operating) + if (ticker.mode.config_tag == "malfunction") + if (src.z == ZLEVEL_STATION) //if (is_type_in_list(get_area(src), the_station_areas)) + ticker.mode:apcs-- + stat |= BROKEN + operating = 0 + if(occupier) + malfvacate(1) + update_icon() + update() + +// overload all the lights in this APC area + +/obj/machinery/power/apc/proc/overload_lighting() + if(/* !get_connection() || */ !operating || shorted) + return + if( cell && cell.charge>=20) + cell.use(20); + spawn(0) + for(var/area/A in area.related) + for(var/obj/machinery/light/L in A) + L.on = 1 + L.broken() + sleep(1) + +/obj/machinery/power/apc/proc/shock(mob/user, prb) + if(!prob(prb)) + return 0 + var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread + s.set_up(5, 1, src) + s.start() + if(isalien(user)) + return 0 + if (electrocute_mob(user, src, src)) + return 1 + else + return 0 + +/obj/machinery/power/apc/proc/setsubsystem(val) + if(cell && cell.charge > 0) + return (val==1) ? 0 : val + else if(val == 3) + return 1 + else + return 0 + +#undef APC_UPDATE_ICON_COOLDOWN + +/*Power module, used for APC construction*/ +/obj/item/weapon/electronics/apc + name = "power control module" + icon_state = "power_mod" + desc = "Heavy-duty switching circuits for power control." diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm index 4a9fe63c99a..04b17c1e4d7 100644 --- a/code/modules/power/singularity/singularity.dm +++ b/code/modules/power/singularity/singularity.dm @@ -1,418 +1,418 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:33 - -/obj/singularity - name = "gravitational singularity" - desc = "A gravitational singularity." - icon = 'icons/obj/singularity.dmi' - icon_state = "singularity_s1" - anchored = 1 - density = 1 - layer = 6 - luminosity = 6 - unacidable = 1 //Don't comment this out. - var/current_size = 1 - var/allowed_size = 1 - var/contained = 1 //Are we going to move around? - var/energy = 100 //How strong are we? - var/dissipate = 1 //Do we lose energy over time? - var/dissipate_delay = 10 - var/dissipate_track = 0 - var/dissipate_strength = 1 //How much energy do we lose? - var/move_self = 1 //Do we move on our own? - var/grav_pull = 4 //How many tiles out do we pull? - var/consume_range = 0 //How many tiles out do we eat - var/event_chance = 15 //Prob for event each tick - var/target = null //its target. moves towards the target if it has one - var/last_failed_movement = 0//Will not move in the same dir if it couldnt before, will help with the getting stuck on fields thing - var/last_warning - var/consumedSupermatter = 0 //If the singularity has eaten a supermatter shard and can go to stage six - -/obj/singularity/New(loc, var/starting_energy = 50, var/temp = 0) - //CARN: admin-alert for chuckle-fuckery. - admin_investigate_setup() - - src.energy = starting_energy - ..() - SSobj.processing |= src - poi_list |= src - for(var/obj/machinery/power/singularity_beacon/singubeacon in machines) - if(singubeacon.active) - target = singubeacon - break - return - -/obj/singularity/Destroy() - SSobj.processing.Remove(src) - poi_list.Remove(src) - return ..() - -/obj/singularity/Move(atom/newloc, direct) - if(current_size >= STAGE_FIVE || check_turfs_in(direct)) - last_failed_movement = 0//Reset this because we moved - return ..() - else - last_failed_movement = direct - return 0 - - -/obj/singularity/attack_hand(mob/user) - consume(user) - return 1 - -/obj/singularity/Process_Spacemove() //The singularity stops drifting for no man! - return 0 - -/obj/singularity/blob_act(severity) - return - -/obj/singularity/ex_act(severity, target) - switch(severity) - if(1) - if(current_size <= STAGE_TWO) - investigate_log("has been destroyed by a heavy explosion.","singulo") - qdel(src) - return - else - energy -= round(((energy+1)/2),1) - if(2) - energy -= round(((energy+1)/3),1) - if(3) - energy -= round(((energy+1)/4),1) - return - - -/obj/singularity/bullet_act(obj/item/projectile/P) - return 0 //Will there be an impact? Who knows. Will we see it? No. - - -/obj/singularity/Bump(atom/A) - consume(A) - return - - -/obj/singularity/Bumped(atom/A) - consume(A) - return - - -/obj/singularity/process() - if(current_size >= STAGE_TWO) - move() - pulse() - if(prob(event_chance))//Chance for it to run a special event TODO:Come up with one or two more that fit - event() - eat() - dissipate() - check_energy() - - return - - -/obj/singularity/attack_ai() //to prevent ais from gibbing themselves when they click on one. - return - - -/obj/singularity/proc/admin_investigate_setup() - last_warning = world.time - var/count = locate(/obj/machinery/field/containment) in ultra_range(30, src, 1) - if(!count) message_admins("A singulo has been created without containment fields active ([x],[y],[z])",1) - investigate_log("was created. [count?"":"No containment fields were active"]","singulo") - -/obj/singularity/proc/dissipate() - if(!dissipate) - return - if(dissipate_track >= dissipate_delay) - src.energy -= dissipate_strength - dissipate_track = 0 - else - dissipate_track++ - - -/obj/singularity/proc/expand(force_size = 0) - var/temp_allowed_size = src.allowed_size - if(force_size) - temp_allowed_size = force_size - if(temp_allowed_size >= STAGE_SIX && !consumedSupermatter) - temp_allowed_size = STAGE_FIVE - switch(temp_allowed_size) - if(STAGE_ONE) - current_size = STAGE_ONE - icon = 'icons/obj/singularity.dmi' - icon_state = "singularity_s1" - pixel_x = 0 - pixel_y = 0 - grav_pull = 4 - consume_range = 0 - dissipate_delay = 10 - dissipate_track = 0 - dissipate_strength = 1 - if(STAGE_TWO)//1 to 3 does not check for the turfs if you put the gens right next to a 1x1 then its going to eat them - current_size = STAGE_TWO - icon = 'icons/effects/96x96.dmi' - icon_state = "singularity_s3" - pixel_x = -32 - pixel_y = -32 - grav_pull = 6 - consume_range = 1 - dissipate_delay = 5 - dissipate_track = 0 - dissipate_strength = 5 - if(STAGE_THREE) - if((check_turfs_in(1,2))&&(check_turfs_in(2,2))&&(check_turfs_in(4,2))&&(check_turfs_in(8,2))) - current_size = STAGE_THREE - icon = 'icons/effects/160x160.dmi' - icon_state = "singularity_s5" - pixel_x = -64 - pixel_y = -64 - grav_pull = 8 - consume_range = 2 - dissipate_delay = 4 - dissipate_track = 0 - dissipate_strength = 20 - if(STAGE_FOUR) - if((check_turfs_in(1,3))&&(check_turfs_in(2,3))&&(check_turfs_in(4,3))&&(check_turfs_in(8,3))) - current_size = STAGE_FOUR - icon = 'icons/effects/224x224.dmi' - icon_state = "singularity_s7" - pixel_x = -96 - pixel_y = -96 - grav_pull = 10 - consume_range = 3 - dissipate_delay = 10 - dissipate_track = 0 - dissipate_strength = 10 - if(STAGE_FIVE)//this one also lacks a check for gens because it eats everything - current_size = STAGE_FIVE - icon = 'icons/effects/288x288.dmi' - icon_state = "singularity_s9" - pixel_x = -128 - pixel_y = -128 - grav_pull = 10 - consume_range = 4 - dissipate = 0 //It cant go smaller due to e loss - if(STAGE_SIX) //This only happens if a stage 5 singulo consumes a supermatter shard. - current_size = STAGE_SIX - icon = 'icons/effects/352x352.dmi' - icon_state = "singularity_s11" - pixel_x = -160 - pixel_y = -160 - grav_pull = 15 - consume_range = 5 - dissipate = 0 - if(current_size == allowed_size) - investigate_log("grew to size [current_size]","singulo") - return 1 - else if(current_size < (--temp_allowed_size)) - expand(temp_allowed_size) - else - return 0 - - -/obj/singularity/proc/check_energy() - if(energy <= 0) - investigate_log("collapsed.","singulo") - qdel(src) - return 0 - switch(energy)//Some of these numbers might need to be changed up later -Mport - if(1 to 199) - allowed_size = STAGE_ONE - if(200 to 499) - allowed_size = STAGE_TWO - if(500 to 999) - allowed_size = STAGE_THREE - if(1000 to 1999) - allowed_size = STAGE_FOUR - if(2000 to INFINITY) - if(energy >= 3000 && consumedSupermatter) - allowed_size = STAGE_SIX - else - allowed_size = STAGE_FIVE - if(current_size != allowed_size) - expand() - return 1 - - -/obj/singularity/proc/eat() - set background = BACKGROUND_ENABLED - var/list/L = grav_pull > 8 ? ultra_range(grav_pull, src, 1) : orange(grav_pull, src) - for(var/atom/X in L) - var/dist = get_dist(X, src) - var/obj/singularity/S = src - if(dist > consume_range) - X.singularity_pull(S, current_size) - else if(dist <= consume_range) - consume(X) - return - - -/obj/singularity/proc/consume(atom/A) - var/gain = A.singularity_act(current_size, src) - src.energy += gain - if(istype(A, /obj/machinery/power/supermatter_shard) && !consumedSupermatter) - desc = "[initial(desc)] It glows fiercely with inner fire." - name = "supermatter-charged [initial(name)]" - consumedSupermatter = 1 - luminosity = 10 - return - - -/obj/singularity/proc/move(force_move = 0) - if(!move_self) - return 0 - - var/movement_dir = pick(alldirs - last_failed_movement) - - if(force_move) - movement_dir = force_move - - if(target && prob(60)) - movement_dir = get_dir(src,target) //moves to a singulo beacon, if there is one - - step(src, movement_dir) - - -/obj/singularity/proc/check_turfs_in(direction = 0, step = 0) - if(!direction) - return 0 - var/steps = 0 - if(!step) - switch(current_size) - if(STAGE_ONE) - steps = 1 - if(STAGE_TWO) - steps = 3//Yes this is right - if(STAGE_THREE) - steps = 3 - if(STAGE_FOUR) - steps = 4 - if(STAGE_FIVE) - steps = 5 - else - steps = step - var/list/turfs = list() - var/turf/T = src.loc - for(var/i = 1 to steps) - T = get_step(T,direction) - if(!isturf(T)) - return 0 - turfs.Add(T) - var/dir2 = 0 - var/dir3 = 0 - switch(direction) - if(NORTH||SOUTH) - dir2 = 4 - dir3 = 8 - if(EAST||WEST) - dir2 = 1 - dir3 = 2 - var/turf/T2 = T - for(var/j = 1 to steps-1) - T2 = get_step(T2,dir2) - if(!isturf(T2)) - return 0 - turfs.Add(T2) - for(var/k = 1 to steps-1) - T = get_step(T,dir3) - if(!isturf(T)) - return 0 - turfs.Add(T) - for(var/turf/T3 in turfs) - if(isnull(T3)) - continue - if(!can_move(T3)) - return 0 - return 1 - - -/obj/singularity/proc/can_move(turf/T) - if(!T) - return 0 - if((locate(/obj/machinery/field/containment) in T)||(locate(/obj/machinery/shieldwall) in T)) - return 0 - else if(locate(/obj/machinery/field/generator) in T) - var/obj/machinery/field/generator/G = locate(/obj/machinery/field/generator) in T - if(G && G.active) - return 0 - else if(locate(/obj/machinery/shieldwallgen) in T) - var/obj/machinery/shieldwallgen/S = locate(/obj/machinery/shieldwallgen) in T - if(S && S.active) - return 0 - return 1 - - -/obj/singularity/proc/event() - var/numb = pick(1,2,3,4,5,6) - switch(numb) - if(1)//EMP - emp_area() - if(2,3)//tox damage all carbon mobs in area - toxmob() - if(4)//Stun mobs who lack optic scanners - mezzer() - if(5,6) //Sets all nearby mobs on fire - if(current_size < STAGE_SIX) - return 0 - combust_mobs() - else - return 0 - return 1 - - -/obj/singularity/proc/toxmob() - var/toxrange = 10 - var/radiation = 15 - var/radiationmin = 3 - if (energy>200) - radiation += round((energy-150)/10,1) - radiationmin = round((radiation/5),1) - for(var/mob/living/M in view(toxrange, src.loc)) - M.rad_act(rand(radiationmin,radiation)) - - -/obj/singularity/proc/combust_mobs() - for(var/mob/living/carbon/C in ultra_range(20, src, 1)) - C.visible_message("[C]'s skin bursts into flame!", \ - "You feel an inner fire as your skin bursts into flames!") - C.adjust_fire_stacks(5) - C.IgniteMob() - return - - -/obj/singularity/proc/mezzer() - for(var/mob/living/carbon/M in oviewers(8, src)) - if(istype(M, /mob/living/carbon/brain)) //Ignore brains - continue - - if(M.stat == CONSCIOUS) - if (istype(M,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - if(istype(H.glasses, /obj/item/clothing/glasses/meson)) - var/obj/item/clothing/glasses/meson/MS = H.glasses - if(MS.vision_flags == SEE_TURFS) - H << "You look directly into the [src.name], good thing you had your protective eyewear on!" - return - - M.apply_effect(3, STUN) - M.visible_message("[M] stares blankly at the [src.name]!", \ - "You look directly into the [src.name] and feel weak.") - return - - -/obj/singularity/proc/emp_area() - empulse(src, 8, 10) - return - - -/obj/singularity/proc/pulse() - - 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(energy) - return - -/obj/singularity/singularity_act() - var/gain = (energy/2) - var/dist = max((current_size - 2),1) - explosion(src.loc,(dist),(dist*2),(dist*4)) - qdel(src) - return(gain) +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:33 + +/obj/singularity + name = "gravitational singularity" + desc = "A gravitational singularity." + icon = 'icons/obj/singularity.dmi' + icon_state = "singularity_s1" + anchored = 1 + density = 1 + layer = 6 + luminosity = 6 + unacidable = 1 //Don't comment this out. + var/current_size = 1 + var/allowed_size = 1 + var/contained = 1 //Are we going to move around? + var/energy = 100 //How strong are we? + var/dissipate = 1 //Do we lose energy over time? + var/dissipate_delay = 10 + var/dissipate_track = 0 + var/dissipate_strength = 1 //How much energy do we lose? + var/move_self = 1 //Do we move on our own? + var/grav_pull = 4 //How many tiles out do we pull? + var/consume_range = 0 //How many tiles out do we eat + var/event_chance = 15 //Prob for event each tick + var/target = null //its target. moves towards the target if it has one + var/last_failed_movement = 0//Will not move in the same dir if it couldnt before, will help with the getting stuck on fields thing + var/last_warning + var/consumedSupermatter = 0 //If the singularity has eaten a supermatter shard and can go to stage six + +/obj/singularity/New(loc, var/starting_energy = 50, var/temp = 0) + //CARN: admin-alert for chuckle-fuckery. + admin_investigate_setup() + + src.energy = starting_energy + ..() + SSobj.processing |= src + poi_list |= src + for(var/obj/machinery/power/singularity_beacon/singubeacon in machines) + if(singubeacon.active) + target = singubeacon + break + return + +/obj/singularity/Destroy() + SSobj.processing.Remove(src) + poi_list.Remove(src) + return ..() + +/obj/singularity/Move(atom/newloc, direct) + if(current_size >= STAGE_FIVE || check_turfs_in(direct)) + last_failed_movement = 0//Reset this because we moved + return ..() + else + last_failed_movement = direct + return 0 + + +/obj/singularity/attack_hand(mob/user) + consume(user) + return 1 + +/obj/singularity/Process_Spacemove() //The singularity stops drifting for no man! + return 0 + +/obj/singularity/blob_act(severity) + return + +/obj/singularity/ex_act(severity, target) + switch(severity) + if(1) + if(current_size <= STAGE_TWO) + investigate_log("has been destroyed by a heavy explosion.","singulo") + qdel(src) + return + else + energy -= round(((energy+1)/2),1) + if(2) + energy -= round(((energy+1)/3),1) + if(3) + energy -= round(((energy+1)/4),1) + return + + +/obj/singularity/bullet_act(obj/item/projectile/P) + return 0 //Will there be an impact? Who knows. Will we see it? No. + + +/obj/singularity/Bump(atom/A) + consume(A) + return + + +/obj/singularity/Bumped(atom/A) + consume(A) + return + + +/obj/singularity/process() + if(current_size >= STAGE_TWO) + move() + pulse() + if(prob(event_chance))//Chance for it to run a special event TODO:Come up with one or two more that fit + event() + eat() + dissipate() + check_energy() + + return + + +/obj/singularity/attack_ai() //to prevent ais from gibbing themselves when they click on one. + return + + +/obj/singularity/proc/admin_investigate_setup() + last_warning = world.time + var/count = locate(/obj/machinery/field/containment) in ultra_range(30, src, 1) + if(!count) message_admins("A singulo has been created without containment fields active ([x],[y],[z])",1) + investigate_log("was created. [count?"":"No containment fields were active"]","singulo") + +/obj/singularity/proc/dissipate() + if(!dissipate) + return + if(dissipate_track >= dissipate_delay) + src.energy -= dissipate_strength + dissipate_track = 0 + else + dissipate_track++ + + +/obj/singularity/proc/expand(force_size = 0) + var/temp_allowed_size = src.allowed_size + if(force_size) + temp_allowed_size = force_size + if(temp_allowed_size >= STAGE_SIX && !consumedSupermatter) + temp_allowed_size = STAGE_FIVE + switch(temp_allowed_size) + if(STAGE_ONE) + current_size = STAGE_ONE + icon = 'icons/obj/singularity.dmi' + icon_state = "singularity_s1" + pixel_x = 0 + pixel_y = 0 + grav_pull = 4 + consume_range = 0 + dissipate_delay = 10 + dissipate_track = 0 + dissipate_strength = 1 + if(STAGE_TWO)//1 to 3 does not check for the turfs if you put the gens right next to a 1x1 then its going to eat them + current_size = STAGE_TWO + icon = 'icons/effects/96x96.dmi' + icon_state = "singularity_s3" + pixel_x = -32 + pixel_y = -32 + grav_pull = 6 + consume_range = 1 + dissipate_delay = 5 + dissipate_track = 0 + dissipate_strength = 5 + if(STAGE_THREE) + if((check_turfs_in(1,2))&&(check_turfs_in(2,2))&&(check_turfs_in(4,2))&&(check_turfs_in(8,2))) + current_size = STAGE_THREE + icon = 'icons/effects/160x160.dmi' + icon_state = "singularity_s5" + pixel_x = -64 + pixel_y = -64 + grav_pull = 8 + consume_range = 2 + dissipate_delay = 4 + dissipate_track = 0 + dissipate_strength = 20 + if(STAGE_FOUR) + if((check_turfs_in(1,3))&&(check_turfs_in(2,3))&&(check_turfs_in(4,3))&&(check_turfs_in(8,3))) + current_size = STAGE_FOUR + icon = 'icons/effects/224x224.dmi' + icon_state = "singularity_s7" + pixel_x = -96 + pixel_y = -96 + grav_pull = 10 + consume_range = 3 + dissipate_delay = 10 + dissipate_track = 0 + dissipate_strength = 10 + if(STAGE_FIVE)//this one also lacks a check for gens because it eats everything + current_size = STAGE_FIVE + icon = 'icons/effects/288x288.dmi' + icon_state = "singularity_s9" + pixel_x = -128 + pixel_y = -128 + grav_pull = 10 + consume_range = 4 + dissipate = 0 //It cant go smaller due to e loss + if(STAGE_SIX) //This only happens if a stage 5 singulo consumes a supermatter shard. + current_size = STAGE_SIX + icon = 'icons/effects/352x352.dmi' + icon_state = "singularity_s11" + pixel_x = -160 + pixel_y = -160 + grav_pull = 15 + consume_range = 5 + dissipate = 0 + if(current_size == allowed_size) + investigate_log("grew to size [current_size]","singulo") + return 1 + else if(current_size < (--temp_allowed_size)) + expand(temp_allowed_size) + else + return 0 + + +/obj/singularity/proc/check_energy() + if(energy <= 0) + investigate_log("collapsed.","singulo") + qdel(src) + return 0 + switch(energy)//Some of these numbers might need to be changed up later -Mport + if(1 to 199) + allowed_size = STAGE_ONE + if(200 to 499) + allowed_size = STAGE_TWO + if(500 to 999) + allowed_size = STAGE_THREE + if(1000 to 1999) + allowed_size = STAGE_FOUR + if(2000 to INFINITY) + if(energy >= 3000 && consumedSupermatter) + allowed_size = STAGE_SIX + else + allowed_size = STAGE_FIVE + if(current_size != allowed_size) + expand() + return 1 + + +/obj/singularity/proc/eat() + set background = BACKGROUND_ENABLED + var/list/L = grav_pull > 8 ? ultra_range(grav_pull, src, 1) : orange(grav_pull, src) + for(var/atom/X in L) + var/dist = get_dist(X, src) + var/obj/singularity/S = src + if(dist > consume_range) + X.singularity_pull(S, current_size) + else if(dist <= consume_range) + consume(X) + return + + +/obj/singularity/proc/consume(atom/A) + var/gain = A.singularity_act(current_size, src) + src.energy += gain + if(istype(A, /obj/machinery/power/supermatter_shard) && !consumedSupermatter) + desc = "[initial(desc)] It glows fiercely with inner fire." + name = "supermatter-charged [initial(name)]" + consumedSupermatter = 1 + luminosity = 10 + return + + +/obj/singularity/proc/move(force_move = 0) + if(!move_self) + return 0 + + var/movement_dir = pick(alldirs - last_failed_movement) + + if(force_move) + movement_dir = force_move + + if(target && prob(60)) + movement_dir = get_dir(src,target) //moves to a singulo beacon, if there is one + + step(src, movement_dir) + + +/obj/singularity/proc/check_turfs_in(direction = 0, step = 0) + if(!direction) + return 0 + var/steps = 0 + if(!step) + switch(current_size) + if(STAGE_ONE) + steps = 1 + if(STAGE_TWO) + steps = 3//Yes this is right + if(STAGE_THREE) + steps = 3 + if(STAGE_FOUR) + steps = 4 + if(STAGE_FIVE) + steps = 5 + else + steps = step + var/list/turfs = list() + var/turf/T = src.loc + for(var/i = 1 to steps) + T = get_step(T,direction) + if(!isturf(T)) + return 0 + turfs.Add(T) + var/dir2 = 0 + var/dir3 = 0 + switch(direction) + if(NORTH||SOUTH) + dir2 = 4 + dir3 = 8 + if(EAST||WEST) + dir2 = 1 + dir3 = 2 + var/turf/T2 = T + for(var/j = 1 to steps-1) + T2 = get_step(T2,dir2) + if(!isturf(T2)) + return 0 + turfs.Add(T2) + for(var/k = 1 to steps-1) + T = get_step(T,dir3) + if(!isturf(T)) + return 0 + turfs.Add(T) + for(var/turf/T3 in turfs) + if(isnull(T3)) + continue + if(!can_move(T3)) + return 0 + return 1 + + +/obj/singularity/proc/can_move(turf/T) + if(!T) + return 0 + if((locate(/obj/machinery/field/containment) in T)||(locate(/obj/machinery/shieldwall) in T)) + return 0 + else if(locate(/obj/machinery/field/generator) in T) + var/obj/machinery/field/generator/G = locate(/obj/machinery/field/generator) in T + if(G && G.active) + return 0 + else if(locate(/obj/machinery/shieldwallgen) in T) + var/obj/machinery/shieldwallgen/S = locate(/obj/machinery/shieldwallgen) in T + if(S && S.active) + return 0 + return 1 + + +/obj/singularity/proc/event() + var/numb = pick(1,2,3,4,5,6) + switch(numb) + if(1)//EMP + emp_area() + if(2,3)//tox damage all carbon mobs in area + toxmob() + if(4)//Stun mobs who lack optic scanners + mezzer() + if(5,6) //Sets all nearby mobs on fire + if(current_size < STAGE_SIX) + return 0 + combust_mobs() + else + return 0 + return 1 + + +/obj/singularity/proc/toxmob() + var/toxrange = 10 + var/radiation = 15 + var/radiationmin = 3 + if (energy>200) + radiation += round((energy-150)/10,1) + radiationmin = round((radiation/5),1) + for(var/mob/living/M in view(toxrange, src.loc)) + M.rad_act(rand(radiationmin,radiation)) + + +/obj/singularity/proc/combust_mobs() + for(var/mob/living/carbon/C in ultra_range(20, src, 1)) + C.visible_message("[C]'s skin bursts into flame!", \ + "You feel an inner fire as your skin bursts into flames!") + C.adjust_fire_stacks(5) + C.IgniteMob() + return + + +/obj/singularity/proc/mezzer() + for(var/mob/living/carbon/M in oviewers(8, src)) + if(istype(M, /mob/living/carbon/brain)) //Ignore brains + continue + + if(M.stat == CONSCIOUS) + if (istype(M,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + if(istype(H.glasses, /obj/item/clothing/glasses/meson)) + var/obj/item/clothing/glasses/meson/MS = H.glasses + if(MS.vision_flags == SEE_TURFS) + H << "You look directly into the [src.name], good thing you had your protective eyewear on!" + return + + M.apply_effect(3, STUN) + M.visible_message("[M] stares blankly at the [src.name]!", \ + "You look directly into the [src.name] and feel weak.") + return + + +/obj/singularity/proc/emp_area() + empulse(src, 8, 10) + return + + +/obj/singularity/proc/pulse() + + 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(energy) + return + +/obj/singularity/singularity_act() + var/gain = (energy/2) + var/dist = max((current_size - 2),1) + explosion(src.loc,(dist),(dist*2),(dist*4)) + qdel(src) + return(gain) diff --git a/code/modules/power/smes.dm b/code/modules/power/smes.dm index 17ea7713b36..a51b1f0c7bd 100644 --- a/code/modules/power/smes.dm +++ b/code/modules/power/smes.dm @@ -1,440 +1,440 @@ -// the SMES -// stores power - -#define SMESRATE 0.05 // rate of internal charge to external power - -//Cache defines -#define SMES_CLEVEL_1 1 -#define SMES_CLEVEL_2 2 -#define SMES_CLEVEL_3 3 -#define SMES_CLEVEL_4 4 -#define SMES_CLEVEL_5 5 -#define SMES_OUTPUTTING 6 -#define SMES_NOT_OUTPUTTING 7 -#define SMES_INPUTTING 8 -#define SMES_INPUT_ATTEMPT 9 - -/obj/machinery/power/smes - name = "power storage unit" - desc = "A high-capacity superconducting magnetic energy storage (SMES) unit." - icon_state = "smes" - density = 1 - anchored = 1 - use_power = 0 - var/capacity = 5e6 // maximum charge - var/charge = 0 // actual charge - - var/input_attempt = 1 // 1 = attempting to charge, 0 = not attempting to charge - var/inputting = 1 // 1 = actually inputting, 0 = not inputting - var/input_level = 50000 // amount of power the SMES attempts to charge by - var/input_level_max = 200000 // cap on input_level - var/input_available = 0 // amount of charge available from input last tick - - var/output_attempt = 1 // 1 = attempting to output, 0 = not attempting to output - var/outputting = 1 // 1 = actually outputting, 0 = not outputting - var/output_level = 50000 // amount of power the SMES attempts to output - var/output_level_max = 200000 // cap on output_level - var/output_used = 0 // amount of power actually outputted. may be less than output_level if the powernet returns excess power - - var/obj/machinery/power/terminal/terminal = null - - var/static/list/smesImageCache - - -/obj/machinery/power/smes/New() - ..() - component_parts = list() - component_parts += new /obj/item/weapon/circuitboard/smes(null) - component_parts += new /obj/item/weapon/stock_parts/cell/high(null) - component_parts += new /obj/item/weapon/stock_parts/cell/high(null) - component_parts += new /obj/item/weapon/stock_parts/cell/high(null) - component_parts += new /obj/item/weapon/stock_parts/cell/high(null) - component_parts += new /obj/item/weapon/stock_parts/cell/high(null) - component_parts += new /obj/item/weapon/stock_parts/capacitor(null) - component_parts += new /obj/item/stack/cable_coil(null, 5) - RefreshParts() - spawn(5) - dir_loop: - for(var/d in cardinal) - var/turf/T = get_step(src, d) - for(var/obj/machinery/power/terminal/term in T) - if(term && term.dir == turn(d, 180)) - terminal = term - break dir_loop - - if(!terminal) - stat |= BROKEN - return - terminal.master = src - update_icon() - return - -/obj/machinery/power/smes/RefreshParts() - var/IO = 0 - var/C = 0 - for(var/obj/item/weapon/stock_parts/capacitor/CP in component_parts) - IO += CP.rating - input_level_max = 200000 * IO - output_level_max = 200000 * IO - for(var/obj/item/weapon/stock_parts/cell/PC in component_parts) - C += PC.maxcharge - capacity = C / (15000) * 1e6 - -/obj/machinery/power/smes/attackby(obj/item/I, mob/user, params) - //opening using screwdriver - if(default_deconstruction_screwdriver(user, "[initial(icon_state)]-o", initial(icon_state), I)) - update_icon() - return - - //changing direction using wrench - if(default_change_direction_wrench(user, I)) - terminal = null - var/turf/T = get_step(src, dir) - for(var/obj/machinery/power/terminal/term in T) - if(term && term.dir == turn(dir, 180)) - terminal = term - terminal.master = src - user << "Terminal found." - break - if(!terminal) - user << "No power source found." - return - stat &= ~BROKEN - update_icon() - return - - //exchanging parts using the RPE - if(exchange_parts(user, I)) - return - - //building and linking a terminal - if(istype(I, /obj/item/stack/cable_coil)) - var/dir = get_dir(user,src) - if(dir & (dir-1))//we don't want diagonal click - return - - if(terminal) //is there already a terminal ? - user << "This SMES already have a power terminal!" - return - - if(!panel_open) //is the panel open ? - user << "You must open the maintenance panel first!" - return - - var/turf/T = get_turf(user) - if (T.intact) //is the floor plating removed ? - user << "You must first remove the floor plating!" - return - - - var/obj/item/stack/cable_coil/C = I - if(C.amount < 10) - user << "You need more wires!" - return - - user << "You start building the power terminal..." - playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) - - if(do_after(user, 20, target = src) && C.amount >= 10) - var/obj/structure/cable/N = T.get_cable_node() //get the connecting node cable, if there's one - if (prob(50) && electrocute_mob(usr, N, N)) //animate the electrocution if uncautious and unlucky - var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread - s.set_up(5, 1, src) - s.start() - return - - C.use(10) - user.visible_message(\ - "[user.name] has built a power terminal.",\ - "You build the power terminal.") - - //build the terminal and link it to the network - make_terminal(T) - terminal.connect_to_network() - return - - //disassembling the terminal - if(istype(I, /obj/item/weapon/wirecutters) && terminal && panel_open) - terminal.dismantle(user) - - //crowbarring it ! - if(default_deconstruction_crowbar(I)) - message_admins("[src] has been deconstructed by [key_name_admin(user)](?) (FLW) in ([x],[y],[z] - JMP)",0,1) - log_game("[src] has been deconstructed by [key_name(user)]") - investigate_log("SMES deconstructed by [key_name(user)]","singulo") - -/obj/machinery/power/smes/Destroy() - if(ticker && ticker.current_state == GAME_STATE_PLAYING) - var/area/area = get_area(src) - message_admins("SMES deleted at ([area.name])") - log_game("SMES deleted at ([area.name])") - investigate_log("deleted at ([area.name])","singulo") - if(terminal) - disconnect_terminal() - return ..() - -// create a terminal object pointing towards the SMES -// wires will attach to this -/obj/machinery/power/smes/proc/make_terminal(turf/T) - terminal = new/obj/machinery/power/terminal(T) - terminal.dir = get_dir(T,src) - terminal.master = src - -/obj/machinery/power/smes/disconnect_terminal() - if(terminal) - terminal.master = null - terminal = null - - -/obj/machinery/power/smes/update_icon() - overlays.Cut() - if(stat & BROKEN) return - - if(panel_open) - return - - if(!smesImageCache || !smesImageCache.len) - smesImageCache = list() - smesImageCache.len = 9 - - smesImageCache[SMES_CLEVEL_1] = image('icons/obj/power.dmi',"smes-og1") - smesImageCache[SMES_CLEVEL_2] = image('icons/obj/power.dmi',"smes-og2") - smesImageCache[SMES_CLEVEL_3] = image('icons/obj/power.dmi',"smes-og3") - smesImageCache[SMES_CLEVEL_4] = image('icons/obj/power.dmi',"smes-og4") - smesImageCache[SMES_CLEVEL_5] = image('icons/obj/power.dmi',"smes-og5") - - smesImageCache[SMES_OUTPUTTING] = image('icons/obj/power.dmi', "smes-op1") - smesImageCache[SMES_NOT_OUTPUTTING] = image('icons/obj/power.dmi',"smes-op0") - smesImageCache[SMES_INPUTTING] = image('icons/obj/power.dmi', "smes-oc1") - smesImageCache[SMES_INPUT_ATTEMPT] = image('icons/obj/power.dmi', "smes-oc0") - - if(outputting) - overlays += smesImageCache[SMES_OUTPUTTING] - else - overlays += smesImageCache[SMES_NOT_OUTPUTTING] - - if(inputting) - overlays += smesImageCache[SMES_INPUTTING] - else - if(input_attempt) - overlays += smesImageCache[SMES_INPUT_ATTEMPT] - - var/clevel = chargedisplay() - if(clevel>0) - overlays += smesImageCache[clevel] - return - - -/obj/machinery/power/smes/proc/chargedisplay() - return round(5.5*charge/capacity) - -/obj/machinery/power/smes/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 = inputting - var/last_onln = outputting - - //inputting - if(terminal && input_attempt) - input_available = terminal.surplus() - - if(inputting) - if(input_available > 0 && input_available >= input_level) // if there's power available, try to charge - - var/load = min((capacity-charge)/SMESRATE, input_level) // 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 - inputting = 0 // stop inputting - - else - if(input_attempt && input_available > 0 && input_available >= input_level) - inputting = 1 - - //outputting - if(outputting) - output_used = min( charge/SMESRATE, output_level) //limit output to that stored - - charge -= output_used*SMESRATE // reduce the storage (may be recovered in /restore() if excessive) - - add_avail(output_used) // add output to powernet (smes side) - - if(output_used < 0.0001) // either from no charge or set to 0 - outputting = 0 - investigate_log("lost power and turned off","singulo") - else if(output_attempt && charge > output_level && output_level > 0) - outputting = 1 - else - output_used = 0 - - // only update icon if state changed - if(last_disp != chargedisplay() || last_chrg != inputting || last_onln != outputting) - update_icon() - - - -// called after all power processes are finished -// restores charge level to smes if there was excess this ptick -/obj/machinery/power/smes/proc/restore() - if(stat & BROKEN) - return - - if(!outputting) - output_used = 0 - return - - var/excess = powernet.netexcess // this was how much wasn't used on the network last ptick, minus any removed by other SMESes - - excess = min(output_used, excess) // clamp it to how much was actually output by this SMES last ptick - - excess = min((capacity-charge)/SMESRATE, excess) // for safety, also limit recharge by space capacity of SMES (shouldn't happen) - - // now recharge this amount - - var/clev = chargedisplay() - - charge += excess * SMESRATE // restore unused power - powernet.netexcess -= excess // remove the excess from the powernet, so later SMESes don't try to use it - - output_used -= excess - - if(clev != chargedisplay() ) //if needed updates the icons overlay - update_icon() - return - - -/obj/machinery/power/smes/add_load(amount) - if(terminal && terminal.powernet) - terminal.powernet.load += amount - -/obj/machinery/power/smes/attack_hand(mob/user) - if (!user) - return - add_fingerprint(user) - interact(user) - -/obj/machinery/power/smes/interact(mob/user) - if (stat & BROKEN) - return - ui_interact(user) - -/obj/machinery/power/smes/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 0) - ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open) - if (!ui) - ui = new(user, src, ui_key, "smes", name, 500, 455) - ui.open() - -/obj/machinery/power/smes/get_ui_data() - var/list/data = list( - "capacityPercent" = round(100*charge/capacity, 0.1), - "capacity" = capacity, - "charge" = charge, - - "inputAttempt" = input_attempt, - "inputting" = inputting, - "inputLevel" = input_level, - "inputLevelMax" = input_level_max, - "inputAvailable" = input_available, - - "outputAttempt" = output_attempt, - "outputting" = outputting, - "outputLevel" = output_level, - "outputLevelMax" = output_level_max, - "outputUsed" = output_used - ) - return data - -/obj/machinery/power/smes/ui_act(action, params) - if(..()) - return - - switch(action) - if("tryinput") - input_attempt = !input_attempt - log_smes(usr.ckey) - update_icon() - if("tryoutput") - output_attempt = !output_attempt - log_smes(usr.ckey) - update_icon() - if("input") - switch(params["set"]) - if("custom") - var/custom = input(usr, "What rate would you like this SMES to attempt to charge at? Max is [input_level_max].") as null|num - if(custom) - input_level = custom - if("min") - input_level = 0 - if("max") - input_level = input_level_max - if("plus") - input_level += 10000 - if("minus") - input_level -= 10000 - input_level = Clamp(input_level, 0, input_level_max) - log_smes(usr.ckey) - if("output") - switch(params["set"]) - if("custom") - var/custom = input(usr, "What rate would you like this SMES to attempt to output at? Max is [output_level_max].") as null|num - if(custom) - output_level = custom - if("min") - output_level = 0 - if("max") - output_level = output_level_max - if("plus") - output_level += 10000 - if("minus") - output_level -= 10000 - output_level = Clamp(output_level, 0, output_level_max) - log_smes(usr.ckey) - return 1 - -/obj/machinery/power/smes/proc/log_smes(user = "") - investigate_log("input/output; [input_level>output_level?"":""][input_level]/[output_level] | Charge: [charge] | Output-mode: [output_attempt?"on":"off"] | Input-mode: [input_attempt?"auto":"off"] by [user]","singulo") - - -/obj/machinery/power/smes/emp_act(severity) - input_attempt = rand(0,1) - inputting = input_attempt - output_attempt = rand(0,1) - outputting = output_attempt - output_level = rand(0, output_level_max) - input_level = rand(0, input_level_max) - charge -= 1e6/severity - if (charge < 0) - charge = 0 - update_icon() - log_smes("an emp") - ..() - -/obj/machinery/power/smes/engineering - charge = 1.5e6 // Engineering starts with some charge for singulo - -/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 - ..() - - -#undef SMESRATE - -#undef SMES_CLEVEL_1 -#undef SMES_CLEVEL_2 -#undef SMES_CLEVEL_3 -#undef SMES_CLEVEL_4 -#undef SMES_CLEVEL_5 -#undef SMES_OUTPUTTING -#undef SMES_NOT_OUTPUTTING -#undef SMES_INPUTTING +// the SMES +// stores power + +#define SMESRATE 0.05 // rate of internal charge to external power + +//Cache defines +#define SMES_CLEVEL_1 1 +#define SMES_CLEVEL_2 2 +#define SMES_CLEVEL_3 3 +#define SMES_CLEVEL_4 4 +#define SMES_CLEVEL_5 5 +#define SMES_OUTPUTTING 6 +#define SMES_NOT_OUTPUTTING 7 +#define SMES_INPUTTING 8 +#define SMES_INPUT_ATTEMPT 9 + +/obj/machinery/power/smes + name = "power storage unit" + desc = "A high-capacity superconducting magnetic energy storage (SMES) unit." + icon_state = "smes" + density = 1 + anchored = 1 + use_power = 0 + var/capacity = 5e6 // maximum charge + var/charge = 0 // actual charge + + var/input_attempt = 1 // 1 = attempting to charge, 0 = not attempting to charge + var/inputting = 1 // 1 = actually inputting, 0 = not inputting + var/input_level = 50000 // amount of power the SMES attempts to charge by + var/input_level_max = 200000 // cap on input_level + var/input_available = 0 // amount of charge available from input last tick + + var/output_attempt = 1 // 1 = attempting to output, 0 = not attempting to output + var/outputting = 1 // 1 = actually outputting, 0 = not outputting + var/output_level = 50000 // amount of power the SMES attempts to output + var/output_level_max = 200000 // cap on output_level + var/output_used = 0 // amount of power actually outputted. may be less than output_level if the powernet returns excess power + + var/obj/machinery/power/terminal/terminal = null + + var/static/list/smesImageCache + + +/obj/machinery/power/smes/New() + ..() + component_parts = list() + component_parts += new /obj/item/weapon/circuitboard/smes(null) + component_parts += new /obj/item/weapon/stock_parts/cell/high(null) + component_parts += new /obj/item/weapon/stock_parts/cell/high(null) + component_parts += new /obj/item/weapon/stock_parts/cell/high(null) + component_parts += new /obj/item/weapon/stock_parts/cell/high(null) + component_parts += new /obj/item/weapon/stock_parts/cell/high(null) + component_parts += new /obj/item/weapon/stock_parts/capacitor(null) + component_parts += new /obj/item/stack/cable_coil(null, 5) + RefreshParts() + spawn(5) + dir_loop: + for(var/d in cardinal) + var/turf/T = get_step(src, d) + for(var/obj/machinery/power/terminal/term in T) + if(term && term.dir == turn(d, 180)) + terminal = term + break dir_loop + + if(!terminal) + stat |= BROKEN + return + terminal.master = src + update_icon() + return + +/obj/machinery/power/smes/RefreshParts() + var/IO = 0 + var/C = 0 + for(var/obj/item/weapon/stock_parts/capacitor/CP in component_parts) + IO += CP.rating + input_level_max = 200000 * IO + output_level_max = 200000 * IO + for(var/obj/item/weapon/stock_parts/cell/PC in component_parts) + C += PC.maxcharge + capacity = C / (15000) * 1e6 + +/obj/machinery/power/smes/attackby(obj/item/I, mob/user, params) + //opening using screwdriver + if(default_deconstruction_screwdriver(user, "[initial(icon_state)]-o", initial(icon_state), I)) + update_icon() + return + + //changing direction using wrench + if(default_change_direction_wrench(user, I)) + terminal = null + var/turf/T = get_step(src, dir) + for(var/obj/machinery/power/terminal/term in T) + if(term && term.dir == turn(dir, 180)) + terminal = term + terminal.master = src + user << "Terminal found." + break + if(!terminal) + user << "No power source found." + return + stat &= ~BROKEN + update_icon() + return + + //exchanging parts using the RPE + if(exchange_parts(user, I)) + return + + //building and linking a terminal + if(istype(I, /obj/item/stack/cable_coil)) + var/dir = get_dir(user,src) + if(dir & (dir-1))//we don't want diagonal click + return + + if(terminal) //is there already a terminal ? + user << "This SMES already have a power terminal!" + return + + if(!panel_open) //is the panel open ? + user << "You must open the maintenance panel first!" + return + + var/turf/T = get_turf(user) + if (T.intact) //is the floor plating removed ? + user << "You must first remove the floor plating!" + return + + + var/obj/item/stack/cable_coil/C = I + if(C.amount < 10) + user << "You need more wires!" + return + + user << "You start building the power terminal..." + playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) + + if(do_after(user, 20, target = src) && C.amount >= 10) + var/obj/structure/cable/N = T.get_cable_node() //get the connecting node cable, if there's one + if (prob(50) && electrocute_mob(usr, N, N)) //animate the electrocution if uncautious and unlucky + var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread + s.set_up(5, 1, src) + s.start() + return + + C.use(10) + user.visible_message(\ + "[user.name] has built a power terminal.",\ + "You build the power terminal.") + + //build the terminal and link it to the network + make_terminal(T) + terminal.connect_to_network() + return + + //disassembling the terminal + if(istype(I, /obj/item/weapon/wirecutters) && terminal && panel_open) + terminal.dismantle(user) + + //crowbarring it ! + if(default_deconstruction_crowbar(I)) + message_admins("[src] has been deconstructed by [key_name_admin(user)](?) (FLW) in ([x],[y],[z] - JMP)",0,1) + log_game("[src] has been deconstructed by [key_name(user)]") + investigate_log("SMES deconstructed by [key_name(user)]","singulo") + +/obj/machinery/power/smes/Destroy() + if(ticker && ticker.current_state == GAME_STATE_PLAYING) + var/area/area = get_area(src) + message_admins("SMES deleted at ([area.name])") + log_game("SMES deleted at ([area.name])") + investigate_log("deleted at ([area.name])","singulo") + if(terminal) + disconnect_terminal() + return ..() + +// create a terminal object pointing towards the SMES +// wires will attach to this +/obj/machinery/power/smes/proc/make_terminal(turf/T) + terminal = new/obj/machinery/power/terminal(T) + terminal.dir = get_dir(T,src) + terminal.master = src + +/obj/machinery/power/smes/disconnect_terminal() + if(terminal) + terminal.master = null + terminal = null + + +/obj/machinery/power/smes/update_icon() + overlays.Cut() + if(stat & BROKEN) return + + if(panel_open) + return + + if(!smesImageCache || !smesImageCache.len) + smesImageCache = list() + smesImageCache.len = 9 + + smesImageCache[SMES_CLEVEL_1] = image('icons/obj/power.dmi',"smes-og1") + smesImageCache[SMES_CLEVEL_2] = image('icons/obj/power.dmi',"smes-og2") + smesImageCache[SMES_CLEVEL_3] = image('icons/obj/power.dmi',"smes-og3") + smesImageCache[SMES_CLEVEL_4] = image('icons/obj/power.dmi',"smes-og4") + smesImageCache[SMES_CLEVEL_5] = image('icons/obj/power.dmi',"smes-og5") + + smesImageCache[SMES_OUTPUTTING] = image('icons/obj/power.dmi', "smes-op1") + smesImageCache[SMES_NOT_OUTPUTTING] = image('icons/obj/power.dmi',"smes-op0") + smesImageCache[SMES_INPUTTING] = image('icons/obj/power.dmi', "smes-oc1") + smesImageCache[SMES_INPUT_ATTEMPT] = image('icons/obj/power.dmi', "smes-oc0") + + if(outputting) + overlays += smesImageCache[SMES_OUTPUTTING] + else + overlays += smesImageCache[SMES_NOT_OUTPUTTING] + + if(inputting) + overlays += smesImageCache[SMES_INPUTTING] + else + if(input_attempt) + overlays += smesImageCache[SMES_INPUT_ATTEMPT] + + var/clevel = chargedisplay() + if(clevel>0) + overlays += smesImageCache[clevel] + return + + +/obj/machinery/power/smes/proc/chargedisplay() + return round(5.5*charge/capacity) + +/obj/machinery/power/smes/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 = inputting + var/last_onln = outputting + + //inputting + if(terminal && input_attempt) + input_available = terminal.surplus() + + if(inputting) + if(input_available > 0 && input_available >= input_level) // if there's power available, try to charge + + var/load = min((capacity-charge)/SMESRATE, input_level) // 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 + inputting = 0 // stop inputting + + else + if(input_attempt && input_available > 0 && input_available >= input_level) + inputting = 1 + + //outputting + if(outputting) + output_used = min( charge/SMESRATE, output_level) //limit output to that stored + + charge -= output_used*SMESRATE // reduce the storage (may be recovered in /restore() if excessive) + + add_avail(output_used) // add output to powernet (smes side) + + if(output_used < 0.0001) // either from no charge or set to 0 + outputting = 0 + investigate_log("lost power and turned off","singulo") + else if(output_attempt && charge > output_level && output_level > 0) + outputting = 1 + else + output_used = 0 + + // only update icon if state changed + if(last_disp != chargedisplay() || last_chrg != inputting || last_onln != outputting) + update_icon() + + + +// called after all power processes are finished +// restores charge level to smes if there was excess this ptick +/obj/machinery/power/smes/proc/restore() + if(stat & BROKEN) + return + + if(!outputting) + output_used = 0 + return + + var/excess = powernet.netexcess // this was how much wasn't used on the network last ptick, minus any removed by other SMESes + + excess = min(output_used, excess) // clamp it to how much was actually output by this SMES last ptick + + excess = min((capacity-charge)/SMESRATE, excess) // for safety, also limit recharge by space capacity of SMES (shouldn't happen) + + // now recharge this amount + + var/clev = chargedisplay() + + charge += excess * SMESRATE // restore unused power + powernet.netexcess -= excess // remove the excess from the powernet, so later SMESes don't try to use it + + output_used -= excess + + if(clev != chargedisplay() ) //if needed updates the icons overlay + update_icon() + return + + +/obj/machinery/power/smes/add_load(amount) + if(terminal && terminal.powernet) + terminal.powernet.load += amount + +/obj/machinery/power/smes/attack_hand(mob/user) + if (!user) + return + add_fingerprint(user) + interact(user) + +/obj/machinery/power/smes/interact(mob/user) + if (stat & BROKEN) + return + ui_interact(user) + +/obj/machinery/power/smes/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 0) + ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open) + if (!ui) + ui = new(user, src, ui_key, "smes", name, 500, 455) + ui.open() + +/obj/machinery/power/smes/get_ui_data() + var/list/data = list( + "capacityPercent" = round(100*charge/capacity, 0.1), + "capacity" = capacity, + "charge" = charge, + + "inputAttempt" = input_attempt, + "inputting" = inputting, + "inputLevel" = input_level, + "inputLevelMax" = input_level_max, + "inputAvailable" = input_available, + + "outputAttempt" = output_attempt, + "outputting" = outputting, + "outputLevel" = output_level, + "outputLevelMax" = output_level_max, + "outputUsed" = output_used + ) + return data + +/obj/machinery/power/smes/ui_act(action, params) + if(..()) + return + + switch(action) + if("tryinput") + input_attempt = !input_attempt + log_smes(usr.ckey) + update_icon() + if("tryoutput") + output_attempt = !output_attempt + log_smes(usr.ckey) + update_icon() + if("input") + switch(params["set"]) + if("custom") + var/custom = input(usr, "What rate would you like this SMES to attempt to charge at? Max is [input_level_max].") as null|num + if(custom) + input_level = custom + if("min") + input_level = 0 + if("max") + input_level = input_level_max + if("plus") + input_level += 10000 + if("minus") + input_level -= 10000 + input_level = Clamp(input_level, 0, input_level_max) + log_smes(usr.ckey) + if("output") + switch(params["set"]) + if("custom") + var/custom = input(usr, "What rate would you like this SMES to attempt to output at? Max is [output_level_max].") as null|num + if(custom) + output_level = custom + if("min") + output_level = 0 + if("max") + output_level = output_level_max + if("plus") + output_level += 10000 + if("minus") + output_level -= 10000 + output_level = Clamp(output_level, 0, output_level_max) + log_smes(usr.ckey) + return 1 + +/obj/machinery/power/smes/proc/log_smes(user = "") + investigate_log("input/output; [input_level>output_level?"":""][input_level]/[output_level] | Charge: [charge] | Output-mode: [output_attempt?"on":"off"] | Input-mode: [input_attempt?"auto":"off"] by [user]","singulo") + + +/obj/machinery/power/smes/emp_act(severity) + input_attempt = rand(0,1) + inputting = input_attempt + output_attempt = rand(0,1) + outputting = output_attempt + output_level = rand(0, output_level_max) + input_level = rand(0, input_level_max) + charge -= 1e6/severity + if (charge < 0) + charge = 0 + update_icon() + log_smes("an emp") + ..() + +/obj/machinery/power/smes/engineering + charge = 1.5e6 // Engineering starts with some charge for singulo + +/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 + ..() + + +#undef SMESRATE + +#undef SMES_CLEVEL_1 +#undef SMES_CLEVEL_2 +#undef SMES_CLEVEL_3 +#undef SMES_CLEVEL_4 +#undef SMES_CLEVEL_5 +#undef SMES_OUTPUTTING +#undef SMES_NOT_OUTPUTTING +#undef SMES_INPUTTING #undef SMES_INPUT_ATTEMPT \ No newline at end of file diff --git a/code/modules/power/solar.dm b/code/modules/power/solar.dm index 7992fd85fdc..58f62ad9f8f 100644 --- a/code/modules/power/solar.dm +++ b/code/modules/power/solar.dm @@ -1,518 +1,518 @@ -#define SOLAR_MAX_DIST 40 -#define SOLARGENRATE 1500 - -/obj/machinery/power/solar - name = "solar panel" - desc = "A solar panel. Generates electricity when in contact with sunlight." - icon = 'icons/obj/power.dmi' - icon_state = "sp_base" - anchored = 1 - density = 1 - use_power = 0 - idle_power_usage = 0 - active_power_usage = 0 - var/id = 0 - var/health = 10 - var/obscured = 0 - var/sunfrac = 0 - var/adir = SOUTH // actual dir - var/ndir = SOUTH // target dir - var/turn_angle = 0 - var/obj/machinery/power/solar_control/control = null - -/obj/machinery/power/solar/New(var/turf/loc, var/obj/item/solar_assembly/S) - ..(loc) - Make(S) - connect_to_network() - -/obj/machinery/power/solar/Destroy() - unset_control() //remove from control computer - return ..() - -//set the control of the panel to a given computer if closer than SOLAR_MAX_DIST -/obj/machinery/power/solar/proc/set_control(obj/machinery/power/solar_control/SC) - if(!SC || (get_dist(src, SC) > SOLAR_MAX_DIST)) - return 0 - control = SC - SC.connected_panels |= src - return 1 - -//set the control of the panel to null and removes it from the control list of the previous control computer if needed -/obj/machinery/power/solar/proc/unset_control() - if(control) - control.connected_panels.Remove(src) - control = null - -/obj/machinery/power/solar/proc/Make(obj/item/solar_assembly/S) - if(!S) - S = new /obj/item/solar_assembly(src) - S.glass_type = /obj/item/stack/sheet/glass - S.anchored = 1 - S.loc = src - if(S.glass_type == /obj/item/stack/sheet/rglass) //if the panel is in reinforced glass - health *= 2 //this need to be placed here, because panels already on the map don't have an assembly linked to - update_icon() - - - -/obj/machinery/power/solar/attackby(obj/item/weapon/W, mob/user, params) - if(istype(W, /obj/item/weapon/crowbar)) - playsound(src.loc, 'sound/machines/click.ogg', 50, 1) - user.visible_message("[user] begins to take the glass off the solar panel.", "You begin to take the glass off the solar panel...") - if(do_after(user, 50/W.toolspeed, target = src)) - var/obj/item/solar_assembly/S = locate() in src - if(S) - S.loc = src.loc - S.give_glass(stat & BROKEN) - - playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) - user.visible_message("[user] takes the glass off the solar panel.", "You take the glass off the solar panel.") - qdel(src) - return - else if (W) - src.add_fingerprint(user) - src.health -= W.force - src.healthcheck() - ..() - -/obj/machinery/power/solar/proc/healthcheck() - if (src.health <= 0) - if(!(stat & BROKEN)) - set_broken() - else - new /obj/item/weapon/shard(src.loc) - new /obj/item/weapon/shard(src.loc) - qdel(src) - return - return - - -/obj/machinery/power/solar/update_icon() - ..() - overlays.Cut() - if(stat & BROKEN) - overlays += image('icons/obj/power.dmi', icon_state = "solar_panel-b", layer = FLY_LAYER) - else - overlays += image('icons/obj/power.dmi', icon_state = "solar_panel", layer = FLY_LAYER) - src.dir = angle2dir(adir) - return - -//calculates the fraction of the sunlight that the panel recieves -/obj/machinery/power/solar/proc/update_solar_exposure() - if(obscured) - sunfrac = 0 - return - - //find the smaller angle between the direction the panel is facing and the direction of the sun (the sign is not important here) - var/p_angle = min(abs(adir - SSsun.angle), 360 - abs(adir - SSsun.angle)) - - if(p_angle > 90) // if facing more than 90deg from sun, zero output - sunfrac = 0 - return - - sunfrac = cos(p_angle) ** 2 - //isn't the power recieved from the incoming light proportionnal to cos(p_angle) (Lambert's cosine law) rather than cos(p_angle)^2 ? - -/obj/machinery/power/solar/process()//TODO: remove/add this from machines to save on processing as needed ~Carn PRIORITY - if(stat & BROKEN) - return - if(!control) //if there's no sun or the panel is not linked to a solar control computer, no need to proceed - return - - if(powernet) - if(powernet == control.powernet)//check if the panel is still connected to the computer - if(obscured) //get no light from the sun, so don't generate power - return - var/sgen = SOLARGENRATE * sunfrac - add_avail(sgen) - control.gen += sgen - else //if we're no longer on the same powernet, remove from control computer - unset_control() - -/obj/machinery/power/solar/proc/set_broken() - . = (!(stat & BROKEN)) - stat |= BROKEN - unset_control() - update_icon() - return - - -/obj/machinery/power/solar/ex_act(severity, target) - ..() - if(!gc_destroyed) - switch(severity) - if(2) - if(prob(50)) - set_broken() - if(3) - if(prob(25)) - set_broken() - -/obj/machinery/power/solar/fake/New(var/turf/loc, var/obj/item/solar_assembly/S) - ..(loc, S, 0) - -/obj/machinery/power/solar/fake/process() - . = PROCESS_KILL - return - -//trace towards sun to see if we're in shadow -/obj/machinery/power/solar/proc/occlusion() - - var/ax = x // start at the solar panel - var/ay = y - var/turf/T = null - var/dx = SSsun.dx - var/dy = SSsun.dy - - for(var/i = 1 to 20) // 20 steps is enough - ax += dx // do step - ay += dy - - T = locate( round(ax,0.5),round(ay,0.5),z) - - if(T.x == 1 || T.x==world.maxx || T.y==1 || T.y==world.maxy) // not obscured if we reach the edge - break - - if(T.density) // if we hit a solid turf, panel is obscured - obscured = 1 - return - - obscured = 0 // if hit the edge or stepped 20 times, not obscured - update_solar_exposure() - - -// -// Solar Assembly - For construction of solar arrays. -// - -/obj/item/solar_assembly - name = "solar panel assembly" - desc = "A solar panel assembly kit, allows constructions of a solar panel, or with a tracking circuit board, a solar tracker." - icon = 'icons/obj/power.dmi' - icon_state = "sp_base" - item_state = "electropack" - w_class = 4 // Pretty big! - anchored = 0 - var/tracker = 0 - var/glass_type = null - -/obj/item/solar_assembly/attack_hand(mob/user) - if(!anchored && isturf(loc)) // You can't pick it up - ..() - -// Give back the glass type we were supplied with -/obj/item/solar_assembly/proc/give_glass(device_broken) - if(device_broken) - new /obj/item/weapon/shard(loc) - new /obj/item/weapon/shard(loc) - else if(glass_type) - var/obj/item/stack/sheet/S = new glass_type(loc) - S.amount = 2 - glass_type = null - - -/obj/item/solar_assembly/attackby(obj/item/weapon/W, mob/user, params) - if(istype(W, /obj/item/weapon/wrench) && isturf(loc)) - if(isinspace()) - user << "You can't secure [src] here." - return - anchored = !anchored - if(anchored) - user.visible_message("[user] wrenches the solar assembly into place.", "You wrench the solar assembly into place.") - playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1) - else - user.visible_message("[user] unwrenches the solar assembly from its place.", "You unwrench the solar assembly from its place.") - playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1) - return 1 - - if(istype(W, /obj/item/stack/sheet/glass) || istype(W, /obj/item/stack/sheet/rglass)) - if(!anchored) - user << "You need to secure the assembly before you can add glass." - return - var/obj/item/stack/sheet/S = W - if(S.use(2)) - glass_type = W.type - playsound(src.loc, 'sound/machines/click.ogg', 50, 1) - user.visible_message("[user] places the glass on the solar assembly.", "You place the glass on the solar assembly.") - if(tracker) - new /obj/machinery/power/tracker(get_turf(src), src) - else - new /obj/machinery/power/solar(get_turf(src), src) - else - user << "You need two sheets of glass to put them into a solar panel!" - return - return 1 - - if(!tracker) - if(istype(W, /obj/item/weapon/electronics/tracker)) - if(!user.drop_item()) - return - tracker = 1 - qdel(W) - user.visible_message("[user] inserts the electronics into the solar assembly.", "You insert the electronics into the solar assembly.") - return 1 - else - if(istype(W, /obj/item/weapon/crowbar)) - new /obj/item/weapon/electronics/tracker(src.loc) - tracker = 0 - user.visible_message("[user] takes out the electronics from the solar assembly.", "You take out the electronics from the solar assembly.") - return 1 - ..() - -// -// Solar Control Computer -// - -/obj/machinery/power/solar_control - name = "solar panel control" - desc = "A controller for solar panel arrays." - icon = 'icons/obj/computer.dmi' - icon_state = "computer" - anchored = 1 - density = 1 - use_power = 1 - idle_power_usage = 250 - var/icon_screen = "solar" - var/icon_keyboard = "power_key" - var/id = 0 - var/cdir = 0 - var/targetdir = 0 // target angle in manual tracking (since it updates every game minute) - var/gen = 0 - var/lastgen = 0 - var/track = 0 // 0= off 1=timed 2=auto (tracker) - var/trackrate = 600 // 300-900 seconds - var/nexttime = 0 // time for a panel to rotate of 1° in manual tracking - var/obj/machinery/power/tracker/connected_tracker = null - var/list/connected_panels = list() - - -/obj/machinery/power/solar_control/New() - ..() - if(ticker) - initialize() - connect_to_network() - -/obj/machinery/power/solar_control/Destroy() - for(var/obj/machinery/power/solar/M in connected_panels) - M.unset_control() - if(connected_tracker) - connected_tracker.unset_control() - return ..() - -/obj/machinery/power/solar_control/disconnect_from_network() - ..() - SSsun.solars.Remove(src) - -/obj/machinery/power/solar_control/connect_to_network() - var/to_return = ..() - if(powernet) //if connected and not already in solar_list... - SSsun.solars |= src //... add it - return to_return - -//search for unconnected panels and trackers in the computer powernet and connect them -/obj/machinery/power/solar_control/proc/search_for_connected() - if(powernet) - for(var/obj/machinery/power/M in powernet.nodes) - if(istype(M, /obj/machinery/power/solar)) - var/obj/machinery/power/solar/S = M - if(!S.control) //i.e unconnected - S.set_control(src) - else if(istype(M, /obj/machinery/power/tracker)) - if(!connected_tracker) //if there's already a tracker connected to the computer don't add another - var/obj/machinery/power/tracker/T = M - if(!T.control) //i.e unconnected - T.set_control(src) - -//called by the sun controller, update the facing angle (either manually or via tracking) and rotates the panels accordingly -/obj/machinery/power/solar_control/proc/update() - if(stat & (NOPOWER | BROKEN)) - return - - switch(track) - if(1) - if(trackrate) //we're manual tracking. If we set a rotation speed... - cdir = targetdir //...the current direction is the targetted one (and rotates panels to it) - if(2) // auto-tracking - if(connected_tracker) - connected_tracker.set_angle(SSsun.angle) - - set_panels(cdir) - updateDialog() - - -/obj/machinery/power/solar_control/initialize() - ..() - if(!powernet) return - set_panels(cdir) - -/obj/machinery/power/solar_control/update_icon() - overlays.Cut() - if(stat & NOPOWER) - overlays += "[icon_keyboard]_off" - return - overlays += icon_keyboard - if(stat & BROKEN) - overlays += "[icon_state]_broken" - else - overlays += icon_screen - if(cdir > -1) - overlays += image('icons/obj/computer.dmi', "solcon-o", FLY_LAYER, angle2dir(cdir)) - -/obj/machinery/power/solar_control/attack_hand(mob/user) - if (..() || !user) - return - add_fingerprint(user) - interact(user) - -/obj/machinery/power/solar_control/interact(mob/user) - if (stat & BROKEN) - return - ui_interact(user) - -/obj/machinery/power/solar_control/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 0) - ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open) - if (!ui) - ui = new(user, src, ui_key, "solar_control", name, 515, 425) - ui.open() - -/obj/machinery/power/solar_control/get_ui_data() - var/data = list() - - data["generated"] = round(lastgen) - data["angle"] = cdir - data["direction"] = angle2text(cdir) - - data["tracking_state"] = track - data["tracking_rate"] = trackrate - data["rotating_way"] = (trackrate<0 ? "CCW" : "CW") - - data["connected_panels"] = connected_panels.len - data["connected_tracker"] = (connected_tracker ? 1 : 0) - return data - -/obj/machinery/power/solar_control/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/weapon/screwdriver)) - playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1) - if(do_after(user, 20/I.toolspeed, target = src)) - if (src.stat & BROKEN) - user << "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/solar_control/M = new /obj/item/weapon/circuitboard/solar_control( A ) - for (var/obj/C in src) - C.loc = src.loc - A.circuit = M - A.state = 3 - A.icon_state = "3" - A.anchored = 1 - qdel(src) - else - user << "You disconnect the monitor." - var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc ) - var/obj/item/weapon/circuitboard/solar_control/M = new /obj/item/weapon/circuitboard/solar_control( A ) - for (var/obj/C in src) - C.loc = src.loc - A.circuit = M - A.state = 4 - A.icon_state = "4" - A.anchored = 1 - qdel(src) - else - src.attack_hand(user) - return - -/obj/machinery/power/solar_control/process() - lastgen = gen - gen = 0 - - if(stat & (NOPOWER | BROKEN)) - return - - if(connected_tracker) //NOTE : handled here so that we don't add trackers to the processing list - if(connected_tracker.powernet != powernet) - connected_tracker.unset_control() - - if(track==1 && trackrate) //manual tracking and set a rotation speed - if(nexttime <= world.time) //every time we need to increase/decrease the angle by 1°... - targetdir = (targetdir + trackrate/abs(trackrate) + 360) % 360 //... do it - nexttime += 36000/abs(trackrate) //reset the counter for the next 1° - -/obj/machinery/power/solar_control/ui_act(action, params) - if(..()) - return - - switch(action) - if("control") - if(params["cdir"]) - src.cdir = dd_range(0,359,(360+src.cdir+text2num(params["cdir"]))%360) - src.targetdir = src.cdir - if(track == 2) //manual update, so losing auto-tracking - track = 0 - spawn(1) - set_panels(cdir) - if(params["tdir"]) - src.trackrate = dd_range(-7200,7200,src.trackrate+text2num(params["tdir"])) - if(src.trackrate) nexttime = world.time + 36000/abs(trackrate) - if("tracking") - track = text2num(params["mode"]) - if(track == 2) - if(connected_tracker) - connected_tracker.set_angle(SSsun.angle) - set_panels(cdir) - else if (track == 1) //begin manual tracking - src.targetdir = src.cdir - if(src.trackrate) nexttime = world.time + 36000/abs(trackrate) - set_panels(targetdir) - if("refresh") - search_for_connected() - if(connected_tracker && track == 2) - connected_tracker.set_angle(SSsun.angle) - set_panels(cdir) - return 1 - - -//rotates the panel to the passed angle -/obj/machinery/power/solar_control/proc/set_panels(cdir) - - for(var/obj/machinery/power/solar/S in connected_panels) - S.adir = cdir //instantly rotates the panel - S.occlusion()//and - S.update_icon() //update it - - update_icon() - - -/obj/machinery/power/solar_control/power_change() - ..() - update_icon() - - -/obj/machinery/power/solar_control/proc/set_broken() - stat |= BROKEN - update_icon() - - -/obj/machinery/power/solar_control/ex_act(severity, target) - ..() - if(!gc_destroyed) - switch(severity) - if(2) - if(prob(50)) - set_broken() - if(3) - if(prob(25)) - set_broken() - -/obj/machinery/power/solar_control/blob_act() - if (prob(75)) - set_broken() - src.density = 0 - - -// -// MISC -// - -/obj/item/weapon/paper/solar - name = "paper- 'Going green! Setup your own solar array instructions.'" - info = "

        Welcome

        At greencorps we love the environment, and space. With this package you are able to help mother nature and produce energy without any usage of fossil fuel or plasma! Singularity energy is dangerous while solar energy is safe, which is why it's better. Now here is how you setup your own solar array.

        You can make a solar panel by wrenching the solar assembly onto a cable node. Adding a glass panel, reinforced or regular glass will do, will finish the construction of your solar panel. It is that easy!

        Now after setting up 19 more of these solar panels you will want to create a solar tracker to keep track of our mother nature's gift, the sun. These are the same steps as before except you insert the tracker equipment circuit into the assembly before performing the final step of adding the glass. You now have a tracker! Now the last step is to add a computer to calculate the sun's movements and to send commands to the solar panels to change direction with the sun. Setting up the solar computer is the same as setting up any computer, so you should have no trouble in doing that. You do need to put a wire node under the computer, and the wire needs to be connected to the tracker.

        Congratulations, you should have a working solar array. If you are having trouble, here are some tips. Make sure all solar equipment are on a cable node, even the computer. You can always deconstruct your creations if you make a mistake.

        That's all to it, be safe, be green!

        " +#define SOLAR_MAX_DIST 40 +#define SOLARGENRATE 1500 + +/obj/machinery/power/solar + name = "solar panel" + desc = "A solar panel. Generates electricity when in contact with sunlight." + icon = 'icons/obj/power.dmi' + icon_state = "sp_base" + anchored = 1 + density = 1 + use_power = 0 + idle_power_usage = 0 + active_power_usage = 0 + var/id = 0 + var/health = 10 + var/obscured = 0 + var/sunfrac = 0 + var/adir = SOUTH // actual dir + var/ndir = SOUTH // target dir + var/turn_angle = 0 + var/obj/machinery/power/solar_control/control = null + +/obj/machinery/power/solar/New(var/turf/loc, var/obj/item/solar_assembly/S) + ..(loc) + Make(S) + connect_to_network() + +/obj/machinery/power/solar/Destroy() + unset_control() //remove from control computer + return ..() + +//set the control of the panel to a given computer if closer than SOLAR_MAX_DIST +/obj/machinery/power/solar/proc/set_control(obj/machinery/power/solar_control/SC) + if(!SC || (get_dist(src, SC) > SOLAR_MAX_DIST)) + return 0 + control = SC + SC.connected_panels |= src + return 1 + +//set the control of the panel to null and removes it from the control list of the previous control computer if needed +/obj/machinery/power/solar/proc/unset_control() + if(control) + control.connected_panels.Remove(src) + control = null + +/obj/machinery/power/solar/proc/Make(obj/item/solar_assembly/S) + if(!S) + S = new /obj/item/solar_assembly(src) + S.glass_type = /obj/item/stack/sheet/glass + S.anchored = 1 + S.loc = src + if(S.glass_type == /obj/item/stack/sheet/rglass) //if the panel is in reinforced glass + health *= 2 //this need to be placed here, because panels already on the map don't have an assembly linked to + update_icon() + + + +/obj/machinery/power/solar/attackby(obj/item/weapon/W, mob/user, params) + if(istype(W, /obj/item/weapon/crowbar)) + playsound(src.loc, 'sound/machines/click.ogg', 50, 1) + user.visible_message("[user] begins to take the glass off the solar panel.", "You begin to take the glass off the solar panel...") + if(do_after(user, 50/W.toolspeed, target = src)) + var/obj/item/solar_assembly/S = locate() in src + if(S) + S.loc = src.loc + S.give_glass(stat & BROKEN) + + playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) + user.visible_message("[user] takes the glass off the solar panel.", "You take the glass off the solar panel.") + qdel(src) + return + else if (W) + src.add_fingerprint(user) + src.health -= W.force + src.healthcheck() + ..() + +/obj/machinery/power/solar/proc/healthcheck() + if (src.health <= 0) + if(!(stat & BROKEN)) + set_broken() + else + new /obj/item/weapon/shard(src.loc) + new /obj/item/weapon/shard(src.loc) + qdel(src) + return + return + + +/obj/machinery/power/solar/update_icon() + ..() + overlays.Cut() + if(stat & BROKEN) + overlays += image('icons/obj/power.dmi', icon_state = "solar_panel-b", layer = FLY_LAYER) + else + overlays += image('icons/obj/power.dmi', icon_state = "solar_panel", layer = FLY_LAYER) + src.dir = angle2dir(adir) + return + +//calculates the fraction of the sunlight that the panel recieves +/obj/machinery/power/solar/proc/update_solar_exposure() + if(obscured) + sunfrac = 0 + return + + //find the smaller angle between the direction the panel is facing and the direction of the sun (the sign is not important here) + var/p_angle = min(abs(adir - SSsun.angle), 360 - abs(adir - SSsun.angle)) + + if(p_angle > 90) // if facing more than 90deg from sun, zero output + sunfrac = 0 + return + + sunfrac = cos(p_angle) ** 2 + //isn't the power recieved from the incoming light proportionnal to cos(p_angle) (Lambert's cosine law) rather than cos(p_angle)^2 ? + +/obj/machinery/power/solar/process()//TODO: remove/add this from machines to save on processing as needed ~Carn PRIORITY + if(stat & BROKEN) + return + if(!control) //if there's no sun or the panel is not linked to a solar control computer, no need to proceed + return + + if(powernet) + if(powernet == control.powernet)//check if the panel is still connected to the computer + if(obscured) //get no light from the sun, so don't generate power + return + var/sgen = SOLARGENRATE * sunfrac + add_avail(sgen) + control.gen += sgen + else //if we're no longer on the same powernet, remove from control computer + unset_control() + +/obj/machinery/power/solar/proc/set_broken() + . = (!(stat & BROKEN)) + stat |= BROKEN + unset_control() + update_icon() + return + + +/obj/machinery/power/solar/ex_act(severity, target) + ..() + if(!gc_destroyed) + switch(severity) + if(2) + if(prob(50)) + set_broken() + if(3) + if(prob(25)) + set_broken() + +/obj/machinery/power/solar/fake/New(var/turf/loc, var/obj/item/solar_assembly/S) + ..(loc, S, 0) + +/obj/machinery/power/solar/fake/process() + . = PROCESS_KILL + return + +//trace towards sun to see if we're in shadow +/obj/machinery/power/solar/proc/occlusion() + + var/ax = x // start at the solar panel + var/ay = y + var/turf/T = null + var/dx = SSsun.dx + var/dy = SSsun.dy + + for(var/i = 1 to 20) // 20 steps is enough + ax += dx // do step + ay += dy + + T = locate( round(ax,0.5),round(ay,0.5),z) + + if(T.x == 1 || T.x==world.maxx || T.y==1 || T.y==world.maxy) // not obscured if we reach the edge + break + + if(T.density) // if we hit a solid turf, panel is obscured + obscured = 1 + return + + obscured = 0 // if hit the edge or stepped 20 times, not obscured + update_solar_exposure() + + +// +// Solar Assembly - For construction of solar arrays. +// + +/obj/item/solar_assembly + name = "solar panel assembly" + desc = "A solar panel assembly kit, allows constructions of a solar panel, or with a tracking circuit board, a solar tracker." + icon = 'icons/obj/power.dmi' + icon_state = "sp_base" + item_state = "electropack" + w_class = 4 // Pretty big! + anchored = 0 + var/tracker = 0 + var/glass_type = null + +/obj/item/solar_assembly/attack_hand(mob/user) + if(!anchored && isturf(loc)) // You can't pick it up + ..() + +// Give back the glass type we were supplied with +/obj/item/solar_assembly/proc/give_glass(device_broken) + if(device_broken) + new /obj/item/weapon/shard(loc) + new /obj/item/weapon/shard(loc) + else if(glass_type) + var/obj/item/stack/sheet/S = new glass_type(loc) + S.amount = 2 + glass_type = null + + +/obj/item/solar_assembly/attackby(obj/item/weapon/W, mob/user, params) + if(istype(W, /obj/item/weapon/wrench) && isturf(loc)) + if(isinspace()) + user << "You can't secure [src] here." + return + anchored = !anchored + if(anchored) + user.visible_message("[user] wrenches the solar assembly into place.", "You wrench the solar assembly into place.") + playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1) + else + user.visible_message("[user] unwrenches the solar assembly from its place.", "You unwrench the solar assembly from its place.") + playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1) + return 1 + + if(istype(W, /obj/item/stack/sheet/glass) || istype(W, /obj/item/stack/sheet/rglass)) + if(!anchored) + user << "You need to secure the assembly before you can add glass." + return + var/obj/item/stack/sheet/S = W + if(S.use(2)) + glass_type = W.type + playsound(src.loc, 'sound/machines/click.ogg', 50, 1) + user.visible_message("[user] places the glass on the solar assembly.", "You place the glass on the solar assembly.") + if(tracker) + new /obj/machinery/power/tracker(get_turf(src), src) + else + new /obj/machinery/power/solar(get_turf(src), src) + else + user << "You need two sheets of glass to put them into a solar panel!" + return + return 1 + + if(!tracker) + if(istype(W, /obj/item/weapon/electronics/tracker)) + if(!user.drop_item()) + return + tracker = 1 + qdel(W) + user.visible_message("[user] inserts the electronics into the solar assembly.", "You insert the electronics into the solar assembly.") + return 1 + else + if(istype(W, /obj/item/weapon/crowbar)) + new /obj/item/weapon/electronics/tracker(src.loc) + tracker = 0 + user.visible_message("[user] takes out the electronics from the solar assembly.", "You take out the electronics from the solar assembly.") + return 1 + ..() + +// +// Solar Control Computer +// + +/obj/machinery/power/solar_control + name = "solar panel control" + desc = "A controller for solar panel arrays." + icon = 'icons/obj/computer.dmi' + icon_state = "computer" + anchored = 1 + density = 1 + use_power = 1 + idle_power_usage = 250 + var/icon_screen = "solar" + var/icon_keyboard = "power_key" + var/id = 0 + var/cdir = 0 + var/targetdir = 0 // target angle in manual tracking (since it updates every game minute) + var/gen = 0 + var/lastgen = 0 + var/track = 0 // 0= off 1=timed 2=auto (tracker) + var/trackrate = 600 // 300-900 seconds + var/nexttime = 0 // time for a panel to rotate of 1° in manual tracking + var/obj/machinery/power/tracker/connected_tracker = null + var/list/connected_panels = list() + + +/obj/machinery/power/solar_control/New() + ..() + if(ticker) + initialize() + connect_to_network() + +/obj/machinery/power/solar_control/Destroy() + for(var/obj/machinery/power/solar/M in connected_panels) + M.unset_control() + if(connected_tracker) + connected_tracker.unset_control() + return ..() + +/obj/machinery/power/solar_control/disconnect_from_network() + ..() + SSsun.solars.Remove(src) + +/obj/machinery/power/solar_control/connect_to_network() + var/to_return = ..() + if(powernet) //if connected and not already in solar_list... + SSsun.solars |= src //... add it + return to_return + +//search for unconnected panels and trackers in the computer powernet and connect them +/obj/machinery/power/solar_control/proc/search_for_connected() + if(powernet) + for(var/obj/machinery/power/M in powernet.nodes) + if(istype(M, /obj/machinery/power/solar)) + var/obj/machinery/power/solar/S = M + if(!S.control) //i.e unconnected + S.set_control(src) + else if(istype(M, /obj/machinery/power/tracker)) + if(!connected_tracker) //if there's already a tracker connected to the computer don't add another + var/obj/machinery/power/tracker/T = M + if(!T.control) //i.e unconnected + T.set_control(src) + +//called by the sun controller, update the facing angle (either manually or via tracking) and rotates the panels accordingly +/obj/machinery/power/solar_control/proc/update() + if(stat & (NOPOWER | BROKEN)) + return + + switch(track) + if(1) + if(trackrate) //we're manual tracking. If we set a rotation speed... + cdir = targetdir //...the current direction is the targetted one (and rotates panels to it) + if(2) // auto-tracking + if(connected_tracker) + connected_tracker.set_angle(SSsun.angle) + + set_panels(cdir) + updateDialog() + + +/obj/machinery/power/solar_control/initialize() + ..() + if(!powernet) return + set_panels(cdir) + +/obj/machinery/power/solar_control/update_icon() + overlays.Cut() + if(stat & NOPOWER) + overlays += "[icon_keyboard]_off" + return + overlays += icon_keyboard + if(stat & BROKEN) + overlays += "[icon_state]_broken" + else + overlays += icon_screen + if(cdir > -1) + overlays += image('icons/obj/computer.dmi', "solcon-o", FLY_LAYER, angle2dir(cdir)) + +/obj/machinery/power/solar_control/attack_hand(mob/user) + if (..() || !user) + return + add_fingerprint(user) + interact(user) + +/obj/machinery/power/solar_control/interact(mob/user) + if (stat & BROKEN) + return + ui_interact(user) + +/obj/machinery/power/solar_control/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 0) + ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open) + if (!ui) + ui = new(user, src, ui_key, "solar_control", name, 515, 425) + ui.open() + +/obj/machinery/power/solar_control/get_ui_data() + var/data = list() + + data["generated"] = round(lastgen) + data["angle"] = cdir + data["direction"] = angle2text(cdir) + + data["tracking_state"] = track + data["tracking_rate"] = trackrate + data["rotating_way"] = (trackrate<0 ? "CCW" : "CW") + + data["connected_panels"] = connected_panels.len + data["connected_tracker"] = (connected_tracker ? 1 : 0) + return data + +/obj/machinery/power/solar_control/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/weapon/screwdriver)) + playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1) + if(do_after(user, 20/I.toolspeed, target = src)) + if (src.stat & BROKEN) + user << "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/solar_control/M = new /obj/item/weapon/circuitboard/solar_control( A ) + for (var/obj/C in src) + C.loc = src.loc + A.circuit = M + A.state = 3 + A.icon_state = "3" + A.anchored = 1 + qdel(src) + else + user << "You disconnect the monitor." + var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc ) + var/obj/item/weapon/circuitboard/solar_control/M = new /obj/item/weapon/circuitboard/solar_control( A ) + for (var/obj/C in src) + C.loc = src.loc + A.circuit = M + A.state = 4 + A.icon_state = "4" + A.anchored = 1 + qdel(src) + else + src.attack_hand(user) + return + +/obj/machinery/power/solar_control/process() + lastgen = gen + gen = 0 + + if(stat & (NOPOWER | BROKEN)) + return + + if(connected_tracker) //NOTE : handled here so that we don't add trackers to the processing list + if(connected_tracker.powernet != powernet) + connected_tracker.unset_control() + + if(track==1 && trackrate) //manual tracking and set a rotation speed + if(nexttime <= world.time) //every time we need to increase/decrease the angle by 1°... + targetdir = (targetdir + trackrate/abs(trackrate) + 360) % 360 //... do it + nexttime += 36000/abs(trackrate) //reset the counter for the next 1° + +/obj/machinery/power/solar_control/ui_act(action, params) + if(..()) + return + + switch(action) + if("control") + if(params["cdir"]) + src.cdir = dd_range(0,359,(360+src.cdir+text2num(params["cdir"]))%360) + src.targetdir = src.cdir + if(track == 2) //manual update, so losing auto-tracking + track = 0 + spawn(1) + set_panels(cdir) + if(params["tdir"]) + src.trackrate = dd_range(-7200,7200,src.trackrate+text2num(params["tdir"])) + if(src.trackrate) nexttime = world.time + 36000/abs(trackrate) + if("tracking") + track = text2num(params["mode"]) + if(track == 2) + if(connected_tracker) + connected_tracker.set_angle(SSsun.angle) + set_panels(cdir) + else if (track == 1) //begin manual tracking + src.targetdir = src.cdir + if(src.trackrate) nexttime = world.time + 36000/abs(trackrate) + set_panels(targetdir) + if("refresh") + search_for_connected() + if(connected_tracker && track == 2) + connected_tracker.set_angle(SSsun.angle) + set_panels(cdir) + return 1 + + +//rotates the panel to the passed angle +/obj/machinery/power/solar_control/proc/set_panels(cdir) + + for(var/obj/machinery/power/solar/S in connected_panels) + S.adir = cdir //instantly rotates the panel + S.occlusion()//and + S.update_icon() //update it + + update_icon() + + +/obj/machinery/power/solar_control/power_change() + ..() + update_icon() + + +/obj/machinery/power/solar_control/proc/set_broken() + stat |= BROKEN + update_icon() + + +/obj/machinery/power/solar_control/ex_act(severity, target) + ..() + if(!gc_destroyed) + switch(severity) + if(2) + if(prob(50)) + set_broken() + if(3) + if(prob(25)) + set_broken() + +/obj/machinery/power/solar_control/blob_act() + if (prob(75)) + set_broken() + src.density = 0 + + +// +// MISC +// + +/obj/item/weapon/paper/solar + name = "paper- 'Going green! Setup your own solar array instructions.'" + info = "

        Welcome

        At greencorps we love the environment, and space. With this package you are able to help mother nature and produce energy without any usage of fossil fuel or plasma! Singularity energy is dangerous while solar energy is safe, which is why it's better. Now here is how you setup your own solar array.

        You can make a solar panel by wrenching the solar assembly onto a cable node. Adding a glass panel, reinforced or regular glass will do, will finish the construction of your solar panel. It is that easy!

        Now after setting up 19 more of these solar panels you will want to create a solar tracker to keep track of our mother nature's gift, the sun. These are the same steps as before except you insert the tracker equipment circuit into the assembly before performing the final step of adding the glass. You now have a tracker! Now the last step is to add a computer to calculate the sun's movements and to send commands to the solar panels to change direction with the sun. Setting up the solar computer is the same as setting up any computer, so you should have no trouble in doing that. You do need to put a wire node under the computer, and the wire needs to be connected to the tracker.

        Congratulations, you should have a working solar array. If you are having trouble, here are some tips. Make sure all solar equipment are on a cable node, even the computer. You can always deconstruct your creations if you make a mistake.

        That's all to it, be safe, be green!

        " diff --git a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm index fef849aaac3..e29f2604fe5 100644 --- a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm +++ b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm @@ -1,354 +1,354 @@ -/obj/machinery/chem_dispenser - name = "chem dispenser" - desc = "Creates and dispenses chemicals." - density = 1 - anchored = 1 - icon = 'icons/obj/chemical.dmi' - icon_state = "dispenser" - use_power = 1 - idle_power_usage = 40 - interact_offline = 1 - var/energy = 100 - var/max_energy = 100 - var/amount = 30 - var/recharged = 0 - var/recharge_delay = 5 - var/image/icon_beaker = null - var/obj/item/weapon/reagent_containers/beaker = null - var/list/dispensable_reagents = list( - "hydrogen", - "lithium", - "carbon", - "nitrogen", - "oxygen", - "fluorine", - "sodium", - "aluminium", - "silicon", - "phosphorus", - "sulfur", - "chlorine", - "potassium", - "iron", - "copper", - "mercury", - "radium", - "water", - "ethanol", - "sugar", - "sacid", - "welding_fuel", - "silver", - "iodine", - "bromine", - "stable_plasma" - ) - -/obj/machinery/chem_dispenser/New() - ..() - recharge() - dispensable_reagents = sortList(dispensable_reagents) - -/obj/machinery/chem_dispenser/power_change() - if(powered()) - stat &= ~NOPOWER - else - spawn(rand(0, 15)) - stat |= NOPOWER - -/obj/machinery/chem_dispenser/process() - - if(recharged < 0) - recharge() - recharged = recharge_delay - else - recharged -= 1 - -/obj/machinery/chem_dispenser/proc/recharge() - if(stat & (BROKEN|NOPOWER)) return - var/addenergy = 1 - var/oldenergy = energy - energy = min(energy + addenergy, max_energy) - if(energy != oldenergy) - use_power(2500) - -/obj/machinery/chem_dispenser/ex_act(severity, target) - if(severity < 3) - ..() - -/obj/machinery/chem_dispenser/blob_act() - if(prob(50)) - qdel(src) - -/obj/machinery/chem_dispenser/interact(mob/user) - if(stat & BROKEN) - return - ui_interact(user) - -/obj/machinery/chem_dispenser/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 0) - ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open) - if (!ui) - ui = new(user, src, ui_key, "chem_dispenser", name, 530, 700) - ui.open() - -/obj/machinery/chem_dispenser/get_ui_data() - var/data = list() - data["amount"] = amount - data["energy"] = energy - data["maxEnergy"] = max_energy - data["isBeakerLoaded"] = beaker ? 1 : 0 - - var beakerContents[0] - var beakerCurrentVolume = 0 - if(beaker && beaker.reagents && beaker.reagents.reagent_list.len) - for(var/datum/reagent/R in beaker.reagents.reagent_list) - beakerContents.Add(list(list("name" = R.name, "volume" = R.volume))) // list in a list because Byond merges the first list... - beakerCurrentVolume += R.volume - data["beakerContents"] = beakerContents - - if (beaker) - data["beakerCurrentVolume"] = beakerCurrentVolume - data["beakerMaxVolume"] = beaker.volume - data["beakerTransferAmounts"] = beaker.possible_transfer_amounts - else - data["beakerCurrentVolume"] = null - data["beakerMaxVolume"] = null - data["beakerTransferAmounts"] = null - - var chemicals[0] - for(var/re in dispensable_reagents) - var/datum/reagent/temp = chemical_reagents_list[re] - if(temp) - chemicals.Add(list(list("title" = temp.name, "id" = temp.id, "commands" = list("reagent" = temp.id)))) - data["chemicals"] = chemicals - return data - -/obj/machinery/chem_dispenser/ui_act(action, params) - if(..()) - return - - switch(action) - if("amount") - amount = round(text2num(params["set"]), 5) // round to nearest 5 - if (amount < 0) // Since the user can actually type the commands himself, some sanity checking - amount = 0 - if (amount > 100) - amount = 100 - if("dispense") - if(beaker && dispensable_reagents.Find(params["reagent"])) - var/datum/reagents/R = beaker.reagents - var/space = R.maximum_volume - R.total_volume - - R.add_reagent(params["reagent"], min(amount, energy * 10, space)) - energy = max(energy - min(amount, energy * 10, space) / 10, 0) - if("remove") - if(beaker) - var/amount = text2num(params["amount"]) - if(isnum(amount) && (amount > 0) && (amount in beaker.possible_transfer_amounts)) - beaker.reagents.remove_all(amount) - if("eject") - if(beaker) - beaker.loc = loc - beaker = null - overlays.Cut() - return 1 - -/obj/machinery/chem_dispenser/attackby(obj/item/I, mob/user, params) - if(default_unfasten_wrench(user, I)) - return - - if(isrobot(user)) - return - - var/obj/item/weapon/reagent_containers/B = I // Get a beaker from it? - if(!istype(B)) - return // Not a beaker? - - if(beaker) - user << "A beaker is already loaded into the machine!" - return - - if(!user.drop_item()) // Can't let go? - return - - beaker = B - beaker.loc = src - user << "You add the beaker to the machine." - - if(!icon_beaker) - icon_beaker = image('icons/obj/chemical.dmi', src, "disp_beaker") //randomize beaker overlay position. - icon_beaker.pixel_x = rand(-10,5) - overlays += icon_beaker - -/obj/machinery/chem_dispenser/attack_hand(mob/user) - if (!user) - return - interact(user) - - -/obj/machinery/chem_dispenser/constructable - name = "portable chem dispenser" - icon = 'icons/obj/chemical.dmi' - icon_state = "minidispenser" - energy = 10 - max_energy = 10 - amount = 5 - recharge_delay = 30 - dispensable_reagents = list() - var/list/dispensable_reagent_tiers = list( - list( - "hydrogen", - "oxygen", - "silicon", - "phosphorus", - "sulfur", - "carbon", - "nitrogen", - "water" - ), - list( - "lithium", - "sugar", - "sacid", - "copper", - "mercury", - "sodium", - "iodine", - "bromine" - ), - list( - "ethanol", - "chlorine", - "potassium", - "aluminium", - "radium", - "fluorine", - "iron", - "welding_fuel", - "silver", - "stable_plasma" - ), - list( - "oil", - "ash", - "acetone", - "saltpetre", - "ammonia", - "diethylamine" - ) - ) - -/obj/machinery/chem_dispenser/constructable/New() - ..() - component_parts = list() - component_parts += new /obj/item/weapon/circuitboard/chem_dispenser(null) - component_parts += new /obj/item/weapon/stock_parts/matter_bin(null) - component_parts += new /obj/item/weapon/stock_parts/matter_bin(null) - component_parts += new /obj/item/weapon/stock_parts/manipulator(null) - component_parts += new /obj/item/weapon/stock_parts/capacitor(null) - component_parts += new /obj/item/weapon/stock_parts/console_screen(null) - component_parts += new /obj/item/weapon/stock_parts/cell/high(null) - RefreshParts() - -/obj/machinery/chem_dispenser/constructable/RefreshParts() - var/time = 0 - var/temp_energy = 0 - var/i - for(var/obj/item/weapon/stock_parts/matter_bin/M in component_parts) - temp_energy += M.rating - temp_energy-- - max_energy = temp_energy * 5 //max energy = (bin1.rating + bin2.rating - 1) * 5, 5 on lowest 25 on highest - for(var/obj/item/weapon/stock_parts/capacitor/C in component_parts) - time += C.rating - for(var/obj/item/weapon/stock_parts/cell/P in component_parts) - time += round(P.maxcharge, 10000) / 10000 - recharge_delay /= time/2 //delay between recharges, double the usual time on lowest 50% less than usual on highest - for(var/obj/item/weapon/stock_parts/manipulator/M in component_parts) - for(i=1, i<=M.rating, i++) - dispensable_reagents |= dispensable_reagent_tiers[i] - dispensable_reagents = sortList(dispensable_reagents) - -/obj/machinery/chem_dispenser/constructable/attackby(var/obj/item/I, var/mob/user, params) - ..() - if(default_deconstruction_screwdriver(user, "minidispenser-o", "minidispenser", I)) - return - - if(exchange_parts(user, I)) - return - - if(panel_open) - if(istype(I, /obj/item/weapon/crowbar)) - if(beaker) - beaker.loc = loc - beaker = null - default_deconstruction_crowbar(I) - return 1 - -/obj/machinery/chem_dispenser/drinks - name = "soda dispenser" - anchored = 1 - icon = 'icons/obj/chemical.dmi' - icon_state = "soda_dispenser" - amount = 10 - dispensable_reagents = list( - "water", - "ice", - "coffee", - "cream", - "tea", - "icetea", - "cola", - "spacemountainwind", - "dr_gibb", - "space_up", - "tonic", - "sodawater", - "lemon_lime", - "sugar", - "orangejuice", - "limejuice", - "tomatojuice" - ) - -/obj/machinery/chem_dispenser/drinks/attackby(obj/item/I, mob/user) - if(default_unfasten_wrench(user, I)) - return - - if (istype(I, /obj/item/weapon/reagent_containers/glass) || \ - istype(I, /obj/item/weapon/reagent_containers/food/drinks/drinkingglass) || \ - istype(I, /obj/item/weapon/reagent_containers/food/drinks/shaker)) - - if (beaker) - return 1 - else - if(!user.drop_item()) - return 1 - src.beaker = I - beaker.loc = src - update_icon() - return - -/obj/machinery/chem_dispenser/drinks/beer - name = "booze dispenser" - anchored = 1 - icon = 'icons/obj/chemical.dmi' - icon_state = "booze_dispenser" - dispensable_reagents = list( - "lemon_lime", - "sugar", - "orangejuice", - "limejuice", - "sodawater", - "tonic", - "beer", - "kahlua", - "whiskey", - "wine", - "vodka", - "gin", - "rum", - "tequila", - "vermouth", - "cognac", - "ale" - ) +/obj/machinery/chem_dispenser + name = "chem dispenser" + desc = "Creates and dispenses chemicals." + density = 1 + anchored = 1 + icon = 'icons/obj/chemical.dmi' + icon_state = "dispenser" + use_power = 1 + idle_power_usage = 40 + interact_offline = 1 + var/energy = 100 + var/max_energy = 100 + var/amount = 30 + var/recharged = 0 + var/recharge_delay = 5 + var/image/icon_beaker = null + var/obj/item/weapon/reagent_containers/beaker = null + var/list/dispensable_reagents = list( + "hydrogen", + "lithium", + "carbon", + "nitrogen", + "oxygen", + "fluorine", + "sodium", + "aluminium", + "silicon", + "phosphorus", + "sulfur", + "chlorine", + "potassium", + "iron", + "copper", + "mercury", + "radium", + "water", + "ethanol", + "sugar", + "sacid", + "welding_fuel", + "silver", + "iodine", + "bromine", + "stable_plasma" + ) + +/obj/machinery/chem_dispenser/New() + ..() + recharge() + dispensable_reagents = sortList(dispensable_reagents) + +/obj/machinery/chem_dispenser/power_change() + if(powered()) + stat &= ~NOPOWER + else + spawn(rand(0, 15)) + stat |= NOPOWER + +/obj/machinery/chem_dispenser/process() + + if(recharged < 0) + recharge() + recharged = recharge_delay + else + recharged -= 1 + +/obj/machinery/chem_dispenser/proc/recharge() + if(stat & (BROKEN|NOPOWER)) return + var/addenergy = 1 + var/oldenergy = energy + energy = min(energy + addenergy, max_energy) + if(energy != oldenergy) + use_power(2500) + +/obj/machinery/chem_dispenser/ex_act(severity, target) + if(severity < 3) + ..() + +/obj/machinery/chem_dispenser/blob_act() + if(prob(50)) + qdel(src) + +/obj/machinery/chem_dispenser/interact(mob/user) + if(stat & BROKEN) + return + ui_interact(user) + +/obj/machinery/chem_dispenser/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 0) + ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open) + if (!ui) + ui = new(user, src, ui_key, "chem_dispenser", name, 530, 700) + ui.open() + +/obj/machinery/chem_dispenser/get_ui_data() + var/data = list() + data["amount"] = amount + data["energy"] = energy + data["maxEnergy"] = max_energy + data["isBeakerLoaded"] = beaker ? 1 : 0 + + var beakerContents[0] + var beakerCurrentVolume = 0 + if(beaker && beaker.reagents && beaker.reagents.reagent_list.len) + for(var/datum/reagent/R in beaker.reagents.reagent_list) + beakerContents.Add(list(list("name" = R.name, "volume" = R.volume))) // list in a list because Byond merges the first list... + beakerCurrentVolume += R.volume + data["beakerContents"] = beakerContents + + if (beaker) + data["beakerCurrentVolume"] = beakerCurrentVolume + data["beakerMaxVolume"] = beaker.volume + data["beakerTransferAmounts"] = beaker.possible_transfer_amounts + else + data["beakerCurrentVolume"] = null + data["beakerMaxVolume"] = null + data["beakerTransferAmounts"] = null + + var chemicals[0] + for(var/re in dispensable_reagents) + var/datum/reagent/temp = chemical_reagents_list[re] + if(temp) + chemicals.Add(list(list("title" = temp.name, "id" = temp.id, "commands" = list("reagent" = temp.id)))) + data["chemicals"] = chemicals + return data + +/obj/machinery/chem_dispenser/ui_act(action, params) + if(..()) + return + + switch(action) + if("amount") + amount = round(text2num(params["set"]), 5) // round to nearest 5 + if (amount < 0) // Since the user can actually type the commands himself, some sanity checking + amount = 0 + if (amount > 100) + amount = 100 + if("dispense") + if(beaker && dispensable_reagents.Find(params["reagent"])) + var/datum/reagents/R = beaker.reagents + var/space = R.maximum_volume - R.total_volume + + R.add_reagent(params["reagent"], min(amount, energy * 10, space)) + energy = max(energy - min(amount, energy * 10, space) / 10, 0) + if("remove") + if(beaker) + var/amount = text2num(params["amount"]) + if(isnum(amount) && (amount > 0) && (amount in beaker.possible_transfer_amounts)) + beaker.reagents.remove_all(amount) + if("eject") + if(beaker) + beaker.loc = loc + beaker = null + overlays.Cut() + return 1 + +/obj/machinery/chem_dispenser/attackby(obj/item/I, mob/user, params) + if(default_unfasten_wrench(user, I)) + return + + if(isrobot(user)) + return + + var/obj/item/weapon/reagent_containers/B = I // Get a beaker from it? + if(!istype(B)) + return // Not a beaker? + + if(beaker) + user << "A beaker is already loaded into the machine!" + return + + if(!user.drop_item()) // Can't let go? + return + + beaker = B + beaker.loc = src + user << "You add the beaker to the machine." + + if(!icon_beaker) + icon_beaker = image('icons/obj/chemical.dmi', src, "disp_beaker") //randomize beaker overlay position. + icon_beaker.pixel_x = rand(-10,5) + overlays += icon_beaker + +/obj/machinery/chem_dispenser/attack_hand(mob/user) + if (!user) + return + interact(user) + + +/obj/machinery/chem_dispenser/constructable + name = "portable chem dispenser" + icon = 'icons/obj/chemical.dmi' + icon_state = "minidispenser" + energy = 10 + max_energy = 10 + amount = 5 + recharge_delay = 30 + dispensable_reagents = list() + var/list/dispensable_reagent_tiers = list( + list( + "hydrogen", + "oxygen", + "silicon", + "phosphorus", + "sulfur", + "carbon", + "nitrogen", + "water" + ), + list( + "lithium", + "sugar", + "sacid", + "copper", + "mercury", + "sodium", + "iodine", + "bromine" + ), + list( + "ethanol", + "chlorine", + "potassium", + "aluminium", + "radium", + "fluorine", + "iron", + "welding_fuel", + "silver", + "stable_plasma" + ), + list( + "oil", + "ash", + "acetone", + "saltpetre", + "ammonia", + "diethylamine" + ) + ) + +/obj/machinery/chem_dispenser/constructable/New() + ..() + component_parts = list() + component_parts += new /obj/item/weapon/circuitboard/chem_dispenser(null) + component_parts += new /obj/item/weapon/stock_parts/matter_bin(null) + component_parts += new /obj/item/weapon/stock_parts/matter_bin(null) + component_parts += new /obj/item/weapon/stock_parts/manipulator(null) + component_parts += new /obj/item/weapon/stock_parts/capacitor(null) + component_parts += new /obj/item/weapon/stock_parts/console_screen(null) + component_parts += new /obj/item/weapon/stock_parts/cell/high(null) + RefreshParts() + +/obj/machinery/chem_dispenser/constructable/RefreshParts() + var/time = 0 + var/temp_energy = 0 + var/i + for(var/obj/item/weapon/stock_parts/matter_bin/M in component_parts) + temp_energy += M.rating + temp_energy-- + max_energy = temp_energy * 5 //max energy = (bin1.rating + bin2.rating - 1) * 5, 5 on lowest 25 on highest + for(var/obj/item/weapon/stock_parts/capacitor/C in component_parts) + time += C.rating + for(var/obj/item/weapon/stock_parts/cell/P in component_parts) + time += round(P.maxcharge, 10000) / 10000 + recharge_delay /= time/2 //delay between recharges, double the usual time on lowest 50% less than usual on highest + for(var/obj/item/weapon/stock_parts/manipulator/M in component_parts) + for(i=1, i<=M.rating, i++) + dispensable_reagents |= dispensable_reagent_tiers[i] + dispensable_reagents = sortList(dispensable_reagents) + +/obj/machinery/chem_dispenser/constructable/attackby(var/obj/item/I, var/mob/user, params) + ..() + if(default_deconstruction_screwdriver(user, "minidispenser-o", "minidispenser", I)) + return + + if(exchange_parts(user, I)) + return + + if(panel_open) + if(istype(I, /obj/item/weapon/crowbar)) + if(beaker) + beaker.loc = loc + beaker = null + default_deconstruction_crowbar(I) + return 1 + +/obj/machinery/chem_dispenser/drinks + name = "soda dispenser" + anchored = 1 + icon = 'icons/obj/chemical.dmi' + icon_state = "soda_dispenser" + amount = 10 + dispensable_reagents = list( + "water", + "ice", + "coffee", + "cream", + "tea", + "icetea", + "cola", + "spacemountainwind", + "dr_gibb", + "space_up", + "tonic", + "sodawater", + "lemon_lime", + "sugar", + "orangejuice", + "limejuice", + "tomatojuice" + ) + +/obj/machinery/chem_dispenser/drinks/attackby(obj/item/I, mob/user) + if(default_unfasten_wrench(user, I)) + return + + if (istype(I, /obj/item/weapon/reagent_containers/glass) || \ + istype(I, /obj/item/weapon/reagent_containers/food/drinks/drinkingglass) || \ + istype(I, /obj/item/weapon/reagent_containers/food/drinks/shaker)) + + if (beaker) + return 1 + else + if(!user.drop_item()) + return 1 + src.beaker = I + beaker.loc = src + update_icon() + return + +/obj/machinery/chem_dispenser/drinks/beer + name = "booze dispenser" + anchored = 1 + icon = 'icons/obj/chemical.dmi' + icon_state = "booze_dispenser" + dispensable_reagents = list( + "lemon_lime", + "sugar", + "orangejuice", + "limejuice", + "sodawater", + "tonic", + "beer", + "kahlua", + "whiskey", + "wine", + "vodka", + "gin", + "rum", + "tequila", + "vermouth", + "cognac", + "ale" + ) diff --git a/code/modules/reagents/chemistry/machinery/chem_heater.dm b/code/modules/reagents/chemistry/machinery/chem_heater.dm index b900f40dcac..2e19186075b 100644 --- a/code/modules/reagents/chemistry/machinery/chem_heater.dm +++ b/code/modules/reagents/chemistry/machinery/chem_heater.dm @@ -1,126 +1,126 @@ -/obj/machinery/chem_heater - name = "chemical heater" - density = 1 - anchored = 1 - icon = 'icons/obj/chemical.dmi' - icon_state = "mixer0b" - use_power = 1 - idle_power_usage = 40 - var/obj/item/weapon/reagent_containers/beaker = null - var/desired_temp = 300 - var/heater_coefficient = 0.10 - var/on = FALSE - -/obj/machinery/chem_heater/New() - ..() - component_parts = list() - component_parts += new /obj/item/weapon/circuitboard/chem_heater(null) - component_parts += new /obj/item/weapon/stock_parts/micro_laser(null) - component_parts += new /obj/item/weapon/stock_parts/console_screen(null) - RefreshParts() - -/obj/machinery/chem_heater/RefreshParts() - heater_coefficient = 0.10 - for(var/obj/item/weapon/stock_parts/micro_laser/M in component_parts) - heater_coefficient *= M.rating - -/obj/machinery/chem_heater/process() - ..() - if(stat & NOPOWER) - return - if(on) - if(beaker) - if(beaker.reagents.chem_temp > desired_temp) - beaker.reagents.chem_temp += min(-1, (desired_temp - beaker.reagents.chem_temp) * heater_coefficient) - if(beaker.reagents.chem_temp < desired_temp) - beaker.reagents.chem_temp += max(1, (desired_temp - beaker.reagents.chem_temp) * heater_coefficient) - beaker.reagents.chem_temp = round(beaker.reagents.chem_temp) //stops stuff like 456.12312312302 - - beaker.reagents.handle_reactions() - -/obj/machinery/chem_heater/power_change() - if(powered()) - stat &= ~NOPOWER - else - spawn(rand(0, 15)) - stat |= NOPOWER - -/obj/machinery/chem_heater/attackby(obj/item/I, mob/user, params) - if(isrobot(user)) - return - - if(istype(I, /obj/item/weapon/reagent_containers/glass)) - if(beaker) - user << "A beaker is already loaded into the machine!" - return - - if(user.drop_item()) - beaker = I - I.loc = src - user << "You add the beaker to the machine." - icon_state = "mixer1b" - - if(default_deconstruction_screwdriver(user, "mixer0b", "mixer0b", I)) - return - - if(exchange_parts(user, I)) - return - - if(panel_open) - if(istype(I, /obj/item/weapon/crowbar)) - eject_beaker() - default_deconstruction_crowbar(I) - return 1 - -/obj/machinery/chem_heater/attack_hand(mob/user) - if (!user) - return - interact(user) - -/obj/machinery/chem_heater/ui_act(action, params) - if(..()) - return - - switch(action) - if("power") - on = !on - if("temperature") - desired_temp = Clamp(input("Please input the target temperature", name) as num, 0, 1000) - if("eject") - eject_beaker() - return 1 - -/obj/machinery/chem_heater/interact(mob/user) - if(stat & BROKEN) - return - ui_interact(user) - -/obj/machinery/chem_heater/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 0) - ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open) - if (!ui) - ui = new(user, src, ui_key, "chem_heater", name, 350, 400) - ui.open() - -/obj/machinery/chem_heater/get_ui_data() - var/data = list() - data["targetTemp"] = desired_temp - data["isActive"] = on - data["isBeakerLoaded"] = beaker ? 1 : 0 - - data["currentTemp"] = beaker ? beaker.reagents.chem_temp : null - data["beakerCurrentVolume"] = beaker ? beaker.reagents.total_volume : null - data["beakerMaxVolume"] = beaker ? beaker.volume : null - - var beakerContents[0] - if(beaker) - for(var/datum/reagent/R in beaker.reagents.reagent_list) - beakerContents.Add(list(list("name" = R.name, "volume" = R.volume))) // list in a list because Byond merges the first list... - data["beakerContents"] = beakerContents - return data - -/obj/machinery/chem_heater/proc/eject_beaker() - if(beaker) - beaker.loc = get_turf(src) - beaker.reagents.handle_reactions() - beaker = null +/obj/machinery/chem_heater + name = "chemical heater" + density = 1 + anchored = 1 + icon = 'icons/obj/chemical.dmi' + icon_state = "mixer0b" + use_power = 1 + idle_power_usage = 40 + var/obj/item/weapon/reagent_containers/beaker = null + var/desired_temp = 300 + var/heater_coefficient = 0.10 + var/on = FALSE + +/obj/machinery/chem_heater/New() + ..() + component_parts = list() + component_parts += new /obj/item/weapon/circuitboard/chem_heater(null) + component_parts += new /obj/item/weapon/stock_parts/micro_laser(null) + component_parts += new /obj/item/weapon/stock_parts/console_screen(null) + RefreshParts() + +/obj/machinery/chem_heater/RefreshParts() + heater_coefficient = 0.10 + for(var/obj/item/weapon/stock_parts/micro_laser/M in component_parts) + heater_coefficient *= M.rating + +/obj/machinery/chem_heater/process() + ..() + if(stat & NOPOWER) + return + if(on) + if(beaker) + if(beaker.reagents.chem_temp > desired_temp) + beaker.reagents.chem_temp += min(-1, (desired_temp - beaker.reagents.chem_temp) * heater_coefficient) + if(beaker.reagents.chem_temp < desired_temp) + beaker.reagents.chem_temp += max(1, (desired_temp - beaker.reagents.chem_temp) * heater_coefficient) + beaker.reagents.chem_temp = round(beaker.reagents.chem_temp) //stops stuff like 456.12312312302 + + beaker.reagents.handle_reactions() + +/obj/machinery/chem_heater/power_change() + if(powered()) + stat &= ~NOPOWER + else + spawn(rand(0, 15)) + stat |= NOPOWER + +/obj/machinery/chem_heater/attackby(obj/item/I, mob/user, params) + if(isrobot(user)) + return + + if(istype(I, /obj/item/weapon/reagent_containers/glass)) + if(beaker) + user << "A beaker is already loaded into the machine!" + return + + if(user.drop_item()) + beaker = I + I.loc = src + user << "You add the beaker to the machine." + icon_state = "mixer1b" + + if(default_deconstruction_screwdriver(user, "mixer0b", "mixer0b", I)) + return + + if(exchange_parts(user, I)) + return + + if(panel_open) + if(istype(I, /obj/item/weapon/crowbar)) + eject_beaker() + default_deconstruction_crowbar(I) + return 1 + +/obj/machinery/chem_heater/attack_hand(mob/user) + if (!user) + return + interact(user) + +/obj/machinery/chem_heater/ui_act(action, params) + if(..()) + return + + switch(action) + if("power") + on = !on + if("temperature") + desired_temp = Clamp(input("Please input the target temperature", name) as num, 0, 1000) + if("eject") + eject_beaker() + return 1 + +/obj/machinery/chem_heater/interact(mob/user) + if(stat & BROKEN) + return + ui_interact(user) + +/obj/machinery/chem_heater/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 0) + ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open) + if (!ui) + ui = new(user, src, ui_key, "chem_heater", name, 350, 400) + ui.open() + +/obj/machinery/chem_heater/get_ui_data() + var/data = list() + data["targetTemp"] = desired_temp + data["isActive"] = on + data["isBeakerLoaded"] = beaker ? 1 : 0 + + data["currentTemp"] = beaker ? beaker.reagents.chem_temp : null + data["beakerCurrentVolume"] = beaker ? beaker.reagents.total_volume : null + data["beakerMaxVolume"] = beaker ? beaker.volume : null + + var beakerContents[0] + if(beaker) + for(var/datum/reagent/R in beaker.reagents.reagent_list) + beakerContents.Add(list(list("name" = R.name, "volume" = R.volume))) // list in a list because Byond merges the first list... + data["beakerContents"] = beakerContents + return data + +/obj/machinery/chem_heater/proc/eject_beaker() + if(beaker) + beaker.loc = get_turf(src) + beaker.reagents.handle_reactions() + beaker = null icon_state = "mixer0b" \ No newline at end of file diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm index 4666b639b2e..8c3330b9045 100644 --- a/code/modules/shuttle/shuttle.dm +++ b/code/modules/shuttle/shuttle.dm @@ -1,638 +1,638 @@ -//use this define to highlight docking port bounding boxes (ONLY FOR DEBUG USE) -//#define DOCKING_PORT_HIGHLIGHT - -//NORTH default dir -/obj/docking_port - invisibility = 101 - icon = 'icons/obj/device.dmi' - //icon = 'icons/dirsquare.dmi' - icon_state = "pinonfar" - - unacidable = 1 - anchored = 1 - - var/id - dir = NORTH //this should point -away- from the dockingport door, ie towards the ship - var/width = 0 //size of covered area, perpendicular to dir - var/height = 0 //size of covered area, paralell to dir - var/dwidth = 0 //position relative to covered area, perpendicular to dir - var/dheight = 0 //position relative to covered area, parallel to dir - - //these objects are indestructable -/obj/docking_port/Destroy() - return QDEL_HINT_LETMELIVE -/obj/docking_port/singularity_pull() - return -/obj/docking_port/singularity_act() - return 0 -/obj/docking_port/shuttleRotate() - return //we don't rotate with shuttles via this code. -//returns a list(x0,y0, x1,y1) where points 0 and 1 are bounding corners of the projected rectangle -/obj/docking_port/proc/return_coords(_x, _y, _dir) - if(!_dir) - _dir = dir - if(!_x) - _x = x - if(!_y) - _y = y - - //byond's sin and cos functions are inaccurate. This is faster and perfectly accurate - var/cos = 1 - var/sin = 0 - switch(_dir) - if(WEST) - cos = 0 - sin = 1 - if(SOUTH) - cos = -1 - sin = 0 - if(EAST) - cos = 0 - sin = -1 - - return list( - _x + (-dwidth*cos) - (-dheight*sin), - _y + (-dwidth*sin) + (-dheight*cos), - _x + (-dwidth+width-1)*cos - (-dheight+height-1)*sin, - _y + (-dwidth+width-1)*sin + (-dheight+height-1)*cos - ) - - -//returns turfs within our projected rectangle in a specific order. -//this ensures that turfs are copied over in the same order, regardless of any rotation -/obj/docking_port/proc/return_ordered_turfs(_x, _y, _z, _dir, area/A) - if(!_dir) - _dir = dir - if(!_x) - _x = x - if(!_y) - _y = y - if(!_z) - _z = z - var/cos = 1 - var/sin = 0 - switch(_dir) - if(WEST) - cos = 0 - sin = 1 - if(SOUTH) - cos = -1 - sin = 0 - if(EAST) - cos = 0 - sin = -1 - - . = list() - - var/xi - var/yi - for(var/dx=0, dx S.dwidth) - return 2 - if(width-dwidth > S.width-S.dwidth) - return 3 - if(dheight > S.dheight) - return 4 - if(height-dheight > S.height-S.dheight) - return 5 - //check the dock isn't occupied - if(S.get_docked()) - return 6 - return 0 //0 means we can dock - -//call the shuttle to destination S -/obj/docking_port/mobile/proc/request(obj/docking_port/stationary/S) - if(canDock(S)) - . = 1 - throw EXCEPTION("request(): shuttle cannot dock") - return 1 //we can't dock at S - - switch(mode) - if(SHUTTLE_CALL) - if(S == destination) - if(world.time <= timer) - timer = world.time - else - destination = S - timer = world.time - if(SHUTTLE_RECALL) - if(S == destination) - timer = world.time - timeLeft(1) - else - destination = S - timer = world.time - mode = SHUTTLE_CALL - else - destination = S - mode = SHUTTLE_CALL - timer = world.time - enterTransit() //hyperspace - -//recall the shuttle to where it was previously -/obj/docking_port/mobile/proc/cancel() - if(mode != SHUTTLE_CALL) - return - - timer = world.time - timeLeft(1) - mode = SHUTTLE_RECALL - -/obj/docking_port/mobile/proc/enterTransit() - previous = null -// if(!destination) -// return - var/obj/docking_port/stationary/S0 = get_docked() - var/obj/docking_port/stationary/S1 = findTransitDock() - if(S1) - if(dock(S1)) - WARNING("shuttle \"[id]\" could not enter transit space. Docked at [S0 ? S0.id : "null"]. Transit dock [S1 ? S1.id : "null"].") - else - previous = S0 - else - WARNING("shuttle \"[id]\" could not enter transit space. S0=[S0 ? S0.id : "null"] S1=[S1 ? S1.id : "null"]") - -//default shuttleRotate -/atom/proc/shuttleRotate(rotation) - //rotate our direction - dir = angle2dir(rotation+dir2angle(dir)) - - //resmooth if need be. - if(smooth) - smooth_icon(src) - - //rotate the pixel offsets too. - if (pixel_x || pixel_y) - if (rotation < 0) - rotation += 360 - for (var/turntimes=rotation/90;turntimes>0;turntimes--) - var/oldPX = pixel_x - var/oldPY = pixel_y - pixel_x = oldPY - pixel_y = (oldPX*(-1)) - - - -//this is the main proc. It instantly moves our mobile port to stationary port S1 -//it handles all the generic behaviour, such as sanity checks, closing doors on the shuttle, stunning mobs, etc -/obj/docking_port/mobile/proc/dock(obj/docking_port/stationary/S1) - . = canDock(S1) - if(.) - throw EXCEPTION("dock(): shuttle cannot dock") - return . - - if(canMove()) - return -1 - - closePortDoors() - -// //rotate transit docking ports, so we don't need zillions of variants -// if(istype(S1, /obj/docking_port/stationary/transit)) -// S1.dir = turn(NORTH, -travelDir) - - var/obj/docking_port/stationary/S0 = get_docked() - var/turf_type = /turf/space - var/area_type = /area/space - if(S0) - if(S0.turf_type) - turf_type = S0.turf_type - if(S0.area_type) - area_type = S0.area_type - - var/list/L0 = return_ordered_turfs(x, y, z, dir, areaInstance) - var/list/L1 = return_ordered_turfs(S1.x, S1.y, S1.z, S1.dir) - - var/rotation = dir2angle(S1.dir)-dir2angle(dir) - if ((rotation % 90) != 0) - rotation += (rotation % 90) //diagonal rotations not allowed, round up - rotation = SimplifyDegrees(rotation) - - - - //remove area surrounding docking port - if(areaInstance.contents.len) - var/area/A0 = locate("[area_type]") - if(!A0) - A0 = new area_type(null) - for(var/turf/T0 in L0) - A0.contents += T0 - - //move or squish anything in the way ship at destination - roadkill(L1, S1.dir) - - for(var/i=1, i<=L0.len, ++i) - var/turf/T0 = L0[i] - if(!T0) - continue - var/turf/T1 = L1[i] - if(!T1) - continue - if(T0.type != T0.baseturf) //So if there is a hole in the shuttle we don't drag along the space/asteroid/etc to wherever we are going next - T0.copyTurf(T1) - areaInstance.contents += T1 - - //copy over air - if(istype(T1, /turf/simulated)) - var/turf/simulated/Ts1 = T1 - Ts1.copy_air_with_tile(T0) - - //move mobile to new location - - - for(var/atom/movable/AM in T0) - if (rotation) - AM.shuttleRotate(rotation) - - if (istype(AM,/obj)) - var/obj/O = AM - if(O.invisibility >= 101) - continue - if(O == T0.lighting_object) - continue - O.loc = T1 - - //close open doors - if(istype(O, /obj/machinery/door)) - var/obj/machinery/door/Door = O - spawn(-1) - if(Door) - Door.close() - else if (istype(AM,/mob)) - var/mob/M = AM - if(!M.move_on_shuttle) - continue - M.loc = T1 - - //docking turbulence - if(M.client) - spawn(0) - if(M.buckled) - shake_camera(M, 2, 1) // turn it down a bit come on - else - shake_camera(M, 7, 1) - if(istype(M, /mob/living/carbon)) - if(!M.buckled) - M.Weaken(3) - - - if (rotation) - T1.shuttleRotate(rotation) - - //lighting stuff - T1.redraw_lighting() - SSair.remove_from_active(T1) - T1.CalculateAdjacentTurfs() - SSair.add_to_active(T1,1) - - T0.ChangeTurf(turf_type) - - T0.redraw_lighting() - SSair.remove_from_active(T0) - T0.CalculateAdjacentTurfs() - SSair.add_to_active(T0,1) - - loc = S1.loc - dir = S1.dir - -/* - if(istype(S1, /obj/docking_port/stationary/transit)) - var/d = turn(dir, 180 + travelDir) - for(var/turf/space/transit/T in S1.return_ordered_turfs()) - T.pushdirection = d - T.update_icon() -*/ - - - -/obj/docking_port/mobile/proc/findTransitDock() - var/obj/docking_port/stationary/transit/T = SSshuttle.getDock("[id]_transit") - if(T && !canDock(T)) - return T -/* commented out due to issues with rotation - for(var/obj/docking_port/stationary/transit/S in SSshuttle.transit) - if(S.id) - continue - if(!canDock(S)) - return S -*/ - - -//shuttle-door closing is handled in the dock() proc whilst looping through turfs -//this one closes the door where we are docked at, if there is one there. -/obj/docking_port/mobile/proc/closePortDoors() - var/turf/T = get_step(loc, turn(dir,180)) - if(T) - var/obj/machinery/door/Door = locate() in T - if(Door) - spawn(-1) - Door.close() - -/obj/docking_port/mobile/proc/roadkill(list/L, dir, x, y) - for(var/turf/T in L) - for(var/atom/movable/AM in T) - if(ismob(AM)) - if(istype(AM, /mob/living)) - var/mob/living/M = AM - M.Paralyse(10) - M.take_organ_damage(80) - M.anchored = 0 - else - continue - - if(!AM.anchored) - step(AM, dir) - else - qdel(AM) -/* -//used to check if atom/A is within the shuttle's bounding box -/obj/docking_port/mobile/proc/onShuttleCheck(atom/A) - var/turf/T = get_turf(A) - if(!T) - return 0 - - var/list/L = return_coords() - if(L[1] > L[3]) - L.Swap(1,3) - if(L[2] > L[4]) - L.Swap(2,4) - - if(L[1] <= T.x && T.x <= L[3]) - if(L[2] <= T.y && T.y <= L[4]) - return 1 - return 0 -*/ -//used by shuttle subsystem to check timers -/obj/docking_port/mobile/proc/check() - var/timeLeft = timeLeft(1) - if(timeLeft <= 0) - switch(mode) - if(SHUTTLE_CALL) - if(dock(destination)) - setTimer(20) //can't dock for some reason, try again in 2 seconds - return - if(SHUTTLE_RECALL) - if(dock(previous)) - setTimer(20) //can't dock for some reason, try again in 2 seconds - return - mode = SHUTTLE_IDLE - timer = 0 - destination = null - - -/obj/docking_port/mobile/proc/setTimer(wait) - if(timer <= 0) - timer = world.time - timer += wait - timeLeft(1) - -//returns timeLeft -/obj/docking_port/mobile/proc/timeLeft(divisor) - if(divisor <= 0) - divisor = 10 - if(!timer) - return round(callTime/divisor, 1) - return max( round((timer+callTime-world.time)/divisor,1), 0 ) - -/obj/docking_port/mobile/proc/getStatusText() - var/obj/docking_port/stationary/dockedAt = get_docked() - . = (dockedAt && dockedAt.name) ? dockedAt.name : "unknown" - if(istype(dockedAt, /obj/docking_port/stationary/transit)) - var/obj/docking_port/stationary/dst - if(mode == SHUTTLE_RECALL) - dst = previous - else - dst = destination - . += " towards [dst ? dst.name : "unknown location"] ([timeLeft(600)]mins)" - -/obj/machinery/computer/shuttle - name = "Shuttle Console" - icon_screen = "shuttle" - icon_keyboard = "tech_key" - req_access = list( ) - circuit = /obj/item/weapon/circuitboard/shuttle - var/shuttleId - var/possible_destinations = "" - var/admin_controlled - -/obj/machinery/computer/shuttle/New(location, obj/item/weapon/circuitboard/shuttle/C) - ..() - if(istype(C)) - possible_destinations = C.possible_destinations - shuttleId = C.shuttleId - -/obj/machinery/computer/shuttle/attack_hand(mob/user) - if(..(user)) - return - src.add_fingerprint(usr) - - var/list/options = params2list(possible_destinations) - var/obj/docking_port/mobile/M = SSshuttle.getShuttle(shuttleId) - var/dat = "Status: [M ? M.getStatusText() : "*Missing*"]

        " - if(M) - var/destination_found - for(var/obj/docking_port/stationary/S in SSshuttle.stationary) - if(!options.Find(S.id)) - continue - if(M.canDock(S)) - continue - destination_found = 1 - dat += "Send to [S.name]
        " - if(!destination_found) - dat += "Shuttle Locked
        " - if(admin_controlled) - dat += "Authorized personnel only
        " - dat += "Request Authorization
        " - dat += "Close" - - var/datum/browser/popup = new(user, "computer", M ? M.name : "shuttle", 300, 200) - popup.set_content("
        [dat]
        ") - popup.set_title_image(usr.browse_rsc_icon(src.icon, src.icon_state)) - popup.open() - -/obj/machinery/computer/shuttle/Topic(href, href_list) - if(..()) - return - usr.set_machine(src) - src.add_fingerprint(usr) - if(!allowed(usr)) - usr << "Access denied." - return - - if(href_list["move"]) - switch(SSshuttle.moveShuttle(shuttleId, href_list["move"], 1)) - if(0) usr << "Shuttle received message and will be sent shortly." - if(1) usr << "Invalid shuttle requested." - else usr << "Unable to comply." - -/obj/machinery/computer/shuttle/emag_act(mob/user) - if(!emagged) - src.req_access = list() - emagged = 1 - user << "You fried the consoles ID checking system." - -/obj/machinery/computer/shuttle/ferry - name = "transport ferry console" - circuit = /obj/item/weapon/circuitboard/ferry - shuttleId = "ferry" - possible_destinations = "ferry_home;ferry_away" - - -/obj/machinery/computer/shuttle/ferry/request - name = "ferry console" - circuit = /obj/item/weapon/circuitboard/ferry/request - var/cooldown //prevents spamming admins - possible_destinations = "ferry_home" - admin_controlled = 1 - -/obj/machinery/computer/shuttle/ferry/request/Topic(href, href_list) - ..() - if(href_list["request"]) - if(cooldown) - return - cooldown = 1 - usr << "Your request has been recieved by Centcom." - admins << "FERRY: [key_name_admin(usr)] (?) (FLW) (Move Ferry) is requesting to move the transport ferry to Centcom." - spawn(600) //One minute cooldown - cooldown = 0 - - -#undef DOCKING_PORT_HIGHLIGHT - - -/turf/proc/copyTurf(turf/T) - if(T.type != type) - var/obj/O - if(underlays.len) //we have underlays, which implies some sort of transparency, so we want to a snapshot of the previous turf as an underlay - O = new() - O.underlays.Add(T) - T.ChangeTurf(type) - if(underlays.len) - T.underlays = O.underlays - if(T.icon_state != icon_state) - T.icon_state = icon_state - if(T.icon != icon) - T.icon = icon - if(T.color != color) - T.color = color - if(T.dir != dir) - T.dir = dir - return T +//use this define to highlight docking port bounding boxes (ONLY FOR DEBUG USE) +//#define DOCKING_PORT_HIGHLIGHT + +//NORTH default dir +/obj/docking_port + invisibility = 101 + icon = 'icons/obj/device.dmi' + //icon = 'icons/dirsquare.dmi' + icon_state = "pinonfar" + + unacidable = 1 + anchored = 1 + + var/id + dir = NORTH //this should point -away- from the dockingport door, ie towards the ship + var/width = 0 //size of covered area, perpendicular to dir + var/height = 0 //size of covered area, paralell to dir + var/dwidth = 0 //position relative to covered area, perpendicular to dir + var/dheight = 0 //position relative to covered area, parallel to dir + + //these objects are indestructable +/obj/docking_port/Destroy() + return QDEL_HINT_LETMELIVE +/obj/docking_port/singularity_pull() + return +/obj/docking_port/singularity_act() + return 0 +/obj/docking_port/shuttleRotate() + return //we don't rotate with shuttles via this code. +//returns a list(x0,y0, x1,y1) where points 0 and 1 are bounding corners of the projected rectangle +/obj/docking_port/proc/return_coords(_x, _y, _dir) + if(!_dir) + _dir = dir + if(!_x) + _x = x + if(!_y) + _y = y + + //byond's sin and cos functions are inaccurate. This is faster and perfectly accurate + var/cos = 1 + var/sin = 0 + switch(_dir) + if(WEST) + cos = 0 + sin = 1 + if(SOUTH) + cos = -1 + sin = 0 + if(EAST) + cos = 0 + sin = -1 + + return list( + _x + (-dwidth*cos) - (-dheight*sin), + _y + (-dwidth*sin) + (-dheight*cos), + _x + (-dwidth+width-1)*cos - (-dheight+height-1)*sin, + _y + (-dwidth+width-1)*sin + (-dheight+height-1)*cos + ) + + +//returns turfs within our projected rectangle in a specific order. +//this ensures that turfs are copied over in the same order, regardless of any rotation +/obj/docking_port/proc/return_ordered_turfs(_x, _y, _z, _dir, area/A) + if(!_dir) + _dir = dir + if(!_x) + _x = x + if(!_y) + _y = y + if(!_z) + _z = z + var/cos = 1 + var/sin = 0 + switch(_dir) + if(WEST) + cos = 0 + sin = 1 + if(SOUTH) + cos = -1 + sin = 0 + if(EAST) + cos = 0 + sin = -1 + + . = list() + + var/xi + var/yi + for(var/dx=0, dx S.dwidth) + return 2 + if(width-dwidth > S.width-S.dwidth) + return 3 + if(dheight > S.dheight) + return 4 + if(height-dheight > S.height-S.dheight) + return 5 + //check the dock isn't occupied + if(S.get_docked()) + return 6 + return 0 //0 means we can dock + +//call the shuttle to destination S +/obj/docking_port/mobile/proc/request(obj/docking_port/stationary/S) + if(canDock(S)) + . = 1 + throw EXCEPTION("request(): shuttle cannot dock") + return 1 //we can't dock at S + + switch(mode) + if(SHUTTLE_CALL) + if(S == destination) + if(world.time <= timer) + timer = world.time + else + destination = S + timer = world.time + if(SHUTTLE_RECALL) + if(S == destination) + timer = world.time - timeLeft(1) + else + destination = S + timer = world.time + mode = SHUTTLE_CALL + else + destination = S + mode = SHUTTLE_CALL + timer = world.time + enterTransit() //hyperspace + +//recall the shuttle to where it was previously +/obj/docking_port/mobile/proc/cancel() + if(mode != SHUTTLE_CALL) + return + + timer = world.time - timeLeft(1) + mode = SHUTTLE_RECALL + +/obj/docking_port/mobile/proc/enterTransit() + previous = null +// if(!destination) +// return + var/obj/docking_port/stationary/S0 = get_docked() + var/obj/docking_port/stationary/S1 = findTransitDock() + if(S1) + if(dock(S1)) + WARNING("shuttle \"[id]\" could not enter transit space. Docked at [S0 ? S0.id : "null"]. Transit dock [S1 ? S1.id : "null"].") + else + previous = S0 + else + WARNING("shuttle \"[id]\" could not enter transit space. S0=[S0 ? S0.id : "null"] S1=[S1 ? S1.id : "null"]") + +//default shuttleRotate +/atom/proc/shuttleRotate(rotation) + //rotate our direction + dir = angle2dir(rotation+dir2angle(dir)) + + //resmooth if need be. + if(smooth) + smooth_icon(src) + + //rotate the pixel offsets too. + if (pixel_x || pixel_y) + if (rotation < 0) + rotation += 360 + for (var/turntimes=rotation/90;turntimes>0;turntimes--) + var/oldPX = pixel_x + var/oldPY = pixel_y + pixel_x = oldPY + pixel_y = (oldPX*(-1)) + + + +//this is the main proc. It instantly moves our mobile port to stationary port S1 +//it handles all the generic behaviour, such as sanity checks, closing doors on the shuttle, stunning mobs, etc +/obj/docking_port/mobile/proc/dock(obj/docking_port/stationary/S1) + . = canDock(S1) + if(.) + throw EXCEPTION("dock(): shuttle cannot dock") + return . + + if(canMove()) + return -1 + + closePortDoors() + +// //rotate transit docking ports, so we don't need zillions of variants +// if(istype(S1, /obj/docking_port/stationary/transit)) +// S1.dir = turn(NORTH, -travelDir) + + var/obj/docking_port/stationary/S0 = get_docked() + var/turf_type = /turf/space + var/area_type = /area/space + if(S0) + if(S0.turf_type) + turf_type = S0.turf_type + if(S0.area_type) + area_type = S0.area_type + + var/list/L0 = return_ordered_turfs(x, y, z, dir, areaInstance) + var/list/L1 = return_ordered_turfs(S1.x, S1.y, S1.z, S1.dir) + + var/rotation = dir2angle(S1.dir)-dir2angle(dir) + if ((rotation % 90) != 0) + rotation += (rotation % 90) //diagonal rotations not allowed, round up + rotation = SimplifyDegrees(rotation) + + + + //remove area surrounding docking port + if(areaInstance.contents.len) + var/area/A0 = locate("[area_type]") + if(!A0) + A0 = new area_type(null) + for(var/turf/T0 in L0) + A0.contents += T0 + + //move or squish anything in the way ship at destination + roadkill(L1, S1.dir) + + for(var/i=1, i<=L0.len, ++i) + var/turf/T0 = L0[i] + if(!T0) + continue + var/turf/T1 = L1[i] + if(!T1) + continue + if(T0.type != T0.baseturf) //So if there is a hole in the shuttle we don't drag along the space/asteroid/etc to wherever we are going next + T0.copyTurf(T1) + areaInstance.contents += T1 + + //copy over air + if(istype(T1, /turf/simulated)) + var/turf/simulated/Ts1 = T1 + Ts1.copy_air_with_tile(T0) + + //move mobile to new location + + + for(var/atom/movable/AM in T0) + if (rotation) + AM.shuttleRotate(rotation) + + if (istype(AM,/obj)) + var/obj/O = AM + if(O.invisibility >= 101) + continue + if(O == T0.lighting_object) + continue + O.loc = T1 + + //close open doors + if(istype(O, /obj/machinery/door)) + var/obj/machinery/door/Door = O + spawn(-1) + if(Door) + Door.close() + else if (istype(AM,/mob)) + var/mob/M = AM + if(!M.move_on_shuttle) + continue + M.loc = T1 + + //docking turbulence + if(M.client) + spawn(0) + if(M.buckled) + shake_camera(M, 2, 1) // turn it down a bit come on + else + shake_camera(M, 7, 1) + if(istype(M, /mob/living/carbon)) + if(!M.buckled) + M.Weaken(3) + + + if (rotation) + T1.shuttleRotate(rotation) + + //lighting stuff + T1.redraw_lighting() + SSair.remove_from_active(T1) + T1.CalculateAdjacentTurfs() + SSair.add_to_active(T1,1) + + T0.ChangeTurf(turf_type) + + T0.redraw_lighting() + SSair.remove_from_active(T0) + T0.CalculateAdjacentTurfs() + SSair.add_to_active(T0,1) + + loc = S1.loc + dir = S1.dir + +/* + if(istype(S1, /obj/docking_port/stationary/transit)) + var/d = turn(dir, 180 + travelDir) + for(var/turf/space/transit/T in S1.return_ordered_turfs()) + T.pushdirection = d + T.update_icon() +*/ + + + +/obj/docking_port/mobile/proc/findTransitDock() + var/obj/docking_port/stationary/transit/T = SSshuttle.getDock("[id]_transit") + if(T && !canDock(T)) + return T +/* commented out due to issues with rotation + for(var/obj/docking_port/stationary/transit/S in SSshuttle.transit) + if(S.id) + continue + if(!canDock(S)) + return S +*/ + + +//shuttle-door closing is handled in the dock() proc whilst looping through turfs +//this one closes the door where we are docked at, if there is one there. +/obj/docking_port/mobile/proc/closePortDoors() + var/turf/T = get_step(loc, turn(dir,180)) + if(T) + var/obj/machinery/door/Door = locate() in T + if(Door) + spawn(-1) + Door.close() + +/obj/docking_port/mobile/proc/roadkill(list/L, dir, x, y) + for(var/turf/T in L) + for(var/atom/movable/AM in T) + if(ismob(AM)) + if(istype(AM, /mob/living)) + var/mob/living/M = AM + M.Paralyse(10) + M.take_organ_damage(80) + M.anchored = 0 + else + continue + + if(!AM.anchored) + step(AM, dir) + else + qdel(AM) +/* +//used to check if atom/A is within the shuttle's bounding box +/obj/docking_port/mobile/proc/onShuttleCheck(atom/A) + var/turf/T = get_turf(A) + if(!T) + return 0 + + var/list/L = return_coords() + if(L[1] > L[3]) + L.Swap(1,3) + if(L[2] > L[4]) + L.Swap(2,4) + + if(L[1] <= T.x && T.x <= L[3]) + if(L[2] <= T.y && T.y <= L[4]) + return 1 + return 0 +*/ +//used by shuttle subsystem to check timers +/obj/docking_port/mobile/proc/check() + var/timeLeft = timeLeft(1) + if(timeLeft <= 0) + switch(mode) + if(SHUTTLE_CALL) + if(dock(destination)) + setTimer(20) //can't dock for some reason, try again in 2 seconds + return + if(SHUTTLE_RECALL) + if(dock(previous)) + setTimer(20) //can't dock for some reason, try again in 2 seconds + return + mode = SHUTTLE_IDLE + timer = 0 + destination = null + + +/obj/docking_port/mobile/proc/setTimer(wait) + if(timer <= 0) + timer = world.time + timer += wait - timeLeft(1) + +//returns timeLeft +/obj/docking_port/mobile/proc/timeLeft(divisor) + if(divisor <= 0) + divisor = 10 + if(!timer) + return round(callTime/divisor, 1) + return max( round((timer+callTime-world.time)/divisor,1), 0 ) + +/obj/docking_port/mobile/proc/getStatusText() + var/obj/docking_port/stationary/dockedAt = get_docked() + . = (dockedAt && dockedAt.name) ? dockedAt.name : "unknown" + if(istype(dockedAt, /obj/docking_port/stationary/transit)) + var/obj/docking_port/stationary/dst + if(mode == SHUTTLE_RECALL) + dst = previous + else + dst = destination + . += " towards [dst ? dst.name : "unknown location"] ([timeLeft(600)]mins)" + +/obj/machinery/computer/shuttle + name = "Shuttle Console" + icon_screen = "shuttle" + icon_keyboard = "tech_key" + req_access = list( ) + circuit = /obj/item/weapon/circuitboard/shuttle + var/shuttleId + var/possible_destinations = "" + var/admin_controlled + +/obj/machinery/computer/shuttle/New(location, obj/item/weapon/circuitboard/shuttle/C) + ..() + if(istype(C)) + possible_destinations = C.possible_destinations + shuttleId = C.shuttleId + +/obj/machinery/computer/shuttle/attack_hand(mob/user) + if(..(user)) + return + src.add_fingerprint(usr) + + var/list/options = params2list(possible_destinations) + var/obj/docking_port/mobile/M = SSshuttle.getShuttle(shuttleId) + var/dat = "Status: [M ? M.getStatusText() : "*Missing*"]

        " + if(M) + var/destination_found + for(var/obj/docking_port/stationary/S in SSshuttle.stationary) + if(!options.Find(S.id)) + continue + if(M.canDock(S)) + continue + destination_found = 1 + dat += "Send to [S.name]
        " + if(!destination_found) + dat += "Shuttle Locked
        " + if(admin_controlled) + dat += "Authorized personnel only
        " + dat += "Request Authorization
        " + dat += "Close" + + var/datum/browser/popup = new(user, "computer", M ? M.name : "shuttle", 300, 200) + popup.set_content("
        [dat]
        ") + popup.set_title_image(usr.browse_rsc_icon(src.icon, src.icon_state)) + popup.open() + +/obj/machinery/computer/shuttle/Topic(href, href_list) + if(..()) + return + usr.set_machine(src) + src.add_fingerprint(usr) + if(!allowed(usr)) + usr << "Access denied." + return + + if(href_list["move"]) + switch(SSshuttle.moveShuttle(shuttleId, href_list["move"], 1)) + if(0) usr << "Shuttle received message and will be sent shortly." + if(1) usr << "Invalid shuttle requested." + else usr << "Unable to comply." + +/obj/machinery/computer/shuttle/emag_act(mob/user) + if(!emagged) + src.req_access = list() + emagged = 1 + user << "You fried the consoles ID checking system." + +/obj/machinery/computer/shuttle/ferry + name = "transport ferry console" + circuit = /obj/item/weapon/circuitboard/ferry + shuttleId = "ferry" + possible_destinations = "ferry_home;ferry_away" + + +/obj/machinery/computer/shuttle/ferry/request + name = "ferry console" + circuit = /obj/item/weapon/circuitboard/ferry/request + var/cooldown //prevents spamming admins + possible_destinations = "ferry_home" + admin_controlled = 1 + +/obj/machinery/computer/shuttle/ferry/request/Topic(href, href_list) + ..() + if(href_list["request"]) + if(cooldown) + return + cooldown = 1 + usr << "Your request has been recieved by Centcom." + admins << "FERRY: [key_name_admin(usr)] (?) (FLW) (Move Ferry) is requesting to move the transport ferry to Centcom." + spawn(600) //One minute cooldown + cooldown = 0 + + +#undef DOCKING_PORT_HIGHLIGHT + + +/turf/proc/copyTurf(turf/T) + if(T.type != type) + var/obj/O + if(underlays.len) //we have underlays, which implies some sort of transparency, so we want to a snapshot of the previous turf as an underlay + O = new() + O.underlays.Add(T) + T.ChangeTurf(type) + if(underlays.len) + T.underlays = O.underlays + if(T.icon_state != icon_state) + T.icon_state = icon_state + if(T.icon != icon) + T.icon = icon + if(T.color != color) + T.color = color + if(T.dir != dir) + T.dir = dir + return T diff --git a/code/modules/surgery/cavity_implant.dm b/code/modules/surgery/cavity_implant.dm index 19ebb11a767..c32bca635a4 100644 --- a/code/modules/surgery/cavity_implant.dm +++ b/code/modules/surgery/cavity_implant.dm @@ -1,45 +1,45 @@ -/datum/surgery/cavity_implant - name = "cavity implant" - steps = list(/datum/surgery_step/incise, /datum/surgery_step/clamp_bleeders, /datum/surgery_step/retract_skin, /datum/surgery_step/incise, /datum/surgery_step/handle_cavity, /datum/surgery_step/close) - species = list(/mob/living/carbon/human, /mob/living/carbon/monkey) - possible_locs = list("chest") - - -//handle cavity -/datum/surgery_step/handle_cavity - name = "implant item" - accept_hand = 1 - accept_any_item = 1 - time = 32 - var/obj/item/IC = null - -/datum/surgery_step/handle_cavity/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) - for(var/obj/item/I in target.internal_organs) - if(!istype(I, /obj/item/organ)) - IC = I - break - if(tool) - user.visible_message("[user] begins to insert [tool] into [target]'s [target_zone].", "You begin to insert [tool] into [target]'s [target_zone]...") - else - user.visible_message("[user] checks for items in [target]'s [target_zone].", "You check for items in [target]'s [target_zone]...") - -/datum/surgery_step/handle_cavity/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) - if(tool) - if(IC || tool.w_class > 3 || NODROP in tool.flags || istype(tool, /obj/item/organ)) - user << "You can't seem to fit [tool] in [target]'s [target_zone]!" - return 0 - else - user.visible_message("[user] stuffs [tool] into [target]'s [target_zone]!", "You stuff [tool] into [target]'s [target_zone].") - user.drop_item() - target.internal_organs += tool - tool.loc = target - return 1 - else - if(IC) - user.visible_message("[user] pulls [IC] out of [target]'s [target_zone]!", "You pull [IC] out of [target]'s [target_zone].") - user.put_in_hands(IC) - target.internal_organs -= IC - return 1 - else - user << "You don't find anything in [target]'s [target_zone]." +/datum/surgery/cavity_implant + name = "cavity implant" + steps = list(/datum/surgery_step/incise, /datum/surgery_step/clamp_bleeders, /datum/surgery_step/retract_skin, /datum/surgery_step/incise, /datum/surgery_step/handle_cavity, /datum/surgery_step/close) + species = list(/mob/living/carbon/human, /mob/living/carbon/monkey) + possible_locs = list("chest") + + +//handle cavity +/datum/surgery_step/handle_cavity + name = "implant item" + accept_hand = 1 + accept_any_item = 1 + time = 32 + var/obj/item/IC = null + +/datum/surgery_step/handle_cavity/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + for(var/obj/item/I in target.internal_organs) + if(!istype(I, /obj/item/organ)) + IC = I + break + if(tool) + user.visible_message("[user] begins to insert [tool] into [target]'s [target_zone].", "You begin to insert [tool] into [target]'s [target_zone]...") + else + user.visible_message("[user] checks for items in [target]'s [target_zone].", "You check for items in [target]'s [target_zone]...") + +/datum/surgery_step/handle_cavity/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + if(tool) + if(IC || tool.w_class > 3 || NODROP in tool.flags || istype(tool, /obj/item/organ)) + user << "You can't seem to fit [tool] in [target]'s [target_zone]!" + return 0 + else + user.visible_message("[user] stuffs [tool] into [target]'s [target_zone]!", "You stuff [tool] into [target]'s [target_zone].") + user.drop_item() + target.internal_organs += tool + tool.loc = target + return 1 + else + if(IC) + user.visible_message("[user] pulls [IC] out of [target]'s [target_zone]!", "You pull [IC] out of [target]'s [target_zone].") + user.put_in_hands(IC) + target.internal_organs -= IC + return 1 + else + user << "You don't find anything in [target]'s [target_zone]." return 0 \ No newline at end of file diff --git a/nano/Gulpfile.coffee b/nano/Gulpfile.coffee index f99fc439c89..1775e2c0232 100644 --- a/nano/Gulpfile.coffee +++ b/nano/Gulpfile.coffee @@ -1,127 +1,127 @@ -### Settings ### -util = require("gulp-util") -s = - min: util.env.min - colorblind: util.env.colorblind - -# Project Paths -input = - fonts: "**/*.{eot,woff2}" - images: "images" - scripts: "scripts" - styles: "styles" - templates: "templates" - -output = - dir: "assets" - scripts: - lib: "nanoui.lib.js" - main: "nanoui.main.js" - styles: - lib: "nanoui.lib.css" - prefix: "nanoui." - templates: "nanoui.templates.js" - -# doT Settings -dotOpts = - evaluate: /\{\{([\s\S]+?)\}\}/g, - interpolate: /\{\{=([\s\S]+?)\}\}/g, - encode: /\{\{!([\s\S]+?)\}\}/g, - use: /\{\{#([\s\S]+?)\}\}/g, - define: /\{\{##\s*([\w\.$]+)\s*(\:|=)([\s\S]+?)#\}\}/g, - conditional: /\{\{\?(\?)?\s*([\s\S]*?)\s*\}\}/g, - iterate: /\{\{~\s*(?:\}\}|([\s\S]+?)\s*\:\s*([\w$]+)\s*(?:\:\s*([\w$]+))?\s*\}\})/g, - varname: "data, config, helper", - strip: true, - append: true, - selfcontained: true - -### Pacakages ### -bower = require "main-bower-files" -child_process = require "child_process" -del = require "del" -gulp = require "gulp" -merge = require "merge-stream" - -### Plugins ### -g = require("gulp-load-plugins")({replaceString: /^gulp(-|\.)|-/g}) -p = - autoprefixer: require "autoprefixer" - colorblind: require "postcss-colorblind" - fontweights: require "postcss-font-weights" - gradient: require "postcss-filter-gradient" - opacity: require "postcss-opacity" - plsfilters: require "pleeease-filters" - rgba: require "postcss-color-rgba-fallback" - -### Helpers ### - -glob = (path) -> - "#{path}/*" - -### Tasks ### -gulp.task "default", ["fonts", "scripts", "styles", "templates"] - -gulp.task "clean", -> - del glob output.dir - -gulp.task "watch", -> - gulp.watch [glob input.images], ["reload"] - gulp.watch [glob input.scripts], ["reload"] - gulp.watch [glob input.styles], ["reload"] - gulp.watch [glob input.templates], ["reload"] - -gulp.task "reload", ["default"], -> - child_process.exec "reload.bat", (err, stdout, stderr) -> - return console.log err if err - -gulp.task "fonts", ["clean"], -> - gulp.src bower input.fonts - .pipe gulp.dest output.dir - -gulp.task "scripts", ["clean"], -> - lib = gulp.src bower "**/*.js" - .pipe g.concat(output.scripts.lib) - .pipe g.if(s.min, g.uglify(), g.jsbeautifier()) - .pipe gulp.dest output.dir - - main = gulp.src glob input.scripts - .pipe g.coffee() - .pipe g.concat(output.scripts.main) - .pipe g.if(s.min, g.uglify(), g.jsbeautifier()) - .pipe gulp.dest output.dir - - merge lib, main - -gulp.task "styles", ["clean"], -> - lib = gulp.src bower "**/*.css" - .pipe g.replace("../fonts/", "") - .pipe g.concat(output.styles.lib) - .pipe g.if(s.min, g.cssnano({discardComments: {removeAll: true}}), g.csscomb()) - .pipe gulp.dest output.dir - - main = gulp.src glob input.styles - .pipe g.filter(["*.less", "!_*.less"]) - .pipe g.less({paths: [input.images]}) - .pipe g.postcss([ - p.autoprefixer({browsers: ["last 2 versions", "ie >= 8"]}), - p.plsfilters({oldIE: true}), - p.rgba({oldie: true}), - p.opacity, - p.gradient, - p.fontweights - ]) - .pipe g.if(s.colorblind, g.postcss([p.colorblind])) - .pipe g.if(s.min, g.cssnano({discardComments: {removeAll: true}}), g.csscomb()) - .pipe g.rename({prefix: output.styles.prefix}) - .pipe gulp.dest output.dir - - merge lib, main - -gulp.task "templates", ["clean"], -> - gulp.src glob input.templates - .pipe g.dotprecompiler({dictionary: "TMPL", templateSettings: dotOpts}) - .pipe g.concat(output.templates) - .pipe g.header("window.TMPL = {};\n") - .pipe g.if(s.min, g.uglify(), g.jsbeautifier()) - .pipe gulp.dest output.dir +### Settings ### +util = require("gulp-util") +s = + min: util.env.min + colorblind: util.env.colorblind + +# Project Paths +input = + fonts: "**/*.{eot,woff2}" + images: "images" + scripts: "scripts" + styles: "styles" + templates: "templates" + +output = + dir: "assets" + scripts: + lib: "nanoui.lib.js" + main: "nanoui.main.js" + styles: + lib: "nanoui.lib.css" + prefix: "nanoui." + templates: "nanoui.templates.js" + +# doT Settings +dotOpts = + evaluate: /\{\{([\s\S]+?)\}\}/g, + interpolate: /\{\{=([\s\S]+?)\}\}/g, + encode: /\{\{!([\s\S]+?)\}\}/g, + use: /\{\{#([\s\S]+?)\}\}/g, + define: /\{\{##\s*([\w\.$]+)\s*(\:|=)([\s\S]+?)#\}\}/g, + conditional: /\{\{\?(\?)?\s*([\s\S]*?)\s*\}\}/g, + iterate: /\{\{~\s*(?:\}\}|([\s\S]+?)\s*\:\s*([\w$]+)\s*(?:\:\s*([\w$]+))?\s*\}\})/g, + varname: "data, config, helper", + strip: true, + append: true, + selfcontained: true + +### Pacakages ### +bower = require "main-bower-files" +child_process = require "child_process" +del = require "del" +gulp = require "gulp" +merge = require "merge-stream" + +### Plugins ### +g = require("gulp-load-plugins")({replaceString: /^gulp(-|\.)|-/g}) +p = + autoprefixer: require "autoprefixer" + colorblind: require "postcss-colorblind" + fontweights: require "postcss-font-weights" + gradient: require "postcss-filter-gradient" + opacity: require "postcss-opacity" + plsfilters: require "pleeease-filters" + rgba: require "postcss-color-rgba-fallback" + +### Helpers ### + +glob = (path) -> + "#{path}/*" + +### Tasks ### +gulp.task "default", ["fonts", "scripts", "styles", "templates"] + +gulp.task "clean", -> + del glob output.dir + +gulp.task "watch", -> + gulp.watch [glob input.images], ["reload"] + gulp.watch [glob input.scripts], ["reload"] + gulp.watch [glob input.styles], ["reload"] + gulp.watch [glob input.templates], ["reload"] + +gulp.task "reload", ["default"], -> + child_process.exec "reload.bat", (err, stdout, stderr) -> + return console.log err if err + +gulp.task "fonts", ["clean"], -> + gulp.src bower input.fonts + .pipe gulp.dest output.dir + +gulp.task "scripts", ["clean"], -> + lib = gulp.src bower "**/*.js" + .pipe g.concat(output.scripts.lib) + .pipe g.if(s.min, g.uglify(), g.jsbeautifier()) + .pipe gulp.dest output.dir + + main = gulp.src glob input.scripts + .pipe g.coffee() + .pipe g.concat(output.scripts.main) + .pipe g.if(s.min, g.uglify(), g.jsbeautifier()) + .pipe gulp.dest output.dir + + merge lib, main + +gulp.task "styles", ["clean"], -> + lib = gulp.src bower "**/*.css" + .pipe g.replace("../fonts/", "") + .pipe g.concat(output.styles.lib) + .pipe g.if(s.min, g.cssnano({discardComments: {removeAll: true}}), g.csscomb()) + .pipe gulp.dest output.dir + + main = gulp.src glob input.styles + .pipe g.filter(["*.less", "!_*.less"]) + .pipe g.less({paths: [input.images]}) + .pipe g.postcss([ + p.autoprefixer({browsers: ["last 2 versions", "ie >= 8"]}), + p.plsfilters({oldIE: true}), + p.rgba({oldie: true}), + p.opacity, + p.gradient, + p.fontweights + ]) + .pipe g.if(s.colorblind, g.postcss([p.colorblind])) + .pipe g.if(s.min, g.cssnano({discardComments: {removeAll: true}}), g.csscomb()) + .pipe g.rename({prefix: output.styles.prefix}) + .pipe gulp.dest output.dir + + merge lib, main + +gulp.task "templates", ["clean"], -> + gulp.src glob input.templates + .pipe g.dotprecompiler({dictionary: "TMPL", templateSettings: dotOpts}) + .pipe g.concat(output.templates) + .pipe g.header("window.TMPL = {};\n") + .pipe g.if(s.min, g.uglify(), g.jsbeautifier()) + .pipe gulp.dest output.dir diff --git a/nano/README.md b/nano/README.md index 61490ced92c..aebc35d75d6 100644 --- a/nano/README.md +++ b/nano/README.md @@ -1,377 +1,377 @@ - - -- [NanoUI](#nanoui) - - [Introduction](#introduction) - - [Components](#components) - - [`ui_interact()`](#uiinteract) - - [`get_ui_data()`](#getuidata) - - [`Topic()`](#topic) - - [Template (doT)](#template-dot) - - [Helpers](#helpers) - - [Link](#link) - - [Bar](#bar) - - [doT](#dot) - - [Styling](#styling) - - [Contributing](#contributing) - - - -# NanoUI - -## Introduction - -NanoUI is the user interface library of /tg/station. While more complex than -traditional `browse()`/stringbuilder based interfaces, it allows much more -control over display of data and gives you many features for free, such as -different `CanUseTopic()` checks (in range/is robot/in inventory/in hand/etc), -automatic refresh, attractive looks, and helpers that make writing interfaces -much easier. - -NanoUI adds a `ui_interact()` proc to all atoms, which should be called from -`interact()`. The interact proc can be called from anywhere in the atom (usually - `attack_self()` or `attack_hand()`), and is where all checks should be made. - The `ui_interact()` proc should only include NanoUI code. - -Baystation12's version of NanoUI, while slightly different in syntax, has a -good [reference](http://wiki.baystation12.net/NanoUI). - -Here is a real example from -[tanks.dm](https://github.com/tgstation/-tg-station/blob/master/code/game/objects/items/weapons/tanks/tanks.dm). - - /obj/item/weapon/tank/attack_self(mob/user) - if (!user) - return - interact(user) - - /obj/item/weapon/tank/interact(mob/user) - add_fingerprint(user) - ui_interact(user) - - /obj/item/weapon/tank/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, force_open = 0) - ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open) - if (!ui) - ui = new(user, src, ui_key, "tanks", name, 525, 175, state = inventory_state) - ui.open() - - - - -## Components - -### `ui_interact()` - -The`ui_interact()` proc is used to open a NanoUI (or update it if already open). -As NanoUI will call this proc to update your UI, you should not put any logic in -it, as NanoUI handles the logic for you. - -The parameters for `try_update_ui` and `/datum/nanoui/new()` are documented in -the code [here](https://github.com/tgstation/-tg-station/tree/master/code/modules/nano). -The most interesting parameter is `state`, which allows the object to choose the -checks that allow the UI to be interacted with. - -The default state (`default_state`) checks that the user is alive, conscious, -and within a few tiles. It allows universal access to silicons. Other states -exist, and may be more appropriate for different interfaces. For example, -`physical_state` requires the user to be nearby, even if they are a silicon. -`inventory_state` checks that the user has the object in their first-level -(not container) inventory, this is suitable for devices such as radios; -`notcontained_state` checks that the user is outside the object (great for cryo -and similar machines). - - /obj/item/the/thing/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, force_open = 0) - ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open) - if (!ui) - ui = new(user, src, ui_key, "template", title, width, height) - ui.open() - -### `get_ui_data()` - -The `get_ui_data()` proc returns a list which is used to populate the `data` -variable in the UI. This is where you should pass variables from your atom to -the UI. Here's another example from tanks.dm. - - /obj/item/weapon/tank/get_ui_data() - var/mob/living/carbon/location = null - - if(istype(loc, /mob/living/carbon)) - location = loc - else if(istype(loc.loc, /mob/living/carbon)) - location = loc.loc - - var/data = list() - data["tankPressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0) - data["releasePressure"] = round(distribute_pressure ? distribute_pressure : 0) - data["defaultReleasePressure"] = round(TANK_DEFAULT_RELEASE_PRESSURE) - data["minReleasePressure"] = round(TANK_MIN_RELEASE_PRESSURE) - data["maxReleasePressure"] = round(TANK_MAX_RELEASE_PRESSURE) - data["valveOpen"] = 0 - data["maskConnected"] = 0 - - if(istype(location)) - var/mask_check = 0 - - if(location.internal == src) // if tank is current internal - mask_check = 1 - data["valveOpen"] = 1 - else if(src in location) // or if tank is in the mobs possession - if(!location.internal) // and they do not have any active internals - mask_check = 1 - - if(mask_check) - if(location.wear_mask && (location.wear_mask.flags & MASKINTERNALS)) - data["maskConnected"] = 1 - return data - -This data can be accessed inside the NanoUI. For example, to find out if the -mask is connected (as checked near the end of the proc), we simply use -`data.maskConnected` in our template. - -### `Topic()` - -`Topic()` handles input from the UI. Typically you will recieve some data from -a button press, or pop up a input dialog to take a numerical value from the -user. Sanity checking is useful here, as `Topic()` is trivial to spoof with -arbitrary data. - -The `Topic()` interface is just the same as with more conventional, -stringbuilder-based UIs, and this needs little explanation. - - /obj/item/weapon/tank/Topic(href, href_list) - if (..()) - return - - if (href_list["dist_p"]) - if (href_list["dist_p"] == "custom") - var/custom = input(usr, "What rate do you set the regulator to? The dial reads from 0 to [TANK_MAX_RELEASE_PRESSURE].") as null|num - if(isnum(custom)) - href_list["dist_p"] = custom - .() - else if (href_list["dist_p"] == "reset") - distribute_pressure = TANK_DEFAULT_RELEASE_PRESSURE - else if (href_list["dist_p"] == "min") - distribute_pressure = TANK_MIN_RELEASE_PRESSURE - else if (href_list["dist_p"] == "max") - distribute_pressure = TANK_MAX_RELEASE_PRESSURE - else - distribute_pressure = text2num(href_list["dist_p"]) - distribute_pressure = min(max(round(distribute_pressure), TANK_MIN_RELEASE_PRESSURE), TANK_MAX_RELEASE_PRESSURE) - if (href_list["stat"]) - if(istype(loc,/mob/living/carbon)) - var/mob/living/carbon/location = loc - if(location.internal == src) - location.internal = null - location.internals.icon_state = "internal0" - usr << "You close the tank release valve." - if (location.internals) - location.internals.icon_state = "internal0" - else - if(location.wear_mask && (location.wear_mask.flags & MASKINTERNALS)) - location.internal = src - usr << "You open \the [src] valve." - if (location.internals) - location.internals.icon_state = "internal1" - else - usr << "You need something to connect to \the [src]!" - -### Template (doT) - -NanoUI templates are written in [doT](https://olado.github.io/doT/index.html), -a Javascript template engine. Data is accessed from the `data` object, -configuration (not used in pratice) from the `config` object, and template -helpers are accessed from the `helper` object. - -#### Helpers - -##### Link - - {{=helpers.link(text, icon, {'parameter': true}, status, class)}} - -Used to create a link (button), which will pass its parameters to `Topic()`. - -* Text: The text content of the link/button -* Icon: The icon shown to the left of the link (http://fontawesome.io/) -* Parameters: The values to be passed to `Topic()`'s `href_list`. -* Status: `null` for clickable, a class for selected/unclickable. -* Class: Styling to apply to the link. - -Status and Class have almost the same effect. However, changing a link's status -from `null` to something else makes it unclickable, while setting a custom Class -does not. - -Ternary operators are often used to avoid writing many `if` statements. -For example, depending on if a value in `data` is true or false we can set a -button to clickable or selected: - - {{=helper.link('Close', 'lock', {'stat': 1}, data.valveOpen ? null : 'selected')}} - -Available classes/statuses are: - -* null (normal) -* selected -* caution -* danger -* disabled - -##### Bar - - {{=helpers.bar(value, min, max, class, text)}} - -Used to create a bar, to display a numerical value visually. Min and Max default -to 0 and 100, but you can change them to avoid doing your own percent calculations. - -* Value: Defaults to a percentage but can be a straight number if Min/Max are set -* Min: The minimum value (left hand side) of the bar -* Max: The maximum value (right hand side) of the bar -* Class: The color of the bar (null/normal, good, average, bad) -* Text: The text label for the data contained in the bar (often just number form) - -As with buttons, ternary operators are quite useful: - - {{=helper.bar(data.tankPressure, 0, 1013, (data.tankPressure > 200) ? 'good' : ((data.tankPressure > 100) ? 'average' : 'bad'))}} - -#### doT - -doT is a simple template language, with control statements mixed in with -regular HTML and interpolation expressions. - -Here is a simple example from tanks, checking if a variable is true: - - {{? data.maskConnected}} - The regulator is connected to a mask. - {{??}} - The regulator is not connected to a mask. - {{?}} - -The doT tutorial is [here](https://olado.github.io/doT/tutorial.html). - -Print: - - {{=expression }} - -Print (with escape): - - {{!expression }} - -If/Else If/Else - - {{? condition}} - // if - {{?? condition}} - // else if - {{??}} - // else - {{?}} - -For - - {{~ object:key:index}} - // key, value - {{~}} - -#### Styling - -For the most part, a NanoUI is just normal HTML. However, to use the NanoUI -styles correctly, you have to be concious of a few elements. - -A `
        ` is the building block of most NanoUIs, and -represents the wells/blocks you see in most NanoUIs. Inside said article should -be a `
        ` labeling it, and many `
        `s representing items inside -(such as a label/button pair). The styling is highly subjective, so ask a -regular contribuitor to NanoUI (@neersighted at time of writing) to take a look -at and help style your UI. - -Here's an example of UI styling from Air Alarms: - -
        -

        Air Status

        - {{? data.environment_data}} - {{~ data.environment_data:info:i}} -
        - - {{=info.name}}: - -
        - {{? info.danger_level == 2}} - - {{?? info.danger_level == 1}} - - {{??}} - - {{?}} - {{=helper.fixed(info.value, 2)}}{{=info.unit}} -
        -
        - {{~}} -
        - - Local Status: - -
        - {{? data.danger_level == 2}} - Danger (Internals Required) - {{?? data.danger_level == 1}} - Caution - {{??}} - Optimal - {{?}} -
        -
        -
        - - Area Status: - -
        - {{? data.atmos_alarm}} - Atmosphere Alarm - {{?? data.fire_alarm}} - Fire Alarm - {{??}} - Nominal - {{?}} -
        -
        - {{??}} -
        Warning: Cannot obtain air sample for analysis.
        - {{?}} - {{? data.dangerous}} -
        -
        - Warning: Safety measures offline. Device may exhibit abnormal behavior. -
        - {{?}} -
        - -## Contributing - -There are a few gotchas when it comes to writing for NanoUI. In order to -simplify server code and make the UI more responsive, we precompile all -templates to Javascript. In addition, Coffeescript and LESS are used to make -development easier, and also need to be precompiled. Precompiling CSS also -allows us to add fallbacks for old versions of Internet Explorer. - -To compile NanoUI (which you will need to do after adding or updating a -template), first install [Node.js](https://nodejs.org). - -Next, you will need to install packages used by NanoUI: - - cd nanoui/ - npm install -g gulp bower - npm install - bower install - -Finally, to compile NanoUI, run Gulp: - - gulp - -Every time you make an update, you will need to recompile. Before comitting, -make sure you minimize the files with Gulp: - - gulp --min - -If you would like to view your changes without restarting, run Gulp reload: - - gulp reload - -Finally, if you want to auto-compile and reload on save, run Gulp watch: - - gulp watch + + +- [NanoUI](#nanoui) + - [Introduction](#introduction) + - [Components](#components) + - [`ui_interact()`](#uiinteract) + - [`get_ui_data()`](#getuidata) + - [`Topic()`](#topic) + - [Template (doT)](#template-dot) + - [Helpers](#helpers) + - [Link](#link) + - [Bar](#bar) + - [doT](#dot) + - [Styling](#styling) + - [Contributing](#contributing) + + + +# NanoUI + +## Introduction + +NanoUI is the user interface library of /tg/station. While more complex than +traditional `browse()`/stringbuilder based interfaces, it allows much more +control over display of data and gives you many features for free, such as +different `CanUseTopic()` checks (in range/is robot/in inventory/in hand/etc), +automatic refresh, attractive looks, and helpers that make writing interfaces +much easier. + +NanoUI adds a `ui_interact()` proc to all atoms, which should be called from +`interact()`. The interact proc can be called from anywhere in the atom (usually + `attack_self()` or `attack_hand()`), and is where all checks should be made. + The `ui_interact()` proc should only include NanoUI code. + +Baystation12's version of NanoUI, while slightly different in syntax, has a +good [reference](http://wiki.baystation12.net/NanoUI). + +Here is a real example from +[tanks.dm](https://github.com/tgstation/-tg-station/blob/master/code/game/objects/items/weapons/tanks/tanks.dm). + + /obj/item/weapon/tank/attack_self(mob/user) + if (!user) + return + interact(user) + + /obj/item/weapon/tank/interact(mob/user) + add_fingerprint(user) + ui_interact(user) + + /obj/item/weapon/tank/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, force_open = 0) + ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open) + if (!ui) + ui = new(user, src, ui_key, "tanks", name, 525, 175, state = inventory_state) + ui.open() + + + + +## Components + +### `ui_interact()` + +The`ui_interact()` proc is used to open a NanoUI (or update it if already open). +As NanoUI will call this proc to update your UI, you should not put any logic in +it, as NanoUI handles the logic for you. + +The parameters for `try_update_ui` and `/datum/nanoui/new()` are documented in +the code [here](https://github.com/tgstation/-tg-station/tree/master/code/modules/nano). +The most interesting parameter is `state`, which allows the object to choose the +checks that allow the UI to be interacted with. + +The default state (`default_state`) checks that the user is alive, conscious, +and within a few tiles. It allows universal access to silicons. Other states +exist, and may be more appropriate for different interfaces. For example, +`physical_state` requires the user to be nearby, even if they are a silicon. +`inventory_state` checks that the user has the object in their first-level +(not container) inventory, this is suitable for devices such as radios; +`notcontained_state` checks that the user is outside the object (great for cryo +and similar machines). + + /obj/item/the/thing/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, force_open = 0) + ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open) + if (!ui) + ui = new(user, src, ui_key, "template", title, width, height) + ui.open() + +### `get_ui_data()` + +The `get_ui_data()` proc returns a list which is used to populate the `data` +variable in the UI. This is where you should pass variables from your atom to +the UI. Here's another example from tanks.dm. + + /obj/item/weapon/tank/get_ui_data() + var/mob/living/carbon/location = null + + if(istype(loc, /mob/living/carbon)) + location = loc + else if(istype(loc.loc, /mob/living/carbon)) + location = loc.loc + + var/data = list() + data["tankPressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0) + data["releasePressure"] = round(distribute_pressure ? distribute_pressure : 0) + data["defaultReleasePressure"] = round(TANK_DEFAULT_RELEASE_PRESSURE) + data["minReleasePressure"] = round(TANK_MIN_RELEASE_PRESSURE) + data["maxReleasePressure"] = round(TANK_MAX_RELEASE_PRESSURE) + data["valveOpen"] = 0 + data["maskConnected"] = 0 + + if(istype(location)) + var/mask_check = 0 + + if(location.internal == src) // if tank is current internal + mask_check = 1 + data["valveOpen"] = 1 + else if(src in location) // or if tank is in the mobs possession + if(!location.internal) // and they do not have any active internals + mask_check = 1 + + if(mask_check) + if(location.wear_mask && (location.wear_mask.flags & MASKINTERNALS)) + data["maskConnected"] = 1 + return data + +This data can be accessed inside the NanoUI. For example, to find out if the +mask is connected (as checked near the end of the proc), we simply use +`data.maskConnected` in our template. + +### `Topic()` + +`Topic()` handles input from the UI. Typically you will recieve some data from +a button press, or pop up a input dialog to take a numerical value from the +user. Sanity checking is useful here, as `Topic()` is trivial to spoof with +arbitrary data. + +The `Topic()` interface is just the same as with more conventional, +stringbuilder-based UIs, and this needs little explanation. + + /obj/item/weapon/tank/Topic(href, href_list) + if (..()) + return + + if (href_list["dist_p"]) + if (href_list["dist_p"] == "custom") + var/custom = input(usr, "What rate do you set the regulator to? The dial reads from 0 to [TANK_MAX_RELEASE_PRESSURE].") as null|num + if(isnum(custom)) + href_list["dist_p"] = custom + .() + else if (href_list["dist_p"] == "reset") + distribute_pressure = TANK_DEFAULT_RELEASE_PRESSURE + else if (href_list["dist_p"] == "min") + distribute_pressure = TANK_MIN_RELEASE_PRESSURE + else if (href_list["dist_p"] == "max") + distribute_pressure = TANK_MAX_RELEASE_PRESSURE + else + distribute_pressure = text2num(href_list["dist_p"]) + distribute_pressure = min(max(round(distribute_pressure), TANK_MIN_RELEASE_PRESSURE), TANK_MAX_RELEASE_PRESSURE) + if (href_list["stat"]) + if(istype(loc,/mob/living/carbon)) + var/mob/living/carbon/location = loc + if(location.internal == src) + location.internal = null + location.internals.icon_state = "internal0" + usr << "You close the tank release valve." + if (location.internals) + location.internals.icon_state = "internal0" + else + if(location.wear_mask && (location.wear_mask.flags & MASKINTERNALS)) + location.internal = src + usr << "You open \the [src] valve." + if (location.internals) + location.internals.icon_state = "internal1" + else + usr << "You need something to connect to \the [src]!" + +### Template (doT) + +NanoUI templates are written in [doT](https://olado.github.io/doT/index.html), +a Javascript template engine. Data is accessed from the `data` object, +configuration (not used in pratice) from the `config` object, and template +helpers are accessed from the `helper` object. + +#### Helpers + +##### Link + + {{=helpers.link(text, icon, {'parameter': true}, status, class)}} + +Used to create a link (button), which will pass its parameters to `Topic()`. + +* Text: The text content of the link/button +* Icon: The icon shown to the left of the link (http://fontawesome.io/) +* Parameters: The values to be passed to `Topic()`'s `href_list`. +* Status: `null` for clickable, a class for selected/unclickable. +* Class: Styling to apply to the link. + +Status and Class have almost the same effect. However, changing a link's status +from `null` to something else makes it unclickable, while setting a custom Class +does not. + +Ternary operators are often used to avoid writing many `if` statements. +For example, depending on if a value in `data` is true or false we can set a +button to clickable or selected: + + {{=helper.link('Close', 'lock', {'stat': 1}, data.valveOpen ? null : 'selected')}} + +Available classes/statuses are: + +* null (normal) +* selected +* caution +* danger +* disabled + +##### Bar + + {{=helpers.bar(value, min, max, class, text)}} + +Used to create a bar, to display a numerical value visually. Min and Max default +to 0 and 100, but you can change them to avoid doing your own percent calculations. + +* Value: Defaults to a percentage but can be a straight number if Min/Max are set +* Min: The minimum value (left hand side) of the bar +* Max: The maximum value (right hand side) of the bar +* Class: The color of the bar (null/normal, good, average, bad) +* Text: The text label for the data contained in the bar (often just number form) + +As with buttons, ternary operators are quite useful: + + {{=helper.bar(data.tankPressure, 0, 1013, (data.tankPressure > 200) ? 'good' : ((data.tankPressure > 100) ? 'average' : 'bad'))}} + +#### doT + +doT is a simple template language, with control statements mixed in with +regular HTML and interpolation expressions. + +Here is a simple example from tanks, checking if a variable is true: + + {{? data.maskConnected}} + The regulator is connected to a mask. + {{??}} + The regulator is not connected to a mask. + {{?}} + +The doT tutorial is [here](https://olado.github.io/doT/tutorial.html). + +Print: + + {{=expression }} + +Print (with escape): + + {{!expression }} + +If/Else If/Else + + {{? condition}} + // if + {{?? condition}} + // else if + {{??}} + // else + {{?}} + +For + + {{~ object:key:index}} + // key, value + {{~}} + +#### Styling + +For the most part, a NanoUI is just normal HTML. However, to use the NanoUI +styles correctly, you have to be concious of a few elements. + +A `
        ` is the building block of most NanoUIs, and +represents the wells/blocks you see in most NanoUIs. Inside said article should +be a `
        ` labeling it, and many `
        `s representing items inside +(such as a label/button pair). The styling is highly subjective, so ask a +regular contribuitor to NanoUI (@neersighted at time of writing) to take a look +at and help style your UI. + +Here's an example of UI styling from Air Alarms: + +
        +

        Air Status

        + {{? data.environment_data}} + {{~ data.environment_data:info:i}} +
        + + {{=info.name}}: + +
        + {{? info.danger_level == 2}} + + {{?? info.danger_level == 1}} + + {{??}} + + {{?}} + {{=helper.fixed(info.value, 2)}}{{=info.unit}} +
        +
        + {{~}} +
        + + Local Status: + +
        + {{? data.danger_level == 2}} + Danger (Internals Required) + {{?? data.danger_level == 1}} + Caution + {{??}} + Optimal + {{?}} +
        +
        +
        + + Area Status: + +
        + {{? data.atmos_alarm}} + Atmosphere Alarm + {{?? data.fire_alarm}} + Fire Alarm + {{??}} + Nominal + {{?}} +
        +
        + {{??}} +
        Warning: Cannot obtain air sample for analysis.
        + {{?}} + {{? data.dangerous}} +
        +
        + Warning: Safety measures offline. Device may exhibit abnormal behavior. +
        + {{?}} +
        + +## Contributing + +There are a few gotchas when it comes to writing for NanoUI. In order to +simplify server code and make the UI more responsive, we precompile all +templates to Javascript. In addition, Coffeescript and LESS are used to make +development easier, and also need to be precompiled. Precompiling CSS also +allows us to add fallbacks for old versions of Internet Explorer. + +To compile NanoUI (which you will need to do after adding or updating a +template), first install [Node.js](https://nodejs.org). + +Next, you will need to install packages used by NanoUI: + + cd nanoui/ + npm install -g gulp bower + npm install + bower install + +Finally, to compile NanoUI, run Gulp: + + gulp + +Every time you make an update, you will need to recompile. Before comitting, +make sure you minimize the files with Gulp: + + gulp --min + +If you would like to view your changes without restarting, run Gulp reload: + + gulp reload + +Finally, if you want to auto-compile and reload on save, run Gulp watch: + + gulp watch diff --git a/nano/images/nanotrasen.svg b/nano/images/nanotrasen.svg index 13ff17a8d8e..d93b01a9545 100644 --- a/nano/images/nanotrasen.svg +++ b/nano/images/nanotrasen.svg @@ -1,6 +1,6 @@ - - - - - - + + + + + + diff --git a/nano/reload.bat b/nano/reload.bat index 2fe4104eb07..a330006173f 100644 --- a/nano/reload.bat +++ b/nano/reload.bat @@ -1,9 +1,9 @@ -@echo off - -for /f "tokens=3* delims= " %%a in ( - 'reg query "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" /v "Personal"' -) do ( - set documents=%%a -) - -copy assets\* "%documents%\BYOND\cache" /y +@echo off + +for /f "tokens=3* delims= " %%a in ( + 'reg query "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" /v "Personal"' +) do ( + set documents=%%a +) + +copy assets\* "%documents%\BYOND\cache" /y diff --git a/nano/scripts/constants.coffee b/nano/scripts/constants.coffee index c9a6532a409..10635ae11ad 100644 --- a/nano/scripts/constants.coffee +++ b/nano/scripts/constants.coffee @@ -1,4 +1,4 @@ -@NANO = - INTERACTIVE: 2 - UPDATE: 1 - DISABLED: 0 +@NANO = + INTERACTIVE: 2 + UPDATE: 1 + DISABLED: 0 diff --git a/nano/scripts/helpers.coffee b/nano/scripts/helpers.coffee index 7eab38240f2..77b20403406 100644 --- a/nano/scripts/helpers.coffee +++ b/nano/scripts/helpers.coffee @@ -1,43 +1,43 @@ -@helpers = - link: (text = "", icon = "", action = "", params = {}, status = "", klass = "") -> - params = JSON.stringify params - - if !!icon - icon = "" - klass += " iconed" - - if !!status - "#{icon}#{text}" - else - "#{icon}#{text}" - - bar: (value = 0, min = 0, max = 100, klass = "", text = "") -> - if min < max - if value < min - value = min - else if value > max - value = max - else - if value > min - value = min - else if value < max - value = max - - percentage = Math.round((value - min) / (max - min) * 100) - - "
        \ - \ - #{text} \ -
        " - - round: (number) -> - Math.round number - - fixed: (number, decimals = 1) -> - Number Math.round(number + "e" + decimals) + "e-" + decimals - - floor: (number) -> - Math.floor number - - ceil: (number) -> - Math.ceil number +@helpers = + link: (text = "", icon = "", action = "", params = {}, status = "", klass = "") -> + params = JSON.stringify params + + if !!icon + icon = "" + klass += " iconed" + + if !!status + "#{icon}#{text}" + else + "#{icon}#{text}" + + bar: (value = 0, min = 0, max = 100, klass = "", text = "") -> + if min < max + if value < min + value = min + else if value > max + value = max + else + if value > min + value = min + else if value < max + value = max + + percentage = Math.round((value - min) / (max - min) * 100) + + "
        \ + \ + #{text} \ +
        " + + round: (number) -> + Math.round number + + fixed: (number, decimals = 1) -> + Number Math.round(number + "e" + decimals) + "e-" + decimals + + floor: (number) -> + Math.floor number + + ceil: (number) -> + Math.ceil number diff --git a/nano/scripts/nanoui.coffee b/nano/scripts/nanoui.coffee index 5fb4e2cd426..104a7c8d003 100644 --- a/nano/scripts/nanoui.coffee +++ b/nano/scripts/nanoui.coffee @@ -1,88 +1,88 @@ -class @NanoUI - constructor: (@bus, @fragment = document) -> - @bus.on "serverUpdate", @serverUpdate - @bus.on "update", @update - @bus.on "render", @render - @bus.on "memes", @render - - @initialized = false - - @data = {} - @initialData = JSON.parse @fragment.query("#data").data "initial" - - unless @initialData? or - not ("data" of @initialData or "config" of @initalData) - @error "Initial data did not load correctly." - - serverUpdate: (dataString) => - try - data = JSON.parse(dataString) - catch error - @error error - - @bus.emit "update", data - return - - update: (data) => - unless data.data? - if @data.data? - data.data = @data.data - else - data.data = {} - - @data = data - - @bus.emit "render", @data if @initialized - @bus.emit "updated" - return - - render: (data) => - data = @initialData unless @initialized - - try - if not @initialized - layout = @fragment.query("#layout") - layout.innerHTML = TMPL[data.config.templates.layout]\ - (data.data, data.config, helpers) - - content = @fragment.query("#content") - content.innerHTML = TMPL[data.config.templates.content]\ - (data.data, data.config, helpers) - - catch error - @error error - return - - @bus.emit "rendered", data - if not @initialized - @initialized = true - @data = @initialData - @bus.emit "initialized", data - - act: (action, params = {}) => - params.src = @data.config.ref - params.nano = action - location.href = util.href null, params - - error: (error) -> - error = "#{error.fileName}:#{error.lineNumber} #{error.message}" if error instanceof Error - params = - nano_error: error - location.href = util.href null, params - - log: (message) -> - params = - nano_log: message - location.href = util.href null, params - - close: => - params = - command: "nanoclose #{@data.config.ref}" - @winset "is-visible", "false" - location.href = util.href "winset", params - - winset: (key, value, window) => - window = @data.config.window.ref unless window? - params = - "#{window}.#{key}": value - location.href = util.href "winset", params +class @NanoUI + constructor: (@bus, @fragment = document) -> + @bus.on "serverUpdate", @serverUpdate + @bus.on "update", @update + @bus.on "render", @render + @bus.on "memes", @render + + @initialized = false + + @data = {} + @initialData = JSON.parse @fragment.query("#data").data "initial" + + unless @initialData? or + not ("data" of @initialData or "config" of @initalData) + @error "Initial data did not load correctly." + + serverUpdate: (dataString) => + try + data = JSON.parse(dataString) + catch error + @error error + + @bus.emit "update", data + return + + update: (data) => + unless data.data? + if @data.data? + data.data = @data.data + else + data.data = {} + + @data = data + + @bus.emit "render", @data if @initialized + @bus.emit "updated" + return + + render: (data) => + data = @initialData unless @initialized + + try + if not @initialized + layout = @fragment.query("#layout") + layout.innerHTML = TMPL[data.config.templates.layout]\ + (data.data, data.config, helpers) + + content = @fragment.query("#content") + content.innerHTML = TMPL[data.config.templates.content]\ + (data.data, data.config, helpers) + + catch error + @error error + return + + @bus.emit "rendered", data + if not @initialized + @initialized = true + @data = @initialData + @bus.emit "initialized", data + + act: (action, params = {}) => + params.src = @data.config.ref + params.nano = action + location.href = util.href null, params + + error: (error) -> + error = "#{error.fileName}:#{error.lineNumber} #{error.message}" if error instanceof Error + params = + nano_error: error + location.href = util.href null, params + + log: (message) -> + params = + nano_log: message + location.href = util.href null, params + + close: => + params = + command: "nanoclose #{@data.config.ref}" + @winset "is-visible", "false" + location.href = util.href "winset", params + + winset: (key, value, window) => + window = @data.config.window.ref unless window? + params = + "#{window}.#{key}": value + location.href = util.href "winset", params diff --git a/nano/scripts/util.coffee b/nano/scripts/util.coffee index 777490d93f2..353972163f4 100644 --- a/nano/scripts/util.coffee +++ b/nano/scripts/util.coffee @@ -1,15 +1,15 @@ -@util = - extend: (first, second) -> - Object.keys(second).forEach (key) -> - secondVal = second[key] - if secondVal and Object::toString.call(secondVal) is "[object Object]" - first[key] = first[key] or {} - util.extend first[key], secondVal - else - first[key] = secondVal - first - - href: (url = "", params = {}) -> - url = new Url("byond://#{url}") - util.extend url.query, params - url +@util = + extend: (first, second) -> + Object.keys(second).forEach (key) -> + secondVal = second[key] + if secondVal and Object::toString.call(secondVal) is "[object Object]" + first[key] = first[key] or {} + util.extend first[key], secondVal + else + first[key] = secondVal + first + + href: (url = "", params = {}) -> + url = new Url("byond://#{url}") + util.extend url.query, params + url diff --git a/nano/scripts/window.coffee b/nano/scripts/window.coffee index 6c173b2cac9..e7b542b7650 100644 --- a/nano/scripts/window.coffee +++ b/nano/scripts/window.coffee @@ -1,120 +1,120 @@ -class @Window - constructor: (@bus, @fragment = document) -> - @dragging = false - @resizing = false - - @bus.once "initialized", (data) => - setTimeout @focusMap, 100 # Return focus once we're set up. - return unless data.config.user.fancy # Bail here if we're not to go chromeless. - @fancyChrome() - @calcOffset() - @attachButtons() - @attachDrag() - @attachResize() - - @bus.on "rendered", @updateStatus - @bus.on "rendered", @updateLinks - @bus.on "rendered", @attachLinks - @fragment.on "keydown", @focusMap # If we get input, return focus. - - setPos: (x, y) -> - nanoui.winset "pos", "#{x},#{y}" - - setSize: (w, h) -> - nanoui.winset "size", "#{w},#{h}" - - focusMap: -> - nanoui.winset "focus", 1, "mapwindow.map" - - fancyChrome: => - nanoui.winset "titlebar", 0 - nanoui.winset "can-resize", 0 - fancy = @fragment.queryAll ".fancy" - fancy.forEach (element) -> - element.style.display = 'inherit' - - calcOffset: => - @xOriginal = window.screenLeft - @yOriginal = window.screenTop - @setPos 0, 0 # Move to 0,0 to measure offsets. - @xOffset = window.screenLeft - @yOffset = window.screenTop - @setPos @xOriginal - @xOffset, @yOriginal - @yOffset # Put the window back. - - attachButtons: => - close = -> nanoui.close() - minimize = -> nanoui.winset "is-minimized", "true" - - closers = @fragment.queryAll ".close" - closers.forEach (closer) -> - closer.on "click", close - minimizers = @fragment.queryAll ".minimize" - minimizers.forEach (minimizer) -> - minimizer.on "click", minimize - - attachDrag: => - titlebar = @fragment.query "#titlebar" - @fragment.on "mousemove", @drag - titlebar.on "mousedown", => @dragging = true - @fragment.on "mouseup", => @dragging = false - - drag: (event = window.event) => - return unless @dragging - - @xDrag = event.screenX unless @xDrag? - @yDrag = event.screenY unless @yDrag? - - x = (event.screenX - @xDrag) + (window.screenLeft - @xOffset) - y = (event.screenY - @yDrag) + (window.screenTop - @yOffset) - @setPos x, y - - @xDrag = event.screenX - @yDrag = event.screenY - - attachResize: => - handle = @fragment.query "#resize" - @fragment.on "mousemove", @resize - handle.on "mousedown", => @resizing = true - @fragment.on "mouseup", => @resizing = false - - resize: (event = window.event) => - return unless @resizing - - @xResize = event.screenX unless @xResize? - @yResize = event.screenY unless @yResize? - - x = (event.screenX - @xResize) + window.innerWidth - y = (event.screenY - @yResize) + window.innerHeight - @setSize x, y - - @xResize = event.screenX - @yResize = event.screenY - - updateStatus: (data) => - statusicons = @fragment.queryAll ".statusicon" - statusicons.forEach (statusicon) -> - statusicon.className = statusicon.className.replace /good|bad|average/g, "" - switch data.config.status - when NANO.INTERACTIVE - klass = "good" - when NANO.UPDATE - klass = "average" - else - klass = "bad" - statusicon.classList.add klass - - updateLinks: (data) => - links = @fragment.queryAll ".link" - if data.config.status isnt NANO.INTERACTIVE - links.forEach (element) -> - element.className = "link disabled" - - attachLinks: (data) => - onClick = -> - action = @data "action" - params = JSON.parse @data "params" - if action? and params? and data.config.status is NANO.INTERACTIVE - nanoui.act action, params - - @fragment.queryAll(".link.active").forEach (link) -> - link.on "click", onClick +class @Window + constructor: (@bus, @fragment = document) -> + @dragging = false + @resizing = false + + @bus.once "initialized", (data) => + setTimeout @focusMap, 100 # Return focus once we're set up. + return unless data.config.user.fancy # Bail here if we're not to go chromeless. + @fancyChrome() + @calcOffset() + @attachButtons() + @attachDrag() + @attachResize() + + @bus.on "rendered", @updateStatus + @bus.on "rendered", @updateLinks + @bus.on "rendered", @attachLinks + @fragment.on "keydown", @focusMap # If we get input, return focus. + + setPos: (x, y) -> + nanoui.winset "pos", "#{x},#{y}" + + setSize: (w, h) -> + nanoui.winset "size", "#{w},#{h}" + + focusMap: -> + nanoui.winset "focus", 1, "mapwindow.map" + + fancyChrome: => + nanoui.winset "titlebar", 0 + nanoui.winset "can-resize", 0 + fancy = @fragment.queryAll ".fancy" + fancy.forEach (element) -> + element.style.display = 'inherit' + + calcOffset: => + @xOriginal = window.screenLeft + @yOriginal = window.screenTop + @setPos 0, 0 # Move to 0,0 to measure offsets. + @xOffset = window.screenLeft + @yOffset = window.screenTop + @setPos @xOriginal - @xOffset, @yOriginal - @yOffset # Put the window back. + + attachButtons: => + close = -> nanoui.close() + minimize = -> nanoui.winset "is-minimized", "true" + + closers = @fragment.queryAll ".close" + closers.forEach (closer) -> + closer.on "click", close + minimizers = @fragment.queryAll ".minimize" + minimizers.forEach (minimizer) -> + minimizer.on "click", minimize + + attachDrag: => + titlebar = @fragment.query "#titlebar" + @fragment.on "mousemove", @drag + titlebar.on "mousedown", => @dragging = true + @fragment.on "mouseup", => @dragging = false + + drag: (event = window.event) => + return unless @dragging + + @xDrag = event.screenX unless @xDrag? + @yDrag = event.screenY unless @yDrag? + + x = (event.screenX - @xDrag) + (window.screenLeft - @xOffset) + y = (event.screenY - @yDrag) + (window.screenTop - @yOffset) + @setPos x, y + + @xDrag = event.screenX + @yDrag = event.screenY + + attachResize: => + handle = @fragment.query "#resize" + @fragment.on "mousemove", @resize + handle.on "mousedown", => @resizing = true + @fragment.on "mouseup", => @resizing = false + + resize: (event = window.event) => + return unless @resizing + + @xResize = event.screenX unless @xResize? + @yResize = event.screenY unless @yResize? + + x = (event.screenX - @xResize) + window.innerWidth + y = (event.screenY - @yResize) + window.innerHeight + @setSize x, y + + @xResize = event.screenX + @yResize = event.screenY + + updateStatus: (data) => + statusicons = @fragment.queryAll ".statusicon" + statusicons.forEach (statusicon) -> + statusicon.className = statusicon.className.replace /good|bad|average/g, "" + switch data.config.status + when NANO.INTERACTIVE + klass = "good" + when NANO.UPDATE + klass = "average" + else + klass = "bad" + statusicon.classList.add klass + + updateLinks: (data) => + links = @fragment.queryAll ".link" + if data.config.status isnt NANO.INTERACTIVE + links.forEach (element) -> + element.className = "link disabled" + + attachLinks: (data) => + onClick = -> + action = @data "action" + params = JSON.parse @data "params" + if action? and params? and data.config.status is NANO.INTERACTIVE + nanoui.act action, params + + @fragment.queryAll(".link.active").forEach (link) -> + link.on "click", onClick diff --git a/nano/styles/_bar.less b/nano/styles/_bar.less index 50e074f1b6e..30e62807be4 100644 --- a/nano/styles/_bar.less +++ b/nano/styles/_bar.less @@ -1,48 +1,48 @@ -@import "_config"; -@import "_util"; - - -.bar { - display: inline-block; - position: relative; - top: 4px; // Because this is an empty div, hack it so that it lines up. - vertical-align: top; - box-sizing: border-box; - width: 100%; - height: 20px; // Buttons are 22px, but look 20px because of their dark borders. - margin: 1px; - padding: 1px; - - border: 1px solid @normal; - - background: @dark; - - .barText { - position: absolute; - top: 1px; - right: 3px; - - .fontReset; - } - - .barFill { - display: block; - height: 100%; - - overflow: hidden; - - background: @normal; - &.good { - background: @good; - } - &.average { - background: @average; - } - &.bad { - background: @bad; - } - &.highlight { - background: @highlight; - } - } -} +@import "_config"; +@import "_util"; + + +.bar { + display: inline-block; + position: relative; + top: 4px; // Because this is an empty div, hack it so that it lines up. + vertical-align: top; + box-sizing: border-box; + width: 100%; + height: 20px; // Buttons are 22px, but look 20px because of their dark borders. + margin: 1px; + padding: 1px; + + border: 1px solid @normal; + + background: @dark; + + .barText { + position: absolute; + top: 1px; + right: 3px; + + .fontReset; + } + + .barFill { + display: block; + height: 100%; + + overflow: hidden; + + background: @normal; + &.good { + background: @good; + } + &.average { + background: @average; + } + &.bad { + background: @bad; + } + &.highlight { + background: @highlight; + } + } +} diff --git a/nano/styles/_config.less b/nano/styles/_config.less index 3a0585d4989..da58aa148e7 100644 --- a/nano/styles/_config.less +++ b/nano/styles/_config.less @@ -1,35 +1,35 @@ -// Fonts -@font: Verdana, Geneva, sans-serif; -@fontsize: 12px; - -// Global Colors -@normal: #40628a; -@good: #537d29; -@average: #be6209; -@bad: #b00e0e; -@highlight: #8ba5c4; -@info: #e9C183; -@label: @highlight; -@dark: #272727; - -// Body Colors -@title: #98b0c3; -@text: white; -@textinverse: black; -@titlebar: #363636; -@background-start: #2a2a2a; -@background-end: #202020; -@resize: #363636; - -// Component Colors -@border: @dark; -@rule: @normal; -@notice1: #bb9b68; -@notice2: #b1905d; - -// Link Colors -@hover: 10%; -@selected: #2f943c; -@caution: #9a9d00; -@danger: #9d0808; -@disabled: #999999; +// Fonts +@font: Verdana, Geneva, sans-serif; +@fontsize: 12px; + +// Global Colors +@normal: #40628a; +@good: #537d29; +@average: #be6209; +@bad: #b00e0e; +@highlight: #8ba5c4; +@info: #e9C183; +@label: @highlight; +@dark: #272727; + +// Body Colors +@title: #98b0c3; +@text: white; +@textinverse: black; +@titlebar: #363636; +@background-start: #2a2a2a; +@background-end: #202020; +@resize: #363636; + +// Component Colors +@border: @dark; +@rule: @normal; +@notice1: #bb9b68; +@notice2: #b1905d; + +// Link Colors +@hover: 10%; +@selected: #2f943c; +@caution: #9a9d00; +@danger: #9d0808; +@disabled: #999999; diff --git a/nano/styles/_document.less b/nano/styles/_document.less index 19c0e8cf4e2..c291b73fef7 100644 --- a/nano/styles/_document.less +++ b/nano/styles/_document.less @@ -1,36 +1,36 @@ -@import "_config"; -@import "_util"; - - -html { - min-height: 100%; - - cursor: default; // Reset the cursor. -} - -body { - min-height: 100%; - - font-family: @font; - font-size: @fontsize; - color: @text; -} - -h1 { - font-size: @fontsize + 6px; - margin: 0; - padding: 6px 0; -} -h2 { - &:extend(h1); - font-size: @fontsize + 4px; -} -h3 { - &:extend(h1); - font-size: @fontsize + 2px; -} - -hr { - background-color: @rule; - border: none; -} +@import "_config"; +@import "_util"; + + +html { + min-height: 100%; + + cursor: default; // Reset the cursor. +} + +body { + min-height: 100%; + + font-family: @font; + font-size: @fontsize; + color: @text; +} + +h1 { + font-size: @fontsize + 6px; + margin: 0; + padding: 6px 0; +} +h2 { + &:extend(h1); + font-size: @fontsize + 4px; +} +h3 { + &:extend(h1); + font-size: @fontsize + 2px; +} + +hr { + background-color: @rule; + border: none; +} diff --git a/nano/styles/_interface.less b/nano/styles/_interface.less index 6f771590332..b36eae5ec9e 100644 --- a/nano/styles/_interface.less +++ b/nano/styles/_interface.less @@ -1,94 +1,94 @@ -@import "_config"; -@import "_util"; - - -article.display { - display: flex; - flex-flow: column nowrap; - width: auto; - padding: 4px; - margin: 6px 2px; - - background-color: rgba(0, 0, 0, 0.33); // Transparent background. - box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.5); - - header { - padding-left: 4px; - margin-bottom: 6px; - - border-bottom: 2px solid @rule; - } - - section { - width: 100%; - padding: 3px 0; - } - - .cell() { - display: inline-block; - margin: 0; - } - - .label { - .cell; - width: 33%; - - color: @label; - } - - .content { - .cell; - width: 66%; - } - - .line { - .cell; - width: 100%; - } - - .buttoninfo { - margin-left: 5px; - } - - table { - width: auto; - - border-collapse: collapse; - } - - table.grow { - width: 100%; - } - - th { - vertical-align: top; - padding: 4px 16px 4px 0; - text-align: left; - - .fontReset; - color: @label; - } - - td { - &:extend(th); - padding: 2px 2px 0 0; - vertical-align: top; - } -} - -article.notice { - &:extend(article.display all); - margin: 8px 2px; - - box-shadow: none; - - color: @textinverse; - font-weight: bold; - font-style: italic; - - .label { - color: @textinverse; - } - - .noticeStripes; -} +@import "_config"; +@import "_util"; + + +article.display { + display: flex; + flex-flow: column nowrap; + width: auto; + padding: 4px; + margin: 6px 2px; + + background-color: rgba(0, 0, 0, 0.33); // Transparent background. + box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.5); + + header { + padding-left: 4px; + margin-bottom: 6px; + + border-bottom: 2px solid @rule; + } + + section { + width: 100%; + padding: 3px 0; + } + + .cell() { + display: inline-block; + margin: 0; + } + + .label { + .cell; + width: 33%; + + color: @label; + } + + .content { + .cell; + width: 66%; + } + + .line { + .cell; + width: 100%; + } + + .buttoninfo { + margin-left: 5px; + } + + table { + width: auto; + + border-collapse: collapse; + } + + table.grow { + width: 100%; + } + + th { + vertical-align: top; + padding: 4px 16px 4px 0; + text-align: left; + + .fontReset; + color: @label; + } + + td { + &:extend(th); + padding: 2px 2px 0 0; + vertical-align: top; + } +} + +article.notice { + &:extend(article.display all); + margin: 8px 2px; + + box-shadow: none; + + color: @textinverse; + font-weight: bold; + font-style: italic; + + .label { + color: @textinverse; + } + + .noticeStripes; +} diff --git a/nano/styles/_link.less b/nano/styles/_link.less index 12d0fe6cf35..e179f3b684a 100644 --- a/nano/styles/_link.less +++ b/nano/styles/_link.less @@ -1,49 +1,49 @@ -@import "_config"; -@import "_util"; - - -.active(@color) { - background-color: @color; - &:hover { - background-color: lighten(@color, @hover); - } -} - - -// Basic Link (Button) Element -span.link { - display: inline-block; - box-sizing: border-box; - height: 22px; - margin: 1px; - padding: 2px 3px; - white-space: nowrap; - - border: 1px solid @border; - - .fontReset; // Reset font settings. - - // Tweak spacing to fit icons. - .fa { margin-right: 3px; } - .iconed { padding-left: 3px; } - - // Link colors: - background-color: @normal; - &.active { - .active(@normal); - &.selected { .active(@selected); } - &.caution { .active(@caution); } - &.danger { .active(@danger); } - } - &.inactive { - background-color: @disabled; - &.selected { background-color: @selected; } - &.caution { background-color: @caution; } - &.danger { background-color: @danger; } - } - &.disabled { background-color: @disabled; } - - &.gridable { - width: 125px; - } -} +@import "_config"; +@import "_util"; + + +.active(@color) { + background-color: @color; + &:hover { + background-color: lighten(@color, @hover); + } +} + + +// Basic Link (Button) Element +span.link { + display: inline-block; + box-sizing: border-box; + height: 22px; + margin: 1px; + padding: 2px 3px; + white-space: nowrap; + + border: 1px solid @border; + + .fontReset; // Reset font settings. + + // Tweak spacing to fit icons. + .fa { margin-right: 3px; } + .iconed { padding-left: 3px; } + + // Link colors: + background-color: @normal; + &.active { + .active(@normal); + &.selected { .active(@selected); } + &.caution { .active(@caution); } + &.danger { .active(@danger); } + } + &.inactive { + background-color: @disabled; + &.selected { background-color: @selected; } + &.caution { background-color: @caution; } + &.danger { background-color: @danger; } + } + &.disabled { background-color: @disabled; } + + &.gridable { + width: 125px; + } +} diff --git a/nano/styles/_util.less b/nano/styles/_util.less index bca0e8155b4..b51538414cb 100644 --- a/nano/styles/_util.less +++ b/nano/styles/_util.less @@ -1,57 +1,57 @@ -@import "_config"; - - -// Colors -.color(@color) { // Explort a given color as a class. - &.@{color} { - color: @@color; - } -} - -.color(normal); -.color(good); -.color(average); -.color(bad); -.color(label); -.color(highlight); -.color(dark); - -// Text Styles -.bold { font-weight: bold; } -.italic { font-style: italic; } -.fontReset { - color: @text; - font-size: @fontsize; - font-weight: normal; - font-style: normal; - text-decoration: none; -} - -// Fancy "Notice Me" Stripes -.noticeStripes { - background-color: @notice1; // Fallback for old browers. - background-image: repeating-linear-gradient( - -45deg, - @notice1, - @notice1 10px, - @notice2 10px, - @notice2 20px - ); -} - -// General Helpers -.hidden { display: none; } - -.clearBoth { clear: both; } -.clearLeft { clear: left; } -.clearRight { clear: right; } - -.floatNone { float: none; } -.floatLeft { float: left; } -.floatRight { float: right; } - -.alignTop { vertical-align: top; } -.alignBottom { vertical-align: bottom; } -.alignLeft { text-align: left; } -.alignCenter { text-align: center; } -.alignRight { text-align: right; } +@import "_config"; + + +// Colors +.color(@color) { // Explort a given color as a class. + &.@{color} { + color: @@color; + } +} + +.color(normal); +.color(good); +.color(average); +.color(bad); +.color(label); +.color(highlight); +.color(dark); + +// Text Styles +.bold { font-weight: bold; } +.italic { font-style: italic; } +.fontReset { + color: @text; + font-size: @fontsize; + font-weight: normal; + font-style: normal; + text-decoration: none; +} + +// Fancy "Notice Me" Stripes +.noticeStripes { + background-color: @notice1; // Fallback for old browers. + background-image: repeating-linear-gradient( + -45deg, + @notice1, + @notice1 10px, + @notice2 10px, + @notice2 20px + ); +} + +// General Helpers +.hidden { display: none; } + +.clearBoth { clear: both; } +.clearLeft { clear: left; } +.clearRight { clear: right; } + +.floatNone { float: none; } +.floatLeft { float: left; } +.floatRight { float: right; } + +.alignTop { vertical-align: top; } +.alignBottom { vertical-align: bottom; } +.alignLeft { text-align: left; } +.alignCenter { text-align: center; } +.alignRight { text-align: right; } diff --git a/nano/styles/common.less b/nano/styles/common.less index 5bb5a44fe7c..076271f06a3 100644 --- a/nano/styles/common.less +++ b/nano/styles/common.less @@ -1,86 +1,86 @@ -@import "_config"; -@import "_document"; -@import "_util"; -@import "_interface"; -@import "_link"; -@import "_bar"; - - -.fancy { - display: none; -} - - -div.loading { - .noticeStripes; - - padding: 8px; -} - -div.content { - margin: 32px 4px 0 4px; - &.titlebared { - padding-top: 3px; - } -} - -header.titlebar { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 32px; - - background-color: @titlebar; - box-shadow: 0 3px 3px rgba(0, 0, 0, 0.1); - - .statusicon { - position: absolute; - top: 4px; - left: 12px; - } - - .title { - position: absolute; - top: 6px; - left: 46px; - - color: @title; - font-size: 16px; - white-space: nowrap; - } - - .titlebutton() { - color: @title; - - &:hover { - color: lighten(@title, @hover); - } - } - .minimize { - .titlebutton; - position: absolute; - top: 6px; - right: 46px; - } - .close { - .titlebutton; - position: absolute; - top: 4px; - right: 12px; - } -} - -div.resize { - position: fixed; - bottom: 0; - right: 0; - width: 0; - height: 0; - - border-style: solid; - border-width: 0 0 30px 30px; - border-color: transparent transparent @resize transparent; - - transform: rotate(360deg); -} +@import "_config"; +@import "_document"; +@import "_util"; +@import "_interface"; +@import "_link"; +@import "_bar"; + + +.fancy { + display: none; +} + + +div.loading { + .noticeStripes; + + padding: 8px; +} + +div.content { + margin: 32px 4px 0 4px; + &.titlebared { + padding-top: 3px; + } +} + +header.titlebar { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 32px; + + background-color: @titlebar; + box-shadow: 0 3px 3px rgba(0, 0, 0, 0.1); + + .statusicon { + position: absolute; + top: 4px; + left: 12px; + } + + .title { + position: absolute; + top: 6px; + left: 46px; + + color: @title; + font-size: 16px; + white-space: nowrap; + } + + .titlebutton() { + color: @title; + + &:hover { + color: lighten(@title, @hover); + } + } + .minimize { + .titlebutton; + position: absolute; + top: 6px; + right: 46px; + } + .close { + .titlebutton; + position: absolute; + top: 4px; + right: 12px; + } +} + +div.resize { + position: fixed; + bottom: 0; + right: 0; + width: 0; + height: 0; + + border-style: solid; + border-width: 0 0 30px 30px; + border-color: transparent transparent @resize transparent; + + transform: rotate(360deg); +} diff --git a/nano/styles/generic.less b/nano/styles/generic.less index 8f9f725f50c..1f8edf7ce30 100644 --- a/nano/styles/generic.less +++ b/nano/styles/generic.less @@ -1,8 +1,8 @@ -@import "_config"; - - -body { - background: linear-gradient(to bottom, - @background-start 0%, - @background-end 100%); -} +@import "_config"; + + +body { + background: linear-gradient(to bottom, + @background-start 0%, + @background-end 100%); +} diff --git a/nano/styles/nanotrasen.less b/nano/styles/nanotrasen.less index 3ae88d052e3..6bf13910d9a 100644 --- a/nano/styles/nanotrasen.less +++ b/nano/styles/nanotrasen.less @@ -1,9 +1,9 @@ -@import "_config"; - - -body { - background: data-uri('nanotrasen.svg') no-repeat fixed center/70% 70%, - linear-gradient(to bottom, - @background-start 0%, - @background-end 100%) no-repeat fixed center/100% 100%; -} +@import "_config"; + + +body { + background: data-uri('nanotrasen.svg') no-repeat fixed center/70% 70%, + linear-gradient(to bottom, + @background-start 0%, + @background-end 100%) no-repeat fixed center/100% 100%; +} diff --git a/nano/templates/_generic.dot b/nano/templates/_generic.dot index d8a52576f66..25d34906376 100644 --- a/nano/templates/_generic.dot +++ b/nano/templates/_generic.dot @@ -1,14 +1,14 @@ -
        -
        - - {{=config.title}} - - -
        - -
        -
        Initiating...
        -
        - -
        -
        +
        +
        + + {{=config.title}} + + +
        + +
        +
        Initiating...
        +
        + +
        +
        diff --git a/nano/templates/_nanotrasen.dot b/nano/templates/_nanotrasen.dot index d8a52576f66..25d34906376 100644 --- a/nano/templates/_nanotrasen.dot +++ b/nano/templates/_nanotrasen.dot @@ -1,14 +1,14 @@ -
        -
        - - {{=config.title}} - - -
        - -
        -
        Initiating...
        -
        - -
        -
        +
        +
        + + {{=config.title}} + + +
        + +
        +
        Initiating...
        +
        + +
        +
        diff --git a/nano/templates/air_alarm.dot b/nano/templates/air_alarm.dot index 69f9a9cdf6f..a80208384c3 100644 --- a/nano/templates/air_alarm.dot +++ b/nano/templates/air_alarm.dot @@ -1,233 +1,233 @@ -
        - {{? data.siliconUser}} -
        - Interface Lock: -
        - {{=helper.link('Engaged', 'lock', 'toggleaccess', null, data.locked ? 'selected' : null)}} - {{=helper.link('Disengaged', 'unlock', 'toggleaccess', null, data.locked ? null : 'selected')}} -
        -
        - {{??}} - {{? data.locked}} - Swipe an ID card to unlock this interface. - {{??}} - Swipe an ID card to lock this interface. - {{?}} - {{?}} -
        -
        -

        Air Status

        - {{? data.environment_data}} - {{~ data.environment_data:info:i}} -
        - {{=info.name}}: -
        - {{? info.danger_level == 2}} - - {{?? info.danger_level == 1}} - - {{??}} - - {{?}} - {{=helper.fixed(info.value, 2)}}{{=info.unit}} -
        -
        - {{~}} -
        - Local Status: -
        - {{? data.danger_level == 2}} - Danger (Internals Required) - {{?? data.danger_level == 1}} - Caution - {{??}} - Optimal - {{?}} -
        -
        -
        - Area Status: -
        - {{? data.atmos_alarm}} - Atmosphere Alarm - {{?? data.fire_alarm}} - Fire Alarm - {{??}} - Nominal - {{?}} -
        -
        - {{??}} -
        - Warning: Cannot obtain air sample for analysis. -
        - {{?}} - {{? data.dangerous}} -
        -
        - Warning: Safety measures offline. Device may exhibit abnormal behavior. -
        - {{?}} -
        -{{? (!data.locked || data.siliconUser)}} - {{? data.screen != 1}} - {{=helper.link('Back', 'arrow-left', 'screen', {'screen': 1})}} - {{?}} - {{? data.screen == 1}} -
        -

        Air Controls

        -
        - {{? !data.atmos_alarm}} - {{=helper.link('Area Atmospheric Alarm', 'hand-stop-o', 'alarm')}} - {{??}} - {{=helper.link('Area Atmospheric Alarm', 'close', 'reset', null, null, 'caution')}} - {{?}} -
        -
        - {{? data.mode != 3}} - {{=helper.link('Panic Siphon', 'exclamation', 'mode', {'mode': 3})}} - {{??}} - {{=helper.link('Panic Siphon', 'close', 'mode', {'mode': 1}, null, 'danger')}} - {{?}} -
        -
        - {{=helper.link('Vent Controls', 'sign-out', 'screen', {'screen': 2})}} -
        -
        - {{=helper.link('Scrubber Controls', 'filter', 'screen', {'screen': 3})}} -
        -
        - {{=helper.link('Set Environmental Mode', 'cog', 'screen', {'screen': 4})}} -
        -
        - {{=helper.link('Set Alarm Threshold', 'bar-chart', 'screen', {'screen': 5})}} -
        -
        - {{?? data.screen == 2}} - {{~ data.vents:vent:i}} -
        -

        {{=vent.long_name}}

        -
        - Power: -
        - {{? vent.power}} - {{=helper.link('On', 'power-off', 'adjust', {'id_tag': vent.id_tag, 'command': 'power', 'val': 0}, null, null)}} - {{??}} - {{=helper.link('Off', 'close', 'adjust', {'id_tag': vent.id_tag, 'command': 'power', 'val': 1}, null, 'danger')}} - {{?}} -
        -
        -
        - Mode: -
        - {{? vent.direction == "release"}} - Pressurizing - {{??}} - Siphoning - {{?}} -
        -
        -
        - Pressure Checks: -
        - {{=helper.link('Internal', 'sign-in', 'adjust', {'id_tag': vent.id_tag, 'command': 'incheck', 'val': vent.checks}, null, vent.incheck ? 'selected' : null)}} - {{=helper.link('External', 'sign-out', 'adjust', {'id_tag': vent.id_tag, 'command': 'excheck', 'val': vent.checks}, null, vent.excheck ? 'selected' : null)}} -
        -
        -
        - Set Pressure: -
        - {{=helper.link(helper.fixed(vent.external), 'pencil', 'adjust', {'id_tag': vent.id_tag, 'command': 'set_external_pressure'})}} - {{=helper.link('Reset', 'refresh', 'adjust', {'id_tag': vent.id_tag, 'command': 'reset_external_pressure'}, vent.extdefault ? 'disabled' : null)}} -
        -
        -
        - {{~}} - {{? !data.vents.length}} - No vents connected. - {{?}} - {{?? data.screen == 3}} - {{~ data.scrubbers:scrubber:i}} -
        -

        {{=scrubber.long_name}}

        -
        - Power: -
        - {{? scrubber.power}} - {{=helper.link('On', 'power-off', 'adjust', {'id_tag': scrubber.id_tag, 'command': 'power', 'val': 0}, null, null)}} - {{??}} - {{=helper.link('Off', 'close', 'adjust', {'id_tag': scrubber.id_tag, 'command': 'power', 'val': 1}, null, 'danger')}} - {{?}} -
        -
        -
        - Mode: -
        - {{? scrubber.scrubbing}} - {{=helper.link('Scrubbing', 'filter', 'adjust', {'id_tag': scrubber.id_tag, 'command': 'scrubbing', 'val': 0}, null, null)}} - {{??}} - {{=helper.link('Siphoning', 'sign-in', 'adjust', {'id_tag': scrubber.id_tag, 'command': 'scrubbing', 'val': 1}, null, 'danger')}} - {{?}} -
        -
        -
        - Range: -
        - {{? scrubber.widenet}} - {{=helper.link('Extended', 'expand', 'adjust', {'id_tag': scrubber.id_tag, 'command': 'widenet', 'val': 0}, null, 'caution')}} - {{??}} - {{=helper.link('Normal', 'compress', 'adjust', {'id_tag': scrubber.id_tag, 'command': 'widenet', 'val': 1}, null, null)}} - {{?}} -
        -
        -
        - Filters: -
        - {{=helper.link("CO2", scrubber.filter_co2 ? 'check-square-o' : 'square-o', 'adjust', {'id_tag': scrubber.id_tag, 'command': "co2_scrub", 'val': scrubber.filter_co2 ? 0 : 1}, null, scrubber.filter_co2 ? 'selected' : null)}} - {{=helper.link("N2O", scrubber.filter_n2o ? 'check-square-o' : 'square-o', 'adjust', {'id_tag': scrubber.id_tag, 'command': "n2o_scrub", 'val': scrubber.filter_n2o ? 0 : 1}, null, scrubber.filter_n2o ? 'selected' : null)}} - {{=helper.link("Plasma", scrubber.filter_toxins ? 'check-square-o' : 'square-o', 'adjust', {'id_tag': scrubber.id_tag, 'command': "tox_scrub", 'val': scrubber.filter_toxins ? 0 : 1}, null, scrubber.filter_toxins ? 'selected' : null)}} -
        -
        -
        - {{~}} - {{? !data.scrubbers.length}} - No scrubbers connected. - {{?}} - {{?? data.screen == 4}} -
        -

        Environmental Modes

        - {{~ data.modes:mode:i}} -
        - {{=helper.link(mode.name, mode.selected ? 'check-square-o' : 'square-o', 'mode', {'mode': mode.mode}, null, mode.selected ? (mode.danger ? 'danger' : 'selected') : null)}} -
        - {{~}} - - {{?? data.screen == 5}} -
        -

        Alarm Thresholds

        - - - - - - - - - - - - {{~ data.thresholds:threshold:i}} - - - {{~ threshold.settings:setting:j}} - - {{~}} - - {{~}} - -
        min2min1max1max2
        {{=threshold.name}} - {{=helper.link(setting.selected >= 0 ? helper.round(setting.selected*100)/100 : "Off", null, 'adjust', {'command': 'set_threshold', 'env': setting.env, 'var': setting.val})}} -
        - - {{?}} -{{?}} +
        + {{? data.siliconUser}} +
        + Interface Lock: +
        + {{=helper.link('Engaged', 'lock', 'toggleaccess', null, data.locked ? 'selected' : null)}} + {{=helper.link('Disengaged', 'unlock', 'toggleaccess', null, data.locked ? null : 'selected')}} +
        +
        + {{??}} + {{? data.locked}} + Swipe an ID card to unlock this interface. + {{??}} + Swipe an ID card to lock this interface. + {{?}} + {{?}} +
        +
        +

        Air Status

        + {{? data.environment_data}} + {{~ data.environment_data:info:i}} +
        + {{=info.name}}: +
        + {{? info.danger_level == 2}} + + {{?? info.danger_level == 1}} + + {{??}} + + {{?}} + {{=helper.fixed(info.value, 2)}}{{=info.unit}} +
        +
        + {{~}} +
        + Local Status: +
        + {{? data.danger_level == 2}} + Danger (Internals Required) + {{?? data.danger_level == 1}} + Caution + {{??}} + Optimal + {{?}} +
        +
        +
        + Area Status: +
        + {{? data.atmos_alarm}} + Atmosphere Alarm + {{?? data.fire_alarm}} + Fire Alarm + {{??}} + Nominal + {{?}} +
        +
        + {{??}} +
        + Warning: Cannot obtain air sample for analysis. +
        + {{?}} + {{? data.dangerous}} +
        +
        + Warning: Safety measures offline. Device may exhibit abnormal behavior. +
        + {{?}} +
        +{{? (!data.locked || data.siliconUser)}} + {{? data.screen != 1}} + {{=helper.link('Back', 'arrow-left', 'screen', {'screen': 1})}} + {{?}} + {{? data.screen == 1}} +
        +

        Air Controls

        +
        + {{? !data.atmos_alarm}} + {{=helper.link('Area Atmospheric Alarm', 'hand-stop-o', 'alarm')}} + {{??}} + {{=helper.link('Area Atmospheric Alarm', 'close', 'reset', null, null, 'caution')}} + {{?}} +
        +
        + {{? data.mode != 3}} + {{=helper.link('Panic Siphon', 'exclamation', 'mode', {'mode': 3})}} + {{??}} + {{=helper.link('Panic Siphon', 'close', 'mode', {'mode': 1}, null, 'danger')}} + {{?}} +
        +
        + {{=helper.link('Vent Controls', 'sign-out', 'screen', {'screen': 2})}} +
        +
        + {{=helper.link('Scrubber Controls', 'filter', 'screen', {'screen': 3})}} +
        +
        + {{=helper.link('Set Environmental Mode', 'cog', 'screen', {'screen': 4})}} +
        +
        + {{=helper.link('Set Alarm Threshold', 'bar-chart', 'screen', {'screen': 5})}} +
        +
        + {{?? data.screen == 2}} + {{~ data.vents:vent:i}} +
        +

        {{=vent.long_name}}

        +
        + Power: +
        + {{? vent.power}} + {{=helper.link('On', 'power-off', 'adjust', {'id_tag': vent.id_tag, 'command': 'power', 'val': 0}, null, null)}} + {{??}} + {{=helper.link('Off', 'close', 'adjust', {'id_tag': vent.id_tag, 'command': 'power', 'val': 1}, null, 'danger')}} + {{?}} +
        +
        +
        + Mode: +
        + {{? vent.direction == "release"}} + Pressurizing + {{??}} + Siphoning + {{?}} +
        +
        +
        + Pressure Checks: +
        + {{=helper.link('Internal', 'sign-in', 'adjust', {'id_tag': vent.id_tag, 'command': 'incheck', 'val': vent.checks}, null, vent.incheck ? 'selected' : null)}} + {{=helper.link('External', 'sign-out', 'adjust', {'id_tag': vent.id_tag, 'command': 'excheck', 'val': vent.checks}, null, vent.excheck ? 'selected' : null)}} +
        +
        +
        + Set Pressure: +
        + {{=helper.link(helper.fixed(vent.external), 'pencil', 'adjust', {'id_tag': vent.id_tag, 'command': 'set_external_pressure'})}} + {{=helper.link('Reset', 'refresh', 'adjust', {'id_tag': vent.id_tag, 'command': 'reset_external_pressure'}, vent.extdefault ? 'disabled' : null)}} +
        +
        +
        + {{~}} + {{? !data.vents.length}} + No vents connected. + {{?}} + {{?? data.screen == 3}} + {{~ data.scrubbers:scrubber:i}} +
        +

        {{=scrubber.long_name}}

        +
        + Power: +
        + {{? scrubber.power}} + {{=helper.link('On', 'power-off', 'adjust', {'id_tag': scrubber.id_tag, 'command': 'power', 'val': 0}, null, null)}} + {{??}} + {{=helper.link('Off', 'close', 'adjust', {'id_tag': scrubber.id_tag, 'command': 'power', 'val': 1}, null, 'danger')}} + {{?}} +
        +
        +
        + Mode: +
        + {{? scrubber.scrubbing}} + {{=helper.link('Scrubbing', 'filter', 'adjust', {'id_tag': scrubber.id_tag, 'command': 'scrubbing', 'val': 0}, null, null)}} + {{??}} + {{=helper.link('Siphoning', 'sign-in', 'adjust', {'id_tag': scrubber.id_tag, 'command': 'scrubbing', 'val': 1}, null, 'danger')}} + {{?}} +
        +
        +
        + Range: +
        + {{? scrubber.widenet}} + {{=helper.link('Extended', 'expand', 'adjust', {'id_tag': scrubber.id_tag, 'command': 'widenet', 'val': 0}, null, 'caution')}} + {{??}} + {{=helper.link('Normal', 'compress', 'adjust', {'id_tag': scrubber.id_tag, 'command': 'widenet', 'val': 1}, null, null)}} + {{?}} +
        +
        +
        + Filters: +
        + {{=helper.link("CO2", scrubber.filter_co2 ? 'check-square-o' : 'square-o', 'adjust', {'id_tag': scrubber.id_tag, 'command': "co2_scrub", 'val': scrubber.filter_co2 ? 0 : 1}, null, scrubber.filter_co2 ? 'selected' : null)}} + {{=helper.link("N2O", scrubber.filter_n2o ? 'check-square-o' : 'square-o', 'adjust', {'id_tag': scrubber.id_tag, 'command': "n2o_scrub", 'val': scrubber.filter_n2o ? 0 : 1}, null, scrubber.filter_n2o ? 'selected' : null)}} + {{=helper.link("Plasma", scrubber.filter_toxins ? 'check-square-o' : 'square-o', 'adjust', {'id_tag': scrubber.id_tag, 'command': "tox_scrub", 'val': scrubber.filter_toxins ? 0 : 1}, null, scrubber.filter_toxins ? 'selected' : null)}} +
        +
        +
        + {{~}} + {{? !data.scrubbers.length}} + No scrubbers connected. + {{?}} + {{?? data.screen == 4}} +
        +

        Environmental Modes

        + {{~ data.modes:mode:i}} +
        + {{=helper.link(mode.name, mode.selected ? 'check-square-o' : 'square-o', 'mode', {'mode': mode.mode}, null, mode.selected ? (mode.danger ? 'danger' : 'selected') : null)}} +
        + {{~}} + + {{?? data.screen == 5}} +
        +

        Alarm Thresholds

        +
        + + + + + + + + + + + {{~ data.thresholds:threshold:i}} + + + {{~ threshold.settings:setting:j}} + + {{~}} + + {{~}} + +
        min2min1max1max2
        {{=threshold.name}} + {{=helper.link(setting.selected >= 0 ? helper.round(setting.selected*100)/100 : "Off", null, 'adjust', {'command': 'set_threshold', 'env': setting.env, 'var': setting.val})}} +
        + + {{?}} +{{?}} diff --git a/nano/templates/apc.dot b/nano/templates/apc.dot index d767e18ff60..3c0d59e702a 100644 --- a/nano/templates/apc.dot +++ b/nano/templates/apc.dot @@ -1,157 +1,157 @@ -
        - {{? data.siliconUser}} -
        - Interface Lock: -
        - {{=helper.link('Engaged', 'lock', 'toggleaccess', null, data.locked ? 'selected' : null)}} - {{=helper.link('Disengaged', 'unlock', 'toggleaccess', null, data.malfStatus >= 2 ? 'linkOff' : (data.locked ? null : 'selected'))}} -
        -
        - {{??}} - {{? data.locked}} - Swipe an ID card to unlock this interface. - {{??}} - Swipe an ID card to lock this interface. - {{?}} - {{?}} -
        -
        -

        Power Status

        -
        - Main Breaker: -
        - {{? data.locked && !data.siliconUser}} - {{? data.isOperating}} - On - {{??}} - Off - {{?}} - {{??}} - {{=helper.link('On', 'power-off', 'breaker', null, data.isOperating ? 'selected' : null)}} - {{=helper.link('Off', 'close', 'breaker', null, data.isOperating ? null : 'selected')}} - {{?}} -
        -
        -
        - External Power: -
        - {{? data.externalPower == 2}} - Good - {{?? data.externalPower == 1}} - Low - {{??}} - None - {{?}} -
        -
        -
        - Power Cell: -
        - {{? data.powerCellStatus != null}} - {{=helper.bar(data.powerCellStatus, 0, 100, data.powerCellStatus >= 50 ? "good" : data.powerCellStatus >= 25 ? "average" : "bad", helper.fixed(data.powerCellStatus) + "%")}} - {{??}} - Power cell removed. - {{?}} -
        -
        - {{? data.powerCellStatus != null}} -
        - Charge Mode: -
        - {{? data.locked && !data.siliconUser}} - {{? data.chargeMode}} - Auto - {{??}} - Off - {{?}} - {{??}} - {{=helper.link('Auto', 'refresh', 'chargemode', null, data.chargeMode ? 'selected' : null)}} - {{=helper.link('Off', 'close', 'chargemode', null, data.chargeMode ? null : 'selected')}} - {{?}} -   - {{? data.chargingStatus > 1}} - [Fully Charged] - {{?? data.chargingStatus == 1}} - [Charging] - {{??}} - [Not Charging] - {{?}} -
        -
        - {{?}} -
        -
        -

        Power Channels

        -
        - {{~ data.powerChannels :channel:i}} - - - - - - - - {{~}} - - - - - - - -
        {{=channel.title}}:{{=channel.powerLoad}} W - {{? channel.status <= 1}} - Off - {{?? channel.status >= 2}} - On - {{?}} - - {{? channel.status == 1 || channel.status == 3}} - [Auto] - {{??}} - [Manual] - {{?}} - - {{? !data.locked || data.siliconUser}} - {{=helper.link('Auto', 'refresh', 'channel', channel.topicParams.auto, (channel.status == 1 || channel.status == 3) ? 'selected' : null)}} - {{=helper.link('On', 'power-off', 'channel', channel.topicParams.on, (channel.status == 2) ? 'selected' : null)}} - {{=helper.link('Off', 'close', 'channel', channel.topicParams.off, (channel.status == 0) ? 'selected' : null)}} - {{?}} -
        Total Load:{{=data.totalLoad}} W
        -
        -{{? data.siliconUser}} -
        -

        System Overrides

        -
        - {{=helper.link('Overload Lighting Circuit', 'lightbulb-o', 'overload')}} -
        -
        - {{? data.malfStatus == 1}} - {{=helper.link('Override Programming', 'terminal', 'hack')}} - {{?? data.malfStatus == 2}} - {{=helper.link('Shunt Core Processes', 'caret-square-o-down', 'occupy')}} - {{?? data.malfStatus == 3}} - {{=helper.link('Return to Main Core', 'carat-square-o-left', 'deoccupy')}} - {{?? data.malfStatus == 4}} - {{=helper.link('Shunt Core Processes', 'caret-square-o-down')}} - {{?}} -
        -
        -{{?}} -
        -
        - Cover Lock: -
        - {{? data.locked && !data.siliconUser}} - {{? data.coverLocked}} - Engaged - {{??}} - Disengaged - {{?}} - {{??}} - {{=helper.link('Engaged', 'lock', 'lock', null, data.coverLocked ? 'selected' : null)}} - {{=helper.link('Disengaged', 'unlock', 'lock', null, data.coverLocked ? null : 'selected')}} - {{?}} -
        -
        -
        +
        + {{? data.siliconUser}} +
        + Interface Lock: +
        + {{=helper.link('Engaged', 'lock', 'toggleaccess', null, data.locked ? 'selected' : null)}} + {{=helper.link('Disengaged', 'unlock', 'toggleaccess', null, data.malfStatus >= 2 ? 'linkOff' : (data.locked ? null : 'selected'))}} +
        +
        + {{??}} + {{? data.locked}} + Swipe an ID card to unlock this interface. + {{??}} + Swipe an ID card to lock this interface. + {{?}} + {{?}} +
        +
        +

        Power Status

        +
        + Main Breaker: +
        + {{? data.locked && !data.siliconUser}} + {{? data.isOperating}} + On + {{??}} + Off + {{?}} + {{??}} + {{=helper.link('On', 'power-off', 'breaker', null, data.isOperating ? 'selected' : null)}} + {{=helper.link('Off', 'close', 'breaker', null, data.isOperating ? null : 'selected')}} + {{?}} +
        +
        +
        + External Power: +
        + {{? data.externalPower == 2}} + Good + {{?? data.externalPower == 1}} + Low + {{??}} + None + {{?}} +
        +
        +
        + Power Cell: +
        + {{? data.powerCellStatus != null}} + {{=helper.bar(data.powerCellStatus, 0, 100, data.powerCellStatus >= 50 ? "good" : data.powerCellStatus >= 25 ? "average" : "bad", helper.fixed(data.powerCellStatus) + "%")}} + {{??}} + Power cell removed. + {{?}} +
        +
        + {{? data.powerCellStatus != null}} +
        + Charge Mode: +
        + {{? data.locked && !data.siliconUser}} + {{? data.chargeMode}} + Auto + {{??}} + Off + {{?}} + {{??}} + {{=helper.link('Auto', 'refresh', 'chargemode', null, data.chargeMode ? 'selected' : null)}} + {{=helper.link('Off', 'close', 'chargemode', null, data.chargeMode ? null : 'selected')}} + {{?}} +   + {{? data.chargingStatus > 1}} + [Fully Charged] + {{?? data.chargingStatus == 1}} + [Charging] + {{??}} + [Not Charging] + {{?}} +
        +
        + {{?}} +
        +
        +

        Power Channels

        + + {{~ data.powerChannels :channel:i}} + + + + + + + + {{~}} + + + + + + + +
        {{=channel.title}}:{{=channel.powerLoad}} W + {{? channel.status <= 1}} + Off + {{?? channel.status >= 2}} + On + {{?}} + + {{? channel.status == 1 || channel.status == 3}} + [Auto] + {{??}} + [Manual] + {{?}} + + {{? !data.locked || data.siliconUser}} + {{=helper.link('Auto', 'refresh', 'channel', channel.topicParams.auto, (channel.status == 1 || channel.status == 3) ? 'selected' : null)}} + {{=helper.link('On', 'power-off', 'channel', channel.topicParams.on, (channel.status == 2) ? 'selected' : null)}} + {{=helper.link('Off', 'close', 'channel', channel.topicParams.off, (channel.status == 0) ? 'selected' : null)}} + {{?}} +
        Total Load:{{=data.totalLoad}} W
        +
        +{{? data.siliconUser}} +
        +

        System Overrides

        +
        + {{=helper.link('Overload Lighting Circuit', 'lightbulb-o', 'overload')}} +
        +
        + {{? data.malfStatus == 1}} + {{=helper.link('Override Programming', 'terminal', 'hack')}} + {{?? data.malfStatus == 2}} + {{=helper.link('Shunt Core Processes', 'caret-square-o-down', 'occupy')}} + {{?? data.malfStatus == 3}} + {{=helper.link('Return to Main Core', 'carat-square-o-left', 'deoccupy')}} + {{?? data.malfStatus == 4}} + {{=helper.link('Shunt Core Processes', 'caret-square-o-down')}} + {{?}} +
        +
        +{{?}} +
        +
        + Cover Lock: +
        + {{? data.locked && !data.siliconUser}} + {{? data.coverLocked}} + Engaged + {{??}} + Disengaged + {{?}} + {{??}} + {{=helper.link('Engaged', 'lock', 'lock', null, data.coverLocked ? 'selected' : null)}} + {{=helper.link('Disengaged', 'unlock', 'lock', null, data.coverLocked ? null : 'selected')}} + {{?}} +
        +
        +
        diff --git a/nano/templates/atmos_filter.dot b/nano/templates/atmos_filter.dot index 80fc3855b75..bb8457fecc3 100644 --- a/nano/templates/atmos_filter.dot +++ b/nano/templates/atmos_filter.dot @@ -1,27 +1,27 @@ -
        -
        - Power: -
        - {{=helper.link(data.on? 'On' : 'Off', data.on ? 'power-off' : 'close', 'power')}} -
        -
        -
        - Output Pressure: -
        - {{=helper.link('Set', 'pencil', 'pressure', {'set': 'custom'})}} - {{=helper.link('Max', 'plus', 'pressure', {'set': 'max'}, data.set_pressure == data.max_pressure ? 'disabled' : null)}} - {{=data.set_pressure}} kPa -
        -
        -
        - Filter: -
        - {{=helper.link('Nothing', null, 'filter', {'mode': -1}, data.filter_type == -1 ? 'selected' : null)}} - {{=helper.link('Plasma', null, 'filter', {'mode': 0}, data.filter_type == 0 ? 'selected' : null)}} - {{=helper.link('O2', null, 'filter', {'mode': 1}, data.filter_type == 1 ? 'selected' : null)}} - {{=helper.link('N2', null, 'filter', {'mode': 2}, data.filter_type == 2 ? 'selected' : null)}} - {{=helper.link('CO2', null, 'filter', {'mode': 3}, data.filter_type == 3 ? 'selected' : null)}} - {{=helper.link('N2O', null, 'filter', {'mode': 4}, data.filter_type == 4 ? 'selected' : null)}} -
        -
        -
        +
        +
        + Power: +
        + {{=helper.link(data.on? 'On' : 'Off', data.on ? 'power-off' : 'close', 'power')}} +
        +
        +
        + Output Pressure: +
        + {{=helper.link('Set', 'pencil', 'pressure', {'set': 'custom'})}} + {{=helper.link('Max', 'plus', 'pressure', {'set': 'max'}, data.set_pressure == data.max_pressure ? 'disabled' : null)}} + {{=data.set_pressure}} kPa +
        +
        +
        + Filter: +
        + {{=helper.link('Nothing', null, 'filter', {'mode': -1}, data.filter_type == -1 ? 'selected' : null)}} + {{=helper.link('Plasma', null, 'filter', {'mode': 0}, data.filter_type == 0 ? 'selected' : null)}} + {{=helper.link('O2', null, 'filter', {'mode': 1}, data.filter_type == 1 ? 'selected' : null)}} + {{=helper.link('N2', null, 'filter', {'mode': 2}, data.filter_type == 2 ? 'selected' : null)}} + {{=helper.link('CO2', null, 'filter', {'mode': 3}, data.filter_type == 3 ? 'selected' : null)}} + {{=helper.link('N2O', null, 'filter', {'mode': 4}, data.filter_type == 4 ? 'selected' : null)}} +
        +
        +
        diff --git a/nano/templates/atmos_mixer.dot b/nano/templates/atmos_mixer.dot index 684d3f9e3f7..3b95b934c6e 100644 --- a/nano/templates/atmos_mixer.dot +++ b/nano/templates/atmos_mixer.dot @@ -1,36 +1,36 @@ -
        -
        - Power: -
        - {{=helper.link(data.on? 'On' : 'Off', data.on? 'power-off' : 'close', 'power')}} -
        -
        -
        - Output Pressure: -
        - {{=helper.link('Set', 'pencil', 'pressure', {'set': 'custom'})}} - {{=helper.link('Max', 'plus', 'pressure', {'set': 'max'}, data.set_pressure == data.max_pressure ? 'disabled' : null)}} - {{=data.set_pressure}} kPa -
        -
        -
        - Node 1: -
        - {{=helper.link('', 'fast-backward', 'node1', {'concentration' : '-0.1'}, null)}} - {{=helper.link('', 'backward', 'node1', {'concentration' : '-0.01'}, null)}} - {{=helper.link('', 'forward', 'node1', {'concentration' : '0.01'}, null)}} - {{=helper.link('', 'fast-forward', 'node1', {'concentration' : '0.1'}, null)}} - {{=data.node1_concentration}}% -
        -
        -
        - Node 2: -
        - {{=helper.link('', 'fast-backward', 'node2', {'concentration' : '-0.1'}, null)}} - {{=helper.link('', 'backward', 'node2', {'concentration' : '-0.01'}, null)}} - {{=helper.link('', 'forward', 'node2', {'concentration' : '0.01'}, null)}} - {{=helper.link('', 'fast-forward', 'node2', {'concentration' : '0.1'}, null)}} - {{=data.node2_concentration}}% -
        -
        -
        +
        +
        + Power: +
        + {{=helper.link(data.on? 'On' : 'Off', data.on? 'power-off' : 'close', 'power')}} +
        +
        +
        + Output Pressure: +
        + {{=helper.link('Set', 'pencil', 'pressure', {'set': 'custom'})}} + {{=helper.link('Max', 'plus', 'pressure', {'set': 'max'}, data.set_pressure == data.max_pressure ? 'disabled' : null)}} + {{=data.set_pressure}} kPa +
        +
        +
        + Node 1: +
        + {{=helper.link('', 'fast-backward', 'node1', {'concentration' : '-0.1'}, null)}} + {{=helper.link('', 'backward', 'node1', {'concentration' : '-0.01'}, null)}} + {{=helper.link('', 'forward', 'node1', {'concentration' : '0.01'}, null)}} + {{=helper.link('', 'fast-forward', 'node1', {'concentration' : '0.1'}, null)}} + {{=data.node1_concentration}}% +
        +
        +
        + Node 2: +
        + {{=helper.link('', 'fast-backward', 'node2', {'concentration' : '-0.1'}, null)}} + {{=helper.link('', 'backward', 'node2', {'concentration' : '-0.01'}, null)}} + {{=helper.link('', 'forward', 'node2', {'concentration' : '0.01'}, null)}} + {{=helper.link('', 'fast-forward', 'node2', {'concentration' : '0.1'}, null)}} + {{=data.node2_concentration}}% +
        +
        +
        diff --git a/nano/templates/chem_dispenser.dot b/nano/templates/chem_dispenser.dot index d3e48e5204a..40d3607ef85 100644 --- a/nano/templates/chem_dispenser.dot +++ b/nano/templates/chem_dispenser.dot @@ -1,55 +1,55 @@ -
        -

        Status

        -
        - Energy: -
        - {{=helper.bar(data.energy, 0, data.maxEnergy, null, data.energy + ' Units')}} -
        -
        -
        - Amount: -
        - {{~ data.beakerTransferAmounts :amount:i}} - {{=helper.link(amount, 'plus', 'amount', {'set': amount}, (data.amount == amount) ? 'selected' : null)}} - {{~}} -
        -
        -
        -
        -

        Dispense

        -
        - {{~ data.chemicals :chem:i}} - {{=helper.link(chem.title, 'tint', 'dispense', chem.commands, null, 'gridable')}} - {{~}} - -
        -
        -

        Beaker

        -
        -
        - {{=helper.link('Eject', 'eject', 'eject', null, data.isBeakerLoaded ? null : 'disabled')}} -
        -
        - {{~ data.beakerTransferAmounts :amount:i}} - {{=helper.link(amount, 'minus', 'remove', {'amount': amount})}} - {{~}} -
        -
        -
        -
        Contents:
        -
        - {{? data.isBeakerLoaded}} - {{? data.beakerContents.length}} - {{=data.beakerCurrentVolume}}/{{=data.beakerMaxVolume}} Units
        - {{~ data.beakerContents :reagent:i}} - {{=reagent.volume}} units of {{=reagent.name}}
        - {{~}} - {{??}} - Beaker Empty - {{?}} - {{??}} - No Beaker Loaded - {{?}} -
        -
        -
        +
        +

        Status

        +
        + Energy: +
        + {{=helper.bar(data.energy, 0, data.maxEnergy, null, data.energy + ' Units')}} +
        +
        +
        + Amount: +
        + {{~ data.beakerTransferAmounts :amount:i}} + {{=helper.link(amount, 'plus', 'amount', {'set': amount}, (data.amount == amount) ? 'selected' : null)}} + {{~}} +
        +
        +
        +
        +

        Dispense

        +
        + {{~ data.chemicals :chem:i}} + {{=helper.link(chem.title, 'tint', 'dispense', chem.commands, null, 'gridable')}} + {{~}} + +
        +
        +

        Beaker

        +
        +
        + {{=helper.link('Eject', 'eject', 'eject', null, data.isBeakerLoaded ? null : 'disabled')}} +
        +
        + {{~ data.beakerTransferAmounts :amount:i}} + {{=helper.link(amount, 'minus', 'remove', {'amount': amount})}} + {{~}} +
        +
        +
        +
        Contents:
        +
        + {{? data.isBeakerLoaded}} + {{? data.beakerContents.length}} + {{=data.beakerCurrentVolume}}/{{=data.beakerMaxVolume}} Units
        + {{~ data.beakerContents :reagent:i}} + {{=reagent.volume}} units of {{=reagent.name}}
        + {{~}} + {{??}} + Beaker Empty + {{?}} + {{??}} + No Beaker Loaded + {{?}} +
        +
        +
        diff --git a/nano/templates/smes.dot b/nano/templates/smes.dot index 442ceb87e93..ddcc232f4ed 100644 --- a/nano/templates/smes.dot +++ b/nano/templates/smes.dot @@ -1,89 +1,89 @@ -
        -

        Storage

        -
        - Stored Energy: -
        - {{=helper.bar(data.capacityPercent, 0, 100, data.capacityPercent >= 50 ? 'good' : data.capacityPercent >= 15 ? 'average' : 'bad', helper.round(data.capacityPercent) + '%')}} -
        -
        -
        -
        -

        Input

        -
        - Charge Mode: -
        - {{=helper.link('Auto', 'refresh', 'tryinput', null, data.inputAttempt ? 'selected' : null)}} - {{=helper.link('Off', 'close', 'tryinput', null, data.inputAttempt ? null : 'selected')}} -   - {{? data.capacityPercent >= 99}} - [Fully Charged] - {{?? data.inputting}} - [Charging] - {{??}} - [Not Charging] - {{?}} -
        -
        -
        - Input Setting: -
        - {{=helper.bar(data.inputLevel, 0, data.inputLevelMax, null, data.inputLevel + ' W')}} -
        -
        -
        - Adjust Input: -
        - {{=helper.link('', 'fast-backward', 'input', {'set': 'min'}, data.inputLevel ? null : 'selected')}} - {{=helper.link('', 'backward', 'input', {'set': 'minus'}, data.inputLevel ? null : 'disabled')}} - {{=helper.link('Set', 'pencil', 'input', {'set': 'custom'}, null)}} - {{=helper.link('', 'forward', 'input', {'set': 'plus'}, data.inputLevel == data.inputLevelMax ? 'disabled' : null)}} - {{=helper.link('', 'fast-forward', 'input', {'set': 'max'}, data.inputLevel == data.inputLevelMax ? 'selected' : null)}} -
        -
        -
        - Available: -
        - {{=data.inputAvailable}} W -
        -
        -
        -
        -

        Output

        -
        - Charge Mode: -
        - {{=helper.link('On', 'power-off', 'tryoutput', null, data.outputAttempt ? 'selected' : null)}} - {{=helper.link('Off', 'close', 'tryoutput', null, data.outputAttempt ? null : 'selected')}} -   - {{? data.outputting}} - [Sending] - {{?? data.charge > 0}} - [Not Sending] - {{??}} - [No Charge] - {{?}} -
        -
        -
        - Output Setting: -
        - {{=helper.bar(data.outputLevel, 0, data.outputLevelMax, null, data.outputLevel + ' W')}} -
        -
        -
        - Adjust Output: -
        - {{=helper.link('', 'fast-backward', 'output', {'set': 'min'}, data.outputLevel ? null : 'selected')}} - {{=helper.link('', 'backward', 'output', {'set': 'minus'}, data.outputLevel ? null : 'disabled')}} - {{=helper.link('Set', 'pencil', 'output', {'set': 'custom'}, null)}} - {{=helper.link('', 'forward', 'output', {'set': 'plus'}, data.outputLevel == data.outputLevelMax ? 'disabled' : null)}} - {{=helper.link('', 'fast-forward', 'output', {'set': 'max'}, data.outputLevel == data.outputLevelMax ? 'selected' : null)}} -
        -
        -
        - Outputting: -
        - {{=data.outputUsed}} W -
        -
        -
        +
        +

        Storage

        +
        + Stored Energy: +
        + {{=helper.bar(data.capacityPercent, 0, 100, data.capacityPercent >= 50 ? 'good' : data.capacityPercent >= 15 ? 'average' : 'bad', helper.round(data.capacityPercent) + '%')}} +
        +
        +
        +
        +

        Input

        +
        + Charge Mode: +
        + {{=helper.link('Auto', 'refresh', 'tryinput', null, data.inputAttempt ? 'selected' : null)}} + {{=helper.link('Off', 'close', 'tryinput', null, data.inputAttempt ? null : 'selected')}} +   + {{? data.capacityPercent >= 99}} + [Fully Charged] + {{?? data.inputting}} + [Charging] + {{??}} + [Not Charging] + {{?}} +
        +
        +
        + Input Setting: +
        + {{=helper.bar(data.inputLevel, 0, data.inputLevelMax, null, data.inputLevel + ' W')}} +
        +
        +
        + Adjust Input: +
        + {{=helper.link('', 'fast-backward', 'input', {'set': 'min'}, data.inputLevel ? null : 'selected')}} + {{=helper.link('', 'backward', 'input', {'set': 'minus'}, data.inputLevel ? null : 'disabled')}} + {{=helper.link('Set', 'pencil', 'input', {'set': 'custom'}, null)}} + {{=helper.link('', 'forward', 'input', {'set': 'plus'}, data.inputLevel == data.inputLevelMax ? 'disabled' : null)}} + {{=helper.link('', 'fast-forward', 'input', {'set': 'max'}, data.inputLevel == data.inputLevelMax ? 'selected' : null)}} +
        +
        +
        + Available: +
        + {{=data.inputAvailable}} W +
        +
        +
        +
        +

        Output

        +
        + Charge Mode: +
        + {{=helper.link('On', 'power-off', 'tryoutput', null, data.outputAttempt ? 'selected' : null)}} + {{=helper.link('Off', 'close', 'tryoutput', null, data.outputAttempt ? null : 'selected')}} +   + {{? data.outputting}} + [Sending] + {{?? data.charge > 0}} + [Not Sending] + {{??}} + [No Charge] + {{?}} +
        +
        +
        + Output Setting: +
        + {{=helper.bar(data.outputLevel, 0, data.outputLevelMax, null, data.outputLevel + ' W')}} +
        +
        +
        + Adjust Output: +
        + {{=helper.link('', 'fast-backward', 'output', {'set': 'min'}, data.outputLevel ? null : 'selected')}} + {{=helper.link('', 'backward', 'output', {'set': 'minus'}, data.outputLevel ? null : 'disabled')}} + {{=helper.link('Set', 'pencil', 'output', {'set': 'custom'}, null)}} + {{=helper.link('', 'forward', 'output', {'set': 'plus'}, data.outputLevel == data.outputLevelMax ? 'disabled' : null)}} + {{=helper.link('', 'fast-forward', 'output', {'set': 'max'}, data.outputLevel == data.outputLevelMax ? 'selected' : null)}} +
        +
        +
        + Outputting: +
        + {{=data.outputUsed}} W +
        +
        +
        diff --git a/nano/templates/solar_control.dot b/nano/templates/solar_control.dot index 1b19e87b1f1..1aab8970560 100644 --- a/nano/templates/solar_control.dot +++ b/nano/templates/solar_control.dot @@ -1,85 +1,85 @@ -
        -

        Status

        -
        - Generated Power: -
        - {{=data.generated}} W -
        -
        -
        - Orientation: -
        - {{=data.angle}}° ({{=data.direction}}) -
        -
        -
        - Adjust: -
        - {{=helper.link('15°', 'step-backward', 'control', {'cdir': '-15'})}} - {{=helper.link('1°', 'backward', 'control', {'cdir': '-1'})}} - {{=helper.link('1°', 'forward', 'control', {'cdir': '1'})}} - {{=helper.link('15°', 'step-forward', 'control', {'cdir': '15'})}} -
        -
        -
        -
        -

        Tracking

        -
        - Tracker Mode: -
        - {{=helper.link('Off', 'close', 'tracking', {'mode': '0'}, (data.tracking_state == 0) ? 'selected' : '')}} - {{=helper.link('Timed', 'clock-o', 'tracking', {'mode': '1'}, (data.tracking_state == 1) ? 'selected' : '')}} - {{? data.connected_tracker}} - {{=helper.link('Auto', 'refresh', 'tracking', {'mode': '2'}, (data.tracking_state == 2) ? 'selected' : '')}} - {{??}} - {{=helper.link('Auto', 'refresh', null, null, 'disabled')}} - {{?}} -
        -
        -
        - Tracking Rate: -
        - {{=data.tracking_rate}} deg/h ({{=data.rotating_way}}) -
        -
        -
        - Adjust: -
        - {{=helper.link('180°', 'fast-backward', 'control', {'tdir': '-180'})}} - {{=helper.link('30°', 'step-backward', 'control', {'tdir': '-30'})}} - {{=helper.link('1°', 'backward', 'control', {'tdir': '-1'})}} - {{=helper.link('1°', 'forward', 'control', {'tdir': '1'})}} - {{=helper.link('30°', 'step-forward', 'control', {'tdir': '30'})}} - {{=helper.link('180°', 'fast-forward', 'control', {'tdir': '180'})}} -
        -
        -
        -
        -

        Devices

        -
        - Search: -
        - {{=helper.link('Refresh', 'refresh', 'refresh')}} -
        -
        -
        - - Solar Tracker: - -
        - {{? data.connected_tracker}} - Found - {{??}} - Not Found - {{?}} -
        -
        -
        - - Solars Panels: - -
        - {{=data.connected_panels}} Connected -
        - -
        +
        +

        Status

        +
        + Generated Power: +
        + {{=data.generated}} W +
        +
        +
        + Orientation: +
        + {{=data.angle}}° ({{=data.direction}}) +
        +
        +
        + Adjust: +
        + {{=helper.link('15°', 'step-backward', 'control', {'cdir': '-15'})}} + {{=helper.link('1°', 'backward', 'control', {'cdir': '-1'})}} + {{=helper.link('1°', 'forward', 'control', {'cdir': '1'})}} + {{=helper.link('15°', 'step-forward', 'control', {'cdir': '15'})}} +
        +
        +
        +
        +

        Tracking

        +
        + Tracker Mode: +
        + {{=helper.link('Off', 'close', 'tracking', {'mode': '0'}, (data.tracking_state == 0) ? 'selected' : '')}} + {{=helper.link('Timed', 'clock-o', 'tracking', {'mode': '1'}, (data.tracking_state == 1) ? 'selected' : '')}} + {{? data.connected_tracker}} + {{=helper.link('Auto', 'refresh', 'tracking', {'mode': '2'}, (data.tracking_state == 2) ? 'selected' : '')}} + {{??}} + {{=helper.link('Auto', 'refresh', null, null, 'disabled')}} + {{?}} +
        +
        +
        + Tracking Rate: +
        + {{=data.tracking_rate}} deg/h ({{=data.rotating_way}}) +
        +
        +
        + Adjust: +
        + {{=helper.link('180°', 'fast-backward', 'control', {'tdir': '-180'})}} + {{=helper.link('30°', 'step-backward', 'control', {'tdir': '-30'})}} + {{=helper.link('1°', 'backward', 'control', {'tdir': '-1'})}} + {{=helper.link('1°', 'forward', 'control', {'tdir': '1'})}} + {{=helper.link('30°', 'step-forward', 'control', {'tdir': '30'})}} + {{=helper.link('180°', 'fast-forward', 'control', {'tdir': '180'})}} +
        +
        +
        +
        +

        Devices

        +
        + Search: +
        + {{=helper.link('Refresh', 'refresh', 'refresh')}} +
        +
        +
        + + Solar Tracker: + +
        + {{? data.connected_tracker}} + Found + {{??}} + Not Found + {{?}} +
        +
        +
        + + Solars Panels: + +
        + {{=data.connected_panels}} Connected +
        + +
        diff --git a/nano/templates/space_heater.dot b/nano/templates/space_heater.dot index 46622e843d4..f4e1fb3005c 100644 --- a/nano/templates/space_heater.dot +++ b/nano/templates/space_heater.dot @@ -1,79 +1,79 @@ -
        -

        Status

        -
        - Power: -
        - {{=helper.link('On', 'power-off', 'power', null, data.on ? 'selected' : null)}} - {{=helper.link('Off', 'close', 'power', null, data.on ? null : 'selected')}} -
        -
        -
        - Stored Energy: -
        - {{? data.hasPowercell}} - {{=helper.bar(data.powerLevel, 0, 100, 'good', data.powerLevel + '%')}} - {{??}} - No power cell loaded. - {{?}} -
        -
        - {{? data.open}} -
        - Cell: -
        - {{? data.hasPowercell}} - {{=helper.link('Eject', 'eject', 'ejectcell')}} - {{??}} - {{=helper.link('Install', 'eject', 'installcell')}} - {{?}} -
        -
        - {{?}} -
        -
        -

        Thermostat

        -
        - Current Temp: -
        - {{=data.currentTemp}}°C -
        -
        -
        - Target Temp: -
        - {{=data.targetTemp}}°C -
        -
        - {{? data.open}} -
        - Adjustment: -
        - {{=helper.link('', 'fast-backward', 'temp', {'set': -20}, data.targetTemp > data.minTemp ? null : 'disabled')}} - {{=helper.link('', 'backward', 'temp', {'set': -5}, data.targetTemp > data.minTemp ? null : 'disabled')}} - {{=helper.link('Set', 'pencil', 'temp', {'set': 'custom'}, null)}} - {{=helper.link('', 'forward', 'temp', {'set': 5}, data.targetTemp < data.maxTemp ? null : 'disabled')}} - {{=helper.link('', 'fast-forward', 'temp', {'set': 20}, data.targetTemp < data.maxTemp ? null : 'disabled')}} -
        -
        - {{?}} -
        - - Operational Mode: - -
        - {{? data.open}} - {{=helper.link('Heat', 'long-arrow-up', 'mode', {'mode': 'heat'}, data.mode != "heat" ? null : 'selected')}} - {{=helper.link('Cool', 'long-arrow-down', 'mode', {'mode': 'cool'}, data.mode != "cool" ? null : 'selected')}} - {{=helper.link('Auto', 'arrows-v', 'mode', {'mode': 'auto'}, (data.mode == "heat" || data.mode == "cool") ? null : 'selected')}} - {{??}} - {{? data.mode == "heat"}} - Heat - {{?? data.mode == "cool"}} - Cool - {{??}} - Auto - {{?}} - {{?}} -
        -
        -
        +
        +

        Status

        +
        + Power: +
        + {{=helper.link('On', 'power-off', 'power', null, data.on ? 'selected' : null)}} + {{=helper.link('Off', 'close', 'power', null, data.on ? null : 'selected')}} +
        +
        +
        + Stored Energy: +
        + {{? data.hasPowercell}} + {{=helper.bar(data.powerLevel, 0, 100, 'good', data.powerLevel + '%')}} + {{??}} + No power cell loaded. + {{?}} +
        +
        + {{? data.open}} +
        + Cell: +
        + {{? data.hasPowercell}} + {{=helper.link('Eject', 'eject', 'ejectcell')}} + {{??}} + {{=helper.link('Install', 'eject', 'installcell')}} + {{?}} +
        +
        + {{?}} +
        +
        +

        Thermostat

        +
        + Current Temp: +
        + {{=data.currentTemp}}°C +
        +
        +
        + Target Temp: +
        + {{=data.targetTemp}}°C +
        +
        + {{? data.open}} +
        + Adjustment: +
        + {{=helper.link('', 'fast-backward', 'temp', {'set': -20}, data.targetTemp > data.minTemp ? null : 'disabled')}} + {{=helper.link('', 'backward', 'temp', {'set': -5}, data.targetTemp > data.minTemp ? null : 'disabled')}} + {{=helper.link('Set', 'pencil', 'temp', {'set': 'custom'}, null)}} + {{=helper.link('', 'forward', 'temp', {'set': 5}, data.targetTemp < data.maxTemp ? null : 'disabled')}} + {{=helper.link('', 'fast-forward', 'temp', {'set': 20}, data.targetTemp < data.maxTemp ? null : 'disabled')}} +
        +
        + {{?}} +
        + + Operational Mode: + +
        + {{? data.open}} + {{=helper.link('Heat', 'long-arrow-up', 'mode', {'mode': 'heat'}, data.mode != "heat" ? null : 'selected')}} + {{=helper.link('Cool', 'long-arrow-down', 'mode', {'mode': 'cool'}, data.mode != "cool" ? null : 'selected')}} + {{=helper.link('Auto', 'arrows-v', 'mode', {'mode': 'auto'}, (data.mode == "heat" || data.mode == "cool") ? null : 'selected')}} + {{??}} + {{? data.mode == "heat"}} + Heat + {{?? data.mode == "cool"}} + Cool + {{??}} + Auto + {{?}} + {{?}} +
        +
        +
        diff --git a/nano/templates/tanks.dot b/nano/templates/tanks.dot index f3c33725104..7bc46a39a4d 100644 --- a/nano/templates/tanks.dot +++ b/nano/templates/tanks.dot @@ -1,37 +1,37 @@ -
        - {{? data.hasHoldingTank }} - The regulator is connected to a mask. - {{??}} - The regulator is not connected to a mask. - {{?}} -
        -
        -
        - Tank Pressure: -
        - {{=helper.bar(data.tankPressure, 0, 1013, (data.tankPressure > 200) ? 'good' : ((data.tankPressure > 100) ? 'average' : 'bad'), data.tankPressure + ' kPa')}} -
        - -
        - Release Pressure: -
        - {{=helper.bar(data.releasePressure, data.minReleasePressure, data.maxReleasePressure, null, data.releasePressure + ' kPa')}} -
        -
        -
        - Pressure Regulator: -
        - {{=helper.link('Reset', 'refresh', 'pressure', {'set': 'reset'}, (data.releasePressure != data.defaultReleasePressure) ? null : 'disabled')}} - {{=helper.link('Min', 'minus', 'pressure', {'set': 'min'}, (data.releasePressure > data.minReleasePressure) ? null : 'disabled')}} - {{=helper.link('Set', 'pencil', 'pressure', {'set': 'custom'}, null)}} - {{=helper.link('Max', 'plus', 'pressure', {'set': 'max'}, (data.releasePressure < data.maxReleasePressure) ? null : 'disabled')}} -
        -
        -
        - Valve: -
        - {{=helper.link('Open', 'unlock', 'valve', null, data.maskConnected ? (data.valveOpen ? 'selected' : null) : 'disabled')}} - {{=helper.link('Close', 'lock', 'valuve', null, data.valveOpen ? null : 'selected')}} -
        -
        -
        +
        + {{? data.hasHoldingTank }} + The regulator is connected to a mask. + {{??}} + The regulator is not connected to a mask. + {{?}} +
        +
        +
        + Tank Pressure: +
        + {{=helper.bar(data.tankPressure, 0, 1013, (data.tankPressure > 200) ? 'good' : ((data.tankPressure > 100) ? 'average' : 'bad'), data.tankPressure + ' kPa')}} +
        + +
        + Release Pressure: +
        + {{=helper.bar(data.releasePressure, data.minReleasePressure, data.maxReleasePressure, null, data.releasePressure + ' kPa')}} +
        +
        +
        + Pressure Regulator: +
        + {{=helper.link('Reset', 'refresh', 'pressure', {'set': 'reset'}, (data.releasePressure != data.defaultReleasePressure) ? null : 'disabled')}} + {{=helper.link('Min', 'minus', 'pressure', {'set': 'min'}, (data.releasePressure > data.minReleasePressure) ? null : 'disabled')}} + {{=helper.link('Set', 'pencil', 'pressure', {'set': 'custom'}, null)}} + {{=helper.link('Max', 'plus', 'pressure', {'set': 'max'}, (data.releasePressure < data.maxReleasePressure) ? null : 'disabled')}} +
        +
        +
        + Valve: +
        + {{=helper.link('Open', 'unlock', 'valve', null, data.maskConnected ? (data.valveOpen ? 'selected' : null) : 'disabled')}} + {{=helper.link('Close', 'lock', 'valuve', null, data.valveOpen ? null : 'selected')}} +
        +
        +
        diff --git a/tgstation.dme b/tgstation.dme index 997dc3956f4..208ea0314d9 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1,1595 +1,1595 @@ -// DM Environment file for tgstation.dme. -// All manual changes should be made outside the BEGIN_ and END_ blocks. -// New source code should be placed in .dm files: choose File/New --> Code File. -// BEGIN_INTERNALS -// END_INTERNALS - -// BEGIN_FILE_DIR -#define FILE_DIR . -// END_FILE_DIR - -// BEGIN_PREFERENCES -#define DEBUG -// END_PREFERENCES - -// BEGIN_INCLUDE -#include "_maps\__MAP_DEFINES.dm" -#include "_maps\tgstation2.dm" -#include "code\_compile_options.dm" -#include "code\hub.dm" -#include "code\world.dm" -#include "code\__DATASTRUCTURES\heap.dm" -#include "code\__DATASTRUCTURES\linked_lists.dm" -#include "code\__DATASTRUCTURES\priority_queue.dm" -#include "code\__DATASTRUCTURES\stacks.dm" -#include "code\__DEFINES\admin.dm" -#include "code\__DEFINES\atmospherics.dm" -#include "code\__DEFINES\bots.dm" -#include "code\__DEFINES\clothing.dm" -#include "code\__DEFINES\combat.dm" -#include "code\__DEFINES\flags.dm" -#include "code\__DEFINES\genetics.dm" -#include "code\__DEFINES\hud.dm" -#include "code\__DEFINES\is_helpers.dm" -#include "code\__DEFINES\machines.dm" -#include "code\__DEFINES\math.dm" -#include "code\__DEFINES\misc.dm" -#include "code\__DEFINES\nano.dm" -#include "code\__DEFINES\pipe_construction.dm" -#include "code\__DEFINES\preferences.dm" -#include "code\__DEFINES\qdel.dm" -#include "code\__DEFINES\reagents.dm" -#include "code\__DEFINES\role_preferences.dm" -#include "code\__DEFINES\say.dm" -#include "code\__DEFINES\sight.dm" -#include "code\__DEFINES\stat.dm" -#include "code\__DEFINES\tablecrafting.dm" -#include "code\__HELPERS\_logging.dm" -#include "code\__HELPERS\_string_lists.dm" -#include "code\__HELPERS\cmp.dm" -#include "code\__HELPERS\files.dm" -#include "code\__HELPERS\game.dm" -#include "code\__HELPERS\global_lists.dm" -#include "code\__HELPERS\icon_smoothing.dm" -#include "code\__HELPERS\icons.dm" -#include "code\__HELPERS\lists.dm" -#include "code\__HELPERS\maths.dm" -#include "code\__HELPERS\matrices.dm" -#include "code\__HELPERS\mobs.dm" -#include "code\__HELPERS\names.dm" -#include "code\__HELPERS\sanitize_values.dm" -#include "code\__HELPERS\text.dm" -#include "code\__HELPERS\time.dm" -#include "code\__HELPERS\type2type.dm" -#include "code\__HELPERS\unsorted.dm" -#include "code\__HELPERS\bygex\bygex.dm" -#include "code\__HELPERS\sorts\__main.dm" -#include "code\__HELPERS\sorts\InsertSort.dm" -#include "code\__HELPERS\sorts\MergeSort.dm" -#include "code\__HELPERS\sorts\TimSort.dm" -#include "code\_globalvars\configuration.dm" -#include "code\_globalvars\database.dm" -#include "code\_globalvars\game_modes.dm" -#include "code\_globalvars\genetics.dm" -#include "code\_globalvars\logging.dm" -#include "code\_globalvars\misc.dm" -#include "code\_globalvars\station.dm" -#include "code\_globalvars\lists\flavor_misc.dm" -#include "code\_globalvars\lists\mapping.dm" -#include "code\_globalvars\lists\mobs.dm" -#include "code\_globalvars\lists\names.dm" -#include "code\_globalvars\lists\objects.dm" -#include "code\_onclick\adjacent.dm" -#include "code\_onclick\ai.dm" -#include "code\_onclick\click.dm" -#include "code\_onclick\cyborg.dm" -#include "code\_onclick\drag_drop.dm" -#include "code\_onclick\god.dm" -#include "code\_onclick\item_attack.dm" -#include "code\_onclick\observer.dm" -#include "code\_onclick\other_mobs.dm" -#include "code\_onclick\overmind.dm" -#include "code\_onclick\telekinesis.dm" -#include "code\_onclick\hud\_defines.dm" -#include "code\_onclick\hud\action.dm" -#include "code\_onclick\hud\ai.dm" -#include "code\_onclick\hud\alert.dm" -#include "code\_onclick\hud\alien.dm" -#include "code\_onclick\hud\alien_larva.dm" -#include "code\_onclick\hud\drones.dm" -#include "code\_onclick\hud\ghost.dm" -#include "code\_onclick\hud\hud.dm" -#include "code\_onclick\hud\human.dm" -#include "code\_onclick\hud\monkey.dm" -#include "code\_onclick\hud\movable_screen_objects.dm" -#include "code\_onclick\hud\other_mobs.dm" -#include "code\_onclick\hud\robot.dm" -#include "code\_onclick\hud\screen_objects.dm" -#include "code\ATMOSPHERICS\atmospherics.dm" -#include "code\ATMOSPHERICS\datum_pipeline.dm" -#include "code\ATMOSPHERICS\components\components_base.dm" -#include "code\ATMOSPHERICS\components\binary_devices\binary_atmos_base.dm" -#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\pump.dm" -#include "code\ATMOSPHERICS\components\binary_devices\valve.dm" -#include "code\ATMOSPHERICS\components\binary_devices\volume_pump.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" -#include "code\ATMOSPHERICS\components\unary_devices\cold_sink.dm" -#include "code\ATMOSPHERICS\components\unary_devices\cryo.dm" -#include "code\ATMOSPHERICS\components\unary_devices\Freezer.dm" -#include "code\ATMOSPHERICS\components\unary_devices\generator_input.dm" -#include "code\ATMOSPHERICS\components\unary_devices\heat_exchanger.dm" -#include "code\ATMOSPHERICS\components\unary_devices\heat_source.dm" -#include "code\ATMOSPHERICS\components\unary_devices\outlet_injector.dm" -#include "code\ATMOSPHERICS\components\unary_devices\oxygen_generator.dm" -#include "code\ATMOSPHERICS\components\unary_devices\portables_connector.dm" -#include "code\ATMOSPHERICS\components\unary_devices\tank.dm" -#include "code\ATMOSPHERICS\components\unary_devices\unary_base.dm" -#include "code\ATMOSPHERICS\components\unary_devices\vent_pump.dm" -#include "code\ATMOSPHERICS\components\unary_devices\vent_scrubber.dm" -#include "code\ATMOSPHERICS\pipes\manifold.dm" -#include "code\ATMOSPHERICS\pipes\manifold4w.dm" -#include "code\ATMOSPHERICS\pipes\pipes.dm" -#include "code\ATMOSPHERICS\pipes\simple.dm" -#include "code\ATMOSPHERICS\pipes\heat_exchange\he_pipes.dm" -#include "code\ATMOSPHERICS\pipes\heat_exchange\junction.dm" -#include "code\ATMOSPHERICS\pipes\heat_exchange\manifold.dm" -#include "code\ATMOSPHERICS\pipes\heat_exchange\simple.dm" -#include "code\controllers\admin.dm" -#include "code\controllers\configuration.dm" -#include "code\controllers\controller.dm" -#include "code\controllers\failsafe.dm" -#include "code\controllers\master.dm" -#include "code\controllers\subsystem.dm" -#include "code\controllers\subsystem\air.dm" -#include "code\controllers\subsystem\assets.dm" -#include "code\controllers\subsystem\diseases.dm" -#include "code\controllers\subsystem\events.dm" -#include "code\controllers\subsystem\garbage.dm" -#include "code\controllers\subsystem\jobs.dm" -#include "code\controllers\subsystem\lighting.dm" -#include "code\controllers\subsystem\machines.dm" -#include "code\controllers\subsystem\minimap.dm" -#include "code\controllers\subsystem\mobs.dm" -#include "code\controllers\subsystem\nano.dm" -#include "code\controllers\subsystem\npcpool.dm" -#include "code\controllers\subsystem\objects.dm" -#include "code\controllers\subsystem\pai.dm" -#include "code\controllers\subsystem\radio.dm" -#include "code\controllers\subsystem\server_maintenance.dm" -#include "code\controllers\subsystem\shuttles.dm" -#include "code\controllers\subsystem\sun.dm" -#include "code\controllers\subsystem\ticker.dm" -#include "code\controllers\subsystem\timer.dm" -#include "code\controllers\subsystem\voting.dm" -#include "code\controllers\subsystem\shuttles\emergency.dm" -#include "code\controllers\subsystem\shuttles\supply.dm" -#include "code\datums\ai_laws.dm" -#include "code\datums\beam.dm" -#include "code\datums\browser.dm" -#include "code\datums\datacore.dm" -#include "code\datums\datumvars.dm" -#include "code\datums\debug.dm" -#include "code\datums\gas_mixture.dm" -#include "code\datums\hud.dm" -#include "code\datums\martial.dm" -#include "code\datums\material_container.dm" -#include "code\datums\mind.dm" -#include "code\datums\modules.dm" -#include "code\datums\mutations.dm" -#include "code\datums\outfit.dm" -#include "code\datums\recipe.dm" -#include "code\datums\spell.dm" -#include "code\datums\supplypacks.dm" -#include "code\datums\uplink_item.dm" -#include "code\datums\votablemap.dm" -#include "code\datums\diseases\_disease.dm" -#include "code\datums\diseases\_MobProcs.dm" -#include "code\datums\diseases\anxiety.dm" -#include "code\datums\diseases\appendicitis.dm" -#include "code\datums\diseases\beesease.dm" -#include "code\datums\diseases\brainrot.dm" -#include "code\datums\diseases\cold.dm" -#include "code\datums\diseases\cold9.dm" -#include "code\datums\diseases\dna_spread.dm" -#include "code\datums\diseases\fake_gbs.dm" -#include "code\datums\diseases\flu.dm" -#include "code\datums\diseases\fluspanish.dm" -#include "code\datums\diseases\gbs.dm" -#include "code\datums\diseases\magnitis.dm" -#include "code\datums\diseases\pierrot_throat.dm" -#include "code\datums\diseases\retrovirus.dm" -#include "code\datums\diseases\rhumba_beat.dm" -#include "code\datums\diseases\transformation.dm" -#include "code\datums\diseases\wizarditis.dm" -#include "code\datums\diseases\advance\advance.dm" -#include "code\datums\diseases\advance\presets.dm" -#include "code\datums\diseases\advance\symptoms\beard.dm" -#include "code\datums\diseases\advance\symptoms\choking.dm" -#include "code\datums\diseases\advance\symptoms\confusion.dm" -#include "code\datums\diseases\advance\symptoms\cough.dm" -#include "code\datums\diseases\advance\symptoms\damage_converter.dm" -#include "code\datums\diseases\advance\symptoms\deafness.dm" -#include "code\datums\diseases\advance\symptoms\dizzy.dm" -#include "code\datums\diseases\advance\symptoms\fever.dm" -#include "code\datums\diseases\advance\symptoms\fire.dm" -#include "code\datums\diseases\advance\symptoms\flesh_eating.dm" -#include "code\datums\diseases\advance\symptoms\genetics.dm" -#include "code\datums\diseases\advance\symptoms\hallucigen.dm" -#include "code\datums\diseases\advance\symptoms\headache.dm" -#include "code\datums\diseases\advance\symptoms\heal.dm" -#include "code\datums\diseases\advance\symptoms\itching.dm" -#include "code\datums\diseases\advance\symptoms\oxygen.dm" -#include "code\datums\diseases\advance\symptoms\shedding.dm" -#include "code\datums\diseases\advance\symptoms\shivering.dm" -#include "code\datums\diseases\advance\symptoms\skin.dm" -#include "code\datums\diseases\advance\symptoms\sneeze.dm" -#include "code\datums\diseases\advance\symptoms\stimulant.dm" -#include "code\datums\diseases\advance\symptoms\symptoms.dm" -#include "code\datums\diseases\advance\symptoms\vision.dm" -#include "code\datums\diseases\advance\symptoms\voice_change.dm" -#include "code\datums\diseases\advance\symptoms\vomit.dm" -#include "code\datums\diseases\advance\symptoms\weakness.dm" -#include "code\datums\diseases\advance\symptoms\weight.dm" -#include "code\datums\diseases\advance\symptoms\youth.dm" -#include "code\datums\helper_datums\construction_datum.dm" -#include "code\datums\helper_datums\events.dm" -#include "code\datums\helper_datums\getrev.dm" -#include "code\datums\helper_datums\icon_snapshot.dm" -#include "code\datums\helper_datums\teleport.dm" -#include "code\datums\helper_datums\topic_input.dm" -#include "code\datums\spells\area_teleport.dm" -#include "code\datums\spells\barnyard.dm" -#include "code\datums\spells\bloodcrawl.dm" -#include "code\datums\spells\charge.dm" -#include "code\datums\spells\conjure.dm" -#include "code\datums\spells\construct_spells.dm" -#include "code\datums\spells\dumbfire.dm" -#include "code\datums\spells\emplosion.dm" -#include "code\datums\spells\ethereal_jaunt.dm" -#include "code\datums\spells\explosion.dm" -#include "code\datums\spells\genetic.dm" -#include "code\datums\spells\inflict_handler.dm" -#include "code\datums\spells\knock.dm" -#include "code\datums\spells\lichdom.dm" -#include "code\datums\spells\lightning.dm" -#include "code\datums\spells\mime.dm" -#include "code\datums\spells\mind_transfer.dm" -#include "code\datums\spells\projectile.dm" -#include "code\datums\spells\santa.dm" -#include "code\datums\spells\shapeshift.dm" -#include "code\datums\spells\summonitem.dm" -#include "code\datums\spells\touch_attacks.dm" -#include "code\datums\spells\trigger.dm" -#include "code\datums\spells\turf_teleport.dm" -#include "code\datums\spells\wizard.dm" -#include "code\datums\wires\airlock.dm" -#include "code\datums\wires\alarm.dm" -#include "code\datums\wires\apc.dm" -#include "code\datums\wires\autolathe.dm" -#include "code\datums\wires\explosive.dm" -#include "code\datums\wires\mulebot.dm" -#include "code\datums\wires\particle_accelerator.dm" -#include "code\datums\wires\pizza_bomb.dm" -#include "code\datums\wires\r_n_d.dm" -#include "code\datums\wires\radio.dm" -#include "code\datums\wires\robot.dm" -#include "code\datums\wires\syndicatebomb.dm" -#include "code\datums\wires\vending.dm" -#include "code\datums\wires\wires.dm" -#include "code\game\asteroid.dm" -#include "code\game\atoms.dm" -#include "code\game\atoms_movable.dm" -#include "code\game\communications.dm" -#include "code\game\data_huds.dm" -#include "code\game\dna.dm" -#include "code\game\say.dm" -#include "code\game\shuttle_engines.dm" -#include "code\game\skincmd.dm" -#include "code\game\sound.dm" -#include "code\game\area\ai_monitored.dm" -#include "code\game\area\areas.dm" -#include "code\game\area\Space Station 13 areas.dm" -#include "code\game\gamemodes\antag_hud.dm" -#include "code\game\gamemodes\antag_spawner.dm" -#include "code\game\gamemodes\events.dm" -#include "code\game\gamemodes\factions.dm" -#include "code\game\gamemodes\game_mode.dm" -#include "code\game\gamemodes\intercept_report.dm" -#include "code\game\gamemodes\objective.dm" -#include "code\game\gamemodes\objective_items.dm" -#include "code\game\gamemodes\setupgame.dm" -#include "code\game\gamemodes\abduction\abduction.dm" -#include "code\game\gamemodes\abduction\abduction_gear.dm" -#include "code\game\gamemodes\abduction\abduction_surgery.dm" -#include "code\game\gamemodes\abduction\gland.dm" -#include "code\game\gamemodes\abduction\machinery\camera.dm" -#include "code\game\gamemodes\abduction\machinery\console.dm" -#include "code\game\gamemodes\abduction\machinery\dispenser.dm" -#include "code\game\gamemodes\abduction\machinery\experiment.dm" -#include "code\game\gamemodes\abduction\machinery\pad.dm" -#include "code\game\gamemodes\blob\blob.dm" -#include "code\game\gamemodes\blob\blob_finish.dm" -#include "code\game\gamemodes\blob\blob_report.dm" -#include "code\game\gamemodes\blob\overmind.dm" -#include "code\game\gamemodes\blob\powers.dm" -#include "code\game\gamemodes\blob\theblob.dm" -#include "code\game\gamemodes\blob\blobs\blob_mobs.dm" -#include "code\game\gamemodes\blob\blobs\core.dm" -#include "code\game\gamemodes\blob\blobs\factory.dm" -#include "code\game\gamemodes\blob\blobs\node.dm" -#include "code\game\gamemodes\blob\blobs\resource.dm" -#include "code\game\gamemodes\blob\blobs\shield.dm" -#include "code\game\gamemodes\blob\blobs\storage.dm" -#include "code\game\gamemodes\changeling\changeling.dm" -#include "code\game\gamemodes\changeling\changeling_power.dm" -#include "code\game\gamemodes\changeling\evolution_menu.dm" -#include "code\game\gamemodes\changeling\traitor_chan.dm" -#include "code\game\gamemodes\changeling\powers\absorb.dm" -#include "code\game\gamemodes\changeling\powers\adrenaline.dm" -#include "code\game\gamemodes\changeling\powers\augmented_eyesight.dm" -#include "code\game\gamemodes\changeling\powers\biodegrade.dm" -#include "code\game\gamemodes\changeling\powers\chameleon_skin.dm" -#include "code\game\gamemodes\changeling\powers\digitalcamo.dm" -#include "code\game\gamemodes\changeling\powers\fakedeath.dm" -#include "code\game\gamemodes\changeling\powers\fleshmend.dm" -#include "code\game\gamemodes\changeling\powers\glands.dm" -#include "code\game\gamemodes\changeling\powers\headcrab.dm" -#include "code\game\gamemodes\changeling\powers\hivemind.dm" -#include "code\game\gamemodes\changeling\powers\humanform.dm" -#include "code\game\gamemodes\changeling\powers\lesserform.dm" -#include "code\game\gamemodes\changeling\powers\mimic_voice.dm" -#include "code\game\gamemodes\changeling\powers\mutations.dm" -#include "code\game\gamemodes\changeling\powers\panacea.dm" -#include "code\game\gamemodes\changeling\powers\revive.dm" -#include "code\game\gamemodes\changeling\powers\shriek.dm" -#include "code\game\gamemodes\changeling\powers\spiders.dm" -#include "code\game\gamemodes\changeling\powers\strained_muscles.dm" -#include "code\game\gamemodes\changeling\powers\tiny_prick.dm" -#include "code\game\gamemodes\changeling\powers\transform.dm" -#include "code\game\gamemodes\cult\cult.dm" -#include "code\game\gamemodes\cult\cult_items.dm" -#include "code\game\gamemodes\cult\cult_structures.dm" -#include "code\game\gamemodes\cult\ritual.dm" -#include "code\game\gamemodes\cult\runes.dm" -#include "code\game\gamemodes\cult\talisman.dm" -#include "code\game\gamemodes\extended\extended.dm" -#include "code\game\gamemodes\gang\dominator.dm" -#include "code\game\gamemodes\gang\gang.dm" -#include "code\game\gamemodes\gang\gang_datum.dm" -#include "code\game\gamemodes\gang\gang_pen.dm" -#include "code\game\gamemodes\gang\recaller.dm" -#include "code\game\gamemodes\handofgod\_handofgod.dm" -#include "code\game\gamemodes\handofgod\actions.dm" -#include "code\game\gamemodes\handofgod\god.dm" -#include "code\game\gamemodes\handofgod\items.dm" -#include "code\game\gamemodes\handofgod\objectives.dm" -#include "code\game\gamemodes\handofgod\powers.dm" -#include "code\game\gamemodes\handofgod\structures.dm" -#include "code\game\gamemodes\handofgod\traps.dm" -#include "code\game\gamemodes\malfunction\Malf_Modules.dm" -#include "code\game\gamemodes\malfunction\malfunction.dm" -#include "code\game\gamemodes\meteor\meteor.dm" -#include "code\game\gamemodes\meteor\meteors.dm" -#include "code\game\gamemodes\monkey\monkey.dm" -#include "code\game\gamemodes\nuclear\nuclear.dm" -#include "code\game\gamemodes\nuclear\nuclear_challenge.dm" -#include "code\game\gamemodes\nuclear\nuclearbomb.dm" -#include "code\game\gamemodes\nuclear\pinpointer.dm" -#include "code\game\gamemodes\revolution\revolution.dm" -#include "code\game\gamemodes\sandbox\airlock_maker.dm" -#include "code\game\gamemodes\sandbox\h_sandbox.dm" -#include "code\game\gamemodes\sandbox\sandbox.dm" -#include "code\game\gamemodes\shadowling\ascendant_shadowling.dm" -#include "code\game\gamemodes\shadowling\shadowling.dm" -#include "code\game\gamemodes\shadowling\shadowling_abilities.dm" -#include "code\game\gamemodes\shadowling\shadowling_items.dm" -#include "code\game\gamemodes\shadowling\special_shadowling_abilities.dm" -#include "code\game\gamemodes\traitor\double_agents.dm" -#include "code\game\gamemodes\traitor\traitor.dm" -#include "code\game\gamemodes\wizard\artefact.dm" -#include "code\game\gamemodes\wizard\godhand.dm" -#include "code\game\gamemodes\wizard\raginmages.dm" -#include "code\game\gamemodes\wizard\rightandwrong.dm" -#include "code\game\gamemodes\wizard\soulstone.dm" -#include "code\game\gamemodes\wizard\spellbook.dm" -#include "code\game\gamemodes\wizard\wizard.dm" -#include "code\game\jobs\access.dm" -#include "code\game\jobs\jobs.dm" -#include "code\game\jobs\whitelist.dm" -#include "code\game\jobs\job\assistant.dm" -#include "code\game\jobs\job\captain.dm" -#include "code\game\jobs\job\cargo_service.dm" -#include "code\game\jobs\job\civilian.dm" -#include "code\game\jobs\job\civilian_chaplain.dm" -#include "code\game\jobs\job\engineering.dm" -#include "code\game\jobs\job\job.dm" -#include "code\game\jobs\job\medical.dm" -#include "code\game\jobs\job\science.dm" -#include "code\game\jobs\job\security.dm" -#include "code\game\jobs\job\silicon.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\announcement_system.dm" -#include "code\game\machinery\atmo_control.dm" -#include "code\game\machinery\autolathe.dm" -#include "code\game\machinery\Beacon.dm" -#include "code\game\machinery\buttons.dm" -#include "code\game\machinery\cell_charger.dm" -#include "code\game\machinery\cloning.dm" -#include "code\game\machinery\constructable_frame.dm" -#include "code\game\machinery\deployable.dm" -#include "code\game\machinery\dna_scanner.dm" -#include "code\game\machinery\doppler_array.dm" -#include "code\game\machinery\droneDispenser.dm" -#include "code\game\machinery\flasher.dm" -#include "code\game\machinery\hologram.dm" -#include "code\game\machinery\igniter.dm" -#include "code\game\machinery\iv_drip.dm" -#include "code\game\machinery\lightswitch.dm" -#include "code\game\machinery\machinery.dm" -#include "code\game\machinery\magnet.dm" -#include "code\game\machinery\mass_driver.dm" -#include "code\game\machinery\navbeacon.dm" -#include "code\game\machinery\newscaster.dm" -#include "code\game\machinery\overview.dm" -#include "code\game\machinery\PDApainter.dm" -#include "code\game\machinery\portable_turret.dm" -#include "code\game\machinery\recharger.dm" -#include "code\game\machinery\rechargestation.dm" -#include "code\game\machinery\recycler.dm" -#include "code\game\machinery\requests_console.dm" -#include "code\game\machinery\robot_fabricator.dm" -#include "code\game\machinery\shieldgen.dm" -#include "code\game\machinery\Sleeper.dm" -#include "code\game\machinery\slotmachine.dm" -#include "code\game\machinery\spaceheater.dm" -#include "code\game\machinery\status_display.dm" -#include "code\game\machinery\suit_storage_unit.dm" -#include "code\game\machinery\syndicatebeacon.dm" -#include "code\game\machinery\syndicatebomb.dm" -#include "code\game\machinery\teleporter.dm" -#include "code\game\machinery\transformer.dm" -#include "code\game\machinery\vending.dm" -#include "code\game\machinery\washing_machine.dm" -#include "code\game\machinery\wishgranter.dm" -#include "code\game\machinery\atmoalter\area_atmos_computer.dm" -#include "code\game\machinery\atmoalter\canister.dm" -#include "code\game\machinery\atmoalter\meter.dm" -#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\camera\camera.dm" -#include "code\game\machinery\camera\camera_assembly.dm" -#include "code\game\machinery\camera\motion.dm" -#include "code\game\machinery\camera\presets.dm" -#include "code\game\machinery\camera\tracking.dm" -#include "code\game\machinery\computer\aifixer.dm" -#include "code\game\machinery\computer\arcade.dm" -#include "code\game\machinery\computer\atmos_alert.dm" -#include "code\game\machinery\computer\buildandrepair.dm" -#include "code\game\machinery\computer\camera.dm" -#include "code\game\machinery\computer\camera_advanced.dm" -#include "code\game\machinery\computer\card.dm" -#include "code\game\machinery\computer\cloning.dm" -#include "code\game\machinery\computer\communications.dm" -#include "code\game\machinery\computer\computer.dm" -#include "code\game\machinery\computer\crew.dm" -#include "code\game\machinery\computer\dna_console.dm" -#include "code\game\machinery\computer\law.dm" -#include "code\game\machinery\computer\medical.dm" -#include "code\game\machinery\computer\message.dm" -#include "code\game\machinery\computer\Operating.dm" -#include "code\game\machinery\computer\pod.dm" -#include "code\game\machinery\computer\power.dm" -#include "code\game\machinery\computer\prisoner.dm" -#include "code\game\machinery\computer\robot.dm" -#include "code\game\machinery\computer\security.dm" -#include "code\game\machinery\computer\shuttle.dm" -#include "code\game\machinery\computer\station_alert.dm" -#include "code\game\machinery\computer\syndicate_shuttle.dm" -#include "code\game\machinery\computer\telecrystalconsoles.dm" -#include "code\game\machinery\doors\airlock.dm" -#include "code\game\machinery\doors\airlock_electronics.dm" -#include "code\game\machinery\doors\airlock_types.dm" -#include "code\game\machinery\doors\alarmlock.dm" -#include "code\game\machinery\doors\brigdoors.dm" -#include "code\game\machinery\doors\checkForMultipleDoors.dm" -#include "code\game\machinery\doors\door.dm" -#include "code\game\machinery\doors\firedoor.dm" -#include "code\game\machinery\doors\poddoor.dm" -#include "code\game\machinery\doors\shutters.dm" -#include "code\game\machinery\doors\unpowered.dm" -#include "code\game\machinery\doors\windowdoor.dm" -#include "code\game\machinery\embedded_controller\access_controller.dm" -#include "code\game\machinery\embedded_controller\airlock_controller.dm" -#include "code\game\machinery\embedded_controller\embedded_controller_base.dm" -#include "code\game\machinery\embedded_controller\simple_vent_controller.dm" -#include "code\game\machinery\pipe\construction.dm" -#include "code\game\machinery\pipe\pipe_dispenser.dm" -#include "code\game\machinery\telecomms\broadcasting.dm" -#include "code\game\machinery\telecomms\machine_interactions.dm" -#include "code\game\machinery\telecomms\telecomunications.dm" -#include "code\game\machinery\telecomms\computers\logbrowser.dm" -#include "code\game\machinery\telecomms\computers\telemonitor.dm" -#include "code\game\machinery\telecomms\machines\allinone.dm" -#include "code\game\machinery\telecomms\machines\broadcaster.dm" -#include "code\game\machinery\telecomms\machines\bus.dm" -#include "code\game\machinery\telecomms\machines\hub.dm" -#include "code\game\machinery\telecomms\machines\processor.dm" -#include "code\game\machinery\telecomms\machines\receiver.dm" -#include "code\game\machinery\telecomms\machines\relay.dm" -#include "code\game\machinery\telecomms\machines\server.dm" -#include "code\game\mecha\mech_bay.dm" -#include "code\game\mecha\mech_fabricator.dm" -#include "code\game\mecha\mecha.dm" -#include "code\game\mecha\mecha_construction_paths.dm" -#include "code\game\mecha\mecha_control_console.dm" -#include "code\game\mecha\mecha_defense.dm" -#include "code\game\mecha\mecha_parts.dm" -#include "code\game\mecha\mecha_topic.dm" -#include "code\game\mecha\mecha_wreckage.dm" -#include "code\game\mecha\combat\combat.dm" -#include "code\game\mecha\combat\durand.dm" -#include "code\game\mecha\combat\gygax.dm" -#include "code\game\mecha\combat\honker.dm" -#include "code\game\mecha\combat\marauder.dm" -#include "code\game\mecha\combat\phazon.dm" -#include "code\game\mecha\combat\reticence.dm" -#include "code\game\mecha\equipment\mecha_equipment.dm" -#include "code\game\mecha\equipment\tools\medical_tools.dm" -#include "code\game\mecha\equipment\tools\mining_tools.dm" -#include "code\game\mecha\equipment\tools\other_tools.dm" -#include "code\game\mecha\equipment\tools\work_tools.dm" -#include "code\game\mecha\equipment\weapons\weapons.dm" -#include "code\game\mecha\medical\medical.dm" -#include "code\game\mecha\medical\odysseus.dm" -#include "code\game\mecha\working\ripley.dm" -#include "code\game\mecha\working\working.dm" -#include "code\game\objects\buckling.dm" -#include "code\game\objects\empulse.dm" -#include "code\game\objects\explosion.dm" -#include "code\game\objects\items.dm" -#include "code\game\objects\objs.dm" -#include "code\game\objects\radiation.dm" -#include "code\game\objects\structures.dm" -#include "code\game\objects\weapons.dm" -#include "code\game\objects\effects\aliens.dm" -#include "code\game\objects\effects\anomalies.dm" -#include "code\game\objects\effects\bump_teleporter.dm" -#include "code\game\objects\effects\forcefields.dm" -#include "code\game\objects\effects\gibs.dm" -#include "code\game\objects\effects\glowshroom.dm" -#include "code\game\objects\effects\landmarks.dm" -#include "code\game\objects\effects\manifest.dm" -#include "code\game\objects\effects\mines.dm" -#include "code\game\objects\effects\misc.dm" -#include "code\game\objects\effects\overlays.dm" -#include "code\game\objects\effects\portals.dm" -#include "code\game\objects\effects\spiders.dm" -#include "code\game\objects\effects\step_triggers.dm" -#include "code\game\objects\effects\decals\cleanable.dm" -#include "code\game\objects\effects\decals\contraband.dm" -#include "code\game\objects\effects\decals\crayon.dm" -#include "code\game\objects\effects\decals\decal.dm" -#include "code\game\objects\effects\decals\misc.dm" -#include "code\game\objects\effects\decals\remains.dm" -#include "code\game\objects\effects\decals\Cleanable\aliens.dm" -#include "code\game\objects\effects\decals\Cleanable\humans.dm" -#include "code\game\objects\effects\decals\Cleanable\misc.dm" -#include "code\game\objects\effects\decals\Cleanable\robots.dm" -#include "code\game\objects\effects\effect_system\effect_system.dm" -#include "code\game\objects\effects\effect_system\effects_explosion.dm" -#include "code\game\objects\effects\effect_system\effects_foam.dm" -#include "code\game\objects\effects\effect_system\effects_other.dm" -#include "code\game\objects\effects\effect_system\effects_smoke.dm" -#include "code\game\objects\effects\effect_system\effects_sparks.dm" -#include "code\game\objects\effects\effect_system\effects_water.dm" -#include "code\game\objects\effects\spawners\bombspawner.dm" -#include "code\game\objects\effects\spawners\gibspawner.dm" -#include "code\game\objects\effects\spawners\lootdrop.dm" -#include "code\game\objects\effects\spawners\structure.dm" -#include "code\game\objects\effects\spawners\vaultspawner.dm" -#include "code\game\objects\items\apc_frame.dm" -#include "code\game\objects\items\blueprints.dm" -#include "code\game\objects\items\body_egg.dm" -#include "code\game\objects\items\bodybag.dm" -#include "code\game\objects\items\candle.dm" -#include "code\game\objects\items\crayons.dm" -#include "code\game\objects\items\dehy_carp.dm" -#include "code\game\objects\items\documents.dm" -#include "code\game\objects\items\holotape.dm" -#include "code\game\objects\items\latexballoon.dm" -#include "code\game\objects\items\nuke_tools.dm" -#include "code\game\objects\items\shooting_range.dm" -#include "code\game\objects\items\toys.dm" -#include "code\game\objects\items\trash.dm" -#include "code\game\objects\items\devices\aicard.dm" -#include "code\game\objects\items\devices\camera_bug.dm" -#include "code\game\objects\items\devices\chameleonproj.dm" -#include "code\game\objects\items\devices\doorCharge.dm" -#include "code\game\objects\items\devices\flashlight.dm" -#include "code\game\objects\items\devices\geiger_counter.dm" -#include "code\game\objects\items\devices\instruments.dm" -#include "code\game\objects\items\devices\laserpointer.dm" -#include "code\game\objects\items\devices\lightreplacer.dm" -#include "code\game\objects\items\devices\megaphone.dm" -#include "code\game\objects\items\devices\multitool.dm" -#include "code\game\objects\items\devices\paicard.dm" -#include "code\game\objects\items\devices\pipe_painter.dm" -#include "code\game\objects\items\devices\pizza_bomb.dm" -#include "code\game\objects\items\devices\powersink.dm" -#include "code\game\objects\items\devices\scanners.dm" -#include "code\game\objects\items\devices\sensor_device.dm" -#include "code\game\objects\items\devices\taperecorder.dm" -#include "code\game\objects\items\devices\traitordevices.dm" -#include "code\game\objects\items\devices\transfer_valve.dm" -#include "code\game\objects\items\devices\uplinks.dm" -#include "code\game\objects\items\devices\PDA\cart.dm" -#include "code\game\objects\items\devices\PDA\PDA.dm" -#include "code\game\objects\items\devices\PDA\radio.dm" -#include "code\game\objects\items\devices\radio\beacon.dm" -#include "code\game\objects\items\devices\radio\electropack.dm" -#include "code\game\objects\items\devices\radio\encryptionkey.dm" -#include "code\game\objects\items\devices\radio\headset.dm" -#include "code\game\objects\items\devices\radio\intercom.dm" -#include "code\game\objects\items\devices\radio\radio.dm" -#include "code\game\objects\items\robot\robot_items.dm" -#include "code\game\objects\items\robot\robot_parts.dm" -#include "code\game\objects\items\robot\robot_upgrades.dm" -#include "code\game\objects\items\stacks\cash.dm" -#include "code\game\objects\items\stacks\medical.dm" -#include "code\game\objects\items\stacks\rods.dm" -#include "code\game\objects\items\stacks\stack.dm" -#include "code\game\objects\items\stacks\sheets\glass.dm" -#include "code\game\objects\items\stacks\sheets\leather.dm" -#include "code\game\objects\items\stacks\sheets\light.dm" -#include "code\game\objects\items\stacks\sheets\mineral.dm" -#include "code\game\objects\items\stacks\sheets\sheet_types.dm" -#include "code\game\objects\items\stacks\sheets\sheets.dm" -#include "code\game\objects\items\stacks\tiles\light.dm" -#include "code\game\objects\items\stacks\tiles\tile_mineral.dm" -#include "code\game\objects\items\stacks\tiles\tile_types.dm" -#include "code\game\objects\items\weapons\AI_modules.dm" -#include "code\game\objects\items\weapons\airlock_painter.dm" -#include "code\game\objects\items\weapons\cards_ids.dm" -#include "code\game\objects\items\weapons\chrono_eraser.dm" -#include "code\game\objects\items\weapons\cigs_lighters.dm" -#include "code\game\objects\items\weapons\clown_items.dm" -#include "code\game\objects\items\weapons\cosmetics.dm" -#include "code\game\objects\items\weapons\courtroom.dm" -#include "code\game\objects\items\weapons\defib.dm" -#include "code\game\objects\items\weapons\dice.dm" -#include "code\game\objects\items\weapons\dna_injector.dm" -#include "code\game\objects\items\weapons\explosives.dm" -#include "code\game\objects\items\weapons\extinguisher.dm" -#include "code\game\objects\items\weapons\flamethrower.dm" -#include "code\game\objects\items\weapons\gift_wrappaper.dm" -#include "code\game\objects\items\weapons\handcuffs.dm" -#include "code\game\objects\items\weapons\janisigns.dm" -#include "code\game\objects\items\weapons\kitchen.dm" -#include "code\game\objects\items\weapons\manuals.dm" -#include "code\game\objects\items\weapons\mop.dm" -#include "code\game\objects\items\weapons\paint.dm" -#include "code\game\objects\items\weapons\paiwire.dm" -#include "code\game\objects\items\weapons\pneumaticCannon.dm" -#include "code\game\objects\items\weapons\RCD.dm" -#include "code\game\objects\items\weapons\RPD.dm" -#include "code\game\objects\items\weapons\RSF.dm" -#include "code\game\objects\items\weapons\scrolls.dm" -#include "code\game\objects\items\weapons\shields.dm" -#include "code\game\objects\items\weapons\signs.dm" -#include "code\game\objects\items\weapons\singularityhammer.dm" -#include "code\game\objects\items\weapons\stunbaton.dm" -#include "code\game\objects\items\weapons\teleportation.dm" -#include "code\game\objects\items\weapons\teleprod.dm" -#include "code\game\objects\items\weapons\tools.dm" -#include "code\game\objects\items\weapons\twohanded.dm" -#include "code\game\objects\items\weapons\vending_items.dm" -#include "code\game\objects\items\weapons\weaponry.dm" -#include "code\game\objects\items\weapons\grenades\chem_grenade.dm" -#include "code\game\objects\items\weapons\grenades\clusterbuster.dm" -#include "code\game\objects\items\weapons\grenades\emgrenade.dm" -#include "code\game\objects\items\weapons\grenades\flashbang.dm" -#include "code\game\objects\items\weapons\grenades\ghettobomb.dm" -#include "code\game\objects\items\weapons\grenades\grenade.dm" -#include "code\game\objects\items\weapons\grenades\smokebomb.dm" -#include "code\game\objects\items\weapons\grenades\spawnergrenade.dm" -#include "code\game\objects\items\weapons\grenades\syndieminibomb.dm" -#include "code\game\objects\items\weapons\implants\implant.dm" -#include "code\game\objects\items\weapons\implants\implant_chem.dm" -#include "code\game\objects\items\weapons\implants\implant_explosive.dm" -#include "code\game\objects\items\weapons\implants\implant_freedom.dm" -#include "code\game\objects\items\weapons\implants\implant_loyality.dm" -#include "code\game\objects\items\weapons\implants\implant_misc.dm" -#include "code\game\objects\items\weapons\implants\implant_storage.dm" -#include "code\game\objects\items\weapons\implants\implant_track.dm" -#include "code\game\objects\items\weapons\implants\implantcase.dm" -#include "code\game\objects\items\weapons\implants\implantchair.dm" -#include "code\game\objects\items\weapons\implants\implanter.dm" -#include "code\game\objects\items\weapons\implants\implantpad.dm" -#include "code\game\objects\items\weapons\implants\implantuplink.dm" -#include "code\game\objects\items\weapons\melee\energy.dm" -#include "code\game\objects\items\weapons\melee\misc.dm" -#include "code\game\objects\items\weapons\storage\backpack.dm" -#include "code\game\objects\items\weapons\storage\bags.dm" -#include "code\game\objects\items\weapons\storage\belt.dm" -#include "code\game\objects\items\weapons\storage\book.dm" -#include "code\game\objects\items\weapons\storage\boxes.dm" -#include "code\game\objects\items\weapons\storage\briefcase.dm" -#include "code\game\objects\items\weapons\storage\fancy.dm" -#include "code\game\objects\items\weapons\storage\firstaid.dm" -#include "code\game\objects\items\weapons\storage\internal.dm" -#include "code\game\objects\items\weapons\storage\lockbox.dm" -#include "code\game\objects\items\weapons\storage\secure.dm" -#include "code\game\objects\items\weapons\storage\storage.dm" -#include "code\game\objects\items\weapons\storage\toolbox.dm" -#include "code\game\objects\items\weapons\storage\uplink_kits.dm" -#include "code\game\objects\items\weapons\storage\wallets.dm" -#include "code\game\objects\items\weapons\tanks\jetpack.dm" -#include "code\game\objects\items\weapons\tanks\tank_types.dm" -#include "code\game\objects\items\weapons\tanks\tanks.dm" -#include "code\game\objects\items\weapons\tanks\watertank.dm" -#include "code\game\objects\structures\ai_core.dm" -#include "code\game\objects\structures\artstuff.dm" -#include "code\game\objects\structures\barsigns.dm" -#include "code\game\objects\structures\bedsheet_bin.dm" -#include "code\game\objects\structures\displaycase.dm" -#include "code\game\objects\structures\door_assembly.dm" -#include "code\game\objects\structures\dresser.dm" -#include "code\game\objects\structures\electricchair.dm" -#include "code\game\objects\structures\extinguisher.dm" -#include "code\game\objects\structures\false_walls.dm" -#include "code\game\objects\structures\fireaxe.dm" -#include "code\game\objects\structures\flora.dm" -#include "code\game\objects\structures\girders.dm" -#include "code\game\objects\structures\grille.dm" -#include "code\game\objects\structures\guncase.dm" -#include "code\game\objects\structures\hivebot.dm" -#include "code\game\objects\structures\janicart.dm" -#include "code\game\objects\structures\kitchen_spike.dm" -#include "code\game\objects\structures\ladders.dm" -#include "code\game\objects\structures\lattice.dm" -#include "code\game\objects\structures\mineral_doors.dm" -#include "code\game\objects\structures\mirror.dm" -#include "code\game\objects\structures\mop_bucket.dm" -#include "code\game\objects\structures\morgue.dm" -#include "code\game\objects\structures\musician.dm" -#include "code\game\objects\structures\noticeboard.dm" -#include "code\game\objects\structures\plasticflaps.dm" -#include "code\game\objects\structures\reflector.dm" -#include "code\game\objects\structures\safe.dm" -#include "code\game\objects\structures\showcase.dm" -#include "code\game\objects\structures\signs.dm" -#include "code\game\objects\structures\spirit_board.dm" -#include "code\game\objects\structures\statues.dm" -#include "code\game\objects\structures\table_frames.dm" -#include "code\game\objects\structures\tables_racks.dm" -#include "code\game\objects\structures\tank_dispenser.dm" -#include "code\game\objects\structures\target_stake.dm" -#include "code\game\objects\structures\watercloset.dm" -#include "code\game\objects\structures\windoor_assembly.dm" -#include "code\game\objects\structures\window.dm" -#include "code\game\objects\structures\beds_chairs\alien_nest.dm" -#include "code\game\objects\structures\beds_chairs\bed.dm" -#include "code\game\objects\structures\beds_chairs\chair.dm" -#include "code\game\objects\structures\crates_lockers\bins.dm" -#include "code\game\objects\structures\crates_lockers\closets.dm" -#include "code\game\objects\structures\crates_lockers\crates.dm" -#include "code\game\objects\structures\crates_lockers\largecrate.dm" -#include "code\game\objects\structures\crates_lockers\closets\cardboardbox.dm" -#include "code\game\objects\structures\crates_lockers\closets\crittercrate.dm" -#include "code\game\objects\structures\crates_lockers\closets\fitness.dm" -#include "code\game\objects\structures\crates_lockers\closets\gimmick.dm" -#include "code\game\objects\structures\crates_lockers\closets\job_closets.dm" -#include "code\game\objects\structures\crates_lockers\closets\l3closet.dm" -#include "code\game\objects\structures\crates_lockers\closets\statue.dm" -#include "code\game\objects\structures\crates_lockers\closets\syndicate.dm" -#include "code\game\objects\structures\crates_lockers\closets\utility_closets.dm" -#include "code\game\objects\structures\crates_lockers\closets\wardrobe.dm" -#include "code\game\objects\structures\crates_lockers\closets\secure\bar.dm" -#include "code\game\objects\structures\crates_lockers\closets\secure\cargo.dm" -#include "code\game\objects\structures\crates_lockers\closets\secure\engineering.dm" -#include "code\game\objects\structures\crates_lockers\closets\secure\freezer.dm" -#include "code\game\objects\structures\crates_lockers\closets\secure\hydroponics.dm" -#include "code\game\objects\structures\crates_lockers\closets\secure\medical.dm" -#include "code\game\objects\structures\crates_lockers\closets\secure\misc.dm" -#include "code\game\objects\structures\crates_lockers\closets\secure\personal.dm" -#include "code\game\objects\structures\crates_lockers\closets\secure\scientist.dm" -#include "code\game\objects\structures\crates_lockers\closets\secure\secure_closets.dm" -#include "code\game\objects\structures\crates_lockers\closets\secure\security.dm" -#include "code\game\objects\structures\transit_tubes\station.dm" -#include "code\game\objects\structures\transit_tubes\transit_tube.dm" -#include "code\game\objects\structures\transit_tubes\transit_tube_construction.dm" -#include "code\game\objects\structures\transit_tubes\transit_tube_pod.dm" -#include "code\game\pooling\pool.dm" -#include "code\game\turfs\simulated.dm" -#include "code\game\turfs\turf.dm" -#include "code\game\turfs\simulated\dirtystation.dm" -#include "code\game\turfs\simulated\floor.dm" -#include "code\game\turfs\simulated\walls.dm" -#include "code\game\turfs\simulated\walls_mineral.dm" -#include "code\game\turfs\simulated\walls_misc.dm" -#include "code\game\turfs\simulated\walls_reinforced.dm" -#include "code\game\turfs\simulated\floor\fancy_floor.dm" -#include "code\game\turfs\simulated\floor\light_floor.dm" -#include "code\game\turfs\simulated\floor\mineral_floor.dm" -#include "code\game\turfs\simulated\floor\misc_floor.dm" -#include "code\game\turfs\simulated\floor\plasteel_floor.dm" -#include "code\game\turfs\simulated\floor\plating.dm" -#include "code\game\turfs\space\space.dm" -#include "code\game\turfs\space\transit.dm" -#include "code\game\verbs\ooc.dm" -#include "code\game\verbs\suicide.dm" -#include "code\game\verbs\who.dm" -#include "code\js\byjax.dm" -#include "code\js\menus.dm" -#include "code\LINDA\LINDA_fire.dm" -#include "code\LINDA\LINDA_system.dm" -#include "code\LINDA\LINDA_turf_tile.dm" -#include "code\modules\admin\admin.dm" -#include "code\modules\admin\admin_investigate.dm" -#include "code\modules\admin\admin_memo.dm" -#include "code\modules\admin\admin_ranks.dm" -#include "code\modules\admin\admin_verbs.dm" -#include "code\modules\admin\banappearance.dm" -#include "code\modules\admin\banjob.dm" -#include "code\modules\admin\create_mob.dm" -#include "code\modules\admin\create_object.dm" -#include "code\modules\admin\create_poll.dm" -#include "code\modules\admin\create_turf.dm" -#include "code\modules\admin\holder2.dm" -#include "code\modules\admin\IsBanned.dm" -#include "code\modules\admin\NewBan.dm" -#include "code\modules\admin\player_panel.dm" -#include "code\modules\admin\secrets.dm" -#include "code\modules\admin\sql_notes.dm" -#include "code\modules\admin\stickyban.dm" -#include "code\modules\admin\topic.dm" -#include "code\modules\admin\watchlist.dm" -#include "code\modules\admin\DB ban\functions.dm" -#include "code\modules\admin\permissionverbs\permissionedit.dm" -#include "code\modules\admin\verbs\adminhelp.dm" -#include "code\modules\admin\verbs\adminjump.dm" -#include "code\modules\admin\verbs\adminpm.dm" -#include "code\modules\admin\verbs\adminsay.dm" -#include "code\modules\admin\verbs\atmosdebug.dm" -#include "code\modules\admin\verbs\bluespacearty.dm" -#include "code\modules\admin\verbs\BrokenInhands.dm" -#include "code\modules\admin\verbs\buildmode.dm" -#include "code\modules\admin\verbs\cinematic.dm" -#include "code\modules\admin\verbs\deadsay.dm" -#include "code\modules\admin\verbs\debug.dm" -#include "code\modules\admin\verbs\diagnostics.dm" -#include "code\modules\admin\verbs\fps.dm" -#include "code\modules\admin\verbs\getlogs.dm" -#include "code\modules\admin\verbs\machine_upgrade.dm" -#include "code\modules\admin\verbs\manipulate_organs.dm" -#include "code\modules\admin\verbs\mapping.dm" -#include "code\modules\admin\verbs\maprotation.dm" -#include "code\modules\admin\verbs\massmodvar.dm" -#include "code\modules\admin\verbs\modifyvariables.dm" -#include "code\modules\admin\verbs\one_click_antag.dm" -#include "code\modules\admin\verbs\onlyone.dm" -#include "code\modules\admin\verbs\panicbunker.dm" -#include "code\modules\admin\verbs\playsound.dm" -#include "code\modules\admin\verbs\possess.dm" -#include "code\modules\admin\verbs\pray.dm" -#include "code\modules\admin\verbs\randomverbs.dm" -#include "code\modules\admin\verbs\reestablish_db_connection.dm" -#include "code\modules\admin\verbs\tripAI.dm" -#include "code\modules\admin\verbs\SDQL2\SDQL_2.dm" -#include "code\modules\admin\verbs\SDQL2\SDQL_2_parser.dm" -#include "code\modules\assembly\assembly.dm" -#include "code\modules\assembly\bomb.dm" -#include "code\modules\assembly\doorcontrol.dm" -#include "code\modules\assembly\flash.dm" -#include "code\modules\assembly\health.dm" -#include "code\modules\assembly\helpers.dm" -#include "code\modules\assembly\holder.dm" -#include "code\modules\assembly\igniter.dm" -#include "code\modules\assembly\infrared.dm" -#include "code\modules\assembly\mousetrap.dm" -#include "code\modules\assembly\proximity.dm" -#include "code\modules\assembly\shock_kit.dm" -#include "code\modules\assembly\signaler.dm" -#include "code\modules\assembly\timer.dm" -#include "code\modules\assembly\voice.dm" -#include "code\modules\awaymissions\bluespaceartillery.dm" -#include "code\modules\awaymissions\corpse.dm" -#include "code\modules\awaymissions\exile.dm" -#include "code\modules\awaymissions\gateway.dm" -#include "code\modules\awaymissions\pamphlet.dm" -#include "code\modules\awaymissions\signpost.dm" -#include "code\modules\awaymissions\zlevel.dm" -#include "code\modules\awaymissions\maploader\dmm_suite.dm" -#include "code\modules\awaymissions\maploader\reader.dm" -#include "code\modules\awaymissions\maploader\swapmaps.dm" -#include "code\modules\awaymissions\maploader\writer.dm" -#include "code\modules\awaymissions\mission_code\Academy.dm" -#include "code\modules\awaymissions\mission_code\blackmarketpackers.dm" -#include "code\modules\awaymissions\mission_code\centcomAway.dm" -#include "code\modules\awaymissions\mission_code\challenge.dm" -#include "code\modules\awaymissions\mission_code\snowdin.dm" -#include "code\modules\awaymissions\mission_code\spacebattle.dm" -#include "code\modules\awaymissions\mission_code\stationCollision.dm" -#include "code\modules\awaymissions\mission_code\wildwest.dm" -#include "code\modules\client\asset_cache.dm" -#include "code\modules\client\client defines.dm" -#include "code\modules\client\client procs.dm" -#include "code\modules\client\message.dm" -#include "code\modules\client\preferences.dm" -#include "code\modules\client\preferences_savefile.dm" -#include "code\modules\client\preferences_toggles.dm" -#include "code\modules\clothing\clothing.dm" -#include "code\modules\clothing\glasses\engine_goggles.dm" -#include "code\modules\clothing\glasses\glasses.dm" -#include "code\modules\clothing\glasses\hud.dm" -#include "code\modules\clothing\gloves\boxing.dm" -#include "code\modules\clothing\gloves\color.dm" -#include "code\modules\clothing\gloves\miscellaneous.dm" -#include "code\modules\clothing\head\collectable.dm" -#include "code\modules\clothing\head\hardhat.dm" -#include "code\modules\clothing\head\helmet.dm" -#include "code\modules\clothing\head\jobs.dm" -#include "code\modules\clothing\head\misc.dm" -#include "code\modules\clothing\head\misc_special.dm" -#include "code\modules\clothing\head\soft_caps.dm" -#include "code\modules\clothing\masks\boxing.dm" -#include "code\modules\clothing\masks\breath.dm" -#include "code\modules\clothing\masks\gasmask.dm" -#include "code\modules\clothing\masks\hailer.dm" -#include "code\modules\clothing\masks\miscellaneous.dm" -#include "code\modules\clothing\outfits\ert.dm" -#include "code\modules\clothing\outfits\standard.dm" -#include "code\modules\clothing\shoes\bananashoes.dm" -#include "code\modules\clothing\shoes\colour.dm" -#include "code\modules\clothing\shoes\magboots.dm" -#include "code\modules\clothing\shoes\miscellaneous.dm" -#include "code\modules\clothing\spacesuits\chronosuit.dm" -#include "code\modules\clothing\spacesuits\hardsuit.dm" -#include "code\modules\clothing\spacesuits\miscellaneous.dm" -#include "code\modules\clothing\spacesuits\plasmamen.dm" -#include "code\modules\clothing\spacesuits\syndi.dm" -#include "code\modules\clothing\suits\armor.dm" -#include "code\modules\clothing\suits\bio.dm" -#include "code\modules\clothing\suits\cloaks.dm" -#include "code\modules\clothing\suits\jobs.dm" -#include "code\modules\clothing\suits\labcoat.dm" -#include "code\modules\clothing\suits\miscellaneous.dm" -#include "code\modules\clothing\suits\toggles.dm" -#include "code\modules\clothing\suits\utility.dm" -#include "code\modules\clothing\suits\wiz_robe.dm" -#include "code\modules\clothing\under\chameleon.dm" -#include "code\modules\clothing\under\color.dm" -#include "code\modules\clothing\under\miscellaneous.dm" -#include "code\modules\clothing\under\pants.dm" -#include "code\modules\clothing\under\shorts.dm" -#include "code\modules\clothing\under\syndicate.dm" -#include "code\modules\clothing\under\ties.dm" -#include "code\modules\clothing\under\jobs\civilian.dm" -#include "code\modules\clothing\under\jobs\engineering.dm" -#include "code\modules\clothing\under\jobs\medsci.dm" -#include "code\modules\clothing\under\jobs\security.dm" -#include "code\modules\crafting\guncrafting.dm" -#include "code\modules\crafting\recipes.dm" -#include "code\modules\crafting\table.dm" -#include "code\modules\detectivework\detective_work.dm" -#include "code\modules\detectivework\evidence.dm" -#include "code\modules\detectivework\footprints_and_rag.dm" -#include "code\modules\detectivework\scanner.dm" -#include "code\modules\emoji\emoji_parse.dm" -#include "code\modules\events\abductor.dm" -#include "code\modules\events\alien_infestation.dm" -#include "code\modules\events\anomaly.dm" -#include "code\modules\events\anomaly_bluespace.dm" -#include "code\modules\events\anomaly_flux.dm" -#include "code\modules\events\anomaly_grav.dm" -#include "code\modules\events\anomaly_pyro.dm" -#include "code\modules\events\anomaly_vortex.dm" -#include "code\modules\events\blob.dm" -#include "code\modules\events\brand_intelligence.dm" -#include "code\modules\events\camerafailure.dm" -#include "code\modules\events\carp_migration.dm" -#include "code\modules\events\communications_blackout.dm" -#include "code\modules\events\disease_outbreak.dm" -#include "code\modules\events\dust.dm" -#include "code\modules\events\electrical_storm.dm" -#include "code\modules\events\event.dm" -#include "code\modules\events\false_alarm.dm" -#include "code\modules\events\immovable_rod.dm" -#include "code\modules\events\ion_storm.dm" -#include "code\modules\events\mass_hallucination.dm" -#include "code\modules\events\meateor_wave.dm" -#include "code\modules\events\meteor_wave.dm" -#include "code\modules\events\operative.dm" -#include "code\modules\events\prison_break.dm" -#include "code\modules\events\radiation_storm.dm" -#include "code\modules\events\shuttle_loan.dm" -#include "code\modules\events\spacevine.dm" -#include "code\modules\events\spider_infestation.dm" -#include "code\modules\events\spontaneous_appendicitis.dm" -#include "code\modules\events\vent_clog.dm" -#include "code\modules\events\wormholes.dm" -#include "code\modules\events\holiday\halloween.dm" -#include "code\modules\events\holiday\xmas.dm" -#include "code\modules\events\wizard\aid.dm" -#include "code\modules\events\wizard\blobies.dm" -#include "code\modules\events\wizard\curseditems.dm" -#include "code\modules\events\wizard\departmentrevolt.dm" -#include "code\modules\events\wizard\fakeexplosion.dm" -#include "code\modules\events\wizard\ghost.dm" -#include "code\modules\events\wizard\greentext.dm" -#include "code\modules\events\wizard\imposter.dm" -#include "code\modules\events\wizard\invincible.dm" -#include "code\modules\events\wizard\lava.dm" -#include "code\modules\events\wizard\magicarp.dm" -#include "code\modules\events\wizard\petsplosion.dm" -#include "code\modules\events\wizard\race.dm" -#include "code\modules\events\wizard\rpgloot.dm" -#include "code\modules\events\wizard\shuffle.dm" -#include "code\modules\events\wizard\summons.dm" -#include "code\modules\flufftext\Dreaming.dm" -#include "code\modules\flufftext\Hallucination.dm" -#include "code\modules\flufftext\TextFilters.dm" -#include "code\modules\food&drinks\food.dm" -#include "code\modules\food&drinks\drinks\drinks.dm" -#include "code\modules\food&drinks\drinks\drinks\bottle.dm" -#include "code\modules\food&drinks\drinks\drinks\drinkingglass.dm" -#include "code\modules\food&drinks\food\condiment.dm" -#include "code\modules\food&drinks\food\customizables.dm" -#include "code\modules\food&drinks\food\snacks.dm" -#include "code\modules\food&drinks\food\snacks_bread.dm" -#include "code\modules\food&drinks\food\snacks_burgers.dm" -#include "code\modules\food&drinks\food\snacks_cake.dm" -#include "code\modules\food&drinks\food\snacks_egg.dm" -#include "code\modules\food&drinks\food\snacks_meat.dm" -#include "code\modules\food&drinks\food\snacks_other.dm" -#include "code\modules\food&drinks\food\snacks_pastry.dm" -#include "code\modules\food&drinks\food\snacks_pie.dm" -#include "code\modules\food&drinks\food\snacks_pizza.dm" -#include "code\modules\food&drinks\food\snacks_salad.dm" -#include "code\modules\food&drinks\food\snacks_sandwichtoast.dm" -#include "code\modules\food&drinks\food\snacks_soup.dm" -#include "code\modules\food&drinks\food\snacks_spaghetti.dm" -#include "code\modules\food&drinks\food\snacks_vend.dm" -#include "code\modules\food&drinks\food\snacks\dough.dm" -#include "code\modules\food&drinks\food\snacks\meat.dm" -#include "code\modules\food&drinks\kitchen machinery\food_cart.dm" -#include "code\modules\food&drinks\kitchen machinery\gibber.dm" -#include "code\modules\food&drinks\kitchen machinery\icecream_vat.dm" -#include "code\modules\food&drinks\kitchen machinery\juicer.dm" -#include "code\modules\food&drinks\kitchen machinery\microwave.dm" -#include "code\modules\food&drinks\kitchen machinery\monkeyrecycler.dm" -#include "code\modules\food&drinks\kitchen machinery\processor.dm" -#include "code\modules\food&drinks\kitchen machinery\smartfridge.dm" -#include "code\modules\food&drinks\recipes\drinks_recipes.dm" -#include "code\modules\food&drinks\recipes\food_mixtures.dm" -#include "code\modules\food&drinks\recipes\tablecraft\recipes_bread.dm" -#include "code\modules\food&drinks\recipes\tablecraft\recipes_burger.dm" -#include "code\modules\food&drinks\recipes\tablecraft\recipes_cake.dm" -#include "code\modules\food&drinks\recipes\tablecraft\recipes_egg.dm" -#include "code\modules\food&drinks\recipes\tablecraft\recipes_meat.dm" -#include "code\modules\food&drinks\recipes\tablecraft\recipes_misc.dm" -#include "code\modules\food&drinks\recipes\tablecraft\recipes_pastry.dm" -#include "code\modules\food&drinks\recipes\tablecraft\recipes_pie.dm" -#include "code\modules\food&drinks\recipes\tablecraft\recipes_pizza.dm" -#include "code\modules\food&drinks\recipes\tablecraft\recipes_salad.dm" -#include "code\modules\food&drinks\recipes\tablecraft\recipes_sandwich.dm" -#include "code\modules\food&drinks\recipes\tablecraft\recipes_soup.dm" -#include "code\modules\food&drinks\recipes\tablecraft\recipes_spaghetti.dm" -#include "code\modules\games\cards.dm" -#include "code\modules\holiday\easter.dm" -#include "code\modules\holiday\holidays.dm" -#include "code\modules\holodeck\area_copy.dm" -#include "code\modules\holodeck\areas.dm" -#include "code\modules\holodeck\computer.dm" -#include "code\modules\holodeck\computer_funcs.dm" -#include "code\modules\holodeck\holo_effect.dm" -#include "code\modules\holodeck\items.dm" -#include "code\modules\holodeck\turfs.dm" -#include "code\modules\html_interface\html_interface.dm" -#include "code\modules\html_interface\html_interface_client.dm" -#include "code\modules\html_interface\cards\cards.dm" -#include "code\modules\html_interface\nanotrasen\nanotrasen.dm" -#include "code\modules\hydroponics\biogenerator.dm" -#include "code\modules\hydroponics\grown.dm" -#include "code\modules\hydroponics\growninedible.dm" -#include "code\modules\hydroponics\hydroitemdefines.dm" -#include "code\modules\hydroponics\hydroponics.dm" -#include "code\modules\hydroponics\seed_extractor.dm" -#include "code\modules\hydroponics\seeds.dm" -#include "code\modules\json\json.dm" -#include "code\modules\library\lib_items.dm" -#include "code\modules\library\lib_machines.dm" -#include "code\modules\library\lib_readme.dm" -#include "code\modules\library\random_books.dm" -#include "code\modules\lighting\lighting_system.dm" -#include "code\modules\mining\abandoned_crates.dm" -#include "code\modules\mining\equipment_locker.dm" -#include "code\modules\mining\machine_input_output_plates.dm" -#include "code\modules\mining\machine_processing.dm" -#include "code\modules\mining\machine_stacking.dm" -#include "code\modules\mining\machine_unloading.dm" -#include "code\modules\mining\mine_areas.dm" -#include "code\modules\mining\mine_items.dm" -#include "code\modules\mining\mine_turfs.dm" -#include "code\modules\mining\mint.dm" -#include "code\modules\mining\money_bag.dm" -#include "code\modules\mining\ores_coins.dm" -#include "code\modules\mining\satchel_ore_boxdm.dm" -#include "code\modules\mining\laborcamp\laborminerals.dm" -#include "code\modules\mining\laborcamp\laborshuttle.dm" -#include "code\modules\mining\laborcamp\laborstacker.dm" -#include "code\modules\mob\death.dm" -#include "code\modules\mob\interactive.dm" -#include "code\modules\mob\inventory.dm" -#include "code\modules\mob\login.dm" -#include "code\modules\mob\logout.dm" -#include "code\modules\mob\mob.dm" -#include "code\modules\mob\mob_cleanup.dm" -#include "code\modules\mob\mob_defines.dm" -#include "code\modules\mob\mob_grab.dm" -#include "code\modules\mob\mob_helpers.dm" -#include "code\modules\mob\mob_movement.dm" -#include "code\modules\mob\mob_transformation_simple.dm" -#include "code\modules\mob\say.dm" -#include "code\modules\mob\transform_procs.dm" -#include "code\modules\mob\update_icons.dm" -#include "code\modules\mob\camera\camera.dm" -#include "code\modules\mob\dead\death.dm" -#include "code\modules\mob\dead\observer\login.dm" -#include "code\modules\mob\dead\observer\logout.dm" -#include "code\modules\mob\dead\observer\observer.dm" -#include "code\modules\mob\dead\observer\say.dm" -#include "code\modules\mob\living\bloodcrawl.dm" -#include "code\modules\mob\living\damage_procs.dm" -#include "code\modules\mob\living\death.dm" -#include "code\modules\mob\living\emote.dm" -#include "code\modules\mob\living\life.dm" -#include "code\modules\mob\living\living.dm" -#include "code\modules\mob\living\living_defense.dm" -#include "code\modules\mob\living\living_defines.dm" -#include "code\modules\mob\living\login.dm" -#include "code\modules\mob\living\logout.dm" -#include "code\modules\mob\living\say.dm" -#include "code\modules\mob\living\ventcrawling.dm" -#include "code\modules\mob\living\carbon\carbon.dm" -#include "code\modules\mob\living\carbon\carbon_defense.dm" -#include "code\modules\mob\living\carbon\carbon_defines.dm" -#include "code\modules\mob\living\carbon\death.dm" -#include "code\modules\mob\living\carbon\emote.dm" -#include "code\modules\mob\living\carbon\examine.dm" -#include "code\modules\mob\living\carbon\inventory.dm" -#include "code\modules\mob\living\carbon\life.dm" -#include "code\modules\mob\living\carbon\say.dm" -#include "code\modules\mob\living\carbon\update_icons.dm" -#include "code\modules\mob\living\carbon\alien\alien.dm" -#include "code\modules\mob\living\carbon\alien\alien_defense.dm" -#include "code\modules\mob\living\carbon\alien\death.dm" -#include "code\modules\mob\living\carbon\alien\life.dm" -#include "code\modules\mob\living\carbon\alien\login.dm" -#include "code\modules\mob\living\carbon\alien\logout.dm" -#include "code\modules\mob\living\carbon\alien\organs.dm" -#include "code\modules\mob\living\carbon\alien\say.dm" -#include "code\modules\mob\living\carbon\alien\screen.dm" -#include "code\modules\mob\living\carbon\alien\humanoid\alien_powers.dm" -#include "code\modules\mob\living\carbon\alien\humanoid\death.dm" -#include "code\modules\mob\living\carbon\alien\humanoid\emote.dm" -#include "code\modules\mob\living\carbon\alien\humanoid\humanoid.dm" -#include "code\modules\mob\living\carbon\alien\humanoid\inventory.dm" -#include "code\modules\mob\living\carbon\alien\humanoid\life.dm" -#include "code\modules\mob\living\carbon\alien\humanoid\login.dm" -#include "code\modules\mob\living\carbon\alien\humanoid\queen.dm" -#include "code\modules\mob\living\carbon\alien\humanoid\update_icons.dm" -#include "code\modules\mob\living\carbon\alien\humanoid\caste\drone.dm" -#include "code\modules\mob\living\carbon\alien\humanoid\caste\hunter.dm" -#include "code\modules\mob\living\carbon\alien\humanoid\caste\praetorian.dm" -#include "code\modules\mob\living\carbon\alien\humanoid\caste\sentinel.dm" -#include "code\modules\mob\living\carbon\alien\larva\death.dm" -#include "code\modules\mob\living\carbon\alien\larva\emote.dm" -#include "code\modules\mob\living\carbon\alien\larva\inventory.dm" -#include "code\modules\mob\living\carbon\alien\larva\larva.dm" -#include "code\modules\mob\living\carbon\alien\larva\life.dm" -#include "code\modules\mob\living\carbon\alien\larva\powers.dm" -#include "code\modules\mob\living\carbon\alien\larva\update_icons.dm" -#include "code\modules\mob\living\carbon\alien\special\alien_embryo.dm" -#include "code\modules\mob\living\carbon\alien\special\facehugger.dm" -#include "code\modules\mob\living\carbon\brain\brain.dm" -#include "code\modules\mob\living\carbon\brain\brain_item.dm" -#include "code\modules\mob\living\carbon\brain\death.dm" -#include "code\modules\mob\living\carbon\brain\emote.dm" -#include "code\modules\mob\living\carbon\brain\life.dm" -#include "code\modules\mob\living\carbon\brain\login.dm" -#include "code\modules\mob\living\carbon\brain\MMI.dm" -#include "code\modules\mob\living\carbon\brain\posibrain.dm" -#include "code\modules\mob\living\carbon\brain\say.dm" -#include "code\modules\mob\living\carbon\human\blood.dm" -#include "code\modules\mob\living\carbon\human\death.dm" -#include "code\modules\mob\living\carbon\human\emote.dm" -#include "code\modules\mob\living\carbon\human\examine.dm" -#include "code\modules\mob\living\carbon\human\human.dm" -#include "code\modules\mob\living\carbon\human\human_attackalien.dm" -#include "code\modules\mob\living\carbon\human\human_attackhand.dm" -#include "code\modules\mob\living\carbon\human\human_attackpaw.dm" -#include "code\modules\mob\living\carbon\human\human_damage.dm" -#include "code\modules\mob\living\carbon\human\human_defense.dm" -#include "code\modules\mob\living\carbon\human\human_defines.dm" -#include "code\modules\mob\living\carbon\human\human_helpers.dm" -#include "code\modules\mob\living\carbon\human\human_movement.dm" -#include "code\modules\mob\living\carbon\human\inventory.dm" -#include "code\modules\mob\living\carbon\human\life.dm" -#include "code\modules\mob\living\carbon\human\login.dm" -#include "code\modules\mob\living\carbon\human\say.dm" -#include "code\modules\mob\living\carbon\human\species.dm" -#include "code\modules\mob\living\carbon\human\species_types.dm" -#include "code\modules\mob\living\carbon\human\update_icons.dm" -#include "code\modules\mob\living\carbon\human\whisper.dm" -#include "code\modules\mob\living\carbon\monkey\death.dm" -#include "code\modules\mob\living\carbon\monkey\emote.dm" -#include "code\modules\mob\living\carbon\monkey\inventory.dm" -#include "code\modules\mob\living\carbon\monkey\life.dm" -#include "code\modules\mob\living\carbon\monkey\login.dm" -#include "code\modules\mob\living\carbon\monkey\monkey.dm" -#include "code\modules\mob\living\carbon\monkey\update_icons.dm" -#include "code\modules\mob\living\silicon\death.dm" -#include "code\modules\mob\living\silicon\laws.dm" -#include "code\modules\mob\living\silicon\login.dm" -#include "code\modules\mob\living\silicon\say.dm" -#include "code\modules\mob\living\silicon\silicon.dm" -#include "code\modules\mob\living\silicon\ai\ai.dm" -#include "code\modules\mob\living\silicon\ai\death.dm" -#include "code\modules\mob\living\silicon\ai\examine.dm" -#include "code\modules\mob\living\silicon\ai\laws.dm" -#include "code\modules\mob\living\silicon\ai\life.dm" -#include "code\modules\mob\living\silicon\ai\login.dm" -#include "code\modules\mob\living\silicon\ai\logout.dm" -#include "code\modules\mob\living\silicon\ai\say.dm" -#include "code\modules\mob\living\silicon\ai\vox_sounds.dm" -#include "code\modules\mob\living\silicon\ai\freelook\cameranet.dm" -#include "code\modules\mob\living\silicon\ai\freelook\chunk.dm" -#include "code\modules\mob\living\silicon\ai\freelook\eye.dm" -#include "code\modules\mob\living\silicon\ai\freelook\read_me.dm" -#include "code\modules\mob\living\silicon\pai\death.dm" -#include "code\modules\mob\living\silicon\pai\examine.dm" -#include "code\modules\mob\living\silicon\pai\life.dm" -#include "code\modules\mob\living\silicon\pai\pai.dm" -#include "code\modules\mob\living\silicon\pai\personality.dm" -#include "code\modules\mob\living\silicon\pai\say.dm" -#include "code\modules\mob\living\silicon\pai\software.dm" -#include "code\modules\mob\living\silicon\robot\death.dm" -#include "code\modules\mob\living\silicon\robot\emote.dm" -#include "code\modules\mob\living\silicon\robot\examine.dm" -#include "code\modules\mob\living\silicon\robot\inventory.dm" -#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\robot.dm" -#include "code\modules\mob\living\silicon\robot\robot_modules.dm" -#include "code\modules\mob\living\silicon\robot\robot_movement.dm" -#include "code\modules\mob\living\silicon\robot\say.dm" -#include "code\modules\mob\living\simple_animal\constructs.dm" -#include "code\modules\mob\living\simple_animal\corpse.dm" -#include "code\modules\mob\living\simple_animal\parrot.dm" -#include "code\modules\mob\living\simple_animal\shade.dm" -#include "code\modules\mob\living\simple_animal\simple_animal.dm" -#include "code\modules\mob\living\simple_animal\spawner.dm" -#include "code\modules\mob\living\simple_animal\bot\bot.dm" -#include "code\modules\mob\living\simple_animal\bot\cleanbot.dm" -#include "code\modules\mob\living\simple_animal\bot\construction.dm" -#include "code\modules\mob\living\simple_animal\bot\ed209bot.dm" -#include "code\modules\mob\living\simple_animal\bot\floorbot.dm" -#include "code\modules\mob\living\simple_animal\bot\medbot.dm" -#include "code\modules\mob\living\simple_animal\bot\mulebot.dm" -#include "code\modules\mob\living\simple_animal\bot\secbot.dm" -#include "code\modules\mob\living\simple_animal\bot_swarm\swarmer.dm" -#include "code\modules\mob\living\simple_animal\bot_swarm\swarmer_event.dm" -#include "code\modules\mob\living\simple_animal\friendly\butterfly.dm" -#include "code\modules\mob\living\simple_animal\friendly\cat.dm" -#include "code\modules\mob\living\simple_animal\friendly\crab.dm" -#include "code\modules\mob\living\simple_animal\friendly\dog.dm" -#include "code\modules\mob\living\simple_animal\friendly\farm_animals.dm" -#include "code\modules\mob\living\simple_animal\friendly\fox.dm" -#include "code\modules\mob\living\simple_animal\friendly\lizard.dm" -#include "code\modules\mob\living\simple_animal\friendly\mouse.dm" -#include "code\modules\mob\living\simple_animal\friendly\pet.dm" -#include "code\modules\mob\living\simple_animal\friendly\drone\_drone.dm" -#include "code\modules\mob\living\simple_animal\friendly\drone\drones_as_items.dm" -#include "code\modules\mob\living\simple_animal\friendly\drone\extra_drone_types.dm" -#include "code\modules\mob\living\simple_animal\friendly\drone\interaction.dm" -#include "code\modules\mob\living\simple_animal\friendly\drone\inventory.dm" -#include "code\modules\mob\living\simple_animal\friendly\drone\say.dm" -#include "code\modules\mob\living\simple_animal\friendly\drone\verbs.dm" -#include "code\modules\mob\living\simple_animal\friendly\drone\visuals_icons.dm" -#include "code\modules\mob\living\simple_animal\guardian\guardian.dm" -#include "code\modules\mob\living\simple_animal\hostile\alien.dm" -#include "code\modules\mob\living\simple_animal\hostile\bear.dm" -#include "code\modules\mob\living\simple_animal\hostile\bees.dm" -#include "code\modules\mob\living\simple_animal\hostile\carp.dm" -#include "code\modules\mob\living\simple_animal\hostile\creature.dm" -#include "code\modules\mob\living\simple_animal\hostile\eyeballs.dm" -#include "code\modules\mob\living\simple_animal\hostile\faithless.dm" -#include "code\modules\mob\living\simple_animal\hostile\giant_spider.dm" -#include "code\modules\mob\living\simple_animal\hostile\headcrab.dm" -#include "code\modules\mob\living\simple_animal\hostile\hivebot.dm" -#include "code\modules\mob\living\simple_animal\hostile\hostile.dm" -#include "code\modules\mob\living\simple_animal\hostile\illusion.dm" -#include "code\modules\mob\living\simple_animal\hostile\killertomato.dm" -#include "code\modules\mob\living\simple_animal\hostile\mimic.dm" -#include "code\modules\mob\living\simple_animal\hostile\mining_mobs.dm" -#include "code\modules\mob\living\simple_animal\hostile\mushroom.dm" -#include "code\modules\mob\living\simple_animal\hostile\pirate.dm" -#include "code\modules\mob\living\simple_animal\hostile\russian.dm" -#include "code\modules\mob\living\simple_animal\hostile\skeleton.dm" -#include "code\modules\mob\living\simple_animal\hostile\statue.dm" -#include "code\modules\mob\living\simple_animal\hostile\syndicate.dm" -#include "code\modules\mob\living\simple_animal\hostile\tree.dm" -#include "code\modules\mob\living\simple_animal\hostile\venus_human_trap.dm" -#include "code\modules\mob\living\simple_animal\hostile\wizard.dm" -#include "code\modules\mob\living\simple_animal\hostile\zombie.dm" -#include "code\modules\mob\living\simple_animal\hostile\retaliate\bat.dm" -#include "code\modules\mob\living\simple_animal\hostile\retaliate\clown.dm" -#include "code\modules\mob\living\simple_animal\hostile\retaliate\retaliate.dm" -#include "code\modules\mob\living\simple_animal\morph\morph.dm" -#include "code\modules\mob\living\simple_animal\revenant\revenant.dm" -#include "code\modules\mob\living\simple_animal\revenant\revenant_abilities.dm" -#include "code\modules\mob\living\simple_animal\revenant\revenant_blight.dm" -#include "code\modules\mob\living\simple_animal\revenant\revenant_spawn_event.dm" -#include "code\modules\mob\living\simple_animal\slaughter\slaughter.dm" -#include "code\modules\mob\living\simple_animal\slaughter\slaughterevent.dm" -#include "code\modules\mob\living\simple_animal\slime\death.dm" -#include "code\modules\mob\living\simple_animal\slime\emote.dm" -#include "code\modules\mob\living\simple_animal\slime\life.dm" -#include "code\modules\mob\living\simple_animal\slime\powers.dm" -#include "code\modules\mob\living\simple_animal\slime\say.dm" -#include "code\modules\mob\living\simple_animal\slime\slime.dm" -#include "code\modules\mob\living\simple_animal\slime\subtypes.dm" -#include "code\modules\mob\new_player\login.dm" -#include "code\modules\mob\new_player\logout.dm" -#include "code\modules\mob\new_player\new_player.dm" -#include "code\modules\mob\new_player\poll.dm" -#include "code\modules\mob\new_player\preferences_setup.dm" -#include "code\modules\mob\new_player\sprite_accessories.dm" -#include "code\modules\nano\external.dm" -#include "code\modules\nano\nanoui.dm" -#include "code\modules\nano\subsystem.dm" -#include "code\modules\nano\states\admin.dm" -#include "code\modules\nano\states\conscious.dm" -#include "code\modules\nano\states\contained.dm" -#include "code\modules\nano\states\deep_inventory.dm" -#include "code\modules\nano\states\default.dm" -#include "code\modules\nano\states\hands.dm" -#include "code\modules\nano\states\inventory.dm" -#include "code\modules\nano\states\notcontained.dm" -#include "code\modules\nano\states\physical.dm" -#include "code\modules\nano\states\self.dm" -#include "code\modules\nano\states\states.dm" -#include "code\modules\nano\states\zlevel.dm" -#include "code\modules\ninja\__ninjaDefines.dm" -#include "code\modules\ninja\admin_ninja_verbs.dm" -#include "code\modules\ninja\energy_katana.dm" -#include "code\modules\ninja\ninja_event.dm" -#include "code\modules\ninja\Ninja_Readme.dm" -#include "code\modules\ninja\suit\gloves.dm" -#include "code\modules\ninja\suit\head.dm" -#include "code\modules\ninja\suit\mask.dm" -#include "code\modules\ninja\suit\ninjaDrainAct.dm" -#include "code\modules\ninja\suit\shoes.dm" -#include "code\modules\ninja\suit\suit.dm" -#include "code\modules\ninja\suit\suit_attackby.dm" -#include "code\modules\ninja\suit\suit_initialisation.dm" -#include "code\modules\ninja\suit\suit_process.dm" -#include "code\modules\ninja\suit\suit_verbs_handlers.dm" -#include "code\modules\ninja\suit\n_suit_verbs\energy_net_nets.dm" -#include "code\modules\ninja\suit\n_suit_verbs\ninja_adrenaline.dm" -#include "code\modules\ninja\suit\n_suit_verbs\ninja_cost_check.dm" -#include "code\modules\ninja\suit\n_suit_verbs\ninja_empulse.dm" -#include "code\modules\ninja\suit\n_suit_verbs\ninja_net.dm" -#include "code\modules\ninja\suit\n_suit_verbs\ninja_smoke.dm" -#include "code\modules\ninja\suit\n_suit_verbs\ninja_stars.dm" -#include "code\modules\ninja\suit\n_suit_verbs\ninja_stealth.dm" -#include "code\modules\ninja\suit\n_suit_verbs\ninja_sword_recall.dm" -#include "code\modules\ninja\suit\n_suit_verbs\ninja_teleporting.dm" -#include "code\modules\paperwork\clipboard.dm" -#include "code\modules\paperwork\filingcabinet.dm" -#include "code\modules\paperwork\folders.dm" -#include "code\modules\paperwork\handlabeler.dm" -#include "code\modules\paperwork\paper.dm" -#include "code\modules\paperwork\paperbin.dm" -#include "code\modules\paperwork\pen.dm" -#include "code\modules\paperwork\photocopier.dm" -#include "code\modules\paperwork\photography.dm" -#include "code\modules\paperwork\stamps.dm" -#include "code\modules\power\apc.dm" -#include "code\modules\power\cable.dm" -#include "code\modules\power\cell.dm" -#include "code\modules\power\engine.dm" -#include "code\modules\power\generator.dm" -#include "code\modules\power\gravitygenerator.dm" -#include "code\modules\power\lighting.dm" -#include "code\modules\power\port_gen.dm" -#include "code\modules\power\power.dm" -#include "code\modules\power\powernet.dm" -#include "code\modules\power\smes.dm" -#include "code\modules\power\solar.dm" -#include "code\modules\power\switch.dm" -#include "code\modules\power\terminal.dm" -#include "code\modules\power\tracker.dm" -#include "code\modules\power\turbine.dm" -#include "code\modules\power\antimatter\containment_jar.dm" -#include "code\modules\power\antimatter\control.dm" -#include "code\modules\power\antimatter\shielding.dm" -#include "code\modules\power\singularity\collector.dm" -#include "code\modules\power\singularity\containment_field.dm" -#include "code\modules\power\singularity\emitter.dm" -#include "code\modules\power\singularity\field_generator.dm" -#include "code\modules\power\singularity\generator.dm" -#include "code\modules\power\singularity\investigate.dm" -#include "code\modules\power\singularity\narsie.dm" -#include "code\modules\power\singularity\singularity.dm" -#include "code\modules\power\singularity\particle_accelerator\particle.dm" -#include "code\modules\power\singularity\particle_accelerator\particle_accelerator.dm" -#include "code\modules\power\singularity\particle_accelerator\particle_chamber.dm" -#include "code\modules\power\singularity\particle_accelerator\particle_control.dm" -#include "code\modules\power\singularity\particle_accelerator\particle_emitter.dm" -#include "code\modules\power\singularity\particle_accelerator\particle_power.dm" -#include "code\modules\power\supermatter\supermatter.dm" -#include "code\modules\power\supermatter\supermatter_crate.dm" -#include "code\modules\procedural mapping\mapGenerator.dm" -#include "code\modules\procedural mapping\mapGeneratorModule.dm" -#include "code\modules\procedural mapping\mapGeneratorReadme.dm" -#include "code\modules\procedural mapping\mapGeneratorModules\helpers.dm" -#include "code\modules\procedural mapping\mapGeneratorModules\nature.dm" -#include "code\modules\procedural mapping\mapGenerators\asteroid.dm" -#include "code\modules\procedural mapping\mapGenerators\nature.dm" -#include "code\modules\procedural mapping\mapGenerators\shuttle.dm" -#include "code\modules\procedural mapping\mapGenerators\syndicate.dm" -#include "code\modules\projectiles\ammunition.dm" -#include "code\modules\projectiles\firing.dm" -#include "code\modules\projectiles\gun.dm" -#include "code\modules\projectiles\pins.dm" -#include "code\modules\projectiles\projectile.dm" -#include "code\modules\projectiles\ammunition\ammo_casings.dm" -#include "code\modules\projectiles\ammunition\boxes.dm" -#include "code\modules\projectiles\ammunition\energy.dm" -#include "code\modules\projectiles\ammunition\magazines.dm" -#include "code\modules\projectiles\ammunition\special.dm" -#include "code\modules\projectiles\guns\energy.dm" -#include "code\modules\projectiles\guns\grenade_launcher.dm" -#include "code\modules\projectiles\guns\magic.dm" -#include "code\modules\projectiles\guns\medbeam.dm" -#include "code\modules\projectiles\guns\projectile.dm" -#include "code\modules\projectiles\guns\syringe_gun.dm" -#include "code\modules\projectiles\guns\energy\laser.dm" -#include "code\modules\projectiles\guns\energy\nuclear.dm" -#include "code\modules\projectiles\guns\energy\pulse.dm" -#include "code\modules\projectiles\guns\energy\special.dm" -#include "code\modules\projectiles\guns\energy\stun.dm" -#include "code\modules\projectiles\guns\magic\staff.dm" -#include "code\modules\projectiles\guns\magic\wand.dm" -#include "code\modules\projectiles\guns\projectile\automatic.dm" -#include "code\modules\projectiles\guns\projectile\launchers.dm" -#include "code\modules\projectiles\guns\projectile\pistol.dm" -#include "code\modules\projectiles\guns\projectile\revolver.dm" -#include "code\modules\projectiles\guns\projectile\shotgun.dm" -#include "code\modules\projectiles\guns\projectile\sniper.dm" -#include "code\modules\projectiles\guns\projectile\toy.dm" -#include "code\modules\projectiles\projectile\beams.dm" -#include "code\modules\projectiles\projectile\bullets.dm" -#include "code\modules\projectiles\projectile\energy.dm" -#include "code\modules\projectiles\projectile\magic.dm" -#include "code\modules\projectiles\projectile\reusable.dm" -#include "code\modules\projectiles\projectile\special.dm" -#include "code\modules\reagents\reagent_containers.dm" -#include "code\modules\reagents\reagent_dispenser.dm" -#include "code\modules\reagents\chemistry\colors.dm" -#include "code\modules\reagents\chemistry\holder.dm" -#include "code\modules\reagents\chemistry\readme.dm" -#include "code\modules\reagents\chemistry\reagents.dm" -#include "code\modules\reagents\chemistry\recipes.dm" -#include "code\modules\reagents\chemistry\machinery\chem_dispenser.dm" -#include "code\modules\reagents\chemistry\machinery\chem_heater.dm" -#include "code\modules\reagents\chemistry\machinery\chem_master.dm" -#include "code\modules\reagents\chemistry\machinery\pandemic.dm" -#include "code\modules\reagents\chemistry\machinery\reagentgrinder.dm" -#include "code\modules\reagents\chemistry\reagents\alcohol_reagents.dm" -#include "code\modules\reagents\chemistry\reagents\blob_reagents.dm" -#include "code\modules\reagents\chemistry\reagents\drink_reagents.dm" -#include "code\modules\reagents\chemistry\reagents\drug_reagents.dm" -#include "code\modules\reagents\chemistry\reagents\food_reagents.dm" -#include "code\modules\reagents\chemistry\reagents\medicine_reagents.dm" -#include "code\modules\reagents\chemistry\reagents\other_reagents.dm" -#include "code\modules\reagents\chemistry\reagents\pyrotechnic_reagents.dm" -#include "code\modules\reagents\chemistry\reagents\toxin_reagents.dm" -#include "code\modules\reagents\chemistry\recipes\drugs.dm" -#include "code\modules\reagents\chemistry\recipes\medicine.dm" -#include "code\modules\reagents\chemistry\recipes\others.dm" -#include "code\modules\reagents\chemistry\recipes\pyrotechnics.dm" -#include "code\modules\reagents\chemistry\recipes\slime_extracts.dm" -#include "code\modules\reagents\chemistry\recipes\toxins.dm" -#include "code\modules\reagents\reagent_containers\blood_pack.dm" -#include "code\modules\reagents\reagent_containers\borghydro.dm" -#include "code\modules\reagents\reagent_containers\bottle.dm" -#include "code\modules\reagents\reagent_containers\dropper.dm" -#include "code\modules\reagents\reagent_containers\glass.dm" -#include "code\modules\reagents\reagent_containers\hypospray.dm" -#include "code\modules\reagents\reagent_containers\patch.dm" -#include "code\modules\reagents\reagent_containers\pill.dm" -#include "code\modules\reagents\reagent_containers\spray.dm" -#include "code\modules\reagents\reagent_containers\syringes.dm" -#include "code\modules\recycling\conveyor2.dm" -#include "code\modules\recycling\disposal-construction.dm" -#include "code\modules\recycling\disposal-structures.dm" -#include "code\modules\recycling\disposal-unit.dm" -#include "code\modules\recycling\sortingmachinery.dm" -#include "code\modules\research\circuitprinter.dm" -#include "code\modules\research\designs.dm" -#include "code\modules\research\destructive_analyzer.dm" -#include "code\modules\research\experimentor.dm" -#include "code\modules\research\message_server.dm" -#include "code\modules\research\protolathe.dm" -#include "code\modules\research\rd-readme.dm" -#include "code\modules\research\rdconsole.dm" -#include "code\modules\research\rdmachines.dm" -#include "code\modules\research\research.dm" -#include "code\modules\research\server.dm" -#include "code\modules\research\stock_parts.dm" -#include "code\modules\research\designs\AI_module_designs.dm" -#include "code\modules\research\designs\autolathe_designs.dm" -#include "code\modules\research\designs\comp_board_designs.dm" -#include "code\modules\research\designs\machine_designs.dm" -#include "code\modules\research\designs\mecha_designs.dm" -#include "code\modules\research\designs\mechfabricator_designs.dm" -#include "code\modules\research\designs\medical_designs.dm" -#include "code\modules\research\designs\power_designs.dm" -#include "code\modules\research\designs\stock_parts_designs.dm" -#include "code\modules\research\designs\telecomms_designs.dm" -#include "code\modules\research\designs\weapon_designs.dm" -#include "code\modules\research\xenobiology\xenobio_camera.dm" -#include "code\modules\research\xenobiology\xenobiology.dm" -#include "code\modules\security levels\keycard authentication.dm" -#include "code\modules\security levels\security levels.dm" -#include "code\modules\shuttle\shuttle.dm" -#include "code\modules\space transition\space_transition.dm" -#include "code\modules\surgery\cavity_implant.dm" -#include "code\modules\surgery\core_removal.dm" -#include "code\modules\surgery\dental_implant.dm" -#include "code\modules\surgery\dethralling.dm" -#include "code\modules\surgery\eye_surgery.dm" -#include "code\modules\surgery\gender_reassignment.dm" -#include "code\modules\surgery\generic_steps.dm" -#include "code\modules\surgery\helpers.dm" -#include "code\modules\surgery\implant_removal.dm" -#include "code\modules\surgery\limb augmentation.dm" -#include "code\modules\surgery\lipoplasty.dm" -#include "code\modules\surgery\organ_manipulation.dm" -#include "code\modules\surgery\plastic_surgery.dm" -#include "code\modules\surgery\remove_embedded_object.dm" -#include "code\modules\surgery\surgery.dm" -#include "code\modules\surgery\surgery_step.dm" -#include "code\modules\surgery\tools.dm" -#include "code\modules\surgery\organs\augments_external.dm" -#include "code\modules\surgery\organs\augments_eyes.dm" -#include "code\modules\surgery\organs\augments_internal.dm" -#include "code\modules\surgery\organs\helpers.dm" -#include "code\modules\surgery\organs\organ_external.dm" -#include "code\modules\surgery\organs\organ_internal.dm" -#include "code\modules\telesci\bscrystal.dm" -#include "code\modules\telesci\gps.dm" -#include "code\modules\telesci\telepad.dm" -#include "code\modules\telesci\telesci_computer.dm" -#include "code\modules\tooltip\tooltip.dm" -#include "code\orphaned procs\AStar.dm" -#include "code\orphaned procs\dbcore.dm" -#include "code\orphaned procs\priority_announce.dm" -#include "code\orphaned procs\statistics.dm" -#include "interface\interface.dm" -#include "interface\stylesheet.dm" -#include "interface\skin.dmf" -// END_INCLUDE +// DM Environment file for tgstation.dme. +// All manual changes should be made outside the BEGIN_ and END_ blocks. +// New source code should be placed in .dm files: choose File/New --> Code File. +// BEGIN_INTERNALS +// END_INTERNALS + +// BEGIN_FILE_DIR +#define FILE_DIR . +// END_FILE_DIR + +// BEGIN_PREFERENCES +#define DEBUG +// END_PREFERENCES + +// BEGIN_INCLUDE +#include "_maps\__MAP_DEFINES.dm" +#include "_maps\tgstation2.dm" +#include "code\_compile_options.dm" +#include "code\hub.dm" +#include "code\world.dm" +#include "code\__DATASTRUCTURES\heap.dm" +#include "code\__DATASTRUCTURES\linked_lists.dm" +#include "code\__DATASTRUCTURES\priority_queue.dm" +#include "code\__DATASTRUCTURES\stacks.dm" +#include "code\__DEFINES\admin.dm" +#include "code\__DEFINES\atmospherics.dm" +#include "code\__DEFINES\bots.dm" +#include "code\__DEFINES\clothing.dm" +#include "code\__DEFINES\combat.dm" +#include "code\__DEFINES\flags.dm" +#include "code\__DEFINES\genetics.dm" +#include "code\__DEFINES\hud.dm" +#include "code\__DEFINES\is_helpers.dm" +#include "code\__DEFINES\machines.dm" +#include "code\__DEFINES\math.dm" +#include "code\__DEFINES\misc.dm" +#include "code\__DEFINES\nano.dm" +#include "code\__DEFINES\pipe_construction.dm" +#include "code\__DEFINES\preferences.dm" +#include "code\__DEFINES\qdel.dm" +#include "code\__DEFINES\reagents.dm" +#include "code\__DEFINES\role_preferences.dm" +#include "code\__DEFINES\say.dm" +#include "code\__DEFINES\sight.dm" +#include "code\__DEFINES\stat.dm" +#include "code\__DEFINES\tablecrafting.dm" +#include "code\__HELPERS\_logging.dm" +#include "code\__HELPERS\_string_lists.dm" +#include "code\__HELPERS\cmp.dm" +#include "code\__HELPERS\files.dm" +#include "code\__HELPERS\game.dm" +#include "code\__HELPERS\global_lists.dm" +#include "code\__HELPERS\icon_smoothing.dm" +#include "code\__HELPERS\icons.dm" +#include "code\__HELPERS\lists.dm" +#include "code\__HELPERS\maths.dm" +#include "code\__HELPERS\matrices.dm" +#include "code\__HELPERS\mobs.dm" +#include "code\__HELPERS\names.dm" +#include "code\__HELPERS\sanitize_values.dm" +#include "code\__HELPERS\text.dm" +#include "code\__HELPERS\time.dm" +#include "code\__HELPERS\type2type.dm" +#include "code\__HELPERS\unsorted.dm" +#include "code\__HELPERS\bygex\bygex.dm" +#include "code\__HELPERS\sorts\__main.dm" +#include "code\__HELPERS\sorts\InsertSort.dm" +#include "code\__HELPERS\sorts\MergeSort.dm" +#include "code\__HELPERS\sorts\TimSort.dm" +#include "code\_globalvars\configuration.dm" +#include "code\_globalvars\database.dm" +#include "code\_globalvars\game_modes.dm" +#include "code\_globalvars\genetics.dm" +#include "code\_globalvars\logging.dm" +#include "code\_globalvars\misc.dm" +#include "code\_globalvars\station.dm" +#include "code\_globalvars\lists\flavor_misc.dm" +#include "code\_globalvars\lists\mapping.dm" +#include "code\_globalvars\lists\mobs.dm" +#include "code\_globalvars\lists\names.dm" +#include "code\_globalvars\lists\objects.dm" +#include "code\_onclick\adjacent.dm" +#include "code\_onclick\ai.dm" +#include "code\_onclick\click.dm" +#include "code\_onclick\cyborg.dm" +#include "code\_onclick\drag_drop.dm" +#include "code\_onclick\god.dm" +#include "code\_onclick\item_attack.dm" +#include "code\_onclick\observer.dm" +#include "code\_onclick\other_mobs.dm" +#include "code\_onclick\overmind.dm" +#include "code\_onclick\telekinesis.dm" +#include "code\_onclick\hud\_defines.dm" +#include "code\_onclick\hud\action.dm" +#include "code\_onclick\hud\ai.dm" +#include "code\_onclick\hud\alert.dm" +#include "code\_onclick\hud\alien.dm" +#include "code\_onclick\hud\alien_larva.dm" +#include "code\_onclick\hud\drones.dm" +#include "code\_onclick\hud\ghost.dm" +#include "code\_onclick\hud\hud.dm" +#include "code\_onclick\hud\human.dm" +#include "code\_onclick\hud\monkey.dm" +#include "code\_onclick\hud\movable_screen_objects.dm" +#include "code\_onclick\hud\other_mobs.dm" +#include "code\_onclick\hud\robot.dm" +#include "code\_onclick\hud\screen_objects.dm" +#include "code\ATMOSPHERICS\atmospherics.dm" +#include "code\ATMOSPHERICS\datum_pipeline.dm" +#include "code\ATMOSPHERICS\components\components_base.dm" +#include "code\ATMOSPHERICS\components\binary_devices\binary_atmos_base.dm" +#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\pump.dm" +#include "code\ATMOSPHERICS\components\binary_devices\valve.dm" +#include "code\ATMOSPHERICS\components\binary_devices\volume_pump.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" +#include "code\ATMOSPHERICS\components\unary_devices\cold_sink.dm" +#include "code\ATMOSPHERICS\components\unary_devices\cryo.dm" +#include "code\ATMOSPHERICS\components\unary_devices\Freezer.dm" +#include "code\ATMOSPHERICS\components\unary_devices\generator_input.dm" +#include "code\ATMOSPHERICS\components\unary_devices\heat_exchanger.dm" +#include "code\ATMOSPHERICS\components\unary_devices\heat_source.dm" +#include "code\ATMOSPHERICS\components\unary_devices\outlet_injector.dm" +#include "code\ATMOSPHERICS\components\unary_devices\oxygen_generator.dm" +#include "code\ATMOSPHERICS\components\unary_devices\portables_connector.dm" +#include "code\ATMOSPHERICS\components\unary_devices\tank.dm" +#include "code\ATMOSPHERICS\components\unary_devices\unary_base.dm" +#include "code\ATMOSPHERICS\components\unary_devices\vent_pump.dm" +#include "code\ATMOSPHERICS\components\unary_devices\vent_scrubber.dm" +#include "code\ATMOSPHERICS\pipes\manifold.dm" +#include "code\ATMOSPHERICS\pipes\manifold4w.dm" +#include "code\ATMOSPHERICS\pipes\pipes.dm" +#include "code\ATMOSPHERICS\pipes\simple.dm" +#include "code\ATMOSPHERICS\pipes\heat_exchange\he_pipes.dm" +#include "code\ATMOSPHERICS\pipes\heat_exchange\junction.dm" +#include "code\ATMOSPHERICS\pipes\heat_exchange\manifold.dm" +#include "code\ATMOSPHERICS\pipes\heat_exchange\simple.dm" +#include "code\controllers\admin.dm" +#include "code\controllers\configuration.dm" +#include "code\controllers\controller.dm" +#include "code\controllers\failsafe.dm" +#include "code\controllers\master.dm" +#include "code\controllers\subsystem.dm" +#include "code\controllers\subsystem\air.dm" +#include "code\controllers\subsystem\assets.dm" +#include "code\controllers\subsystem\diseases.dm" +#include "code\controllers\subsystem\events.dm" +#include "code\controllers\subsystem\garbage.dm" +#include "code\controllers\subsystem\jobs.dm" +#include "code\controllers\subsystem\lighting.dm" +#include "code\controllers\subsystem\machines.dm" +#include "code\controllers\subsystem\minimap.dm" +#include "code\controllers\subsystem\mobs.dm" +#include "code\controllers\subsystem\nano.dm" +#include "code\controllers\subsystem\npcpool.dm" +#include "code\controllers\subsystem\objects.dm" +#include "code\controllers\subsystem\pai.dm" +#include "code\controllers\subsystem\radio.dm" +#include "code\controllers\subsystem\server_maintenance.dm" +#include "code\controllers\subsystem\shuttles.dm" +#include "code\controllers\subsystem\sun.dm" +#include "code\controllers\subsystem\ticker.dm" +#include "code\controllers\subsystem\timer.dm" +#include "code\controllers\subsystem\voting.dm" +#include "code\controllers\subsystem\shuttles\emergency.dm" +#include "code\controllers\subsystem\shuttles\supply.dm" +#include "code\datums\ai_laws.dm" +#include "code\datums\beam.dm" +#include "code\datums\browser.dm" +#include "code\datums\datacore.dm" +#include "code\datums\datumvars.dm" +#include "code\datums\debug.dm" +#include "code\datums\gas_mixture.dm" +#include "code\datums\hud.dm" +#include "code\datums\martial.dm" +#include "code\datums\material_container.dm" +#include "code\datums\mind.dm" +#include "code\datums\modules.dm" +#include "code\datums\mutations.dm" +#include "code\datums\outfit.dm" +#include "code\datums\recipe.dm" +#include "code\datums\spell.dm" +#include "code\datums\supplypacks.dm" +#include "code\datums\uplink_item.dm" +#include "code\datums\votablemap.dm" +#include "code\datums\diseases\_disease.dm" +#include "code\datums\diseases\_MobProcs.dm" +#include "code\datums\diseases\anxiety.dm" +#include "code\datums\diseases\appendicitis.dm" +#include "code\datums\diseases\beesease.dm" +#include "code\datums\diseases\brainrot.dm" +#include "code\datums\diseases\cold.dm" +#include "code\datums\diseases\cold9.dm" +#include "code\datums\diseases\dna_spread.dm" +#include "code\datums\diseases\fake_gbs.dm" +#include "code\datums\diseases\flu.dm" +#include "code\datums\diseases\fluspanish.dm" +#include "code\datums\diseases\gbs.dm" +#include "code\datums\diseases\magnitis.dm" +#include "code\datums\diseases\pierrot_throat.dm" +#include "code\datums\diseases\retrovirus.dm" +#include "code\datums\diseases\rhumba_beat.dm" +#include "code\datums\diseases\transformation.dm" +#include "code\datums\diseases\wizarditis.dm" +#include "code\datums\diseases\advance\advance.dm" +#include "code\datums\diseases\advance\presets.dm" +#include "code\datums\diseases\advance\symptoms\beard.dm" +#include "code\datums\diseases\advance\symptoms\choking.dm" +#include "code\datums\diseases\advance\symptoms\confusion.dm" +#include "code\datums\diseases\advance\symptoms\cough.dm" +#include "code\datums\diseases\advance\symptoms\damage_converter.dm" +#include "code\datums\diseases\advance\symptoms\deafness.dm" +#include "code\datums\diseases\advance\symptoms\dizzy.dm" +#include "code\datums\diseases\advance\symptoms\fever.dm" +#include "code\datums\diseases\advance\symptoms\fire.dm" +#include "code\datums\diseases\advance\symptoms\flesh_eating.dm" +#include "code\datums\diseases\advance\symptoms\genetics.dm" +#include "code\datums\diseases\advance\symptoms\hallucigen.dm" +#include "code\datums\diseases\advance\symptoms\headache.dm" +#include "code\datums\diseases\advance\symptoms\heal.dm" +#include "code\datums\diseases\advance\symptoms\itching.dm" +#include "code\datums\diseases\advance\symptoms\oxygen.dm" +#include "code\datums\diseases\advance\symptoms\shedding.dm" +#include "code\datums\diseases\advance\symptoms\shivering.dm" +#include "code\datums\diseases\advance\symptoms\skin.dm" +#include "code\datums\diseases\advance\symptoms\sneeze.dm" +#include "code\datums\diseases\advance\symptoms\stimulant.dm" +#include "code\datums\diseases\advance\symptoms\symptoms.dm" +#include "code\datums\diseases\advance\symptoms\vision.dm" +#include "code\datums\diseases\advance\symptoms\voice_change.dm" +#include "code\datums\diseases\advance\symptoms\vomit.dm" +#include "code\datums\diseases\advance\symptoms\weakness.dm" +#include "code\datums\diseases\advance\symptoms\weight.dm" +#include "code\datums\diseases\advance\symptoms\youth.dm" +#include "code\datums\helper_datums\construction_datum.dm" +#include "code\datums\helper_datums\events.dm" +#include "code\datums\helper_datums\getrev.dm" +#include "code\datums\helper_datums\icon_snapshot.dm" +#include "code\datums\helper_datums\teleport.dm" +#include "code\datums\helper_datums\topic_input.dm" +#include "code\datums\spells\area_teleport.dm" +#include "code\datums\spells\barnyard.dm" +#include "code\datums\spells\bloodcrawl.dm" +#include "code\datums\spells\charge.dm" +#include "code\datums\spells\conjure.dm" +#include "code\datums\spells\construct_spells.dm" +#include "code\datums\spells\dumbfire.dm" +#include "code\datums\spells\emplosion.dm" +#include "code\datums\spells\ethereal_jaunt.dm" +#include "code\datums\spells\explosion.dm" +#include "code\datums\spells\genetic.dm" +#include "code\datums\spells\inflict_handler.dm" +#include "code\datums\spells\knock.dm" +#include "code\datums\spells\lichdom.dm" +#include "code\datums\spells\lightning.dm" +#include "code\datums\spells\mime.dm" +#include "code\datums\spells\mind_transfer.dm" +#include "code\datums\spells\projectile.dm" +#include "code\datums\spells\santa.dm" +#include "code\datums\spells\shapeshift.dm" +#include "code\datums\spells\summonitem.dm" +#include "code\datums\spells\touch_attacks.dm" +#include "code\datums\spells\trigger.dm" +#include "code\datums\spells\turf_teleport.dm" +#include "code\datums\spells\wizard.dm" +#include "code\datums\wires\airlock.dm" +#include "code\datums\wires\alarm.dm" +#include "code\datums\wires\apc.dm" +#include "code\datums\wires\autolathe.dm" +#include "code\datums\wires\explosive.dm" +#include "code\datums\wires\mulebot.dm" +#include "code\datums\wires\particle_accelerator.dm" +#include "code\datums\wires\pizza_bomb.dm" +#include "code\datums\wires\r_n_d.dm" +#include "code\datums\wires\radio.dm" +#include "code\datums\wires\robot.dm" +#include "code\datums\wires\syndicatebomb.dm" +#include "code\datums\wires\vending.dm" +#include "code\datums\wires\wires.dm" +#include "code\game\asteroid.dm" +#include "code\game\atoms.dm" +#include "code\game\atoms_movable.dm" +#include "code\game\communications.dm" +#include "code\game\data_huds.dm" +#include "code\game\dna.dm" +#include "code\game\say.dm" +#include "code\game\shuttle_engines.dm" +#include "code\game\skincmd.dm" +#include "code\game\sound.dm" +#include "code\game\area\ai_monitored.dm" +#include "code\game\area\areas.dm" +#include "code\game\area\Space Station 13 areas.dm" +#include "code\game\gamemodes\antag_hud.dm" +#include "code\game\gamemodes\antag_spawner.dm" +#include "code\game\gamemodes\events.dm" +#include "code\game\gamemodes\factions.dm" +#include "code\game\gamemodes\game_mode.dm" +#include "code\game\gamemodes\intercept_report.dm" +#include "code\game\gamemodes\objective.dm" +#include "code\game\gamemodes\objective_items.dm" +#include "code\game\gamemodes\setupgame.dm" +#include "code\game\gamemodes\abduction\abduction.dm" +#include "code\game\gamemodes\abduction\abduction_gear.dm" +#include "code\game\gamemodes\abduction\abduction_surgery.dm" +#include "code\game\gamemodes\abduction\gland.dm" +#include "code\game\gamemodes\abduction\machinery\camera.dm" +#include "code\game\gamemodes\abduction\machinery\console.dm" +#include "code\game\gamemodes\abduction\machinery\dispenser.dm" +#include "code\game\gamemodes\abduction\machinery\experiment.dm" +#include "code\game\gamemodes\abduction\machinery\pad.dm" +#include "code\game\gamemodes\blob\blob.dm" +#include "code\game\gamemodes\blob\blob_finish.dm" +#include "code\game\gamemodes\blob\blob_report.dm" +#include "code\game\gamemodes\blob\overmind.dm" +#include "code\game\gamemodes\blob\powers.dm" +#include "code\game\gamemodes\blob\theblob.dm" +#include "code\game\gamemodes\blob\blobs\blob_mobs.dm" +#include "code\game\gamemodes\blob\blobs\core.dm" +#include "code\game\gamemodes\blob\blobs\factory.dm" +#include "code\game\gamemodes\blob\blobs\node.dm" +#include "code\game\gamemodes\blob\blobs\resource.dm" +#include "code\game\gamemodes\blob\blobs\shield.dm" +#include "code\game\gamemodes\blob\blobs\storage.dm" +#include "code\game\gamemodes\changeling\changeling.dm" +#include "code\game\gamemodes\changeling\changeling_power.dm" +#include "code\game\gamemodes\changeling\evolution_menu.dm" +#include "code\game\gamemodes\changeling\traitor_chan.dm" +#include "code\game\gamemodes\changeling\powers\absorb.dm" +#include "code\game\gamemodes\changeling\powers\adrenaline.dm" +#include "code\game\gamemodes\changeling\powers\augmented_eyesight.dm" +#include "code\game\gamemodes\changeling\powers\biodegrade.dm" +#include "code\game\gamemodes\changeling\powers\chameleon_skin.dm" +#include "code\game\gamemodes\changeling\powers\digitalcamo.dm" +#include "code\game\gamemodes\changeling\powers\fakedeath.dm" +#include "code\game\gamemodes\changeling\powers\fleshmend.dm" +#include "code\game\gamemodes\changeling\powers\glands.dm" +#include "code\game\gamemodes\changeling\powers\headcrab.dm" +#include "code\game\gamemodes\changeling\powers\hivemind.dm" +#include "code\game\gamemodes\changeling\powers\humanform.dm" +#include "code\game\gamemodes\changeling\powers\lesserform.dm" +#include "code\game\gamemodes\changeling\powers\mimic_voice.dm" +#include "code\game\gamemodes\changeling\powers\mutations.dm" +#include "code\game\gamemodes\changeling\powers\panacea.dm" +#include "code\game\gamemodes\changeling\powers\revive.dm" +#include "code\game\gamemodes\changeling\powers\shriek.dm" +#include "code\game\gamemodes\changeling\powers\spiders.dm" +#include "code\game\gamemodes\changeling\powers\strained_muscles.dm" +#include "code\game\gamemodes\changeling\powers\tiny_prick.dm" +#include "code\game\gamemodes\changeling\powers\transform.dm" +#include "code\game\gamemodes\cult\cult.dm" +#include "code\game\gamemodes\cult\cult_items.dm" +#include "code\game\gamemodes\cult\cult_structures.dm" +#include "code\game\gamemodes\cult\ritual.dm" +#include "code\game\gamemodes\cult\runes.dm" +#include "code\game\gamemodes\cult\talisman.dm" +#include "code\game\gamemodes\extended\extended.dm" +#include "code\game\gamemodes\gang\dominator.dm" +#include "code\game\gamemodes\gang\gang.dm" +#include "code\game\gamemodes\gang\gang_datum.dm" +#include "code\game\gamemodes\gang\gang_pen.dm" +#include "code\game\gamemodes\gang\recaller.dm" +#include "code\game\gamemodes\handofgod\_handofgod.dm" +#include "code\game\gamemodes\handofgod\actions.dm" +#include "code\game\gamemodes\handofgod\god.dm" +#include "code\game\gamemodes\handofgod\items.dm" +#include "code\game\gamemodes\handofgod\objectives.dm" +#include "code\game\gamemodes\handofgod\powers.dm" +#include "code\game\gamemodes\handofgod\structures.dm" +#include "code\game\gamemodes\handofgod\traps.dm" +#include "code\game\gamemodes\malfunction\Malf_Modules.dm" +#include "code\game\gamemodes\malfunction\malfunction.dm" +#include "code\game\gamemodes\meteor\meteor.dm" +#include "code\game\gamemodes\meteor\meteors.dm" +#include "code\game\gamemodes\monkey\monkey.dm" +#include "code\game\gamemodes\nuclear\nuclear.dm" +#include "code\game\gamemodes\nuclear\nuclear_challenge.dm" +#include "code\game\gamemodes\nuclear\nuclearbomb.dm" +#include "code\game\gamemodes\nuclear\pinpointer.dm" +#include "code\game\gamemodes\revolution\revolution.dm" +#include "code\game\gamemodes\sandbox\airlock_maker.dm" +#include "code\game\gamemodes\sandbox\h_sandbox.dm" +#include "code\game\gamemodes\sandbox\sandbox.dm" +#include "code\game\gamemodes\shadowling\ascendant_shadowling.dm" +#include "code\game\gamemodes\shadowling\shadowling.dm" +#include "code\game\gamemodes\shadowling\shadowling_abilities.dm" +#include "code\game\gamemodes\shadowling\shadowling_items.dm" +#include "code\game\gamemodes\shadowling\special_shadowling_abilities.dm" +#include "code\game\gamemodes\traitor\double_agents.dm" +#include "code\game\gamemodes\traitor\traitor.dm" +#include "code\game\gamemodes\wizard\artefact.dm" +#include "code\game\gamemodes\wizard\godhand.dm" +#include "code\game\gamemodes\wizard\raginmages.dm" +#include "code\game\gamemodes\wizard\rightandwrong.dm" +#include "code\game\gamemodes\wizard\soulstone.dm" +#include "code\game\gamemodes\wizard\spellbook.dm" +#include "code\game\gamemodes\wizard\wizard.dm" +#include "code\game\jobs\access.dm" +#include "code\game\jobs\jobs.dm" +#include "code\game\jobs\whitelist.dm" +#include "code\game\jobs\job\assistant.dm" +#include "code\game\jobs\job\captain.dm" +#include "code\game\jobs\job\cargo_service.dm" +#include "code\game\jobs\job\civilian.dm" +#include "code\game\jobs\job\civilian_chaplain.dm" +#include "code\game\jobs\job\engineering.dm" +#include "code\game\jobs\job\job.dm" +#include "code\game\jobs\job\medical.dm" +#include "code\game\jobs\job\science.dm" +#include "code\game\jobs\job\security.dm" +#include "code\game\jobs\job\silicon.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\announcement_system.dm" +#include "code\game\machinery\atmo_control.dm" +#include "code\game\machinery\autolathe.dm" +#include "code\game\machinery\Beacon.dm" +#include "code\game\machinery\buttons.dm" +#include "code\game\machinery\cell_charger.dm" +#include "code\game\machinery\cloning.dm" +#include "code\game\machinery\constructable_frame.dm" +#include "code\game\machinery\deployable.dm" +#include "code\game\machinery\dna_scanner.dm" +#include "code\game\machinery\doppler_array.dm" +#include "code\game\machinery\droneDispenser.dm" +#include "code\game\machinery\flasher.dm" +#include "code\game\machinery\hologram.dm" +#include "code\game\machinery\igniter.dm" +#include "code\game\machinery\iv_drip.dm" +#include "code\game\machinery\lightswitch.dm" +#include "code\game\machinery\machinery.dm" +#include "code\game\machinery\magnet.dm" +#include "code\game\machinery\mass_driver.dm" +#include "code\game\machinery\navbeacon.dm" +#include "code\game\machinery\newscaster.dm" +#include "code\game\machinery\overview.dm" +#include "code\game\machinery\PDApainter.dm" +#include "code\game\machinery\portable_turret.dm" +#include "code\game\machinery\recharger.dm" +#include "code\game\machinery\rechargestation.dm" +#include "code\game\machinery\recycler.dm" +#include "code\game\machinery\requests_console.dm" +#include "code\game\machinery\robot_fabricator.dm" +#include "code\game\machinery\shieldgen.dm" +#include "code\game\machinery\Sleeper.dm" +#include "code\game\machinery\slotmachine.dm" +#include "code\game\machinery\spaceheater.dm" +#include "code\game\machinery\status_display.dm" +#include "code\game\machinery\suit_storage_unit.dm" +#include "code\game\machinery\syndicatebeacon.dm" +#include "code\game\machinery\syndicatebomb.dm" +#include "code\game\machinery\teleporter.dm" +#include "code\game\machinery\transformer.dm" +#include "code\game\machinery\vending.dm" +#include "code\game\machinery\washing_machine.dm" +#include "code\game\machinery\wishgranter.dm" +#include "code\game\machinery\atmoalter\area_atmos_computer.dm" +#include "code\game\machinery\atmoalter\canister.dm" +#include "code\game\machinery\atmoalter\meter.dm" +#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\camera\camera.dm" +#include "code\game\machinery\camera\camera_assembly.dm" +#include "code\game\machinery\camera\motion.dm" +#include "code\game\machinery\camera\presets.dm" +#include "code\game\machinery\camera\tracking.dm" +#include "code\game\machinery\computer\aifixer.dm" +#include "code\game\machinery\computer\arcade.dm" +#include "code\game\machinery\computer\atmos_alert.dm" +#include "code\game\machinery\computer\buildandrepair.dm" +#include "code\game\machinery\computer\camera.dm" +#include "code\game\machinery\computer\camera_advanced.dm" +#include "code\game\machinery\computer\card.dm" +#include "code\game\machinery\computer\cloning.dm" +#include "code\game\machinery\computer\communications.dm" +#include "code\game\machinery\computer\computer.dm" +#include "code\game\machinery\computer\crew.dm" +#include "code\game\machinery\computer\dna_console.dm" +#include "code\game\machinery\computer\law.dm" +#include "code\game\machinery\computer\medical.dm" +#include "code\game\machinery\computer\message.dm" +#include "code\game\machinery\computer\Operating.dm" +#include "code\game\machinery\computer\pod.dm" +#include "code\game\machinery\computer\power.dm" +#include "code\game\machinery\computer\prisoner.dm" +#include "code\game\machinery\computer\robot.dm" +#include "code\game\machinery\computer\security.dm" +#include "code\game\machinery\computer\shuttle.dm" +#include "code\game\machinery\computer\station_alert.dm" +#include "code\game\machinery\computer\syndicate_shuttle.dm" +#include "code\game\machinery\computer\telecrystalconsoles.dm" +#include "code\game\machinery\doors\airlock.dm" +#include "code\game\machinery\doors\airlock_electronics.dm" +#include "code\game\machinery\doors\airlock_types.dm" +#include "code\game\machinery\doors\alarmlock.dm" +#include "code\game\machinery\doors\brigdoors.dm" +#include "code\game\machinery\doors\checkForMultipleDoors.dm" +#include "code\game\machinery\doors\door.dm" +#include "code\game\machinery\doors\firedoor.dm" +#include "code\game\machinery\doors\poddoor.dm" +#include "code\game\machinery\doors\shutters.dm" +#include "code\game\machinery\doors\unpowered.dm" +#include "code\game\machinery\doors\windowdoor.dm" +#include "code\game\machinery\embedded_controller\access_controller.dm" +#include "code\game\machinery\embedded_controller\airlock_controller.dm" +#include "code\game\machinery\embedded_controller\embedded_controller_base.dm" +#include "code\game\machinery\embedded_controller\simple_vent_controller.dm" +#include "code\game\machinery\pipe\construction.dm" +#include "code\game\machinery\pipe\pipe_dispenser.dm" +#include "code\game\machinery\telecomms\broadcasting.dm" +#include "code\game\machinery\telecomms\machine_interactions.dm" +#include "code\game\machinery\telecomms\telecomunications.dm" +#include "code\game\machinery\telecomms\computers\logbrowser.dm" +#include "code\game\machinery\telecomms\computers\telemonitor.dm" +#include "code\game\machinery\telecomms\machines\allinone.dm" +#include "code\game\machinery\telecomms\machines\broadcaster.dm" +#include "code\game\machinery\telecomms\machines\bus.dm" +#include "code\game\machinery\telecomms\machines\hub.dm" +#include "code\game\machinery\telecomms\machines\processor.dm" +#include "code\game\machinery\telecomms\machines\receiver.dm" +#include "code\game\machinery\telecomms\machines\relay.dm" +#include "code\game\machinery\telecomms\machines\server.dm" +#include "code\game\mecha\mech_bay.dm" +#include "code\game\mecha\mech_fabricator.dm" +#include "code\game\mecha\mecha.dm" +#include "code\game\mecha\mecha_construction_paths.dm" +#include "code\game\mecha\mecha_control_console.dm" +#include "code\game\mecha\mecha_defense.dm" +#include "code\game\mecha\mecha_parts.dm" +#include "code\game\mecha\mecha_topic.dm" +#include "code\game\mecha\mecha_wreckage.dm" +#include "code\game\mecha\combat\combat.dm" +#include "code\game\mecha\combat\durand.dm" +#include "code\game\mecha\combat\gygax.dm" +#include "code\game\mecha\combat\honker.dm" +#include "code\game\mecha\combat\marauder.dm" +#include "code\game\mecha\combat\phazon.dm" +#include "code\game\mecha\combat\reticence.dm" +#include "code\game\mecha\equipment\mecha_equipment.dm" +#include "code\game\mecha\equipment\tools\medical_tools.dm" +#include "code\game\mecha\equipment\tools\mining_tools.dm" +#include "code\game\mecha\equipment\tools\other_tools.dm" +#include "code\game\mecha\equipment\tools\work_tools.dm" +#include "code\game\mecha\equipment\weapons\weapons.dm" +#include "code\game\mecha\medical\medical.dm" +#include "code\game\mecha\medical\odysseus.dm" +#include "code\game\mecha\working\ripley.dm" +#include "code\game\mecha\working\working.dm" +#include "code\game\objects\buckling.dm" +#include "code\game\objects\empulse.dm" +#include "code\game\objects\explosion.dm" +#include "code\game\objects\items.dm" +#include "code\game\objects\objs.dm" +#include "code\game\objects\radiation.dm" +#include "code\game\objects\structures.dm" +#include "code\game\objects\weapons.dm" +#include "code\game\objects\effects\aliens.dm" +#include "code\game\objects\effects\anomalies.dm" +#include "code\game\objects\effects\bump_teleporter.dm" +#include "code\game\objects\effects\forcefields.dm" +#include "code\game\objects\effects\gibs.dm" +#include "code\game\objects\effects\glowshroom.dm" +#include "code\game\objects\effects\landmarks.dm" +#include "code\game\objects\effects\manifest.dm" +#include "code\game\objects\effects\mines.dm" +#include "code\game\objects\effects\misc.dm" +#include "code\game\objects\effects\overlays.dm" +#include "code\game\objects\effects\portals.dm" +#include "code\game\objects\effects\spiders.dm" +#include "code\game\objects\effects\step_triggers.dm" +#include "code\game\objects\effects\decals\cleanable.dm" +#include "code\game\objects\effects\decals\contraband.dm" +#include "code\game\objects\effects\decals\crayon.dm" +#include "code\game\objects\effects\decals\decal.dm" +#include "code\game\objects\effects\decals\misc.dm" +#include "code\game\objects\effects\decals\remains.dm" +#include "code\game\objects\effects\decals\Cleanable\aliens.dm" +#include "code\game\objects\effects\decals\Cleanable\humans.dm" +#include "code\game\objects\effects\decals\Cleanable\misc.dm" +#include "code\game\objects\effects\decals\Cleanable\robots.dm" +#include "code\game\objects\effects\effect_system\effect_system.dm" +#include "code\game\objects\effects\effect_system\effects_explosion.dm" +#include "code\game\objects\effects\effect_system\effects_foam.dm" +#include "code\game\objects\effects\effect_system\effects_other.dm" +#include "code\game\objects\effects\effect_system\effects_smoke.dm" +#include "code\game\objects\effects\effect_system\effects_sparks.dm" +#include "code\game\objects\effects\effect_system\effects_water.dm" +#include "code\game\objects\effects\spawners\bombspawner.dm" +#include "code\game\objects\effects\spawners\gibspawner.dm" +#include "code\game\objects\effects\spawners\lootdrop.dm" +#include "code\game\objects\effects\spawners\structure.dm" +#include "code\game\objects\effects\spawners\vaultspawner.dm" +#include "code\game\objects\items\apc_frame.dm" +#include "code\game\objects\items\blueprints.dm" +#include "code\game\objects\items\body_egg.dm" +#include "code\game\objects\items\bodybag.dm" +#include "code\game\objects\items\candle.dm" +#include "code\game\objects\items\crayons.dm" +#include "code\game\objects\items\dehy_carp.dm" +#include "code\game\objects\items\documents.dm" +#include "code\game\objects\items\holotape.dm" +#include "code\game\objects\items\latexballoon.dm" +#include "code\game\objects\items\nuke_tools.dm" +#include "code\game\objects\items\shooting_range.dm" +#include "code\game\objects\items\toys.dm" +#include "code\game\objects\items\trash.dm" +#include "code\game\objects\items\devices\aicard.dm" +#include "code\game\objects\items\devices\camera_bug.dm" +#include "code\game\objects\items\devices\chameleonproj.dm" +#include "code\game\objects\items\devices\doorCharge.dm" +#include "code\game\objects\items\devices\flashlight.dm" +#include "code\game\objects\items\devices\geiger_counter.dm" +#include "code\game\objects\items\devices\instruments.dm" +#include "code\game\objects\items\devices\laserpointer.dm" +#include "code\game\objects\items\devices\lightreplacer.dm" +#include "code\game\objects\items\devices\megaphone.dm" +#include "code\game\objects\items\devices\multitool.dm" +#include "code\game\objects\items\devices\paicard.dm" +#include "code\game\objects\items\devices\pipe_painter.dm" +#include "code\game\objects\items\devices\pizza_bomb.dm" +#include "code\game\objects\items\devices\powersink.dm" +#include "code\game\objects\items\devices\scanners.dm" +#include "code\game\objects\items\devices\sensor_device.dm" +#include "code\game\objects\items\devices\taperecorder.dm" +#include "code\game\objects\items\devices\traitordevices.dm" +#include "code\game\objects\items\devices\transfer_valve.dm" +#include "code\game\objects\items\devices\uplinks.dm" +#include "code\game\objects\items\devices\PDA\cart.dm" +#include "code\game\objects\items\devices\PDA\PDA.dm" +#include "code\game\objects\items\devices\PDA\radio.dm" +#include "code\game\objects\items\devices\radio\beacon.dm" +#include "code\game\objects\items\devices\radio\electropack.dm" +#include "code\game\objects\items\devices\radio\encryptionkey.dm" +#include "code\game\objects\items\devices\radio\headset.dm" +#include "code\game\objects\items\devices\radio\intercom.dm" +#include "code\game\objects\items\devices\radio\radio.dm" +#include "code\game\objects\items\robot\robot_items.dm" +#include "code\game\objects\items\robot\robot_parts.dm" +#include "code\game\objects\items\robot\robot_upgrades.dm" +#include "code\game\objects\items\stacks\cash.dm" +#include "code\game\objects\items\stacks\medical.dm" +#include "code\game\objects\items\stacks\rods.dm" +#include "code\game\objects\items\stacks\stack.dm" +#include "code\game\objects\items\stacks\sheets\glass.dm" +#include "code\game\objects\items\stacks\sheets\leather.dm" +#include "code\game\objects\items\stacks\sheets\light.dm" +#include "code\game\objects\items\stacks\sheets\mineral.dm" +#include "code\game\objects\items\stacks\sheets\sheet_types.dm" +#include "code\game\objects\items\stacks\sheets\sheets.dm" +#include "code\game\objects\items\stacks\tiles\light.dm" +#include "code\game\objects\items\stacks\tiles\tile_mineral.dm" +#include "code\game\objects\items\stacks\tiles\tile_types.dm" +#include "code\game\objects\items\weapons\AI_modules.dm" +#include "code\game\objects\items\weapons\airlock_painter.dm" +#include "code\game\objects\items\weapons\cards_ids.dm" +#include "code\game\objects\items\weapons\chrono_eraser.dm" +#include "code\game\objects\items\weapons\cigs_lighters.dm" +#include "code\game\objects\items\weapons\clown_items.dm" +#include "code\game\objects\items\weapons\cosmetics.dm" +#include "code\game\objects\items\weapons\courtroom.dm" +#include "code\game\objects\items\weapons\defib.dm" +#include "code\game\objects\items\weapons\dice.dm" +#include "code\game\objects\items\weapons\dna_injector.dm" +#include "code\game\objects\items\weapons\explosives.dm" +#include "code\game\objects\items\weapons\extinguisher.dm" +#include "code\game\objects\items\weapons\flamethrower.dm" +#include "code\game\objects\items\weapons\gift_wrappaper.dm" +#include "code\game\objects\items\weapons\handcuffs.dm" +#include "code\game\objects\items\weapons\janisigns.dm" +#include "code\game\objects\items\weapons\kitchen.dm" +#include "code\game\objects\items\weapons\manuals.dm" +#include "code\game\objects\items\weapons\mop.dm" +#include "code\game\objects\items\weapons\paint.dm" +#include "code\game\objects\items\weapons\paiwire.dm" +#include "code\game\objects\items\weapons\pneumaticCannon.dm" +#include "code\game\objects\items\weapons\RCD.dm" +#include "code\game\objects\items\weapons\RPD.dm" +#include "code\game\objects\items\weapons\RSF.dm" +#include "code\game\objects\items\weapons\scrolls.dm" +#include "code\game\objects\items\weapons\shields.dm" +#include "code\game\objects\items\weapons\signs.dm" +#include "code\game\objects\items\weapons\singularityhammer.dm" +#include "code\game\objects\items\weapons\stunbaton.dm" +#include "code\game\objects\items\weapons\teleportation.dm" +#include "code\game\objects\items\weapons\teleprod.dm" +#include "code\game\objects\items\weapons\tools.dm" +#include "code\game\objects\items\weapons\twohanded.dm" +#include "code\game\objects\items\weapons\vending_items.dm" +#include "code\game\objects\items\weapons\weaponry.dm" +#include "code\game\objects\items\weapons\grenades\chem_grenade.dm" +#include "code\game\objects\items\weapons\grenades\clusterbuster.dm" +#include "code\game\objects\items\weapons\grenades\emgrenade.dm" +#include "code\game\objects\items\weapons\grenades\flashbang.dm" +#include "code\game\objects\items\weapons\grenades\ghettobomb.dm" +#include "code\game\objects\items\weapons\grenades\grenade.dm" +#include "code\game\objects\items\weapons\grenades\smokebomb.dm" +#include "code\game\objects\items\weapons\grenades\spawnergrenade.dm" +#include "code\game\objects\items\weapons\grenades\syndieminibomb.dm" +#include "code\game\objects\items\weapons\implants\implant.dm" +#include "code\game\objects\items\weapons\implants\implant_chem.dm" +#include "code\game\objects\items\weapons\implants\implant_explosive.dm" +#include "code\game\objects\items\weapons\implants\implant_freedom.dm" +#include "code\game\objects\items\weapons\implants\implant_loyality.dm" +#include "code\game\objects\items\weapons\implants\implant_misc.dm" +#include "code\game\objects\items\weapons\implants\implant_storage.dm" +#include "code\game\objects\items\weapons\implants\implant_track.dm" +#include "code\game\objects\items\weapons\implants\implantcase.dm" +#include "code\game\objects\items\weapons\implants\implantchair.dm" +#include "code\game\objects\items\weapons\implants\implanter.dm" +#include "code\game\objects\items\weapons\implants\implantpad.dm" +#include "code\game\objects\items\weapons\implants\implantuplink.dm" +#include "code\game\objects\items\weapons\melee\energy.dm" +#include "code\game\objects\items\weapons\melee\misc.dm" +#include "code\game\objects\items\weapons\storage\backpack.dm" +#include "code\game\objects\items\weapons\storage\bags.dm" +#include "code\game\objects\items\weapons\storage\belt.dm" +#include "code\game\objects\items\weapons\storage\book.dm" +#include "code\game\objects\items\weapons\storage\boxes.dm" +#include "code\game\objects\items\weapons\storage\briefcase.dm" +#include "code\game\objects\items\weapons\storage\fancy.dm" +#include "code\game\objects\items\weapons\storage\firstaid.dm" +#include "code\game\objects\items\weapons\storage\internal.dm" +#include "code\game\objects\items\weapons\storage\lockbox.dm" +#include "code\game\objects\items\weapons\storage\secure.dm" +#include "code\game\objects\items\weapons\storage\storage.dm" +#include "code\game\objects\items\weapons\storage\toolbox.dm" +#include "code\game\objects\items\weapons\storage\uplink_kits.dm" +#include "code\game\objects\items\weapons\storage\wallets.dm" +#include "code\game\objects\items\weapons\tanks\jetpack.dm" +#include "code\game\objects\items\weapons\tanks\tank_types.dm" +#include "code\game\objects\items\weapons\tanks\tanks.dm" +#include "code\game\objects\items\weapons\tanks\watertank.dm" +#include "code\game\objects\structures\ai_core.dm" +#include "code\game\objects\structures\artstuff.dm" +#include "code\game\objects\structures\barsigns.dm" +#include "code\game\objects\structures\bedsheet_bin.dm" +#include "code\game\objects\structures\displaycase.dm" +#include "code\game\objects\structures\door_assembly.dm" +#include "code\game\objects\structures\dresser.dm" +#include "code\game\objects\structures\electricchair.dm" +#include "code\game\objects\structures\extinguisher.dm" +#include "code\game\objects\structures\false_walls.dm" +#include "code\game\objects\structures\fireaxe.dm" +#include "code\game\objects\structures\flora.dm" +#include "code\game\objects\structures\girders.dm" +#include "code\game\objects\structures\grille.dm" +#include "code\game\objects\structures\guncase.dm" +#include "code\game\objects\structures\hivebot.dm" +#include "code\game\objects\structures\janicart.dm" +#include "code\game\objects\structures\kitchen_spike.dm" +#include "code\game\objects\structures\ladders.dm" +#include "code\game\objects\structures\lattice.dm" +#include "code\game\objects\structures\mineral_doors.dm" +#include "code\game\objects\structures\mirror.dm" +#include "code\game\objects\structures\mop_bucket.dm" +#include "code\game\objects\structures\morgue.dm" +#include "code\game\objects\structures\musician.dm" +#include "code\game\objects\structures\noticeboard.dm" +#include "code\game\objects\structures\plasticflaps.dm" +#include "code\game\objects\structures\reflector.dm" +#include "code\game\objects\structures\safe.dm" +#include "code\game\objects\structures\showcase.dm" +#include "code\game\objects\structures\signs.dm" +#include "code\game\objects\structures\spirit_board.dm" +#include "code\game\objects\structures\statues.dm" +#include "code\game\objects\structures\table_frames.dm" +#include "code\game\objects\structures\tables_racks.dm" +#include "code\game\objects\structures\tank_dispenser.dm" +#include "code\game\objects\structures\target_stake.dm" +#include "code\game\objects\structures\watercloset.dm" +#include "code\game\objects\structures\windoor_assembly.dm" +#include "code\game\objects\structures\window.dm" +#include "code\game\objects\structures\beds_chairs\alien_nest.dm" +#include "code\game\objects\structures\beds_chairs\bed.dm" +#include "code\game\objects\structures\beds_chairs\chair.dm" +#include "code\game\objects\structures\crates_lockers\bins.dm" +#include "code\game\objects\structures\crates_lockers\closets.dm" +#include "code\game\objects\structures\crates_lockers\crates.dm" +#include "code\game\objects\structures\crates_lockers\largecrate.dm" +#include "code\game\objects\structures\crates_lockers\closets\cardboardbox.dm" +#include "code\game\objects\structures\crates_lockers\closets\crittercrate.dm" +#include "code\game\objects\structures\crates_lockers\closets\fitness.dm" +#include "code\game\objects\structures\crates_lockers\closets\gimmick.dm" +#include "code\game\objects\structures\crates_lockers\closets\job_closets.dm" +#include "code\game\objects\structures\crates_lockers\closets\l3closet.dm" +#include "code\game\objects\structures\crates_lockers\closets\statue.dm" +#include "code\game\objects\structures\crates_lockers\closets\syndicate.dm" +#include "code\game\objects\structures\crates_lockers\closets\utility_closets.dm" +#include "code\game\objects\structures\crates_lockers\closets\wardrobe.dm" +#include "code\game\objects\structures\crates_lockers\closets\secure\bar.dm" +#include "code\game\objects\structures\crates_lockers\closets\secure\cargo.dm" +#include "code\game\objects\structures\crates_lockers\closets\secure\engineering.dm" +#include "code\game\objects\structures\crates_lockers\closets\secure\freezer.dm" +#include "code\game\objects\structures\crates_lockers\closets\secure\hydroponics.dm" +#include "code\game\objects\structures\crates_lockers\closets\secure\medical.dm" +#include "code\game\objects\structures\crates_lockers\closets\secure\misc.dm" +#include "code\game\objects\structures\crates_lockers\closets\secure\personal.dm" +#include "code\game\objects\structures\crates_lockers\closets\secure\scientist.dm" +#include "code\game\objects\structures\crates_lockers\closets\secure\secure_closets.dm" +#include "code\game\objects\structures\crates_lockers\closets\secure\security.dm" +#include "code\game\objects\structures\transit_tubes\station.dm" +#include "code\game\objects\structures\transit_tubes\transit_tube.dm" +#include "code\game\objects\structures\transit_tubes\transit_tube_construction.dm" +#include "code\game\objects\structures\transit_tubes\transit_tube_pod.dm" +#include "code\game\pooling\pool.dm" +#include "code\game\turfs\simulated.dm" +#include "code\game\turfs\turf.dm" +#include "code\game\turfs\simulated\dirtystation.dm" +#include "code\game\turfs\simulated\floor.dm" +#include "code\game\turfs\simulated\walls.dm" +#include "code\game\turfs\simulated\walls_mineral.dm" +#include "code\game\turfs\simulated\walls_misc.dm" +#include "code\game\turfs\simulated\walls_reinforced.dm" +#include "code\game\turfs\simulated\floor\fancy_floor.dm" +#include "code\game\turfs\simulated\floor\light_floor.dm" +#include "code\game\turfs\simulated\floor\mineral_floor.dm" +#include "code\game\turfs\simulated\floor\misc_floor.dm" +#include "code\game\turfs\simulated\floor\plasteel_floor.dm" +#include "code\game\turfs\simulated\floor\plating.dm" +#include "code\game\turfs\space\space.dm" +#include "code\game\turfs\space\transit.dm" +#include "code\game\verbs\ooc.dm" +#include "code\game\verbs\suicide.dm" +#include "code\game\verbs\who.dm" +#include "code\js\byjax.dm" +#include "code\js\menus.dm" +#include "code\LINDA\LINDA_fire.dm" +#include "code\LINDA\LINDA_system.dm" +#include "code\LINDA\LINDA_turf_tile.dm" +#include "code\modules\admin\admin.dm" +#include "code\modules\admin\admin_investigate.dm" +#include "code\modules\admin\admin_memo.dm" +#include "code\modules\admin\admin_ranks.dm" +#include "code\modules\admin\admin_verbs.dm" +#include "code\modules\admin\banappearance.dm" +#include "code\modules\admin\banjob.dm" +#include "code\modules\admin\create_mob.dm" +#include "code\modules\admin\create_object.dm" +#include "code\modules\admin\create_poll.dm" +#include "code\modules\admin\create_turf.dm" +#include "code\modules\admin\holder2.dm" +#include "code\modules\admin\IsBanned.dm" +#include "code\modules\admin\NewBan.dm" +#include "code\modules\admin\player_panel.dm" +#include "code\modules\admin\secrets.dm" +#include "code\modules\admin\sql_notes.dm" +#include "code\modules\admin\stickyban.dm" +#include "code\modules\admin\topic.dm" +#include "code\modules\admin\watchlist.dm" +#include "code\modules\admin\DB ban\functions.dm" +#include "code\modules\admin\permissionverbs\permissionedit.dm" +#include "code\modules\admin\verbs\adminhelp.dm" +#include "code\modules\admin\verbs\adminjump.dm" +#include "code\modules\admin\verbs\adminpm.dm" +#include "code\modules\admin\verbs\adminsay.dm" +#include "code\modules\admin\verbs\atmosdebug.dm" +#include "code\modules\admin\verbs\bluespacearty.dm" +#include "code\modules\admin\verbs\BrokenInhands.dm" +#include "code\modules\admin\verbs\buildmode.dm" +#include "code\modules\admin\verbs\cinematic.dm" +#include "code\modules\admin\verbs\deadsay.dm" +#include "code\modules\admin\verbs\debug.dm" +#include "code\modules\admin\verbs\diagnostics.dm" +#include "code\modules\admin\verbs\fps.dm" +#include "code\modules\admin\verbs\getlogs.dm" +#include "code\modules\admin\verbs\machine_upgrade.dm" +#include "code\modules\admin\verbs\manipulate_organs.dm" +#include "code\modules\admin\verbs\mapping.dm" +#include "code\modules\admin\verbs\maprotation.dm" +#include "code\modules\admin\verbs\massmodvar.dm" +#include "code\modules\admin\verbs\modifyvariables.dm" +#include "code\modules\admin\verbs\one_click_antag.dm" +#include "code\modules\admin\verbs\onlyone.dm" +#include "code\modules\admin\verbs\panicbunker.dm" +#include "code\modules\admin\verbs\playsound.dm" +#include "code\modules\admin\verbs\possess.dm" +#include "code\modules\admin\verbs\pray.dm" +#include "code\modules\admin\verbs\randomverbs.dm" +#include "code\modules\admin\verbs\reestablish_db_connection.dm" +#include "code\modules\admin\verbs\tripAI.dm" +#include "code\modules\admin\verbs\SDQL2\SDQL_2.dm" +#include "code\modules\admin\verbs\SDQL2\SDQL_2_parser.dm" +#include "code\modules\assembly\assembly.dm" +#include "code\modules\assembly\bomb.dm" +#include "code\modules\assembly\doorcontrol.dm" +#include "code\modules\assembly\flash.dm" +#include "code\modules\assembly\health.dm" +#include "code\modules\assembly\helpers.dm" +#include "code\modules\assembly\holder.dm" +#include "code\modules\assembly\igniter.dm" +#include "code\modules\assembly\infrared.dm" +#include "code\modules\assembly\mousetrap.dm" +#include "code\modules\assembly\proximity.dm" +#include "code\modules\assembly\shock_kit.dm" +#include "code\modules\assembly\signaler.dm" +#include "code\modules\assembly\timer.dm" +#include "code\modules\assembly\voice.dm" +#include "code\modules\awaymissions\bluespaceartillery.dm" +#include "code\modules\awaymissions\corpse.dm" +#include "code\modules\awaymissions\exile.dm" +#include "code\modules\awaymissions\gateway.dm" +#include "code\modules\awaymissions\pamphlet.dm" +#include "code\modules\awaymissions\signpost.dm" +#include "code\modules\awaymissions\zlevel.dm" +#include "code\modules\awaymissions\maploader\dmm_suite.dm" +#include "code\modules\awaymissions\maploader\reader.dm" +#include "code\modules\awaymissions\maploader\swapmaps.dm" +#include "code\modules\awaymissions\maploader\writer.dm" +#include "code\modules\awaymissions\mission_code\Academy.dm" +#include "code\modules\awaymissions\mission_code\blackmarketpackers.dm" +#include "code\modules\awaymissions\mission_code\centcomAway.dm" +#include "code\modules\awaymissions\mission_code\challenge.dm" +#include "code\modules\awaymissions\mission_code\snowdin.dm" +#include "code\modules\awaymissions\mission_code\spacebattle.dm" +#include "code\modules\awaymissions\mission_code\stationCollision.dm" +#include "code\modules\awaymissions\mission_code\wildwest.dm" +#include "code\modules\client\asset_cache.dm" +#include "code\modules\client\client defines.dm" +#include "code\modules\client\client procs.dm" +#include "code\modules\client\message.dm" +#include "code\modules\client\preferences.dm" +#include "code\modules\client\preferences_savefile.dm" +#include "code\modules\client\preferences_toggles.dm" +#include "code\modules\clothing\clothing.dm" +#include "code\modules\clothing\glasses\engine_goggles.dm" +#include "code\modules\clothing\glasses\glasses.dm" +#include "code\modules\clothing\glasses\hud.dm" +#include "code\modules\clothing\gloves\boxing.dm" +#include "code\modules\clothing\gloves\color.dm" +#include "code\modules\clothing\gloves\miscellaneous.dm" +#include "code\modules\clothing\head\collectable.dm" +#include "code\modules\clothing\head\hardhat.dm" +#include "code\modules\clothing\head\helmet.dm" +#include "code\modules\clothing\head\jobs.dm" +#include "code\modules\clothing\head\misc.dm" +#include "code\modules\clothing\head\misc_special.dm" +#include "code\modules\clothing\head\soft_caps.dm" +#include "code\modules\clothing\masks\boxing.dm" +#include "code\modules\clothing\masks\breath.dm" +#include "code\modules\clothing\masks\gasmask.dm" +#include "code\modules\clothing\masks\hailer.dm" +#include "code\modules\clothing\masks\miscellaneous.dm" +#include "code\modules\clothing\outfits\ert.dm" +#include "code\modules\clothing\outfits\standard.dm" +#include "code\modules\clothing\shoes\bananashoes.dm" +#include "code\modules\clothing\shoes\colour.dm" +#include "code\modules\clothing\shoes\magboots.dm" +#include "code\modules\clothing\shoes\miscellaneous.dm" +#include "code\modules\clothing\spacesuits\chronosuit.dm" +#include "code\modules\clothing\spacesuits\hardsuit.dm" +#include "code\modules\clothing\spacesuits\miscellaneous.dm" +#include "code\modules\clothing\spacesuits\plasmamen.dm" +#include "code\modules\clothing\spacesuits\syndi.dm" +#include "code\modules\clothing\suits\armor.dm" +#include "code\modules\clothing\suits\bio.dm" +#include "code\modules\clothing\suits\cloaks.dm" +#include "code\modules\clothing\suits\jobs.dm" +#include "code\modules\clothing\suits\labcoat.dm" +#include "code\modules\clothing\suits\miscellaneous.dm" +#include "code\modules\clothing\suits\toggles.dm" +#include "code\modules\clothing\suits\utility.dm" +#include "code\modules\clothing\suits\wiz_robe.dm" +#include "code\modules\clothing\under\chameleon.dm" +#include "code\modules\clothing\under\color.dm" +#include "code\modules\clothing\under\miscellaneous.dm" +#include "code\modules\clothing\under\pants.dm" +#include "code\modules\clothing\under\shorts.dm" +#include "code\modules\clothing\under\syndicate.dm" +#include "code\modules\clothing\under\ties.dm" +#include "code\modules\clothing\under\jobs\civilian.dm" +#include "code\modules\clothing\under\jobs\engineering.dm" +#include "code\modules\clothing\under\jobs\medsci.dm" +#include "code\modules\clothing\under\jobs\security.dm" +#include "code\modules\crafting\guncrafting.dm" +#include "code\modules\crafting\recipes.dm" +#include "code\modules\crafting\table.dm" +#include "code\modules\detectivework\detective_work.dm" +#include "code\modules\detectivework\evidence.dm" +#include "code\modules\detectivework\footprints_and_rag.dm" +#include "code\modules\detectivework\scanner.dm" +#include "code\modules\emoji\emoji_parse.dm" +#include "code\modules\events\abductor.dm" +#include "code\modules\events\alien_infestation.dm" +#include "code\modules\events\anomaly.dm" +#include "code\modules\events\anomaly_bluespace.dm" +#include "code\modules\events\anomaly_flux.dm" +#include "code\modules\events\anomaly_grav.dm" +#include "code\modules\events\anomaly_pyro.dm" +#include "code\modules\events\anomaly_vortex.dm" +#include "code\modules\events\blob.dm" +#include "code\modules\events\brand_intelligence.dm" +#include "code\modules\events\camerafailure.dm" +#include "code\modules\events\carp_migration.dm" +#include "code\modules\events\communications_blackout.dm" +#include "code\modules\events\disease_outbreak.dm" +#include "code\modules\events\dust.dm" +#include "code\modules\events\electrical_storm.dm" +#include "code\modules\events\event.dm" +#include "code\modules\events\false_alarm.dm" +#include "code\modules\events\immovable_rod.dm" +#include "code\modules\events\ion_storm.dm" +#include "code\modules\events\mass_hallucination.dm" +#include "code\modules\events\meateor_wave.dm" +#include "code\modules\events\meteor_wave.dm" +#include "code\modules\events\operative.dm" +#include "code\modules\events\prison_break.dm" +#include "code\modules\events\radiation_storm.dm" +#include "code\modules\events\shuttle_loan.dm" +#include "code\modules\events\spacevine.dm" +#include "code\modules\events\spider_infestation.dm" +#include "code\modules\events\spontaneous_appendicitis.dm" +#include "code\modules\events\vent_clog.dm" +#include "code\modules\events\wormholes.dm" +#include "code\modules\events\holiday\halloween.dm" +#include "code\modules\events\holiday\xmas.dm" +#include "code\modules\events\wizard\aid.dm" +#include "code\modules\events\wizard\blobies.dm" +#include "code\modules\events\wizard\curseditems.dm" +#include "code\modules\events\wizard\departmentrevolt.dm" +#include "code\modules\events\wizard\fakeexplosion.dm" +#include "code\modules\events\wizard\ghost.dm" +#include "code\modules\events\wizard\greentext.dm" +#include "code\modules\events\wizard\imposter.dm" +#include "code\modules\events\wizard\invincible.dm" +#include "code\modules\events\wizard\lava.dm" +#include "code\modules\events\wizard\magicarp.dm" +#include "code\modules\events\wizard\petsplosion.dm" +#include "code\modules\events\wizard\race.dm" +#include "code\modules\events\wizard\rpgloot.dm" +#include "code\modules\events\wizard\shuffle.dm" +#include "code\modules\events\wizard\summons.dm" +#include "code\modules\flufftext\Dreaming.dm" +#include "code\modules\flufftext\Hallucination.dm" +#include "code\modules\flufftext\TextFilters.dm" +#include "code\modules\food&drinks\food.dm" +#include "code\modules\food&drinks\drinks\drinks.dm" +#include "code\modules\food&drinks\drinks\drinks\bottle.dm" +#include "code\modules\food&drinks\drinks\drinks\drinkingglass.dm" +#include "code\modules\food&drinks\food\condiment.dm" +#include "code\modules\food&drinks\food\customizables.dm" +#include "code\modules\food&drinks\food\snacks.dm" +#include "code\modules\food&drinks\food\snacks_bread.dm" +#include "code\modules\food&drinks\food\snacks_burgers.dm" +#include "code\modules\food&drinks\food\snacks_cake.dm" +#include "code\modules\food&drinks\food\snacks_egg.dm" +#include "code\modules\food&drinks\food\snacks_meat.dm" +#include "code\modules\food&drinks\food\snacks_other.dm" +#include "code\modules\food&drinks\food\snacks_pastry.dm" +#include "code\modules\food&drinks\food\snacks_pie.dm" +#include "code\modules\food&drinks\food\snacks_pizza.dm" +#include "code\modules\food&drinks\food\snacks_salad.dm" +#include "code\modules\food&drinks\food\snacks_sandwichtoast.dm" +#include "code\modules\food&drinks\food\snacks_soup.dm" +#include "code\modules\food&drinks\food\snacks_spaghetti.dm" +#include "code\modules\food&drinks\food\snacks_vend.dm" +#include "code\modules\food&drinks\food\snacks\dough.dm" +#include "code\modules\food&drinks\food\snacks\meat.dm" +#include "code\modules\food&drinks\kitchen machinery\food_cart.dm" +#include "code\modules\food&drinks\kitchen machinery\gibber.dm" +#include "code\modules\food&drinks\kitchen machinery\icecream_vat.dm" +#include "code\modules\food&drinks\kitchen machinery\juicer.dm" +#include "code\modules\food&drinks\kitchen machinery\microwave.dm" +#include "code\modules\food&drinks\kitchen machinery\monkeyrecycler.dm" +#include "code\modules\food&drinks\kitchen machinery\processor.dm" +#include "code\modules\food&drinks\kitchen machinery\smartfridge.dm" +#include "code\modules\food&drinks\recipes\drinks_recipes.dm" +#include "code\modules\food&drinks\recipes\food_mixtures.dm" +#include "code\modules\food&drinks\recipes\tablecraft\recipes_bread.dm" +#include "code\modules\food&drinks\recipes\tablecraft\recipes_burger.dm" +#include "code\modules\food&drinks\recipes\tablecraft\recipes_cake.dm" +#include "code\modules\food&drinks\recipes\tablecraft\recipes_egg.dm" +#include "code\modules\food&drinks\recipes\tablecraft\recipes_meat.dm" +#include "code\modules\food&drinks\recipes\tablecraft\recipes_misc.dm" +#include "code\modules\food&drinks\recipes\tablecraft\recipes_pastry.dm" +#include "code\modules\food&drinks\recipes\tablecraft\recipes_pie.dm" +#include "code\modules\food&drinks\recipes\tablecraft\recipes_pizza.dm" +#include "code\modules\food&drinks\recipes\tablecraft\recipes_salad.dm" +#include "code\modules\food&drinks\recipes\tablecraft\recipes_sandwich.dm" +#include "code\modules\food&drinks\recipes\tablecraft\recipes_soup.dm" +#include "code\modules\food&drinks\recipes\tablecraft\recipes_spaghetti.dm" +#include "code\modules\games\cards.dm" +#include "code\modules\holiday\easter.dm" +#include "code\modules\holiday\holidays.dm" +#include "code\modules\holodeck\area_copy.dm" +#include "code\modules\holodeck\areas.dm" +#include "code\modules\holodeck\computer.dm" +#include "code\modules\holodeck\computer_funcs.dm" +#include "code\modules\holodeck\holo_effect.dm" +#include "code\modules\holodeck\items.dm" +#include "code\modules\holodeck\turfs.dm" +#include "code\modules\html_interface\html_interface.dm" +#include "code\modules\html_interface\html_interface_client.dm" +#include "code\modules\html_interface\cards\cards.dm" +#include "code\modules\html_interface\nanotrasen\nanotrasen.dm" +#include "code\modules\hydroponics\biogenerator.dm" +#include "code\modules\hydroponics\grown.dm" +#include "code\modules\hydroponics\growninedible.dm" +#include "code\modules\hydroponics\hydroitemdefines.dm" +#include "code\modules\hydroponics\hydroponics.dm" +#include "code\modules\hydroponics\seed_extractor.dm" +#include "code\modules\hydroponics\seeds.dm" +#include "code\modules\json\json.dm" +#include "code\modules\library\lib_items.dm" +#include "code\modules\library\lib_machines.dm" +#include "code\modules\library\lib_readme.dm" +#include "code\modules\library\random_books.dm" +#include "code\modules\lighting\lighting_system.dm" +#include "code\modules\mining\abandoned_crates.dm" +#include "code\modules\mining\equipment_locker.dm" +#include "code\modules\mining\machine_input_output_plates.dm" +#include "code\modules\mining\machine_processing.dm" +#include "code\modules\mining\machine_stacking.dm" +#include "code\modules\mining\machine_unloading.dm" +#include "code\modules\mining\mine_areas.dm" +#include "code\modules\mining\mine_items.dm" +#include "code\modules\mining\mine_turfs.dm" +#include "code\modules\mining\mint.dm" +#include "code\modules\mining\money_bag.dm" +#include "code\modules\mining\ores_coins.dm" +#include "code\modules\mining\satchel_ore_boxdm.dm" +#include "code\modules\mining\laborcamp\laborminerals.dm" +#include "code\modules\mining\laborcamp\laborshuttle.dm" +#include "code\modules\mining\laborcamp\laborstacker.dm" +#include "code\modules\mob\death.dm" +#include "code\modules\mob\interactive.dm" +#include "code\modules\mob\inventory.dm" +#include "code\modules\mob\login.dm" +#include "code\modules\mob\logout.dm" +#include "code\modules\mob\mob.dm" +#include "code\modules\mob\mob_cleanup.dm" +#include "code\modules\mob\mob_defines.dm" +#include "code\modules\mob\mob_grab.dm" +#include "code\modules\mob\mob_helpers.dm" +#include "code\modules\mob\mob_movement.dm" +#include "code\modules\mob\mob_transformation_simple.dm" +#include "code\modules\mob\say.dm" +#include "code\modules\mob\transform_procs.dm" +#include "code\modules\mob\update_icons.dm" +#include "code\modules\mob\camera\camera.dm" +#include "code\modules\mob\dead\death.dm" +#include "code\modules\mob\dead\observer\login.dm" +#include "code\modules\mob\dead\observer\logout.dm" +#include "code\modules\mob\dead\observer\observer.dm" +#include "code\modules\mob\dead\observer\say.dm" +#include "code\modules\mob\living\bloodcrawl.dm" +#include "code\modules\mob\living\damage_procs.dm" +#include "code\modules\mob\living\death.dm" +#include "code\modules\mob\living\emote.dm" +#include "code\modules\mob\living\life.dm" +#include "code\modules\mob\living\living.dm" +#include "code\modules\mob\living\living_defense.dm" +#include "code\modules\mob\living\living_defines.dm" +#include "code\modules\mob\living\login.dm" +#include "code\modules\mob\living\logout.dm" +#include "code\modules\mob\living\say.dm" +#include "code\modules\mob\living\ventcrawling.dm" +#include "code\modules\mob\living\carbon\carbon.dm" +#include "code\modules\mob\living\carbon\carbon_defense.dm" +#include "code\modules\mob\living\carbon\carbon_defines.dm" +#include "code\modules\mob\living\carbon\death.dm" +#include "code\modules\mob\living\carbon\emote.dm" +#include "code\modules\mob\living\carbon\examine.dm" +#include "code\modules\mob\living\carbon\inventory.dm" +#include "code\modules\mob\living\carbon\life.dm" +#include "code\modules\mob\living\carbon\say.dm" +#include "code\modules\mob\living\carbon\update_icons.dm" +#include "code\modules\mob\living\carbon\alien\alien.dm" +#include "code\modules\mob\living\carbon\alien\alien_defense.dm" +#include "code\modules\mob\living\carbon\alien\death.dm" +#include "code\modules\mob\living\carbon\alien\life.dm" +#include "code\modules\mob\living\carbon\alien\login.dm" +#include "code\modules\mob\living\carbon\alien\logout.dm" +#include "code\modules\mob\living\carbon\alien\organs.dm" +#include "code\modules\mob\living\carbon\alien\say.dm" +#include "code\modules\mob\living\carbon\alien\screen.dm" +#include "code\modules\mob\living\carbon\alien\humanoid\alien_powers.dm" +#include "code\modules\mob\living\carbon\alien\humanoid\death.dm" +#include "code\modules\mob\living\carbon\alien\humanoid\emote.dm" +#include "code\modules\mob\living\carbon\alien\humanoid\humanoid.dm" +#include "code\modules\mob\living\carbon\alien\humanoid\inventory.dm" +#include "code\modules\mob\living\carbon\alien\humanoid\life.dm" +#include "code\modules\mob\living\carbon\alien\humanoid\login.dm" +#include "code\modules\mob\living\carbon\alien\humanoid\queen.dm" +#include "code\modules\mob\living\carbon\alien\humanoid\update_icons.dm" +#include "code\modules\mob\living\carbon\alien\humanoid\caste\drone.dm" +#include "code\modules\mob\living\carbon\alien\humanoid\caste\hunter.dm" +#include "code\modules\mob\living\carbon\alien\humanoid\caste\praetorian.dm" +#include "code\modules\mob\living\carbon\alien\humanoid\caste\sentinel.dm" +#include "code\modules\mob\living\carbon\alien\larva\death.dm" +#include "code\modules\mob\living\carbon\alien\larva\emote.dm" +#include "code\modules\mob\living\carbon\alien\larva\inventory.dm" +#include "code\modules\mob\living\carbon\alien\larva\larva.dm" +#include "code\modules\mob\living\carbon\alien\larva\life.dm" +#include "code\modules\mob\living\carbon\alien\larva\powers.dm" +#include "code\modules\mob\living\carbon\alien\larva\update_icons.dm" +#include "code\modules\mob\living\carbon\alien\special\alien_embryo.dm" +#include "code\modules\mob\living\carbon\alien\special\facehugger.dm" +#include "code\modules\mob\living\carbon\brain\brain.dm" +#include "code\modules\mob\living\carbon\brain\brain_item.dm" +#include "code\modules\mob\living\carbon\brain\death.dm" +#include "code\modules\mob\living\carbon\brain\emote.dm" +#include "code\modules\mob\living\carbon\brain\life.dm" +#include "code\modules\mob\living\carbon\brain\login.dm" +#include "code\modules\mob\living\carbon\brain\MMI.dm" +#include "code\modules\mob\living\carbon\brain\posibrain.dm" +#include "code\modules\mob\living\carbon\brain\say.dm" +#include "code\modules\mob\living\carbon\human\blood.dm" +#include "code\modules\mob\living\carbon\human\death.dm" +#include "code\modules\mob\living\carbon\human\emote.dm" +#include "code\modules\mob\living\carbon\human\examine.dm" +#include "code\modules\mob\living\carbon\human\human.dm" +#include "code\modules\mob\living\carbon\human\human_attackalien.dm" +#include "code\modules\mob\living\carbon\human\human_attackhand.dm" +#include "code\modules\mob\living\carbon\human\human_attackpaw.dm" +#include "code\modules\mob\living\carbon\human\human_damage.dm" +#include "code\modules\mob\living\carbon\human\human_defense.dm" +#include "code\modules\mob\living\carbon\human\human_defines.dm" +#include "code\modules\mob\living\carbon\human\human_helpers.dm" +#include "code\modules\mob\living\carbon\human\human_movement.dm" +#include "code\modules\mob\living\carbon\human\inventory.dm" +#include "code\modules\mob\living\carbon\human\life.dm" +#include "code\modules\mob\living\carbon\human\login.dm" +#include "code\modules\mob\living\carbon\human\say.dm" +#include "code\modules\mob\living\carbon\human\species.dm" +#include "code\modules\mob\living\carbon\human\species_types.dm" +#include "code\modules\mob\living\carbon\human\update_icons.dm" +#include "code\modules\mob\living\carbon\human\whisper.dm" +#include "code\modules\mob\living\carbon\monkey\death.dm" +#include "code\modules\mob\living\carbon\monkey\emote.dm" +#include "code\modules\mob\living\carbon\monkey\inventory.dm" +#include "code\modules\mob\living\carbon\monkey\life.dm" +#include "code\modules\mob\living\carbon\monkey\login.dm" +#include "code\modules\mob\living\carbon\monkey\monkey.dm" +#include "code\modules\mob\living\carbon\monkey\update_icons.dm" +#include "code\modules\mob\living\silicon\death.dm" +#include "code\modules\mob\living\silicon\laws.dm" +#include "code\modules\mob\living\silicon\login.dm" +#include "code\modules\mob\living\silicon\say.dm" +#include "code\modules\mob\living\silicon\silicon.dm" +#include "code\modules\mob\living\silicon\ai\ai.dm" +#include "code\modules\mob\living\silicon\ai\death.dm" +#include "code\modules\mob\living\silicon\ai\examine.dm" +#include "code\modules\mob\living\silicon\ai\laws.dm" +#include "code\modules\mob\living\silicon\ai\life.dm" +#include "code\modules\mob\living\silicon\ai\login.dm" +#include "code\modules\mob\living\silicon\ai\logout.dm" +#include "code\modules\mob\living\silicon\ai\say.dm" +#include "code\modules\mob\living\silicon\ai\vox_sounds.dm" +#include "code\modules\mob\living\silicon\ai\freelook\cameranet.dm" +#include "code\modules\mob\living\silicon\ai\freelook\chunk.dm" +#include "code\modules\mob\living\silicon\ai\freelook\eye.dm" +#include "code\modules\mob\living\silicon\ai\freelook\read_me.dm" +#include "code\modules\mob\living\silicon\pai\death.dm" +#include "code\modules\mob\living\silicon\pai\examine.dm" +#include "code\modules\mob\living\silicon\pai\life.dm" +#include "code\modules\mob\living\silicon\pai\pai.dm" +#include "code\modules\mob\living\silicon\pai\personality.dm" +#include "code\modules\mob\living\silicon\pai\say.dm" +#include "code\modules\mob\living\silicon\pai\software.dm" +#include "code\modules\mob\living\silicon\robot\death.dm" +#include "code\modules\mob\living\silicon\robot\emote.dm" +#include "code\modules\mob\living\silicon\robot\examine.dm" +#include "code\modules\mob\living\silicon\robot\inventory.dm" +#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\robot.dm" +#include "code\modules\mob\living\silicon\robot\robot_modules.dm" +#include "code\modules\mob\living\silicon\robot\robot_movement.dm" +#include "code\modules\mob\living\silicon\robot\say.dm" +#include "code\modules\mob\living\simple_animal\constructs.dm" +#include "code\modules\mob\living\simple_animal\corpse.dm" +#include "code\modules\mob\living\simple_animal\parrot.dm" +#include "code\modules\mob\living\simple_animal\shade.dm" +#include "code\modules\mob\living\simple_animal\simple_animal.dm" +#include "code\modules\mob\living\simple_animal\spawner.dm" +#include "code\modules\mob\living\simple_animal\bot\bot.dm" +#include "code\modules\mob\living\simple_animal\bot\cleanbot.dm" +#include "code\modules\mob\living\simple_animal\bot\construction.dm" +#include "code\modules\mob\living\simple_animal\bot\ed209bot.dm" +#include "code\modules\mob\living\simple_animal\bot\floorbot.dm" +#include "code\modules\mob\living\simple_animal\bot\medbot.dm" +#include "code\modules\mob\living\simple_animal\bot\mulebot.dm" +#include "code\modules\mob\living\simple_animal\bot\secbot.dm" +#include "code\modules\mob\living\simple_animal\bot_swarm\swarmer.dm" +#include "code\modules\mob\living\simple_animal\bot_swarm\swarmer_event.dm" +#include "code\modules\mob\living\simple_animal\friendly\butterfly.dm" +#include "code\modules\mob\living\simple_animal\friendly\cat.dm" +#include "code\modules\mob\living\simple_animal\friendly\crab.dm" +#include "code\modules\mob\living\simple_animal\friendly\dog.dm" +#include "code\modules\mob\living\simple_animal\friendly\farm_animals.dm" +#include "code\modules\mob\living\simple_animal\friendly\fox.dm" +#include "code\modules\mob\living\simple_animal\friendly\lizard.dm" +#include "code\modules\mob\living\simple_animal\friendly\mouse.dm" +#include "code\modules\mob\living\simple_animal\friendly\pet.dm" +#include "code\modules\mob\living\simple_animal\friendly\drone\_drone.dm" +#include "code\modules\mob\living\simple_animal\friendly\drone\drones_as_items.dm" +#include "code\modules\mob\living\simple_animal\friendly\drone\extra_drone_types.dm" +#include "code\modules\mob\living\simple_animal\friendly\drone\interaction.dm" +#include "code\modules\mob\living\simple_animal\friendly\drone\inventory.dm" +#include "code\modules\mob\living\simple_animal\friendly\drone\say.dm" +#include "code\modules\mob\living\simple_animal\friendly\drone\verbs.dm" +#include "code\modules\mob\living\simple_animal\friendly\drone\visuals_icons.dm" +#include "code\modules\mob\living\simple_animal\guardian\guardian.dm" +#include "code\modules\mob\living\simple_animal\hostile\alien.dm" +#include "code\modules\mob\living\simple_animal\hostile\bear.dm" +#include "code\modules\mob\living\simple_animal\hostile\bees.dm" +#include "code\modules\mob\living\simple_animal\hostile\carp.dm" +#include "code\modules\mob\living\simple_animal\hostile\creature.dm" +#include "code\modules\mob\living\simple_animal\hostile\eyeballs.dm" +#include "code\modules\mob\living\simple_animal\hostile\faithless.dm" +#include "code\modules\mob\living\simple_animal\hostile\giant_spider.dm" +#include "code\modules\mob\living\simple_animal\hostile\headcrab.dm" +#include "code\modules\mob\living\simple_animal\hostile\hivebot.dm" +#include "code\modules\mob\living\simple_animal\hostile\hostile.dm" +#include "code\modules\mob\living\simple_animal\hostile\illusion.dm" +#include "code\modules\mob\living\simple_animal\hostile\killertomato.dm" +#include "code\modules\mob\living\simple_animal\hostile\mimic.dm" +#include "code\modules\mob\living\simple_animal\hostile\mining_mobs.dm" +#include "code\modules\mob\living\simple_animal\hostile\mushroom.dm" +#include "code\modules\mob\living\simple_animal\hostile\pirate.dm" +#include "code\modules\mob\living\simple_animal\hostile\russian.dm" +#include "code\modules\mob\living\simple_animal\hostile\skeleton.dm" +#include "code\modules\mob\living\simple_animal\hostile\statue.dm" +#include "code\modules\mob\living\simple_animal\hostile\syndicate.dm" +#include "code\modules\mob\living\simple_animal\hostile\tree.dm" +#include "code\modules\mob\living\simple_animal\hostile\venus_human_trap.dm" +#include "code\modules\mob\living\simple_animal\hostile\wizard.dm" +#include "code\modules\mob\living\simple_animal\hostile\zombie.dm" +#include "code\modules\mob\living\simple_animal\hostile\retaliate\bat.dm" +#include "code\modules\mob\living\simple_animal\hostile\retaliate\clown.dm" +#include "code\modules\mob\living\simple_animal\hostile\retaliate\retaliate.dm" +#include "code\modules\mob\living\simple_animal\morph\morph.dm" +#include "code\modules\mob\living\simple_animal\revenant\revenant.dm" +#include "code\modules\mob\living\simple_animal\revenant\revenant_abilities.dm" +#include "code\modules\mob\living\simple_animal\revenant\revenant_blight.dm" +#include "code\modules\mob\living\simple_animal\revenant\revenant_spawn_event.dm" +#include "code\modules\mob\living\simple_animal\slaughter\slaughter.dm" +#include "code\modules\mob\living\simple_animal\slaughter\slaughterevent.dm" +#include "code\modules\mob\living\simple_animal\slime\death.dm" +#include "code\modules\mob\living\simple_animal\slime\emote.dm" +#include "code\modules\mob\living\simple_animal\slime\life.dm" +#include "code\modules\mob\living\simple_animal\slime\powers.dm" +#include "code\modules\mob\living\simple_animal\slime\say.dm" +#include "code\modules\mob\living\simple_animal\slime\slime.dm" +#include "code\modules\mob\living\simple_animal\slime\subtypes.dm" +#include "code\modules\mob\new_player\login.dm" +#include "code\modules\mob\new_player\logout.dm" +#include "code\modules\mob\new_player\new_player.dm" +#include "code\modules\mob\new_player\poll.dm" +#include "code\modules\mob\new_player\preferences_setup.dm" +#include "code\modules\mob\new_player\sprite_accessories.dm" +#include "code\modules\nano\external.dm" +#include "code\modules\nano\nanoui.dm" +#include "code\modules\nano\subsystem.dm" +#include "code\modules\nano\states\admin.dm" +#include "code\modules\nano\states\conscious.dm" +#include "code\modules\nano\states\contained.dm" +#include "code\modules\nano\states\deep_inventory.dm" +#include "code\modules\nano\states\default.dm" +#include "code\modules\nano\states\hands.dm" +#include "code\modules\nano\states\inventory.dm" +#include "code\modules\nano\states\notcontained.dm" +#include "code\modules\nano\states\physical.dm" +#include "code\modules\nano\states\self.dm" +#include "code\modules\nano\states\states.dm" +#include "code\modules\nano\states\zlevel.dm" +#include "code\modules\ninja\__ninjaDefines.dm" +#include "code\modules\ninja\admin_ninja_verbs.dm" +#include "code\modules\ninja\energy_katana.dm" +#include "code\modules\ninja\ninja_event.dm" +#include "code\modules\ninja\Ninja_Readme.dm" +#include "code\modules\ninja\suit\gloves.dm" +#include "code\modules\ninja\suit\head.dm" +#include "code\modules\ninja\suit\mask.dm" +#include "code\modules\ninja\suit\ninjaDrainAct.dm" +#include "code\modules\ninja\suit\shoes.dm" +#include "code\modules\ninja\suit\suit.dm" +#include "code\modules\ninja\suit\suit_attackby.dm" +#include "code\modules\ninja\suit\suit_initialisation.dm" +#include "code\modules\ninja\suit\suit_process.dm" +#include "code\modules\ninja\suit\suit_verbs_handlers.dm" +#include "code\modules\ninja\suit\n_suit_verbs\energy_net_nets.dm" +#include "code\modules\ninja\suit\n_suit_verbs\ninja_adrenaline.dm" +#include "code\modules\ninja\suit\n_suit_verbs\ninja_cost_check.dm" +#include "code\modules\ninja\suit\n_suit_verbs\ninja_empulse.dm" +#include "code\modules\ninja\suit\n_suit_verbs\ninja_net.dm" +#include "code\modules\ninja\suit\n_suit_verbs\ninja_smoke.dm" +#include "code\modules\ninja\suit\n_suit_verbs\ninja_stars.dm" +#include "code\modules\ninja\suit\n_suit_verbs\ninja_stealth.dm" +#include "code\modules\ninja\suit\n_suit_verbs\ninja_sword_recall.dm" +#include "code\modules\ninja\suit\n_suit_verbs\ninja_teleporting.dm" +#include "code\modules\paperwork\clipboard.dm" +#include "code\modules\paperwork\filingcabinet.dm" +#include "code\modules\paperwork\folders.dm" +#include "code\modules\paperwork\handlabeler.dm" +#include "code\modules\paperwork\paper.dm" +#include "code\modules\paperwork\paperbin.dm" +#include "code\modules\paperwork\pen.dm" +#include "code\modules\paperwork\photocopier.dm" +#include "code\modules\paperwork\photography.dm" +#include "code\modules\paperwork\stamps.dm" +#include "code\modules\power\apc.dm" +#include "code\modules\power\cable.dm" +#include "code\modules\power\cell.dm" +#include "code\modules\power\engine.dm" +#include "code\modules\power\generator.dm" +#include "code\modules\power\gravitygenerator.dm" +#include "code\modules\power\lighting.dm" +#include "code\modules\power\port_gen.dm" +#include "code\modules\power\power.dm" +#include "code\modules\power\powernet.dm" +#include "code\modules\power\smes.dm" +#include "code\modules\power\solar.dm" +#include "code\modules\power\switch.dm" +#include "code\modules\power\terminal.dm" +#include "code\modules\power\tracker.dm" +#include "code\modules\power\turbine.dm" +#include "code\modules\power\antimatter\containment_jar.dm" +#include "code\modules\power\antimatter\control.dm" +#include "code\modules\power\antimatter\shielding.dm" +#include "code\modules\power\singularity\collector.dm" +#include "code\modules\power\singularity\containment_field.dm" +#include "code\modules\power\singularity\emitter.dm" +#include "code\modules\power\singularity\field_generator.dm" +#include "code\modules\power\singularity\generator.dm" +#include "code\modules\power\singularity\investigate.dm" +#include "code\modules\power\singularity\narsie.dm" +#include "code\modules\power\singularity\singularity.dm" +#include "code\modules\power\singularity\particle_accelerator\particle.dm" +#include "code\modules\power\singularity\particle_accelerator\particle_accelerator.dm" +#include "code\modules\power\singularity\particle_accelerator\particle_chamber.dm" +#include "code\modules\power\singularity\particle_accelerator\particle_control.dm" +#include "code\modules\power\singularity\particle_accelerator\particle_emitter.dm" +#include "code\modules\power\singularity\particle_accelerator\particle_power.dm" +#include "code\modules\power\supermatter\supermatter.dm" +#include "code\modules\power\supermatter\supermatter_crate.dm" +#include "code\modules\procedural mapping\mapGenerator.dm" +#include "code\modules\procedural mapping\mapGeneratorModule.dm" +#include "code\modules\procedural mapping\mapGeneratorReadme.dm" +#include "code\modules\procedural mapping\mapGeneratorModules\helpers.dm" +#include "code\modules\procedural mapping\mapGeneratorModules\nature.dm" +#include "code\modules\procedural mapping\mapGenerators\asteroid.dm" +#include "code\modules\procedural mapping\mapGenerators\nature.dm" +#include "code\modules\procedural mapping\mapGenerators\shuttle.dm" +#include "code\modules\procedural mapping\mapGenerators\syndicate.dm" +#include "code\modules\projectiles\ammunition.dm" +#include "code\modules\projectiles\firing.dm" +#include "code\modules\projectiles\gun.dm" +#include "code\modules\projectiles\pins.dm" +#include "code\modules\projectiles\projectile.dm" +#include "code\modules\projectiles\ammunition\ammo_casings.dm" +#include "code\modules\projectiles\ammunition\boxes.dm" +#include "code\modules\projectiles\ammunition\energy.dm" +#include "code\modules\projectiles\ammunition\magazines.dm" +#include "code\modules\projectiles\ammunition\special.dm" +#include "code\modules\projectiles\guns\energy.dm" +#include "code\modules\projectiles\guns\grenade_launcher.dm" +#include "code\modules\projectiles\guns\magic.dm" +#include "code\modules\projectiles\guns\medbeam.dm" +#include "code\modules\projectiles\guns\projectile.dm" +#include "code\modules\projectiles\guns\syringe_gun.dm" +#include "code\modules\projectiles\guns\energy\laser.dm" +#include "code\modules\projectiles\guns\energy\nuclear.dm" +#include "code\modules\projectiles\guns\energy\pulse.dm" +#include "code\modules\projectiles\guns\energy\special.dm" +#include "code\modules\projectiles\guns\energy\stun.dm" +#include "code\modules\projectiles\guns\magic\staff.dm" +#include "code\modules\projectiles\guns\magic\wand.dm" +#include "code\modules\projectiles\guns\projectile\automatic.dm" +#include "code\modules\projectiles\guns\projectile\launchers.dm" +#include "code\modules\projectiles\guns\projectile\pistol.dm" +#include "code\modules\projectiles\guns\projectile\revolver.dm" +#include "code\modules\projectiles\guns\projectile\shotgun.dm" +#include "code\modules\projectiles\guns\projectile\sniper.dm" +#include "code\modules\projectiles\guns\projectile\toy.dm" +#include "code\modules\projectiles\projectile\beams.dm" +#include "code\modules\projectiles\projectile\bullets.dm" +#include "code\modules\projectiles\projectile\energy.dm" +#include "code\modules\projectiles\projectile\magic.dm" +#include "code\modules\projectiles\projectile\reusable.dm" +#include "code\modules\projectiles\projectile\special.dm" +#include "code\modules\reagents\reagent_containers.dm" +#include "code\modules\reagents\reagent_dispenser.dm" +#include "code\modules\reagents\chemistry\colors.dm" +#include "code\modules\reagents\chemistry\holder.dm" +#include "code\modules\reagents\chemistry\readme.dm" +#include "code\modules\reagents\chemistry\reagents.dm" +#include "code\modules\reagents\chemistry\recipes.dm" +#include "code\modules\reagents\chemistry\machinery\chem_dispenser.dm" +#include "code\modules\reagents\chemistry\machinery\chem_heater.dm" +#include "code\modules\reagents\chemistry\machinery\chem_master.dm" +#include "code\modules\reagents\chemistry\machinery\pandemic.dm" +#include "code\modules\reagents\chemistry\machinery\reagentgrinder.dm" +#include "code\modules\reagents\chemistry\reagents\alcohol_reagents.dm" +#include "code\modules\reagents\chemistry\reagents\blob_reagents.dm" +#include "code\modules\reagents\chemistry\reagents\drink_reagents.dm" +#include "code\modules\reagents\chemistry\reagents\drug_reagents.dm" +#include "code\modules\reagents\chemistry\reagents\food_reagents.dm" +#include "code\modules\reagents\chemistry\reagents\medicine_reagents.dm" +#include "code\modules\reagents\chemistry\reagents\other_reagents.dm" +#include "code\modules\reagents\chemistry\reagents\pyrotechnic_reagents.dm" +#include "code\modules\reagents\chemistry\reagents\toxin_reagents.dm" +#include "code\modules\reagents\chemistry\recipes\drugs.dm" +#include "code\modules\reagents\chemistry\recipes\medicine.dm" +#include "code\modules\reagents\chemistry\recipes\others.dm" +#include "code\modules\reagents\chemistry\recipes\pyrotechnics.dm" +#include "code\modules\reagents\chemistry\recipes\slime_extracts.dm" +#include "code\modules\reagents\chemistry\recipes\toxins.dm" +#include "code\modules\reagents\reagent_containers\blood_pack.dm" +#include "code\modules\reagents\reagent_containers\borghydro.dm" +#include "code\modules\reagents\reagent_containers\bottle.dm" +#include "code\modules\reagents\reagent_containers\dropper.dm" +#include "code\modules\reagents\reagent_containers\glass.dm" +#include "code\modules\reagents\reagent_containers\hypospray.dm" +#include "code\modules\reagents\reagent_containers\patch.dm" +#include "code\modules\reagents\reagent_containers\pill.dm" +#include "code\modules\reagents\reagent_containers\spray.dm" +#include "code\modules\reagents\reagent_containers\syringes.dm" +#include "code\modules\recycling\conveyor2.dm" +#include "code\modules\recycling\disposal-construction.dm" +#include "code\modules\recycling\disposal-structures.dm" +#include "code\modules\recycling\disposal-unit.dm" +#include "code\modules\recycling\sortingmachinery.dm" +#include "code\modules\research\circuitprinter.dm" +#include "code\modules\research\designs.dm" +#include "code\modules\research\destructive_analyzer.dm" +#include "code\modules\research\experimentor.dm" +#include "code\modules\research\message_server.dm" +#include "code\modules\research\protolathe.dm" +#include "code\modules\research\rd-readme.dm" +#include "code\modules\research\rdconsole.dm" +#include "code\modules\research\rdmachines.dm" +#include "code\modules\research\research.dm" +#include "code\modules\research\server.dm" +#include "code\modules\research\stock_parts.dm" +#include "code\modules\research\designs\AI_module_designs.dm" +#include "code\modules\research\designs\autolathe_designs.dm" +#include "code\modules\research\designs\comp_board_designs.dm" +#include "code\modules\research\designs\machine_designs.dm" +#include "code\modules\research\designs\mecha_designs.dm" +#include "code\modules\research\designs\mechfabricator_designs.dm" +#include "code\modules\research\designs\medical_designs.dm" +#include "code\modules\research\designs\power_designs.dm" +#include "code\modules\research\designs\stock_parts_designs.dm" +#include "code\modules\research\designs\telecomms_designs.dm" +#include "code\modules\research\designs\weapon_designs.dm" +#include "code\modules\research\xenobiology\xenobio_camera.dm" +#include "code\modules\research\xenobiology\xenobiology.dm" +#include "code\modules\security levels\keycard authentication.dm" +#include "code\modules\security levels\security levels.dm" +#include "code\modules\shuttle\shuttle.dm" +#include "code\modules\space transition\space_transition.dm" +#include "code\modules\surgery\cavity_implant.dm" +#include "code\modules\surgery\core_removal.dm" +#include "code\modules\surgery\dental_implant.dm" +#include "code\modules\surgery\dethralling.dm" +#include "code\modules\surgery\eye_surgery.dm" +#include "code\modules\surgery\gender_reassignment.dm" +#include "code\modules\surgery\generic_steps.dm" +#include "code\modules\surgery\helpers.dm" +#include "code\modules\surgery\implant_removal.dm" +#include "code\modules\surgery\limb augmentation.dm" +#include "code\modules\surgery\lipoplasty.dm" +#include "code\modules\surgery\organ_manipulation.dm" +#include "code\modules\surgery\plastic_surgery.dm" +#include "code\modules\surgery\remove_embedded_object.dm" +#include "code\modules\surgery\surgery.dm" +#include "code\modules\surgery\surgery_step.dm" +#include "code\modules\surgery\tools.dm" +#include "code\modules\surgery\organs\augments_external.dm" +#include "code\modules\surgery\organs\augments_eyes.dm" +#include "code\modules\surgery\organs\augments_internal.dm" +#include "code\modules\surgery\organs\helpers.dm" +#include "code\modules\surgery\organs\organ_external.dm" +#include "code\modules\surgery\organs\organ_internal.dm" +#include "code\modules\telesci\bscrystal.dm" +#include "code\modules\telesci\gps.dm" +#include "code\modules\telesci\telepad.dm" +#include "code\modules\telesci\telesci_computer.dm" +#include "code\modules\tooltip\tooltip.dm" +#include "code\orphaned procs\AStar.dm" +#include "code\orphaned procs\dbcore.dm" +#include "code\orphaned procs\priority_announce.dm" +#include "code\orphaned procs\statistics.dm" +#include "interface\interface.dm" +#include "interface\stylesheet.dm" +#include "interface\skin.dmf" +// END_INCLUDE